|  | // Copyright 2020 The Tint Authors. | 
|  | // | 
|  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | // you may not use this file except in compliance with the License. | 
|  | // You may obtain a copy of the License at | 
|  | // | 
|  | //     http://www.apache.org/licenses/LICENSE-2.0 | 
|  | // | 
|  | // Unless required by applicable law or agreed to in writing, software | 
|  | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | // See the License for the specific language governing permissions and | 
|  | // limitations under the License. | 
|  |  | 
|  | #include <cstdio> | 
|  | #include <fstream> | 
|  | #include <iostream> | 
|  | #include <memory> | 
|  | #include <sstream> | 
|  | #include <string> | 
|  | #include <vector> | 
|  |  | 
|  | #if TINT_BUILD_GLSL_WRITER | 
|  | #include "StandAlone/ResourceLimits.h" | 
|  | #include "glslang/Public/ShaderLang.h" | 
|  | #endif | 
|  |  | 
|  | #if TINT_BUILD_SPV_READER | 
|  | #include "spirv-tools/libspirv.hpp" | 
|  | #endif  // TINT_BUILD_SPV_READER | 
|  |  | 
|  | #include "src/utils/io/command.h" | 
|  | #include "src/val/val.h" | 
|  | #include "tint/tint.h" | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | [[noreturn]] void TintInternalCompilerErrorReporter( | 
|  | const tint::diag::List& diagnostics) { | 
|  | auto printer = tint::diag::Printer::create(stderr, true); | 
|  | tint::diag::Formatter{}.format(diagnostics, printer.get()); | 
|  | tint::diag::Style bold_red{tint::diag::Color::kRed, true}; | 
|  | constexpr const char* please_file_bug = R"( | 
|  | ******************************************************************** | 
|  | *  The tint shader compiler has encountered an unexpected error.   * | 
|  | *                                                                  * | 
|  | *  Please help us fix this issue by submitting a bug report at     * | 
|  | *  crbug.com/tint with the source program that triggered the bug.  * | 
|  | ******************************************************************** | 
|  | )"; | 
|  | printer->write(please_file_bug, bold_red); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | enum class Format { | 
|  | kNone = -1, | 
|  | kSpirv, | 
|  | kSpvAsm, | 
|  | kWgsl, | 
|  | kMsl, | 
|  | kHlsl, | 
|  | kGlsl, | 
|  | }; | 
|  |  | 
|  | struct Options { | 
|  | bool show_help = false; | 
|  |  | 
|  | std::string input_filename; | 
|  | std::string output_file = "-";  // Default to stdout | 
|  |  | 
|  | bool parse_only = false; | 
|  | bool disable_workgroup_init = false; | 
|  | bool validate = false; | 
|  | bool demangle = false; | 
|  | bool dump_inspector_bindings = false; | 
|  |  | 
|  | Format format = Format::kNone; | 
|  |  | 
|  | bool emit_single_entry_point = false; | 
|  | std::string ep_name; | 
|  |  | 
|  | std::vector<std::string> transforms; | 
|  |  | 
|  | bool use_fxc = false; | 
|  | std::string dxc_path; | 
|  | std::string xcrun_path; | 
|  | }; | 
|  |  | 
|  | const char kUsage[] = R"(Usage: tint [options] <input-file> | 
|  |  | 
|  | options: | 
|  | --format <spirv|spvasm|wgsl|msl|hlsl>  -- Output format. | 
|  | If not provided, will be inferred from output | 
|  | filename extension: | 
|  | .spvasm -> spvasm | 
|  | .spv    -> spirv | 
|  | .wgsl   -> wgsl | 
|  | .metal  -> msl | 
|  | .hlsl   -> hlsl | 
|  | If none matches, then default to SPIR-V assembly. | 
|  | -ep <name>                -- Output single entry point | 
|  | --output-file <name>      -- Output file name.  Use "-" for standard output | 
|  | -o <name>                 -- Output file name.  Use "-" for standard output | 
|  | --transform <name list>   -- Runs transforms, name list is comma separated | 
|  | Available transforms: | 
|  | first_index_offset | 
|  | fold_trivial_single_use_lets | 
|  | renamer | 
|  | robustness | 
|  | --parse-only              -- Stop after parsing the input | 
|  | --dump-ast                -- Dump the generated AST to stdout | 
|  | --disable-workgroup-init  -- Disable workgroup memory zero initialization. | 
|  | --demangle                -- Preserve original source names. Demangle them. | 
|  | Affects AST dumping, and text-based output languages. | 
|  | --dump-inspector-bindings -- Dump reflection data about bindins to stdout. | 
|  | -h                        -- This help text | 
|  | --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. | 
|  | When specified, automatically enables --validate)"; | 
|  |  | 
|  | Format parse_format(const std::string& fmt) { | 
|  | (void)fmt; | 
|  |  | 
|  | #if TINT_BUILD_SPV_WRITER | 
|  | if (fmt == "spirv") | 
|  | return Format::kSpirv; | 
|  | if (fmt == "spvasm") | 
|  | return Format::kSpvAsm; | 
|  | #endif  // TINT_BUILD_SPV_WRITER | 
|  |  | 
|  | #if TINT_BUILD_WGSL_WRITER | 
|  | if (fmt == "wgsl") | 
|  | return Format::kWgsl; | 
|  | #endif  // TINT_BUILD_WGSL_WRITER | 
|  |  | 
|  | #if TINT_BUILD_MSL_WRITER | 
|  | if (fmt == "msl") | 
|  | return Format::kMsl; | 
|  | #endif  // TINT_BUILD_MSL_WRITER | 
|  |  | 
|  | #if TINT_BUILD_HLSL_WRITER | 
|  | if (fmt == "hlsl") | 
|  | return Format::kHlsl; | 
|  | #endif  // TINT_BUILD_HLSL_WRITER | 
|  |  | 
|  | #if TINT_BUILD_GLSL_WRITER | 
|  | if (fmt == "glsl") | 
|  | return Format::kGlsl; | 
|  | #endif  // TINT_BUILD_GLSL_WRITER | 
|  |  | 
|  | return Format::kNone; | 
|  | } | 
|  |  | 
|  | #if TINT_BUILD_SPV_WRITER || TINT_BUILD_WGSL_WRITER || \ | 
|  | TINT_BUILD_MSL_WRITER || TINT_BUILD_HLSL_WRITER | 
|  | /// @param input input string | 
|  | /// @param suffix potential suffix string | 
|  | /// @returns true if input ends with the given suffix. | 
|  | bool ends_with(const std::string& input, const std::string& suffix) { | 
|  | const auto input_len = input.size(); | 
|  | const auto suffix_len = suffix.size(); | 
|  | // Avoid integer overflow. | 
|  | return (input_len >= suffix_len) && | 
|  | (input_len - suffix_len == input.rfind(suffix)); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | /// @param filename the filename to inspect | 
|  | /// @returns the inferred format for the filename suffix | 
|  | Format infer_format(const std::string& filename) { | 
|  | (void)filename; | 
|  |  | 
|  | #if TINT_BUILD_SPV_WRITER | 
|  | if (ends_with(filename, ".spv")) { | 
|  | return Format::kSpirv; | 
|  | } | 
|  | if (ends_with(filename, ".spvasm")) { | 
|  | return Format::kSpvAsm; | 
|  | } | 
|  | #endif  // TINT_BUILD_SPV_WRITER | 
|  |  | 
|  | #if TINT_BUILD_WGSL_WRITER | 
|  | if (ends_with(filename, ".wgsl")) { | 
|  | return Format::kWgsl; | 
|  | } | 
|  | #endif  // TINT_BUILD_WGSL_WRITER | 
|  |  | 
|  | #if TINT_BUILD_MSL_WRITER | 
|  | if (ends_with(filename, ".metal")) { | 
|  | return Format::kMsl; | 
|  | } | 
|  | #endif  // TINT_BUILD_MSL_WRITER | 
|  |  | 
|  | #if TINT_BUILD_HLSL_WRITER | 
|  | if (ends_with(filename, ".hlsl")) { | 
|  | return Format::kHlsl; | 
|  | } | 
|  | #endif  // TINT_BUILD_HLSL_WRITER | 
|  |  | 
|  | return Format::kNone; | 
|  | } | 
|  |  | 
|  | std::vector<std::string> split_transform_names(std::string list) { | 
|  | std::vector<std::string> res; | 
|  |  | 
|  | std::stringstream str(list); | 
|  | while (str.good()) { | 
|  | std::string substr; | 
|  | getline(str, substr, ','); | 
|  | res.push_back(substr); | 
|  | } | 
|  | return res; | 
|  | } | 
|  |  | 
|  | std::string TextureDimensionToString( | 
|  | tint::inspector::ResourceBinding::TextureDimension dim) { | 
|  | switch (dim) { | 
|  | case tint::inspector::ResourceBinding::TextureDimension::kNone: | 
|  | return "None"; | 
|  | case tint::inspector::ResourceBinding::TextureDimension::k1d: | 
|  | return "1d"; | 
|  | case tint::inspector::ResourceBinding::TextureDimension::k2d: | 
|  | return "2d"; | 
|  | case tint::inspector::ResourceBinding::TextureDimension::k2dArray: | 
|  | return "2dArray"; | 
|  | case tint::inspector::ResourceBinding::TextureDimension::k3d: | 
|  | return "3d"; | 
|  | case tint::inspector::ResourceBinding::TextureDimension::kCube: | 
|  | return "Cube"; | 
|  | case tint::inspector::ResourceBinding::TextureDimension::kCubeArray: | 
|  | return "CubeArray"; | 
|  | } | 
|  |  | 
|  | return "Unknown"; | 
|  | } | 
|  |  | 
|  | std::string SampledKindToString( | 
|  | tint::inspector::ResourceBinding::SampledKind kind) { | 
|  | switch (kind) { | 
|  | case tint::inspector::ResourceBinding::SampledKind::kFloat: | 
|  | return "Float"; | 
|  | case tint::inspector::ResourceBinding::SampledKind::kUInt: | 
|  | return "UInt"; | 
|  | case tint::inspector::ResourceBinding::SampledKind::kSInt: | 
|  | return "SInt"; | 
|  | case tint::inspector::ResourceBinding::SampledKind::kUnknown: | 
|  | break; | 
|  | } | 
|  |  | 
|  | return "Unknown"; | 
|  | } | 
|  |  | 
|  | std::string ImageFormatToString( | 
|  | tint::inspector::ResourceBinding::ImageFormat format) { | 
|  | switch (format) { | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kR8Unorm: | 
|  | return "R8Unorm"; | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kR8Snorm: | 
|  | return "R8Snorm"; | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kR8Uint: | 
|  | return "R8Uint"; | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kR8Sint: | 
|  | return "R8Sint"; | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kR16Uint: | 
|  | return "R16Uint"; | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kR16Sint: | 
|  | return "R16Sint"; | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kR16Float: | 
|  | return "R16Float"; | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kRg8Unorm: | 
|  | return "Rg8Unorm"; | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kRg8Snorm: | 
|  | return "Rg8Snorm"; | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kRg8Uint: | 
|  | return "Rg8Uint"; | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kRg8Sint: | 
|  | return "Rg8Sint"; | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kR32Uint: | 
|  | return "R32Uint"; | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kR32Sint: | 
|  | return "R32Sint"; | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kR32Float: | 
|  | return "R32Float"; | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kRg16Uint: | 
|  | return "Rg16Uint"; | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kRg16Sint: | 
|  | return "Rg16Sint"; | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kRg16Float: | 
|  | return "Rg16Float"; | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kRgba8Unorm: | 
|  | return "Rgba8Unorm"; | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kRgba8UnormSrgb: | 
|  | return "Rgba8UnormSrgb"; | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kRgba8Snorm: | 
|  | return "Rgba8Snorm"; | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kRgba8Uint: | 
|  | return "Rgba8Uint"; | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kRgba8Sint: | 
|  | return "Rgba8Sint"; | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kBgra8Unorm: | 
|  | return "Bgra8Unorm"; | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kBgra8UnormSrgb: | 
|  | return "Bgra8UnormSrgb"; | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kRgb10A2Unorm: | 
|  | return "Rgb10A2Unorm"; | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kRg11B10Float: | 
|  | return "Rg11B10Float"; | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kRg32Uint: | 
|  | return "Rg32Uint"; | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kRg32Sint: | 
|  | return "Rg32Sint"; | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kRg32Float: | 
|  | return "Rg32Float"; | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kRgba16Uint: | 
|  | return "Rgba16Uint"; | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kRgba16Sint: | 
|  | return "Rgba16Sint"; | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kRgba16Float: | 
|  | return "Rgba16Float"; | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kRgba32Uint: | 
|  | return "Rgba32Uint"; | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kRgba32Sint: | 
|  | return "Rgba32Sint"; | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kRgba32Float: | 
|  | return "Rgba32Float"; | 
|  | case tint::inspector::ResourceBinding::ImageFormat::kNone: | 
|  | return "None"; | 
|  | } | 
|  | return "Unknown"; | 
|  | } | 
|  |  | 
|  | std::string ResourceTypeToString( | 
|  | tint::inspector::ResourceBinding::ResourceType type) { | 
|  | switch (type) { | 
|  | case tint::inspector::ResourceBinding::ResourceType::kUniformBuffer: | 
|  | return "UniformBuffer"; | 
|  | case tint::inspector::ResourceBinding::ResourceType::kStorageBuffer: | 
|  | return "StorageBuffer"; | 
|  | case tint::inspector::ResourceBinding::ResourceType::kReadOnlyStorageBuffer: | 
|  | return "ReadOnlyStorageBuffer"; | 
|  | case tint::inspector::ResourceBinding::ResourceType::kSampler: | 
|  | return "Sampler"; | 
|  | case tint::inspector::ResourceBinding::ResourceType::kComparisonSampler: | 
|  | return "ComparisonSampler"; | 
|  | case tint::inspector::ResourceBinding::ResourceType::kSampledTexture: | 
|  | return "SampledTexture"; | 
|  | case tint::inspector::ResourceBinding::ResourceType::kMultisampledTexture: | 
|  | return "MultisampledTexture"; | 
|  | case tint::inspector::ResourceBinding::ResourceType:: | 
|  | kWriteOnlyStorageTexture: | 
|  | return "WriteOnlyStorageTexture"; | 
|  | case tint::inspector::ResourceBinding::ResourceType::kDepthTexture: | 
|  | return "DepthTexture"; | 
|  | case tint::inspector::ResourceBinding::ResourceType:: | 
|  | kDepthMultisampledTexture: | 
|  | return "DepthMultisampledTexture"; | 
|  | case tint::inspector::ResourceBinding::ResourceType::kExternalTexture: | 
|  | return "ExternalTexture"; | 
|  | } | 
|  |  | 
|  | return "Unknown"; | 
|  | } | 
|  |  | 
|  | bool ParseArgs(const std::vector<std::string>& args, Options* opts) { | 
|  | for (size_t i = 1; i < args.size(); ++i) { | 
|  | const std::string& arg = args[i]; | 
|  | if (arg == "--format") { | 
|  | ++i; | 
|  | if (i >= args.size()) { | 
|  | std::cerr << "Missing value for --format argument." << std::endl; | 
|  | return false; | 
|  | } | 
|  | opts->format = parse_format(args[i]); | 
|  |  | 
|  | if (opts->format == Format::kNone) { | 
|  | std::cerr << "Unknown output format: " << args[i] << std::endl; | 
|  | return false; | 
|  | } | 
|  | } else if (arg == "-ep") { | 
|  | if (i + 1 >= args.size()) { | 
|  | std::cerr << "Missing value for -ep" << std::endl; | 
|  | return false; | 
|  | } | 
|  | i++; | 
|  | opts->ep_name = args[i]; | 
|  | opts->emit_single_entry_point = true; | 
|  |  | 
|  | } else if (arg == "-o" || arg == "--output-name") { | 
|  | ++i; | 
|  | if (i >= args.size()) { | 
|  | std::cerr << "Missing value for " << arg << std::endl; | 
|  | return false; | 
|  | } | 
|  | opts->output_file = args[i]; | 
|  |  | 
|  | } else if (arg == "-h" || arg == "--help") { | 
|  | opts->show_help = true; | 
|  | } else if (arg == "--transform") { | 
|  | ++i; | 
|  | if (i >= args.size()) { | 
|  | std::cerr << "Missing value for " << arg << std::endl; | 
|  | return false; | 
|  | } | 
|  | opts->transforms = split_transform_names(args[i]); | 
|  | } else if (arg == "--parse-only") { | 
|  | opts->parse_only = true; | 
|  | } else if (arg == "--disable-workgroup-init") { | 
|  | opts->disable_workgroup_init = true; | 
|  | } else if (arg == "--demangle") { | 
|  | opts->demangle = true; | 
|  | } else if (arg == "--dump-inspector-bindings") { | 
|  | 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()) { | 
|  | std::cerr << "Missing value for " << arg << std::endl; | 
|  | return false; | 
|  | } | 
|  | opts->dxc_path = args[i]; | 
|  | opts->validate = true; | 
|  | } else if (arg == "--xcrun") { | 
|  | ++i; | 
|  | if (i >= args.size()) { | 
|  | std::cerr << "Missing value for " << arg << std::endl; | 
|  | return false; | 
|  | } | 
|  | opts->xcrun_path = args[i]; | 
|  | opts->validate = true; | 
|  | } else if (!arg.empty()) { | 
|  | if (arg[0] == '-') { | 
|  | std::cerr << "Unrecognized option: " << arg << std::endl; | 
|  | return false; | 
|  | } | 
|  | if (!opts->input_filename.empty()) { | 
|  | std::cerr << "More than one input file specified: '" | 
|  | << opts->input_filename << "' and '" << arg << "'" | 
|  | << std::endl; | 
|  | return false; | 
|  | } | 
|  | opts->input_filename = arg; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /// Copies the content from the file named `input_file` to `buffer`, | 
|  | /// assuming each element in the file is of type `T`.  If any error occurs, | 
|  | /// writes error messages to the standard error stream and returns false. | 
|  | /// Assumes the size of a `T` object is divisible by its required alignment. | 
|  | /// @returns true if we successfully read the file. | 
|  | template <typename T> | 
|  | bool ReadFile(const std::string& input_file, std::vector<T>* buffer) { | 
|  | if (!buffer) { | 
|  | std::cerr << "The buffer pointer was null" << std::endl; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | FILE* file = nullptr; | 
|  | #if defined(_MSC_VER) | 
|  | fopen_s(&file, input_file.c_str(), "rb"); | 
|  | #else | 
|  | file = fopen(input_file.c_str(), "rb"); | 
|  | #endif | 
|  | if (!file) { | 
|  | std::cerr << "Failed to open " << input_file << std::endl; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | fseek(file, 0, SEEK_END); | 
|  | uint64_t tell_file_size = static_cast<uint64_t>(ftell(file)); | 
|  | if (tell_file_size <= 0) { | 
|  | std::cerr << "Input file of incorrect size: " << input_file << std::endl; | 
|  | fclose(file); | 
|  | return {}; | 
|  | } | 
|  | const auto file_size = static_cast<size_t>(tell_file_size); | 
|  | if (0 != (file_size % sizeof(T))) { | 
|  | std::cerr << "File " << input_file | 
|  | << " does not contain an integral number of objects: " | 
|  | << file_size << " bytes in the file, require " << sizeof(T) | 
|  | << " bytes per object" << std::endl; | 
|  | fclose(file); | 
|  | return false; | 
|  | } | 
|  | fseek(file, 0, SEEK_SET); | 
|  |  | 
|  | buffer->clear(); | 
|  | buffer->resize(file_size / sizeof(T)); | 
|  |  | 
|  | size_t bytes_read = fread(buffer->data(), 1, file_size, file); | 
|  | fclose(file); | 
|  | if (bytes_read != file_size) { | 
|  | std::cerr << "Failed to read " << input_file << std::endl; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /// Writes the given `buffer` into the file named as `output_file` using the | 
|  | /// given `mode`.  If `output_file` is empty or "-", writes to standard | 
|  | /// output. If any error occurs, returns false and outputs error message to | 
|  | /// standard error. The ContainerT type must have data() and size() methods, | 
|  | /// like `std::string` and `std::vector` do. | 
|  | /// @returns true on success | 
|  | template <typename ContainerT> | 
|  | bool WriteFile(const std::string& output_file, | 
|  | const std::string mode, | 
|  | const ContainerT& buffer) { | 
|  | const bool use_stdout = output_file.empty() || output_file == "-"; | 
|  | FILE* file = stdout; | 
|  |  | 
|  | if (!use_stdout) { | 
|  | #if defined(_MSC_VER) | 
|  | fopen_s(&file, output_file.c_str(), mode.c_str()); | 
|  | #else | 
|  | file = fopen(output_file.c_str(), mode.c_str()); | 
|  | #endif | 
|  | if (!file) { | 
|  | std::cerr << "Could not open file " << output_file << " for writing" | 
|  | << std::endl; | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | size_t written = | 
|  | fwrite(buffer.data(), sizeof(typename ContainerT::value_type), | 
|  | buffer.size(), file); | 
|  | if (buffer.size() != written) { | 
|  | if (use_stdout) { | 
|  | std::cerr << "Could not write all output to standard output" << std::endl; | 
|  | } else { | 
|  | std::cerr << "Could not write to file " << output_file << std::endl; | 
|  | fclose(file); | 
|  | } | 
|  | return false; | 
|  | } | 
|  | if (!use_stdout) { | 
|  | fclose(file); | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | #if TINT_BUILD_SPV_WRITER | 
|  | std::string Disassemble(const std::vector<uint32_t>& data) { | 
|  | std::string spv_errors; | 
|  | spv_target_env target_env = SPV_ENV_UNIVERSAL_1_0; | 
|  |  | 
|  | auto msg_consumer = [&spv_errors](spv_message_level_t level, const char*, | 
|  | const spv_position_t& position, | 
|  | const char* message) { | 
|  | switch (level) { | 
|  | case SPV_MSG_FATAL: | 
|  | case SPV_MSG_INTERNAL_ERROR: | 
|  | case SPV_MSG_ERROR: | 
|  | spv_errors += "error: line " + std::to_string(position.index) + ": " + | 
|  | message + "\n"; | 
|  | break; | 
|  | case SPV_MSG_WARNING: | 
|  | spv_errors += "warning: line " + std::to_string(position.index) + ": " + | 
|  | message + "\n"; | 
|  | break; | 
|  | case SPV_MSG_INFO: | 
|  | spv_errors += "info: line " + std::to_string(position.index) + ": " + | 
|  | message + "\n"; | 
|  | break; | 
|  | case SPV_MSG_DEBUG: | 
|  | break; | 
|  | } | 
|  | }; | 
|  |  | 
|  | spvtools::SpirvTools tools(target_env); | 
|  | tools.SetMessageConsumer(msg_consumer); | 
|  |  | 
|  | std::string result; | 
|  | if (!tools.Disassemble(data, &result, | 
|  | SPV_BINARY_TO_TEXT_OPTION_INDENT | | 
|  | SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES)) { | 
|  | std::cerr << spv_errors << std::endl; | 
|  | } | 
|  | return result; | 
|  | } | 
|  | #endif  // TINT_BUILD_SPV_WRITER | 
|  |  | 
|  | /// PrintWGSL writes the WGSL of the program to the provided ostream, if the | 
|  | /// WGSL writer is enabled, otherwise it does nothing. | 
|  | /// @param out the output stream to write the WGSL to | 
|  | /// @param program the program | 
|  | void PrintWGSL(std::ostream& out, const tint::Program& program) { | 
|  | #if TINT_BUILD_WGSL_WRITER | 
|  | tint::writer::wgsl::Options options; | 
|  | auto result = tint::writer::wgsl::Generate(&program, options); | 
|  | out << std::endl << result.wgsl << std::endl; | 
|  | #else | 
|  | (void)out; | 
|  | (void)program; | 
|  | #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; | 
|  | gen_options.disable_workgroup_init = options.disable_workgroup_init; | 
|  | 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 | 
|  | (void)program; | 
|  | (void)options; | 
|  | 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 | 
|  | (void)program; | 
|  | (void)options; | 
|  | 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 | 
|  | const tint::Program* input_program = program; | 
|  |  | 
|  | // Remap resource numbers to a flat namespace. | 
|  | // TODO(crbug.com/tint/1101): Make this more robust for multiple entry points. | 
|  | using BindingPoint = tint::transform::BindingPoint; | 
|  | tint::transform::BindingRemapper::BindingPoints binding_points; | 
|  | uint32_t next_buffer_idx = 0; | 
|  | uint32_t next_sampler_idx = 0; | 
|  | uint32_t next_texture_idx = 0; | 
|  |  | 
|  | tint::inspector::Inspector inspector(program); | 
|  | auto entry_points = inspector.GetEntryPoints(); | 
|  | for (auto& entry_point : entry_points) { | 
|  | auto bindings = inspector.GetResourceBindings(entry_point.name); | 
|  | for (auto& binding : bindings) { | 
|  | BindingPoint src = {binding.bind_group, binding.binding}; | 
|  | if (binding_points.count(src)) { | 
|  | continue; | 
|  | } | 
|  | switch (binding.resource_type) { | 
|  | case tint::inspector::ResourceBinding::ResourceType::kUniformBuffer: | 
|  | case tint::inspector::ResourceBinding::ResourceType::kStorageBuffer: | 
|  | case tint::inspector::ResourceBinding::ResourceType:: | 
|  | kReadOnlyStorageBuffer: | 
|  | binding_points.emplace(src, BindingPoint{0, next_buffer_idx++}); | 
|  | break; | 
|  | case tint::inspector::ResourceBinding::ResourceType::kSampler: | 
|  | case tint::inspector::ResourceBinding::ResourceType::kComparisonSampler: | 
|  | binding_points.emplace(src, BindingPoint{0, next_sampler_idx++}); | 
|  | break; | 
|  | case tint::inspector::ResourceBinding::ResourceType::kSampledTexture: | 
|  | case tint::inspector::ResourceBinding::ResourceType:: | 
|  | kMultisampledTexture: | 
|  | case tint::inspector::ResourceBinding::ResourceType:: | 
|  | kWriteOnlyStorageTexture: | 
|  | case tint::inspector::ResourceBinding::ResourceType::kDepthTexture: | 
|  | case tint::inspector::ResourceBinding::ResourceType:: | 
|  | kDepthMultisampledTexture: | 
|  | case tint::inspector::ResourceBinding::ResourceType::kExternalTexture: | 
|  | binding_points.emplace(src, BindingPoint{0, next_texture_idx++}); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Run the binding remapper transform. | 
|  | tint::transform::Output transform_output; | 
|  | if (!binding_points.empty()) { | 
|  | tint::transform::Manager manager; | 
|  | tint::transform::DataMap inputs; | 
|  | inputs.Add<tint::transform::BindingRemapper::Remappings>( | 
|  | std::move(binding_points), | 
|  | tint::transform::BindingRemapper::AccessControls{}, | 
|  | /* mayCollide */ true); | 
|  | manager.Add<tint::transform::BindingRemapper>(); | 
|  | transform_output = manager.Run(program, inputs); | 
|  | input_program = &transform_output.program; | 
|  | } | 
|  |  | 
|  | // TODO(jrprice): Provide a way for the user to set non-default options. | 
|  | tint::writer::msl::Options gen_options; | 
|  | gen_options.disable_workgroup_init = options.disable_workgroup_init; | 
|  | auto result = tint::writer::msl::Generate(input_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 | 
|  | (void)program; | 
|  | (void)options; | 
|  | 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; | 
|  | gen_options.disable_workgroup_init = options.disable_workgroup_init; | 
|  | 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 | 
|  | (void)program; | 
|  | (void)options; | 
|  | std::cerr << "HLSL writer not enabled in tint build" << std::endl; | 
|  | return false; | 
|  | #endif  // TINT_BUILD_HLSL_WRITER | 
|  | } | 
|  |  | 
|  | #if TINT_BUILD_GLSL_WRITER | 
|  | EShLanguage pipeline_stage_to_esh_language(tint::ast::PipelineStage stage) { | 
|  | switch (stage) { | 
|  | case tint::ast::PipelineStage::kFragment: | 
|  | return EShLangFragment; | 
|  | case tint::ast::PipelineStage::kVertex: | 
|  | return EShLangVertex; | 
|  | case tint::ast::PipelineStage::kCompute: | 
|  | return EShLangCompute; | 
|  | default: | 
|  | TINT_ASSERT(AST, false); | 
|  | return EShLangVertex; | 
|  | } | 
|  | } | 
|  | #endif | 
|  |  | 
|  | /// Generate GLSL code for a program. | 
|  | /// @param program the program to generate | 
|  | /// @param options the options that Tint was invoked with | 
|  | /// @returns true on success | 
|  | bool GenerateGlsl(const tint::Program* program, const Options& options) { | 
|  | #if TINT_BUILD_GLSL_WRITER | 
|  | if (options.validate) { | 
|  | glslang::InitializeProcess(); | 
|  | } | 
|  | tint::writer::glsl::Options gen_options; | 
|  | tint::inspector::Inspector inspector(program); | 
|  | for (auto& entry_point : inspector.GetEntryPoints()) { | 
|  | auto result = | 
|  | tint::writer::glsl::Generate(program, gen_options, entry_point.name); | 
|  | 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.glsl)) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (options.validate) { | 
|  | for (auto entry_pt : result.entry_points) { | 
|  | EShLanguage lang = pipeline_stage_to_esh_language(entry_pt.second); | 
|  | glslang::TShader shader(lang); | 
|  | const char* strings[1] = {result.glsl.c_str()}; | 
|  | int lengths[1] = {static_cast<int>(result.glsl.length())}; | 
|  | shader.setStringsWithLengths(strings, lengths, 1); | 
|  | shader.setEntryPoint("main"); | 
|  | bool glslang_result = | 
|  | shader.parse(&glslang::DefaultTBuiltInResource, 310, EEsProfile, | 
|  | false, false, EShMsgDefault); | 
|  | if (!glslang_result) { | 
|  | std::cerr << "Error parsing GLSL shader:\n" | 
|  | << shader.getInfoLog() << "\n" | 
|  | << shader.getInfoDebugLog() << "\n"; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return true; | 
|  | #else | 
|  | (void)program; | 
|  | (void)options; | 
|  | std::cerr << "GLSL writer not enabled in tint build" << std::endl; | 
|  | return false; | 
|  | #endif  // TINT_BUILD_GLSL_WRITER | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | int main(int argc, const char** argv) { | 
|  | std::vector<std::string> args(argv, argv + argc); | 
|  | Options options; | 
|  |  | 
|  | tint::SetInternalCompilerErrorReporter(&TintInternalCompilerErrorReporter); | 
|  |  | 
|  | #if TINT_BUILD_WGSL_WRITER | 
|  | tint::Program::printer = [](const tint::Program* program) { | 
|  | auto result = tint::writer::wgsl::Generate(program, {}); | 
|  | if (!result.error.empty()) { | 
|  | return "error: " + result.error; | 
|  | } | 
|  | return result.wgsl; | 
|  | }; | 
|  | #endif  //  TINT_BUILD_WGSL_WRITER | 
|  |  | 
|  | if (!ParseArgs(args, &options)) { | 
|  | std::cerr << "Failed to parse arguments." << std::endl; | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | if (options.show_help) { | 
|  | std::cout << kUsage << std::endl; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | // Implement output format defaults. | 
|  | if (options.format == Format::kNone) { | 
|  | // Try inferring from filename. | 
|  | options.format = infer_format(options.output_file); | 
|  | } | 
|  | if (options.format == Format::kNone) { | 
|  | // Ultimately, default to SPIR-V assembly. That's nice for interactive use. | 
|  | options.format = Format::kSpvAsm; | 
|  | } | 
|  |  | 
|  | auto diag_printer = tint::diag::Printer::create(stderr, true); | 
|  | tint::diag::Formatter diag_formatter; | 
|  |  | 
|  | std::unique_ptr<tint::Program> program; | 
|  | std::unique_ptr<tint::Source::File> source_file; | 
|  | #if TINT_BUILD_WGSL_READER | 
|  | if (options.input_filename.size() > 5 && | 
|  | options.input_filename.substr(options.input_filename.size() - 5) == | 
|  | ".wgsl") { | 
|  | std::vector<uint8_t> data; | 
|  | if (!ReadFile<uint8_t>(options.input_filename, &data)) { | 
|  | return 1; | 
|  | } | 
|  | source_file = std::make_unique<tint::Source::File>( | 
|  | options.input_filename, std::string(data.begin(), data.end())); | 
|  | program = std::make_unique<tint::Program>( | 
|  | tint::reader::wgsl::Parse(source_file.get())); | 
|  | } | 
|  | #endif  // TINT_BUILD_WGSL_READER | 
|  |  | 
|  | #if TINT_BUILD_SPV_READER | 
|  | // Handle SPIR-V binary input, in files ending with .spv | 
|  | if (options.input_filename.size() > 4 && | 
|  | options.input_filename.substr(options.input_filename.size() - 4) == | 
|  | ".spv") { | 
|  | std::vector<uint32_t> data; | 
|  | if (!ReadFile<uint32_t>(options.input_filename, &data)) { | 
|  | return 1; | 
|  | } | 
|  | program = std::make_unique<tint::Program>(tint::reader::spirv::Parse(data)); | 
|  | } | 
|  | // Handle SPIR-V assembly input, in files ending with .spvasm | 
|  | if (options.input_filename.size() > 7 && | 
|  | options.input_filename.substr(options.input_filename.size() - 7) == | 
|  | ".spvasm") { | 
|  | std::vector<char> text; | 
|  | if (!ReadFile<char>(options.input_filename, &text)) { | 
|  | return 1; | 
|  | } | 
|  | // Use Vulkan 1.1, since this is what Tint, internally, is expecting. | 
|  | 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; | 
|  | }); | 
|  | std::vector<uint32_t> data; | 
|  | if (!tools.Assemble(text.data(), text.size(), &data, | 
|  | SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS)) { | 
|  | return 1; | 
|  | } | 
|  | program = std::make_unique<tint::Program>(tint::reader::spirv::Parse(data)); | 
|  | } | 
|  | #endif  // TINT_BUILD_SPV_READER | 
|  |  | 
|  | if (!program) { | 
|  | std::cerr << "Failed to create reader for input file: " | 
|  | << options.input_filename << std::endl; | 
|  | return 1; | 
|  | } | 
|  | if (program->Diagnostics().count() > 0) { | 
|  | diag_formatter.format(program->Diagnostics(), diag_printer.get()); | 
|  | } | 
|  |  | 
|  | if (!program->IsValid()) { | 
|  | return 1; | 
|  | } | 
|  | if (options.parse_only) { | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | tint::transform::Manager transform_manager; | 
|  | tint::transform::DataMap transform_inputs; | 
|  | for (const auto& name : options.transforms) { | 
|  | // TODO(dsinclair): The vertex pulling transform requires setup code to | 
|  | // be run that needs user input. Should we find a way to support that here | 
|  | // maybe through a provided file? | 
|  |  | 
|  | if (name == "first_index_offset") { | 
|  | transform_inputs.Add<tint::transform::FirstIndexOffset::BindingPoint>(0, | 
|  | 0); | 
|  | transform_manager.Add<tint::transform::FirstIndexOffset>(); | 
|  | } else if (name == "fold_trivial_single_use_lets") { | 
|  | transform_manager.Add<tint::transform::FoldTrivialSingleUseLets>(); | 
|  | } else if (name == "renamer") { | 
|  | transform_manager.Add<tint::transform::Renamer>(); | 
|  | } else if (name == "robustness") { | 
|  | transform_manager.Add<tint::transform::Robustness>(); | 
|  | } else { | 
|  | std::cerr << "Unknown transform name: " << name << std::endl; | 
|  | return 1; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (options.emit_single_entry_point) { | 
|  | transform_manager.append( | 
|  | std::make_unique<tint::transform::SingleEntryPoint>()); | 
|  | transform_inputs.Add<tint::transform::SingleEntryPoint::Config>( | 
|  | options.ep_name); | 
|  | } | 
|  |  | 
|  | switch (options.format) { | 
|  | case Format::kMsl: { | 
|  | #if TINT_BUILD_MSL_WRITER | 
|  | transform_inputs.Add<tint::transform::Renamer::Config>( | 
|  | tint::transform::Renamer::Target::kMslKeywords); | 
|  | transform_manager.Add<tint::transform::Renamer>(); | 
|  | #endif  // TINT_BUILD_MSL_WRITER | 
|  | break; | 
|  | } | 
|  | #if TINT_BUILD_GLSL_WRITER | 
|  | case Format::kGlsl: { | 
|  | transform_inputs.Add<tint::transform::Renamer::Config>( | 
|  | tint::transform::Renamer::Target::kGlslKeywords); | 
|  | transform_manager.Add<tint::transform::Renamer>(); | 
|  | break; | 
|  | } | 
|  | #endif  // TINT_BUILD_GLSL_WRITER | 
|  | case Format::kHlsl: { | 
|  | #if TINT_BUILD_HLSL_WRITER | 
|  | transform_inputs.Add<tint::transform::Renamer::Config>( | 
|  | tint::transform::Renamer::Target::kHlslKeywords); | 
|  | transform_manager.Add<tint::transform::Renamer>(); | 
|  | #endif  // TINT_BUILD_HLSL_WRITER | 
|  | break; | 
|  | } | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | auto out = transform_manager.Run(program.get(), std::move(transform_inputs)); | 
|  | if (!out.program.IsValid()) { | 
|  | PrintWGSL(std::cerr, out.program); | 
|  | diag_formatter.format(out.program.Diagnostics(), diag_printer.get()); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | *program = std::move(out.program); | 
|  |  | 
|  | if (options.dump_inspector_bindings) { | 
|  | std::cout << std::string(80, '-') << std::endl; | 
|  | tint::inspector::Inspector inspector(program.get()); | 
|  | auto entry_points = inspector.GetEntryPoints(); | 
|  | if (!inspector.error().empty()) { | 
|  | std::cerr << "Failed to get entry points from Inspector: " | 
|  | << inspector.error() << std::endl; | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | for (auto& entry_point : entry_points) { | 
|  | auto bindings = inspector.GetResourceBindings(entry_point.name); | 
|  | if (!inspector.error().empty()) { | 
|  | std::cerr << "Failed to get bindings from Inspector: " | 
|  | << inspector.error() << std::endl; | 
|  | return 1; | 
|  | } | 
|  | std::cout << "Entry Point = " << entry_point.name << std::endl; | 
|  | for (auto& binding : bindings) { | 
|  | std::cout << "\t[" << binding.bind_group << "][" << binding.binding | 
|  | << "]:" << std::endl; | 
|  | std::cout << "\t\t resource_type = " | 
|  | << ResourceTypeToString(binding.resource_type) << std::endl; | 
|  | std::cout << "\t\t dim = " << TextureDimensionToString(binding.dim) | 
|  | << std::endl; | 
|  | std::cout << "\t\t sampled_kind = " | 
|  | << SampledKindToString(binding.sampled_kind) << std::endl; | 
|  | std::cout << "\t\t image_format = " | 
|  | << ImageFormatToString(binding.image_format) << std::endl; | 
|  | } | 
|  | } | 
|  | std::cout << std::string(80, '-') << std::endl; | 
|  | } | 
|  |  | 
|  | bool success = false; | 
|  | switch (options.format) { | 
|  | case Format::kSpirv: | 
|  | case Format::kSpvAsm: | 
|  | success = GenerateSpirv(program.get(), options); | 
|  | break; | 
|  | case Format::kWgsl: | 
|  | success = GenerateWgsl(program.get(), options); | 
|  | break; | 
|  | case Format::kMsl: | 
|  | success = GenerateMsl(program.get(), options); | 
|  | break; | 
|  | case Format::kHlsl: | 
|  | success = GenerateHlsl(program.get(), options); | 
|  | break; | 
|  | case Format::kGlsl: | 
|  | success = GenerateGlsl(program.get(), options); | 
|  | break; | 
|  | default: | 
|  | std::cerr << "Unknown output format specified" << std::endl; | 
|  | return 1; | 
|  | } | 
|  | if (!success) { | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } |