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