From addf7f90727a50040d79e6da6d59ffb031074674 Mon Sep 17 00:00:00 2001 From: James Benton Date: Fri, 20 Jul 2012 18:56:19 +0100 Subject: [PATCH] Add support for basic GPU profiling of draw calls to retrace. Currently only supports opengl using glBegin/EndQuery and glQueryCounter. Based on code from Yuanhan Liu timing-trace branch. --- CMakeLists.txt | 1 + common/trace_profiler.cpp | 61 +++++++++++++++++++++++ common/trace_profiler.hpp | 99 ++++++++++++++++++++++++++++++++++++++ retrace/glretrace.hpp | 6 +++ retrace/glretrace.py | 26 +++++++++- retrace/glretrace_main.cpp | 90 ++++++++++++++++++++++++++++++++++ retrace/retrace.hpp | 6 +++ retrace/retrace_main.cpp | 6 ++- 8 files changed, 293 insertions(+), 2 deletions(-) create mode 100644 common/trace_profiler.cpp create mode 100644 common/trace_profiler.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2edc51b..a7cbf20 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -295,6 +295,7 @@ add_library (common STATIC common/trace_loader.cpp common/trace_resource.cpp common/trace_tools_trace.cpp + common/trace_profiler.cpp common/image.cpp common/image_bmp.cpp common/image_pnm.cpp diff --git a/common/trace_profiler.cpp b/common/trace_profiler.cpp new file mode 100644 index 0000000..fdc0fd4 --- /dev/null +++ b/common/trace_profiler.cpp @@ -0,0 +1,61 @@ +/************************************************************************** + * + * Copyright 2012 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + +#include "trace_profiler.hpp" +#include + +namespace trace { +Profiler::Profiler() + : baseTime(0) +{ +} + +Profiler::~Profiler() +{ +} + +void Profiler::addCall(const Call& call) +{ + if (baseTime == 0) + baseTime = call.gpu.start; + + std::cout << "call " + << call.no << " " + << (call.gpu.start - baseTime) << " " + << call.gpu.duration << " " + << call.name << std::endl; +} + +void Profiler::addFrame(const Frame& frame) +{ + if (baseTime == 0) + baseTime = frame.gpu.start; + + std::cout << "frame " + << frame.no << " " + << (frame.gpu.start - baseTime) << " " + << frame.gpu.duration << std::endl; +} +} diff --git a/common/trace_profiler.hpp b/common/trace_profiler.hpp new file mode 100644 index 0000000..19a874f --- /dev/null +++ b/common/trace_profiler.hpp @@ -0,0 +1,99 @@ +/************************************************************************** + * + * Copyright 2012 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + +#ifndef TRACE_PROFILER_H +#define TRACE_PROFILER_H + +#include +#include + +namespace trace +{ +class Profiler +{ +public: + struct GpuTime + { + GpuTime() + : start(0), duration(0) + { + } + + GpuTime(uint64_t start_, uint64_t duration_) + : start(start_), duration(duration_) + { + } + + uint64_t start; + uint64_t duration; + }; + + struct Call + { + Call() + : no(0) + { + } + + Call(unsigned no_, const char* name_, uint64_t gpu_start, uint64_t gpu_duration) + : no(no_), name(name_), gpu(gpu_start, gpu_duration) + { + } + + unsigned no; + std::string name; + + GpuTime gpu; + }; + + struct Frame + { + Frame() + : no(0) + { + } + + Frame(unsigned no_, uint64_t gpu_start, uint64_t gpu_duration) + : no(no_), gpu(gpu_start, gpu_duration) + { + } + + unsigned no; + GpuTime gpu; + }; + +public: + Profiler(); + ~Profiler(); + + void addCall(const Call& call); + void addFrame(const Frame& frame); + +private: + uint64_t baseTime; +}; +} + +#endif // TRACE_PROFILER_H diff --git a/retrace/glretrace.hpp b/retrace/glretrace.hpp index fea187c..820b4e1 100644 --- a/retrace/glretrace.hpp +++ b/retrace/glretrace.hpp @@ -33,6 +33,7 @@ namespace glretrace { +extern bool insideList; extern bool insideGlBeginEnd; @@ -64,10 +65,15 @@ extern const retrace::Entry glx_callbacks[]; extern const retrace::Entry wgl_callbacks[]; extern const retrace::Entry egl_callbacks[]; +void frame_start(); void frame_complete(trace::Call &call); void updateDrawable(int width, int height); +void completeQueries(); +void beginProfileGPU(trace::Call &call); +void endProfileGPU(trace::Call &call); + } /* namespace glretrace */ diff --git a/retrace/glretrace.py b/retrace/glretrace.py index 08fc6a0..440366c 100644 --- a/retrace/glretrace.py +++ b/retrace/glretrace.py @@ -289,7 +289,26 @@ class GlRetracer(Retracer): print r' if (pipeline) {' print r' _pipelineHasBeenBound = true;' print r' }' - + + profileDraw = ( + function.name in self.draw_array_function_names or + function.name in self.draw_elements_function_names or + function.name in self.draw_indirect_function_names or + function.name in self.misc_draw_function_names + ) + + # Only profile if not inside a list as the queries get inserted into lsit + if function.name == 'glNewList': + print r' glretrace::insideList = true;'; + + if function.name == 'glEndList': + print r' glretrace::insideList = false;'; + + if profileDraw: + print r' if (!glretrace::insideList && retrace::profileGPU) {' + print r' glretrace::beginProfileGPU(call);' + print r' }' + if function.name == 'glCreateShaderProgramv': # When dumping state, break down glCreateShaderProgramv so that the # shader source can be recovered. @@ -321,6 +340,11 @@ class GlRetracer(Retracer): else: Retracer.invokeFunction(self, function) + if profileDraw: + print r' if (!glretrace::insideList && retrace::profileGPU) {' + print r' glretrace::endProfileGPU(call);' + print r' }' + # Error checking if function.name == "glBegin": print ' glretrace::insideGlBeginEnd = true;' diff --git a/retrace/glretrace_main.cpp b/retrace/glretrace_main.cpp index 49be505..6dd44eb 100644 --- a/retrace/glretrace_main.cpp +++ b/retrace/glretrace_main.cpp @@ -34,6 +34,7 @@ namespace glretrace { +bool insideList = false; bool insideGlBeginEnd = false; @@ -82,10 +83,38 @@ checkGlError(trace::Call &call) { os << "\n"; } +struct CallQuery +{ + GLuint ids[2]; + unsigned call; + const trace::FunctionSig *sig; +}; + +static std::vector callQueries; +static GLuint frameQueries[2] = { 0, 0 }; + +void frame_start() { + if (retrace::profileGPU) { + glGenQueries(2, frameQueries); + + /* Query frame start time */ + glQueryCounter(frameQueries[0], GL_TIMESTAMP); + } +} void frame_complete(trace::Call &call) { + if (retrace::profileGPU) { + /* Query frame end time */ + glQueryCounter(frameQueries[1], GL_TIMESTAMP); + + completeQueries(); + } + retrace::frameComplete(call); + /* Indicate start of next frame */ + frame_start(); + if (!currentDrawable) { return; } @@ -95,6 +124,67 @@ void frame_complete(trace::Call &call) { } } +void +completeQueries() +{ + if (callQueries.size() == 0) + return; + + GLint available; + GLuint64 frameBegin, frameEnd; + + /* Wait for frame to finish */ + do { + glGetQueryObjectiv(frameQueries[1], GL_QUERY_RESULT_AVAILABLE, &available); + } while(!available); + + /* Get frame start and end */ + glGetQueryObjectui64vEXT(frameQueries[0], GL_QUERY_RESULT, &frameBegin); + glGetQueryObjectui64vEXT(frameQueries[1], GL_QUERY_RESULT, &frameEnd); + glDeleteQueries(2, frameQueries); + + /* Add frame to profile */ + retrace::profiler.addFrame(trace::Profiler::Frame(retrace::frameNo, frameBegin, frameEnd - frameBegin)); + + /* Loop through all active call queries */ + for (std::vector::iterator itr = callQueries.begin(); itr != callQueries.end(); ++itr) { + CallQuery& query = *itr; + GLuint64 timestamp, duration; + + /* 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); + + /* Add call to profile */ + retrace::profiler.addCall(trace::Profiler::Call(query.call, query.sig->name, timestamp, duration)); + } + + callQueries.clear(); +} + +void +beginProfileGPU(trace::Call &call) { + if (frameQueries[0] == 0) { + frame_start(); + } + + CallQuery query; + query.call = call.no; + query.sig = call.sig; + + /* Create start and duration queries */ + glGenQueries(2, query.ids); + glQueryCounter(query.ids[0], GL_TIMESTAMP); + glBeginQuery(GL_TIME_ELAPSED, query.ids[1]); + + callQueries.push_back(query); +} + +void +endProfileGPU(trace::Call &call) { + glEndQuery(GL_TIME_ELAPSED); +} } /* namespace glretrace */ diff --git a/retrace/retrace.hpp b/retrace/retrace.hpp index a019de7..311cef9 100644 --- a/retrace/retrace.hpp +++ b/retrace/retrace.hpp @@ -36,6 +36,7 @@ #include "trace_model.hpp" #include "trace_parser.hpp" +#include "trace_profiler.hpp" namespace image { @@ -47,6 +48,7 @@ namespace retrace { extern trace::Parser parser; +extern trace::Profiler profiler; /** @@ -156,6 +158,10 @@ extern bool dumpingState; extern bool doubleBuffer; extern bool coreProfile; +extern bool profileGPU; + +extern unsigned frameNo; + std::ostream &warning(trace::Call &call); diff --git a/retrace/retrace_main.cpp b/retrace/retrace_main.cpp index f7fb711..164a596 100644 --- a/retrace/retrace_main.cpp +++ b/retrace/retrace_main.cpp @@ -49,11 +49,13 @@ namespace retrace { trace::Parser parser; +trace::Profiler profiler; int verbosity = 0; bool debug = true; bool profiling = false; +bool profileGPU = false; bool dumpingState = false; @@ -61,7 +63,7 @@ bool doubleBuffer = true; bool coreProfile = false; -static unsigned frameNo = 0; +unsigned frameNo = 0; void @@ -233,6 +235,8 @@ int main(int argc, char **argv) retrace::debug = false; retrace::profiling = true; retrace::verbosity = -1; + } else if (!strcmp(arg, "-pgpu")) { + retrace::profileGPU = true; } else if (!strcmp(arg, "-c")) { comparePrefix = argv[++i]; if (compareFrequency.empty()) { -- 2.43.0