]> git.cworth.org Git - apitrace/blob - retrace/glretrace_main.cpp
Kill currentDrawable.
[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     /* 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     /* Ensure we have adequate extension support */
234     assert(currentContext);
235     supportsTimestamp   = currentContext->hasExtension("GL_ARB_timer_query");
236     supportsElapsed     = currentContext->hasExtension("GL_EXT_timer_query") || supportsTimestamp;
237     supportsOcclusion   = currentContext->hasExtension("GL_ARB_occlusion_query");
238     supportsDebugOutput = currentContext->hasExtension("GL_ARB_debug_output");
239
240     /* Check for timer query support */
241     if (retrace::profilingGpuTimes) {
242         if (!supportsTimestamp && !supportsElapsed) {
243             std::cout << "Error: Cannot run profile, GL_EXT_timer_query extension is not supported." << std::endl;
244             exit(-1);
245         }
246
247         GLint bits = 0;
248         glGetQueryiv(GL_TIME_ELAPSED, GL_QUERY_COUNTER_BITS, &bits);
249
250         if (!bits) {
251             std::cout << "Error: Cannot run profile, GL_QUERY_COUNTER_BITS == 0." << std::endl;
252             exit(-1);
253         }
254     }
255
256     /* Check for occlusion query support */
257     if (retrace::profilingPixelsDrawn && !supportsOcclusion) {
258         std::cout << "Error: Cannot run profile, GL_ARB_occlusion_query extension is not supported." << std::endl;
259         exit(-1);
260     }
261
262     /* Setup debug message call back */
263     if (retrace::debug && supportsDebugOutput) {
264         glDebugMessageCallbackARB(&debugOutputCallback, currentContext);
265
266         if (DEBUG_OUTPUT_SYNCHRONOUS) {
267             glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
268         }
269     }
270
271     /* Sync the gpu and cpu start times */
272     if (retrace::profilingCpuTimes || retrace::profilingGpuTimes) {
273         if (!retrace::profiler.hasBaseTimes()) {
274             GLint64 gpuTime, cpuTime;
275
276             getCurrentTimes(cpuTime, gpuTime);
277             retrace::profiler.setBaseCpuTime(cpuTime);
278             retrace::profiler.setBaseGpuTime(gpuTime);
279         }
280     }
281 }
282
283 void
284 frame_complete(trace::Call &call) {
285     if (retrace::profiling) {
286         /* Complete any remaining queries */
287         flushQueries();
288
289         /* GPU time drifts due to being relative times, not absolute and can be
290          * affected by the gpu switch between processes.
291          *
292          * To attempt to compensate we resynchronise on frame end however there is
293          * still noticeable drift within a single frame which we do not account for.
294          */
295         if (retrace::profilingCpuTimes || retrace::profilingGpuTimes) {
296             int64_t cpuTime, gpuTime, error;
297
298             getCurrentTimes(cpuTime, gpuTime);
299             cpuTime = cpuTime - retrace::profiler.getBaseCpuTime();
300             gpuTime = gpuTime - retrace::profiler.getBaseGpuTime();
301             error   = gpuTime - cpuTime * (1.0E9 / os::timeFrequency);
302
303             retrace::profiler.setBaseGpuTime(retrace::profiler.getBaseGpuTime() + error);
304         }
305
306         /* Indicate end of current frame */
307         retrace::profiler.addFrameEnd();
308     }
309
310     retrace::frameComplete(call);
311
312     if (!currentContext) {
313         return;
314     }
315
316     if (retrace::debug && !currentContext->drawable->visible) {
317         retrace::warning(call) << "could not infer drawable size (glViewport never called)\n";
318     }
319 }
320
321 static const char*
322 getDebugOutputSource(GLenum source) {
323     switch(source) {
324     case GL_DEBUG_SOURCE_API_ARB:
325         return "API";
326     case GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB:
327         return "Window System";
328     case GL_DEBUG_SOURCE_SHADER_COMPILER_ARB:
329         return "Shader Compiler";
330     case GL_DEBUG_SOURCE_THIRD_PARTY_ARB:
331         return "Third Party";
332     case GL_DEBUG_SOURCE_APPLICATION_ARB:
333         return "Application";
334     case GL_DEBUG_SOURCE_OTHER_ARB:
335     default:
336         return "";
337     }
338 }
339
340 static const char*
341 getDebugOutputType(GLenum type) {
342     switch(type) {
343     case GL_DEBUG_TYPE_ERROR_ARB:
344         return "error";
345     case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB:
346         return "deprecated behaviour";
347     case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB:
348         return "undefined behaviour";
349     case GL_DEBUG_TYPE_PORTABILITY_ARB:
350         return "portability issue";
351     case GL_DEBUG_TYPE_PERFORMANCE_ARB:
352         return "performance issue";
353     case GL_DEBUG_TYPE_OTHER_ARB:
354     default:
355         return "unknown issue";
356     }
357 }
358
359 static const char*
360 getDebugOutputSeverity(GLenum severity) {
361     switch(severity) {
362     case GL_DEBUG_SEVERITY_HIGH_ARB:
363         return "High";
364     case GL_DEBUG_SEVERITY_MEDIUM_ARB:
365         return "Medium";
366     case GL_DEBUG_SEVERITY_LOW_ARB:
367         return "Low";
368     default:
369         return "usnknown";
370     }
371 }
372
373 static void APIENTRY
374 debugOutputCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, GLvoid* userParam) {
375     std::cerr << retrace::callNo << ": ";
376     std::cerr << "glDebugOutputCallback: ";
377     std::cerr << getDebugOutputSeverity(severity) << " severity ";
378     std::cerr << getDebugOutputSource(source) << " " << getDebugOutputType(type);
379     std::cerr << " " << id;
380     std::cerr << ", " << message;
381     std::cerr << std::endl;
382 }
383
384 } /* namespace glretrace */
385
386
387 void
388 retrace::setUp(void) {
389     glws::init();
390 }
391
392
393 void
394 retrace::addCallbacks(retrace::Retracer &retracer)
395 {
396     retracer.addCallbacks(glretrace::gl_callbacks);
397     retracer.addCallbacks(glretrace::glx_callbacks);
398     retracer.addCallbacks(glretrace::wgl_callbacks);
399     retracer.addCallbacks(glretrace::cgl_callbacks);
400     retracer.addCallbacks(glretrace::egl_callbacks);
401 }
402
403
404 image::Image *
405 retrace::getSnapshot(void) {
406     if (!glretrace::currentContext) {
407         return NULL;
408     }
409
410     return glstate::getDrawBufferImage();
411 }
412
413
414 bool
415 retrace::dumpState(std::ostream &os)
416 {
417     if (glretrace::insideGlBeginEnd ||
418         !glretrace::currentContext) {
419         return false;
420     }
421
422     glstate::dumpCurrentContext(os);
423
424     return true;
425 }
426
427 void
428 retrace::flushRendering(void) {
429     glretrace::flushQueries();
430     glFlush();
431 }
432
433 void
434 retrace::waitForInput(void) {
435     while (glws::processEvents()) {
436         os::sleep(100*1000);
437     }
438 }
439
440 void
441 retrace::cleanUp(void) {
442     glws::cleanup();
443 }