]> git.cworth.org Git - apitrace/blob - retrace/glretrace_main.cpp
CPU Profiling now includes all OpenGL calls (was only draw calls).
[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     int64_t cpuStart;
53     int64_t cpuEnd;
54 };
55
56 static bool supportsElapsed = true;
57 static bool supportsTimestamp = true;
58 static bool supportsOcclusion = true;
59 static bool supportsDebugOutput = true;
60
61 static bool firstFrame = true;
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 inline GLint64
113 getGpuTimestamp() {
114     GLuint query = 0;
115     GLint64 timestamp = 0;
116
117     if (retrace::profilingGpuTimes && supportsTimestamp) {
118         glGenQueries(1, &query);
119         glQueryCounter(query, GL_TIMESTAMP);
120         glGetQueryObjecti64vEXT(query, GL_QUERY_RESULT, &timestamp);
121         glDeleteQueries(1, &query);
122     }
123
124     return timestamp;
125 }
126
127 static inline GLint64
128 getCpuTimestamp() {
129     if (retrace::profilingCpuTimes) {
130         return os::getTime();
131     } else {
132         return 0;
133     }
134 }
135
136 static void
137 completeCallQuery(CallQuery& query) {
138     /* Get call start and duration */
139     GLint64 gpuStart = 0, gpuDuration = 0, cpuDuration = 0, samples = 0;
140
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::profilingCpuTimes) {
150         cpuDuration = query.cpuEnd - query.cpuStart;
151     }
152
153     if (retrace::profilingPixelsDrawn) {
154         glGetQueryObjecti64vEXT(query.ids[2], GL_QUERY_RESULT, &samples);
155     }
156
157     glDeleteQueries(3, query.ids);
158
159     /* Add call to profile */
160     retrace::profiler.addCall(query.call, query.sig->name, query.program, samples, gpuStart, gpuDuration, query.cpuStart, cpuDuration);
161 }
162
163 void
164 flushQueries() {
165     for (std::list<CallQuery>::iterator itr = callQueries.begin(); itr != callQueries.end(); ++itr) {
166         completeCallQuery(*itr);
167     }
168
169     callQueries.clear();
170 }
171
172 void
173 beginProfile(trace::Call &call, bool isDraw) {
174     if (firstFrame) {
175         frame_start();
176     }
177
178     /* Create call query */
179     CallQuery query;
180     query.call = call.no;
181     query.sig = call.sig;
182     query.program = glretrace::currentContext ? glretrace::currentContext->activeProgram : 0;
183
184     if (isDraw) {
185         glGenQueries(3, query.ids);
186
187         if (retrace::profilingGpuTimes) {
188             if (supportsTimestamp) {
189                 glQueryCounter(query.ids[0], GL_TIMESTAMP);
190             }
191
192             glBeginQuery(GL_TIME_ELAPSED, query.ids[1]);
193         }
194
195         if (retrace::profilingPixelsDrawn) {
196             glBeginQuery(GL_SAMPLES_PASSED, query.ids[2]);
197         }
198     }
199
200     if (retrace::profilingCpuTimes) {
201         query.cpuStart = getCpuTimestamp();
202     }
203
204     callQueries.push_back(query);
205 }
206
207 void
208 endProfile(trace::Call &call, bool isDraw) {
209     if (retrace::profilingCpuTimes) {
210         CallQuery& query = callQueries.back();
211         query.cpuEnd = getCpuTimestamp();
212
213         if (!isDraw) {
214             retrace::profiler.addCall(query.call, query.sig->name, query.program, -1, 0, 0, query.cpuStart, query.cpuEnd - query.cpuStart);
215             callQueries.pop_back();
216         }
217     }
218
219     if (isDraw) {
220         if (retrace::profilingGpuTimes) {
221             glEndQuery(GL_TIME_ELAPSED);
222         }
223
224         if (retrace::profilingPixelsDrawn) {
225             glEndQuery(GL_SAMPLES_PASSED);
226         }
227     }
228 }
229
230 void
231 initContext() {
232     GLint64 gpuTime = 0, cpuTime = 0;
233     const char* extensions;
234
235     extensions = (const char*)glGetString(GL_EXTENSIONS);
236
237     /* Ensure we have adequate extension support */
238     supportsTimestamp   = glws::checkExtension("GL_ARB_timer_query", extensions);
239     supportsElapsed     = glws::checkExtension("GL_EXT_timer_query", extensions) || supportsTimestamp;
240     supportsOcclusion   = glws::checkExtension("GL_ARB_occlusion_query", extensions);
241     supportsDebugOutput = glws::checkExtension("GL_ARB_debug_output", extensions);
242
243     if (retrace::profilingGpuTimes) {
244         if (!supportsTimestamp && !supportsElapsed) {
245             std::cout << "Error: Cannot run profile, GL_EXT_timer_query extension is not supported." << std::endl;
246             exit(-1);
247         }
248
249         GLint bits = 0;
250         glGetQueryiv(GL_TIME_ELAPSED, GL_QUERY_COUNTER_BITS, &bits);
251
252         if (!bits) {
253             std::cout << "Error: Cannot run profile, GL_QUERY_COUNTER_BITS == 0." << std::endl;
254             exit(-1);
255         }
256     }
257
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     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::profiler.hasBaseTimes()) {
273         if (retrace::profilingGpuTimes) {
274             gpuTime = getGpuTimestamp();
275         }
276
277         if (retrace::profilingCpuTimes) {
278             cpuTime = getCpuTimestamp();
279         }
280
281         retrace::profiler.setBaseTimes(gpuTime, cpuTime);
282     }
283 }
284
285 void
286 frame_start() {
287     firstFrame = false;
288
289     if (retrace::profiling) {
290         int64_t gpuStart = getGpuTimestamp();
291         int64_t cpuStart = getCpuTimestamp();
292         retrace::profiler.addFrameStart(retrace::frameNo, gpuStart, cpuStart);
293     }
294 }
295
296 void
297 frame_complete(trace::Call &call) {
298     if (retrace::profiling) {
299         /* Complete any remaining queries */
300         flushQueries();
301
302         /* Indicate end of current frame */
303         int64_t gpuEnd = getGpuTimestamp();
304         int64_t cpuEnd = getCpuTimestamp();
305         retrace::profiler.addFrameEnd(gpuEnd, cpuEnd);
306     }
307
308     retrace::frameComplete(call);
309
310     /* Indicate start of next frame */
311     frame_start();
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 }