1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
43 #define GL_GLEXT_PROTOTYPES
45 #include "qsgdefaultrenderer_p.h"
46 #include "qsgmaterial.h"
48 #include <QtCore/qvarlengtharray.h>
49 #include <QtGui/qguiapplication.h>
50 #include <QtCore/qpair.h>
51 #include <QtCore/QElapsedTimer>
53 //#define FORCE_NO_REORDER
55 // #define RENDERER_DEBUG
57 #define DEBUG_THRESHOLD 0
58 QElapsedTimer debugTimer;
60 int geometryNodesDrawn;
65 static bool nodeLessThan(QSGGeometryNode *a, QSGGeometryNode *b)
68 if (a->clipList() != b->clipList())
69 return a->clipList() < b->clipList();
71 // Sort by material definition
72 QSGMaterialType *aDef = a->material()->type();
73 QSGMaterialType *bDef = b->material()->type();
78 // Sort by material state
79 int cmp = a->material()->compare(b->material());
83 return a->matrix() < b->matrix();
86 static bool nodeLessThanWithRenderOrder(QSGGeometryNode *a, QSGGeometryNode *b)
89 if (a->clipList() != b->clipList())
90 return a->clipList() < b->clipList();
92 // Sort by material definition
93 QSGMaterialType *aDef = a->material()->type();
94 QSGMaterialType *bDef = b->material()->type();
96 if (!(a->material()->flags() & QSGMaterial::Blending)) {
97 int aOrder = a->renderOrder();
98 int bOrder = b->renderOrder();
100 return aOrder > bOrder;
106 // Sort by material state
107 int cmp = a->material()->compare(b->material());
111 return a->matrix() < b->matrix();
115 QSGDefaultRenderer::IndexGeometryNodePair::IndexGeometryNodePair(int i, QSGGeometryNode *node)
116 : QPair<int, QSGGeometryNode *>(i, node)
120 bool QSGDefaultRenderer::IndexGeometryNodePair::operator < (const QSGDefaultRenderer::IndexGeometryNodePair &other) const
122 return nodeLessThan(second, other.second);
126 QSGDefaultRenderer::IndexGeometryNodePairHeap::IndexGeometryNodePairHeap()
131 void QSGDefaultRenderer::IndexGeometryNodePairHeap::insert(const QSGDefaultRenderer::IndexGeometryNodePair &x)
135 while (i != 0 && v.at(i) < v.at(parent(i))) {
136 qSwap(v.at(parent(i)), v.at(i));
141 QSGDefaultRenderer::IndexGeometryNodePair QSGDefaultRenderer::IndexGeometryNodePairHeap::pop()
143 IndexGeometryNodePair x = top();
145 qSwap(v.first(), v.last());
148 while (left(i) < v.size()) {
150 if (right(i) < v.size() && v.at(right(i)) < v.at(low))
152 if (!(v.at(low) < v.at(i)))
154 qSwap(v.at(i), v.at(low));
161 QSGDefaultRenderer::QSGDefaultRenderer(QSGContext *context)
162 : QSGRenderer(context)
164 , m_transparentNodes(64)
166 , m_rebuild_lists(false)
167 , m_needs_sorting(false)
168 , m_sort_front_to_back(false)
169 , m_currentRenderOrder(1)
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"));
178 void QSGDefaultRenderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state)
180 QSGRenderer::nodeChanged(node, state);
182 const quint32 rebuildBits = QSGNode::DirtyNodeAdded | QSGNode::DirtyNodeRemoved
183 | QSGNode::DirtyMaterial | QSGNode::DirtyOpacity
184 | QSGNode::DirtyForceUpdate | QSGNode::DirtyChildrenDoNotOverlap;
186 if (state & rebuildBits)
187 m_rebuild_lists = true;
189 if (state & (rebuildBits | QSGNode::DirtyClipList))
190 m_needs_sorting = true;
193 void QSGDefaultRenderer::render()
195 #if defined (QML_RUNTIME_TESTING)
196 static bool dumpTree = qApp->arguments().contains(QLatin1String("--dump-tree"));
199 QSGNodeDumper::dump(rootNode());
203 #ifdef RENDERER_DEBUG
204 debugTimer.invalidate();
206 geometryNodesDrawn = 0;
210 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
213 glFrontFace(isMirrored() ? GL_CW : GL_CCW);
214 glDisable(GL_CULL_FACE);
216 glEnable(GL_DEPTH_TEST);
218 glDepthFunc(GL_GREATER);
219 #if defined(QT_OPENGL_ES)
225 glDisable(GL_SCISSOR_TEST);
226 glClearColor(m_clear_color.redF(), m_clear_color.greenF(), m_clear_color.blueF(), m_clear_color.alphaF());
228 #ifdef RENDERER_DEBUG
229 int debugtimeSetup = debugTimer.elapsed();
232 bindable()->clear(clearMode());
234 #ifdef RENDERER_DEBUG
235 int debugtimeClear = debugTimer.elapsed();
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();
244 glDisable(GL_STENCIL_TEST);
246 m_currentMaterial = 0;
247 m_currentProgram = 0;
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;
258 #ifdef RENDERER_DEBUG
259 int debugtimeLists = debugTimer.elapsed();
263 if (m_needs_sorting) {
264 if (!m_opaqueNodes.isEmpty()) {
265 qSort(&m_opaqueNodes.first(), &m_opaqueNodes.first() + m_opaqueNodes.size(),
267 ? nodeLessThanWithRenderOrder
270 m_needs_sorting = false;
273 #ifdef RENDERER_DEBUG
274 int debugtimeSorting = debugTimer.elapsed();
277 m_renderOrderMatrix.setToIdentity();
278 m_renderOrderMatrix.scale(1, 1, qreal(1) / m_currentRenderOrder);
282 #ifdef QML_RUNTIME_TESTING
283 if (m_render_opaque_nodes)
286 #if defined (QML_RUNTIME_TESTING)
288 qDebug() << "Opaque Nodes:";
290 renderNodes(m_opaqueNodes);
293 #ifdef RENDERER_DEBUG
294 int debugtimeOpaque = debugTimer.elapsed();
295 int opaqueNodes = geometryNodesDrawn;
296 int opaqueMaterialChanges = materialChanges;
301 #ifdef QML_RUNTIME_TESTING
302 if (m_render_alpha_nodes)
305 #if defined (QML_RUNTIME_TESTING)
307 qDebug() << "Alpha Nodes:";
309 renderNodes(m_transparentNodes);
312 #ifdef RENDERER_DEBUG
313 int debugtimeAlpha = debugTimer.elapsed();
317 if (m_currentProgram)
318 m_currentProgram->deactivate();
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",
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);
339 void QSGDefaultRenderer::setSortFrontToBackEnabled(bool sort)
341 printf("setting sorting to... %d\n", sort);
342 m_sort_front_to_back = sort;
345 bool QSGDefaultRenderer::isSortFrontToBackEnabled() const
347 return m_sort_front_to_back;
350 void QSGDefaultRenderer::buildLists(QSGNode *node)
352 if (node->isSubtreeBlocked())
355 if (node->type() == QSGNode::GeometryNodeType) {
356 QSGGeometryNode *geomNode = static_cast<QSGGeometryNode *>(node);
357 qreal opacity = geomNode->inheritedOpacity();
358 QSGMaterial *m = geomNode->activeMaterial();
360 #ifdef FORCE_NO_REORDER
363 if ((m->flags() & QSGMaterial::Blending) || opacity < 1) {
365 geomNode->setRenderOrder(m_currentRenderOrder - 1);
366 m_transparentNodes.add(geomNode);
368 geomNode->setRenderOrder(m_currentRenderOrder);
369 m_opaqueNodes.add(geomNode);
370 m_currentRenderOrder += 2;
374 if (!node->firstChild())
377 #ifdef FORCE_NO_REORDER
378 static bool reorder = false;
380 static bool reorder = qApp->arguments().contains(QLatin1String("--reorder"));
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();
388 for (QSGNode *c = node->firstChild(); c; c = c->nextSibling()) {
389 beginIndices.append(m_transparentNodes.size());
391 endIndices.append(m_transparentNodes.size());
395 int childNodeCount = m_transparentNodes.size() - baseCount;
396 if (childNodeCount) {
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]++)));
404 while (!m_heap.isEmpty()) {
405 IndexGeometryNodePair pair = m_heap.pop();
406 m_tempNodes.add(pair.second);
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]++)));
413 Q_ASSERT(m_tempNodes.size() == m_transparentNodes.size() - baseCount);
415 qMemCopy(&m_transparentNodes.at(baseCount), &m_tempNodes.at(0), m_tempNodes.size() * sizeof(QSGGeometryNode *));
418 for (QSGNode *c = node->firstChild(); c; c = c->nextSibling())
423 void QSGDefaultRenderer::renderNodes(const QDataBuffer<QSGGeometryNode *> &list)
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));
430 //int clipChangeCount = 0;
431 //int programChangeCount = 0;
432 //int materialChangeCount = 0;
434 for (int i = 0; i < count; ++i) {
435 QSGGeometryNode *geomNode = list.at(i);
437 QSGMaterialShader::RenderState::DirtyStates updates;
439 #if defined (QML_RUNTIME_TESTING)
440 static bool dumpTree = qApp->arguments().contains(QLatin1String("--dump-tree"));
442 qDebug() << geomNode;
445 bool changeMatrix = m_currentMatrix != geomNode->matrix();
448 m_currentMatrix = geomNode->matrix();
450 m_current_model_view_matrix = *m_currentMatrix;
452 m_current_model_view_matrix.setToIdentity();
453 updates |= QSGMaterialShader::RenderState::DirtyMatrix;
456 bool changeOpacity = m_current_opacity != geomNode->inheritedOpacity();
458 updates |= QSGMaterialShader::RenderState::DirtyOpacity;
459 m_current_opacity = geomNode->inheritedOpacity();
462 Q_ASSERT(geomNode->activeMaterial());
464 QSGMaterial *material = geomNode->activeMaterial();
465 QSGMaterialShader *program = m_context->prepareMaterial(material);
466 Q_ASSERT(program->program()->isLinked());
468 bool changeClip = geomNode->clipList() != m_currentClip;
469 QSGRenderer::ClipType clipType = QSGRenderer::NoClip;
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
479 glDepthMask((material->flags() & QSGMaterial::Blending) == 0 && m_current_opacity == 1);
484 bool changeProgram = (changeClip && clipType == QSGRenderer::StencilClip) || m_currentProgram != program;
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);
493 #ifdef RENDERER_DEBUG
498 bool changeRenderOrder = currentRenderOrder != geomNode->renderOrder();
499 if (changeRenderOrder) {
500 currentRenderOrder = geomNode->renderOrder();
501 m_current_projection_matrix.setColumn(3, projectionMatrix().column(3)
503 * m_current_projection_matrix.column(2));
504 updates |= QSGMaterialShader::RenderState::DirtyMatrix;
507 if (changeProgram || m_currentMaterial != material) {
508 program->updateState(state(updates), material, changeProgram ? 0 : m_currentMaterial);
509 m_currentMaterial = material;
510 //++materialChangeCount;
513 //glDepthRange((geomNode->renderOrder() + 0.1) * scale, (geomNode->renderOrder() + 0.9) * scale);
515 const QSGGeometry *g = geomNode->geometry();
518 #ifdef RENDERER_DEBUG
519 geometryNodesDrawn++;
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");