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