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