]> git.cworth.org Git - apitrace/blob - retrace/glretrace_main.cpp
d3dretrace: Give more helpful messages for d3d debug layer failures.
[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         double cpuTimeScale = 1.0E9 / getTimeFrequency();
182         cpuDuration = (query.cpuEnd - query.cpuStart) * cpuTimeScale;
183         query.cpuStart *= cpuTimeScale;
184     }
185
186     if (retrace::profilingMemoryUsage) {
187         vsizeDuration = query.vsizeEnd - query.vsizeStart;
188         rssDuration = query.rssEnd - query.rssStart;
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, query.vsizeStart, vsizeDuration, query.rssStart, rssDuration);
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     if (retrace::profilingMemoryUsage) {
243         CallQuery& query = callQueries.back();
244         query.vsizeStart = os::getVsize();
245         query.rssStart = os::getRss();
246     }
247 }
248
249 void
250 endProfile(trace::Call &call, bool isDraw) {
251
252     /* CPU profiling for all calls */
253     if (retrace::profilingCpuTimes) {
254         CallQuery& query = callQueries.back();
255         query.cpuEnd = getCurrentTime();
256     }
257
258     /* GPU profiling only for draw calls */
259     if (isDraw) {
260         if (retrace::profilingGpuTimes) {
261             glEndQuery(GL_TIME_ELAPSED);
262         }
263
264         if (retrace::profilingPixelsDrawn) {
265             glEndQuery(GL_SAMPLES_PASSED);
266         }
267     }
268
269     if (retrace::profilingMemoryUsage) {
270         CallQuery& query = callQueries.back();
271         query.vsizeEnd = os::getVsize();
272         query.rssEnd = os::getRss();
273     }
274 }
275
276 void
277 initContext() {
278     glretrace::Context *currentContext = glretrace::getCurrentContext();
279
280     /* Ensure we have adequate extension support */
281     assert(currentContext);
282     supportsTimestamp   = currentContext->hasExtension("GL_ARB_timer_query");
283     supportsElapsed     = currentContext->hasExtension("GL_EXT_timer_query") || supportsTimestamp;
284     supportsOcclusion   = currentContext->hasExtension("GL_ARB_occlusion_query");
285     supportsDebugOutput = currentContext->hasExtension("GL_ARB_debug_output");
286     supportsARBShaderObjects = currentContext->hasExtension("GL_ARB_shader_objects");
287
288     /* Check for timer query support */
289     if (retrace::profilingGpuTimes) {
290         if (!supportsTimestamp && !supportsElapsed) {
291             std::cout << "Error: Cannot run profile, GL_EXT_timer_query extension is not supported." << std::endl;
292             exit(-1);
293         }
294
295         GLint bits = 0;
296         glGetQueryiv(GL_TIME_ELAPSED, GL_QUERY_COUNTER_BITS, &bits);
297
298         if (!bits) {
299             std::cout << "Error: Cannot run profile, GL_QUERY_COUNTER_BITS == 0." << std::endl;
300             exit(-1);
301         }
302     }
303
304     /* Check for occlusion query support */
305     if (retrace::profilingPixelsDrawn && !supportsOcclusion) {
306         std::cout << "Error: Cannot run profile, GL_ARB_occlusion_query extension is not supported." << std::endl;
307         exit(-1);
308     }
309
310     /* Setup debug message call back */
311     if (retrace::debug && supportsDebugOutput) {
312         glretrace::Context *currentContext = glretrace::getCurrentContext();
313         glDebugMessageCallbackARB(&debugOutputCallback, currentContext);
314
315         if (DEBUG_OUTPUT_SYNCHRONOUS) {
316             glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
317         }
318     }
319
320     /* Sync the gpu and cpu start times */
321     if (retrace::profilingCpuTimes || retrace::profilingGpuTimes) {
322         if (!retrace::profiler.hasBaseTimes()) {
323             double cpuTimeScale = 1.0E9 / getTimeFrequency();
324             GLint64 currentTime = getCurrentTime() * cpuTimeScale;
325             retrace::profiler.setBaseCpuTime(currentTime);
326             retrace::profiler.setBaseGpuTime(currentTime);
327         }
328     }
329
330     if (retrace::profilingMemoryUsage) {
331         GLint64 currentVsize, currentRss;
332         getCurrentVsize(currentVsize);
333         retrace::profiler.setBaseVsizeUsage(currentVsize);
334         getCurrentRss(currentRss);
335         retrace::profiler.setBaseRssUsage(currentRss);
336     }
337 }
338
339 void
340 frame_complete(trace::Call &call) {
341     if (retrace::profiling) {
342         /* Complete any remaining queries */
343         flushQueries();
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     glretrace::Context *currentContext = glretrace::getCurrentContext();
483     if (currentContext) {
484         glretrace::flushQueries();
485         glFlush();
486     }
487     while (glws::processEvents()) {
488         os::sleep(100*1000);
489     }
490 }
491
492 void
493 retrace::cleanUp(void) {
494     glws::cleanup();
495 }