From 3cbbeaf1db680d805ad57b7a23f30d37fc1b495e Mon Sep 17 00:00:00 2001 From: Corentin Wallez Date: Mon, 28 Jun 2021 12:02:17 +0000 Subject: [PATCH] sample: add a --fxc option to validate HLSL with FXC Change-Id: I39d684a71d9f985a1f30b871f08f51bdf50f17a1 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/56064 Kokoro: Kokoro Reviewed-by: Ben Clayton Commit-Queue: Corentin Wallez --- samples/main.cc | 44 +++++++++++++++++-------- src/val/hlsl.cc | 87 +++++++++++++++++++++++++++++++++++++++++++++++-- src/val/val.h | 15 +++++++-- 3 files changed, 126 insertions(+), 20 deletions(-) diff --git a/samples/main.cc b/samples/main.cc index 2d43d3f82e..3ad5aa89a7 100644 --- a/samples/main.cc +++ b/samples/main.cc @@ -65,6 +65,7 @@ struct Options { std::vector transforms; + bool use_fxc = false; std::string dxc_path; std::string xcrun_path; }; @@ -95,8 +96,9 @@ const char kUsage[] = R"(Usage: tint [options] Affects AST dumping, and text-based output languages. --dump-inspector-bindings -- Dump reflection data about bindins to stdout. -h -- This help text - --validate -- Validates generated SPIR-V with spirv-val. - Has no effect on non-SPIRV outputs. + --validate -- Validates the generated shader + --fxc -- Ask to validate HLSL output using FXC instead of DXC. + When specified, automatically enables --validate --dxc -- Path to DXC executable, used to validate HLSL output. When specified, automatically enables --validate --xcrun -- Path to xcrun executable, used to validate MSL output. @@ -404,6 +406,9 @@ bool ParseArgs(const std::vector& args, Options* opts) { opts->dump_inspector_bindings = true; } else if (arg == "--validate") { opts->validate = true; + } else if (arg == "--fxc") { + opts->validate = true; + opts->use_fxc = true; } else if (arg == "--dxc") { ++i; if (i >= args.size()) { @@ -872,20 +877,31 @@ int main(int argc, const char** argv) { #endif #if TINT_BUILD_HLSL_WRITER case Format::kHlsl: { - auto dxc = tint::utils::Command::LookPath( - options.dxc_path.empty() ? "dxc" : options.dxc_path); - if (dxc.Found()) { - auto* w = static_cast(writer.get()); - auto hlsl = w->result(); - auto res = tint::val::Hlsl(dxc.Path(), hlsl, program.get()); - if (res.failed) { - validation_failed = true; - validation_msgs << res.source << std::endl; - validation_msgs << res.output; - } + 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 << "DXC executable not found. Cannot validate"; + validation_msgs << res.source << std::endl; + validation_msgs << res.output; } break; } diff --git a/src/val/hlsl.cc b/src/val/hlsl.cc index 23a67e6f62..c1c3315a05 100644 --- a/src/val/hlsl.cc +++ b/src/val/hlsl.cc @@ -19,12 +19,21 @@ #include "src/utils/io/command.h" #include "src/utils/io/tmpfile.h" +#ifdef _WIN32 +#include +#include +#include + +#include +using Microsoft::WRL::ComPtr; +#endif // _WIN32 + namespace tint { namespace val { -Result Hlsl(const std::string& dxc_path, - const std::string& source, - Program* program) { +Result HlslUsingDXC(const std::string& dxc_path, + const std::string& source, + Program* program) { Result result; auto dxc = utils::Command(dxc_path); @@ -89,5 +98,77 @@ Result Hlsl(const std::string& dxc_path, return result; } +#ifdef _WIN32 +Result HlslUsingFXC(const std::string& source, Program* program) { + Result result; + + // This library leaks if an error happens in this function, but it is ok + // because it is loaded at most once, and the executables using HlslUsingFXC + // are short-lived. + HMODULE fxcLib = LoadLibraryA("d3dcompiler_47.dll"); + if (fxcLib == nullptr) { + result.output = "Couldn't load FXC"; + result.failed = true; + return result; + } + + pD3DCompile d3dCompile = + reinterpret_cast(GetProcAddress(fxcLib, "D3DCompile")); + if (d3dCompile == nullptr) { + result.output = "Couldn't load D3DCompile from FXC"; + result.failed = true; + return result; + } + + 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()); + result.failed = true; + return result; + } + } + } + + FreeLibrary(fxcLib); + + if (!found_an_entrypoint) { + result.output = "No entrypoint found"; + result.failed = true; + return result; + } + + return result; +} +#endif // _WIN32 + } // namespace val } // namespace tint diff --git a/src/val/val.h b/src/val/val.h index 309e44d39f..fd3ae0ac9a 100644 --- a/src/val/val.h +++ b/src/val/val.h @@ -41,9 +41,18 @@ struct Result { /// @param source the generated HLSL source /// @param program the HLSL program /// @return the result of the compile -Result Hlsl(const std::string& dxc_path, - const std::string& source, - Program* program); +Result HlslUsingDXC(const std::string& dxc_path, + const std::string& source, + Program* program); + +#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 +/// @return the result of the compile +Result HlslUsingFXC(const std::string& source, Program* program); +#endif // _WIN32 /// Msl attempts to compile the shader with the Metal Shader Compiler, /// verifying that the shader compiles successfully.