1/****************************************************************************
2**
3** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt3D module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
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 General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#ifndef QT3DRENDER_RENDER_FRAMEPROFILER_P_H
30#define QT3DRENDER_RENDER_FRAMEPROFILER_P_H
31
32//
33// W A R N I N G
34// -------------
35//
36// This file is not part of the Qt API. It exists for the convenience
37// of other Qt classes. This header file may change from version to
38// version without notice, or even be removed.
39//
40// We mean it.
41//
42
43#include <QOpenGLTimeMonitor>
44#include <Qt3DCore/private/qthreadpooler_p.h>
45#include <Qt3DCore/private/qt3dcore_global_p.h>
46#include <memory>
47
48QT_BEGIN_NAMESPACE
49
50namespace Qt3DRender {
51
52namespace Render {
53
54namespace Profiling {
55
56enum RecordingType
57{
58 DrawArray = 512,
59 DrawElement,
60 DispatchCompute,
61 StateUpdate,
62 UniformUpdate,
63 ShaderUpdate,
64 TextureUpload,
65 BufferUpload,
66 ShaderUpload,
67 ClearBuffer,
68 VAOUpdate,
69 VAOUpload,
70 RenderTargetUpdate
71};
72
73#if QT_CONFIG(qt3d_profile_gl)
74
75class FrameTimeRecorder
76{
77public:
78 FrameTimeRecorder()
79 {}
80
81 ~FrameTimeRecorder()
82 {
83 }
84
85 void init(int eventCount)
86 {
87 if (m_monitor.isCreated()) {
88 m_remainingEvents = m_monitor.sampleCount();
89 reset();
90 } else {
91 m_monitor.setSampleCount(eventCount * 2);
92 m_monitor.create();
93 m_remainingEvents = eventCount;
94 }
95 }
96
97 void startRecordEvent()
98 {
99 m_monitor.recordSample();
100 --m_remainingEvents;
101 }
102
103 void recordEvent(RecordingType type)
104 {
105 m_monitor.recordSample();
106 --m_remainingEvents;
107
108 GLRecording rec;
109 rec.type = type;
110 rec.startTime = Qt3DCore::QThreadPooler::m_jobsStatTimer.nsecsElapsed();
111 m_recordings.push_back(rec);
112 }
113
114 void reset()
115 {
116 m_monitor.reset();
117 m_recordings.clear();
118 }
119
120 inline bool canStillRecord() { return m_remainingEvents > 0; }
121
122 bool tryWriteResults()
123 {
124 if (m_monitor.isResultAvailable()) {
125 const QVector<GLuint64> samples = m_monitor.waitForSamples();
126 Q_ASSERT(samples.count() >= 2 * m_recordings.count());
127
128 int j = 0;
129 for (int i = 0, m = m_recordings.size(); i < m; ++i) {
130 const GLRecording rec = m_recordings.at(i);
131 Qt3DCore::JobRunStats glRecordingStat;
132
133 glRecordingStat.jobId.typeAndInstance[0] = rec.type;
134 glRecordingStat.jobId.typeAndInstance[1] = 0;
135 glRecordingStat.threadId = FrameTimeRecorder::GLThreadID;
136 glRecordingStat.startTime = rec.startTime;
137 glRecordingStat.endTime = rec.startTime + (samples.at(j + 1) - (samples.at(j)));
138
139 Qt3DCore::QThreadPooler::addSubmissionLogStatsEntry(glRecordingStat);
140 j += 2;
141 }
142 return true;
143 }
144 return false;
145 }
146
147private:
148 struct GLRecording
149 {
150 RecordingType type;
151 qint64 startTime;
152 };
153
154 static const int GLThreadID = 0x454;
155
156 QOpenGLTimeMonitor m_monitor;
157 QVector<GLRecording> m_recordings;
158 int m_remainingEvents = 0;
159};
160
161class FrameProfiler
162{
163public:
164 FrameProfiler()
165 : m_currentRecorder(nullptr)
166 {}
167
168 ~FrameProfiler()
169 {
170 qDeleteAll(m_recorders);
171 }
172
173 void startRecordEvent()
174 {
175 if (m_currentRecorder == nullptr) {
176 if (!m_availableRecorders.empty()) {
177 m_currentRecorder = m_availableRecorders.takeFirst();
178 } else {
179 m_recorders.push_back(new FrameTimeRecorder());
180 m_currentRecorder = m_recorders.last();
181 }
182 // We record events 10 by 10
183 m_currentRecorder->init(10);
184 }
185 m_currentRecorder->startRecordEvent();
186 }
187
188 void recordEvent(RecordingType type)
189 {
190 m_currentRecorder->recordEvent(type);
191 if (!m_currentRecorder->canStillRecord()) {
192 m_busyRecorders.push_back(m_currentRecorder);
193 m_currentRecorder = nullptr;
194 }
195 }
196
197 void writeResults()
198 {
199 for (int i = m_busyRecorders.size() - 1; i >= 0; --i) {
200 FrameTimeRecorder *recorder = m_busyRecorders.at(i);
201 if (recorder->tryWriteResults()) {
202 m_availableRecorders.push_back(m_busyRecorders.takeAt(i));
203 }
204 }
205 }
206
207private:
208 QVector<FrameTimeRecorder *> m_recorders;
209 QVector<FrameTimeRecorder *> m_availableRecorders;
210 QVector<FrameTimeRecorder *> m_busyRecorders;
211 FrameTimeRecorder *m_currentRecorder;
212};
213
214#endif
215
216class GLTimeRecorder
217{
218public:
219 explicit GLTimeRecorder(RecordingType type)
220 : m_type(type)
221 {
222#if QT_CONFIG(qt3d_profile_gl)
223 frameProfiler.startRecordEvent();
224#endif
225 }
226
227 ~GLTimeRecorder()
228 {
229#if QT_CONFIG(qt3d_profile_gl)
230 frameProfiler.recordEvent(m_type);
231#else
232 Q_UNUSED(m_type);
233#endif
234 }
235
236 static void writeResults()
237 {
238#if QT_CONFIG(qt3d_profile_gl)
239 frameProfiler.writeResults();
240#endif
241 }
242
243private:
244#if QT_CONFIG(qt3d_profile_gl)
245 static FrameProfiler frameProfiler;
246#endif
247 RecordingType m_type;
248};
249
250#if QT_CONFIG(qt3d_profile_gl)
251FrameProfiler GLTimeRecorder::frameProfiler;
252#endif
253
254} // Profiling
255
256} // Render
257
258} // Qt3DRender
259
260QT_END_NAMESPACE
261
262#endif // QT3DRENDER_RENDER_FRAMEPROFILER_P_H
263