/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2016 Jolla Ltd, author: ** Copyright (C) 2016 Robin Burchell ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQuick module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QSGBATCHRENDERER_P_H #define QSGBATCHRENDERER_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE class QOpenGLVertexArrayObject; namespace QSGBatchRenderer { #define QSG_RENDERER_COORD_LIMIT 1000000.0f struct Vec; struct Rect; struct Buffer; struct Chunk; struct Batch; struct Node; class Updater; class Renderer; class ShaderManager; template class AllocatorPage { public: // The memory used by this allocator char data[sizeof(Type) * PageSize]; // 'blocks' contains a list of free indices which can be allocated. // The first available index is found in PageSize - available. int blocks[PageSize]; // 'available' is the number of available instances this page has left to allocate. int available; // This is not strictly needed, but useful for sanity checking and anyway // pretty small.. QBitArray allocated; AllocatorPage() : available(PageSize) , allocated(PageSize) { for (int i=0; i class Allocator { public: Allocator() { pages.push_back(new AllocatorPage()); } ~Allocator() { qDeleteAll(pages); } Type *allocate() { AllocatorPage *p = 0; for (int i = m_freePage; i < pages.size(); i++) { if (pages.at(i)->available > 0) { p = pages.at(i); m_freePage = i; break; } } // we couldn't find a free page from m_freePage to the last page. // either there is no free pages, or there weren't any in the area we // scanned: rescanning is expensive, so let's just assume there isn't // one. when an item is released, we'll reset m_freePage anyway. if (!p) { p = new AllocatorPage(); m_freePage = pages.count(); pages.push_back(p); } uint pos = p->blocks[PageSize - p->available]; void *mem = p->at(pos); p->available--; p->allocated.setBit(pos); Type *t = (Type*)mem; return t; } void releaseExplicit(uint pageIndex, uint index) { AllocatorPage *page = pages.at(pageIndex); if (!page->allocated.testBit(index)) qFatal("Double delete in allocator: page=%d, index=%d", pageIndex , index); // Zero this instance as we're done with it. void *mem = page->at(index); memset(mem, 0, sizeof(Type)); page->allocated[index] = false; page->available++; page->blocks[PageSize - page->available] = index; // Remove the pages if they are empty and they are the last ones. We need to keep the // order of pages since we have references to their index, so we can only remove // from the end. while (page->available == PageSize && pages.size() > 1 && pages.back() == page) { pages.pop_back(); delete page; page = pages.back(); } // Reset the free page to force a scan for a new free point. m_freePage = 0; } void release(Type *t) { int pageIndex = -1; for (int i=0; i *p = pages.at(i); if ((Type *) (&p->data[0]) <= t && (Type *) (&p->data[PageSize * sizeof(Type)]) > t) { pageIndex = i; break; } } Q_ASSERT(pageIndex >= 0); AllocatorPage *page = pages.at(pageIndex); int index = (quint64(t) - quint64(&page->data[0])) / sizeof(Type); releaseExplicit(pageIndex, index); } QVector *> pages; int m_freePage = 0; }; inline bool hasMaterialWithBlending(QSGGeometryNode *n) { return (n->opaqueMaterial() ? n->opaqueMaterial()->flags() & QSGMaterial::Blending : n->material()->flags() & QSGMaterial::Blending); } struct Pt { float x, y; void map(const QMatrix4x4 &mat) { Pt r; const float *m = mat.constData(); r.x = x * m[0] + y * m[4] + m[12]; r.y = x * m[1] + y * m[5] + m[13]; x = r.x; y = r.y; } void set(float nx, float ny) { x = nx; y = ny; } }; inline QDebug operator << (QDebug d, const Pt &p) { d << "Pt(" << p.x << p.y << ")"; return d; } struct Rect { Pt tl, br; // Top-Left (min) and Bottom-Right (max) void operator |= (const Pt &pt) { if (pt.x < tl.x) tl.x = pt.x; if (pt.x > br.x) br.x = pt.x; if (pt.y < tl.y) tl.y = pt.y; if (pt.y > br.y) br.y = pt.y; } void operator |= (const Rect &r) { if (r.tl.x < tl.x) tl.x = r.tl.x; if (r.tl.y < tl.y) tl.y = r.tl.y; if (r.br.x > br.x) br.x = r.br.x; if (r.br.y > br.y) br.y = r.br.y; } void map(const QMatrix4x4 &m); void set(float left, float top, float right, float bottom) { tl.set(left, top); br.set(right, bottom); } bool intersects(const Rect &r) { bool xOverlap = r.tl.x < br.x && r.br.x > tl.x; bool yOverlap = r.tl.y < br.y && r.br.y > tl.y; return xOverlap && yOverlap; } bool isOutsideFloatRange() const { return tl.x < -QSG_RENDERER_COORD_LIMIT || tl.y < -QSG_RENDERER_COORD_LIMIT || br.x > QSG_RENDERER_COORD_LIMIT || br.y > QSG_RENDERER_COORD_LIMIT; } }; inline QDebug operator << (QDebug d, const Rect &r) { d << "Rect(" << r.tl.x << r.tl.y << r.br.x << r.br.y << ")"; return d; } struct Buffer { GLuint id; int size; // Data is only valid while preparing the upload. Exception is if we are using the // broken IBO workaround or we are using a visualization mode. char *data; QRhiBuffer *buf; uint nonDynamicChangeCount; }; struct Element { Element() : boundsComputed(false) , boundsOutsideFloatRange(false) , translateOnlyToRoot(false) , removed(false) , orphaned(false) , isRenderNode(false) , isMaterialBlended(false) { } void setNode(QSGGeometryNode *n) { node = n; isMaterialBlended = hasMaterialWithBlending(n); } inline void ensureBoundsValid() { if (!boundsComputed) computeBounds(); } void computeBounds(); QSGGeometryNode *node = nullptr; Batch *batch = nullptr; Element *nextInBatch = nullptr; Node *root = nullptr; Rect bounds; // in device coordinates int order = 0; QRhiShaderResourceBindings *srb = nullptr; QRhiGraphicsPipeline *ps = nullptr; uint boundsComputed : 1; uint boundsOutsideFloatRange : 1; uint translateOnlyToRoot : 1; uint removed : 1; uint orphaned : 1; uint isRenderNode : 1; uint isMaterialBlended : 1; }; struct RenderNodeElement : public Element { RenderNodeElement(QSGRenderNode *rn) : renderNode(rn) { isRenderNode = true; } QSGRenderNode *renderNode; }; struct BatchRootInfo { BatchRootInfo() {} QSet subRoots; Node *parentRoot = nullptr; int lastOrder = -1; int firstOrder = -1; int availableOrders = 0; }; struct ClipBatchRootInfo : public BatchRootInfo { QMatrix4x4 matrix; }; struct DrawSet { DrawSet(int v, int z, int i) : vertices(v) , zorders(z) , indices(i) { } DrawSet() {} int vertices = 0; int zorders = 0; int indices = 0; int indexCount = 0; }; enum BatchCompatibility { BatchBreaksOnCompare, BatchIsCompatible }; struct ClipState { enum ClipTypeBit { NoClip = 0x00, ScissorClip = 0x01, StencilClip = 0x02 }; Q_DECLARE_FLAGS(ClipType, ClipTypeBit) const QSGClipNode *clipList; ClipType type; QRhiScissor scissor; int stencilRef; inline void reset(); }; struct StencilClipState { StencilClipState() : drawCalls(1) { } bool updateStencilBuffer = false; QRhiShaderResourceBindings *srb = nullptr; QRhiBuffer *vbuf = nullptr; QRhiBuffer *ibuf = nullptr; QRhiBuffer *ubuf = nullptr; struct StencilDrawCall { int stencilRef; int vertexCount; int indexCount; QRhiCommandBuffer::IndexFormat indexFormat; quint32 vbufOffset; quint32 ibufOffset; quint32 ubufOffset; }; QDataBuffer drawCalls; inline void reset(); }; struct Batch { Batch() : drawSets(1) {} bool geometryWasChanged(QSGGeometryNode *gn); BatchCompatibility isMaterialCompatible(Element *e) const; void invalidate(); void cleanupRemovedElements(); bool isTranslateOnlyToRoot() const; bool isSafeToBatch() const; // pseudo-constructor... void init() { // Only non-reusable members are reset here. See Renderer::newBatch(). first = nullptr; root = nullptr; vertexCount = 0; indexCount = 0; isOpaque = false; needsUpload = false; merged = false; positionAttribute = -1; uploadedThisFrame = false; isRenderNode = false; ubufDataValid = false; clipState.reset(); blendConstant = QColor(); } Element *first; Node *root; int positionAttribute; int vertexCount; int indexCount; int lastOrderInBatch; uint isOpaque : 1; uint needsUpload : 1; uint merged : 1; uint isRenderNode : 1; uint ubufDataValid : 1; mutable uint uploadedThisFrame : 1; // solely for debugging purposes Buffer vbo; Buffer ibo; QRhiBuffer *ubuf; ClipState clipState; StencilClipState stencilClipState; QColor blendConstant; QDataBuffer drawSets; }; // NOTE: Node is zero-initialized by the Allocator. struct Node { QSGNode *sgNode; void *data; Node *m_parent; Node *m_child; Node *m_next; Node *m_prev; Node *parent() const { return m_parent; } void append(Node *child) { Q_ASSERT(child); Q_ASSERT(!hasChild(child)); Q_ASSERT(child->m_parent == nullptr); Q_ASSERT(child->m_next == nullptr); Q_ASSERT(child->m_prev == nullptr); if (!m_child) { child->m_next = child; child->m_prev = child; m_child = child; } else { m_child->m_prev->m_next = child; child->m_prev = m_child->m_prev; m_child->m_prev = child; child->m_next = m_child; } child->setParent(this); } void remove(Node *child) { Q_ASSERT(child); Q_ASSERT(hasChild(child)); // only child.. if (child->m_next == child) { m_child = nullptr; } else { if (m_child == child) m_child = child->m_next; child->m_next->m_prev = child->m_prev; child->m_prev->m_next = child->m_next; } child->m_next = nullptr; child->m_prev = nullptr; child->setParent(nullptr); } Node *firstChild() const { return m_child; } Node *sibling() const { Q_ASSERT(m_parent); return m_next == m_parent->m_child ? nullptr : m_next; } void setParent(Node *p) { Q_ASSERT(m_parent == nullptr || p == nullptr); m_parent = p; } bool hasChild(Node *child) const { Node *n = m_child; while (n && n != child) n = n->sibling(); return n; } QSGNode::DirtyState dirtyState; uint isOpaque : 1; uint isBatchRoot : 1; uint becameBatchRoot : 1; inline QSGNode::NodeType type() const { return sgNode->type(); } inline Element *element() const { Q_ASSERT(sgNode->type() == QSGNode::GeometryNodeType); return (Element *) data; } inline RenderNodeElement *renderNodeElement() const { Q_ASSERT(sgNode->type() == QSGNode::RenderNodeType); return (RenderNodeElement *) data; } inline ClipBatchRootInfo *clipInfo() const { Q_ASSERT(sgNode->type() == QSGNode::ClipNodeType); return (ClipBatchRootInfo *) data; } inline BatchRootInfo *rootInfo() const { Q_ASSERT(sgNode->type() == QSGNode::ClipNodeType || (sgNode->type() == QSGNode::TransformNodeType && isBatchRoot)); return (BatchRootInfo *) data; } }; class Updater : public QSGNodeUpdater { public: Updater(Renderer *r); void visitOpacityNode(Node *n); void visitTransformNode(Node *n); void visitGeometryNode(Node *n); void visitClipNode(Node *n); void updateRootTransforms(Node *n); void updateRootTransforms(Node *n, Node *root, const QMatrix4x4 &combined); void updateStates(QSGNode *n) override; void visitNode(Node *n); void registerWithParentRoot(QSGNode *subRoot, QSGNode *parentRoot); private: Renderer *renderer; QDataBuffer m_roots; QDataBuffer m_rootMatrices; int m_added; int m_transformChange; int m_opacityChange; QMatrix4x4 m_identityMatrix; }; struct GraphicsState { bool depthTest = false; bool depthWrite = false; QRhiGraphicsPipeline::CompareOp depthFunc = QRhiGraphicsPipeline::Less; bool blending = false; QRhiGraphicsPipeline::BlendFactor srcColor = QRhiGraphicsPipeline::One; QRhiGraphicsPipeline::BlendFactor dstColor = QRhiGraphicsPipeline::OneMinusSrcAlpha; QRhiGraphicsPipeline::ColorMask colorWrite = QRhiGraphicsPipeline::ColorMask(0xF); QRhiGraphicsPipeline::CullMode cullMode = QRhiGraphicsPipeline::None; bool usesScissor = false; bool stencilTest = false; int sampleCount = 1; QSGGeometry::DrawingMode drawMode = QSGGeometry::DrawTriangles; float lineWidth = 1.0f; }; bool operator==(const GraphicsState &a, const GraphicsState &b) Q_DECL_NOTHROW; bool operator!=(const GraphicsState &a, const GraphicsState &b) Q_DECL_NOTHROW; uint qHash(const GraphicsState &s, uint seed = 0) Q_DECL_NOTHROW; struct ShaderManagerShader; struct GraphicsPipelineStateKey { GraphicsState state; const ShaderManagerShader *sms; const QRhiRenderPassDescriptor *compatibleRenderPassDescriptor; const QRhiShaderResourceBindings *layoutCompatibleSrb; }; bool operator==(const GraphicsPipelineStateKey &a, const GraphicsPipelineStateKey &b) Q_DECL_NOTHROW; bool operator!=(const GraphicsPipelineStateKey &a, const GraphicsPipelineStateKey &b) Q_DECL_NOTHROW; uint qHash(const GraphicsPipelineStateKey &k, uint seed = 0) Q_DECL_NOTHROW; struct ShaderManagerShader { ~ShaderManagerShader() { delete programRhi.program; delete programGL.program; } struct { QSGMaterialShader *program = nullptr; int pos_order; } programGL; struct { QSGMaterialRhiShader *program = nullptr; QRhiVertexInputLayout inputLayout; QVarLengthArray shaderStages; } programRhi; float lastOpacity; }; class ShaderManager : public QObject { Q_OBJECT public: using Shader = ShaderManagerShader; ShaderManager(QSGDefaultRenderContext *ctx) : blitProgram(nullptr), context(ctx) { } ~ShaderManager() { qDeleteAll(rewrittenShaders); qDeleteAll(stockShaders); } void clearCachedRendererData(); using ShaderResourceBindingList = QVarLengthArray; QRhiShaderResourceBindings *srb(const ShaderResourceBindingList &bindings); QHash pipelineCache; public Q_SLOTS: void invalidated(); public: Shader *prepareMaterial(QSGMaterial *material, bool enableRhiShaders = false, const QSGGeometry *geometry = nullptr); Shader *prepareMaterialNoRewrite(QSGMaterial *material, bool enableRhiShaders = false, const QSGGeometry *geometry = nullptr); private: QHash rewrittenShaders; QHash stockShaders; QOpenGLShaderProgram *blitProgram; QSGDefaultRenderContext *context; QHash srbCache; }; struct RenderPassState { QRhiViewport viewport; QColor clearColor; QRhiDepthStencilClearValue dsClear; bool viewportSet; bool scissorSet; }; class Visualizer { public: enum VisualizeMode { VisualizeNothing, VisualizeBatches, VisualizeClipping, VisualizeChanges, VisualizeOverdraw }; Visualizer(Renderer *renderer); virtual ~Visualizer(); VisualizeMode mode() const { return m_visualizeMode; } void setMode(VisualizeMode mode) { m_visualizeMode = mode; } virtual void visualizeChangesPrepare(Node *n, uint parentChanges = 0); virtual void prepareVisualize() = 0; virtual void visualize() = 0; virtual void releaseResources() = 0; protected: Renderer *m_renderer; VisualizeMode m_visualizeMode; QHash m_visualizeChangeSet; }; class Q_QUICK_PRIVATE_EXPORT Renderer : public QSGRenderer, public QOpenGLFunctions { public: Renderer(QSGDefaultRenderContext *); ~Renderer(); protected: void nodeChanged(QSGNode *node, QSGNode::DirtyState state) override; void render() override; void releaseCachedResources() override; private: enum RebuildFlag { BuildRenderListsForTaggedRoots = 0x0001, BuildRenderLists = 0x0002, BuildBatches = 0x0004, FullRebuild = 0xffff }; friend class Updater; friend class OpenGLVisualizer; friend class RhiVisualizer; void destroyGraphicsResources(); void map(Buffer *buffer, int size, bool isIndexBuf = false); void unmap(Buffer *buffer, bool isIndexBuf = false); void buildRenderListsFromScratch(); void buildRenderListsForTaggedRoots(); void tagSubRoots(Node *node); void buildRenderLists(QSGNode *node); void deleteRemovedElements(); void cleanupBatches(QDataBuffer *batches); void prepareOpaqueBatches(); bool checkOverlap(int first, int last, const Rect &bounds); void prepareAlphaBatches(); void invalidateBatchAndOverlappingRenderOrders(Batch *batch); void uploadBatch(Batch *b); void uploadMergedElement(Element *e, int vaOffset, char **vertexData, char **zData, char **indexData, void *iBasePtr, int *indexCount); struct PreparedRenderBatch { const Batch *batch; ShaderManager::Shader *sms; }; void renderBatches(); bool ensurePipelineState(Element *e, const ShaderManager::Shader *sms); QRhiTexture *dummyTexture(); void updateMaterialDynamicData(ShaderManager::Shader *sms, QSGMaterialRhiShader::RenderState &renderState, QSGMaterial *material, ShaderManager::ShaderResourceBindingList *bindings, const Batch *batch, int ubufOffset, int ubufRegionSize); void updateMaterialStaticData(ShaderManager::Shader *sms, QSGMaterialRhiShader::RenderState &renderState, QSGMaterial *material, Batch *batch, bool *gstateChanged); void checkLineWidth(QSGGeometry *g); bool prepareRenderMergedBatch(Batch *batch, PreparedRenderBatch *renderBatch); void renderMergedBatch(PreparedRenderBatch *renderBatch); bool prepareRenderUnmergedBatch(Batch *batch, PreparedRenderBatch *renderBatch); void renderUnmergedBatch(PreparedRenderBatch *renderBatch); void setGraphicsPipeline(QRhiCommandBuffer *cb, const Batch *batch, Element *e); void renderMergedBatch(const Batch *batch); // GL void renderUnmergedBatch(const Batch *batch); // GL ClipState::ClipType updateStencilClip(const QSGClipNode *clip); void updateClip(const QSGClipNode *clipList, const Batch *batch); void applyClipStateToGraphicsState(); QRhiGraphicsPipeline *buildStencilPipeline(const Batch *batch, bool firstStencilClipInBatch); void updateClipState(const QSGClipNode *clipList, Batch *batch); void enqueueStencilDraw(const Batch *batch); const QMatrix4x4 &matrixForRoot(Node *node); void renderRenderNode(Batch *batch); bool prepareRhiRenderNode(Batch *batch, PreparedRenderBatch *renderBatch); void renderRhiRenderNode(const Batch *batch); void setActiveShader(QSGMaterialShader *program, ShaderManager::Shader *shader); void setActiveRhiShader(QSGMaterialRhiShader *program, ShaderManager::Shader *shader); bool changeBatchRoot(Node *node, Node *newRoot); void registerBatchRoot(Node *childRoot, Node *parentRoot); void removeBatchRootFromParent(Node *childRoot); void nodeChangedBatchRoot(Node *node, Node *root); void turnNodeIntoBatchRoot(Node *node); void nodeWasTransformed(Node *node, int *vertexCount); void nodeWasRemoved(Node *node); void nodeWasAdded(QSGNode *node, Node *shadowParent); BatchRootInfo *batchRootInfo(Node *node); void updateLineWidth(QSGGeometry *g); inline Batch *newBatch(); void invalidateAndRecycleBatch(Batch *b); void setCustomRenderMode(const QByteArray &mode) override; bool hasCustomRenderModeWithContinuousUpdate() const override; QSGDefaultRenderContext *m_context; QSet m_taggedRoots; QDataBuffer m_opaqueRenderList; QDataBuffer m_alphaRenderList; int m_nextRenderOrder; bool m_partialRebuild; QSGNode *m_partialRebuildRoot; bool m_useDepthBuffer; QHash m_renderNodeElements; QDataBuffer m_opaqueBatches; QDataBuffer m_alphaBatches; QHash m_nodes; QDataBuffer m_batchPool; QDataBuffer m_elementsToDelete; QDataBuffer m_tmpAlphaElements; QDataBuffer m_tmpOpaqueElements; uint m_rebuild; qreal m_zRange; int m_renderOrderRebuildLower; int m_renderOrderRebuildUpper; GLuint m_bufferStrategy; int m_batchNodeThreshold; int m_batchVertexThreshold; Visualizer *m_visualizer; // Stuff used during rendering only... ShaderManager *m_shaderManager; // per rendercontext, shared QSGMaterial *m_currentMaterial; QSGMaterialShader *m_currentProgram; QSGMaterialRhiShader *m_currentRhiProgram; ShaderManager::Shader *m_currentShader; ClipState m_currentClipState; // *** legacy (GL) only QRect m_currentScissorRect; int m_currentStencilValue; QOpenGLShaderProgram m_clipProgram; int m_clipMatrixId; const QSGClipNode *m_currentClip; ClipState::ClipType m_currentClipType; // *** QDataBuffer m_vertexUploadPool; QDataBuffer m_indexUploadPool; // For minimal OpenGL core profile support QOpenGLVertexArrayObject *m_vao; Allocator m_nodeAllocator; Allocator m_elementAllocator; QRhiResourceUpdateBatch *m_resourceUpdates = nullptr; uint m_ubufAlignment; bool m_uint32IndexForRhi; GraphicsState m_gstate; RenderPassState m_pstate; QStack m_gstateStack; QHash m_samplers; QRhiTexture *m_dummyTexture = nullptr; struct StencilClipCommonData { QRhiGraphicsPipeline *replacePs = nullptr; QRhiGraphicsPipeline *incrPs = nullptr; QShader vs; QShader fs; QRhiVertexInputLayout inputLayout; QRhiGraphicsPipeline::Topology topology; inline void reset(); } m_stencilClipCommon; inline int mergedIndexElemSize() const; }; Batch *Renderer::newBatch() { Batch *b; int size = m_batchPool.size(); if (size) { b = m_batchPool.at(size - 1); // vbo, ibo, ubuf, stencil-related buffers are reused m_batchPool.resize(size - 1); } else { b = new Batch(); Q_ASSERT(offsetof(Batch, ibo) == sizeof(Buffer) + offsetof(Batch, vbo)); memset(&b->vbo, 0, sizeof(Buffer) * 2); // Clear VBO & IBO b->ubuf = nullptr; b->stencilClipState.reset(); } // initialize (when new batch) or reset (when reusing a batch) the non-reusable fields b->init(); return b; } int Renderer::mergedIndexElemSize() const { return m_uint32IndexForRhi ? sizeof(quint32) : sizeof(quint16); } void Renderer::StencilClipCommonData::reset() { delete replacePs; replacePs = nullptr; delete incrPs; incrPs = nullptr; vs = QShader(); fs = QShader(); } void ClipState::reset() { clipList = nullptr; type = NoClip; stencilRef = 0; } void StencilClipState::reset() { updateStencilBuffer = false; delete srb; srb = nullptr; delete vbuf; vbuf = nullptr; delete ibuf; ibuf = nullptr; delete ubuf; ubuf = nullptr; drawCalls.reset(); } } Q_DECLARE_TYPEINFO(QSGBatchRenderer::GraphicsState, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(QSGBatchRenderer::GraphicsPipelineStateKey, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(QSGBatchRenderer::RenderPassState, Q_MOVABLE_TYPE); QT_END_NAMESPACE #endif // QSGBATCHRENDERER_P_H