blob: 0a92cc48b168ef0330ed576a2184addc5a77adc4 [file] [log] [blame]
// Copyright 2023 The Dawn & Tint Authors
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "src/tint/cmd/common/helper.h"
#include <cstdio>
#include <iostream>
#include <utility>
#include <vector>
#if TINT_BUILD_SPV_READER
#include "spirv-tools/libspirv.hpp"
#include "src/tint/lang/spirv/reader/reader.h"
#endif
#if TINT_BUILD_WGSL_WRITER
#include "src/tint/lang/wgsl/writer/ir_to_program/ir_to_program.h"
#include "src/tint/lang/wgsl/writer/writer.h"
#endif
#if TINT_BUILD_WGSL_READER
#include "src/tint/lang/wgsl/reader/reader.h"
#endif
#include "src/tint/utils/diagnostic/formatter.h"
#include "src/tint/utils/text/string.h"
#include "src/tint/utils/text/styled_text.h"
#include "src/tint/utils/text/styled_text_printer.h"
#include "src/tint/utils/text/text_style.h"
#include "src/tint/utils/traits/traits.h"
namespace tint::cmd {
namespace {
enum class InputFormat {
kUnknown,
kWgsl,
kSpirvBin,
kSpirvAsm,
};
/// @param out the stream to write to
/// @param value the InputFormat
/// @returns @p out so calls can be chained
template <typename STREAM, typename = traits::EnableIfIsOStream<STREAM>>
auto& operator<<(STREAM& out, InputFormat value) {
switch (value) {
case InputFormat::kUnknown:
break;
case InputFormat::kWgsl:
return out << "wgsl";
case InputFormat::kSpirvBin:
return out << "spirv";
case InputFormat::kSpirvAsm:
return out << "spirv asm";
}
return out << "unknown";
}
InputFormat InputFormatFromFilename(const std::string& filename) {
auto input_format = InputFormat::kUnknown;
if (tint::HasSuffix(filename, ".wgsl")) {
input_format = InputFormat::kWgsl;
} else if (tint::HasSuffix(filename, ".spv")) {
input_format = InputFormat::kSpirvBin;
} else if (tint::HasSuffix(filename, ".spvasm")) {
input_format = InputFormat::kSpirvAsm;
}
return input_format;
}
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() << "\n";
exit(1);
}
for (auto& binding : bindings) {
std::cout << "\t[" << binding.bind_group << "][" << binding.binding << "]:\n"
<< "\t\t resource_type = " << ResourceTypeToString(binding.resource_type) << "\n"
<< "\t\t dim = " << TextureDimensionToString(binding.dim) << "\n"
<< "\t\t sampled_kind = " << SampledKindToString(binding.sampled_kind) << "\n"
<< "\t\t image_format = " << TexelFormatToString(binding.image_format) << "\n\n";
}
}
#if TINT_BUILD_SPV_READER
tint::Program ReadSpirv(const std::vector<uint32_t>& data, const LoadProgramOptions& opts) {
if (opts.use_ir) {
#if TINT_BUILD_WGSL_WRITER
// Parse the SPIR-V binary to a core Tint IR module.
auto result = tint::spirv::reader::ReadIR(data);
if (result != Success) {
std::cerr << "Failed to parse SPIR-V: " << result.Failure() << "\n";
exit(1);
}
// Convert the IR module to a Program.
tint::wgsl::writer::ProgramOptions writer_options;
writer_options.allow_non_uniform_derivatives =
opts.spirv_reader_options.allow_non_uniform_derivatives;
writer_options.allowed_features = opts.spirv_reader_options.allowed_features;
auto prog_result = tint::wgsl::writer::ProgramFromIR(result.Get(), writer_options);
if (prog_result != Success) {
std::cerr << "Failed to convert IR to Program:\n\n"
<< prog_result.Failure().reason << "\n";
exit(1);
}
return prog_result.Move();
#else
std::cerr << "Tint not built with the WGSL writer enabled\n";
exit(1);
#endif // TINT_BUILD_WGSL_READER
} else {
return tint::spirv::reader::Read(data, opts.spirv_reader_options);
}
}
#endif // TINT_BUILD_SPV_READER
} // namespace
void TintInternalCompilerErrorReporter(const InternalCompilerError& err) {
auto printer = StyledTextPrinter::Create(stderr);
StyledText msg;
msg << (style::Error + style::Bold) << err.Error();
msg << 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->Print(msg);
}
void PrintWGSL(std::ostream& out, const tint::Program& program) {
#if TINT_BUILD_WGSL_WRITER
tint::wgsl::writer::Options options;
auto result = tint::wgsl::writer::Generate(program, options);
if (result == Success) {
out << "\n" << result->wgsl << "\n";
} else {
out << result.Failure() << "\n";
}
#else
(void)out;
(void)program;
#endif
}
ProgramInfo LoadProgramInfo(const LoadProgramOptions& opts) {
auto input_format = InputFormatFromFilename(opts.filename);
auto load = [&]() -> ProgramInfo {
switch (input_format) {
case InputFormat::kUnknown:
break;
case InputFormat::kWgsl: {
#if TINT_BUILD_WGSL_READER
std::vector<uint8_t> data;
if (!ReadFile<uint8_t>(opts.filename, &data)) {
exit(1);
}
tint::wgsl::reader::Options options;
options.allowed_features = tint::wgsl::AllowedFeatures::Everything();
options.mode = opts.mode;
auto file = std::make_unique<tint::Source::File>(
opts.filename, std::string(data.begin(), data.end()));
return ProgramInfo{
/* program */ tint::wgsl::reader::Parse(file.get(), options),
/* source_file */ std::move(file),
};
#else
std::cerr << "Tint not built with the WGSL reader enabled\n";
exit(1);
#endif // TINT_BUILD_WGSL_READER
}
case InputFormat::kSpirvBin: {
#if TINT_BUILD_SPV_READER
std::vector<uint32_t> data;
if (!ReadFile<uint32_t>(opts.filename, &data)) {
exit(1);
}
return ProgramInfo{
/* program */ ReadSpirv(data, opts),
/* source_file */ nullptr,
};
#else
std::cerr << "Tint not built with the SPIR-V reader enabled\n";
exit(1);
#endif // TINT_BUILD_SPV_READER
}
case InputFormat::kSpirvAsm: {
#if TINT_BUILD_SPV_READER
std::vector<char> text;
if (!ReadFile<char>(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 << "\n";
});
std::vector<uint32_t> data;
if (!tools.Assemble(text.data(), text.size(), &data,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS)) {
exit(1);
}
auto file = std::make_unique<tint::Source::File>(
opts.filename, std::string(text.begin(), text.end()));
return ProgramInfo{
/* program */ ReadSpirv(data, opts),
/* source_file */ std::move(file),
};
#else
std::cerr << "Tint not built with the SPIR-V reader enabled\n";
exit(1);
#endif // TINT_BUILD_SPV_READER
}
}
std::cerr << "Unknown input format: " << input_format << "\n";
exit(1);
};
ProgramInfo info = load();
if (info.program.Diagnostics().Count() > 0) {
if (!info.program.IsValid() && input_format != InputFormat::kWgsl) {
// Invalid program from a non-wgsl source.
// Print the WGSL, to help understand the diagnostics.
PrintWGSL(std::cout, info.program);
}
tint::diag::Formatter formatter;
if (opts.printer) {
opts.printer->Print(formatter.Format(info.program.Diagnostics()));
} else {
tint::StyledTextPrinter::Create(stderr)->Print(
formatter.Format(info.program.Diagnostics()));
}
// Flush any diagnostics written to stderr. We depend on these being emitted to the console
// before the program for end-to-end tests.
fflush(stderr);
}
if (!info.program.IsValid()) {
exit(1);
}
return info;
}
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() << "\n";
exit(1);
}
for (auto& entry_point : entry_points) {
std::cout << "Entry Point = " << entry_point.name << " ("
<< EntryPointStageToString(entry_point.stage) << ")\n";
if (entry_point.workgroup_size) {
std::cout << " Workgroup Size (" << entry_point.workgroup_size->x << ", "
<< entry_point.workgroup_size->y << ", " << entry_point.workgroup_size->z
<< ")\n";
}
if (!entry_point.input_variables.empty()) {
std::cout << " Input Variables:\n";
for (const auto& var : entry_point.input_variables) {
std::cout << "\t";
if (auto location = var.attributes.location) {
std::cout << "@location(" << location.value() << ") ";
}
if (auto color = var.attributes.color) {
std::cout << "@color(" << color.value() << ") ";
}
std::cout << var.name << "\n";
}
}
if (!entry_point.output_variables.empty()) {
std::cout << " Output Variables:\n";
for (const auto& var : entry_point.output_variables) {
std::cout << "\t";
if (auto location = var.attributes.location) {
std::cout << "@location(" << location.value() << ") ";
}
std::cout << var.name << "\n";
}
}
if (!entry_point.overrides.empty()) {
std::cout << " Overrides:\n";
for (const auto& var : entry_point.overrides) {
std::cout << "\tname: " << var.name << "\n";
std::cout << "\tid: " << var.id.value << "\n";
}
}
auto bindings = inspector.GetResourceBindings(entry_point.name);
if (!inspector.error().empty()) {
std::cerr << "Failed to get bindings from Inspector: " << inspector.error() << "\n";
exit(1);
}
if (!bindings.empty()) {
std::cout << " Bindings:\n";
PrintBindings(inspector, entry_point.name);
std::cout << "\n";
}
std::cout << "\n";
}
}
void PrintInspectorBindings(tint::inspector::Inspector& inspector) {
std::cout << std::string(80, '-') << "\n";
auto entry_points = inspector.GetEntryPoints();
if (!inspector.error().empty()) {
std::cerr << "Failed to get entry points from Inspector: " << inspector.error() << "\n";
exit(1);
}
for (auto& entry_point : entry_points) {
std::cout << "Entry Point = " << entry_point.name << "\n";
PrintBindings(inspector, entry_point.name);
}
std::cout << std::string(80, '-') << "\n";
}
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::kR8Unorm:
return "R8Unorm";
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::kReadOnlyStorageTexture:
return "ReadOnlyStorageTexture";
case tint::inspector::ResourceBinding::ResourceType::kReadWriteStorageTexture:
return "ReadWriteStorageTexture";
case tint::inspector::ResourceBinding::ResourceType::kDepthTexture:
return "DepthTexture";
case tint::inspector::ResourceBinding::ResourceType::kDepthMultisampledTexture:
return "DepthMultisampledTexture";
case tint::inspector::ResourceBinding::ResourceType::kExternalTexture:
return "ExternalTexture";
case tint::inspector::ResourceBinding::ResourceType::kInputAttachment:
return "InputAttachment";
}
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