diff --git a/samples/main.cc b/samples/main.cc index 3e8de72657..e44fbfa53c 100644 --- a/samples/main.cc +++ b/samples/main.cc @@ -589,12 +589,182 @@ std::string Disassemble(const std::vector& data) { /// @param program the program void PrintWGSL(std::ostream& out, const tint::Program& program) { #if TINT_BUILD_WGSL_WRITER - tint::writer::wgsl::Generator writer(&program); - writer.Generate(); - out << std::endl << writer.result() << std::endl; + tint::writer::wgsl::Options options; + auto result = tint::writer::wgsl::Generate(&program, options); + out << std::endl << result.wgsl << std::endl; #endif } +/// Generate SPIR-V code for a program. +/// @param program the program to generate +/// @param options the options that Tint was invoked with +/// @returns true on success +bool GenerateSpirv(const tint::Program* program, const Options& options) { +#if TINT_BUILD_SPV_WRITER + // TODO(jrprice): Provide a way for the user to set non-default options. + tint::writer::spirv::Options gen_options; + auto result = tint::writer::spirv::Generate(program, gen_options); + if (!result.success) { + PrintWGSL(std::cerr, *program); + std::cerr << "Failed to generate: " << result.error << std::endl; + return false; + } + + if (options.format == Format::kSpvAsm) { + if (!WriteFile(options.output_file, "w", Disassemble(result.spirv))) { + return false; + } + } else { + if (!WriteFile(options.output_file, "wb", result.spirv)) { + return false; + } + } + + if (options.validate) { + // Use Vulkan 1.1, since this is what Tint, internally, uses. + spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_1); + tools.SetMessageConsumer([](spv_message_level_t, const char*, + const spv_position_t& pos, const char* msg) { + std::cerr << (pos.line + 1) << ":" << (pos.column + 1) << ": " << msg + << std::endl; + }); + if (!tools.Validate(result.spirv.data(), result.spirv.size(), + spvtools::ValidatorOptions())) { + return false; + } + } + + return true; +#else + 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 +/// @param options the options that Tint was invoked with +/// @returns true on success +bool GenerateWgsl(const tint::Program* program, const Options& options) { +#if TINT_BUILD_WGSL_WRITER + // TODO(jrprice): Provide a way for the user to set non-default options. + 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 WriteFile(options.output_file, "w", result.wgsl); +#else + 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 +/// @param options the options that Tint was invoked with +/// @returns true on success +bool GenerateMsl(const tint::Program* program, const Options& options) { +#if TINT_BUILD_MSL_WRITER + // TODO(jrprice): Provide a way for the user to set non-default options. + tint::writer::msl::Options gen_options; + auto result = tint::writer::msl::Generate(program, gen_options); + if (!result.success) { + PrintWGSL(std::cerr, *program); + std::cerr << "Failed to generate: " << result.error << std::endl; + return false; + } + + if (!WriteFile(options.output_file, "w", result.msl)) { + return false; + } + + if (options.validate) { + tint::val::Result res; +#ifdef TINT_ENABLE_MSL_VALIDATION_USING_METAL_API + res = tint::val::MslUsingMetalAPI(result.msl); +#else +#ifdef _WIN32 + const char* default_xcrun_exe = "metal.exe"; +#else + const char* default_xcrun_exe = "xcrun"; +#endif + auto xcrun = tint::utils::Command::LookPath( + options.xcrun_path.empty() ? default_xcrun_exe : options.xcrun_path); + if (xcrun.Found()) { + res = tint::val::Msl(xcrun.Path(), result.msl); + } else { + res.output = "xcrun executable not found. Cannot validate."; + res.failed = true; + } +#endif // TINT_ENABLE_MSL_VALIDATION_USING_METAL_API + if (res.failed) { + std::cerr << res.output << std::endl; + return false; + } + } + + return true; +#else + 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 +/// @param options the options that Tint was invoked with +/// @returns true on success +bool GenerateHlsl(const tint::Program* program, const Options& options) { +#if TINT_BUILD_HLSL_WRITER + // TODO(jrprice): Provide a way for the user to set non-default options. + tint::writer::hlsl::Options gen_options; + auto result = tint::writer::hlsl::Generate(program, gen_options); + if (!result.success) { + PrintWGSL(std::cerr, *program); + std::cerr << "Failed to generate: " << result.error << std::endl; + return false; + } + + if (!WriteFile(options.output_file, "w", result.hlsl)) { + return false; + } + + if (options.validate) { + tint::val::Result res; + if (options.use_fxc) { +#ifdef _WIN32 + res = tint::val::HlslUsingFXC(result.hlsl, result.entry_points); +#else + res.failed = true; + res.output = "FXC can only be used on Windows. Sorry :X"; +#endif // _WIN32 + } else { + auto dxc = tint::utils::Command::LookPath( + options.dxc_path.empty() ? "dxc" : options.dxc_path); + if (dxc.Found()) { + res = tint::val::HlslUsingDXC(dxc.Path(), result.hlsl, + result.entry_points); + } else { + res.failed = true; + res.output = "DXC executable not found. Cannot validate"; + } + } + if (res.failed) { + std::cerr << res.output << std::endl; + return false; + } + } + + return true; +#else + std::cerr << "MSL writer not enabled in tint build" << std::endl; + return false; +#endif // TINT_BUILD_HLSL_WRITER +} + } // namespace int main(int argc, const char** argv) { @@ -729,19 +899,11 @@ int main(int argc, const char** argv) { } switch (options.format) { -#if TINT_BUILD_SPV_WRITER - case Format::kSpirv: - case Format::kSpvAsm: - transform_manager.Add(); - transform_inputs.Add(true); - break; -#endif // TINT_BUILD_SPV_WRITER #if TINT_BUILD_MSL_WRITER case Format::kMsl: { transform_inputs.Add( tint::transform::Renamer::Target::kMslKeywords); transform_manager.Add(); - transform_manager.Add(); break; } #endif // TINT_BUILD_MSL_WRITER @@ -750,7 +912,6 @@ int main(int argc, const char** argv) { transform_inputs.Add( tint::transform::Renamer::Target::kHlslKeywords); transform_manager.Add(); - transform_manager.Add(); break; } #endif // TINT_BUILD_HLSL_WRITER @@ -801,185 +962,28 @@ int main(int argc, const char** argv) { std::cout << std::string(80, '-') << std::endl; } - std::unique_ptr writer; - + bool success = false; switch (options.format) { case Format::kSpirv: case Format::kSpvAsm: -#if TINT_BUILD_SPV_WRITER - writer = std::make_unique(program.get()); + success = GenerateSpirv(program.get(), options); break; -#else - std::cerr << "SPIR-V writer not enabled in tint build" << std::endl; - return 1; -#endif // TINT_BUILD_SPV_WRITER - case Format::kWgsl: -#if TINT_BUILD_WGSL_WRITER - writer = std::make_unique(program.get()); + success = GenerateWgsl(program.get(), options); break; -#else - std::cerr << "WGSL writer not enabled in tint build" << std::endl; - return 1; -#endif // TINT_BUILD_WGSL_WRITER - case Format::kMsl: -#if TINT_BUILD_MSL_WRITER - writer = std::make_unique(program.get()); + success = GenerateMsl(program.get(), options); break; -#else - std::cerr << "MSL writer not enabled in tint build" << std::endl; - return 1; -#endif // TINT_BUILD_MSL_WRITER - case Format::kHlsl: -#if TINT_BUILD_HLSL_WRITER - writer = std::make_unique(program.get()); + success = GenerateHlsl(program.get(), options); break; -#else - std::cerr << "HLSL writer not enabled in tint build" << std::endl; - return 1; -#endif // TINT_BUILD_HLSL_WRITER - default: std::cerr << "Unknown output format specified" << std::endl; return 1; } - - if (!writer->Generate()) { - PrintWGSL(std::cerr, *program); - std::cerr << "Failed to generate: " << writer->error() << std::endl; + if (!success) { return 1; } - bool validation_failed = false; - std::ostringstream validation_msgs; - - if (options.validate) { - switch (options.format) { -#if TINT_BUILD_SPV_WRITER - case Format::kSpirv: - case Format::kSpvAsm: { - // Use Vulkan 1.1, since this is what Tint, internally, uses. - spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_1); - tools.SetMessageConsumer( - [&validation_msgs](spv_message_level_t, const char*, - const spv_position_t& pos, const char* msg) { - validation_msgs << (pos.line + 1) << ":" << (pos.column + 1) - << ": " << msg << std::endl; - }); - auto* w = static_cast(writer.get()); - if (!tools.Validate(w->result().data(), w->result().size(), - spvtools::ValidatorOptions())) { - validation_failed = true; - } - break; - } -#endif -#if TINT_BUILD_HLSL_WRITER - case Format::kHlsl: { - auto* w = static_cast(writer.get()); - auto hlsl = w->result(); - - tint::val::Result res; - if (options.use_fxc) { -#ifdef _WIN32 - res = tint::val::HlslUsingFXC(hlsl, program.get()); -#else - res.failed = true; - res.output = "FXC can only be used on Windows. Sorry :X"; -#endif // _WIN32 - } else { - auto dxc = tint::utils::Command::LookPath( - options.dxc_path.empty() ? "dxc" : options.dxc_path); - if (dxc.Found()) { - res = tint::val::HlslUsingDXC(dxc.Path(), hlsl, program.get()); - } else { - res.failed = true; - res.output = "DXC executable not found. Cannot validate"; - } - } - if (res.failed) { - validation_failed = true; - validation_msgs << res.source << std::endl; - validation_msgs << res.output; - } - break; - } -#endif // TINT_BUILD_HLSL_WRITER -#if TINT_BUILD_MSL_WRITER - case Format::kMsl: { - auto* w = static_cast(writer.get()); - auto msl = w->result(); -#ifdef TINT_ENABLE_MSL_VALIDATION_USING_METAL_API - auto res = tint::val::MslUsingMetalAPI(msl); - if (res.failed) { - validation_failed = true; - validation_msgs << res.source << std::endl; - validation_msgs << res.output; - } -#else -#ifdef _WIN32 - const char* default_xcrun_exe = "metal.exe"; -#else - const char* default_xcrun_exe = "xcrun"; -#endif - auto xcrun = tint::utils::Command::LookPath(options.xcrun_path.empty() - ? default_xcrun_exe - : options.xcrun_path); - if (xcrun.Found()) { - auto res = tint::val::Msl(xcrun.Path(), msl); - if (res.failed) { - validation_failed = true; - validation_msgs << res.source << std::endl; - validation_msgs << res.output; - } - } else { - validation_failed = true; - validation_msgs << "xcrun executable not found. Cannot validate"; - } -#endif // TINT_ENABLE_MSL_VALIDATION_USING_METAL_API - break; - } - -#endif // TINT_BUILD_MSL_WRITER - default: - break; - } - } - -#if TINT_BUILD_SPV_WRITER - if (options.format == Format::kSpvAsm) { - auto* w = static_cast(writer.get()); - auto str = Disassemble(w->result()); - if (!WriteFile(options.output_file, "w", str)) { - return 1; - } - } - if (options.format == Format::kSpirv) { - auto* w = static_cast(writer.get()); - if (!WriteFile(options.output_file, "wb", w->result())) { - return 1; - } - } -#endif // TINT_BUILD_SPV_WRITER - - if (validation_failed) { - std::cerr << std::endl << std::endl << "Validation Failure:" << std::endl; - std::cerr << validation_msgs.str(); - return 1; - } - - if (options.format != Format::kSpvAsm && options.format != Format::kSpirv) { - auto* w = static_cast(writer.get()); - auto output = w->result(); - if (options.demangle) { - output = tint::Demangler().Demangle(program->Symbols(), output); - } - if (!WriteFile(options.output_file, "w", output)) { - return 1; - } - } - return 0; } diff --git a/src/ast/module_clone_test.cc b/src/ast/module_clone_test.cc index 0dc6eb9e11..2a78bb5cd2 100644 --- a/src/ast/module_clone_test.cc +++ b/src/ast/module_clone_test.cc @@ -144,11 +144,12 @@ let declaration_order_check_3 : i32 = 1; // Regenerate the wgsl for the src program. We use this instead of the // original source so that reformatting doesn't impact the final wgsl // comparison. + writer::wgsl::Options options; std::string src_wgsl; { - writer::wgsl::Generator src_gen(&src); - ASSERT_TRUE(src_gen.Generate()) << src_gen.error(); - src_wgsl = src_gen.result(); + auto result = writer::wgsl::Generate(&src, options); + ASSERT_TRUE(result.success) << result.error; + src_wgsl = result.wgsl; // Move the src program to a temporary that'll be dropped, so that the src // program is released before we attempt to print the dst program. This @@ -159,9 +160,9 @@ let declaration_order_check_3 : i32 = 1; } // Print the dst module, check it matches the original source - writer::wgsl::Generator dst_gen(&dst); - ASSERT_TRUE(dst_gen.Generate()); - auto dst_wgsl = dst_gen.result(); + auto result = writer::wgsl::Generate(&dst, options); + ASSERT_TRUE(result.success); + auto dst_wgsl = result.wgsl; ASSERT_EQ(src_wgsl, dst_wgsl); #else // #if TINT_BUILD_WGSL_READER && TINT_BUILD_WGSL_WRITER diff --git a/src/inspector/inspector_test.cc b/src/inspector/inspector_test.cc index 3c9b9350a4..2aa1c49d02 100644 --- a/src/inspector/inspector_test.cc +++ b/src/inspector/inspector_test.cc @@ -1549,9 +1549,6 @@ TEST_F(InspectorGetEntryPointTest, OverridableConstantReferencedByEntryPoint) { Inspector& inspector = Build(); - tint::writer::wgsl::Generator writer(program_.get()); - writer.Generate(); - auto result = inspector.GetEntryPoints(); ASSERT_EQ(1u, result.size()); diff --git a/src/transform/test_helper.h b/src/transform/test_helper.h index ed21a60415..4b35a71675 100644 --- a/src/transform/test_helper.h +++ b/src/transform/test_helper.h @@ -82,12 +82,13 @@ class TransformTestBase : public BASE { return diag::Formatter(style).format(output.program.Diagnostics()); } - writer::wgsl::Generator generator(&output.program); - if (!generator.Generate()) { - return "WGSL writer failed:\n" + generator.error(); + writer::wgsl::Options options; + auto result = writer::wgsl::Generate(&output.program, options); + if (!result.success) { + return "WGSL writer failed:\n" + result.error; } - auto res = generator.result(); + auto res = result.wgsl; if (res.empty()) { return res; } diff --git a/src/val/hlsl.cc b/src/val/hlsl.cc index e7e3aea777..2a02f29d93 100644 --- a/src/val/hlsl.cc +++ b/src/val/hlsl.cc @@ -14,8 +14,6 @@ #include "src/val/val.h" -#include "src/ast/module.h" -#include "src/program.h" #include "src/utils/io/command.h" #include "src/utils/io/tmpfile.h" @@ -33,7 +31,7 @@ namespace val { Result HlslUsingDXC(const std::string& dxc_path, const std::string& source, - Program* program) { + const EntryPointList& entry_points) { Result result; auto dxc = utils::Command(dxc_path); @@ -48,48 +46,42 @@ Result HlslUsingDXC(const std::string& dxc_path, utils::TmpFile file; file << source; - bool found_an_entrypoint = false; - for (auto* func : program->AST().Functions()) { - if (func->IsEntryPoint()) { - found_an_entrypoint = true; + for (auto ep : entry_points) { + const char* profile = ""; - const char* profile = ""; - - switch (func->pipeline_stage()) { - case ast::PipelineStage::kNone: - result.output = "Invalid PipelineStage"; - result.failed = true; - return result; - case ast::PipelineStage::kVertex: - profile = "-T vs_6_0"; - break; - case ast::PipelineStage::kFragment: - profile = "-T ps_6_0"; - break; - case ast::PipelineStage::kCompute: - profile = "-T cs_6_0"; - break; - } - - auto name = program->Symbols().NameFor(func->symbol()); - auto res = dxc(profile, "-E " + name, file.Path()); - if (!res.out.empty()) { - if (!result.output.empty()) { - result.output += "\n"; - } - result.output += res.out; - } - if (!res.err.empty()) { - if (!result.output.empty()) { - result.output += "\n"; - } - result.output += res.err; - } - result.failed = (res.error_code != 0); + switch (ep.second) { + case ast::PipelineStage::kNone: + result.output = "Invalid PipelineStage"; + result.failed = true; + return result; + case ast::PipelineStage::kVertex: + profile = "-T vs_6_0"; + break; + case ast::PipelineStage::kFragment: + profile = "-T ps_6_0"; + break; + case ast::PipelineStage::kCompute: + profile = "-T cs_6_0"; + break; } + + auto res = dxc(profile, "-E " + ep.first, file.Path()); + if (!res.out.empty()) { + if (!result.output.empty()) { + result.output += "\n"; + } + result.output += res.out; + } + if (!res.err.empty()) { + if (!result.output.empty()) { + result.output += "\n"; + } + result.output += res.err; + } + result.failed = (res.error_code != 0); } - if (!found_an_entrypoint) { + if (entry_points.empty()) { result.output = "No entrypoint found"; result.failed = true; return result; @@ -99,7 +91,8 @@ Result HlslUsingDXC(const std::string& dxc_path, } #ifdef _WIN32 -Result HlslUsingFXC(const std::string& source, Program* program) { +Result HlslUsingFXC(const std::string& source, + const EntryPointList& entry_points) { Result result; // This library leaks if an error happens in this function, but it is ok @@ -122,45 +115,38 @@ Result HlslUsingFXC(const std::string& source, Program* program) { result.source = source; - bool found_an_entrypoint = false; - for (auto* func : program->AST().Functions()) { - if (func->IsEntryPoint()) { - found_an_entrypoint = true; - - const char* profile = ""; - switch (func->pipeline_stage()) { - case ast::PipelineStage::kNone: - result.output = "Invalid PipelineStage"; - result.failed = true; - return result; - case ast::PipelineStage::kVertex: - profile = "vs_5_1"; - break; - case ast::PipelineStage::kFragment: - profile = "ps_5_1"; - break; - case ast::PipelineStage::kCompute: - profile = "cs_5_1"; - break; - } - - auto name = program->Symbols().NameFor(func->symbol()); - - ComPtr compiledShader; - ComPtr errors; - if (FAILED(d3dCompile(source.c_str(), source.length(), nullptr, nullptr, - nullptr, name.c_str(), profile, 0, 0, - &compiledShader, &errors))) { - result.output = static_cast(errors->GetBufferPointer()); + for (auto ep : entry_points) { + const char* profile = ""; + switch (ep.second) { + case ast::PipelineStage::kNone: + result.output = "Invalid PipelineStage"; result.failed = true; return result; - } + case ast::PipelineStage::kVertex: + profile = "vs_5_1"; + break; + case ast::PipelineStage::kFragment: + profile = "ps_5_1"; + break; + case ast::PipelineStage::kCompute: + profile = "cs_5_1"; + break; + } + + ComPtr compiledShader; + ComPtr errors; + if (FAILED(d3dCompile(source.c_str(), source.length(), nullptr, nullptr, + nullptr, ep.first.c_str(), profile, 0, 0, + &compiledShader, &errors))) { + result.output = static_cast(errors->GetBufferPointer()); + result.failed = true; + return result; } } FreeLibrary(fxcLib); - if (!found_an_entrypoint) { + if (entry_points.empty()) { result.output = "No entrypoint found"; result.failed = true; return result; diff --git a/src/val/val.h b/src/val/val.h index fd3ae0ac9a..233a868d63 100644 --- a/src/val/val.h +++ b/src/val/val.h @@ -16,6 +16,10 @@ #define SRC_VAL_VAL_H_ #include +#include +#include + +#include "src/ast/pipeline_stage.h" // Forward declarations namespace tint { @@ -25,6 +29,8 @@ class Program; namespace tint { namespace val { +using EntryPointList = std::vector>; + /// The return structure of Validate() struct Result { /// True if validation passed @@ -39,19 +45,20 @@ struct Result { /// compiles successfully. /// @param dxc_path path to DXC /// @param source the generated HLSL source -/// @param program the HLSL program +/// @param entry_points the list of entry points to validate /// @return the result of the compile Result HlslUsingDXC(const std::string& dxc_path, const std::string& source, - Program* program); + const EntryPointList& entry_points); #ifdef _WIN32 /// Hlsl attempts to compile the shader with FXC, verifying that the shader /// compiles successfully. /// @param source the generated HLSL source -/// @param program the HLSL program +/// @param entry_points the list of entry points to validate /// @return the result of the compile -Result HlslUsingFXC(const std::string& source, Program* program); +Result HlslUsingFXC(const std::string& source, + const EntryPointList& entry_points); #endif // _WIN32 /// Msl attempts to compile the shader with the Metal Shader Compiler, diff --git a/src/writer/hlsl/generator.cc b/src/writer/hlsl/generator.cc index 297788e0fb..d6815a3fd7 100644 --- a/src/writer/hlsl/generator.cc +++ b/src/writer/hlsl/generator.cc @@ -14,12 +14,46 @@ #include "src/writer/hlsl/generator.h" +#include "src/transform/hlsl.h" #include "src/writer/hlsl/generator_impl.h" namespace tint { namespace writer { namespace hlsl { +Result::Result() = default; +Result::~Result() = default; +Result::Result(const Result&) = default; + +Result Generate(const Program* program, const Options&) { + Result result; + + // Run the HLSL sanitizer. + transform::Hlsl sanitizer; + auto output = sanitizer.Run(program); + if (!output.program.IsValid()) { + result.success = false; + result.error = output.program.Diagnostics().str(); + return result; + } + + // Generate the HLSL code. + auto impl = std::make_unique(&output.program); + result.success = impl->Generate(); + result.error = impl->error(); + result.hlsl = impl->result(); + + // Collect the list of entry points in the sanitized program. + for (auto* func : output.program.AST().Functions()) { + if (func->IsEntryPoint()) { + auto name = output.program.Symbols().NameFor(func->symbol()); + result.entry_points.push_back({name, func->pipeline_stage()}); + } + } + + return result; +} + Generator::Generator(const Program* program) : impl_(std::make_unique(program)) {} diff --git a/src/writer/hlsl/generator.h b/src/writer/hlsl/generator.h index a3bef0059a..75d47d80f4 100644 --- a/src/writer/hlsl/generator.h +++ b/src/writer/hlsl/generator.h @@ -17,16 +17,59 @@ #include #include +#include +#include +#include "src/ast/pipeline_stage.h" #include "src/writer/text.h" namespace tint { + +// Forward declarations +class Program; + namespace writer { namespace hlsl { // Forward declarations class GeneratorImpl; +/// Configuration options used for generating HLSL. +struct Options {}; + +/// The result produced when generating HLSL. +struct Result { + /// Constructor + Result(); + + /// Destructor + ~Result(); + + /// Copy constructor + Result(const Result&); + + /// True if generation was successful. + bool success = false; + + /// The errors generated during code generation, if any. + std::string error; + + /// The generated HLSL. + std::string hlsl = ""; + + /// The list of entry points in the generated HLSL. + std::vector> entry_points; +}; + +/// Generate HLSL for a program, according to a set of configuration options. +/// The result will contain the HLSL, as well as success status and diagnostic +/// information. +/// @param program the program to translate to HLSL +/// @param options the configuration options to use when generating HLSL +/// @returns the resulting HLSL and supplementary information +Result Generate(const Program* program, const Options& options); + +// TODO(jrprice): Remove this once Dawn is using the new interface. /// Class to generate HLSL source class Generator : public Text { public: diff --git a/src/writer/msl/generator.cc b/src/writer/msl/generator.cc index 3c2190fc3d..45287febb8 100644 --- a/src/writer/msl/generator.cc +++ b/src/writer/msl/generator.cc @@ -13,12 +13,45 @@ // limitations under the License. #include "src/writer/msl/generator.h" + +#include "src/transform/msl.h" #include "src/writer/msl/generator_impl.h" namespace tint { namespace writer { namespace msl { +Result::Result() = default; +Result::~Result() = default; +Result::Result(const Result&) = default; + +Result Generate(const Program* program, const Options& options) { + Result result; + + // Run the MSL sanitizer. + transform::Msl sanitizer; + transform::DataMap transform_input; + transform_input.Add(options.buffer_size_ubo_index, + options.fixed_sample_mask); + auto output = sanitizer.Run(program, transform_input); + if (!output.program.IsValid()) { + result.success = false; + result.error = output.program.Diagnostics().str(); + return result; + } + auto* transform_output = output.data.Get(); + result.needs_storage_buffer_sizes = + transform_output->needs_storage_buffer_sizes; + + // Generate the MSL code. + auto impl = std::make_unique(&output.program); + result.success = impl->Generate(); + result.error = impl->error(); + result.msl = impl->result(); + + return result; +} + Generator::Generator(const Program* program) : impl_(std::make_unique(program)) {} diff --git a/src/writer/msl/generator.h b/src/writer/msl/generator.h index d63f755e79..b4891c90f9 100644 --- a/src/writer/msl/generator.h +++ b/src/writer/msl/generator.h @@ -21,11 +21,59 @@ #include "src/writer/text.h" namespace tint { + +// Forward declarations +class Program; + namespace writer { namespace msl { class GeneratorImpl; +/// Configuration options used for generating MSL. +struct Options { + /// The index to use when generating a UBO to receive storage buffer sizes. + /// Defaults to 30, which is the last valid buffer slot. + uint32_t buffer_size_ubo_index = 30; + + /// The fixed sample mask to combine with fragment shader outputs. + /// Defaults to 0xFFFFFFFF. + uint32_t fixed_sample_mask = 0xFFFFFFFF; +}; + +/// The result produced when generating MSL. +struct Result { + /// Constructor + Result(); + + /// Destructor + ~Result(); + + /// Copy constructor + Result(const Result&); + + /// True if generation was successful. + bool success = false; + + /// The errors generated during code generation, if any. + std::string error; + + /// The generated MSL. + std::string msl = ""; + + /// True if the shader needs a UBO of buffer sizes. + bool needs_storage_buffer_sizes = false; +}; + +/// Generate MSL for a program, according to a set of configuration options. The +/// result will contain the MSL, as well as success status and diagnostic +/// information. +/// @param program the program to translate to MSL +/// @param options the configuration options to use when generating MSL +/// @returns the resulting MSL and supplementary information +Result Generate(const Program* program, const Options& options); + +// TODO(jrprice): Remove this once Dawn is using the new interface. /// Class to generate MSL source class Generator : public Text { public: diff --git a/src/writer/spirv/generator.cc b/src/writer/spirv/generator.cc index 8a1e5980e1..8fae66c860 100644 --- a/src/writer/spirv/generator.cc +++ b/src/writer/spirv/generator.cc @@ -14,12 +14,49 @@ #include "src/writer/spirv/generator.h" +#include "src/transform/spirv.h" #include "src/writer/spirv/binary_writer.h" namespace tint { namespace writer { namespace spirv { +Result::Result() = default; +Result::~Result() = default; +Result::Result(const Result&) = default; + +Result Generate(const Program* program, const Options& options) { + Result result; + + // Run the SPIR-V sanitizer. + transform::Spirv sanitizer; + transform::DataMap transform_input; + transform_input.Add(options.emit_vertex_point_size); + auto output = sanitizer.Run(program, transform_input); + if (!output.program.IsValid()) { + result.success = false; + result.error = output.program.Diagnostics().str(); + return result; + } + + // Generate the SPIR-V code. + auto builder = std::make_unique(&output.program); + auto writer = std::make_unique(); + if (!builder->Build()) { + result.success = false; + result.error = builder->error(); + return result; + } + + writer->WriteHeader(builder->id_bound()); + writer->WriteBuilder(builder.get()); + + result.success = true; + result.spirv = writer->result(); + + return result; +} + Generator::Generator(const Program* program) : builder_(std::make_unique(program)), writer_(std::make_unique()) {} diff --git a/src/writer/spirv/generator.h b/src/writer/spirv/generator.h index 5227a3f499..122f12bc3d 100644 --- a/src/writer/spirv/generator.h +++ b/src/writer/spirv/generator.h @@ -22,6 +22,10 @@ #include "src/writer/writer.h" namespace tint { + +// Forward declarations +class Program; + namespace writer { namespace spirv { @@ -29,6 +33,43 @@ namespace spirv { class Builder; class BinaryWriter; +/// Configuration options used for generating SPIR-V. +struct Options { + /// Set to `true` to generate a PointSize builtin and have it set to 1.0 + /// from all vertex shaders in the module. + bool emit_vertex_point_size = true; +}; + +/// The result produced when generating SPIR-V. +struct Result { + /// Constructor + Result(); + + /// Destructor + ~Result(); + + /// Copy constructor + Result(const Result&); + + /// True if generation was successful. + bool success = false; + + /// The errors generated during code generation, if any. + std::string error; + + /// The generated SPIR-V. + std::vector spirv; +}; + +/// Generate SPIR-V for a program, according to a set of configuration options. +/// The result will contain the SPIR-V, as well as success status and diagnostic +/// information. +/// @param program the program to translate to SPIR-V +/// @param options the configuration options to use when generating SPIR-V +/// @returns the resulting SPIR-V and supplementary information +Result Generate(const Program* program, const Options& options); + +// TODO(jrprice): Remove this once Dawn is using the new interface. /// Class to generate SPIR-V from a Tint program class Generator : public writer::Writer { public: diff --git a/src/writer/wgsl/generator.cc b/src/writer/wgsl/generator.cc index 36eeabc58d..48e1cebcd5 100644 --- a/src/writer/wgsl/generator.cc +++ b/src/writer/wgsl/generator.cc @@ -19,6 +19,22 @@ namespace tint { namespace writer { namespace wgsl { +Result::Result() = default; +Result::~Result() = default; +Result::Result(const Result&) = default; + +Result Generate(const Program* program, const Options&) { + Result result; + + // Generate the WGSL code. + auto impl = std::make_unique(program); + result.success = impl->Generate(); + result.error = impl->error(); + result.wgsl = impl->result(); + + return result; +} + Generator::Generator(const Program* program) : impl_(std::make_unique(program)) {} diff --git a/src/writer/wgsl/generator.h b/src/writer/wgsl/generator.h index 6b0b358905..35f194fbe3 100644 --- a/src/writer/wgsl/generator.h +++ b/src/writer/wgsl/generator.h @@ -21,11 +21,48 @@ #include "src/writer/text.h" namespace tint { + +// Forward declarations +class Program; + namespace writer { namespace wgsl { class GeneratorImpl; +/// Configuration options used for generating WGSL. +struct Options {}; + +/// The result produced when generating WGSL. +struct Result { + /// Constructor + Result(); + + /// Destructor + ~Result(); + + /// Copy constructor + Result(const Result&); + + /// True if generation was successful. + bool success = false; + + /// The errors generated during code generation, if any. + std::string error; + + /// The generated WGSL. + std::string wgsl = ""; +}; + +/// Generate WGSL for a program, according to a set of configuration options. +/// The result will contain the WGSL, as well as success status and diagnostic +/// information. +/// @param program the program to translate to WGSL +/// @param options the configuration options to use when generating WGSL +/// @returns the resulting WGSL and supplementary information +Result Generate(const Program* program, const Options& options); + +// TODO(jrprice): Remove this once Dawn is using the new interface. /// Class to generate WGSL source class Generator : public Text { public: diff --git a/src/writer/writer.h b/src/writer/writer.h index 3e5737cef9..fdbb569ce8 100644 --- a/src/writer/writer.h +++ b/src/writer/writer.h @@ -17,8 +17,6 @@ #include -#include "src/program.h" - namespace tint { namespace writer {