1/****************************************************************************
2**
3** Copyright (C) 2019 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 "qsgmaterialrhishader_p.h"
61
62#include "qsgopenglvisualizer_p.h"
63#include "qsgrhivisualizer_p.h"
64
65#include <qtquick_tracepoints_p.h>
66
67#include <algorithm>
68
69#ifndef GL_DOUBLE
70 #define GL_DOUBLE 0x140A
71#endif
72
73QT_BEGIN_NAMESPACE
74
75#ifndef QT_NO_DEBUG
76Q_QUICK_PRIVATE_EXPORT bool qsg_test_and_clear_material_failure();
77#endif
78
79extern QByteArray qsgShaderRewriter_insertZAttributes(const char *input, QSurfaceFormat::OpenGLContextProfile profile);
80
81int qt_sg_envInt(const char *name, int defaultValue);
82
83namespace QSGBatchRenderer
84{
85
86#define DECLARE_DEBUG_VAR(variable) \
87 static bool debug_ ## variable() \
88 { static bool value = qgetenv("QSG_RENDERER_DEBUG").contains(QT_STRINGIFY(variable)); return value; }
89DECLARE_DEBUG_VAR(render)
90DECLARE_DEBUG_VAR(build)
91DECLARE_DEBUG_VAR(change)
92DECLARE_DEBUG_VAR(upload)
93DECLARE_DEBUG_VAR(roots)
94DECLARE_DEBUG_VAR(dump)
95DECLARE_DEBUG_VAR(noalpha)
96DECLARE_DEBUG_VAR(noopaque)
97DECLARE_DEBUG_VAR(noclip)
98#undef DECLARE_DEBUG_VAR
99
100static QElapsedTimer qsg_renderer_timer;
101
102#define QSGNODE_TRAVERSE(NODE) for (QSGNode *child = NODE->firstChild(); child; child = child->nextSibling())
103#define SHADOWNODE_TRAVERSE(NODE) for (Node *child = NODE->firstChild(); child; child = child->sibling())
104
105static inline int size_of_type(GLenum type)
106{
107 static int sizes[] = {
108 sizeof(char),
109 sizeof(unsigned char),
110 sizeof(short),
111 sizeof(unsigned short),
112 sizeof(int),
113 sizeof(unsigned int),
114 sizeof(float),
115 2,
116 3,
117 4,
118 sizeof(double)
119 };
120 Q_ASSERT(type >= QSGGeometry::ByteType && type <= QSGGeometry::DoubleType);
121 return sizes[type - QSGGeometry::ByteType];
122}
123
124bool qsg_sort_element_increasing_order(Element *a, Element *b) { return a->order < b->order; }
125bool qsg_sort_element_decreasing_order(Element *a, Element *b) { return a->order > b->order; }
126bool qsg_sort_batch_is_valid(Batch *a, Batch *b) { return a->first && !b->first; }
127bool qsg_sort_batch_increasing_order(Batch *a, Batch *b) { return a->first->order < b->first->order; }
128bool qsg_sort_batch_decreasing_order(Batch *a, Batch *b) { return a->first->order > b->first->order; }
129
130QSGMaterial::Flag QSGMaterial_FullMatrix = (QSGMaterial::Flag) (QSGMaterial::RequiresFullMatrix & ~QSGMaterial::RequiresFullMatrixExceptTranslate);
131
132struct QMatrix4x4_Accessor
133{
134 float m[4][4];
135 int flagBits;
136
137 static bool isTranslate(const QMatrix4x4 &m) { return ((const QMatrix4x4_Accessor &) m).flagBits <= 0x1; }
138 static bool isScale(const QMatrix4x4 &m) { return ((const QMatrix4x4_Accessor &) m).flagBits <= 0x2; }
139 static bool is2DSafe(const QMatrix4x4 &m) { return ((const QMatrix4x4_Accessor &) m).flagBits < 0x8; }
140};
141
142const float OPAQUE_LIMIT = 0.999f;
143
144const uint DYNAMIC_VERTEX_INDEX_BUFFER_THRESHOLD = 4;
145const int VERTEX_BUFFER_BINDING = 0;
146const int ZORDER_BUFFER_BINDING = VERTEX_BUFFER_BINDING + 1;
147
148static inline uint aligned(uint v, uint byteAlign)
149{
150 return (v + byteAlign - 1) & ~(byteAlign - 1);
151}
152
153QRhiVertexInputAttribute::Format qsg_vertexInputFormat(const QSGGeometry::Attribute &a)
154{
155 switch (a.type) {
156 case QSGGeometry::FloatType:
157 if (a.tupleSize == 4)
158 return QRhiVertexInputAttribute::Float4;
159 if (a.tupleSize == 3)
160 return QRhiVertexInputAttribute::Float3;
161 if (a.tupleSize == 2)
162 return QRhiVertexInputAttribute::Float2;
163 if (a.tupleSize == 1)
164 return QRhiVertexInputAttribute::Float;
165 break;
166 case QSGGeometry::UnsignedByteType:
167 if (a.tupleSize == 4)
168 return QRhiVertexInputAttribute::UNormByte4;
169 if (a.tupleSize == 2)
170 return QRhiVertexInputAttribute::UNormByte2;
171 if (a.tupleSize == 1)
172 return QRhiVertexInputAttribute::UNormByte;
173 break;
174 default:
175 break;
176 }
177 qWarning("Unsupported attribute type 0x%x with %d components", a.type, a.tupleSize);
178 Q_UNREACHABLE();
179 return QRhiVertexInputAttribute::Float;
180}
181
182static QRhiVertexInputLayout calculateVertexInputLayout(const QSGMaterialRhiShader *s, const QSGGeometry *geometry, bool batchable)
183{
184 Q_ASSERT(geometry);
185 const QSGMaterialRhiShaderPrivate *sd = QSGMaterialRhiShaderPrivate::get(s);
186 if (!sd->vertexShader) {
187 qWarning("No vertex shader in QSGMaterialRhiShader %p", s);
188 return QRhiVertexInputLayout();
189 }
190
191 const int attrCount = geometry->attributeCount();
192 QVarLengthArray<QRhiVertexInputAttribute, 8> inputAttributes;
193 inputAttributes.reserve(attrCount + 1);
194 int offset = 0;
195 for (int i = 0; i < attrCount; ++i) {
196 const QSGGeometry::Attribute &a = geometry->attributes()[i];
197 if (!sd->vertexShader->vertexInputLocations.contains(a.position)) {
198 qWarning("Vertex input %d is present in material but not in shader. This is wrong.",
199 a.position);
200 }
201 inputAttributes.append(QRhiVertexInputAttribute(VERTEX_BUFFER_BINDING, a.position, qsg_vertexInputFormat(a), offset));
202 offset += a.tupleSize * size_of_type(a.type);
203 }
204 if (batchable) {
205 inputAttributes.append(QRhiVertexInputAttribute(ZORDER_BUFFER_BINDING, sd->vertexShader->qt_order_attrib_location,
206 QRhiVertexInputAttribute::Float, 0));
207 }
208
209 Q_ASSERT(VERTEX_BUFFER_BINDING == 0 && ZORDER_BUFFER_BINDING == 1); // not very flexible
210 QVarLengthArray<QRhiVertexInputBinding, 2> inputBindings;
211 inputBindings.append(QRhiVertexInputBinding(geometry->sizeOfVertex()));
212 if (batchable)
213 inputBindings.append(QRhiVertexInputBinding(sizeof(float)));
214
215 QRhiVertexInputLayout inputLayout;
216 inputLayout.setBindings(inputBindings.cbegin(), inputBindings.cend());
217 inputLayout.setAttributes(inputAttributes.cbegin(), inputAttributes.cend());
218
219 return inputLayout;
220}
221
222QRhiCommandBuffer::IndexFormat qsg_indexFormat(const QSGGeometry *geometry)
223{
224 switch (geometry->indexType()) {
225 case QSGGeometry::UnsignedShortType:
226 return QRhiCommandBuffer::IndexUInt16;
227 break;
228 case QSGGeometry::UnsignedIntType:
229 return QRhiCommandBuffer::IndexUInt32;
230 break;
231 default:
232 Q_UNREACHABLE();
233 return QRhiCommandBuffer::IndexUInt16;
234 }
235}
236
237QRhiGraphicsPipeline::Topology qsg_topology(int geomDrawMode)
238{
239 QRhiGraphicsPipeline::Topology topology = QRhiGraphicsPipeline::Triangles;
240 switch (geomDrawMode) {
241 case QSGGeometry::DrawPoints:
242 topology = QRhiGraphicsPipeline::Points;
243 break;
244 case QSGGeometry::DrawLines:
245 topology = QRhiGraphicsPipeline::Lines;
246 break;
247 case QSGGeometry::DrawLineStrip:
248 topology = QRhiGraphicsPipeline::LineStrip;
249 break;
250 case QSGGeometry::DrawTriangles:
251 topology = QRhiGraphicsPipeline::Triangles;
252 break;
253 case QSGGeometry::DrawTriangleStrip:
254 topology = QRhiGraphicsPipeline::TriangleStrip;
255 break;
256 default:
257 qWarning("Primitive topology 0x%x not supported", geomDrawMode);
258 break;
259 }
260 return topology;
261}
262
263ShaderManager::Shader *ShaderManager::prepareMaterial(QSGMaterial *material, bool enableRhiShaders, const QSGGeometry *geometry)
264{
265 QSGMaterialType *type = material->type();
266 Shader *shader = rewrittenShaders.value(type, 0);
267 if (shader)
268 return shader;
269
270 if (enableRhiShaders && !material->flags().testFlag(QSGMaterial::SupportsRhiShader)) {
271 qWarning("The material failed to provide a working QShader pack");
272 return nullptr;
273 }
274
275 Q_TRACE_SCOPE(QSG_prepareMaterial);
276 if (QSG_LOG_TIME_COMPILATION().isDebugEnabled())
277 qsg_renderer_timer.start();
278 Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphContextFrame);
279
280 shader = new Shader;
281 if (enableRhiShaders) {
282 material->setFlag(QSGMaterial::RhiShaderWanted, true);
283 QSGMaterialRhiShader *s = static_cast<QSGMaterialRhiShader *>(material->createShader());
284 material->setFlag(QSGMaterial::RhiShaderWanted, false);
285 context->initializeRhiShader(s, QShader::BatchableVertexShader);
286 shader->programRhi.program = s;
287 shader->programRhi.inputLayout = calculateVertexInputLayout(s, geometry, true);
288 QSGMaterialRhiShaderPrivate *sD = QSGMaterialRhiShaderPrivate::get(s);
289 shader->programRhi.shaderStages = {
290 { QRhiGraphicsShaderStage::Vertex, sD->shader(QShader::VertexStage), QShader::BatchableVertexShader },
291 { QRhiGraphicsShaderStage::Fragment, sD->shader(QShader::FragmentStage) }
292 };
293 } else {
294 QSGMaterialShader *s = material->createShader();
295 QOpenGLContext *ctx = context->openglContext();
296 QSurfaceFormat::OpenGLContextProfile profile = ctx->format().profile();
297 QOpenGLShaderProgram *p = s->program();
298 char const *const *attr = s->attributeNames();
299 int i;
300 for (i = 0; attr[i]; ++i) {
301 if (*attr[i])
302 p->bindAttributeLocation(attr[i], i);
303 }
304 p->bindAttributeLocation("_qt_order", i);
305 context->compileShader(s, material, qsgShaderRewriter_insertZAttributes(s->vertexShader(), profile), nullptr);
306 context->initializeShader(s);
307 if (!p->isLinked()) {
308 delete shader;
309 return nullptr;
310 }
311 shader->programGL.program = s;
312 shader->programGL.pos_order = i;
313 }
314
315 shader->lastOpacity = 0;
316
317 qCDebug(QSG_LOG_TIME_COMPILATION, "material shaders prepared in %dms", (int) qsg_renderer_timer.elapsed());
318
319 Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphContextFrame,
320 QQuickProfiler::SceneGraphContextMaterialCompile);
321
322 rewrittenShaders[type] = shader;
323 return shader;
324}
325
326ShaderManager::Shader *ShaderManager::prepareMaterialNoRewrite(QSGMaterial *material, bool enableRhiShaders, const QSGGeometry *geometry)
327{
328 QSGMaterialType *type = material->type();
329 Shader *shader = stockShaders.value(type, 0);
330 if (shader)
331 return shader;
332
333 if (enableRhiShaders && !material->flags().testFlag(QSGMaterial::SupportsRhiShader)) {
334 qWarning("The material failed to provide a working QShader pack");
335 return nullptr;
336 }
337
338 Q_TRACE_SCOPE(QSG_prepareMaterial);
339 if (QSG_LOG_TIME_COMPILATION().isDebugEnabled())
340 qsg_renderer_timer.start();
341 Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphContextFrame);
342
343 shader = new Shader;
344 if (enableRhiShaders) {
345 material->setFlag(QSGMaterial::RhiShaderWanted, true);
346 QSGMaterialRhiShader *s = static_cast<QSGMaterialRhiShader *>(material->createShader());
347 material->setFlag(QSGMaterial::RhiShaderWanted, false);
348 context->initializeRhiShader(s, QShader::StandardShader);
349 shader->programRhi.program = s;
350 shader->programRhi.inputLayout = calculateVertexInputLayout(s, geometry, false);
351 QSGMaterialRhiShaderPrivate *sD = QSGMaterialRhiShaderPrivate::get(s);
352 shader->programRhi.shaderStages = {
353 { QRhiGraphicsShaderStage::Vertex, sD->shader(QShader::VertexStage) },
354 { QRhiGraphicsShaderStage::Fragment, sD->shader(QShader::FragmentStage) }
355 };
356 } else {
357 QSGMaterialShader *s = material->createShader();
358 context->compileShader(s, material);
359 context->initializeShader(s);
360 shader->programGL.program = s;
361 shader->programGL.pos_order = -1;
362 }
363
364 shader->lastOpacity = 0;
365
366 stockShaders[type] = shader;
367
368 qCDebug(QSG_LOG_TIME_COMPILATION, "shader compiled in %dms (no rewrite)", (int) qsg_renderer_timer.elapsed());
369
370 Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphContextFrame,
371 QQuickProfiler::SceneGraphContextMaterialCompile);
372 return shader;
373}
374
375void ShaderManager::invalidated()
376{
377 qDeleteAll(stockShaders);
378 stockShaders.clear();
379 qDeleteAll(rewrittenShaders);
380 rewrittenShaders.clear();
381 delete blitProgram;
382 blitProgram = nullptr;
383
384 qDeleteAll(srbCache);
385 srbCache.clear();
386
387 qDeleteAll(pipelineCache);
388 pipelineCache.clear();
389}
390
391void ShaderManager::clearCachedRendererData()
392{
393 for (ShaderManager::Shader *sms : stockShaders) {
394 QSGMaterialRhiShader *s = sms->programRhi.program;
395 if (s) {
396 QSGMaterialRhiShaderPrivate *sd = QSGMaterialRhiShaderPrivate::get(s);
397 sd->clearCachedRendererData();
398 }
399 }
400 for (ShaderManager::Shader *sms : rewrittenShaders) {
401 QSGMaterialRhiShader *s = sms->programRhi.program;
402 if (s) {
403 QSGMaterialRhiShaderPrivate *sd = QSGMaterialRhiShaderPrivate::get(s);
404 sd->clearCachedRendererData();
405 }
406 }
407}
408
409QRhiShaderResourceBindings *ShaderManager::srb(const ShaderResourceBindingList &bindings)
410{
411 auto it = srbCache.constFind(bindings);
412 if (it != srbCache.constEnd())
413 return *it;
414
415 QRhiShaderResourceBindings *srb = context->rhi()->newShaderResourceBindings();
416 srb->setBindings(bindings.cbegin(), bindings.cend());
417 if (srb->build()) {
418 srbCache.insert(bindings, srb);
419 } else {
420 qWarning("Failed to build srb");
421 delete srb;
422 srb = nullptr;
423 }
424 return srb;
425}
426
427void qsg_dumpShadowRoots(BatchRootInfo *i, int indent)
428{
429 static int extraIndent = 0;
430 ++extraIndent;
431
432 QByteArray ind(indent + extraIndent + 10, ' ');
433
434 if (!i) {
435 qDebug("%s - no info", ind.constData());
436 } else {
437 qDebug() << ind.constData() << "- parent:" << i->parentRoot << "orders" << i->firstOrder << "->" << i->lastOrder << ", avail:" << i->availableOrders;
438 for (QSet<Node *>::const_iterator it = i->subRoots.constBegin();
439 it != i->subRoots.constEnd(); ++it) {
440 qDebug() << ind.constData() << "-" << *it;
441 qsg_dumpShadowRoots((*it)->rootInfo(), indent);
442 }
443 }
444
445 --extraIndent;
446}
447
448void qsg_dumpShadowRoots(Node *n)
449{
450#ifndef QT_NO_DEBUG_OUTPUT
451 static int indent = 0;
452 ++indent;
453
454 QByteArray ind(indent, ' ');
455
456 if (n->type() == QSGNode::ClipNodeType || n->isBatchRoot) {
457 qDebug() << ind.constData() << "[X]" << n->sgNode << Qt::hex << uint(n->sgNode->flags());
458 qsg_dumpShadowRoots(n->rootInfo(), indent);
459 } else {
460 QDebug d = qDebug();
461 d << ind.constData() << "[ ]" << n->sgNode << Qt::hex << uint(n->sgNode->flags());
462 if (n->type() == QSGNode::GeometryNodeType)
463 d << "order" << Qt::dec << n->element()->order;
464 }
465
466 SHADOWNODE_TRAVERSE(n)
467 qsg_dumpShadowRoots(child);
468
469 --indent;
470#else
471 Q_UNUSED(n)
472#endif
473}
474
475Updater::Updater(Renderer *r)
476 : renderer(r)
477 , m_roots(32)
478 , m_rootMatrices(8)
479{
480 m_roots.add(0);
481 m_combined_matrix_stack.add(&m_identityMatrix);
482 m_rootMatrices.add(m_identityMatrix);
483
484 Q_ASSERT(sizeof(QMatrix4x4_Accessor) == sizeof(QMatrix4x4));
485}
486
487void Updater::updateStates(QSGNode *n)
488{
489 m_current_clip = nullptr;
490
491 m_added = 0;
492 m_transformChange = 0;
493 m_opacityChange = 0;
494
495 Node *sn = renderer->m_nodes.value(n, 0);
496 Q_ASSERT(sn);
497
498 if (Q_UNLIKELY(debug_roots()))
499 qsg_dumpShadowRoots(sn);
500
501 if (Q_UNLIKELY(debug_build())) {
502 qDebug("Updater::updateStates()");
503 if (sn->dirtyState & (QSGNode::DirtyNodeAdded << 16))
504 qDebug(" - nodes have been added");
505 if (sn->dirtyState & (QSGNode::DirtyMatrix << 16))
506 qDebug(" - transforms have changed");
507 if (sn->dirtyState & (QSGNode::DirtyOpacity << 16))
508 qDebug(" - opacity has changed");
509 if (uint(sn->dirtyState) & uint(QSGNode::DirtyForceUpdate << 16))
510 qDebug(" - forceupdate");
511 }
512
513 if (Q_UNLIKELY(renderer->m_visualizer->mode() == Visualizer::VisualizeChanges))
514 renderer->m_visualizer->visualizeChangesPrepare(sn);
515
516 visitNode(sn);
517}
518
519void Updater::visitNode(Node *n)
520{
521 if (m_added == 0 && n->dirtyState == 0 && m_force_update == 0 && m_transformChange == 0 && m_opacityChange == 0)
522 return;
523
524 int count = m_added;
525 if (n->dirtyState & QSGNode::DirtyNodeAdded)
526 ++m_added;
527
528 int force = m_force_update;
529 if (n->dirtyState & QSGNode::DirtyForceUpdate)
530 ++m_force_update;
531
532 switch (n->type()) {
533 case QSGNode::OpacityNodeType:
534 visitOpacityNode(n);
535 break;
536 case QSGNode::TransformNodeType:
537 visitTransformNode(n);
538 break;
539 case QSGNode::GeometryNodeType:
540 visitGeometryNode(n);
541 break;
542 case QSGNode::ClipNodeType:
543 visitClipNode(n);
544 break;
545 case QSGNode::RenderNodeType:
546 if (m_added)
547 n->renderNodeElement()->root = m_roots.last();
548 Q_FALLTHROUGH(); // to visit children
549 default:
550 SHADOWNODE_TRAVERSE(n) visitNode(child);
551 break;
552 }
553
554 m_added = count;
555 m_force_update = force;
556 n->dirtyState = {};
557}
558
559void Updater::visitClipNode(Node *n)
560{
561 ClipBatchRootInfo *extra = n->clipInfo();
562
563 QSGClipNode *cn = static_cast<QSGClipNode *>(n->sgNode);
564
565 if (m_roots.last() && m_added > 0)
566 renderer->registerBatchRoot(n, m_roots.last());
567
568 cn->setRendererClipList(m_current_clip);
569 m_current_clip = cn;
570 m_roots << n;
571 m_rootMatrices.add(m_rootMatrices.last() * *m_combined_matrix_stack.last());
572 extra->matrix = m_rootMatrices.last();
573 cn->setRendererMatrix(&extra->matrix);
574 m_combined_matrix_stack << &m_identityMatrix;
575
576 SHADOWNODE_TRAVERSE(n) visitNode(child);
577
578 m_current_clip = cn->clipList();
579 m_rootMatrices.pop_back();
580 m_combined_matrix_stack.pop_back();
581 m_roots.pop_back();
582}
583
584void Updater::visitOpacityNode(Node *n)
585{
586 QSGOpacityNode *on = static_cast<QSGOpacityNode *>(n->sgNode);
587
588 qreal combined = m_opacity_stack.last() * on->opacity();
589 on->setCombinedOpacity(combined);
590 m_opacity_stack.add(combined);
591
592 if (m_added == 0 && n->dirtyState & QSGNode::DirtyOpacity) {
593 bool was = n->isOpaque;
594 bool is = on->opacity() > OPAQUE_LIMIT;
595 if (was != is) {
596 renderer->m_rebuild = Renderer::FullRebuild;
597 n->isOpaque = is;
598 }
599 ++m_opacityChange;
600 SHADOWNODE_TRAVERSE(n) visitNode(child);
601 --m_opacityChange;
602 } else {
603 if (m_added > 0)
604 n->isOpaque = on->opacity() > OPAQUE_LIMIT;
605 SHADOWNODE_TRAVERSE(n) visitNode(child);
606 }
607
608 m_opacity_stack.pop_back();
609}
610
611void Updater::visitTransformNode(Node *n)
612{
613 bool popMatrixStack = false;
614 bool popRootStack = false;
615 bool dirty = n->dirtyState & QSGNode::DirtyMatrix;
616
617 QSGTransformNode *tn = static_cast<QSGTransformNode *>(n->sgNode);
618
619 if (n->isBatchRoot) {
620 if (m_added > 0 && m_roots.last())
621 renderer->registerBatchRoot(n, m_roots.last());
622 tn->setCombinedMatrix(m_rootMatrices.last() * *m_combined_matrix_stack.last() * tn->matrix());
623
624 // The only change in this subtree is ourselves and we are a batch root, so
625 // only update subroots and return, saving tons of child-processing (flickable-panning)
626
627 if (!n->becameBatchRoot && m_added == 0 && m_force_update == 0 && m_opacityChange == 0 && dirty && (n->dirtyState & ~QSGNode::DirtyMatrix) == 0) {
628 BatchRootInfo *info = renderer->batchRootInfo(n);
629 for (QSet<Node *>::const_iterator it = info->subRoots.constBegin();
630 it != info->subRoots.constEnd(); ++it) {
631 updateRootTransforms(*it, n, tn->combinedMatrix());
632 }
633 return;
634 }
635
636 n->becameBatchRoot = false;
637
638 m_combined_matrix_stack.add(&m_identityMatrix);
639 m_roots.add(n);
640 m_rootMatrices.add(tn->combinedMatrix());
641
642 popMatrixStack = true;
643 popRootStack = true;
644 } else if (!tn->matrix().isIdentity()) {
645 tn->setCombinedMatrix(*m_combined_matrix_stack.last() * tn->matrix());
646 m_combined_matrix_stack.add(&tn->combinedMatrix());
647 popMatrixStack = true;
648 } else {
649 tn->setCombinedMatrix(*m_combined_matrix_stack.last());
650 }
651
652 if (dirty)
653 ++m_transformChange;
654
655 SHADOWNODE_TRAVERSE(n) visitNode(child);
656
657 if (dirty)
658 --m_transformChange;
659 if (popMatrixStack)
660 m_combined_matrix_stack.pop_back();
661 if (popRootStack) {
662 m_roots.pop_back();
663 m_rootMatrices.pop_back();
664 }
665}
666
667void Updater::visitGeometryNode(Node *n)
668{
669 QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(n->sgNode);
670
671 gn->setRendererMatrix(m_combined_matrix_stack.last());
672 gn->setRendererClipList(m_current_clip);
673 gn->setInheritedOpacity(m_opacity_stack.last());
674
675 if (m_added) {
676 Element *e = n->element();
677 e->root = m_roots.last();
678 e->translateOnlyToRoot = QMatrix4x4_Accessor::isTranslate(*gn->matrix());
679
680 if (e->root) {
681 BatchRootInfo *info = renderer->batchRootInfo(e->root);
682 while (info != nullptr) {
683 info->availableOrders--;
684 if (info->availableOrders < 0) {
685 renderer->m_rebuild |= Renderer::BuildRenderLists;
686 } else {
687 renderer->m_rebuild |= Renderer::BuildRenderListsForTaggedRoots;
688 renderer->m_taggedRoots << e->root;
689 }
690 if (info->parentRoot != nullptr)
691 info = renderer->batchRootInfo(info->parentRoot);
692 else
693 info = nullptr;
694 }
695 } else {
696 renderer->m_rebuild |= Renderer::FullRebuild;
697 }
698 } else {
699 if (m_transformChange) {
700 Element *e = n->element();
701 e->translateOnlyToRoot = QMatrix4x4_Accessor::isTranslate(*gn->matrix());
702 }
703 if (m_opacityChange) {
704 Element *e = n->element();
705 if (e->batch)
706 renderer->invalidateBatchAndOverlappingRenderOrders(e->batch);
707 }
708 }
709
710 SHADOWNODE_TRAVERSE(n) visitNode(child);
711}
712
713void Updater::updateRootTransforms(Node *node, Node *root, const QMatrix4x4 &combined)
714{
715 BatchRootInfo *info = renderer->batchRootInfo(node);
716 QMatrix4x4 m;
717 Node *n = node;
718
719 while (n != root) {
720 if (n->type() == QSGNode::TransformNodeType)
721 m = static_cast<QSGTransformNode *>(n->sgNode)->matrix() * m;
722 n = n->parent();
723 }
724
725 m = combined * m;
726
727 if (node->type() == QSGNode::ClipNodeType) {
728 static_cast<ClipBatchRootInfo *>(info)->matrix = m;
729 } else {
730 Q_ASSERT(node->type() == QSGNode::TransformNodeType);
731 static_cast<QSGTransformNode *>(node->sgNode)->setCombinedMatrix(m);
732 }
733
734 for (QSet<Node *>::const_iterator it = info->subRoots.constBegin();
735 it != info->subRoots.constEnd(); ++it) {
736 updateRootTransforms(*it, node, m);
737 }
738}
739
740int qsg_positionAttribute(QSGGeometry *g)
741{
742 int vaOffset = 0;
743 for (int a=0; a<g->attributeCount(); ++a) {
744 const QSGGeometry::Attribute &attr = g->attributes()[a];
745 if (attr.isVertexCoordinate && attr.tupleSize == 2 && attr.type == QSGGeometry::FloatType) {
746 return vaOffset;
747 }
748 vaOffset += attr.tupleSize * size_of_type(attr.type);
749 }
750 return -1;
751}
752
753
754void Rect::map(const QMatrix4x4 &matrix)
755{
756 const float *m = matrix.constData();
757 if (QMatrix4x4_Accessor::isScale(matrix)) {
758 tl.x = tl.x * m[0] + m[12];
759 tl.y = tl.y * m[5] + m[13];
760 br.x = br.x * m[0] + m[12];
761 br.y = br.y * m[5] + m[13];
762 if (tl.x > br.x)
763 qSwap(tl.x, br.x);
764 if (tl.y > br.y)
765 qSwap(tl.y, br.y);
766 } else {
767 Pt mtl = tl;
768 Pt mtr = { br.x, tl.y };
769 Pt mbl = { tl.x, br.y };
770 Pt mbr = br;
771
772 mtl.map(matrix);
773 mtr.map(matrix);
774 mbl.map(matrix);
775 mbr.map(matrix);
776
777 set(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
778 (*this) |= mtl;
779 (*this) |= mtr;
780 (*this) |= mbl;
781 (*this) |= mbr;
782 }
783}
784
785void Element::computeBounds()
786{
787 Q_ASSERT(!boundsComputed);
788 boundsComputed = true;
789
790 QSGGeometry *g = node->geometry();
791 int offset = qsg_positionAttribute(g);
792 if (offset == -1) {
793 // No position attribute means overlaps with everything..
794 bounds.set(-FLT_MAX, -FLT_MAX, FLT_MAX, FLT_MAX);
795 return;
796 }
797
798 bounds.set(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
799 char *vd = (char *) g->vertexData() + offset;
800 for (int i=0; i<g->vertexCount(); ++i) {
801 bounds |= *(Pt *) vd;
802 vd += g->sizeOfVertex();
803 }
804 bounds.map(*node->matrix());
805
806 if (!qt_is_finite(bounds.tl.x) || bounds.tl.x == FLT_MAX)
807 bounds.tl.x = -FLT_MAX;
808 if (!qt_is_finite(bounds.tl.y) || bounds.tl.y == FLT_MAX)
809 bounds.tl.y = -FLT_MAX;
810 if (!qt_is_finite(bounds.br.x) || bounds.br.x == -FLT_MAX)
811 bounds.br.x = FLT_MAX;
812 if (!qt_is_finite(bounds.br.y) || bounds.br.y == -FLT_MAX)
813 bounds.br.y = FLT_MAX;
814
815 Q_ASSERT(bounds.tl.x <= bounds.br.x);
816 Q_ASSERT(bounds.tl.y <= bounds.br.y);
817
818 boundsOutsideFloatRange = bounds.isOutsideFloatRange();
819}
820
821BatchCompatibility Batch::isMaterialCompatible(Element *e) const
822{
823 Element *n = first;
824 // Skip to the first node other than e which has not been removed
825 while (n && (n == e || n->removed))
826 n = n->nextInBatch;
827
828 // Only 'e' in this batch, so a material change doesn't change anything as long as
829 // its blending is still in sync with this batch...
830 if (!n)
831 return BatchIsCompatible;
832
833 QSGMaterial *m = e->node->activeMaterial();
834 QSGMaterial *nm = n->node->activeMaterial();
835 return (nm->type() == m->type() && nm->compare(m) == 0)
836 ? BatchIsCompatible
837 : BatchBreaksOnCompare;
838}
839
840/*
841 * Marks this batch as dirty or in the case where the geometry node has
842 * changed to be incompatible with this batch, return false so that
843 * the caller can mark the entire sg for a full rebuild...
844 */
845bool Batch::geometryWasChanged(QSGGeometryNode *gn)
846{
847 Element *e = first;
848 Q_ASSERT_X(e, "Batch::geometryWasChanged", "Batch is expected to 'valid' at this time");
849 // 'gn' is the first node in the batch, compare against the next one.
850 while (e && (e->node == gn || e->removed))
851 e = e->nextInBatch;
852 if (!e || e->node->geometry()->attributes() == gn->geometry()->attributes()) {
853 needsUpload = true;
854 return true;
855 } else {
856 return false;
857 }
858}
859
860void Batch::cleanupRemovedElements()
861{
862 if (!needsPurge)
863 return;
864
865 // remove from front of batch..
866 while (first && first->removed) {
867 first = first->nextInBatch;
868 }
869
870 // Then continue and remove other nodes further out in the batch..
871 if (first) {
872 Element *e = first;
873 while (e->nextInBatch) {
874 if (e->nextInBatch->removed)
875 e->nextInBatch = e->nextInBatch->nextInBatch;
876 else
877 e = e->nextInBatch;
878
879 }
880 }
881
882 needsPurge = false;
883}
884
885/*
886 * Iterates through all geometry nodes in this batch and unsets their batch,
887 * thus forcing them to be rebuilt
888 */
889void Batch::invalidate()
890{
891 // If doing removal here is a performance issue, we might add a "hasRemoved" bit to
892 // the batch to do an early out..
893 cleanupRemovedElements();
894 Element *e = first;
895 first = nullptr;
896 root = nullptr;
897 while (e) {
898 e->batch = nullptr;
899 Element *n = e->nextInBatch;
900 e->nextInBatch = nullptr;
901 e = n;
902 }
903}
904
905bool Batch::isTranslateOnlyToRoot() const {
906 bool only = true;
907 Element *e = first;
908 while (e && only) {
909 only &= e->translateOnlyToRoot;
910 e = e->nextInBatch;
911 }
912 return only;
913}
914
915/*
916 * Iterates through all the nodes in the batch and returns true if the
917 * nodes are all safe to batch. There are two separate criteria:
918 *
919 * - The matrix is such that the z component of the result is of no
920 * consequence.
921 *
922 * - The bounds are inside the stable floating point range. This applies
923 * to desktop only where we in this case can trigger a fallback to
924 * unmerged in which case we pass the geometry straight through and
925 * just apply the matrix.
926 *
927 * NOTE: This also means a slight performance impact for geometries which
928 * are defined to be outside the stable floating point range and still
929 * use single precision float, but given that this implicitly fixes
930 * huge lists and tables, it is worth it.
931 */
932bool Batch::isSafeToBatch() const {
933 Element *e = first;
934 while (e) {
935 if (e->boundsOutsideFloatRange)
936 return false;
937 if (!QMatrix4x4_Accessor::is2DSafe(*e->node->matrix()))
938 return false;
939 e = e->nextInBatch;
940 }
941 return true;
942}
943
944static int qsg_countNodesInBatch(const Batch *batch)
945{
946 int sum = 0;
947 Element *e = batch->first;
948 while (e) {
949 ++sum;
950 e = e->nextInBatch;
951 }
952 return sum;
953}
954
955static int qsg_countNodesInBatches(const QDataBuffer<Batch *> &batches)
956{
957 int sum = 0;
958 for (int i=0; i<batches.size(); ++i) {
959 sum += qsg_countNodesInBatch(batches.at(i));
960 }
961 return sum;
962}
963
964Renderer::Renderer(QSGDefaultRenderContext *ctx)
965 : QSGRenderer(ctx)
966 , m_context(ctx)
967 , m_opaqueRenderList(64)
968 , m_alphaRenderList(64)
969 , m_nextRenderOrder(0)
970 , m_partialRebuild(false)
971 , m_partialRebuildRoot(nullptr)
972 , m_useDepthBuffer(true)
973 , m_opaqueBatches(16)
974 , m_alphaBatches(16)
975 , m_batchPool(16)
976 , m_elementsToDelete(64)
977 , m_tmpAlphaElements(16)
978 , m_tmpOpaqueElements(16)
979 , m_rebuild(FullRebuild)
980 , m_zRange(0)
981 , m_renderOrderRebuildLower(-1)
982 , m_renderOrderRebuildUpper(-1)
983 , m_currentMaterial(nullptr)
984 , m_currentShader(nullptr)
985 , m_currentStencilValue(0)
986 , m_clipMatrixId(0)
987 , m_currentClip(nullptr)
988 , m_currentClipType(ClipState::NoClip)
989 , m_vertexUploadPool(256)
990 , m_indexUploadPool(64)
991 , m_vao(nullptr)
992{
993 m_rhi = m_context->rhi();
994 if (m_rhi) {
995 m_ubufAlignment = m_rhi->ubufAlignment();
996 m_uint32IndexForRhi = !m_rhi->isFeatureSupported(QRhi::NonFourAlignedEffectiveIndexBufferOffset);
997 if (qEnvironmentVariableIntValue("QSG_RHI_UINT32_INDEX"))
998 m_uint32IndexForRhi = true;
999 m_visualizer = new RhiVisualizer(this);
1000 } else {
1001 initializeOpenGLFunctions();
1002 m_uint32IndexForRhi = false;
1003 m_visualizer = new OpenGLVisualizer(this);
1004 }
1005
1006 setNodeUpdater(new Updater(this));
1007
1008 // The shader manager is shared between renderers (think for example Item
1009 // layers that create a new Renderer each) with the same rendercontext
1010 // (i.e. QRhi or QOpenGLContext).
1011 m_shaderManager = ctx->findChild<ShaderManager *>(QStringLiteral("__qt_ShaderManager"), Qt::FindDirectChildrenOnly);
1012 if (!m_shaderManager) {
1013 m_shaderManager = new ShaderManager(ctx);
1014 m_shaderManager->setObjectName(QStringLiteral("__qt_ShaderManager"));
1015 m_shaderManager->setParent(ctx);
1016 QObject::connect(ctx, SIGNAL(invalidated()), m_shaderManager, SLOT(invalidated()), Qt::DirectConnection);
1017 }
1018
1019 m_bufferStrategy = GL_STATIC_DRAW;
1020 if (Q_UNLIKELY(qEnvironmentVariableIsSet("QSG_RENDERER_BUFFER_STRATEGY"))) {
1021 const QByteArray strategy = qgetenv("QSG_RENDERER_BUFFER_STRATEGY");
1022 if (strategy == "dynamic")
1023 m_bufferStrategy = GL_DYNAMIC_DRAW;
1024 else if (strategy == "stream")
1025 m_bufferStrategy = GL_STREAM_DRAW;
1026 }
1027
1028 m_batchNodeThreshold = qt_sg_envInt("QSG_RENDERER_BATCH_NODE_THRESHOLD", 64);
1029 m_batchVertexThreshold = qt_sg_envInt("QSG_RENDERER_BATCH_VERTEX_THRESHOLD", 1024);
1030
1031 if (Q_UNLIKELY(debug_build() || debug_render())) {
1032 qDebug("Batch thresholds: nodes: %d vertices: %d",
1033 m_batchNodeThreshold, m_batchVertexThreshold);
1034 qDebug("Using buffer strategy: %s",
1035 (m_bufferStrategy == GL_STATIC_DRAW
1036 ? "static" : (m_bufferStrategy == GL_DYNAMIC_DRAW ? "dynamic" : "stream")));
1037 }
1038
1039 static const bool useDepth = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER");
1040 if (!m_rhi) {
1041 // If rendering with an OpenGL Core profile context, we need to create a VAO
1042 // to hold our vertex specification state.
1043 if (m_context->openglContext()->format().profile() == QSurfaceFormat::CoreProfile) {
1044 m_vao = new QOpenGLVertexArrayObject(this);
1045 m_vao->create();
1046 }
1047 m_useDepthBuffer = useDepth && ctx->openglContext()->format().depthBufferSize() > 0;
1048 } else {
1049 m_useDepthBuffer = useDepth;
1050 }
1051}
1052
1053static void qsg_wipeBuffer(Buffer *buffer, QOpenGLFunctions *funcs)
1054{
1055 if (buffer->buf) {
1056 //qDebug("releasing rhibuf %p", buffer->buf);
1057 delete buffer->buf;
1058 }
1059
1060 if (buffer->id)
1061 funcs->glDeleteBuffers(1, &buffer->id);
1062
1063 // The free here is ok because we're in one of two situations.
1064 // 1. We're using the upload pool in which case unmap will have set the
1065 // data pointer to 0 and calling free on 0 is ok.
1066 // 2. We're using dedicated buffers because of visualization or IBO workaround
1067 // and the data something we malloced and must be freed.
1068 free(buffer->data);
1069}
1070
1071static void qsg_wipeBatch(Batch *batch, QOpenGLFunctions *funcs, bool separateIndexBuffer)
1072{
1073 qsg_wipeBuffer(&batch->vbo, funcs);
1074 if (separateIndexBuffer)
1075 qsg_wipeBuffer(&batch->ibo, funcs);
1076 delete batch->ubuf;
1077 batch->stencilClipState.reset();
1078 delete batch;
1079}
1080
1081Renderer::~Renderer()
1082{
1083 if (m_rhi || QOpenGLContext::currentContext()) {
1084 // Clean up batches and buffers
1085 const bool separateIndexBuffer = m_context->separateIndexBuffer();
1086 for (int i = 0; i < m_opaqueBatches.size(); ++i)
1087 qsg_wipeBatch(m_opaqueBatches.at(i), this, separateIndexBuffer);
1088 for (int i = 0; i < m_alphaBatches.size(); ++i)
1089 qsg_wipeBatch(m_alphaBatches.at(i), this, separateIndexBuffer);
1090 for (int i = 0; i < m_batchPool.size(); ++i)
1091 qsg_wipeBatch(m_batchPool.at(i), this, separateIndexBuffer);
1092 }
1093
1094 for (Node *n : qAsConst(m_nodes))
1095 m_nodeAllocator.release(n);
1096
1097 // Remaining elements...
1098 for (int i=0; i<m_elementsToDelete.size(); ++i) {
1099 Element *e = m_elementsToDelete.at(i);
1100 if (e->isRenderNode)
1101 delete static_cast<RenderNodeElement *>(e);
1102 else
1103 m_elementAllocator.release(e);
1104 }
1105
1106 destroyGraphicsResources();
1107
1108 delete m_visualizer;
1109}
1110
1111void Renderer::destroyGraphicsResources()
1112{
1113 // If this is from the dtor, then the shader manager and its already
1114 // prepared shaders will stay around for other renderers -> the cached data
1115 // in the rhi shaders have to be purged as it may refer to samplers we
1116 // are going to destroy.
1117 m_shaderManager->clearCachedRendererData();
1118
1119 qDeleteAll(m_samplers);
1120 m_stencilClipCommon.reset();
1121 delete m_dummyTexture;
1122 m_visualizer->releaseResources();
1123}
1124
1125void Renderer::releaseCachedResources()
1126{
1127 m_shaderManager->invalidated();
1128
1129 destroyGraphicsResources();
1130
1131 m_samplers.clear();
1132 m_dummyTexture = nullptr;
1133
1134 if (m_rhi)
1135 m_rhi->releaseCachedResources();
1136}
1137
1138void Renderer::invalidateAndRecycleBatch(Batch *b)
1139{
1140 b->invalidate();
1141 for (int i=0; i<m_batchPool.size(); ++i)
1142 if (b == m_batchPool.at(i))
1143 return;
1144 m_batchPool.add(b);
1145}
1146
1147/* The code here does a CPU-side allocation which might seem like a performance issue
1148 * compared to using glMapBuffer or glMapBufferRange which would give me back
1149 * potentially GPU allocated memory and saving me one deep-copy, but...
1150 *
1151 * Because we do a lot of CPU-side transformations, we need random-access memory
1152 * and the memory returned from glMapBuffer/glMapBufferRange is typically
1153 * uncached and thus very slow for our purposes.
1154 *
1155 * ref: http://www.opengl.org/wiki/Buffer_Object
1156 */
1157void Renderer::map(Buffer *buffer, int byteSize, bool isIndexBuf)
1158{
1159 if (!m_context->hasBrokenIndexBufferObjects() && m_visualizer->mode() == Visualizer::VisualizeNothing) {
1160 // Common case, use a shared memory pool for uploading vertex data to avoid
1161 // excessive reevaluation
1162 QDataBuffer<char> &pool = m_context->separateIndexBuffer() && isIndexBuf
1163 ? m_indexUploadPool : m_vertexUploadPool;
1164 if (byteSize > pool.size())
1165 pool.resize(byteSize);
1166 buffer->data = pool.data();
1167 } else if (buffer->size != byteSize) {
1168 free(buffer->data);
1169 buffer->data = (char *) malloc(byteSize);
1170 Q_CHECK_PTR(buffer->data);
1171 }
1172 buffer->size = byteSize;
1173}
1174
1175void Renderer::unmap(Buffer *buffer, bool isIndexBuf)
1176{
1177 if (m_rhi) {
1178 // Batches are pooled and reused which means the QRhiBuffer will be
1179 // still valid in a recycled Batch. We only hit the newBuffer() path
1180 // for brand new Batches.
1181 if (!buffer->buf) {
1182 buffer->buf = m_rhi->newBuffer(QRhiBuffer::Immutable,
1183 isIndexBuf ? QRhiBuffer::IndexBuffer : QRhiBuffer::VertexBuffer,
1184 buffer->size);
1185 if (!buffer->buf->build())
1186 qWarning("Failed to build vertex/index buffer of size %d", buffer->size);
1187// else
1188// qDebug("created rhibuf %p size %d", buffer->buf, buffer->size);
1189 } else {
1190 bool needsRebuild = false;
1191 if (buffer->buf->size() < buffer->size) {
1192 buffer->buf->setSize(buffer->size);
1193 needsRebuild = true;
1194 }
1195 if (buffer->buf->type() != QRhiBuffer::Dynamic
1196 && buffer->nonDynamicChangeCount > DYNAMIC_VERTEX_INDEX_BUFFER_THRESHOLD)
1197 {
1198 buffer->buf->setType(QRhiBuffer::Dynamic);
1199 buffer->nonDynamicChangeCount = 0;
1200 needsRebuild = true;
1201 }
1202 if (needsRebuild) {
1203 //qDebug("rebuilding rhibuf %p size %d type Dynamic", buffer->buf, buffer->size);
1204 buffer->buf->build();
1205 }
1206 }
1207 if (buffer->buf->type() != QRhiBuffer::Dynamic) {
1208 m_resourceUpdates->uploadStaticBuffer(buffer->buf,
1209 QByteArray::fromRawData(buffer->data, buffer->size));
1210 buffer->nonDynamicChangeCount += 1;
1211 } else {
1212 m_resourceUpdates->updateDynamicBuffer(buffer->buf, 0, buffer->size,
1213 QByteArray::fromRawData(buffer->data, buffer->size));
1214 }
1215 if (m_visualizer->mode() == Visualizer::VisualizeNothing)
1216 buffer->data = nullptr;
1217 } else {
1218 if (buffer->id == 0)
1219 glGenBuffers(1, &buffer->id);
1220 GLenum target = isIndexBuf ? GL_ELEMENT_ARRAY_BUFFER : GL_ARRAY_BUFFER;
1221 glBindBuffer(target, buffer->id);
1222 glBufferData(target, buffer->size, buffer->data, m_bufferStrategy);
1223 if (!m_context->hasBrokenIndexBufferObjects() && m_visualizer->mode() == Visualizer::VisualizeNothing)
1224 buffer->data = nullptr;
1225 }
1226}
1227
1228BatchRootInfo *Renderer::batchRootInfo(Node *node)
1229{
1230 BatchRootInfo *info = node->rootInfo();
1231 if (!info) {
1232 if (node->type() == QSGNode::ClipNodeType)
1233 info = new ClipBatchRootInfo;
1234 else {
1235 Q_ASSERT(node->type() == QSGNode::TransformNodeType);
1236 info = new BatchRootInfo;
1237 }
1238 node->data = info;
1239 }
1240 return info;
1241}
1242
1243void Renderer::removeBatchRootFromParent(Node *childRoot)
1244{
1245 BatchRootInfo *childInfo = batchRootInfo(childRoot);
1246 if (!childInfo->parentRoot)
1247 return;
1248 BatchRootInfo *parentInfo = batchRootInfo(childInfo->parentRoot);
1249
1250 Q_ASSERT(parentInfo->subRoots.contains(childRoot));
1251 parentInfo->subRoots.remove(childRoot);
1252 childInfo->parentRoot = nullptr;
1253}
1254
1255void Renderer::registerBatchRoot(Node *subRoot, Node *parentRoot)
1256{
1257 BatchRootInfo *subInfo = batchRootInfo(subRoot);
1258 BatchRootInfo *parentInfo = batchRootInfo(parentRoot);
1259 subInfo->parentRoot = parentRoot;
1260 parentInfo->subRoots << subRoot;
1261}
1262
1263bool Renderer::changeBatchRoot(Node *node, Node *root)
1264{
1265 BatchRootInfo *subInfo = batchRootInfo(node);
1266 if (subInfo->parentRoot == root)
1267 return false;
1268 if (subInfo->parentRoot) {
1269 BatchRootInfo *oldRootInfo = batchRootInfo(subInfo->parentRoot);
1270 oldRootInfo->subRoots.remove(node);
1271 }
1272 BatchRootInfo *newRootInfo = batchRootInfo(root);
1273 newRootInfo->subRoots << node;
1274 subInfo->parentRoot = root;
1275 return true;
1276}
1277
1278void Renderer::nodeChangedBatchRoot(Node *node, Node *root)
1279{
1280 if (node->type() == QSGNode::ClipNodeType || node->isBatchRoot) {
1281 // When we reach a batchroot, we only need to update it. Its subtree
1282 // is relative to that root, so no need to recurse further.
1283 changeBatchRoot(node, root);
1284 return;
1285 } else if (node->type() == QSGNode::GeometryNodeType) {
1286 // Only need to change the root as nodeChanged anyway flags a full update.
1287 Element *e = node->element();
1288 if (e) {
1289 e->root = root;
1290 e->boundsComputed = false;
1291 }
1292 } else if (node->type() == QSGNode::RenderNodeType) {
1293 RenderNodeElement *e = node->renderNodeElement();
1294 if (e)
1295 e->root = root;
1296 }
1297
1298 SHADOWNODE_TRAVERSE(node)
1299 nodeChangedBatchRoot(child, root);
1300}
1301
1302void Renderer::nodeWasTransformed(Node *node, int *vertexCount)
1303{
1304 if (node->type() == QSGNode::GeometryNodeType) {
1305 QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(node->sgNode);
1306 *vertexCount += gn->geometry()->vertexCount();
1307 Element *e = node->element();
1308 if (e) {
1309 e->boundsComputed = false;
1310 if (e->batch) {
1311 if (!e->batch->isOpaque) {
1312 invalidateBatchAndOverlappingRenderOrders(e->batch);
1313 } else if (e->batch->merged) {
1314 e->batch->needsUpload = true;
1315 }
1316 }
1317 }
1318 }
1319
1320 SHADOWNODE_TRAVERSE(node)
1321 nodeWasTransformed(child, vertexCount);
1322}
1323
1324void Renderer::nodeWasAdded(QSGNode *node, Node *shadowParent)
1325{
1326 Q_ASSERT(!m_nodes.contains(node));
1327 if (node->isSubtreeBlocked())
1328 return;
1329
1330 Node *snode = m_nodeAllocator.allocate();
1331 snode->sgNode = node;
1332 m_nodes.insert(node, snode);
1333 if (shadowParent)
1334 shadowParent->append(snode);
1335
1336 if (node->type() == QSGNode::GeometryNodeType) {
1337 snode->data = m_elementAllocator.allocate();
1338 snode->element()->setNode(static_cast<QSGGeometryNode *>(node));
1339
1340 } else if (node->type() == QSGNode::ClipNodeType) {
1341 snode->data = new ClipBatchRootInfo;
1342 m_rebuild |= FullRebuild;
1343
1344 } else if (node->type() == QSGNode::RenderNodeType) {
1345 QSGRenderNode *rn = static_cast<QSGRenderNode *>(node);
1346 RenderNodeElement *e = new RenderNodeElement(rn);
1347 snode->data = e;
1348 Q_ASSERT(!m_renderNodeElements.contains(rn));
1349 m_renderNodeElements.insert(e->renderNode, e);
1350 if (!rn->flags().testFlag(QSGRenderNode::DepthAwareRendering))
1351 m_useDepthBuffer = false;
1352 m_rebuild |= FullRebuild;
1353 }
1354
1355 QSGNODE_TRAVERSE(node)
1356 nodeWasAdded(child, snode);
1357}
1358
1359void Renderer::nodeWasRemoved(Node *node)
1360{
1361 // Prefix traversal as removeBatchRootFromParent below removes nodes
1362 // in a bottom-up manner. Note that we *cannot* use SHADOWNODE_TRAVERSE
1363 // here, because we delete 'child' (when recursed, down below), so we'd
1364 // have a use-after-free.
1365 {
1366 Node *child = node->firstChild();
1367 while (child) {
1368 // Remove (and delete) child
1369 node->remove(child);
1370 nodeWasRemoved(child);
1371 child = node->firstChild();
1372 }
1373 }
1374
1375 if (node->type() == QSGNode::GeometryNodeType) {
1376 Element *e = node->element();
1377 if (e) {
1378 e->removed = true;
1379 m_elementsToDelete.add(e);
1380 e->node = nullptr;
1381 if (e->root) {
1382 BatchRootInfo *info = batchRootInfo(e->root);
1383 info->availableOrders++;
1384 }
1385 if (e->batch) {
1386 e->batch->needsUpload = true;
1387 e->batch->needsPurge = true;
1388 }
1389
1390 }
1391
1392 } else if (node->type() == QSGNode::ClipNodeType) {
1393 removeBatchRootFromParent(node);
1394 delete node->clipInfo();
1395 m_rebuild |= FullRebuild;
1396 m_taggedRoots.remove(node);
1397
1398 } else if (node->isBatchRoot) {
1399 removeBatchRootFromParent(node);
1400 delete node->rootInfo();
1401 m_rebuild |= FullRebuild;
1402 m_taggedRoots.remove(node);
1403
1404 } else if (node->type() == QSGNode::RenderNodeType) {
1405 RenderNodeElement *e = m_renderNodeElements.take(static_cast<QSGRenderNode *>(node->sgNode));
1406 if (e) {
1407 e->removed = true;
1408 m_elementsToDelete.add(e);
1409 if (m_renderNodeElements.isEmpty()) {
1410 static const bool useDepth = qEnvironmentVariableIsEmpty("QSG_NO_DEPTH_BUFFER");
1411 if (m_rhi)
1412 m_useDepthBuffer = useDepth;
1413 else
1414 m_useDepthBuffer = useDepth && m_context->openglContext()->format().depthBufferSize() > 0;
1415 }
1416
1417 if (e->batch != nullptr)
1418 e->batch->needsPurge = true;
1419 }
1420 }
1421
1422 Q_ASSERT(m_nodes.contains(node->sgNode));
1423
1424 m_nodeAllocator.release(m_nodes.take(node->sgNode));
1425}
1426
1427void Renderer::turnNodeIntoBatchRoot(Node *node)
1428{
1429 if (Q_UNLIKELY(debug_change())) qDebug(" - new batch root");
1430 m_rebuild |= FullRebuild;
1431 node->isBatchRoot = true;
1432 node->becameBatchRoot = true;
1433
1434 Node *p = node->parent();
1435 while (p) {
1436 if (p->type() == QSGNode::ClipNodeType || p->isBatchRoot) {
1437 registerBatchRoot(node, p);
1438 break;
1439 }
1440 p = p->parent();
1441 }
1442
1443 SHADOWNODE_TRAVERSE(node)
1444 nodeChangedBatchRoot(child, node);
1445}
1446
1447
1448void Renderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state)
1449{
1450#ifndef QT_NO_DEBUG_OUTPUT
1451 if (Q_UNLIKELY(debug_change())) {
1452 QDebug debug = qDebug();
1453 debug << "dirty:";
1454 if (state & QSGNode::DirtyGeometry)
1455 debug << "Geometry";
1456 if (state & QSGNode::DirtyMaterial)
1457 debug << "Material";
1458 if (state & QSGNode::DirtyMatrix)
1459 debug << "Matrix";
1460 if (state & QSGNode::DirtyNodeAdded)
1461 debug << "Added";
1462 if (state & QSGNode::DirtyNodeRemoved)
1463 debug << "Removed";
1464 if (state & QSGNode::DirtyOpacity)
1465 debug << "Opacity";
1466 if (state & QSGNode::DirtySubtreeBlocked)
1467 debug << "SubtreeBlocked";
1468 if (state & QSGNode::DirtyForceUpdate)
1469 debug << "ForceUpdate";
1470
1471 // when removed, some parts of the node could already have been destroyed
1472 // so don't debug it out.
1473 if (state & QSGNode::DirtyNodeRemoved)
1474 debug << (void *) node << node->type();
1475 else
1476 debug << node;
1477 }
1478#endif
1479 // As this function calls nodeChanged recursively, we do it at the top
1480 // to avoid that any of the others are processed twice.
1481 if (state & QSGNode::DirtySubtreeBlocked) {
1482 Node *sn = m_nodes.value(node);
1483
1484 // Force a batch rebuild if this includes an opacity change
1485 if (state & QSGNode::DirtyOpacity)
1486 m_rebuild |= FullRebuild;
1487
1488 bool blocked = node->isSubtreeBlocked();
1489 if (blocked && sn) {
1490 nodeChanged(node, QSGNode::DirtyNodeRemoved);
1491 Q_ASSERT(m_nodes.value(node) == 0);
1492 } else if (!blocked && !sn) {
1493 nodeChanged(node, QSGNode::DirtyNodeAdded);
1494 }
1495 return;
1496 }
1497
1498 if (state & QSGNode::DirtyNodeAdded) {
1499 if (nodeUpdater()->isNodeBlocked(node, rootNode())) {
1500 QSGRenderer::nodeChanged(node, state);
1501 return;
1502 }
1503 if (node == rootNode())
1504 nodeWasAdded(node, nullptr);
1505 else
1506 nodeWasAdded(node, m_nodes.value(node->parent()));
1507 }
1508
1509 // Mark this node dirty in the shadow tree.
1510 Node *shadowNode = m_nodes.value(node);
1511
1512 // Blocked subtrees won't have shadow nodes, so we can safely abort
1513 // here..
1514 if (!shadowNode) {
1515 QSGRenderer::nodeChanged(node, state);
1516 return;
1517 }
1518
1519 shadowNode->dirtyState |= state;
1520
1521 if (state & QSGNode::DirtyMatrix && !shadowNode->isBatchRoot) {
1522 Q_ASSERT(node->type() == QSGNode::TransformNodeType);
1523 if (node->m_subtreeRenderableCount > m_batchNodeThreshold) {
1524 turnNodeIntoBatchRoot(shadowNode);
1525 } else {
1526 int vertices = 0;
1527 nodeWasTransformed(shadowNode, &vertices);
1528 if (vertices > m_batchVertexThreshold) {
1529 turnNodeIntoBatchRoot(shadowNode);
1530 }
1531 }
1532 }
1533
1534 if (state & QSGNode::DirtyGeometry && node->type() == QSGNode::GeometryNodeType) {
1535 QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(node);
1536 Element *e = shadowNode->element();
1537 if (e) {
1538 e->boundsComputed = false;
1539 Batch *b = e->batch;
1540 if (b) {
1541 if (!e->batch->geometryWasChanged(gn) || !e->batch->isOpaque) {
1542 invalidateBatchAndOverlappingRenderOrders(e->batch);
1543 } else {
1544 b->needsUpload = true;
1545 }
1546 }
1547 }
1548 }
1549
1550 if (state & QSGNode::DirtyMaterial && node->type() == QSGNode::GeometryNodeType) {
1551 Element *e = shadowNode->element();
1552 if (e) {
1553 bool blended = hasMaterialWithBlending(static_cast<QSGGeometryNode *>(node));
1554 if (e->isMaterialBlended != blended) {
1555 m_rebuild |= Renderer::FullRebuild;
1556 e->isMaterialBlended = blended;
1557 } else if (e->batch) {
1558 if (e->batch->isMaterialCompatible(e) == BatchBreaksOnCompare)
1559 invalidateBatchAndOverlappingRenderOrders(e->batch);
1560 } else {
1561 m_rebuild |= Renderer::BuildBatches;
1562 }
1563 }
1564 }
1565
1566 // Mark the shadow tree dirty all the way back to the root...
1567 QSGNode::DirtyState dirtyChain = state & (QSGNode::DirtyNodeAdded
1568 | QSGNode::DirtyOpacity
1569 | QSGNode::DirtyMatrix
1570 | QSGNode::DirtySubtreeBlocked
1571 | QSGNode::DirtyForceUpdate);
1572 if (dirtyChain != 0) {
1573 dirtyChain = QSGNode::DirtyState(dirtyChain << 16);
1574 Node *sn = shadowNode->parent();
1575 while (sn) {
1576 sn->dirtyState |= dirtyChain;
1577 sn = sn->parent();
1578 }
1579 }
1580
1581 // Delete happens at the very end because it deletes the shadownode.
1582 if (state & QSGNode::DirtyNodeRemoved) {
1583 Node *parent = shadowNode->parent();
1584 if (parent)
1585 parent->remove(shadowNode);
1586 nodeWasRemoved(shadowNode);
1587 Q_ASSERT(m_nodes.value(node) == 0);
1588 }
1589
1590 QSGRenderer::nodeChanged(node, state);
1591}
1592
1593/*
1594 * Traverses the tree and builds two list of geometry nodes. One for
1595 * the opaque and one for the translucent. These are populated
1596 * in the order they should visually appear in, meaning first
1597 * to the back and last to the front.
1598 *
1599 * We split opaque and translucent as we can perform different
1600 * types of reordering / batching strategies on them, depending
1601 *
1602 * Note: It would be tempting to use the shadow nodes instead of the QSGNodes
1603 * for traversal to avoid hash lookups, but the order of the children
1604 * is important and they are not preserved in the shadow tree, so we must
1605 * use the actual QSGNode tree.
1606 */
1607void Renderer::buildRenderLists(QSGNode *node)
1608{
1609 if (node->isSubtreeBlocked())
1610 return;
1611
1612 Node *shadowNode = m_nodes.value(node);
1613 Q_ASSERT(shadowNode);
1614
1615 if (node->type() == QSGNode::GeometryNodeType) {
1616 QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(node);
1617
1618 Element *e = shadowNode->element();
1619 Q_ASSERT(e);
1620
1621 bool opaque = gn->inheritedOpacity() > OPAQUE_LIMIT && !(gn->activeMaterial()->flags() & QSGMaterial::Blending);
1622 if (opaque && m_useDepthBuffer)
1623 m_opaqueRenderList << e;
1624 else
1625 m_alphaRenderList << e;
1626
1627 e->order = ++m_nextRenderOrder;
1628 // Used while rebuilding partial roots.
1629 if (m_partialRebuild)
1630 e->orphaned = false;
1631
1632 } else if (node->type() == QSGNode::ClipNodeType || shadowNode->isBatchRoot) {
1633 Q_ASSERT(m_nodes.contains(node));
1634 BatchRootInfo *info = batchRootInfo(shadowNode);
1635 if (node == m_partialRebuildRoot) {
1636 m_nextRenderOrder = info->firstOrder;
1637 QSGNODE_TRAVERSE(node)
1638 buildRenderLists(child);
1639 m_nextRenderOrder = info->lastOrder + 1;
1640 } else {
1641 int currentOrder = m_nextRenderOrder;
1642 QSGNODE_TRAVERSE(node)
1643 buildRenderLists(child);
1644 int padding = (m_nextRenderOrder - currentOrder) >> 2;
1645 info->firstOrder = currentOrder;
1646 info->availableOrders = padding;
1647 info->lastOrder = m_nextRenderOrder + padding;
1648 m_nextRenderOrder = info->lastOrder;
1649 }
1650 return;
1651 } else if (node->type() == QSGNode::RenderNodeType) {
1652 RenderNodeElement *e = shadowNode->renderNodeElement();
1653 m_alphaRenderList << e;
1654 e->order = ++m_nextRenderOrder;
1655 Q_ASSERT(e);
1656 }
1657
1658 QSGNODE_TRAVERSE(node)
1659 buildRenderLists(child);
1660}
1661
1662void Renderer::tagSubRoots(Node *node)
1663{
1664 BatchRootInfo *i = batchRootInfo(node);
1665 m_taggedRoots << node;
1666 for (QSet<Node *>::const_iterator it = i->subRoots.constBegin();
1667 it != i->subRoots.constEnd(); ++it) {
1668 tagSubRoots(*it);
1669 }
1670}
1671
1672static void qsg_addOrphanedElements(QDataBuffer<Element *> &orphans, const QDataBuffer<Element *> &renderList)
1673{
1674 orphans.reset();
1675 for (int i=0; i<renderList.size(); ++i) {
1676 Element *e = renderList.at(i);
1677 if (e && !e->removed) {
1678 e->orphaned = true;
1679 orphans.add(e);
1680 }
1681 }
1682}
1683
1684static void qsg_addBackOrphanedElements(QDataBuffer<Element *> &orphans, QDataBuffer<Element *> &renderList)
1685{
1686 for (int i=0; i<orphans.size(); ++i) {
1687 Element *e = orphans.at(i);
1688 if (e->orphaned)
1689 renderList.add(e);
1690 }
1691 orphans.reset();
1692}
1693
1694/*
1695 * To rebuild the tagged roots, we start by putting all subroots of tagged
1696 * roots into the list of tagged roots. This is to make the rest of the
1697 * algorithm simpler.
1698 *
1699 * Second, we invalidate all batches which belong to tagged roots, which now
1700 * includes the entire subtree under a given root
1701 *
1702 * Then we call buildRenderLists for all tagged subroots which do not have
1703 * parents which are tagged, aka, we traverse only the topmosts roots.
1704 *
1705 * Then we sort the render lists based on their render order, to restore the
1706 * right order for rendering.
1707 */
1708void Renderer::buildRenderListsForTaggedRoots()
1709{
1710 // Flag any element that is currently in the render lists, but which
1711 // is not in a batch. This happens when we have a partial rebuild
1712 // in one sub tree while we have a BuildBatches change in another
1713 // isolated subtree. So that batch-building takes into account
1714 // these "orphaned" nodes, we flag them now. The ones under tagged
1715 // roots will be cleared again. The remaining ones are added into the
1716 // render lists so that they contain all visual nodes after the
1717 // function completes.
1718 qsg_addOrphanedElements(m_tmpOpaqueElements, m_opaqueRenderList);
1719 qsg_addOrphanedElements(m_tmpAlphaElements, m_alphaRenderList);
1720
1721 // Take a copy now, as we will be adding to this while traversing..
1722 QSet<Node *> roots = m_taggedRoots;
1723 for (QSet<Node *>::const_iterator it = roots.constBegin();
1724 it != roots.constEnd(); ++it) {
1725 tagSubRoots(*it);
1726 }
1727
1728 for (int i=0; i<m_opaqueBatches.size(); ++i) {
1729 Batch *b = m_opaqueBatches.at(i);
1730 if (m_taggedRoots.contains(b->root))
1731 invalidateAndRecycleBatch(b);
1732
1733 }
1734 for (int i=0; i<m_alphaBatches.size(); ++i) {
1735 Batch *b = m_alphaBatches.at(i);
1736 if (m_taggedRoots.contains(b->root))
1737 invalidateAndRecycleBatch(b);
1738 }
1739
1740 m_opaqueRenderList.reset();
1741 m_alphaRenderList.reset();
1742 int maxRenderOrder = m_nextRenderOrder;
1743 m_partialRebuild = true;
1744 // Traverse each root, assigning it
1745 for (QSet<Node *>::const_iterator it = m_taggedRoots.constBegin();
1746 it != m_taggedRoots.constEnd(); ++it) {
1747 Node *root = *it;
1748 BatchRootInfo *i = batchRootInfo(root);
1749 if ((!i->parentRoot || !m_taggedRoots.contains(i->parentRoot))
1750 && !nodeUpdater()->isNodeBlocked(root->sgNode, rootNode())) {
1751 m_nextRenderOrder = i->firstOrder;
1752 m_partialRebuildRoot = root->sgNode;
1753 buildRenderLists(root->sgNode);
1754 }
1755 }
1756 m_partialRebuild = false;
1757 m_partialRebuildRoot = nullptr;
1758 m_taggedRoots.clear();
1759 m_nextRenderOrder = qMax(m_nextRenderOrder, maxRenderOrder);
1760
1761 // Add orphaned elements back into the list and then sort it..
1762 qsg_addBackOrphanedElements(m_tmpOpaqueElements, m_opaqueRenderList);
1763 qsg_addBackOrphanedElements(m_tmpAlphaElements, m_alphaRenderList);
1764
1765 if (m_opaqueRenderList.size())
1766 std::sort(&m_opaqueRenderList.first(), &m_opaqueRenderList.last() + 1, qsg_sort_element_decreasing_order);
1767 if (m_alphaRenderList.size())
1768 std::sort(&m_alphaRenderList.first(), &m_alphaRenderList.last() + 1, qsg_sort_element_increasing_order);
1769
1770}
1771
1772void Renderer::buildRenderListsFromScratch()
1773{
1774 m_opaqueRenderList.reset();
1775 m_alphaRenderList.reset();
1776
1777 for (int i=0; i<m_opaqueBatches.size(); ++i)
1778 invalidateAndRecycleBatch(m_opaqueBatches.at(i));
1779 for (int i=0; i<m_alphaBatches.size(); ++i)
1780 invalidateAndRecycleBatch(m_alphaBatches.at(i));
1781 m_opaqueBatches.reset();
1782 m_alphaBatches.reset();
1783
1784 m_nextRenderOrder = 0;
1785
1786 buildRenderLists(rootNode());
1787}
1788
1789void Renderer::invalidateBatchAndOverlappingRenderOrders(Batch *batch)
1790{
1791 Q_ASSERT(batch);
1792 Q_ASSERT(batch->first);
1793
1794 if (m_renderOrderRebuildLower < 0 || batch->first->order < m_renderOrderRebuildLower)
1795 m_renderOrderRebuildLower = batch->first->order;
1796 if (m_renderOrderRebuildUpper < 0 || batch->lastOrderInBatch > m_renderOrderRebuildUpper)
1797 m_renderOrderRebuildUpper = batch->lastOrderInBatch;
1798
1799 batch->invalidate();
1800
1801 for (int i=0; i<m_alphaBatches.size(); ++i) {
1802 Batch *b = m_alphaBatches.at(i);
1803 if (b->first) {
1804 int bf = b->first->order;
1805 int bl = b->lastOrderInBatch;
1806 if (bl > m_renderOrderRebuildLower && bf < m_renderOrderRebuildUpper)
1807 b->invalidate();
1808 }
1809 }
1810
1811 m_rebuild |= BuildBatches;
1812}
1813
1814/* Clean up batches by making it a consecutive list of "valid"
1815 * batches and moving all invalidated batches to the batches pool.
1816 */
1817void Renderer::cleanupBatches(QDataBuffer<Batch *> *batches) {
1818 if (batches->size()) {
1819 std::stable_sort(&batches->first(), &batches->last() + 1, qsg_sort_batch_is_valid);
1820 int count = 0;
1821 while (count < batches->size() && batches->at(count)->first)
1822 ++count;
1823 for (int i=count; i<batches->size(); ++i)
1824 invalidateAndRecycleBatch(batches->at(i));
1825 batches->resize(count);
1826 }
1827}
1828
1829void Renderer::prepareOpaqueBatches()
1830{
1831 for (int i=m_opaqueRenderList.size() - 1; i >= 0; --i) {
1832 Element *ei = m_opaqueRenderList.at(i);
1833 if (!ei || ei->batch || ei->node->geometry()->vertexCount() == 0)
1834 continue;
1835 Batch *batch = newBatch();
1836 batch->first = ei;
1837 batch->root = ei->root;
1838 batch->isOpaque = true;
1839 batch->needsUpload = true;
1840 batch->positionAttribute = qsg_positionAttribute(ei->node->geometry());
1841
1842 m_opaqueBatches.add(batch);
1843
1844 ei->batch = batch;
1845 Element *next = ei;
1846
1847 QSGGeometryNode *gni = ei->node;
1848
1849 for (int j = i - 1; j >= 0; --j) {
1850 Element *ej = m_opaqueRenderList.at(j);
1851 if (!ej)
1852 continue;
1853 if (ej->root != ei->root)
1854 break;
1855 if (ej->batch || ej->node->geometry()->vertexCount() == 0)
1856 continue;
1857
1858 QSGGeometryNode *gnj = ej->node;
1859
1860 if (gni->clipList() == gnj->clipList()
1861 && gni->geometry()->drawingMode() == gnj->geometry()->drawingMode()
1862 && (gni->geometry()->drawingMode() != QSGGeometry::DrawLines || gni->geometry()->lineWidth() == gnj->geometry()->lineWidth())
1863 && gni->geometry()->attributes() == gnj->geometry()->attributes()
1864 && gni->inheritedOpacity() == gnj->inheritedOpacity()
1865 && gni->activeMaterial()->type() == gnj->activeMaterial()->type()
1866 && gni->activeMaterial()->compare(gnj->activeMaterial()) == 0) {
1867 ej->batch = batch;
1868 next->nextInBatch = ej;
1869 next = ej;
1870 }
1871 }
1872
1873 batch->lastOrderInBatch = next->order;
1874 }
1875}
1876
1877bool Renderer::checkOverlap(int first, int last, const Rect &bounds)
1878{
1879 for (int i=first; i<=last; ++i) {
1880 Element *e = m_alphaRenderList.at(i);
1881 if (!e || e->batch)
1882 continue;
1883 Q_ASSERT(e->boundsComputed);
1884 if (e->bounds.intersects(bounds))
1885 return true;
1886 }
1887 return false;
1888}
1889
1890/*
1891 *
1892 * To avoid the O(n^2) checkOverlap check in most cases, we have the
1893 * overlapBounds which is the union of all bounding rects to check overlap
1894 * for. We know that if it does not overlap, then none of the individual
1895 * ones will either. For the typical list case, this results in no calls
1896 * to checkOverlap what-so-ever. This also ensures that when all consecutive
1897 * items are matching (such as a table of text), we don't build up an
1898 * overlap bounds and thus do not require full overlap checks.
1899 */
1900
1901void Renderer::prepareAlphaBatches()
1902{
1903 for (int i=0; i<m_alphaRenderList.size(); ++i) {
1904 Element *e = m_alphaRenderList.at(i);
1905 if (!e || e->isRenderNode)
1906 continue;
1907 Q_ASSERT(!e->removed);
1908 e->ensureBoundsValid();
1909 }
1910
1911 for (int i=0; i<m_alphaRenderList.size(); ++i) {
1912 Element *ei = m_alphaRenderList.at(i);
1913 if (!ei || ei->batch)
1914 continue;
1915
1916 if (ei->isRenderNode) {
1917 Batch *rnb = newBatch();
1918 rnb->first = ei;
1919 rnb->root = ei->root;
1920 rnb->isOpaque = false;
1921 rnb->isRenderNode = true;
1922 ei->batch = rnb;
1923 m_alphaBatches.add(rnb);
1924 continue;
1925 }
1926
1927 if (ei->node->geometry()->vertexCount() == 0)
1928 continue;
1929
1930 Batch *batch = newBatch();
1931 batch->first = ei;
1932 batch->root = ei->root;
1933 batch->isOpaque = false;
1934 batch->needsUpload = true;
1935 m_alphaBatches.add(batch);
1936 ei->batch = batch;
1937
1938 QSGGeometryNode *gni = ei->node;
1939 batch->positionAttribute = qsg_positionAttribute(gni->geometry());
1940
1941 Rect overlapBounds;
1942 overlapBounds.set(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX);
1943
1944 Element *next = ei;
1945
1946 for (int j = i + 1; j < m_alphaRenderList.size(); ++j) {
1947 Element *ej = m_alphaRenderList.at(j);
1948 if (!ej)
1949 continue;
1950 if (ej->root != ei->root || ej->isRenderNode)
1951 break;
1952 if (ej->batch)
1953 continue;
1954
1955 QSGGeometryNode *gnj = ej->node;
1956 if (gnj->geometry()->vertexCount() == 0)
1957 continue;
1958
1959 if (gni->clipList() == gnj->clipList()
1960 && gni->geometry()->drawingMode() == gnj->geometry()->drawingMode()
1961 && (gni->geometry()->drawingMode() != QSGGeometry::DrawLines || gni->geometry()->lineWidth() == gnj->geometry()->lineWidth())
1962 && gni->geometry()->attributes() == gnj->geometry()->attributes()
1963 && gni->inheritedOpacity() == gnj->inheritedOpacity()
1964 && gni->activeMaterial()->type() == gnj->activeMaterial()->type()
1965 && gni->activeMaterial()->compare(gnj->activeMaterial()) == 0) {
1966 if (!overlapBounds.intersects(ej->bounds) || !checkOverlap(i+1, j - 1, ej->bounds)) {
1967 ej->batch = batch;
1968 next->nextInBatch = ej;
1969 next = ej;
1970 } else {
1971 /* When we come across a compatible element which hits an overlap, we
1972 * need to stop the batch right away. We cannot add more elements
1973 * to the current batch as they will be rendered before the batch that the
1974 * current 'ej' will be added to.
1975 */
1976 break;
1977 }
1978 } else {
1979 overlapBounds |= ej->bounds;
1980 }
1981 }
1982
1983 batch->lastOrderInBatch = next->order;
1984 }
1985
1986
1987}
1988
1989static inline int qsg_fixIndexCount(int iCount, int drawMode)
1990{
1991 switch (drawMode) {
1992 case QSGGeometry::DrawTriangleStrip:
1993 // Merged triangle strips need to contain degenerate triangles at the beginning and end.
1994 // One could save 2 uploaded ushorts here by ditching the padding for the front of the
1995 // first and the end of the last, but for simplicity, we simply don't care.
1996 // Those extra triangles will be skipped while drawing to preserve the strip's parity
1997 // anyhow.
1998 return iCount + 2;
1999 case QSGGeometry::DrawLines:
2000 // For lines we drop the last vertex if the number of vertices is uneven.
2001 return iCount - (iCount % 2);
2002 case QSGGeometry::DrawTriangles:
2003 // For triangles we drop trailing vertices until the result is divisible by 3.
2004 return iCount - (iCount % 3);
2005 default:
2006 return iCount;
2007 }
2008}
2009
2010/* These parameters warrant some explanation...
2011 *
2012 * vaOffset: The byte offset into the vertex data to the location of the
2013 * 2D float point vertex attributes.
2014 *
2015 * vertexData: destination where the geometry's vertex data should go
2016 *
2017 * zData: destination of geometries injected Z positioning
2018 *
2019 * indexData: destination of the indices for this element
2020 *
2021 * iBase: The starting index for this element in the batch
2022 */
2023
2024void Renderer::uploadMergedElement(Element *e, int vaOffset, char **vertexData, char **zData, char **indexData, void *iBasePtr, int *indexCount)
2025{
2026 if (Q_UNLIKELY(debug_upload())) qDebug() << " - uploading element:" << e << e->node << (void *) *vertexData << (qintptr) (*zData - *vertexData) << (qintptr) (*indexData - *vertexData);
2027 QSGGeometry *g = e->node->geometry();
2028
2029 const QMatrix4x4 &localx = *e->node->matrix();
2030
2031 const int vCount = g->vertexCount();
2032 const int vSize = g->sizeOfVertex();
2033 memcpy(*vertexData, g->vertexData(), vSize * vCount);
2034
2035 // apply vertex transform..
2036 char *vdata = *vertexData + vaOffset;
2037 if (((const QMatrix4x4_Accessor &) localx).flagBits == 1) {
2038 for (int i=0; i<vCount; ++i) {
2039 Pt *p = (Pt *) vdata;
2040 p->x += ((const QMatrix4x4_Accessor &) localx).m[3][0];
2041 p->y += ((const QMatrix4x4_Accessor &) localx).m[3][1];
2042 vdata += vSize;
2043 }
2044 } else if (((const QMatrix4x4_Accessor &) localx).flagBits > 1) {
2045 for (int i=0; i<vCount; ++i) {
2046 ((Pt *) vdata)->map(localx);
2047 vdata += vSize;
2048 }
2049 }
2050
2051 if (m_useDepthBuffer) {
2052 float *vzorder = (float *) *zData;
2053 float zorder = 1.0f - e->order * m_zRange;
2054 for (int i=0; i<vCount; ++i)
2055 vzorder[i] = zorder;
2056 *zData += vCount * sizeof(float);
2057 }
2058
2059 int iCount = g->indexCount();
2060 if (m_uint32IndexForRhi) {
2061 // can only happen when using the rhi
2062 quint32 *iBase = (quint32 *) iBasePtr;
2063 quint32 *indices = (quint32 *) *indexData;
2064 if (iCount == 0) {
2065 iCount = vCount;
2066 if (g->drawingMode() == QSGGeometry::DrawTriangleStrip)
2067 *indices++ = *iBase;
2068 else
2069 iCount = qsg_fixIndexCount(iCount, g->drawingMode());
2070
2071 for (int i=0; i<iCount; ++i)
2072 indices[i] = *iBase + i;
2073 } else {
2074 // source index data in QSGGeometry is always ushort (we would not merge otherwise)
2075 const quint16 *srcIndices = g->indexDataAsUShort();
2076 if (g->drawingMode() == QSGGeometry::DrawTriangleStrip)
2077 *indices++ = *iBase + srcIndices[0];
2078 else
2079 iCount = qsg_fixIndexCount(iCount, g->drawingMode());
2080
2081 for (int i=0; i<iCount; ++i)
2082 indices[i] = *iBase + srcIndices[i];
2083 }
2084 if (g->drawingMode() == QSGGeometry::DrawTriangleStrip) {
2085 indices[iCount] = indices[iCount - 1];
2086 iCount += 2;
2087 }
2088 *iBase += vCount;
2089 } else {
2090 // normally batching is only done for ushort index data
2091 quint16 *iBase = (quint16 *) iBasePtr;
2092 quint16 *indices = (quint16 *) *indexData;
2093 if (iCount == 0) {
2094 iCount = vCount;
2095 if (g->drawingMode() == QSGGeometry::DrawTriangleStrip)
2096 *indices++ = *iBase;
2097 else
2098 iCount = qsg_fixIndexCount(iCount, g->drawingMode());
2099
2100 for (int i=0; i<iCount; ++i)
2101 indices[i] = *iBase + i;
2102 } else {
2103 const quint16 *srcIndices = g->indexDataAsUShort();
2104 if (g->drawingMode() == QSGGeometry::DrawTriangleStrip)
2105 *indices++ = *iBase + srcIndices[0];
2106 else
2107 iCount = qsg_fixIndexCount(iCount, g->drawingMode());
2108
2109 for (int i=0; i<iCount; ++i)
2110 indices[i] = *iBase + srcIndices[i];
2111 }
2112 if (g->drawingMode() == QSGGeometry::DrawTriangleStrip) {
2113 indices[iCount] = indices[iCount - 1];
2114 iCount += 2;
2115 }
2116 *iBase += vCount;
2117 }
2118
2119 *vertexData += vCount * vSize;
2120 *indexData += iCount * mergedIndexElemSize();
2121 *indexCount += iCount;
2122}
2123
2124QMatrix4x4 qsg_matrixForRoot(Node *node)
2125{
2126 if (node->type() == QSGNode::TransformNodeType)
2127 return static_cast<QSGTransformNode *>(node->sgNode)->combinedMatrix();
2128 Q_ASSERT(node->type() == QSGNode::ClipNodeType);
2129 QSGClipNode *c = static_cast<QSGClipNode *>(node->sgNode);
2130 return *c->matrix();
2131}
2132
2133void Renderer::uploadBatch(Batch *b)
2134{
2135 // Early out if nothing has changed in this batch..
2136 if (!b->needsUpload) {
2137 if (Q_UNLIKELY(debug_upload())) qDebug() << " Batch:" << b << "already uploaded...";
2138 return;
2139 }
2140
2141 if (!b->first) {
2142 if (Q_UNLIKELY(debug_upload())) qDebug() << " Batch:" << b << "is invalid...";
2143 return;
2144 }
2145
2146 if (b->isRenderNode) {
2147 if (Q_UNLIKELY(debug_upload())) qDebug() << " Batch: " << b << "is a render node...";
2148 return;
2149 }
2150
2151 // Figure out if we can merge or not, if not, then just render the batch as is..
2152 Q_ASSERT(b->first);
2153 Q_ASSERT(b->first->node);
2154
2155 QSGGeometryNode *gn = b->first->node;
2156 QSGGeometry *g = gn->geometry();
2157 QSGMaterial::Flags flags = gn->activeMaterial()->flags();
2158 bool canMerge = (g->drawingMode() == QSGGeometry::DrawTriangles || g->drawingMode() == QSGGeometry::DrawTriangleStrip ||
2159 g->drawingMode() == QSGGeometry::DrawLines || g->drawingMode() == QSGGeometry::DrawPoints)
2160 && b->positionAttribute >= 0
2161 && g->indexType() == QSGGeometry::UnsignedShortType
2162 && (flags & (QSGMaterial::CustomCompileStep | QSGMaterial_FullMatrix)) == 0
2163 && ((flags & QSGMaterial::RequiresFullMatrixExceptTranslate) == 0 || b->isTranslateOnlyToRoot())
2164 && b->isSafeToBatch();
2165
2166 b->merged = canMerge;
2167
2168 // Figure out how much memory we need...
2169 b->vertexCount = 0;
2170 b->indexCount = 0;
2171 int unmergedIndexSize = 0;
2172 Element *e = b->first;
2173
2174 while (e) {
2175 QSGGeometry *eg = e->node->geometry();
2176 b->vertexCount += eg->vertexCount();
2177 int iCount = eg->indexCount();
2178 if (b->merged) {
2179 if (iCount == 0)
2180 iCount = eg->vertexCount();
2181 iCount = qsg_fixIndexCount(iCount, g->drawingMode());
2182 } else {
2183 const int effectiveIndexSize = m_uint32IndexForRhi ? sizeof(quint32) : eg->sizeOfIndex();
2184 unmergedIndexSize += iCount * effectiveIndexSize;
2185 }
2186 b->indexCount += iCount;
2187 e = e->nextInBatch;
2188 }
2189
2190 // Abort if there are no vertices in this batch.. We abort this late as
2191 // this is a broken usecase which we do not care to optimize for...
2192 if (b->vertexCount == 0 || (b->merged && b->indexCount == 0))
2193 return;
2194
2195 /* Allocate memory for this batch. Merged batches are divided into three separate blocks
2196 1. Vertex data for all elements, as they were in the QSGGeometry object, but
2197 with the tranform relative to this batch's root applied. The vertex data
2198 is otherwise unmodified.
2199 2. Z data for all elements, derived from each elements "render order".
2200 This is present for merged data only.
2201 3. Indices for all elements, as they were in the QSGGeometry object, but
2202 adjusted so that each index matches its.
2203 And for TRIANGLE_STRIPs, we need to insert degenerate between each
2204 primitive. These are unsigned shorts for merged and arbitrary for
2205 non-merged.
2206 */
2207 int bufferSize = b->vertexCount * g->sizeOfVertex();
2208 int ibufferSize = 0;
2209 if (b->merged) {
2210 ibufferSize = b->indexCount * mergedIndexElemSize();
2211 if (m_useDepthBuffer)
2212 bufferSize += b->vertexCount * sizeof(float);
2213 } else {
2214 ibufferSize = unmergedIndexSize;
2215 }
2216
2217 const bool separateIndexBuffer = m_context->separateIndexBuffer();
2218 if (separateIndexBuffer)
2219 map(&b->ibo, ibufferSize, true);
2220 else
2221 bufferSize += ibufferSize;
2222 map(&b->vbo, bufferSize);
2223
2224 if (Q_UNLIKELY(debug_upload())) qDebug() << " - batch" << b << " first:" << b->first << " root:"
2225 << b->root << " merged:" << b->merged << " positionAttribute" << b->positionAttribute
2226 << " vbo:" << b->vbo.id << ":" << b->vbo.size;
2227
2228 if (b->merged) {
2229 char *vertexData = b->vbo.data;
2230 char *zData = vertexData + b->vertexCount * g->sizeOfVertex();
2231 char *indexData = separateIndexBuffer
2232 ? b->ibo.data
2233 : zData + (int(m_useDepthBuffer) * b->vertexCount * sizeof(float));
2234
2235 quint16 iOffset16 = 0;
2236 quint32 iOffset32 = 0;
2237 e = b->first;
2238 uint verticesInSet = 0;
2239 // Start a new set already after 65534 vertices because 0xFFFF may be
2240 // used for an always-on primitive restart with some apis (adapt for
2241 // uint32 indices as appropriate).
2242 const uint verticesInSetLimit = m_uint32IndexForRhi ? 0xfffffffe : 0xfffe;
2243 int indicesInSet = 0;
2244 b->drawSets.reset();
2245 int drawSetIndices = separateIndexBuffer ? 0 : indexData - vertexData;
2246 const char *indexBase = separateIndexBuffer ? b->ibo.data : b->vbo.data;
2247 b->drawSets << DrawSet(0, zData - vertexData, drawSetIndices);
2248 while (e) {
2249 verticesInSet += e->node->geometry()->vertexCount();
2250 if (verticesInSet > verticesInSetLimit) {
2251 b->drawSets.last().indexCount = indicesInSet;
2252 if (g->drawingMode() == QSGGeometry::DrawTriangleStrip) {
2253 b->drawSets.last().indices += 1 * mergedIndexElemSize();
2254 b->drawSets.last().indexCount -= 2;
2255 }
2256 drawSetIndices = indexData - indexBase;
2257 b->drawSets << DrawSet(vertexData - b->vbo.data,
2258 zData - b->vbo.