diff options
Diffstat (limited to 'src/quick/scenegraph/coreapi')
-rw-r--r-- | src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp | 407 | ||||
-rw-r--r-- | src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h | 49 | ||||
-rw-r--r-- | src/quick/scenegraph/coreapi/qsgopenglvisualizer.cpp | 365 | ||||
-rw-r--r-- | src/quick/scenegraph/coreapi/qsgopenglvisualizer_p.h | 91 | ||||
-rw-r--r-- | src/quick/scenegraph/coreapi/qsgrenderer_p.h | 1 | ||||
-rw-r--r-- | src/quick/scenegraph/coreapi/qsgrhivisualizer.cpp | 929 | ||||
-rw-r--r-- | src/quick/scenegraph/coreapi/qsgrhivisualizer_p.h | 233 |
7 files changed, 1730 insertions, 345 deletions
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp index b5437ffb7e..2cd9ee689b 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp @@ -59,6 +59,9 @@ #include <private/qquickprofiler_p.h> #include "qsgmaterialrhishader_p.h" +#include "qsgopenglvisualizer_p.h" +#include "qsgrhivisualizer_p.h" + #include <algorithm> #ifndef GL_DOUBLE @@ -145,7 +148,7 @@ static inline uint aligned(uint v, uint byteAlign) return (v + byteAlign - 1) & ~(byteAlign - 1); } -static inline QRhiVertexInputAttribute::Format vertexInputFormat(const QSGGeometry::Attribute &a) +QRhiVertexInputAttribute::Format qsg_vertexInputFormat(const QSGGeometry::Attribute &a) { switch (a.type) { case QSGGeometry::FloatType: @@ -193,7 +196,7 @@ static QRhiVertexInputLayout calculateVertexInputLayout(const QSGMaterialRhiShad qWarning("Vertex input %d is present in material but not in shader. This is wrong.", a.position); } - inputAttributes.append(QRhiVertexInputAttribute(VERTEX_BUFFER_BINDING, a.position, vertexInputFormat(a), offset)); + inputAttributes.append(QRhiVertexInputAttribute(VERTEX_BUFFER_BINDING, a.position, qsg_vertexInputFormat(a), offset)); offset += a.tupleSize * size_of_type(a.type); } if (batchable) { @@ -215,7 +218,7 @@ static QRhiVertexInputLayout calculateVertexInputLayout(const QSGMaterialRhiShad return inputLayout; } -static inline QRhiCommandBuffer::IndexFormat indexFormat(const QSGGeometry *geometry) +QRhiCommandBuffer::IndexFormat qsg_indexFormat(const QSGGeometry *geometry) { switch (geometry->indexType()) { case QSGGeometry::UnsignedShortType: @@ -230,7 +233,7 @@ static inline QRhiCommandBuffer::IndexFormat indexFormat(const QSGGeometry *geom } } -static inline QRhiGraphicsPipeline::Topology gpTopology(int geomDrawMode) +QRhiGraphicsPipeline::Topology qsg_topology(int geomDrawMode) { QRhiGraphicsPipeline::Topology topology = QRhiGraphicsPipeline::Triangles; switch (geomDrawMode) { @@ -501,8 +504,8 @@ void Updater::updateStates(QSGNode *n) qDebug(" - forceupdate"); } - if (Q_UNLIKELY(renderer->m_visualizeMode == Renderer::VisualizeChanges)) - renderer->visualizeChangesPrepare(sn); + if (Q_UNLIKELY(renderer->m_visualizer->mode() == Visualizer::VisualizeChanges)) + renderer->m_visualizer->visualizeChangesPrepare(sn); visitNode(sn); } @@ -975,7 +978,6 @@ Renderer::Renderer(QSGDefaultRenderContext *ctx) , m_vertexUploadPool(256) , m_indexUploadPool(64) , m_vao(nullptr) - , m_visualizeMode(VisualizeNothing) { m_rhi = m_context->rhi(); if (m_rhi) { @@ -983,9 +985,11 @@ Renderer::Renderer(QSGDefaultRenderContext *ctx) m_uint32IndexForRhi = !m_rhi->isFeatureSupported(QRhi::NonFourAlignedEffectiveIndexBufferOffset); if (qEnvironmentVariableIntValue("QSG_RHI_UINT32_INDEX")) m_uint32IndexForRhi = true; + m_visualizer = new RhiVisualizer(this); } else { initializeOpenGLFunctions(); m_uint32IndexForRhi = false; + m_visualizer = new OpenGLVisualizer(this); } setNodeUpdater(new Updater(this)); @@ -1088,6 +1092,8 @@ Renderer::~Renderer() } destroyGraphicsResources(); + + delete m_visualizer; } void Renderer::destroyGraphicsResources() @@ -1104,6 +1110,8 @@ void Renderer::destroyGraphicsResources() m_stencilClipCommon.reset(); delete m_dummyTexture; + + m_visualizer->releaseResources(); } void Renderer::releaseCachedResources() @@ -1138,7 +1146,7 @@ void Renderer::invalidateAndRecycleBatch(Batch *b) */ void Renderer::map(Buffer *buffer, int byteSize, bool isIndexBuf) { - if (!m_context->hasBrokenIndexBufferObjects() && m_visualizeMode == VisualizeNothing) { + if (!m_context->hasBrokenIndexBufferObjects() && m_visualizer->mode() == Visualizer::VisualizeNothing) { // Common case, use a shared memory pool for uploading vertex data to avoid // excessive reevaluation QDataBuffer<char> &pool = m_context->separateIndexBuffer() && isIndexBuf @@ -1194,7 +1202,7 @@ void Renderer::unmap(Buffer *buffer, bool isIndexBuf) m_resourceUpdates->updateDynamicBuffer(buffer->buf, 0, buffer->size, QByteArray::fromRawData(buffer->data, buffer->size)); } - if (m_visualizeMode == VisualizeNothing) + if (m_visualizer->mode() == Visualizer::VisualizeNothing) buffer->data = nullptr; } else { if (buffer->id == 0) @@ -1202,7 +1210,7 @@ void Renderer::unmap(Buffer *buffer, bool isIndexBuf) GLenum target = isIndexBuf ? GL_ELEMENT_ARRAY_BUFFER : GL_ARRAY_BUFFER; glBindBuffer(target, buffer->id); glBufferData(target, buffer->size, buffer->data, m_bufferStrategy); - if (!m_context->hasBrokenIndexBufferObjects() && m_visualizeMode == VisualizeNothing) + if (!m_context->hasBrokenIndexBufferObjects() && m_visualizer->mode() == Visualizer::VisualizeNothing) buffer->data = nullptr; } } @@ -2096,7 +2104,7 @@ void Renderer::uploadMergedElement(Element *e, int vaOffset, char **vertexData, *indexCount += iCount; } -static QMatrix4x4 qsg_matrixForRoot(Node *node) +QMatrix4x4 qsg_matrixForRoot(Node *node) { if (node->type() == QSGNode::TransformNodeType) return static_cast<QSGTransformNode *>(node->sgNode)->combinedMatrix(); @@ -2830,31 +2838,31 @@ void Renderer::updateClipState(const QSGClipNode *clipList, Batch *batch) // RHI if (firstStencilClipInBatch) { m_stencilClipCommon.inputLayout.setBindings({ QRhiVertexInputBinding(g->sizeOfVertex()) }); - m_stencilClipCommon.inputLayout.setAttributes({ QRhiVertexInputAttribute(0, 0, vertexInputFormat(*a), 0) }); - m_stencilClipCommon.topology = gpTopology(g->drawingMode()); + m_stencilClipCommon.inputLayout.setAttributes({ QRhiVertexInputAttribute(0, 0, qsg_vertexInputFormat(*a), 0) }); + m_stencilClipCommon.topology = qsg_topology(g->drawingMode()); } #ifndef QT_NO_DEBUG else { - if (gpTopology(g->drawingMode()) != m_stencilClipCommon.topology) + if (qsg_topology(g->drawingMode()) != m_stencilClipCommon.topology) qWarning("updateClipState: Clip list entries have different primitive topologies, this is not currently supported."); - if (vertexInputFormat(*a) != m_stencilClipCommon.inputLayout.attributes().first().format()) + if (qsg_vertexInputFormat(*a) != m_stencilClipCommon.inputLayout.attributes().first().format()) qWarning("updateClipState: Clip list entries have different vertex input layouts, this is must not happen."); } #endif drawCall.vbufOffset = aligned(vOffset, 4); const int vertexByteSize = g->sizeOfVertex() * g->vertexCount(); - vOffset += vertexByteSize; + vOffset = drawCall.vbufOffset + vertexByteSize; int indexByteSize = 0; if (g->indexCount()) { drawCall.ibufOffset = aligned(iOffset, 4); indexByteSize = g->sizeOfIndex() * g->indexCount(); - iOffset += indexByteSize; + iOffset = drawCall.ibufOffset + indexByteSize; } drawCall.ubufOffset = aligned(uOffset, m_ubufAlignment); - uOffset += StencilClipUbufSize; + uOffset = drawCall.ubufOffset + StencilClipUbufSize; QMatrix4x4 matrixYUpNDC = m_current_projection_matrix; if (clip->matrix()) @@ -2874,7 +2882,7 @@ void Renderer::updateClipState(const QSGClipNode *clipList, Batch *batch) // RHI drawCall.vertexCount = g->vertexCount(); drawCall.indexCount = g->indexCount(); - drawCall.indexFormat = indexFormat(g); + drawCall.indexFormat = qsg_indexFormat(g); batch->stencilClipState.drawCalls.add(drawCall); } @@ -2919,13 +2927,13 @@ void Renderer::enqueueStencilDraw(const Batch *batch) // RHI only QRhiCommandBuffer::DynamicOffset ubufOffset(0, drawCall.ubufOffset); if (i == 0) { cb->setGraphicsPipeline(m_stencilClipCommon.replacePs); - cb->setShaderResources(srb, 1, &ubufOffset); cb->setViewport(m_pstate.viewport); } else if (i == 1) { cb->setGraphicsPipeline(m_stencilClipCommon.incrPs); - cb->setShaderResources(srb, 1, &ubufOffset); cb->setViewport(m_pstate.viewport); } + // else incrPs is already bound + cb->setShaderResources(srb, 1, &ubufOffset); cb->setStencilRef(drawCall.stencilRef); const QRhiCommandBuffer::VertexInput vbufBinding(batch->stencilClipState.vbuf, drawCall.vbufOffset); if (drawCall.indexCount) { @@ -3255,7 +3263,7 @@ bool Renderer::ensurePipelineState(Element *e, const ShaderManager::Shader *sms) flags |= QRhiGraphicsPipeline::UsesStencilRef; ps->setFlags(flags); - ps->setTopology(gpTopology(m_gstate.drawMode)); + ps->setTopology(qsg_topology(m_gstate.drawMode)); ps->setCullMode(m_gstate.cullMode); QRhiGraphicsPipeline::TargetBlend blend; @@ -4089,6 +4097,9 @@ void Renderer::renderBatches() } } + if (m_visualizer->mode() != Visualizer::VisualizeNothing) + m_visualizer->prepareVisualize(); + QRhiCommandBuffer *cb = commandBuffer(); cb->beginPass(renderTarget(), m_pstate.clearColor, m_pstate.dsClear, m_resourceUpdates); m_resourceUpdates = nullptr; @@ -4120,7 +4131,8 @@ void Renderer::renderBatches() if (m_renderPassRecordingCallbacks.end) m_renderPassRecordingCallbacks.end(m_renderPassRecordingCallbacks.userData); - cb->endPass(); + if (m_visualizer->mode() == Visualizer::VisualizeNothing) + cb->endPass(); } } @@ -4314,13 +4326,16 @@ void Renderer::render() m_renderOrderRebuildLower = -1; m_renderOrderRebuildUpper = -1; - if (!m_rhi) { - if (m_visualizeMode != VisualizeNothing) - visualize(); + if (m_visualizer->mode() != Visualizer::VisualizeNothing) + m_visualizer->visualize(); + if (!m_rhi) { if (m_vao) m_vao->release(); } else { + if (m_visualizer->mode() != Visualizer::VisualizeNothing) + commandBuffer()->endPass(); + if (m_resourceUpdates) { m_resourceUpdates->release(); m_resourceUpdates = nullptr; @@ -4460,311 +4475,23 @@ void Renderer::renderRenderNode(Batch *batch) glBindFramebuffer(GL_FRAMEBUFFER, prevFbo); } -class VisualizeShader : public QOpenGLShaderProgram -{ -public: - int color; - int matrix; - int rotation; - int pattern; - int projection; -}; - -void Renderer::visualizeDrawGeometry(const QSGGeometry *g) -{ - if (g->attributeCount() < 1) - return; - const QSGGeometry::Attribute *a = g->attributes(); - glVertexAttribPointer(0, a->tupleSize, a->type, false, g->sizeOfVertex(), g->vertexData()); - if (g->indexCount()) - glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), g->indexData()); - else - glDrawArrays(g->drawingMode(), 0, g->vertexCount()); - -} - -void Renderer::visualizeBatch(Batch *b) -{ - VisualizeShader *shader = static_cast<VisualizeShader *>(m_shaderManager->visualizeProgram); - - if (b->positionAttribute != 0) - return; - - QSGGeometryNode *gn = b->first->node; - QSGGeometry *g = gn->geometry(); - const QSGGeometry::Attribute &a = g->attributes()[b->positionAttribute]; - - glBindBuffer(GL_ARRAY_BUFFER, b->vbo.id); - - QMatrix4x4 matrix(m_current_projection_matrix); - if (b->root) - matrix = matrix * qsg_matrixForRoot(b->root); - - shader->setUniformValue(shader->pattern, float(b->merged ? 0 : 1)); - - QColor color = QColor::fromHsvF((rand() & 1023) / 1023.0, 1.0, 1.0); - float cr = color.redF(); - float cg = color.greenF(); - float cb = color.blueF(); - shader->setUniformValue(shader->color, cr, cg, cb, 1.0); - - if (b->merged) { - shader->setUniformValue(shader->matrix, matrix); - const char *dataStart = m_context->separateIndexBuffer() ? b->ibo.data : b->vbo.data; - for (int ds=0; ds<b->drawSets.size(); ++ds) { - const DrawSet &set = b->drawSets.at(ds); - glVertexAttribPointer(a.position, 2, a.type, false, g->sizeOfVertex(), (void *) (qintptr) (set.vertices)); - glDrawElements(g->drawingMode(), set.indexCount, GL_UNSIGNED_SHORT, - (void *)(qintptr)(dataStart + set.indices)); - } - } else { - Element *e = b->first; - int offset = 0; - while (e) { - gn = e->node; - g = gn->geometry(); - shader->setUniformValue(shader->matrix, matrix * *gn->matrix()); - glVertexAttribPointer(a.position, a.tupleSize, a.type, false, g->sizeOfVertex(), (void *) (qintptr) offset); - if (g->indexCount()) - glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), g->indexData()); - else - glDrawArrays(g->drawingMode(), 0, g->vertexCount()); - offset += g->sizeOfVertex() * g->vertexCount(); - e = e->nextInBatch; - } - } -} - - - - -void Renderer::visualizeClipping(QSGNode *node) -{ - if (node->type() == QSGNode::ClipNodeType) { - VisualizeShader *shader = static_cast<VisualizeShader *>(m_shaderManager->visualizeProgram); - QSGClipNode *clipNode = static_cast<QSGClipNode *>(node); - QMatrix4x4 matrix = m_current_projection_matrix; - if (clipNode->matrix()) - matrix = matrix * *clipNode->matrix(); - shader->setUniformValue(shader->matrix, matrix); - visualizeDrawGeometry(clipNode->geometry()); - } - - QSGNODE_TRAVERSE(node) { - visualizeClipping(child); - } -} - -#define QSGNODE_DIRTY_PARENT (QSGNode::DirtyNodeAdded \ - | QSGNode::DirtyOpacity \ - | QSGNode::DirtyMatrix \ - | QSGNode::DirtyNodeRemoved) - -void Renderer::visualizeChangesPrepare(Node *n, uint parentChanges) -{ - uint childDirty = (parentChanges | n->dirtyState) & QSGNODE_DIRTY_PARENT; - uint selfDirty = n->dirtyState | parentChanges; - if (n->type() == QSGNode::GeometryNodeType && selfDirty != 0) - m_visualizeChanceSet.insert(n, selfDirty); - SHADOWNODE_TRAVERSE(n) { - visualizeChangesPrepare(child, childDirty); - } -} - -void Renderer::visualizeChanges(Node *n) -{ - - if (n->type() == QSGNode::GeometryNodeType && n->element()->batch && m_visualizeChanceSet.contains(n)) { - uint dirty = m_visualizeChanceSet.value(n); - bool tinted = (dirty & QSGNODE_DIRTY_PARENT) != 0; - - VisualizeShader *shader = static_cast<VisualizeShader *>(m_shaderManager->visualizeProgram); - QColor color = QColor::fromHsvF((rand() & 1023) / 1023.0, 0.3, 1.0); - float ca = 0.5; - float cr = color.redF() * ca; - float cg = color.greenF() * ca; - float cb = color.blueF() * ca; - shader->setUniformValue(shader->color, cr, cg, cb, ca); - shader->setUniformValue(shader->pattern, float(tinted ? 0.5 : 0)); - - QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(n->sgNode); - - QMatrix4x4 matrix = m_current_projection_matrix; - if (n->element()->batch->root) - matrix = matrix * qsg_matrixForRoot(n->element()->batch->root); - matrix = matrix * *gn->matrix(); - shader->setUniformValue(shader->matrix, matrix); - visualizeDrawGeometry(gn->geometry()); - - // This is because many changes don't propegate their dirty state to the - // parent so the node updater will not unset these states. They are - // not used for anything so, unsetting it should have no side effects. - n->dirtyState = nullptr; - } - - SHADOWNODE_TRAVERSE(n) { - visualizeChanges(child); - } -} - -void Renderer::visualizeOverdraw_helper(Node *node) -{ - if (node->type() == QSGNode::GeometryNodeType && node->element()->batch) { - VisualizeShader *shader = static_cast<VisualizeShader *>(m_shaderManager->visualizeProgram); - QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(node->sgNode); - - QMatrix4x4 matrix = m_current_projection_matrix; - matrix(2, 2) = m_zRange; - matrix(2, 3) = 1.0f - node->element()->order * m_zRange; - - if (node->element()->batch->root) - matrix = matrix * qsg_matrixForRoot(node->element()->batch->root); - matrix = matrix * *gn->matrix(); - shader->setUniformValue(shader->matrix, matrix); - - QColor color = node->element()->batch->isOpaque ? QColor::fromRgbF(0.3, 1.0, 0.3) : QColor::fromRgbF(1.0, 0.3, 0.3); - float ca = 0.33f; - shader->setUniformValue(shader->color, color.redF() * ca, color.greenF() * ca, color.blueF() * ca, ca); - - visualizeDrawGeometry(gn->geometry()); - } - - SHADOWNODE_TRAVERSE(node) { - visualizeOverdraw_helper(child); - } -} - -void Renderer::visualizeOverdraw() -{ - VisualizeShader *shader = static_cast<VisualizeShader *>(m_shaderManager->visualizeProgram); - shader->setUniformValue(shader->color, 0.5f, 0.5f, 1.0f, 1.0f); - shader->setUniformValue(shader->projection, 1); - - glBlendFunc(GL_ONE, GL_ONE); - - static float step = 0; - step += static_cast<float>(M_PI * 2 / 1000.); - if (step > M_PI * 2) - step = 0; - float angle = 80.0 * std::sin(step); - - QMatrix4x4 xrot; xrot.rotate(20, 1, 0, 0); - QMatrix4x4 zrot; zrot.rotate(angle, 0, 0, 1); - QMatrix4x4 tx; tx.translate(0, 0, 1); - - QMatrix4x4 m; - -// m.rotate(180, 0, 1, 0); - - m.translate(0, 0.5, 4); - m.scale(2, 2, 1); - - m.rotate(-30, 1, 0, 0); - m.rotate(angle, 0, 1, 0); - m.translate(0, 0, -1); - - shader->setUniformValue(shader->rotation, m); - - float box[] = { - // lower - -1, 1, 0, 1, 1, 0, - -1, 1, 0, -1, -1, 0, - 1, 1, 0, 1, -1, 0, - -1, -1, 0, 1, -1, 0, - - // upper - -1, 1, 1, 1, 1, 1, - -1, 1, 1, -1, -1, 1, - 1, 1, 1, 1, -1, 1, - -1, -1, 1, 1, -1, 1, - - // sides - -1, -1, 0, -1, -1, 1, - 1, -1, 0, 1, -1, 1, - -1, 1, 0, -1, 1, 1, - 1, 1, 0, 1, 1, 1 - }; - glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, box); - glLineWidth(2); - glDrawArrays(GL_LINES, 0, 24); - - visualizeOverdraw_helper(m_nodes.value(rootNode())); - - // Animate the view... - QSurface *surface = m_context->openglContext()->surface(); - if (surface->surfaceClass() == QSurface::Window) - if (QQuickWindow *window = qobject_cast<QQuickWindow *>(static_cast<QWindow *>(surface))) - window->update(); -} - void Renderer::setCustomRenderMode(const QByteArray &mode) { - if (mode.isEmpty()) m_visualizeMode = VisualizeNothing; - else if (mode == "clip") m_visualizeMode = VisualizeClipping; - else if (mode == "overdraw") m_visualizeMode = VisualizeOverdraw; - else if (mode == "batches") m_visualizeMode = VisualizeBatches; - else if (mode == "changes") m_visualizeMode = VisualizeChanges; + if (mode.isEmpty()) + m_visualizer->setMode(Visualizer::VisualizeNothing); + else if (mode == "clip") + m_visualizer->setMode(Visualizer::VisualizeClipping); + else if (mode == "overdraw") + m_visualizer->setMode(Visualizer::VisualizeOverdraw); + else if (mode == "batches") + m_visualizer->setMode(Visualizer::VisualizeBatches); + else if (mode == "changes") + m_visualizer->setMode(Visualizer::VisualizeChanges); } -void Renderer::visualize() +bool Renderer::hasCustomRenderModeWithContinuousUpdate() const { - if (!m_shaderManager->visualizeProgram) { - VisualizeShader *prog = new VisualizeShader(); - QSGShaderSourceBuilder::initializeProgramFromFiles( - prog, - QStringLiteral(":/qt-project.org/scenegraph/shaders/visualization.vert"), - QStringLiteral(":/qt-project.org/scenegraph/shaders/visualization.frag")); - prog->bindAttributeLocation("v", 0); - prog->link(); - prog->bind(); - prog->color = prog->uniformLocation("color"); - prog->pattern = prog->uniformLocation("pattern"); - prog->projection = prog->uniformLocation("projection"); - prog->matrix = prog->uniformLocation("matrix"); - prog->rotation = prog->uniformLocation("rotation"); - m_shaderManager->visualizeProgram = prog; - } else { - m_shaderManager->visualizeProgram->bind(); - } - VisualizeShader *shader = static_cast<VisualizeShader *>(m_shaderManager->visualizeProgram); - - glDisable(GL_DEPTH_TEST); - glEnable(GL_BLEND); - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - glEnableVertexAttribArray(0); - - // Blacken out the actual rendered content... - float bgOpacity = 0.8f; - if (m_visualizeMode == VisualizeBatches) - bgOpacity = 1.0; - float v[] = { -1, 1, 1, 1, -1, -1, 1, -1 }; - shader->setUniformValue(shader->color, 0.0f, 0.0f, 0.0f, bgOpacity); - shader->setUniformValue(shader->matrix, QMatrix4x4()); - shader->setUniformValue(shader->rotation, QMatrix4x4()); - shader->setUniformValue(shader->pattern, 0.0f); - shader->setUniformValue(shader->projection, false); - glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, v); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - if (m_visualizeMode == VisualizeBatches) { - srand(0); // To force random colors to be roughly the same every time.. - for (int i=0; i<m_opaqueBatches.size(); ++i) visualizeBatch(m_opaqueBatches.at(i)); - for (int i=0; i<m_alphaBatches.size(); ++i) visualizeBatch(m_alphaBatches.at(i)); - } else if (m_visualizeMode == VisualizeClipping) { - shader->setUniformValue(shader->pattern, 0.5f); - shader->setUniformValue(shader->color, 0.2f, 0.0f, 0.0f, 0.2f); - visualizeClipping(rootNode()); - } else if (m_visualizeMode == VisualizeChanges) { - visualizeChanges(m_nodes.value(rootNode())); - m_visualizeChanceSet.clear(); - } else if (m_visualizeMode == VisualizeOverdraw) { - visualizeOverdraw(); - } - - // Reset state back to defaults.. - glDisable(GL_BLEND); - glDisableVertexAttribArray(0); - shader->release(); + return m_visualizer->mode() == Visualizer::VisualizeOverdraw; } bool operator==(const GraphicsState &a, const GraphicsState &b) Q_DECL_NOTHROW @@ -4822,8 +4549,34 @@ uint qHash(const GraphicsPipelineStateKey &k, uint seed) Q_DECL_NOTHROW return qHash(k.state, seed) + qHash(k.sms->programRhi.program, seed) + qHash(k.rpDesc, seed); } -QT_END_NAMESPACE +Visualizer::Visualizer(Renderer *renderer) + : m_renderer(renderer), + m_visualizeMode(VisualizeNothing) +{ +} + +Visualizer::~Visualizer() +{ +} + +#define QSGNODE_DIRTY_PARENT (QSGNode::DirtyNodeAdded \ + | QSGNode::DirtyOpacity \ + | QSGNode::DirtyMatrix \ + | QSGNode::DirtyNodeRemoved) +void Visualizer::visualizeChangesPrepare(Node *n, uint parentChanges) +{ + uint childDirty = (parentChanges | n->dirtyState) & QSGNODE_DIRTY_PARENT; + uint selfDirty = n->dirtyState | parentChanges; + if (n->type() == QSGNode::GeometryNodeType && selfDirty != 0) + m_visualizeChangeSet.insert(n, selfDirty); + SHADOWNODE_TRAVERSE(n) { + visualizeChangesPrepare(child, childDirty); + } } +} // namespace QSGBatchRenderer + +QT_END_NAMESPACE + #include "moc_qsgbatchrenderer_p.cpp" diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h index 9dec203e73..ea9dab244f 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h @@ -648,7 +648,7 @@ public: float lastOpacity; }; - ShaderManager(QSGDefaultRenderContext *ctx) : visualizeProgram(nullptr), blitProgram(nullptr), context(ctx) { } + ShaderManager(QSGDefaultRenderContext *ctx) : blitProgram(nullptr), context(ctx) { } ~ShaderManager() { qDeleteAll(rewrittenShaders); qDeleteAll(stockShaders); @@ -665,8 +665,6 @@ public: Shader *prepareMaterial(QSGMaterial *material, bool enableRhiShaders = false, const QSGGeometry *geometry = nullptr); Shader *prepareMaterialNoRewrite(QSGMaterial *material, bool enableRhiShaders = false, const QSGGeometry *geometry = nullptr); - QOpenGLShaderProgram *visualizeProgram; - private: QHash<QSGMaterialType *, Shader *> rewrittenShaders; QHash<QSGMaterialType *, Shader *> stockShaders; @@ -719,12 +717,9 @@ struct RenderPassState bool scissorSet; }; -class Q_QUICK_PRIVATE_EXPORT Renderer : public QSGRenderer, public QOpenGLFunctions +class Visualizer { public: - Renderer(QSGDefaultRenderContext *); - ~Renderer(); - enum VisualizeMode { VisualizeNothing, VisualizeBatches, @@ -733,6 +728,30 @@ public: 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<Node *, uint> 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; @@ -747,6 +766,8 @@ private: }; friend class Updater; + friend class OpenGLVisualizer; + friend class RhiVisualizer; void destroyGraphicsResources(); void map(Buffer *buffer, int size, bool isIndexBuf = false); @@ -813,15 +834,8 @@ private: 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) override; + bool hasCustomRenderModeWithContinuousUpdate() const override; QSGDefaultRenderContext *m_context; QSet<Node *> m_taggedRoots; @@ -852,6 +866,8 @@ private: int m_batchNodeThreshold; int m_batchVertexThreshold; + Visualizer *m_visualizer; + // Stuff used during rendering only... ShaderManager *m_shaderManager; // per rendercontext, shared QSGMaterial *m_currentMaterial; @@ -874,9 +890,6 @@ private: // For minimal OpenGL core profile support QOpenGLVertexArrayObject *m_vao; - QHash<Node *, uint> m_visualizeChanceSet; - VisualizeMode m_visualizeMode; - Allocator<Node, 256> m_nodeAllocator; Allocator<Element, 64> m_elementAllocator; diff --git a/src/quick/scenegraph/coreapi/qsgopenglvisualizer.cpp b/src/quick/scenegraph/coreapi/qsgopenglvisualizer.cpp new file mode 100644 index 0000000000..6c2ff0b176 --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsgopenglvisualizer.cpp @@ -0,0 +1,365 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com> +** Copyright (C) 2016 Robin Burchell <robin.burchell@viroteck.net> +** 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$ +** +****************************************************************************/ + +#include "qsgopenglvisualizer_p.h" +#include <qmath.h> +#include <private/qsgshadersourcebuilder_p.h> + +QT_BEGIN_NAMESPACE + +namespace QSGBatchRenderer +{ + +#define QSGNODE_TRAVERSE(NODE) for (QSGNode *child = NODE->firstChild(); child; child = child->nextSibling()) +#define SHADOWNODE_TRAVERSE(NODE) for (Node *child = NODE->firstChild(); child; child = child->sibling()) +#define QSGNODE_DIRTY_PARENT (QSGNode::DirtyNodeAdded \ + | QSGNode::DirtyOpacity \ + | QSGNode::DirtyMatrix \ + | QSGNode::DirtyNodeRemoved) + +QMatrix4x4 qsg_matrixForRoot(Node *node); + +class VisualizeShader : public QOpenGLShaderProgram +{ +public: + int color; + int matrix; + int rotation; + int pattern; + int projection; +}; + +OpenGLVisualizer::OpenGLVisualizer(Renderer *renderer) + : Visualizer(renderer), + m_funcs(QOpenGLContext::currentContext()->functions()), + m_visualizeProgram(nullptr) +{ +} + +OpenGLVisualizer::~OpenGLVisualizer() +{ + releaseResources(); +} + +void OpenGLVisualizer::releaseResources() +{ + delete m_visualizeProgram; + m_visualizeProgram = nullptr; +} + +void OpenGLVisualizer::prepareVisualize() +{ + // nothing to do here +} + +void OpenGLVisualizer::visualizeDrawGeometry(const QSGGeometry *g) +{ + if (g->attributeCount() < 1) + return; + const QSGGeometry::Attribute *a = g->attributes(); + m_funcs->glVertexAttribPointer(0, a->tupleSize, a->type, false, g->sizeOfVertex(), g->vertexData()); + if (g->indexCount()) + m_funcs->glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), g->indexData()); + else + m_funcs->glDrawArrays(g->drawingMode(), 0, g->vertexCount()); + +} + +void OpenGLVisualizer::visualizeBatch(Batch *b) +{ + VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram); + + if (b->positionAttribute != 0) + return; + + QSGGeometryNode *gn = b->first->node; + QSGGeometry *g = gn->geometry(); + const QSGGeometry::Attribute &a = g->attributes()[b->positionAttribute]; + + m_funcs->glBindBuffer(GL_ARRAY_BUFFER, b->vbo.id); + + QMatrix4x4 matrix(m_renderer->m_current_projection_matrix); + if (b->root) + matrix = matrix * qsg_matrixForRoot(b->root); + + shader->setUniformValue(shader->pattern, float(b->merged ? 0 : 1)); + + QColor color = QColor::fromHsvF((rand() & 1023) / 1023.0, 1.0, 1.0); + float cr = color.redF(); + float cg = color.greenF(); + float cb = color.blueF(); + shader->setUniformValue(shader->color, cr, cg, cb, 1.0); + + if (b->merged) { + shader->setUniformValue(shader->matrix, matrix); + const char *dataStart = m_renderer->m_context->separateIndexBuffer() ? b->ibo.data : b->vbo.data; + for (int ds=0; ds<b->drawSets.size(); ++ds) { + const DrawSet &set = b->drawSets.at(ds); + m_funcs->glVertexAttribPointer(a.position, 2, a.type, false, g->sizeOfVertex(), + (void *) (qintptr) (set.vertices)); + m_funcs->glDrawElements(g->drawingMode(), set.indexCount, GL_UNSIGNED_SHORT, + (void *)(qintptr)(dataStart + set.indices)); + } + } else { + Element *e = b->first; + int offset = 0; + while (e) { + gn = e->node; + g = gn->geometry(); + shader->setUniformValue(shader->matrix, matrix * *gn->matrix()); + m_funcs->glVertexAttribPointer(a.position, a.tupleSize, a.type, false, g->sizeOfVertex(), + (void *) (qintptr) offset); + if (g->indexCount()) + m_funcs->glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), g->indexData()); + else + m_funcs->glDrawArrays(g->drawingMode(), 0, g->vertexCount()); + offset += g->sizeOfVertex() * g->vertexCount(); + e = e->nextInBatch; + } + } +} + +void OpenGLVisualizer::visualizeClipping(QSGNode *node) +{ + if (node->type() == QSGNode::ClipNodeType) { + VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram); + QSGClipNode *clipNode = static_cast<QSGClipNode *>(node); + QMatrix4x4 matrix = m_renderer->m_current_projection_matrix; + if (clipNode->matrix()) + matrix = matrix * *clipNode->matrix(); + shader->setUniformValue(shader->matrix, matrix); + visualizeDrawGeometry(clipNode->geometry()); + } + + QSGNODE_TRAVERSE(node) { + visualizeClipping(child); + } +} + +void OpenGLVisualizer::visualizeChanges(Node *n) +{ + + if (n->type() == QSGNode::GeometryNodeType && n->element()->batch && m_visualizeChangeSet.contains(n)) { + uint dirty = m_visualizeChangeSet.value(n); + bool tinted = (dirty & QSGNODE_DIRTY_PARENT) != 0; + + VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram); + QColor color = QColor::fromHsvF((rand() & 1023) / 1023.0, 0.3, 1.0); + float ca = 0.5; + float cr = color.redF() * ca; + float cg = color.greenF() * ca; + float cb = color.blueF() * ca; + shader->setUniformValue(shader->color, cr, cg, cb, ca); + shader->setUniformValue(shader->pattern, float(tinted ? 0.5 : 0)); + + QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(n->sgNode); + + QMatrix4x4 matrix = m_renderer->m_current_projection_matrix; + if (n->element()->batch->root) + matrix = matrix * qsg_matrixForRoot(n->element()->batch->root); + matrix = matrix * *gn->matrix(); + shader->setUniformValue(shader->matrix, matrix); + visualizeDrawGeometry(gn->geometry()); + + // This is because many changes don't propegate their dirty state to the + // parent so the node updater will not unset these states. They are + // not used for anything so, unsetting it should have no side effects. + n->dirtyState = nullptr; + } + + SHADOWNODE_TRAVERSE(n) { + visualizeChanges(child); + } +} + +void OpenGLVisualizer::visualizeOverdraw_helper(Node *node) +{ + if (node->type() == QSGNode::GeometryNodeType && node->element()->batch) { + VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram); + QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(node->sgNode); + + QMatrix4x4 matrix = m_renderer->m_current_projection_matrix; + matrix(2, 2) = m_renderer->m_zRange; + matrix(2, 3) = 1.0f - node->element()->order * m_renderer->m_zRange; + + if (node->element()->batch->root) + matrix = matrix * qsg_matrixForRoot(node->element()->batch->root); + matrix = matrix * *gn->matrix(); + shader->setUniformValue(shader->matrix, matrix); + + QColor color = node->element()->batch->isOpaque ? QColor::fromRgbF(0.3, 1.0, 0.3) : QColor::fromRgbF(1.0, 0.3, 0.3); + float ca = 0.33f; + shader->setUniformValue(shader->color, color.redF() * ca, color.greenF() * ca, color.blueF() * ca, ca); + + visualizeDrawGeometry(gn->geometry()); + } + + SHADOWNODE_TRAVERSE(node) { + visualizeOverdraw_helper(child); + } +} + +void OpenGLVisualizer::visualizeOverdraw() +{ + VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram); + shader->setUniformValue(shader->color, 0.5f, 0.5f, 1.0f, 1.0f); + shader->setUniformValue(shader->projection, 1); + + m_funcs->glBlendFunc(GL_ONE, GL_ONE); + + static float step = 0; + step += static_cast<float>(M_PI * 2 / 1000.); + if (step > M_PI * 2) + step = 0; + float angle = 80.0 * std::sin(step); + + QMatrix4x4 xrot; xrot.rotate(20, 1, 0, 0); + QMatrix4x4 zrot; zrot.rotate(angle, 0, 0, 1); + QMatrix4x4 tx; tx.translate(0, 0, 1); + + QMatrix4x4 m; + +// m.rotate(180, 0, 1, 0); + + m.translate(0, 0.5, 4); + m.scale(2, 2, 1); + + m.rotate(-30, 1, 0, 0); + m.rotate(angle, 0, 1, 0); + m.translate(0, 0, -1); + + shader->setUniformValue(shader->rotation, m); + + float box[] = { + // lower + -1, 1, 0, 1, 1, 0, + -1, 1, 0, -1, -1, 0, + 1, 1, 0, 1, -1, 0, + -1, -1, 0, 1, -1, 0, + + // upper + -1, 1, 1, 1, 1, 1, + -1, 1, 1, -1, -1, 1, + 1, 1, 1, 1, -1, 1, + -1, -1, 1, 1, -1, 1, + + // sides + -1, -1, 0, -1, -1, 1, + 1, -1, 0, 1, -1, 1, + -1, 1, 0, -1, 1, 1, + 1, 1, 0, 1, 1, 1 + }; + m_funcs->glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, box); + m_funcs->glLineWidth(2); + m_funcs->glDrawArrays(GL_LINES, 0, 24); + + visualizeOverdraw_helper(m_renderer->m_nodes.value(m_renderer->rootNode())); +} + +void OpenGLVisualizer::visualize() +{ + if (m_visualizeMode == VisualizeNothing) + return; + + if (!m_visualizeProgram) { + VisualizeShader *prog = new VisualizeShader(); + QSGShaderSourceBuilder::initializeProgramFromFiles( + prog, + QStringLiteral(":/qt-project.org/scenegraph/shaders/visualization.vert"), + QStringLiteral(":/qt-project.org/scenegraph/shaders/visualization.frag")); + prog->bindAttributeLocation("v", 0); + prog->link(); + prog->bind(); + prog->color = prog->uniformLocation("color"); + prog->pattern = prog->uniformLocation("pattern"); + prog->projection = prog->uniformLocation("projection"); + prog->matrix = prog->uniformLocation("matrix"); + prog->rotation = prog->uniformLocation("rotation"); + m_visualizeProgram = prog; + } else { + m_visualizeProgram->bind(); + } + VisualizeShader *shader = static_cast<VisualizeShader *>(m_visualizeProgram); + + m_funcs->glDisable(GL_DEPTH_TEST); + m_funcs->glEnable(GL_BLEND); + m_funcs->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + m_funcs->glEnableVertexAttribArray(0); + + // Blacken out the actual rendered content... + float bgOpacity = 0.8f; + if (m_visualizeMode == VisualizeBatches) + bgOpacity = 1.0; + float v[] = { -1, 1, 1, 1, -1, -1, 1, -1 }; + shader->setUniformValue(shader->color, 0.0f, 0.0f, 0.0f, bgOpacity); + shader->setUniformValue(shader->matrix, QMatrix4x4()); + shader->setUniformValue(shader->rotation, QMatrix4x4()); + shader->setUniformValue(shader->pattern, 0.0f); + shader->setUniformValue(shader->projection, false); + m_funcs->glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, v); + m_funcs->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + if (m_visualizeMode == VisualizeBatches) { + srand(0); // To force random colors to be roughly the same every time.. + for (int i = 0; i < m_renderer->m_opaqueBatches.size(); ++i) + visualizeBatch(m_renderer->m_opaqueBatches.at(i)); + for (int i = 0; i < m_renderer->m_alphaBatches.size(); ++i) + visualizeBatch(m_renderer->m_alphaBatches.at(i)); + } else if (m_visualizeMode == VisualizeClipping) { + shader->setUniformValue(shader->pattern, 0.5f); + shader->setUniformValue(shader->color, 0.2f, 0.0f, 0.0f, 0.2f); + visualizeClipping(m_renderer->rootNode()); + } else if (m_visualizeMode == VisualizeChanges) { + visualizeChanges(m_renderer->m_nodes.value(m_renderer->rootNode())); + m_visualizeChangeSet.clear(); + } else if (m_visualizeMode == VisualizeOverdraw) { + visualizeOverdraw(); + } + + // Reset state back to defaults.. + m_funcs->glDisable(GL_BLEND); + m_funcs->glDisableVertexAttribArray(0); + shader->release(); +} + +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/coreapi/qsgopenglvisualizer_p.h b/src/quick/scenegraph/coreapi/qsgopenglvisualizer_p.h new file mode 100644 index 0000000000..0b21694351 --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsgopenglvisualizer_p.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com> +** Copyright (C) 2016 Robin Burchell <robin.burchell@viroteck.net> +** 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 QSGOPENGLVISUALIZER_P_H +#define QSGOPENGLVISUALIZER_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 "qsgbatchrenderer_p.h" + +QT_BEGIN_NAMESPACE + +namespace QSGBatchRenderer +{ + +// ### Qt 6: remove +class OpenGLVisualizer : public Visualizer +{ +public: + OpenGLVisualizer(Renderer *renderer); + ~OpenGLVisualizer(); + + void prepareVisualize() override; + void visualize() override; + + void releaseResources() override; + +private: + void visualizeBatch(Batch *b); + void visualizeClipping(QSGNode *node); + void visualizeChanges(Node *n); + void visualizeOverdraw(); + void visualizeOverdraw_helper(Node *node); + void visualizeDrawGeometry(const QSGGeometry *g); + + QOpenGLFunctions *m_funcs; + QOpenGLShaderProgram *m_visualizeProgram; +}; + +} // namespace QSGBatchRenderer + +QT_END_NAMESPACE + +#endif // QSGOPENGLVISUALIZER_P_H diff --git a/src/quick/scenegraph/coreapi/qsgrenderer_p.h b/src/quick/scenegraph/coreapi/qsgrenderer_p.h index 9c83ddf111..c4ed0072f6 100644 --- a/src/quick/scenegraph/coreapi/qsgrenderer_p.h +++ b/src/quick/scenegraph/coreapi/qsgrenderer_p.h @@ -97,6 +97,7 @@ public: inline QSGMaterialShader::RenderState state(QSGMaterialShader::RenderState::DirtyStates dirty) const; inline QSGMaterialRhiShader::RenderState rhiState(QSGMaterialRhiShader::RenderState::DirtyStates dirty) const; virtual void setCustomRenderMode(const QByteArray &) { } + virtual bool hasCustomRenderModeWithContinuousUpdate() const { return false; } virtual void releaseCachedResources() { } void clearChangedFlag() { m_changed_emitted = false; } diff --git a/src/quick/scenegraph/coreapi/qsgrhivisualizer.cpp b/src/quick/scenegraph/coreapi/qsgrhivisualizer.cpp new file mode 100644 index 0000000000..38d4c4440f --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsgrhivisualizer.cpp @@ -0,0 +1,929 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com> +** Copyright (C) 2016 Robin Burchell <robin.burchell@viroteck.net> +** 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$ +** +****************************************************************************/ + +#include "qsgrhivisualizer_p.h" +#include <qmath.h> +#include <QQuickWindow> +#include <private/qsgmaterialrhishader_p.h> +#include <private/qsgshadersourcebuilder_p.h> + +QT_BEGIN_NAMESPACE + +namespace QSGBatchRenderer +{ + +#define QSGNODE_TRAVERSE(NODE) for (QSGNode *child = NODE->firstChild(); child; child = child->nextSibling()) +#define SHADOWNODE_TRAVERSE(NODE) for (Node *child = NODE->firstChild(); child; child = child->sibling()) +#define QSGNODE_DIRTY_PARENT (QSGNode::DirtyNodeAdded \ + | QSGNode::DirtyOpacity \ + | QSGNode::DirtyMatrix \ + | QSGNode::DirtyNodeRemoved) + +QMatrix4x4 qsg_matrixForRoot(Node *node); +QRhiVertexInputAttribute::Format qsg_vertexInputFormat(const QSGGeometry::Attribute &a); +QRhiCommandBuffer::IndexFormat qsg_indexFormat(const QSGGeometry *geometry); +QRhiGraphicsPipeline::Topology qsg_topology(int geomDrawMode); + +RhiVisualizer::RhiVisualizer(Renderer *renderer) + : Visualizer(renderer) +{ +} + +RhiVisualizer::~RhiVisualizer() +{ + releaseResources(); +} + +void RhiVisualizer::releaseResources() +{ + m_pipelines.releaseResources(); + + m_fade.releaseResources(); + + m_changeVis.releaseResources(); + m_batchVis.releaseResources(); + m_clipVis.releaseResources(); + m_overdrawVis.releaseResources(); +} + +void RhiVisualizer::prepareVisualize() +{ + // Called before the render pass has begun (but after preparing the + // batches). Now is the time to put resource updates to the renderer's + // current m_resourceUpdates instance. + + if (m_visualizeMode == VisualizeNothing) + return; + + if (!m_vs.isValid()) { + m_vs = QSGMaterialRhiShaderPrivate::loadShader( + QLatin1String(":/qt-project.org/scenegraph/shaders_ng/visualization.vert.qsb")); + m_fs = QSGMaterialRhiShaderPrivate::loadShader( + QLatin1String(":/qt-project.org/scenegraph/shaders_ng/visualization.frag.qsb")); + } + + m_fade.prepare(this, m_renderer->m_rhi, m_renderer->m_resourceUpdates, m_renderer->renderPassDescriptor()); + + const bool forceUintIndex = m_renderer->m_uint32IndexForRhi; + + switch (m_visualizeMode) { + case VisualizeBatches: + m_batchVis.prepare(m_renderer->m_opaqueBatches, m_renderer->m_alphaBatches, + this, + m_renderer->m_rhi, m_renderer->m_resourceUpdates, + forceUintIndex); + break; + case VisualizeClipping: + m_clipVis.prepare(m_renderer->rootNode(), this, + m_renderer->m_rhi, m_renderer->m_resourceUpdates); + break; + case VisualizeChanges: + m_changeVis.prepare(m_renderer->m_nodes.value(m_renderer->rootNode()), + this, + m_renderer->m_rhi, m_renderer->m_resourceUpdates); + m_visualizeChangeSet.clear(); + break; + case VisualizeOverdraw: + m_overdrawVis.prepare(m_renderer->m_nodes.value(m_renderer->rootNode()), + this, + m_renderer->m_rhi, m_renderer->m_resourceUpdates); + break; + default: + Q_UNREACHABLE(); + break; + } +} + +void RhiVisualizer::visualize() +{ + if (m_visualizeMode == VisualizeNothing) + return; + + QRhiCommandBuffer *cb = m_renderer->commandBuffer(); + m_fade.render(cb); + + switch (m_visualizeMode) { + case VisualizeBatches: + m_batchVis.render(cb); + break; + case VisualizeClipping: + m_clipVis.render(cb); + break; + case VisualizeChanges: + m_changeVis.render(cb); + break; + case VisualizeOverdraw: + m_overdrawVis.render(cb); + break; + default: + Q_UNREACHABLE(); + break; + } +} + +void RhiVisualizer::recordDrawCalls(const QVector<DrawCall> &drawCalls, + QRhiCommandBuffer *cb, + QRhiShaderResourceBindings *srb, + bool blendOneOne) +{ + for (const DrawCall &dc : drawCalls) { + QRhiGraphicsPipeline *ps = m_pipelines.pipeline(this, m_renderer->m_rhi, srb, m_renderer->renderPassDescriptor(), + dc.vertex.topology, dc.vertex.format, dc.vertex.stride, + blendOneOne); + if (!ps) + continue; + cb->setGraphicsPipeline(ps); // no-op if same as the last one + QRhiCommandBuffer::DynamicOffset dynofs(0, dc.buf.ubufOffset); + cb->setShaderResources(srb, 1, &dynofs); + QRhiCommandBuffer::VertexInput vb(dc.buf.vbuf, dc.buf.vbufOffset); + if (dc.index.count) { + cb->setVertexInput(0, 1, &vb, dc.buf.ibuf, dc.buf.ibufOffset, dc.index.format); + cb->drawIndexed(dc.index.count); + } else { + cb->setVertexInput(0, 1, &vb); + cb->draw(dc.vertex.count); + } + } +} + +const QRhiShaderResourceBinding::StageFlags ubufVisibility = + QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage; + +void RhiVisualizer::Fade::prepare(RhiVisualizer *visualizer, + QRhi *rhi, QRhiResourceUpdateBatch *u, QRhiRenderPassDescriptor *rpDesc) +{ + this->visualizer = visualizer; + + if (!vbuf) { + float v[] = { -1, 1, 1, 1, -1, -1, 1, -1 }; + vbuf = rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(v)); + if (!vbuf->build()) + return; + u->uploadStaticBuffer(vbuf, v); + } + + if (!ubuf) { + ubuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, DrawCall::UBUF_SIZE); + if (!ubuf->build()) + return; + float bgOpacity = 0.8f; + if (visualizer->m_visualizeMode == Visualizer::VisualizeBatches) + bgOpacity = 1.0; + QMatrix4x4 ident; + u->updateDynamicBuffer(ubuf, 0, 64, ident.constData()); // matrix + u->updateDynamicBuffer(ubuf, 64, 64, ident.constData()); // rotation + float color[4] = { 0.0f, 0.0f, 0.0f, bgOpacity }; + u->updateDynamicBuffer(ubuf, 128, 16, color); + float pattern = 0.0f; + u->updateDynamicBuffer(ubuf, 144, 4, &pattern); + qint32 projection = 0; + u->updateDynamicBuffer(ubuf, 148, 4, &projection); + } + + if (!srb) { + srb = rhi->newShaderResourceBindings(); + srb->setBindings({ QRhiShaderResourceBinding::uniformBuffer(0, ubufVisibility, ubuf) }); + if (!srb->build()) + return; + } + + if (!ps) { + ps = rhi->newGraphicsPipeline(); + ps->setTopology(QRhiGraphicsPipeline::TriangleStrip); + QRhiGraphicsPipeline::TargetBlend blend; // defaults to premul alpha, just what we need + blend.enable = true; + ps->setTargetBlends({ blend }); + ps->setShaderStages({ { QRhiShaderStage::Vertex, visualizer->m_vs }, + { QRhiShaderStage::Fragment, visualizer->m_fs } }); + QRhiVertexInputLayout inputLayout; + inputLayout.setBindings({ { 2 * sizeof(float) } }); + inputLayout.setAttributes({ { 0, 0, QRhiVertexInputAttribute::Float2, 0 } }); + ps->setVertexInputLayout(inputLayout); + ps->setShaderResourceBindings(srb); + ps->setRenderPassDescriptor(rpDesc); + if (!ps->build()) + return; + } +} + +void RhiVisualizer::Fade::releaseResources() +{ + delete ps; + ps = nullptr; + + delete srb; + srb = nullptr; + + delete ubuf; + ubuf = nullptr; + + delete vbuf; + vbuf = nullptr; +} + +void RhiVisualizer::Fade::render(QRhiCommandBuffer *cb) +{ + cb->setGraphicsPipeline(ps); + cb->setViewport(visualizer->m_renderer->m_pstate.viewport); + cb->setShaderResources(); + QRhiCommandBuffer::VertexInput vb(vbuf, 0); + cb->setVertexInput(0, 1, &vb); + cb->draw(4); +} + +static void fillVertexIndex(RhiVisualizer::DrawCall *dc, QSGGeometry *g, bool withData, bool forceUintIndex) +{ + dc->vertex.topology = qsg_topology(g->drawingMode()); + dc->vertex.format = qsg_vertexInputFormat(g->attributes()[0]); + dc->vertex.count = g->vertexCount(); + dc->vertex.stride = g->sizeOfVertex(); + if (withData) + dc->vertex.data = g->vertexData(); + + dc->index.format = forceUintIndex ? QRhiCommandBuffer::IndexUInt32 : qsg_indexFormat(g); + dc->index.count = g->indexCount(); + dc->index.stride = forceUintIndex ? sizeof(quint32) : g->sizeOfIndex(); + if (withData && g->indexCount()) + dc->index.data = g->indexData(); +} + +static inline uint aligned(uint v, uint byteAlign) +{ + return (v + byteAlign - 1) & ~(byteAlign - 1); +} + +static bool ensureBuffer(QRhi *rhi, QRhiBuffer **buf, QRhiBuffer::UsageFlags usage, int newSize) +{ + if (!*buf) { + *buf = rhi->newBuffer(QRhiBuffer::Dynamic, usage, newSize); + if (!(*buf)->build()) + return false; + } else if ((*buf)->size() < newSize) { + (*buf)->setSize(newSize); + if (!(*buf)->build()) + return false; + } + return true; +} + +QRhiGraphicsPipeline *RhiVisualizer::PipelineCache::pipeline(RhiVisualizer *visualizer, + QRhi *rhi, + QRhiShaderResourceBindings *srb, + QRhiRenderPassDescriptor *rpDesc, + QRhiGraphicsPipeline::Topology topology, + QRhiVertexInputAttribute::Format vertexFormat, + quint32 vertexStride, + bool blendOneOne) +{ + for (int i = 0, ie = pipelines.count(); i != ie; ++i) { + const Pipeline &p(pipelines.at(i)); + if (p.topology == topology && p.format == vertexFormat && p.stride == vertexStride) + return p.ps; + } + + QRhiGraphicsPipeline *ps = rhi->newGraphicsPipeline(); + ps->setTopology(topology); + QRhiGraphicsPipeline::TargetBlend blend; // premul alpha + blend.enable = true; + if (blendOneOne) { + // only for visualizing overdraw, other modes use premul alpha + blend.srcColor = QRhiGraphicsPipeline::One; + blend.dstColor = QRhiGraphicsPipeline::One; + blend.srcAlpha = QRhiGraphicsPipeline::One; + blend.dstAlpha = QRhiGraphicsPipeline::One; + } + ps->setTargetBlends({ blend }); + ps->setShaderStages({ { QRhiShaderStage::Vertex, visualizer->m_vs }, + { QRhiShaderStage::Fragment, visualizer->m_fs } }); + QRhiVertexInputLayout inputLayout; + inputLayout.setBindings({ { vertexStride } }); + inputLayout.setAttributes({ { 0, 0, vertexFormat, 0 } }); + ps->setVertexInputLayout(inputLayout); + ps->setShaderResourceBindings(srb); + ps->setRenderPassDescriptor(rpDesc); + if (!ps->build()) + return nullptr; + + Pipeline p; + p.topology = topology; + p.format = vertexFormat; + p.stride = vertexStride; + p.ps = ps; + pipelines.append(p); + + return ps; +} + +void RhiVisualizer::PipelineCache::releaseResources() +{ + for (int i = 0, ie = pipelines.count(); i != ie; ++i) + delete pipelines.at(i).ps; + + pipelines.clear(); +} + +void RhiVisualizer::ChangeVis::gather(Node *n) +{ + if (n->type() == QSGNode::GeometryNodeType && n->element()->batch && visualizer->m_visualizeChangeSet.contains(n)) { + const uint dirty = visualizer->m_visualizeChangeSet.value(n); + const bool tinted = (dirty & QSGNODE_DIRTY_PARENT) != 0; + const QColor color = QColor::fromHsvF((rand() & 1023) / 1023.0f, 0.3f, 1.0f); + const float alpha = 0.5f; + + QMatrix4x4 matrix = visualizer->m_renderer->m_current_projection_matrix; + if (n->element()->batch->root) + matrix = matrix * qsg_matrixForRoot(n->element()->batch->root); + + QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(n->sgNode); + matrix = matrix * *gn->matrix(); + + QSGGeometry *g = gn->geometry(); + if (g->attributeCount() >= 1) { + DrawCall dc; + memcpy(dc.uniforms.data, matrix.constData(), 64); + QMatrix4x4 rotation; + memcpy(dc.uniforms.data + 64, rotation.constData(), 64); + float c[4] = { + float(color.redF()) * alpha, + float(color.greenF()) * alpha, + float(color.blueF()) * alpha, + alpha + }; + memcpy(dc.uniforms.data + 128, c, 16); + float pattern = tinted ? 0.5f : 0.0f; + memcpy(dc.uniforms.data + 144, &pattern, 4); + qint32 projection = 0; + memcpy(dc.uniforms.data + 148, &projection, 4); + + fillVertexIndex(&dc, g, true, false); + drawCalls.append(dc); + } + + // This is because many changes don't propegate their dirty state to the + // parent so the node updater will not unset these states. They are + // not used for anything so, unsetting it should have no side effects. + n->dirtyState = nullptr; + } + + SHADOWNODE_TRAVERSE(n) { + gather(child); + } +} + +void RhiVisualizer::ChangeVis::prepare(Node *n, RhiVisualizer *visualizer, + QRhi *rhi, QRhiResourceUpdateBatch *u) +{ + this->visualizer = visualizer; + + drawCalls.clear(); + gather(n); + + if (drawCalls.isEmpty()) + return; + + const int ubufAlign = rhi->ubufAlignment(); + int vbufOffset = 0; + int ibufOffset = 0; + int ubufOffset = 0; + for (RhiVisualizer::DrawCall &dc : drawCalls) { + dc.buf.vbufOffset = aligned(vbufOffset, 4); + vbufOffset = dc.buf.vbufOffset + dc.vertex.count * dc.vertex.stride; + + dc.buf.ibufOffset = aligned(ibufOffset, 4); + ibufOffset = dc.buf.ibufOffset + dc.index.count * dc.index.stride; + + dc.buf.ubufOffset = aligned(ubufOffset, ubufAlign); + ubufOffset = dc.buf.ubufOffset + DrawCall::UBUF_SIZE; + } + + ensureBuffer(rhi, &vbuf, QRhiBuffer::VertexBuffer, vbufOffset); + if (ibufOffset) + ensureBuffer(rhi, &ibuf, QRhiBuffer::IndexBuffer, ibufOffset); + const int ubufSize = ubufOffset; + ensureBuffer(rhi, &ubuf, QRhiBuffer::UniformBuffer, ubufSize); + + for (RhiVisualizer::DrawCall &dc : drawCalls) { + u->updateDynamicBuffer(vbuf, dc.buf.vbufOffset, dc.vertex.count * dc.vertex.stride, dc.vertex.data); + dc.buf.vbuf = vbuf; + if (dc.index.count) { + u->updateDynamicBuffer(ibuf, dc.buf.ibufOffset, dc.index.count * dc.index.stride, dc.index.data); + dc.buf.ibuf = ibuf; + } + u->updateDynamicBuffer(ubuf, dc.buf.ubufOffset, DrawCall::UBUF_SIZE, dc.uniforms.data); + } + + if (!srb) { + srb = rhi->newShaderResourceBindings(); + srb->setBindings({ QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, ubufVisibility, ubuf, DrawCall::UBUF_SIZE) }); + if (!srb->build()) + return; + } +} + +void RhiVisualizer::ChangeVis::releaseResources() +{ + delete srb; + srb = nullptr; + + delete ubuf; + ubuf = nullptr; + + delete ibuf; + ibuf = nullptr; + + delete vbuf; + vbuf = nullptr; +} + +void RhiVisualizer::ChangeVis::render(QRhiCommandBuffer *cb) +{ + visualizer->recordDrawCalls(drawCalls, cb, srb); +} + +void RhiVisualizer::BatchVis::gather(Batch *b) +{ + if (b->positionAttribute != 0) + return; + + QMatrix4x4 matrix(visualizer->m_renderer->m_current_projection_matrix); + if (b->root) + matrix = matrix * qsg_matrixForRoot(b->root); + + DrawCall dc; + + QMatrix4x4 rotation; + memcpy(dc.uniforms.data + 64, rotation.constData(), 64); + + QColor color = QColor::fromHsvF((rand() & 1023) / 1023.0, 1.0, 1.0); + + float c[4] = { + float(color.redF()), + float(color.greenF()), + float(color.blueF()), + 1.0f + }; + memcpy(dc.uniforms.data + 128, c, 16); + + float pattern = b->merged ? 0.0f : 1.0f; + memcpy(dc.uniforms.data + 144, &pattern, 4); + + qint32 projection = 0; + memcpy(dc.uniforms.data + 148, &projection, 4); + + if (b->merged) { + memcpy(dc.uniforms.data, matrix.constData(), 64); + + QSGGeometryNode *gn = b->first->node; + QSGGeometry *g = gn->geometry(); + + fillVertexIndex(&dc, g, false, forceUintIndex); + + for (int ds = 0; ds < b->drawSets.size(); ++ds) { + const DrawSet &set = b->drawSets.at(ds); + dc.buf.vbuf = b->vbo.buf; + dc.buf.vbufOffset = set.vertices; + dc.buf.ibuf = b->ibo.buf; + dc.buf.ibufOffset = set.indices; + dc.index.count = set.indexCount; + drawCalls.append(dc); + } + } else { + Element *e = b->first; + int vOffset = 0; + int iOffset = 0; + + while (e) { + QSGGeometryNode *gn = e->node; + QSGGeometry *g = gn->geometry(); + + QMatrix4x4 m = matrix * *gn->matrix(); + memcpy(dc.uniforms.data, m.constData(), 64); + + fillVertexIndex(&dc, g, false, forceUintIndex); + + dc.buf.vbuf = b->vbo.buf; + dc.buf.vbufOffset = vOffset; + if (g->indexCount()) { + dc.buf.ibuf = b->ibo.buf; + dc.buf.ibufOffset = iOffset; + } + + drawCalls.append(dc); + + vOffset += dc.vertex.count * dc.vertex.stride; + iOffset += dc.index.count * dc.index.stride; + + e = e->nextInBatch; + } + } +} + +void RhiVisualizer::BatchVis::prepare(const QDataBuffer<Batch *> &opaqueBatches, const QDataBuffer<Batch *> &alphaBatches, + RhiVisualizer *visualizer, + QRhi *rhi, QRhiResourceUpdateBatch *u, + bool forceUintIndex) +{ + this->visualizer = visualizer; + this->forceUintIndex = forceUintIndex; + + drawCalls.clear(); + + srand(0); // To force random colors to be roughly the same every time.. + for (int i = 0; i < opaqueBatches.size(); ++i) + gather(opaqueBatches.at(i)); + for (int i = 0; i < alphaBatches.size(); ++i) + gather(alphaBatches.at(i)); + + if (drawCalls.isEmpty()) + return; + + const int ubufAlign = rhi->ubufAlignment(); + int ubufOffset = 0; + for (RhiVisualizer::DrawCall &dc : drawCalls) { + dc.buf.ubufOffset = aligned(ubufOffset, ubufAlign); + ubufOffset = dc.buf.ubufOffset + DrawCall::UBUF_SIZE; + } + + const int ubufSize = ubufOffset; + ensureBuffer(rhi, &ubuf, QRhiBuffer::UniformBuffer, ubufSize); + + for (RhiVisualizer::DrawCall &dc : drawCalls) + u->updateDynamicBuffer(ubuf, dc.buf.ubufOffset, DrawCall::UBUF_SIZE, dc.uniforms.data); + + if (!srb) { + srb = rhi->newShaderResourceBindings(); + srb->setBindings({ QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, ubufVisibility, ubuf, DrawCall::UBUF_SIZE) }); + if (!srb->build()) + return; + } +} + +void RhiVisualizer::BatchVis::releaseResources() +{ + delete srb; + srb = nullptr; + + delete ubuf; + ubuf = nullptr; +} + +void RhiVisualizer::BatchVis::render(QRhiCommandBuffer *cb) +{ + visualizer->recordDrawCalls(drawCalls, cb, srb); +} + +void RhiVisualizer::ClipVis::gather(QSGNode *node) +{ + if (node->type() == QSGNode::ClipNodeType) { + QSGClipNode *clipNode = static_cast<QSGClipNode *>(node); + QMatrix4x4 matrix = visualizer->m_renderer->m_current_projection_matrix; + if (clipNode->matrix()) + matrix = matrix * *clipNode->matrix(); + + QSGGeometry *g = clipNode->geometry(); + if (g->attributeCount() >= 1) { + DrawCall dc; + memcpy(dc.uniforms.data, matrix.constData(), 64); + QMatrix4x4 rotation; + memcpy(dc.uniforms.data + 64, rotation.constData(), 64); + float c[4] = { 0.2f, 0.0f, 0.0f, 0.2f }; + memcpy(dc.uniforms.data + 128, c, 16); + float pattern = 0.5f; + memcpy(dc.uniforms.data + 144, &pattern, 4); + qint32 projection = 0; + memcpy(dc.uniforms.data + 148, &projection, 4); + fillVertexIndex(&dc, g, true, false); + drawCalls.append(dc); + } + } + + QSGNODE_TRAVERSE(node) { + gather(child); + } +} + +void RhiVisualizer::ClipVis::prepare(QSGNode *node, RhiVisualizer *visualizer, + QRhi *rhi, QRhiResourceUpdateBatch *u) +{ + this->visualizer = visualizer; + + drawCalls.clear(); + gather(node); + + if (drawCalls.isEmpty()) + return; + + const int ubufAlign = rhi->ubufAlignment(); + int vbufOffset = 0; + int ibufOffset = 0; + int ubufOffset = 0; + for (RhiVisualizer::DrawCall &dc : drawCalls) { + dc.buf.vbufOffset = aligned(vbufOffset, 4); + vbufOffset = dc.buf.vbufOffset + dc.vertex.count * dc.vertex.stride; + + dc.buf.ibufOffset = aligned(ibufOffset, 4); + ibufOffset = dc.buf.ibufOffset + dc.index.count * dc.index.stride; + + dc.buf.ubufOffset = aligned(ubufOffset, ubufAlign); + ubufOffset = dc.buf.ubufOffset + DrawCall::UBUF_SIZE; + } + + ensureBuffer(rhi, &vbuf, QRhiBuffer::VertexBuffer, vbufOffset); + if (ibufOffset) + ensureBuffer(rhi, &ibuf, QRhiBuffer::IndexBuffer, ibufOffset); + const int ubufSize = ubufOffset; + ensureBuffer(rhi, &ubuf, QRhiBuffer::UniformBuffer, ubufSize); + + for (RhiVisualizer::DrawCall &dc : drawCalls) { + u->updateDynamicBuffer(vbuf, dc.buf.vbufOffset, dc.vertex.count * dc.vertex.stride, dc.vertex.data); + dc.buf.vbuf = vbuf; + if (dc.index.count) { + u->updateDynamicBuffer(ibuf, dc.buf.ibufOffset, dc.index.count * dc.index.stride, dc.index.data); + dc.buf.ibuf = ibuf; + } + u->updateDynamicBuffer(ubuf, dc.buf.ubufOffset, DrawCall::UBUF_SIZE, dc.uniforms.data); + } + + if (!srb) { + srb = rhi->newShaderResourceBindings(); + srb->setBindings({ QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, ubufVisibility, ubuf, DrawCall::UBUF_SIZE) }); + if (!srb->build()) + return; + } +} + +void RhiVisualizer::ClipVis::releaseResources() +{ + delete srb; + srb = nullptr; + + delete ubuf; + ubuf = nullptr; + + delete ibuf; + ibuf = nullptr; + + delete vbuf; + vbuf = nullptr; +} + +void RhiVisualizer::ClipVis::render(QRhiCommandBuffer *cb) +{ + visualizer->recordDrawCalls(drawCalls, cb, srb); +} + +void RhiVisualizer::OverdrawVis::gather(Node *n) +{ + if (n->type() == QSGNode::GeometryNodeType && n->element()->batch) { + QMatrix4x4 matrix = visualizer->m_renderer->m_current_projection_matrix; + matrix(2, 2) = visualizer->m_renderer->m_zRange; + matrix(2, 3) = 1.0f - n->element()->order * visualizer->m_renderer->m_zRange; + + if (n->element()->batch->root) + matrix = matrix * qsg_matrixForRoot(n->element()->batch->root); + + QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(n->sgNode); + matrix = matrix * *gn->matrix(); + + QSGGeometry *g = gn->geometry(); + if (g->attributeCount() >= 1) { + DrawCall dc; + memcpy(dc.uniforms.data, matrix.constData(), 64); + memcpy(dc.uniforms.data + 64, rotation.constData(), 64); + + float c[4]; + const float ca = 0.33f; + if (n->element()->batch->isOpaque) { + c[0] = ca * 0.3f; c[1] = ca * 1.0f; c[2] = ca * 0.3f; c[3] = ca; + } else { + c[0] = ca * 1.0f; c[1] = ca * 0.3f; c[2] = ca * 0.3f; c[3] = ca; + } + memcpy(dc.uniforms.data + 128, c, 16); + float pattern = 0.0f; + memcpy(dc.uniforms.data + 144, &pattern, 4); + qint32 projection = 1; + memcpy(dc.uniforms.data + 148, &projection, 4); + + fillVertexIndex(&dc, g, true, false); + drawCalls.append(dc); + } + } + + SHADOWNODE_TRAVERSE(n) { + gather(child); + } +} + +void RhiVisualizer::OverdrawVis::prepare(Node *n, RhiVisualizer *visualizer, + QRhi *rhi, QRhiResourceUpdateBatch *u) +{ + this->visualizer = visualizer; + + step += float(M_PI * 2 / 1000.0); + if (step > float(M_PI * 2)) + step = 0.0f; + + const float yfix = rhi->isYUpInNDC() ? 1.0f : -1.0f; + rotation.setToIdentity(); + rotation.translate(0.0f, 0.5f * yfix, 4.0f); + rotation.scale(2.0f, 2.0f, 1.0f); + rotation.rotate(-30.0f * yfix, 1.0f, 0.0f, 0.0f); + rotation.rotate(80.0f * std::sin(step), 0.0f, 1.0f, 0.0f); + rotation.translate(0.0f, 0.0f, -1.0f); + + drawCalls.clear(); + gather(n); + + if (!box.vbuf) { + const float v[] = { + // lower + -1, 1, 0, 1, 1, 0, + -1, 1, 0, -1, -1, 0, + 1, 1, 0, 1, -1, 0, + -1, -1, 0, 1, -1, 0, + + // upper + -1, 1, 1, 1, 1, 1, + -1, 1, 1, -1, -1, 1, + 1, 1, 1, 1, -1, 1, + -1, -1, 1, 1, -1, 1, + + // sides + -1, -1, 0, -1, -1, 1, + 1, -1, 0, 1, -1, 1, + -1, 1, 0, -1, 1, 1, + 1, 1, 0, 1, 1, 1 + }; + box.vbuf = rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(v)); + if (!box.vbuf->build()) + return; + u->uploadStaticBuffer(box.vbuf, v); + } + + if (!box.ubuf) { + box.ubuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, DrawCall::UBUF_SIZE); + if (!box.ubuf->build()) + return; + QMatrix4x4 ident; + u->updateDynamicBuffer(box.ubuf, 0, 64, ident.constData()); + float color[4] = { 0.5f, 0.5f, 1.0f, 1.0f }; + u->updateDynamicBuffer(box.ubuf, 128, 16, color); + float pattern = 0.0f; + u->updateDynamicBuffer(box.ubuf, 144, 4, &pattern); + qint32 projection = 1; + u->updateDynamicBuffer(box.ubuf, 148, 4, &projection); + } + + u->updateDynamicBuffer(box.ubuf, 64, 64, rotation.constData()); + + if (!box.srb) { + box.srb = rhi->newShaderResourceBindings(); + box.srb->setBindings({ QRhiShaderResourceBinding::uniformBuffer(0, ubufVisibility, box.ubuf) }); + if (!box.srb->build()) + return; + } + + if (!box.ps) { + box.ps = rhi->newGraphicsPipeline(); + box.ps->setTopology(QRhiGraphicsPipeline::Lines); + box.ps->setLineWidth(2); // may be be ignored (D3D, Metal), but may be used on GL and Vulkan + QRhiGraphicsPipeline::TargetBlend blend; + blend.enable = true; + blend.srcColor = QRhiGraphicsPipeline::One; + blend.dstColor = QRhiGraphicsPipeline::One; + blend.srcAlpha = QRhiGraphicsPipeline::One; + blend.dstAlpha = QRhiGraphicsPipeline::One; + box.ps->setTargetBlends({ blend }); + box.ps->setShaderStages({ { QRhiShaderStage::Vertex, visualizer->m_vs }, + { QRhiShaderStage::Fragment, visualizer->m_fs } }); + QRhiVertexInputLayout inputLayout; + inputLayout.setBindings({ { 3 * sizeof(float) } }); + inputLayout.setAttributes({ { 0, 0, QRhiVertexInputAttribute::Float3, 0 } }); + box.ps->setVertexInputLayout(inputLayout); + box.ps->setShaderResourceBindings(box.srb); + box.ps->setRenderPassDescriptor(visualizer->m_renderer->renderPassDescriptor()); + if (!box.ps->build()) + return; + } + + if (drawCalls.isEmpty()) + return; + + const int ubufAlign = rhi->ubufAlignment(); + int vbufOffset = 0; + int ibufOffset = 0; + int ubufOffset = 0; + for (RhiVisualizer::DrawCall &dc : drawCalls) { + dc.buf.vbufOffset = aligned(vbufOffset, 4); + vbufOffset = dc.buf.vbufOffset + dc.vertex.count * dc.vertex.stride; + + dc.buf.ibufOffset = aligned(ibufOffset, 4); + ibufOffset = dc.buf.ibufOffset + dc.index.count * dc.index.stride; + + dc.buf.ubufOffset = aligned(ubufOffset, ubufAlign); + ubufOffset = dc.buf.ubufOffset + DrawCall::UBUF_SIZE; + } + + ensureBuffer(rhi, &vbuf, QRhiBuffer::VertexBuffer, vbufOffset); + if (ibufOffset) + ensureBuffer(rhi, &ibuf, QRhiBuffer::IndexBuffer, ibufOffset); + const int ubufSize = ubufOffset; + ensureBuffer(rhi, &ubuf, QRhiBuffer::UniformBuffer, ubufSize); + + for (RhiVisualizer::DrawCall &dc : drawCalls) { + u->updateDynamicBuffer(vbuf, dc.buf.vbufOffset, dc.vertex.count * dc.vertex.stride, dc.vertex.data); + dc.buf.vbuf = vbuf; + if (dc.index.count) { + u->updateDynamicBuffer(ibuf, dc.buf.ibufOffset, dc.index.count * dc.index.stride, dc.index.data); + dc.buf.ibuf = ibuf; + } + u->updateDynamicBuffer(ubuf, dc.buf.ubufOffset, DrawCall::UBUF_SIZE, dc.uniforms.data); + } + + if (!srb) { + srb = rhi->newShaderResourceBindings(); + srb->setBindings({ QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, ubufVisibility, ubuf, DrawCall::UBUF_SIZE) }); + if (!srb->build()) + return; + } +} + +void RhiVisualizer::OverdrawVis::releaseResources() +{ + delete srb; + srb = nullptr; + + delete ubuf; + ubuf = nullptr; + + delete ibuf; + ibuf = nullptr; + + delete vbuf; + vbuf = nullptr; + + delete box.ps; + box.ps = nullptr; + + delete box.srb; + box.srb = nullptr; + + delete box.ubuf; + box.ubuf = nullptr; + + delete box.vbuf; + box.vbuf = nullptr; +} + +void RhiVisualizer::OverdrawVis::render(QRhiCommandBuffer *cb) +{ + cb->setGraphicsPipeline(box.ps); + cb->setShaderResources(); + QRhiCommandBuffer::VertexInput vb(box.vbuf, 0); + cb->setVertexInput(0, 1, &vb); + cb->draw(24); + + visualizer->recordDrawCalls(drawCalls, cb, srb, true); +} + +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/coreapi/qsgrhivisualizer_p.h b/src/quick/scenegraph/coreapi/qsgrhivisualizer_p.h new file mode 100644 index 0000000000..1ce52a81f1 --- /dev/null +++ b/src/quick/scenegraph/coreapi/qsgrhivisualizer_p.h @@ -0,0 +1,233 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com> +** Copyright (C) 2016 Robin Burchell <robin.burchell@viroteck.net> +** 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 QSGRHIVISUALIZER_P_H +#define QSGRHIVISUALIZER_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 "qsgbatchrenderer_p.h" + +QT_BEGIN_NAMESPACE + +namespace QSGBatchRenderer +{ + +class RhiVisualizer : public Visualizer +{ +public: + RhiVisualizer(Renderer *renderer); + ~RhiVisualizer(); + + void prepareVisualize() override; + void visualize() override; + + void releaseResources() override; + + struct DrawCall + { + static const int UBUF_SIZE = 152; // visualization.vert/frag + struct { + char data[UBUF_SIZE]; // matrix, rotation, color, pattern, projection + } uniforms; + struct { + QRhiGraphicsPipeline::Topology topology; + QRhiVertexInputAttribute::Format format; + int count; + int stride; + const void *data; // only when using own vbuf + } vertex; + struct { + QRhiCommandBuffer::IndexFormat format; + int count; + int stride; + const void *data; // only when using own ibuf + } index; + struct { + QRhiBuffer *vbuf; // either same for all draw calls and owned by the *Vis, or points to a Batch.Buffer.vbo.buf + int vbufOffset; + QRhiBuffer *ibuf; // same, but for index + int ibufOffset; + int ubufOffset; + } buf; + }; + +private: + QShader m_vs; + QShader m_fs; + + void recordDrawCalls(const QVector<DrawCall> &drawCalls, + QRhiCommandBuffer *cb, + QRhiShaderResourceBindings *srb, + bool blendOneOne = false); + + class PipelineCache { + public: + QRhiGraphicsPipeline *pipeline(RhiVisualizer *visualizer, + QRhi *rhi, + QRhiShaderResourceBindings *srb, + QRhiRenderPassDescriptor *rpDesc, + QRhiGraphicsPipeline::Topology topology, + QRhiVertexInputAttribute::Format vertexFormat, + quint32 vertexStride, + bool blendOneOne); + void releaseResources(); + private: + struct Pipeline { + QRhiGraphicsPipeline::Topology topology; + QRhiVertexInputAttribute::Format format; + quint32 stride; + QRhiGraphicsPipeline *ps; + }; + QVarLengthArray<Pipeline, 16> pipelines; + }; + + PipelineCache m_pipelines; + + class Fade { + public: + void prepare(RhiVisualizer *visualizer, + QRhi *rhi, QRhiResourceUpdateBatch *u, QRhiRenderPassDescriptor *rpDesc); + void releaseResources(); + void render(QRhiCommandBuffer *cb); + private: + RhiVisualizer *visualizer; + QRhiBuffer *vbuf = nullptr; + QRhiBuffer *ubuf = nullptr; + QRhiGraphicsPipeline *ps = nullptr; + QRhiShaderResourceBindings *srb = nullptr; + } m_fade; + + class ChangeVis { + public: + void prepare(Node *n, RhiVisualizer *visualizer, + QRhi *rhi, QRhiResourceUpdateBatch *u); + void releaseResources(); + void render(QRhiCommandBuffer *cb); + private: + void gather(Node *n); + RhiVisualizer *visualizer; + QVector<DrawCall> drawCalls; + QRhiBuffer *vbuf = nullptr; + QRhiBuffer *ibuf = nullptr; + QRhiBuffer *ubuf = nullptr; + QRhiShaderResourceBindings *srb = nullptr; + } m_changeVis; + + class BatchVis { + public: + void prepare(const QDataBuffer<Batch *> &opaqueBatches, + const QDataBuffer<Batch *> &alphaBatches, + RhiVisualizer *visualizer, + QRhi *rhi, QRhiResourceUpdateBatch *u, + bool forceUintIndex); + void releaseResources(); + void render(QRhiCommandBuffer *cb); + private: + void gather(Batch *b); + RhiVisualizer *visualizer; + bool forceUintIndex; + QVector<DrawCall> drawCalls; + QRhiBuffer *ubuf = nullptr; + QRhiShaderResourceBindings *srb = nullptr; + } m_batchVis; + + class ClipVis { + public: + void prepare(QSGNode *node, RhiVisualizer *visualizer, + QRhi *rhi, QRhiResourceUpdateBatch *u); + void releaseResources(); + void render(QRhiCommandBuffer *cb); + private: + void gather(QSGNode *node); + RhiVisualizer *visualizer; + QVector<DrawCall> drawCalls; + QRhiBuffer *vbuf = nullptr; + QRhiBuffer *ibuf = nullptr; + QRhiBuffer *ubuf = nullptr; + QRhiShaderResourceBindings *srb = nullptr; + } m_clipVis; + + class OverdrawVis { + public: + void prepare(Node *n, RhiVisualizer *visualizer, + QRhi *rhi, QRhiResourceUpdateBatch *u); + void releaseResources(); + void render(QRhiCommandBuffer *cb); + private: + void gather(Node *n); + RhiVisualizer *visualizer; + QVector<DrawCall> drawCalls; + QRhiBuffer *vbuf = nullptr; + QRhiBuffer *ibuf = nullptr; + QRhiBuffer *ubuf = nullptr; + QRhiShaderResourceBindings *srb = nullptr; + float step = 0.0f; + QMatrix4x4 rotation; + struct { + QRhiBuffer *vbuf = nullptr; + QRhiBuffer *ubuf = nullptr; + QRhiShaderResourceBindings *srb = nullptr; + QRhiGraphicsPipeline *ps = nullptr; + } box; + } m_overdrawVis; + + friend class Fade; + friend class PipelineCache; + friend class ChangeVis; + friend class ClipVis; + friend class OverdrawVis; +}; + +} // namespace QSGBatchRenderer + +QT_END_NAMESPACE + +#endif // QSGRHIVISUALIZER_P_H |