1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qquick3drenderstats_p.h"
5#include <QtQuick3DRuntimeRender/private/qssgrendermesh_p.h>
6#include <QtQuick/qquickwindow.h>
7#include <QtQuick/qquickitem.h>
8
9QT_BEGIN_NAMESPACE
10
11/*!
12 \qmltype RenderStats
13 \inqmlmodule QtQuick3D
14 \brief Provides information of the scene rendering.
15
16 The RenderStats type provides information about scene rendering statistics. This
17 cannot be created directly, but can be retrieved from a \l View3D.
18
19 Use the \l DebugView item to display the data on-screen.
20*/
21
22QQuick3DRenderStats::QQuick3DRenderStats(QObject *parent)
23 : QObject(parent)
24{
25 m_frameTimer.start();
26}
27
28/*!
29 \qmlproperty int QtQuick3D::RenderStats::fps
30 \readonly
31
32 This property holds the number of frames rendered during the last second.
33*/
34int QQuick3DRenderStats::fps() const
35{
36 return m_fps;
37}
38
39/*!
40 \qmlproperty float QtQuick3D::RenderStats::frameTime
41 \readonly
42
43 This property holds the amount of time elapsed since the last frame, in
44 milliseconds.
45*/
46float QQuick3DRenderStats::frameTime() const
47{
48 return m_results.frameTime;
49}
50
51/*!
52 \qmlproperty float QtQuick3D::RenderStats::renderTime
53 \readonly
54
55 This property holds the amount of time spent on generating a new frame,
56 including both the preparation phase and the recording of draw calls. The
57 value is in milliseconds.
58*/
59float QQuick3DRenderStats::renderTime() const
60{
61 return m_results.renderTime;
62}
63
64/*!
65 \qmlproperty float QtQuick3D::RenderStats::renderPrepareTime
66 \readonly
67
68 This property holds the amount of time spent in the preparation phase of
69 rendering, in milliseconds. This is a subset of the total render time
70 reported in renderTime.
71*/
72float QQuick3DRenderStats::renderPrepareTime() const
73{
74 return m_results.renderPrepareTime;
75}
76
77/*!
78 \qmlproperty float QtQuick3D::RenderStats::syncTime
79 \readonly
80
81 This property holds the amount of time spent inside the sync function, in
82 milliseconds. The property values of the objects are updated during the
83 sync.
84*/
85float QQuick3DRenderStats::syncTime() const
86{
87 return m_results.syncTime;
88}
89
90/*!
91 \qmlproperty float QtQuick3D::RenderStats::maxFrameTime
92 \readonly
93
94 This property holds the maximum time spent rendering a single frame during
95 the last second.
96*/
97float QQuick3DRenderStats::maxFrameTime() const
98{
99 return m_maxFrameTime;
100}
101
102float QQuick3DRenderStats::timestamp() const
103{
104 return m_frameTimer.nsecsElapsed() / 1000000.0f;
105}
106
107void QQuick3DRenderStats::startSync()
108{
109 m_syncStartTime = timestamp();
110}
111
112void QQuick3DRenderStats::endSync(bool dump)
113{
114 m_results.syncTime = timestamp() - m_syncStartTime;
115
116 if (dump)
117 qDebug(msg: "Sync took: %f ms", m_results.syncTime);
118}
119
120void QQuick3DRenderStats::startRender()
121{
122 m_renderStartTime = timestamp();
123}
124
125void QQuick3DRenderStats::startRenderPrepare()
126{
127 m_renderPrepareStartTime = timestamp();
128}
129
130void QQuick3DRenderStats::endRenderPrepare()
131{
132 m_results.renderPrepareTime = timestamp() - m_renderPrepareStartTime;
133}
134
135void QQuick3DRenderStats::endRender(bool dump)
136{
137 // Threading-wise this and onFrameSwapped are not perfect. These are called
138 // on the render thread (if there is one) outside of the sync step, so
139 // writing the data in m_results, which then may be read by the properties
140 // on the main thread concurrently, is not ideal. But at least the data the
141 // results are generated from (the m_* timings and all the stuff from
142 // QSSGRhiContextStats) belong to the render thread, so that's good.
143
144 m_renderingThisFrame = true;
145 const float endTime = timestamp();
146 m_results.renderTime = endTime - m_renderStartTime;
147
148 if (dump)
149 qDebug(msg: "Render took: %f ms (of which prep: %f ms)", m_results.renderTime, m_results.renderPrepareTime);
150}
151
152void QQuick3DRenderStats::onFrameSwapped()
153{
154 // NOTE: This is called on the render thread
155 // This is the real start and end of a frame
156
157 if (m_renderingThisFrame) {
158 ++m_frameCount;
159 m_results.frameTime = timestamp();
160 m_internalMaxFrameTime = qMax(a: m_results.frameTime, b: m_internalMaxFrameTime);
161
162 m_secTimer += m_results.frameTime;
163 m_notifyTimer += m_results.frameTime;
164
165 m_results.renderTime = m_results.frameTime - m_renderStartTime;
166
167 processRhiContextStats();
168
169 if (m_window) {
170 QRhiSwapChain *sc = m_window->swapChain();
171 if (sc) {
172 QRhiCommandBuffer *cb = sc->currentFrameCommandBuffer();
173 if (cb) {
174 const float msecs = float(cb->lastCompletedGpuTime() * 1000.0);
175 if (!qFuzzyIsNull(f: msecs))
176 m_results.lastCompletedGpuTime = msecs;
177 }
178 }
179 }
180
181 const float notifyInterval = 200.0f;
182 if (m_notifyTimer >= notifyInterval) {
183 m_notifyTimer -= notifyInterval;
184
185 if (m_results.frameTime != m_notifiedResults.frameTime) {
186 m_notifiedResults.frameTime = m_results.frameTime;
187 emit frameTimeChanged();
188 }
189
190 if (m_results.syncTime != m_notifiedResults.syncTime) {
191 m_notifiedResults.syncTime = m_results.syncTime;
192 emit syncTimeChanged();
193 }
194
195 if (m_results.renderTime != m_notifiedResults.renderTime) {
196 m_notifiedResults.renderTime = m_results.renderTime;
197 m_notifiedResults.renderPrepareTime = m_results.renderPrepareTime;
198 emit renderTimeChanged();
199 }
200
201 if (m_results.lastCompletedGpuTime != m_notifiedResults.lastCompletedGpuTime) {
202 m_notifiedResults.lastCompletedGpuTime = m_results.lastCompletedGpuTime;
203 emit lastCompletedGpuTimeChanged();
204 }
205
206 notifyRhiContextStats();
207 }
208
209 const float fpsInterval = 1000.0f;
210 if (m_secTimer >= fpsInterval) {
211 m_secTimer -= fpsInterval;
212
213 m_fps = m_frameCount;
214 m_frameCount = 0;
215 emit fpsChanged();
216
217 m_maxFrameTime = m_internalMaxFrameTime;
218 m_internalMaxFrameTime = 0;
219 emit maxFrameTimeChanged();
220 }
221
222 m_renderingThisFrame = false; // reset for next frame
223 }
224
225 // Always reset the frame timer
226 m_frameTimer.restart();
227}
228
229void QQuick3DRenderStats::setRhiContext(QSSGRhiContext *ctx, QSSGRenderLayer *layer)
230{
231 // called from synchronize(), so on the render thread with gui blocked
232
233 m_layer = layer;
234 m_contextStats = &ctx->stats();
235
236 // setExtendedDataCollectionEnabled will likely get called at some point
237 // before this (so too early), sync the flag here as well now that we know
238 // all we need to know.
239 if (m_extendedDataCollectionEnabled)
240 m_contextStats->dynamicDataSources.insert(value: layer);
241
242 if (m_contextStats && m_contextStats->context.rhi()) {
243 const QString backendName = QString::fromUtf8(utf8: m_contextStats->context.rhi()->backendName());
244 if (m_graphicsApiName != backendName) {
245 m_graphicsApiName = backendName;
246 emit graphicsApiNameChanged();
247 }
248 }
249}
250
251void QQuick3DRenderStats::setWindow(QQuickWindow *window)
252{
253 if (m_window == window)
254 return;
255
256 if (m_window)
257 disconnect(m_frameSwappedConnection);
258
259 m_window = window;
260
261 if (m_window) {
262 m_frameSwappedConnection = connect(sender: m_window, signal: &QQuickWindow::afterFrameEnd,
263 context: this, slot: &QQuick3DRenderStats::onFrameSwapped,
264 type: Qt::DirectConnection);
265 }
266}
267
268/*!
269 \qmlproperty bool QtQuick3D::RenderStats::extendedDataCollectionEnabled
270
271 This property controls if render pass and draw call statistics are
272 processed and made available. This may incur a small performance cost and
273 is therefore optional.
274
275 Properties such as drawCallCount, drawVertexCount, or renderPassCount are
276 updated only when this property is set to true.
277
278 The default value is false.
279
280 \note Changing the visibility of a \l DebugView associated with the \l
281 View3D automatically toggles the value based on the DebugView's
282 \l{QQuickItem::visible}{visible} property.
283
284 \since 6.5
285 */
286bool QQuick3DRenderStats::extendedDataCollectionEnabled() const
287{
288 return m_extendedDataCollectionEnabled;
289}
290
291void QQuick3DRenderStats::setExtendedDataCollectionEnabled(bool enable)
292{
293 if (enable != m_extendedDataCollectionEnabled) {
294 m_extendedDataCollectionEnabled = enable;
295 emit extendedDataCollectionEnabledChanged();
296 }
297 if (m_contextStats) {
298 // This is what allows recognizing that there is at least one DebugView
299 // that is visible and wants all the data, and also helps in not
300 // performing all the processing if the set is empty (because then we
301 // know that no DebugView wants to display the data)
302 if (m_extendedDataCollectionEnabled)
303 m_contextStats->dynamicDataSources.insert(value: m_layer);
304 else
305 m_contextStats->dynamicDataSources.remove(value: m_layer);
306 }
307}
308
309static const char *textureFormatStr(QRhiTexture::Format format)
310{
311 switch (format) {
312 case QRhiTexture::RGBA8:
313 return "RGBA8";
314 case QRhiTexture::BGRA8:
315 return "BGRA8";
316 case QRhiTexture::R8:
317 return "R8";
318 case QRhiTexture::RG8:
319 return "RG8";
320 case QRhiTexture::R16:
321 return "R16";
322 case QRhiTexture::RG16:
323 return "RG16";
324 case QRhiTexture::RED_OR_ALPHA8:
325 return "R8/A8";
326 case QRhiTexture::RGBA16F:
327 return "RGBA16F";
328 case QRhiTexture::RGBA32F:
329 return "RGBA32F";
330 case QRhiTexture::R16F:
331 return "R16F";
332 case QRhiTexture::R32F:
333 return "R32F";
334 case QRhiTexture::RGB10A2:
335 return "RGB10A2";
336 case QRhiTexture::D16:
337 return "D16";
338 case QRhiTexture::D24:
339 return "D24";
340 case QRhiTexture::D24S8:
341 return "D24S8";
342 case QRhiTexture::D32F:
343 return "D32F";
344 case QRhiTexture::BC1:
345 return "BC1";
346 case QRhiTexture::BC2:
347 return "BC2";
348 case QRhiTexture::BC3:
349 return "BC3";
350 case QRhiTexture::BC4:
351 return "BC4";
352 case QRhiTexture::BC5:
353 return "BC5";
354 case QRhiTexture::BC6H:
355 return "BC6H";
356 case QRhiTexture::BC7:
357 return "BC7";
358 case QRhiTexture::ETC2_RGB8:
359 return "ETC2_RGB8";
360 case QRhiTexture::ETC2_RGB8A1:
361 return "ETC2_RGB8A1";
362 case QRhiTexture::ETC2_RGBA8:
363 return "ETC2_RGBA8";
364 case QRhiTexture::ASTC_4x4:
365 return "ASTC_4x4";
366 case QRhiTexture::ASTC_5x4:
367 return "ASTC_5x4";
368 case QRhiTexture::ASTC_5x5:
369 return "ASTC_5x5";
370 case QRhiTexture::ASTC_6x5:
371 return "ASTC_6x5";
372 case QRhiTexture::ASTC_6x6:
373 return "ASTC_6x6";
374 case QRhiTexture::ASTC_8x5:
375 return "ASTC_8x5";
376 case QRhiTexture::ASTC_8x6:
377 return "ASTC_8x6";
378 case QRhiTexture::ASTC_8x8:
379 return "ASTC_8x8";
380 case QRhiTexture::ASTC_10x5:
381 return "ASTC_10x5";
382 case QRhiTexture::ASTC_10x6:
383 return "ASTC_10x6";
384 case QRhiTexture::ASTC_10x8:
385 return "ASTC_10x8";
386 case QRhiTexture::ASTC_10x10:
387 return "ASTC_10x10";
388 case QRhiTexture::ASTC_12x10:
389 return "ASTC_12x10";
390 case QRhiTexture::ASTC_12x12:
391 return "ASTC_12x12";
392 default:
393 break;
394 }
395 return "<unknown>";
396}
397
398static inline void printRenderPassDetails(QString *dst, const QSSGRhiContextStats::RenderPassInfo &rp)
399{
400 *dst += QString::asprintf(format: "| %s | %dx%d | %llu | %llu |\n",
401 rp.rtName.constData(),
402 rp.pixelSize.width(),
403 rp.pixelSize.height(),
404 QSSGRhiContextStats::totalVertexCountForPass(pass: rp),
405 QSSGRhiContextStats::totalDrawCallCountForPass(pass: rp));
406}
407
408static inline QByteArray nameForRenderMesh(const QSSGRenderMesh *mesh)
409{
410 if (!mesh->subsets.isEmpty()) {
411 auto buf = mesh->subsets[0].rhi.vertexBuffer;
412 if (buf)
413 return buf->buffer()->name();
414 }
415 return {};
416}
417
418void QQuick3DRenderStats::processRhiContextStats()
419{
420 if (!m_contextStats || !m_extendedDataCollectionEnabled)
421 return;
422
423 // the render pass list is per renderer, i.e. per View3D
424 const QSSGRhiContextStats::PerLayerInfo data = m_contextStats->perLayerInfo[m_layer];
425
426 // textures and meshes include all assets registered to the per-QQuickWindow QSSGRhiContext
427 const QSSGRhiContextStats::GlobalInfo globalData = m_contextStats->globalInfo;
428 const QSet<QRhiTexture *> textures = m_contextStats->context.registeredTextures();
429 const QSet<QSSGRenderMesh *> meshes = m_contextStats->context.registeredMeshes();
430 const QHash<QSSGGraphicsPipelineStateKey, QRhiGraphicsPipeline *> pipelines = m_contextStats->context.pipelines();
431
432 m_results.drawCallCount = 0;
433 m_results.drawVertexCount = 0;
434 for (const auto &pass : data.renderPasses) {
435 m_results.drawCallCount += QSSGRhiContextStats::totalDrawCallCountForPass(pass);
436 m_results.drawVertexCount += QSSGRhiContextStats::totalVertexCountForPass(pass);
437 }
438 m_results.drawCallCount += QSSGRhiContextStats::totalDrawCallCountForPass(pass: data.externalRenderPass);
439 m_results.drawVertexCount += QSSGRhiContextStats::totalVertexCountForPass(pass: data.externalRenderPass);
440
441 m_results.imageDataSize = globalData.imageDataSize;
442 m_results.meshDataSize = globalData.meshDataSize;
443
444 m_results.renderPassCount = data.renderPasses.size()
445 + (data.externalRenderPass.pixelSize.isEmpty() ? 0 : 1);
446
447 QString renderPassDetails = QLatin1String(R"(
448| Name | Size | Vertices | Draw calls |
449| ---- | ---- | -------- | ---------- |
450)");
451
452 if (!data.externalRenderPass.pixelSize.isEmpty())
453 printRenderPassDetails(dst: &renderPassDetails, rp: data.externalRenderPass);
454 for (const auto &pass : data.renderPasses) {
455 if (!pass.pixelSize.isEmpty())
456 printRenderPassDetails(dst: &renderPassDetails, rp: pass);
457 }
458 renderPassDetails += QString::asprintf(format: "\nGenerated from QSSGRenderLayer %p", m_layer);
459 m_results.renderPassDetails = renderPassDetails;
460
461 if (m_results.activeTextures != textures) {
462 m_results.activeTextures = textures;
463 QString texDetails = QLatin1String(R"(
464| Name | Size | Format | Mip | Flags |
465| ---- | ---- | ------ | --- | ----- |
466)");
467 QList<QRhiTexture *> textureList = textures.values();
468 std::sort(first: textureList.begin(), last: textureList.end(), comp: [](QRhiTexture *a, QRhiTexture *b) {
469 return a->name() < b->name();
470 });
471 for (QRhiTexture *tex : textureList) {
472 int mipCount = 1;
473 const QRhiTexture::Flags flags = tex->flags();
474 if (flags.testFlag(flag: QRhiTexture::MipMapped))
475 mipCount = m_contextStats->context.rhi()->mipLevelsForSize(size: tex->pixelSize());
476 QByteArray flagMsg;
477 if (flags.testFlag(flag: QRhiTexture::CubeMap))
478 flagMsg += QByteArrayLiteral("[cube]");
479 texDetails += QString::asprintf(format: "| %s | %dx%d | %s | %d | %s |\n",
480 tex->name().constData(),
481 tex->pixelSize().width(),
482 tex->pixelSize().height(),
483 textureFormatStr(format: tex->format()),
484 mipCount,
485 flagMsg.constData());
486 }
487 texDetails += QString::asprintf(format: "\nAsset textures registered with QSSGRhiContext %p", &m_contextStats->context);
488 m_results.textureDetails = texDetails;
489 }
490
491 if (m_results.activeMeshes != meshes) {
492 m_results.activeMeshes = meshes;
493 QString meshDetails = QLatin1String(R"(
494| Name | Submeshes | Vertices | V.buf size | I.buf size |
495| ---- | --------- | -------- | ---------- | ---------- |
496)");
497 QList<QSSGRenderMesh *> meshList = meshes.values();
498 std::sort(first: meshList.begin(), last: meshList.end(), comp: [](QSSGRenderMesh *a, QSSGRenderMesh *b) {
499 return nameForRenderMesh(mesh: a) < nameForRenderMesh(mesh: b);
500 });
501 for (QSSGRenderMesh *mesh : meshList) {
502 const QByteArray name = nameForRenderMesh(mesh);
503 const int subsetCount = int(mesh->subsets.size());
504 quint64 vertexCount = 0;
505 quint32 vbufSize = 0;
506 quint32 ibufSize = 0;
507 if (subsetCount > 0) {
508 for (const QSSGRenderSubset &subset : std::as_const(t&: mesh->subsets))
509 vertexCount += subset.count;
510 // submeshes ref into the same vertex and index buffer
511 const QSSGRhiBuffer *vbuf = mesh->subsets[0].rhi.vertexBuffer.get();
512 if (vbuf)
513 vbufSize = vbuf->buffer()->size();
514 const QSSGRhiBuffer *ibuf = mesh->subsets[0].rhi.indexBuffer.get();
515 if (ibuf)
516 ibufSize = ibuf->buffer()->size();
517 }
518 meshDetails += QString::asprintf(format: "| %s | %d | %llu | %u | %u |\n",
519 name.constData(),
520 subsetCount,
521 vertexCount,
522 vbufSize,
523 ibufSize);
524
525 }
526 meshDetails += QString::asprintf(format: "\nAsset meshes registered with QSSGRhiContext %p", &m_contextStats->context);
527 m_results.meshDetails = meshDetails;
528 }
529
530 m_results.pipelineCount = pipelines.count();
531
532 m_results.materialGenerationTime = m_contextStats->globalInfo.materialGenerationTime;
533 m_results.effectGenerationTime = m_contextStats->globalInfo.effectGenerationTime;
534
535 m_results.rhiStats = m_contextStats->context.rhi()->statistics();
536}
537
538void QQuick3DRenderStats::notifyRhiContextStats()
539{
540 if (!m_contextStats || !m_extendedDataCollectionEnabled)
541 return;
542
543 if (m_results.drawCallCount != m_notifiedResults.drawCallCount) {
544 m_notifiedResults.drawCallCount = m_results.drawCallCount;
545 emit drawCallCountChanged();
546 }
547
548 if (m_results.drawVertexCount != m_notifiedResults.drawVertexCount) {
549 m_notifiedResults.drawVertexCount = m_results.drawVertexCount;
550 emit drawVertexCountChanged();
551 }
552
553 if (m_results.imageDataSize != m_notifiedResults.imageDataSize) {
554 m_notifiedResults.imageDataSize = m_results.imageDataSize;
555 emit imageDataSizeChanged();
556 }
557
558 if (m_results.meshDataSize != m_notifiedResults.meshDataSize) {
559 m_notifiedResults.meshDataSize = m_results.meshDataSize;
560 emit meshDataSizeChanged();
561 }
562
563 if (m_results.renderPassCount != m_notifiedResults.renderPassCount) {
564 m_notifiedResults.renderPassCount = m_results.renderPassCount;
565 emit renderPassCountChanged();
566 }
567
568 if (m_results.renderPassDetails != m_notifiedResults.renderPassDetails) {
569 m_notifiedResults.renderPassDetails = m_results.renderPassDetails;
570 emit renderPassDetailsChanged();
571 }
572
573 if (m_results.textureDetails != m_notifiedResults.textureDetails) {
574 m_notifiedResults.textureDetails = m_results.textureDetails;
575 emit textureDetailsChanged();
576 }
577
578 if (m_results.meshDetails != m_notifiedResults.meshDetails) {
579 m_notifiedResults.meshDetails = m_results.meshDetails;
580 emit meshDetailsChanged();
581 }
582
583 if (m_results.pipelineCount != m_notifiedResults.pipelineCount) {
584 m_notifiedResults.pipelineCount = m_results.pipelineCount;
585 emit pipelineCountChanged();
586 }
587
588 if (m_results.materialGenerationTime != m_notifiedResults.materialGenerationTime) {
589 m_notifiedResults.materialGenerationTime = m_results.materialGenerationTime;
590 emit materialGenerationTimeChanged();
591 }
592
593 if (m_results.effectGenerationTime != m_notifiedResults.effectGenerationTime) {
594 m_notifiedResults.effectGenerationTime = m_results.effectGenerationTime;
595 emit effectGenerationTimeChanged();
596 }
597
598 if (m_results.rhiStats.totalPipelineCreationTime != m_notifiedResults.rhiStats.totalPipelineCreationTime) {
599 m_notifiedResults.rhiStats.totalPipelineCreationTime = m_results.rhiStats.totalPipelineCreationTime;
600 emit pipelineCreationTimeChanged();
601 }
602
603 if (m_results.rhiStats.allocCount != m_notifiedResults.rhiStats.allocCount) {
604 m_notifiedResults.rhiStats.allocCount = m_results.rhiStats.allocCount;
605 emit vmemAllocCountChanged();
606 }
607
608 if (m_results.rhiStats.usedBytes != m_notifiedResults.rhiStats.usedBytes) {
609 m_notifiedResults.rhiStats.usedBytes = m_results.rhiStats.usedBytes;
610 emit vmemUsedBytesChanged();
611 }
612}
613
614/*!
615 \qmlproperty quint64 QtQuick3D::RenderStats::drawCallCount
616 \readonly
617
618 This property holds the total number of draw calls (including non-indexed,
619 indexed, instanced, and instanced indexed draw calls) that were registered
620 during the last render of the \l View3D.
621
622 The value is updated only when extendedDataCollectionEnabled is enabled.
623
624 \since 6.5
625*/
626quint64 QQuick3DRenderStats::drawCallCount() const
627{
628 return m_results.drawCallCount;
629}
630
631/*!
632 \qmlproperty quint64 QtQuick3D::RenderStats::drawVertexCount
633 \readonly
634
635 This property holds the total number of vertices in draw calls that were
636 registered during the last render of the \l View3D.
637
638 The value includes the number of vertex and index count from draw calls
639 that were registered during the last render of the \l View3D. While the
640 number is not guaranteed to be totally accurate, it is expected to give a
641 good indication of the complexity of the scene rendering.
642
643 The value is updated only when extendedDataCollectionEnabled is enabled.
644
645 \since 6.5
646*/
647quint64 QQuick3DRenderStats::drawVertexCount() const
648{
649 return m_results.drawVertexCount;
650}
651
652/*!
653 \qmlproperty quint64 QtQuick3D::RenderStats::imageDataSize
654 \readonly
655
656 This property holds the approximate size in bytes of the image data for
657 texture maps currently registered with the View3D's window. The value is
658 per-window, meaning if there are multiple View3D objects within the same
659 QQuickWindow, those will likely report the same value.
660
661 The value is updated only when extendedDataCollectionEnabled is enabled.
662
663 \note The value is reported on a per-QQuickWindow basis. If there are
664 multiple View3D instances within the same window, the DebugView shows the
665 same value for all those View3Ds.
666
667 \since 6.5
668*/
669quint64 QQuick3DRenderStats::imageDataSize() const
670{
671 return m_results.imageDataSize;
672}
673
674/*!
675 \qmlproperty quint64 QtQuick3D::RenderStats::meshDataSize
676 \readonly
677
678 This property holds the approximate size in bytes of the mesh data
679 currently registered with the View3D's window. The value is per-window,
680 meaning if there are multiple View3D objects within the same QQuickWindow,
681 those will likely report the same value.
682
683 The value is updated only when extendedDataCollectionEnabled is enabled.
684
685 \note The value is reported on a per-QQuickWindow basis. If there are
686 multiple View3D instances within the same window, the DebugView shows the
687 same value for all those View3Ds.
688
689 \since 6.5
690*/
691quint64 QQuick3DRenderStats::meshDataSize() const
692{
693 return m_results.meshDataSize;
694}
695
696/*!
697 \qmlproperty int QtQuick3D::RenderStats::renderPassCount
698 \readonly
699
700 This property holds the total number of render passes that were registered
701 during the last render of the \l View3D.
702
703 Many features, such as realtime shadow mapping, postprocessing effects, the
704 depth and screen textures, and certain antialiasing methods involve
705 multiple additional render passes. While the number is not guaranteed to
706 include absolutely all render passes, it is expected to give a good
707 indication of the complexity of the scene rendering.
708
709 The value is updated only when extendedDataCollectionEnabled is enabled.
710
711 \since 6.5
712*/
713int QQuick3DRenderStats::renderPassCount() const
714{
715 return m_results.renderPassCount;
716}
717
718/*!
719 \qmlproperty string QtQuick3D::RenderStats::renderPassDetails
720 \readonly
721 \internal
722 \since 6.5
723*/
724QString QQuick3DRenderStats::renderPassDetails() const
725{
726 return m_results.renderPassDetails;
727}
728
729/*!
730 \qmlproperty string QtQuick3D::RenderStats::textureDetails
731 \readonly
732 \internal
733 \since 6.5
734*/
735QString QQuick3DRenderStats::textureDetails() const
736{
737 return m_results.textureDetails;
738}
739
740/*!
741 \qmlproperty string QtQuick3D::RenderStats::meshDetails
742 \readonly
743 \internal
744 \since 6.5
745*/
746QString QQuick3DRenderStats::meshDetails() const
747{
748 return m_results.meshDetails;
749}
750
751/*!
752 \qmlproperty int QtQuick3D::RenderStats::pipelineCount
753 \readonly
754
755 This property holds the total number of cached graphics pipelines for the
756 window the \l View3D belongs to.
757
758 The value is updated only when extendedDataCollectionEnabled is enabled.
759
760 \note The value is reported on a per-QQuickWindow basis. If there are
761 multiple View3D instances within the same window, the DebugView shows the
762 same value for all those View3Ds.
763
764 \since 6.5
765*/
766int QQuick3DRenderStats::pipelineCount() const
767{
768 return m_results.pipelineCount;
769}
770
771/*!
772 \qmlproperty qint64 QtQuick3D::RenderStats::materialGenerationTime
773 \readonly
774
775 This property holds the total number of milliseconds spent on generating
776 and processing shader code for \l DefaultMaterial, \l PrincipledMaterial,
777 and \l CustomMaterial in the window the \l View3D belongs to.
778
779 The value is updated only when extendedDataCollectionEnabled is enabled.
780
781 \note The value is reported on a per-QQuickWindow basis. If there are
782 multiple View3D instances within the same window, the DebugView shows the
783 same value for all those View3Ds.
784
785 \since 6.5
786*/
787qint64 QQuick3DRenderStats::materialGenerationTime() const
788{
789 return m_results.materialGenerationTime;
790}
791
792/*!
793 \qmlproperty qint64 QtQuick3D::RenderStats::effectGenerationTime
794 \readonly
795
796 This property holds the total number of milliseconds spent on generating
797 and processing shader code for post-processing effects in the window the \l
798 View3D belongs to.
799
800 The value is updated only when extendedDataCollectionEnabled is enabled.
801
802 \note The value is reported on a per-QQuickWindow basis. If there are
803 multiple View3D instances within the same window, the DebugView shows the
804 same value for all those View3Ds.
805
806 \since 6.5
807*/
808qint64 QQuick3DRenderStats::effectGenerationTime() const
809{
810 return m_results.effectGenerationTime;
811}
812
813/*!
814 \qmlproperty qint64 QtQuick3D::RenderStats::pipelineCreationTime
815 \readonly
816
817 This property holds the total number of milliseconds spent on creating
818 graphics pipelines on the rendering hardware interface level. This can
819 include, among other things: compilation times for compiling HLSL to an
820 intermediate format, compiling MSL, compiling GLSL code with
821 glCompileShader or linking using program binaries, and generating Vulkan
822 pipelines with all that entails (e.g. SPIR-V -> ISA compilation). The value
823 reflects all Qt Quick and Qt Quick 3D rendering in the window the \l View3D
824 belongs to.
825
826 \note The value includes operations that are under Qt's control. Depending
827 on the underlying graphics API, some pipeline (shader, graphics state)
828 related operations may happen asynchronously, and may be affected by
829 caching on various levels in the graphics stack. Releasing cached resource
830 by calling QQuickWindow::releaseResources() or clicking the corresponding
831 DebugView button may also have varying results, depending on the underlying
832 details (rhi backend, graphics API); it may or may not affect this counter
833 due to a varying number of factors.
834
835 This timing is provided as a general, high level indication. Combined with
836 \l materialGenerationTime, application developers can use these values to
837 confirm that the time spent on material and graphics pipeline processing is
838 reasonably low during the normal use of the application, once all caches
839 (both persistent and in-memory) are warm. Avoid drawing conclusions from
840 the first run of the application. (since that may not benefit from
841 persistent, disk-based caches yet)
842
843 The value is updated only when extendedDataCollectionEnabled is enabled.
844
845 \note The value is reported on a per-QQuickWindow basis. If there are
846 multiple View3D instances within the same window, the DebugView shows the
847 same value for all those View3Ds.
848
849 \since 6.5
850*/
851qint64 QQuick3DRenderStats::pipelineCreationTime() const
852{
853 return m_results.rhiStats.totalPipelineCreationTime;
854}
855
856/*!
857 \qmlproperty quint32 QtQuick3D::RenderStats::vmemAllocCount
858 \readonly
859
860 When applicable, the number of allocations made by the graphics memory
861 allocator library. This includes allocations from all Qt Quick and Qt Quick
862 3D rendering in the QQuickWindow to which the \l View3D belongs. The value
863 is zero with graphics APIs such as OpenGL, Direct3D, and Metal because
864 memory allocation is not under Qt's control then.
865
866 The value is updated only when extendedDataCollectionEnabled is enabled.
867
868 \note The value is reported on a per-QQuickWindow basis. If there are
869 multiple View3D instances within the same window, the DebugView shows the
870 same value for all those View3Ds.
871
872 \since 6.5
873*/
874quint32 QQuick3DRenderStats::vmemAllocCount() const
875{
876 return m_results.rhiStats.allocCount;
877}
878
879/*!
880 \qmlproperty quint64 QtQuick3D::RenderStats::vmemUsedBytes
881 \readonly
882
883 When applicable, the number of bytes used by allocations made by the
884 graphics memory allocator library. This includes allocations from all Qt
885 Quick and Qt Quick 3D rendering in the QQuickWindow to which the \l View3D
886 belongs. The value is zero with graphics APIs such as OpenGL, Direct3D, and
887 Metal because memory allocation is not under Qt's control then.
888
889 The value is updated only when extendedDataCollectionEnabled is enabled.
890
891 \note The value is reported on a per-QQuickWindow basis. If there are
892 multiple View3D instances within the same window, the DebugView shows the
893 same value for all those View3Ds.
894
895 \since 6.5
896*/
897quint64 QQuick3DRenderStats::vmemUsedBytes() const
898{
899 return m_results.rhiStats.usedBytes;
900}
901
902/*!
903 \qmlproperty string QtQuick3D::RenderStats::graphicsAPIName
904 \readonly
905
906 This property holds the name of the current graphics API (RHI) backend
907 currently in use.
908
909 \since 6.5
910*/
911QString QQuick3DRenderStats::graphicsApiName() const
912{
913 return m_graphicsApiName;
914}
915
916/*!
917 \qmlproperty float QtQuick3D::RenderStats::lastCompletedGpuTime
918 \readonly
919
920 When GPU timing collection is
921 \l{QQuickGraphicsConfiguration::setTimestamps()}{enabled in Qt Quick}, and
922 the relevant features are supported by the underlying graphics API, this
923 property contains the last retrieved elapsed GPU time in milliseconds.
924
925 \note The value is retrieved asynchronously, and usually refers to a frame
926 older than the previous one, meaning that the value is not necessarily in
927 sync with the other, CPU-side timings.
928
929 \note The result is based on the rendering of the entire contents of the
930 QQuickWindow the View3D belongs to. It includes all the contents of Qt
931 Quick scene, including all 2D elements and all View3D items within that
932 window.
933
934 \since 6.6
935
936 \sa QQuickGraphicsConfiguration::setTimestamps()
937*/
938float QQuick3DRenderStats::lastCompletedGpuTime() const
939{
940 return m_results.lastCompletedGpuTime;
941}
942
943/*!
944 \internal
945 */
946void QQuick3DRenderStats::releaseCachedResources()
947{
948 if (m_window)
949 m_window->releaseResources();
950 else
951 qWarning(msg: "QQuick3DRenderStats: No window, cannot request releasing cached resources");
952}
953
954QT_END_NAMESPACE
955

source code of qtquick3d/src/quick3d/qquick3drenderstats.cpp