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