Add simple program for perf testing.
This CL adds a simple program which can be set to loop over specific
parts of the tint pipeline to generate profiling data.
Change-Id: I6375940619b7ef2f7e66540d4f740e6e0b9b3132
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/128541
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/cmd/CMakeLists.txt b/src/tint/cmd/CMakeLists.txt
index 142595b..441954d 100644
--- a/src/tint/cmd/CMakeLists.txt
+++ b/src/tint/cmd/CMakeLists.txt
@@ -53,3 +53,15 @@
if (${TINT_BUILD_SPV_READER})
target_link_libraries(tint_info SPIRV-Tools)
endif()
+
+add_executable(tint-loopy "")
+target_sources(tint-loopy PRIVATE
+ "generate_external_texture_bindings.cc"
+ "generate_external_texture_bindings.h"
+ "helper.cc"
+ "helper.h"
+ "loopy.cc"
+)
+tint_default_compile_options(tint-loopy)
+target_link_libraries(tint-loopy libtint tint_val)
+
diff --git a/src/tint/cmd/helper.cc b/src/tint/cmd/helper.cc
index 6ff1e7a..a00e24a 100644
--- a/src/tint/cmd/helper.cc
+++ b/src/tint/cmd/helper.cc
@@ -45,54 +45,6 @@
return input_format;
}
-/// 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>
-bool ReadFile(const std::string& input_file, std::vector<T>* buffer) {
- if (!buffer) {
- std::cerr << "The buffer pointer was null" << std::endl;
- return false;
- }
-
- 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) {
- std::cerr << "Failed to open " << input_file << std::endl;
- return false;
- }
-
- fseek(file, 0, SEEK_END);
- const auto file_size = static_cast<size_t>(ftell(file));
- if (0 != (file_size % sizeof(T))) {
- std::cerr << "File " << input_file
- << " does not contain an integral number of objects: " << file_size
- << " bytes in the file, require " << sizeof(T) << " bytes per object"
- << std::endl;
- fclose(file);
- return false;
- }
- fseek(file, 0, SEEK_SET);
-
- buffer->clear();
- buffer->resize(file_size / sizeof(T));
-
- size_t bytes_read = fread(buffer->data(), 1, file_size, file);
- fclose(file);
- if (bytes_read != file_size) {
- std::cerr << "Failed to read " << input_file << std::endl;
- return false;
- }
-
- return true;
-}
-
void PrintBindings(tint::inspector::Inspector& inspector, const std::string& ep_name) {
auto bindings = inspector.GetResourceBindings(ep_name);
if (!inspector.error().empty()) {
diff --git a/src/tint/cmd/helper.h b/src/tint/cmd/helper.h
index 3a48ea4..6878288 100644
--- a/src/tint/cmd/helper.h
+++ b/src/tint/cmd/helper.h
@@ -15,8 +15,10 @@
#ifndef SRC_TINT_CMD_HELPER_H_
#define SRC_TINT_CMD_HELPER_H_
+#include <iostream>
#include <memory>
#include <string>
+#include <vector>
#include "tint/tint.h"
@@ -102,6 +104,54 @@
/// @return the text name
std::string OverrideTypeToString(tint::inspector::Override::Type type);
+/// 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>
+bool ReadFile(const std::string& input_file, std::vector<T>* buffer) {
+ if (!buffer) {
+ std::cerr << "The buffer pointer was null" << std::endl;
+ return false;
+ }
+
+ 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) {
+ std::cerr << "Failed to open " << input_file << std::endl;
+ return false;
+ }
+
+ fseek(file, 0, SEEK_END);
+ const auto file_size = static_cast<size_t>(ftell(file));
+ if (0 != (file_size % sizeof(T))) {
+ std::cerr << "File " << input_file
+ << " does not contain an integral number of objects: " << file_size
+ << " bytes in the file, require " << sizeof(T) << " bytes per object"
+ << std::endl;
+ fclose(file);
+ return false;
+ }
+ fseek(file, 0, SEEK_SET);
+
+ buffer->clear();
+ buffer->resize(file_size / sizeof(T));
+
+ size_t bytes_read = fread(buffer->data(), 1, file_size, file);
+ fclose(file);
+ if (bytes_read != file_size) {
+ std::cerr << "Failed to read " << input_file << std::endl;
+ return false;
+ }
+
+ return true;
+}
+
} // namespace tint::cmd
#endif // SRC_TINT_CMD_HELPER_H_
diff --git a/src/tint/cmd/loopy.cc b/src/tint/cmd/loopy.cc
new file mode 100644
index 0000000..1714e3b
--- /dev/null
+++ b/src/tint/cmd/loopy.cc
@@ -0,0 +1,433 @@
+// Copyright 2023 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <iostream>
+
+#include "src/tint/cmd/generate_external_texture_bindings.h"
+#include "src/tint/cmd/helper.h"
+#include "tint/tint.h"
+
+#if TINT_BUILD_IR
+#include "src/tint/ir/module.h"
+#endif // TINT_BUILD_IR
+
+namespace {
+
+enum class Format {
+ kUnknown,
+ kNone,
+ kSpirv,
+ kWgsl,
+ kMsl,
+ kHlsl,
+ kGlsl,
+};
+
+enum class Looper {
+ kLoad,
+ kIRGenerate,
+ kWriter,
+};
+
+struct Options {
+ bool show_help = false;
+
+ std::string input_filename;
+ Format format = Format::kUnknown;
+
+ Looper loop = Looper::kLoad;
+ uint32_t loop_count = 100;
+};
+
+const char kUsage[] = R"(Usage: tint-loopy [options] <input-file>
+
+ options:
+ --format <spirv|wgsl|msl|hlsl|none> -- Generation format. Default SPIR-V.
+ --loop <load,ir-gen,writer> -- Item to loop
+ --loop-count <num> -- Number of loops to run, default 100.
+)";
+
+Format parse_format(const std::string& fmt) {
+ (void)fmt;
+
+#if TINT_BUILD_SPV_WRITER
+ if (fmt == "spirv") {
+ return Format::kSpirv;
+ }
+#endif // TINT_BUILD_SPV_WRITER
+
+#if TINT_BUILD_WGSL_WRITER
+ if (fmt == "wgsl") {
+ return Format::kWgsl;
+ }
+#endif // TINT_BUILD_WGSL_WRITER
+
+#if TINT_BUILD_MSL_WRITER
+ if (fmt == "msl") {
+ return Format::kMsl;
+ }
+#endif // TINT_BUILD_MSL_WRITER
+
+#if TINT_BUILD_HLSL_WRITER
+ if (fmt == "hlsl") {
+ return Format::kHlsl;
+ }
+#endif // TINT_BUILD_HLSL_WRITER
+
+#if TINT_BUILD_GLSL_WRITER
+ if (fmt == "glsl") {
+ return Format::kGlsl;
+ }
+#endif // TINT_BUILD_GLSL_WRITER
+
+ if (fmt == "none") {
+ return Format::kNone;
+ }
+
+ return Format::kUnknown;
+}
+
+bool ParseArgs(const std::vector<std::string>& args, Options* opts) {
+ for (size_t i = 1; i < args.size(); ++i) {
+ const std::string& arg = args[i];
+ if (arg == "--format") {
+ ++i;
+ if (i >= args.size()) {
+ std::cerr << "Missing value for --format argument." << std::endl;
+ return false;
+ }
+ opts->format = parse_format(args[i]);
+
+ if (opts->format == Format::kUnknown) {
+ std::cerr << "Unknown output format: " << args[i] << std::endl;
+ return false;
+ }
+ } else if (arg == "-h" || arg == "--help") {
+ opts->show_help = true;
+ } else if (arg == "--loop") {
+ ++i;
+ if (i >= args.size()) {
+ std::cerr << "Missing value for --loop argument." << std::endl;
+ return false;
+ }
+ if (args[i] == "load") {
+ opts->loop = Looper::kLoad;
+ } else if (args[i] == "ir-gen") {
+ opts->loop = Looper::kIRGenerate;
+ } else if (args[i] == "writer") {
+ opts->loop = Looper::kWriter;
+ } else {
+ std::cerr << "Invalid loop value" << std::endl;
+ return false;
+ }
+ } else if (arg == "--loop-count") {
+ ++i;
+ if (i >= args.size()) {
+ std::cerr << "Missing value for --loop-count argument." << std::endl;
+ return false;
+ }
+ int32_t val = atoi(args[i].c_str());
+ if (val <= 0) {
+ std::cerr << "Loop count must be greater then 0" << std::endl;
+ return false;
+ }
+ opts->loop_count = static_cast<uint32_t>(val);
+ } else if (!arg.empty()) {
+ if (arg[0] == '-') {
+ std::cerr << "Unrecognized option: " << arg << std::endl;
+ return false;
+ }
+ if (!opts->input_filename.empty()) {
+ std::cerr << "More than one input file specified: '" << opts->input_filename
+ << "' and '" << arg << "'" << std::endl;
+ return false;
+ }
+ opts->input_filename = arg;
+ }
+ }
+ return true;
+}
+
+/// Generate SPIR-V code for a program.
+/// @param program the program to generate
+/// @returns true on success
+bool GenerateSpirv(const tint::Program* program) {
+#if TINT_BUILD_SPV_WRITER
+ tint::writer::spirv::Options gen_options;
+ gen_options.external_texture_options.bindings_map =
+ tint::cmd::GenerateExternalTextureBindings(program);
+ auto result = tint::writer::spirv::Generate(program, gen_options);
+ if (!result.success) {
+ tint::cmd::PrintWGSL(std::cerr, *program);
+ std::cerr << "Failed to generate: " << result.error << std::endl;
+ return false;
+ }
+ return true;
+#else
+ (void)program;
+ std::cerr << "SPIR-V writer not enabled in tint build" << std::endl;
+ return false;
+#endif // TINT_BUILD_SPV_WRITER
+}
+
+/// Generate WGSL code for a program.
+/// @param program the program to generate
+/// @returns true on success
+bool GenerateWgsl(const tint::Program* program) {
+#if TINT_BUILD_WGSL_WRITER
+ tint::writer::wgsl::Options gen_options;
+ auto result = tint::writer::wgsl::Generate(program, gen_options);
+ if (!result.success) {
+ std::cerr << "Failed to generate: " << result.error << std::endl;
+ return false;
+ }
+
+ return true;
+#else
+ (void)program;
+ std::cerr << "WGSL writer not enabled in tint build" << std::endl;
+ return false;
+#endif // TINT_BUILD_WGSL_WRITER
+}
+
+/// Generate MSL code for a program.
+/// @param program the program to generate
+/// @returns true on success
+bool GenerateMsl(const tint::Program* program) {
+#if TINT_BUILD_MSL_WRITER
+ // Remap resource numbers to a flat namespace.
+ // TODO(crbug.com/tint/1501): Do this via Options::BindingMap.
+ const tint::Program* input_program = program;
+ auto flattened = tint::writer::FlattenBindings(program);
+ if (flattened) {
+ input_program = &*flattened;
+ }
+
+ tint::writer::msl::Options gen_options;
+ gen_options.external_texture_options.bindings_map =
+ tint::cmd::GenerateExternalTextureBindings(input_program);
+ gen_options.array_length_from_uniform.ubo_binding = tint::writer::BindingPoint{0, 30};
+ gen_options.array_length_from_uniform.bindpoint_to_size_index.emplace(
+ tint::writer::BindingPoint{0, 0}, 0);
+ gen_options.array_length_from_uniform.bindpoint_to_size_index.emplace(
+ tint::writer::BindingPoint{0, 1}, 1);
+ auto result = tint::writer::msl::Generate(input_program, gen_options);
+ if (!result.success) {
+ tint::cmd::PrintWGSL(std::cerr, *program);
+ std::cerr << "Failed to generate: " << result.error << std::endl;
+ return false;
+ }
+
+ return true;
+#else
+ (void)program;
+ std::cerr << "MSL writer not enabled in tint build" << std::endl;
+ return false;
+#endif // TINT_BUILD_MSL_WRITER
+}
+
+/// Generate HLSL code for a program.
+/// @param program the program to generate
+/// @returns true on success
+bool GenerateHlsl(const tint::Program* program) {
+#if TINT_BUILD_HLSL_WRITER
+ tint::writer::hlsl::Options gen_options;
+ gen_options.external_texture_options.bindings_map =
+ tint::cmd::GenerateExternalTextureBindings(program);
+ auto result = tint::writer::hlsl::Generate(program, gen_options);
+ if (!result.success) {
+ tint::cmd::PrintWGSL(std::cerr, *program);
+ std::cerr << "Failed to generate: " << result.error << std::endl;
+ return false;
+ }
+
+ return true;
+#else
+ (void)program;
+ std::cerr << "HLSL writer not enabled in tint build" << std::endl;
+ return false;
+#endif // TINT_BUILD_HLSL_WRITER
+}
+
+/// Generate GLSL code for a program.
+/// @param program the program to generate
+/// @returns true on success
+bool GenerateGlsl(const tint::Program* program) {
+#if TINT_BUILD_GLSL_WRITER
+ tint::writer::glsl::Options gen_options;
+ gen_options.external_texture_options.bindings_map =
+ tint::cmd::GenerateExternalTextureBindings(program);
+ auto result = tint::writer::glsl::Generate(program, gen_options, "");
+ if (!result.success) {
+ tint::cmd::PrintWGSL(std::cerr, *program);
+ std::cerr << "Failed to generate: " << result.error << std::endl;
+ return false;
+ }
+
+ return true;
+
+#else
+ (void)program;
+ std::cerr << "GLSL writer not enabled in tint build" << std::endl;
+ return false;
+#endif // TINT_BUILD_GLSL_WRITER
+}
+
+} // namespace
+
+int main(int argc, const char** argv) {
+ std::vector<std::string> args(argv, argv + argc);
+ Options options;
+
+ tint::SetInternalCompilerErrorReporter(&tint::cmd::TintInternalCompilerErrorReporter);
+
+#if TINT_BUILD_WGSL_WRITER
+ tint::Program::printer = [](const tint::Program* program) {
+ auto result = tint::writer::wgsl::Generate(program, {});
+ if (!result.error.empty()) {
+ return "error: " + result.error;
+ }
+ return result.wgsl;
+ };
+#endif // TINT_BUILD_WGSL_WRITER
+
+ if (!ParseArgs(args, &options)) {
+ std::cerr << "Failed to parse arguments." << std::endl;
+ return 1;
+ }
+
+ if (options.show_help) {
+ std::cout << kUsage << std::endl;
+ return 0;
+ }
+
+ // Implement output format defaults.
+ if (options.format == Format::kUnknown) {
+ options.format = Format::kSpirv;
+ }
+
+ auto diag_printer = tint::diag::Printer::create(stderr, true);
+ tint::diag::Formatter diag_formatter;
+
+ std::unique_ptr<tint::Program> program;
+ std::unique_ptr<tint::Source::File> source_file;
+
+ if (options.loop == Looper::kLoad) {
+ if (options.input_filename.size() > 5 &&
+ options.input_filename.substr(options.input_filename.size() - 5) == ".wgsl") {
+#if TINT_BUILD_WGSL_READER
+ std::vector<uint8_t> data;
+ if (!tint::cmd::ReadFile<uint8_t>(options.input_filename, &data)) {
+ exit(1);
+ }
+ source_file = std::make_unique<tint::Source::File>(
+ options.input_filename, std::string(data.begin(), data.end()));
+
+ uint32_t loop_count = options.loop_count;
+ for (uint32_t i = 0; i < loop_count; ++i) {
+ program =
+ std::make_unique<tint::Program>(tint::reader::wgsl::Parse(source_file.get()));
+ }
+#else
+ std::cerr << "Tint not built with the WGSL reader enabled" << std::endl;
+ exit(1);
+#endif // TINT_BUILD_WGSL_READER
+ } else {
+#if TINT_BUILD_SPV_READER
+ std::vector<uint32_t> data;
+ if (!tint::cmd::ReadFile<uint32_t>(options.input_filename, &data)) {
+ exit(1);
+ }
+
+ uint32_t loop_count = options.loop_count;
+ for (uint32_t i = 0; i < loop_count; ++i) {
+ program = std::make_unique<tint::Program>(tint::reader::spirv::Parse(data, {}));
+ }
+#else
+ std::cerr << "Tint not built with the SPIR-V reader enabled" << std::endl;
+ exit(1);
+#endif // TINT_BUILD_SPV_READER
+ }
+ }
+
+ // Load the program that will actually be used
+ {
+ tint::cmd::LoadProgramOptions opts;
+ opts.filename = options.input_filename;
+
+ auto info = tint::cmd::LoadProgramInfo(opts);
+ program = std::move(info.program);
+ source_file = std::move(info.source_file);
+ }
+#if TINT_BUILD_IR
+ {
+ uint32_t loop_count = 1;
+ if (options.loop == Looper::kIRGenerate) {
+ loop_count = options.loop_count;
+ }
+ for (uint32_t i = 0; i < loop_count; ++i) {
+ auto result = tint::ir::Module::FromProgram(program.get());
+ if (!result) {
+ std::cerr << "Failed to build IR from program: " << result.Failure() << std::endl;
+ }
+ }
+ }
+#endif // TINT_BUILD_IR
+
+ bool success = false;
+ {
+ uint32_t loop_count = 1;
+ if (options.loop == Looper::kWriter) {
+ loop_count = options.loop_count;
+ }
+
+ switch (options.format) {
+ case Format::kSpirv:
+ for (uint32_t i = 0; i < loop_count; ++i) {
+ success = GenerateSpirv(program.get());
+ }
+ break;
+ case Format::kWgsl:
+ for (uint32_t i = 0; i < loop_count; ++i) {
+ success = GenerateWgsl(program.get());
+ }
+ break;
+ case Format::kMsl:
+ for (uint32_t i = 0; i < loop_count; ++i) {
+ success = GenerateMsl(program.get());
+ }
+ break;
+ case Format::kHlsl:
+ for (uint32_t i = 0; i < loop_count; ++i) {
+ success = GenerateHlsl(program.get());
+ }
+ break;
+ case Format::kGlsl:
+ for (uint32_t i = 0; i < loop_count; ++i) {
+ success = GenerateGlsl(program.get());
+ }
+ break;
+ case Format::kNone:
+ break;
+ default:
+ std::cerr << "Unknown output format specified" << std::endl;
+ return 1;
+ }
+ }
+ if (!success) {
+ return 1;
+ }
+
+ return 0;
+}