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