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