diff --git a/renderdoc/api/replay/replay_enums.h b/renderdoc/api/replay/replay_enums.h index 6aab99f1c..50bb125d9 100644 --- a/renderdoc/api/replay/replay_enums.h +++ b/renderdoc/api/replay/replay_enums.h @@ -508,11 +508,13 @@ enum GPUCounters eCounter_SamplesWritten, eCounter_VSInvocations, eCounter_HSInvocations, + eCounter_TCSInvocations = eCounter_HSInvocations, eCounter_DSInvocations, eCounter_TESInvocations = eCounter_DSInvocations, eCounter_GSInvocations, eCounter_PSInvocations, eCounter_CSInvocations, + eCounter_GLMaxCounters, // IHV specific counters can be set above this point // with ranges reserved for each IHV diff --git a/renderdoc/driver/gl/gl_common.cpp b/renderdoc/driver/gl/gl_common.cpp index 44adf3f58..6392d15c4 100644 --- a/renderdoc/driver/gl/gl_common.cpp +++ b/renderdoc/driver/gl/gl_common.cpp @@ -429,30 +429,56 @@ GLenum BufferEnum(size_t idx) size_t QueryIdx(GLenum query) { + size_t idx = 0; + switch(query) { - case eGL_SAMPLES_PASSED: return 0; - case eGL_ANY_SAMPLES_PASSED: return 1; - case eGL_ANY_SAMPLES_PASSED_CONSERVATIVE: return 2; - case eGL_PRIMITIVES_GENERATED: return 3; - case eGL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: return 4; - case eGL_TIME_ELAPSED: return 5; + case eGL_SAMPLES_PASSED: idx = 0; break; + case eGL_ANY_SAMPLES_PASSED: idx = 1; break; + case eGL_ANY_SAMPLES_PASSED_CONSERVATIVE: idx = 2; break; + case eGL_PRIMITIVES_GENERATED: idx = 3; break; + case eGL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: idx = 4; break; + case eGL_TIME_ELAPSED: idx = 5; break; + case eGL_VERTICES_SUBMITTED_ARB: idx = 6; break; + case eGL_PRIMITIVES_SUBMITTED_ARB: idx = 7; break; + case eGL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB: idx = 8; break; + case eGL_CLIPPING_INPUT_PRIMITIVES_ARB: idx = 9; break; + case eGL_CLIPPING_OUTPUT_PRIMITIVES_ARB: idx = 10; break; + case eGL_VERTEX_SHADER_INVOCATIONS_ARB: idx = 11; break; + case eGL_TESS_CONTROL_SHADER_PATCHES_ARB: idx = 12; break; + case eGL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB: idx = 13; break; + case eGL_GEOMETRY_SHADER_INVOCATIONS: idx = 14; break; + case eGL_FRAGMENT_SHADER_INVOCATIONS_ARB: idx = 15; break; + case eGL_COMPUTE_SHADER_INVOCATIONS_ARB: idx = 16; break; + default: RDCERR("Unexpected enum as query target: %s", ToStr::Get(query).c_str()); } - return 0; + if(idx >= WrappedOpenGL::MAX_QUERIES) + RDCERR("Query index for enum %s out of range %d", ToStr::Get(query).c_str(), idx); + + return idx; } GLenum QueryEnum(size_t idx) { - GLenum enums[] = { - eGL_SAMPLES_PASSED, - eGL_ANY_SAMPLES_PASSED, - eGL_ANY_SAMPLES_PASSED_CONSERVATIVE, - eGL_PRIMITIVES_GENERATED, - eGL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, - eGL_TIME_ELAPSED, - }; + GLenum enums[] = {eGL_SAMPLES_PASSED, + eGL_ANY_SAMPLES_PASSED, + eGL_ANY_SAMPLES_PASSED_CONSERVATIVE, + eGL_PRIMITIVES_GENERATED, + eGL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, + eGL_TIME_ELAPSED, + eGL_VERTICES_SUBMITTED_ARB, + eGL_PRIMITIVES_SUBMITTED_ARB, + eGL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB, + eGL_CLIPPING_INPUT_PRIMITIVES_ARB, + eGL_CLIPPING_OUTPUT_PRIMITIVES_ARB, + eGL_VERTEX_SHADER_INVOCATIONS_ARB, + eGL_TESS_CONTROL_SHADER_PATCHES_ARB, + eGL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB, + eGL_GEOMETRY_SHADER_INVOCATIONS, + eGL_FRAGMENT_SHADER_INVOCATIONS_ARB, + eGL_COMPUTE_SHADER_INVOCATIONS_ARB}; if(idx < ARRAY_COUNT(enums)) return enums[idx]; diff --git a/renderdoc/driver/gl/gl_counters.cpp b/renderdoc/driver/gl/gl_counters.cpp index 053bd7ab9..433fb8b28 100644 --- a/renderdoc/driver/gl/gl_counters.cpp +++ b/renderdoc/driver/gl/gl_counters.cpp @@ -47,6 +47,18 @@ vector GLReplay::EnumerateCounters() vector ret; ret.push_back(eCounter_EventGPUDuration); + ret.push_back(eCounter_InputVerticesRead); + ret.push_back(eCounter_IAPrimitives); + ret.push_back(eCounter_GSPrimitives); + ret.push_back(eCounter_RasterizerInvocations); + ret.push_back(eCounter_RasterizedPrimitives); + ret.push_back(eCounter_SamplesWritten); + ret.push_back(eCounter_VSInvocations); + ret.push_back(eCounter_TCSInvocations); + ret.push_back(eCounter_TESInvocations); + ret.push_back(eCounter_GSInvocations); + ret.push_back(eCounter_PSInvocations); + ret.push_back(eCounter_CSInvocations); return ret; } @@ -55,39 +67,142 @@ void GLReplay::DescribeCounter(uint32_t counterID, CounterDescription &desc) { desc.counterID = counterID; - if(counterID == eCounter_EventGPUDuration) + switch(counterID) { - desc.name = "GPU Duration"; - desc.description = - "Time taken for this event on the GPU, as measured by delta between two GPU timestamps."; - desc.resultByteWidth = 8; - desc.resultCompType = eCompType_Double; - desc.units = eUnits_Seconds; - } - else - { - desc.name = "Unknown"; - desc.description = "Unknown counter ID"; - desc.resultByteWidth = 0; - desc.resultCompType = eCompType_None; - desc.units = eUnits_Absolute; + case eCounter_EventGPUDuration: + desc.name = "GPU Duration"; + desc.description = + "Time taken for this event on the GPU, as measured by delta between two GPU timestamps."; + desc.resultByteWidth = 8; + desc.resultCompType = eCompType_Double; + desc.units = eUnits_Seconds; + break; + case eCounter_InputVerticesRead: + desc.name = "Input Vertices Read"; + desc.description = "Number of vertices read by input assembler."; + desc.resultByteWidth = 8; + desc.resultCompType = eCompType_UInt; + desc.units = eUnits_Absolute; + break; + case eCounter_IAPrimitives: + desc.name = "Input Primitives"; + desc.description = "Number of primitives read by the input assembler."; + desc.resultByteWidth = 8; + desc.resultCompType = eCompType_UInt; + desc.units = eUnits_Absolute; + break; + case eCounter_GSPrimitives: + desc.name = "GS Primitives"; + desc.description = "Number of primitives output by a geometry shader."; + desc.resultByteWidth = 8; + desc.resultCompType = eCompType_UInt; + desc.units = eUnits_Absolute; + break; + case eCounter_RasterizerInvocations: + desc.name = "Rasterizer Invocations"; + desc.description = "Number of primitives that were sent to the rasterizer."; + desc.resultByteWidth = 8; + desc.resultCompType = eCompType_UInt; + desc.units = eUnits_Absolute; + break; + case eCounter_RasterizedPrimitives: + desc.name = "Rasterized Primitives"; + desc.description = "Number of primitives that were rendered."; + desc.resultByteWidth = 8; + desc.resultCompType = eCompType_UInt; + desc.units = eUnits_Absolute; + break; + case eCounter_SamplesWritten: + desc.name = "Samples Written"; + desc.description = "Number of samples that passed depth/stencil test."; + desc.resultByteWidth = 8; + desc.resultCompType = eCompType_UInt; + desc.units = eUnits_Absolute; + break; + case eCounter_VSInvocations: + desc.name = "VS Invocations"; + desc.description = "Number of times a vertex shader was invoked."; + desc.resultByteWidth = 8; + desc.resultCompType = eCompType_UInt; + desc.units = eUnits_Absolute; + break; + case eCounter_GSInvocations: + desc.name = "GS Invocations"; + desc.description = "Number of times a geometry shader was invoked."; + desc.resultByteWidth = 8; + desc.resultCompType = eCompType_UInt; + desc.units = eUnits_Absolute; + break; + case eCounter_TCSInvocations: + desc.name = "TCS Invocations"; + desc.description = "Number of times a tesselation control shader was invoked."; + desc.resultByteWidth = 8; + desc.resultCompType = eCompType_UInt; + desc.units = eUnits_Absolute; + break; + case eCounter_TESInvocations: + desc.name = "TES Invocations"; + desc.description = "Number of times a tesselation evaluation shader was invoked."; + desc.resultByteWidth = 8; + desc.resultCompType = eCompType_UInt; + desc.units = eUnits_Absolute; + break; + case eCounter_PSInvocations: + desc.name = "PS Invocations"; + desc.description = "Number of times a pixel shader was invoked."; + desc.resultByteWidth = 8; + desc.resultCompType = eCompType_UInt; + desc.units = eUnits_Absolute; + break; + case eCounter_CSInvocations: + desc.name = "CS Invocations"; + desc.description = "Number of times a compute shader was invoked."; + desc.resultByteWidth = 8; + desc.resultCompType = eCompType_UInt; + desc.units = eUnits_Absolute; + break; + default: + desc.name = "Unknown"; + desc.description = "Unknown counter ID"; + desc.resultByteWidth = 0; + desc.resultCompType = eCompType_None; + desc.units = eUnits_Absolute; + break; } } -struct GPUTimer +struct GPUQueries { - GLuint obj; + GLuint obj[eCounter_GLMaxCounters]; uint32_t eventID; }; struct CounterContext { uint32_t eventStart; - vector timers; + vector queries; int reuseIdx; }; -void GLReplay::FillTimers(CounterContext &ctx, const DrawcallTreeNode &drawnode) +GLenum glCounters[] = { + eGL_NONE, // Undefined!! + eGL_TIME_ELAPSED, // eCounter_EventGPUDuration + eGL_VERTICES_SUBMITTED_ARB, // eCounter_InputVerticesRead + eGL_PRIMITIVES_SUBMITTED_ARB, // eCounter_IAPrimitives + eGL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB, // eCounter_GSPrimitives + eGL_CLIPPING_INPUT_PRIMITIVES_ARB, // eCounter_RasterizerInvocations + eGL_CLIPPING_OUTPUT_PRIMITIVES_ARB, // eCounter_RasterizedPrimitives + eGL_SAMPLES_PASSED, // eCounter_SamplesWritten + eGL_VERTEX_SHADER_INVOCATIONS_ARB, // eCounter_VSInvocations + eGL_TESS_CONTROL_SHADER_PATCHES_ARB, // eCounter_TCSInvocations + eGL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB, // eCounter_TESInvocations + eGL_GEOMETRY_SHADER_INVOCATIONS, // eCounter_GSInvocations + eGL_FRAGMENT_SHADER_INVOCATIONS_ARB, // eCounter_PSInvocations + eGL_COMPUTE_SHADER_INVOCATIONS_ARB // eCounter_CSInvocations +}; + +void GLReplay::FillTimers(CounterContext &ctx, const DrawcallTreeNode &drawnode, + const vector &counters) { if(drawnode.children.empty()) return; @@ -95,42 +210,55 @@ void GLReplay::FillTimers(CounterContext &ctx, const DrawcallTreeNode &drawnode) for(size_t i = 0; i < drawnode.children.size(); i++) { const FetchDrawcall &d = drawnode.children[i].draw; - FillTimers(ctx, drawnode.children[i]); + FillTimers(ctx, drawnode.children[i], counters); if(d.events.count == 0) continue; - GPUTimer *timer = NULL; + GPUQueries *queries = NULL; { if(ctx.reuseIdx == -1) { - ctx.timers.push_back(GPUTimer()); + ctx.queries.push_back(GPUQueries()); - timer = &ctx.timers.back(); - timer->eventID = d.eventID; - timer->obj = 0; + queries = &ctx.queries.back(); + queries->eventID = d.eventID; + for(uint32_t q = 0; q < eCounter_GLMaxCounters; q++) + queries->obj[q] = 0; - m_pDriver->glGenQueries(1, &timer->obj); + for(uint32_t c = 0; c < counters.size(); c++) + { + m_pDriver->glGenQueries(1, &queries->obj[counters[c]]); + if(m_pDriver->glGetError()) + queries->obj[counters[c]] = 0; + } } else { - timer = &ctx.timers[ctx.reuseIdx++]; + queries = &ctx.queries[ctx.reuseIdx++]; } } m_pDriver->ReplayLog(ctx.eventStart, d.eventID, eReplay_WithoutDraw); - if(timer->obj) - { - m_pDriver->glBeginQuery(eGL_TIME_ELAPSED, timer->obj); - m_pDriver->ReplayLog(ctx.eventStart, d.eventID, eReplay_OnlyDraw); - m_pDriver->glEndQuery(eGL_TIME_ELAPSED); - } - else - { - m_pDriver->ReplayLog(ctx.eventStart, d.eventID, eReplay_OnlyDraw); - } + // Reverse order so that Timer counter is queried the last. + for(int32_t q = (eCounter_GLMaxCounters - 1); q >= 0; q--) + if(queries->obj[q]) + { + m_pDriver->glBeginQuery(glCounters[q], queries->obj[q]); + if(m_pDriver->glGetError()) + { + m_pDriver->glDeleteQueries(1, &queries->obj[q]); + queries->obj[q] = 0; + } + } + + m_pDriver->ReplayLog(ctx.eventStart, d.eventID, eReplay_OnlyDraw); + + for(uint32_t q = 0; q < eCounter_GLMaxCounters; q++) + if(queries->obj[q]) + m_pDriver->glEndQuery(glCounters[q]); ctx.eventStart = d.eventID + 1; } @@ -148,19 +276,15 @@ vector GLReplay::FetchCounters(const vector &counters) MakeCurrentReplayContext(&m_ReplayCtx); - uint32_t counterID = counters[0]; - RDCASSERT(counters.size() == 1); - RDCASSERT(counterID == eCounter_EventGPUDuration); - - SCOPED_TIMER("Fetch Counters for %u", counterID); - CounterContext ctx; for(int loop = 0; loop < 1; loop++) { ctx.eventStart = 0; ctx.reuseIdx = loop == 0 ? -1 : 0; - FillTimers(ctx, m_pDriver->GetRootDraw()); + m_pDriver->SetFetchCounters(true); + FillTimers(ctx, m_pDriver->GetRootDraw(), counters); + m_pDriver->SetFetchCounters(false); double nanosToSecs = 1.0 / 1000000000.0; @@ -168,28 +292,42 @@ vector GLReplay::FetchCounters(const vector &counters) m_pDriver->glGetIntegerv(eGL_QUERY_BUFFER_BINDING, (GLint *)&prevbind); m_pDriver->glBindBuffer(eGL_QUERY_BUFFER, 0); - for(size_t i = 0; i < ctx.timers.size(); i++) + for(size_t i = 0; i < ctx.queries.size(); i++) { - if(ctx.timers[i].obj) + for(uint32_t c = 0; c < counters.size(); c++) { - GLuint elapsed = 0; - m_pDriver->glGetQueryObjectuiv(ctx.timers[i].obj, eGL_QUERY_RESULT, &elapsed); + if(ctx.queries[i].obj[counters[c]]) + { + GLuint64 data = 0; + m_pDriver->glGetQueryObjectui64v(ctx.queries[i].obj[counters[c]], eGL_QUERY_RESULT, &data); - double duration = double(elapsed) * nanosToSecs; + double duration = double(data) * nanosToSecs; - ret.push_back(CounterResult(ctx.timers[i].eventID, counterID, duration)); - } - else - { - ret.push_back(CounterResult(ctx.timers[i].eventID, counterID, 0.0)); + if(m_pDriver->glGetError()) + { + data = (uint64_t)-1; + duration = -1; + } + + if(counters[c] == eCounter_EventGPUDuration) + { + ret.push_back(CounterResult(ctx.queries[i].eventID, eCounter_EventGPUDuration, duration)); + } + else + ret.push_back(CounterResult(ctx.queries[i].eventID, counters[c], data)); + } + else + ret.push_back(CounterResult(ctx.queries[i].eventID, counters[c], (uint64_t)-1)); } } m_pDriver->glBindBuffer(eGL_QUERY_BUFFER, prevbind); } - for(size_t i = 0; i < ctx.timers.size(); i++) - m_pDriver->glDeleteQueries(1, &ctx.timers[i].obj); + for(size_t i = 0; i < ctx.queries.size(); i++) + for(uint32_t c = 0; c < counters.size(); c++) + if(ctx.queries[i].obj[counters[c]]) + m_pDriver->glDeleteQueries(1, &ctx.queries[i].obj[counters[c]]); return ret; } diff --git a/renderdoc/driver/gl/gl_driver.cpp b/renderdoc/driver/gl/gl_driver.cpp index 171136b01..911483c9c 100644 --- a/renderdoc/driver/gl/gl_driver.cpp +++ b/renderdoc/driver/gl/gl_driver.cpp @@ -739,6 +739,8 @@ WrappedOpenGL::WrappedOpenGL(const char *logfile, const GLHookSet &funcs) : m_Re m_FirstEventID = 0; m_LastEventID = ~0U; + m_FetchCounters = false; + RDCEraseEl(m_ActiveQueries); m_ActiveConditional = false; m_ActiveFeedback = false; diff --git a/renderdoc/driver/gl/gl_driver.h b/renderdoc/driver/gl/gl_driver.h index f2250b2cd..58abb47a2 100644 --- a/renderdoc/driver/gl/gl_driver.h +++ b/renderdoc/driver/gl/gl_driver.h @@ -156,7 +156,16 @@ private: vector m_LastContexts; - bool m_ActiveQueries[8][8]; // first index type, second index (for some, always 0) +public: + enum + { + MAX_QUERIES = 17, + MAX_QUERY_INDICES = 8 + }; + +private: + bool m_ActiveQueries[MAX_QUERIES] + [MAX_QUERY_INDICES]; // first index type, second index (for some, always 0) bool m_ActiveConditional; bool m_ActiveFeedback; @@ -221,6 +230,8 @@ private: map > m_ResourceUses; + bool m_FetchCounters; + // buffer used vector m_ScratchBuf; @@ -484,6 +495,7 @@ public: GLReplay *GetReplay() { return &m_Replay; } void *GetCtx(); + void SetFetchCounters(bool in) { m_FetchCounters = in; }; const GLHookSet &GetHookset() { return m_Real; } void SetDebugMsgContext(const char *context) { m_DebugMsgContext = context; } void AddDebugMessage(DebugMessage msg) diff --git a/renderdoc/driver/gl/gl_replay.h b/renderdoc/driver/gl/gl_replay.h index 27236deb1..b74dad661 100644 --- a/renderdoc/driver/gl/gl_replay.h +++ b/renderdoc/driver/gl/gl_replay.h @@ -382,7 +382,8 @@ private: // called before the context is destroyed, to shutdown any counters void PreContextShutdownCounters(); - void FillTimers(CounterContext &ctx, const DrawcallTreeNode &drawnode); + void FillTimers(CounterContext &ctx, const DrawcallTreeNode &drawnode, + const vector &counters); GLuint CreateShaderProgram(const vector &vs, const vector &fs, const vector &gs); diff --git a/renderdoc/driver/gl/wrappers/gl_query_funcs.cpp b/renderdoc/driver/gl/wrappers/gl_query_funcs.cpp index f3ce81994..43fb92ffa 100644 --- a/renderdoc/driver/gl/wrappers/gl_query_funcs.cpp +++ b/renderdoc/driver/gl/wrappers/gl_query_funcs.cpp @@ -262,8 +262,12 @@ bool WrappedOpenGL::Serialise_glBeginQuery(GLenum target, GLuint qid) if(m_State < WRITING) { - m_Real.glBeginQuery(Target, GetResourceManager()->GetLiveResource(id).name); - m_ActiveQueries[QueryIdx(Target)][0] = true; + // Queries in the log interfere with the queries from FetchCounters. + if(!m_FetchCounters) + { + m_Real.glBeginQuery(Target, GetResourceManager()->GetLiveResource(id).name); + m_ActiveQueries[QueryIdx(Target)][0] = true; + } } return true; @@ -272,6 +276,8 @@ bool WrappedOpenGL::Serialise_glBeginQuery(GLenum target, GLuint qid) void WrappedOpenGL::glBeginQuery(GLenum target, GLuint id) { m_Real.glBeginQuery(target, id); + if(m_ActiveQueries[QueryIdx(target)][0]) + RDCLOG("Query already active %s", ToStr::Get(target).c_str()); m_ActiveQueries[QueryIdx(target)][0] = true; if(m_State == WRITING_CAPFRAME) @@ -320,8 +326,12 @@ bool WrappedOpenGL::Serialise_glEndQuery(GLenum target) if(m_State < WRITING) { - m_ActiveQueries[QueryIdx(Target)][0] = false; - m_Real.glEndQuery(Target); + // Queries in the log interfere with the queries from FetchCounters. + if(!m_FetchCounters) + { + m_ActiveQueries[QueryIdx(Target)][0] = false; + m_Real.glEndQuery(Target); + } } return true;