Commit ecbac31c by Jamie Madill Committed by Commit Bot

Perf Tests: Add offscreen mode.

This lets the trace perf tests run configurable number of frames within a single swap. The offscreen config is similar to how gfxbench works. It renders to a user FBO (by overriding calls to BindFramebuffer) and then composits multiple frames into the real backbuffer. This allows us to get a perf measurement with less overhead from composition and display. Adds emulation for some APIs that operate on Framebuffers like BindFramebuffer, Invalidate, DrawBuffers and ReadBuffer. Bug: angleproject:4845 Change-Id: I1044c1d52c82f1c215a68a6c46d74c52ed0f3d2a Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2300207 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarCody Northrop <cnorthrop@google.com> Reviewed-by: 's avatarCourtney Goeltzenleuchter <courtneygo@google.com>
parent 50348a8a
......@@ -22,6 +22,7 @@ bool gVerboseLogging = false;
double gTestTimeSeconds = 1.0;
int gTestTrials = 3;
bool gNoFinish = false;
bool gEnableOffscreen = false;
// Default to three warmup loops. There's no science to this. More than two loops was experimentally
// helpful on a Windows NVIDIA setup when testing with Vulkan and native trace tests.
......@@ -116,6 +117,10 @@ void ANGLEProcessPerfTestArgs(int *argc, char **argv)
{
gNoFinish = true;
}
else if (strcmp("--enable-offscreen", argv[argIndex]) == 0)
{
gEnableOffscreen = true;
}
else
{
argv[argcOutCount++] = argv[argIndex];
......
......@@ -24,6 +24,7 @@ extern int gWarmupLoops;
extern double gTestTimeSeconds;
extern int gTestTrials;
extern bool gNoFinish;
extern bool gEnableOffscreen;
inline bool OneFrame()
{
......
......@@ -35,6 +35,7 @@ Several command-line arguments control how the tests run:
* `--test-time`: Run each test trial in a fixed time. Defaults to 1 second.
* `--trials`: Number of times to repeat testing. Defaults to 3.
* `--no-finish`: Don't call glFinish after each test trial.
* `--enable-offscreen`: Offscreen tests disabled by default to reduce test time.
For example, for an endless run with no warmup, run:
......
......@@ -11,6 +11,7 @@
#include "common/PackedEnums.h"
#include "common/system_utils.h"
#include "tests/perf_tests/ANGLEPerfTest.h"
#include "tests/perf_tests/ANGLEPerfTestArgs.h"
#include "tests/perf_tests/DrawCallPerfParams.h"
#include "util/egl_loader_autogen.h"
#include "util/frame_capture_test_utils.h"
......@@ -68,6 +69,23 @@ class TracePerfTest : public ANGLERenderTest, public ::testing::WithParamInterfa
void drawBenchmark() override;
void onReplayFramebufferChange(GLenum target, GLuint framebuffer);
void onReplayInvalidateFramebuffer(GLenum target,
GLsizei numAttachments,
const GLenum *attachments);
void onReplayInvalidateSubFramebuffer(GLenum target,
GLsizei numAttachments,
const GLenum *attachments,
GLint x,
GLint y,
GLsizei width,
GLsizei height);
void onReplayDrawBuffers(GLsizei n, const GLenum *bufs);
void onReplayReadBuffer(GLenum src);
void onReplayDiscardFramebufferEXT(GLenum target,
GLsizei numAttachments,
const GLenum *attachments);
bool isDefaultFramebuffer(GLenum target) const;
uint32_t mStartFrame;
uint32_t mEndFrame;
......@@ -97,7 +115,14 @@ class TracePerfTest : public ANGLERenderTest, public ::testing::WithParamInterfa
std::vector<TimeSample> mTimeline;
std::string mStartingDirectory;
bool mUseTimestampQueries = false;
bool mUseTimestampQueries = false;
GLuint mOffscreenFramebuffer = 0;
GLuint mOffscreenTexture = 0;
GLuint mOffscreenDepthStencil = 0;
int mWindowWidth = 0;
int mWindowHeight = 0;
GLuint mDrawFramebufferBinding = 0;
GLuint mReadFramebufferBinding = 0;
};
class TracePerfTest;
......@@ -106,16 +131,71 @@ TracePerfTest *gCurrentTracePerfTest = nullptr;
// Don't forget to include KHRONOS_APIENTRY in override methods. Neccessary on Win/x86.
void KHRONOS_APIENTRY BindFramebufferProc(GLenum target, GLuint framebuffer)
{
glBindFramebuffer(target, framebuffer);
gCurrentTracePerfTest->onReplayFramebufferChange(target, framebuffer);
}
void KHRONOS_APIENTRY InvalidateFramebufferProc(GLenum target,
GLsizei numAttachments,
const GLenum *attachments)
{
gCurrentTracePerfTest->onReplayInvalidateFramebuffer(target, numAttachments, attachments);
}
void KHRONOS_APIENTRY InvalidateSubFramebufferProc(GLenum target,
GLsizei numAttachments,
const GLenum *attachments,
GLint x,
GLint y,
GLsizei width,
GLsizei height)
{
gCurrentTracePerfTest->onReplayInvalidateSubFramebuffer(target, numAttachments, attachments, x,
y, width, height);
}
void KHRONOS_APIENTRY DrawBuffersProc(GLsizei n, const GLenum *bufs)
{
gCurrentTracePerfTest->onReplayDrawBuffers(n, bufs);
}
void KHRONOS_APIENTRY ReadBufferProc(GLenum src)
{
gCurrentTracePerfTest->onReplayReadBuffer(src);
}
void KHRONOS_APIENTRY DiscardFramebufferEXTProc(GLenum target,
GLsizei numAttachments,
const GLenum *attachments)
{
gCurrentTracePerfTest->onReplayDiscardFramebufferEXT(target, numAttachments, attachments);
}
angle::GenericProc KHRONOS_APIENTRY TraceLoadProc(const char *procName)
{
if (strcmp(procName, "glBindFramebuffer") == 0)
{
return reinterpret_cast<angle::GenericProc>(BindFramebufferProc);
}
if (strcmp(procName, "glInvalidateFramebuffer") == 0)
{
return reinterpret_cast<angle::GenericProc>(InvalidateFramebufferProc);
}
if (strcmp(procName, "glInvalidateSubFramebuffer") == 0)
{
return reinterpret_cast<angle::GenericProc>(InvalidateSubFramebufferProc);
}
if (strcmp(procName, "glDrawBuffers") == 0)
{
return reinterpret_cast<angle::GenericProc>(DrawBuffersProc);
}
if (strcmp(procName, "glReadBuffer") == 0)
{
return reinterpret_cast<angle::GenericProc>(ReadBufferProc);
}
if (strcmp(procName, "glDiscardFramebufferEXT") == 0)
{
return reinterpret_cast<angle::GenericProc>(DiscardFramebufferEXTProc);
}
return gCurrentTracePerfTest->getGLWindow()->getProcAddress(procName);
}
......@@ -138,6 +218,12 @@ TracePerfTest::TracePerfTest()
mSkipTest = true;
}
if (param.surfaceType == SurfaceType::Offscreen && !gEnableOffscreen)
{
printf("Test skipped because offscreen tests are disabled.\n");
mSkipTest = true;
}
if (param.testID == RestrictedTraceID::cod_mobile)
{
// TODO: http://anglebug.com/4967 Vulkan: GL_EXT_color_buffer_float not supported on Pixel 2
......@@ -185,18 +271,54 @@ void TracePerfTest::initializeBenchmark()
std::string testDataDir = testDataDirStr.str();
SetBinaryDataDir(params.testID, testDataDir.c_str());
mWindowWidth = mTestParams.windowWidth;
mWindowHeight = mTestParams.windowHeight;
if (IsAndroid())
{
// On Android, set the orientation used by the app, based on width/height
getWindow()->setOrientation(mTestParams.windowWidth, mTestParams.windowHeight);
}
// If we're rendering offscreen we set up a default backbuffer.
if (params.surfaceType == SurfaceType::Offscreen)
{
if (!IsAndroid())
{
mWindowWidth *= 4;
mWindowHeight *= 4;
}
glGenFramebuffers(1, &mOffscreenFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, mOffscreenFramebuffer);
// Hard-code RGBA8/D24S8. This should be specified in the trace info.
glGenTextures(1, &mOffscreenTexture);
glBindTexture(GL_TEXTURE_2D, mOffscreenTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, mWindowWidth, mWindowHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr);
glGenRenderbuffers(1, &mOffscreenDepthStencil);
glBindRenderbuffer(GL_RENDERBUFFER, mOffscreenDepthStencil);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, mWindowWidth, mWindowHeight);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
mOffscreenTexture, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
mOffscreenDepthStencil);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
mOffscreenDepthStencil);
glBindTexture(GL_TEXTURE_2D, 0);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
}
// Potentially slow. Can load a lot of resources.
SetupReplay(params.testID);
glFinish();
ASSERT_TRUE(mEndFrame > mStartFrame);
getWindow()->ignoreSizeEvents();
getWindow()->setVisible(true);
}
......@@ -204,6 +326,24 @@ void TracePerfTest::initializeBenchmark()
void TracePerfTest::destroyBenchmark()
{
if (mOffscreenTexture != 0)
{
glDeleteTextures(1, &mOffscreenTexture);
mOffscreenTexture = 0;
}
if (mOffscreenDepthStencil != 0)
{
glDeleteRenderbuffers(1, &mOffscreenDepthStencil);
mOffscreenDepthStencil = 0;
}
if (mOffscreenFramebuffer != 0)
{
glDeleteFramebuffers(1, &mOffscreenFramebuffer);
mOffscreenFramebuffer = 0;
}
// In order for the next test to load, restore the working directory
angle::SetCWD(mStartingDirectory.c_str());
}
......@@ -229,6 +369,24 @@ void TracePerfTest::sampleTime()
void TracePerfTest::drawBenchmark()
{
constexpr uint32_t kFramesPerX = 6;
constexpr uint32_t kFramesPerY = 4;
constexpr uint32_t kFramesPerXY = kFramesPerY * kFramesPerX;
const uint32_t kOffscreenOffsetX =
static_cast<uint32_t>(static_cast<double>(mTestParams.windowWidth) / 3.0f);
const uint32_t kOffscreenOffsetY =
static_cast<uint32_t>(static_cast<double>(mTestParams.windowHeight) / 3.0f);
const uint32_t kOffscreenWidth = kOffscreenOffsetX;
const uint32_t kOffscreenHeight = kOffscreenOffsetY;
const uint32_t kOffscreenFrameWidth = static_cast<uint32_t>(
static_cast<double>(kOffscreenWidth / static_cast<double>(kFramesPerX)));
const uint32_t kOffscreenFrameHeight = static_cast<uint32_t>(
static_cast<double>(kOffscreenHeight / static_cast<double>(kFramesPerY)));
const TracePerfParams &params = GetParam();
// Add a time sample from GL and the host.
sampleTime();
......@@ -240,8 +398,59 @@ void TracePerfTest::drawBenchmark()
sprintf(frameName, "Frame %u", frame);
beginInternalTraceEvent(frameName);
ReplayFrame(GetParam().testID, frame);
getGLWindow()->swap();
ReplayFrame(params.testID, frame);
if (params.surfaceType == SurfaceType::Window)
{
getGLWindow()->swap();
}
else
{
GLint currentDrawFBO, currentReadFBO;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &currentDrawFBO);
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &currentReadFBO);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBindFramebuffer(GL_READ_FRAMEBUFFER, mOffscreenFramebuffer);
uint32_t frames = getNumStepsPerformed() + (frame - mStartFrame);
uint32_t frameX = (frames % kFramesPerXY) % kFramesPerX;
uint32_t frameY = (frames % kFramesPerXY) / kFramesPerX;
uint32_t windowX = kOffscreenOffsetX + frameX * kOffscreenFrameWidth;
uint32_t windowY = kOffscreenOffsetY + frameY * kOffscreenFrameHeight;
if (angle::gVerboseLogging)
{
printf("Frame %d: x %d y %d (screen x %d, screen y %d)\n", frames, frameX, frameY,
windowX, windowY);
}
GLboolean scissorTest = GL_FALSE;
glGetBooleanv(GL_SCISSOR_TEST, &scissorTest);
if (scissorTest)
{
glDisable(GL_SCISSOR_TEST);
}
glBlitFramebuffer(0, 0, mWindowWidth, mWindowHeight, windowX, windowY,
windowX + kOffscreenFrameWidth, windowY + kOffscreenFrameHeight,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
if (frameX == kFramesPerX - 1 && frameY == kFramesPerY - 1)
{
getGLWindow()->swap();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClear(GL_COLOR_BUFFER_BIT);
}
if (scissorTest)
{
glEnable(GL_SCISSOR_TEST);
}
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, currentDrawFBO);
glBindFramebuffer(GL_READ_FRAMEBUFFER, currentReadFBO);
}
endInternalTraceEvent(frameName);
......@@ -252,7 +461,7 @@ void TracePerfTest::drawBenchmark()
}
}
ResetReplay(GetParam().testID);
ResetReplay(params.testID);
// Process any running queries once per iteration.
for (size_t queryIndex = 0; queryIndex < mRunningQueries.size();)
......@@ -335,8 +544,32 @@ double TracePerfTest::getHostTimeFromGLTime(GLint64 glTime)
// Triggered when the replay calls glBindFramebuffer.
void TracePerfTest::onReplayFramebufferChange(GLenum target, GLuint framebuffer)
{
if (target != GL_FRAMEBUFFER && target != GL_DRAW_FRAMEBUFFER)
return;
if (framebuffer == 0 && GetParam().surfaceType == SurfaceType::Offscreen)
{
glBindFramebuffer(target, mOffscreenFramebuffer);
}
else
{
glBindFramebuffer(target, framebuffer);
}
switch (target)
{
case GL_FRAMEBUFFER:
mDrawFramebufferBinding = framebuffer;
mReadFramebufferBinding = framebuffer;
break;
case GL_DRAW_FRAMEBUFFER:
mDrawFramebufferBinding = framebuffer;
break;
case GL_READ_FRAMEBUFFER:
mReadFramebufferBinding = framebuffer;
return;
default:
UNREACHABLE();
break;
}
if (!mUseTimestampQueries)
return;
......@@ -358,6 +591,136 @@ void TracePerfTest::onReplayFramebufferChange(GLenum target, GLuint framebuffer)
mCurrentQuery.framebuffer = framebuffer;
}
bool TracePerfTest::isDefaultFramebuffer(GLenum target) const
{
switch (target)
{
case GL_FRAMEBUFFER:
case GL_DRAW_FRAMEBUFFER:
return (mDrawFramebufferBinding == 0);
case GL_READ_FRAMEBUFFER:
return (mReadFramebufferBinding == 0);
default:
UNREACHABLE();
return false;
}
}
GLenum ConvertDefaultFramebufferEnum(GLenum value)
{
switch (value)
{
case GL_NONE:
return GL_NONE;
case GL_BACK:
case GL_COLOR:
return GL_COLOR_ATTACHMENT0;
case GL_DEPTH:
return GL_DEPTH_ATTACHMENT;
case GL_STENCIL:
return GL_STENCIL_ATTACHMENT;
case GL_DEPTH_STENCIL:
return GL_DEPTH_STENCIL_ATTACHMENT;
default:
UNREACHABLE();
return GL_NONE;
}
}
std::vector<GLenum> ConvertDefaultFramebufferEnums(GLsizei numAttachments,
const GLenum *attachments)
{
std::vector<GLenum> translatedAttachments;
for (GLsizei attachmentIndex = 0; attachmentIndex < numAttachments; ++attachmentIndex)
{
GLenum converted = ConvertDefaultFramebufferEnum(attachments[attachmentIndex]);
translatedAttachments.push_back(converted);
}
return translatedAttachments;
}
// Needs special handling to treat the 0 framebuffer in offscreen mode.
void TracePerfTest::onReplayInvalidateFramebuffer(GLenum target,
GLsizei numAttachments,
const GLenum *attachments)
{
if (GetParam().surfaceType == SurfaceType::Window || !isDefaultFramebuffer(target))
{
glInvalidateFramebuffer(target, numAttachments, attachments);
}
else
{
std::vector<GLenum> translatedAttachments =
ConvertDefaultFramebufferEnums(numAttachments, attachments);
glInvalidateFramebuffer(target, numAttachments, translatedAttachments.data());
}
}
void TracePerfTest::onReplayInvalidateSubFramebuffer(GLenum target,
GLsizei numAttachments,
const GLenum *attachments,
GLint x,
GLint y,
GLsizei width,
GLsizei height)
{
if (GetParam().surfaceType == SurfaceType::Window || !isDefaultFramebuffer(target))
{
glInvalidateSubFramebuffer(target, numAttachments, attachments, x, y, width, height);
}
else
{
std::vector<GLenum> translatedAttachments =
ConvertDefaultFramebufferEnums(numAttachments, attachments);
glInvalidateSubFramebuffer(target, numAttachments, translatedAttachments.data(), x, y,
width, height);
}
}
void TracePerfTest::onReplayDrawBuffers(GLsizei n, const GLenum *bufs)
{
if (GetParam().surfaceType == SurfaceType::Window || !isDefaultFramebuffer(GL_DRAW_FRAMEBUFFER))
{
glDrawBuffers(n, bufs);
}
else
{
std::vector<GLenum> translatedBufs = ConvertDefaultFramebufferEnums(n, bufs);
glDrawBuffers(n, translatedBufs.data());
}
}
void TracePerfTest::onReplayReadBuffer(GLenum src)
{
if (GetParam().surfaceType == SurfaceType::Window || !isDefaultFramebuffer(GL_READ_FRAMEBUFFER))
{
glReadBuffer(src);
}
else
{
GLenum translated = ConvertDefaultFramebufferEnum(src);
glReadBuffer(translated);
}
}
void TracePerfTest::onReplayDiscardFramebufferEXT(GLenum target,
GLsizei numAttachments,
const GLenum *attachments)
{
if (GetParam().surfaceType == SurfaceType::Window || !isDefaultFramebuffer(target))
{
glDiscardFramebufferEXT(target, numAttachments, attachments);
}
else
{
std::vector<GLenum> translatedAttachments =
ConvertDefaultFramebufferEnums(numAttachments, attachments);
glDiscardFramebufferEXT(target, numAttachments, translatedAttachments.data());
}
}
void TracePerfTest::saveScreenshot(const std::string &screenshotName)
{
// Render a single frame.
......@@ -427,13 +790,31 @@ bool NoAndroidMockICD(const TracePerfParams &in)
return in.eglParameters.deviceType != EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE || !IsAndroid();
}
TracePerfParams CombineWithSurfaceType(const TracePerfParams &in, SurfaceType surfaceType)
{
TracePerfParams out = in;
out.surfaceType = surfaceType;
if (!IsAndroid() && surfaceType == SurfaceType::Offscreen)
{
out.windowWidth /= 4;
out.windowHeight /= 4;
}
return out;
}
using namespace params;
using P = TracePerfParams;
std::vector<P> gTestsWithID =
CombineWithValues({P()}, AllEnums<RestrictedTraceID>(), CombineTestID);
std::vector<P> gTestsWithSurfaceType =
CombineWithValues(gTestsWithID,
{SurfaceType::Offscreen, SurfaceType::Window},
CombineWithSurfaceType);
std::vector<P> gTestsWithRenderer =
CombineWithFuncs(gTestsWithID, {Vulkan<P>, VulkanMockICD<P>, Native<P>});
CombineWithFuncs(gTestsWithSurfaceType, {Vulkan<P>, VulkanMockICD<P>, Native<P>});
std::vector<P> gTestsWithoutMockICD = FilterWithFunc(gTestsWithRenderer, NoAndroidMockICD);
ANGLE_INSTANTIATE_TEST_ARRAY(TracePerfTest, gTestsWithoutMockICD);
......
......@@ -347,7 +347,8 @@ static void PrintEvent(const Event &event)
}
#endif
OSWindow::OSWindow() : mX(0), mY(0), mWidth(0), mHeight(0) {}
OSWindow::OSWindow() : mX(0), mY(0), mWidth(0), mHeight(0), mValid(false), mIgnoreSizeEvents(false)
{}
OSWindow::~OSWindow() {}
......
......@@ -68,6 +68,8 @@ class ANGLE_UTIL_EXPORT OSWindow
// Whether window has been successfully initialized.
bool valid() const { return mValid; }
void ignoreSizeEvents() { mIgnoreSizeEvents = true; }
protected:
OSWindow();
virtual ~OSWindow();
......@@ -82,6 +84,7 @@ class ANGLE_UTIL_EXPORT OSWindow
std::list<Event> mEvents;
bool mValid;
bool mIgnoreSizeEvents;
};
#endif // UTIL_OSWINDOW_H_
......@@ -277,6 +277,9 @@ LRESULT CALLBACK Win32Window::WndProc(HWND hWnd, UINT message, WPARAM wParam, LP
case WM_SIZE:
{
if (window->mIgnoreSizeEvents)
break;
RECT winRect;
GetClientRect(hWnd, &winRect);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment