7#include <QtQuick3DRuntimeRender/private/qssgrendermesh_p.h>
8#include <QtQuick/qquickwindow.h>
9#include <QtQuick/qquickitem.h>
14
15
16
17
18
19
20
21
22
24QQuick3DRenderStats::QQuick3DRenderStats(QObject *parent)
31
32
33
34
35
36int QQuick3DRenderStats::fps()
const
42
43
44
45
46
47
48float QQuick3DRenderStats::frameTime()
const
50 return m_results.frameTime;
54
55
56
57
58
59
60
61float QQuick3DRenderStats::renderTime()
const
63 return m_results.renderTime;
67
68
69
70
71
72
73
74float QQuick3DRenderStats::renderPrepareTime()
const
76 return m_results.renderPrepareTime;
80
81
82
83
84
85
86
87float QQuick3DRenderStats::syncTime()
const
89 return m_results.syncTime;
93
94
95
96
97
98
99float QQuick3DRenderStats::maxFrameTime()
const
101 return m_maxFrameTime;
104float QQuick3DRenderStats::timestamp()
const
106 return m_frameTimer.nsecsElapsed() / 1000000.0f;
109void QQuick3DRenderStats::startSync()
111 m_syncStartTime = timestamp();
114void QQuick3DRenderStats::endSync(
bool dump)
116 m_results.syncTime = timestamp() - m_syncStartTime;
119 qDebug(
"Sync took: %f ms", m_results.syncTime);
122void QQuick3DRenderStats::startRender()
124 m_renderStartTime = timestamp();
127void QQuick3DRenderStats::startRenderPrepare()
129 m_renderPrepareStartTime = timestamp();
132void QQuick3DRenderStats::endRenderPrepare()
134 m_results.renderPrepareTime = timestamp() - m_renderPrepareStartTime;
137void QQuick3DRenderStats::endRender(
bool dump)
146 m_renderingThisFrame =
true;
147 const float endTime = timestamp();
148 m_results.renderTime = endTime - m_renderStartTime;
151 qDebug(
"Render took: %f ms (of which prep: %f ms)", m_results.renderTime, m_results.renderPrepareTime);
154void QQuick3DRenderStats::onFrameSwapped()
159 if (m_renderingThisFrame) {
161 m_results.frameTime = timestamp();
162 m_internalMaxFrameTime = qMax(m_results.frameTime, m_internalMaxFrameTime);
164 m_secTimer += m_results.frameTime;
165 m_notifyTimer += m_results.frameTime;
167 m_results.renderTime = m_results.frameTime - m_renderStartTime;
169 processRhiContextStats();
172 QRhiSwapChain *sc = m_window->swapChain();
174 QRhiCommandBuffer *cb = sc->currentFrameCommandBuffer();
176 const float msecs =
float(cb->lastCompletedGpuTime() * 1000.0);
177 if (!qFuzzyIsNull(msecs))
178 m_results.lastCompletedGpuTime = msecs;
183 const float notifyInterval = 200.0f;
184 if (m_notifyTimer >= notifyInterval) {
185 m_notifyTimer -= notifyInterval;
187 if (m_results.frameTime != m_notifiedResults.frameTime) {
188 m_notifiedResults.frameTime = m_results.frameTime;
189 emit frameTimeChanged();
192 if (m_results.syncTime != m_notifiedResults.syncTime) {
193 m_notifiedResults.syncTime = m_results.syncTime;
194 emit syncTimeChanged();
197 if (m_results.renderTime != m_notifiedResults.renderTime) {
198 m_notifiedResults.renderTime = m_results.renderTime;
199 m_notifiedResults.renderPrepareTime = m_results.renderPrepareTime;
200 emit renderTimeChanged();
203 if (m_results.lastCompletedGpuTime != m_notifiedResults.lastCompletedGpuTime) {
204 m_notifiedResults.lastCompletedGpuTime = m_results.lastCompletedGpuTime;
205 emit lastCompletedGpuTimeChanged();
208 notifyRhiContextStats();
211 const float fpsInterval = 1000.0f;
212 if (m_secTimer >= fpsInterval) {
213 m_secTimer -= fpsInterval;
215 m_fps = m_frameCount;
219 m_maxFrameTime = m_internalMaxFrameTime;
220 m_internalMaxFrameTime = 0;
221 emit maxFrameTimeChanged();
224 m_renderingThisFrame =
false;
228 m_frameTimer.start();
231void QQuick3DRenderStats::setRhiContext(QSSGRhiContext *ctx, QSSGRenderLayer *layer)
236 m_contextStats = &QSSGRhiContextStats::get(*ctx);
241 if (m_extendedDataCollectionEnabled)
242 m_contextStats->dynamicDataSources.insert(layer);
244 if (m_contextStats && m_contextStats->rhiCtx->rhi()) {
245 const QString backendName = QString::fromUtf8(m_contextStats->rhiCtx->rhi()->backendName());
246 if (m_graphicsApiName != backendName) {
247 m_graphicsApiName = backendName;
248 emit graphicsApiNameChanged();
253void QQuick3DRenderStats::setWindow(QQuickWindow *window)
255 if (m_window == window)
259 disconnect(m_frameSwappedConnection);
264 m_frameSwappedConnection = connect(m_window, &QQuickWindow::afterFrameEnd,
265 this, &QQuick3DRenderStats::onFrameSwapped,
266 Qt::DirectConnection);
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288bool QQuick3DRenderStats::extendedDataCollectionEnabled()
const
290 return m_extendedDataCollectionEnabled;
293void QQuick3DRenderStats::setExtendedDataCollectionEnabled(
bool enable)
295 if (enable != m_extendedDataCollectionEnabled) {
296 m_extendedDataCollectionEnabled = enable;
297 emit extendedDataCollectionEnabledChanged();
299 if (m_contextStats) {
304 if (m_extendedDataCollectionEnabled)
305 m_contextStats->dynamicDataSources.insert(m_layer);
307 m_contextStats->dynamicDataSources.remove(m_layer);
314 case QRhiTexture::RGBA8:
316 case QRhiTexture::BGRA8:
318 case QRhiTexture::R8:
320 case QRhiTexture::RG8:
322 case QRhiTexture::R16:
324 case QRhiTexture::RG16:
326 case QRhiTexture::RED_OR_ALPHA8:
328 case QRhiTexture::RGBA16F:
330 case QRhiTexture::RGBA32F:
332 case QRhiTexture::R16F:
334 case QRhiTexture::R32F:
336 case QRhiTexture::RGB10A2:
338 case QRhiTexture::D16:
340 case QRhiTexture::D24:
342 case QRhiTexture::D24S8:
344 case QRhiTexture::D32F:
346 case QRhiTexture::BC1:
348 case QRhiTexture::BC2:
350 case QRhiTexture::BC3:
352 case QRhiTexture::BC4:
354 case QRhiTexture::BC5:
356 case QRhiTexture::BC6H:
358 case QRhiTexture::BC7:
360 case QRhiTexture::ETC2_RGB8:
362 case QRhiTexture::ETC2_RGB8A1:
363 return "ETC2_RGB8A1";
364 case QRhiTexture::ETC2_RGBA8:
366 case QRhiTexture::ASTC_4x4:
368 case QRhiTexture::ASTC_5x4:
370 case QRhiTexture::ASTC_5x5:
372 case QRhiTexture::ASTC_6x5:
374 case QRhiTexture::ASTC_6x6:
376 case QRhiTexture::ASTC_8x5:
378 case QRhiTexture::ASTC_8x6:
380 case QRhiTexture::ASTC_8x8:
382 case QRhiTexture::ASTC_10x5:
384 case QRhiTexture::ASTC_10x6:
386 case QRhiTexture::ASTC_10x8:
388 case QRhiTexture::ASTC_10x10:
390 case QRhiTexture::ASTC_12x10:
392 case QRhiTexture::ASTC_12x12:
402 *dst += QString::asprintf(
"| %s | %dx%d | %llu | %llu |\n",
403 rp.rtName.constData(),
404 rp.pixelSize.width(),
405 rp.pixelSize.height(),
406 QSSGRhiContextStats::totalVertexCountForPass(rp),
407 QSSGRhiContextStats::totalDrawCallCountForPass(rp));
412 if (!mesh->subsets.isEmpty()) {
413 auto buf = mesh->subsets[0].rhi.vertexBuffer;
415 return buf->buffer()->name();
420void QQuick3DRenderStats::processRhiContextStats()
422 if (!m_contextStats || !m_extendedDataCollectionEnabled)
426 const QSSGRhiContextStats::PerLayerInfo data = m_contextStats->perLayerInfo[m_layer];
428 const QSSGRhiContext *rhiCtx = m_contextStats->rhiCtx;
429 const QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx);
432 const QSSGRhiContextStats::GlobalInfo globalData = m_contextStats->globalInfo;
433 const auto textures = rhiCtxD->m_textures;
434 const auto meshes = rhiCtxD->m_meshes;
435 const auto pipelines = rhiCtxD->m_pipelines;
437 m_results.drawCallCount = 0;
438 m_results.drawVertexCount = 0;
439 for (
const auto &pass : data.renderPasses) {
440 m_results.drawCallCount += QSSGRhiContextStats::totalDrawCallCountForPass(pass);
441 m_results.drawVertexCount += QSSGRhiContextStats::totalVertexCountForPass(pass);
443 m_results.drawCallCount += QSSGRhiContextStats::totalDrawCallCountForPass(data.externalRenderPass);
444 m_results.drawVertexCount += QSSGRhiContextStats::totalVertexCountForPass(data.externalRenderPass);
446 m_results.imageDataSize = globalData.imageDataSize;
447 m_results.meshDataSize = globalData.meshDataSize;
449 m_results.renderPassCount = data.renderPasses.size()
450 + (data.externalRenderPass.pixelSize.isEmpty() ? 0 : 1);
452 QString renderPassDetails = QLatin1String(R"(
453| Name | Size | Vertices | Draw calls |
454| ---- | ---- | -------- | ---------- |
455)");
457 if (!data.externalRenderPass.pixelSize.isEmpty())
458 printRenderPassDetails(&renderPassDetails, data.externalRenderPass);
459 for (
const auto &pass : data.renderPasses) {
460 if (!pass.pixelSize.isEmpty())
461 printRenderPassDetails(&renderPassDetails, pass);
463 renderPassDetails += QString::asprintf(
"\nGenerated from QSSGRenderLayer %p", m_layer);
464 m_results.renderPassDetails = renderPassDetails;
466 if (m_results.activeTextures != textures) {
467 m_results.activeTextures = textures;
468 QString texDetails = QLatin1String(R"(
469| Name | Size | Format | Mip | Flags |
470| ---- | ---- | ------ | --- | ----- |
471)");
472 QList<QRhiTexture *> textureList = textures.values();
473 std::sort(textureList.begin(), textureList.end(), [](QRhiTexture *a, QRhiTexture *b) {
474 return a->name() < b->name();
476 for (QRhiTexture *tex : textureList) {
478 const QRhiTexture::Flags flags = tex->flags();
479 if (flags.testFlag(QRhiTexture::MipMapped))
480 mipCount = m_contextStats->rhiCtx->rhi()->mipLevelsForSize(tex->pixelSize());
482 if (flags.testFlag(QRhiTexture::CubeMap))
483 flagMsg += QByteArrayLiteral(
"[cube]");
484 texDetails += QString::asprintf(
"| %s | %dx%d | %s | %d | %s |\n",
485 tex->name().constData(),
486 tex->pixelSize().width(),
487 tex->pixelSize().height(),
488 textureFormatStr(tex->format()),
490 flagMsg.constData());
492 texDetails += QString::asprintf(
"\nAsset textures registered with QSSGRhiContext %p", m_contextStats->rhiCtx);
493 m_results.textureDetails = texDetails;
496 if (m_results.activeMeshes != meshes) {
497 m_results.activeMeshes = meshes;
498 QString meshDetails = QLatin1String(R"(
499| Name | Submeshes | Vertices | V.buf size | I.buf size |
500| ---- | --------- | -------- | ---------- | ---------- |
501)");
502 QList<QSSGRenderMesh *> meshList = meshes.values();
503 std::sort(meshList.begin(), meshList.end(), [](QSSGRenderMesh *a, QSSGRenderMesh *b) {
504 return nameForRenderMesh(a) < nameForRenderMesh(b);
506 for (QSSGRenderMesh *mesh : meshList) {
507 const QByteArray name = nameForRenderMesh(mesh);
508 const int subsetCount =
int(mesh->subsets.size());
509 quint64 vertexCount = 0;
510 quint32 vbufSize = 0;
511 quint32 ibufSize = 0;
512 if (subsetCount > 0) {
513 for (
const QSSGRenderSubset &subset : std::as_const(mesh->subsets))
514 vertexCount += subset.count;
516 const QSSGRhiBuffer *vbuf = mesh->subsets[0].rhi.vertexBuffer.get();
518 vbufSize = vbuf->buffer()->size();
519 const QSSGRhiBuffer *ibuf = mesh->subsets[0].rhi.indexBuffer.get();
521 ibufSize = ibuf->buffer()->size();
523 meshDetails += QString::asprintf(
"| %s | %d | %llu | %u | %u |\n",
531 meshDetails += QString::asprintf(
"\nAsset meshes registered with QSSGRhiContext %p", m_contextStats->rhiCtx);
532 m_results.meshDetails = meshDetails;
535 m_results.pipelineCount = pipelines.count();
537 m_results.materialGenerationTime = m_contextStats->globalInfo.materialGenerationTime;
538 m_results.effectGenerationTime = m_contextStats->globalInfo.effectGenerationTime;
540 m_results.rhiStats = m_contextStats->rhiCtx->rhi()->statistics();
543void QQuick3DRenderStats::notifyRhiContextStats()
545 if (!m_contextStats || !m_extendedDataCollectionEnabled)
548 if (m_results.drawCallCount != m_notifiedResults.drawCallCount) {
549 m_notifiedResults.drawCallCount = m_results.drawCallCount;
550 emit drawCallCountChanged();
553 if (m_results.drawVertexCount != m_notifiedResults.drawVertexCount) {
554 m_notifiedResults.drawVertexCount = m_results.drawVertexCount;
555 emit drawVertexCountChanged();
558 if (m_results.imageDataSize != m_notifiedResults.imageDataSize) {
559 m_notifiedResults.imageDataSize = m_results.imageDataSize;
560 emit imageDataSizeChanged();
563 if (m_results.meshDataSize != m_notifiedResults.meshDataSize) {
564 m_notifiedResults.meshDataSize = m_results.meshDataSize;
565 emit meshDataSizeChanged();
568 if (m_results.renderPassCount != m_notifiedResults.renderPassCount) {
569 m_notifiedResults.renderPassCount = m_results.renderPassCount;
570 emit renderPassCountChanged();
573 if (m_results.renderPassDetails != m_notifiedResults.renderPassDetails) {
574 m_notifiedResults.renderPassDetails = m_results.renderPassDetails;
575 emit renderPassDetailsChanged();
578 if (m_results.textureDetails != m_notifiedResults.textureDetails) {
579 m_notifiedResults.textureDetails = m_results.textureDetails;
580 emit textureDetailsChanged();
583 if (m_results.meshDetails != m_notifiedResults.meshDetails) {
584 m_notifiedResults.meshDetails = m_results.meshDetails;
585 emit meshDetailsChanged();
588 if (m_results.pipelineCount != m_notifiedResults.pipelineCount) {
589 m_notifiedResults.pipelineCount = m_results.pipelineCount;
590 emit pipelineCountChanged();
593 if (m_results.materialGenerationTime != m_notifiedResults.materialGenerationTime) {
594 m_notifiedResults.materialGenerationTime = m_results.materialGenerationTime;
595 emit materialGenerationTimeChanged();
598 if (m_results.effectGenerationTime != m_notifiedResults.effectGenerationTime) {
599 m_notifiedResults.effectGenerationTime = m_results.effectGenerationTime;
600 emit effectGenerationTimeChanged();
603 if (m_results.rhiStats.totalPipelineCreationTime != m_notifiedResults.rhiStats.totalPipelineCreationTime) {
604 m_notifiedResults.rhiStats.totalPipelineCreationTime = m_results.rhiStats.totalPipelineCreationTime;
605 emit pipelineCreationTimeChanged();
608 if (m_results.rhiStats.allocCount != m_notifiedResults.rhiStats.allocCount) {
609 m_notifiedResults.rhiStats.allocCount = m_results.rhiStats.allocCount;
610 emit vmemAllocCountChanged();
613 if (m_results.rhiStats.usedBytes != m_notifiedResults.rhiStats.usedBytes) {
614 m_notifiedResults.rhiStats.usedBytes = m_results.rhiStats.usedBytes;
615 emit vmemUsedBytesChanged();
620
621
626
627
628
629
630
631quint64 QQuick3DRenderStats::drawCallCount()
const
633 return m_results.drawCallCount;
637
638
639
640
641
642
647
648
649
650
651
652quint64 QQuick3DRenderStats::drawVertexCount()
const
654 return m_results.drawVertexCount;
658
659
660
661
662
663
664
669
670
671
672
673
674quint64 QQuick3DRenderStats::imageDataSize()
const
676 return m_results.imageDataSize;
680
681
682
683
684
685
686
691
692
693
694
695
696quint64 QQuick3DRenderStats::meshDataSize()
const
698 return m_results.meshDataSize;
702
703
704
705
706
707
708
713
714
715
716
717
718int QQuick3DRenderStats::renderPassCount()
const
720 return m_results.renderPassCount;
724
725
726
727
728
729QString QQuick3DRenderStats::renderPassDetails()
const
731 return m_results.renderPassDetails;
735
736
737
738
739
740QString QQuick3DRenderStats::textureDetails()
const
742 return m_results.textureDetails;
746
747
748
749
750
751QString QQuick3DRenderStats::meshDetails()
const
753 return m_results.meshDetails;
757
758
759
760
761
766
767
768
769
770
771int QQuick3DRenderStats::pipelineCount()
const
773 return m_results.pipelineCount;
777
778
779
780
781
782
787
788
789
790
791
792qint64 QQuick3DRenderStats::materialGenerationTime()
const
794 return m_results.materialGenerationTime;
798
799
800
801
802
803
808
809
810
811
812
813qint64 QQuick3DRenderStats::effectGenerationTime()
const
815 return m_results.effectGenerationTime;
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
851
852
853
854
855
856qint64 QQuick3DRenderStats::pipelineCreationTime()
const
858 return m_results.rhiStats.totalPipelineCreationTime;
862
863
864
865
866
867
868
869
874
875
876
877
878
879quint32 QQuick3DRenderStats::vmemAllocCount()
const
881 return m_results.rhiStats.allocCount;
885
886
887
888
889
890
891
892
897
898
899
900
901
902quint64 QQuick3DRenderStats::vmemUsedBytes()
const
904 return m_results.rhiStats.usedBytes;
911
912
913
914
915
916QString QQuick3DRenderStats::graphicsApiName()
const
918 return m_graphicsApiName;
922
923
924
925
926
927
928
929
930
931
932
933
938
939
940
941
943float QQuick3DRenderStats::lastCompletedGpuTime()
const
945 return m_results.lastCompletedGpuTime;
949
950
951void QQuick3DRenderStats::releaseCachedResources()
954 m_window->releaseResources();
956 qWarning(
"QQuick3DRenderStats: No window, cannot request releasing cached resources");
static QByteArray nameForRenderMesh(const QSSGRenderMesh *mesh)
static void printRenderPassDetails(QString *dst, const QSSGRhiContextStats::RenderPassInfo &rp)
static const char * textureFormatStr(QRhiTexture::Format format)