blob: 72a32a84f4a67cf56b079febe4d40d3de85e1cb2 [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)
327 // If context is destroyed explicitly in trace file
328 // this is not going to work :(
329 processQueries();
330 curPass++;
331}
332
333void MetricBackend_opengl::beginQuery(QueryBoundary boundary) {
334 // GPU related
335 if (glQueriesNeeded[boundary]) {
336 std::array<GLuint, QUERY_LIST_END> query;
337 glGenQueries(QUERY_LIST_END, query.data());
338
339 if (metrics[METRIC_GPU_START].profiled[boundary] ||
340 (metrics[METRIC_GPU_DURATION].profiled[boundary] && supportsTimestamp))
341 {
342 glQueryCounter(query[QUERY_GPU_START], GL_TIMESTAMP);
343 }
344 if (metrics[METRIC_GPU_DURATION].profiled[boundary] && !supportsTimestamp) {
345 glBeginQuery(GL_TIME_ELAPSED, query[QUERY_GPU_DURATION]);
346 }
347 if (metrics[METRIC_GPU_PIXELS].profiled[boundary]) {
348 glBeginQuery(GL_SAMPLES_PASSED, query[QUERY_OCCLUSION]);
349 }
350 queries[boundary].push(std::move(query));
351 }
352
353
354 // CPU related
355 if (metrics[METRIC_CPU_START].profiled[boundary] ||
356 metrics[METRIC_CPU_DURATION].profiled[boundary])
357 {
358 cpuStart[boundary] = getCurrentTime();
359 if (metrics[METRIC_CPU_START].profiled[boundary]) {
360 int64_t time = cpuStart[boundary] * cpuTimeScale - baseTime;
361 data[METRIC_CPU_START][boundary]->addData(boundary, time);
362 }
363 }
364 if (metrics[METRIC_CPU_VSIZE_START].profiled[boundary] ||
365 metrics[METRIC_CPU_VSIZE_DURATION].profiled[boundary])
366 {
367 vsizeStart[boundary] = os::getVsize();
368 if (metrics[METRIC_CPU_VSIZE_START].profiled[boundary]) {
369 int64_t time = vsizeStart[boundary];
370 data[METRIC_CPU_VSIZE_START][boundary]->addData(boundary, time);
371 }
372 }
373 if (metrics[METRIC_CPU_RSS_START].profiled[boundary] ||
374 metrics[METRIC_CPU_RSS_DURATION].profiled[boundary])
375 {
376 rssStart[boundary] = os::getRss();
377 if (metrics[METRIC_CPU_RSS_START].profiled[boundary]) {
378 int64_t time = rssStart[boundary];
379 data[METRIC_CPU_RSS_START][boundary]->addData(boundary, time);
380 }
381 }
382 // DRAWCALL is a CALL
383 if (boundary == QUERY_BOUNDARY_DRAWCALL) beginQuery(QUERY_BOUNDARY_CALL);
384}
385
386void MetricBackend_opengl::endQuery(QueryBoundary boundary) {
387 // CPU related
388 if (metrics[METRIC_CPU_DURATION].profiled[boundary])
389 {
390 cpuEnd[boundary] = getCurrentTime();
391 int64_t time = (cpuEnd[boundary] - cpuStart[boundary]) * cpuTimeScale;
392 data[METRIC_CPU_DURATION][boundary]->addData(boundary, time);
393 }
394 if (metrics[METRIC_CPU_VSIZE_DURATION].profiled[boundary])
395 {
396 vsizeEnd[boundary] = os::getVsize();
397 int64_t time = vsizeEnd[boundary] - vsizeStart[boundary];
398 data[METRIC_CPU_VSIZE_DURATION][boundary]->addData(boundary, time);
399 }
400 if (metrics[METRIC_CPU_RSS_DURATION].profiled[boundary])
401 {
402 rssEnd[boundary] = os::getRss();
403 int64_t time = rssEnd[boundary] - rssStart[boundary];
404 data[METRIC_CPU_RSS_DURATION][boundary]->addData(boundary, time);
405 }
406 // GPU related
407 if (glQueriesNeeded[boundary]) {
408 std::array<GLuint, QUERY_LIST_END> &query = queries[boundary].back();
409 if (metrics[METRIC_GPU_DURATION].profiled[boundary] && supportsTimestamp) {
410 // GL_TIME_ELAPSED cannot be used in nested queries
411 // so prefer this if timestamps are supported
412 glQueryCounter(query[QUERY_GPU_DURATION], GL_TIMESTAMP);
413 }
414 if (metrics[METRIC_GPU_PIXELS].profiled[boundary]) {
415 glEndQuery(GL_SAMPLES_PASSED);
416 }
417 }
418 // DRAWCALL is a CALL
419 if (boundary == QUERY_BOUNDARY_DRAWCALL) endQuery(QUERY_BOUNDARY_CALL);
420 // clear queries after each frame
421 if (boundary == QUERY_BOUNDARY_FRAME && glQueriesNeededAnyBoundary) {
422 processQueries();
423 }
424}
425
426void MetricBackend_opengl::enumDataQueryId(unsigned id, enumDataCallback callback,
427 QueryBoundary boundary, void* userData) {
428 for (int i = 0; i < METRIC_LIST_END; i++) {
429 Metric_opengl &metric = metrics[i];
430 if (metric.enabled[boundary]) {
431 callback(&metric, id, data[i][boundary]->getData(boundary, id), 0,
432 userData);
433 }
434 }
435}
436
437unsigned MetricBackend_opengl::getNumPasses() {
438 return twoPasses ? 2 : 1;
439}
440
441MetricBackend_opengl&
Alexander Trukhinca8139c2015-08-26 14:58:26 +0300442MetricBackend_opengl::getInstance(glretrace::Context* context, MmapAllocator<char> &alloc) {
443 static MetricBackend_opengl backend(context, alloc);
Alexander Trukhinb90d8002015-08-14 15:55:14 +0300444 return backend;
445}