diff options
Diffstat (limited to 'src/quick/scenegraph/coreapi/qsgrhivisualizer.cpp')
-rw-r--r-- | src/quick/scenegraph/coreapi/qsgrhivisualizer.cpp | 929 |
1 files changed, 929 insertions, 0 deletions
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 |