1/****************************************************************************
2**
3** Copyright (C) 2019 The Qt Company Ltd.
4** Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
5** Copyright (C) 2016 Robin Burchell <robin.burchell@viroteck.net>
6** Contact: https://www.qt.io/licensing/
7**
8** This file is part of the QtQuick module of the Qt Toolkit.
9**
10** $QT_BEGIN_LICENSE:LGPL$
11** Commercial License Usage
12** Licensees holding valid commercial Qt licenses may use this file in
13** accordance with the commercial license agreement provided with the
14** Software or, alternatively, in accordance with the terms contained in
15** a written agreement between you and The Qt Company. For licensing terms
16** and conditions see https://www.qt.io/terms-conditions. For further
17** information use the contact form at https://www.qt.io/contact-us.
18**
19** GNU Lesser General Public License Usage
20** Alternatively, this file may be used under the terms of the GNU Lesser
21** General Public License version 3 as published by the Free Software
22** Foundation and appearing in the file LICENSE.LGPL3 included in the
23** packaging of this file. Please review the following information to
24** ensure the GNU Lesser General Public License version 3 requirements
25** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
26**
27** GNU General Public License Usage
28** Alternatively, this file may be used under the terms of the GNU
29** General Public License version 2.0 or (at your option) the GNU General
30** Public license version 3 or any later version approved by the KDE Free
31** Qt Foundation. The licenses are as published by the Free Software
32** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
33** included in the packaging of this file. Please review the following
34** information to ensure the GNU General Public License requirements will
35** be met: https://www.gnu.org/licenses/gpl-2.0.html and
36** https://www.gnu.org/licenses/gpl-3.0.html.
37**
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qsgrhivisualizer_p.h"
43#include <qmath.h>
44#include <QQuickWindow>
45#include <private/qsgmaterialrhishader_p.h>
46#include <private/qsgshadersourcebuilder_p.h>
47
48QT_BEGIN_NAMESPACE
49
50namespace QSGBatchRenderer
51{
52
53#define QSGNODE_TRAVERSE(NODE) for (QSGNode *child = NODE->firstChild(); child; child = child->nextSibling())
54#define SHADOWNODE_TRAVERSE(NODE) for (Node *child = NODE->firstChild(); child; child = child->sibling())
55#define QSGNODE_DIRTY_PARENT (QSGNode::DirtyNodeAdded \
56 | QSGNode::DirtyOpacity \
57 | QSGNode::DirtyMatrix \
58 | QSGNode::DirtyNodeRemoved)
59
60QMatrix4x4 qsg_matrixForRoot(Node *node);
61QRhiVertexInputAttribute::Format qsg_vertexInputFormat(const QSGGeometry::Attribute &a);
62QRhiCommandBuffer::IndexFormat qsg_indexFormat(const QSGGeometry *geometry);
63QRhiGraphicsPipeline::Topology qsg_topology(int geomDrawMode);
64
65RhiVisualizer::RhiVisualizer(Renderer *renderer)
66 : Visualizer(renderer)
67{
68}
69
70RhiVisualizer::~RhiVisualizer()
71{
72 releaseResources();
73}
74
75void RhiVisualizer::releaseResources()
76{
77 m_pipelines.releaseResources();
78
79 m_fade.releaseResources();
80
81 m_changeVis.releaseResources();
82 m_batchVis.releaseResources();
83 m_clipVis.releaseResources();
84 m_overdrawVis.releaseResources();
85}
86
87void RhiVisualizer::prepareVisualize()
88{
89 // Called before the render pass has begun (but after preparing the
90 // batches). Now is the time to put resource updates to the renderer's
91 // current m_resourceUpdates instance.
92
93 if (m_visualizeMode == VisualizeNothing)
94 return;
95
96 if (!m_vs.isValid()) {
97 m_vs = QSGMaterialRhiShaderPrivate::loadShader(
98 filename: QLatin1String(":/qt-project.org/scenegraph/shaders_ng/visualization.vert.qsb"));
99 m_fs = QSGMaterialRhiShaderPrivate::loadShader(
100 filename: QLatin1String(":/qt-project.org/scenegraph/shaders_ng/visualization.frag.qsb"));
101 }
102
103 m_fade.prepare(visualizer: this, rhi: m_renderer->m_rhi, u: m_renderer->m_resourceUpdates, rpDesc: m_renderer->renderPassDescriptor());
104
105 const bool forceUintIndex = m_renderer->m_uint32IndexForRhi;
106
107 switch (m_visualizeMode) {
108 case VisualizeBatches:
109 m_batchVis.prepare(opaqueBatches: m_renderer->m_opaqueBatches, alphaBatches: m_renderer->m_alphaBatches,
110 visualizer: this,
111 rhi: m_renderer->m_rhi, u: m_renderer->m_resourceUpdates,
112 forceUintIndex);
113 break;
114 case VisualizeClipping:
115 m_clipVis.prepare(node: m_renderer->rootNode(), visualizer: this,
116 rhi: m_renderer->m_rhi, u: m_renderer->m_resourceUpdates);
117 break;
118 case VisualizeChanges:
119 m_changeVis.prepare(n: m_renderer->m_nodes.value(akey: m_renderer->rootNode()),
120 visualizer: this,
121 rhi: m_renderer->m_rhi, u: m_renderer->m_resourceUpdates);
122 m_visualizeChangeSet.clear();
123 break;
124 case VisualizeOverdraw:
125 m_overdrawVis.prepare(n: m_renderer->m_nodes.value(akey: m_renderer->rootNode()),
126 visualizer: this,
127 rhi: m_renderer->m_rhi, u: m_renderer->m_resourceUpdates);
128 break;
129 default:
130 Q_UNREACHABLE();
131 break;
132 }
133}
134
135void RhiVisualizer::visualize()
136{
137 if (m_visualizeMode == VisualizeNothing)
138 return;
139
140 QRhiCommandBuffer *cb = m_renderer->commandBuffer();
141 m_fade.render(cb);
142
143 switch (m_visualizeMode) {
144 case VisualizeBatches:
145 m_batchVis.render(cb);
146 break;
147 case VisualizeClipping:
148 m_clipVis.render(cb);
149 break;
150 case VisualizeChanges:
151 m_changeVis.render(cb);
152 break;
153 case VisualizeOverdraw:
154 m_overdrawVis.render(cb);
155 break;
156 default:
157 Q_UNREACHABLE();
158 break;
159 }
160}
161
162void RhiVisualizer::recordDrawCalls(const QVector<DrawCall> &drawCalls,
163 QRhiCommandBuffer *cb,
164 QRhiShaderResourceBindings *srb,
165 bool blendOneOne)
166{
167 for (const DrawCall &dc : drawCalls) {
168 QRhiGraphicsPipeline *ps = m_pipelines.pipeline(visualizer: this, rhi: m_renderer->m_rhi, srb, rpDesc: m_renderer->renderPassDescriptor(),
169 topology: dc.vertex.topology, vertexFormat: dc.vertex.format, vertexStride: dc.vertex.stride,
170 blendOneOne);
171 if (!ps)
172 continue;
173 cb->setGraphicsPipeline(ps); // no-op if same as the last one
174 QRhiCommandBuffer::DynamicOffset dynofs(0, dc.buf.ubufOffset);
175 cb->setShaderResources(srb, dynamicOffsetCount: 1, dynamicOffsets: &dynofs);
176 QRhiCommandBuffer::VertexInput vb(dc.buf.vbuf, dc.buf.vbufOffset);
177 if (dc.index.count) {
178 cb->setVertexInput(startBinding: 0, bindingCount: 1, bindings: &vb, indexBuf: dc.buf.ibuf, indexOffset: dc.buf.ibufOffset, indexFormat: dc.index.format);
179 cb->drawIndexed(indexCount: dc.index.count);
180 } else {
181 cb->setVertexInput(startBinding: 0, bindingCount: 1, bindings: &vb);
182 cb->draw(vertexCount: dc.vertex.count);
183 }
184 }
185}
186
187const QRhiShaderResourceBinding::StageFlags ubufVisibility =
188 QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage;
189
190void RhiVisualizer::Fade::prepare(RhiVisualizer *visualizer,
191 QRhi *rhi, QRhiResourceUpdateBatch *u, QRhiRenderPassDescriptor *rpDesc)
192{
193 this->visualizer = visualizer;
194
195 if (!vbuf) {
196 float v[] = { -1, 1, 1, 1, -1, -1, 1, -1 };
197 vbuf = rhi->newBuffer(type: QRhiBuffer::Immutable, usage: QRhiBuffer::VertexBuffer, size: sizeof(v));
198 if (!vbuf->build())
199 return;
200 u->uploadStaticBuffer(buf: vbuf, data: v);
201 }
202
203 if (!ubuf) {
204 ubuf = rhi->newBuffer(type: QRhiBuffer::Dynamic, usage: QRhiBuffer::UniformBuffer, size: DrawCall::UBUF_SIZE);
205 if (!ubuf->build())
206 return;
207 float bgOpacity = 0.8f;
208 if (visualizer->m_visualizeMode == Visualizer::VisualizeBatches)
209 bgOpacity = 1.0;
210 QMatrix4x4 ident;
211 u->updateDynamicBuffer(buf: ubuf, offset: 0, size: 64, data: ident.constData()); // matrix
212 u->updateDynamicBuffer(buf: ubuf, offset: 64, size: 64, data: ident.constData()); // rotation
213 float color[4] = { 0.0f, 0.0f, 0.0f, bgOpacity };
214 u->updateDynamicBuffer(buf: ubuf, offset: 128, size: 16, data: color);
215 float pattern = 0.0f;
216 u->updateDynamicBuffer(buf: ubuf, offset: 144, size: 4, data: &pattern);
217 qint32 projection = 0;
218 u->updateDynamicBuffer(buf: ubuf, offset: 148, size: 4, data: &projection);
219 }
220
221 if (!srb) {
222 srb = rhi->newShaderResourceBindings();
223 srb->setBindings({ QRhiShaderResourceBinding::uniformBuffer(binding: 0, stage: ubufVisibility, buf: ubuf) });
224 if (!srb->build())
225 return;
226 }
227
228 if (!ps) {
229 ps = rhi->newGraphicsPipeline();
230 ps->setTopology(QRhiGraphicsPipeline::TriangleStrip);
231 QRhiGraphicsPipeline::TargetBlend blend; // defaults to premul alpha, just what we need
232 blend.enable = true;
233 ps->setTargetBlends({ blend });
234 ps->setShaderStages({ { QRhiShaderStage::Vertex, visualizer->m_vs },
235 { QRhiShaderStage::Fragment, visualizer->m_fs } });
236 QRhiVertexInputLayout inputLayout;
237 inputLayout.setBindings({ { 2 * sizeof(float) } });
238 inputLayout.setAttributes({ { 0, 0, QRhiVertexInputAttribute::Float2, 0 } });
239 ps->setVertexInputLayout(inputLayout);
240 ps->setShaderResourceBindings(srb);
241 ps->setRenderPassDescriptor(rpDesc);
242 if (!ps->build())
243 return;
244 }
245}
246
247void RhiVisualizer::Fade::releaseResources()
248{
249 delete ps;
250 ps = nullptr;
251
252 delete srb;
253 srb = nullptr;
254
255 delete ubuf;
256 ubuf = nullptr;
257
258 delete vbuf;
259 vbuf = nullptr;
260}
261
262void RhiVisualizer::Fade::render(QRhiCommandBuffer *cb)
263{
264 cb->setGraphicsPipeline(ps);
265 cb->setViewport(visualizer->m_renderer->m_pstate.viewport);
266 cb->setShaderResources();
267 QRhiCommandBuffer::VertexInput vb(vbuf, 0);
268 cb->setVertexInput(startBinding: 0, bindingCount: 1, bindings: &vb);
269 cb->draw(vertexCount: 4);
270}
271
272static void fillVertexIndex(RhiVisualizer::DrawCall *dc, QSGGeometry *g, bool withData, bool forceUintIndex)
273{
274 dc->vertex.topology = qsg_topology(geomDrawMode: g->drawingMode());
275 dc->vertex.format = qsg_vertexInputFormat(a: g->attributes()[0]);
276 dc->vertex.count = g->vertexCount();
277 dc->vertex.stride = g->sizeOfVertex();
278 if (withData)
279 dc->vertex.data = g->vertexData();
280
281 dc->index.format = forceUintIndex ? QRhiCommandBuffer::IndexUInt32 : qsg_indexFormat(geometry: g);
282 dc->index.count = g->indexCount();
283 dc->index.stride = forceUintIndex ? sizeof(quint32) : g->sizeOfIndex();
284 if (withData && g->indexCount())
285 dc->index.data = g->indexData();
286}
287
288static inline uint aligned(uint v, uint byteAlign)
289{
290 return (v + byteAlign - 1) & ~(byteAlign - 1);
291}
292
293static bool ensureBuffer(QRhi *rhi, QRhiBuffer **buf, QRhiBuffer::UsageFlags usage, int newSize)
294{
295 if (!*buf) {
296 *buf = rhi->newBuffer(type: QRhiBuffer::Dynamic, usage, size: newSize);
297 if (!(*buf)->build())
298 return false;
299 } else if ((*buf)->size() < newSize) {
300 (*buf)->setSize(newSize);
301 if (!(*buf)->build())
302 return false;
303 }
304 return true;
305}
306
307QRhiGraphicsPipeline *RhiVisualizer::PipelineCache::pipeline(RhiVisualizer *visualizer,
308 QRhi *rhi,
309 QRhiShaderResourceBindings *srb,
310 QRhiRenderPassDescriptor *rpDesc,
311 QRhiGraphicsPipeline::Topology topology,
312 QRhiVertexInputAttribute::Format vertexFormat,
313 quint32 vertexStride,
314 bool blendOneOne)
315{
316 for (int i = 0, ie = pipelines.count(); i != ie; ++i) {
317 const Pipeline &p(pipelines.at(idx: i));
318 if (p.topology == topology && p.format == vertexFormat && p.stride == vertexStride)
319 return p.ps;
320 }
321
322 QRhiGraphicsPipeline *ps = rhi->newGraphicsPipeline();
323 ps->setTopology(topology);
324 QRhiGraphicsPipeline::TargetBlend blend; // premul alpha
325 blend.enable = true;
326 if (blendOneOne) {
327 // only for visualizing overdraw, other modes use premul alpha
328 blend.srcColor = QRhiGraphicsPipeline::One;
329 blend.dstColor = QRhiGraphicsPipeline::One;
330 blend.srcAlpha = QRhiGraphicsPipeline::One;
331 blend.dstAlpha = QRhiGraphicsPipeline::One;
332 }
333 ps->setTargetBlends({ blend });
334 ps->setShaderStages({ { QRhiShaderStage::Vertex, visualizer->m_vs },
335 { QRhiShaderStage::Fragment, visualizer->m_fs } });
336 QRhiVertexInputLayout inputLayout;
337 inputLayout.setBindings({ { vertexStride } });
338 inputLayout.setAttributes({ { 0, 0, vertexFormat, 0 } });
339 ps->setVertexInputLayout(inputLayout);
340 ps->setShaderResourceBindings(srb);
341 ps->setRenderPassDescriptor(rpDesc);
342 if (!ps->build())
343 return nullptr;
344
345 Pipeline p;
346 p.topology = topology;
347 p.format = vertexFormat;
348 p.stride = vertexStride;
349 p.ps = ps;
350 pipelines.append(t: p);
351
352 return ps;
353}
354
355void RhiVisualizer::PipelineCache::releaseResources()
356{
357 for (int i = 0, ie = pipelines.count(); i != ie; ++i)
358 delete pipelines.at(idx: i).ps;
359
360 pipelines.clear();
361}
362
363void RhiVisualizer::ChangeVis::gather(Node *n)
364{
365 if (n->type() == QSGNode::GeometryNodeType && n->element()->batch && visualizer->m_visualizeChangeSet.contains(akey: n)) {
366 const uint dirty = visualizer->m_visualizeChangeSet.value(akey: n);
367 const bool tinted = (dirty & QSGNODE_DIRTY_PARENT) != 0;
368 const QColor color = QColor::fromHsvF(h: (rand() & 1023) / 1023.0f, s: 0.3f, v: 1.0f);
369 const float alpha = 0.5f;
370
371 QMatrix4x4 matrix = visualizer->m_renderer->m_current_projection_matrix;
372 if (n->element()->batch->root)
373 matrix = matrix * qsg_matrixForRoot(node: n->element()->batch->root);
374
375 QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(n->sgNode);
376 matrix = matrix * *gn->matrix();
377
378 QSGGeometry *g = gn->geometry();
379 if (g->attributeCount() >= 1) {
380 DrawCall dc;
381 memcpy(dest: dc.uniforms.data, src: matrix.constData(), n: 64);
382 QMatrix4x4 rotation;
383 memcpy(dest: dc.uniforms.data + 64, src: rotation.constData(), n: 64);
384 float c[4] = {
385 float(color.redF()) * alpha,
386 float(color.greenF()) * alpha,
387 float(color.blueF()) * alpha,
388 alpha
389 };
390 memcpy(dest: dc.uniforms.data + 128, src: c, n: 16);
391 float pattern = tinted ? 0.5f : 0.0f;
392 memcpy(dest: dc.uniforms.data + 144, src: &pattern, n: 4);
393 qint32 projection = 0;
394 memcpy(dest: dc.uniforms.data + 148, src: &projection, n: 4);
395
396 fillVertexIndex(dc: &dc, g, withData: true, forceUintIndex: false);
397 drawCalls.append(t: dc);
398 }
399
400 // This is because many changes don't propegate their dirty state to the
401 // parent so the node updater will not unset these states. They are
402 // not used for anything so, unsetting it should have no side effects.
403 n->dirtyState = { };
404 }
405
406 SHADOWNODE_TRAVERSE(n) {
407 gather(n: child);
408 }
409}
410
411void RhiVisualizer::ChangeVis::prepare(Node *n, RhiVisualizer *visualizer,
412 QRhi *rhi, QRhiResourceUpdateBatch *u)
413{
414 this->visualizer = visualizer;
415
416 drawCalls.clear();
417 gather(n);
418
419 if (drawCalls.isEmpty())
420 return;
421
422 const int ubufAlign = rhi->ubufAlignment();
423 int vbufOffset = 0;
424 int ibufOffset = 0;
425 int ubufOffset = 0;
426 for (RhiVisualizer::DrawCall &dc : drawCalls) {
427 dc.buf.vbufOffset = aligned(v: vbufOffset, byteAlign: 4);
428 vbufOffset = dc.buf.vbufOffset + dc.vertex.count * dc.vertex.stride;
429
430 dc.buf.ibufOffset = aligned(v: ibufOffset, byteAlign: 4);
431 ibufOffset = dc.buf.ibufOffset + dc.index.count * dc.index.stride;
432
433 dc.buf.ubufOffset = aligned(v: ubufOffset, byteAlign: ubufAlign);
434 ubufOffset = dc.buf.ubufOffset + DrawCall::UBUF_SIZE;
435 }
436
437 ensureBuffer(rhi, buf: &vbuf, usage: QRhiBuffer::VertexBuffer, newSize: vbufOffset);
438 if (ibufOffset)
439 ensureBuffer(rhi, buf: &ibuf, usage: QRhiBuffer::IndexBuffer, newSize: ibufOffset);
440 const int ubufSize = ubufOffset;
441 ensureBuffer(rhi, buf: &ubuf, usage: QRhiBuffer::UniformBuffer, newSize: ubufSize);
442
443 for (RhiVisualizer::DrawCall &dc : drawCalls) {
444 u->updateDynamicBuffer(buf: vbuf, offset: dc.buf.vbufOffset, size: dc.vertex.count * dc.vertex.stride, data: dc.vertex.data);
445 dc.buf.vbuf = vbuf;
446 if (dc.index.count) {
447 u->updateDynamicBuffer(buf: ibuf, offset: dc.buf.ibufOffset, size: dc.index.count * dc.index.stride, data: dc.index.data);
448 dc.buf.ibuf = ibuf;
449 }
450 u->updateDynamicBuffer(buf: ubuf, offset: dc.buf.ubufOffset, size: DrawCall::UBUF_SIZE, data: dc.uniforms.data);
451 }
452
453 if (!srb) {
454 srb = rhi->newShaderResourceBindings();
455 srb->setBindings({ QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(binding: 0, stage: ubufVisibility, buf: ubuf, size: DrawCall::UBUF_SIZE) });
456 if (!srb->build())
457 return;
458 }
459}
460
461void RhiVisualizer::ChangeVis::releaseResources()
462{
463 delete srb;
464 srb = nullptr;
465
466 delete ubuf;
467 ubuf = nullptr;
468
469 delete ibuf;
470 ibuf = nullptr;
471
472 delete vbuf;
473 vbuf = nullptr;
474}
475
476void RhiVisualizer::ChangeVis::render(QRhiCommandBuffer *cb)
477{
478 visualizer->recordDrawCalls(drawCalls, cb, srb);
479}
480
481void RhiVisualizer::BatchVis::gather(Batch *b)
482{
483 if (b->positionAttribute != 0)
484 return;
485
486 QMatrix4x4 matrix(visualizer->m_renderer->m_current_projection_matrix);
487 if (b->root)
488 matrix = matrix * qsg_matrixForRoot(node: b->root);
489
490 DrawCall dc;
491
492 QMatrix4x4 rotation;
493 memcpy(dest: dc.uniforms.data + 64, src: rotation.constData(), n: 64);
494
495 QColor color = QColor::fromHsvF(h: (rand() & 1023) / 1023.0, s: 1.0, v: 1.0);
496
497 float c[4] = {
498 float(color.redF()),
499 float(color.greenF()),
500 float(color.blueF()),
501 1.0f
502 };
503 memcpy(dest: dc.uniforms.data + 128, src: c, n: 16);
504
505 float pattern = b->merged ? 0.0f : 1.0f;
506 memcpy(dest: dc.uniforms.data + 144, src: &pattern, n: 4);
507
508 qint32 projection = 0;
509 memcpy(dest: dc.uniforms.data + 148, src: &projection, n: 4);
510
511 if (b->merged) {
512 memcpy(dest: dc.uniforms.data, src: matrix.constData(), n: 64);
513
514 QSGGeometryNode *gn = b->first->node;
515 QSGGeometry *g = gn->geometry();
516
517 fillVertexIndex(dc: &dc, g, withData: false, forceUintIndex);
518
519 for (int ds = 0; ds < b->drawSets.size(); ++ds) {
520 const DrawSet &set = b->drawSets.at(i: ds);
521 dc.buf.vbuf = b->vbo.buf;
522 dc.buf.vbufOffset = set.vertices;
523 dc.buf.ibuf = b->ibo.buf;
524 dc.buf.ibufOffset = set.indices;
525 dc.index.count = set.indexCount;
526 drawCalls.append(t: dc);
527 }
528 } else {
529 Element *e = b->first;
530 int vOffset = 0;
531 int iOffset = 0;
532
533 while (e) {
534 QSGGeometryNode *gn = e->node;
535 QSGGeometry *g = gn->geometry();
536
537 QMatrix4x4 m = matrix * *gn->matrix();
538 memcpy(dest: dc.uniforms.data, src: m.constData(), n: 64);
539
540 fillVertexIndex(dc: &dc, g, withData: false, forceUintIndex);
541
542 dc.buf.vbuf = b->vbo.buf;
543 dc.buf.vbufOffset = vOffset;
544 if (g->indexCount()) {
545 dc.buf.ibuf = b->ibo.buf;
546 dc.buf.ibufOffset = iOffset;
547 }
548
549 drawCalls.append(t: dc);
550
551 vOffset += dc.vertex.count * dc.vertex.stride;
552 iOffset += dc.index.count * dc.index.stride;
553
554 e = e->nextInBatch;
555 }
556 }
557}
558
559void RhiVisualizer::BatchVis::prepare(const QDataBuffer<Batch *> &opaqueBatches, const QDataBuffer<Batch *> &alphaBatches,
560 RhiVisualizer *visualizer,
561 QRhi *rhi, QRhiResourceUpdateBatch *u,
562 bool forceUintIndex)
563{
564 this->visualizer = visualizer;
565 this->forceUintIndex = forceUintIndex;
566
567 drawCalls.clear();
568
569 srand(seed: 0); // To force random colors to be roughly the same every time..
570 for (int i = 0; i < opaqueBatches.size(); ++i)
571 gather(b: opaqueBatches.at(i));
572 for (int i = 0; i < alphaBatches.size(); ++i)
573 gather(b: alphaBatches.at(i));
574
575 if (drawCalls.isEmpty())
576 return;
577
578 const int ubufAlign = rhi->ubufAlignment();
579 int ubufOffset = 0;
580 for (RhiVisualizer::DrawCall &dc : drawCalls) {
581 dc.buf.ubufOffset = aligned(v: ubufOffset, byteAlign: ubufAlign);
582 ubufOffset = dc.buf.ubufOffset + DrawCall::UBUF_SIZE;
583 }
584
585 const int ubufSize = ubufOffset;
586 ensureBuffer(rhi, buf: &ubuf, usage: QRhiBuffer::UniformBuffer, newSize: ubufSize);
587
588 for (RhiVisualizer::DrawCall &dc : drawCalls)
589 u->updateDynamicBuffer(buf: ubuf, offset: dc.buf.ubufOffset, size: DrawCall::UBUF_SIZE, data: dc.uniforms.data);
590
591 if (!srb) {
592 srb = rhi->newShaderResourceBindings();
593 srb->setBindings({ QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(binding: 0, stage: ubufVisibility, buf: ubuf, size: DrawCall::UBUF_SIZE) });
594 if (!srb->build())
595 return;
596 }
597}
598
599void RhiVisualizer::BatchVis::releaseResources()
600{
601 delete srb;
602 srb = nullptr;
603
604 delete ubuf;
605 ubuf = nullptr;
606}
607
608void RhiVisualizer::BatchVis::render(QRhiCommandBuffer *cb)
609{
610 visualizer->recordDrawCalls(drawCalls, cb, srb);
611}
612
613void RhiVisualizer::ClipVis::gather(QSGNode *node)
614{
615 if (node->type() == QSGNode::ClipNodeType) {
616 QSGClipNode *clipNode = static_cast<QSGClipNode *>(node);
617 QMatrix4x4 matrix = visualizer->m_renderer->m_current_projection_matrix;
618 if (clipNode->matrix())
619 matrix = matrix * *clipNode->matrix();
620
621 QSGGeometry *g = clipNode->geometry();
622 if (g->attributeCount() >= 1) {
623 DrawCall dc;
624 memcpy(dest: dc.uniforms.data, src: matrix.constData(), n: 64);
625 QMatrix4x4 rotation;
626 memcpy(dest: dc.uniforms.data + 64, src: rotation.constData(), n: 64);
627 float c[4] = { 0.2f, 0.0f, 0.0f, 0.2f };
628 memcpy(dest: dc.uniforms.data + 128, src: c, n: 16);
629 float pattern = 0.5f;
630 memcpy(dest: dc.uniforms.data + 144, src: &pattern, n: 4);
631 qint32 projection = 0;
632 memcpy(dest: dc.uniforms.data + 148, src: &projection, n: 4);
633 fillVertexIndex(dc: &dc, g, withData: true, forceUintIndex: false);
634 drawCalls.append(t: dc);
635 }
636 }
637
638 QSGNODE_TRAVERSE(node) {
639 gather(node: child);
640 }
641}
642
643void RhiVisualizer::ClipVis::prepare(QSGNode *node, RhiVisualizer *visualizer,
644 QRhi *rhi, QRhiResourceUpdateBatch *u)
645{
646 this->visualizer = visualizer;
647
648 drawCalls.clear();
649 gather(node);
650
651 if (drawCalls.isEmpty())
652 return;
653
654 const int ubufAlign = rhi->ubufAlignment();
655 int vbufOffset = 0;
656 int ibufOffset = 0;
657 int ubufOffset = 0;
658 for (RhiVisualizer::DrawCall &dc : drawCalls) {
659 dc.buf.vbufOffset = aligned(v: vbufOffset, byteAlign: 4);
660 vbufOffset = dc.buf.vbufOffset + dc.vertex.count * dc.vertex.stride;
661
662 dc.buf.ibufOffset = aligned(v: ibufOffset, byteAlign: 4);
663 ibufOffset = dc.buf.ibufOffset + dc.index.count * dc.index.stride;
664
665 dc.buf.ubufOffset = aligned(v: ubufOffset, byteAlign: ubufAlign);
666 ubufOffset = dc.buf.ubufOffset + DrawCall::UBUF_SIZE;
667 }
668
669 ensureBuffer(rhi, buf: &vbuf, usage: QRhiBuffer::VertexBuffer, newSize: vbufOffset);
670 if (ibufOffset)
671 ensureBuffer(rhi, buf: &ibuf, usage: QRhiBuffer::IndexBuffer, newSize: ibufOffset);
672 const int ubufSize = ubufOffset;
673 ensureBuffer(rhi, buf: &ubuf, usage: QRhiBuffer::UniformBuffer, newSize: ubufSize);
674
675 for (RhiVisualizer::DrawCall &dc : drawCalls) {
676 u->updateDynamicBuffer(buf: vbuf, offset: dc.buf.vbufOffset, size: dc.vertex.count * dc.vertex.stride, data: dc.vertex.data);
677 dc.buf.vbuf = vbuf;
678 if (dc.index.count) {
679 u->updateDynamicBuffer(buf: ibuf, offset: dc.buf.ibufOffset, size: dc.index.count * dc.index.stride, data: dc.index.data);
680 dc.buf.ibuf = ibuf;
681 }
682 u->updateDynamicBuffer(buf: ubuf, offset: dc.buf.ubufOffset, size: DrawCall::UBUF_SIZE, data: dc.uniforms.data);
683 }
684
685 if (!srb) {
686 srb = rhi->newShaderResourceBindings();
687 srb->setBindings({ QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(binding: 0, stage: ubufVisibility, buf: ubuf, size: DrawCall::UBUF_SIZE) });
688 if (!srb->build())
689 return;
690 }
691}
692
693void RhiVisualizer::ClipVis::releaseResources()
694{
695 delete srb;
696 srb = nullptr;
697
698 delete ubuf;
699 ubuf = nullptr;
700
701 delete ibuf;
702 ibuf = nullptr;
703
704 delete vbuf;
705 vbuf = nullptr;
706}
707
708void RhiVisualizer::ClipVis::render(QRhiCommandBuffer *cb)
709{
710 visualizer->recordDrawCalls(drawCalls, cb, srb);
711}
712
713void RhiVisualizer::OverdrawVis::gather(Node *n)
714{
715 if (n->type() == QSGNode::GeometryNodeType && n->element()->batch) {
716 QMatrix4x4 matrix = visualizer->m_renderer->m_current_projection_matrix;
717 matrix(2, 2) = visualizer->m_renderer->m_zRange;
718 matrix(2, 3) = 1.0f - n->element()->order * visualizer->m_renderer->m_zRange;
719
720 if (n->element()->batch->root)
721 matrix = matrix * qsg_matrixForRoot(node: n->element()->batch->root);
722
723 QSGGeometryNode *gn = static_cast<QSGGeometryNode *>(n->sgNode);
724 matrix = matrix * *gn->matrix();
725
726 QSGGeometry *g = gn->geometry();
727 if (g->attributeCount() >= 1) {
728 DrawCall dc;
729 memcpy(dest: dc.uniforms.data, src: matrix.constData(), n: 64);
730 memcpy(dest: dc.uniforms.data + 64, src: rotation.constData(), n: 64);
731
732 float c[4];
733 const float ca = 0.33f;
734 if (n->element()->batch->isOpaque) {
735 c[0] = ca * 0.3f; c[1] = ca * 1.0f; c[2] = ca * 0.3f; c[3] = ca;
736 } else {
737 c[0] = ca * 1.0f; c[1] = ca * 0.3f; c[2] = ca * 0.3f; c[3] = ca;
738 }
739 memcpy(dest: dc.uniforms.data + 128, src: c, n: 16);
740 float pattern = 0.0f;
741 memcpy(dest: dc.uniforms.data + 144, src: &pattern, n: 4);
742 qint32 projection = 1;
743 memcpy(dest: dc.uniforms.data + 148, src: &projection, n: 4);
744
745 fillVertexIndex(dc: &dc, g, withData: true, forceUintIndex: false);
746 drawCalls.append(t: dc);
747 }
748 }
749
750 SHADOWNODE_TRAVERSE(n) {
751 gather(n: child);
752 }
753}
754
755void RhiVisualizer::OverdrawVis::prepare(Node *n, RhiVisualizer *visualizer,
756 QRhi *rhi, QRhiResourceUpdateBatch *u)
757{
758 this->visualizer = visualizer;
759
760 step += float(M_PI * 2 / 1000.0);
761 if (step > float(M_PI * 2))
762 step = 0.0f;
763
764 const float yfix = rhi->isYUpInNDC() ? 1.0f : -1.0f;
765 rotation.setToIdentity();
766 rotation.translate(x: 0.0f, y: 0.5f * yfix, z: 4.0f);
767 rotation.scale(x: 2.0f, y: 2.0f, z: 1.0f);
768 rotation.rotate(angle: -30.0f * yfix, x: 1.0f, y: 0.0f, z: 0.0f);
769 rotation.rotate(angle: 80.0f * std::sin(x: step), x: 0.0f, y: 1.0f, z: 0.0f);
770 rotation.translate(x: 0.0f, y: 0.0f, z: -1.0f);
771
772 drawCalls.clear();
773 gather(n);
774
775 if (!box.vbuf) {
776 const float v[] = {
777 // lower
778 -1, 1, 0, 1, 1, 0,
779 -1, 1, 0, -1, -1, 0,
780 1, 1, 0, 1, -1, 0,
781 -1, -1, 0, 1, -1, 0,
782
783 // upper
784 -1, 1, 1, 1, 1, 1,
785 -1, 1, 1, -1, -1, 1,
786 1, 1, 1, 1, -1, 1,
787 -1, -1, 1, 1, -1, 1,
788
789 // sides
790 -1, -1, 0, -1, -1, 1,
791 1, -1, 0, 1, -1, 1,
792 -1, 1, 0, -1, 1, 1,
793 1, 1, 0, 1, 1, 1
794 };
795 box.vbuf = rhi->newBuffer(type: QRhiBuffer::Immutable, usage: QRhiBuffer::VertexBuffer, size: sizeof(v));
796 if (!box.vbuf->build())
797 return;
798 u->uploadStaticBuffer(buf: box.vbuf, data: v);
799 }
800
801 if (!box.ubuf) {
802 box.ubuf = rhi->newBuffer(type: QRhiBuffer::Dynamic, usage: QRhiBuffer::UniformBuffer, size: DrawCall::UBUF_SIZE);
803 if (!box.ubuf->build())
804 return;
805 QMatrix4x4 ident;
806 u->updateDynamicBuffer(buf: box.ubuf, offset: 0, size: 64, data: ident.constData());
807 float color[4] = { 0.5f, 0.5f, 1.0f, 1.0f };
808 u->updateDynamicBuffer(buf: box.ubuf, offset: 128, size: 16, data: color);
809 float pattern = 0.0f;
810 u->updateDynamicBuffer(buf: box.ubuf, offset: 144, size: 4, data: &pattern);
811 qint32 projection = 1;
812 u->updateDynamicBuffer(buf: box.ubuf, offset: 148, size: 4, data: &projection);
813 }
814
815 u->updateDynamicBuffer(buf: box.ubuf, offset: 64, size: 64, data: rotation.constData());
816
817 if (!box.srb) {
818 box.srb = rhi->newShaderResourceBindings();
819 box.srb->setBindings({ QRhiShaderResourceBinding::uniformBuffer(binding: 0, stage: ubufVisibility, buf: box.ubuf) });
820 if (!box.srb->build())
821 return;
822 }
823
824 if (!box.ps) {
825 box.ps = rhi->newGraphicsPipeline();
826 box.ps->setTopology(QRhiGraphicsPipeline::Lines);
827 box.ps->setLineWidth(2); // may be be ignored (D3D, Metal), but may be used on GL and Vulkan
828 QRhiGraphicsPipeline::TargetBlend blend;
829 blend.enable = true;
830 blend.srcColor = QRhiGraphicsPipeline::One;
831 blend.dstColor = QRhiGraphicsPipeline::One;
832 blend.srcAlpha = QRhiGraphicsPipeline::One;
833 blend.dstAlpha = QRhiGraphicsPipeline::One;
834 box.ps->setTargetBlends({ blend });
835 box.ps->setShaderStages({ { QRhiShaderStage::Vertex, visualizer->m_vs },
836 { QRhiShaderStage::Fragment, visualizer->m_fs } });
837 QRhiVertexInputLayout inputLayout;
838 inputLayout.setBindings({ { 3 * sizeof(float) } });
839 inputLayout.setAttributes({ { 0, 0, QRhiVertexInputAttribute::Float3, 0 } });
840 box.ps->setVertexInputLayout(inputLayout);
841 box.ps->setShaderResourceBindings(box.srb);
842 box.ps->setRenderPassDescriptor(visualizer->m_renderer->renderPassDescriptor());
843 if (!box.ps->build())
844 return;
845 }
846
847 if (drawCalls.isEmpty())
848 return;
849
850 const int ubufAlign = rhi->ubufAlignment();
851 int vbufOffset = 0;
852 int ibufOffset = 0;
853 int ubufOffset = 0;
854 for (RhiVisualizer::DrawCall &dc : drawCalls) {
855 dc.buf.vbufOffset = aligned(v: vbufOffset, byteAlign: 4);
856 vbufOffset = dc.buf.vbufOffset + dc.vertex.count * dc.vertex.stride;
857
858 dc.buf.ibufOffset = aligned(v: ibufOffset, byteAlign: 4);
859 ibufOffset = dc.buf.ibufOffset + dc.index.count * dc.index.stride;
860
861 dc.buf.ubufOffset = aligned(v: ubufOffset, byteAlign: ubufAlign);
862 ubufOffset = dc.buf.ubufOffset + DrawCall::UBUF_SIZE;
863 }
864
865 ensureBuffer(rhi, buf: &vbuf, usage: QRhiBuffer::VertexBuffer, newSize: vbufOffset);
866 if (ibufOffset)
867 ensureBuffer(rhi, buf: &ibuf, usage: QRhiBuffer::IndexBuffer, newSize: ibufOffset);
868 const int ubufSize = ubufOffset;
869 ensureBuffer(rhi, buf: &ubuf, usage: QRhiBuffer::UniformBuffer, newSize: ubufSize);
870
871 for (RhiVisualizer::DrawCall &dc : drawCalls) {
872 u->updateDynamicBuffer(buf: vbuf, offset: dc.buf.vbufOffset, size: dc.vertex.count * dc.vertex.stride, data: dc.vertex.data);
873 dc.buf.vbuf = vbuf;
874 if (dc.index.count) {
875 u->updateDynamicBuffer(buf: ibuf, offset: dc.buf.ibufOffset, size: dc.index.count * dc.index.stride, data: dc.index.data);
876 dc.buf.ibuf = ibuf;
877 }
878 u->updateDynamicBuffer(buf: ubuf, offset: dc.buf.ubufOffset, size: DrawCall::UBUF_SIZE, data: dc.uniforms.data);
879 }
880
881 if (!srb) {
882 srb = rhi->newShaderResourceBindings();
883 srb->setBindings({ QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(binding: 0, stage: ubufVisibility, buf: ubuf, size: DrawCall::UBUF_SIZE) });
884 if (!srb->build())
885 return;
886 }
887}
888
889void RhiVisualizer::OverdrawVis::releaseResources()
890{
891 delete srb;
892 srb = nullptr;
893
894 delete ubuf;
895 ubuf = nullptr;
896
897 delete ibuf;
898 ibuf = nullptr;
899
900 delete vbuf;
901 vbuf = nullptr;
902
903 delete box.ps;
904 box.ps = nullptr;
905
906 delete box.srb;
907 box.srb = nullptr;
908
909 delete box.ubuf;
910 box.ubuf = nullptr;
911
912 delete box.vbuf;
913 box.vbuf = nullptr;
914}
915
916void RhiVisualizer::OverdrawVis::render(QRhiCommandBuffer *cb)
917{
918 cb->setGraphicsPipeline(box.ps);
919 cb->setShaderResources();
920 QRhiCommandBuffer::VertexInput vb(box.vbuf, 0);
921 cb->setVertexInput(startBinding: 0, bindingCount: 1, bindings: &vb);
922 cb->draw(vertexCount: 24);
923
924 visualizer->recordDrawCalls(drawCalls, cb, srb, blendOneOne: true);
925}
926
927}
928
929QT_END_NAMESPACE
930

source code of qtdeclarative/src/quick/scenegraph/coreapi/qsgrhivisualizer.cpp