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 
40 QT_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  */
QRhiProfiler()175 QRhiProfiler::QRhiProfiler()
176     : d(new QRhiProfilerPrivate)
177 {
178     d->ts.start();
179 }
180 
181 /*!
182     Destructor.
183  */
~QRhiProfiler()184 QRhiProfiler::~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(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  */
setDevice(QIODevice * device)199 void 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  */
addVMemAllocatorStats()210 void QRhiProfiler::addVMemAllocatorStats()
211 {
212     if (d->rhiDWhenEnabled)
213         d->rhiDWhenEnabled->sendVMemStatsToProfiler();
214 }
215 
216 /*!
217     \return the currently set frame timing writeout interval.
218  */
frameTimingWriteInterval() const219 int 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  */
setFrameTimingWriteInterval(int frameCount)231 void 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  */
frameToFrameTimes(QRhiSwapChain * sc) const244 QRhiProfiler::CpuTime QRhiProfiler::frameToFrameTimes(QRhiSwapChain *sc) const
245 {
246     auto it = d->swapchains.constFind(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  */
frameBuildTimes(QRhiSwapChain * sc) const260 QRhiProfiler::CpuTime QRhiProfiler::frameBuildTimes(QRhiSwapChain *sc) const
261 {
262     auto it = d->swapchains.constFind(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  */
gpuFrameTimes(QRhiSwapChain * sc) const285 QRhiProfiler::GpuTime QRhiProfiler::gpuFrameTimes(QRhiSwapChain *sc) const
286 {
287     auto it = d->swapchains.constFind(sc);
288     if (it != d->swapchains.constEnd())
289         return it->gpuFrameTime;
290 
291     return QRhiProfiler::GpuTime();
292 }
293 
startEntry(QRhiProfiler::StreamOp op,qint64 timestamp,QRhiResource * res)294 void QRhiProfilerPrivate::startEntry(QRhiProfiler::StreamOp op, qint64 timestamp, QRhiResource *res)
295 {
296     buf.clear();
297     buf.append(QByteArray::number(op));
298     buf.append(',');
299     buf.append(QByteArray::number(timestamp));
300     buf.append(',');
301     buf.append(QByteArray::number(quint64(quintptr(res))));
302     buf.append(',');
303     if (res)
304         buf.append(res->name());
305     buf.append(',');
306 }
307 
writeInt(const char * key,qint64 v)308 void QRhiProfilerPrivate::writeInt(const char *key, qint64 v)
309 {
310     Q_ASSERT(key[0] != 'F');
311     buf.append(key);
312     buf.append(',');
313     buf.append(QByteArray::number(v));
314     buf.append(',');
315 }
316 
writeFloat(const char * key,float f)317 void QRhiProfilerPrivate::writeFloat(const char *key, float f)
318 {
319     Q_ASSERT(key[0] == 'F');
320     buf.append(key);
321     buf.append(',');
322     buf.append(QByteArray::number(double(f)));
323     buf.append(',');
324 }
325 
endEntry()326 void QRhiProfilerPrivate::endEntry()
327 {
328     buf.append('\n');
329     outputDevice->write(buf);
330 }
331 
newBuffer(QRhiBuffer * buf,quint32 realSize,int backingGpuBufCount,int backingCpuBufCount)332 void QRhiProfilerPrivate::newBuffer(QRhiBuffer *buf, quint32 realSize, int backingGpuBufCount, int backingCpuBufCount)
333 {
334     if (!outputDevice)
335         return;
336 
337     startEntry(QRhiProfiler::NewBuffer, ts.elapsed(), buf);
338     writeInt("type", buf->type());
339     writeInt("usage", buf->usage());
340     writeInt("logical_size", buf->size());
341     writeInt("effective_size", realSize);
342     writeInt("backing_gpu_buf_count", backingGpuBufCount);
343     writeInt("backing_cpu_buf_count", backingCpuBufCount);
344     endEntry();
345 }
346 
releaseBuffer(QRhiBuffer * buf)347 void QRhiProfilerPrivate::releaseBuffer(QRhiBuffer *buf)
348 {
349     if (!outputDevice)
350         return;
351 
352     startEntry(QRhiProfiler::ReleaseBuffer, ts.elapsed(), buf);
353     endEntry();
354 }
355 
newBufferStagingArea(QRhiBuffer * buf,int slot,quint32 size)356 void QRhiProfilerPrivate::newBufferStagingArea(QRhiBuffer *buf, int slot, quint32 size)
357 {
358     if (!outputDevice)
359         return;
360 
361     startEntry(QRhiProfiler::NewBufferStagingArea, ts.elapsed(), buf);
362     writeInt("slot", slot);
363     writeInt("size", size);
364     endEntry();
365 }
366 
releaseBufferStagingArea(QRhiBuffer * buf,int slot)367 void QRhiProfilerPrivate::releaseBufferStagingArea(QRhiBuffer *buf, int slot)
368 {
369     if (!outputDevice)
370         return;
371 
372     startEntry(QRhiProfiler::ReleaseBufferStagingArea, ts.elapsed(), buf);
373     writeInt("slot", slot);
374     endEntry();
375 }
376 
newRenderBuffer(QRhiRenderBuffer * rb,bool transientBacking,bool winSysBacking,int sampleCount)377 void 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(assumedFormat, sz, 1, 1);
387     if (sampleCount > 1)
388         byteSize *= uint(sampleCount);
389 
390     startEntry(QRhiProfiler::NewRenderBuffer, ts.elapsed(), rb);
391     writeInt("type", type);
392     writeInt("width", sz.width());
393     writeInt("height", sz.height());
394     writeInt("effective_sample_count", sampleCount);
395     writeInt("transient_backing", transientBacking);
396     writeInt("winsys_backing", winSysBacking);
397     writeInt("approx_byte_size", byteSize);
398     endEntry();
399 }
400 
releaseRenderBuffer(QRhiRenderBuffer * rb)401 void QRhiProfilerPrivate::releaseRenderBuffer(QRhiRenderBuffer *rb)
402 {
403     if (!outputDevice)
404         return;
405 
406     startEntry(QRhiProfiler::ReleaseRenderBuffer, ts.elapsed(), rb);
407     endEntry();
408 }
409 
newTexture(QRhiTexture * tex,bool owns,int mipCount,int layerCount,int sampleCount)410 void 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, sz, mipCount, layerCount);
418     if (sampleCount > 1)
419         byteSize *= uint(sampleCount);
420 
421     startEntry(QRhiProfiler::NewTexture, ts.elapsed(), tex);
422     writeInt("width", sz.width());
423     writeInt("height", sz.height());
424     writeInt("format", format);
425     writeInt("owns_native_resource", owns);
426     writeInt("mip_count", mipCount);
427     writeInt("layer_count", layerCount);
428     writeInt("effective_sample_count", sampleCount);
429     writeInt("approx_byte_size", byteSize);
430     endEntry();
431 }
432 
releaseTexture(QRhiTexture * tex)433 void QRhiProfilerPrivate::releaseTexture(QRhiTexture *tex)
434 {
435     if (!outputDevice)
436         return;
437 
438     startEntry(QRhiProfiler::ReleaseTexture, ts.elapsed(), tex);
439     endEntry();
440 }
441 
newTextureStagingArea(QRhiTexture * tex,int slot,quint32 size)442 void QRhiProfilerPrivate::newTextureStagingArea(QRhiTexture *tex, int slot, quint32 size)
443 {
444     if (!outputDevice)
445         return;
446 
447     startEntry(QRhiProfiler::NewTextureStagingArea, ts.elapsed(), tex);
448     writeInt("slot", slot);
449     writeInt("size", size);
450     endEntry();
451 }
452 
releaseTextureStagingArea(QRhiTexture * tex,int slot)453 void QRhiProfilerPrivate::releaseTextureStagingArea(QRhiTexture *tex, int slot)
454 {
455     if (!outputDevice)
456         return;
457 
458     startEntry(QRhiProfiler::ReleaseTextureStagingArea, ts.elapsed(), tex);
459     writeInt("slot", slot);
460     endEntry();
461 }
462 
resizeSwapChain(QRhiSwapChain * sc,int bufferCount,int msaaBufferCount,int sampleCount)463 void 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(QRhiTexture::BGRA8, sz, 1, 1);
470     byteSize = byteSize * uint(bufferCount) + byteSize * uint(msaaBufferCount) * uint(sampleCount);
471 
472     startEntry(QRhiProfiler::ResizeSwapChain, ts.elapsed(), sc);
473     writeInt("width", sz.width());
474     writeInt("height", sz.height());
475     writeInt("buffer_count", bufferCount);
476     writeInt("msaa_buffer_count", msaaBufferCount);
477     writeInt("effective_sample_count", sampleCount);
478     writeInt("approx_total_byte_size", byteSize);
479     endEntry();
480 }
481 
releaseSwapChain(QRhiSwapChain * sc)482 void QRhiProfilerPrivate::releaseSwapChain(QRhiSwapChain *sc)
483 {
484     if (!outputDevice)
485         return;
486 
487     startEntry(QRhiProfiler::ReleaseSwapChain, ts.elapsed(), sc);
488     endEntry();
489 }
490 
491 template<typename T>
calcTiming(QVector<T> * vec,T * minDelta,T * maxDelta,float * avgDelta)492 void 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 
beginSwapChainFrame(QRhiSwapChain * sc)511 void QRhiProfilerPrivate::beginSwapChainFrame(QRhiSwapChain *sc)
512 {
513     Sc &scd(swapchains[sc]);
514     scd.beginToEndTimer.start();
515 }
516 
endSwapChainFrame(QRhiSwapChain * sc,int frameCount)517 void 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(scd.frameToFrameTimer.restart());
527     if (scd.frameToFrameSamples.count() >= frameTimingWriteInterval) {
528         calcTiming(&scd.frameToFrameSamples,
529                    &scd.frameToFrameTime.minTime, &scd.frameToFrameTime.maxTime, &scd.frameToFrameTime.avgTime);
530         if (outputDevice) {
531             startEntry(QRhiProfiler::FrameToFrameTime, ts.elapsed(), sc);
532             writeInt("frames_since_resize", frameCount);
533             writeInt("min_ms_frame_delta", scd.frameToFrameTime.minTime);
534             writeInt("max_ms_frame_delta", scd.frameToFrameTime.maxTime);
535             writeFloat("Favg_ms_frame_delta", scd.frameToFrameTime.avgTime);
536             endEntry();
537         }
538     }
539 
540     scd.beginToEndSamples.append(scd.beginToEndTimer.elapsed());
541     if (scd.beginToEndSamples.count() >= frameTimingWriteInterval) {
542         calcTiming(&scd.beginToEndSamples,
543                    &scd.beginToEndFrameTime.minTime, &scd.beginToEndFrameTime.maxTime, &scd.beginToEndFrameTime.avgTime);
544         if (outputDevice) {
545             startEntry(QRhiProfiler::FrameBuildTime, ts.elapsed(), sc);
546             writeInt("frames_since_resize", frameCount);
547             writeInt("min_ms_frame_build", scd.beginToEndFrameTime.minTime);
548             writeInt("max_ms_frame_build", scd.beginToEndFrameTime.maxTime);
549             writeFloat("Favg_ms_frame_build", scd.beginToEndFrameTime.avgTime);
550             endEntry();
551         }
552     }
553 }
554 
swapChainFrameGpuTime(QRhiSwapChain * sc,float gpuTime)555 void QRhiProfilerPrivate::swapChainFrameGpuTime(QRhiSwapChain *sc, float gpuTime)
556 {
557     Sc &scd(swapchains[sc]);
558     scd.gpuFrameSamples.append(gpuTime);
559     if (scd.gpuFrameSamples.count() >= frameTimingWriteInterval) {
560         calcTiming(&scd.gpuFrameSamples,
561                    &scd.gpuFrameTime.minTime, &scd.gpuFrameTime.maxTime, &scd.gpuFrameTime.avgTime);
562         if (outputDevice) {
563             startEntry(QRhiProfiler::GpuFrameTime, ts.elapsed(), sc);
564             writeFloat("Fmin_ms_gpu_frame_time", scd.gpuFrameTime.minTime);
565             writeFloat("Fmax_ms_gpu_frame_time", scd.gpuFrameTime.maxTime);
566             writeFloat("Favg_ms_gpu_frame_time", scd.gpuFrameTime.avgTime);
567             endEntry();
568         }
569     }
570 }
571 
newReadbackBuffer(qint64 id,QRhiResource * src,quint32 size)572 void QRhiProfilerPrivate::newReadbackBuffer(qint64 id, QRhiResource *src, quint32 size)
573 {
574     if (!outputDevice)
575         return;
576 
577     startEntry(QRhiProfiler::NewReadbackBuffer, ts.elapsed(), src);
578     writeInt("id", id);
579     writeInt("size", size);
580     endEntry();
581 }
582 
releaseReadbackBuffer(qint64 id)583 void QRhiProfilerPrivate::releaseReadbackBuffer(qint64 id)
584 {
585     if (!outputDevice)
586         return;
587 
588     startEntry(QRhiProfiler::ReleaseReadbackBuffer, ts.elapsed(), nullptr);
589     writeInt("id", id);
590     endEntry();
591 }
592 
vmemStat(uint realAllocCount,uint subAllocCount,quint32 totalSize,quint32 unusedSize)593 void QRhiProfilerPrivate::vmemStat(uint realAllocCount, uint subAllocCount, quint32 totalSize, quint32 unusedSize)
594 {
595     if (!outputDevice)
596         return;
597 
598     startEntry(QRhiProfiler::GpuMemAllocStats, ts.elapsed(), nullptr);
599     writeInt("real_alloc_count", realAllocCount);
600     writeInt("sub_alloc_count", subAllocCount);
601     writeInt("total_size", totalSize);
602     writeInt("unused_size", unusedSize);
603     endEntry();
604 }
605 
606 QT_END_NAMESPACE
607