// Copyright 2023 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 "src/tint/cmd/helper.h" #include #include #include #if TINT_BUILD_SPV_READER #include "spirv-tools/libspirv.hpp" #endif namespace tint::cmd { namespace { enum class InputFormat { kUnknown, kWgsl, kSpirvBin, kSpirvAsm, }; InputFormat InputFormatFromFilename(const std::string& filename) { auto input_format = InputFormat::kUnknown; if (filename.size() > 5 && filename.substr(filename.size() - 5) == ".wgsl") { input_format = InputFormat::kWgsl; } else if (filename.size() > 4 && filename.substr(filename.size() - 4) == ".spv") { input_format = InputFormat::kSpirvBin; } else if (filename.size() > 7 && filename.substr(filename.size() - 7) == ".spvasm") { input_format = InputFormat::kSpirvAsm; } return input_format; } /// 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 bool ReadFile(const std::string& input_file, std::vector* 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); const auto file_size = static_cast(ftell(file)); 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; } void PrintBindings(tint::inspector::Inspector& inspector, const std::string& ep_name) { auto bindings = inspector.GetResourceBindings(ep_name); if (!inspector.error().empty()) { std::cerr << "Failed to get bindings from Inspector: " << inspector.error() << std::endl; exit(1); } 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 = " << TexelFormatToString(binding.image_format) << std::endl; std::cout << std::endl; } } } // 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); } 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 } ProgramInfo LoadProgramInfo(const LoadProgramOptions& opts) { std::unique_ptr program; std::unique_ptr source_file; auto input_format = InputFormatFromFilename(opts.filename); switch (input_format) { case InputFormat::kUnknown: { std::cerr << "Unknown input format" << std::endl; exit(1); } case InputFormat::kWgsl: { #if TINT_BUILD_WGSL_READER std::vector data; if (!ReadFile(opts.filename, &data)) { exit(1); } source_file = std::make_unique( opts.filename, std::string(data.begin(), data.end())); program = std::make_unique(tint::reader::wgsl::Parse(source_file.get())); break; #else std::cerr << "Tint not built with the WGSL reader enabled" << std::endl; exit(1); #endif // TINT_BUILD_WGSL_READER } case InputFormat::kSpirvBin: { #if TINT_BUILD_SPV_READER std::vector data; if (!ReadFile(opts.filename, &data)) { exit(1); } program = std::make_unique( tint::reader::spirv::Parse(data, opts.spirv_reader_options)); break; #else std::cerr << "Tint not built with the SPIR-V reader enabled" << std::endl; exit(1); #endif // TINT_BUILD_SPV_READER } case InputFormat::kSpirvAsm: { #if TINT_BUILD_SPV_READER std::vector text; if (!ReadFile(opts.filename, &text)) { exit(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 data; if (!tools.Assemble(text.data(), text.size(), &data, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS)) { exit(1); } program = std::make_unique( tint::reader::spirv::Parse(data, opts.spirv_reader_options)); break; #else std::cerr << "Tint not built with the SPIR-V reader enabled" << std::endl; exit(1); #endif // TINT_BUILD_SPV_READER } } if (!program) { std::cerr << "Failed to parse input file: " << opts.filename << std::endl; exit(1); } if (program->Diagnostics().count() > 0) { if (!program->IsValid() && input_format != InputFormat::kWgsl) { // Invalid program from a non-wgsl source. Print the WGSL, to help // understand the diagnostics. PrintWGSL(std::cout, *program); } auto diag_printer = tint::diag::Printer::create(stderr, true); tint::diag::Formatter diag_formatter; diag_formatter.format(program->Diagnostics(), diag_printer.get()); } if (!program->IsValid()) { exit(1); } return ProgramInfo{ std::move(program), std::move(source_file), }; } void PrintInspectorData(tint::inspector::Inspector& inspector) { auto entry_points = inspector.GetEntryPoints(); if (!inspector.error().empty()) { std::cerr << "Failed to get entry points from Inspector: " << inspector.error() << std::endl; exit(1); } for (auto& entry_point : entry_points) { std::cout << "Entry Point = " << entry_point.name << " (" << EntryPointStageToString(entry_point.stage) << ")" << std::endl; if (entry_point.workgroup_size) { std::cout << " Workgroup Size (" << entry_point.workgroup_size->x << ", " << entry_point.workgroup_size->y << ", " << entry_point.workgroup_size->z << ")" << std::endl; } if (!entry_point.input_variables.empty()) { std::cout << " Input Variables:" << std::endl; for (const auto& var : entry_point.input_variables) { std::cout << "\t"; if (var.has_location_attribute) { std::cout << "@location(" << var.location_attribute << ") "; } std::cout << var.name << std::endl; } } if (!entry_point.output_variables.empty()) { std::cout << " Output Variables:" << std::endl; for (const auto& var : entry_point.output_variables) { std::cout << "\t"; if (var.has_location_attribute) { std::cout << "@location(" << var.location_attribute << ") "; } std::cout << var.name << std::endl; } } if (!entry_point.overrides.empty()) { std::cout << " Overrides:" << std::endl; for (const auto& var : entry_point.overrides) { std::cout << "\tname: " << var.name << std::endl; std::cout << "\tid: " << var.id.value << std::endl; } } auto bindings = inspector.GetResourceBindings(entry_point.name); if (!inspector.error().empty()) { std::cerr << "Failed to get bindings from Inspector: " << inspector.error() << std::endl; exit(1); } if (!bindings.empty()) { std::cout << " Bindings:" << std::endl; PrintBindings(inspector, entry_point.name); std::cout << std::endl; } std::cout << std::endl; } } void PrintInspectorBindings(tint::inspector::Inspector& inspector) { std::cout << std::string(80, '-') << std::endl; auto entry_points = inspector.GetEntryPoints(); if (!inspector.error().empty()) { std::cerr << "Failed to get entry points from Inspector: " << inspector.error() << std::endl; exit(1); } for (auto& entry_point : entry_points) { std::cout << "Entry Point = " << entry_point.name << std::endl; PrintBindings(inspector, entry_point.name); } std::cout << std::string(80, '-') << std::endl; } std::string EntryPointStageToString(tint::inspector::PipelineStage stage) { switch (stage) { case tint::inspector::PipelineStage::kVertex: return "vertex"; case tint::inspector::PipelineStage::kFragment: return "fragment"; case tint::inspector::PipelineStage::kCompute: return "compute"; } return "Unknown"; } 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 TexelFormatToString(tint::inspector::ResourceBinding::TexelFormat format) { switch (format) { case tint::inspector::ResourceBinding::TexelFormat::kR32Uint: return "R32Uint"; case tint::inspector::ResourceBinding::TexelFormat::kR32Sint: return "R32Sint"; case tint::inspector::ResourceBinding::TexelFormat::kR32Float: return "R32Float"; case tint::inspector::ResourceBinding::TexelFormat::kBgra8Unorm: return "Bgra8Unorm"; case tint::inspector::ResourceBinding::TexelFormat::kRgba8Unorm: return "Rgba8Unorm"; case tint::inspector::ResourceBinding::TexelFormat::kRgba8Snorm: return "Rgba8Snorm"; case tint::inspector::ResourceBinding::TexelFormat::kRgba8Uint: return "Rgba8Uint"; case tint::inspector::ResourceBinding::TexelFormat::kRgba8Sint: return "Rgba8Sint"; case tint::inspector::ResourceBinding::TexelFormat::kRg32Uint: return "Rg32Uint"; case tint::inspector::ResourceBinding::TexelFormat::kRg32Sint: return "Rg32Sint"; case tint::inspector::ResourceBinding::TexelFormat::kRg32Float: return "Rg32Float"; case tint::inspector::ResourceBinding::TexelFormat::kRgba16Uint: return "Rgba16Uint"; case tint::inspector::ResourceBinding::TexelFormat::kRgba16Sint: return "Rgba16Sint"; case tint::inspector::ResourceBinding::TexelFormat::kRgba16Float: return "Rgba16Float"; case tint::inspector::ResourceBinding::TexelFormat::kRgba32Uint: return "Rgba32Uint"; case tint::inspector::ResourceBinding::TexelFormat::kRgba32Sint: return "Rgba32Sint"; case tint::inspector::ResourceBinding::TexelFormat::kRgba32Float: return "Rgba32Float"; case tint::inspector::ResourceBinding::TexelFormat::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"; } std::string ComponentTypeToString(tint::inspector::ComponentType type) { switch (type) { case tint::inspector::ComponentType::kUnknown: return "unknown"; case tint::inspector::ComponentType::kF32: return "f32"; case tint::inspector::ComponentType::kU32: return "u32"; case tint::inspector::ComponentType::kI32: return "i32"; case tint::inspector::ComponentType::kF16: return "f16"; } return "unknown"; } std::string CompositionTypeToString(tint::inspector::CompositionType type) { switch (type) { case tint::inspector::CompositionType::kUnknown: return "unknown"; case tint::inspector::CompositionType::kScalar: return "scalar"; case tint::inspector::CompositionType::kVec2: return "vec2"; case tint::inspector::CompositionType::kVec3: return "vec3"; case tint::inspector::CompositionType::kVec4: return "vec4"; } return "unknown"; } std::string InterpolationTypeToString(tint::inspector::InterpolationType type) { switch (type) { case tint::inspector::InterpolationType::kUnknown: return "unknown"; case tint::inspector::InterpolationType::kPerspective: return "perspective"; case tint::inspector::InterpolationType::kLinear: return "linear"; case tint::inspector::InterpolationType::kFlat: return "flat"; } return "unknown"; } std::string InterpolationSamplingToString(tint::inspector::InterpolationSampling type) { switch (type) { case tint::inspector::InterpolationSampling::kUnknown: return "unknown"; case tint::inspector::InterpolationSampling::kNone: return "none"; case tint::inspector::InterpolationSampling::kCenter: return "center"; case tint::inspector::InterpolationSampling::kCentroid: return "centroid"; case tint::inspector::InterpolationSampling::kSample: return "sample"; } return "unknown"; } std::string OverrideTypeToString(tint::inspector::Override::Type type) { switch (type) { case tint::inspector::Override::Type::kBool: return "bool"; case tint::inspector::Override::Type::kFloat32: return "f32"; case tint::inspector::Override::Type::kFloat16: return "f16"; case tint::inspector::Override::Type::kUint32: return "u32"; case tint::inspector::Override::Type::kInt32: return "i32"; } return "unknown"; } } // namespace tint::cmd