1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef QQUICKPROFILER_P_H
5#define QQUICKPROFILER_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <QtCore/private/qabstractanimation_p.h>
19#include <QtQuick/private/qtquickglobal_p.h>
20
21#if QT_CONFIG(qml_debug)
22#include <QtQml/private/qqmlprofilerdefinitions_p.h>
23#endif
24
25#include <QtCore/private/qnumeric_p.h>
26#include <QtCore/qurl.h>
27#include <QtCore/qsize.h>
28#include <QtCore/qmutex.h>
29#include <QtCore/qthreadstorage.h>
30
31QT_BEGIN_NAMESPACE
32
33#if !QT_CONFIG(qml_debug)
34
35#define Q_QUICK_PROFILE_IF_ENABLED(feature, Code)
36
37struct QQuickProfiler {
38 static void registerAnimationCallback() {}
39};
40
41#else
42
43#define Q_QUICK_PROFILE_IF_ENABLED(feature, Code)\
44 if (QQuickProfiler::featuresEnabled & (1 << feature)) {\
45 Code;\
46 } else\
47 (void)0
48
49// This struct is somewhat dangerous to use:
50// You can save values either with 32 or 64 bit precision. toByteArrays will
51// guess the precision from messageType. If you state the wrong messageType
52// you will get undefined results.
53// The messageType is itself a bit field. You can pack multiple messages into
54// one object, e.g. RangeStart and RangeLocation. Each one will be read
55// independently by toByteArrays. Thus you can only pack messages if their data
56// doesn't overlap. Again, it's up to you to figure that out.
57struct Q_AUTOTEST_EXPORT QQuickProfilerData
58{
59 QQuickProfilerData() {}
60
61 QQuickProfilerData(qint64 time, int messageType, int detailType, const QUrl &url, int x = 0,
62 int y = 0, int framerate = 0, int count = 0) :
63 time(time), messageType(messageType), detailType(detailType), detailUrl(url), x(x), y(y),
64 framerate(framerate), count(count) {}
65
66 QQuickProfilerData(qint64 time, int messageType, int detailType, int framerateOrInputType = 0,
67 int countOrInputA = 0, int threadIdOrInputB = 0) :
68 time(time), messageType(messageType), detailType(detailType),
69 framerate(framerateOrInputType), count(countOrInputA), threadId(threadIdOrInputB) {}
70
71 // Special ctor for scenegraph frames. Note that it's missing the QString/QUrl params.
72 // This is slightly ugly, but makes it easier to disambiguate between int and qint64 params.
73 QQuickProfilerData(qint64 time, int messageType, int detailType, qint64 d1, qint64 d2,
74 qint64 d3, qint64 d4, qint64 d5) :
75 time(time), messageType(messageType), detailType(detailType), subtime_1(d1), subtime_2(d2),
76 subtime_3(d3), subtime_4(d4), subtime_5(d5) {}
77
78
79 qint64 time;
80 int messageType; //bit field of Message
81 int detailType;
82
83 QUrl detailUrl;
84
85 union {
86 qint64 subtime_1;
87 int x; //used for pixmaps
88 };
89
90 union {
91 qint64 subtime_2;
92 int y; //used for pixmaps
93 };
94
95 union {
96 qint64 subtime_3;
97 int framerate; //used by animation events
98 int inputType;
99 };
100
101 union {
102 qint64 subtime_4;
103 int count; //used by animation events and for pixmaps
104 int inputA; //used by input events
105 };
106
107 union {
108 qint64 subtime_5;
109 int threadId;
110 int inputB; //used by input events
111 };
112};
113
114Q_DECLARE_TYPEINFO(QQuickProfilerData, Q_RELOCATABLE_TYPE);
115
116class QQuickProfilerSceneGraphData : public QQmlProfilerDefinitions {
117private:
118 static const uint s_numSceneGraphTimings = 5;
119
120 template<uint size>
121 struct TimingData {
122 qint64 values[size][s_numSceneGraphTimings + 1];
123 };
124
125 static inline thread_local TimingData<NumRenderThreadFrameTypes> renderThreadTimings;
126 TimingData<NumGUIThreadFrameTypes> guiThreadTimings;
127
128public:
129 template<SceneGraphFrameType type>
130 qint64 *timings()
131 {
132 if constexpr (type < NumRenderThreadFrameTypes)
133 return renderThreadTimings.values[type];
134 else
135 return guiThreadTimings.values[type - NumRenderThreadFrameTypes];
136 }
137};
138
139class Q_QUICK_EXPORT QQuickProfiler : public QObject, public QQmlProfilerDefinitions {
140 Q_OBJECT
141public:
142
143 enum AnimationThread {
144 GuiThread,
145 RenderThread
146 };
147
148 enum SceneGraphContextStage {
149 SceneGraphContextStart,
150 SceneGraphContextMaterialCompile
151 };
152
153 enum SceneGraphRendererStage {
154 SceneGraphRendererStart,
155 SceneGraphRendererPreprocess,
156 SceneGraphRendererUpdate,
157 SceneGraphRendererBinding,
158 SceneGraphRendererRender
159 };
160
161 enum SceneGraphAdaptationLayerStage {
162 SceneGraphAdaptationLayerStart,
163 SceneGraphAdaptationLayerGlyphRender,
164 SceneGraphAdaptationLayerGlyphStore
165 };
166
167 enum SceneGraphRenderLoopStage {
168 SceneGraphRenderLoopStart,
169 SceneGraphRenderLoopSync,
170 SceneGraphRenderLoopRender,
171 SceneGraphRenderLoopSwap
172 };
173
174 enum SceneGraphPolishStage {
175 SceneGraphPolishStart,
176 SceneGraphPolishPolish
177 };
178
179 enum SceneGraphPolishAndSyncStage {
180 SceneGraphPolishAndSyncStart,
181 SceneGraphPolishAndSyncPolish,
182 SceneGraphPolishAndSyncWait,
183 SceneGraphPolishAndSyncSync,
184 SceneGraphPolishAndSyncAnimations
185 };
186
187 enum SceneGraphTexturePrepareStage {
188 SceneGraphTexturePrepareStart,
189 SceneGraphTexturePrepareBind,
190 SceneGraphTexturePrepareConvert,
191 SceneGraphTexturePrepareSwizzle,
192 SceneGraphTexturePrepareUpload,
193 SceneGraphTexturePrepareMipmap
194 };
195
196 enum SceneGraphTextureDeletionStage {
197 SceneGraphTextureDeletionStart,
198 SceneGraphTextureDeletionDelete
199 };
200
201 template<EventType DetailType, InputEventType InputType>
202 static void inputEvent(int x, int y = 0)
203 {
204 s_instance->processMessage(message: QQuickProfilerData(s_instance->timestamp(), 1 << Event,
205 1 << DetailType, InputType, x, y));
206 }
207
208 static void animationFrame(qint64 delta, AnimationThread threadId)
209 {
210 const qsizetype animCount = QUnifiedTimer::instance()->runningAnimationCount();
211
212 if (animCount > 0 && delta > 0) {
213 s_instance->processMessage(message: QQuickProfilerData(s_instance->timestamp(), 1 << Event,
214 1 << AnimationFrame, 1000 / (int)delta /* trim fps to integer */,
215 qt_saturate<int>(x: animCount),
216 threadId));
217 }
218 }
219
220 template<SceneGraphFrameType FrameType1, SceneGraphFrameType FrameType2>
221 static void startSceneGraphFrame()
222 {
223 startSceneGraphFrame<FrameType1>();
224 s_instance->m_sceneGraphData.timings<FrameType2>()[0] =
225 s_instance->m_sceneGraphData.timings<FrameType1>()[0];
226 }
227
228 template<SceneGraphFrameType FrameType>
229 static void startSceneGraphFrame()
230 {
231 s_instance->m_sceneGraphData.timings<FrameType>()[0] = s_instance->timestamp();
232 }
233
234 template<SceneGraphFrameType FrameType>
235 static void recordSceneGraphTimestamp(uint position)
236 {
237 s_instance->m_sceneGraphData.timings<FrameType>()[position] = s_instance->timestamp();
238 }
239
240 template<SceneGraphFrameType FrameType, uint Skip>
241 static void skipSceneGraphTimestamps(uint position)
242 {
243 qint64 *timings = s_instance->m_sceneGraphData.timings<FrameType>();
244 const qint64 last = timings[position];
245 for (uint i = 0; i < Skip; ++i)
246 timings[++position] = last;
247 }
248
249 template<SceneGraphFrameType FrameType, bool Record>
250 static void reportSceneGraphFrame(uint position, quint64 payload = ~0)
251 {
252 qint64 *timings = s_instance->m_sceneGraphData.timings<FrameType>();
253 if (Record)
254 timings[position] = s_instance->timestamp();
255 s_instance->processMessage(message: QQuickProfilerData(
256 timings[position], 1 << SceneGraphFrame, 1 << FrameType,
257 position > 0 ? timings[1] - timings[0] : payload,
258 position > 1 ? timings[2] - timings[1] : payload,
259 position > 2 ? timings[3] - timings[2] : payload,
260 position > 3 ? timings[4] - timings[3] : payload,
261 position > 4 ? timings[5] - timings[4] : payload));
262 }
263
264 template<SceneGraphFrameType FrameType, bool Record, SceneGraphFrameType SwitchTo>
265 static void reportSceneGraphFrame(uint position, quint64 payload = ~0)
266 {
267 reportSceneGraphFrame<FrameType, Record>(position, payload);
268 s_instance->m_sceneGraphData.timings<SwitchTo>()[0] =
269 s_instance->m_sceneGraphData.timings<FrameType>()[position];
270 }
271
272 template<PixmapEventType PixmapState>
273 static void pixmapStateChanged(const QUrl &url)
274 {
275 s_instance->processMessage(message: QQuickProfilerData(s_instance->timestamp(),
276 1 << PixmapCacheEvent, 1 << PixmapState, url));
277 }
278
279 static void pixmapLoadingFinished(const QUrl &url, const QSize &size)
280 {
281 s_instance->processMessage(message: QQuickProfilerData(s_instance->timestamp(),
282 1 << PixmapCacheEvent,
283 (1 << PixmapLoadingFinished) | ((size.width() > 0 && size.height() > 0) ? (1 << PixmapSizeKnown) : 0),
284 url, size.width(), size.height()));
285 }
286
287 template<PixmapEventType CountType>
288 static void pixmapCountChanged(const QUrl &url, int count)
289 {
290 s_instance->processMessage(message: QQuickProfilerData(s_instance->timestamp(),
291 1 << PixmapCacheEvent, 1 << CountType, url, 0, 0, 0, count));
292 }
293
294 static void registerAnimationCallback();
295
296 qint64 timestamp() { return m_timer.nsecsElapsed(); }
297
298 static quint64 featuresEnabled;
299
300 static void initialize(QObject *parent);
301
302 ~QQuickProfiler() override;
303
304Q_SIGNALS:
305 void dataReady(const QVector<QQuickProfilerData> &data);
306
307protected:
308 friend class QQuickProfilerAdapter;
309
310 static QQuickProfiler *s_instance;
311 QMutex m_dataMutex;
312 QElapsedTimer m_timer;
313 QVector<QQuickProfilerData> m_data;
314 QQuickProfilerSceneGraphData m_sceneGraphData;
315
316 QQuickProfiler(QObject *parent);
317
318 void processMessage(const QQuickProfilerData &message)
319 {
320 QMutexLocker lock(&m_dataMutex);
321 if (Q_LIKELY(m_data.isEmpty() || m_data.last().time <= message.time)) {
322 m_data.append(t: message);
323 return;
324 }
325
326 // Since the scenegraph data is recorded from different threads, contention for the lock
327 // can cause it to be processed out of order here. Insert the message at the right place.
328 const auto it = std::find_if(
329 first: m_data.rbegin(), last: m_data.rend(),
330 pred: [t = message.time](const QQuickProfilerData &i) { return i.time <= t; });
331 m_data.insert(before: it.base(), t: message);
332 }
333
334 void startProfilingImpl(quint64 features);
335 void stopProfilingImpl();
336 void reportDataImpl();
337 void setTimer(const QElapsedTimer &t);
338};
339
340#endif // QT_CONFIG(qml_debug)
341
342#define Q_QUICK_PROFILE(feature, Method)\
343 Q_QUICK_PROFILE_IF_ENABLED(feature, QQuickProfiler::Method)
344
345// Record current timestamp for \a Type at position 0.
346#define Q_QUICK_SG_PROFILE_START(Type)\
347 Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
348 (QQuickProfiler::startSceneGraphFrame<Type>()))
349
350// Record current timestamp for \a Type at \a position.
351#define Q_QUICK_SG_PROFILE_RECORD(Type, position)\
352 Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
353 (QQuickProfiler::recordSceneGraphTimestamp<Type>(position)))
354
355// Use the timestamp for \a Type at position \a position and repeat it \a Skip times in subsequent
356// positions.
357#define Q_QUICK_SG_PROFILE_SKIP(Type, position, Skip)\
358 Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
359 (QQuickProfiler::skipSceneGraphTimestamps<Type, Skip>(position)))
360
361// Record current timestamp for both \a Type1 and \a Type2 at position 0.
362#define Q_QUICK_SG_PROFILE_START_SYNCHRONIZED(Type1, Type2)\
363 Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
364 (QQuickProfiler::startSceneGraphFrame<Type1, Type2>()))
365
366// report \a Type1, using the current timestamp at \a position, and switch to \a Typ2, using
367// the current timestamp at position 0.
368#define Q_QUICK_SG_PROFILE_SWITCH(Type1, Type2, position)\
369 Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
370 (QQuickProfiler::reportSceneGraphFrame<Type1, true, Type2>(\
371 position)))
372
373// report \a Type, using data points 0 to \a position, including \a position.
374#define Q_QUICK_SG_PROFILE_REPORT(Type, position)\
375 Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
376 (QQuickProfiler::reportSceneGraphFrame<Type, false>(position)))
377
378// report \a Type, using data points 0 to \a position, including \a position, and setting the
379// timestamp at \a position to the current one.
380#define Q_QUICK_SG_PROFILE_END(Type, position)\
381 Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
382 (QQuickProfiler::reportSceneGraphFrame<Type, true>(position)))
383
384// report \a Type, using data points 0 to \a position, including \a position, and setting the
385// timestamp at \a position to the current one. Remaining data points up to position 5 are filled
386// with \a Payload.
387#define Q_QUICK_SG_PROFILE_END_WITH_PAYLOAD(Type, position, Payload)\
388 Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileSceneGraph,\
389 (QQuickProfiler::reportSceneGraphFrame<Type, true>(position,\
390 Payload)))
391
392#define Q_QUICK_INPUT_PROFILE(Type, DetailType, A, B)\
393 Q_QUICK_PROFILE_IF_ENABLED(QQuickProfiler::ProfileInputEvents,\
394 (QQuickProfiler::inputEvent<Type, DetailType>(A, B)))
395
396QT_END_NAMESPACE
397
398#endif
399

source code of qtdeclarative/src/quick/util/qquickprofiler_p.h