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 = context->openglContext();
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), nullptr);
159 context->initializeShader(s);
160 if (!p->isLinked())
161 return nullptr;
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 = nullptr;
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("%s - no info", ind.constData());
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 = nullptr;
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 = nullptr;
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 != nullptr) {
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 != nullptr)
485 info = renderer->batchRootInfo(info->parentRoot);
486 else
487 info = nullptr;
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 = nullptr;
684 root = nullptr;
685 while (e) {
686 e->batch = nullptr;
687 Element *n = e->nextInBatch;
688 e->nextInBatch = nullptr;
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(nullptr)
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(nullptr)
772 , m_currentShader(nullptr)
773 , m_currentStencilValue(0)
774 , m_clipMatrixId(0)
775 , m_currentClip(nullptr)
776 , m_currentClipType(NoClip)
777 , m_vertexUploadPool(256)
778 , m_indexUploadPool(64)
779 , m_vao(nullptr)
780 , m_visualizeMode(VisualizeNothing)
781{
782 initializeOpenGLFunctions();
783 setNodeUpdater(new Updater(this));
784
785 m_shaderManager = ctx->findChild<ShaderManager *>(QStringLiteral("__qt_ShaderManager"), Qt::FindDirectChildrenOnly);
786 if (!m_shaderManager) {
787 m_shaderManager = new ShaderManager(ctx);
788 m_shaderManager->setObjectName(QStringLiteral("__qt_ShaderManager"));
789 m_shaderManager->setParent(ctx);
790 QObject::connect(ctx, SIGNAL(invalidated()), m_shaderManager, SLOT(invalidated()), Qt::DirectConnection);
791 }
792
793 m_bufferStrategy = GL_STATIC_DRAW;
794 if (Q_UNLIKELY(qEnvironmentVariableIsSet("QSG_RENDERER_BUFFER_STRATEGY"))) {
795 const QByteArray strategy = qgetenv("QSG_RENDERER_BUFFER_STRATEGY");
796 if (strategy == "dynamic")
797 m_bufferStrategy = GL_DYNAMIC_DRAW;
798 else if (strategy == "stream")
799 m_bufferStrategy = GL_STREAM_DRAW;
800 }
801
802 m_batchNodeThreshold = qt_sg_envInt("QSG_RENDERER_BATCH_NODE_THRESHOLD", 64);
803 m_batchVertexThreshold = qt_sg_envInt("QSG_RENDERER_BATCH_VERTEX_THRESHOLD", 1024);
804
805 if (Q_UNLIKELY(debug_build() || debug_render())) {
806 qDebug("Batch thresholds: nodes: %d vertices: %d",
807 m_batchNodeThreshold, m_batchVertexThreshold);
808 qDebug("Using buffer strategy: %s",
809 (m_bufferStrategy == GL_STATIC_DRAW
810 ? "static" : (m_bufferStrategy == GL_DYNAMIC_DRAW ? "dynamic" : "stream")));
811 }
812
813 // If rendering with an OpenGL Core profile context, we need to create a VAO
814 // to hold our vertex specification state.
815 if (m_context->openglContext()->format().profile() == QSurfaceFormat::CoreProfile) {
816 m_vao = new QOpenGLVertexArrayObject(this);
817 m_vao->create();
818 }
819
820 bool useDepth = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER");
821 m_useDepthBuffer = useDepth && ctx->openglContext()->format().depthBufferSize() > 0;
822}
823
824static void qsg_wipeBuffer(Buffer *buffer, QOpenGLFunctions *funcs)
825{
826 funcs->glDeleteBuffers(1, &buffer->id);
827 // The free here is ok because we're in one of two situations.
828 // 1. We're using the upload pool in which case unmap will have set the
829 // data pointer to 0 and calling free on 0 is ok.
830 // 2. We're using dedicated buffers because of visualization or IBO workaround
831 // and the data something we malloced and must be freed.
832 free(buffer->data);
833}
834
835static void qsg_wipeBatch(Batch *batch, QOpenGLFunctions *funcs, bool separateIndexBuffer)
836{
837 qsg_wipeBuffer(&batch->vbo, funcs);
838 if (separateIndexBuffer)
839 qsg_wipeBuffer(&batch->ibo, funcs);
840 delete batch;
841}
842
843Renderer::~Renderer()
844{
845 if (QOpenGLContext::currentContext()) {
846 // Clean up batches and buffers
847 const bool separateIndexBuffer = m_context->separateIndexBuffer();
848 for (int i = 0; i < m_opaqueBatches.size(); ++i)
849 qsg_wipeBatch(m_opaqueBatches.at(i), this, separateIndexBuffer);
850 for (int i = 0; i < m_alphaBatches.size(); ++i)
851 qsg_wipeBatch(m_alphaBatches.at(i), this, separateIndexBuffer);
852 for (int i = 0; i < m_batchPool.size(); ++i)
853 qsg_wipeBatch(m_batchPool.at(i), this, separateIndexBuffer);
854 }
855
856 for (Node *n : qAsConst(m_nodes))
857 m_nodeAllocator.release(n);
858
859 // Remaining elements...
860 for (int i=0; i<m_elementsToDelete.size(); ++i) {
861 Element *e = m_elementsToDelete.at(i);
862 if (e->isRenderNode)
863 delete static_cast<RenderNodeElement *>(e);
864 else
865 m_elementAllocator.release(e);
866 }
867}
868
869void Renderer::invalidateAndRecycleBatch(Batch *b)
870{
871 b->invalidate();
872 for (int i=0; i<m_batchPool.size(); ++i)
873 if (b == m_batchPool.at(i))
874 return;
875 m_batchPool.add(b);
876}
877
878/* The code here does a CPU-side allocation which might seem like a performance issue
879 * compared to using glMapBuffer or glMapBufferRange which would give me back
880 * potentially GPU allocated memory and saving me one deep-copy, but...
881 *
882 * Because we do a lot of CPU-side transformations, we need random-access memory
883 * and the memory returned from glMapBuffer/glMapBufferRange is typically
884 * uncached and thus very slow for our purposes.
885 *
886 * ref: http://www.opengl.org/wiki/Buffer_Object
887 */
888void Renderer::map(Buffer *buffer, int byteSize, bool isIndexBuf)
889{
890 if (!m_context->hasBrokenIndexBufferObjects() && m_visualizeMode == VisualizeNothing) {
891 // Common case, use a shared memory pool for uploading vertex data to avoid
892 // excessive reevaluation
893 QDataBuffer<char> &pool = m_context->separateIndexBuffer() && isIndexBuf
894 ? m_indexUploadPool : m_vertexUploadPool;
895 if (byteSize > pool.size())
896 pool.resize(byteSize);
897 buffer->data = pool.data();
898 } else if (buffer->size != byteSize) {
899 free(buffer->data);
900 buffer->data = (char *) malloc(byteSize);
901 Q_CHECK_PTR(buffer->data);
902 }
903 buffer->size = byteSize;
904}
905
906void Renderer::unmap(Buffer *buffer, bool isIndexBuf)
907{
908 if (buffer->id == 0)
909 glGenBuffers(1, &buffer->id);
910 GLenum target = isIndexBuf ? GL_ELEMENT_ARRAY_BUFFER : GL_ARRAY_BUFFER;
911 glBindBuffer(target, buffer->id);
912 glBufferData(target, buffer->size, buffer->data, m_bufferStrategy);
913
914 if (!m_context->hasBrokenIndexBufferObjects() && m_visualizeMode == VisualizeNothing) {
915 buffer->data = nullptr;
916 }
917}
918
919BatchRootInfo *Renderer::batchRootInfo(Node *node)
920{
921 BatchRootInfo *info = node->rootInfo();
922 if (!info) {
923 if (node->type() == QSGNode::ClipNodeType)
924 info = new ClipBatchRootInfo;
925 else {
926 Q_ASSERT(node->type() == QSGNode::TransformNodeType);
927 info = new BatchRootInfo;
928 }
929 node->data = info;
930 }
931 return info;
932}
933
934void Renderer::removeBatchRootFromParent(Node *childRoot)
935{
936 BatchRootInfo *childInfo = batchRootInfo(childRoot);
937 if (!childInfo->parentRoot)
938 return;
939 BatchRootInfo *parentInfo = batchRootInfo(childInfo->parentRoot);
940
941 Q_ASSERT(parentInfo->subRoots.contains(childRoot));
942 parentInfo->subRoots.remove(childRoot);
943 childInfo->parentRoot = nullptr;
944}
945
946void Renderer::registerBatchRoot(Node *subRoot, Node *parentRoot)
947{
948 BatchRootInfo *subInfo = batchRootInfo(subRoot);
949 BatchRootInfo *parentInfo = batchRootInfo(parentRoot);
950 subInfo->parentRoot = parentRoot;
951 parentInfo->subRoots << subRoot;
952}
953
954bool Renderer::changeBatchRoot(Node *node, Node *root)
955{
956 BatchRootInfo *subInfo = batchRootInfo(node);
957 if (subInfo->parentRoot == root)
958 return false;
959 if (subInfo->parentRoot) {
960 BatchRootInfo *oldRootInfo = batchRootInfo(subInfo->parentRoot);
961 oldRootInfo->subRoots.remove(node);
962 }
963 BatchRootInfo *newRootInfo = batchRootInfo(root);
964 newRootInfo->subRoots << node;
965 subInfo->parentRoot = root;
966 return true;
967}
968
969void Renderer::nodeChangedBatchRoot(Node *node, Node *root)
970{
971 if (node->type() == QSGNode::ClipNodeType || node->isBatchRoot) {
972 // When we reach a batchroot, we only need to update it. Its subtree
973 // is relative to that root, so no need to recurse further.
974 changeBatchRoot(node, root);
975 return;
976 } else if (node->type() == QSGNode::GeometryNodeType) {
977 // Only need to change the root as nodeChanged anyway flags a full update.
978 Element *e = node->element();
979 if (e) {
980 e->root = root;
981 e->boundsComputed = false;
982 }
983 } else if (node->type() == QSGNode::RenderNodeType) {
984 RenderNodeElement *e = node->renderNodeElement();
985 if (e)
986 e->root = root;
987 }
988
989 SHADOWNODE_TRAVERSE(node)
990 nodeChangedBatchRoot(child, root);
991}
992
993void Renderer::nodeWasTransformed(Node *node, int *vertexCount)
994{
995 if (node->type() == QSGNode::GeometryNodeType) {
996 QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(node->sgNode);
997 *vertexCount += gn->geometry()->vertexCount();
998 Element *e = node->element();
999 if (e) {
1000 e->boundsComputed = false;
1001 if (e->batch) {
1002 if (!e->batch->isOpaque) {
1003 invalidateBatchAndOverlappingRenderOrders(e->batch);
1004 } else if (e->batch->merged) {
1005 e->batch->needsUpload = true;
1006 }
1007 }
1008 }
1009 }
1010
1011 SHADOWNODE_TRAVERSE(node)
1012 nodeWasTransformed(child, vertexCount);
1013}
1014
1015void Renderer::nodeWasAdded(QSGNode *node, Node *shadowParent)
1016{
1017 Q_ASSERT(!m_nodes.contains(node));
1018 if (node->isSubtreeBlocked())
1019 return;
1020
1021 Node *snode = m_nodeAllocator.allocate();
1022 snode->sgNode = node;
1023 m_nodes.insert(node, snode);
1024 if (shadowParent)
1025 shadowParent->append(snode);
1026
1027 if (node->type() == QSGNode::GeometryNodeType) {
1028 snode->data = m_elementAllocator.allocate();
1029 snode->element()->setNode(static_cast<QSGGeometryNode *>(node));
1030
1031 } else if (node->type() == QSGNode::ClipNodeType) {
1032 snode->data = new ClipBatchRootInfo;
1033 m_rebuild |= FullRebuild;
1034
1035 } else if (node->type() == QSGNode::RenderNodeType) {
1036 QSGRenderNode *rn = static_cast<QSGRenderNode *>(node);
1037 RenderNodeElement *e = new RenderNodeElement(rn);
1038 snode->data = e;
1039 Q_ASSERT(!m_renderNodeElements.contains(rn));
1040 m_renderNodeElements.insert(e->renderNode, e);
1041 if (!rn->flags().testFlag(QSGRenderNode::DepthAwareRendering))
1042 m_useDepthBuffer = false;
1043 m_rebuild |= FullRebuild;
1044 }
1045
1046 QSGNODE_TRAVERSE(node)
1047 nodeWasAdded(child, snode);
1048}
1049
1050void Renderer::nodeWasRemoved(Node *node)
1051{
1052 // Prefix traversal as removeBatchRootFromParent below removes nodes
1053 // in a bottom-up manner. Note that we *cannot* use SHADOWNODE_TRAVERSE
1054 // here, because we delete 'child' (when recursed, down below), so we'd
1055 // have a use-after-free.
1056 {
1057 Node *child = node->firstChild();
1058 while (child) {
1059 // Remove (and delete) child
1060 node->remove(child);
1061 nodeWasRemoved(child);
1062 child = node->firstChild();
1063 }
1064 }
1065
1066 if (node->type() == QSGNode::GeometryNodeType) {
1067 Element *e = node->element();
1068 if (e) {
1069 e->removed = true;
1070 m_elementsToDelete.add(e);
1071 e->node = nullptr;
1072 if (e->root) {
1073 BatchRootInfo *info = batchRootInfo(e->root);
1074 info->availableOrders++;
1075 }
1076 if (e->batch) {
1077 e->batch->needsUpload = true;
1078 }
1079
1080 }
1081
1082 } else if (node->type() == QSGNode::ClipNodeType) {
1083 removeBatchRootFromParent(node);
1084 delete node->clipInfo();
1085 m_rebuild |= FullRebuild;
1086 m_taggedRoots.remove(node);
1087
1088 } else if (node->isBatchRoot) {
1089 removeBatchRootFromParent(node);
1090 delete node->rootInfo();
1091 m_rebuild |= FullRebuild;
1092 m_taggedRoots.remove(node);
1093
1094 } else if (node->type() == QSGNode::RenderNodeType) {
1095 RenderNodeElement *e = m_renderNodeElements.take(static_cast<QSGRenderNode *>(node->sgNode));
1096 if (e) {
1097 e->removed = true;
1098 m_elementsToDelete.add(e);
1099
1100 if (m_renderNodeElements.isEmpty()) {
1101 static bool useDepth = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER");
1102 m_useDepthBuffer = useDepth && m_context->openglContext()->format().depthBufferSize() > 0;
1103 }
1104 }
1105 }
1106
1107 Q_ASSERT(m_nodes.contains(node->sgNode));
1108
1109 m_nodeAllocator.release(m_nodes.take(node->sgNode));
1110}
1111
1112void Renderer::turnNodeIntoBatchRoot(Node *node)
1113{
1114 if (Q_UNLIKELY(debug_change())) qDebug(" - new batch root");
1115 m_rebuild |= FullRebuild;
1116 node->isBatchRoot = true;
1117 node->becameBatchRoot = true;
1118
1119 Node *p = node->parent();
1120 while (p) {
1121 if (p->type() == QSGNode::ClipNodeType || p->isBatchRoot) {
1122 registerBatchRoot(node, p);
1123 break;
1124 }
1125 p = p->parent();
1126 }
1127
1128 SHADOWNODE_TRAVERSE(node)
1129 nodeChangedBatchRoot(child, node);
1130}
1131
1132
1133void Renderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state)
1134{
1135#ifndef QT_NO_DEBUG_OUTPUT
1136 if (Q_UNLIKELY(debug_change())) {
1137 QDebug debug = qDebug();
1138 debug << "dirty:";
1139 if (state & QSGNode::DirtyGeometry)
1140 debug << "Geometry";
1141 if (state & QSGNode::DirtyMaterial)
1142 debug << "Material";
1143 if (state & QSGNode::DirtyMatrix)
1144 debug << "Matrix";
1145 if (state & QSGNode::DirtyNodeAdded)
1146 debug << "Added";
1147 if (state & QSGNode::DirtyNodeRemoved)
1148 debug << "Removed";
1149 if (state & QSGNode::DirtyOpacity)
1150 debug << "Opacity";
1151 if (state & QSGNode::DirtySubtreeBlocked)
1152 debug << "SubtreeBlocked";
1153 if (state & QSGNode::DirtyForceUpdate)
1154 debug << "ForceUpdate";
1155
1156 // when removed, some parts of the node could already have been destroyed
1157 // so don't debug it out.
1158 if (state & QSGNode::DirtyNodeRemoved)
1159 debug << (void *) node << node->type();
1160 else
1161 debug << node;
1162 }
1163#endif
1164 // As this function calls nodeChanged recursively, we do it at the top
1165 // to avoid that any of the others are processed twice.
1166 if (state & QSGNode::DirtySubtreeBlocked) {
1167 Node *sn = m_nodes.value(node);
1168 bool blocked = node->isSubtreeBlocked();
1169 if (blocked && sn) {
1170 nodeChanged(node, QSGNode::DirtyNodeRemoved);
1171 Q_ASSERT(m_nodes.value(node) == 0);
1172 } else if (!blocked && !sn) {
1173 nodeChanged(node, QSGNode::DirtyNodeAdded);
1174 }
1175 return;
1176 }
1177
1178 if (state & QSGNode::DirtyNodeAdded) {
1179 if (nodeUpdater()->isNodeBlocked(node, rootNode())) {
1180 QSGRenderer::nodeChanged(node, state);
1181 return;
1182 }
1183 if (node == rootNode())
1184 nodeWasAdded(node, nullptr);
1185 else
1186 nodeWasAdded(node, m_nodes.value(node->parent()));
1187 }
1188
1189 // Mark this node dirty in the shadow tree.
1190 Node *shadowNode = m_nodes.value(node);
1191
1192 // Blocked subtrees won't have shadow nodes, so we can safely abort
1193 // here..
1194 if (!shadowNode) {
1195 QSGRenderer::nodeChanged(node, state);
1196 return;
1197 }
1198
1199 shadowNode->dirtyState |= state;
1200
1201 if (state & QSGNode::DirtyMatrix && !shadowNode->isBatchRoot) {
1202 Q_ASSERT(node->type() == QSGNode::TransformNodeType);
1203 if (node->m_subtreeRenderableCount > m_batchNodeThreshold) {
1204 turnNodeIntoBatchRoot(shadowNode);
1205 } else {
1206 int vertices = 0;
1207 nodeWasTransformed(shadowNode, &vertices);
1208 if (vertices > m_batchVertexThreshold) {
1209 turnNodeIntoBatchRoot(shadowNode);
1210 }
1211 }
1212 }
1213
1214 if (state & QSGNode::DirtyGeometry && node->type() == QSGNode::GeometryNodeType) {
1215 QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(node);
1216 Element *e = shadowNode->element();
1217 if (e) {
1218 e->boundsComputed = false;
1219 Batch *b = e->batch;
1220 if (b) {
1221 if (!e->batch->geometryWasChanged(gn) || !e->batch->isOpaque) {
1222 invalidateBatchAndOverlappingRenderOrders(e->batch);
1223 } else {
1224 b->needsUpload = true;
1225 }
1226 }
1227 }
1228 }
1229
1230 if (state & QSGNode::DirtyMaterial && node->type() == QSGNode::GeometryNodeType) {
1231 Element *e = shadowNode->element();
1232 if (e) {
1233 bool blended = hasMaterialWithBlending(static_cast<QSGGeometryNode *>(node));
1234 if (e->isMaterialBlended != blended) {
1235 m_rebuild |= Renderer::FullRebuild;
1236 e->isMaterialBlended = blended;
1237 } else if (e->batch) {
1238 if (e->batch->isMaterialCompatible(e) == BatchBreaksOnCompare)
1239 invalidateBatchAndOverlappingRenderOrders(e->batch);
1240 } else {
1241 m_rebuild |= Renderer::BuildBatches;
1242 }
1243 }
1244 }
1245
1246 // Mark the shadow tree dirty all the way back to the root...
1247 QSGNode::DirtyState dirtyChain = state & (QSGNode::DirtyNodeAdded
1248 | QSGNode::DirtyOpacity
1249 | QSGNode::DirtyMatrix
1250 | QSGNode::DirtySubtreeBlocked
1251 | QSGNode::DirtyForceUpdate);
1252 if (dirtyChain != 0) {
1253 dirtyChain = QSGNode::DirtyState(dirtyChain << 16);
1254 Node *sn = shadowNode->parent();
1255 while (sn) {
1256 sn->dirtyState |= dirtyChain;
1257 sn = sn->parent();
1258 }
1259 }
1260
1261 // Delete happens at the very end because it deletes the shadownode.
1262 if (state & QSGNode::DirtyNodeRemoved) {
1263 Node *parent = shadowNode->parent();
1264 if (parent)
1265 parent->remove(shadowNode);
1266 nodeWasRemoved(shadowNode);
1267 Q_ASSERT(m_nodes.value(node) == 0);
1268 }
1269
1270 QSGRenderer::nodeChanged(node, state);
1271}
1272
1273/*
1274 * Traverses the tree and builds two list of geometry nodes. One for
1275 * the opaque and one for the translucent. These are populated
1276 * in the order they should visually appear in, meaning first
1277 * to the back and last to the front.
1278 *
1279 * We split opaque and translucent as we can perform different
1280 * types of reordering / batching strategies on them, depending
1281 *
1282 * Note: It would be tempting to use the shadow nodes instead of the QSGNodes
1283 * for traversal to avoid hash lookups, but the order of the children
1284 * is important and they are not preserved in the shadow tree, so we must
1285 * use the actual QSGNode tree.
1286 */
1287void Renderer::buildRenderLists(QSGNode *node)
1288{
1289 if (node->isSubtreeBlocked())
1290 return;
1291
1292 Node *shadowNode = m_nodes.value(node);
1293 Q_ASSERT(shadowNode);
1294
1295 if (node->type() == QSGNode::GeometryNodeType) {
1296 QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(node);
1297
1298 Element *e = shadowNode->element();
1299 Q_ASSERT(e);
1300
1301 bool opaque = gn->inheritedOpacity() > OPAQUE_LIMIT && !(gn->activeMaterial()->flags() & QSGMaterial::Blending);
1302 if (opaque && m_useDepthBuffer)
1303 m_opaqueRenderList << e;
1304 else
1305 m_alphaRenderList << e;
1306
1307 e->order = ++m_nextRenderOrder;
1308 // Used while rebuilding partial roots.
1309 if (m_partialRebuild)
1310 e->orphaned = false;
1311
1312 } else if (node->type() == QSGNode::ClipNodeType || shadowNode->isBatchRoot) {
1313 Q_ASSERT(m_nodes.contains(node));
1314 BatchRootInfo *info = batchRootInfo(shadowNode);
1315 if (node == m_partialRebuildRoot) {
1316 m_nextRenderOrder = info->firstOrder;
1317 QSGNODE_TRAVERSE(node)
1318 buildRenderLists(child);
1319 m_nextRenderOrder = info->lastOrder + 1;
1320 } else {
1321 int currentOrder = m_nextRenderOrder;
1322 QSGNODE_TRAVERSE(node)
1323 buildRenderLists(child);
1324 int padding = (m_nextRenderOrder - currentOrder) >> 2;
1325 info->firstOrder = currentOrder;
1326 info->availableOrders = padding;
1327 info->lastOrder = m_nextRenderOrder + padding;
1328 m_nextRenderOrder = info->lastOrder;
1329 }
1330 return;
1331 } else if (node->type() == QSGNode::RenderNodeType) {
1332 RenderNodeElement *e = shadowNode->renderNodeElement();
1333 m_alphaRenderList << e;
1334 e->order = ++m_nextRenderOrder;
1335 Q_ASSERT(e);
1336 }
1337
1338 QSGNODE_TRAVERSE(node)
1339 buildRenderLists(child);
1340}
1341
1342void Renderer::tagSubRoots(Node *node)
1343{
1344 BatchRootInfo *i = batchRootInfo(node);
1345 m_taggedRoots << node;
1346 for (QSet<Node *>::const_iterator it = i->subRoots.constBegin();
1347 it != i->subRoots.constEnd(); ++it) {
1348 tagSubRoots(*it);
1349 }
1350}
1351
1352static void qsg_addOrphanedElements(QDataBuffer<Element *> &orphans, const QDataBuffer<Element *> &renderList)
1353{
1354 orphans.reset();
1355 for (int i=0; i<renderList.size(); ++i) {
1356 Element *e = renderList.at(i);
1357 if (e && !e->removed) {
1358 e->orphaned = true;
1359 orphans.add(e);
1360 }
1361 }
1362}
1363
1364static void qsg_addBackOrphanedElements(QDataBuffer<Element *> &orphans, QDataBuffer<Element *> &renderList)
1365{
1366 for (int i=0; i<orphans.size(); ++i) {
1367 Element *e = orphans.at(i);
1368 if (e->orphaned)
1369 renderList.add(e);
1370 }
1371 orphans.reset();
1372}
1373
1374/*
1375 * To rebuild the tagged roots, we start by putting all subroots of tagged
1376 * roots into the list of tagged roots. This is to make the rest of the
1377 * algorithm simpler.
1378 *
1379 * Second, we invalidate all batches which belong to tagged roots, which now
1380 * includes the entire subtree under a given root
1381 *
1382 * Then we call buildRenderLists for all tagged subroots which do not have
1383 * parents which are tagged, aka, we traverse only the topmosts roots.
1384 *
1385 * Then we sort the render lists based on their render order, to restore the
1386 * right order for rendering.
1387 */
1388void Renderer::buildRenderListsForTaggedRoots()
1389{
1390 // Flag any element that is currently in the render lists, but which
1391 // is not in a batch. This happens when we have a partial rebuild
1392 // in one sub tree while we have a BuildBatches change in another
1393 // isolated subtree. So that batch-building takes into account
1394 // these "orphaned" nodes, we flag them now. The ones under tagged
1395 // roots will be cleared again. The remaining ones are added into the
1396 // render lists so that they contain all visual nodes after the
1397 // function completes.
1398 qsg_addOrphanedElements(m_tmpOpaqueElements, m_opaqueRenderList);
1399 qsg_addOrphanedElements(m_tmpAlphaElements, m_alphaRenderList);
1400
1401 // Take a copy now, as we will be adding to this while traversing..
1402 QSet<Node *> roots = m_taggedRoots;
1403 for (QSet<Node *>::const_iterator it = roots.constBegin();
1404 it != roots.constEnd(); ++it) {
1405 tagSubRoots(*it);
1406 }
1407
1408 for (int i=0; i<m_opaqueBatches.size(); ++i) {
1409 Batch *b = m_opaqueBatches.at(i);
1410 if (m_taggedRoots.contains(b->root))
1411 invalidateAndRecycleBatch(b);
1412
1413 }
1414 for (int i=0; i<m_alphaBatches.size(); ++i) {
1415 Batch *b = m_alphaBatches.at(i);
1416 if (m_taggedRoots.contains(b->root))
1417 invalidateAndRecycleBatch(b);
1418 }
1419
1420 m_opaqueRenderList.reset();
1421 m_alphaRenderList.reset();
1422 int maxRenderOrder = m_nextRenderOrder;
1423 m_partialRebuild = true;
1424 // Traverse each root, assigning it
1425 for (QSet<Node *>::const_iterator it = m_taggedRoots.constBegin();
1426 it != m_taggedRoots.constEnd(); ++it) {
1427 Node *root = *it;
1428 BatchRootInfo *i = batchRootInfo(root);
1429 if ((!i->parentRoot || !m_taggedRoots.contains(i->parentRoot))
1430 && !nodeUpdater()->isNodeBlocked(root->sgNode, rootNode())) {
1431 m_nextRenderOrder = i->firstOrder;
1432 m_partialRebuildRoot = root->sgNode;
1433 buildRenderLists(root->sgNode);
1434 }
1435 }
1436 m_partialRebuild = false;
1437 m_partialRebuildRoot = nullptr;
1438 m_taggedRoots.clear();
1439 m_nextRenderOrder = qMax(m_nextRenderOrder, maxRenderOrder);
1440
1441 // Add orphaned elements back into the list and then sort it..
1442 qsg_addBackOrphanedElements(m_tmpOpaqueElements, m_opaqueRenderList);
1443 qsg_addBackOrphanedElements(m_tmpAlphaElements, m_alphaRenderList);
1444
1445 if (m_opaqueRenderList.size())
1446 std::sort(&m_opaqueRenderList.first(), &m_opaqueRenderList.last() + 1, qsg_sort_element_decreasing_order);
1447 if (m_alphaRenderList.size())
1448 std::sort(&m_alphaRenderList.first(), &m_alphaRenderList.last() + 1, qsg_sort_element_increasing_order);
1449
1450}
1451
1452void Renderer::buildRenderListsFromScratch()
1453{
1454 m_opaqueRenderList.reset();
1455 m_alphaRenderList.reset();
1456
1457 for (int i=0; i<m_opaqueBatches.size(); ++i)
1458 invalidateAndRecycleBatch(m_opaqueBatches.at(i));
1459 for (int i=0; i<m_alphaBatches.size(); ++i)
1460 invalidateAndRecycleBatch(m_alphaBatches.at(i));
1461 m_opaqueBatches.reset();
1462 m_alphaBatches.reset();
1463
1464 m_nextRenderOrder = 0;
1465
1466 buildRenderLists(rootNode());
1467}
1468
1469void Renderer::invalidateBatchAndOverlappingRenderOrders(Batch *batch)
1470{
1471 Q_ASSERT(batch);
1472 Q_ASSERT(batch->first);
1473
1474 if (m_renderOrderRebuildLower < 0 || batch->first->order < m_renderOrderRebuildLower)
1475 m_renderOrderRebuildLower = batch->first->order;
1476 if (m_renderOrderRebuildUpper < 0 || batch->lastOrderInBatch > m_renderOrderRebuildUpper)
1477 m_renderOrderRebuildUpper = batch->lastOrderInBatch;
1478
1479 batch->invalidate();
1480
1481 for (int i=0; i<m_alphaBatches.size(); ++i) {
1482 Batch *b = m_alphaBatches.at(i);
1483 if (b->first) {
1484 int bf = b->first->order;
1485 int bl = b->lastOrderInBatch;
1486 if (bl > m_renderOrderRebuildLower && bf < m_renderOrderRebuildUpper)
1487 b->invalidate();
1488 }
1489 }
1490
1491 m_rebuild |= BuildBatches;
1492}
1493
1494/* Clean up batches by making it a consecutive list of "valid"
1495 * batches and moving all invalidated batches to the batches pool.
1496 */
1497void Renderer::cleanupBatches(QDataBuffer<Batch *> *batches) {
1498 if (batches->size()) {
1499 std::stable_sort(&batches->first(), &batches->last() + 1, qsg_sort_batch_is_valid);
1500 int count = 0;
1501 while (count < batches->size() && batches->at(count)->first)
1502 ++count;
1503 for (int i=count; i<batches->size(); ++i)
1504 invalidateAndRecycleBatch(batches->at(i));
1505 batches->resize(count);
1506 }
1507}
1508
1509void Renderer::prepareOpaqueBatches()
1510{
1511 for (int i=m_opaqueRenderList.size() - 1; i >= 0; --i) {
1512 Element *ei = m_opaqueRenderList.at(i);
1513 if (!ei || ei->batch || ei->node->geometry()->vertexCount() == 0)
1514 continue;
1515 Batch *batch = newBatch();
1516 batch->first = ei;
1517 batch->root = ei->root;
1518 batch->isOpaque = true;
1519 batch->needsUpload = true;
1520 batch->positionAttribute = qsg_positionAttribute(ei->node->geometry());
1521
1522 m_opaqueBatches.add(batch);
1523
1524 ei->batch = batch;
1525 Element *next = ei;
1526
1527 QSGGeometryNode *gni = ei->node;
1528
1529 for (int j = i - 1; j >= 0; --j) {
1530 Element *ej = m_opaqueRenderList.at(j);
1531 if (!ej)
1532 continue;
1533 if (ej->root != ei->root)
1534 break;
1535 if (ej->batch || ej->node->geometry()->vertexCount() == 0)
1536 continue;
1537
1538 QSGGeometryNode *gnj = ej->node;
1539
1540 if (gni->clipList() == gnj->clipList()
1541 && gni->geometry()->drawingMode() == gnj->geometry()->drawingMode()
1542 && (gni->geometry()->drawingMode() != GL_LINES || gni->geometry()->lineWidth() == gnj->geometry()->lineWidth())
1543 && gni->geometry()->attributes() == gnj->geometry()->attributes()
1544 && gni->inheritedOpacity() == gnj->inheritedOpacity()
1545 && gni->activeMaterial()->type() == gnj->activeMaterial()->type()
1546 && gni->activeMaterial()->compare(gnj->activeMaterial()) == 0) {
1547 ej->batch = batch;
1548 next->nextInBatch = ej;
1549 next = ej;
1550 }
1551 }
1552
1553 batch->lastOrderInBatch = next->order;
1554 }
1555}
1556
1557bool Renderer::checkOverlap(int first, int last, const Rect &bounds)
1558{
1559 for (int i=first; i<=last; ++i) {
1560 Element *e = m_alphaRenderList.at(i);
1561 if (!e || e->batch)
1562 continue;
1563 Q_ASSERT(e->boundsComputed);
1564 if (e->bounds.intersects(bounds))
1565 return true;
1566 }
1567 return false;
1568}
1569
1570/*
1571 *
1572 * To avoid the O(n^2) checkOverlap check in most cases, we have the
1573 * overlapBounds which is the union of all bounding rects to check overlap
1574 * for. We know that if it does not overlap, then none of the individual
1575 * ones will either. For the typical list case, this results in no calls
1576 * to checkOverlap what-so-ever. This also ensures that when all consecutive
1577 * items are matching (such as a table of text), we don't build up an
1578 * overlap bounds and thus do not require full overlap checks.
1579 */
1580
1581void Renderer::prepareAlphaBatches()
1582{
1583 for (int i=0; i<m_alphaRenderList.size(); ++i) {
1584 Element *e = m_alphaRenderList.at(i);
1585 if (!e || e->isRenderNode)
1586 continue;
1587 Q_ASSERT(!e->removed);
1588 e->ensureBoundsValid();
1589 }
1590
1591 for (int i=0; i<m_alphaRenderList.size(); ++i) {
1592 Element *ei = m_alphaRenderList.at(i);
1593 if (!ei || ei->batch)
1594 continue;
1595
1596 if (ei->isRenderNode) {
1597 Batch *rnb = newBatch();
1598 rnb->first = ei;
1599 rnb->root = ei->root;
1600 rnb->isOpaque = false;
1601 rnb->isRenderNode = true;
1602 ei->batch = rnb;
1603 m_alphaBatches.add(rnb);
1604 continue;
1605 }
1606
1607 if (ei->node->geometry()->vertexCount() == 0)
1608 continue;
1609
1610 Batch *batch = newBatch();
1611 batch->first = ei;
1612 batch->root = ei->root;
1613 batch->isOpaque = false;
1614 batch->needsUpload = true;
1615 m_alphaBatches.add(batch);
1616 ei->batch = batch;
1617
1618 QSGGeometryNode *gni = ei->node;
1619 batch->positionAttribute = qsg_positionAttribute(gni->geometry());
1620
1621 Rect overlapBounds;
1622 overlapBounds.set(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
1623
1624 Element *next = ei;
1625
1626 for (int j = i + 1; j < m_alphaRenderList.size(); ++j) {
1627 Element *ej = m_alphaRenderList.at(j);
1628 if (!ej)
1629 continue;
1630 if (ej->root != ei->root || ej->isRenderNode)
1631 break;
1632 if (ej->batch)
1633 continue;
1634
1635 QSGGeometryNode *gnj = ej->node;
1636 if (gnj->geometry()->vertexCount() == 0)
1637 continue;
1638
1639 if (gni->clipList() == gnj->clipList()
1640 && gni->geometry()->drawingMode() == gnj->geometry()->drawingMode()
1641 && (gni->geometry()->drawingMode() != GL_LINES || gni->geometry()->lineWidth() == gnj->geometry()->lineWidth())
1642 && gni->geometry()->attributes() == gnj->geometry()->attributes()
1643 && gni->inheritedOpacity() == gnj->inheritedOpacity()
1644 && gni->activeMaterial()->type() == gnj->activeMaterial()->type()
1645 && gni->activeMaterial()->compare(gnj->activeMaterial()) == 0) {
1646 if (!overlapBounds.intersects(ej->bounds) || !checkOverlap(i+1, j - 1, ej->bounds)) {
1647 ej->batch = batch;
1648 next->nextInBatch = ej;
1649 next = ej;
1650 } else {
1651 /* When we come across a compatible element which hits an overlap, we
1652 * need to stop the batch right away. We cannot add more elements
1653 * to the current batch as they will be rendered before the batch that the
1654 * current 'ej' will be added to.
1655 */
1656 break;
1657 }
1658 } else {
1659 overlapBounds |= ej->bounds;
1660 }
1661 }
1662
1663 batch->lastOrderInBatch = next->order;
1664 }
1665
1666
1667}
1668
1669static inline int qsg_fixIndexCount(int iCount, GLenum drawMode) {
1670 switch (drawMode) {
1671 case GL_TRIANGLE_STRIP:
1672 // Merged triangle strips need to contain degenerate triangles at the beginning and end.
1673 // One could save 2 uploaded ushorts here by ditching the padding for the front of the
1674 // first and the end of the last, but for simplicity, we simply don't care.
1675 // Those extra triangles will be skipped while drawing to preserve the strip's parity
1676 // anyhow.
1677 return iCount + 2;
1678 case GL_LINES:
1679 // For lines we drop the last vertex if the number of vertices is uneven.
1680 return iCount - (iCount % 2);
1681 case GL_TRIANGLES:
1682 // For triangles we drop trailing vertices until the result is divisible by 3.
1683 return iCount - (iCount % 3);
1684 default:
1685 return iCount;
1686 }
1687}
1688
1689/* These parameters warrant some explanation...
1690 *
1691 * vaOffset: The byte offset into the vertex data to the location of the
1692 * 2D float point vertex attributes.
1693 *
1694 * vertexData: destination where the geometry's vertex data should go
1695 *
1696 * zData: destination of geometries injected Z positioning
1697 *
1698 * indexData: destination of the indices for this element
1699 *
1700 * iBase: The starting index for this element in the batch
1701 */
1702
1703void Renderer::uploadMergedElement(Element *e, int vaOffset, char **vertexData, char **zData, char **indexData, quint16 *iBase, int *indexCount)
1704{
1705 if (Q_UNLIKELY(debug_upload())) qDebug() << " - uploading element:" << e << e->node << (void *) *vertexData << (qintptr) (*zData - *vertexData) << (qintptr) (*indexData - *vertexData);
1706 QSGGeometry *g = e->node->geometry();
1707
1708 const QMatrix4x4 &localx = *e->node->matrix();
1709
1710 const int vCount = g->vertexCount();
1711 const int vSize = g->sizeOfVertex();
1712 memcpy(*vertexData, g->vertexData(), vSize * vCount);
1713
1714 // apply vertex transform..
1715 char *vdata = *vertexData + vaOffset;
1716 if (((const QMatrix4x4_Accessor &) localx).flagBits == 1) {
1717 for (int i=0; i<vCount; ++i) {
1718 Pt *p = (Pt *) vdata;
1719 p->x += ((const QMatrix4x4_Accessor &) localx).m[3][0];
1720 p->y += ((const QMatrix4x4_Accessor &) localx).m[3][1];
1721 vdata += vSize;
1722 }
1723 } else if (((const QMatrix4x4_Accessor &) localx).flagBits > 1) {
1724 for (int i=0; i<vCount; ++i) {
1725 ((Pt *) vdata)->map(localx);
1726 vdata += vSize;
1727 }
1728 }
1729
1730 if (m_useDepthBuffer) {
1731 float *vzorder = (float *) *zData;
1732 float zorder = 1.0f - e->order * m_zRange;
1733 for (int i=0; i<vCount; ++i)
1734 vzorder[i] = zorder;
1735 *zData += vCount * sizeof(float);
1736 }
1737
1738 int iCount = g->indexCount();
1739 quint16 *indices = (quint16 *) *indexData;
1740
1741 if (iCount == 0) {
1742 iCount = vCount;
1743 if (g->drawingMode() == GL_TRIANGLE_STRIP)
1744 *indices++ = *iBase;
1745 else
1746 iCount = qsg_fixIndexCount(iCount, g->drawingMode());
1747
1748 for (int i=0; i<iCount; ++i)
1749 indices[i] = *iBase + i;
1750 } else {
1751 const quint16 *srcIndices = g->indexDataAsUShort();
1752 if (g->drawingMode() == GL_TRIANGLE_STRIP)
1753 *indices++ = *iBase + srcIndices[0];
1754 else
1755 iCount = qsg_fixIndexCount(iCount, g->drawingMode());
1756
1757 for (int i=0; i<iCount; ++i)
1758 indices[i] = *iBase + srcIndices[i];
1759 }
1760 if (g->drawingMode() == GL_TRIANGLE_STRIP) {
1761 indices[iCount] = indices[iCount - 1];
1762 iCount += 2;
1763 }
1764
1765 *vertexData += vCount * vSize;
1766 *indexData += iCount * sizeof(quint16);
1767 *iBase += vCount;
1768 *indexCount += iCount;
1769}
1770
1771static QMatrix4x4 qsg_matrixForRoot(Node *node)
1772{
1773 if (node->type() == QSGNode::TransformNodeType)
1774 return static_cast<QSGTransformNode *>(node->sgNode)->combinedMatrix();
1775 Q_ASSERT(node->type() == QSGNode::ClipNodeType);
1776 QSGClipNode *c = static_cast<QSGClipNode *>(node->sgNode);
1777 return *c->matrix();
1778}
1779
1780void Renderer::uploadBatch(Batch *b)
1781{
1782 // Early out if nothing has changed in this batch..
1783 if (!b->needsUpload) {
1784 if (Q_UNLIKELY(debug_upload())) qDebug() << " Batch:" << b << "already uploaded...";
1785 return;
1786 }
1787
1788 if (!b->first) {
1789 if (Q_UNLIKELY(debug_upload())) qDebug() << " Batch:" << b << "is invalid...";
1790 return;
1791 }
1792
1793 if (b->isRenderNode) {
1794 if (Q_UNLIKELY(debug_upload())) qDebug() << " Batch: " << b << "is a render node...";
1795 return;
1796 }
1797
1798 // Figure out if we can merge or not, if not, then just render the batch as is..
1799 Q_ASSERT(b->first);
1800 Q_ASSERT(b->first->node);
1801
1802 QSGGeometryNode *gn = b->first->node;
1803 QSGGeometry *g = gn->geometry();
1804 QSGMaterial::Flags flags = gn->activeMaterial()->flags();
1805 bool canMerge = (g->drawingMode() == GL_TRIANGLES || g->drawingMode() == GL_TRIANGLE_STRIP ||
1806 g->drawingMode() == GL_LINES || g->drawingMode() == GL_POINTS)
1807 && b->positionAttribute >= 0
1808 && g->indexType() == GL_UNSIGNED_SHORT
1809 && (flags & (QSGMaterial::CustomCompileStep | QSGMaterial_FullMatrix)) == 0
1810 && ((flags & QSGMaterial::RequiresFullMatrixExceptTranslate) == 0 || b->isTranslateOnlyToRoot())
1811 && b->isSafeToBatch();
1812
1813 b->merged = canMerge;
1814
1815 // Figure out how much memory we need...
1816 b->vertexCount = 0;
1817 b->indexCount = 0;
1818 int unmergedIndexSize = 0;
1819 Element *e = b->first;
1820
1821 while (e) {
1822 QSGGeometry *eg = e->node->geometry();
1823 b->vertexCount += eg->vertexCount();
1824 int iCount = eg->indexCount();
1825 if (b->merged) {
1826 if (iCount == 0)
1827 iCount = eg->vertexCount();
1828 iCount = qsg_fixIndexCount(iCount, g->drawingMode());
1829 } else {
1830 unmergedIndexSize += iCount * eg->sizeOfIndex();
1831 }
1832 b->indexCount += iCount;
1833 e = e->nextInBatch;
1834 }
1835
1836 // Abort if there are no vertices in this batch.. We abort this late as
1837 // this is a broken usecase which we do not care to optimize for...
1838 if (b->vertexCount == 0 || (b->merged && b->indexCount == 0))
1839 return;
1840
1841 /* Allocate memory for this batch. Merged batches are divided into three separate blocks
1842 1. Vertex data for all elements, as they were in the QSGGeometry object, but
1843 with the tranform relative to this batch's root applied. The vertex data
1844 is otherwise unmodified.
1845 2. Z data for all elements, derived from each elements "render order".
1846 This is present for merged data only.
1847 3. Indices for all elements, as they were in the QSGGeometry object, but
1848 adjusted so that each index matches its.
1849 And for TRIANGLE_STRIPs, we need to insert degenerate between each
1850 primitive. These are unsigned shorts for merged and arbitrary for
1851 non-merged.
1852 */
1853 int bufferSize = b->vertexCount * g->sizeOfVertex();
1854 int ibufferSize = 0;
1855 if (b->merged) {
1856 ibufferSize = b->indexCount * sizeof(quint16);
1857 if (m_useDepthBuffer)
1858 bufferSize += b->vertexCount * sizeof(float);
1859 } else {
1860 ibufferSize = unmergedIndexSize;
1861 }
1862
1863 const bool separateIndexBuffer = m_context->separateIndexBuffer();
1864 if (separateIndexBuffer)
1865 map(&b->ibo, ibufferSize, true);
1866 else
1867 bufferSize += ibufferSize;
1868 map(&b->vbo, bufferSize);
1869
1870 if (Q_UNLIKELY(debug_upload())) qDebug() << " - batch" << b << " first:" << b->first << " root:"
1871 << b->root << " merged:" << b->merged << " positionAttribute" << b->positionAttribute
1872 << " vbo:" << b->vbo.id << ":" << b->vbo.size;
1873
1874 if (b->merged) {
1875 char *vertexData = b->vbo.data;
1876 char *zData = vertexData + b->vertexCount * g->sizeOfVertex();
1877 char *indexData = separateIndexBuffer
1878 ? b->ibo.data
1879 : zData + (int(m_useDepthBuffer) * b->vertexCount * sizeof(float));
1880
1881 quint16 iOffset = 0;
1882 e = b->first;
1883 int verticesInSet = 0;
1884 int indicesInSet = 0;
1885 b->drawSets.reset();
1886 int drawSetIndices = separateIndexBuffer ? 0 : indexData - vertexData;
1887 const auto indexBase = separateIndexBuffer ? b->ibo.data : b->vbo.data;
1888 b->drawSets << DrawSet(0, zData - vertexData, drawSetIndices);
1889 while (e) {
1890 verticesInSet += e->node->geometry()->vertexCount();
1891 if (verticesInSet > 0xffff) {
1892 b->drawSets.last().indexCount = indicesInSet;
1893 if (g->drawingMode() == GL_TRIANGLE_STRIP) {
1894 b->drawSets.last().indices += 1 * sizeof(quint16);
1895 b->drawSets.last().indexCount -= 2;
1896 }
1897 drawSetIndices = indexData - indexBase;
1898 b->drawSets << DrawSet(vertexData - b->vbo.data,
1899 zData - b->vbo.data,
1900 drawSetIndices);
1901 iOffset = 0;
1902 verticesInSet = e->node->geometry()->vertexCount();
1903 indicesInSet = 0;
1904 }
1905 uploadMergedElement(e, b->positionAttribute, &vertexData, &zData, &indexData, &iOffset, &indicesInSet);
1906 e = e->nextInBatch;
1907 }
1908 b->drawSets.last().indexCount = indicesInSet;
1909 // We skip the very first and very last degenerate triangles since they aren't needed
1910 // and the first one would reverse the vertex ordering of the merged strips.
1911 if (g->drawingMode() == GL_TRIANGLE_STRIP) {
1912 b->drawSets.last().indices += 1 * sizeof(quint16);
1913 b->drawSets.last().indexCount -= 2;
1914 }
1915 } else {
1916 char *vboData = b->vbo.data;
1917 char *iboData = separateIndexBuffer ? b->ibo.data
1918 : vboData + b->vertexCount * g->sizeOfVertex();
1919 Element *e = b->first;
1920 while (e) {
1921 QSGGeometry *g = e->node->geometry();
1922 int vbs = g->vertexCount() * g->sizeOfVertex();
1923 memcpy(vboData, g->vertexData(), vbs);
1924 vboData = vboData + vbs;
1925 if (g->indexCount()) {
1926 int ibs = g->indexCount() * g->sizeOfIndex();
1927 memcpy(iboData, g->indexData(), ibs);
1928 iboData += ibs;
1929 }
1930 e = e->nextInBatch;
1931 }
1932 }
1933#ifndef QT_NO_DEBUG_OUTPUT
1934 if (Q_UNLIKELY(debug_upload())) {
1935 const char *vd = b->vbo.data;
1936 qDebug() << " -- Vertex Data, count:" << b->vertexCount << " - " << g->sizeOfVertex() << "bytes/vertex";
1937 for (int i=0; i<b->vertexCount; ++i) {
1938 QDebug dump = qDebug().nospace();
1939 dump << " --- " << i << ": ";
1940 int offset = 0;
1941 for (int a=0; a<g->attributeCount(); ++a) {
1942 const QSGGeometry::Attribute &attr = g->attributes()[a];
1943 dump << attr.position << ":(" << attr.tupleSize << ",";
1944 if (attr.type == GL_FLOAT) {
1945 dump << "float ";
1946 if (attr.isVertexCoordinate)
1947 dump << "* ";
1948 for (int t=0; t<attr.tupleSize; ++t)
1949 dump << *(const float *)(vd + offset + t * sizeof(float)) << " ";
1950 } else if (attr.type == GL_UNSIGNED_BYTE) {
1951 dump << "ubyte ";
1952 for (int t=0; t<attr.tupleSize; ++t)
1953 dump << *(const unsigned char *)(vd + offset + t * sizeof(unsigned char)) << " ";
1954 }
1955 dump << ") ";
1956 offset += attr.tupleSize * size_of_type(attr.type);
1957 }
1958 if (b->merged && m_useDepthBuffer) {
1959 float zorder = ((float*)(b->vbo.data + b->vertexCount * g->sizeOfVertex()))[i];
1960 dump << " Z:(" << zorder << ")";
1961 }
1962 vd += g->sizeOfVertex();
1963 }
1964
1965 if (!b->drawSets.isEmpty()) {
1966 const quint16 *id = (const quint16 *)(separateIndexBuffer
1967 ? b->ibo.data
1968 : b->vbo.data + b->drawSets.at(0).indices);
1969 {
1970 QDebug iDump = qDebug();
1971 iDump << " -- Index Data, count:" << b->indexCount;
1972 for (int i=0; i<b->indexCount; ++i) {
1973 if ((i % 24) == 0)
1974 iDump << endl << " --- ";
1975 iDump << id[i];
1976 }
1977 }
1978
1979 for (int i=0; i<b->drawSets.size(); ++i) {
1980 const DrawSet &s = b->drawSets.at(i);
1981 qDebug() << " -- DrawSet: indexCount:" << s.indexCount << " vertices:" << s.vertices << " z:" << s.zorders << " indices:" << s.indices;
1982 }
1983 }
1984 }
1985#endif // QT_NO_DEBUG_OUTPUT
1986
1987 unmap(&b->vbo);
1988 if (separateIndexBuffer)
1989 unmap(&b->ibo, true);
1990
1991 if (Q_UNLIKELY(debug_upload())) qDebug() << " --- vertex/index buffers unmapped, batch upload completed...";
1992
1993 b->needsUpload = false;
1994
1995 if (Q_UNLIKELY(debug_render()))
1996 b->uploadedThisFrame = true;
1997}
1998
1999/*!
2000 * Convenience function to set up the stencil buffer for clipping based on \a clip.
2001 *
2002 * If the clip is a pixel aligned rectangle, this function will use glScissor instead
2003 * of stencil.
2004 */
2005Renderer::ClipType Renderer::updateStencilClip(const QSGClipNode *clip)
2006{
2007 if (!clip) {
2008 glDisable(GL_STENCIL_TEST);
2009 glDisable(GL_SCISSOR_TEST);
2010 return NoClip;
2011 }
2012
2013 ClipType clipType = NoClip;
2014 GLuint vbo = 0;
2015 int vboSize = 0;
2016
2017 bool useVBO = false;
2018 QOpenGLContext *ctx = m_context->openglContext();
2019 QSurfaceFormat::OpenGLContextProfile profile = ctx->format().profile();
2020
2021 if (!ctx->isOpenGLES() && profile == QSurfaceFormat::CoreProfile) {
2022 // VBO are more expensive, so only use them if we must.
2023 useVBO = true;
2024 }
2025
2026 glDisable(GL_SCISSOR_TEST);
2027
2028 m_currentStencilValue = 0;
2029 m_currentScissorRect = QRect();
2030 while (clip) {
2031 QMatrix4x4 m = m_current_projection_matrix;
2032 if (clip->matrix())
2033 m *= *clip->matrix();
2034
2035 // TODO: Check for multisampling and pixel grid alignment.
2036 bool isRectangleWithNoPerspective = clip->isRectangular()
2037 && qFuzzyIsNull(m(3, 0)) && qFuzzyIsNull(m(3, 1));
2038 bool noRotate = qFuzzyIsNull(m(0, 1)) && qFuzzyIsNull(m(1, 0));
2039 bool isRotate90 = qFuzzyIsNull(m(0, 0)) && qFuzzyIsNull(m(1, 1));
2040
2041 if (isRectangleWithNoPerspective && (noRotate || isRotate90)) {
2042 QRectF bbox = clip->clipRect();
2043 qreal invW = 1 / m(3, 3);
2044 qreal fx1, fy1, fx2, fy2;
2045 if (noRotate) {
2046 fx1 = (bbox.left() * m(0, 0) + m(0, 3)) * invW;
2047 fy1 = (bbox.bottom() * m(1, 1) + m(1, 3)) * invW;
2048 fx2 = (bbox.right() * m(0, 0) + m(0, 3)) * invW;
2049 fy2 = (bbox.top() * m(1, 1) + m(1, 3)) * invW;
2050 } else {
2051 Q_ASSERT(isRotate90);
2052 fx1 = (bbox.bottom() * m(0, 1) + m(0, 3)) * invW;
2053 fy1 = (bbox.left() * m(1, 0) + m(1, 3)) * invW;
2054 fx2 = (bbox.top() * m(0, 1) + m(0, 3)) * invW;
2055 fy2 = (bbox.right() * m(1, 0) + m(1, 3)) * invW;
2056 }
2057
2058 if (fx1 > fx2)
2059 qSwap(fx1, fx2);
2060 if (fy1 > fy2)
2061 qSwap(fy1, fy2);
2062
2063 QRect deviceRect = this->deviceRect();
2064
2065 GLint ix1 = qRound((fx1 + 1) * deviceRect.width() * qreal(0.5));
2066 GLint iy1 = qRound((fy1 + 1) * deviceRect.height() * qreal(0.5));
2067 GLint ix2 = qRound((fx2 + 1) * deviceRect.width() * qreal(0.5));
2068 GLint iy2 = qRound((fy2 + 1) * deviceRect.height() * qreal(0.5));
2069
2070 if (!(clipType & ScissorClip)) {
2071 m_currentScissorRect = QRect(ix1, iy1, ix2 - ix1, iy2 - iy1);
2072 glEnable(GL_SCISSOR_TEST);
2073 clipType |= ScissorClip;
2074 } else {
2075 m_currentScissorRect &= QRect(ix1, iy1, ix2 - ix1, iy2 - iy1);
2076 }
2077 glScissor(m_currentScissorRect.x(), m_currentScissorRect.y(),
2078 m_currentScissorRect.width(), m_currentScissorRect.height());
2079 } else {
2080 if (!(clipType & StencilClip)) {
2081 if (!m_clipProgram.isLinked()) {
2082 QSGShaderSourceBuilder::initializeProgramFromFiles(
2083 &m_clipProgram,
2084 QStringLiteral(":/qt-project.org/scenegraph/shaders/stencilclip.vert"),
2085 QStringLiteral(":/qt-project.org/scenegraph/shaders/stencilclip.frag"));
2086 m_clipProgram.bindAttributeLocation("vCoord", 0);
2087 m_clipProgram.link();
2088 m_clipMatrixId = m_clipProgram.uniformLocation("matrix");
2089 }
2090
2091 glClearStencil(0);
2092 glClear(GL_STENCIL_BUFFER_BIT);
2093 glEnable(GL_STENCIL_TEST);
2094 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
2095 glDepthMask(GL_FALSE);
2096
2097 m_clipProgram.bind();
2098 m_clipProgram.enableAttributeArray(0);
2099
2100 clipType |= StencilClip;
2101 }
2102
2103 glStencilFunc(GL_EQUAL, m_currentStencilValue, 0xff); // stencil test, ref, test mask
2104 glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); // stencil fail, z fail, z pass
2105
2106 const QSGGeometry *g = clip->geometry();
2107 Q_ASSERT(g->attributeCount() > 0);
2108 const QSGGeometry::Attribute *a = g->attributes();
2109
2110 const GLvoid *pointer;
2111 if (!useVBO) {
2112 pointer = g->vertexData();
2113 } else {
2114 if (!vbo)
2115 glGenBuffers(1, &vbo);
2116
2117 glBindBuffer(GL_ARRAY_BUFFER, vbo);
2118
2119 const int vertexByteSize = g->sizeOfVertex() * g->vertexCount();
2120 if (vboSize < vertexByteSize) {
2121 vboSize = vertexByteSize;
2122 glBufferData(GL_ARRAY_BUFFER, vertexByteSize, g->vertexData(), GL_STATIC_DRAW);
2123 } else {
2124 glBufferSubData(GL_ARRAY_BUFFER, 0, vertexByteSize, g->vertexData());
2125 }
2126
2127 pointer = nullptr;
2128 }
2129
2130 glVertexAttribPointer(0, a->tupleSize, a->type, GL_FALSE, g->sizeOfVertex(), pointer);
2131
2132 m_clipProgram.setUniformValue(m_clipMatrixId, m);
2133 if (g->indexCount()) {
2134 glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), g->indexData());
2135 } else {
2136 glDrawArrays(g->drawingMode(), 0, g->vertexCount());
2137 }
2138
2139 if (useVBO)
2140 glBindBuffer(GL_ARRAY_BUFFER, 0);
2141
2142 ++m_currentStencilValue;
2143 }
2144
2145 clip = clip->clipList();
2146 }
2147
2148 if (vbo)
2149 glDeleteBuffers(1, &vbo);
2150
2151 if (clipType & StencilClip) {
2152 m_clipProgram.disableAttributeArray(0);
2153 glStencilFunc(GL_EQUAL, m_currentStencilValue, 0xff); // stencil test, ref, test mask
2154 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // stencil fail, z fail, z pass
2155 bindable()->reactivate();
2156 } else {
2157 glDisable(GL_STENCIL_TEST);
2158 }
2159
2160 return clipType;
2161}
2162
2163void Renderer::updateClip(const QSGClipNode *clipList, const Batch *batch)
2164{
2165 if (clipList != m_currentClip && Q_LIKELY(!debug_noclip())) {
2166 m_currentClip = clipList;
2167 // updateClip sets another program, so force-reactivate our own
2168 if (m_currentShader)
2169 setActiveShader(nullptr, nullptr);
2170 glBindBuffer(GL_ARRAY_BUFFER, 0);
2171 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
2172 if (batch->isOpaque)
2173 glDisable(GL_DEPTH_TEST);
2174 m_currentClipType = updateStencilClip(m_currentClip);
2175 if (batch->isOpaque) {
2176 glEnable(GL_DEPTH_TEST);
2177 if (m_currentClipType & StencilClip)
2178 glDepthMask(true);
2179 }
2180 }
2181}
2182
2183/*!
2184 * Look at the attribute arrays and potentially the injected z attribute to figure out
2185 * which vertex attribute arrays need to be enabled and not. Then update the current
2186 * Shader and current QSGMaterialShader.
2187 */
2188void Renderer::setActiveShader(QSGMaterialShader *program, ShaderManager::Shader *shader)
2189{
2190 const char * const *c = m_currentProgram ? m_currentProgram->attributeNames() : nullptr;
2191 const char * const *n = program ? program->attributeNames() : nullptr;
2192
2193 int cza = m_currentShader ? m_currentShader->pos_order : -1;
2194 int nza = shader ? shader->pos_order : -1;
2195
2196 int i = 0;
2197 while (c || n) {
2198
2199 bool was = c;
2200 if (cza == i) {
2201 was = true;
2202 c = nullptr;
2203 } else if (c && !c[i]) { // end of the attribute array names
2204 c = nullptr;
2205 was = false;
2206 }
2207
2208 bool is = n;
2209 if (nza == i) {
2210 is = true;
2211 n = nullptr;
2212 } else if (n && !n[i]) {
2213 n = nullptr;
2214 is = false;
2215 }
2216
2217 if (is && !was)
2218 glEnableVertexAttribArray(i);
2219 else if (was && !is)
2220 glDisableVertexAttribArray(i);
2221
2222 ++i;
2223 }
2224
2225 if (m_currentProgram)
2226 m_currentProgram->deactivate();
2227 m_currentProgram = program;
2228 m_currentShader = shader;
2229 m_currentMaterial = nullptr;
2230 if (m_currentProgram) {
2231 m_currentProgram->program()->bind();
2232 m_currentProgram->activate();
2233 }
2234}
2235
2236void Renderer::renderMergedBatch(const Batch *batch)
2237{
2238 if (batch->vertexCount == 0 || batch->indexCount == 0)
2239 return;
2240
2241 Element *e = batch->first;
2242 Q_ASSERT(e);
2243
2244#ifndef QT_NO_DEBUG_OUTPUT
2245 if (Q_UNLIKELY(debug_render())) {
2246 QDebug debug = qDebug();
2247 debug << " -"
2248 << batch
2249 << (batch->uploadedThisFrame ? "[ upload]" : "[retained]")
2250 << (e->node->clipList() ? "[ clip]" : "[noclip]")
2251 << (batch->isOpaque ? "[opaque]" : "[ alpha]")
2252 << "[ merged]"
2253 << " Nodes:" << QString::fromLatin1("%1").arg(qsg_countNodesInBatch(batch), 4).toLatin1().constData()
2254 << " Vertices:" << QString::fromLatin1("%1").arg(batch->vertexCount, 5).toLatin1().constData()
2255 << " Indices:" << QString::fromLatin1("%1").arg(batch->indexCount, 5).toLatin1().constData()
2256 << " root:" << batch->root;
2257 if (batch->drawSets.size() > 1)
2258 debug << "sets:" << batch->drawSets.size();
2259 if (!batch->isOpaque)
2260 debug << "opacity:" << e->node->inheritedOpacity();
2261 batch->uploadedThisFrame = false;
2262 }
2263#endif
2264
2265 QSGGeometryNode *gn = e->node;
2266
2267 // We always have dirty matrix as all batches are at a unique z range.
2268 QSGMaterialShader::RenderState::DirtyStates dirty = QSGMaterialShader::RenderState::DirtyMatrix;
2269 if (batch->root)
2270 m_current_model_view_matrix = qsg_matrixForRoot(batch->root);
2271 else
2272 m_current_model_view_matrix.setToIdentity();
2273 m_current_determinant = m_current_model_view_matrix.determinant();
2274 m_current_projection_matrix = projectionMatrix(); // has potentially been changed by renderUnmergedBatch..
2275
2276 // updateClip() uses m_current_projection_matrix.
2277 updateClip(gn->clipList(), batch);
2278
2279 glBindBuffer(GL_ARRAY_BUFFER, batch->vbo.id);
2280
2281 char *indexBase = nullptr;
2282 const Buffer *indexBuf = m_context->separateIndexBuffer() ? &batch->ibo : &batch->vbo;
2283 if (m_context->hasBrokenIndexBufferObjects()) {
2284 indexBase = indexBuf->data;
2285 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
2286 } else {
2287 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuf->id);
2288 }
2289
2290
2291 QSGMaterial *material = gn->activeMaterial();
2292 ShaderManager::Shader *sms = m_useDepthBuffer ? m_shaderManager->prepareMaterial(material) : m_shaderManager->prepareMaterialNoRewrite(material);
2293 if (!sms)
2294 return;
2295 QSGMaterialShader *program = sms->program;
2296
2297 if (m_currentShader != sms)
2298 setActiveShader(program, sms);
2299
2300 m_current_opacity = gn->inheritedOpacity();
2301 if (!qFuzzyCompare(sms->lastOpacity, float(m_current_opacity))) {
2302 dirty |= QSGMaterialShader::RenderState::DirtyOpacity;
2303 sms->lastOpacity = m_current_opacity;
2304 }
2305
2306 program->updateState(state(dirty), material, m_currentMaterial);
2307
2308#ifndef QT_NO_DEBUG
2309 if (qsg_test_and_clear_material_failure()) {
2310 qDebug("QSGMaterial::updateState triggered an error (merged), batch will be skipped:");
2311 Element *ee = e;
2312 while (ee) {
2313 qDebug() << " -" << ee->node;
2314 ee = ee->nextInBatch;
2315 }
2316 QSGNodeDumper::dump(rootNode());
2317 qFatal("Aborting: scene graph is invalid...");
2318 }
2319#endif
2320
2321 m_currentMaterial = material;
2322
2323 QSGGeometry* g = gn->geometry();
2324 updateLineWidth(g);
2325 char const *const *attrNames = program->attributeNames();
2326 for (int i=0; i<batch->drawSets.size(); ++i) {
2327 const DrawSet &draw = batch->drawSets.at(i);
2328 int offset = 0;
2329 for (int j = 0; attrNames[j]; ++j) {
2330 if (!*attrNames[j])
2331 continue;
2332 const QSGGeometry::Attribute &a = g->attributes()[j];
2333 GLboolean normalize = a.type != GL_FLOAT && a.type != GL_DOUBLE;
2334 glVertexAttribPointer(a.position, a.tupleSize, a.type, normalize, g->sizeOfVertex(), (void *) (qintptr) (offset + draw.vertices));
2335 offset += a.tupleSize * size_of_type(a.type);
2336 }
2337 if (m_useDepthBuffer)
2338 glVertexAttribPointer(sms->pos_order, 1, GL_FLOAT, false, 0, (void *) (qintptr) (draw.zorders));
2339
2340 glDrawElements(g->drawingMode(), draw.indexCount, GL_UNSIGNED_SHORT, (void *) (qintptr) (indexBase + draw.indices));
2341 }
2342}
2343
2344void Renderer::renderUnmergedBatch(const Batch *batch)
2345{
2346 if (batch->vertexCount == 0)
2347 return;
2348
2349 Element *e = batch->first;
2350 Q_ASSERT(e);
2351
2352 if (Q_UNLIKELY(debug_render())) {
2353 qDebug() << " -"
2354 << batch
2355 << (batch->uploadedThisFrame ? "[ upload]" : "[retained]")
2356 << (e->node->clipList() ? "[ clip]" : "[noclip]")
2357 << (batch->isOpaque ? "[opaque]" : "[ alpha]")
2358 << "[unmerged]"
2359 << " Nodes:" << QString::fromLatin1("%1").arg(qsg_countNodesInBatch(batch), 4).toLatin1().constData()
2360 << " Vertices:" << QString::fromLatin1("%1").arg(batch->vertexCount, 5).toLatin1().constData()
2361 << " Indices:" << QString::fromLatin1("%1").arg(batch->indexCount, 5).toLatin1().constData()
2362 << " root:" << batch->root;
2363
2364 batch->uploadedThisFrame = false;
2365 }
2366
2367 QSGGeometryNode *gn = e->node;
2368
2369 m_current_projection_matrix = projectionMatrix();
2370 updateClip(gn->clipList(), batch);
2371
2372 glBindBuffer(GL_ARRAY_BUFFER, batch->vbo.id);
2373 char *indexBase = nullptr;
2374 const auto separateIndexBuffer = m_context->separateIndexBuffer();
2375 const Buffer *indexBuf = separateIndexBuffer ? &batch->ibo : &batch->vbo;
2376 if (batch->indexCount) {
2377 if (m_context->hasBrokenIndexBufferObjects()) {
2378 indexBase = indexBuf->data;
2379 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
2380 } else {
2381 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuf->id);
2382 }
2383 }
2384
2385 // We always have dirty matrix as all batches are at a unique z range.
2386 QSGMaterialShader::RenderState::DirtyStates dirty = QSGMaterialShader::RenderState::DirtyMatrix;
2387
2388 QSGMaterial *material = gn->activeMaterial();
2389 ShaderManager::Shader *sms = m_shaderManager->prepareMaterialNoRewrite(material);
2390 if (!sms)
2391 return;
2392 QSGMaterialShader *program = sms->program;
2393
2394 if (sms != m_currentShader)
2395 setActiveShader(program, sms);
2396
2397 m_current_opacity = gn->inheritedOpacity();
2398 if (sms->lastOpacity != m_current_opacity) {
2399 dirty |= QSGMaterialShader::RenderState::DirtyOpacity;
2400 sms->lastOpacity = m_current_opacity;
2401 }
2402
2403 int vOffset = 0;
2404 char *iOffset = indexBase;
2405 if (!separateIndexBuffer)
2406 iOffset += batch->vertexCount * gn->geometry()->sizeOfVertex();
2407
2408 QMatrix4x4 rootMatrix = batch->root ? qsg_matrixForRoot(batch->root) : QMatrix4x4();
2409
2410 while (e) {
2411 gn = e->node;
2412
2413 m_current_model_view_matrix = rootMatrix * *gn->matrix();
2414 m_current_determinant = m_current_model_view_matrix.determinant();
2415
2416 m_current_projection_matrix = projectionMatrix();
2417 if (m_useDepthBuffer) {
2418 m_current_projection_matrix(2, 2) = m_zRange;
2419 m_current_projection_matrix(2, 3) = 1.0f - e->order * m_zRange;
2420 }
2421
2422 program->updateState(state(dirty), material, m_currentMaterial);
2423
2424#ifndef QT_NO_DEBUG
2425 if (qsg_test_and_clear_material_failure()) {
2426 qDebug("QSGMaterial::updateState() triggered an error (unmerged), batch will be skipped:");
2427 qDebug() << " - offending node is" << e->node;
2428 QSGNodeDumper::dump(rootNode());
2429 qFatal("Aborting: scene graph is invalid...");
2430 return;
2431 }
2432#endif
2433
2434 // We don't need to bother with asking each node for its material as they
2435 // are all identical (compare==0) since they are in the same batch.
2436 m_currentMaterial = material;
2437
2438 QSGGeometry* g = gn->geometry();
2439 char const *const *attrNames = program->attributeNames();
2440 int offset = 0;
2441 for (int j = 0; attrNames[j]; ++j) {
2442 if (!*attrNames[j])
2443 continue;
2444 const QSGGeometry::Attribute &a = g->attributes()[j];
2445 GLboolean normalize = a.type != GL_FLOAT && a.type != GL_DOUBLE;
2446 glVertexAttribPointer(a.position, a.tupleSize, a.type, normalize, g->sizeOfVertex(), (void *) (qintptr) (offset + vOffset));
2447 offset += a.tupleSize * size_of_type(a.type);
2448 }
2449
2450 updateLineWidth(g);
2451 if (g->indexCount())
2452 glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), iOffset);
2453 else
2454 glDrawArrays(g->drawingMode(), 0, g->vertexCount());
2455
2456 vOffset += g->sizeOfVertex() * g->vertexCount();
2457 iOffset += g->indexCount() * g->sizeOfIndex();
2458
2459 // We only need to push this on the very first iteration...
2460 dirty &= ~QSGMaterialShader::RenderState::DirtyOpacity;
2461
2462 e = e->nextInBatch;
2463 }
2464}
2465
2466void Renderer::updateLineWidth(QSGGeometry *g)
2467{
2468 if (g->drawingMode() == GL_LINE_STRIP || g->drawingMode() == GL_LINE_LOOP || g->drawingMode() == GL_LINES)
2469 glLineWidth(g->lineWidth());
2470#if !defined(QT_OPENGL_ES_2)
2471 else {
2472 QOpenGLContext *ctx = m_context->openglContext();
2473 if (!ctx->isOpenGLES() && g->drawingMode() == GL_POINTS) {
2474 QOpenGLFunctions_1_0 *gl1funcs = nullptr;
2475 QOpenGLFunctions_3_2_Core *gl3funcs = nullptr;
2476 if (ctx->format().profile() == QSurfaceFormat::CoreProfile)
2477 gl3funcs = ctx->versionFunctions<QOpenGLFunctions_3_2_Core>();
2478 else
2479 gl1funcs = ctx->versionFunctions<QOpenGLFunctions_1_0>();
2480 Q_ASSERT(gl1funcs || gl3funcs);
2481 if (gl1funcs)
2482 gl1funcs->glPointSize(g->lineWidth());
2483 else
2484 gl3funcs->glPointSize(g->lineWidth());
2485 }
2486 }
2487#endif
2488}
2489
2490void Renderer::renderBatches()
2491{
2492 if (Q_UNLIKELY(debug_render())) {
2493 qDebug().nospace() << "Rendering:" << endl
2494 << " -> Opaque: " << qsg_countNodesInBatches(m_opaqueBatches) << " nodes in " << m_opaqueBatches.size() << " batches..." << endl
2495 << " -> Alpha: " << qsg_countNodesInBatches(m_alphaBatches) << " nodes in " << m_alphaBatches.size() << " batches...";
2496 }
2497
2498 QRect r = viewportRect();
2499 glViewport(r.x(), deviceRect().bottom() - r.bottom(), r.width(), r.height());
2500 glClearColor(clearColor().redF(), clearColor().greenF(), clearColor().blueF(), clearColor().alphaF());
2501
2502 if (m_useDepthBuffer) {
2503 glClearDepthf(1); // calls glClearDepth() under the hood for desktop OpenGL
2504 glEnable(GL_DEPTH_TEST);
2505 glDepthFunc(GL_LESS);
2506 glDepthMask(true);
2507 glDisable(GL_BLEND);
2508 } else {
2509 glDisable(GL_DEPTH_TEST);
2510 glDepthMask(false);
2511 }
2512 glDisable(GL_CULL_FACE);
2513 glColorMask(true, true, true, true);
2514 glDisable(GL_SCISSOR_TEST);
2515 glDisable(GL_STENCIL_TEST);
2516
2517 bindable()->clear(clearMode());
2518
2519 m_current_opacity = 1;
2520 m_currentMaterial = nullptr;
2521 m_currentShader = nullptr;
2522 m_currentProgram = nullptr;
2523 m_currentClip = nullptr;
2524
2525 bool renderOpaque = !debug_noopaque();
2526 bool renderAlpha = !debug_noalpha();
2527
2528 if (Q_LIKELY(renderOpaque)) {
2529 for (int i=0; i<m_opaqueBatches.size(); ++i) {
2530 Batch *b = m_opaqueBatches.at(i);
2531 if (b->merged)
2532 renderMergedBatch(b);
2533 else
2534 renderUnmergedBatch(b);
2535 }
2536 }
2537
2538 glEnable(GL_BLEND);
2539 if (m_useDepthBuffer)
2540 glDepthMask(false);
2541 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
2542
2543 if (Q_LIKELY(renderAlpha)) {
2544 for (int i=0; i<m_alphaBatches.size(); ++i) {
2545 Batch *b = m_alphaBatches.at(i);
2546 if (b->merged)
2547 renderMergedBatch(b);
2548 else if (b->isRenderNode)
2549 renderRenderNode(b);
2550 else
2551 renderUnmergedBatch(b);
2552 }
2553 }
2554
2555 if (m_currentShader)
2556 setActiveShader(nullptr, nullptr);
2557 updateStencilClip(nullptr);
2558 glBindBuffer(GL_ARRAY_BUFFER, 0);
2559 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
2560 glDepthMask(true);
2561}
2562
2563void Renderer::deleteRemovedElements()
2564{
2565 if (!m_elementsToDelete.size())
2566 return;
2567
2568 for (int i=0; i<m_opaqueRenderList.size(); ++i) {
2569 Element **e = m_opaqueRenderList.data() + i;
2570 if (*e && (*e)->removed)
2571 *e = nullptr;
2572 }
2573 for (int i=0; i<m_alphaRenderList.size(); ++i) {
2574 Element **e = m_alphaRenderList.data() + i;
2575 if (*e && (*e)->removed)
2576 *e = nullptr;
2577 }
2578
2579 for (int i=0; i<m_elementsToDelete.size(); ++i) {
2580 Element *e = m_elementsToDelete.at(i);
2581 if (e->isRenderNode)
2582 delete static_cast<RenderNodeElement *>(e);
2583 else
2584 m_elementAllocator.release(e);
2585 }
2586 m_elementsToDelete.reset();
2587}
2588
2589void Renderer::render()
2590{
2591 Q_ASSERT(m_context->openglContext() == QOpenGLContext::currentContext());
2592
2593 if (Q_UNLIKELY(debug_dump())) {
2594 qDebug("\n");
2595 QSGNodeDumper::dump(rootNode());
2596 }
2597
2598 QElapsedTimer timer;
2599 quint64 timeRenderLists = 0;
2600 quint64 timePrepareOpaque = 0;
2601 quint64 timePrepareAlpha = 0;
2602 quint64 timeSorting = 0;
2603 quint64 timeUploadOpaque = 0;
2604 quint64 timeUploadAlpha = 0;
2605
2606 if (Q_UNLIKELY(debug_render() || debug_build())) {
2607 QByteArray type("rebuild:");
2608 if (m_rebuild == 0)
2609 type += " none";
2610 if (m_rebuild == FullRebuild)
2611 type += " full";
2612 else {
2613 if (m_rebuild & BuildRenderLists)
2614 type += " renderlists";
2615 else if (m_rebuild & BuildRenderListsForTaggedRoots)
2616 type += " partial";
2617 else if (m_rebuild & BuildBatches)
2618 type += " batches";
2619 }
2620
2621 qDebug() << "Renderer::render()" << this << type;
2622 timer.start();
2623 }
2624
2625 if (m_vao)
2626 m_vao->bind();
2627
2628 if (m_rebuild & (BuildRenderLists | BuildRenderListsForTaggedRoots)) {
2629 bool complete = (m_rebuild & BuildRenderLists) != 0;
2630 if (complete)
2631 buildRenderListsFromScratch();
2632 else
2633 buildRenderListsForTaggedRoots();
2634 m_rebuild |= BuildBatches;
2635
2636 if (Q_UNLIKELY(debug_build())) {
2637 qDebug("Opaque render lists %s:", (complete ? "(complete)" : "(partial)"));
2638 for (int i=0; i<m_opaqueRenderList.size(); ++i) {
2639 Element *e = m_opaqueRenderList.at(i);
2640 qDebug() << " - element:" << e << " batch:" << e->batch << " node:" << e->node << " order:" << e->order;
2641 }
2642 qDebug("Alpha render list %s:", complete ? "(complete)" : "(partial)");
2643 for (int i=0; i<m_alphaRenderList.size(); ++i) {
2644 Element *e = m_alphaRenderList.at(i);
2645 qDebug() << " - element:" << e << " batch:" << e->batch << " node:" << e->node << " order:" << e->order;
2646 }
2647 }
2648 }
2649 if (Q_UNLIKELY(debug_render())) timeRenderLists = timer.restart();
2650
2651 for (int i=0; i<m_opaqueBatches.size(); ++i)
2652 m_opaqueBatches.at(i)->cleanupRemovedElements();
2653 for (int i=0; i<m_alphaBatches.size(); ++i)
2654 m_alphaBatches.at(i)->cleanupRemovedElements();
2655 deleteRemovedElements();
2656
2657 cleanupBatches(&m_opaqueBatches);
2658 cleanupBatches(&m_alphaBatches);
2659
2660 if (m_rebuild & BuildBatches) {
2661 prepareOpaqueBatches();
2662 if (Q_UNLIKELY(debug_render())) timePrepareOpaque = timer.restart();
2663 prepareAlphaBatches();
2664 if (Q_UNLIKELY(debug_render())) timePrepareAlpha = timer.restart();
2665
2666 if (Q_UNLIKELY(debug_build())) {
2667 qDebug("Opaque Batches:");
2668 for (int i=0; i<m_opaqueBatches.size(); ++i) {
2669 Batch *b = m_opaqueBatches.at(i);
2670 qDebug() << " - Batch " << i << b << (b->needsUpload ? "upload" : "") << " root:" << b->root;
2671 for (Element *e = b->first; e; e = e->nextInBatch) {
2672 qDebug() << " - element:" << e << " node:" << e->node << e->order;
2673 }
2674 }
2675 qDebug("Alpha Batches:");
2676 for (int i=0; i<m_alphaBatches.size(); ++i) {
2677 Batch *b = m_alphaBatches.at(i);
2678 qDebug() << " - Batch " << i << b << (b->needsUpload ? "upload" : "") << " root:" << b->root;
2679 for (Element *e = b->first; e; e = e->nextInBatch) {
2680