1/****************************************************************************
2**
3** Copyright (C) 2019 The Qt Company Ltd.
4** Contact: http://www.qt.io/licensing/
5**
6** This file is part of the Qt Gui module
7**
8** $QT_BEGIN_LICENSE:LGPL3$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see http://www.qt.io/terms-conditions. For further
15** information use the contact form at http://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPLv3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or later as published by the Free
28** Software Foundation and appearing in the file LICENSE.GPL included in
29** the packaging of this file. Please review the following information to
30** ensure the GNU General Public License version 2.0 requirements will be
31** met: http://www.gnu.org/licenses/gpl-2.0.html.
32**
33** $QT_END_LICENSE$
34**
35****************************************************************************/
36
37#include "qrhiprofiler_p_p.h"
38#include "qrhi_p_p.h"
39
40QT_BEGIN_NAMESPACE
41
42/*!
43 \class QRhiProfiler
44 \internal
45 \inmodule QtGui
46
47 \brief Collects resource and timing information from an active QRhi.
48
49 A QRhiProfiler is present for each QRhi. Query it via QRhi::profiler(). The
50 profiler is active only when the QRhi was created with
51 QRhi::EnableProfiling. No data is collected otherwise.
52
53 \note GPU timings are only available when QRhi indicates that
54 QRhi::Timestamps is supported.
55
56 Besides collecting data from the QRhi implementations, some additional
57 values are calculated. For example, for textures and similar resources the
58 profiler gives an estimate of the complete amount of memory the resource
59 needs.
60
61 \section2 Output Format
62
63 The output is comma-separated text. Each line has a number of
64 comma-separated entries and each line ends with a comma.
65
66 For example:
67
68 \badcode
69 1,0,140446057946208,Triangle vbuf,type,0,usage,1,logical_size,84,effective_size,84,backing_gpu_buf_count,1,backing_cpu_buf_count,0,
70 1,0,140446057947376,Triangle ubuf,type,2,usage,4,logical_size,68,effective_size,256,backing_gpu_buf_count,2,backing_cpu_buf_count,0,
71 1,1,140446057950416,,type,0,usage,1,logical_size,112,effective_size,112,backing_gpu_buf_count,1,backing_cpu_buf_count,0,
72 1,1,140446057950544,,type,0,usage,2,logical_size,12,effective_size,12,backing_gpu_buf_count,1,backing_cpu_buf_count,0,
73 1,1,140446057947440,,type,2,usage,4,logical_size,68,effective_size,256,backing_gpu_buf_count,2,backing_cpu_buf_count,0,
74 1,1,140446057984784,Cube vbuf (textured),type,0,usage,1,logical_size,720,effective_size,720,backing_gpu_buf_count,1,backing_cpu_buf_count,0,
75 1,1,140446057982528,Cube ubuf (textured),type,2,usage,4,logical_size,68,effective_size,256,backing_gpu_buf_count,2,backing_cpu_buf_count,0,
76 7,8,140446058913648,Qt texture,width,256,height,256,format,1,owns_native_resource,1,mip_count,9,layer_count,1,effective_sample_count,1,approx_byte_size,349524,
77 1,8,140446058795856,Cube vbuf (textured with offscreen),type,0,usage,1,logical_size,720,effective_size,720,backing_gpu_buf_count,1,backing_cpu_buf_count,0,
78 1,8,140446058947920,Cube ubuf (textured with offscreen),type,2,usage,4,logical_size,68,effective_size,256,backing_gpu_buf_count,2,backing_cpu_buf_count,0,
79 7,8,140446058794928,Texture for offscreen content,width,512,height,512,format,1,owns_native_resource,1,mip_count,1,layer_count,1,effective_sample_count,1,approx_byte_size,1048576,
80 1,8,140446058963904,Triangle vbuf,type,0,usage,1,logical_size,84,effective_size,84,backing_gpu_buf_count,1,backing_cpu_buf_count,0,
81 1,8,140446058964560,Triangle ubuf,type,2,usage,4,logical_size,68,effective_size,256,backing_gpu_buf_count,2,backing_cpu_buf_count,0,
82 5,9,140446057945392,,type,0,width,1280,height,720,effective_sample_count,1,transient_backing,0,winsys_backing,0,approx_byte_size,3686400,
83 11,9,140446057944592,,width,1280,height,720,buffer_count,2,msaa_buffer_count,0,effective_sample_count,1,approx_total_byte_size,7372800,
84 9,9,140446058913648,Qt texture,slot,0,size,262144,
85 10,9,140446058913648,Qt texture,slot,0,
86 17,2019,140446057944592,,frames_since_resize,121,min_ms_frame_delta,9,max_ms_frame_delta,33,Favg_ms_frame_delta,16.1167,
87 18,2019,140446057944592,,frames_since_resize,121,min_ms_frame_build,0,max_ms_frame_build,1,Favg_ms_frame_build,0.00833333,
88 17,4019,140446057944592,,frames_since_resize,241,min_ms_frame_delta,15,max_ms_frame_delta,17,Favg_ms_frame_delta,16.0583,
89 18,4019,140446057944592,,frames_since_resize,241,min_ms_frame_build,0,max_ms_frame_build,0,Favg_ms_frame_build,0,
90 12,5070,140446057944592,,
91 2,5079,140446057947376,Triangle ubuf,
92 2,5079,140446057946208,Triangle vbuf,
93 2,5079,140446057947440,,
94 2,5079,140446057950544,,
95 2,5079,140446057950416,,
96 8,5079,140446058913648,Qt texture,
97 2,5079,140446057982528,Cube ubuf (textured),
98 2,5079,140446057984784,Cube vbuf (textured),
99 2,5079,140446058964560,Triangle ubuf,
100 2,5079,140446058963904,Triangle vbuf,
101 8,5079,140446058794928,Texture for offscreen content,
102 2,5079,140446058947920,Cube ubuf (textured with offscreen),
103 2,5079,140446058795856,Cube vbuf (textured with offscreen),
104 6,5079,140446057945392,,
105 \endcode
106
107 Each line starts with \c op, \c timestamp, \c res, \c name where op is a
108 value from StreamOp, timestamp is a recording timestamp in milliseconds
109 (qint64), res is a number (quint64) referring to the QRhiResource the entry
110 refers to, or 0 if not applicable. \c name is the value of
111 QRhiResource::name() and may be empty as well. The \c name will never
112 contain a comma.
113
114 This is followed by any number of \c{key, value} pairs where \c key is an
115 unspecified string and \c value is a number. If \c key starts with \c F, it
116 indicates the value is a float. Otherwise assume that the value is a
117 qint64.
118 */
119
120/*!
121 \enum QRhiProfiler::StreamOp
122 Describes an entry in the profiler's output stream.
123
124 \value NewBuffer A buffer is created
125 \value ReleaseBuffer A buffer is destroyed
126 \value NewBufferStagingArea A staging buffer for buffer upload is created
127 \value ReleaseBufferStagingArea A staging buffer for buffer upload is destroyed
128 \value NewRenderBuffer A renderbuffer is created
129 \value ReleaseRenderBuffer A renderbuffer is destroyed
130 \value NewTexture A texture is created
131 \value ReleaseTexture A texture is destroyed
132 \value NewTextureStagingArea A staging buffer for texture upload is created
133 \value ReleaseTextureStagingArea A staging buffer for texture upload is destroyed
134 \value ResizeSwapChain A swapchain is created or resized
135 \value ReleaseSwapChain A swapchain is destroyed
136 \value NewReadbackBuffer A staging buffer for readback is created
137 \value ReleaseReadbackBuffer A staging buffer for readback is destroyed
138 \value GpuMemAllocStats GPU memory allocator statistics
139 \value GpuFrameTime GPU frame times
140 \value FrameToFrameTime CPU frame-to-frame times
141 \value FrameBuildTime CPU beginFrame-endFrame times
142 */
143
144/*!
145 \class QRhiProfiler::CpuTime
146 \internal
147 \inmodule QtGui
148 \brief Contains CPU-side frame timings.
149
150 Once sufficient number of frames have been rendered, the minimum, maximum,
151 and average values (in milliseconds) from various measurements are made
152 available in this struct queriable from QRhiProfiler::frameToFrameTimes()
153 and QRhiProfiler::frameBuildTimes().
154
155 \sa QRhiProfiler::setFrameTimingWriteInterval()
156 */
157
158/*!
159 \class QRhiProfiler::GpuTime
160 \internal
161 \inmodule QtGui
162 \brief Contains GPU-side frame timings.
163
164 Once sufficient number of frames have been rendered, the minimum, maximum,
165 and average values (in milliseconds) calculated from GPU command buffer
166 timestamps are made available in this struct queriable from
167 QRhiProfiler::gpuFrameTimes().
168
169 \sa QRhiProfiler::setFrameTimingWriteInterval()
170 */
171
172/*!
173 \internal
174 */
175QRhiProfiler::QRhiProfiler()
176 : d(new QRhiProfilerPrivate)
177{
178 d->ts.start();
179}
180
181/*!
182 Destructor.
183 */
184QRhiProfiler::~QRhiProfiler()
185{
186 // Flush because there is a high chance we have writes that were made since
187 // the event loop last ran. (esp. relevant for network devices like QTcpSocket)
188 if (d->outputDevice)
189 d->outputDevice->waitForBytesWritten(msecs: 1000);
190
191 delete d;
192}
193
194/*!
195 Sets the output \a device.
196
197 \note No output will be generated when QRhi::EnableProfiling was not set.
198 */
199void QRhiProfiler::setDevice(QIODevice *device)
200{
201 d->outputDevice = device;
202}
203
204/*!
205 Requests writing a GpuMemAllocStats entry into the output, when applicable.
206 Backends that do not support this will ignore the request. This is an
207 explicit request since getting the allocator status and statistics may be
208 an expensive operation.
209 */
210void QRhiProfiler::addVMemAllocatorStats()
211{
212 if (d->rhiDWhenEnabled)
213 d->rhiDWhenEnabled->sendVMemStatsToProfiler();
214}
215
216/*!
217 \return the currently set frame timing writeout interval.
218 */
219int QRhiProfiler::frameTimingWriteInterval() const
220{
221 return d->frameTimingWriteInterval;
222}
223
224/*!
225 Sets the number of frames that need to be rendered before the collected CPU
226 and GPU timings are processed (min, max, average are calculated) to \a
227 frameCount.
228
229 The default value is 120.
230 */
231void QRhiProfiler::setFrameTimingWriteInterval(int frameCount)
232{
233 if (frameCount > 0)
234 d->frameTimingWriteInterval = frameCount;
235}
236
237/*!
238 \return min, max, and avg in milliseconds for the time that elapsed between two
239 QRhi::endFrame() calls.
240
241 \note The values are all 0 until at least frameTimingWriteInterval() frames
242 have been rendered.
243 */
244QRhiProfiler::CpuTime QRhiProfiler::frameToFrameTimes(QRhiSwapChain *sc) const
245{
246 auto it = d->swapchains.constFind(akey: sc);
247 if (it != d->swapchains.constEnd())
248 return it->frameToFrameTime;
249
250 return QRhiProfiler::CpuTime();
251}
252
253/*!
254 \return min, max, and avg in milliseconds for the time that elapsed between
255 a QRhi::beginFrame() and QRhi::endFrame().
256
257 \note The values are all 0 until at least frameTimingWriteInterval() frames
258 have been rendered.
259 */
260QRhiProfiler::CpuTime QRhiProfiler::frameBuildTimes(QRhiSwapChain *sc) const
261{
262 auto it = d->swapchains.constFind(akey: sc);
263 if (it != d->swapchains.constEnd())
264 return it->beginToEndFrameTime;
265
266 return QRhiProfiler::CpuTime();
267}
268
269/*!
270 \return min, max, and avg in milliseconds for the GPU time that is spent on
271 one frame.
272
273 \note The values are all 0 until at least frameTimingWriteInterval() frames
274 have been rendered.
275
276 The GPU times should only be compared between runs on the same GPU of the
277 same system with the same backend. Comparing times for different graphics
278 cards or for different backends can give misleading results. The numbers are
279 not meant to be comparable that way.
280
281 \note Some backends have no support for this, and even for those that have,
282 it is not guaranteed that the driver will support it at run time. Support
283 can be checked via QRhi::Timestamps.
284 */
285QRhiProfiler::GpuTime QRhiProfiler::gpuFrameTimes(QRhiSwapChain *sc) const
286{
287 auto it = d->swapchains.constFind(akey: sc);
288 if (it != d->swapchains.constEnd())
289 return it->gpuFrameTime;
290
291 return QRhiProfiler::GpuTime();
292}
293
294void QRhiProfilerPrivate::startEntry(QRhiProfiler::StreamOp op, qint64 timestamp, QRhiResource *res)
295{
296 buf.clear();
297 buf.append(a: QByteArray::number(op));
298 buf.append(c: ',');
299 buf.append(a: QByteArray::number(timestamp));
300 buf.append(c: ',');
301 buf.append(a: QByteArray::number(quint64(quintptr(res))));
302 buf.append(c: ',');
303 if (res)
304 buf.append(a: res->name());
305 buf.append(c: ',');
306}
307
308void QRhiProfilerPrivate::writeInt(const char *key, qint64 v)
309{
310 Q_ASSERT(key[0] != 'F');
311 buf.append(s: key);
312 buf.append(c: ',');
313 buf.append(a: QByteArray::number(v));
314 buf.append(c: ',');
315}
316
317void QRhiProfilerPrivate::writeFloat(const char *key, float f)
318{
319 Q_ASSERT(key[0] == 'F');
320 buf.append(s: key);
321 buf.append(c: ',');
322 buf.append(a: QByteArray::number(double(f)));
323 buf.append(c: ',');
324}
325
326void QRhiProfilerPrivate::endEntry()
327{
328 buf.append(c: '\n');
329 outputDevice->write(data: buf);
330}
331
332void QRhiProfilerPrivate::newBuffer(QRhiBuffer *buf, quint32 realSize, int backingGpuBufCount, int backingCpuBufCount)
333{
334 if (!outputDevice)
335 return;
336
337 startEntry(op: QRhiProfiler::NewBuffer, timestamp: ts.elapsed(), res: buf);
338 writeInt(key: "type", v: buf->type());
339 writeInt(key: "usage", v: buf->usage());
340 writeInt(key: "logical_size", v: buf->size());
341 writeInt(key: "effective_size", v: realSize);
342 writeInt(key: "backing_gpu_buf_count", v: backingGpuBufCount);
343 writeInt(key: "backing_cpu_buf_count", v: backingCpuBufCount);
344 endEntry();
345}
346
347void QRhiProfilerPrivate::releaseBuffer(QRhiBuffer *buf)
348{
349 if (!outputDevice)
350 return;
351
352 startEntry(op: QRhiProfiler::ReleaseBuffer, timestamp: ts.elapsed(), res: buf);
353 endEntry();
354}
355
356void QRhiProfilerPrivate::newBufferStagingArea(QRhiBuffer *buf, int slot, quint32 size)
357{
358 if (!outputDevice)
359 return;
360
361 startEntry(op: QRhiProfiler::NewBufferStagingArea, timestamp: ts.elapsed(), res: buf);
362 writeInt(key: "slot", v: slot);
363 writeInt(key: "size", v: size);
364 endEntry();
365}
366
367void QRhiProfilerPrivate::releaseBufferStagingArea(QRhiBuffer *buf, int slot)
368{
369 if (!outputDevice)
370 return;
371
372 startEntry(op: QRhiProfiler::ReleaseBufferStagingArea, timestamp: ts.elapsed(), res: buf);
373 writeInt(key: "slot", v: slot);
374 endEntry();
375}
376
377void QRhiProfilerPrivate::newRenderBuffer(QRhiRenderBuffer *rb, bool transientBacking, bool winSysBacking, int sampleCount)
378{
379 if (!outputDevice)
380 return;
381
382 const QRhiRenderBuffer::Type type = rb->type();
383 const QSize sz = rb->pixelSize();
384 // just make up something, ds is likely D24S8 while color is RGBA8 or similar
385 const QRhiTexture::Format assumedFormat = type == QRhiRenderBuffer::DepthStencil ? QRhiTexture::D32F : QRhiTexture::RGBA8;
386 quint32 byteSize = rhiDWhenEnabled->approxByteSizeForTexture(format: assumedFormat, baseSize: sz, mipCount: 1, layerCount: 1);
387 if (sampleCount > 1)
388 byteSize *= uint(sampleCount);
389
390 startEntry(op: QRhiProfiler::NewRenderBuffer, timestamp: ts.elapsed(), res: rb);
391 writeInt(key: "type", v: type);
392 writeInt(key: "width", v: sz.width());
393 writeInt(key: "height", v: sz.height());
394 writeInt(key: "effective_sample_count", v: sampleCount);
395 writeInt(key: "transient_backing", v: transientBacking);
396 writeInt(key: "winsys_backing", v: winSysBacking);
397 writeInt(key: "approx_byte_size", v: byteSize);
398 endEntry();
399}
400
401void QRhiProfilerPrivate::releaseRenderBuffer(QRhiRenderBuffer *rb)
402{
403 if (!outputDevice)
404 return;
405
406 startEntry(op: QRhiProfiler::ReleaseRenderBuffer, timestamp: ts.elapsed(), res: rb);
407 endEntry();
408}
409
410void QRhiProfilerPrivate::newTexture(QRhiTexture *tex, bool owns, int mipCount, int layerCount, int sampleCount)
411{
412 if (!outputDevice)
413 return;
414
415 const QRhiTexture::Format format = tex->format();
416 const QSize sz = tex->pixelSize();
417 quint32 byteSize = rhiDWhenEnabled->approxByteSizeForTexture(format, baseSize: sz, mipCount, layerCount);
418 if (sampleCount > 1)
419 byteSize *= uint(sampleCount);
420
421 startEntry(op: QRhiProfiler::NewTexture, timestamp: ts.elapsed(), res: tex);
422 writeInt(key: "width", v: sz.width());
423 writeInt(key: "height", v: sz.height());
424 writeInt(key: "format", v: format);
425 writeInt(key: "owns_native_resource", v: owns);
426 writeInt(key: "mip_count", v: mipCount);
427 writeInt(key: "layer_count", v: layerCount);
428 writeInt(key: "effective_sample_count", v: sampleCount);
429 writeInt(key: "approx_byte_size", v: byteSize);
430 endEntry();
431}
432
433void QRhiProfilerPrivate::releaseTexture(QRhiTexture *tex)
434{
435 if (!outputDevice)
436 return;
437
438 startEntry(op: QRhiProfiler::ReleaseTexture, timestamp: ts.elapsed(), res: tex);
439 endEntry();
440}
441
442void QRhiProfilerPrivate::newTextureStagingArea(QRhiTexture *tex, int slot, quint32 size)
443{
444 if (!outputDevice)
445 return;
446
447 startEntry(op: QRhiProfiler::NewTextureStagingArea, timestamp: ts.elapsed(), res: tex);
448 writeInt(key: "slot", v: slot);
449 writeInt(key: "size", v: size);
450 endEntry();
451}
452
453void QRhiProfilerPrivate::releaseTextureStagingArea(QRhiTexture *tex, int slot)
454{
455 if (!outputDevice)
456 return;
457
458 startEntry(op: QRhiProfiler::ReleaseTextureStagingArea, timestamp: ts.elapsed(), res: tex);
459 writeInt(key: "slot", v: slot);
460 endEntry();
461}
462
463void QRhiProfilerPrivate::resizeSwapChain(QRhiSwapChain *sc, int bufferCount, int msaaBufferCount, int sampleCount)
464{
465 if (!outputDevice)
466 return;
467
468 const QSize sz = sc->currentPixelSize();
469 quint32 byteSize = rhiDWhenEnabled->approxByteSizeForTexture(format: QRhiTexture::BGRA8, baseSize: sz, mipCount: 1, layerCount: 1);
470 byteSize = byteSize * uint(bufferCount) + byteSize * uint(msaaBufferCount) * uint(sampleCount);
471
472 startEntry(op: QRhiProfiler::ResizeSwapChain, timestamp: ts.elapsed(), res: sc);
473 writeInt(key: "width", v: sz.width());
474 writeInt(key: "height", v: sz.height());
475 writeInt(key: "buffer_count", v: bufferCount);
476 writeInt(key: "msaa_buffer_count", v: msaaBufferCount);
477 writeInt(key: "effective_sample_count", v: sampleCount);
478 writeInt(key: "approx_total_byte_size", v: byteSize);
479 endEntry();
480}
481
482void QRhiProfilerPrivate::releaseSwapChain(QRhiSwapChain *sc)
483{
484 if (!outputDevice)
485 return;
486
487 startEntry(op: QRhiProfiler::ReleaseSwapChain, timestamp: ts.elapsed(), res: sc);
488 endEntry();
489}
490
491template<typename T>
492void calcTiming(QVector<T> *vec, T *minDelta, T *maxDelta, float *avgDelta)
493{
494 if (vec->isEmpty())
495 return;
496
497 *minDelta = *maxDelta = 0;
498 float totalDelta = 0;
499 for (T delta : qAsConst(*vec)) {
500 totalDelta += float(delta);
501 if (*minDelta == 0 || delta < *minDelta)
502 *minDelta = delta;
503 if (*maxDelta == 0 || delta > *maxDelta)
504 *maxDelta = delta;
505 }
506 *avgDelta = totalDelta / vec->count();
507
508 vec->clear();
509}
510
511void QRhiProfilerPrivate::beginSwapChainFrame(QRhiSwapChain *sc)
512{
513 Sc &scd(swapchains[sc]);
514 scd.beginToEndTimer.start();
515}
516
517void QRhiProfilerPrivate::endSwapChainFrame(QRhiSwapChain *sc, int frameCount)
518{
519 Sc &scd(swapchains[sc]);
520 if (!scd.frameToFrameRunning) {
521 scd.frameToFrameTimer.start();
522 scd.frameToFrameRunning = true;
523 return;
524 }
525
526 scd.frameToFrameSamples.append(t: scd.frameToFrameTimer.restart());
527 if (scd.frameToFrameSamples.count() >= frameTimingWriteInterval) {
528 calcTiming(vec: &scd.frameToFrameSamples,
529 minDelta: &scd.frameToFrameTime.minTime, maxDelta: &scd.frameToFrameTime.maxTime, avgDelta: &scd.frameToFrameTime.avgTime);
530 if (outputDevice) {
531 startEntry(op: QRhiProfiler::FrameToFrameTime, timestamp: ts.elapsed(), res: sc);
532 writeInt(key: "frames_since_resize", v: frameCount);
533 writeInt(key: "min_ms_frame_delta", v: scd.frameToFrameTime.minTime);
534 writeInt(key: "max_ms_frame_delta", v: scd.frameToFrameTime.maxTime);
535 writeFloat(key: "Favg_ms_frame_delta", f: scd.frameToFrameTime.avgTime);
536 endEntry();
537 }
538 }
539
540 scd.beginToEndSamples.append(t: scd.beginToEndTimer.elapsed());
541 if (scd.beginToEndSamples.count() >= frameTimingWriteInterval) {
542 calcTiming(vec: &scd.beginToEndSamples,
543 minDelta: &scd.beginToEndFrameTime.minTime, maxDelta: &scd.beginToEndFrameTime.maxTime, avgDelta: &scd.beginToEndFrameTime.avgTime);
544 if (outputDevice) {
545 startEntry(op: QRhiProfiler::FrameBuildTime, timestamp: ts.elapsed(), res: sc);
546 writeInt(key: "frames_since_resize", v: frameCount);
547 writeInt(key: "min_ms_frame_build", v: scd.beginToEndFrameTime.minTime);
548 writeInt(key: "max_ms_frame_build", v: scd.beginToEndFrameTime.maxTime);
549 writeFloat(key: "Favg_ms_frame_build", f: scd.beginToEndFrameTime.avgTime);
550 endEntry();
551 }
552 }
553}
554
555void QRhiProfilerPrivate::swapChainFrameGpuTime(QRhiSwapChain *sc, float gpuTime)
556{
557 Sc &scd(swapchains[sc]);
558 scd.gpuFrameSamples.append(t: gpuTime);
559 if (scd.gpuFrameSamples.count() >= frameTimingWriteInterval) {
560 calcTiming(vec: &scd.gpuFrameSamples,
561 minDelta: &scd.gpuFrameTime.minTime, maxDelta: &scd.gpuFrameTime.maxTime, avgDelta: &scd.gpuFrameTime.avgTime);
562 if (outputDevice) {
563 startEntry(op: QRhiProfiler::GpuFrameTime, timestamp: ts.elapsed(), res: sc);
564 writeFloat(key: "Fmin_ms_gpu_frame_time", f: scd.gpuFrameTime.minTime);
565 writeFloat(key: "Fmax_ms_gpu_frame_time", f: scd.gpuFrameTime.maxTime);
566 writeFloat(key: "Favg_ms_gpu_frame_time", f: scd.gpuFrameTime.avgTime);
567 endEntry();
568 }
569 }
570}
571
572void QRhiProfilerPrivate::newReadbackBuffer(qint64 id, QRhiResource *src, quint32 size)
573{
574 if (!outputDevice)
575 return;
576
577 startEntry(op: QRhiProfiler::NewReadbackBuffer, timestamp: ts.elapsed(), res: src);
578 writeInt(key: "id", v: id);
579 writeInt(key: "size", v: size);
580 endEntry();
581}
582
583void QRhiProfilerPrivate::releaseReadbackBuffer(qint64 id)
584{
585 if (!outputDevice)
586 return;
587
588 startEntry(op: QRhiProfiler::ReleaseReadbackBuffer, timestamp: ts.elapsed(), res: nullptr);
589 writeInt(key: "id", v: id);
590 endEntry();
591}
592
593void QRhiProfilerPrivate::vmemStat(uint realAllocCount, uint subAllocCount, quint32 totalSize, quint32 unusedSize)
594{
595 if (!outputDevice)
596 return;
597
598 startEntry(op: QRhiProfiler::GpuMemAllocStats, timestamp: ts.elapsed(), res: nullptr);
599 writeInt(key: "real_alloc_count", v: realAllocCount);
600 writeInt(key: "sub_alloc_count", v: subAllocCount);
601 writeInt(key: "total_size", v: totalSize);
602 writeInt(key: "unused_size", v: unusedSize);
603 endEntry();
604}
605
606QT_END_NAMESPACE
607

source code of qtbase/src/gui/rhi/qrhiprofiler.cpp