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

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