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