Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 9acaf66

Browse files
null77Commit Bot
authored and
Commit Bot
committed
Add timer query support to TracePerfTests.
This can allow us to get some profiling measurements on ANGLE vs a native driver. We correlate host times with GL times using the blocking timestamp query. We also time RenderPasses/FBO switches using the trace instrumentation we added in an earlier patch combined with timer queries. Not currently instrumented for the current tests. We'll need to re-run capture to get the callbacks working correctly. Bug: angleproject:4433 Change-Id: I8f61774a3a090ac9460a378d34715a72954d1331 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2080597 Reviewed-by: Cody Northrop <[email protected]> Commit-Queue: Jamie Madill <[email protected]>
1 parent 55228e5 commit 9acaf66

File tree

6 files changed

+233
-11
lines changed

6 files changed

+233
-11
lines changed

src/tests/perf_tests/ANGLEPerfTest.cpp

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,7 @@ void UpdateTraceEventDuration(angle::PlatformMethods *platform,
116116

117117
double MonotonicallyIncreasingTime(angle::PlatformMethods *platform)
118118
{
119-
// Move the time origin to the first call to this function, to avoid generating unnecessarily
120-
// large timestamps.
121-
static double origin = angle::GetCurrentTime();
122-
return angle::GetCurrentTime() - origin;
119+
return GetHostTimeSeconds();
123120
}
124121

125122
void DumpTraceEventsToJSONFile(const std::vector<TraceEvent> &traceEvents,
@@ -174,6 +171,16 @@ ANGLE_MAYBE_UNUSED void KHRONOS_APIENTRY DebugMessageCallback(GLenum source,
174171
}
175172
} // anonymous namespace
176173

174+
TraceEvent::TraceEvent(char phaseIn,
175+
const char *categoryNameIn,
176+
const char *nameIn,
177+
double timestampIn)
178+
: phase(phaseIn), categoryName(categoryNameIn), name{}, timestamp(timestampIn)
179+
{
180+
ASSERT(strlen(nameIn) < kMaxNameLen);
181+
strcpy(name, nameIn);
182+
}
183+
177184
ANGLEPerfTest::ANGLEPerfTest(const std::string &name,
178185
const std::string &backend,
179186
const std::string &story,
@@ -394,9 +401,9 @@ ANGLERenderTest::ANGLERenderTest(const std::string &name, const RenderTestParams
394401
testParams.story(),
395402
OneFrame() ? 1 : testParams.iterationsPerStep),
396403
mTestParams(testParams),
404+
mIsTimestampQueryAvailable(false),
397405
mGLWindow(nullptr),
398-
mOSWindow(nullptr),
399-
mIsTimestampQueryAvailable(false)
406+
mOSWindow(nullptr)
400407
{
401408
// Force fast tests to make sure our slowest bots don't time out.
402409
if (OneFrame())
@@ -592,6 +599,24 @@ void ANGLERenderTest::endInternalTraceEvent(const char *name)
592599
}
593600
}
594601

602+
void ANGLERenderTest::beginGLTraceEvent(const char *name, double hostTimeSec)
603+
{
604+
if (gEnableTrace)
605+
{
606+
mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_BEGIN, gTraceCategories[1].name, name,
607+
hostTimeSec);
608+
}
609+
}
610+
611+
void ANGLERenderTest::endGLTraceEvent(const char *name, double hostTimeSec)
612+
{
613+
if (gEnableTrace)
614+
{
615+
mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_END, gTraceCategories[1].name, name,
616+
hostTimeSec);
617+
}
618+
}
619+
595620
void ANGLERenderTest::step()
596621
{
597622
beginInternalTraceEvent("step");
@@ -714,3 +739,14 @@ std::vector<TraceEvent> &ANGLERenderTest::getTraceEventBuffer()
714739
{
715740
return mTraceEventBuffer;
716741
}
742+
743+
namespace angle
744+
{
745+
double GetHostTimeSeconds()
746+
{
747+
// Move the time origin to the first call to this function, to avoid generating unnecessarily
748+
// large timestamps.
749+
static double origin = angle::GetCurrentTime();
750+
return angle::GetCurrentTime() - origin;
751+
}
752+
} // namespace angle

src/tests/perf_tests/ANGLEPerfTest.h

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,13 @@ class Event;
4343
struct TraceEvent final
4444
{
4545
TraceEvent() {}
46+
TraceEvent(char phaseIn, const char *categoryNameIn, const char *nameIn, double timestampIn);
4647

47-
TraceEvent(char phaseIn, const char *categoryNameIn, const char *nameIn, double timestampIn)
48-
: phase(phaseIn), categoryName(categoryNameIn), name(nameIn), timestamp(timestampIn)
49-
{}
48+
static constexpr uint32_t kMaxNameLen = 64;
5049

5150
char phase = 0;
5251
const char *categoryName = nullptr;
53-
const char *name = nullptr;
52+
char name[kMaxNameLen] = {};
5453
double timestamp = 0;
5554
};
5655

@@ -148,6 +147,10 @@ class ANGLERenderTest : public ANGLEPerfTest
148147

149148
void beginInternalTraceEvent(const char *name);
150149
void endInternalTraceEvent(const char *name);
150+
void beginGLTraceEvent(const char *name, double hostTimeSec);
151+
void endGLTraceEvent(const char *name, double hostTimeSec);
152+
153+
bool mIsTimestampQueryAvailable;
151154

152155
private:
153156
void SetUp() override;
@@ -165,7 +168,6 @@ class ANGLERenderTest : public ANGLEPerfTest
165168
angle::PlatformMethods mPlatformMethods;
166169
ConfigParameters mConfigParams;
167170

168-
bool mIsTimestampQueryAvailable;
169171
GLuint mTimestampQuery;
170172

171173
// Trace event record that can be output.
@@ -201,4 +203,10 @@ ParamsT Passthrough(const ParamsT &input)
201203
return input;
202204
}
203205
} // namespace params
206+
207+
namespace angle
208+
{
209+
// Returns the time of the host since the application started in seconds.
210+
double GetHostTimeSeconds();
211+
} // namespace angle
204212
#endif // PERF_TESTS_ANGLE_PERF_TEST_H_

src/tests/perf_tests/DrawCallPerfParams.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ ParamsT GL(const ParamsT &in)
5454
return out;
5555
}
5656

57+
template <typename ParamsT>
58+
ParamsT GL3(const ParamsT &in)
59+
{
60+
ParamsT out = in;
61+
out.eglParameters = angle::egl_platform::OPENGL_OR_GLES(3, 0);
62+
return out;
63+
}
64+
5765
template <typename ParamsT>
5866
ParamsT Vulkan(const ParamsT &in)
5967
{

src/tests/perf_tests/TracePerfTest.cpp

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ using namespace egl_platform;
2828

2929
namespace
3030
{
31+
void FramebufferChangeCallback(void *userData, GLenum target, GLuint framebuffer);
3132

3233
enum class TracePerfTestID
3334
{
@@ -99,9 +100,34 @@ class TracePerfTest : public ANGLERenderTest, public ::testing::WithParamInterfa
99100
void destroyBenchmark() override;
100101
void drawBenchmark() override;
101102

103+
void onFramebufferChange(GLenum target, GLuint framebuffer);
104+
102105
uint32_t mStartFrame;
103106
uint32_t mEndFrame;
104107
std::function<void(uint32_t)> mReplayFunc;
108+
109+
double getHostTimeFromGLTime(GLint64 glTime);
110+
111+
private:
112+
struct QueryInfo
113+
{
114+
GLuint beginTimestampQuery;
115+
GLuint endTimestampQuery;
116+
GLuint framebuffer;
117+
};
118+
119+
struct TimeSample
120+
{
121+
GLint64 glTime;
122+
double hostTime;
123+
};
124+
125+
void sampleTime();
126+
127+
// For tracking RenderPass/FBO change timing.
128+
QueryInfo mCurrentQuery = {};
129+
std::vector<QueryInfo> mRunningQueries;
130+
std::vector<TimeSample> mTimeline;
105131
};
106132

107133
TracePerfTest::TracePerfTest()
@@ -157,19 +183,153 @@ void TracePerfTest::initializeBenchmark()
157183

158184
void TracePerfTest::destroyBenchmark() {}
159185

186+
void TracePerfTest::sampleTime()
187+
{
188+
if (mIsTimestampQueryAvailable)
189+
{
190+
GLint64 glTime;
191+
// glGetInteger64vEXT is exported by newer versions of the timer query extensions.
192+
// Unfortunately only the core EP is exposed by some desktop drivers (e.g. NVIDIA).
193+
if (glGetInteger64vEXT)
194+
{
195+
glGetInteger64vEXT(GL_TIMESTAMP_EXT, &glTime);
196+
}
197+
else
198+
{
199+
glGetInteger64v(GL_TIMESTAMP_EXT, &glTime);
200+
}
201+
mTimeline.push_back({glTime, angle::GetHostTimeSeconds()});
202+
}
203+
}
204+
160205
void TracePerfTest::drawBenchmark()
161206
{
207+
// Add a time sample from GL and the host.
208+
sampleTime();
209+
162210
startGpuTimer();
163211

164212
for (uint32_t frame = mStartFrame; frame < mEndFrame; ++frame)
165213
{
214+
char frameName[32];
215+
sprintf(frameName, "Frame %u", frame);
216+
beginInternalTraceEvent(frameName);
217+
166218
mReplayFunc(frame);
167219
getGLWindow()->swap();
220+
221+
endInternalTraceEvent(frameName);
222+
}
223+
224+
// Process any running queries once per iteration.
225+
for (size_t queryIndex = 0; queryIndex < mRunningQueries.size();)
226+
{
227+
const QueryInfo &query = mRunningQueries[queryIndex];
228+
229+
GLuint endResultAvailable = 0;
230+
glGetQueryObjectuivEXT(query.endTimestampQuery, GL_QUERY_RESULT_AVAILABLE,
231+
&endResultAvailable);
232+
233+
if (endResultAvailable == GL_TRUE)
234+
{
235+
char fboName[32];
236+
sprintf(fboName, "FBO %u", query.framebuffer);
237+
238+
GLint64 beginTimestamp = 0;
239+
glGetQueryObjecti64vEXT(query.beginTimestampQuery, GL_QUERY_RESULT, &beginTimestamp);
240+
glDeleteQueriesEXT(1, &query.beginTimestampQuery);
241+
double beginHostTime = getHostTimeFromGLTime(beginTimestamp);
242+
beginGLTraceEvent(fboName, beginHostTime);
243+
244+
GLint64 endTimestamp = 0;
245+
glGetQueryObjecti64vEXT(query.endTimestampQuery, GL_QUERY_RESULT, &endTimestamp);
246+
glDeleteQueriesEXT(1, &query.endTimestampQuery);
247+
double endHostTime = getHostTimeFromGLTime(endTimestamp);
248+
endGLTraceEvent(fboName, endHostTime);
249+
250+
mRunningQueries.erase(mRunningQueries.begin() + queryIndex);
251+
}
252+
else
253+
{
254+
queryIndex++;
255+
}
168256
}
169257

170258
stopGpuTimer();
171259
}
172260

261+
// Converts a GL timestamp into a host-side CPU time aligned with "GetHostTimeSeconds".
262+
// This check is necessary to line up sampled trace events in a consistent timeline.
263+
// Uses a linear interpolation from a series of samples. We do a blocking call to sample
264+
// both host and GL time once per swap. We then find the two closest GL timestamps and
265+
// interpolate the host times between them to compute our result. If we are past the last
266+
// GL timestamp we sample a new data point pair.
267+
double TracePerfTest::getHostTimeFromGLTime(GLint64 glTime)
268+
{
269+
// Find two samples to do a lerp.
270+
size_t firstSampleIndex = mTimeline.size() - 1;
271+
while (firstSampleIndex > 0)
272+
{
273+
if (mTimeline[firstSampleIndex].glTime < glTime)
274+
{
275+
break;
276+
}
277+
firstSampleIndex--;
278+
}
279+
280+
// Add an extra sample if we're missing an ending sample.
281+
if (firstSampleIndex == mTimeline.size() - 1)
282+
{
283+
sampleTime();
284+
}
285+
286+
const TimeSample &start = mTimeline[firstSampleIndex];
287+
const TimeSample &end = mTimeline[firstSampleIndex + 1];
288+
289+
// Note: we have observed in some odd cases later timestamps producing values that are
290+
// smaller than preceding timestamps. This bears further investigation.
291+
292+
// Compute the scaling factor for the lerp.
293+
double glDelta = static_cast<double>(glTime - start.glTime);
294+
double glRange = static_cast<double>(end.glTime - start.glTime);
295+
double t = glDelta / glRange;
296+
297+
// Lerp(t1, t2, t)
298+
double hostRange = end.hostTime - start.hostTime;
299+
return mTimeline[firstSampleIndex].hostTime + hostRange * t;
300+
}
301+
302+
// Callback from the perf tests.
303+
void TracePerfTest::onFramebufferChange(GLenum target, GLuint framebuffer)
304+
{
305+
if (!mIsTimestampQueryAvailable)
306+
return;
307+
308+
if (target != GL_FRAMEBUFFER && target != GL_DRAW_FRAMEBUFFER)
309+
return;
310+
311+
// We have at most one active timestamp query at a time. This code will end the current query
312+
// and immediately start a new one.
313+
if (mCurrentQuery.beginTimestampQuery != 0)
314+
{
315+
glGenQueriesEXT(1, &mCurrentQuery.endTimestampQuery);
316+
glQueryCounterEXT(mCurrentQuery.endTimestampQuery, GL_TIMESTAMP_EXT);
317+
mRunningQueries.push_back(mCurrentQuery);
318+
mCurrentQuery = {};
319+
}
320+
321+
ASSERT(mCurrentQuery.beginTimestampQuery == 0);
322+
323+
glGenQueriesEXT(1, &mCurrentQuery.beginTimestampQuery);
324+
glQueryCounterEXT(mCurrentQuery.beginTimestampQuery, GL_TIMESTAMP_EXT);
325+
mCurrentQuery.framebuffer = framebuffer;
326+
}
327+
328+
ANGLE_MAYBE_UNUSED void FramebufferChangeCallback(void *userData, GLenum target, GLuint framebuffer)
329+
{
330+
reinterpret_cast<TracePerfTest *>(userData)->onFramebufferChange(target, framebuffer);
331+
}
332+
173333
TEST_P(TracePerfTest, Run)
174334
{
175335
run();

src/tests/test_utils/angle_test_configs.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,15 @@ EGLPlatformParameters OPENGL_OR_GLES()
398398
#endif
399399
}
400400

401+
EGLPlatformParameters OPENGL_OR_GLES(EGLint major, EGLint minor)
402+
{
403+
#if defined(ANGLE_PLATFORM_ANDROID)
404+
return OPENGLES(major, minor);
405+
#else
406+
return OPENGL(major, minor);
407+
#endif
408+
}
409+
401410
EGLPlatformParameters OPENGL_OR_GLES_NULL()
402411
{
403412
#if defined(ANGLE_PLATFORM_ANDROID)

src/tests/test_utils/angle_test_configs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ EGLPlatformParameters OPENGLES(EGLint major, EGLint minor);
109109
EGLPlatformParameters OPENGLES_NULL();
110110

111111
EGLPlatformParameters OPENGL_OR_GLES();
112+
EGLPlatformParameters OPENGL_OR_GLES(EGLint major, EGLint minor);
112113
EGLPlatformParameters OPENGL_OR_GLES_NULL();
113114

114115
EGLPlatformParameters VULKAN();

0 commit comments

Comments
 (0)