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

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