Perf tests: Add screenshot capture mode.

This adds a "--screenshot-dir" argument to capture screenshots.
If we're running with screenshot capture then the test will
early exit after the first capture. The screenshots use the same
naming pattern as the test name:

TracePerfTest.Run/vulkan_trex_200 -> angle_vulkan_trex_200.png

Note the screenshot dir is relative to the test binary directory,
not the CWD. Also adds a PNG saving utility function.

Bug: angleproject:4615
Change-Id: I1de8ae6a6e6892586bb0b743e7b9a842f90f98e5
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2184834
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Cody Northrop <cnorthrop@google.com>
This commit is contained in:
Jamie Madill
2020-05-05 12:54:07 -04:00
committed by Commit Bot
parent 21d330b76d
commit d08f1d8dc0
8 changed files with 210 additions and 6 deletions

View File

@@ -238,7 +238,10 @@ template("angle_perftests_common") {
"perf_tests/third_party/perf/perf_test.cc",
"perf_tests/third_party/perf/perf_test.h",
]
deps = [ "$angle_jsoncpp_dir:jsoncpp" ]
deps = [
"$angle_jsoncpp_dir:jsoncpp",
"$angle_root/util:angle_png_utils",
]
public_deps = [ "${invoker.test_utils}" ]
public_configs += [ "${angle_root}:libANGLE_config" ]
}

View File

@@ -16,6 +16,7 @@
#include "common/utilities.h"
#include "third_party/perf/perf_test.h"
#include "third_party/trace_event/trace_event.h"
#include "util/png_utils.h"
#include "util/shader_utils.h"
#include "util/test_utils.h"
@@ -239,6 +240,12 @@ void ANGLEPerfTest::run()
mStepsToRun = gStepsToRunOverride;
}
// Check again for early exit.
if (mSkipTest)
{
return;
}
// Do another warmup run. Seems to consistently improve results.
doRunLoop(kMaximumRunTimeSeconds);
@@ -647,6 +654,10 @@ void ANGLERenderTest::step()
else
{
drawBenchmark();
// Saves a screenshot. The test will also exit early if we're taking screenshots.
saveScreenshotIfEnabled();
// Swap is needed so that the GPU driver will occasionally flush its
// internal command queue to the GPU. This is enabled for null back-end
// devices because some back-ends (e.g. Vulkan) also accumulate internal
@@ -747,6 +758,33 @@ std::vector<TraceEvent> &ANGLERenderTest::getTraceEventBuffer()
return mTraceEventBuffer;
}
void ANGLERenderTest::saveScreenshotIfEnabled()
{
if (gScreenShotDir == nullptr)
{
return;
}
std::stringstream screenshotNameStr;
screenshotNameStr << gScreenShotDir << GetPathSeparator() << "angle" << mBackend << "_"
<< mStory << ".png";
std::string screenshotName = screenshotNameStr.str();
// RGBA 4-byte data.
std::vector<uint8_t> pixelData(mTestParams.windowWidth * mTestParams.windowHeight * 4);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glReadPixels(0, 0, mTestParams.windowWidth, mTestParams.windowHeight, GL_RGBA, GL_UNSIGNED_BYTE,
pixelData.data());
angle::SavePNG(screenshotName.c_str(), "ANGLE Screenshot", mTestParams.windowWidth,
mTestParams.windowHeight, pixelData);
// Early exit.
abortTest();
mSkipTest = true;
}
namespace angle
{
double GetHostTimeSeconds()

View File

@@ -69,6 +69,7 @@ class ANGLEPerfTest : public testing::Test, angle::NonCopyable
virtual void startTest() {}
// Called right before timer is stopped to let the test wait for asynchronous operations.
virtual void finishTest() {}
virtual void flush() {}
protected:
void run();
@@ -153,6 +154,8 @@ class ANGLERenderTest : public ANGLEPerfTest
bool mIsTimestampQueryAvailable;
void saveScreenshotIfEnabled();
private:
void SetUp() override;
void TearDown() override;

View File

@@ -13,10 +13,11 @@
namespace angle
{
bool gCalibration = false;
int gStepsToRunOverride = -1;
bool gEnableTrace = false;
const char *gTraceFile = "ANGLETrace.json";
bool gCalibration = false;
int gStepsToRunOverride = -1;
bool gEnableTrace = false;
const char *gTraceFile = "ANGLETrace.json";
const char *gScreenShotDir = nullptr;
} // namespace angle
using namespace angle;
@@ -37,7 +38,7 @@ void ANGLEProcessPerfTestArgs(int *argc, char **argv)
}
else if (strcmp("--trace-file", argv[argIndex]) == 0 && argIndex < *argc - 1)
{
gTraceFile = argv[argIndex];
gTraceFile = argv[argIndex + 1];
// Skip an additional argument.
argIndex++;
}
@@ -55,6 +56,11 @@ void ANGLEProcessPerfTestArgs(int *argc, char **argv)
// Skip an additional argument.
argIndex++;
}
else if (strcmp("--screenshot-dir", argv[argIndex]) == 0 && argIndex < *argc - 1)
{
gScreenShotDir = argv[argIndex + 1];
argIndex++;
}
else
{
argv[argcOutCount++] = argv[argIndex];

View File

@@ -18,6 +18,7 @@ extern bool gCalibration;
extern int gStepsToRunOverride;
extern bool gEnableTrace;
extern const char *gTraceFile;
extern const char *gScreenShotDir;
inline bool OneFrame()
{

View File

@@ -249,6 +249,19 @@ if (is_win && !angle_is_winuwp) {
}
}
config("angle_png_utils_config") {
include_dirs = [ ".." ]
}
angle_source_set("angle_png_utils") {
deps = [ "$angle_libpng_dir" ]
sources = [
"png_utils.cpp",
"png_utils.h",
]
public_configs = [ ":angle_png_utils_config" ]
}
config("angle_test_util_config") {
include_dirs = [ ".." ]
}

116
util/png_utils.cpp Normal file
View File

@@ -0,0 +1,116 @@
//
// Copyright 2020 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// png_utils: Wrapper around libpng.
//
#include "util/png_utils.h"
#include <array>
#include <cstring>
#include <png.h>
namespace angle
{
namespace
{
class ScopedFILE
{
public:
ScopedFILE(FILE *fp) : mFP(fp) {}
~ScopedFILE() { close(); }
FILE *get() const { return mFP; }
void close()
{
if (mFP)
{
fclose(mFP);
mFP = nullptr;
}
}
private:
FILE *mFP;
};
} // namespace
bool SavePNG(const char *fileName,
const char *title,
uint32_t width,
uint32_t height,
const std::vector<uint8_t> &data)
{
ScopedFILE fp(fopen(fileName, "wb"));
if (!fp.get())
{
fprintf(stderr, "Error opening '%s'.\n", fileName);
return false;
}
png_struct *writeStruct =
png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
if (!writeStruct)
{
fprintf(stderr, "Error on png_create_write_struct.\n");
return false;
}
png_info *infoStruct = png_create_info_struct(writeStruct);
if (!infoStruct)
{
fprintf(stderr, "Error on png_create_info_struct.\n");
return false;
}
if (setjmp(png_jmpbuf(writeStruct)))
{
fp.close();
png_free_data(writeStruct, infoStruct, PNG_FREE_ALL, -1);
png_destroy_write_struct(&writeStruct, &infoStruct);
return false;
}
png_init_io(writeStruct, fp.get());
// Write header (8 bit colour depth)
png_set_IHDR(writeStruct, infoStruct, width, height, 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
// Set title
if (title != nullptr && strlen(title) > 0)
{
std::array<char, 50> mutableKey = {};
strcpy(mutableKey.data(), "Title");
std::array<char, 200> mutableText = {};
strncpy(mutableText.data(), title, 199);
png_text titleText;
titleText.compression = PNG_TEXT_COMPRESSION_NONE;
titleText.key = mutableKey.data();
titleText.text = mutableText.data();
png_set_text(writeStruct, infoStruct, &titleText, 1);
}
png_write_info(writeStruct, infoStruct);
// RGBA 4-byte stride.
const uint32_t rowStride = width * 4;
for (uint32_t row = 0; row < height; ++row)
{
uint32_t rowOffset = row * rowStride;
png_write_row(writeStruct, &data[rowOffset]);
}
png_write_end(writeStruct, infoStruct);
fp.close();
png_free_data(writeStruct, infoStruct, PNG_FREE_ALL, -1);
png_destroy_write_struct(&writeStruct, &infoStruct);
return true;
}
} // namespace angle

24
util/png_utils.h Normal file
View File

@@ -0,0 +1,24 @@
//
// Copyright 2020 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// png_utils: Wrapper around libpng.
//
#ifndef UTIL_PNG_UTILS_H_
#define UTIL_PNG_UTILS_H_
#include <cstdint>
#include <vector>
namespace angle
{
bool SavePNG(const char *fileName,
const char *title,
uint32_t width,
uint32_t height,
const std::vector<uint8_t> &data);
} // namespace angle
#endif // UTIL_PNG_UTILS_H_