1 | // Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB). |
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 QT3DRENDER_RENDER_FRAMEPROFILER_P_H |
5 | #define QT3DRENDER_RENDER_FRAMEPROFILER_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 for the convenience |
12 | // of other Qt classes. 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 <QOpenGLTimeMonitor> |
19 | #include <Qt3DCore/private/qthreadpooler_p.h> |
20 | #include <Qt3DCore/private/qt3dcore_global_p.h> |
21 | #include <memory> |
22 | |
23 | QT_BEGIN_NAMESPACE |
24 | |
25 | #if !defined(QT_NO_OPENGL) && !QT_CONFIG(opengles2) |
26 | #define QT3D_SUPPORTS_GL_MONITOR |
27 | #endif |
28 | |
29 | namespace Qt3DCore { |
30 | class QSystemInformationService; |
31 | } |
32 | |
33 | namespace Qt3DRender { |
34 | |
35 | namespace Render { |
36 | |
37 | namespace Profiling { |
38 | |
39 | enum RecordingType |
40 | { |
41 | DrawArray = 512, |
42 | DrawElement, |
43 | DispatchCompute, |
44 | StateUpdate, |
45 | UniformUpdate, |
46 | ShaderUpdate, |
47 | TextureUpload, |
48 | BufferUpload, |
49 | ShaderUpload, |
50 | ClearBuffer, |
51 | VAOUpdate, |
52 | VAOUpload, |
53 | RenderTargetUpdate |
54 | }; |
55 | |
56 | class FrameTimeRecorder |
57 | { |
58 | public: |
59 | FrameTimeRecorder(Qt3DCore::QSystemInformationService *service) |
60 | : m_service(service) |
61 | { |
62 | } |
63 | |
64 | ~FrameTimeRecorder() |
65 | { |
66 | } |
67 | |
68 | void init(int eventCount) |
69 | { |
70 | #ifdef QT3D_SUPPORTS_GL_MONITOR |
71 | if (m_monitor.isCreated()) { |
72 | m_remainingEvents = m_monitor.sampleCount(); |
73 | reset(); |
74 | } else { |
75 | m_monitor.setSampleCount(eventCount * 2); |
76 | m_monitor.create(); |
77 | m_remainingEvents = eventCount; |
78 | } |
79 | #else |
80 | m_remainingEvents = eventCount; |
81 | #endif |
82 | } |
83 | |
84 | void startRecordEvent() |
85 | { |
86 | #ifdef QT3D_SUPPORTS_GL_MONITOR |
87 | m_monitor.recordSample(); |
88 | #endif |
89 | --m_remainingEvents; |
90 | } |
91 | |
92 | void recordEvent(RecordingType type) |
93 | { |
94 | #ifdef QT3D_SUPPORTS_GL_MONITOR |
95 | m_monitor.recordSample(); |
96 | #endif |
97 | --m_remainingEvents; |
98 | |
99 | GLRecording rec; |
100 | rec.type = type; |
101 | rec.startTime = Qt3DCore::QSystemInformationServicePrivate::get(q: m_service)->m_jobsStatTimer.nsecsElapsed(); |
102 | m_recordings.push_back(t: rec); |
103 | } |
104 | |
105 | void reset() |
106 | { |
107 | #ifdef QT3D_SUPPORTS_GL_MONITOR |
108 | m_monitor.reset(); |
109 | #endif |
110 | m_recordings.clear(); |
111 | } |
112 | |
113 | inline bool canStillRecord() { return m_remainingEvents > 0; } |
114 | |
115 | bool tryWriteResults() |
116 | { |
117 | #ifdef QT3D_SUPPORTS_GL_MONITOR |
118 | if (m_monitor.isResultAvailable()) { |
119 | const auto &samples = m_monitor.waitForSamples(); |
120 | Q_ASSERT(samples.size() >= 2 * m_recordings.size()); |
121 | |
122 | Qt3DCore::QSystemInformationServicePrivate *dservice = Qt3DCore::QSystemInformationServicePrivate::get(q: m_service); |
123 | |
124 | qsizetype j = 0; |
125 | for (qsizetype i = 0, m = m_recordings.size(); i < m; ++i) { |
126 | const GLRecording rec = m_recordings.at(i); |
127 | Qt3DCore::QSystemInformationServicePrivate::JobRunStats glRecordingStat; |
128 | |
129 | glRecordingStat.jobId.typeAndInstance[0] = rec.type; |
130 | glRecordingStat.jobId.typeAndInstance[1] = 0; |
131 | glRecordingStat.threadId = FrameTimeRecorder::GLThreadID; |
132 | glRecordingStat.startTime = rec.startTime; |
133 | glRecordingStat.endTime = rec.startTime + (samples.at(i: j + 1) - (samples.at(i: j))); |
134 | |
135 | dservice->addSubmissionLogStatsEntry(stats&: glRecordingStat); |
136 | j += 2; |
137 | } |
138 | return true; |
139 | } |
140 | #endif |
141 | return false; |
142 | } |
143 | |
144 | private: |
145 | struct GLRecording |
146 | { |
147 | RecordingType type; |
148 | qint64 startTime; |
149 | }; |
150 | |
151 | static const int GLThreadID = 0x454; |
152 | |
153 | Qt3DCore::QSystemInformationService *m_service; |
154 | #ifdef QT3D_SUPPORTS_GL_MONITOR |
155 | QOpenGLTimeMonitor m_monitor; |
156 | #endif |
157 | QList<GLRecording> m_recordings; |
158 | int m_remainingEvents = 0; |
159 | }; |
160 | |
161 | class FrameProfiler |
162 | { |
163 | public: |
164 | FrameProfiler(Qt3DCore::QSystemInformationService *service) |
165 | : m_service(service) |
166 | , m_currentRecorder(nullptr) |
167 | {} |
168 | |
169 | ~FrameProfiler() |
170 | { |
171 | qDeleteAll(c: m_recorders); |
172 | } |
173 | |
174 | void startRecordEvent() |
175 | { |
176 | if (m_currentRecorder == nullptr) { |
177 | if (!m_availableRecorders.empty()) { |
178 | m_currentRecorder = m_availableRecorders.takeFirst(); |
179 | } else { |
180 | m_recorders.push_back(t: new FrameTimeRecorder(m_service)); |
181 | m_currentRecorder = m_recorders.last(); |
182 | } |
183 | // We record events 10 by 10 |
184 | m_currentRecorder->init(eventCount: 10); |
185 | } |
186 | m_currentRecorder->startRecordEvent(); |
187 | } |
188 | |
189 | void recordEvent(RecordingType type) |
190 | { |
191 | m_currentRecorder->recordEvent(type); |
192 | if (!m_currentRecorder->canStillRecord()) { |
193 | m_busyRecorders.push_back(t: m_currentRecorder); |
194 | m_currentRecorder = nullptr; |
195 | } |
196 | } |
197 | |
198 | void writeResults() |
199 | { |
200 | for (int i = m_busyRecorders.size() - 1; i >= 0; --i) { |
201 | FrameTimeRecorder *recorder = m_busyRecorders.at(i); |
202 | if (recorder->tryWriteResults()) { |
203 | m_availableRecorders.push_back(t: m_busyRecorders.takeAt(i)); |
204 | } |
205 | } |
206 | } |
207 | |
208 | private: |
209 | Qt3DCore::QSystemInformationService *m_service; |
210 | QList<FrameTimeRecorder *> m_recorders; |
211 | QList<FrameTimeRecorder *> m_availableRecorders; |
212 | QList<FrameTimeRecorder *> m_busyRecorders; |
213 | FrameTimeRecorder *m_currentRecorder; |
214 | }; |
215 | |
216 | |
217 | class GLTimeRecorder |
218 | { |
219 | public: |
220 | explicit GLTimeRecorder(RecordingType type, FrameProfiler *profiler) |
221 | : m_type(type) |
222 | , m_frameProfiler(profiler) |
223 | { |
224 | if (m_frameProfiler) |
225 | m_frameProfiler->startRecordEvent(); |
226 | } |
227 | |
228 | ~GLTimeRecorder() |
229 | { |
230 | if (m_frameProfiler) |
231 | m_frameProfiler->recordEvent(type: m_type); |
232 | } |
233 | |
234 | private: |
235 | RecordingType m_type; |
236 | FrameProfiler *m_frameProfiler; |
237 | }; |
238 | |
239 | } // Profiling |
240 | |
241 | } // Render |
242 | |
243 | } // Qt3DRender |
244 | |
245 | QT_END_NAMESPACE |
246 | |
247 | #endif // QT3DRENDER_RENDER_FRAMEPROFILER_P_H |
248 | |