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 | \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 | */ |
172 | QRhiProfiler::QRhiProfiler() |
173 | : d(new QRhiProfilerPrivate) |
174 | { |
175 | d->ts.start(); |
176 | } |
177 | |
178 | /*! |
179 | Destructor. |
180 | */ |
181 | QRhiProfiler::~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 | */ |
196 | void 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 | */ |
207 | void QRhiProfiler::addVMemAllocatorStats() |
208 | { |
209 | if (d->rhiDWhenEnabled) |
210 | d->rhiDWhenEnabled->sendVMemStatsToProfiler(); |
211 | } |
212 | |
213 | /*! |
214 | \return the currently set frame timing writeout interval. |
215 | */ |
216 | int 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 | */ |
228 | void 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 | */ |
241 | QRhiProfiler::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 | */ |
257 | QRhiProfiler::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 | */ |
282 | QRhiProfiler::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 | |
291 | void 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 | |
305 | void 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 | |
314 | void 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 | |
323 | void QRhiProfilerPrivate::endEntry() |
324 | { |
325 | buf.append('\n'); |
326 | outputDevice->write(buf); |
327 | } |
328 | |
329 | void 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 | |
344 | void QRhiProfilerPrivate::releaseBuffer(QRhiBuffer *buf) |
345 | { |
346 | if (!outputDevice) |
347 | return; |
348 | |
349 | startEntry(QRhiProfiler::ReleaseBuffer, ts.elapsed(), buf); |
350 | endEntry(); |
351 | } |
352 | |
353 | void 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 | |
364 | void 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 | |
374 | void 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 | |
398 | void QRhiProfilerPrivate::releaseRenderBuffer(QRhiRenderBuffer *rb) |
399 | { |
400 | if (!outputDevice) |
401 | return; |
402 | |
403 | startEntry(QRhiProfiler::ReleaseRenderBuffer, ts.elapsed(), rb); |
404 | endEntry(); |
405 | } |
406 | |
407 | void 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 | |
430 | void QRhiProfilerPrivate::releaseTexture(QRhiTexture *tex) |
431 | { |
432 | if (!outputDevice) |
433 | return; |
434 | |
435 | startEntry(QRhiProfiler::ReleaseTexture, ts.elapsed(), tex); |
436 | endEntry(); |
437 | } |
438 | |
439 | void 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 | |
450 | void 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 | |
460 | void 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 | |
479 | void QRhiProfilerPrivate::releaseSwapChain(QRhiSwapChain *sc) |
480 | { |
481 | if (!outputDevice) |
482 | return; |
483 | |
484 | startEntry(QRhiProfiler::ReleaseSwapChain, ts.elapsed(), sc); |
485 | endEntry(); |
486 | } |
487 | |
488 | template<typename T> |
489 | void 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 | |
508 | void QRhiProfilerPrivate::beginSwapChainFrame(QRhiSwapChain *sc) |
509 | { |
510 | Sc &scd(swapchains[sc]); |
511 | scd.beginToEndTimer.start(); |
512 | } |
513 | |
514 | void 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 | |
552 | void 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 | |
569 | void 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 | |
580 | void 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 | |
590 | void 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 | |
603 | QT_END_NAMESPACE |
604 | |