/**************************************************************************** ** ** Copyright (C) 2021 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:COMM$ ** ** 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. ** ** $QT_END_LICENSE$ ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ****************************************************************************/ #include "qsgrhivisualizer_p.h" #include #include #include #include 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 &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(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 = { }; } 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 &opaqueBatches, const QDataBuffer &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(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(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