blob: fec8dcdef3861871a20ac7691a96b57308dc3a7a [file] [log] [blame]
Alexander Trukhinb90d8002015-08-14 15:55:14 +03001#include "metric_backend_opengl.hpp"
2#include "os_time.hpp"
3#include "os_memory.hpp"
4
5void
6MetricBackend_opengl::Storage::addData(QueryBoundary boundary, int64_t data) {
7 this->data[boundary].push_back(data);
8}
9
10int64_t* MetricBackend_opengl::Storage::getData(QueryBoundary boundary,
11 unsigned eventId)
12{
13 return &(data[boundary][eventId]);
14}
15
16Metric_opengl::Metric_opengl(unsigned gId, unsigned id, const std::string &name,
17 const std::string &desc, MetricNumType nT, MetricType t)
18 : m_gId(gId), m_id(id), m_name(name), m_desc(desc), m_nType(nT),
19 m_type(t), available(false)
20{
21 for (int i = 0; i < QUERY_BOUNDARY_LIST_END; i++) {
22 profiled[i] = false;
23 enabled[i] = false;
24 }
25}
26
27unsigned Metric_opengl::id() {
28 return m_id;
29}
30
31unsigned Metric_opengl::groupId() {
32 return m_gId;
33}
34
35std::string Metric_opengl::name() {
36 return m_name;
37}
38
39std::string Metric_opengl::description() {
40 return m_desc;
41}
42
43MetricNumType Metric_opengl::numType() {
44 return m_nType;
45}
46
47MetricType Metric_opengl::type() {
48 return m_type;
49}
50
Alexander Trukhinca8139c2015-08-26 14:58:26 +030051MetricBackend_opengl::MetricBackend_opengl(glretrace::Context* context,
52 MmapAllocator<char> &alloc)
53 : alloc(alloc)
Alexander Trukhinb90d8002015-08-14 15:55:14 +030054{
55 glprofile::Profile currentProfile = context->actualProfile();
56 supportsTimestamp = currentProfile.versionGreaterOrEqual(glprofile::API_GL, 3, 3) ||
57 context->hasExtension("GL_ARB_timer_query");
58 supportsElapsed = context->hasExtension("GL_EXT_timer_query") || supportsTimestamp;
59 supportsOcclusion = currentProfile.versionGreaterOrEqual(glprofile::API_GL, 1, 5);
60
61 #ifdef __APPLE__
62 // GL_TIMESTAMP doesn't work on Apple. GL_TIME_ELAPSED still does however.
63 // http://lists.apple.com/archives/mac-opengl/2014/Nov/threads.html#00001
64 supportsTimestamp = false;
65 #endif
66
67 // Add metrics below
68 metrics.emplace_back(0, 0, "CPU Start", "", CNT_NUM_INT64, CNT_TYPE_TIMESTAMP);
69 metrics.emplace_back(0, 1, "CPU Duration", "", CNT_NUM_INT64, CNT_TYPE_DURATION);
70 metrics.emplace_back(1, 0, "GPU Start", "", CNT_NUM_INT64, CNT_TYPE_TIMESTAMP);
71 metrics.emplace_back(1, 1, "GPU Duration", "", CNT_NUM_INT64, CNT_TYPE_DURATION);
72 metrics.emplace_back(1, 2, "Pixels Drawn", "", CNT_NUM_INT64, CNT_TYPE_GENERIC);
73 metrics.emplace_back(0, 2, "VSIZE Start", "", CNT_NUM_INT64, CNT_TYPE_GENERIC);
74 metrics.emplace_back(0, 3, "VSIZE Duration", "", CNT_NUM_INT64, CNT_TYPE_GENERIC);
75 metrics.emplace_back(0, 4, "RSS Start", "", CNT_NUM_INT64, CNT_TYPE_GENERIC);
76 metrics.emplace_back(0, 5, "RSS Duration", "", CNT_NUM_INT64, CNT_TYPE_GENERIC);
77
78 metrics[METRIC_CPU_START].available = true;
79 metrics[METRIC_CPU_DURATION].available = true;
80 metrics[METRIC_CPU_VSIZE_START].available = true;
81 metrics[METRIC_CPU_VSIZE_DURATION].available = true;
82 metrics[METRIC_CPU_RSS_START].available = true;
83 metrics[METRIC_CPU_RSS_DURATION].available = true;
84 if (supportsTimestamp) metrics[METRIC_GPU_START].available = true;
85 if (supportsElapsed) {
86 GLint bits = 0;
87 glGetQueryiv(GL_TIME_ELAPSED, GL_QUERY_COUNTER_BITS, &bits);
88 if (bits) metrics[METRIC_GPU_DURATION].available = true;
89 }
90 if (supportsOcclusion) {
91 metrics[METRIC_GPU_PIXELS].available = true;
92 }
93
94 // populate lookups
95 for (auto &m : metrics) {
96 idLookup[std::make_pair(m.groupId(), m.id())] = &m;
97 nameLookup[m.name()] = &m;
98 }
99}
100
101int64_t MetricBackend_opengl::getCurrentTime(void) {
102 if (supportsTimestamp && cpugpuSync) {
103 /* Get the current GL time without stalling */
104 GLint64 timestamp = 0;
105 glGetInteger64v(GL_TIMESTAMP, &timestamp);
106 return timestamp;
107 } else {
108 return os::getTime();
109 }
110}
111
112int64_t MetricBackend_opengl::getTimeFrequency(void) {
113 if (supportsTimestamp && cpugpuSync) {
114 return 1000000000;
115 } else {
116 return os::timeFrequency;
117 }
118}
119
120
121bool MetricBackend_opengl::isSupported() {
122 return true;
123 // though individual metrics might be not supported
124}
125
126void MetricBackend_opengl::enumGroups(enumGroupsCallback callback, void* userData) {
127 callback(0, 0, userData); // cpu group
128 callback(1, 0, userData); // gpu group
129}
130
131std::string MetricBackend_opengl::getGroupName(unsigned group) {
132 switch(group) {
133 case 0:
134 return "CPU";
135 case 1:
136 return "GPU";
137 default:
138 return "";
139 }
140}
141
142void MetricBackend_opengl::enumMetrics(unsigned group, enumMetricsCallback callback, void* userData) {
143 for (auto &m : metrics) {
144 if (m.groupId() == group && m.available) {
145 callback(&m, 0, userData);
146 }
147 }
148}
149
150std::unique_ptr<Metric>
151MetricBackend_opengl::getMetricById(unsigned groupId, unsigned metricId) {
152 auto entryToCopy = idLookup.find(std::make_pair(groupId, metricId));
153 if (entryToCopy != idLookup.end()) {
154 return std::unique_ptr<Metric>(new Metric_opengl(*entryToCopy->second));
155 } else {
156 return nullptr;
157 }
158}
159
160std::unique_ptr<Metric>
161MetricBackend_opengl::getMetricByName(std::string metricName) {
162 auto entryToCopy = nameLookup.find(metricName);
163 if (entryToCopy != nameLookup.end()) {
164 return std::unique_ptr<Metric>(new Metric_opengl(*entryToCopy->second));
165 } else {
166 return nullptr;
167 }
168}
169
170
171int MetricBackend_opengl::enableMetric(Metric* metric, QueryBoundary pollingRule) {
172 // metric is not necessarily the same object as in metrics[]
173 auto entry = idLookup.find(std::make_pair(metric->groupId(), metric->id()));
174 if ((entry != idLookup.end()) && entry->second->available) {
175 entry->second->enabled[pollingRule] = true;
176 return 0;
177 }
178 return 1;
179}
180
181unsigned MetricBackend_opengl::generatePasses() {
182 // draw calls profiling not needed if all calls are profiled
183 for (int i = 0; i < METRIC_LIST_END; i++) {
184 if (metrics[i].enabled[QUERY_BOUNDARY_CALL]) {
185 metrics[i].enabled[QUERY_BOUNDARY_DRAWCALL] = false;
186 }
187 }
188 // setup storage for profiled metrics
189 for (int i = 0; i < METRIC_LIST_END; i++) {
190 for (int j = 0; j < QUERY_BOUNDARY_LIST_END; j++) {
191 if (metrics[i].enabled[j]) {
Alexander Trukhinca8139c2015-08-26 14:58:26 +0300192 data[i][j] = std::unique_ptr<Storage>(new Storage(alloc));
Alexander Trukhinb90d8002015-08-14 15:55:14 +0300193 }
194 }
195 }
196 // check if GL queries are needed
197 glQueriesNeededAnyBoundary = false;
198 for (int i = 0; i < QUERY_BOUNDARY_LIST_END; i++) {
199 if (metrics[METRIC_GPU_START].enabled[i] ||
200 metrics[METRIC_GPU_DURATION].enabled[i] ||
201 metrics[METRIC_GPU_PIXELS].enabled[i])
202 {
203 glQueriesNeeded[i] = true;
204 glQueriesNeededAnyBoundary = true;
205 } else {
206 glQueriesNeeded[i] = false;
207 }
208 }
209 // check if CPU <-> GPU sync is required
210 // this is the case if any gpu time is requested
211 cpugpuSync = false;
212 for (int i = 0; i < QUERY_BOUNDARY_LIST_END; i++) {
213 if (metrics[METRIC_GPU_START].enabled[i] ||
214 metrics[METRIC_GPU_DURATION].enabled[i])
215 {
216 cpugpuSync = true;
217 break;
218 }
219 }
220 // check if two passes are needed
221 // GL_TIME_ELAPSED (gpu dur) and GL_SAMPLES_PASSED (pixels) cannot be nested
222 if (!supportsTimestamp &&
223 metrics[METRIC_GPU_DURATION].enabled[QUERY_BOUNDARY_FRAME] &&
224 (metrics[METRIC_GPU_DURATION].enabled[QUERY_BOUNDARY_CALL] ||
225 metrics[METRIC_GPU_DURATION].enabled[QUERY_BOUNDARY_DRAWCALL]))
226 {
227 twoPasses = true;
228 }
229 if (metrics[METRIC_GPU_PIXELS].enabled[QUERY_BOUNDARY_FRAME] &&
230 (metrics[METRIC_GPU_PIXELS].enabled[QUERY_BOUNDARY_CALL] ||
231 metrics[METRIC_GPU_PIXELS].enabled[QUERY_BOUNDARY_DRAWCALL]))
232 {
233 twoPasses = true;
234 }
235
236 curPass = 1;
237 return twoPasses ? 2 : 1;
238}
239
240void MetricBackend_opengl::beginPass() {
241 if (curPass == 1) {
242 for (int i = 0; i < QUERY_BOUNDARY_LIST_END; i++) {
243 for (auto &m : metrics) {
244 if (m.enabled[i]) m.profiled[i] = true;
245 }
246 }
247 // profile frames in first pass
248 if (twoPasses) {
249 if (!supportsTimestamp) {
250 metrics[METRIC_GPU_DURATION].profiled[QUERY_BOUNDARY_DRAWCALL] = false;
251 metrics[METRIC_GPU_DURATION].profiled[QUERY_BOUNDARY_CALL] = false;
252 }
253 metrics[METRIC_GPU_PIXELS].profiled[QUERY_BOUNDARY_DRAWCALL] = false;
254 metrics[METRIC_GPU_PIXELS].profiled[QUERY_BOUNDARY_CALL] = false;
255 }
256 }
257 else if (curPass == 2) {
258 for (int i = 0; i < QUERY_BOUNDARY_LIST_END; i++) {
259 for (auto &m : metrics) {
260 m.profiled[i] = false;
261 }
262 }
263 // profile calls/draw calls in second pass
264 if (!supportsTimestamp) {
265 if (metrics[METRIC_GPU_DURATION].enabled[QUERY_BOUNDARY_DRAWCALL]) {
266 metrics[METRIC_GPU_DURATION].profiled[QUERY_BOUNDARY_DRAWCALL] = true;
267 }
268 if (metrics[METRIC_GPU_DURATION].enabled[QUERY_BOUNDARY_CALL]) {
269 metrics[METRIC_GPU_DURATION].profiled[QUERY_BOUNDARY_CALL] = true;
270 }
271 }
272 if (metrics[METRIC_GPU_PIXELS].enabled[QUERY_BOUNDARY_DRAWCALL]) {
273 metrics[METRIC_GPU_PIXELS].profiled[QUERY_BOUNDARY_DRAWCALL] = true;
274 }
275 if (metrics[METRIC_GPU_PIXELS].enabled[QUERY_BOUNDARY_CALL]) {
276 metrics[METRIC_GPU_PIXELS].profiled[QUERY_BOUNDARY_CALL] = true;
277 }
278 }
279 // setup times
280 cpuTimeScale = 1.0E9 / getTimeFrequency();
281 baseTime = getCurrentTime() * cpuTimeScale;
282}
283
284void MetricBackend_opengl::processQueries() {
285 int64_t gpuStart, gpuEnd, pixels;
286 for (int i = 0; i < QUERY_BOUNDARY_LIST_END; i++) {
287 QueryBoundary boundary = static_cast<QueryBoundary>(i);
288 while (!queries[i].empty()) {
289 auto &query = queries[i].front();
290 if (metrics[METRIC_GPU_START].profiled[i]) {
291 glGetQueryObjecti64v(query[QUERY_GPU_START], GL_QUERY_RESULT,
292 &gpuStart);
293 int64_t value = gpuStart - baseTime;
294 data[METRIC_GPU_START][i]->addData(boundary, value);
295 }
296 if (metrics[METRIC_GPU_DURATION].profiled[i]) {
297 if (supportsTimestamp) {
298 glGetQueryObjecti64v(query[QUERY_GPU_DURATION], GL_QUERY_RESULT,
299 &gpuEnd);
300 gpuEnd -= gpuStart;
301 } else {
302 glGetQueryObjecti64vEXT(query[QUERY_GPU_DURATION], GL_QUERY_RESULT,
303 &gpuEnd);
304 }
305 data[METRIC_GPU_DURATION][i]->addData(boundary, gpuEnd);
306 }
307 if (metrics[METRIC_GPU_PIXELS].profiled[i]) {
308 if (supportsTimestamp) {
309 glGetQueryObjecti64v(query[QUERY_OCCLUSION], GL_QUERY_RESULT, &pixels);
310 } else if (supportsElapsed) {
311 glGetQueryObjecti64vEXT(query[QUERY_OCCLUSION], GL_QUERY_RESULT, &pixels);
312 } else {
313 uint32_t pixels32;
314 glGetQueryObjectuiv(query[QUERY_OCCLUSION], GL_QUERY_RESULT, &pixels32);
315 pixels = static_cast<int64_t>(pixels32);
316 }
317 data[METRIC_GPU_PIXELS][i]->addData(boundary, pixels);
318 }
319 glDeleteQueries(QUERY_LIST_END, query.data());
320 queries[i].pop();
321 }
322 }
323}
324
325void MetricBackend_opengl::endPass() {
326 // process rest of the queries (it can be the last frame)
Alexander Trukhinb90d8002015-08-14 15:55:14 +0300327 processQueries();
328 curPass++;
329}
330
Alexander Trukhin1d0bf772015-08-24 23:17:43 +0300331void MetricBackend_opengl::pausePass() {
332 if (queryInProgress[QUERY_BOUNDARY_FRAME]) endQuery(QUERY_BOUNDARY_FRAME);
333 processQueries();
334}
335
336void MetricBackend_opengl::continuePass() {
337 // TODO if context switches check what it actually supports
338}
339
Alexander Trukhinb90d8002015-08-14 15:55:14 +0300340void MetricBackend_opengl::beginQuery(QueryBoundary boundary) {
341 // GPU related
342 if (glQueriesNeeded[boundary]) {
343 std::array<GLuint, QUERY_LIST_END> query;
344 glGenQueries(QUERY_LIST_END, query.data());
345
346 if (metrics[METRIC_GPU_START].profiled[boundary] ||
347 (metrics[METRIC_GPU_DURATION].profiled[boundary] && supportsTimestamp))
348 {
349 glQueryCounter(query[QUERY_GPU_START], GL_TIMESTAMP);
350 }
351 if (metrics[METRIC_GPU_DURATION].profiled[boundary] && !supportsTimestamp) {
352 glBeginQuery(GL_TIME_ELAPSED, query[QUERY_GPU_DURATION]);
353 }
354 if (metrics[METRIC_GPU_PIXELS].profiled[boundary]) {
355 glBeginQuery(GL_SAMPLES_PASSED, query[QUERY_OCCLUSION]);
356 }
357 queries[boundary].push(std::move(query));
358 }
359
360
361 // CPU related
362 if (metrics[METRIC_CPU_START].profiled[boundary] ||
363 metrics[METRIC_CPU_DURATION].profiled[boundary])
364 {
365 cpuStart[boundary] = getCurrentTime();
366 if (metrics[METRIC_CPU_START].profiled[boundary]) {
367 int64_t time = cpuStart[boundary] * cpuTimeScale - baseTime;
368 data[METRIC_CPU_START][boundary]->addData(boundary, time);
369 }
370 }
371 if (metrics[METRIC_CPU_VSIZE_START].profiled[boundary] ||
372 metrics[METRIC_CPU_VSIZE_DURATION].profiled[boundary])
373 {
374 vsizeStart[boundary] = os::getVsize();
375 if (metrics[METRIC_CPU_VSIZE_START].profiled[boundary]) {
376 int64_t time = vsizeStart[boundary];
377 data[METRIC_CPU_VSIZE_START][boundary]->addData(boundary, time);
378 }
379 }
380 if (metrics[METRIC_CPU_RSS_START].profiled[boundary] ||
381 metrics[METRIC_CPU_RSS_DURATION].profiled[boundary])
382 {
383 rssStart[boundary] = os::getRss();
384 if (metrics[METRIC_CPU_RSS_START].profiled[boundary]) {
385 int64_t time = rssStart[boundary];
386 data[METRIC_CPU_RSS_START][boundary]->addData(boundary, time);
387 }
388 }
Alexander Trukhin1d0bf772015-08-24 23:17:43 +0300389 queryInProgress[boundary] = true;
Alexander Trukhinb90d8002015-08-14 15:55:14 +0300390 // DRAWCALL is a CALL
391 if (boundary == QUERY_BOUNDARY_DRAWCALL) beginQuery(QUERY_BOUNDARY_CALL);
392}
393
394void MetricBackend_opengl::endQuery(QueryBoundary boundary) {
Alexander Trukhin1d0bf772015-08-24 23:17:43 +0300395 if (queryInProgress[boundary]) {
396 // CPU related
397 if (metrics[METRIC_CPU_DURATION].profiled[boundary])
398 {
399 cpuEnd[boundary] = getCurrentTime();
400 int64_t time = (cpuEnd[boundary] - cpuStart[boundary]) * cpuTimeScale;
401 data[METRIC_CPU_DURATION][boundary]->addData(boundary, time);
Alexander Trukhinb90d8002015-08-14 15:55:14 +0300402 }
Alexander Trukhin1d0bf772015-08-24 23:17:43 +0300403 if (metrics[METRIC_CPU_VSIZE_DURATION].profiled[boundary])
404 {
405 vsizeEnd[boundary] = os::getVsize();
406 int64_t time = vsizeEnd[boundary] - vsizeStart[boundary];
407 data[METRIC_CPU_VSIZE_DURATION][boundary]->addData(boundary, time);
Alexander Trukhinb90d8002015-08-14 15:55:14 +0300408 }
Alexander Trukhin1d0bf772015-08-24 23:17:43 +0300409 if (metrics[METRIC_CPU_RSS_DURATION].profiled[boundary])
410 {
411 rssEnd[boundary] = os::getRss();
412 int64_t time = rssEnd[boundary] - rssStart[boundary];
413 data[METRIC_CPU_RSS_DURATION][boundary]->addData(boundary, time);
414 }
415 // GPU related
416 if (glQueriesNeeded[boundary]) {
417 std::array<GLuint, QUERY_LIST_END> &query = queries[boundary].back();
418 if (metrics[METRIC_GPU_DURATION].profiled[boundary] && supportsTimestamp) {
419 // GL_TIME_ELAPSED cannot be used in nested queries
420 // so prefer this if timestamps are supported
421 glQueryCounter(query[QUERY_GPU_DURATION], GL_TIMESTAMP);
422 }
423 if (metrics[METRIC_GPU_PIXELS].profiled[boundary]) {
424 glEndQuery(GL_SAMPLES_PASSED);
425 }
426 }
427 queryInProgress[boundary] = false;
Alexander Trukhinb90d8002015-08-14 15:55:14 +0300428 }
429 // DRAWCALL is a CALL
430 if (boundary == QUERY_BOUNDARY_DRAWCALL) endQuery(QUERY_BOUNDARY_CALL);
431 // clear queries after each frame
432 if (boundary == QUERY_BOUNDARY_FRAME && glQueriesNeededAnyBoundary) {
433 processQueries();
434 }
435}
436
437void MetricBackend_opengl::enumDataQueryId(unsigned id, enumDataCallback callback,
438 QueryBoundary boundary, void* userData) {
439 for (int i = 0; i < METRIC_LIST_END; i++) {
440 Metric_opengl &metric = metrics[i];
441 if (metric.enabled[boundary]) {
442 callback(&metric, id, data[i][boundary]->getData(boundary, id), 0,
443 userData);
444 }
445 }
446}
447
448unsigned MetricBackend_opengl::getNumPasses() {
449 return twoPasses ? 2 : 1;
450}
451
452MetricBackend_opengl&
Alexander Trukhinca8139c2015-08-26 14:58:26 +0300453MetricBackend_opengl::getInstance(glretrace::Context* context, MmapAllocator<char> &alloc) {
454 static MetricBackend_opengl backend(context, alloc);
Alexander Trukhinb90d8002015-08-14 15:55:14 +0300455 return backend;
456}