]> git.cworth.org Git - apitrace/blob - retrace/glretrace_main.cpp
1591287c22d9d254507dba27a23c5245a5e83d20
[apitrace] / retrace / glretrace_main.cpp
1 /**************************************************************************
2  *
3  * Copyright 2011 Jose Fonseca
4  * Copyright (C) 2013 Intel Corporation. All rights reversed.
5  * Author: Shuang He <shuang.he@intel.com>
6  * All Rights Reserved.
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a copy
9  * of this software and associated documentation files (the "Software"), to deal
10  * in the Software without restriction, including without limitation the rights
11  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12  * copies of the Software, and to permit persons to whom the Software is
13  * furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included in
16  * all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24  * THE SOFTWARE.
25  *
26  **************************************************************************/
27
28
29 #include <string.h>
30
31 #include "retrace.hpp"
32 #include "glproc.hpp"
33 #include "glstate.hpp"
34 #include "glretrace.hpp"
35 #include "os_time.hpp"
36 #include "os_memory.hpp"
37
38 /* Synchronous debug output may reduce performance however,
39  * without it the callNo in the callback may be inaccurate
40  * as the callback may be called at any time.
41  */
42 #define DEBUG_OUTPUT_SYNCHRONOUS 0
43
44 namespace glretrace {
45
46 bool insideList = false;
47 bool insideGlBeginEnd = false;
48 bool supportsARBShaderObjects = false;
49
50 enum {
51     GPU_START = 0,
52     GPU_DURATION,
53     OCCLUSION,
54     NUM_QUERIES,
55 };
56
57 struct CallQuery
58 {
59     GLuint ids[NUM_QUERIES];
60     unsigned call;
61     bool isDraw;
62     GLuint program;
63     const trace::FunctionSig *sig;
64     int64_t cpuStart;
65     int64_t cpuEnd;
66     int64_t vsizeStart;
67     int64_t vsizeEnd;
68     int64_t rssStart;
69     int64_t rssEnd;
70 };
71
72 static bool supportsElapsed = true;
73 static bool supportsTimestamp = true;
74 static bool supportsOcclusion = true;
75 static bool supportsDebugOutput = true;
76
77 static std::list<CallQuery> callQueries;
78
79 static void APIENTRY
80 debugOutputCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, GLvoid* userParam);
81
82 void
83 checkGlError(trace::Call &call) {
84     GLenum error = glGetError();
85     while (error != GL_NO_ERROR) {
86         std::ostream & os = retrace::warning(call);
87
88         os << "glGetError(";
89         os << call.name();
90         os << ") = ";
91
92         switch (error) {
93         case GL_INVALID_ENUM:
94             os << "GL_INVALID_ENUM";
95             break;
96         case GL_INVALID_VALUE:
97             os << "GL_INVALID_VALUE";
98             break;
99         case GL_INVALID_OPERATION:
100             os << "GL_INVALID_OPERATION";
101             break;
102         case GL_STACK_OVERFLOW:
103             os << "GL_STACK_OVERFLOW";
104             break;
105         case GL_STACK_UNDERFLOW:
106             os << "GL_STACK_UNDERFLOW";
107             break;
108         case GL_OUT_OF_MEMORY:
109             os << "GL_OUT_OF_MEMORY";
110             break;
111         case GL_INVALID_FRAMEBUFFER_OPERATION:
112             os << "GL_INVALID_FRAMEBUFFER_OPERATION";
113             break;
114         case GL_TABLE_TOO_LARGE:
115             os << "GL_TABLE_TOO_LARGE";
116             break;
117         default:
118             os << error;
119             break;
120         }
121         os << "\n";
122     
123         error = glGetError();
124     }
125 }
126
127 static inline int64_t
128 getCurrentTime(void) {
129     if (retrace::profilingGpuTimes && supportsTimestamp) {
130         /* Get the current GL time without stalling */
131         GLint64 timestamp = 0;
132         glGetInteger64v(GL_TIMESTAMP, &timestamp);
133         return timestamp;
134     } else {
135         return os::getTime();
136     }
137 }
138
139 static inline int64_t
140 getTimeFrequency(void) {
141     if (retrace::profilingGpuTimes && supportsTimestamp) {
142         return 1000000000;
143     } else {
144         return os::timeFrequency;
145     }
146 }
147
148 static inline void
149 getCurrentVsize(int64_t& vsize) {
150     vsize = os::getVsize();
151 }
152
153 static inline void
154 getCurrentRss(int64_t& rss) {
155     rss = os::getRss();
156 }
157
158 static void
159 completeCallQuery(CallQuery& query) {
160     /* Get call start and duration */
161     int64_t gpuStart = 0, gpuDuration = 0, cpuDuration = 0, pixels = 0, vsizeDuration = 0, rssDuration = 0;
162
163     if (query.isDraw) {
164         if (retrace::profilingGpuTimes) {
165             if (supportsTimestamp) {
166                 glGetQueryObjecti64vEXT(query.ids[GPU_START], GL_QUERY_RESULT, &gpuStart);
167             }
168
169             glGetQueryObjecti64vEXT(query.ids[GPU_DURATION], GL_QUERY_RESULT, &gpuDuration);
170         }
171
172         if (retrace::profilingPixelsDrawn) {
173             glGetQueryObjecti64vEXT(query.ids[OCCLUSION], GL_QUERY_RESULT, &pixels);
174         }
175
176     } else {
177         pixels = -1;
178     }
179
180     if (retrace::profilingCpuTimes) {
181         cpuDuration = query.cpuEnd - query.cpuStart;
182     }
183
184     if (retrace::profilingMemoryUsage) {
185         vsizeDuration = query.vsizeEnd - query.vsizeStart;
186         rssDuration = query.rssEnd - query.rssStart;
187     }
188
189     glDeleteQueries(NUM_QUERIES, query.ids);
190
191     /* Add call to profile */
192     retrace::profiler.addCall(query.call, query.sig->name, query.program, pixels, gpuStart, gpuDuration, query.cpuStart, cpuDuration, query.vsizeStart, vsizeDuration, query.rssStart, rssDuration);
193 }
194
195 void
196 flushQueries() {
197     for (std::list<CallQuery>::iterator itr = callQueries.begin(); itr != callQueries.end(); ++itr) {
198         completeCallQuery(*itr);
199     }
200
201     callQueries.clear();
202 }
203
204 void
205 beginProfile(trace::Call &call, bool isDraw) {
206     glretrace::Context *currentContext = glretrace::getCurrentContext();
207
208     /* Create call query */
209     CallQuery query;
210     query.isDraw = isDraw;
211     query.call = call.no;
212     query.sig = call.sig;
213     query.program = currentContext ? currentContext->activeProgram : 0;
214
215     glGenQueries(NUM_QUERIES, query.ids);
216
217     /* GPU profiling only for draw calls */
218     if (isDraw) {
219         if (retrace::profilingGpuTimes) {
220             if (supportsTimestamp) {
221                 glQueryCounter(query.ids[GPU_START], GL_TIMESTAMP);
222             }
223
224             glBeginQuery(GL_TIME_ELAPSED, query.ids[GPU_DURATION]);
225         }
226
227         if (retrace::profilingPixelsDrawn) {
228             glBeginQuery(GL_SAMPLES_PASSED, query.ids[OCCLUSION]);
229         }
230     }
231
232     callQueries.push_back(query);
233
234     /* CPU profiling for all calls */
235     if (retrace::profilingCpuTimes) {
236         CallQuery& query = callQueries.back();
237         query.cpuStart = getCurrentTime();
238     }
239
240     if (retrace::profilingMemoryUsage) {
241         CallQuery& query = callQueries.back();
242         query.vsizeStart = os::getVsize();
243         query.rssStart = os::getRss();
244     }
245 }
246
247 void
248 endProfile(trace::Call &call, bool isDraw) {
249
250     /* CPU profiling for all calls */
251     if (retrace::profilingCpuTimes) {
252         CallQuery& query = callQueries.back();
253         query.cpuEnd = getCurrentTime();
254     }
255
256     /* GPU profiling only for draw calls */
257     if (isDraw) {
258         if (retrace::profilingGpuTimes) {
259             glEndQuery(GL_TIME_ELAPSED);
260         }
261
262         if (retrace::profilingPixelsDrawn) {
263             glEndQuery(GL_SAMPLES_PASSED);
264         }
265     }
266
267     if (retrace::profilingMemoryUsage) {
268         CallQuery& query = callQueries.back();
269         query.vsizeEnd = os::getVsize();
270         query.rssEnd = os::getRss();
271     }
272 }
273
274 void
275 initContext() {
276     glretrace::Context *currentContext = glretrace::getCurrentContext();
277
278     /* Ensure we have adequate extension support */
279     assert(currentContext);
280     supportsTimestamp   = currentContext->hasExtension("GL_ARB_timer_query");
281     supportsElapsed     = currentContext->hasExtension("GL_EXT_timer_query") || supportsTimestamp;
282     supportsOcclusion   = currentContext->hasExtension("GL_ARB_occlusion_query");
283     supportsDebugOutput = currentContext->hasExtension("GL_ARB_debug_output");
284     supportsARBShaderObjects = currentContext->hasExtension("GL_ARB_shader_objects");
285
286     /* Check for timer query support */
287     if (retrace::profilingGpuTimes) {
288         if (!supportsTimestamp && !supportsElapsed) {
289             std::cout << "Error: Cannot run profile, GL_EXT_timer_query extension is not supported." << std::endl;
290             exit(-1);
291         }
292
293         GLint bits = 0;
294         glGetQueryiv(GL_TIME_ELAPSED, GL_QUERY_COUNTER_BITS, &bits);
295
296         if (!bits) {
297             std::cout << "Error: Cannot run profile, GL_QUERY_COUNTER_BITS == 0." << std::endl;
298             exit(-1);
299         }
300     }
301
302     /* Check for occlusion query support */
303     if (retrace::profilingPixelsDrawn && !supportsOcclusion) {
304         std::cout << "Error: Cannot run profile, GL_ARB_occlusion_query extension is not supported." << std::endl;
305         exit(-1);
306     }
307
308     /* Setup debug message call back */
309     if (retrace::debug && supportsDebugOutput) {
310         glretrace::Context *currentContext = glretrace::getCurrentContext();
311         glDebugMessageCallbackARB(&debugOutputCallback, currentContext);
312
313         if (DEBUG_OUTPUT_SYNCHRONOUS) {
314             glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
315         }
316     }
317
318     /* Sync the gpu and cpu start times */
319     if (retrace::profilingCpuTimes || retrace::profilingGpuTimes) {
320         if (!retrace::profiler.hasBaseTimes()) {
321             GLint64 currentTime = getCurrentTime();
322             retrace::profiler.setBaseCpuTime(currentTime);
323             retrace::profiler.setBaseGpuTime(currentTime);
324         }
325     }
326
327     if (retrace::profilingMemoryUsage) {
328         GLint64 currentVsize, currentRss;
329         getCurrentVsize(currentVsize);
330         retrace::profiler.setBaseVsizeUsage(currentVsize);
331         getCurrentRss(currentRss);
332         retrace::profiler.setBaseRssUsage(currentRss);
333     }
334 }
335
336 void
337 frame_complete(trace::Call &call) {
338     if (retrace::profiling) {
339         /* Complete any remaining queries */
340         flushQueries();
341
342         /* Indicate end of current frame */
343         retrace::profiler.addFrameEnd();
344     }
345
346     retrace::frameComplete(call);
347
348     glretrace::Context *currentContext = glretrace::getCurrentContext();
349     if (!currentContext) {
350         return;
351     }
352
353     assert(currentContext->drawable);
354     if (retrace::debug && !currentContext->drawable->visible) {
355         retrace::warning(call) << "could not infer drawable size (glViewport never called)\n";
356     }
357 }
358
359 static const char*
360 getDebugOutputSource(GLenum source) {
361     switch(source) {
362     case GL_DEBUG_SOURCE_API_ARB:
363         return "API";
364     case GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB:
365         return "Window System";
366     case GL_DEBUG_SOURCE_SHADER_COMPILER_ARB:
367         return "Shader Compiler";
368     case GL_DEBUG_SOURCE_THIRD_PARTY_ARB:
369         return "Third Party";
370     case GL_DEBUG_SOURCE_APPLICATION_ARB:
371         return "Application";
372     case GL_DEBUG_SOURCE_OTHER_ARB:
373     default:
374         return "";
375     }
376 }
377
378 static const char*
379 getDebugOutputType(GLenum type) {
380     switch(type) {
381     case GL_DEBUG_TYPE_ERROR_ARB:
382         return "error";
383     case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB:
384         return "deprecated behaviour";
385     case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB:
386         return "undefined behaviour";
387     case GL_DEBUG_TYPE_PORTABILITY_ARB:
388         return "portability issue";
389     case GL_DEBUG_TYPE_PERFORMANCE_ARB:
390         return "performance issue";
391     case GL_DEBUG_TYPE_OTHER_ARB:
392     default:
393         return "unknown issue";
394     }
395 }
396
397 static const char*
398 getDebugOutputSeverity(GLenum severity) {
399     switch(severity) {
400     case GL_DEBUG_SEVERITY_HIGH_ARB:
401         return "High";
402     case GL_DEBUG_SEVERITY_MEDIUM_ARB:
403         return "Medium";
404     case GL_DEBUG_SEVERITY_LOW_ARB:
405         return "Low";
406     default:
407         return "usnknown";
408     }
409 }
410
411 static void APIENTRY
412 debugOutputCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, GLvoid* userParam) {
413     std::cerr << retrace::callNo << ": ";
414     std::cerr << "glDebugOutputCallback: ";
415     std::cerr << getDebugOutputSeverity(severity) << " severity ";
416     std::cerr << getDebugOutputSource(source) << " " << getDebugOutputType(type);
417     std::cerr << " " << id;
418     std::cerr << ", " << message;
419     std::cerr << std::endl;
420 }
421
422 } /* namespace glretrace */
423
424
425 class GLDumper : public retrace::Dumper {
426 public:
427     image::Image *
428     getSnapshot(void) {
429         if (!glretrace::getCurrentContext()) {
430             return NULL;
431         }
432         return glstate::getDrawBufferImage();
433     }
434
435     bool
436     dumpState(std::ostream &os) {
437         glretrace::Context *currentContext = glretrace::getCurrentContext();
438         if (glretrace::insideGlBeginEnd ||
439             !currentContext) {
440             return false;
441         }
442         glstate::dumpCurrentContext(os);
443         return true;
444     }
445 };
446
447 static GLDumper glDumper;
448
449
450 void
451 retrace::setUp(void) {
452     glws::init();
453     dumper = &glDumper;
454 }
455
456
457 void
458 retrace::addCallbacks(retrace::Retracer &retracer)
459 {
460     retracer.addCallbacks(glretrace::gl_callbacks);
461     retracer.addCallbacks(glretrace::glx_callbacks);
462     retracer.addCallbacks(glretrace::wgl_callbacks);
463     retracer.addCallbacks(glretrace::cgl_callbacks);
464     retracer.addCallbacks(glretrace::egl_callbacks);
465 }
466
467
468 void
469 retrace::flushRendering(void) {
470     glretrace::Context *currentContext = glretrace::getCurrentContext();
471     if (currentContext) {
472         glretrace::flushQueries();
473         glFlush();
474     }
475 }
476
477 void
478 retrace::waitForInput(void) {
479     while (glws::processEvents()) {
480         os::sleep(100*1000);
481     }
482 }
483
484 void
485 retrace::cleanUp(void) {
486     glws::cleanup();
487 }