48c34d39dd8cf8770fd504d6cb4daac5cee1bc34
[profile/ivi/qtdeclarative.git] / src / declarative / scenegraph / coreapi / qsgrenderer.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qsgrenderer_p.h"
43 #include "qsgnode.h"
44 #include "qsgmaterial.h"
45 #include "qsgnodeupdater_p.h"
46
47 #include "private/qsgadaptationlayer_p.h"
48
49 #include <QGLShaderProgram>
50 #include <qglframebufferobject.h>
51 #include <QtGui/qapplication.h>
52
53 #include <qdatetime.h>
54
55 QT_BEGIN_NAMESPACE
56
57 //#define RENDERER_DEBUG
58 //#define QT_GL_NO_SCISSOR_TEST
59
60 // #define QSG_RENDERER_TIMING
61 #ifdef QSG_RENDERER_TIMING
62 static QTime frameTimer;
63 static int preprocessTime;
64 static int updatePassTime;
65 static int frameNumber = 0;
66 #endif
67
68 void Bindable::clear(QSGRenderer::ClearMode mode) const
69 {
70     GLuint bits = 0;
71     if (mode & QSGRenderer::ClearColorBuffer) bits |= GL_COLOR_BUFFER_BIT;
72     if (mode & QSGRenderer::ClearDepthBuffer) bits |= GL_DEPTH_BUFFER_BIT;
73     if (mode & QSGRenderer::ClearStencilBuffer) bits |= GL_STENCIL_BUFFER_BIT;
74     glClear(bits);
75 }
76
77 // Reactivate the color buffer after switching to the stencil.
78 void Bindable::reactivate() const
79 {
80     glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
81 }
82
83 BindableFbo::BindableFbo(QGLFramebufferObject *fbo) : m_fbo(fbo)
84 {
85 }
86
87
88 void BindableFbo::bind() const
89 {
90     m_fbo->bind();
91 }
92
93 /*!
94     \class QSGRenderer
95     \brief The renderer class is the abstract baseclass use for rendering the
96     QML scene graph.
97
98     The renderer is not tied to any particular surface. It expects a context to
99     be current and will render into that surface according to how the device rect,
100     viewport rect and projection transformation are set up.
101
102     Rendering is a sequence of steps initiated by calling renderScene(). This will
103     effectively draw the scene graph starting at the root node. The QSGNode::preprocess()
104     function will be called for all the nodes in the graph, followed by an update
105     pass which updates all matrices, opacity, clip states and similar in the graph.
106     Because the update pass is called after preprocess, it is safe to modify the graph
107     during preprocess. To run a custom update pass over the graph, install a custom
108     QSGNodeUpdater using setNodeUpdater(). Once all the graphs dirty states are updated,
109     the virtual render() function is called.
110
111     The render() function is implemented by QSGRenderer subclasses to render the graph
112     in the most optimal way for a given hardware.
113
114     The renderer can make use of stencil, depth and color buffers in addition to the
115     scissor rect.
116
117     \internal
118  */
119
120
121 QSGRenderer::QSGRenderer(QSGContext *context)
122     : QObject()
123     , m_clear_color(Qt::transparent)
124     , m_clear_mode(ClearColorBuffer | ClearDepthBuffer)
125     , m_current_opacity(1)
126     , m_context(context)
127     , m_root_node(0)
128     , m_node_updater(0)
129     , m_bindable(0)
130     , m_changed_emitted(false)
131     , m_mirrored(false)
132     , m_is_rendering(false)
133 {
134     initializeGLFunctions();
135 }
136
137
138 QSGRenderer::~QSGRenderer()
139 {
140     setRootNode(0);
141     delete m_node_updater;
142 }
143
144 /*!
145     Returns the scene graph context for this renderer.
146
147     \internal
148  */
149
150 QSGContext *QSGRenderer::context()
151 {
152     return m_context;
153 }
154
155
156
157
158 /*!
159     Returns the node updater that this renderer uses to update states in the
160     scene graph.
161
162     If no updater is specified a default one is constructed.
163  */
164
165 QSGNodeUpdater *QSGRenderer::nodeUpdater() const
166 {
167     if (!m_node_updater)
168         const_cast<QSGRenderer *>(this)->m_node_updater = new QSGNodeUpdater();
169     return m_node_updater;
170 }
171
172
173 /*!
174     Sets the node updater that this renderer uses to update states in the
175     scene graph.
176
177     This will delete and override any existing node updater
178   */
179 void QSGRenderer::setNodeUpdater(QSGNodeUpdater *updater)
180 {
181     if (m_node_updater)
182         delete m_node_updater;
183     m_node_updater = updater;
184 }
185
186
187 void QSGRenderer::setRootNode(QSGRootNode *node)
188 {
189     if (m_root_node == node)
190         return;
191     if (m_root_node) {
192         m_root_node->m_renderers.removeOne(this);
193         nodeChanged(m_root_node, QSGNode::DirtyNodeRemoved);
194     }
195     m_root_node = node;
196     if (m_root_node) {
197         Q_ASSERT(!m_root_node->m_renderers.contains(this));
198         m_root_node->m_renderers << this;
199         nodeChanged(m_root_node, QSGNode::DirtyNodeAdded);
200     }
201 }
202
203
204 void QSGRenderer::renderScene()
205 {
206     class B : public Bindable
207     {
208     public:
209         void bind() const { QGLFramebufferObject::bindDefault(); }
210     } b;
211     renderScene(b);
212 }
213
214 void QSGRenderer::renderScene(const Bindable &bindable)
215 {
216     if (!m_root_node)
217         return;
218
219     m_is_rendering = true;
220 #ifdef QSG_RENDERER_TIMING
221     frameTimer.start();
222 #endif
223
224     m_bindable = &bindable;
225     preprocess();
226
227     bindable.bind();
228 #ifdef QSG_RENDERER_TIMING
229     int bindTime = frameTimer.elapsed();
230 #endif
231
232 #ifndef QT_NO_DEBUG
233     // Sanity check that attribute registers are disabled
234     {
235         GLint count;
236         glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &count);
237         GLint enabled;
238         for (int i=0; i<count; ++i) {
239             glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &enabled);
240             if (enabled) {
241                 qWarning("QSGRenderer: attribute %d is enabled, this can lead to memory corruption and crashes.", i);
242             }
243         }
244     }
245 #endif
246
247     render();
248 #ifdef QSG_RENDERER_TIMING
249     int renderTime = frameTimer.elapsed();
250 #endif
251
252     glDisable(GL_SCISSOR_TEST);
253     m_is_rendering = false;
254     m_changed_emitted = false;
255     m_bindable = 0;
256
257 #ifdef QSG_RENDERER_TIMING
258     printf(" - Breakdown of frametime: preprocess=%d, updates=%d, binding=%d, render=%d, total=%d\n",
259            preprocessTime,
260            updatePassTime - preprocessTime,
261            bindTime - updatePassTime,
262            renderTime - bindTime,
263            renderTime);
264 #endif
265 }
266
267 void QSGRenderer::setProjectionMatrixToDeviceRect()
268 {
269     setProjectionMatrixToRect(m_device_rect);
270 }
271
272 void QSGRenderer::setProjectionMatrixToRect(const QRectF &rect)
273 {
274     QMatrix4x4 matrix;
275     matrix.ortho(rect.x(),
276                  rect.x() + rect.width(),
277                  rect.y() + rect.height(),
278                  rect.y(),
279                  qreal(0.01),
280                  -1);
281     setProjectionMatrix(matrix);
282 }
283
284 void QSGRenderer::setProjectionMatrix(const QMatrix4x4 &matrix)
285 {
286     m_projection_matrix = matrix;
287     // Mirrored relative to the usual Qt coordinate system with origin in the top left corner.
288     m_mirrored = matrix(0, 0) * matrix(1, 1) - matrix(0, 1) * matrix(1, 0) > 0;
289 }
290
291 void QSGRenderer::setClearColor(const QColor &color)
292 {
293     m_clear_color = color;
294 }
295
296 /*!
297     Updates internal data structures and emits the sceneGraphChanged() signal.
298
299     If \a flags contains the QSGNode::DirtyNodeRemoved flag, the node might be
300     in the process of being destroyed. It is then not safe to downcast the node
301     pointer.
302 */
303
304 void QSGRenderer::nodeChanged(QSGNode *node, QSGNode::DirtyFlags flags)
305 {
306     Q_UNUSED(node);
307     Q_UNUSED(flags);
308
309     if (flags & QSGNode::DirtyNodeAdded)
310         addNodesToPreprocess(node);
311     if (flags & QSGNode::DirtyNodeRemoved)
312         removeNodesToPreprocess(node);
313
314     if (!m_changed_emitted && !m_is_rendering) {
315         // Premature overoptimization to avoid excessive signal emissions
316         m_changed_emitted = true;
317         emit sceneGraphChanged();
318     }
319 }
320
321 void QSGRenderer::materialChanged(QSGGeometryNode *, QSGMaterial *, QSGMaterial *)
322 {
323 }
324
325 void QSGRenderer::preprocess()
326 {
327     Q_ASSERT(m_root_node);
328
329     // We need to take a copy here, in case any of the preprocess calls deletes a node that
330     // is in the preprocess list and thus, changes the m_nodes_to_preprocess behind our backs
331     // For the default case, when this does not happen, the cost is neglishible.
332     QSet<QSGNode *> items = m_nodes_to_preprocess;
333
334     for (QSet<QSGNode *>::const_iterator it = items.constBegin();
335          it != items.constEnd(); ++it) {
336         QSGNode *n = *it;
337         if (!nodeUpdater()->isNodeBlocked(n, m_root_node)) {
338             n->preprocess();
339         }
340     }
341
342 #ifdef QSG_RENDERER_TIMING
343     preprocessTime = frameTimer.elapsed();
344 #endif
345
346     nodeUpdater()->setToplevelOpacity(context()->renderAlpha());
347     nodeUpdater()->updateStates(m_root_node);
348
349 #ifdef QSG_RENDERER_TIMING
350     updatePassTime = frameTimer.elapsed();
351 #endif
352
353 }
354
355 void QSGRenderer::addNodesToPreprocess(QSGNode *node)
356 {
357     for (QSGNode *c = node->firstChild(); c; c = c->nextSibling())
358         addNodesToPreprocess(c);
359     if (node->flags() & QSGNode::UsePreprocess)
360         m_nodes_to_preprocess.insert(node);
361 }
362
363 void QSGRenderer::removeNodesToPreprocess(QSGNode *node)
364 {
365     for (QSGNode *c = node->firstChild(); c; c = c->nextSibling())
366         removeNodesToPreprocess(c);
367     if (node->flags() & QSGNode::UsePreprocess)
368         m_nodes_to_preprocess.remove(node);
369 }
370
371
372 /*!
373     Convenience function to set up the stencil buffer for clipping based on \a clip.
374
375     If the clip is a pixel aligned rectangle, this function will use glScissor instead
376     of stencil.
377  */
378
379 QSGRenderer::ClipType QSGRenderer::updateStencilClip(const QSGClipNode *clip)
380 {
381     if (!clip) {
382         glDisable(GL_STENCIL_TEST);
383         glDisable(GL_SCISSOR_TEST);
384         return NoClip;
385     }
386
387     bool stencilEnabled = false;
388     bool scissorEnabled = false;
389
390     glDisable(GL_SCISSOR_TEST);
391
392     int clipDepth = 0;
393     QRect clipRect;
394     while (clip) {
395         QMatrix4x4 m = m_current_projection_matrix;
396         if (clip->matrix())
397             m *= *clip->matrix();
398
399         // TODO: Check for multisampling and pixel grid alignment.
400         bool canUseScissor = clip->isRectangular()
401                            && qFuzzyIsNull(m(0, 1)) && qFuzzyIsNull(m(0, 2))
402                            && qFuzzyIsNull(m(1, 0)) && qFuzzyIsNull(m(1, 2));
403
404         if (canUseScissor) {
405             QRectF bbox = clip->clipRect();
406             qreal invW = 1 / m(3, 3);
407             qreal fx1 = (bbox.left() * m(0, 0) + m(0, 3)) * invW;
408             qreal fy1 = (bbox.bottom() * m(1, 1) + m(1, 3)) * invW;
409             qreal fx2 = (bbox.right() * m(0, 0) + m(0, 3)) * invW;
410             qreal fy2 = (bbox.top() * m(1, 1) + m(1, 3)) * invW;
411
412             GLint ix1 = qRound((fx1 + 1) * m_device_rect.width() * qreal(0.5));
413             GLint iy1 = qRound((fy1 + 1) * m_device_rect.height() * qreal(0.5));
414             GLint ix2 = qRound((fx2 + 1) * m_device_rect.width() * qreal(0.5));
415             GLint iy2 = qRound((fy2 + 1) * m_device_rect.height() * qreal(0.5));
416
417             if (!scissorEnabled) {
418                 clipRect = QRect(ix1, iy1, ix2 - ix1, iy2 - iy1);
419                 glEnable(GL_SCISSOR_TEST);
420                 scissorEnabled = true;
421             } else {
422                 clipRect &= QRect(ix1, iy1, ix2 - ix1, iy2 - iy1);
423             }
424
425             clipRect = clipRect.normalized();
426             glScissor(clipRect.x(), clipRect.y(), clipRect.width(), clipRect.height());
427         } else {
428             if (!stencilEnabled) {
429                 if (!m_clip_program.isLinked()) {
430                     m_clip_program.addShaderFromSourceCode(QGLShader::Vertex,
431                         "attribute highp vec4 vCoord;       \n"
432                         "uniform highp mat4 matrix;         \n"
433                         "void main() {                      \n"
434                         "    gl_Position = matrix * vCoord; \n"
435                         "}");
436                     m_clip_program.addShaderFromSourceCode(QGLShader::Fragment,
437                         "void main() {                                   \n"
438                         "    gl_FragColor = vec4(0.81, 0.83, 0.12, 1.0); \n" // Trolltech green ftw!
439                         "}");
440                     m_clip_program.bindAttributeLocation("vCoord", 0);
441                     m_clip_program.link();
442                     m_clip_matrix_id = m_clip_program.uniformLocation("matrix");
443                 }
444
445                 glStencilMask(0xff); // write mask
446                 glClearStencil(0);
447                 glClear(GL_STENCIL_BUFFER_BIT);
448                 glEnable(GL_STENCIL_TEST);
449                 glDisable(GL_DEPTH_TEST);
450                 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
451                 glDepthMask(GL_FALSE);
452
453                 m_clip_program.bind();
454                 m_clip_program.enableAttributeArray(0);
455
456                 stencilEnabled = true;
457             }
458
459             glStencilFunc(GL_EQUAL, clipDepth, 0xff); // stencil test, ref, test mask
460             glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); // stencil fail, z fail, z pass
461
462             const QSGGeometry *geometry = clip->geometry();
463             Q_ASSERT(geometry->attributeCount() > 0);
464             const QSGGeometry::Attribute *a = geometry->attributes();
465
466             glVertexAttribPointer(0, a->tupleSize, a->type, GL_FALSE, geometry->stride(), geometry->vertexData());
467
468             m_clip_program.setUniformValue(m_clip_matrix_id, m);
469             draw(clip);
470
471             ++clipDepth;
472         }
473
474         clip = clip->clipList();
475     }
476
477     if (stencilEnabled) {
478         m_clip_program.disableAttributeArray(0);
479         glEnable(GL_DEPTH_TEST);
480         glStencilFunc(GL_EQUAL, clipDepth, 0xff); // stencil test, ref, test mask
481         glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // stencil fail, z fail, z pass
482         glStencilMask(0); // write mask
483         bindable()->reactivate();
484         //glDepthMask(GL_TRUE); // must be reset correctly by caller.
485     } else {
486         glDisable(GL_STENCIL_TEST);
487     }
488
489     if (!scissorEnabled)
490         glDisable(GL_SCISSOR_TEST);
491
492     return stencilEnabled ? StencilClip : ScissorClip;
493 }
494
495
496 /*!
497     Issues the GL draw call for \a geometryNode.
498
499     The function assumes that attributes have been bound and set up prior
500     to making this call.
501
502     \internal
503  */
504
505 void QSGRenderer::draw(const QSGBasicGeometryNode *node)
506 {
507     const QSGGeometry *g = node->geometry();
508     if (g->indexCount()) {
509         glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), g->indexData());
510     } else {
511         glDrawArrays(g->drawingMode(), 0, g->vertexCount());
512     }
513 }
514
515
516 static inline int size_of_type(GLenum type)
517 {
518     static int sizes[] = {
519         sizeof(char),
520         sizeof(unsigned char),
521         sizeof(short),
522         sizeof(unsigned short),
523         sizeof(int),
524         sizeof(unsigned int),
525         sizeof(float),
526         2,
527         3,
528         4,
529         sizeof(double)
530     };
531     return sizes[type - GL_BYTE];
532 }
533
534 /*!
535     Convenience function to set up and bind the vertex data in \a g to the
536     required attribute positions defined in \a material.
537
538     \internal
539  */
540
541 void QSGRenderer::bindGeometry(QSGMaterialShader *material, const QSGGeometry *g)
542 {
543     char const *const *attrNames = material->attributeNames();
544     int offset = 0;
545     for (int j = 0; attrNames[j]; ++j) {
546         if (!*attrNames[j])
547             continue;
548         Q_ASSERT_X(j < g->attributeCount(), "QSGRenderer::bindGeometry()", "Geometry lacks attribute required by material");
549         const QSGGeometry::Attribute &a = g->attributes()[j];
550         Q_ASSERT_X(j == a.position, "QSGRenderer::bindGeometry()", "Geometry does not have continuous attribute positions");
551 #if defined(QT_OPENGL_ES_2)
552         GLboolean normalize = a.type != GL_FLOAT;
553 #else
554         GLboolean normalize = a.type != GL_FLOAT && a.type != GL_DOUBLE;
555 #endif
556         glVertexAttribPointer(a.position, a.tupleSize, a.type, normalize, g->stride(), (char *) g->vertexData() + offset);
557         offset += a.tupleSize * size_of_type(a.type);
558     }
559 }
560
561
562 QT_END_NAMESPACE