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
23QT_BEGIN_NAMESPACE
24
25#if !defined(QT_NO_OPENGL) && !QT_CONFIG(opengles2)
26#define QT3D_SUPPORTS_GL_MONITOR
27#endif
28
29namespace Qt3DCore {
30class QSystemInformationService;
31}
32
33namespace Qt3DRender {
34
35namespace Render {
36
37namespace Profiling {
38
39enum 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
56class FrameTimeRecorder
57{
58public:
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
144private:
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
161class FrameProfiler
162{
163public:
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
208private:
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
217class GLTimeRecorder
218{
219public:
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
234private:
235 RecordingType m_type;
236 FrameProfiler *m_frameProfiler;
237};
238
239} // Profiling
240
241} // Render
242
243} // Qt3DRender
244
245QT_END_NAMESPACE
246
247#endif // QT3DRENDER_RENDER_FRAMEPROFILER_P_H
248

source code of qt3d/src/plugins/renderers/opengl/renderer/frameprofiler_p.h