Implemented OpenGL counters using pipeline stats and occlusion queries

Implemented OpenGL counters based on ARB_pipeline_statistics extension
and occlusion queries.

Because OpenGL doesn't seem to allow concurrent queries of the same
type occlusion queries in the log are ignored when executing
FetchCounters.

Renamed GPUTimer to GPUQueries because D3D11 GPUTimer structure seems
to be visible and creates conflicts on VS.
This commit is contained in:
Victor Moya
2016-12-29 22:36:01 +00:00
committed by Baldur Karlsson
parent 460f785a5b
commit cba4e8aed2
7 changed files with 268 additions and 77 deletions
+2
View File
@@ -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
+41 -15
View File
@@ -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];
+194 -56
View File
@@ -47,6 +47,18 @@ vector<uint32_t> GLReplay::EnumerateCounters()
vector<uint32_t> 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<GPUTimer> timers;
vector<GPUQueries> 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<uint32_t> &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<CounterResult> GLReplay::FetchCounters(const vector<uint32_t> &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<CounterResult> GLReplay::FetchCounters(const vector<uint32_t> &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;
}
+2
View File
@@ -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;
+13 -1
View File
@@ -156,7 +156,16 @@ private:
vector<GLWindowingData> 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<ResourceId, vector<EventUsage> > m_ResourceUses;
bool m_FetchCounters;
// buffer used
vector<byte> 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)
+2 -1
View File
@@ -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<uint32_t> &counters);
GLuint CreateShaderProgram(const vector<string> &vs, const vector<string> &fs,
const vector<string> &gs);
@@ -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;