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("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(mem, 0, 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(left, top);
278 br.set(right, 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 clipState.reset();
467 blendConstant = QColor();
468 }
469
470 Element *first;
471 Node *root;
472
473 int positionAttribute;
474
475 int vertexCount;
476 int indexCount;
477
478 int lastOrderInBatch;
479
480 uint isOpaque : 1;
481 uint needsUpload : 1;
482 uint merged : 1;
483 uint isRenderNode : 1;
484 uint ubufDataValid : 1;
485
486 mutable uint uploadedThisFrame : 1; // solely for debugging purposes
487
488 Buffer vbo;
489 Buffer ibo;
490 QRhiBuffer *ubuf;
491 ClipState clipState;
492 StencilClipState stencilClipState;
493 QColor blendConstant;
494
495 QDataBuffer<DrawSet> drawSets;
496};
497
498// NOTE: Node is zero-initialized by the Allocator.
499struct Node
500{
501 QSGNode *sgNode;
502 void *data;
503
504 Node *m_parent;
505 Node *m_child;
506 Node *m_next;
507 Node *m_prev;
508
509 Node *parent() const { return m_parent; }
510
511 void append(Node *child) {
512 Q_ASSERT(child);
513 Q_ASSERT(!hasChild(child));
514 Q_ASSERT(child->m_parent == nullptr);
515 Q_ASSERT(child->m_next == nullptr);
516 Q_ASSERT(child->m_prev == nullptr);
517
518 if (!m_child) {
519 child->m_next = child;
520 child->m_prev = child;
521 m_child = child;
522 } else {
523 m_child->m_prev->m_next = child;
524 child->m_prev = m_child->m_prev;
525 m_child->m_prev = child;
526 child->m_next = m_child;
527 }
528 child->setParent(this);
529 }
530
531 void remove(Node *child) {
532 Q_ASSERT(child);
533 Q_ASSERT(hasChild(child));
534
535 // only child..
536 if (child->m_next == child) {
537 m_child = nullptr;
538 } else {
539 if (m_child == child)
540 m_child = child->m_next;
541 child->m_next->m_prev = child->m_prev;
542 child->m_prev->m_next = child->m_next;
543 }
544 child->m_next = nullptr;
545 child->m_prev = nullptr;
546 child->setParent(nullptr);
547 }
548
549 Node *firstChild() const { return m_child; }
550
551 Node *sibling() const {
552 Q_ASSERT(m_parent);
553 return m_next == m_parent->m_child ? nullptr : m_next;
554 }
555
556 void setParent(Node *p) {
557 Q_ASSERT(m_parent == nullptr || p == nullptr);
558 m_parent = p;
559 }
560
561 bool hasChild(Node *child) const {
562 Node *n = m_child;
563 while (n && n != child)
564 n = n->sibling();
565 return n;
566 }
567
568
569
570 QSGNode::DirtyState dirtyState;
571
572 uint isOpaque : 1;
573 uint isBatchRoot : 1;
574 uint becameBatchRoot : 1;
575
576 inline QSGNode::NodeType type() const { return sgNode->type(); }
577
578 inline Element *element() const {
579 Q_ASSERT(sgNode->type() == QSGNode::GeometryNodeType);
580 return (Element *) data;
581 }
582
583 inline RenderNodeElement *renderNodeElement() const {
584 Q_ASSERT(sgNode->type() == QSGNode::RenderNodeType);
585 return (RenderNodeElement *) data;
586 }
587
588 inline ClipBatchRootInfo *clipInfo() const {
589 Q_ASSERT(sgNode->type() == QSGNode::ClipNodeType);
590 return (ClipBatchRootInfo *) data;
591 }
592
593 inline BatchRootInfo *rootInfo() const {
594 Q_ASSERT(sgNode->type() == QSGNode::ClipNodeType
595 || (sgNode->type() == QSGNode::TransformNodeType && isBatchRoot));
596 return (BatchRootInfo *) data;
597 }
598};
599
600class Updater : public QSGNodeUpdater
601{
602public:
603 Updater(Renderer *r);
604
605 void visitOpacityNode(Node *n);
606 void visitTransformNode(Node *n);
607 void visitGeometryNode(Node *n);
608 void visitClipNode(Node *n);
609 void updateRootTransforms(Node *n);
610 void updateRootTransforms(Node *n, Node *root, const QMatrix4x4 &combined);
611
612 void updateStates(QSGNode *n) override;
613 void visitNode(Node *n);
614 void registerWithParentRoot(QSGNode *subRoot, QSGNode *parentRoot);
615
616private:
617 Renderer *renderer;
618
619 QDataBuffer<Node *> m_roots;
620 QDataBuffer<QMatrix4x4> m_rootMatrices;
621
622 int m_added;
623 int m_transformChange;
624 int m_opacityChange;
625
626 QMatrix4x4 m_identityMatrix;
627};
628
629class ShaderManager : public QObject
630{
631 Q_OBJECT
632public:
633 struct Shader {
634 ~Shader() {
635 delete programRhi.program;
636 delete programGL.program;
637 }
638 struct {
639 QSGMaterialShader *program = nullptr;
640 int pos_order;
641 } programGL;
642 struct {
643 QSGMaterialRhiShader *program = nullptr;
644 QRhiVertexInputLayout inputLayout;
645 QVector<QRhiGraphicsShaderStage> shaderStages;
646 } programRhi;
647
648 float lastOpacity;
649 };
650
651 ShaderManager(QSGDefaultRenderContext *ctx) : visualizeProgram(nullptr), blitProgram(nullptr), context(ctx) { }
652 ~ShaderManager() {
653 qDeleteAll(rewrittenShaders);
654 qDeleteAll(stockShaders);
655 }
656
657 void clearCachedRendererData();
658
659 QRhiShaderResourceBindings *srb(const QVector<QRhiShaderResourceBinding> &bindings);
660
661public Q_SLOTS:
662 void invalidated();
663
664public:
665 Shader *prepareMaterial(QSGMaterial *material, bool enableRhiShaders = false, const QSGGeometry *geometry = nullptr);
666 Shader *prepareMaterialNoRewrite(QSGMaterial *material, bool enableRhiShaders = false, const QSGGeometry *geometry = nullptr);
667
668 QOpenGLShaderProgram *visualizeProgram;
669
670private:
671 QHash<QSGMaterialType *, Shader *> rewrittenShaders;
672 QHash<QSGMaterialType *, Shader *> stockShaders;
673
674 QOpenGLShaderProgram *blitProgram;
675 QSGDefaultRenderContext *context;
676
677 QHash<QVector<QRhiShaderResourceBinding>, QRhiShaderResourceBindings *> srbCache;
678};
679
680struct GraphicsState
681{
682 bool depthTest = false;
683 bool depthWrite = false;
684 QRhiGraphicsPipeline::CompareOp depthFunc = QRhiGraphicsPipeline::Less;
685 bool blending = false;
686 QRhiGraphicsPipeline::BlendFactor srcColor = QRhiGraphicsPipeline::One;
687 QRhiGraphicsPipeline::BlendFactor dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha;
688 QRhiGraphicsPipeline::ColorMask colorWrite = QRhiGraphicsPipeline::ColorMask(0xF);
689 QRhiGraphicsPipeline::CullMode cullMode = QRhiGraphicsPipeline::None;
690 bool usesScissor = false;
691 bool stencilTest = false;
692 int sampleCount = 1;
693 QSGGeometry::DrawingMode drawMode = QSGGeometry::DrawTriangles;
694 float lineWidth = 1.0f;
695};
696
697bool operator==(const GraphicsState &a, const GraphicsState &b) Q_DECL_NOTHROW;
698bool operator!=(const GraphicsState &a, const GraphicsState &b) Q_DECL_NOTHROW;
699uint qHash(const GraphicsState &s, uint seed = 0) Q_DECL_NOTHROW;
700
701struct GraphicsPipelineStateKey
702{
703 GraphicsState state;
704 const ShaderManager::Shader *sms;
705 const QRhiRenderPassDescriptor *rpDesc;
706 const QRhiShaderResourceBindings *layoutCompatibleSrb;
707};
708
709bool operator==(const GraphicsPipelineStateKey &a, const GraphicsPipelineStateKey &b) Q_DECL_NOTHROW;
710bool operator!=(const GraphicsPipelineStateKey &a, const GraphicsPipelineStateKey &b) Q_DECL_NOTHROW;
711uint qHash(const GraphicsPipelineStateKey &k, uint seed = 0) Q_DECL_NOTHROW;
712
713struct RenderPassState
714{
715 QRhiViewport viewport;
716 QColor clearColor;
717 QRhiDepthStencilClearValue dsClear;
718 bool viewportSet;
719 bool scissorSet;
720};
721
722class Q_QUICK_PRIVATE_EXPORT Renderer : public QSGRenderer, public QOpenGLFunctions
723{
724public:
725 Renderer(QSGDefaultRenderContext *);
726 ~Renderer();
727
728 enum VisualizeMode {
729 VisualizeNothing,
730 VisualizeBatches,
731 VisualizeClipping,
732 VisualizeChanges,
733 VisualizeOverdraw
734 };
735
736protected:
737 void nodeChanged(QSGNode *node, QSGNode::DirtyState state) override;
738 void render() override;
739 void releaseCachedResources() override;
740
741private:
742 enum RebuildFlag {
743 BuildRenderListsForTaggedRoots = 0x0001,
744 BuildRenderLists = 0x0002,
745 BuildBatches = 0x0004,
746 FullRebuild = 0xffff
747 };
748
749 friend class Updater;
750
751 void destroyGraphicsResources();
752 void map(Buffer *buffer, int size, bool isIndexBuf = false);
753 void unmap(Buffer *buffer, bool isIndexBuf = false);
754
755 void buildRenderListsFromScratch();
756 void buildRenderListsForTaggedRoots();
757 void tagSubRoots(Node *node);
758 void buildRenderLists(QSGNode *node);
759
760 void deleteRemovedElements();
761 void cleanupBatches(QDataBuffer<Batch *> *batches);
762 void prepareOpaqueBatches();
763 bool checkOverlap(int first, int last, const Rect &bounds);
764 void prepareAlphaBatches();
765 void invalidateBatchAndOverlappingRenderOrders(Batch *batch);
766
767 void uploadBatch(Batch *b);
768 void uploadMergedElement(Element *e, int vaOffset, char **vertexData, char **zData, char **indexData, void *iBasePtr, int *indexCount);
769
770 struct PreparedRenderBatch {
771 const Batch *batch;
772 ShaderManager::Shader *sms;
773 };
774
775 void renderBatches();
776 bool ensurePipelineState(Element *e, const ShaderManager::Shader *sms);
777 QRhiTexture *dummyTexture();
778 void updateMaterialDynamicData(ShaderManager::Shader *sms, const QSGMaterialRhiShader::RenderState &renderState,
779 QSGMaterial *material, QVector<QRhiShaderResourceBinding> *bindings,
780 const Batch *batch, int ubufOffset, int ubufRegionSize);
781 void updateMaterialStaticData(ShaderManager::Shader *sms, const QSGMaterialRhiShader::RenderState &renderState,
782 QSGMaterial *material, Batch *batch, bool *gstateChanged);
783 void checkLineWidth(QSGGeometry *g);
784 bool prepareRenderMergedBatch(Batch *batch, PreparedRenderBatch *renderBatch);
785 void renderMergedBatch(PreparedRenderBatch *renderBatch);
786 bool prepareRenderUnmergedBatch(Batch *batch, PreparedRenderBatch *renderBatch);
787 void renderUnmergedBatch(PreparedRenderBatch *renderBatch);
788 void setGraphicsPipeline(QRhiCommandBuffer *cb, const Batch *batch, Element *e);
789 void renderMergedBatch(const Batch *batch); // GL
790 void renderUnmergedBatch(const Batch *batch); // GL
791 ClipState::ClipType updateStencilClip(const QSGClipNode *clip);
792 void updateClip(const QSGClipNode *clipList, const Batch *batch);
793 void applyClipStateToGraphicsState();
794 QRhiGraphicsPipeline *buildStencilPipeline(const Batch *batch, bool firstStencilClipInBatch);
795 void updateClipState(const QSGClipNode *clipList, Batch *batch);
796 void enqueueStencilDraw(const Batch *batch);
797 const QMatrix4x4 &matrixForRoot(Node *node);
798 void renderRenderNode(Batch *batch);
799 void setActiveShader(QSGMaterialShader *program, ShaderManager::Shader *shader);
800 void setActiveRhiShader(QSGMaterialRhiShader *program, ShaderManager::Shader *shader);
801
802 bool changeBatchRoot(Node *node, Node *newRoot);
803 void registerBatchRoot(Node *childRoot, Node *parentRoot);
804 void removeBatchRootFromParent(Node *childRoot);
805 void nodeChangedBatchRoot(Node *node, Node *root);
806 void turnNodeIntoBatchRoot(Node *node);
807 void nodeWasTransformed(Node *node, int *vertexCount);
808 void nodeWasRemoved(Node *node);
809 void nodeWasAdded(QSGNode *node, Node *shadowParent);
810 BatchRootInfo *batchRootInfo(Node *node);
811 void updateLineWidth(QSGGeometry *g);
812
813 inline Batch *newBatch();
814 void invalidateAndRecycleBatch(Batch *b);
815
816 void visualize();
817 void visualizeBatch(Batch *b);
818 void visualizeClipping(QSGNode *node);
819 void visualizeChangesPrepare(Node *n, uint parentChanges = 0);
820 void visualizeChanges(Node *n);
821 void visualizeOverdraw();
822 void visualizeOverdraw_helper(Node *node);
823 void visualizeDrawGeometry(const QSGGeometry *g);
824 void setCustomRenderMode(const QByteArray &mode) override;
825
826 QSGDefaultRenderContext *m_context;
827 QSet<Node *> m_taggedRoots;
828 QDataBuffer<Element *> m_opaqueRenderList;
829 QDataBuffer<Element *> m_alphaRenderList;
830 int m_nextRenderOrder;
831 bool m_partialRebuild;
832 QSGNode *m_partialRebuildRoot;
833
834 bool m_useDepthBuffer;
835
836 QHash<QSGRenderNode *, RenderNodeElement *> m_renderNodeElements;
837 QDataBuffer<Batch *> m_opaqueBatches;
838 QDataBuffer<Batch *> m_alphaBatches;
839 QHash<QSGNode *, Node *> m_nodes;
840
841 QDataBuffer<Batch *> m_batchPool;
842 QDataBuffer<Element *> m_elementsToDelete;
843 QDataBuffer<Element *> m_tmpAlphaElements;
844 QDataBuffer<Element *> m_tmpOpaqueElements;
845
846 uint m_rebuild;
847 qreal m_zRange;
848 int m_renderOrderRebuildLower;
849 int m_renderOrderRebuildUpper;
850
851 GLuint m_bufferStrategy;
852 int m_batchNodeThreshold;
853 int m_batchVertexThreshold;
854
855 // Stuff used during rendering only...
856 ShaderManager *m_shaderManager; // per rendercontext, shared
857 QSGMaterial *m_currentMaterial;
858 QSGMaterialShader *m_currentProgram;
859 QSGMaterialRhiShader *m_currentRhiProgram;
860 ShaderManager::Shader *m_currentShader;
861 ClipState m_currentClipState;
862
863 // *** legacy (GL) only
864 QRect m_currentScissorRect;
865 int m_currentStencilValue;
866 QOpenGLShaderProgram m_clipProgram;
867 int m_clipMatrixId;
868 const QSGClipNode *m_currentClip;
869 ClipState::ClipType m_currentClipType;
870 // ***
871
872 QDataBuffer<char> m_vertexUploadPool;
873 QDataBuffer<char> m_indexUploadPool;
874 // For minimal OpenGL core profile support
875 QOpenGLVertexArrayObject *m_vao;
876
877 QHash<Node *, uint> m_visualizeChanceSet;
878 VisualizeMode m_visualizeMode;
879
880 Allocator<Node, 256> m_nodeAllocator;
881 Allocator<Element, 64> m_elementAllocator;
882
883 QRhiResourceUpdateBatch *m_resourceUpdates = nullptr;
884 uint m_ubufAlignment;
885 bool m_uint32IndexForRhi;
886 GraphicsState m_gstate;
887 RenderPassState m_pstate;
888 QStack<GraphicsState> m_gstateStack;
889 QHash<GraphicsPipelineStateKey, QRhiGraphicsPipeline *> m_pipelines;
890 QHash<QSGSamplerDescription, QRhiSampler *> m_samplers;
891 QRhiTexture *m_dummyTexture = nullptr;
892
893 struct StencilClipCommonData {
894 QRhiGraphicsPipeline *replacePs = nullptr;
895 QRhiGraphicsPipeline *incrPs = nullptr;
896 QShader vs;
897 QShader fs;
898 QRhiVertexInputLayout inputLayout;
899 QRhiGraphicsPipeline::Topology topology;
900 inline void reset();
901 } m_stencilClipCommon;
902
903 inline int mergedIndexElemSize() const;
904};
905
906Batch *Renderer::newBatch()
907{
908 Batch *b;
909 int size = m_batchPool.size();
910 if (size) {
911 b = m_batchPool.at(size - 1);
912 // vbo, ibo, ubuf, stencil-related buffers are reused
913 m_batchPool.resize(size - 1);
914 } else {
915 b = new Batch();
916 Q_ASSERT(offsetof(Batch, ibo) == sizeof(Buffer) + offsetof(Batch, vbo));
917 memset(&b->vbo, 0, sizeof(Buffer) * 2); // Clear VBO & IBO
918 b->ubuf = nullptr;
919 b->stencilClipState.reset();
920 }
921 // initialize (when new batch) or reset (when reusing a batch) the non-reusable fields
922 b->init();
923 return b;
924}
925
926int Renderer::mergedIndexElemSize() const
927{
928 return m_uint32IndexForRhi ? sizeof(quint32) : sizeof(quint16);
929}
930
931void Renderer::StencilClipCommonData::reset()
932{
933 delete replacePs;
934 replacePs = nullptr;
935
936 delete incrPs;
937 incrPs = nullptr;
938
939 vs = QShader();
940 fs = QShader();
941}
942
943void ClipState::reset()
944{
945 clipList = nullptr;
946 type = NoClip;
947 stencilRef = 0;
948}
949
950void StencilClipState::reset()
951{
952 updateStencilBuffer = false;
953
954 delete srb;
955 srb = nullptr;
956
957 delete vbuf;
958 vbuf = nullptr;
959
960 delete ibuf;
961 ibuf = nullptr;
962
963 delete ubuf;
964 ubuf = nullptr;
965
966 drawCalls.reset();
967}
968
969}
970
971Q_DECLARE_TYPEINFO(QSGBatchRenderer::GraphicsState, Q_MOVABLE_TYPE);
972Q_DECLARE_TYPEINFO(QSGBatchRenderer::GraphicsPipelineStateKey, Q_MOVABLE_TYPE);
973Q_DECLARE_TYPEINFO(QSGBatchRenderer::RenderPassState, Q_MOVABLE_TYPE);
974
975QT_END_NAMESPACE
976
977#endif // QSGBATCHRENDERER_P_H
978