]> git.cworth.org Git - apitrace/blob - retrace/glretrace_main.cpp
1418ca3d0fcc9bd54326263af35763136dd656c8
[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 struct CallQuery
47 {
48     GLuint ids[3];
49     unsigned call;
50     bool isDraw;
51     GLuint program;
52     const trace::FunctionSig *sig;
53     int64_t cpuStart;
54     int64_t cpuEnd;
55 };
56
57 static bool supportsElapsed = true;
58 static bool supportsTimestamp = true;
59 static bool supportsOcclusion = true;
60 static bool supportsDebugOutput = true;
61
62 static std::list<CallQuery> callQueries;
63
64 static void APIENTRY
65 debugOutputCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, GLvoid* userParam);
66
67 void
68 checkGlError(trace::Call &call) {
69     GLenum error = glGetError();
70     while (error != GL_NO_ERROR) {
71         std::ostream & os = retrace::warning(call);
72
73         os << "glGetError(";
74         os << call.name();
75         os << ") = ";
76
77         switch (error) {
78         case GL_INVALID_ENUM:
79             os << "GL_INVALID_ENUM";
80             break;
81         case GL_INVALID_VALUE:
82             os << "GL_INVALID_VALUE";
83             break;
84         case GL_INVALID_OPERATION:
85             os << "GL_INVALID_OPERATION";
86             break;
87         case GL_STACK_OVERFLOW:
88             os << "GL_STACK_OVERFLOW";
89             break;
90         case GL_STACK_UNDERFLOW:
91             os << "GL_STACK_UNDERFLOW";
92             break;
93         case GL_OUT_OF_MEMORY:
94             os << "GL_OUT_OF_MEMORY";
95             break;
96         case GL_INVALID_FRAMEBUFFER_OPERATION:
97             os << "GL_INVALID_FRAMEBUFFER_OPERATION";
98             break;
99         case GL_TABLE_TOO_LARGE:
100             os << "GL_TABLE_TOO_LARGE";
101             break;
102         default:
103             os << error;
104             break;
105         }
106         os << "\n";
107     
108         error = glGetError();
109     }
110 }
111
112 static void
113 getCurrentTimes(int64_t& cpuTime, int64_t& gpuTime) {
114     GLuint query;
115
116     if (retrace::profilingGpuTimes && supportsTimestamp) {
117         glGenQueries(1, &query);
118         glQueryCounter(query, GL_TIMESTAMP);
119         glGetQueryObjecti64vEXT(query, GL_QUERY_RESULT, &gpuTime);
120     } else {
121         gpuTime = 0;
122     }
123
124     if (retrace::profilingCpuTimes) {
125         cpuTime = os::getTime();
126     } else {
127         cpuTime = 0;
128     }
129
130     if (retrace::profilingGpuTimes && supportsTimestamp) {
131         glDeleteQueries(1, &query);
132     }
133 }
134
135 static void
136 completeCallQuery(CallQuery& query) {
137     /* Get call start and duration */
138     int64_t gpuStart = 0, gpuDuration = 0, cpuDuration = 0, pixels = 0;
139
140     if (query.isDraw) {
141         if (retrace::profilingGpuTimes) {
142             if (supportsTimestamp) {
143                 glGetQueryObjecti64vEXT(query.ids[0], GL_QUERY_RESULT, &gpuStart);
144             }
145
146             glGetQueryObjecti64vEXT(query.ids[1], GL_QUERY_RESULT, &gpuDuration);
147         }
148
149         if (retrace::profilingPixelsDrawn) {
150             glGetQueryObjecti64vEXT(query.ids[2], GL_QUERY_RESULT, &pixels);
151         }
152
153         glDeleteQueries(3, query.ids);
154     } else {
155         pixels = -1;
156     }
157
158     if (retrace::profilingCpuTimes) {
159         cpuDuration = query.cpuEnd - query.cpuStart;
160     }
161
162     /* Add call to profile */
163     retrace::profiler.addCall(query.call, query.sig->name, query.program, pixels, gpuStart, gpuDuration, query.cpuStart, cpuDuration);
164 }
165
166 void
167 flushQueries() {
168     for (std::list<CallQuery>::iterator itr = callQueries.begin(); itr != callQueries.end(); ++itr) {
169         completeCallQuery(*itr);
170     }
171
172     callQueries.clear();
173 }
174
175 void
176 beginProfile(trace::Call &call, bool isDraw) {
177     glretrace::Context *currentContext = glretrace::getCurrentContext();
178
179     /* Create call query */
180     CallQuery query;
181     query.isDraw = isDraw;
182     query.call = call.no;
183     query.sig = call.sig;
184     query.program = currentContext ? currentContext->activeProgram : 0;
185
186     /* GPU profiling only for draw calls */
187     if (isDraw) {
188         glGenQueries(3, query.ids);
189
190         if (retrace::profilingGpuTimes) {
191             if (supportsTimestamp) {
192                 glQueryCounter(query.ids[0], GL_TIMESTAMP);
193             }
194
195             glBeginQuery(GL_TIME_ELAPSED, query.ids[1]);
196         }
197
198         if (retrace::profilingPixelsDrawn) {
199             glBeginQuery(GL_SAMPLES_PASSED, query.ids[2]);
200         }
201     }
202
203     callQueries.push_back(query);
204
205     /* CPU profiling for all calls */
206     if (retrace::profilingCpuTimes) {
207        callQueries.back().cpuStart = os::getTime();
208     }
209 }
210
211 void
212 endProfile(trace::Call &call, bool isDraw) {
213     GLint64 time = os::getTime();
214
215     /* CPU profiling for all calls */
216     if (retrace::profilingCpuTimes) {
217         CallQuery& query = callQueries.back();
218         query.cpuEnd = time;
219     }
220
221     /* GPU profiling only for draw calls */
222     if (isDraw) {
223         if (retrace::profilingGpuTimes) {
224             glEndQuery(GL_TIME_ELAPSED);
225         }
226
227         if (retrace::profilingPixelsDrawn) {
228             glEndQuery(GL_SAMPLES_PASSED);
229         }
230     }
231 }
232
233 void
234 initContext() {
235     glretrace::Context *currentContext = glretrace::getCurrentContext();
236
237     /* Ensure we have adequate extension support */
238     assert(currentContext);
239     supportsTimestamp   = currentContext->hasExtension("GL_ARB_timer_query");
240     supportsElapsed     = currentContext->hasExtension("GL_EXT_timer_query") || supportsTimestamp;
241     supportsOcclusion   = currentContext->hasExtension("GL_ARB_occlusion_query");
242     supportsDebugOutput = currentContext->hasExtension("GL_ARB_debug_output");
243
244     /* Check for timer query support */
245     if (retrace::profilingGpuTimes) {
246         if (!supportsTimestamp && !supportsElapsed) {
247             std::cout << "Error: Cannot run profile, GL_EXT_timer_query extension is not supported." << std::endl;
248             exit(-1);
249         }
250
251         GLint bits = 0;
252         glGetQueryiv(GL_TIME_ELAPSED, GL_QUERY_COUNTER_BITS, &bits);
253
254         if (!bits) {
255             std::cout << "Error: Cannot run profile, GL_QUERY_COUNTER_BITS == 0." << std::endl;
256             exit(-1);
257         }
258     }
259
260     /* Check for occlusion query support */
261     if (retrace::profilingPixelsDrawn && !supportsOcclusion) {
262         std::cout << "Error: Cannot run profile, GL_ARB_occlusion_query extension is not supported." << std::endl;
263         exit(-1);
264     }
265
266     /* Setup debug message call back */
267     if (retrace::debug && supportsDebugOutput) {
268         glretrace::Context *currentContext = glretrace::getCurrentContext();
269         glDebugMessageCallbackARB(&debugOutputCallback, currentContext);
270
271         if (DEBUG_OUTPUT_SYNCHRONOUS) {
272             glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
273         }
274     }
275
276     /* Sync the gpu and cpu start times */
277     if (retrace::profilingCpuTimes || retrace::profilingGpuTimes) {
278         if (!retrace::profiler.hasBaseTimes()) {
279             GLint64 gpuTime, cpuTime;
280
281             getCurrentTimes(cpuTime, gpuTime);
282             retrace::profiler.setBaseCpuTime(cpuTime);
283             retrace::profiler.setBaseGpuTime(gpuTime);
284         }
285     }
286 }
287
288 void
289 frame_complete(trace::Call &call) {
290     if (retrace::profiling) {
291         /* Complete any remaining queries */
292         flushQueries();
293
294         /* GPU time drifts due to being relative times, not absolute and can be
295          * affected by the gpu switch between processes.
296          *
297          * To attempt to compensate we resynchronise on frame end however there is
298          * still noticeable drift within a single frame which we do not account for.
299          */
300         if (retrace::profilingCpuTimes || retrace::profilingGpuTimes) {
301             int64_t cpuTime, gpuTime, error;
302
303             getCurrentTimes(cpuTime, gpuTime);
304             cpuTime = cpuTime - retrace::profiler.getBaseCpuTime();
305             gpuTime = gpuTime - retrace::profiler.getBaseGpuTime();
306             error   = gpuTime - cpuTime * (1.0E9 / os::timeFrequency);
307
308             retrace::profiler.setBaseGpuTime(retrace::profiler.getBaseGpuTime() + error);
309         }
310
311         /* Indicate end of current frame */
312         retrace::profiler.addFrameEnd();
313     }
314
315     retrace::frameComplete(call);
316
317     glretrace::Context *currentContext = glretrace::getCurrentContext();
318     if (!currentContext) {
319         return;
320     }
321
322     assert(currentContext->drawable);
323     if (retrace::debug && !currentContext->drawable->visible) {
324         retrace::warning(call) << "could not infer drawable size (glViewport never called)\n";
325     }
326 }
327
328 static const char*
329 getDebugOutputSource(GLenum source) {
330     switch(source) {
331     case GL_DEBUG_SOURCE_API_ARB:
332         return "API";
333     case GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB:
334         return "Window System";
335     case GL_DEBUG_SOURCE_SHADER_COMPILER_ARB:
336         return "Shader Compiler";
337     case GL_DEBUG_SOURCE_THIRD_PARTY_ARB:
338         return "Third Party";
339     case GL_DEBUG_SOURCE_APPLICATION_ARB:
340         return "Application";
341     case GL_DEBUG_SOURCE_OTHER_ARB:
342     default:
343         return "";
344     }
345 }
346
347 static const char*
348 getDebugOutputType(GLenum type) {
349     switch(type) {
350     case GL_DEBUG_TYPE_ERROR_ARB:
351         return "error";
352     case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB:
353         return "deprecated behaviour";
354     case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB:
355         return "undefined behaviour";
356     case GL_DEBUG_TYPE_PORTABILITY_ARB:
357         return "portability issue";
358     case GL_DEBUG_TYPE_PERFORMANCE_ARB:
359         return "performance issue";
360     case GL_DEBUG_TYPE_OTHER_ARB:
361     default:
362         return "unknown issue";
363     }
364 }
365
366 static const char*
367 getDebugOutputSeverity(GLenum severity) {
368     switch(severity) {
369     case GL_DEBUG_SEVERITY_HIGH_ARB:
370         return "High";
371     case GL_DEBUG_SEVERITY_MEDIUM_ARB:
372         return "Medium";
373     case GL_DEBUG_SEVERITY_LOW_ARB:
374         return "Low";
375     default:
376         return "usnknown";
377     }
378 }
379
380 static void APIENTRY
381 debugOutputCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, GLvoid* userParam) {
382     std::cerr << retrace::callNo << ": ";
383     std::cerr << "glDebugOutputCallback: ";
384     std::cerr << getDebugOutputSeverity(severity) << " severity ";
385     std::cerr << getDebugOutputSource(source) << " " << getDebugOutputType(type);
386     std::cerr << " " << id;
387     std::cerr << ", " << message;
388     std::cerr << std::endl;
389 }
390
391 } /* namespace glretrace */
392
393
394 class GLDumper : public retrace::Dumper {
395 public:
396     image::Image *
397     getSnapshot(void) {
398         if (!glretrace::getCurrentContext()) {
399             return NULL;
400         }
401         return glstate::getDrawBufferImage();
402     }
403
404     bool
405     dumpState(std::ostream &os) {
406         glretrace::Context *currentContext = glretrace::getCurrentContext();
407         if (glretrace::insideGlBeginEnd ||
408             !currentContext) {
409             return false;
410         }
411         glstate::dumpCurrentContext(os);
412         return true;
413     }
414 };
415
416 static GLDumper glDumper;
417
418
419 void
420 retrace::setUp(void) {
421     glws::init();
422     dumper = &glDumper;
423 }
424
425
426 void
427 retrace::addCallbacks(retrace::Retracer &retracer)
428 {
429     retracer.addCallbacks(glretrace::gl_callbacks);
430     retracer.addCallbacks(glretrace::glx_callbacks);
431     retracer.addCallbacks(glretrace::wgl_callbacks);
432     retracer.addCallbacks(glretrace::cgl_callbacks);
433     retracer.addCallbacks(glretrace::egl_callbacks);
434 }
435
436
437 void
438 retrace::flushRendering(void) {
439     glretrace::Context *currentContext = glretrace::getCurrentContext();
440     if (currentContext) {
441         glretrace::flushQueries();
442         glFlush();
443     }
444 }
445
446 void
447 retrace::waitForInput(void) {
448     while (glws::processEvents()) {
449         os::sleep(100*1000);
450     }
451 }
452
453 void
454 retrace::cleanUp(void) {
455     glws::cleanup();
456 }