1 /**************************************************************************
3 * Copyright 2011 Jose Fonseca
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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
24 **************************************************************************/
29 #include "retrace.hpp"
31 #include "glstate.hpp"
32 #include "glretrace.hpp"
33 #include "os_time.hpp"
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.
39 #define DEBUG_OUTPUT_SYNCHRONOUS 0
43 bool insideList = false;
44 bool insideGlBeginEnd = false;
55 GLuint ids[NUM_QUERIES];
59 const trace::FunctionSig *sig;
64 static bool supportsElapsed = true;
65 static bool supportsTimestamp = true;
66 static bool supportsOcclusion = true;
67 static bool supportsDebugOutput = true;
69 static std::list<CallQuery> callQueries;
72 debugOutputCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, GLvoid* userParam);
75 checkGlError(trace::Call &call) {
76 GLenum error = glGetError();
77 while (error != GL_NO_ERROR) {
78 std::ostream & os = retrace::warning(call);
86 os << "GL_INVALID_ENUM";
88 case GL_INVALID_VALUE:
89 os << "GL_INVALID_VALUE";
91 case GL_INVALID_OPERATION:
92 os << "GL_INVALID_OPERATION";
94 case GL_STACK_OVERFLOW:
95 os << "GL_STACK_OVERFLOW";
97 case GL_STACK_UNDERFLOW:
98 os << "GL_STACK_UNDERFLOW";
100 case GL_OUT_OF_MEMORY:
101 os << "GL_OUT_OF_MEMORY";
103 case GL_INVALID_FRAMEBUFFER_OPERATION:
104 os << "GL_INVALID_FRAMEBUFFER_OPERATION";
106 case GL_TABLE_TOO_LARGE:
107 os << "GL_TABLE_TOO_LARGE";
115 error = glGetError();
119 static inline int64_t
120 getCurrentTime(void) {
121 if (retrace::profilingGpuTimes && supportsTimestamp) {
122 /* Get the current GL time without stalling */
123 GLint64 timestamp = 0;
124 glGetInteger64v(GL_TIMESTAMP, ×tamp);
127 return os::getTime();
131 static inline int64_t
132 getTimeFrequency(void) {
133 if (retrace::profilingGpuTimes && supportsTimestamp) {
136 return os::timeFrequency;
141 completeCallQuery(CallQuery& query) {
142 /* Get call start and duration */
143 int64_t gpuStart = 0, gpuDuration = 0, cpuDuration = 0, pixels = 0;
146 if (retrace::profilingGpuTimes) {
147 if (supportsTimestamp) {
148 glGetQueryObjecti64vEXT(query.ids[GPU_START], GL_QUERY_RESULT, &gpuStart);
151 glGetQueryObjecti64vEXT(query.ids[GPU_DURATION], GL_QUERY_RESULT, &gpuDuration);
154 if (retrace::profilingPixelsDrawn) {
155 glGetQueryObjecti64vEXT(query.ids[OCCLUSION], GL_QUERY_RESULT, &pixels);
162 if (retrace::profilingCpuTimes) {
163 cpuDuration = query.cpuEnd - query.cpuStart;
166 glDeleteQueries(NUM_QUERIES, query.ids);
168 /* Add call to profile */
169 retrace::profiler.addCall(query.call, query.sig->name, query.program, pixels, gpuStart, gpuDuration, query.cpuStart, cpuDuration);
174 for (std::list<CallQuery>::iterator itr = callQueries.begin(); itr != callQueries.end(); ++itr) {
175 completeCallQuery(*itr);
182 beginProfile(trace::Call &call, bool isDraw) {
183 glretrace::Context *currentContext = glretrace::getCurrentContext();
185 /* Create call query */
187 query.isDraw = isDraw;
188 query.call = call.no;
189 query.sig = call.sig;
190 query.program = currentContext ? currentContext->activeProgram : 0;
192 glGenQueries(NUM_QUERIES, query.ids);
194 /* GPU profiling only for draw calls */
196 if (retrace::profilingGpuTimes) {
197 if (supportsTimestamp) {
198 glQueryCounter(query.ids[GPU_START], GL_TIMESTAMP);
201 glBeginQuery(GL_TIME_ELAPSED, query.ids[GPU_DURATION]);
204 if (retrace::profilingPixelsDrawn) {
205 glBeginQuery(GL_SAMPLES_PASSED, query.ids[OCCLUSION]);
209 callQueries.push_back(query);
211 /* CPU profiling for all calls */
212 if (retrace::profilingCpuTimes) {
213 CallQuery& query = callQueries.back();
214 query.cpuStart = getCurrentTime();
219 endProfile(trace::Call &call, bool isDraw) {
221 /* CPU profiling for all calls */
222 if (retrace::profilingCpuTimes) {
223 CallQuery& query = callQueries.back();
224 query.cpuEnd = getCurrentTime();
227 /* GPU profiling only for draw calls */
229 if (retrace::profilingGpuTimes) {
230 glEndQuery(GL_TIME_ELAPSED);
233 if (retrace::profilingPixelsDrawn) {
234 glEndQuery(GL_SAMPLES_PASSED);
241 glretrace::Context *currentContext = glretrace::getCurrentContext();
243 /* Ensure we have adequate extension support */
244 assert(currentContext);
245 supportsTimestamp = currentContext->hasExtension("GL_ARB_timer_query");
246 supportsElapsed = currentContext->hasExtension("GL_EXT_timer_query") || supportsTimestamp;
247 supportsOcclusion = currentContext->hasExtension("GL_ARB_occlusion_query");
248 supportsDebugOutput = currentContext->hasExtension("GL_ARB_debug_output");
250 /* Check for timer query support */
251 if (retrace::profilingGpuTimes) {
252 if (!supportsTimestamp && !supportsElapsed) {
253 std::cout << "Error: Cannot run profile, GL_EXT_timer_query extension is not supported." << std::endl;
258 glGetQueryiv(GL_TIME_ELAPSED, GL_QUERY_COUNTER_BITS, &bits);
261 std::cout << "Error: Cannot run profile, GL_QUERY_COUNTER_BITS == 0." << std::endl;
266 /* Check for occlusion query support */
267 if (retrace::profilingPixelsDrawn && !supportsOcclusion) {
268 std::cout << "Error: Cannot run profile, GL_ARB_occlusion_query extension is not supported." << std::endl;
272 /* Setup debug message call back */
273 if (retrace::debug && supportsDebugOutput) {
274 glretrace::Context *currentContext = glretrace::getCurrentContext();
275 glDebugMessageCallbackARB(&debugOutputCallback, currentContext);
277 if (DEBUG_OUTPUT_SYNCHRONOUS) {
278 glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
282 /* Sync the gpu and cpu start times */
283 if (retrace::profilingCpuTimes || retrace::profilingGpuTimes) {
284 if (!retrace::profiler.hasBaseTimes()) {
285 GLint64 currentTime = getCurrentTime();
286 retrace::profiler.setBaseCpuTime(currentTime);
287 retrace::profiler.setBaseGpuTime(currentTime);
293 frame_complete(trace::Call &call) {
294 if (retrace::profiling) {
295 /* Complete any remaining queries */
298 /* Indicate end of current frame */
299 retrace::profiler.addFrameEnd();
302 retrace::frameComplete(call);
304 glretrace::Context *currentContext = glretrace::getCurrentContext();
305 if (!currentContext) {
309 assert(currentContext->drawable);
310 if (retrace::debug && !currentContext->drawable->visible) {
311 retrace::warning(call) << "could not infer drawable size (glViewport never called)\n";
316 getDebugOutputSource(GLenum source) {
318 case GL_DEBUG_SOURCE_API_ARB:
320 case GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB:
321 return "Window System";
322 case GL_DEBUG_SOURCE_SHADER_COMPILER_ARB:
323 return "Shader Compiler";
324 case GL_DEBUG_SOURCE_THIRD_PARTY_ARB:
325 return "Third Party";
326 case GL_DEBUG_SOURCE_APPLICATION_ARB:
327 return "Application";
328 case GL_DEBUG_SOURCE_OTHER_ARB:
335 getDebugOutputType(GLenum type) {
337 case GL_DEBUG_TYPE_ERROR_ARB:
339 case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB:
340 return "deprecated behaviour";
341 case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB:
342 return "undefined behaviour";
343 case GL_DEBUG_TYPE_PORTABILITY_ARB:
344 return "portability issue";
345 case GL_DEBUG_TYPE_PERFORMANCE_ARB:
346 return "performance issue";
347 case GL_DEBUG_TYPE_OTHER_ARB:
349 return "unknown issue";
354 getDebugOutputSeverity(GLenum severity) {
356 case GL_DEBUG_SEVERITY_HIGH_ARB:
358 case GL_DEBUG_SEVERITY_MEDIUM_ARB:
360 case GL_DEBUG_SEVERITY_LOW_ARB:
368 debugOutputCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, GLvoid* userParam) {
369 std::cerr << retrace::callNo << ": ";
370 std::cerr << "glDebugOutputCallback: ";
371 std::cerr << getDebugOutputSeverity(severity) << " severity ";
372 std::cerr << getDebugOutputSource(source) << " " << getDebugOutputType(type);
373 std::cerr << " " << id;
374 std::cerr << ", " << message;
375 std::cerr << std::endl;
378 } /* namespace glretrace */
381 class GLDumper : public retrace::Dumper {
385 if (!glretrace::getCurrentContext()) {
388 return glstate::getDrawBufferImage();
392 dumpState(std::ostream &os) {
393 glretrace::Context *currentContext = glretrace::getCurrentContext();
394 if (glretrace::insideGlBeginEnd ||
398 glstate::dumpCurrentContext(os);
403 static GLDumper glDumper;
407 retrace::setUp(void) {
414 retrace::addCallbacks(retrace::Retracer &retracer)
416 retracer.addCallbacks(glretrace::gl_callbacks);
417 retracer.addCallbacks(glretrace::glx_callbacks);
418 retracer.addCallbacks(glretrace::wgl_callbacks);
419 retracer.addCallbacks(glretrace::cgl_callbacks);
420 retracer.addCallbacks(glretrace::egl_callbacks);
425 retrace::flushRendering(void) {
426 glretrace::Context *currentContext = glretrace::getCurrentContext();
427 if (currentContext) {
428 glretrace::flushQueries();
434 retrace::waitForInput(void) {
435 while (glws::processEvents()) {
441 retrace::cleanUp(void) {