]> git.cworth.org Git - apitrace/blob - retrace/glretrace_main.cpp
Synchronise gpuStart and cpuStart for profiling.
[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 cpuStart;
53     uint64_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 GLuint64
113 getGpuTimestamp() {
114     GLuint query = 0;
115     GLuint64 timestamp = 0;
116
117     if (retrace::profilingGpuTimes && supportsTimestamp) {
118         glGenQueries(1, &query);
119         glQueryCounter(query, GL_TIMESTAMP);
120         glGetQueryObjectui64vEXT(query, GL_QUERY_RESULT, &timestamp);
121         glDeleteQueries(1, &query);
122     }
123
124     return timestamp;
125 }
126
127 static inline GLuint64
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     GLuint64 gpuStart = 0, gpuDuration = 0, cpuDuration = 0, samples = 0;
140
141     if (retrace::profilingGpuTimes) {
142         if (supportsTimestamp) {
143             glGetQueryObjectui64vEXT(query.ids[0], GL_QUERY_RESULT, &gpuStart);
144         }
145
146         glGetQueryObjectui64vEXT(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         glGetQueryObjectui64vEXT(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) {
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     glGenQueries(3, query.ids);
185
186     if (retrace::profilingGpuTimes) {
187         if (supportsTimestamp) {
188             glQueryCounter(query.ids[0], GL_TIMESTAMP);
189         }
190
191         glBeginQuery(GL_TIME_ELAPSED, query.ids[1]);
192     }
193
194     if (retrace::profilingPixelsDrawn) {
195         glBeginQuery(GL_SAMPLES_PASSED, query.ids[2]);
196     }
197
198     if (retrace::profilingCpuTimes) {
199         query.cpuStart = getCpuTimestamp();
200     }
201
202     callQueries.push_back(query);
203 }
204
205 void
206 endProfile(trace::Call &call) {
207     if (retrace::profilingCpuTimes) {
208         CallQuery& query = callQueries.back();
209         query.cpuEnd = getCpuTimestamp();
210     }
211
212     if (retrace::profilingGpuTimes) {
213         glEndQuery(GL_TIME_ELAPSED);
214     }
215
216     if (retrace::profilingPixelsDrawn) {
217         glEndQuery(GL_SAMPLES_PASSED);
218     }
219 }
220
221 void
222 initContext() {
223     GLuint64 gpuTime, cpuTime;
224     const char* extensions;
225
226     extensions = (const char*)glGetString(GL_EXTENSIONS);
227
228     /* Ensure we have adequate extension support */
229     supportsTimestamp   = glws::checkExtension("GL_ARB_timer_query", extensions);
230     supportsElapsed     = glws::checkExtension("GL_EXT_timer_query", extensions) || supportsTimestamp;
231     supportsOcclusion   = glws::checkExtension("GL_ARB_occlusion_query", extensions);
232     supportsDebugOutput = glws::checkExtension("GL_ARB_debug_output", extensions);
233
234     if (retrace::profilingGpuTimes) {
235         if (!supportsTimestamp && !supportsElapsed) {
236             std::cout << "Error: Cannot run profile, GL_EXT_timer_query extension is not supported." << std::endl;
237             exit(-1);
238         }
239
240         GLint bits = 0;
241         glGetQueryiv(GL_TIME_ELAPSED, GL_QUERY_COUNTER_BITS, &bits);
242
243         if (!bits) {
244             std::cout << "Error: Cannot run profile, GL_QUERY_COUNTER_BITS == 0." << std::endl;
245             exit(-1);
246         }
247     }
248
249     if (retrace::profilingPixelsDrawn && !supportsOcclusion) {
250         std::cout << "Error: Cannot run profile, GL_ARB_occlusion_query extension is not supported." << std::endl;
251         exit(-1);
252     }
253
254     if (retrace::debug && supportsDebugOutput) {
255         glDebugMessageCallbackARB(&debugOutputCallback, currentContext);
256
257         if (DEBUG_OUTPUT_SYNCHRONOUS) {
258             glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
259         }
260     }
261
262     /* Sync the gpu and cpu start times */
263     gpuTime = getGpuTimestamp();
264     cpuTime = getCpuTimestamp();
265     retrace::profiler.setBaseTimes(gpuTime, cpuTime);
266 }
267
268 void
269 frame_start() {
270     firstFrame = false;
271
272     if (retrace::profiling) {
273         retrace::profiler.addFrameStart(retrace::frameNo, getGpuTimestamp(), getCpuTimestamp());
274     }
275 }
276
277 void
278 frame_complete(trace::Call &call) {
279     if (retrace::profiling) {
280         /* Complete any remaining queries */
281         flushQueries();
282
283         /* Indicate end of current frame */
284         retrace::profiler.addFrameEnd(getGpuTimestamp(), getCpuTimestamp());
285     }
286
287     retrace::frameComplete(call);
288
289     /* Indicate start of next frame */
290     frame_start();
291
292     if (!currentDrawable) {
293         return;
294     }
295
296     if (retrace::debug && !currentDrawable->visible) {
297         retrace::warning(call) << "could not infer drawable size (glViewport never called)\n";
298     }
299 }
300
301 static const char*
302 getDebugOutputSource(GLenum source) {
303     switch(source) {
304     case GL_DEBUG_SOURCE_API_ARB:
305         return "API";
306     case GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB:
307         return "Window System";
308     case GL_DEBUG_SOURCE_SHADER_COMPILER_ARB:
309         return "Shader Compiler";
310     case GL_DEBUG_SOURCE_THIRD_PARTY_ARB:
311         return "Third Party";
312     case GL_DEBUG_SOURCE_APPLICATION_ARB:
313         return "Application";
314     case GL_DEBUG_SOURCE_OTHER_ARB:
315     default:
316         return "";
317     }
318 }
319
320 static const char*
321 getDebugOutputType(GLenum type) {
322     switch(type) {
323     case GL_DEBUG_TYPE_ERROR_ARB:
324         return "error";
325     case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB:
326         return "deprecated behaviour";
327     case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB:
328         return "undefined behaviour";
329     case GL_DEBUG_TYPE_PORTABILITY_ARB:
330         return "portability issue";
331     case GL_DEBUG_TYPE_PERFORMANCE_ARB:
332         return "performance issue";
333     case GL_DEBUG_TYPE_OTHER_ARB:
334     default:
335         return "unknown issue";
336     }
337 }
338
339 static const char*
340 getDebugOutputSeverity(GLenum severity) {
341     switch(severity) {
342     case GL_DEBUG_SEVERITY_HIGH_ARB:
343         return "High";
344     case GL_DEBUG_SEVERITY_MEDIUM_ARB:
345         return "Medium";
346     case GL_DEBUG_SEVERITY_LOW_ARB:
347         return "Low";
348     default:
349         return "usnknown";
350     }
351 }
352
353 static void APIENTRY
354 debugOutputCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, GLvoid* userParam) {
355     std::cerr << retrace::callNo << ": ";
356     std::cerr << "glDebugOutputCallback: ";
357     std::cerr << getDebugOutputSeverity(severity) << " severity ";
358     std::cerr << getDebugOutputSource(source) << " " << getDebugOutputType(type);
359     std::cerr << " " << id;
360     std::cerr << ", " << message;
361     std::cerr << std::endl;
362 }
363
364 } /* namespace glretrace */
365
366
367 void
368 retrace::setUp(void) {
369     glws::init();
370 }
371
372
373 void
374 retrace::addCallbacks(retrace::Retracer &retracer)
375 {
376     retracer.addCallbacks(glretrace::gl_callbacks);
377     retracer.addCallbacks(glretrace::glx_callbacks);
378     retracer.addCallbacks(glretrace::wgl_callbacks);
379     retracer.addCallbacks(glretrace::cgl_callbacks);
380     retracer.addCallbacks(glretrace::egl_callbacks);
381 }
382
383
384 image::Image *
385 retrace::getSnapshot(void) {
386     if (!glretrace::currentDrawable) {
387         return NULL;
388     }
389
390     return glstate::getDrawBufferImage();
391 }
392
393
394 bool
395 retrace::dumpState(std::ostream &os)
396 {
397     if (glretrace::insideGlBeginEnd ||
398         !glretrace::currentDrawable ||
399         !glretrace::currentContext) {
400         return false;
401     }
402
403     glstate::dumpCurrentContext(os);
404
405     return true;
406 }
407
408 void
409 retrace::flushRendering(void) {
410     glretrace::flushQueries();
411     glFlush();
412 }
413
414 void
415 retrace::waitForInput(void) {
416     while (glws::processEvents()) {
417     }
418 }
419
420 void
421 retrace::cleanUp(void) {
422     glws::cleanup();
423 }