/**************************************************************************** ** ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtQuick module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL21$ ** 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 Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/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 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef ULTRARENDERER_H #define ULTRARENDERER_H #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; 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; char *data; }; struct Element { Element(QSGGeometryNode *n) : node(n) , batch(0) , nextInBatch(0) , root(0) , order(0) , boundsComputed(false) , boundsOutsideFloatRange(false) , translateOnlyToRoot(false) , removed(false) , orphaned(false) , isRenderNode(false) , isMaterialBlended(n ? hasMaterialWithBlending(n) : false) { } inline void ensureBoundsValid() { if (!boundsComputed) computeBounds(); } void computeBounds(); QSGGeometryNode *node; Batch *batch; Element *nextInBatch; Node *root; Rect bounds; // in device coordinates int order; 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) : Element(0) , renderNode(rn) { isRenderNode = true; } QSGRenderNode *renderNode; }; struct BatchRootInfo { BatchRootInfo() : parentRoot(0), lastOrder(-1), firstOrder(-1), availableOrders(0) { } QSet subRoots; Node *parentRoot; int lastOrder; int firstOrder; int availableOrders; }; struct ClipBatchRootInfo : public BatchRootInfo { QMatrix4x4 matrix; }; struct DrawSet { DrawSet(int v, int z, int i) : vertices(v) , zorders(z) , indices(i) , indexCount(0) { } DrawSet() : vertices(0), zorders(0), indices(0), indexCount(0) {} int vertices; int zorders; int indices; int indexCount; }; enum BatchCompatibility { BatchBreaksOnCompare, BatchIsCompatible }; 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() { first = 0; root = 0; vertexCount = 0; indexCount = 0; isOpaque = false; needsUpload = false; merged = false; positionAttribute = -1; uploadedThisFrame = false; isRenderNode = false; } Element *first; Node *root; int positionAttribute; int vertexCount; int indexCount; int lastOrderInBatch; uint isOpaque : 1; uint needsUpload : 1; uint merged : 1; uint isRenderNode : 1; mutable uint uploadedThisFrame : 1; // solely for debugging purposes Buffer vbo; #ifdef QSG_SEPARATE_INDEX_BUFFER Buffer ibo; #endif QDataBuffer drawSets; }; struct Node { Node(QSGNode *node, Node *sparent = 0) : sgNode(node) , parent(sparent) , data(0) , dirtyState(0) , isOpaque(false) , isBatchRoot(false) { } QSGNode *sgNode; Node *parent; void *data; QList children; 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); 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; }; class ShaderManager : public QObject { Q_OBJECT public: struct Shader { ~Shader() { delete program; } int id_zRange; int pos_order; QSGMaterialShader *program; float lastOpacity; }; ShaderManager(QSGRenderContext *ctx) : blitProgram(0), visualizeProgram(0), context(ctx) { } ~ShaderManager() { qDeleteAll(rewrittenShaders.values()); qDeleteAll(stockShaders.values()); } public Q_SLOTS: void invalidated(); public: Shader *prepareMaterial(QSGMaterial *material); Shader *prepareMaterialNoRewrite(QSGMaterial *material); QHash rewrittenShaders; QHash stockShaders; QOpenGLShaderProgram *blitProgram; QOpenGLShaderProgram *visualizeProgram; QSGRenderContext *context; }; class Q_QUICK_PRIVATE_EXPORT Renderer : public QSGRenderer, public QOpenGLFunctions { public: Renderer(QSGRenderContext *); ~Renderer(); enum VisualizeMode { VisualizeNothing, VisualizeBatches, VisualizeClipping, VisualizeChanges, VisualizeOverdraw }; protected: void nodeChanged(QSGNode *node, QSGNode::DirtyState state); void preprocess() Q_DECL_OVERRIDE; void render(); private: enum ClipTypeBit { NoClip = 0x00, ScissorClip = 0x01, StencilClip = 0x02 }; Q_DECLARE_FLAGS(ClipType, ClipTypeBit) enum RebuildFlag { BuildRenderListsForTaggedRoots = 0x0001, BuildRenderLists = 0x0002, BuildBatches = 0x0004, FullRebuild = 0xffff }; friend class Updater; void map(Buffer *buffer, int size); 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, quint16 *iBase, int *indexCount); void renderBatches(); void renderMergedBatch(const Batch *batch); void renderUnmergedBatch(const Batch *batch); ClipType updateStencilClip(const QSGClipNode *clip); void updateClip(const QSGClipNode *clipList, const Batch *batch); const QMatrix4x4 &matrixForRoot(Node *node); void renderRenderNode(Batch *batch); void setActiveShader(QSGMaterialShader *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); inline Batch *newBatch(); void invalidateAndRecycleBatch(Batch *b); void visualize(); void visualizeBatch(Batch *b); void visualizeClipping(QSGNode *node); void visualizeChangesPrepare(Node *n, uint parentChanges = 0); void visualizeChanges(Node *n); void visualizeOverdraw(); void visualizeOverdraw_helper(Node *node); void visualizeDrawGeometry(const QSGGeometry *g); void setCustomRenderMode(const QByteArray &mode); 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; // Stuff used during rendering only... ShaderManager *m_shaderManager; QSGMaterial *m_currentMaterial; QSGMaterialShader *m_currentProgram; ShaderManager::Shader *m_currentShader; QRect m_currentScissorRect; int m_currentStencilValue; QOpenGLShaderProgram m_clipProgram; int m_clipMatrixId; const QSGClipNode *m_currentClip; ClipType m_currentClipType; // For minimal OpenGL core profile support QOpenGLVertexArrayObject *m_vao; QHash m_visualizeChanceSet; VisualizeMode m_visualizeMode; }; Batch *Renderer::newBatch() { Batch *b; int size = m_batchPool.size(); if (size) { b = m_batchPool.at(size - 1); m_batchPool.resize(size - 1); } else { b = new Batch(); memset(&b->vbo, 0, sizeof(Buffer)); #ifdef QSG_SEPARATE_INDEX_BUFFER memset(&b->ibo, 0, sizeof(Buffer)); #endif } b->init(); return b; } } QT_END_NAMESPACE #endif // ULTRARENDERER_H