X-Git-Url: https://git.cworth.org/git?a=blobdiff_plain;f=retrace%2Fglretrace_main.cpp;h=63912ec9f76a4272980639a57c1d37f6574522d7;hb=9115776479fc67fe12cc3f7ccb8da2fd684d2232;hp=6dd44eba76060e1ef0513695f6374dae2a370420;hpb=addf7f90727a50040d79e6da6d59ffb031074674;p=apitrace diff --git a/retrace/glretrace_main.cpp b/retrace/glretrace_main.cpp old mode 100644 new mode 100755 index 6dd44eb..63912ec --- a/retrace/glretrace_main.cpp +++ b/retrace/glretrace_main.cpp @@ -30,13 +30,38 @@ #include "glproc.hpp" #include "glstate.hpp" #include "glretrace.hpp" +#include "os_time.hpp" +/* Synchronous debug output may reduce performance however, + * without it the callNo in the callback may be inaccurate + * as the callback may be called at any time. + */ +#define DEBUG_OUTPUT_SYNCHRONOUS 0 namespace glretrace { bool insideList = false; bool insideGlBeginEnd = false; +struct CallQuery +{ + GLuint ids[3]; + unsigned call; + GLuint program; + const trace::FunctionSig *sig; + uint64_t start; + uint64_t duration; +}; + +static bool supportsElapsed = true; +static bool supportsTimestamp = true; +static bool supportsOcclusion = true; + +static bool firstFrame = true; +static std::list callQueries; + +static void APIENTRY +debugOutputCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, GLvoid* userParam); void checkGlError(trace::Call &call) { @@ -83,107 +108,251 @@ checkGlError(trace::Call &call) { os << "\n"; } -struct CallQuery -{ - GLuint ids[2]; - unsigned call; - const trace::FunctionSig *sig; -}; +static GLuint64 +getGpuTimestamp() { + GLuint query = 0; + GLuint64 timestamp = 0; -static std::vector callQueries; -static GLuint frameQueries[2] = { 0, 0 }; + if (retrace::profilingGpuTimes && supportsTimestamp) { + glGenQueries(1, &query); + glQueryCounter(query, GL_TIMESTAMP); + glGetQueryObjectui64vEXT(query, GL_QUERY_RESULT, ×tamp); + glDeleteQueries(1, &query); + } -void frame_start() { - if (retrace::profileGPU) { - glGenQueries(2, frameQueries); + return timestamp; +} - /* Query frame start time */ - glQueryCounter(frameQueries[0], GL_TIMESTAMP); - } +static GLuint64 +getCpuTimestamp() { + if (retrace::profilingCpuTimes) { + return os::getTime(); + } else { + return 0; + } } -void frame_complete(trace::Call &call) { - if (retrace::profileGPU) { - /* Query frame end time */ - glQueryCounter(frameQueries[1], GL_TIMESTAMP); +static void +completeCallQuery(CallQuery& query) { + /* Get call start and duration */ + GLuint64 timestamp = 0, duration = 0, samples = 0; + + if (retrace::profilingGpuTimes) { + if (supportsTimestamp) { + glGetQueryObjectui64vEXT(query.ids[0], GL_QUERY_RESULT, ×tamp); + } - completeQueries(); + if (supportsElapsed) { + glGetQueryObjectui64vEXT(query.ids[1], GL_QUERY_RESULT, &duration); + } } - retrace::frameComplete(call); + if (retrace::profilingPixelsDrawn && supportsOcclusion) { + glGetQueryObjectui64vEXT(query.ids[2], GL_QUERY_RESULT, &samples); + } - /* Indicate start of next frame */ - frame_start(); + glDeleteQueries(3, query.ids); - if (!currentDrawable) { - return; - } + /* Add call to profile */ + retrace::profiler.addCall(query.call, query.sig->name, query.program, samples, timestamp, duration, query.start, query.duration); +} - if (retrace::debug && !currentDrawable->visible) { - retrace::warning(call) << "could not infer drawable size (glViewport never called)\n"; +void +flushQueries() { + for (std::list::iterator itr = callQueries.begin(); itr != callQueries.end(); ++itr) { + completeCallQuery(*itr); } + + callQueries.clear(); } void -completeQueries() -{ - if (callQueries.size() == 0) - return; +beginProfile(trace::Call &call) { + if (firstFrame) { + frame_start(); + } - GLint available; - GLuint64 frameBegin, frameEnd; + /* Create call query */ + CallQuery query; + query.call = call.no; + query.sig = call.sig; + query.program = glretrace::currentContext ? glretrace::currentContext->activeProgram : 0; - /* Wait for frame to finish */ - do { - glGetQueryObjectiv(frameQueries[1], GL_QUERY_RESULT_AVAILABLE, &available); - } while(!available); + glGenQueries(3, query.ids); - /* Get frame start and end */ - glGetQueryObjectui64vEXT(frameQueries[0], GL_QUERY_RESULT, &frameBegin); - glGetQueryObjectui64vEXT(frameQueries[1], GL_QUERY_RESULT, &frameEnd); - glDeleteQueries(2, frameQueries); + if (retrace::profilingGpuTimes) { + if (supportsTimestamp) { + glQueryCounter(query.ids[0], GL_TIMESTAMP); + } - /* Add frame to profile */ - retrace::profiler.addFrame(trace::Profiler::Frame(retrace::frameNo, frameBegin, frameEnd - frameBegin)); + if (supportsElapsed) { + glBeginQuery(GL_TIME_ELAPSED, query.ids[1]); + } + } - /* Loop through all active call queries */ - for (std::vector::iterator itr = callQueries.begin(); itr != callQueries.end(); ++itr) { - CallQuery& query = *itr; - GLuint64 timestamp, duration; + if (retrace::profilingPixelsDrawn && supportsOcclusion) { + glBeginQuery(GL_SAMPLES_PASSED, query.ids[2]); + } - /* Get queue start and duration */ - glGetQueryObjectui64vEXT(query.ids[0], GL_QUERY_RESULT, ×tamp); - glGetQueryObjectui64vEXT(query.ids[1], GL_QUERY_RESULT, &duration); - glDeleteQueries(2, query.ids); + if (retrace::profilingCpuTimes) { + query.start = os::getTime(); + } + + callQueries.push_back(query); +} - /* Add call to profile */ - retrace::profiler.addCall(trace::Profiler::Call(query.call, query.sig->name, timestamp, duration)); - } +void +endProfile(trace::Call &call) { + if (retrace::profilingCpuTimes) { + CallQuery& query = callQueries.back(); + query.duration = os::getTime() - query.start; + } - callQueries.clear(); + if (retrace::profilingGpuTimes && supportsElapsed) { + glEndQuery(GL_TIME_ELAPSED); + } + + if (retrace::profilingPixelsDrawn && supportsOcclusion) { + glEndQuery(GL_SAMPLES_PASSED); + } } void -beginProfileGPU(trace::Call &call) { - if (frameQueries[0] == 0) { - frame_start(); - } +initContext() { + /* Check for extension support */ + const char* extensions = (const char*)glGetString(GL_EXTENSIONS); + GLint bits; + + supportsTimestamp = glws::checkExtension("GL_ARB_timer_query", extensions); + supportsElapsed = glws::checkExtension("GL_EXT_timer_query", extensions) || supportsTimestamp; + supportsOcclusion = glws::checkExtension("GL_ARB_occlusion_query", extensions); + + if (retrace::profilingGpuTimes) { + if (!supportsTimestamp && !supportsElapsed) { + std::cout << "Error: Cannot run profile, GL_EXT_timer_query extension is not supported." << std::endl; + exit(-1); + } + + glGetQueryiv(GL_TIME_ELAPSED, GL_QUERY_COUNTER_BITS, &bits); + + if (!bits) { + std::cout << "Error: Cannot run profile, GL_QUERY_COUNTER_BITS == 0." << std::endl; + exit(-1); + } + } - CallQuery query; - query.call = call.no; - query.sig = call.sig; + if (retrace::profilingPixelsDrawn && !supportsOcclusion) { + std::cout << "Error: Cannot run profile, GL_ARB_occlusion_query extension is not supported." << std::endl; + exit(-1); + } + + if (retrace::debug) { + bool supportsDebugOutput = glws::checkExtension("GL_ARB_debug_output", extensions); + + if (supportsDebugOutput) { + glDebugMessageCallbackARB(&debugOutputCallback, currentContext); + + if (DEBUG_OUTPUT_SYNCHRONOUS) { + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); + } + } + } +} - /* Create start and duration queries */ - glGenQueries(2, query.ids); - glQueryCounter(query.ids[0], GL_TIMESTAMP); - glBeginQuery(GL_TIME_ELAPSED, query.ids[1]); +void +frame_start() { + firstFrame = false; - callQueries.push_back(query); + if (retrace::profiling) { + retrace::profiler.addFrameStart(retrace::frameNo, getGpuTimestamp(), getCpuTimestamp()); + } } void -endProfileGPU(trace::Call &call) { - glEndQuery(GL_TIME_ELAPSED); +frame_complete(trace::Call &call) { + if (retrace::profiling) { + /* Complete any remaining queries */ + flushQueries(); + + /* Indicate end of current frame */ + retrace::profiler.addFrameEnd(getGpuTimestamp(), getCpuTimestamp()); + } + + retrace::frameComplete(call); + + /* Indicate start of next frame */ + frame_start(); + + if (!currentDrawable) { + return; + } + + if (retrace::debug && !currentDrawable->visible) { + retrace::warning(call) << "could not infer drawable size (glViewport never called)\n"; + } +} + +static const char* +getDebugOutputSource(GLenum source) { + switch(source) { + case GL_DEBUG_SOURCE_API_ARB: + return "API"; + case GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB: + return "Window System"; + case GL_DEBUG_SOURCE_SHADER_COMPILER_ARB: + return "Shader Compiler"; + case GL_DEBUG_SOURCE_THIRD_PARTY_ARB: + return "Third Party"; + case GL_DEBUG_SOURCE_APPLICATION_ARB: + return "Application"; + case GL_DEBUG_SOURCE_OTHER_ARB: + default: + return ""; + } +} + +static const char* +getDebugOutputType(GLenum type) { + switch(type) { + case GL_DEBUG_TYPE_ERROR_ARB: + return "error"; + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB: + return "deprecated behaviour"; + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB: + return "undefined behaviour"; + case GL_DEBUG_TYPE_PORTABILITY_ARB: + return "portability issue"; + case GL_DEBUG_TYPE_PERFORMANCE_ARB: + return "performance issue"; + case GL_DEBUG_TYPE_OTHER_ARB: + default: + return "unknown issue"; + } +} + +static const char* +getDebugOutputSeverity(GLenum severity) { + switch(severity) { + case GL_DEBUG_SEVERITY_HIGH_ARB: + return "High"; + case GL_DEBUG_SEVERITY_MEDIUM_ARB: + return "Medium"; + case GL_DEBUG_SEVERITY_LOW_ARB: + return "Low"; + default: + return "usnknown"; + } +} + +static void APIENTRY +debugOutputCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, GLvoid* userParam) { + std::cerr << retrace::callNo << ": "; + std::cerr << "glDebugOutputCallback: "; + std::cerr << getDebugOutputSeverity(severity) << " severity "; + std::cerr << getDebugOutputSource(source) << " " << getDebugOutputType(type); + std::cerr << " " << id; + std::cerr << ", " << message; + std::cerr << std::endl; } } /* namespace glretrace */ @@ -232,6 +401,7 @@ retrace::dumpState(std::ostream &os) void retrace::flushRendering(void) { + glretrace::flushQueries(); glFlush(); }