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