Remove "All rights reserved" line from license headers.
[profile/ivi/qtdeclarative.git] / src / quick / scenegraph / coreapi / qsgdefaultrenderer.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42
43 #define GL_GLEXT_PROTOTYPES
44
45 #include "qsgdefaultrenderer_p.h"
46 #include "qsgmaterial.h"
47
48 #include <QtCore/qvarlengtharray.h>
49 #include <QtGui/qguiapplication.h>
50 #include <QtCore/qpair.h>
51 #include <QtCore/QElapsedTimer>
52
53 //#define FORCE_NO_REORDER
54
55 // #define RENDERER_DEBUG
56 #ifdef RENDERER_DEBUG
57 #define DEBUG_THRESHOLD 0
58 QElapsedTimer debugTimer;
59 int materialChanges;
60 int geometryNodesDrawn;
61 #endif
62
63 QT_BEGIN_NAMESPACE
64
65 static bool nodeLessThan(QSGGeometryNode *a, QSGGeometryNode *b)
66 {
67     // Sort by clip...
68     if (a->clipList() != b->clipList())
69         return a->clipList() < b->clipList();
70
71     // Sort by material definition
72     QSGMaterialType *aDef = a->material()->type();
73     QSGMaterialType *bDef = b->material()->type();
74
75     if (aDef != bDef)
76         return aDef < bDef;
77
78     // Sort by material state
79     int cmp = a->material()->compare(b->material());
80     if (cmp != 0)
81         return cmp < 0;
82
83     return a->matrix() < b->matrix();
84 }
85
86 static bool nodeLessThanWithRenderOrder(QSGGeometryNode *a, QSGGeometryNode *b)
87 {
88     // Sort by clip...
89     if (a->clipList() != b->clipList())
90         return a->clipList() < b->clipList();
91
92     // Sort by material definition
93     QSGMaterialType *aDef = a->material()->type();
94     QSGMaterialType *bDef = b->material()->type();
95
96     if (!(a->material()->flags() & QSGMaterial::Blending)) {
97         int aOrder = a->renderOrder();
98         int bOrder = b->renderOrder();
99         if (aOrder != bOrder)
100             return aOrder > bOrder;
101     }
102
103     if (aDef != bDef)
104         return aDef < bDef;
105
106     // Sort by material state
107     int cmp = a->material()->compare(b->material());
108     if (cmp != 0)
109         return cmp < 0;
110
111     return a->matrix() < b->matrix();
112 }
113
114
115 QSGDefaultRenderer::IndexGeometryNodePair::IndexGeometryNodePair(int i, QSGGeometryNode *node)
116     : QPair<int, QSGGeometryNode *>(i, node)
117 {
118 }
119
120 bool QSGDefaultRenderer::IndexGeometryNodePair::operator < (const QSGDefaultRenderer::IndexGeometryNodePair &other) const
121 {
122     return nodeLessThan(second, other.second);
123 }
124
125
126 QSGDefaultRenderer::IndexGeometryNodePairHeap::IndexGeometryNodePairHeap()
127     : v(64)
128 {
129 }
130
131 void QSGDefaultRenderer::IndexGeometryNodePairHeap::insert(const QSGDefaultRenderer::IndexGeometryNodePair &x)
132 {
133     int i = v.size();
134     v.add(x);
135     while (i != 0 && v.at(i) < v.at(parent(i))) {
136         qSwap(v.at(parent(i)), v.at(i));
137         i = parent(i);
138     }
139 }
140
141 QSGDefaultRenderer::IndexGeometryNodePair QSGDefaultRenderer::IndexGeometryNodePairHeap::pop()
142 {
143     IndexGeometryNodePair x = top();
144     if (v.size() > 1)
145         qSwap(v.first(), v.last());
146     v.pop_back();
147     int i = 0;
148     while (left(i) < v.size()) {
149         int low = left(i);
150         if (right(i) < v.size() && v.at(right(i)) < v.at(low))
151             low = right(i);
152         if (!(v.at(low) < v.at(i)))
153             break;
154         qSwap(v.at(i), v.at(low));
155         i = low;
156     }
157     return x;
158 }
159
160
161 QSGDefaultRenderer::QSGDefaultRenderer(QSGContext *context)
162     : QSGRenderer(context)
163     , m_opaqueNodes(64)
164     , m_transparentNodes(64)
165     , m_tempNodes(64)
166     , m_rebuild_lists(false)
167     , m_needs_sorting(false)
168     , m_sort_front_to_back(false)
169     , m_currentRenderOrder(1)
170 {
171     QStringList args = qApp->arguments();
172 #if defined(QML_RUNTIME_TESTING)
173     m_render_opaque_nodes = !args.contains(QLatin1String("--no-opaque-nodes"));
174     m_render_alpha_nodes = !args.contains(QLatin1String("--no-alpha-nodes"));
175 #endif
176 }
177
178 void QSGDefaultRenderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state)
179 {
180     QSGRenderer::nodeChanged(node, state);
181
182     const quint32 rebuildBits = QSGNode::DirtyNodeAdded | QSGNode::DirtyNodeRemoved
183                                 | QSGNode::DirtyMaterial | QSGNode::DirtyOpacity
184                                 | QSGNode::DirtyForceUpdate | QSGNode::DirtyChildrenDoNotOverlap;
185
186     if (state & rebuildBits)
187         m_rebuild_lists = true;
188
189     if (state & (rebuildBits | QSGNode::DirtyClipList))
190         m_needs_sorting = true;
191 }
192
193 void QSGDefaultRenderer::render()
194 {
195 #if defined (QML_RUNTIME_TESTING)
196     static bool dumpTree = qApp->arguments().contains(QLatin1String("--dump-tree"));
197     if (dumpTree) {
198         printf("\n\n");
199         QSGNodeDumper::dump(rootNode());
200     }
201 #endif
202
203 #ifdef RENDERER_DEBUG
204     debugTimer.invalidate();
205     debugTimer.start();
206     geometryNodesDrawn = 0;
207     materialChanges = 0;
208 #endif
209
210     glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
211     glDisable(GL_BLEND);
212
213     glFrontFace(isMirrored() ? GL_CW : GL_CCW);
214     glDisable(GL_CULL_FACE);
215
216     glEnable(GL_DEPTH_TEST);
217     glDepthMask(true);
218     glDepthFunc(GL_GREATER);
219 #if defined(QT_OPENGL_ES)
220     glClearDepthf(0);
221 #else
222     glClearDepth(0);
223 #endif
224
225     glDisable(GL_SCISSOR_TEST);
226     glClearColor(m_clear_color.redF(), m_clear_color.greenF(), m_clear_color.blueF(), m_clear_color.alphaF());
227
228 #ifdef RENDERER_DEBUG
229     int debugtimeSetup = debugTimer.elapsed();
230 #endif
231
232     bindable()->clear(clearMode());
233
234 #ifdef RENDERER_DEBUG
235     int debugtimeClear = debugTimer.elapsed();
236 #endif
237
238     QRect r = viewportRect();
239     glViewport(r.x(), deviceRect().bottom() - r.bottom(), r.width(), r.height());
240     m_current_projection_matrix = projectionMatrix();
241     m_current_model_view_matrix.setToIdentity();
242
243     m_currentClip = 0;
244     glDisable(GL_STENCIL_TEST);
245
246     m_currentMaterial = 0;
247     m_currentProgram = 0;
248     m_currentMatrix = 0;
249
250     if (m_rebuild_lists) {
251         m_opaqueNodes.reset();
252         m_transparentNodes.reset();
253         m_currentRenderOrder = 1;
254         buildLists(rootNode());
255         m_rebuild_lists = false;
256     }
257
258 #ifdef RENDERER_DEBUG
259     int debugtimeLists = debugTimer.elapsed();
260 #endif
261
262
263     if (m_needs_sorting) {
264         if (!m_opaqueNodes.isEmpty()) {
265             qSort(&m_opaqueNodes.first(), &m_opaqueNodes.first() + m_opaqueNodes.size(),
266                   m_sort_front_to_back
267                   ? nodeLessThanWithRenderOrder
268                   : nodeLessThan);
269         }
270         m_needs_sorting = false;
271     }
272
273 #ifdef RENDERER_DEBUG
274     int debugtimeSorting = debugTimer.elapsed();
275 #endif
276
277     m_renderOrderMatrix.setToIdentity();
278     m_renderOrderMatrix.scale(1, 1, qreal(1) / m_currentRenderOrder);
279
280     glDisable(GL_BLEND);
281     glDepthMask(true);
282 #ifdef QML_RUNTIME_TESTING
283     if (m_render_opaque_nodes)
284 #endif
285     {
286 #if defined (QML_RUNTIME_TESTING)
287         if (dumpTree)
288             qDebug() << "Opaque Nodes:";
289 #endif
290         renderNodes(m_opaqueNodes);
291     }
292
293 #ifdef RENDERER_DEBUG
294     int debugtimeOpaque = debugTimer.elapsed();
295     int opaqueNodes = geometryNodesDrawn;
296     int opaqueMaterialChanges = materialChanges;
297 #endif
298
299     glEnable(GL_BLEND);
300     glDepthMask(false);
301 #ifdef QML_RUNTIME_TESTING
302     if (m_render_alpha_nodes)
303 #endif
304     {
305 #if defined (QML_RUNTIME_TESTING)
306         if (dumpTree)
307             qDebug() << "Alpha Nodes:";
308 #endif
309         renderNodes(m_transparentNodes);
310     }
311
312 #ifdef RENDERER_DEBUG
313     int debugtimeAlpha = debugTimer.elapsed();
314 #endif
315
316
317     if (m_currentProgram)
318         m_currentProgram->deactivate();
319
320 #ifdef RENDERER_DEBUG
321     if (debugTimer.elapsed() > DEBUG_THRESHOLD) {
322         printf(" --- Renderer breakdown:\n"
323                "     - setup=%d, clear=%d, building=%d, sorting=%d, opaque=%d, alpha=%d\n"
324                "     - material changes: opaque=%d, alpha=%d, total=%d\n"
325                "     - geometry ndoes: opaque=%d, alpha=%d, total=%d\n",
326                debugtimeSetup,
327                debugtimeClear - debugtimeSetup,
328                debugtimeLists - debugtimeClear,
329                debugtimeSorting - debugtimeLists,
330                debugtimeOpaque - debugtimeSorting,
331                debugtimeAlpha - debugtimeOpaque,
332                opaqueMaterialChanges, materialChanges - opaqueMaterialChanges, materialChanges,
333                opaqueNodes, geometryNodesDrawn - opaqueNodes, geometryNodesDrawn);
334     }
335 #endif
336
337 }
338
339 void QSGDefaultRenderer::setSortFrontToBackEnabled(bool sort)
340 {
341     printf("setting sorting to... %d\n", sort);
342     m_sort_front_to_back = sort;
343 }
344
345 bool QSGDefaultRenderer::isSortFrontToBackEnabled() const
346 {
347     return m_sort_front_to_back;
348 }
349
350 void QSGDefaultRenderer::buildLists(QSGNode *node)
351 {
352     if (node->isSubtreeBlocked())
353         return;
354
355     if (node->type() == QSGNode::GeometryNodeType) {
356         QSGGeometryNode *geomNode = static_cast<QSGGeometryNode *>(node);
357         qreal opacity = geomNode->inheritedOpacity();
358         QSGMaterial *m = geomNode->activeMaterial();
359
360 #ifdef FORCE_NO_REORDER
361         if (true) {
362 #else
363         if ((m->flags() & QSGMaterial::Blending) || opacity < 1) {
364 #endif
365             geomNode->setRenderOrder(m_currentRenderOrder - 1);
366             m_transparentNodes.add(geomNode);
367         } else {
368             geomNode->setRenderOrder(m_currentRenderOrder);
369             m_opaqueNodes.add(geomNode);
370             m_currentRenderOrder += 2;
371         }
372     }
373
374     if (!node->firstChild())
375         return;
376
377 #ifdef FORCE_NO_REORDER
378     static bool reorder = false;
379 #else
380     static bool reorder = qApp->arguments().contains(QLatin1String("--reorder"));
381 #endif
382
383     if (reorder && node->firstChild() != node->lastChild() && (node->flags() & QSGNode::ChildrenDoNotOverlap)) {
384         QVarLengthArray<int, 16> beginIndices;
385         QVarLengthArray<int, 16> endIndices;
386         int baseCount = m_transparentNodes.size();
387         int count = 0;
388         for (QSGNode *c = node->firstChild(); c; c = c->nextSibling()) {
389             beginIndices.append(m_transparentNodes.size());
390             buildLists(c);
391             endIndices.append(m_transparentNodes.size());
392             ++count;
393         }
394
395         int childNodeCount = m_transparentNodes.size() - baseCount;
396         if (childNodeCount) {
397             m_tempNodes.reset();
398             m_tempNodes.reserve(childNodeCount);
399             while (childNodeCount) {
400                 for (int i = 0; i < count; ++i) {
401                     if (beginIndices[i] != endIndices[i])
402                         m_heap.insert(IndexGeometryNodePair(i, m_transparentNodes.at(beginIndices[i]++)));
403                 }
404                 while (!m_heap.isEmpty()) {
405                     IndexGeometryNodePair pair = m_heap.pop();
406                     m_tempNodes.add(pair.second);
407                     --childNodeCount;
408                     int i = pair.first;
409                     if (beginIndices[i] != endIndices[i] && !nodeLessThan(m_transparentNodes.at(beginIndices[i]), pair.second))
410                         m_heap.insert(IndexGeometryNodePair(i, m_transparentNodes.at(beginIndices[i]++)));
411                 }
412             }
413             Q_ASSERT(m_tempNodes.size() == m_transparentNodes.size() - baseCount);
414
415             qMemCopy(&m_transparentNodes.at(baseCount), &m_tempNodes.at(0), m_tempNodes.size() * sizeof(QSGGeometryNode *));
416         }
417     } else {
418         for (QSGNode *c = node->firstChild(); c; c = c->nextSibling())
419             buildLists(c);
420     }
421 }
422
423 void QSGDefaultRenderer::renderNodes(const QDataBuffer<QSGGeometryNode *> &list)
424 {
425     const float scale = 1.0f / m_currentRenderOrder;
426     int count = list.size();
427     int currentRenderOrder = 0x80000000;
428     m_current_projection_matrix.setColumn(2, scale * projectionMatrix().column(2));
429
430     //int clipChangeCount = 0;
431     //int programChangeCount = 0;
432     //int materialChangeCount = 0;
433
434     for (int i = 0; i < count; ++i) {
435         QSGGeometryNode *geomNode = list.at(i);
436
437         QSGMaterialShader::RenderState::DirtyStates updates;
438
439 #if defined (QML_RUNTIME_TESTING)
440         static bool dumpTree = qApp->arguments().contains(QLatin1String("--dump-tree"));
441         if (dumpTree)
442             qDebug() << geomNode;
443 #endif
444
445         bool changeMatrix = m_currentMatrix != geomNode->matrix();
446
447         if (changeMatrix) {
448             m_currentMatrix = geomNode->matrix();
449             if (m_currentMatrix)
450                 m_current_model_view_matrix = *m_currentMatrix;
451             else
452                 m_current_model_view_matrix.setToIdentity();
453             updates |= QSGMaterialShader::RenderState::DirtyMatrix;
454         }
455
456         bool changeOpacity = m_current_opacity != geomNode->inheritedOpacity();
457         if (changeOpacity) {
458             updates |= QSGMaterialShader::RenderState::DirtyOpacity;
459             m_current_opacity = geomNode->inheritedOpacity();
460         }
461
462         Q_ASSERT(geomNode->activeMaterial());
463
464         QSGMaterial *material = geomNode->activeMaterial();
465         QSGMaterialShader *program = m_context->prepareMaterial(material);
466         Q_ASSERT(program->program()->isLinked());
467
468         bool changeClip = geomNode->clipList() != m_currentClip;
469         QSGRenderer::ClipType clipType = QSGRenderer::NoClip;
470         if (changeClip) {
471             // The clip function relies on there not being any depth testing..
472             glDisable(GL_DEPTH_TEST);
473             clipType = updateStencilClip(geomNode->clipList());
474             glEnable(GL_DEPTH_TEST);
475             m_currentClip = geomNode->clipList();
476 #ifdef FORCE_NO_REORDER
477             glDepthMask(false);
478 #else
479             glDepthMask((material->flags() & QSGMaterial::Blending) == 0 && m_current_opacity == 1);
480 #endif
481             //++clipChangeCount;
482         }
483
484         bool changeProgram = (changeClip && clipType == QSGRenderer::StencilClip) || m_currentProgram != program;
485         if (changeProgram) {
486             if (m_currentProgram)
487                 m_currentProgram->deactivate();
488             m_currentProgram = program;
489             m_currentProgram->activate();
490             //++programChangeCount;
491             updates |= (QSGMaterialShader::RenderState::DirtyMatrix | QSGMaterialShader::RenderState::DirtyOpacity);
492
493 #ifdef RENDERER_DEBUG
494             materialChanges++;
495 #endif
496         }
497
498         bool changeRenderOrder = currentRenderOrder != geomNode->renderOrder();
499         if (changeRenderOrder) {
500             currentRenderOrder = geomNode->renderOrder();
501             m_current_projection_matrix.setColumn(3, projectionMatrix().column(3)
502                                                   + currentRenderOrder
503                                                   * m_current_projection_matrix.column(2));
504             updates |= QSGMaterialShader::RenderState::DirtyMatrix;
505         }
506
507         if (changeProgram || m_currentMaterial != material) {
508             program->updateState(state(updates), material, changeProgram ? 0 : m_currentMaterial);
509             m_currentMaterial = material;
510             //++materialChangeCount;
511         }
512
513         //glDepthRange((geomNode->renderOrder() + 0.1) * scale, (geomNode->renderOrder() + 0.9) * scale);
514
515         const QSGGeometry *g = geomNode->geometry();
516         draw(program, g);
517
518 #ifdef RENDERER_DEBUG
519         geometryNodesDrawn++;
520 #endif
521     }
522     //qDebug("Clip: %i, shader program: %i, material: %i times changed while drawing %s items",
523     //    clipChangeCount, programChangeCount, materialChangeCount,
524     //    &list == &m_transparentNodes ? "transparent" : "opaque");
525 }
526
527 QT_END_NAMESPACE