1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
5** Copyright (C) 2016 Robin Burchell <robin.burchell@viroteck.net>
6** Contact: https://www.qt.io/licensing/
7**
8** This file is part of the QtQuick module of the Qt Toolkit.
9**
10** $QT_BEGIN_LICENSE:LGPL$
11** Commercial License Usage
12** Licensees holding valid commercial Qt licenses may use this file in
13** accordance with the commercial license agreement provided with the
14** Software or, alternatively, in accordance with the terms contained in
15** a written agreement between you and The Qt Company. For licensing terms
16** and conditions see https://www.qt.io/terms-conditions. For further
17** information use the contact form at https://www.qt.io/contact-us.
18**
19** GNU Lesser General Public License Usage
20** Alternatively, this file may be used under the terms of the GNU Lesser
21** General Public License version 3 as published by the Free Software
22** Foundation and appearing in the file LICENSE.LGPL3 included in the
23** packaging of this file. Please review the following information to
24** ensure the GNU Lesser General Public License version 3 requirements
25** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
26**
27** GNU General Public License Usage
28** Alternatively, this file may be used under the terms of the GNU
29** General Public License version 2.0 or (at your option) the GNU General
30** Public license version 3 or any later version approved by the KDE Free
31** Qt Foundation. The licenses are as published by the Free Software
32** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
33** included in the packaging of this file. Please review the following
34** information to ensure the GNU General Public License requirements will
35** be met: https://www.gnu.org/licenses/gpl-2.0.html and
36** https://www.gnu.org/licenses/gpl-3.0.html.
37**
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#ifndef QSGBATCHRENDERER_P_H
43#define QSGBATCHRENDERER_P_H
44
45//
46// W A R N I N G
47// -------------
48//
49// This file is not part of the Qt API. It exists purely as an
50// implementation detail. This header file may change from version to
51// version without notice, or even be removed.
52//
53// We mean it.
54//
55
56#include <private/qsgrenderer_p.h>
57#include <private/qsgdefaultrendercontext_p.h>
58#include <private/qsgnodeupdater_p.h>
59#include <private/qsgrendernode_p.h>
60#include <private/qdatabuffer_p.h>
61#include <private/qsgtexture_p.h>
62
63#include <QtCore/QBitArray>
64#include <QtCore/QStack>
65#include <QtGui/QOpenGLFunctions>
66
67#include <QtGui/private/qrhi_p.h>
68
69QT_BEGIN_NAMESPACE
70
71class QOpenGLVertexArrayObject;
72
73namespace QSGBatchRenderer
74{
75
76#define QSG_RENDERER_COORD_LIMIT 1000000.0f
77
78struct Vec;
79struct Rect;
80struct Buffer;
81struct Chunk;
82struct Batch;
83struct Node;
84class Updater;
85class Renderer;
86class ShaderManager;
87
88template <typename Type, int PageSize> class AllocatorPage
89{
90public:
91 // The memory used by this allocator
92 char data[sizeof(Type) * PageSize];
93
94 // 'blocks' contains a list of free indices which can be allocated.
95 // The first available index is found in PageSize - available.
96 int blocks[PageSize];
97
98 // 'available' is the number of available instances this page has left to allocate.
99 int available;
100
101 // This is not strictly needed, but useful for sanity checking and anyway
102 // pretty small..
103 QBitArray allocated;
104
105 AllocatorPage()
106 : available(PageSize)
107 , allocated(PageSize)
108 {
109 for (int i=0; i<PageSize; ++i)
110 blocks[i] = i;
111
112 // Zero out all new pages.
113 memset(data, 0, sizeof(data));
114 }
115
116 const Type *at(uint index) const
117 {
118 return (Type *) &data[index * sizeof(Type)];
119 }
120
121 Type *at(uint index)
122 {
123 return (Type *) &data[index * sizeof(Type)];
124 }
125};
126
127template <typename Type, int PageSize> class Allocator
128{
129public:
130 Allocator()
131 {
132 pages.push_back(new AllocatorPage<Type, PageSize>());
133 }
134
135 ~Allocator()
136 {
137 qDeleteAll(pages);
138 }
139
140 Type *allocate()
141 {
142 AllocatorPage<Type, PageSize> *p = 0;
143 for (int i = m_freePage; i < pages.size(); i++) {
144 if (pages.at(i)->available > 0) {
145 p = pages.at(i);
146 m_freePage = i;
147 break;
148 }
149 }
150
151 // we couldn't find a free page from m_freePage to the last page.
152 // either there is no free pages, or there weren't any in the area we
153 // scanned: rescanning is expensive, so let's just assume there isn't
154 // one. when an item is released, we'll reset m_freePage anyway.
155 if (!p) {
156 p = new AllocatorPage<Type, PageSize>();
157 m_freePage = pages.count();
158 pages.push_back(p);
159 }
160 uint pos = p->blocks[PageSize - p->available];
161 void *mem = p->at(pos);
162 p->available--;
163 p->allocated.setBit(pos);
164 Type *t = (Type*)mem;
165 return t;
166 }
167
168 void releaseExplicit(uint pageIndex, uint index)
169 {
170 AllocatorPage<Type, PageSize> *page = pages.at(pageIndex);
171 if (!page->allocated.testBit(index))
172 qFatal(msg: "Double delete in allocator: page=%d, index=%d", pageIndex , index);
173
174 // Zero this instance as we're done with it.
175 void *mem = page->at(index);
176 memset(s: mem, c: 0, n: sizeof(Type));
177
178 page->allocated[index] = false;
179 page->available++;
180 page->blocks[PageSize - page->available] = index;
181
182 // Remove the pages if they are empty and they are the last ones. We need to keep the
183 // order of pages since we have references to their index, so we can only remove
184 // from the end.
185 while (page->available == PageSize && pages.size() > 1 && pages.back() == page) {
186 pages.pop_back();
187 delete page;
188 page = pages.back();
189 }
190
191 // Reset the free page to force a scan for a new free point.
192 m_freePage = 0;
193 }
194
195 void release(Type *t)
196 {
197 int pageIndex = -1;
198 for (int i=0; i<pages.size(); ++i) {
199 AllocatorPage<Type, PageSize> *p = pages.at(i);
200 if ((Type *) (&p->data[0]) <= t && (Type *) (&p->data[PageSize * sizeof(Type)]) > t) {
201 pageIndex = i;
202 break;
203 }
204 }
205 Q_ASSERT(pageIndex >= 0);
206
207 AllocatorPage<Type, PageSize> *page = pages.at(pageIndex);
208 int index = (quint64(t) - quint64(&page->data[0])) / sizeof(Type);
209
210 releaseExplicit(pageIndex, index);
211 }
212
213 QVector<AllocatorPage<Type, PageSize> *> pages;
214 int m_freePage = 0;
215};
216
217
218inline bool hasMaterialWithBlending(QSGGeometryNode *n)
219{
220 return (n->opaqueMaterial() ? n->opaqueMaterial()->flags() & QSGMaterial::Blending
221 : n->material()->flags() & QSGMaterial::Blending);
222}
223
224struct Pt {
225 float x, y;
226
227 void map(const QMatrix4x4 &mat) {
228 Pt r;
229 const float *m = mat.constData();
230 r.x = x * m[0] + y * m[4] + m[12];
231 r.y = x * m[1] + y * m[5] + m[13];
232 x = r.x;
233 y = r.y;
234 }
235
236 void set(float nx, float ny) {
237 x = nx;
238 y = ny;
239 }
240};
241
242inline QDebug operator << (QDebug d, const Pt &p) {
243 d << "Pt(" << p.x << p.y << ")";
244 return d;
245}
246
247
248
249struct Rect {
250 Pt tl, br; // Top-Left (min) and Bottom-Right (max)
251
252 void operator |= (const Pt &pt) {
253 if (pt.x < tl.x)
254 tl.x = pt.x;
255 if (pt.x > br.x)
256 br.x = pt.x;
257 if (pt.y < tl.y)
258 tl.y = pt.y;
259 if (pt.y > br.y)
260 br.y = pt.y;
261 }
262
263 void operator |= (const Rect &r) {
264 if (r.tl.x < tl.x)
265 tl.x = r.tl.x;
266 if (r.tl.y < tl.y)
267 tl.y = r.tl.y;
268 if (r.br.x > br.x)
269 br.x = r.br.x;
270 if (r.br.y > br.y)
271 br.y = r.br.y;
272 }
273
274 void map(const QMatrix4x4 &m);
275
276 void set(float left, float top, float right, float bottom) {
277 tl.set(nx: left, ny: top);
278 br.set(nx: right, ny: bottom);
279 }
280
281 bool intersects(const Rect &r) {
282 bool xOverlap = r.tl.x < br.x && r.br.x > tl.x;
283 bool yOverlap = r.tl.y < br.y && r.br.y > tl.y;
284 return xOverlap && yOverlap;
285 }
286
287 bool isOutsideFloatRange() const {
288 return tl.x < -QSG_RENDERER_COORD_LIMIT
289 || tl.y < -QSG_RENDERER_COORD_LIMIT
290 || br.x > QSG_RENDERER_COORD_LIMIT
291 || br.y > QSG_RENDERER_COORD_LIMIT;
292 }
293};
294
295inline QDebug operator << (QDebug d, const Rect &r) {
296 d << "Rect(" << r.tl.x << r.tl.y << r.br.x << r.br.y << ")";
297 return d;
298}
299
300struct Buffer {
301 GLuint id;
302 int size;
303 // Data is only valid while preparing the upload. Exception is if we are using the
304 // broken IBO workaround or we are using a visualization mode.
305 char *data;
306 QRhiBuffer *buf;
307 uint nonDynamicChangeCount;
308};
309
310struct Element {
311 Element()
312 : boundsComputed(false)
313 , boundsOutsideFloatRange(false)
314 , translateOnlyToRoot(false)
315 , removed(false)
316 , orphaned(false)
317 , isRenderNode(false)
318 , isMaterialBlended(false)
319 {
320 }
321
322 void setNode(QSGGeometryNode *n) {
323 node = n;
324 isMaterialBlended = hasMaterialWithBlending(n);
325 }
326
327 inline void ensureBoundsValid() {
328 if (!boundsComputed)
329 computeBounds();
330 }
331 void computeBounds();
332
333 QSGGeometryNode *node = nullptr;
334 Batch *batch = nullptr;
335 Element *nextInBatch = nullptr;
336 Node *root = nullptr;
337
338 Rect bounds; // in device coordinates
339
340 int order = 0;
341 QRhiShaderResourceBindings *srb = nullptr;
342 QRhiGraphicsPipeline *ps = nullptr;
343
344 uint boundsComputed : 1;
345 uint boundsOutsideFloatRange : 1;
346 uint translateOnlyToRoot : 1;
347 uint removed : 1;
348 uint orphaned : 1;
349 uint isRenderNode : 1;
350 uint isMaterialBlended : 1;
351};
352
353struct RenderNodeElement : public Element {
354
355 RenderNodeElement(QSGRenderNode *rn)
356 : renderNode(rn)
357 {
358 isRenderNode = true;
359 }
360
361 QSGRenderNode *renderNode;
362};
363
364struct BatchRootInfo {
365 BatchRootInfo() {}
366 QSet<Node *> subRoots;
367 Node *parentRoot = nullptr;
368 int lastOrder = -1;
369 int firstOrder = -1;
370 int availableOrders = 0;
371};
372
373struct ClipBatchRootInfo : public BatchRootInfo
374{
375 QMatrix4x4 matrix;
376};
377
378struct DrawSet
379{
380 DrawSet(int v, int z, int i)
381 : vertices(v)
382 , zorders(z)
383 , indices(i)
384 {
385 }
386 DrawSet() {}
387 int vertices = 0;
388 int zorders = 0;
389 int indices = 0;
390 int indexCount = 0;
391};
392
393enum BatchCompatibility
394{
395 BatchBreaksOnCompare,
396 BatchIsCompatible
397};
398
399struct ClipState
400{
401 enum ClipTypeBit
402 {
403 NoClip = 0x00,
404 ScissorClip = 0x01,
405 StencilClip = 0x02
406 };
407 Q_DECLARE_FLAGS(ClipType, ClipTypeBit)
408
409 const QSGClipNode *clipList;
410 ClipType type;
411 QRhiScissor scissor;
412 int stencilRef;
413
414 inline void reset();
415};
416
417struct StencilClipState
418{
419 StencilClipState() : drawCalls(1) { }
420
421 bool updateStencilBuffer = false;
422 QRhiShaderResourceBindings *srb = nullptr;
423 QRhiBuffer *vbuf = nullptr;
424 QRhiBuffer *ibuf = nullptr;
425 QRhiBuffer *ubuf = nullptr;
426
427 struct StencilDrawCall {
428 int stencilRef;
429 int vertexCount;
430 int indexCount;
431 QRhiCommandBuffer::IndexFormat indexFormat;
432 quint32 vbufOffset;
433 quint32 ibufOffset;
434 quint32 ubufOffset;
435 };
436 QDataBuffer<StencilDrawCall> drawCalls;
437
438 inline void reset();
439};
440
441struct Batch
442{
443 Batch() : drawSets(1) {}
444 bool geometryWasChanged(QSGGeometryNode *gn);
445 BatchCompatibility isMaterialCompatible(Element *e) const;
446 void invalidate();
447 void cleanupRemovedElements();
448
449 bool isTranslateOnlyToRoot() const;
450 bool isSafeToBatch() const;
451
452 // pseudo-constructor...
453 void init() {
454 // Only non-reusable members are reset here. See Renderer::newBatch().
455 first = nullptr;
456 root = nullptr;
457 vertexCount = 0;
458 indexCount = 0;
459 isOpaque = false;
460 needsUpload = false;
461 merged = false;
462 positionAttribute = -1;
463 uploadedThisFrame = false;
464 isRenderNode = false;
465 ubufDataValid = false;
466 needsPurge = false;
467 clipState.reset();
468 blendConstant = QColor();
469 }
470
471 Element *first;
472 Node *root;
473
474 int positionAttribute;
475
476 int vertexCount;
477 int indexCount;
478
479 int lastOrderInBatch;
480
481 uint isOpaque : 1;
482 uint needsUpload : 1;
483 uint merged : 1;
484 uint isRenderNode : 1;
485 uint ubufDataValid : 1;
486 uint needsPurge : 1;
487
488 mutable uint uploadedThisFrame : 1; // solely for debugging purposes
489
490 Buffer vbo;
491 Buffer ibo;
492 QRhiBuffer *ubuf;
493 ClipState clipState;
494 StencilClipState stencilClipState;
495 QColor blendConstant;
496
497 QDataBuffer<DrawSet> drawSets;
498};
499
500// NOTE: Node is zero-initialized by the Allocator.
501struct Node
502{
503 QSGNode *sgNode;
504 void *data;
505
506 Node *m_parent;
507 Node *m_child;
508 Node *m_next;
509 Node *m_prev;
510
511 Node *parent() const { return m_parent; }
512
513 void append(Node *child) {
514 Q_ASSERT(child);
515 Q_ASSERT(!hasChild(child));
516 Q_ASSERT(child->m_parent == nullptr);
517 Q_ASSERT(child->m_next == nullptr);
518 Q_ASSERT(child->m_prev == nullptr);
519
520 if (!m_child) {
521 child->m_next = child;
522 child->m_prev = child;
523 m_child = child;
524 } else {
525 m_child->m_prev->m_next = child;
526 child->m_prev = m_child->m_prev;
527 m_child->m_prev = child;
528 child->m_next = m_child;
529 }
530 child->setParent(this);
531 }
532
533 void remove(Node *child) {
534 Q_ASSERT(child);
535 Q_ASSERT(hasChild(child));
536
537 // only child..
538 if (child->m_next == child) {
539 m_child = nullptr;
540 } else {
541 if (m_child == child)
542 m_child = child->m_next;
543 child->m_next->m_prev = child->m_prev;
544 child->m_prev->m_next = child->m_next;
545 }
546 child->m_next = nullptr;
547 child->m_prev = nullptr;
548 child->setParent(nullptr);
549 }
550
551 Node *firstChild() const { return m_child; }
552
553 Node *sibling() const {
554 Q_ASSERT(m_parent);
555 return m_next == m_parent->m_child ? nullptr : m_next;
556 }
557
558 void setParent(Node *p) {
559 Q_ASSERT(m_parent == nullptr || p == nullptr);
560 m_parent = p;
561 }
562
563 bool hasChild(Node *child) const {
564 Node *n = m_child;
565 while (n && n != child)
566 n = n->sibling();
567 return n;
568 }
569
570
571
572 QSGNode::DirtyState dirtyState;
573
574 uint isOpaque : 1;
575 uint isBatchRoot : 1;
576 uint becameBatchRoot : 1;
577
578 inline QSGNode::NodeType type() const { return sgNode->type(); }
579
580 inline Element *element() const {
581 Q_ASSERT(sgNode->type() == QSGNode::GeometryNodeType);
582 return (Element *) data;
583 }
584
585 inline RenderNodeElement *renderNodeElement() const {
586 Q_ASSERT(sgNode->type() == QSGNode::RenderNodeType);
587 return (RenderNodeElement *) data;
588 }
589
590 inline ClipBatchRootInfo *clipInfo() const {
591 Q_ASSERT(sgNode->type() == QSGNode::ClipNodeType);
592 return (ClipBatchRootInfo *) data;
593 }
594
595 inline BatchRootInfo *rootInfo() const {
596 Q_ASSERT(sgNode->type() == QSGNode::ClipNodeType
597 || (sgNode->type() == QSGNode::TransformNodeType && isBatchRoot));
598 return (BatchRootInfo *) data;
599 }
600};
601
602class Updater : public QSGNodeUpdater
603{
604public:
605 Updater(Renderer *r);
606
607 void visitOpacityNode(Node *n);
608 void visitTransformNode(Node *n);
609 void visitGeometryNode(Node *n);
610 void visitClipNode(Node *n);
611 void updateRootTransforms(Node *n);
612 void updateRootTransforms(Node *n, Node *root, const QMatrix4x4 &combined);
613
614 void updateStates(QSGNode *n) override;
615 void visitNode(Node *n);
616 void registerWithParentRoot(QSGNode *subRoot, QSGNode *parentRoot);
617
618private:
619 Renderer *renderer;
620
621 QDataBuffer<Node *> m_roots;
622 QDataBuffer<QMatrix4x4> m_rootMatrices;
623
624 int m_added;
625 int m_transformChange;
626 int m_opacityChange;
627
628 QMatrix4x4 m_identityMatrix;
629};
630
631struct GraphicsState
632{
633 bool depthTest = false;
634 bool depthWrite = false;
635 QRhiGraphicsPipeline::CompareOp depthFunc = QRhiGraphicsPipeline::Less;
636 bool blending = false;
637 QRhiGraphicsPipeline::BlendFactor srcColor = QRhiGraphicsPipeline::One;
638 QRhiGraphicsPipeline::BlendFactor dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha;
639 QRhiGraphicsPipeline::ColorMask colorWrite = QRhiGraphicsPipeline::ColorMask(0xF);
640 QRhiGraphicsPipeline::CullMode cullMode = QRhiGraphicsPipeline::None;
641 bool usesScissor = false;
642 bool stencilTest = false;
643 int sampleCount = 1;
644 QSGGeometry::DrawingMode drawMode = QSGGeometry::DrawTriangles;
645 float lineWidth = 1.0f;
646};
647
648bool operator==(const GraphicsState &a, const GraphicsState &b) Q_DECL_NOTHROW;
649bool operator!=(const GraphicsState &a, const GraphicsState &b) Q_DECL_NOTHROW;
650uint qHash(const GraphicsState &s, uint seed = 0) Q_DECL_NOTHROW;
651
652struct ShaderManagerShader;
653
654struct GraphicsPipelineStateKey
655{
656 GraphicsState state;
657 const ShaderManagerShader *sms;
658 const QRhiRenderPassDescriptor *compatibleRenderPassDescriptor;
659 const QRhiShaderResourceBindings *layoutCompatibleSrb;
660};
661
662bool operator==(const GraphicsPipelineStateKey &a, const GraphicsPipelineStateKey &b) Q_DECL_NOTHROW;
663bool operator!=(const GraphicsPipelineStateKey &a, const GraphicsPipelineStateKey &b) Q_DECL_NOTHROW;
664uint qHash(const GraphicsPipelineStateKey &k, uint seed = 0) Q_DECL_NOTHROW;
665
666struct ShaderManagerShader
667{
668 ~ShaderManagerShader() {
669 delete programRhi.program;
670 delete programGL.program;
671 }
672 struct {
673 QSGMaterialShader *program = nullptr;
674 int pos_order;
675 } programGL;
676 struct {
677 QSGMaterialRhiShader *program = nullptr;
678 QRhiVertexInputLayout inputLayout;
679 QVarLengthArray<QRhiGraphicsShaderStage, 2> shaderStages;
680 } programRhi;
681
682 float lastOpacity;
683};
684
685class ShaderManager : public QObject
686{
687 Q_OBJECT
688public:
689 using Shader = ShaderManagerShader;
690
691 ShaderManager(QSGDefaultRenderContext *ctx) : blitProgram(nullptr), context(ctx) { }
692 ~ShaderManager() {
693 qDeleteAll(c: rewrittenShaders);
694 qDeleteAll(c: stockShaders);
695 }
696
697 void clearCachedRendererData();
698
699 using ShaderResourceBindingList = QVarLengthArray<QRhiShaderResourceBinding, 8>;
700 QRhiShaderResourceBindings *srb(const ShaderResourceBindingList &bindings);
701
702 QHash<GraphicsPipelineStateKey, QRhiGraphicsPipeline *> pipelineCache;
703
704public Q_SLOTS:
705 void invalidated();
706
707public:
708 Shader *prepareMaterial(QSGMaterial *material, bool enableRhiShaders = false, const QSGGeometry *geometry = nullptr);
709 Shader *prepareMaterialNoRewrite(QSGMaterial *material, bool enableRhiShaders = false, const QSGGeometry *geometry = nullptr);
710
711private:
712 QHash<QSGMaterialType *, Shader *> rewrittenShaders;
713 QHash<QSGMaterialType *, Shader *> stockShaders;
714
715 QOpenGLShaderProgram *blitProgram;
716 QSGDefaultRenderContext *context;
717
718 QHash<ShaderResourceBindingList, QRhiShaderResourceBindings *> srbCache;
719};
720
721struct RenderPassState
722{
723 QRhiViewport viewport;
724 QColor clearColor;
725 QRhiDepthStencilClearValue dsClear;
726 bool viewportSet;
727 bool scissorSet;
728};
729
730class Visualizer
731{
732public:
733 enum VisualizeMode {
734 VisualizeNothing,
735 VisualizeBatches,
736 VisualizeClipping,
737 VisualizeChanges,
738 VisualizeOverdraw
739 };
740
741 Visualizer(Renderer *renderer);
742 virtual ~Visualizer();
743
744 VisualizeMode mode() const { return m_visualizeMode; }
745 void setMode(VisualizeMode mode) { m_visualizeMode = mode; }
746
747 virtual void visualizeChangesPrepare(Node *n, uint parentChanges = 0);
748 virtual void prepareVisualize() = 0;
749 virtual void visualize() = 0;
750
751 virtual void releaseResources() = 0;
752
753protected:
754 Renderer *m_renderer;
755 VisualizeMode m_visualizeMode;
756 QHash<Node *, uint> m_visualizeChangeSet;
757};
758
759class Q_QUICK_PRIVATE_EXPORT Renderer : public QSGRenderer, public QOpenGLFunctions
760{
761public:
762 Renderer(QSGDefaultRenderContext *);
763 ~Renderer();
764
765protected:
766 void nodeChanged(QSGNode *node, QSGNode::DirtyState state) override;
767 void render() override;
768 void releaseCachedResources() override;
769
770private:
771 enum RebuildFlag {
772 BuildRenderListsForTaggedRoots = 0x0001,
773 BuildRenderLists = 0x0002,
774 BuildBatches = 0x0004,
775 FullRebuild = 0xffff
776 };
777
778 friend class Updater;
779 friend class OpenGLVisualizer;
780 friend class RhiVisualizer;
781
782 void destroyGraphicsResources();
783 void map(Buffer *buffer, int size, bool isIndexBuf = false);
784 void unmap(Buffer *buffer, bool isIndexBuf = false);
785
786 void buildRenderListsFromScratch();
787 void buildRenderListsForTaggedRoots();
788 void tagSubRoots(Node *node);
789 void buildRenderLists(QSGNode *node);
790
791 void deleteRemovedElements();
792 void cleanupBatches(QDataBuffer<Batch *> *batches);
793 void prepareOpaqueBatches();
794 bool checkOverlap(int first, int last, const Rect &bounds);
795 void prepareAlphaBatches();
796 void invalidateBatchAndOverlappingRenderOrders(Batch *batch);
797
798 void uploadBatch(Batch *b);
799 void uploadMergedElement(Element *e, int vaOffset, char **vertexData, char **zData, char **indexData, void *iBasePtr, int *indexCount);
800
801 struct PreparedRenderBatch {
802 const Batch *batch;
803 ShaderManager::Shader *sms;
804 };
805
806 void renderBatches();
807 bool ensurePipelineState(Element *e, const ShaderManager::Shader *sms);
808 QRhiTexture *dummyTexture();
809 void updateMaterialDynamicData(ShaderManager::Shader *sms, QSGMaterialRhiShader::RenderState &renderState,
810 QSGMaterial *material, ShaderManager::ShaderResourceBindingList *bindings,
811 const Batch *batch, int ubufOffset, int ubufRegionSize);
812 void updateMaterialStaticData(ShaderManager::Shader *sms, QSGMaterialRhiShader::RenderState &renderState,
813 QSGMaterial *material, Batch *batch, bool *gstateChanged);
814 void checkLineWidth(QSGGeometry *g);
815 bool prepareRenderMergedBatch(Batch *batch, PreparedRenderBatch *renderBatch);
816 void renderMergedBatch(PreparedRenderBatch *renderBatch);
817 bool prepareRenderUnmergedBatch(Batch *batch, PreparedRenderBatch *renderBatch);
818 void renderUnmergedBatch(PreparedRenderBatch *renderBatch);
819 void setGraphicsPipeline(QRhiCommandBuffer *cb, const Batch *batch, Element *e);
820 void renderMergedBatch(const Batch *batch); // GL
821 void renderUnmergedBatch(const Batch *batch); // GL
822 ClipState::ClipType updateStencilClip(const QSGClipNode *clip);
823 void updateClip(const QSGClipNode *clipList, const Batch *batch);
824 void applyClipStateToGraphicsState();
825 QRhiGraphicsPipeline *buildStencilPipeline(const Batch *batch, bool firstStencilClipInBatch);
826 void updateClipState(const QSGClipNode *clipList, Batch *batch);
827 void enqueueStencilDraw(const Batch *batch);
828 const QMatrix4x4 &matrixForRoot(Node *node);
829 void renderRenderNode(Batch *batch);
830 bool prepareRhiRenderNode(Batch *batch, PreparedRenderBatch *renderBatch);
831 void renderRhiRenderNode(const Batch *batch);
832 void setActiveShader(QSGMaterialShader *program, ShaderManager::Shader *shader);
833 void setActiveRhiShader(QSGMaterialRhiShader *program, ShaderManager::Shader *shader);
834
835 bool changeBatchRoot(Node *node, Node *newRoot);
836 void registerBatchRoot(Node *childRoot, Node *parentRoot);
837 void removeBatchRootFromParent(Node *childRoot);
838 void nodeChangedBatchRoot(Node *node, Node *root);
839 void turnNodeIntoBatchRoot(Node *node);
840 void nodeWasTransformed(Node *node, int *vertexCount);
841 void nodeWasRemoved(Node *node);
842 void nodeWasAdded(QSGNode *node, Node *shadowParent);
843 BatchRootInfo *batchRootInfo(Node *node);
844 void updateLineWidth(QSGGeometry *g);
845
846 inline Batch *newBatch();
847 void invalidateAndRecycleBatch(Batch *b);
848
849 void setCustomRenderMode(const QByteArray &mode) override;
850 bool hasCustomRenderModeWithContinuousUpdate() const override;
851
852 QSGDefaultRenderContext *m_context;
853 QSet<Node *> m_taggedRoots;
854 QDataBuffer<Element *> m_opaqueRenderList;
855 QDataBuffer<Element *> m_alphaRenderList;
856 int m_nextRenderOrder;
857 bool m_partialRebuild;
858 QSGNode *m_partialRebuildRoot;
859
860 bool m_useDepthBuffer;
861
862 QHash<QSGRenderNode *, RenderNodeElement *> m_renderNodeElements;
863 QDataBuffer<Batch *> m_opaqueBatches;
864 QDataBuffer<Batch *> m_alphaBatches;
865 QHash<QSGNode *, Node *> m_nodes;
866
867 QDataBuffer<Batch *> m_batchPool;
868 QDataBuffer<Element *> m_elementsToDelete;
869 QDataBuffer<Element *> m_tmpAlphaElements;
870 QDataBuffer<Element *> m_tmpOpaqueElements;
871
872 uint m_rebuild;
873 qreal m_zRange;
874 int m_renderOrderRebuildLower;
875 int m_renderOrderRebuildUpper;
876
877 GLuint m_bufferStrategy;
878 int m_batchNodeThreshold;
879 int m_batchVertexThreshold;
880
881 Visualizer *m_visualizer;
882
883 // Stuff used during rendering only...
884 ShaderManager *m_shaderManager; // per rendercontext, shared
885 QSGMaterial *m_currentMaterial;
886 QSGMaterialShader *m_currentProgram;
887 QSGMaterialRhiShader *m_currentRhiProgram;
888 ShaderManager::Shader *m_currentShader;
889 ClipState m_currentClipState;
890
891 // *** legacy (GL) only
892 QRect m_currentScissorRect;
893 int m_currentStencilValue;
894 QOpenGLShaderProgram m_clipProgram;
895 int m_clipMatrixId;
896 const QSGClipNode *m_currentClip;
897 ClipState::ClipType m_currentClipType;
898 // ***
899
900 QDataBuffer<char> m_vertexUploadPool;
901 QDataBuffer<char> m_indexUploadPool;
902 // For minimal OpenGL core profile support
903 QOpenGLVertexArrayObject *m_vao;
904
905 Allocator<Node, 256> m_nodeAllocator;
906 Allocator<Element, 64> m_elementAllocator;
907
908 QRhiResourceUpdateBatch *m_resourceUpdates = nullptr;
909 uint m_ubufAlignment;
910 bool m_uint32IndexForRhi;
911 GraphicsState m_gstate;
912 RenderPassState m_pstate;
913 QStack<GraphicsState> m_gstateStack;
914 QHash<QSGSamplerDescription, QRhiSampler *> m_samplers;
915 QRhiTexture *m_dummyTexture = nullptr;
916
917 struct StencilClipCommonData {
918 QRhiGraphicsPipeline *replacePs = nullptr;
919 QRhiGraphicsPipeline *incrPs = nullptr;
920 QShader vs;
921 QShader fs;
922 QRhiVertexInputLayout inputLayout;
923 QRhiGraphicsPipeline::Topology topology;
924 inline void reset();
925 } m_stencilClipCommon;
926
927 inline int mergedIndexElemSize() const;
928};
929
930Batch *Renderer::newBatch()
931{
932 Batch *b;
933 int size = m_batchPool.size();
934 if (size) {
935 b = m_batchPool.at(i: size - 1);
936 // vbo, ibo, ubuf, stencil-related buffers are reused
937 m_batchPool.resize(size: size - 1);
938 } else {
939 b = new Batch();
940 Q_ASSERT(offsetof(Batch, ibo) == sizeof(Buffer) + offsetof(Batch, vbo));
941 memset(s: &b->vbo, c: 0, n: sizeof(Buffer) * 2); // Clear VBO & IBO
942 b->ubuf = nullptr;
943 b->stencilClipState.reset();
944 }
945 // initialize (when new batch) or reset (when reusing a batch) the non-reusable fields
946 b->init();
947 return b;
948}
949
950int Renderer::mergedIndexElemSize() const
951{
952 return m_uint32IndexForRhi ? sizeof(quint32) : sizeof(quint16);
953}
954
955void Renderer::StencilClipCommonData::reset()
956{
957 delete replacePs;
958 replacePs = nullptr;
959
960 delete incrPs;
961 incrPs = nullptr;
962
963 vs = QShader();
964 fs = QShader();
965}
966
967void ClipState::reset()
968{
969 clipList = nullptr;
970 type = NoClip;
971 stencilRef = 0;
972}
973
974void StencilClipState::reset()
975{
976 updateStencilBuffer = false;
977
978 delete srb;
979 srb = nullptr;
980
981 delete vbuf;
982 vbuf = nullptr;
983
984 delete ibuf;
985 ibuf = nullptr;
986
987 delete ubuf;
988 ubuf = nullptr;
989
990 drawCalls.reset();
991}
992
993}
994
995Q_DECLARE_TYPEINFO(QSGBatchRenderer::GraphicsState, Q_MOVABLE_TYPE);
996Q_DECLARE_TYPEINFO(QSGBatchRenderer::GraphicsPipelineStateKey, Q_MOVABLE_TYPE);
997Q_DECLARE_TYPEINFO(QSGBatchRenderer::RenderPassState, Q_MOVABLE_TYPE);
998
999QT_END_NAMESPACE
1000
1001#endif // QSGBATCHRENDERER_P_H
1002

source code of qtdeclarative/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h