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

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