1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
5** Copyright (C) 2016 Robin Burchell <robin.burchell@viroteck.net>
6** Contact: https://www.qt.io/licensing/
7**
8** This file is part of the QtQuick module of the Qt Toolkit.
9**
10** $QT_BEGIN_LICENSE:LGPL$
11** Commercial License Usage
12** Licensees holding valid commercial Qt licenses may use this file in
13** accordance with the commercial license agreement provided with the
14** Software or, alternatively, in accordance with the terms contained in
15** a written agreement between you and The Qt Company. For licensing terms
16** and conditions see https://www.qt.io/terms-conditions. For further
17** information use the contact form at https://www.qt.io/contact-us.
18**
19** GNU Lesser General Public License Usage
20** Alternatively, this file may be used under the terms of the GNU Lesser
21** General Public License version 3 as published by the Free Software
22** Foundation and appearing in the file LICENSE.LGPL3 included in the
23** packaging of this file. Please review the following information to
24** ensure the GNU Lesser General Public License version 3 requirements
25** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
26**
27** GNU General Public License Usage
28** Alternatively, this file may be used under the terms of the GNU
29** General Public License version 2.0 or (at your option) the GNU General
30** Public license version 3 or any later version approved by the KDE Free
31** Qt Foundation. The licenses are as published by the Free Software
32** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
33** included in the packaging of this file. Please review the following
34** information to ensure the GNU General Public License requirements will
35** be met: https://www.gnu.org/licenses/gpl-2.0.html and
36** https://www.gnu.org/licenses/gpl-3.0.html.
37**
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qsgbatchrenderer_p.h"
43#include <private/qsgshadersourcebuilder_p.h>
44
45#include <QQuickWindow>
46
47#include <qmath.h>
48
49#include <QtCore/QElapsedTimer>
50#include <QtCore/QtNumeric>
51
52#include <QtGui/QGuiApplication>
53#include <QtGui/QOpenGLFramebufferObject>
54#include <QtGui/QOpenGLVertexArrayObject>
55#include <QtGui/QOpenGLFunctions_1_0>
56#include <QtGui/QOpenGLFunctions_3_2_Core>
57
58#include <private/qnumeric_p.h>
59#include <private/qquickprofiler_p.h>
60#include "qsgmaterialshader_p.h"
61
62#include <algorithm>
63
64#ifndef GL_DOUBLE
65 #define GL_DOUBLE 0x140A
66#endif
67
68QT_BEGIN_NAMESPACE
69
70extern QByteArray qsgShaderRewriter_insertZAttributes(const char *input, QSurfaceFormat::OpenGLContextProfile profile);
71
72int qt_sg_envInt(const char *name, int defaultValue);
73
74namespace QSGBatchRenderer
75{
76
77#define DECLARE_DEBUG_VAR(variable) \
78 static bool debug_ ## variable() \
79 { static bool value = qgetenv("QSG_RENDERER_DEBUG").contains(QT_STRINGIFY(variable)); return value; }
80DECLARE_DEBUG_VAR(render)
81DECLARE_DEBUG_VAR(build)
82DECLARE_DEBUG_VAR(change)
83DECLARE_DEBUG_VAR(upload)
84DECLARE_DEBUG_VAR(roots)
85DECLARE_DEBUG_VAR(dump)
86DECLARE_DEBUG_VAR(noalpha)
87DECLARE_DEBUG_VAR(noopaque)
88DECLARE_DEBUG_VAR(noclip)
89#undef DECLARE_DEBUG_VAR
90
91static QElapsedTimer qsg_renderer_timer;
92
93#define QSGNODE_TRAVERSE(NODE) for (QSGNode *child = NODE->firstChild(); child; child = child->nextSibling())
94#define SHADOWNODE_TRAVERSE(NODE) for (Node *child = NODE->firstChild(); child; child = child->sibling())
95
96static inline int size_of_type(GLenum type)
97{
98 static int sizes[] = {
99 sizeof(char),
100 sizeof(unsigned char),
101 sizeof(short),
102 sizeof(unsigned short),
103 sizeof(int),
104 sizeof(unsigned int),
105 sizeof(float),
106 2,
107 3,
108 4,
109 sizeof(double)
110 };
111 Q_ASSERT(type >= GL_BYTE && type <= 0x140A); // the value of GL_DOUBLE
112 return sizes[type - GL_BYTE];
113}
114
115bool qsg_sort_element_increasing_order(Element *a, Element *b) { return a->order < b->order; }
116bool qsg_sort_element_decreasing_order(Element *a, Element *b) { return a->order > b->order; }
117bool qsg_sort_batch_is_valid(Batch *a, Batch *b) { return a->first && !b->first; }
118bool qsg_sort_batch_increasing_order(Batch *a, Batch *b) { return a->first->order < b->first->order; }
119bool qsg_sort_batch_decreasing_order(Batch *a, Batch *b) { return a->first->order > b->first->order; }
120
121QSGMaterial::Flag QSGMaterial_FullMatrix = (QSGMaterial::Flag) (QSGMaterial::RequiresFullMatrix & ~QSGMaterial::RequiresFullMatrixExceptTranslate);
122
123struct QMatrix4x4_Accessor
124{
125 float m[4][4];
126 int flagBits;
127
128 static bool isTranslate(const QMatrix4x4 &m) { return ((const QMatrix4x4_Accessor &) m).flagBits <= 0x1; }
129 static bool isScale(const QMatrix4x4 &m) { return ((const QMatrix4x4_Accessor &) m).flagBits <= 0x2; }
130 static bool is2DSafe(const QMatrix4x4 &m) { return ((const QMatrix4x4_Accessor &) m).flagBits < 0x8; }
131};
132
133const float OPAQUE_LIMIT = 0.999f;
134
135ShaderManager::Shader *ShaderManager::prepareMaterial(QSGMaterial *material)
136{
137 QSGMaterialType *type = material->type();
138 Shader *shader = rewrittenShaders.value(type, 0);
139 if (shader)
140 return shader;
141
142 if (QSG_LOG_TIME_COMPILATION().isDebugEnabled())
143 qsg_renderer_timer.start();
144 Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphContextFrame);
145
146 QSGMaterialShader *s = material->createShader();
147 QOpenGLContext *ctx = QOpenGLContext::currentContext();
148 QSurfaceFormat::OpenGLContextProfile profile = ctx->format().profile();
149
150 QOpenGLShaderProgram *p = s->program();
151 char const *const *attr = s->attributeNames();
152 int i;
153 for (i = 0; attr[i]; ++i) {
154 if (*attr[i])
155 p->bindAttributeLocation(attr[i], i);
156 }
157 p->bindAttributeLocation("_qt_order", i);
158 context->compileShader(s, material, qsgShaderRewriter_insertZAttributes(s->vertexShader(), profile), 0);
159 context->initializeShader(s);
160 if (!p->isLinked())
161 return 0;
162
163 shader = new Shader;
164 shader->program = s;
165 shader->pos_order = i;
166 shader->id_zRange = p->uniformLocation("_qt_zRange");
167 shader->lastOpacity = 0;
168
169 Q_ASSERT(shader->pos_order >= 0);
170 Q_ASSERT(shader->id_zRange >= 0);
171
172 qCDebug(QSG_LOG_TIME_COMPILATION, "shader compiled in %dms", (int) qsg_renderer_timer.elapsed());
173
174 Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphContextFrame,
175 QQuickProfiler::SceneGraphContextMaterialCompile);
176
177 rewrittenShaders[type] = shader;
178 return shader;
179}
180
181ShaderManager::Shader *ShaderManager::prepareMaterialNoRewrite(QSGMaterial *material)
182{
183 QSGMaterialType *type = material->type();
184 Shader *shader = stockShaders.value(type, 0);
185 if (shader)
186 return shader;
187
188 if (QSG_LOG_TIME_COMPILATION().isDebugEnabled())
189 qsg_renderer_timer.start();
190 Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphContextFrame);
191
192 QSGMaterialShader *s = static_cast<QSGMaterialShader *>(material->createShader());
193 context->compileShader(s, material);
194 context->initializeShader(s);
195
196 shader = new Shader();
197 shader->program = s;
198 shader->id_zRange = -1;
199 shader->pos_order = -1;
200 shader->lastOpacity = 0;
201
202 stockShaders[type] = shader;
203
204 qCDebug(QSG_LOG_TIME_COMPILATION, "shader compiled in %dms (no rewrite)", (int) qsg_renderer_timer.elapsed());
205
206 Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphContextFrame,
207 QQuickProfiler::SceneGraphContextMaterialCompile);
208 return shader;
209}
210
211void ShaderManager::invalidated()
212{
213 qDeleteAll(stockShaders);
214 stockShaders.clear();
215 qDeleteAll(rewrittenShaders);
216 rewrittenShaders.clear();
217 delete blitProgram;
218 blitProgram = 0;
219}
220
221void qsg_dumpShadowRoots(BatchRootInfo *i, int indent)
222{
223 static int extraIndent = 0;
224 ++extraIndent;
225
226 QByteArray ind(indent + extraIndent + 10, ' ');
227
228 if (!i) {
229 qDebug() << ind.constData() << "- no info";
230 } else {
231 qDebug() << ind.constData() << "- parent:" << i->parentRoot << "orders" << i->firstOrder << "->" << i->lastOrder << ", avail:" << i->availableOrders;
232 for (QSet<Node *>::const_iterator it = i->subRoots.constBegin();
233 it != i->subRoots.constEnd(); ++it) {
234 qDebug() << ind.constData() << "-" << *it;
235 qsg_dumpShadowRoots((*it)->rootInfo(), indent);
236 }
237 }
238
239 --extraIndent;
240}
241
242void qsg_dumpShadowRoots(Node *n)
243{
244#ifndef QT_NO_DEBUG_OUTPUT
245 static int indent = 0;
246 ++indent;
247
248 QByteArray ind(indent, ' ');
249
250 if (n->type() == QSGNode::ClipNodeType || n->isBatchRoot) {
251 qDebug() << ind.constData() << "[X]" << n->sgNode << hex << uint(n->sgNode->flags());
252 qsg_dumpShadowRoots(n->rootInfo(), indent);
253 } else {
254 QDebug d = qDebug();
255 d << ind.constData() << "[ ]" << n->sgNode << hex << uint(n->sgNode->flags());
256 if (n->type() == QSGNode::GeometryNodeType)
257 d << "order" << dec << n->element()->order;
258 }
259
260 SHADOWNODE_TRAVERSE(n)
261 qsg_dumpShadowRoots(child);
262
263 --indent;
264#else
265 Q_UNUSED(n)
266#endif
267}
268
269Updater::Updater(Renderer *r)
270 : renderer(r)
271 , m_roots(32)
272 , m_rootMatrices(8)
273{
274 m_roots.add(0);
275 m_combined_matrix_stack.add(&m_identityMatrix);
276 m_rootMatrices.add(m_identityMatrix);
277
278 Q_ASSERT(sizeof(QMatrix4x4_Accessor) == sizeof(QMatrix4x4));
279}
280
281void Updater::updateStates(QSGNode *n)
282{
283 m_current_clip = 0;
284
285 m_added = 0;
286 m_transformChange = 0;
287 m_opacityChange = 0;
288
289 Node *sn = renderer->m_nodes.value(n, 0);
290 Q_ASSERT(sn);
291
292 if (Q_UNLIKELY(debug_roots()))
293 qsg_dumpShadowRoots(sn);
294
295 if (Q_UNLIKELY(debug_build())) {
296 qDebug() << "Updater::updateStates()";
297 if (sn->dirtyState & (QSGNode::DirtyNodeAdded << 16))
298 qDebug() << " - nodes have been added";
299 if (sn->dirtyState & (QSGNode::DirtyMatrix << 16))
300 qDebug() << " - transforms have changed";
301 if (sn->dirtyState & (QSGNode::DirtyOpacity << 16))
302 qDebug() << " - opacity has changed";
303 if (uint(sn->dirtyState) & uint(QSGNode::DirtyForceUpdate << 16))
304 qDebug() << " - forceupdate";
305 }
306
307 if (Q_UNLIKELY(renderer->m_visualizeMode == Renderer::VisualizeChanges))
308 renderer->visualizeChangesPrepare(sn);
309
310 visitNode(sn);
311}
312
313void Updater::visitNode(Node *n)
314{
315 if (m_added == 0 && n->dirtyState == 0 && m_force_update == 0 && m_transformChange == 0 && m_opacityChange == 0)
316 return;
317
318 int count = m_added;
319 if (n->dirtyState & QSGNode::DirtyNodeAdded)
320 ++m_added;
321
322 int force = m_force_update;
323 if (n->dirtyState & QSGNode::DirtyForceUpdate)
324 ++m_force_update;
325
326 switch (n->type()) {
327 case QSGNode::OpacityNodeType:
328 visitOpacityNode(n);
329 break;
330 case QSGNode::TransformNodeType:
331 visitTransformNode(n);
332 break;
333 case QSGNode::GeometryNodeType:
334 visitGeometryNode(n);
335 break;
336 case QSGNode::ClipNodeType:
337 visitClipNode(n);
338 break;
339 case QSGNode::RenderNodeType:
340 if (m_added)
341 n->renderNodeElement()->root = m_roots.last();
342 Q_FALLTHROUGH(); // to visit children
343 default:
344 SHADOWNODE_TRAVERSE(n) visitNode(child);
345 break;
346 }
347
348 m_added = count;
349 m_force_update = force;
350 n->dirtyState = 0;
351}
352
353void Updater::visitClipNode(Node *n)
354{
355 ClipBatchRootInfo *extra = n->clipInfo();
356
357 QSGClipNode *cn = static_cast<QSGClipNode *>(n->sgNode);
358
359 if (m_roots.last() && m_added > 0)
360 renderer->registerBatchRoot(n, m_roots.last());
361
362 cn->setRendererClipList(m_current_clip);
363 m_current_clip = cn;
364 m_roots << n;
365 m_rootMatrices.add(m_rootMatrices.last() * *m_combined_matrix_stack.last());
366 extra->matrix = m_rootMatrices.last();
367 cn->setRendererMatrix(&extra->matrix);
368 m_combined_matrix_stack << &m_identityMatrix;
369
370 SHADOWNODE_TRAVERSE(n) visitNode(child);
371
372 m_current_clip = cn->clipList();
373 m_rootMatrices.pop_back();
374 m_combined_matrix_stack.pop_back();
375 m_roots.pop_back();
376}
377
378void Updater::visitOpacityNode(Node *n)
379{
380 QSGOpacityNode *on = static_cast<QSGOpacityNode *>(n->sgNode);
381
382 qreal combined = m_opacity_stack.last() * on->opacity();
383 on->setCombinedOpacity(combined);
384 m_opacity_stack.add(combined);
385
386 if (m_added == 0 && n->dirtyState & QSGNode::DirtyOpacity) {
387 bool was = n->isOpaque;
388 bool is = on->opacity() > OPAQUE_LIMIT;
389 if (was != is) {
390 renderer->m_rebuild = Renderer::FullRebuild;
391 n->isOpaque = is;
392 }
393 ++m_opacityChange;
394 SHADOWNODE_TRAVERSE(n) visitNode(child);
395 --m_opacityChange;
396 } else {
397 if (m_added > 0)
398 n->isOpaque = on->opacity() > OPAQUE_LIMIT;
399 SHADOWNODE_TRAVERSE(n) visitNode(child);
400 }
401
402 m_opacity_stack.pop_back();
403}
404
405void Updater::visitTransformNode(Node *n)
406{
407 bool popMatrixStack = false;
408 bool popRootStack = false;
409 bool dirty = n->dirtyState & QSGNode::DirtyMatrix;
410
411 QSGTransformNode *tn = static_cast<QSGTransformNode *>(n->sgNode);
412
413 if (n->isBatchRoot) {
414 if (m_added > 0 && m_roots.last())
415 renderer->registerBatchRoot(n, m_roots.last());
416 tn->setCombinedMatrix(m_rootMatrices.last() * *m_combined_matrix_stack.last() * tn->matrix());
417
418 // The only change in this subtree is ourselves and we are a batch root, so
419 // only update subroots and return, saving tons of child-processing (flickable-panning)
420
421 if (!n->becameBatchRoot && m_added == 0 && m_force_update == 0 && m_opacityChange == 0 && dirty && (n->dirtyState & ~QSGNode::DirtyMatrix) == 0) {
422 BatchRootInfo *info = renderer->batchRootInfo(n);
423 for (QSet<Node *>::const_iterator it = info->subRoots.constBegin();
424 it != info->subRoots.constEnd(); ++it) {
425 updateRootTransforms(*it, n, tn->combinedMatrix());
426 }
427 return;
428 }
429
430 n->becameBatchRoot = false;
431
432 m_combined_matrix_stack.add(&m_identityMatrix);
433 m_roots.add(n);
434 m_rootMatrices.add(tn->combinedMatrix());
435
436 popMatrixStack = true;
437 popRootStack = true;
438 } else if (!tn->matrix().isIdentity()) {
439 tn->setCombinedMatrix(*m_combined_matrix_stack.last() * tn->matrix());
440 m_combined_matrix_stack.add(&tn->combinedMatrix());
441 popMatrixStack = true;
442 } else {
443 tn->setCombinedMatrix(*m_combined_matrix_stack.last());
444 }
445
446 if (dirty)
447 ++m_transformChange;
448
449 SHADOWNODE_TRAVERSE(n) visitNode(child);
450
451 if (dirty)
452 --m_transformChange;
453 if (popMatrixStack)
454 m_combined_matrix_stack.pop_back();
455 if (popRootStack) {
456 m_roots.pop_back();
457 m_rootMatrices.pop_back();
458 }
459}
460
461void Updater::visitGeometryNode(Node *n)
462{
463 QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(n->sgNode);
464
465 gn->setRendererMatrix(m_combined_matrix_stack.last());
466 gn->setRendererClipList(m_current_clip);
467 gn->setInheritedOpacity(m_opacity_stack.last());
468
469 if (m_added) {
470 Element *e = n->element();
471 e->root = m_roots.last();
472 e->translateOnlyToRoot = QMatrix4x4_Accessor::isTranslate(*gn->matrix());
473
474 if (e->root) {
475 BatchRootInfo *info = renderer->batchRootInfo(e->root);
476 while (info != 0) {
477 info->availableOrders--;
478 if (info->availableOrders < 0) {
479 renderer->m_rebuild |= Renderer::BuildRenderLists;
480 } else {
481 renderer->m_rebuild |= Renderer::BuildRenderListsForTaggedRoots;
482 renderer->m_taggedRoots << e->root;
483 }
484 if (info->parentRoot != 0)
485 info = renderer->batchRootInfo(info->parentRoot);
486 else
487 info = 0;
488 }
489 } else {
490 renderer->m_rebuild |= Renderer::FullRebuild;
491 }
492 } else {
493 if (m_transformChange) {
494 Element *e = n->element();
495 e->translateOnlyToRoot = QMatrix4x4_Accessor::isTranslate(*gn->matrix());
496 }
497 if (m_opacityChange) {
498 Element *e = n->element();
499 if (e->batch)
500 renderer->invalidateBatchAndOverlappingRenderOrders(e->batch);
501 }
502 }
503
504 SHADOWNODE_TRAVERSE(n) visitNode(child);
505}
506
507void Updater::updateRootTransforms(Node *node, Node *root, const QMatrix4x4 &combined)
508{
509 BatchRootInfo *info = renderer->batchRootInfo(node);
510 QMatrix4x4 m;
511 Node *n = node;
512
513 while (n != root) {
514 if (n->type() == QSGNode::TransformNodeType)
515 m = static_cast<QSGTransformNode *>(n->sgNode)->matrix() * m;
516 n = n->parent();
517 }
518
519 m = combined * m;
520
521 if (node->type() == QSGNode::ClipNodeType) {
522 static_cast<ClipBatchRootInfo *>(info)->matrix = m;
523 } else {
524 Q_ASSERT(node->type() == QSGNode::TransformNodeType);
525 static_cast<QSGTransformNode *>(node->sgNode)->setCombinedMatrix(m);
526 }
527
528 for (QSet<Node *>::const_iterator it = info->subRoots.constBegin();
529 it != info->subRoots.constEnd(); ++it) {
530 updateRootTransforms(*it, node, m);
531 }
532}
533
534int qsg_positionAttribute(QSGGeometry *g) {
535 int vaOffset = 0;
536 for (int a=0; a<g->attributeCount(); ++a) {
537 const QSGGeometry::Attribute &attr = g->attributes()[a];
538 if (attr.isVertexCoordinate && attr.tupleSize == 2 && attr.type == GL_FLOAT) {
539 return vaOffset;
540 }
541 vaOffset += attr.tupleSize * size_of_type(attr.type);
542 }
543 return -1;
544}
545
546
547void Rect::map(const QMatrix4x4 &matrix)
548{
549 const float *m = matrix.constData();
550 if (QMatrix4x4_Accessor::isScale(matrix)) {
551 tl.x = tl.x * m[0] + m[12];
552 tl.y = tl.y * m[5] + m[13];
553 br.x = br.x * m[0] + m[12];
554 br.y = br.y * m[5] + m[13];
555 if (tl.x > br.x)
556 qSwap(tl.x, br.x);
557 if (tl.y > br.y)
558 qSwap(tl.y, br.y);
559 } else {
560 Pt mtl = tl;
561 Pt mtr = { br.x, tl.y };
562 Pt mbl = { tl.x, br.y };
563 Pt mbr = br;
564
565 mtl.map(matrix);
566 mtr.map(matrix);
567 mbl.map(matrix);
568 mbr.map(matrix);
569
570 set(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
571 (*this) |= mtl;
572 (*this) |= mtr;
573 (*this) |= mbl;
574 (*this) |= mbr;
575 }
576}
577
578void Element::computeBounds()
579{
580 Q_ASSERT(!boundsComputed);
581 boundsComputed = true;
582
583 QSGGeometry *g = node->geometry();
584 int offset = qsg_positionAttribute(g);
585 if (offset == -1) {
586 // No position attribute means overlaps with everything..
587 bounds.set(-FLT_MAX, -FLT_MAX, FLT_MAX, FLT_MAX);
588 return;
589 }
590
591 bounds.set(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
592 char *vd = (char *) g->vertexData() + offset;
593 for (int i=0; i<g->vertexCount(); ++i) {
594 bounds |= *(Pt *) vd;
595 vd += g->sizeOfVertex();
596 }
597 bounds.map(*node->matrix());
598
599 if (!qt_is_finite(bounds.tl.x) || bounds.tl.x == FLT_MAX)
600 bounds.tl.x = -FLT_MAX;
601 if (!qt_is_finite(bounds.tl.y) || bounds.tl.y == FLT_MAX)
602 bounds.tl.y = -FLT_MAX;
603 if (!qt_is_finite(bounds.br.x) || bounds.br.x == -FLT_MAX)
604 bounds.br.x = FLT_MAX;
605 if (!qt_is_finite(bounds.br.y) || bounds.br.y == -FLT_MAX)
606 bounds.br.y = FLT_MAX;
607
608 Q_ASSERT(bounds.tl.x <= bounds.br.x);
609 Q_ASSERT(bounds.tl.y <= bounds.br.y);
610
611 boundsOutsideFloatRange = bounds.isOutsideFloatRange();
612}
613
614BatchCompatibility Batch::isMaterialCompatible(Element *e) const
615{
616 Element *n = first;
617 // Skip to the first node other than e which has not been removed
618 while (n && (n == e || n->removed))
619 n = n->nextInBatch;
620
621 // Only 'e' in this batch, so a material change doesn't change anything as long as
622 // its blending is still in sync with this batch...
623 if (!n)
624 return BatchIsCompatible;
625
626 QSGMaterial *m = e->node->activeMaterial();
627 QSGMaterial *nm = n->node->activeMaterial();
628 return (nm->type() == m->type() && nm->compare(m) == 0)
629 ? BatchIsCompatible
630 : BatchBreaksOnCompare;
631}
632
633/*
634 * Marks this batch as dirty or in the case where the geometry node has
635 * changed to be incompatible with this batch, return false so that
636 * the caller can mark the entire sg for a full rebuild...
637 */
638bool Batch::geometryWasChanged(QSGGeometryNode *gn)
639{
640 Element *e = first;
641 Q_ASSERT_X(e, "Batch::geometryWasChanged", "Batch is expected to 'valid' at this time");
642 // 'gn' is the first node in the batch, compare against the next one.
643 while (e && (e->node == gn || e->removed))
644 e = e->nextInBatch;
645 if (!e || e->node->geometry()->attributes() == gn->geometry()->attributes()) {
646 needsUpload = true;
647 return true;
648 } else {
649 return false;
650 }
651}
652
653void Batch::cleanupRemovedElements()
654{
655 // remove from front of batch..
656 while (first && first->removed) {
657 first = first->nextInBatch;
658 }
659
660 // Then continue and remove other nodes further out in the batch..
661 if (first) {
662 Element *e = first;
663 while (e->nextInBatch) {
664 if (e->nextInBatch->removed)
665 e->nextInBatch = e->nextInBatch->nextInBatch;
666 else
667 e = e->nextInBatch;
668
669 }
670 }
671}
672
673/*
674 * Iterates through all geometry nodes in this batch and unsets their batch,
675 * thus forcing them to be rebuilt
676 */
677void Batch::invalidate()
678{
679 // If doing removal here is a performance issue, we might add a "hasRemoved" bit to
680 // the batch to do an early out..
681 cleanupRemovedElements();
682 Element *e = first;
683 first = 0;
684 root = 0;
685 while (e) {
686 e->batch = 0;
687 Element *n = e->nextInBatch;
688 e->nextInBatch = 0;
689 e = n;
690 }
691}
692
693bool Batch::isTranslateOnlyToRoot() const {
694 bool only = true;
695 Element *e = first;
696 while (e && only) {
697 only &= e->translateOnlyToRoot;
698 e = e->nextInBatch;
699 }
700 return only;
701}
702
703/*
704 * Iterates through all the nodes in the batch and returns true if the
705 * nodes are all safe to batch. There are two separate criteria:
706 *
707 * - The matrix is such that the z component of the result is of no
708 * consequence.
709 *
710 * - The bounds are inside the stable floating point range. This applies
711 * to desktop only where we in this case can trigger a fallback to
712 * unmerged in which case we pass the geometry straight through and
713 * just apply the matrix.
714 *
715 * NOTE: This also means a slight performance impact for geometries which
716 * are defined to be outside the stable floating point range and still
717 * use single precision float, but given that this implicitly fixes
718 * huge lists and tables, it is worth it.
719 */
720bool Batch::isSafeToBatch() const {
721 Element *e = first;
722 while (e) {
723 if (e->boundsOutsideFloatRange)
724 return false;
725 if (!QMatrix4x4_Accessor::is2DSafe(*e->node->matrix()))
726 return false;
727 e = e->nextInBatch;
728 }
729 return true;
730}
731
732static int qsg_countNodesInBatch(const Batch *batch)
733{
734 int sum = 0;
735 Element *e = batch->first;
736 while (e) {
737 ++sum;
738 e = e->nextInBatch;
739 }
740 return sum;
741}
742
743static int qsg_countNodesInBatches(const QDataBuffer<Batch *> &batches)
744{
745 int sum = 0;
746 for (int i=0; i<batches.size(); ++i) {
747 sum += qsg_countNodesInBatch(batches.at(i));
748 }
749 return sum;
750}
751
752Renderer::Renderer(QSGDefaultRenderContext *ctx)
753 : QSGRenderer(ctx)
754 , m_context(ctx)
755 , m_opaqueRenderList(64)
756 , m_alphaRenderList(64)
757 , m_nextRenderOrder(0)
758 , m_partialRebuild(false)
759 , m_partialRebuildRoot(0)
760 , m_useDepthBuffer(true)
761 , m_opaqueBatches(16)
762 , m_alphaBatches(16)
763 , m_batchPool(16)
764 , m_elementsToDelete(64)
765 , m_tmpAlphaElements(16)
766 , m_tmpOpaqueElements(16)
767 , m_rebuild(FullRebuild)
768 , m_zRange(0)
769 , m_renderOrderRebuildLower(-1)
770 , m_renderOrderRebuildUpper(-1)
771 , m_currentMaterial(0)
772 , m_currentShader(0)
773 , m_currentStencilValue(0)
774 , m_clipMatrixId(0)
775 , m_currentClip(0)
776 , m_currentClipType(NoClip)
777 , m_vertexUploadPool(256)
778#ifdef QSG_SEPARATE_INDEX_BUFFER
779 , m_indexUploadPool(64)
780#endif
781 , m_vao(0)
782 , m_visualizeMode(VisualizeNothing)
783{
784 initializeOpenGLFunctions();
785 setNodeUpdater(new Updater(this));
786
787 m_shaderManager = ctx->findChild<ShaderManager *>(QStringLiteral("__qt_ShaderManager"), Qt::FindDirectChildrenOnly);
788 if (!m_shaderManager) {
789 m_shaderManager = new ShaderManager(ctx);
790 m_shaderManager->setObjectName(QStringLiteral("__qt_ShaderManager"));
791 m_shaderManager->setParent(ctx);
792 QObject::connect(ctx, SIGNAL(invalidated()), m_shaderManager, SLOT(invalidated()), Qt::DirectConnection);
793 }
794
795 m_bufferStrategy = GL_STATIC_DRAW;
796 if (Q_UNLIKELY(qEnvironmentVariableIsSet("QSG_RENDERER_BUFFER_STRATEGY"))) {
797 const QByteArray strategy = qgetenv("QSG_RENDERER_BUFFER_STRATEGY");
798 if (strategy == "dynamic")
799 m_bufferStrategy = GL_DYNAMIC_DRAW;
800 else if (strategy == "stream")
801 m_bufferStrategy = GL_STREAM_DRAW;
802 }
803
804 m_batchNodeThreshold = qt_sg_envInt("QSG_RENDERER_BATCH_NODE_THRESHOLD", 64);
805 m_batchVertexThreshold = qt_sg_envInt("QSG_RENDERER_BATCH_VERTEX_THRESHOLD", 1024);
806
807 if (Q_UNLIKELY(debug_build() || debug_render())) {
808 qDebug() << "Batch thresholds: nodes:" << m_batchNodeThreshold << " vertices:" << m_batchVertexThreshold;
809 qDebug() << "Using buffer strategy:" << (m_bufferStrategy == GL_STATIC_DRAW ? "static" : (m_bufferStrategy == GL_DYNAMIC_DRAW ? "dynamic" : "stream"));
810 }
811
812 // If rendering with an OpenGL Core profile context, we need to create a VAO
813 // to hold our vertex specification state.
814 if (m_context->openglContext()->format().profile() == QSurfaceFormat::CoreProfile) {
815 m_vao = new QOpenGLVertexArrayObject(this);
816 m_vao->create();
817 }
818
819 bool useDepth = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER");
820 m_useDepthBuffer = useDepth && ctx->openglContext()->format().depthBufferSize() > 0;
821}
822
823static void qsg_wipeBuffer(Buffer *buffer, QOpenGLFunctions *funcs)
824{
825 funcs->glDeleteBuffers(1, &buffer->id);
826 // The free here is ok because we're in one of two situations.
827 // 1. We're using the upload pool in which case unmap will have set the
828 // data pointer to 0 and calling free on 0 is ok.
829 // 2. We're using dedicated buffers because of visualization or IBO workaround
830 // and the data something we malloced and must be freed.
831 free(buffer->data);
832}
833
834static void qsg_wipeBatch(Batch *batch, QOpenGLFunctions *funcs)
835{
836 qsg_wipeBuffer(&batch->vbo, funcs);
837#ifdef QSG_SEPARATE_INDEX_BUFFER
838 qsg_wipeBuffer(&batch->ibo, funcs);
839#endif
840 delete batch;
841}
842
843Renderer::~Renderer()
844{
845 if (QOpenGLContext::currentContext()) {
846 // Clean up batches and buffers
847 for (int i=0; i<m_opaqueBatches.size(); ++i) qsg_wipeBatch(m_opaqueBatches.at(i), this);
848 for (int i=0; i<m_alphaBatches.size(); ++i) qsg_wipeBatch(m_alphaBatches.at(i), this);
849 for (int i=0; i<m_batchPool.size(); ++i) qsg_wipeBatch(m_batchPool.at(i), this);
850 }
851
852 for (Node *n : qAsConst(m_nodes))
853 m_nodeAllocator.release(n);
854
855 // Remaining elements...
856 for (int i=0; i<m_elementsToDelete.size(); ++i) {
857 Element *e = m_elementsToDelete.at(i);
858 if (e->isRenderNode)
859 delete static_cast<RenderNodeElement *>(e);
860 else
861 m_elementAllocator.release(e);
862 }
863}
864
865void Renderer::invalidateAndRecycleBatch(Batch *b)
866{
867 b->invalidate();
868 for (int i=0; i<m_batchPool.size(); ++i)
869 if (b == m_batchPool.at(i))
870 return;
871 m_batchPool.add(b);
872}
873
874/* The code here does a CPU-side allocation which might seem like a performance issue
875 * compared to using glMapBuffer or glMapBufferRange which would give me back
876 * potentially GPU allocated memory and saving me one deep-copy, but...
877 *
878 * Because we do a lot of CPU-side transformations, we need random-access memory
879 * and the memory returned from glMapBuffer/glMapBufferRange is typically
880 * uncached and thus very slow for our purposes.
881 *
882 * ref: http://www.opengl.org/wiki/Buffer_Object
883 */
884void Renderer::map(Buffer *buffer, int byteSize, bool isIndexBuf)
885{
886 if (!m_context->hasBrokenIndexBufferObjects() && m_visualizeMode == VisualizeNothing) {
887 // Common case, use a shared memory pool for uploading vertex data to avoid
888 // excessive reevaluation
889 QDataBuffer<char> &pool =
890#ifdef QSG_SEPARATE_INDEX_BUFFER
891 isIndexBuf ? m_indexUploadPool : m_vertexUploadPool;
892#else
893 m_vertexUploadPool;
894 Q_UNUSED(isIndexBuf);
895#endif
896 if (byteSize > pool.size())
897 pool.resize(byteSize);
898 buffer->data = pool.data();
899 } else if (buffer->size != byteSize) {
900 free(buffer->data);
901 buffer->data = (char *) malloc(byteSize);
902 Q_CHECK_PTR(buffer->data);
903 }
904 buffer->size = byteSize;
905}
906
907void Renderer::unmap(Buffer *buffer, bool isIndexBuf)
908{
909 if (buffer->id == 0)
910 glGenBuffers(1, &buffer->id);
911 GLenum target = isIndexBuf ? GL_ELEMENT_ARRAY_BUFFER : GL_ARRAY_BUFFER;
912 glBindBuffer(target, buffer->id);
913 glBufferData(target, buffer->size, buffer->data, m_bufferStrategy);
914
915 if (!m_context->hasBrokenIndexBufferObjects() && m_visualizeMode == VisualizeNothing) {
916 buffer->data = 0;
917 }
918}
919
920BatchRootInfo *Renderer::batchRootInfo(Node *node)
921{
922 BatchRootInfo *info = node->rootInfo();
923 if (!info) {
924 if (node->type() == QSGNode::ClipNodeType)
925 info = new ClipBatchRootInfo;
926 else {
927 Q_ASSERT(node->type() == QSGNode::TransformNodeType);
928 info = new BatchRootInfo;
929 }
930 node->data = info;
931 }
932 return info;
933}
934
935void Renderer::removeBatchRootFromParent(Node *childRoot)
936{
937 BatchRootInfo *childInfo = batchRootInfo(childRoot);
938 if (!childInfo->parentRoot)
939 return;
940 BatchRootInfo *parentInfo = batchRootInfo(childInfo->parentRoot);
941
942 Q_ASSERT(parentInfo->subRoots.contains(childRoot));
943 parentInfo->subRoots.remove(childRoot);
944 childInfo->parentRoot = 0;
945}
946
947void Renderer::registerBatchRoot(Node *subRoot, Node *parentRoot)
948{
949 BatchRootInfo *subInfo = batchRootInfo(subRoot);
950 BatchRootInfo *parentInfo = batchRootInfo(parentRoot);
951 subInfo->parentRoot = parentRoot;
952 parentInfo->subRoots << subRoot;
953}
954
955bool Renderer::changeBatchRoot(Node *node, Node *root)
956{
957 BatchRootInfo *subInfo = batchRootInfo(node);
958 if (subInfo->parentRoot == root)
959 return false;
960 if (subInfo->parentRoot) {
961 BatchRootInfo *oldRootInfo = batchRootInfo(subInfo->parentRoot);
962 oldRootInfo->subRoots.remove(node);
963 }
964 BatchRootInfo *newRootInfo = batchRootInfo(root);
965 newRootInfo->subRoots << node;
966 subInfo->parentRoot = root;
967 return true;
968}
969
970void Renderer::nodeChangedBatchRoot(Node *node, Node *root)
971{
972 if (node->type() == QSGNode::ClipNodeType || node->isBatchRoot) {
973 // When we reach a batchroot, we only need to update it. Its subtree
974 // is relative to that root, so no need to recurse further.
975 changeBatchRoot(node, root);
976 return;
977 } else if (node->type() == QSGNode::GeometryNodeType) {
978 // Only need to change the root as nodeChanged anyway flags a full update.
979 Element *e = node->element();
980 if (e) {
981 e->root = root;
982 e->boundsComputed = false;
983 }
984 } else if (node->type() == QSGNode::RenderNodeType) {
985 RenderNodeElement *e = node->renderNodeElement();
986 if (e)
987 e->root = root;
988 }
989
990 SHADOWNODE_TRAVERSE(node)
991 nodeChangedBatchRoot(child, root);
992}
993
994void Renderer::nodeWasTransformed(Node *node, int *vertexCount)
995{
996 if (node->type() == QSGNode::GeometryNodeType) {
997 QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(node->sgNode);
998 *vertexCount += gn->geometry()->vertexCount();
999 Element *e = node->element();
1000 if (e) {
1001 e->boundsComputed = false;
1002 if (e->batch) {
1003 if (!e->batch->isOpaque) {
1004 invalidateBatchAndOverlappingRenderOrders(e->batch);
1005 } else if (e->batch->merged) {
1006 e->batch->needsUpload = true;
1007 }
1008 }
1009 }
1010 }
1011
1012 SHADOWNODE_TRAVERSE(node)
1013 nodeWasTransformed(child, vertexCount);
1014}
1015
1016void Renderer::nodeWasAdded(QSGNode *node, Node *shadowParent)
1017{
1018 Q_ASSERT(!m_nodes.contains(node));
1019 if (node->isSubtreeBlocked())
1020 return;
1021
1022 Node *snode = m_nodeAllocator.allocate();
1023 snode->sgNode = node;
1024 m_nodes.insert(node, snode);
1025 if (shadowParent)
1026 shadowParent->append(snode);
1027
1028 if (node->type() == QSGNode::GeometryNodeType) {
1029 snode->data = m_elementAllocator.allocate();
1030 snode->element()->setNode(static_cast<QSGGeometryNode *>(node));
1031
1032 } else if (node->type() == QSGNode::ClipNodeType) {
1033 snode->data = new ClipBatchRootInfo;
1034 m_rebuild |= FullRebuild;
1035
1036 } else if (node->type() == QSGNode::RenderNodeType) {
1037 QSGRenderNode *rn = static_cast<QSGRenderNode *>(node);
1038 RenderNodeElement *e = new RenderNodeElement(rn);
1039 snode->data = e;
1040 Q_ASSERT(!m_renderNodeElements.contains(rn));
1041 m_renderNodeElements.insert(e->renderNode, e);
1042 if (!rn->flags().testFlag(QSGRenderNode::DepthAwareRendering))
1043 m_useDepthBuffer = false;
1044 m_rebuild |= FullRebuild;
1045 }
1046
1047 QSGNODE_TRAVERSE(node)
1048 nodeWasAdded(child, snode);
1049}
1050
1051void Renderer::nodeWasRemoved(Node *node)
1052{
1053 // Prefix traversal as removeBatchRootFromParent below removes nodes
1054 // in a bottom-up manner. Note that we *cannot* use SHADOWNODE_TRAVERSE
1055 // here, because we delete 'child' (when recursed, down below), so we'd
1056 // have a use-after-free.
1057 {
1058 Node *child = node->firstChild();
1059 while (child) {
1060 // Remove (and delete) child
1061 node->remove(child);
1062 nodeWasRemoved(child);
1063 child = node->firstChild();
1064 }
1065 }
1066
1067 if (node->type() == QSGNode::GeometryNodeType) {
1068 Element *e = node->element();
1069 if (e) {
1070 e->removed = true;
1071 m_elementsToDelete.add(e);
1072 e->node = 0;
1073 if (e->root) {
1074 BatchRootInfo *info = batchRootInfo(e->root);
1075 info->availableOrders++;
1076 }
1077 if (e->batch) {
1078 e->batch->needsUpload = true;
1079 }
1080
1081 }
1082
1083 } else if (node->type() == QSGNode::ClipNodeType) {
1084 removeBatchRootFromParent(node);
1085 delete node->clipInfo();
1086 m_rebuild |= FullRebuild;
1087 m_taggedRoots.remove(node);
1088
1089 } else if (node->isBatchRoot) {
1090 removeBatchRootFromParent(node);
1091 delete node->rootInfo();
1092 m_rebuild |= FullRebuild;
1093 m_taggedRoots.remove(node);
1094
1095 } else if (node->type() == QSGNode::RenderNodeType) {
1096 RenderNodeElement *e = m_renderNodeElements.take(static_cast<QSGRenderNode *>(node->sgNode));
1097 if (e) {
1098 e->removed = true;
1099 m_elementsToDelete.add(e);
1100
1101 if (m_renderNodeElements.isEmpty()) {
1102 static bool useDepth = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER");
1103 m_useDepthBuffer = useDepth && m_context->openglContext()->format().depthBufferSize() > 0;
1104 }
1105 }
1106 }
1107
1108 Q_ASSERT(m_nodes.contains(node->sgNode));
1109
1110 m_nodeAllocator.release(m_nodes.take(node->sgNode));
1111}
1112
1113void Renderer::turnNodeIntoBatchRoot(Node *node)
1114{
1115 if (Q_UNLIKELY(debug_change())) qDebug() << " - new batch root";
1116 m_rebuild |= FullRebuild;
1117 node->isBatchRoot = true;
1118 node->becameBatchRoot = true;
1119
1120 Node *p = node->parent();
1121 while (p) {
1122 if (p->type() == QSGNode::ClipNodeType || p->isBatchRoot) {
1123 registerBatchRoot(node, p);
1124 break;
1125 }
1126 p = p->parent();
1127 }
1128
1129 SHADOWNODE_TRAVERSE(node)
1130 nodeChangedBatchRoot(child, node);
1131}
1132
1133
1134void Renderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state)
1135{
1136#ifndef QT_NO_DEBUG_OUTPUT
1137 if (Q_UNLIKELY(debug_change())) {
1138 QDebug debug = qDebug();
1139 debug << "dirty:";
1140 if (state & QSGNode::DirtyGeometry)
1141 debug << "Geometry";
1142 if (state & QSGNode::DirtyMaterial)
1143 debug << "Material";
1144 if (state & QSGNode::DirtyMatrix)
1145 debug << "Matrix";
1146 if (state & QSGNode::DirtyNodeAdded)
1147 debug << "Added";
1148 if (state & QSGNode::DirtyNodeRemoved)
1149 debug << "Removed";
1150 if (state & QSGNode::DirtyOpacity)
1151 debug << "Opacity";
1152 if (state & QSGNode::DirtySubtreeBlocked)
1153 debug << "SubtreeBlocked";
1154 if (state & QSGNode::DirtyForceUpdate)
1155 debug << "ForceUpdate";
1156
1157 // when removed, some parts of the node could already have been destroyed
1158 // so don't debug it out.
1159 if (state & QSGNode::DirtyNodeRemoved)
1160 debug << (void *) node << node->type();
1161 else
1162 debug << node;
1163 }
1164#endif
1165 // As this function calls nodeChanged recursively, we do it at the top
1166 // to avoid that any of the others are processed twice.
1167 if (state & QSGNode::DirtySubtreeBlocked) {
1168 Node *sn = m_nodes.value(node);
1169 bool blocked = node->isSubtreeBlocked();
1170 if (blocked && sn) {
1171 nodeChanged(node, QSGNode::DirtyNodeRemoved);
1172 Q_ASSERT(m_nodes.value(node) == 0);
1173 } else if (!blocked && !sn) {
1174 nodeChanged(node, QSGNode::DirtyNodeAdded);
1175 }
1176 return;
1177 }
1178
1179 if (state & QSGNode::DirtyNodeAdded) {
1180 if (nodeUpdater()->isNodeBlocked(node, rootNode())) {
1181 QSGRenderer::nodeChanged(node, state);
1182 return;
1183 }
1184 if (node == rootNode())
1185 nodeWasAdded(node, 0);
1186 else
1187 nodeWasAdded(node, m_nodes.value(node->parent()));
1188 }
1189
1190 // Mark this node dirty in the shadow tree.
1191 Node *shadowNode = m_nodes.value(node);
1192
1193 // Blocked subtrees won't have shadow nodes, so we can safely abort
1194 // here..
1195 if (!shadowNode) {
1196 QSGRenderer::nodeChanged(node, state);
1197 return;
1198 }
1199
1200 shadowNode->dirtyState |= state;
1201
1202 if (state & QSGNode::DirtyMatrix && !shadowNode->isBatchRoot) {
1203 Q_ASSERT(node->type() == QSGNode::TransformNodeType);
1204 if (node->m_subtreeRenderableCount > m_batchNodeThreshold) {
1205 turnNodeIntoBatchRoot(shadowNode);
1206 } else {
1207 int vertices = 0;
1208 nodeWasTransformed(shadowNode, &vertices);
1209 if (vertices > m_batchVertexThreshold) {
1210 turnNodeIntoBatchRoot(shadowNode);
1211 }
1212 }
1213 }
1214
1215 if (state & QSGNode::DirtyGeometry && node->type() == QSGNode::GeometryNodeType) {
1216 QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(node);
1217 Element *e = shadowNode->element();
1218 if (e) {
1219 e->boundsComputed = false;
1220 Batch *b = e->batch;
1221 if (b) {
1222 if (!e->batch->geometryWasChanged(gn) || !e->batch->isOpaque) {
1223 invalidateBatchAndOverlappingRenderOrders(e->batch);
1224 } else {
1225 b->needsUpload = true;
1226 }
1227 }
1228 }
1229 }
1230
1231 if (state & QSGNode::DirtyMaterial && node->type() == QSGNode::GeometryNodeType) {
1232 Element *e = shadowNode->element();
1233 if (e) {
1234 bool blended = hasMaterialWithBlending(static_cast<QSGGeometryNode *>(node));
1235 if (e->isMaterialBlended != blended) {
1236 m_rebuild |= Renderer::FullRebuild;
1237 e->isMaterialBlended = blended;
1238 } else if (e->batch) {
1239 if (e->batch->isMaterialCompatible(e) == BatchBreaksOnCompare)
1240 invalidateBatchAndOverlappingRenderOrders(e->batch);
1241 } else {
1242 m_rebuild |= Renderer::BuildBatches;
1243 }
1244 }
1245 }
1246
1247 // Mark the shadow tree dirty all the way back to the root...
1248 QSGNode::DirtyState dirtyChain = state & (QSGNode::DirtyNodeAdded
1249 | QSGNode::DirtyOpacity
1250 | QSGNode::DirtyMatrix
1251 | QSGNode::DirtySubtreeBlocked
1252 | QSGNode::DirtyForceUpdate);
1253 if (dirtyChain != 0) {
1254 dirtyChain = QSGNode::DirtyState(dirtyChain << 16);
1255 Node *sn = shadowNode->parent();
1256 while (sn) {
1257 sn->dirtyState |= dirtyChain;
1258 sn = sn->parent();
1259 }
1260 }
1261
1262 // Delete happens at the very end because it deletes the shadownode.
1263 if (state & QSGNode::DirtyNodeRemoved) {
1264 Node *parent = shadowNode->parent();
1265 if (parent)
1266 parent->remove(shadowNode);
1267 nodeWasRemoved(shadowNode);
1268 Q_ASSERT(m_nodes.value(node) == 0);
1269 }
1270
1271 QSGRenderer::nodeChanged(node, state);
1272}
1273
1274/*
1275 * Traverses the tree and builds two list of geometry nodes. One for
1276 * the opaque and one for the translucent. These are populated
1277 * in the order they should visually appear in, meaning first
1278 * to the back and last to the front.
1279 *
1280 * We split opaque and translucent as we can perform different
1281 * types of reordering / batching strategies on them, depending
1282 *
1283 * Note: It would be tempting to use the shadow nodes instead of the QSGNodes
1284 * for traversal to avoid hash lookups, but the order of the children
1285 * is important and they are not preserved in the shadow tree, so we must
1286 * use the actual QSGNode tree.
1287 */
1288void Renderer::buildRenderLists(QSGNode *node)
1289{
1290 if (node->isSubtreeBlocked())
1291 return;
1292
1293 Node *shadowNode = m_nodes.value(node);
1294 Q_ASSERT(shadowNode);
1295
1296 if (node->type() == QSGNode::GeometryNodeType) {
1297 QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(node);
1298
1299 Element *e = shadowNode->element();
1300 Q_ASSERT(e);
1301
1302 bool opaque = gn->inheritedOpacity() > OPAQUE_LIMIT && !(gn->activeMaterial()->flags() & QSGMaterial::Blending);
1303 if (opaque && m_useDepthBuffer)
1304 m_opaqueRenderList << e;
1305 else
1306 m_alphaRenderList << e;
1307
1308 e->order = ++m_nextRenderOrder;
1309 // Used while rebuilding partial roots.
1310 if (m_partialRebuild)
1311 e->orphaned = false;
1312
1313 } else if (node->type() == QSGNode::ClipNodeType || shadowNode->isBatchRoot) {
1314 Q_ASSERT(m_nodes.contains(node));
1315 BatchRootInfo *info = batchRootInfo(shadowNode);
1316 if (node == m_partialRebuildRoot) {
1317 m_nextRenderOrder = info->firstOrder;
1318 QSGNODE_TRAVERSE(node)
1319 buildRenderLists(child);
1320 m_nextRenderOrder = info->lastOrder + 1;
1321 } else {
1322 int currentOrder = m_nextRenderOrder;
1323 QSGNODE_TRAVERSE(node)
1324 buildRenderLists(child);
1325 int padding = (m_nextRenderOrder - currentOrder) >> 2;
1326 info->firstOrder = currentOrder;
1327 info->availableOrders = padding;
1328 info->lastOrder = m_nextRenderOrder + padding;
1329 m_nextRenderOrder = info->lastOrder;
1330 }
1331 return;
1332 } else if (node->type() == QSGNode::RenderNodeType) {
1333 RenderNodeElement *e = shadowNode->renderNodeElement();
1334 m_alphaRenderList << e;
1335 e->order = ++m_nextRenderOrder;
1336 Q_ASSERT(e);
1337 }
1338
1339 QSGNODE_TRAVERSE(node)
1340 buildRenderLists(child);
1341}
1342
1343void Renderer::tagSubRoots(Node *node)
1344{
1345 BatchRootInfo *i = batchRootInfo(node);
1346 m_taggedRoots << node;
1347 for (QSet<Node *>::const_iterator it = i->subRoots.constBegin();
1348 it != i->subRoots.constEnd(); ++it) {
1349 tagSubRoots(*it);
1350 }
1351}
1352
1353static void qsg_addOrphanedElements(QDataBuffer<Element *> &orphans, const QDataBuffer<Element *> &renderList)
1354{
1355 orphans.reset();
1356 for (int i=0; i<renderList.size(); ++i) {
1357 Element *e = renderList.at(i);
1358 if (e && !e->removed) {
1359 e->orphaned = true;
1360 orphans.add(e);
1361 }
1362 }
1363}
1364
1365static void qsg_addBackOrphanedElements(QDataBuffer<Element *> &orphans, QDataBuffer<Element *> &renderList)
1366{
1367 for (int i=0; i<orphans.size(); ++i) {
1368 Element *e = orphans.at(i);
1369 if (e->orphaned)
1370 renderList.add(e);
1371 }
1372 orphans.reset();
1373}
1374
1375/*
1376 * To rebuild the tagged roots, we start by putting all subroots of tagged
1377 * roots into the list of tagged roots. This is to make the rest of the
1378 * algorithm simpler.
1379 *
1380 * Second, we invalidate all batches which belong to tagged roots, which now
1381 * includes the entire subtree under a given root
1382 *
1383 * Then we call buildRenderLists for all tagged subroots which do not have
1384 * parents which are tagged, aka, we traverse only the topmosts roots.
1385 *
1386 * Then we sort the render lists based on their render order, to restore the
1387 * right order for rendering.
1388 */
1389void Renderer::buildRenderListsForTaggedRoots()
1390{
1391 // Flag any element that is currently in the render lists, but which
1392 // is not in a batch. This happens when we have a partial rebuild
1393 // in one sub tree while we have a BuildBatches change in another
1394 // isolated subtree. So that batch-building takes into account
1395 // these "orphaned" nodes, we flag them now. The ones under tagged
1396 // roots will be cleared again. The remaining ones are added into the
1397 // render lists so that they contain all visual nodes after the
1398 // function completes.
1399 qsg_addOrphanedElements(m_tmpOpaqueElements, m_opaqueRenderList);
1400 qsg_addOrphanedElements(m_tmpAlphaElements, m_alphaRenderList);
1401
1402 // Take a copy now, as we will be adding to this while traversing..
1403 QSet<Node *> roots = m_taggedRoots;
1404 for (QSet<Node *>::const_iterator it = roots.constBegin();
1405 it != roots.constEnd(); ++it) {
1406 tagSubRoots(*it);
1407 }
1408
1409 for (int i=0; i<m_opaqueBatches.size(); ++i) {
1410 Batch *b = m_opaqueBatches.at(i);
1411 if (m_taggedRoots.contains(b->root))
1412 invalidateAndRecycleBatch(b);
1413
1414 }
1415 for (int i=0; i<m_alphaBatches.size(); ++i) {
1416 Batch *b = m_alphaBatches.at(i);
1417 if (m_taggedRoots.contains(b->root))
1418 invalidateAndRecycleBatch(b);
1419 }
1420
1421 m_opaqueRenderList.reset();
1422 m_alphaRenderList.reset();
1423 int maxRenderOrder = m_nextRenderOrder;
1424 m_partialRebuild = true;
1425 // Traverse each root, assigning it
1426 for (QSet<Node *>::const_iterator it = m_taggedRoots.constBegin();
1427 it != m_taggedRoots.constEnd(); ++it) {
1428 Node *root = *it;
1429 BatchRootInfo *i = batchRootInfo(root);
1430 if ((!i->parentRoot || !m_taggedRoots.contains(i->parentRoot))
1431 && !nodeUpdater()->isNodeBlocked(root->sgNode, rootNode())) {
1432 m_nextRenderOrder = i->firstOrder;
1433 m_partialRebuildRoot = root->sgNode;
1434 buildRenderLists(root->sgNode);
1435 }
1436 }
1437 m_partialRebuild = false;
1438 m_partialRebuildRoot = 0;
1439 m_taggedRoots.clear();
1440 m_nextRenderOrder = qMax(m_nextRenderOrder, maxRenderOrder);
1441
1442 // Add orphaned elements back into the list and then sort it..
1443 qsg_addBackOrphanedElements(m_tmpOpaqueElements, m_opaqueRenderList);
1444 qsg_addBackOrphanedElements(m_tmpAlphaElements, m_alphaRenderList);
1445
1446 if (m_opaqueRenderList.size())
1447 std::sort(&m_opaqueRenderList.first(), &m_opaqueRenderList.last() + 1, qsg_sort_element_decreasing_order);
1448 if (m_alphaRenderList.size())
1449 std::sort(&m_alphaRenderList.first(), &m_alphaRenderList.last() + 1, qsg_sort_element_increasing_order);
1450
1451}
1452
1453void Renderer::buildRenderListsFromScratch()
1454{
1455 m_opaqueRenderList.reset();
1456 m_alphaRenderList.reset();
1457
1458 for (int i=0; i<m_opaqueBatches.size(); ++i)
1459 invalidateAndRecycleBatch(m_opaqueBatches.at(i));
1460 for (int i=0; i<m_alphaBatches.size(); ++i)
1461 invalidateAndRecycleBatch(m_alphaBatches.at(i));
1462 m_opaqueBatches.reset();
1463 m_alphaBatches.reset();
1464
1465 m_nextRenderOrder = 0;
1466
1467 buildRenderLists(rootNode());
1468}
1469
1470void Renderer::invalidateBatchAndOverlappingRenderOrders(Batch *batch)
1471{
1472 Q_ASSERT(batch);
1473 Q_ASSERT(batch->first);
1474
1475 if (m_renderOrderRebuildLower < 0 || batch->first->order < m_renderOrderRebuildLower)
1476 m_renderOrderRebuildLower = batch->first->order;
1477 if (m_renderOrderRebuildUpper < 0 || batch->lastOrderInBatch > m_renderOrderRebuildUpper)
1478 m_renderOrderRebuildUpper = batch->lastOrderInBatch;
1479
1480 batch->invalidate();
1481
1482 for (int i=0; i<m_alphaBatches.size(); ++i) {
1483 Batch *b = m_alphaBatches.at(i);
1484 if (b->first) {
1485 int bf = b->first->order;
1486 int bl = b->lastOrderInBatch;
1487 if (bl > m_renderOrderRebuildLower && bf < m_renderOrderRebuildUpper)
1488 b->invalidate();
1489 }
1490 }
1491
1492 m_rebuild |= BuildBatches;
1493}
1494
1495/* Clean up batches by making it a consecutive list of "valid"
1496 * batches and moving all invalidated batches to the batches pool.
1497 */
1498void Renderer::cleanupBatches(QDataBuffer<Batch *> *batches) {
1499 if (batches->size()) {
1500 std::stable_sort(&batches->first(), &batches->last() + 1, qsg_sort_batch_is_valid);
1501 int count = 0;
1502 while (count < batches->size() && batches->at(count)->first)
1503 ++count;
1504 for (int i=count; i<batches->size(); ++i)
1505 invalidateAndRecycleBatch(batches->at(i));
1506 batches->resize(count);
1507 }
1508}
1509
1510void Renderer::prepareOpaqueBatches()
1511{
1512 for (int i=m_opaqueRenderList.size() - 1; i >= 0; --i) {
1513 Element *ei = m_opaqueRenderList.at(i);
1514 if (!ei || ei->batch || ei->node->geometry()->vertexCount() == 0)
1515 continue;
1516 Batch *batch = newBatch();
1517 batch->first = ei;
1518 batch->root = ei->root;
1519 batch->isOpaque = true;
1520 batch->needsUpload = true;
1521 batch->positionAttribute = qsg_positionAttribute(ei->node->geometry());
1522
1523 m_opaqueBatches.add(batch);
1524
1525 ei->batch = batch;
1526 Element *next = ei;
1527
1528 QSGGeometryNode *gni = ei->node;
1529
1530 for (int j = i - 1; j >= 0; --j) {
1531 Element *ej = m_opaqueRenderList.at(j);
1532 if (!ej)
1533 continue;
1534 if (ej->root != ei->root)
1535 break;
1536 if (ej->batch || ej->node->geometry()->vertexCount() == 0)
1537 continue;
1538
1539 QSGGeometryNode *gnj = ej->node;
1540
1541 if (gni->clipList() == gnj->clipList()
1542 && gni->geometry()->drawingMode() == gnj->geometry()->drawingMode()
1543 && (gni->geometry()->drawingMode() != GL_LINES || gni->geometry()->lineWidth() == gnj->geometry()->lineWidth())
1544 && gni->geometry()->attributes() == gnj->geometry()->attributes()
1545 && gni->inheritedOpacity() == gnj->inheritedOpacity()
1546 && gni->activeMaterial()->type() == gnj->activeMaterial()->type()
1547 && gni->activeMaterial()->compare(gnj->activeMaterial()) == 0) {
1548 ej->batch = batch;
1549 next->nextInBatch = ej;
1550 next = ej;
1551 }
1552 }
1553
1554 batch->lastOrderInBatch = next->order;
1555 }
1556}
1557
1558bool Renderer::checkOverlap(int first, int last, const Rect &bounds)
1559{
1560 for (int i=first; i<=last; ++i) {
1561 Element *e = m_alphaRenderList.at(i);
1562 if (!e || e->batch)
1563 continue;
1564 Q_ASSERT(e->boundsComputed);
1565 if (e->bounds.intersects(bounds))
1566 return true;
1567 }
1568 return false;
1569}
1570
1571/*
1572 *
1573 * To avoid the O(n^2) checkOverlap check in most cases, we have the
1574 * overlapBounds which is the union of all bounding rects to check overlap
1575 * for. We know that if it does not overlap, then none of the individual
1576 * ones will either. For the typical list case, this results in no calls
1577 * to checkOverlap what-so-ever. This also ensures that when all consecutive
1578 * items are matching (such as a table of text), we don't build up an
1579 * overlap bounds and thus do not require full overlap checks.
1580 */
1581
1582void Renderer::prepareAlphaBatches()
1583{
1584 for (int i=0; i<m_alphaRenderList.size(); ++i) {
1585 Element *e = m_alphaRenderList.at(i);
1586 if (!e || e->isRenderNode)
1587 continue;
1588 Q_ASSERT(!e->removed);
1589 e->ensureBoundsValid();
1590 }
1591
1592 for (int i=0; i<m_alphaRenderList.size(); ++i) {
1593 Element *ei = m_alphaRenderList.at(i);
1594 if (!ei || ei->batch)
1595 continue;
1596
1597 if (ei->isRenderNode) {
1598 Batch *rnb = newBatch();
1599 rnb->first = ei;
1600 rnb->root = ei->root;
1601 rnb->isOpaque = false;
1602 rnb->isRenderNode = true;
1603 ei->batch = rnb;
1604 m_alphaBatches.add(rnb);
1605 continue;
1606 }
1607
1608 if (ei->node->geometry()->vertexCount() == 0)
1609 continue;
1610
1611 Batch *batch = newBatch();
1612 batch->first = ei;
1613 batch->root = ei->root;
1614 batch->isOpaque = false;
1615 batch->needsUpload = true;
1616 m_alphaBatches.add(batch);
1617 ei->batch = batch;
1618
1619 QSGGeometryNode *gni = ei->node;
1620 batch->positionAttribute = qsg_positionAttribute(gni->geometry());
1621
1622 Rect overlapBounds;
1623 overlapBounds.set(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
1624
1625 Element *next = ei;
1626
1627 for (int j = i + 1; j < m_alphaRenderList.size(); ++j) {
1628 Element *ej = m_alphaRenderList.at(j);
1629 if (!ej)
1630 continue;
1631 if (ej->root != ei->root || ej->isRenderNode)
1632 break;
1633 if (ej->batch)
1634 continue;
1635
1636 QSGGeometryNode *gnj = ej->node;
1637 if (gnj->geometry()->vertexCount() == 0)
1638 continue;
1639
1640 if (gni->clipList() == gnj->clipList()
1641 && gni->geometry()->drawingMode() == gnj->geometry()->drawingMode()
1642 && (gni->geometry()->drawingMode() != GL_LINES || gni->geometry()->lineWidth() == gnj->geometry()->lineWidth())
1643 && gni->geometry()->attributes() == gnj->geometry()->attributes()
1644 && gni->inheritedOpacity() == gnj->inheritedOpacity()
1645 && gni->activeMaterial()->type() == gnj->activeMaterial()->type()
1646 && gni->activeMaterial()->compare(gnj->activeMaterial()) == 0) {
1647 if (!overlapBounds.intersects(ej->bounds) || !checkOverlap(i+1, j - 1, ej->bounds)) {
1648 ej->batch = batch;
1649 next->nextInBatch = ej;
1650 next = ej;
1651 } else {
1652 /* When we come across a compatible element which hits an overlap, we
1653 * need to stop the batch right away. We cannot add more elements
1654 * to the current batch as they will be rendered before the batch that the
1655 * current 'ej' will be added to.
1656 */
1657 break;
1658 }
1659 } else {
1660 overlapBounds |= ej->bounds;
1661 }
1662 }
1663
1664 batch->lastOrderInBatch = next->order;
1665 }
1666
1667
1668}
1669
1670static inline int qsg_fixIndexCount(int iCount, GLenum drawMode) {
1671 switch (drawMode) {
1672 case GL_TRIANGLE_STRIP:
1673 // Merged triangle strips need to contain degenerate triangles at the beginning and end.
1674 // One could save 2 uploaded ushorts here by ditching the padding for the front of the
1675 // first and the end of the last, but for simplicity, we simply don't care.
1676 // Those extra triangles will be skipped while drawing to preserve the strip's parity
1677 // anyhow.
1678 return iCount + 2;
1679 case GL_LINES:
1680 // For lines we drop the last vertex if the number of vertices is uneven.
1681 return iCount - (iCount % 2);
1682 case GL_TRIANGLES:
1683 // For triangles we drop trailing vertices until the result is divisible by 3.
1684 return iCount - (iCount % 3);
1685 default:
1686 return iCount;
1687 }
1688}
1689
1690/* These parameters warrant some explanation...
1691 *
1692 * vaOffset: The byte offset into the vertex data to the location of the
1693 * 2D float point vertex attributes.
1694 *
1695 * vertexData: destination where the geometry's vertex data should go
1696 *
1697 * zData: destination of geometries injected Z positioning
1698 *
1699 * indexData: destination of the indices for this element
1700 *
1701 * iBase: The starting index for this element in the batch
1702 */
1703
1704void Renderer::uploadMergedElement(Element *e, int vaOffset, char **vertexData, char **zData, char **indexData, quint16 *iBase, int *indexCount)
1705{
1706 if (Q_UNLIKELY(debug_upload())) qDebug() << " - uploading element:" << e << e->node << (void *) *vertexData << (qintptr) (*zData - *vertexData) << (qintptr) (*indexData - *vertexData);
1707 QSGGeometry *g = e->node->geometry();
1708
1709 const QMatrix4x4 &localx = *e->node->matrix();
1710
1711 const int vCount = g->vertexCount();
1712 const int vSize = g->sizeOfVertex();
1713 memcpy(*vertexData, g->vertexData(), vSize * vCount);
1714
1715 // apply vertex transform..
1716 char *vdata = *vertexData + vaOffset;
1717 if (((const QMatrix4x4_Accessor &) localx).flagBits == 1) {
1718 for (int i=0; i<vCount; ++i) {
1719 Pt *p = (Pt *) vdata;
1720 p->x += ((const QMatrix4x4_Accessor &) localx).m[3][0];
1721 p->y += ((const QMatrix4x4_Accessor &) localx).m[3][1];
1722 vdata += vSize;
1723 }
1724 } else if (((const QMatrix4x4_Accessor &) localx).flagBits > 1) {
1725 for (int i=0; i<vCount; ++i) {
1726 ((Pt *) vdata)->map(localx);
1727 vdata += vSize;
1728 }
1729 }
1730
1731 if (m_useDepthBuffer) {
1732 float *vzorder = (float *) *zData;
1733 float zorder = 1.0f - e->order * m_zRange;
1734 for (int i=0; i<vCount; ++i)
1735 vzorder[i] = zorder;
1736 *zData += vCount * sizeof(float);
1737 }
1738
1739 int iCount = g->indexCount();
1740 quint16 *indices = (quint16 *) *indexData;
1741
1742 if (iCount == 0) {
1743 iCount = vCount;
1744 if (g->drawingMode() == GL_TRIANGLE_STRIP)
1745 *indices++ = *iBase;
1746 else
1747 iCount = qsg_fixIndexCount(iCount, g->drawingMode());
1748
1749 for (int i=0; i<iCount; ++i)
1750 indices[i] = *iBase + i;
1751 } else {
1752 const quint16 *srcIndices = g->indexDataAsUShort();
1753 if (g->drawingMode() == GL_TRIANGLE_STRIP)
1754 *indices++ = *iBase + srcIndices[0];
1755 else
1756 iCount = qsg_fixIndexCount(iCount, g->drawingMode());
1757
1758 for (int i=0; i<iCount; ++i)
1759 indices[i] = *iBase + srcIndices[i];
1760 }
1761 if (g->drawingMode() == GL_TRIANGLE_STRIP) {
1762 indices[iCount] = indices[iCount - 1];
1763 iCount += 2;
1764 }
1765
1766 *vertexData += vCount * vSize;
1767 *indexData += iCount * sizeof(quint16);
1768 *iBase += vCount;
1769 *indexCount += iCount;
1770}
1771
1772static QMatrix4x4 qsg_matrixForRoot(Node *node)
1773{
1774 if (node->type() == QSGNode::TransformNodeType)
1775 return static_cast<QSGTransformNode *>(node->sgNode)->combinedMatrix();
1776 Q_ASSERT(node->type() == QSGNode::ClipNodeType);
1777 QSGClipNode *c = static_cast<QSGClipNode *>(node->sgNode);
1778 return *c->matrix();
1779}
1780
1781void Renderer::uploadBatch(Batch *b)
1782{
1783 // Early out if nothing has changed in this batch..
1784 if (!b->needsUpload) {
1785 if (Q_UNLIKELY(debug_upload())) qDebug() << " Batch:" << b << "already uploaded...";
1786 return;
1787 }
1788
1789 if (!b->first) {
1790 if (Q_UNLIKELY(debug_upload())) qDebug() << " Batch:" << b << "is invalid...";
1791 return;
1792 }
1793
1794 if (b->isRenderNode) {
1795 if (Q_UNLIKELY(debug_upload())) qDebug() << " Batch: " << b << "is a render node...";
1796 return;
1797 }
1798
1799 // Figure out if we can merge or not, if not, then just render the batch as is..
1800 Q_ASSERT(b->first);
1801 Q_ASSERT(b->first->node);
1802
1803 QSGGeometryNode *gn = b->first->node;
1804 QSGGeometry *g = gn->geometry();
1805 QSGMaterial::Flags flags = gn->activeMaterial()->flags();
1806 bool canMerge = (g->drawingMode() == GL_TRIANGLES || g->drawingMode() == GL_TRIANGLE_STRIP ||
1807 g->drawingMode() == GL_LINES || g->drawingMode() == GL_POINTS)
1808 && b->positionAttribute >= 0
1809 && g->indexType() == GL_UNSIGNED_SHORT
1810 && (flags & (QSGMaterial::CustomCompileStep | QSGMaterial_FullMatrix)) == 0
1811 && ((flags & QSGMaterial::RequiresFullMatrixExceptTranslate) == 0 || b->isTranslateOnlyToRoot())
1812 && b->isSafeToBatch();
1813
1814 b->merged = canMerge;
1815
1816 // Figure out how much memory we need...
1817 b->vertexCount = 0;
1818 b->indexCount = 0;
1819 int unmergedIndexSize = 0;
1820 Element *e = b->first;
1821
1822 while (e) {
1823 QSGGeometry *eg = e->node->geometry();
1824 b->vertexCount += eg->vertexCount();
1825 int iCount = eg->indexCount();
1826 if (b->merged) {
1827 if (iCount == 0)
1828 iCount = eg->vertexCount();
1829 iCount = qsg_fixIndexCount(iCount, g->drawingMode());
1830 } else {
1831 unmergedIndexSize += iCount * eg->sizeOfIndex();
1832 }
1833 b->indexCount += iCount;
1834 e = e->nextInBatch;
1835 }
1836
1837 // Abort if there are no vertices in this batch.. We abort this late as
1838 // this is a broken usecase which we do not care to optimize for...
1839 if (b->vertexCount == 0 || (b->merged && b->indexCount == 0))
1840 return;
1841
1842 /* Allocate memory for this batch. Merged batches are divided into three separate blocks
1843 1. Vertex data for all elements, as they were in the QSGGeometry object, but
1844 with the tranform relative to this batch's root applied. The vertex data
1845 is otherwise unmodified.
1846 2. Z data for all elements, derived from each elements "render order".
1847 This is present for merged data only.
1848 3. Indices for all elements, as they were in the QSGGeometry object, but
1849 adjusted so that each index matches its.
1850 And for TRIANGLE_STRIPs, we need to insert degenerate between each
1851 primitive. These are unsigned shorts for merged and arbitrary for
1852 non-merged.
1853 */
1854 int bufferSize = b->vertexCount * g->sizeOfVertex();
1855 int ibufferSize = 0;
1856 if (b->merged) {
1857 ibufferSize = b->indexCount * sizeof(quint16);
1858 if (m_useDepthBuffer)
1859 bufferSize += b->vertexCount * sizeof(float);
1860 } else {
1861 ibufferSize = unmergedIndexSize;
1862 }
1863
1864#ifdef QSG_SEPARATE_INDEX_BUFFER
1865 map(&b->ibo, ibufferSize, true);
1866#else
1867 bufferSize += ibufferSize;
1868#endif
1869 map(&b->vbo, bufferSize);
1870
1871 if (Q_UNLIKELY(debug_upload())) qDebug() << " - batch" << b << " first:" << b->first << " root:"
1872 << b->root << " merged:" << b->merged << " positionAttribute" << b->positionAttribute
1873 << " vbo:" << b->vbo.id << ":" << b->vbo.size;
1874
1875 if (b->merged) {
1876 char *vertexData = b->vbo.data;
1877 char *zData = vertexData + b->vertexCount * g->sizeOfVertex();
1878#ifdef QSG_SEPARATE_INDEX_BUFFER
1879 char *indexData = b->ibo.data;
1880#else
1881 char *indexData = zData + (m_useDepthBuffer ? b->vertexCount * sizeof(float) : 0);
1882#endif
1883
1884 quint16 iOffset = 0;
1885 e = b->first;
1886 int verticesInSet = 0;
1887 int indicesInSet = 0;
1888 b->drawSets.reset();
1889#ifdef QSG_SEPARATE_INDEX_BUFFER
1890 int drawSetIndices = 0;
1891#else
1892 int drawSetIndices = indexData - vertexData;
1893#endif
1894 b->drawSets << DrawSet(0, zData - vertexData, drawSetIndices);
1895 while (e) {
1896 verticesInSet += e->node->geometry()->vertexCount();
1897 if (verticesInSet > 0xffff) {
1898 b->drawSets.last().indexCount = indicesInSet;
1899 if (g->drawingMode() == GL_TRIANGLE_STRIP) {
1900 b->drawSets.last().indices += 1 * sizeof(quint16);
1901 b->drawSets.last().indexCount -= 2;
1902 }
1903#ifdef QSG_SEPARATE_INDEX_BUFFER
1904 drawSetIndices = indexData - b->ibo.data;
1905#else
1906 drawSetIndices = indexData - b->vbo.data;
1907#endif
1908 b->drawSets << DrawSet(vertexData - b->vbo.data,
1909 zData - b->vbo.data,
1910 drawSetIndices);
1911 iOffset = 0;
1912 verticesInSet = e->node->geometry()->vertexCount();
1913 indicesInSet = 0;
1914 }
1915 uploadMergedElement(e, b->positionAttribute, &vertexData, &zData, &indexData, &iOffset, &indicesInSet);
1916 e = e->nextInBatch;
1917 }
1918 b->drawSets.last().indexCount = indicesInSet;
1919 // We skip the very first and very last degenerate triangles since they aren't needed
1920 // and the first one would reverse the vertex ordering of the merged strips.
1921 if (g->drawingMode() == GL_TRIANGLE_STRIP) {
1922 b->drawSets.last().indices += 1 * sizeof(quint16);
1923 b->drawSets.last().indexCount -= 2;
1924 }
1925 } else {
1926 char *vboData = b->vbo.data;
1927#ifdef QSG_SEPARATE_INDEX_BUFFER
1928 char *iboData = b->ibo.data;
1929#else
1930 char *iboData = vboData + b->vertexCount * g->sizeOfVertex();
1931#endif
1932 Element *e = b->first;
1933 while (e) {
1934 QSGGeometry *g = e->node->geometry();
1935 int vbs = g->vertexCount() * g->sizeOfVertex();
1936 memcpy(vboData, g->vertexData(), vbs);
1937 vboData = vboData + vbs;
1938 if (g->indexCount()) {
1939 int ibs = g->indexCount() * g->sizeOfIndex();
1940 memcpy(iboData, g->indexData(), ibs);
1941 iboData += ibs;
1942 }
1943 e = e->nextInBatch;
1944 }
1945 }
1946#ifndef QT_NO_DEBUG_OUTPUT
1947 if (Q_UNLIKELY(debug_upload())) {
1948 const char *vd = b->vbo.data;
1949 qDebug() << " -- Vertex Data, count:" << b->vertexCount << " - " << g->sizeOfVertex() << "bytes/vertex";
1950 for (int i=0; i<b->vertexCount; ++i) {
1951 QDebug dump = qDebug().nospace();
1952 dump << " --- " << i << ": ";
1953 int offset = 0;
1954 for (int a=0; a<g->attributeCount(); ++a) {
1955 const QSGGeometry::Attribute &attr = g->attributes()[a];
1956 dump << attr.position << ":(" << attr.tupleSize << ",";
1957 if (attr.type == GL_FLOAT) {
1958 dump << "float ";
1959 if (attr.isVertexCoordinate)
1960 dump << "* ";
1961 for (int t=0; t<attr.tupleSize; ++t)
1962 dump << *(const float *)(vd + offset + t * sizeof(float)) << " ";
1963 } else if (attr.type == GL_UNSIGNED_BYTE) {
1964 dump << "ubyte ";
1965 for (int t=0; t<attr.tupleSize; ++t)
1966 dump << *(const unsigned char *)(vd + offset + t * sizeof(unsigned char)) << " ";
1967 }
1968 dump << ") ";
1969 offset += attr.tupleSize * size_of_type(attr.type);
1970 }
1971 if (b->merged && m_useDepthBuffer) {
1972 float zorder = ((float*)(b->vbo.data + b->vertexCount * g->sizeOfVertex()))[i];
1973 dump << " Z:(" << zorder << ")";
1974 }
1975 vd += g->sizeOfVertex();
1976 }
1977
1978 if (!b->drawSets.isEmpty()) {
1979 const quint16 *id =
1980# ifdef QSG_SEPARATE_INDEX_BUFFER
1981 (const quint16 *) (b->ibo.data);
1982# else
1983 (const quint16 *) (b->vbo.data + b->drawSets.at(0).indices);
1984# endif
1985 {
1986 QDebug iDump = qDebug();
1987 iDump << " -- Index Data, count:" << b->indexCount;
1988 for (int i=0; i<b->indexCount; ++i) {
1989 if ((i % 24) == 0)
1990 iDump << endl << " --- ";
1991 iDump << id[i];
1992 }
1993 }
1994
1995 for (int i=0; i<b->drawSets.size(); ++i) {
1996 const DrawSet &s = b->drawSets.at(i);
1997 qDebug() << " -- DrawSet: indexCount:" << s.indexCount << " vertices:" << s.vertices << " z:" << s.zorders << " indices:" << s.indices;
1998 }
1999 }
2000 }
2001#endif // QT_NO_DEBUG_OUTPUT
2002
2003 unmap(&b->vbo);
2004#ifdef QSG_SEPARATE_INDEX_BUFFER
2005 unmap(&b->ibo, true);
2006#endif
2007
2008 if (Q_UNLIKELY(debug_upload())) qDebug() << " --- vertex/index buffers unmapped, batch upload completed...";
2009
2010 b->needsUpload = false;
2011
2012 if (Q_UNLIKELY(debug_render()))
2013 b->uploadedThisFrame = true;
2014}
2015
2016/*!
2017 * Convenience function to set up the stencil buffer for clipping based on \a clip.
2018 *
2019 * If the clip is a pixel aligned rectangle, this function will use glScissor instead
2020 * of stencil.
2021 */
2022Renderer::ClipType Renderer::updateStencilClip(const QSGClipNode *clip)
2023{
2024 if (!clip) {
2025 glDisable(GL_STENCIL_TEST);
2026 glDisable(GL_SCISSOR_TEST);
2027 return NoClip;
2028 }
2029
2030 ClipType clipType = NoClip;
2031 GLuint vbo = 0;
2032 int vboSize = 0;
2033
2034 bool useVBO = false;
2035 QOpenGLContext *ctx = QOpenGLContext::currentContext();
2036 QSurfaceFormat::OpenGLContextProfile profile = ctx->format().profile();
2037
2038 if (!ctx->isOpenGLES() && profile == QSurfaceFormat::CoreProfile) {
2039 // VBO are more expensive, so only use them if we must.
2040 useVBO = true;
2041 }
2042
2043 glDisable(GL_SCISSOR_TEST);
2044
2045 m_currentStencilValue = 0;
2046 m_currentScissorRect = QRect();
2047 while (clip) {
2048 QMatrix4x4 m = m_current_projection_matrix;
2049 if (clip->matrix())
2050 m *= *clip->matrix();
2051
2052 // TODO: Check for multisampling and pixel grid alignment.
2053 bool isRectangleWithNoPerspective = clip->isRectangular()
2054 && qFuzzyIsNull(m(3, 0)) && qFuzzyIsNull(m(3, 1));
2055 bool noRotate = qFuzzyIsNull(m(0, 1)) && qFuzzyIsNull(m(1, 0));
2056 bool isRotate90 = qFuzzyIsNull(m(0, 0)) && qFuzzyIsNull(m(1, 1));
2057
2058 if (isRectangleWithNoPerspective && (noRotate || isRotate90)) {
2059 QRectF bbox = clip->clipRect();
2060 qreal invW = 1 / m(3, 3);
2061 qreal fx1, fy1, fx2, fy2;
2062 if (noRotate) {
2063 fx1 = (bbox.left() * m(0, 0) + m(0, 3)) * invW;
2064 fy1 = (bbox.bottom() * m(1, 1) + m(1, 3)) * invW;
2065 fx2 = (bbox.right() * m(0, 0) + m(0, 3)) * invW;
2066 fy2 = (bbox.top() * m(1, 1) + m(1, 3)) * invW;
2067 } else {
2068 Q_ASSERT(isRotate90);
2069 fx1 = (bbox.bottom() * m(0, 1) + m(0, 3)) * invW;
2070 fy1 = (bbox.left() * m(1, 0) + m(1, 3)) * invW;
2071 fx2 = (bbox.top() * m(0, 1) + m(0, 3)) * invW;
2072 fy2 = (bbox.right() * m(1, 0) + m(1, 3)) * invW;
2073 }
2074
2075 if (fx1 > fx2)
2076 qSwap(fx1, fx2);
2077 if (fy1 > fy2)
2078 qSwap(fy1, fy2);
2079
2080 QRect deviceRect = this->deviceRect();
2081
2082 GLint ix1 = qRound((fx1 + 1) * deviceRect.width() * qreal(0.5));
2083 GLint iy1 = qRound((fy1 + 1) * deviceRect.height() * qreal(0.5));
2084 GLint ix2 = qRound((fx2 + 1) * deviceRect.width() * qreal(0.5));
2085 GLint iy2 = qRound((fy2 + 1) * deviceRect.height() * qreal(0.5));
2086
2087 if (!(clipType & ScissorClip)) {
2088 m_currentScissorRect = QRect(ix1, iy1, ix2 - ix1, iy2 - iy1);
2089 glEnable(GL_SCISSOR_TEST);
2090 clipType |= ScissorClip;
2091 } else {
2092 m_currentScissorRect &= QRect(ix1, iy1, ix2 - ix1, iy2 - iy1);
2093 }
2094 glScissor(m_currentScissorRect.x(), m_currentScissorRect.y(),
2095 m_currentScissorRect.width(), m_currentScissorRect.height());
2096 } else {
2097 if (!(clipType & StencilClip)) {
2098 if (!m_clipProgram.isLinked()) {
2099 QSGShaderSourceBuilder::initializeProgramFromFiles(
2100 &m_clipProgram,
2101 QStringLiteral(":/qt-project.org/scenegraph/shaders/stencilclip.vert"),
2102 QStringLiteral(":/qt-project.org/scenegraph/shaders/stencilclip.frag"));
2103 m_clipProgram.bindAttributeLocation("vCoord", 0);
2104 m_clipProgram.link();
2105 m_clipMatrixId = m_clipProgram.uniformLocation("matrix");
2106 }
2107
2108 glClearStencil(0);
2109 glClear(GL_STENCIL_BUFFER_BIT);
2110 glEnable(GL_STENCIL_TEST);
2111 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
2112 glDepthMask(GL_FALSE);
2113
2114 m_clipProgram.bind();
2115 m_clipProgram.enableAttributeArray(0);
2116
2117 clipType |= StencilClip;
2118 }
2119
2120 glStencilFunc(GL_EQUAL, m_currentStencilValue, 0xff); // stencil test, ref, test mask
2121 glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); // stencil fail, z fail, z pass
2122
2123 const QSGGeometry *g = clip->geometry();
2124 Q_ASSERT(g->attributeCount() > 0);
2125 const QSGGeometry::Attribute *a = g->attributes();
2126
2127 const GLvoid *pointer;
2128 if (!useVBO) {
2129 pointer = g->vertexData();
2130 } else {
2131 if (!vbo)
2132 glGenBuffers(1, &vbo);
2133
2134 glBindBuffer(GL_ARRAY_BUFFER, vbo);
2135
2136 const int vertexByteSize = g->sizeOfVertex() * g->vertexCount();
2137 if (vboSize < vertexByteSize) {
2138 vboSize = vertexByteSize;
2139 glBufferData(GL_ARRAY_BUFFER, vertexByteSize, g->vertexData(), GL_STATIC_DRAW);
2140 } else {
2141 glBufferSubData(GL_ARRAY_BUFFER, 0, vertexByteSize, g->vertexData());
2142 }
2143
2144 pointer = 0;
2145 }
2146
2147 glVertexAttribPointer(0, a->tupleSize, a->type, GL_FALSE, g->sizeOfVertex(), pointer);
2148
2149 m_clipProgram.setUniformValue(m_clipMatrixId, m);
2150 if (g->indexCount()) {
2151 glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), g->indexData());
2152 } else {
2153 glDrawArrays(g->drawingMode(), 0, g->vertexCount());
2154 }
2155
2156 if (useVBO)
2157 glBindBuffer(GL_ARRAY_BUFFER, 0);
2158
2159 ++m_currentStencilValue;
2160 }
2161
2162 clip = clip->clipList();
2163 }
2164
2165 if (vbo)
2166 glDeleteBuffers(1, &vbo);
2167
2168 if (clipType & StencilClip) {
2169 m_clipProgram.disableAttributeArray(0);
2170 glStencilFunc(GL_EQUAL, m_currentStencilValue, 0xff); // stencil test, ref, test mask
2171 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // stencil fail, z fail, z pass
2172 bindable()->reactivate();
2173 } else {
2174 glDisable(GL_STENCIL_TEST);
2175 }
2176
2177 return clipType;
2178}
2179
2180void Renderer::updateClip(const QSGClipNode *clipList, const Batch *batch)
2181{
2182 if (clipList != m_currentClip && Q_LIKELY(!debug_noclip())) {
2183 m_currentClip = clipList;
2184 // updateClip sets another program, so force-reactivate our own
2185 if (m_currentShader)
2186 setActiveShader(0, 0);
2187 glBindBuffer(GL_ARRAY_BUFFER, 0);
2188 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
2189 if (batch->isOpaque)
2190 glDisable(GL_DEPTH_TEST);
2191 m_currentClipType = updateStencilClip(m_currentClip);
2192 if (batch->isOpaque) {
2193 glEnable(GL_DEPTH_TEST);
2194 if (m_currentClipType & StencilClip)
2195 glDepthMask(true);
2196 }
2197 }
2198}
2199
2200/*!
2201 * Look at the attribute arrays and potentially the injected z attribute to figure out
2202 * which vertex attribute arrays need to be enabled and not. Then update the current
2203 * Shader and current QSGMaterialShader.
2204 */
2205void Renderer::setActiveShader(QSGMaterialShader *program, ShaderManager::Shader *shader)
2206{
2207 const char * const *c = m_currentProgram ? m_currentProgram->attributeNames() : 0;
2208 const char * const *n = program ? program->attributeNames() : 0;
2209
2210 int cza = m_currentShader ? m_currentShader->pos_order : -1;
2211 int nza = shader ? shader->pos_order : -1;
2212
2213 int i = 0;
2214 while (c || n) {
2215
2216 bool was = c;
2217 if (cza == i) {
2218 was = true;
2219 c = 0;
2220 } else if (c && !c[i]) { // end of the attribute array names
2221 c = 0;
2222 was = false;
2223 }
2224
2225 bool is = n;
2226 if (nza == i) {
2227 is = true;
2228 n = 0;
2229 } else if (n && !n[i]) {
2230 n = 0;
2231 is = false;
2232 }
2233
2234 if (is && !was)
2235 glEnableVertexAttribArray(i);
2236 else if (was && !is)
2237 glDisableVertexAttribArray(i);
2238
2239 ++i;
2240 }
2241
2242 if (m_currentProgram)
2243 m_currentProgram->deactivate();
2244 m_currentProgram = program;
2245 m_currentShader = shader;
2246 m_currentMaterial = 0;
2247 if (m_currentProgram) {
2248 m_currentProgram->program()->bind();
2249 m_currentProgram->activate();
2250 }
2251}
2252
2253void Renderer::renderMergedBatch(const Batch *batch)
2254{
2255 if (batch->vertexCount == 0 || batch->indexCount == 0)
2256 return;
2257
2258 Element *e = batch->first;
2259 Q_ASSERT(e);
2260
2261#ifndef QT_NO_DEBUG_OUTPUT
2262 if (Q_UNLIKELY(debug_render())) {
2263 QDebug debug = qDebug();
2264 debug << " -"
2265 << batch
2266 << (batch->uploadedThisFrame ? "[ upload]" : "[retained]")
2267 << (e->node->clipList() ? "[ clip]" : "[noclip]")
2268 << (batch->isOpaque ? "[opaque]" : "[ alpha]")
2269 << "[ merged]"
2270 << " Nodes:" << QString::fromLatin1("%1").arg(qsg_countNodesInBatch(batch), 4).toLatin1().constData()
2271 << " Vertices:" << QString::fromLatin1("%1").arg(batch->vertexCount, 5).toLatin1().constData()
2272 << " Indices:" << QString::fromLatin1("%1").arg(batch->indexCount, 5).toLatin1().constData()
2273 << " root:" << batch->root;
2274 if (batch->drawSets.size() > 1)
2275 debug << "sets:" << batch->drawSets.size();
2276 if (!batch->isOpaque)
2277 debug << "opacity:" << e->node->inheritedOpacity();
2278 batch->uploadedThisFrame = false;
2279 }
2280#endif
2281
2282 QSGGeometryNode *gn = e->node;
2283
2284 // We always have dirty matrix as all batches are at a unique z range.
2285 QSGMaterialShader::RenderState::DirtyStates dirty = QSGMaterialShader::RenderState::DirtyMatrix;
2286 if (batch->root)
2287 m_current_model_view_matrix = qsg_matrixForRoot(batch->root);
2288 else
2289 m_current_model_view_matrix.setToIdentity();
2290 m_current_determinant = m_current_model_view_matrix.determinant();
2291 m_current_projection_matrix = projectionMatrix(); // has potentially been changed by renderUnmergedBatch..
2292
2293 // updateClip() uses m_current_projection_matrix.
2294 updateClip(gn->clipList(), batch);
2295
2296 glBindBuffer(GL_ARRAY_BUFFER, batch->vbo.id);
2297
2298 char *indexBase = 0;
2299#ifdef QSG_SEPARATE_INDEX_BUFFER
2300 const Buffer *indexBuf = &batch->ibo;
2301#else
2302 const Buffer *indexBuf = &batch->vbo;
2303#endif
2304 if (m_context->hasBrokenIndexBufferObjects()) {
2305 indexBase = indexBuf->data;
2306 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
2307 } else {
2308 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuf->id);
2309 }
2310
2311
2312 QSGMaterial *material = gn->activeMaterial();
2313 ShaderManager::Shader *sms = m_useDepthBuffer ? m_shaderManager->prepareMaterial(material) : m_shaderManager->prepareMaterialNoRewrite(material);
2314 if (!sms)
2315 return;
2316 QSGMaterialShader *program = sms->program;
2317
2318 if (m_currentShader != sms)
2319 setActiveShader(program, sms);
2320
2321 m_current_opacity = gn->inheritedOpacity();
2322 if (sms->lastOpacity != m_current_opacity) {
2323 dirty |= QSGMaterialShader::RenderState::DirtyOpacity;
2324 sms->lastOpacity = m_current_opacity;
2325 }
2326
2327 program->updateState(state(dirty), material, m_currentMaterial);
2328
2329#ifndef QT_NO_DEBUG
2330 if (qsg_test_and_clear_material_failure()) {
2331 qDebug() << "QSGMaterial::updateState triggered an error (merged), batch will be skipped:";
2332 Element *ee = e;
2333 while (ee) {
2334 qDebug() << " -" << ee->node;
2335 ee = ee->nextInBatch;
2336 }
2337 QSGNodeDumper::dump(rootNode());
2338 qFatal("Aborting: scene graph is invalid...");
2339 }
2340#endif
2341
2342 m_currentMaterial = material;
2343
2344 QSGGeometry* g = gn->geometry();
2345 updateLineWidth(g);
2346 char const *const *attrNames = program->attributeNames();
2347 for (int i=0; i<batch->drawSets.size(); ++i) {
2348 const DrawSet &draw = batch->drawSets.at(i);
2349 int offset = 0;
2350 for (int j = 0; attrNames[j]; ++j) {
2351 if (!*attrNames[j])
2352 continue;
2353 const QSGGeometry::Attribute &a = g->attributes()[j];
2354 GLboolean normalize = a.type != GL_FLOAT && a.type != GL_DOUBLE;
2355 glVertexAttribPointer(a.position, a.tupleSize, a.type, normalize, g->sizeOfVertex(), (void *) (qintptr) (offset + draw.vertices));
2356 offset += a.tupleSize * size_of_type(a.type);
2357 }
2358 if (m_useDepthBuffer)
2359 glVertexAttribPointer(sms->pos_order, 1, GL_FLOAT, false, 0, (void *) (qintptr) (draw.zorders));
2360
2361 glDrawElements(g->drawingMode(), draw.indexCount, GL_UNSIGNED_SHORT, (void *) (qintptr) (indexBase + draw.indices));
2362 }
2363}
2364
2365void Renderer::renderUnmergedBatch(const Batch *batch)
2366{
2367 if (batch->vertexCount == 0)
2368 return;
2369
2370 Element *e = batch->first;
2371 Q_ASSERT(e);
2372
2373 if (Q_UNLIKELY(debug_render())) {
2374 qDebug() << " -"
2375 << batch
2376 << (batch->uploadedThisFrame ? "[ upload]" : "[retained]")
2377 << (e->node->clipList() ? "[ clip]" : "[noclip]")
2378 << (batch->isOpaque ? "[opaque]" : "[ alpha]")
2379 << "[unmerged]"
2380 << " Nodes:" << QString::fromLatin1("%1").arg(qsg_countNodesInBatch(batch), 4).toLatin1().constData()
2381 << " Vertices:" << QString::fromLatin1("%1").arg(batch->vertexCount, 5).toLatin1().constData()
2382 << " Indices:" << QString::fromLatin1("%1").arg(batch->indexCount, 5).toLatin1().constData()
2383 << " root:" << batch->root;
2384
2385 batch->uploadedThisFrame = false;
2386 }
2387
2388 QSGGeometryNode *gn = e->node;
2389
2390 m_current_projection_matrix = projectionMatrix();
2391 updateClip(gn->clipList(), batch);
2392
2393 glBindBuffer(GL_ARRAY_BUFFER, batch->vbo.id);
2394 char *indexBase = 0;
2395#ifdef QSG_SEPARATE_INDEX_BUFFER
2396 const Buffer *indexBuf = &batch->ibo;
2397#else
2398 const Buffer *indexBuf = &batch->vbo;
2399#endif
2400 if (batch->indexCount) {
2401 if (m_context->hasBrokenIndexBufferObjects()) {
2402 indexBase = indexBuf->data;
2403 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
2404 } else {
2405 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuf->id);
2406 }
2407 }
2408
2409 // We always have dirty matrix as all batches are at a unique z range.
2410 QSGMaterialShader::RenderState::DirtyStates dirty = QSGMaterialShader::RenderState::DirtyMatrix;
2411
2412 QSGMaterial *material = gn->activeMaterial();
2413 ShaderManager::Shader *sms = m_shaderManager->prepareMaterialNoRewrite(material);
2414 if (!sms)
2415 return;
2416 QSGMaterialShader *program = sms->program;
2417
2418 if (sms != m_currentShader)
2419 setActiveShader(program, sms);
2420
2421 m_current_opacity = gn->inheritedOpacity();
2422 if (sms->lastOpacity != m_current_opacity) {
2423 dirty |= QSGMaterialShader::RenderState::DirtyOpacity;
2424 sms->lastOpacity = m_current_opacity;
2425 }
2426
2427 int vOffset = 0;
2428#ifdef QSG_SEPARATE_INDEX_BUFFER
2429 char *iOffset = indexBase;
2430#else
2431 char *iOffset = indexBase + batch->vertexCount * gn->geometry()->sizeOfVertex();
2432#endif
2433
2434 QMatrix4x4 rootMatrix = batch->root ? qsg_matrixForRoot(batch->root) : QMatrix4x4();
2435
2436 while (e) {
2437 gn = e->node;
2438
2439 m_current_model_view_matrix = rootMatrix * *gn->matrix();
2440 m_current_determinant = m_current_model_view_matrix.determinant();
2441
2442 m_current_projection_matrix = projectionMatrix();
2443 if (m_useDepthBuffer) {
2444 m_current_projection_matrix(2, 2) = m_zRange;
2445 m_current_projection_matrix(2, 3) = 1.0f - e->order * m_zRange;
2446 }
2447
2448 program->updateState(state(dirty), material, m_currentMaterial);
2449
2450#ifndef QT_NO_DEBUG
2451 if (qsg_test_and_clear_material_failure()) {
2452 qDebug() << "QSGMaterial::updateState() triggered an error (unmerged), batch will be skipped:";
2453 qDebug() << " - offending node is" << e->node;
2454 QSGNodeDumper::dump(rootNode());
2455 qFatal("Aborting: scene graph is invalid...");
2456 return;
2457 }
2458#endif
2459
2460 // We don't need to bother with asking each node for its material as they
2461 // are all identical (compare==0) since they are in the same batch.
2462 m_currentMaterial = material;
2463
2464 QSGGeometry* g = gn->geometry();
2465 char const *const *attrNames = program->attributeNames();
2466 int offset = 0;
2467 for (int j = 0; attrNames[j]; ++j) {
2468 if (!*attrNames[j])
2469 continue;
2470 const QSGGeometry::Attribute &a = g->attributes()[j];
2471 GLboolean normalize = a.type != GL_FLOAT && a.type != GL_DOUBLE;
2472 glVertexAttribPointer(a.position, a.tupleSize, a.type, normalize, g->sizeOfVertex(), (void *) (qintptr) (offset + vOffset));
2473 offset += a.tupleSize * size_of_type(a.type);
2474 }
2475
2476 updateLineWidth(g);
2477 if (g->indexCount())
2478 glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), iOffset);
2479 else
2480 glDrawArrays(g->drawingMode(), 0, g->vertexCount());
2481
2482 vOffset += g->sizeOfVertex() * g->vertexCount();
2483 iOffset += g->indexCount() * g->sizeOfIndex();
2484
2485 // We only need to push this on the very first iteration...
2486 dirty &= ~QSGMaterialShader::RenderState::DirtyOpacity;
2487
2488 e = e->nextInBatch;
2489 }
2490}
2491
2492void Renderer::updateLineWidth(QSGGeometry *g)
2493{
2494 if (g->drawingMode() == GL_LINE_STRIP || g->drawingMode() == GL_LINE_LOOP || g->drawingMode() == GL_LINES)
2495 glLineWidth(g->lineWidth());
2496#if !defined(QT_OPENGL_ES_2)
2497 else if (!QOpenGLContext::currentContext()->isOpenGLES() && g->drawingMode() == GL_POINTS) {
2498 QOpenGLFunctions_1_0 *gl1funcs = 0;
2499 QOpenGLFunctions_3_2_Core *gl3funcs = 0;
2500 if (QOpenGLContext::currentContext()->format().profile() == QSurfaceFormat::CoreProfile)
2501 gl3funcs = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_2_Core>();
2502 else
2503 gl1funcs = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_1_0>();
2504 Q_ASSERT(gl1funcs || gl3funcs);
2505 if (gl1funcs)
2506 gl1funcs->glPointSize(g->lineWidth());
2507 else
2508 gl3funcs->glPointSize(g->lineWidth());
2509 }
2510#endif
2511}
2512
2513void Renderer::renderBatches()
2514{
2515 if (Q_UNLIKELY(debug_render())) {
2516 qDebug().nospace() << "Rendering:" << endl
2517 << " -> Opaque: " << qsg_countNodesInBatches(m_opaqueBatches) << " nodes in " << m_opaqueBatches.size() << " batches..." << endl
2518 << " -> Alpha: " << qsg_countNodesInBatches(m_alphaBatches) << " nodes in " << m_alphaBatches.size() << " batches...";
2519 }
2520
2521 QRect r = viewportRect();
2522 glViewport(r.x(), deviceRect().bottom() - r.bottom(), r.width(), r.height());
2523 glClearColor(clearColor().redF(), clearColor().greenF(), clearColor().blueF(), clearColor().alphaF());
2524
2525 if (m_useDepthBuffer) {
2526 glClearDepthf(1); // calls glClearDepth() under the hood for desktop OpenGL
2527 glEnable(GL_DEPTH_TEST);
2528 glDepthFunc(GL_LESS);
2529 glDepthMask(true);
2530 glDisable(GL_BLEND);
2531 } else {
2532 glDisable(GL_DEPTH_TEST);
2533 glDepthMask(false);
2534 }
2535 glDisable(GL_CULL_FACE);
2536 glColorMask(true, true, true, true);
2537 glDisable(GL_SCISSOR_TEST);
2538 glDisable(GL_STENCIL_TEST);
2539
2540 bindable()->clear(clearMode());
2541
2542 m_current_opacity = 1;
2543 m_currentMaterial = 0;
2544 m_currentShader = 0;
2545 m_currentProgram = 0;
2546 m_currentClip = 0;
2547
2548 bool renderOpaque = !debug_noopaque();
2549 bool renderAlpha = !debug_noalpha();
2550
2551 if (Q_LIKELY(renderOpaque)) {
2552 for (int i=0; i<m_opaqueBatches.size(); ++i) {
2553 Batch *b = m_opaqueBatches.at(i);
2554 if (b->merged)
2555 renderMergedBatch(b);
2556 else
2557 renderUnmergedBatch(b);
2558 }
2559 }
2560
2561 glEnable(GL_BLEND);
2562 if (m_useDepthBuffer)
2563 glDepthMask(false);
2564 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
2565
2566 if (Q_LIKELY(renderAlpha)) {
2567 for (int i=0; i<m_alphaBatches.size(); ++i) {
2568 Batch *b = m_alphaBatches.at(i);
2569 if (b->merged)
2570 renderMergedBatch(b);
2571 else if (b->isRenderNode)
2572 renderRenderNode(b);
2573 else
2574 renderUnmergedBatch(b);
2575 }
2576 }
2577
2578 if (m_currentShader)
2579 setActiveShader(0, 0);
2580 updateStencilClip(0);
2581 glBindBuffer(GL_ARRAY_BUFFER, 0);
2582 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
2583 glDepthMask(true);
2584}
2585
2586void Renderer::deleteRemovedElements()
2587{
2588 if (!m_elementsToDelete.size())
2589 return;
2590
2591 for (int i=0; i<m_opaqueRenderList.size(); ++i) {
2592 Element **e = m_opaqueRenderList.data() + i;
2593 if (*e && (*e)->removed)
2594 *e = 0;
2595 }
2596 for (int i=0; i<m_alphaRenderList.size(); ++i) {
2597 Element **e = m_alphaRenderList.data() + i;
2598 if (*e && (*e)->removed)
2599 *e = 0;
2600 }
2601
2602 for (int i=0; i<m_elementsToDelete.size(); ++i) {
2603 Element *e = m_elementsToDelete.at(i);
2604 if (e->isRenderNode)
2605 delete static_cast<RenderNodeElement *>(e);
2606 else
2607 m_elementAllocator.release(e);
2608 }
2609 m_elementsToDelete.reset();
2610}
2611
2612void Renderer::render()
2613{
2614 if (Q_UNLIKELY(debug_dump())) {
2615 qDebug("\n");
2616 QSGNodeDumper::dump(rootNode());
2617 }
2618
2619 QElapsedTimer timer;
2620 quint64 timeRenderLists = 0;
2621 quint64 timePrepareOpaque = 0;
2622 quint64 timePrepareAlpha = 0;
2623 quint64 timeSorting = 0;
2624 quint64 timeUploadOpaque = 0;
2625 quint64 timeUploadAlpha = 0;
2626
2627 if (Q_UNLIKELY(debug_render() || debug_build())) {
2628 QByteArray type("rebuild:");
2629 if (m_rebuild == 0)
2630 type += " none";
2631 if (m_rebuild == FullRebuild)
2632 type += " full";
2633 else {
2634 if (m_rebuild & BuildRenderLists)
2635 type += " renderlists";
2636 else if (m_rebuild & BuildRenderListsForTaggedRoots)
2637 type += " partial";
2638 else if (m_rebuild & BuildBatches)
2639 type += " batches";
2640 }
2641
2642 qDebug() << "Renderer::render()" << this << type;
2643 timer.start();
2644 }
2645
2646 if (m_vao)
2647 m_vao->bind();
2648
2649 if (m_rebuild & (BuildRenderLists | BuildRenderListsForTaggedRoots)) {
2650 bool complete = (m_rebuild & BuildRenderLists) != 0;
2651 if (complete)
2652 buildRenderListsFromScratch();
2653 else
2654 buildRenderListsForTaggedRoots();
2655 m_rebuild |= BuildBatches;
2656
2657 if (Q_UNLIKELY(debug_build())) {
2658 qDebug() << "Opaque render lists" << (complete ? "(complete)" : "(partial)") << ":";
2659 for (int i=0; i<m_opaqueRenderList.size(); ++i) {
2660 Element *e = m_opaqueRenderList.at(i);
2661 qDebug() << " - element:" << e << " batch:" << e->batch << " node:" << e->node << " order:" << e->order;
2662 }
2663 qDebug() << "Alpha render list:" << (complete ? "(complete)" : "(partial)") << ":";
2664 for (int i=0; i<m_alphaRenderList.size(); ++i) {
2665 Element *e = m_alphaRenderList.at(i);
2666 qDebug() << " - element:" << e << " batch:" << e->batch << " node:" << e->node << " order:" << e->order;
2667 }
2668 }
2669 }
2670 if (Q_UNLIKELY(debug_render())) timeRenderLists = timer.restart();
2671
2672 for (int i=0; i<m_opaqueBatches.size(); ++i)
2673 m_opaqueBatches.at(i)->cleanupRemovedElements();
2674 for (int i=0; i<m_alphaBatches.size(); ++i)
2675 m_alphaBatches.at(i)->cleanupRemovedElements();
2676 deleteRemovedElements();
2677
2678 cleanupBatches(&m_opaqueBatches);
2679 cleanupBatches(&m_alphaBatches);
2680
2681 if (m_rebuild & BuildBatches) {
2682 prepareOpaqueBatches();
2683 if (Q_UNLIKELY(debug_render())) timePrepareOpaque = timer.restart();
2684 prepareAlphaBatches();
2685 if (Q_UNLIKELY(debug_render())) timePrepareAlpha = timer.restart();
2686
2687 if (Q_UNLIKELY(debug_build())) {
2688 qDebug() << "Opaque Batches:";
2689 for (int i=0; i<m_opaqueBatches.size(); ++i) {
2690 Batch *b = m_opaqueBatches.at(i);
2691 qDebug() << " - Batch " << i << b << (b->needsUpload ? "upload" : "") << " root:" << b->root;
2692 for (Element *e = b->first; e; e = e->nextInBatch) {
2693 qDebug() << " - element:" << e << " node:" << e->node << e->order;
2694 }
2695 }
2696 qDebug() << "Alpha Batches:";
2697 for (int i=0; i<m_alphaBatches.size(); ++i) {
2698 Batch *b = m_alphaBatches.at(i);
2699 qDebug() << " - Batch " << i << b << (b->needsUpload ? "upload" : "") << " root:" << b->root;
2700 for (Element *e = b->first; e; e = e->nextInBatch) {
2701 qDebug() << " - element:" << e << e->bounds << " node:" << e->node << " order:" << e->order;
2702 }
2703 }
2704 }
2705 } else {
2706