5#include <QtQuick3DRuntimeRender/private/qssgrendermesh_p.h>
6#include <QtQuick/qquickwindow.h>
7#include <QtQuick/qquickitem.h>
12
13
14
15
16
17
18
19
20
22QQuick3DRenderStats::QQuick3DRenderStats(QObject *parent)
29
30
31
32
33
34int QQuick3DRenderStats::fps()
const
40
41
42
43
44
45
46float QQuick3DRenderStats::frameTime()
const
48 return m_results.frameTime;
52
53
54
55
56
57
58
59float QQuick3DRenderStats::renderTime()
const
61 return m_results.renderTime;
65
66
67
68
69
70
71
72float QQuick3DRenderStats::renderPrepareTime()
const
74 return m_results.renderPrepareTime;
78
79
80
81
82
83
84
85float QQuick3DRenderStats::syncTime()
const
87 return m_results.syncTime;
91
92
93
94
95
96
97float QQuick3DRenderStats::maxFrameTime()
const
99 return m_maxFrameTime;
102float QQuick3DRenderStats::timestamp()
const
104 return m_frameTimer.nsecsElapsed() / 1000000.0f;
107void QQuick3DRenderStats::startSync()
109 m_syncStartTime = timestamp();
112void QQuick3DRenderStats::endSync(
bool dump)
114 m_results.syncTime = timestamp() - m_syncStartTime;
117 qDebug(
"Sync took: %f ms", m_results.syncTime);
120void QQuick3DRenderStats::startRender()
122 m_renderStartTime = timestamp();
125void QQuick3DRenderStats::startRenderPrepare()
127 m_renderPrepareStartTime = timestamp();
130void QQuick3DRenderStats::endRenderPrepare()
132 m_results.renderPrepareTime = timestamp() - m_renderPrepareStartTime;
135void QQuick3DRenderStats::endRender(
bool dump)
144 m_renderingThisFrame =
true;
145 const float endTime = timestamp();
146 m_results.renderTime = endTime - m_renderStartTime;
149 qDebug(
"Render took: %f ms (of which prep: %f ms)", m_results.renderTime, m_results.renderPrepareTime);
152void QQuick3DRenderStats::onFrameSwapped()
157 if (m_renderingThisFrame) {
159 m_results.frameTime = timestamp();
160 m_internalMaxFrameTime = qMax(m_results.frameTime, m_internalMaxFrameTime);
162 m_secTimer += m_results.frameTime;
163 m_notifyTimer += m_results.frameTime;
165 m_results.renderTime = m_results.frameTime - m_renderStartTime;
167 processRhiContextStats();
170 QRhiSwapChain *sc = m_window->swapChain();
172 QRhiCommandBuffer *cb = sc->currentFrameCommandBuffer();
174 const float msecs =
float(cb->lastCompletedGpuTime() * 1000.0);
175 if (!qFuzzyIsNull(msecs))
176 m_results.lastCompletedGpuTime = msecs;
181 const float notifyInterval = 200.0f;
182 if (m_notifyTimer >= notifyInterval) {
183 m_notifyTimer -= notifyInterval;
185 if (m_results.frameTime != m_notifiedResults.frameTime) {
186 m_notifiedResults.frameTime = m_results.frameTime;
187 emit frameTimeChanged();
190 if (m_results.syncTime != m_notifiedResults.syncTime) {
191 m_notifiedResults.syncTime = m_results.syncTime;
192 emit syncTimeChanged();
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();
201 if (m_results.lastCompletedGpuTime != m_notifiedResults.lastCompletedGpuTime) {
202 m_notifiedResults.lastCompletedGpuTime = m_results.lastCompletedGpuTime;
203 emit lastCompletedGpuTimeChanged();
206 notifyRhiContextStats();
209 const float fpsInterval = 1000.0f;
210 if (m_secTimer >= fpsInterval) {
211 m_secTimer -= fpsInterval;
213 m_fps = m_frameCount;
217 m_maxFrameTime = m_internalMaxFrameTime;
218 m_internalMaxFrameTime = 0;
219 emit maxFrameTimeChanged();
222 m_renderingThisFrame =
false;
226 m_frameTimer.start();
229void QQuick3DRenderStats::setRhiContext(QSSGRhiContext *ctx, QSSGRenderLayer *layer)
234 m_contextStats = &QSSGRhiContextStats::get(*ctx);
239 if (m_extendedDataCollectionEnabled)
240 m_contextStats->dynamicDataSources.insert(layer);
242 if (m_contextStats && m_contextStats->rhiCtx->rhi()) {
243 const QString backendName = QString::fromUtf8(m_contextStats->rhiCtx->rhi()->backendName());
244 if (m_graphicsApiName != backendName) {
245 m_graphicsApiName = backendName;
246 emit graphicsApiNameChanged();
251void QQuick3DRenderStats::setWindow(QQuickWindow *window)
253 if (m_window == window)
257 disconnect(m_frameSwappedConnection);
262 m_frameSwappedConnection = connect(m_window, &QQuickWindow::afterFrameEnd,
263 this, &QQuick3DRenderStats::onFrameSwapped,
264 Qt::DirectConnection);
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286bool QQuick3DRenderStats::extendedDataCollectionEnabled()
const
288 return m_extendedDataCollectionEnabled;
291void QQuick3DRenderStats::setExtendedDataCollectionEnabled(
bool enable)
293 if (enable != m_extendedDataCollectionEnabled) {
294 m_extendedDataCollectionEnabled = enable;
295 emit extendedDataCollectionEnabledChanged();
297 if (m_contextStats) {
302 if (m_extendedDataCollectionEnabled)
303 m_contextStats->dynamicDataSources.insert(m_layer);
305 m_contextStats->dynamicDataSources.remove(m_layer);
312 case QRhiTexture::RGBA8:
314 case QRhiTexture::BGRA8:
316 case QRhiTexture::R8:
318 case QRhiTexture::RG8:
320 case QRhiTexture::R16:
322 case QRhiTexture::RG16:
324 case QRhiTexture::RED_OR_ALPHA8:
326 case QRhiTexture::RGBA16F:
328 case QRhiTexture::RGBA32F:
330 case QRhiTexture::R16F:
332 case QRhiTexture::R32F:
334 case QRhiTexture::RGB10A2:
336 case QRhiTexture::D16:
338 case QRhiTexture::D24:
340 case QRhiTexture::D24S8:
342 case QRhiTexture::D32F:
344 case QRhiTexture::BC1:
346 case QRhiTexture::BC2:
348 case QRhiTexture::BC3:
350 case QRhiTexture::BC4:
352 case QRhiTexture::BC5:
354 case QRhiTexture::BC6H:
356 case QRhiTexture::BC7:
358 case QRhiTexture::ETC2_RGB8:
360 case QRhiTexture::ETC2_RGB8A1:
361 return "ETC2_RGB8A1";
362 case QRhiTexture::ETC2_RGBA8:
364 case QRhiTexture::ASTC_4x4:
366 case QRhiTexture::ASTC_5x4:
368 case QRhiTexture::ASTC_5x5:
370 case QRhiTexture::ASTC_6x5:
372 case QRhiTexture::ASTC_6x6:
374 case QRhiTexture::ASTC_8x5:
376 case QRhiTexture::ASTC_8x6:
378 case QRhiTexture::ASTC_8x8:
380 case QRhiTexture::ASTC_10x5:
382 case QRhiTexture::ASTC_10x6:
384 case QRhiTexture::ASTC_10x8:
386 case QRhiTexture::ASTC_10x10:
388 case QRhiTexture::ASTC_12x10:
390 case QRhiTexture::ASTC_12x12:
400 *dst += QString::asprintf(
"| %s | %dx%d | %llu | %llu |\n",
401 rp.rtName.constData(),
402 rp.pixelSize.width(),
403 rp.pixelSize.height(),
404 QSSGRhiContextStats::totalVertexCountForPass(rp),
405 QSSGRhiContextStats::totalDrawCallCountForPass(rp));
410 if (!mesh->subsets.isEmpty()) {
411 auto buf = mesh->subsets[0].rhi.vertexBuffer;
413 return buf->buffer()->name();
418void QQuick3DRenderStats::processRhiContextStats()
420 if (!m_contextStats || !m_extendedDataCollectionEnabled)
424 const QSSGRhiContextStats::PerLayerInfo data = m_contextStats->perLayerInfo[m_layer];
426 const QSSGRhiContext *rhiCtx = m_contextStats->rhiCtx;
427 const QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx);
430 const QSSGRhiContextStats::GlobalInfo globalData = m_contextStats->globalInfo;
431 const auto textures = rhiCtxD->m_textures;
432 const auto meshes = rhiCtxD->m_meshes;
433 const auto pipelines = rhiCtxD->m_pipelines;
435 m_results.drawCallCount = 0;
436 m_results.drawVertexCount = 0;
437 for (
const auto &pass : data.renderPasses) {
438 m_results.drawCallCount += QSSGRhiContextStats::totalDrawCallCountForPass(pass);
439 m_results.drawVertexCount += QSSGRhiContextStats::totalVertexCountForPass(pass);
441 m_results.drawCallCount += QSSGRhiContextStats::totalDrawCallCountForPass(data.externalRenderPass);
442 m_results.drawVertexCount += QSSGRhiContextStats::totalVertexCountForPass(data.externalRenderPass);
444 m_results.imageDataSize = globalData.imageDataSize;
445 m_results.meshDataSize = globalData.meshDataSize;
447 m_results.renderPassCount = data.renderPasses.size()
448 + (data.externalRenderPass.pixelSize.isEmpty() ? 0 : 1);
450 QString renderPassDetails = QLatin1String(R"(
451| Name | Size | Vertices | Draw calls |
452| ---- | ---- | -------- | ---------- |
453)");
455 if (!data.externalRenderPass.pixelSize.isEmpty())
456 printRenderPassDetails(&renderPassDetails, data.externalRenderPass);
457 for (
const auto &pass : data.renderPasses) {
458 if (!pass.pixelSize.isEmpty())
459 printRenderPassDetails(&renderPassDetails, pass);
461 renderPassDetails += QString::asprintf(
"\nGenerated from QSSGRenderLayer %p", m_layer);
462 m_results.renderPassDetails = renderPassDetails;
464 if (m_results.activeTextures != textures) {
465 m_results.activeTextures = textures;
466 QString texDetails = QLatin1String(R"(
467| Name | Size | Format | Mip | Flags |
468| ---- | ---- | ------ | --- | ----- |
469)");
470 QList<QRhiTexture *> textureList = textures.values();
471 std::sort(textureList.begin(), textureList.end(), [](QRhiTexture *a, QRhiTexture *b) {
472 return a->name() < b->name();
474 for (QRhiTexture *tex : textureList) {
476 const QRhiTexture::Flags flags = tex->flags();
477 if (flags.testFlag(QRhiTexture::MipMapped))
478 mipCount = m_contextStats->rhiCtx->rhi()->mipLevelsForSize(tex->pixelSize());
480 if (flags.testFlag(QRhiTexture::CubeMap))
481 flagMsg += QByteArrayLiteral(
"[cube]");
482 texDetails += QString::asprintf(
"| %s | %dx%d | %s | %d | %s |\n",
483 tex->name().constData(),
484 tex->pixelSize().width(),
485 tex->pixelSize().height(),
486 textureFormatStr(tex->format()),
488 flagMsg.constData());
490 texDetails += QString::asprintf(
"\nAsset textures registered with QSSGRhiContext %p", m_contextStats->rhiCtx);
491 m_results.textureDetails = texDetails;
494 if (m_results.activeMeshes != meshes) {
495 m_results.activeMeshes = meshes;
496 QString meshDetails = QLatin1String(R"(
497| Name | Submeshes | Vertices | V.buf size | I.buf size |
498| ---- | --------- | -------- | ---------- | ---------- |
499)");
500 QList<QSSGRenderMesh *> meshList = meshes.values();
501 std::sort(meshList.begin(), meshList.end(), [](QSSGRenderMesh *a, QSSGRenderMesh *b) {
502 return nameForRenderMesh(a) < nameForRenderMesh(b);
504 for (QSSGRenderMesh *mesh : meshList) {
505 const QByteArray name = nameForRenderMesh(mesh);
506 const int subsetCount =
int(mesh->subsets.size());
507 quint64 vertexCount = 0;
508 quint32 vbufSize = 0;
509 quint32 ibufSize = 0;
510 if (subsetCount > 0) {
511 for (
const QSSGRenderSubset &subset : std::as_const(mesh->subsets))
512 vertexCount += subset.count;
514 const QSSGRhiBuffer *vbuf = mesh->subsets[0].rhi.vertexBuffer.get();
516 vbufSize = vbuf->buffer()->size();
517 const QSSGRhiBuffer *ibuf = mesh->subsets[0].rhi.indexBuffer.get();
519 ibufSize = ibuf->buffer()->size();
521 meshDetails += QString::asprintf(
"| %s | %d | %llu | %u | %u |\n",
529 meshDetails += QString::asprintf(
"\nAsset meshes registered with QSSGRhiContext %p", m_contextStats->rhiCtx);
530 m_results.meshDetails = meshDetails;
533 m_results.pipelineCount = pipelines.count();
535 m_results.materialGenerationTime = m_contextStats->globalInfo.materialGenerationTime;
536 m_results.effectGenerationTime = m_contextStats->globalInfo.effectGenerationTime;
538 m_results.rhiStats = m_contextStats->rhiCtx->rhi()->statistics();
541void QQuick3DRenderStats::notifyRhiContextStats()
543 if (!m_contextStats || !m_extendedDataCollectionEnabled)
546 if (m_results.drawCallCount != m_notifiedResults.drawCallCount) {
547 m_notifiedResults.drawCallCount = m_results.drawCallCount;
548 emit drawCallCountChanged();
551 if (m_results.drawVertexCount != m_notifiedResults.drawVertexCount) {
552 m_notifiedResults.drawVertexCount = m_results.drawVertexCount;
553 emit drawVertexCountChanged();
556 if (m_results.imageDataSize != m_notifiedResults.imageDataSize) {
557 m_notifiedResults.imageDataSize = m_results.imageDataSize;
558 emit imageDataSizeChanged();
561 if (m_results.meshDataSize != m_notifiedResults.meshDataSize) {
562 m_notifiedResults.meshDataSize = m_results.meshDataSize;
563 emit meshDataSizeChanged();
566 if (m_results.renderPassCount != m_notifiedResults.renderPassCount) {
567 m_notifiedResults.renderPassCount = m_results.renderPassCount;
568 emit renderPassCountChanged();
571 if (m_results.renderPassDetails != m_notifiedResults.renderPassDetails) {
572 m_notifiedResults.renderPassDetails = m_results.renderPassDetails;
573 emit renderPassDetailsChanged();
576 if (m_results.textureDetails != m_notifiedResults.textureDetails) {
577 m_notifiedResults.textureDetails = m_results.textureDetails;
578 emit textureDetailsChanged();
581 if (m_results.meshDetails != m_notifiedResults.meshDetails) {
582 m_notifiedResults.meshDetails = m_results.meshDetails;
583 emit meshDetailsChanged();
586 if (m_results.pipelineCount != m_notifiedResults.pipelineCount) {
587 m_notifiedResults.pipelineCount = m_results.pipelineCount;
588 emit pipelineCountChanged();
591 if (m_results.materialGenerationTime != m_notifiedResults.materialGenerationTime) {
592 m_notifiedResults.materialGenerationTime = m_results.materialGenerationTime;
593 emit materialGenerationTimeChanged();
596 if (m_results.effectGenerationTime != m_notifiedResults.effectGenerationTime) {
597 m_notifiedResults.effectGenerationTime = m_results.effectGenerationTime;
598 emit effectGenerationTimeChanged();
601 if (m_results.rhiStats.totalPipelineCreationTime != m_notifiedResults.rhiStats.totalPipelineCreationTime) {
602 m_notifiedResults.rhiStats.totalPipelineCreationTime = m_results.rhiStats.totalPipelineCreationTime;
603 emit pipelineCreationTimeChanged();
606 if (m_results.rhiStats.allocCount != m_notifiedResults.rhiStats.allocCount) {
607 m_notifiedResults.rhiStats.allocCount = m_results.rhiStats.allocCount;
608 emit vmemAllocCountChanged();
611 if (m_results.rhiStats.usedBytes != m_notifiedResults.rhiStats.usedBytes) {
612 m_notifiedResults.rhiStats.usedBytes = m_results.rhiStats.usedBytes;
613 emit vmemUsedBytesChanged();
618
619
624
625
626
627
628
629quint64 QQuick3DRenderStats::drawCallCount()
const
631 return m_results.drawCallCount;
635
636
637
638
639
640
645
646
647
648
649
650quint64 QQuick3DRenderStats::drawVertexCount()
const
652 return m_results.drawVertexCount;
656
657
658
659
660
661
662
667
668
669
670
671
672quint64 QQuick3DRenderStats::imageDataSize()
const
674 return m_results.imageDataSize;
678
679
680
681
682
683
684
689
690
691
692
693
694quint64 QQuick3DRenderStats::meshDataSize()
const
696 return m_results.meshDataSize;
700
701
702
703
704
705
706
711
712
713
714
715
716int QQuick3DRenderStats::renderPassCount()
const
718 return m_results.renderPassCount;
722
723
724
725
726
727QString QQuick3DRenderStats::renderPassDetails()
const
729 return m_results.renderPassDetails;
733
734
735
736
737
738QString QQuick3DRenderStats::textureDetails()
const
740 return m_results.textureDetails;
744
745
746
747
748
749QString QQuick3DRenderStats::meshDetails()
const
751 return m_results.meshDetails;
755
756
757
758
759
764
765
766
767
768
769int QQuick3DRenderStats::pipelineCount()
const
771 return m_results.pipelineCount;
775
776
777
778
779
780
785
786
787
788
789
790qint64 QQuick3DRenderStats::materialGenerationTime()
const
792 return m_results.materialGenerationTime;
796
797
798
799
800
801
806
807
808
809
810
811qint64 QQuick3DRenderStats::effectGenerationTime()
const
813 return m_results.effectGenerationTime;
817
818
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
849
850
851
852
853
854qint64 QQuick3DRenderStats::pipelineCreationTime()
const
856 return m_results.rhiStats.totalPipelineCreationTime;
860
861
862
863
864
865
866
867
872
873
874
875
876
877quint32 QQuick3DRenderStats::vmemAllocCount()
const
879 return m_results.rhiStats.allocCount;
883
884
885
886
887
888
889
890
895
896
897
898
899
900quint64 QQuick3DRenderStats::vmemUsedBytes()
const
902 return m_results.rhiStats.usedBytes;
909
910
911
912
913
914QString QQuick3DRenderStats::graphicsApiName()
const
916 return m_graphicsApiName;
920
921
922
923
924
925
926
927
928
929
930
931
936
937
938
939
941float QQuick3DRenderStats::lastCompletedGpuTime()
const
943 return m_results.lastCompletedGpuTime;
947
948
949void QQuick3DRenderStats::releaseCachedResources()
952 m_window->releaseResources();
954 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)