writer: Move sanitizers into the backends

Adds a new single-function API for the generators, which applies the
sanitizing transform and performs the generation in one step, and
returns a result object which contains the generated code and success
status/diagnostics.

The new APIs take an `Option` structure to control backend-specific
generation details (e.g. MSL fixed sample mask). The result objects
also provide backend-specific feedback (e.g. whether a UBO of buffer
lengths was generated).

HLSL needs a list of entry points to validate, and it's the HLSL
sanitizer that generates an entry point for programs that do not have
one. This change makes the HLSL generator return the list of
post-sanitize entry points so that the Tint executable can forward
them to the validation code.

Change-Id: I2d5aa27fda95d7c50c5bef41e206aee38f2fd2eb
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/57101
Auto-Submit: James Price <jrprice@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: James Price <jrprice@google.com>
Reviewed-by: Ben Clayton <bclayton@chromium.org>
This commit is contained in:
James Price 2021-07-08 16:00:23 +00:00 committed by Tint LUCI CQ
parent 08b0ab9b92
commit af89c729ed
15 changed files with 551 additions and 268 deletions

View File

@ -589,12 +589,182 @@ std::string Disassemble(const std::vector<uint32_t>& data) {
/// @param program the program /// @param program the program
void PrintWGSL(std::ostream& out, const tint::Program& program) { void PrintWGSL(std::ostream& out, const tint::Program& program) {
#if TINT_BUILD_WGSL_WRITER #if TINT_BUILD_WGSL_WRITER
tint::writer::wgsl::Generator writer(&program); tint::writer::wgsl::Options options;
writer.Generate(); auto result = tint::writer::wgsl::Generate(&program, options);
out << std::endl << writer.result() << std::endl; out << std::endl << result.wgsl << std::endl;
#endif #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 } // namespace
int main(int argc, const char** argv) { int main(int argc, const char** argv) {
@ -729,19 +899,11 @@ int main(int argc, const char** argv) {
} }
switch (options.format) { switch (options.format) {
#if TINT_BUILD_SPV_WRITER
case Format::kSpirv:
case Format::kSpvAsm:
transform_manager.Add<tint::transform::Spirv>();
transform_inputs.Add<tint::transform::Spirv::Config>(true);
break;
#endif // TINT_BUILD_SPV_WRITER
#if TINT_BUILD_MSL_WRITER #if TINT_BUILD_MSL_WRITER
case Format::kMsl: { case Format::kMsl: {
transform_inputs.Add<tint::transform::Renamer::Config>( transform_inputs.Add<tint::transform::Renamer::Config>(
tint::transform::Renamer::Target::kMslKeywords); tint::transform::Renamer::Target::kMslKeywords);
transform_manager.Add<tint::transform::Renamer>(); transform_manager.Add<tint::transform::Renamer>();
transform_manager.Add<tint::transform::Msl>();
break; break;
} }
#endif // TINT_BUILD_MSL_WRITER #endif // TINT_BUILD_MSL_WRITER
@ -750,7 +912,6 @@ int main(int argc, const char** argv) {
transform_inputs.Add<tint::transform::Renamer::Config>( transform_inputs.Add<tint::transform::Renamer::Config>(
tint::transform::Renamer::Target::kHlslKeywords); tint::transform::Renamer::Target::kHlslKeywords);
transform_manager.Add<tint::transform::Renamer>(); transform_manager.Add<tint::transform::Renamer>();
transform_manager.Add<tint::transform::Hlsl>();
break; break;
} }
#endif // TINT_BUILD_HLSL_WRITER #endif // TINT_BUILD_HLSL_WRITER
@ -801,185 +962,28 @@ int main(int argc, const char** argv) {
std::cout << std::string(80, '-') << std::endl; std::cout << std::string(80, '-') << std::endl;
} }
std::unique_ptr<tint::writer::Writer> writer; bool success = false;
switch (options.format) { switch (options.format) {
case Format::kSpirv: case Format::kSpirv:
case Format::kSpvAsm: case Format::kSpvAsm:
#if TINT_BUILD_SPV_WRITER success = GenerateSpirv(program.get(), options);
writer = std::make_unique<tint::writer::spirv::Generator>(program.get());
break; break;
#else
std::cerr << "SPIR-V writer not enabled in tint build" << std::endl;
return 1;
#endif // TINT_BUILD_SPV_WRITER
case Format::kWgsl: case Format::kWgsl:
#if TINT_BUILD_WGSL_WRITER success = GenerateWgsl(program.get(), options);
writer = std::make_unique<tint::writer::wgsl::Generator>(program.get());
break; break;
#else
std::cerr << "WGSL writer not enabled in tint build" << std::endl;
return 1;
#endif // TINT_BUILD_WGSL_WRITER
case Format::kMsl: case Format::kMsl:
#if TINT_BUILD_MSL_WRITER success = GenerateMsl(program.get(), options);
writer = std::make_unique<tint::writer::msl::Generator>(program.get());
break; break;
#else
std::cerr << "MSL writer not enabled in tint build" << std::endl;
return 1;
#endif // TINT_BUILD_MSL_WRITER
case Format::kHlsl: case Format::kHlsl:
#if TINT_BUILD_HLSL_WRITER success = GenerateHlsl(program.get(), options);
writer = std::make_unique<tint::writer::hlsl::Generator>(program.get());
break; break;
#else
std::cerr << "HLSL writer not enabled in tint build" << std::endl;
return 1;
#endif // TINT_BUILD_HLSL_WRITER
default: default:
std::cerr << "Unknown output format specified" << std::endl; std::cerr << "Unknown output format specified" << std::endl;
return 1; return 1;
} }
if (!success) {
if (!writer->Generate()) {
PrintWGSL(std::cerr, *program);
std::cerr << "Failed to generate: " << writer->error() << std::endl;
return 1; 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<tint::writer::spirv::Generator*>(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<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 << 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<tint::writer::Text*>(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<tint::writer::spirv::Generator*>(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<tint::writer::spirv::Generator*>(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<tint::writer::Text*>(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; return 0;
} }

View File

@ -144,11 +144,12 @@ let declaration_order_check_3 : i32 = 1;
// Regenerate the wgsl for the src program. We use this instead of the // Regenerate the wgsl for the src program. We use this instead of the
// original source so that reformatting doesn't impact the final wgsl // original source so that reformatting doesn't impact the final wgsl
// comparison. // comparison.
writer::wgsl::Options options;
std::string src_wgsl; std::string src_wgsl;
{ {
writer::wgsl::Generator src_gen(&src); auto result = writer::wgsl::Generate(&src, options);
ASSERT_TRUE(src_gen.Generate()) << src_gen.error(); ASSERT_TRUE(result.success) << result.error;
src_wgsl = src_gen.result(); src_wgsl = result.wgsl;
// Move the src program to a temporary that'll be dropped, so that the src // 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 // 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 // Print the dst module, check it matches the original source
writer::wgsl::Generator dst_gen(&dst); auto result = writer::wgsl::Generate(&dst, options);
ASSERT_TRUE(dst_gen.Generate()); ASSERT_TRUE(result.success);
auto dst_wgsl = dst_gen.result(); auto dst_wgsl = result.wgsl;
ASSERT_EQ(src_wgsl, dst_wgsl); ASSERT_EQ(src_wgsl, dst_wgsl);
#else // #if TINT_BUILD_WGSL_READER && TINT_BUILD_WGSL_WRITER #else // #if TINT_BUILD_WGSL_READER && TINT_BUILD_WGSL_WRITER

View File

@ -1549,9 +1549,6 @@ TEST_F(InspectorGetEntryPointTest, OverridableConstantReferencedByEntryPoint) {
Inspector& inspector = Build(); Inspector& inspector = Build();
tint::writer::wgsl::Generator writer(program_.get());
writer.Generate();
auto result = inspector.GetEntryPoints(); auto result = inspector.GetEntryPoints();
ASSERT_EQ(1u, result.size()); ASSERT_EQ(1u, result.size());

View File

@ -82,12 +82,13 @@ class TransformTestBase : public BASE {
return diag::Formatter(style).format(output.program.Diagnostics()); return diag::Formatter(style).format(output.program.Diagnostics());
} }
writer::wgsl::Generator generator(&output.program); writer::wgsl::Options options;
if (!generator.Generate()) { auto result = writer::wgsl::Generate(&output.program, options);
return "WGSL writer failed:\n" + generator.error(); if (!result.success) {
return "WGSL writer failed:\n" + result.error;
} }
auto res = generator.result(); auto res = result.wgsl;
if (res.empty()) { if (res.empty()) {
return res; return res;
} }

View File

@ -14,8 +14,6 @@
#include "src/val/val.h" #include "src/val/val.h"
#include "src/ast/module.h"
#include "src/program.h"
#include "src/utils/io/command.h" #include "src/utils/io/command.h"
#include "src/utils/io/tmpfile.h" #include "src/utils/io/tmpfile.h"
@ -33,7 +31,7 @@ namespace val {
Result HlslUsingDXC(const std::string& dxc_path, Result HlslUsingDXC(const std::string& dxc_path,
const std::string& source, const std::string& source,
Program* program) { const EntryPointList& entry_points) {
Result result; Result result;
auto dxc = utils::Command(dxc_path); auto dxc = utils::Command(dxc_path);
@ -48,48 +46,42 @@ Result HlslUsingDXC(const std::string& dxc_path,
utils::TmpFile file; utils::TmpFile file;
file << source; file << source;
bool found_an_entrypoint = false; for (auto ep : entry_points) {
for (auto* func : program->AST().Functions()) { const char* profile = "";
if (func->IsEntryPoint()) {
found_an_entrypoint = true;
const char* profile = ""; switch (ep.second) {
case ast::PipelineStage::kNone:
switch (func->pipeline_stage()) { result.output = "Invalid PipelineStage";
case ast::PipelineStage::kNone: result.failed = true;
result.output = "Invalid PipelineStage"; return result;
result.failed = true; case ast::PipelineStage::kVertex:
return result; profile = "-T vs_6_0";
case ast::PipelineStage::kVertex: break;
profile = "-T vs_6_0"; case ast::PipelineStage::kFragment:
break; profile = "-T ps_6_0";
case ast::PipelineStage::kFragment: break;
profile = "-T ps_6_0"; case ast::PipelineStage::kCompute:
break; profile = "-T cs_6_0";
case ast::PipelineStage::kCompute: break;
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);
} }
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.output = "No entrypoint found";
result.failed = true; result.failed = true;
return result; return result;
@ -99,7 +91,8 @@ Result HlslUsingDXC(const std::string& dxc_path,
} }
#ifdef _WIN32 #ifdef _WIN32
Result HlslUsingFXC(const std::string& source, Program* program) { Result HlslUsingFXC(const std::string& source,
const EntryPointList& entry_points) {
Result result; Result result;
// This library leaks if an error happens in this function, but it is ok // 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; result.source = source;
bool found_an_entrypoint = false; for (auto ep : entry_points) {
for (auto* func : program->AST().Functions()) { const char* profile = "";
if (func->IsEntryPoint()) { switch (ep.second) {
found_an_entrypoint = true; case ast::PipelineStage::kNone:
result.output = "Invalid PipelineStage";
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; result.failed = true;
return result; 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<ID3DBlob> compiledShader;
ComPtr<ID3DBlob> 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<char*>(errors->GetBufferPointer());
result.failed = true;
return result;
} }
} }
FreeLibrary(fxcLib); FreeLibrary(fxcLib);
if (!found_an_entrypoint) { if (entry_points.empty()) {
result.output = "No entrypoint found"; result.output = "No entrypoint found";
result.failed = true; result.failed = true;
return result; return result;

View File

@ -16,6 +16,10 @@
#define SRC_VAL_VAL_H_ #define SRC_VAL_VAL_H_
#include <string> #include <string>
#include <utility>
#include <vector>
#include "src/ast/pipeline_stage.h"
// Forward declarations // Forward declarations
namespace tint { namespace tint {
@ -25,6 +29,8 @@ class Program;
namespace tint { namespace tint {
namespace val { namespace val {
using EntryPointList = std::vector<std::pair<std::string, ast::PipelineStage>>;
/// The return structure of Validate() /// The return structure of Validate()
struct Result { struct Result {
/// True if validation passed /// True if validation passed
@ -39,19 +45,20 @@ struct Result {
/// compiles successfully. /// compiles successfully.
/// @param dxc_path path to DXC /// @param dxc_path path to DXC
/// @param source the generated HLSL source /// @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 /// @return the result of the compile
Result HlslUsingDXC(const std::string& dxc_path, Result HlslUsingDXC(const std::string& dxc_path,
const std::string& source, const std::string& source,
Program* program); const EntryPointList& entry_points);
#ifdef _WIN32 #ifdef _WIN32
/// Hlsl attempts to compile the shader with FXC, verifying that the shader /// Hlsl attempts to compile the shader with FXC, verifying that the shader
/// compiles successfully. /// compiles successfully.
/// @param source the generated HLSL source /// @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 /// @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 #endif // _WIN32
/// Msl attempts to compile the shader with the Metal Shader Compiler, /// Msl attempts to compile the shader with the Metal Shader Compiler,

View File

@ -14,12 +14,46 @@
#include "src/writer/hlsl/generator.h" #include "src/writer/hlsl/generator.h"
#include "src/transform/hlsl.h"
#include "src/writer/hlsl/generator_impl.h" #include "src/writer/hlsl/generator_impl.h"
namespace tint { namespace tint {
namespace writer { namespace writer {
namespace hlsl { 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<GeneratorImpl>(&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) Generator::Generator(const Program* program)
: impl_(std::make_unique<GeneratorImpl>(program)) {} : impl_(std::make_unique<GeneratorImpl>(program)) {}

View File

@ -17,16 +17,59 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include <utility>
#include <vector>
#include "src/ast/pipeline_stage.h"
#include "src/writer/text.h" #include "src/writer/text.h"
namespace tint { namespace tint {
// Forward declarations
class Program;
namespace writer { namespace writer {
namespace hlsl { namespace hlsl {
// Forward declarations // Forward declarations
class GeneratorImpl; 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<std::pair<std::string, ast::PipelineStage>> 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 to generate HLSL source
class Generator : public Text { class Generator : public Text {
public: public:

View File

@ -13,12 +13,45 @@
// limitations under the License. // limitations under the License.
#include "src/writer/msl/generator.h" #include "src/writer/msl/generator.h"
#include "src/transform/msl.h"
#include "src/writer/msl/generator_impl.h" #include "src/writer/msl/generator_impl.h"
namespace tint { namespace tint {
namespace writer { namespace writer {
namespace msl { 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<transform::Msl::Config>(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<transform::Msl::Result>();
result.needs_storage_buffer_sizes =
transform_output->needs_storage_buffer_sizes;
// Generate the MSL code.
auto impl = std::make_unique<GeneratorImpl>(&output.program);
result.success = impl->Generate();
result.error = impl->error();
result.msl = impl->result();
return result;
}
Generator::Generator(const Program* program) Generator::Generator(const Program* program)
: impl_(std::make_unique<GeneratorImpl>(program)) {} : impl_(std::make_unique<GeneratorImpl>(program)) {}

View File

@ -21,11 +21,59 @@
#include "src/writer/text.h" #include "src/writer/text.h"
namespace tint { namespace tint {
// Forward declarations
class Program;
namespace writer { namespace writer {
namespace msl { namespace msl {
class GeneratorImpl; 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 to generate MSL source
class Generator : public Text { class Generator : public Text {
public: public:

View File

@ -14,12 +14,49 @@
#include "src/writer/spirv/generator.h" #include "src/writer/spirv/generator.h"
#include "src/transform/spirv.h"
#include "src/writer/spirv/binary_writer.h" #include "src/writer/spirv/binary_writer.h"
namespace tint { namespace tint {
namespace writer { namespace writer {
namespace spirv { 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<transform::Spirv::Config>(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<Builder>(&output.program);
auto writer = std::make_unique<BinaryWriter>();
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) Generator::Generator(const Program* program)
: builder_(std::make_unique<Builder>(program)), : builder_(std::make_unique<Builder>(program)),
writer_(std::make_unique<BinaryWriter>()) {} writer_(std::make_unique<BinaryWriter>()) {}

View File

@ -22,6 +22,10 @@
#include "src/writer/writer.h" #include "src/writer/writer.h"
namespace tint { namespace tint {
// Forward declarations
class Program;
namespace writer { namespace writer {
namespace spirv { namespace spirv {
@ -29,6 +33,43 @@ namespace spirv {
class Builder; class Builder;
class BinaryWriter; 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<uint32_t> 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 to generate SPIR-V from a Tint program
class Generator : public writer::Writer { class Generator : public writer::Writer {
public: public:

View File

@ -19,6 +19,22 @@ namespace tint {
namespace writer { namespace writer {
namespace wgsl { 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<GeneratorImpl>(program);
result.success = impl->Generate();
result.error = impl->error();
result.wgsl = impl->result();
return result;
}
Generator::Generator(const Program* program) Generator::Generator(const Program* program)
: impl_(std::make_unique<GeneratorImpl>(program)) {} : impl_(std::make_unique<GeneratorImpl>(program)) {}

View File

@ -21,11 +21,48 @@
#include "src/writer/text.h" #include "src/writer/text.h"
namespace tint { namespace tint {
// Forward declarations
class Program;
namespace writer { namespace writer {
namespace wgsl { namespace wgsl {
class GeneratorImpl; 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 to generate WGSL source
class Generator : public Text { class Generator : public Text {
public: public:

View File

@ -17,8 +17,6 @@
#include <string> #include <string>
#include "src/program.h"
namespace tint { namespace tint {
namespace writer { namespace writer {