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 <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
diff --git a/samples/main.cc b/samples/main.cc
index 2d43d3f..3ad5aa8 100644
--- a/samples/main.cc
+++ b/samples/main.cc
@@ -65,6 +65,7 @@
std::vector<std::string> transforms;
+ bool use_fxc = false;
std::string dxc_path;
std::string xcrun_path;
};
@@ -95,8 +96,9 @@
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 @@
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 @@
#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<tint::writer::Text*>(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<tint::writer::Text*>(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 23a67e6..c1c3315 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 <d3dcommon.h>
+#include <d3dcompiler.h>
+#include <windows.h>
+
+#include <wrl.h>
+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 @@
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<pD3DCompile>(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<ID3DBlob> compiledShader;
+ ComPtr<ID3DBlob> errors;
+ if (FAILED(d3dCompile(source.c_str(), source.length(), nullptr, nullptr,
+ nullptr, name.c_str(), profile, 0, 0,
+ &compiledShader, &errors))) {
+ result.output = static_cast<char*>(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 309e44d..fd3ae0a 100644
--- a/src/val/val.h
+++ b/src/val/val.h
@@ -41,9 +41,18 @@
/// @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.