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;
45 bool supportsARBShaderObjects = false;
56 GLuint ids[NUM_QUERIES];
60 const trace::FunctionSig *sig;
65 static bool supportsElapsed = true;
66 static bool supportsTimestamp = true;
67 static bool supportsOcclusion = true;
68 static bool supportsDebugOutput = true;
70 static std::list<CallQuery> callQueries;
73 debugOutputCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, GLvoid* userParam);
76 checkGlError(trace::Call &call) {
77 GLenum error = glGetError();
78 while (error != GL_NO_ERROR) {
79 std::ostream & os = retrace::warning(call);
87 os << "GL_INVALID_ENUM";
89 case GL_INVALID_VALUE:
90 os << "GL_INVALID_VALUE";
92 case GL_INVALID_OPERATION:
93 os << "GL_INVALID_OPERATION";
95 case GL_STACK_OVERFLOW:
96 os << "GL_STACK_OVERFLOW";
98 case GL_STACK_UNDERFLOW:
99 os << "GL_STACK_UNDERFLOW";
101 case GL_OUT_OF_MEMORY:
102 os << "GL_OUT_OF_MEMORY";
104 case GL_INVALID_FRAMEBUFFER_OPERATION:
105 os << "GL_INVALID_FRAMEBUFFER_OPERATION";
107 case GL_TABLE_TOO_LARGE:
108 os << "GL_TABLE_TOO_LARGE";
116 error = glGetError();
120 static inline int64_t
121 getCurrentTime(void) {
122 if (retrace::profilingGpuTimes && supportsTimestamp) {
123 /* Get the current GL time without stalling */
124 GLint64 timestamp = 0;
125 glGetInteger64v(GL_TIMESTAMP, ×tamp);
128 return os::getTime();
132 static inline int64_t
133 getTimeFrequency(void) {
134 if (retrace::profilingGpuTimes && supportsTimestamp) {
137 return os::timeFrequency;
142 completeCallQuery(CallQuery& query) {
143 /* Get call start and duration */
144 int64_t gpuStart = 0, gpuDuration = 0, cpuDuration = 0, pixels = 0;
147 if (retrace::profilingGpuTimes) {
148 if (supportsTimestamp) {
149 glGetQueryObjecti64vEXT(query.ids[GPU_START], GL_QUERY_RESULT, &gpuStart);
152 glGetQueryObjecti64vEXT(query.ids[GPU_DURATION], GL_QUERY_RESULT, &gpuDuration);
155 if (retrace::profilingPixelsDrawn) {
156 glGetQueryObjecti64vEXT(query.ids[OCCLUSION], GL_QUERY_RESULT, &pixels);
163 if (retrace::profilingCpuTimes) {
164 cpuDuration = query.cpuEnd - query.cpuStart;
167 glDeleteQueries(NUM_QUERIES, query.ids);
169 /* Add call to profile */
170 retrace::profiler.addCall(query.call, query.sig->name, query.program, pixels, gpuStart, gpuDuration, query.cpuStart, cpuDuration);
175 for (std::list<CallQuery>::iterator itr = callQueries.begin(); itr != callQueries.end(); ++itr) {
176 completeCallQuery(*itr);
183 beginProfile(trace::Call &call, bool isDraw) {
184 glretrace::Context *currentContext = glretrace::getCurrentContext();
186 /* Create call query */
188 query.isDraw = isDraw;
189 query.call = call.no;
190 query.sig = call.sig;
191 query.program = currentContext ? currentContext->activeProgram : 0;
193 glGenQueries(NUM_QUERIES, query.ids);
195 /* GPU profiling only for draw calls */
197 if (retrace::profilingGpuTimes) {
198 if (supportsTimestamp) {
199 glQueryCounter(query.ids[GPU_START], GL_TIMESTAMP);
202 glBeginQuery(GL_TIME_ELAPSED, query.ids[GPU_DURATION]);
205 if (retrace::profilingPixelsDrawn) {
206 glBeginQuery(GL_SAMPLES_PASSED, query.ids[OCCLUSION]);
210 callQueries.push_back(query);
212 /* CPU profiling for all calls */
213 if (retrace::profilingCpuTimes) {
214 CallQuery& query = callQueries.back();
215 query.cpuStart = getCurrentTime();
220 endProfile(trace::Call &call, bool isDraw) {
222 /* CPU profiling for all calls */
223 if (retrace::profilingCpuTimes) {
224 CallQuery& query = callQueries.back();
225 query.cpuEnd = getCurrentTime();
228 /* GPU profiling only for draw calls */
230 if (retrace::profilingGpuTimes) {
231 glEndQuery(GL_TIME_ELAPSED);
234 if (retrace::profilingPixelsDrawn) {
235 glEndQuery(GL_SAMPLES_PASSED);
242 glretrace::Context *currentContext = glretrace::getCurrentContext();
244 /* Ensure we have adequate extension support */
245 assert(currentContext);
246 supportsTimestamp = currentContext->hasExtension("GL_ARB_timer_query");
247 supportsElapsed = currentContext->hasExtension("GL_EXT_timer_query") || supportsTimestamp;
248 supportsOcclusion = currentContext->hasExtension("GL_ARB_occlusion_query");
249 supportsDebugOutput = currentContext->hasExtension("GL_ARB_debug_output");
250 supportsARBShaderObjects = currentContext->hasExtension("GL_ARB_shader_objects");
252 /* Check for timer query support */
253 if (retrace::profilingGpuTimes) {
254 if (!supportsTimestamp && !supportsElapsed) {
255 std::cout << "Error: Cannot run profile, GL_EXT_timer_query extension is not supported." << std::endl;
260 glGetQueryiv(GL_TIME_ELAPSED, GL_QUERY_COUNTER_BITS, &bits);
263 std::cout << "Error: Cannot run profile, GL_QUERY_COUNTER_BITS == 0." << std::endl;
268 /* Check for occlusion query support */
269 if (retrace::profilingPixelsDrawn && !supportsOcclusion) {
270 std::cout << "Error: Cannot run profile, GL_ARB_occlusion_query extension is not supported." << std::endl;
274 /* Setup debug message call back */
275 if (retrace::debug && supportsDebugOutput) {
276 glretrace::Context *currentContext = glretrace::getCurrentContext();
277 glDebugMessageCallbackARB(&debugOutputCallback, currentContext);
279 if (DEBUG_OUTPUT_SYNCHRONOUS) {
280 glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
284 /* Sync the gpu and cpu start times */
285 if (retrace::profilingCpuTimes || retrace::profilingGpuTimes) {
286 if (!retrace::profiler.hasBaseTimes()) {
287 GLint64 currentTime = getCurrentTime();
288 retrace::profiler.setBaseCpuTime(currentTime);
289 retrace::profiler.setBaseGpuTime(currentTime);
295 frame_complete(trace::Call &call) {
296 if (retrace::profiling) {
297 /* Complete any remaining queries */
300 /* Indicate end of current frame */
301 retrace::profiler.addFrameEnd();
304 retrace::frameComplete(call);
306 glretrace::Context *currentContext = glretrace::getCurrentContext();
307 if (!currentContext) {
311 assert(currentContext->drawable);
312 if (retrace::debug && !currentContext->drawable->visible) {
313 retrace::warning(call) << "could not infer drawable size (glViewport never called)\n";
318 getDebugOutputSource(GLenum source) {
320 case GL_DEBUG_SOURCE_API_ARB:
322 case GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB:
323 return "Window System";
324 case GL_DEBUG_SOURCE_SHADER_COMPILER_ARB:
325 return "Shader Compiler";
326 case GL_DEBUG_SOURCE_THIRD_PARTY_ARB:
327 return "Third Party";
328 case GL_DEBUG_SOURCE_APPLICATION_ARB:
329 return "Application";
330 case GL_DEBUG_SOURCE_OTHER_ARB:
337 getDebugOutputType(GLenum type) {
339 case GL_DEBUG_TYPE_ERROR_ARB:
341 case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB:
342 return "deprecated behaviour";
343 case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB:
344 return "undefined behaviour";
345 case GL_DEBUG_TYPE_PORTABILITY_ARB:
346 return "portability issue";
347 case GL_DEBUG_TYPE_PERFORMANCE_ARB:
348 return "performance issue";
349 case GL_DEBUG_TYPE_OTHER_ARB:
351 return "unknown issue";
356 getDebugOutputSeverity(GLenum severity) {
358 case GL_DEBUG_SEVERITY_HIGH_ARB:
360 case GL_DEBUG_SEVERITY_MEDIUM_ARB:
362 case GL_DEBUG_SEVERITY_LOW_ARB:
370 debugOutputCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, GLvoid* userParam) {
371 std::cerr << retrace::callNo << ": ";
372 std::cerr << "glDebugOutputCallback: ";
373 std::cerr << getDebugOutputSeverity(severity) << " severity ";
374 std::cerr << getDebugOutputSource(source) << " " << getDebugOutputType(type);
375 std::cerr << " " << id;
376 std::cerr << ", " << message;
377 std::cerr << std::endl;
380 } /* namespace glretrace */
383 class GLDumper : public retrace::Dumper {
387 if (!glretrace::getCurrentContext()) {
390 return glstate::getDrawBufferImage();
394 dumpState(std::ostream &os) {
395 glretrace::Context *currentContext = glretrace::getCurrentContext();
396 if (glretrace::insideGlBeginEnd ||
400 glstate::dumpCurrentContext(os);
405 static GLDumper glDumper;
409 retrace::setUp(void) {
416 retrace::addCallbacks(retrace::Retracer &retracer)
418 retracer.addCallbacks(glretrace::gl_callbacks);
419 retracer.addCallbacks(glretrace::glx_callbacks);
420 retracer.addCallbacks(glretrace::wgl_callbacks);
421 retracer.addCallbacks(glretrace::cgl_callbacks);
422 retracer.addCallbacks(glretrace::egl_callbacks);
427 retrace::flushRendering(void) {
428 glretrace::Context *currentContext = glretrace::getCurrentContext();
429 if (currentContext) {
430 glretrace::flushQueries();
436 retrace::waitForInput(void) {
437 while (glws::processEvents()) {
443 retrace::cleanUp(void) {