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:
parent
08b0ab9b92
commit
af89c729ed
354
samples/main.cc
354
samples/main.cc
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
134
src/val/hlsl.cc
134
src/val/hlsl.cc
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)) {}
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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)) {}
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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>()) {}
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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)) {}
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -17,8 +17,6 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "src/program.h"
|
|
||||||
|
|
||||||
namespace tint {
|
namespace tint {
|
||||||
namespace writer {
|
namespace writer {
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue