[tint] Use embedded benchmark inputs
Rework the benchmark framework to use the auto-generate benchmark
input header instead of loading shaders at runtime.
During initialization, build a map from benchmark name to WGSL shader,
converting SPIR-V inputs as necessary. This map is used when a
benchmark requests the parsed WGSL program for a registered benchmark.
Bug: 42251293
Change-Id: I002d7ad3690eb973c999866e4aa57e8d4aecb7a2
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/200517
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Commit-Queue: James Price <jrprice@google.com>
diff --git a/src/tint/cmd/bench/bench.cc b/src/tint/cmd/bench/bench.cc
index 5114ada..9fbe9ef 100644
--- a/src/tint/cmd/bench/bench.cc
+++ b/src/tint/cmd/bench/bench.cc
@@ -25,145 +25,65 @@
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#include <filesystem>
-#include <iostream>
-#include <utility>
-#include <vector>
-
#include "src/tint/cmd/bench/bench.h"
-#if TINT_BUILD_SPV_READER
+#include <iostream>
+#include <utility>
+
#include "src/tint/lang/spirv/reader/reader.h"
-#endif
-
-#if TINT_BUILD_WGSL_WRITER
-#include "src/tint/lang/wgsl/writer/writer.h"
-#endif
-
-#if TINT_BUILD_WGSL_READER
#include "src/tint/lang/wgsl/reader/reader.h"
-#endif
-
-#include "src/tint/utils/text/string.h"
-#include "src/tint/utils/text/string_stream.h"
+#include "src/tint/lang/wgsl/writer/writer.h"
+#include "src/tint/utils/containers/hashmap.h"
namespace tint::bench {
namespace {
-std::filesystem::path kInputFileDir;
-
-/// Copies the content from the file named `input_file` to `buffer`,
-/// assuming each element in the file is of type `T`. If any error occurs,
-/// writes error messages to the standard error stream and returns false.
-/// Assumes the size of a `T` object is divisible by its required alignment.
-/// @returns true if we successfully read the file.
-template <typename T>
-Result<std::vector<T>> ReadFile(const std::string& input_file) {
- FILE* file = nullptr;
-#if defined(_MSC_VER)
- fopen_s(&file, input_file.c_str(), "rb");
-#else
- file = fopen(input_file.c_str(), "rb");
-#endif
- if (!file) {
- return Failure{"Failed to open " + input_file};
- }
-
- fseek(file, 0, SEEK_END);
- const auto file_size = static_cast<size_t>(ftell(file));
- if (0 != (file_size % sizeof(T))) {
- StringStream err;
- err << "File " << input_file
- << " does not contain an integral number of objects: " << file_size
- << " bytes in the file, require " << sizeof(T) << " bytes per object";
- fclose(file);
- return Failure{err.str()};
- }
- fseek(file, 0, SEEK_SET);
-
- std::vector<T> buffer;
- buffer.resize(file_size / sizeof(T));
-
- size_t bytes_read = fread(buffer.data(), 1, file_size, file);
- fclose(file);
- if (bytes_read != file_size) {
- return Failure{"Failed to read " + input_file};
- }
-
- return buffer;
-}
-
-bool FindBenchmarkInputDir() {
- // Attempt to find the benchmark input files by searching up from the current
- // working directory.
- auto path = std::filesystem::current_path();
- while (std::filesystem::is_directory(path)) {
- auto test = path / "test" / "tint" / "benchmark";
- if (std::filesystem::is_directory(test)) {
- kInputFileDir = test;
- return true;
- }
- auto parent = path.parent_path();
- if (path == parent) {
- break;
- }
- path = parent;
- }
- return false;
-}
+// A map from benchmark input name to the corresponding WGSL shader.
+Hashmap<std::string, std::string, 16> kBenchmarkWgslShaders;
} // namespace
bool Initialize() {
- if (!FindBenchmarkInputDir()) {
- std::cerr << "failed to locate benchmark input files\n";
- return false;
- }
- return true;
-}
-
-Result<Source::File> LoadInputFile(std::string name) {
- auto path = std::filesystem::path(name).is_absolute() ? name : (kInputFileDir / name).string();
- if (tint::HasSuffix(path, ".wgsl")) {
-#if TINT_BUILD_WGSL_READER
- auto data = ReadFile<uint8_t>(path);
- if (data != Success) {
- return data.Failure();
- }
- return tint::Source::File(path, std::string(data->begin(), data->end()));
-#else
- return Failure{"cannot load " + path + " file as TINT_BUILD_WGSL_READER is not enabled"};
-#endif
- }
- if (tint::HasSuffix(path, ".spv")) {
-#if !TINT_BUILD_SPV_READER
- return Failure{"cannot load " + path + " as TINT_BUILD_SPV_READER is not enabled"};
-#elif !TINT_BUILD_WGSL_WRITER
- return Failure{"cannot load " + path + " as TINT_BUILD_WGSL_WRITER is not enabled"};
-#else
-
- auto spirv = ReadFile<uint32_t>(path);
- if (spirv == Success) {
+ // Populate the map from benchmark input name to WGSL shader.
+ for (auto& benchmark : kBenchmarkInputs) {
+ if (!benchmark.wgsl.empty()) {
+ // If the input is WGSL, we just add it as is.
+ kBenchmarkWgslShaders.Add(benchmark.name, benchmark.wgsl);
+ } else if (!benchmark.spirv.empty()) {
+ // If the input is SPIR-V, we convert it to WGSL and add that.
tint::spirv::reader::Options spirv_opts;
spirv_opts.allow_non_uniform_derivatives = true;
- auto program = tint::spirv::reader::Read(spirv.Get(), spirv_opts);
+ auto program = tint::spirv::reader::Read(benchmark.spirv, spirv_opts);
if (!program.IsValid()) {
- return Failure{program.Diagnostics()};
+ std::cerr << "Failed to convert '" << benchmark.name
+ << "': " << program.Diagnostics() << "\n";
+ return false;
}
auto result = tint::wgsl::writer::Generate(program, {});
if (result != Success) {
- return result.Failure();
+ std::cerr << "Failed to generate WGSL for '" << benchmark.name
+ << "': " << result.Failure() << "\n";
+ return false;
}
- return tint::Source::File(path, result->wgsl);
+ kBenchmarkWgslShaders.Add(benchmark.name, result->wgsl);
+ } else {
+ TINT_UNREACHABLE();
}
- return spirv.Failure();
-#endif
}
- return Failure{"unsupported file extension: '" + name + "'"};
+
+ return true;
}
-Result<ProgramAndFile> LoadProgram(std::string name) {
- auto res = bench::LoadInputFile(name);
+Result<Source::File> GetWgslFile(std::string name) {
+ auto wgsl = kBenchmarkWgslShaders.GetOr(name, "");
+ if (wgsl.empty()) {
+ return Failure{"failed to find WGSL shader for '" + name + "'"};
+ }
+ return tint::Source::File("<input>", wgsl);
+}
+
+Result<ProgramAndFile> GetWgslProgram(std::string name) {
+ auto res = GetWgslFile(name);
if (res != Success) {
return res.Failure();
}
diff --git a/src/tint/cmd/bench/bench.h b/src/tint/cmd/bench/bench.h
index e6cf6e7..7a8fb70 100644
--- a/src/tint/cmd/bench/bench.h
+++ b/src/tint/cmd/bench/bench.h
@@ -38,8 +38,10 @@
#include "src/tint/utils/macros/concat.h"
#include "src/tint/utils/result/result.h"
-namespace tint::bench {
+// Autogenerated header that defines the benchmark shaders and macros for registering benchmarks.
+#include "src/tint/cmd/bench/benchmark_inputs.h" // GEN_BUILD:IGNORE_INCLUDE
+namespace tint::bench {
/// ProgramAndFile holds a Program and a Source::File.
struct ProgramAndFile {
/// The tint program parsed from file.
@@ -53,52 +55,16 @@
/// @returns true on success, false of failure
bool Initialize();
-/// LoadInputFile attempts to load a benchmark input file with the given file
-/// name. Accepts files with the .wgsl and .spv extension.
-/// SPIR-V files are automatically converted to WGSL.
-/// @param name the file name
-/// @returns the loaded Source::File
-Result<Source::File> LoadInputFile(std::string name);
+/// GetWgslFile retrieves the WGSL for the benchmark input shader called @p name.
+/// Benchmarks that are SPIR-V shaders will have been converted to WGSL at startup.
+/// @param name the benchmark input name
+/// @returns the Source::File
+Result<Source::File> GetWgslFile(std::string name);
-/// LoadInputFile attempts to load a benchmark input program with the given file
-/// name.
-/// @param name the file name
-/// @returns the loaded Program
-Result<ProgramAndFile> LoadProgram(std::string name);
-
-// If TINT_BENCHMARK_EXTERNAL_SHADERS_HEADER is defined, include that to
-// declare the TINT_BENCHMARK_EXTERNAL_WGSL_PROGRAMS() and TINT_BENCHMARK_EXTERNAL_SPV_PROGRAMS()
-// macros, which appends external programs to the TINT_BENCHMARK_WGSL_PROGRAMS() and
-// TINT_BENCHMARK_SPV_PROGRAMS() list.
-#ifdef TINT_BENCHMARK_EXTERNAL_SHADERS_HEADER
-#include TINT_BENCHMARK_EXTERNAL_SHADERS_HEADER
-#else
-#define TINT_BENCHMARK_EXTERNAL_WGSL_PROGRAMS(x)
-#define TINT_BENCHMARK_EXTERNAL_SPV_PROGRAMS(x)
-#endif
-
-/// Declares a benchmark with the given function and WGSL file name
-#define TINT_BENCHMARK_WGSL_PROGRAM(FUNC, WGSL_NAME) BENCHMARK_CAPTURE(FUNC, WGSL_NAME, WGSL_NAME)
-
-/// Declares a set of benchmarks for the given function using a list of WGSL files.
-#define TINT_BENCHMARK_WGSL_PROGRAMS(FUNC) \
- TINT_BENCHMARK_WGSL_PROGRAM(FUNC, "atan2-const-eval.wgsl"); \
- TINT_BENCHMARK_WGSL_PROGRAM(FUNC, "cluster-lights.wgsl"); \
- TINT_BENCHMARK_WGSL_PROGRAM(FUNC, "metaball-isosurface.wgsl"); \
- TINT_BENCHMARK_WGSL_PROGRAM(FUNC, "particles.wgsl"); \
- TINT_BENCHMARK_WGSL_PROGRAM(FUNC, "shadow-fragment.wgsl"); \
- TINT_BENCHMARK_WGSL_PROGRAM(FUNC, "skinned-shadowed-pbr-fragment.wgsl"); \
- TINT_BENCHMARK_WGSL_PROGRAM(FUNC, "skinned-shadowed-pbr-vertex.wgsl"); \
- TINT_BENCHMARK_EXTERNAL_WGSL_PROGRAMS(FUNC)
-
-/// Declares a set of benchmarks for the given function using a list of SPIR-V files.
-#define TINT_BENCHMARK_SPV_PROGRAMS(FUNC) TINT_BENCHMARK_EXTERNAL_SPV_PROGRAMS(FUNC)
-
-/// Declares a set of benchmarks for the given function using a list of WGSL and SPIR-V files.
-#define TINT_BENCHMARK_PROGRAMS(FUNC) \
- TINT_BENCHMARK_WGSL_PROGRAMS(FUNC) \
- TINT_BENCHMARK_SPV_PROGRAMS(FUNC) \
- TINT_REQUIRE_SEMICOLON
+/// GetWgslProgram parses the WGSL for a benchmark input program with the given name.
+/// @param name the benchmark input name
+/// @returns the parsed WGSL program
+Result<ProgramAndFile> GetWgslProgram(std::string name);
} // namespace tint::bench
diff --git a/src/tint/lang/glsl/writer/writer_bench.cc b/src/tint/lang/glsl/writer/writer_bench.cc
index 8505dac..659adb9 100644
--- a/src/tint/lang/glsl/writer/writer_bench.cc
+++ b/src/tint/lang/glsl/writer/writer_bench.cc
@@ -36,7 +36,7 @@
namespace {
void GenerateGLSL(benchmark::State& state, std::string input_name) {
- auto res = bench::LoadProgram(input_name);
+ auto res = bench::GetWgslProgram(input_name);
if (res != Success) {
state.SkipWithError(res.Failure().reason.Str());
return;
diff --git a/src/tint/lang/hlsl/writer/writer_bench.cc b/src/tint/lang/hlsl/writer/writer_bench.cc
index 55939cf..f84d151 100644
--- a/src/tint/lang/hlsl/writer/writer_bench.cc
+++ b/src/tint/lang/hlsl/writer/writer_bench.cc
@@ -34,7 +34,7 @@
namespace {
void GenerateHLSL(benchmark::State& state, std::string input_name) {
- auto res = bench::LoadProgram(input_name);
+ auto res = bench::GetWgslProgram(input_name);
if (res != Success) {
state.SkipWithError(res.Failure().reason.Str());
return;
diff --git a/src/tint/lang/msl/writer/writer_bench.cc b/src/tint/lang/msl/writer/writer_bench.cc
index 47d07a1..e9f30ae 100644
--- a/src/tint/lang/msl/writer/writer_bench.cc
+++ b/src/tint/lang/msl/writer/writer_bench.cc
@@ -37,7 +37,7 @@
namespace {
void GenerateMSL(benchmark::State& state, std::string input_name) {
- auto res = bench::LoadProgram(input_name);
+ auto res = bench::GetWgslProgram(input_name);
if (res != Success) {
state.SkipWithError(res.Failure().reason.Str());
return;
diff --git a/src/tint/lang/spirv/writer/writer_bench.cc b/src/tint/lang/spirv/writer/writer_bench.cc
index 11a1c7a..a89e10d 100644
--- a/src/tint/lang/spirv/writer/writer_bench.cc
+++ b/src/tint/lang/spirv/writer/writer_bench.cc
@@ -46,7 +46,7 @@
namespace {
void GenerateSPIRV(benchmark::State& state, std::string input_name) {
- auto res = bench::LoadProgram(input_name);
+ auto res = bench::GetWgslProgram(input_name);
if (res != Success) {
state.SkipWithError(res.Failure().reason.Str());
return;
@@ -61,7 +61,7 @@
void GenerateSPIRV_UseIR(benchmark::State& state, std::string input_name) {
#if TINT_BUILD_WGSL_READER
- auto res = bench::LoadProgram(input_name);
+ auto res = bench::GetWgslProgram(input_name);
if (res != Success) {
state.SkipWithError(res.Failure().reason.Str());
return;
diff --git a/src/tint/lang/wgsl/reader/reader_bench.cc b/src/tint/lang/wgsl/reader/reader_bench.cc
index ba260af..39e1aa6 100644
--- a/src/tint/lang/wgsl/reader/reader_bench.cc
+++ b/src/tint/lang/wgsl/reader/reader_bench.cc
@@ -34,7 +34,7 @@
namespace {
void ParseWGSL(benchmark::State& state, std::string input_name) {
- auto res = bench::LoadInputFile(input_name);
+ auto res = bench::GetWgslFile(input_name);
if (res != Success) {
state.SkipWithError(res.Failure().reason.Str());
return;
diff --git a/src/tint/lang/wgsl/writer/writer_bench.cc b/src/tint/lang/wgsl/writer/writer_bench.cc
index 285710e..1f9cbfc 100644
--- a/src/tint/lang/wgsl/writer/writer_bench.cc
+++ b/src/tint/lang/wgsl/writer/writer_bench.cc
@@ -34,7 +34,7 @@
namespace {
void GenerateWGSL(benchmark::State& state, std::string input_name) {
- auto res = bench::LoadProgram(input_name);
+ auto res = bench::GetWgslProgram(input_name);
if (res != Success) {
state.SkipWithError(res.Failure().reason.Str());
return;