[ir][spirv-writer] Emit shader IO without structs
Some implementations cannot handle built-ins inside structures, so
emit every IO variable as a separate module-scope variable instead.
Adds an IOAttributes field to the core Var instruction, as the GLSL
backend will also need this.
Bug: tint:1906
Change-Id: Ic2508ceaa16ada377ebcc154c6fcb1b91c09350e
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/151583
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/lang/core/ir/disassembler.cc b/src/tint/lang/core/ir/disassembler.cc
index 5a9d29c..66ce989 100644
--- a/src/tint/lang/core/ir/disassembler.cc
+++ b/src/tint/lang/core/ir/disassembler.cc
@@ -480,6 +480,23 @@
out_ << " ";
EmitBindingPoint(v->BindingPoint().value());
}
+ if (v->Attributes().invariant) {
+ out_ << " @invariant";
+ }
+ if (v->Attributes().location.has_value()) {
+ out_ << " @location(" << v->Attributes().location.value() << ")";
+ }
+ if (v->Attributes().interpolation.has_value()) {
+ auto& interp = v->Attributes().interpolation.value();
+ out_ << " @interpolate(" << interp.type;
+ if (interp.sampling != core::InterpolationSampling::kUndefined) {
+ out_ << ", " << interp.sampling;
+ }
+ out_ << ")";
+ }
+ if (v->Attributes().builtin.has_value()) {
+ out_ << " @builtin(" << v->Attributes().builtin.value() << ")";
+ }
},
[&](Swizzle* s) {
EmitValueWithType(s);
diff --git a/src/tint/lang/core/ir/var.cc b/src/tint/lang/core/ir/var.cc
index 1042786..501d6af 100644
--- a/src/tint/lang/core/ir/var.cc
+++ b/src/tint/lang/core/ir/var.cc
@@ -15,6 +15,7 @@
#include "src/tint/lang/core/ir/var.h"
#include "src/tint/lang/core/ir/store.h"
+#include "src/tint/lang/core/type/pointer.h"
#include "src/tint/utils/ice/ice.h"
TINT_INSTANTIATE_TYPEINFO(tint::core::ir::Var);
diff --git a/src/tint/lang/core/ir/var.h b/src/tint/lang/core/ir/var.h
index c10adf3..384f7b1 100644
--- a/src/tint/lang/core/ir/var.h
+++ b/src/tint/lang/core/ir/var.h
@@ -18,15 +18,27 @@
#include <string>
#include "src/tint/api/common/binding_point.h"
-#include "src/tint/lang/core/access.h"
-#include "src/tint/lang/core/address_space.h"
+#include "src/tint/lang/core/builtin_value.h"
+#include "src/tint/lang/core/interpolation.h"
#include "src/tint/lang/core/ir/operand_instruction.h"
-#include "src/tint/lang/core/type/pointer.h"
-#include "src/tint/utils/containers/vector.h"
#include "src/tint/utils/rtti/castable.h"
namespace tint::core::ir {
+/// Attributes that can be applied to a variable that will be used for shader IO.
+struct IOAttributes {
+ /// The value of a `@location` attribute.
+ std::optional<uint32_t> location;
+ /// The value of a `@index` attribute.
+ std::optional<uint32_t> index;
+ /// The value of a `@builtin` attribute.
+ std::optional<core::BuiltinValue> builtin;
+ /// The values of a `@interpolate` attribute.
+ std::optional<core::Interpolation> interpolation;
+ /// True if the variable is annotated with `@invariant`.
+ bool invariant = false;
+};
+
/// A var instruction in the IR.
class Var : public Castable<Var, OperandInstruction<1, 1>> {
public:
@@ -51,6 +63,12 @@
/// @returns the binding points if `Attributes` contains `kBindingPoint`
std::optional<struct BindingPoint> BindingPoint() { return binding_point_; }
+ /// Sets the IO attributes
+ /// @param attrs the attributes
+ void SetAttributes(const IOAttributes& attrs) { attributes_ = attrs; }
+ /// @returns the IO attributes
+ const IOAttributes& Attributes() { return attributes_; }
+
/// Destroys this instruction along with any assignment instructions, if the var is never read.
void DestroyIfOnlyAssigned();
@@ -59,6 +77,7 @@
private:
std::optional<struct BindingPoint> binding_point_;
+ IOAttributes attributes_;
};
} // namespace tint::core::ir
diff --git a/src/tint/lang/spirv/writer/discard_test.cc b/src/tint/lang/spirv/writer/discard_test.cc
index 5fe3f7c..cf190b9 100644
--- a/src/tint/lang/spirv/writer/discard_test.cc
+++ b/src/tint/lang/spirv/writer/discard_test.cc
@@ -43,30 +43,30 @@
ASSERT_TRUE(Generate()) << Error() << output_;
EXPECT_INST(R"(
- %ep_inner = OpFunction %float None %18
+ %ep_inner = OpFunction %float None %16
%front_facing = OpFunctionParameter %bool
+ %17 = OpLabel
+ OpSelectionMerge %18 None
+ OpBranchConditional %front_facing %19 %18
%19 = OpLabel
- OpSelectionMerge %20 None
- OpBranchConditional %front_facing %21 %20
- %21 = OpLabel
OpStore %continue_execution %false
- OpBranch %20
- %20 = OpLabel
- %23 = OpAccessChain %_ptr_StorageBuffer_int %1 %uint_0
- %27 = OpLoad %bool %continue_execution
- OpSelectionMerge %28 None
- OpBranchConditional %27 %29 %28
- %29 = OpLabel
- OpStore %23 %int_42
- OpBranch %28
- %28 = OpLabel
- %31 = OpLoad %bool %continue_execution
- %32 = OpLogicalEqual %bool %31 %false
- OpSelectionMerge %33 None
- OpBranchConditional %32 %34 %33
- %34 = OpLabel
+ OpBranch %18
+ %18 = OpLabel
+ %21 = OpAccessChain %_ptr_StorageBuffer_int %1 %uint_0
+ %25 = OpLoad %bool %continue_execution
+ OpSelectionMerge %26 None
+ OpBranchConditional %25 %27 %26
+ %27 = OpLabel
+ OpStore %21 %int_42
+ OpBranch %26
+ %26 = OpLabel
+ %29 = OpLoad %bool %continue_execution
+ %30 = OpLogicalEqual %bool %29 %false
+ OpSelectionMerge %31 None
+ OpBranchConditional %30 %32 %31
+ %32 = OpLabel
OpKill
- %33 = OpLabel
+ %31 = OpLabel
OpReturnValue %float_0_5
OpFunctionEnd
)");
diff --git a/src/tint/lang/spirv/writer/printer/printer.cc b/src/tint/lang/spirv/writer/printer/printer.cc
index b098a86..856d5d3 100644
--- a/src/tint/lang/spirv/writer/printer/printer.cc
+++ b/src/tint/lang/spirv/writer/printer/printer.cc
@@ -324,8 +324,7 @@
});
}
-uint32_t Printer::Type(const core::type::Type* ty,
- core::AddressSpace addrspace /* = kUndefined */) {
+uint32_t Printer::Type(const core::type::Type* ty) {
ty = DedupType(ty, ir_->Types());
return types_.GetOrCreate(ty, [&] {
auto id = module_.NextId();
@@ -369,11 +368,11 @@
{id, U32Operand(SpvDecorationArrayStride), arr->Stride()});
},
[&](const core::type::Pointer* ptr) {
- module_.PushType(spv::Op::OpTypePointer,
- {id, U32Operand(StorageClass(ptr->AddressSpace())),
- Type(ptr->StoreType(), ptr->AddressSpace())});
+ module_.PushType(
+ spv::Op::OpTypePointer,
+ {id, U32Operand(StorageClass(ptr->AddressSpace())), Type(ptr->StoreType())});
},
- [&](const core::type::Struct* str) { EmitStructType(id, str, addrspace); },
+ [&](const core::type::Struct* str) { EmitStructType(id, str); },
[&](const core::type::Texture* tex) { EmitTextureType(id, tex); },
[&](const core::type::Sampler*) { module_.PushType(spv::Op::OpTypeSampler, {id}); },
[&](const raise::SampledImage* s) {
@@ -401,9 +400,7 @@
return block_labels_.GetOrCreate(block, [&] { return module_.NextId(); });
}
-void Printer::EmitStructType(uint32_t id,
- const core::type::Struct* str,
- core::AddressSpace addrspace /* = kUndefined */) {
+void Printer::EmitStructType(uint32_t id, const core::type::Struct* str) {
// Helper to return `type` or a potentially nested array element type within `type` as a matrix
// type, or nullptr if no such matrix type is present.
auto get_nested_matrix_type = [&](const core::type::Type* type) {
@@ -422,56 +419,6 @@
spv::Op::OpMemberDecorate,
{operands[0], member->Index(), U32Operand(SpvDecorationOffset), member->Offset()});
- // Generate shader IO decorations.
- const auto& attrs = member->Attributes();
- if (attrs.location) {
- module_.PushAnnot(
- spv::Op::OpMemberDecorate,
- {operands[0], member->Index(), U32Operand(SpvDecorationLocation), *attrs.location});
- if (attrs.interpolation) {
- switch (attrs.interpolation->type) {
- case core::InterpolationType::kLinear:
- module_.PushAnnot(
- spv::Op::OpMemberDecorate,
- {operands[0], member->Index(), U32Operand(SpvDecorationNoPerspective)});
- break;
- case core::InterpolationType::kFlat:
- module_.PushAnnot(
- spv::Op::OpMemberDecorate,
- {operands[0], member->Index(), U32Operand(SpvDecorationFlat)});
- break;
- case core::InterpolationType::kPerspective:
- case core::InterpolationType::kUndefined:
- break;
- }
- switch (attrs.interpolation->sampling) {
- case core::InterpolationSampling::kCentroid:
- module_.PushAnnot(
- spv::Op::OpMemberDecorate,
- {operands[0], member->Index(), U32Operand(SpvDecorationCentroid)});
- break;
- case core::InterpolationSampling::kSample:
- module_.PushCapability(SpvCapabilitySampleRateShading);
- module_.PushAnnot(
- spv::Op::OpMemberDecorate,
- {operands[0], member->Index(), U32Operand(SpvDecorationSample)});
- break;
- case core::InterpolationSampling::kCenter:
- case core::InterpolationSampling::kUndefined:
- break;
- }
- }
- }
- if (attrs.builtin) {
- module_.PushAnnot(spv::Op::OpMemberDecorate,
- {operands[0], member->Index(), U32Operand(SpvDecorationBuiltIn),
- Builtin(*attrs.builtin, addrspace)});
- }
- if (attrs.invariant) {
- module_.PushAnnot(spv::Op::OpMemberDecorate,
- {operands[0], member->Index(), U32Operand(SpvDecorationInvariant)});
- }
-
// Emit matrix layout decorations if necessary.
if (auto* matrix_type = get_nested_matrix_type(member->Type())) {
const uint32_t effective_row_count = (matrix_type->rows() == 2) ? 2 : 4;
@@ -691,13 +638,9 @@
operands.push_back(Value(var));
// Add the `DepthReplacing` execution mode if `frag_depth` is used.
- if (auto* str = ptr->StoreType()->As<core::type::Struct>()) {
- for (auto* member : str->Members()) {
- if (member->Attributes().builtin == core::BuiltinValue::kFragDepth) {
- module_.PushExecutionMode(spv::Op::OpExecutionMode,
- {id, U32Operand(SpvExecutionModeDepthReplacing)});
- }
- }
+ if (var->Attributes().builtin == core::BuiltinValue::kFragDepth) {
+ module_.PushExecutionMode(spv::Op::OpExecutionMode,
+ {id, U32Operand(SpvExecutionModeDepthReplacing)});
}
}
}
@@ -1835,6 +1778,48 @@
current_function_.push_inst(spv::Op::OpFunctionCall, operands);
}
+void Printer::EmitIOAttributes(uint32_t id,
+ const core::ir::IOAttributes& attrs,
+ core::AddressSpace addrspace) {
+ if (attrs.location) {
+ module_.PushAnnot(spv::Op::OpDecorate,
+ {id, U32Operand(SpvDecorationLocation), *attrs.location});
+ }
+ if (attrs.interpolation) {
+ switch (attrs.interpolation->type) {
+ case core::InterpolationType::kLinear:
+ module_.PushAnnot(spv::Op::OpDecorate,
+ {id, U32Operand(SpvDecorationNoPerspective)});
+ break;
+ case core::InterpolationType::kFlat:
+ module_.PushAnnot(spv::Op::OpDecorate, {id, U32Operand(SpvDecorationFlat)});
+ break;
+ case core::InterpolationType::kPerspective:
+ case core::InterpolationType::kUndefined:
+ break;
+ }
+ switch (attrs.interpolation->sampling) {
+ case core::InterpolationSampling::kCentroid:
+ module_.PushAnnot(spv::Op::OpDecorate, {id, U32Operand(SpvDecorationCentroid)});
+ break;
+ case core::InterpolationSampling::kSample:
+ module_.PushCapability(SpvCapabilitySampleRateShading);
+ module_.PushAnnot(spv::Op::OpDecorate, {id, U32Operand(SpvDecorationSample)});
+ break;
+ case core::InterpolationSampling::kCenter:
+ case core::InterpolationSampling::kUndefined:
+ break;
+ }
+ }
+ if (attrs.builtin) {
+ module_.PushAnnot(spv::Op::OpDecorate, {id, U32Operand(SpvDecorationBuiltIn),
+ Builtin(*attrs.builtin, addrspace)});
+ }
+ if (attrs.invariant) {
+ module_.PushAnnot(spv::Op::OpDecorate, {id, U32Operand(SpvDecorationInvariant)});
+ }
+}
+
void Printer::EmitVar(core::ir::Var* var) {
auto id = Value(var);
auto* ptr = var->Result()->Type()->As<core::type::Pointer>();
@@ -1855,6 +1840,7 @@
case core::AddressSpace::kIn: {
TINT_ASSERT(!current_function_);
module_.PushType(spv::Op::OpVariable, {ty, id, U32Operand(SpvStorageClassInput)});
+ EmitIOAttributes(id, var->Attributes(), core::AddressSpace::kIn);
break;
}
case core::AddressSpace::kPrivate: {
@@ -1878,6 +1864,7 @@
case core::AddressSpace::kOut: {
TINT_ASSERT(!current_function_);
module_.PushType(spv::Op::OpVariable, {ty, id, U32Operand(SpvStorageClassOutput)});
+ EmitIOAttributes(id, var->Attributes(), core::AddressSpace::kOut);
break;
}
case core::AddressSpace::kHandle:
diff --git a/src/tint/lang/spirv/writer/printer/printer.h b/src/tint/lang/spirv/writer/printer/printer.h
index 2f0d53a..4071ab0 100644
--- a/src/tint/lang/spirv/writer/printer/printer.h
+++ b/src/tint/lang/spirv/writer/printer/printer.h
@@ -98,10 +98,8 @@
/// Get the result ID of the type `ty`, emitting a type declaration instruction if necessary.
/// @param ty the type to get the ID for
- /// @param addrspace the optional address space that this type is being used for
/// @returns the result ID of the type
- uint32_t Type(const core::type::Type* ty,
- core::AddressSpace addrspace = core::AddressSpace::kUndefined);
+ uint32_t Type(const core::type::Type* ty);
private:
/// Convert a builtin to the corresponding SPIR-V enum value, taking into account the target
@@ -148,11 +146,8 @@
/// Emit a struct type.
/// @param id the result ID to use
- /// @param addrspace the optional address space that this type is being used for
/// @param str the struct type to emit
- void EmitStructType(uint32_t id,
- const core::type::Struct* str,
- core::AddressSpace addrspace = core::AddressSpace::kUndefined);
+ void EmitStructType(uint32_t id, const core::type::Struct* str);
/// Emit a texture type.
/// @param id the result ID to use
@@ -220,6 +215,14 @@
/// @param call the intrinsic call instruction to emit
void EmitIntrinsicCall(spirv::ir::IntrinsicCall* call);
+ /// Emit IO attributes.
+ /// @param id the ID of the variable to decorate
+ /// @param attrs the shader IO attrs
+ /// @param addrspace the address of the variable
+ void EmitIOAttributes(uint32_t id,
+ const core::ir::IOAttributes& attrs,
+ core::AddressSpace addrspace);
+
/// Emit a load instruction.
/// @param load the load instruction to emit
void EmitLoad(core::ir::Load* load);
diff --git a/src/tint/lang/spirv/writer/raise/shader_io.cc b/src/tint/lang/spirv/writer/raise/shader_io.cc
index 2716b67..b6cab42 100644
--- a/src/tint/lang/spirv/writer/raise/shader_io.cc
+++ b/src/tint/lang/spirv/writer/raise/shader_io.cc
@@ -22,7 +22,6 @@
#include "src/tint/lang/core/ir/transform/shader_io.h"
#include "src/tint/lang/core/ir/validator.h"
#include "src/tint/lang/core/type/array.h"
-#include "src/tint/lang/core/type/struct.h"
using namespace tint::core::fluent_types; // NOLINT
using namespace tint::core::number_suffixes; // NOLINT
@@ -32,23 +31,14 @@
namespace {
/// PIMPL state for the parts of the shader IO transform specific to SPIR-V.
-/// For SPIR-V, we split builtins and locations into two separate structures each for input and
-/// output, and declare global variables for them. The wrapper entry point then loads from and
-/// stores to these variables.
-/// We also modify the type of the SampleMask builtin to be an array, as required by Vulkan.
+/// For SPIR-V, we declare a global variable for each input and output. The wrapper entry point then
+/// loads from and stores to these variables. We also modify the type of the SampleMask builtin to
+/// be an array, as required by Vulkan.
struct StateImpl : core::ir::transform::ShaderIOBackendState {
- /// The global variable for input builtins.
- core::ir::Var* builtin_input_var = nullptr;
- /// The global variable for input locations.
- core::ir::Var* location_input_var = nullptr;
- /// The global variable for output builtins.
- core::ir::Var* builtin_output_var = nullptr;
- /// The global variable for output locations.
- core::ir::Var* location_output_var = nullptr;
- /// The member indices for inputs.
- Vector<uint32_t, 4> input_indices;
- /// The member indices for outputs.
- Vector<uint32_t, 4> output_indices;
+ /// The input variables.
+ Vector<core::ir::Var*, 4> input_vars;
+ /// The output variables.
+ Vector<core::ir::Var*, 4> output_vars;
/// The configuration options.
const ShaderIOConfig& config;
@@ -63,69 +53,63 @@
/// Destructor
~StateImpl() override {}
- /// Split the members listed in @p entries into two separate structures for builtins and
- /// locations, and make global variables for them. Record the new member indices in @p indices.
- /// @param builtin_var the generated global variable for builtins
- /// @param location_var the generated global variable for locations
- /// @param indices the new member indices
- /// @param entries the entries to split
+ /// Declare a global variable for each IO entry listed in @p entries.
+ /// @param vars the list of variables
+ /// @param entries the entries to emit
/// @param addrspace the address to use for the global variables
/// @param access the access mode to use for the global variables
/// @param name_suffix the suffix to add to struct and variable names
- void MakeStructs(core::ir::Var*& builtin_var,
- core::ir::Var*& location_var,
- Vector<uint32_t, 4>* indices,
- Vector<core::type::Manager::StructMemberDesc, 4>& entries,
- core::AddressSpace addrspace,
- core::Access access,
- const char* name_suffix) {
- // Build separate lists of builtin and location entries and record their new indices.
- uint32_t next_builtin_idx = 0;
- uint32_t next_location_idx = 0;
- Vector<core::type::Manager::StructMemberDesc, 4> builtin_members;
- Vector<core::type::Manager::StructMemberDesc, 4> location_members;
+ void MakeVars(Vector<core::ir::Var*, 4>& vars,
+ Vector<core::type::Manager::StructMemberDesc, 4>& entries,
+ core::AddressSpace addrspace,
+ core::Access access,
+ const char* name_suffix) {
for (auto io : entries) {
+ StringStream name;
+ name << ir->NameOf(func).Name();
+
if (io.attributes.builtin) {
// SampleMask must be an array for Vulkan.
if (io.attributes.builtin.value() == core::BuiltinValue::kSampleMask) {
io.type = ty.array<u32, 1>();
}
- builtin_members.Push(io);
- indices->Push(next_builtin_idx++);
- } else {
- location_members.Push(io);
- indices->Push(next_location_idx++);
- }
- }
+ name << "_" << io.attributes.builtin.value();
- // Declare the structs and variables if needed.
- auto make_struct = [&](auto& members, const char* iotype) {
- auto name = ir->NameOf(func).Name() + iotype + name_suffix;
- auto* str = ty.Struct(ir->symbols.New(name + "Struct"), std::move(members));
- auto* var = b.Var(name, ty.ptr(addrspace, str, access));
- str->SetStructFlag(core::type::kBlock);
+ // Vulkan requires that fragment integer builtin inputs be Flat decorated.
+ if (func->Stage() == core::ir::Function::PipelineStage::kFragment &&
+ addrspace == core::AddressSpace::kIn &&
+ io.type->is_integer_scalar_or_vector()) {
+ io.attributes.interpolation = {core::InterpolationType::kFlat};
+ }
+ } else {
+ name << "_loc" << io.attributes.location.value();
+ }
+ name << name_suffix;
+
+ // Create an IO variable and add it to the root block.
+ auto* ptr = ty.ptr(addrspace, io.type, access);
+ auto* var = b.Var(name.str(), ptr);
+ var->SetAttributes(core::ir::IOAttributes{
+ io.attributes.location,
+ io.attributes.index,
+ io.attributes.builtin,
+ io.attributes.interpolation,
+ io.attributes.invariant,
+ });
b.RootBlock()->Append(var);
- return var;
- };
- if (!builtin_members.IsEmpty()) {
- builtin_var = make_struct(builtin_members, "_Builtin");
- }
- if (!location_members.IsEmpty()) {
- location_var = make_struct(location_members, "_Location");
+ vars.Push(var);
}
}
/// @copydoc ShaderIO::BackendState::FinalizeInputs
Vector<core::ir::FunctionParam*, 4> FinalizeInputs() override {
- MakeStructs(builtin_input_var, location_input_var, &input_indices, inputs,
- core::AddressSpace::kIn, core::Access::kRead, "Inputs");
+ MakeVars(input_vars, inputs, core::AddressSpace::kIn, core::Access::kRead, "_Input");
return tint::Empty;
}
/// @copydoc ShaderIO::BackendState::FinalizeOutputs
core::ir::Value* FinalizeOutputs() override {
- MakeStructs(builtin_output_var, location_output_var, &output_indices, outputs,
- core::AddressSpace::kOut, core::Access::kWrite, "Outputs");
+ MakeVars(output_vars, outputs, core::AddressSpace::kOut, core::Access::kWrite, "_Output");
return nullptr;
}
@@ -133,16 +117,12 @@
core::ir::Value* GetInput(core::ir::Builder& builder, uint32_t idx) override {
// Load the input from the global variable declared earlier.
auto* ptr = ty.ptr(core::AddressSpace::kIn, inputs[idx].type, core::Access::kRead);
- core::ir::Access* from = nullptr;
+ auto* from = input_vars[idx]->Result();
if (inputs[idx].attributes.builtin) {
if (inputs[idx].attributes.builtin.value() == core::BuiltinValue::kSampleMask) {
// SampleMask becomes an array for SPIR-V, so load from the first element.
- from = builder.Access(ptr, builtin_input_var, u32(input_indices[idx]), 0_u);
- } else {
- from = builder.Access(ptr, builtin_input_var, u32(input_indices[idx]));
+ from = builder.Access(ptr, input_vars[idx], 0_u)->Result();
}
- } else {
- from = builder.Access(ptr, location_input_var, u32(input_indices[idx]));
}
return builder.Load(from)->Result();
}
@@ -151,21 +131,17 @@
void SetOutput(core::ir::Builder& builder, uint32_t idx, core::ir::Value* value) override {
// Store the output to the global variable declared earlier.
auto* ptr = ty.ptr(core::AddressSpace::kOut, outputs[idx].type, core::Access::kWrite);
- core::ir::Access* to = nullptr;
+ auto* to = output_vars[idx]->Result();
if (outputs[idx].attributes.builtin) {
if (outputs[idx].attributes.builtin.value() == core::BuiltinValue::kSampleMask) {
// SampleMask becomes an array for SPIR-V, so store to the first element.
- to = builder.Access(ptr, builtin_output_var, u32(output_indices[idx]), 0_u);
- } else {
- to = builder.Access(ptr, builtin_output_var, u32(output_indices[idx]));
+ to = builder.Access(ptr, to, 0_u)->Result();
}
// Clamp frag_depth values if necessary.
if (outputs[idx].attributes.builtin.value() == core::BuiltinValue::kFragDepth) {
value = ClampFragDepth(builder, value);
}
- } else {
- to = builder.Access(ptr, location_output_var, u32(output_indices[idx]));
}
builder.Store(to, value);
}
diff --git a/src/tint/lang/spirv/writer/raise/shader_io.h b/src/tint/lang/spirv/writer/raise/shader_io.h
index 5924c12..29d9e73 100644
--- a/src/tint/lang/spirv/writer/raise/shader_io.h
+++ b/src/tint/lang/spirv/writer/raise/shader_io.h
@@ -32,8 +32,8 @@
bool clamp_frag_depth = false;
};
-/// ShaderIO is a transform that modifies each entry point function's parameters and return
-/// value to prepare them for SPIR-V codegen.
+/// ShaderIO is a transform that moves each entry point function's parameters and return value to
+/// global variables to prepare them for SPIR-V codegen.
/// @param module the module to transform
/// @param config the configuration
/// @returns an error string on failure
diff --git a/src/tint/lang/spirv/writer/raise/shader_io_test.cc b/src/tint/lang/spirv/writer/raise/shader_io_test.cc
index 0638d50..8814aee 100644
--- a/src/tint/lang/spirv/writer/raise/shader_io_test.cc
+++ b/src/tint/lang/spirv/writer/raise/shader_io_test.cc
@@ -94,27 +94,19 @@
EXPECT_EQ(src, str());
auto* expect = R"(
-foo_BuiltinInputsStruct = struct @align(16), @block {
- front_facing:bool @offset(0), @builtin(front_facing)
- position:vec4<f32> @offset(16), @invariant, @builtin(position)
-}
-
-foo_LocationInputsStruct = struct @align(4), @block {
- color1:f32 @offset(0), @location(0)
- color2:f32 @offset(4), @location(1), @interpolate(linear, sample)
-}
-
%b1 = block { # root
- %foo_BuiltinInputs:ptr<__in, foo_BuiltinInputsStruct, read> = var
- %foo_LocationInputs:ptr<__in, foo_LocationInputsStruct, read> = var
+ %foo_front_facing_Input:ptr<__in, bool, read> = var @builtin(front_facing)
+ %foo_position_Input:ptr<__in, vec4<f32>, read> = var @invariant @builtin(position)
+ %foo_loc0_Input:ptr<__in, f32, read> = var @location(0)
+ %foo_loc1_Input:ptr<__in, f32, read> = var @location(1) @interpolate(linear, sample)
}
%foo_inner = func(%front_facing:bool, %position:vec4<f32>, %color1:f32, %color2:f32):void -> %b2 {
%b2 = block {
if %front_facing [t: %b3] { # if_1
%b3 = block { # true
- %8:f32 = add %color1, %color2
- %9:vec4<f32> = mul %position, %8
+ %10:f32 = add %color1, %color2
+ %11:vec4<f32> = mul %position, %10
exit_if # if_1
}
}
@@ -123,15 +115,11 @@
}
%foo = @fragment func():void -> %b4 {
%b4 = block {
- %11:ptr<__in, bool, read> = access %foo_BuiltinInputs, 0u
- %12:bool = load %11
- %13:ptr<__in, vec4<f32>, read> = access %foo_BuiltinInputs, 1u
- %14:vec4<f32> = load %13
- %15:ptr<__in, f32, read> = access %foo_LocationInputs, 0u
- %16:f32 = load %15
- %17:ptr<__in, f32, read> = access %foo_LocationInputs, 1u
- %18:f32 = load %17
- %19:void = call %foo_inner, %12, %14, %16, %18
+ %13:bool = load %foo_front_facing_Input
+ %14:vec4<f32> = load %foo_position_Input
+ %15:f32 = load %foo_loc0_Input
+ %16:f32 = load %foo_loc1_Input
+ %17:void = call %foo_inner, %13, %14, %15, %16
ret
}
}
@@ -226,31 +214,23 @@
color2:f32 @offset(36)
}
-foo_BuiltinInputsStruct = struct @align(16), @block {
- Inputs_front_facing:bool @offset(0), @builtin(front_facing)
- Inputs_position:vec4<f32> @offset(16), @invariant, @builtin(position)
-}
-
-foo_LocationInputsStruct = struct @align(4), @block {
- Inputs_color1:f32 @offset(0), @location(0)
- Inputs_color2:f32 @offset(4), @location(1), @interpolate(linear, sample)
-}
-
%b1 = block { # root
- %foo_BuiltinInputs:ptr<__in, foo_BuiltinInputsStruct, read> = var
- %foo_LocationInputs:ptr<__in, foo_LocationInputsStruct, read> = var
+ %foo_front_facing_Input:ptr<__in, bool, read> = var @builtin(front_facing)
+ %foo_position_Input:ptr<__in, vec4<f32>, read> = var @invariant @builtin(position)
+ %foo_loc0_Input:ptr<__in, f32, read> = var @location(0)
+ %foo_loc1_Input:ptr<__in, f32, read> = var @location(1) @interpolate(linear, sample)
}
%foo_inner = func(%inputs:Inputs):void -> %b2 {
%b2 = block {
- %5:bool = access %inputs, 0i
- if %5 [t: %b3] { # if_1
+ %7:bool = access %inputs, 0i
+ if %7 [t: %b3] { # if_1
%b3 = block { # true
- %6:vec4<f32> = access %inputs, 1i
- %7:f32 = access %inputs, 2i
- %8:f32 = access %inputs, 3i
- %9:f32 = add %7, %8
- %10:vec4<f32> = mul %6, %9
+ %8:vec4<f32> = access %inputs, 1i
+ %9:f32 = access %inputs, 2i
+ %10:f32 = access %inputs, 3i
+ %11:f32 = add %9, %10
+ %12:vec4<f32> = mul %8, %11
exit_if # if_1
}
}
@@ -259,16 +239,12 @@
}
%foo = @fragment func():void -> %b4 {
%b4 = block {
- %12:ptr<__in, bool, read> = access %foo_BuiltinInputs, 0u
- %13:bool = load %12
- %14:ptr<__in, vec4<f32>, read> = access %foo_BuiltinInputs, 1u
- %15:vec4<f32> = load %14
- %16:ptr<__in, f32, read> = access %foo_LocationInputs, 0u
- %17:f32 = load %16
- %18:ptr<__in, f32, read> = access %foo_LocationInputs, 1u
- %19:f32 = load %18
- %20:Inputs = construct %13, %15, %17, %19
- %21:void = call %foo_inner, %20
+ %14:bool = load %foo_front_facing_Input
+ %15:vec4<f32> = load %foo_position_Input
+ %16:f32 = load %foo_loc0_Input
+ %17:f32 = load %foo_loc1_Input
+ %18:Inputs = construct %14, %15, %16, %17
+ %19:void = call %foo_inner, %18
ret
}
}
@@ -347,29 +323,21 @@
color1:f32 @offset(16)
}
-foo_BuiltinInputsStruct = struct @align(16), @block {
- front_facing:bool @offset(0), @builtin(front_facing)
- Inputs_position:vec4<f32> @offset(16), @invariant, @builtin(position)
-}
-
-foo_LocationInputsStruct = struct @align(4), @block {
- Inputs_color1:f32 @offset(0), @location(0)
- color2:f32 @offset(4), @location(1), @interpolate(linear, sample)
-}
-
%b1 = block { # root
- %foo_BuiltinInputs:ptr<__in, foo_BuiltinInputsStruct, read> = var
- %foo_LocationInputs:ptr<__in, foo_LocationInputsStruct, read> = var
+ %foo_front_facing_Input:ptr<__in, bool, read> = var @builtin(front_facing)
+ %foo_position_Input:ptr<__in, vec4<f32>, read> = var @invariant @builtin(position)
+ %foo_loc0_Input:ptr<__in, f32, read> = var @location(0)
+ %foo_loc1_Input:ptr<__in, f32, read> = var @location(1) @interpolate(linear, sample)
}
%foo_inner = func(%front_facing:bool, %inputs:Inputs, %color2:f32):void -> %b2 {
%b2 = block {
if %front_facing [t: %b3] { # if_1
%b3 = block { # true
- %7:vec4<f32> = access %inputs, 0i
- %8:f32 = access %inputs, 1i
- %9:f32 = add %8, %color2
- %10:vec4<f32> = mul %7, %9
+ %9:vec4<f32> = access %inputs, 0i
+ %10:f32 = access %inputs, 1i
+ %11:f32 = add %10, %color2
+ %12:vec4<f32> = mul %9, %11
exit_if # if_1
}
}
@@ -378,16 +346,12 @@
}
%foo = @fragment func():void -> %b4 {
%b4 = block {
- %12:ptr<__in, bool, read> = access %foo_BuiltinInputs, 0u
- %13:bool = load %12
- %14:ptr<__in, vec4<f32>, read> = access %foo_BuiltinInputs, 1u
- %15:vec4<f32> = load %14
- %16:ptr<__in, f32, read> = access %foo_LocationInputs, 0u
- %17:f32 = load %16
- %18:Inputs = construct %15, %17
- %19:ptr<__in, f32, read> = access %foo_LocationInputs, 1u
- %20:f32 = load %19
- %21:void = call %foo_inner, %13, %18, %20
+ %14:bool = load %foo_front_facing_Input
+ %15:vec4<f32> = load %foo_position_Input
+ %16:f32 = load %foo_loc0_Input
+ %17:Inputs = construct %15, %16
+ %18:f32 = load %foo_loc1_Input
+ %19:void = call %foo_inner, %14, %17, %18
ret
}
}
@@ -421,12 +385,8 @@
EXPECT_EQ(src, str());
auto* expect = R"(
-foo_BuiltinOutputsStruct = struct @align(16), @block {
- tint_symbol:vec4<f32> @offset(0), @invariant, @builtin(position)
-}
-
%b1 = block { # root
- %foo_BuiltinOutputs:ptr<__out, foo_BuiltinOutputsStruct, write> = var
+ %foo_position_Output:ptr<__out, vec4<f32>, write> = var @invariant @builtin(position)
}
%foo_inner = func():vec4<f32> -> %b2 {
@@ -438,8 +398,7 @@
%foo = @vertex func():void -> %b3 {
%b3 = block {
%5:vec4<f32> = call %foo_inner
- %6:ptr<__out, vec4<f32>, write> = access %foo_BuiltinOutputs, 0u
- store %6, %5
+ store %foo_position_Output, %5
ret
}
}
@@ -472,12 +431,8 @@
EXPECT_EQ(src, str());
auto* expect = R"(
-foo_LocationOutputsStruct = struct @align(16), @block {
- tint_symbol:vec4<f32> @offset(0), @location(1)
-}
-
%b1 = block { # root
- %foo_LocationOutputs:ptr<__out, foo_LocationOutputsStruct, write> = var
+ %foo_loc1_Output:ptr<__out, vec4<f32>, write> = var @location(1)
}
%foo_inner = func():vec4<f32> -> %b2 {
@@ -489,8 +444,7 @@
%foo = @fragment func():void -> %b3 {
%b3 = block {
%5:vec4<f32> = call %foo_inner
- %6:ptr<__out, vec4<f32>, write> = access %foo_LocationOutputs, 0u
- store %6, %5
+ store %foo_loc1_Output, %5
ret
}
}
@@ -559,39 +513,28 @@
color2:f32 @offset(20)
}
-foo_BuiltinOutputsStruct = struct @align(16), @block {
- Outputs_position:vec4<f32> @offset(0), @invariant, @builtin(position)
-}
-
-foo_LocationOutputsStruct = struct @align(4), @block {
- Outputs_color1:f32 @offset(0), @location(0)
- Outputs_color2:f32 @offset(4), @location(1), @interpolate(linear, sample)
-}
-
%b1 = block { # root
- %foo_BuiltinOutputs:ptr<__out, foo_BuiltinOutputsStruct, write> = var
- %foo_LocationOutputs:ptr<__out, foo_LocationOutputsStruct, write> = var
+ %foo_position_Output:ptr<__out, vec4<f32>, write> = var @invariant @builtin(position)
+ %foo_loc0_Output:ptr<__out, f32, write> = var @location(0)
+ %foo_loc1_Output:ptr<__out, f32, write> = var @location(1) @interpolate(linear, sample)
}
%foo_inner = func():Outputs -> %b2 {
%b2 = block {
- %4:vec4<f32> = construct 0.0f
- %5:Outputs = construct %4, 0.25f, 0.75f
- ret %5
+ %5:vec4<f32> = construct 0.0f
+ %6:Outputs = construct %5, 0.25f, 0.75f
+ ret %6
}
}
%foo = @vertex func():void -> %b3 {
%b3 = block {
- %7:Outputs = call %foo_inner
- %8:vec4<f32> = access %7, 0u
- %9:ptr<__out, vec4<f32>, write> = access %foo_BuiltinOutputs, 0u
- store %9, %8
- %10:f32 = access %7, 1u
- %11:ptr<__out, f32, write> = access %foo_LocationOutputs, 0u
- store %11, %10
- %12:f32 = access %7, 2u
- %13:ptr<__out, f32, write> = access %foo_LocationOutputs, 1u
- store %13, %12
+ %8:Outputs = call %foo_inner
+ %9:vec4<f32> = access %8, 0u
+ store %foo_position_Output, %9
+ %10:f32 = access %8, 1u
+ store %foo_loc0_Output, %10
+ %11:f32 = access %8, 2u
+ store %foo_loc1_Output, %11
ret
}
}
@@ -638,6 +581,7 @@
auto* inputs = b.FunctionParam("inputs", str_ty);
ep->SetStage(core::ir::Function::PipelineStage::kFragment);
ep->SetParams({inputs});
+ ep->SetReturnLocation(0u, {});
b.Append(ep->Block(), [&] { //
auto* position = b.Access(vec4f, inputs, 0_u);
@@ -660,7 +604,7 @@
ret %4
}
}
-%frag = @fragment func(%inputs:Interface):vec4<f32> -> %b2 {
+%frag = @fragment func(%inputs:Interface):vec4<f32> [@location(0)] -> %b2 {
%b2 = block {
%7:vec4<f32> = access %inputs, 0u
%8:vec4<f32> = access %inputs, 1u
@@ -677,32 +621,12 @@
color:vec4<f32> @offset(16)
}
-vert_BuiltinOutputsStruct = struct @align(16), @block {
- Interface_position:vec4<f32> @offset(0), @builtin(position)
-}
-
-vert_LocationOutputsStruct = struct @align(16), @block {
- Interface_color:vec4<f32> @offset(0), @location(0)
-}
-
-frag_BuiltinInputsStruct = struct @align(16), @block {
- Interface_position:vec4<f32> @offset(0), @builtin(position)
-}
-
-frag_LocationInputsStruct = struct @align(16), @block {
- Interface_color:vec4<f32> @offset(0), @location(0)
-}
-
-frag_LocationOutputsStruct = struct @align(16), @block {
- tint_symbol:vec4<f32> @offset(0)
-}
-
%b1 = block { # root
- %vert_BuiltinOutputs:ptr<__out, vert_BuiltinOutputsStruct, write> = var
- %vert_LocationOutputs:ptr<__out, vert_LocationOutputsStruct, write> = var
- %frag_BuiltinInputs:ptr<__in, frag_BuiltinInputsStruct, read> = var
- %frag_LocationInputs:ptr<__in, frag_LocationInputsStruct, read> = var
- %frag_LocationOutputs:ptr<__out, frag_LocationOutputsStruct, write> = var
+ %vert_position_Output:ptr<__out, vec4<f32>, write> = var @builtin(position)
+ %vert_loc0_Output:ptr<__out, vec4<f32>, write> = var @location(0)
+ %frag_position_Input:ptr<__in, vec4<f32>, read> = var @builtin(position)
+ %frag_loc0_Input:ptr<__in, vec4<f32>, read> = var @location(0)
+ %frag_loc0_Output:ptr<__out, vec4<f32>, write> = var @location(0)
}
%vert_inner = func():Interface -> %b2 {
@@ -725,24 +649,19 @@
%b4 = block {
%16:Interface = call %vert_inner
%17:vec4<f32> = access %16, 0u
- %18:ptr<__out, vec4<f32>, write> = access %vert_BuiltinOutputs, 0u
- store %18, %17
- %19:vec4<f32> = access %16, 1u
- %20:ptr<__out, vec4<f32>, write> = access %vert_LocationOutputs, 0u
- store %20, %19
+ store %vert_position_Output, %17
+ %18:vec4<f32> = access %16, 1u
+ store %vert_loc0_Output, %18
ret
}
}
%frag = @fragment func():void -> %b5 {
%b5 = block {
- %22:ptr<__in, vec4<f32>, read> = access %frag_BuiltinInputs, 0u
- %23:vec4<f32> = load %22
- %24:ptr<__in, vec4<f32>, read> = access %frag_LocationInputs, 0u
- %25:vec4<f32> = load %24
- %26:Interface = construct %23, %25
- %27:vec4<f32> = call %frag_inner, %26
- %28:ptr<__out, vec4<f32>, write> = access %frag_LocationOutputs, 0u
- store %28, %27
+ %20:vec4<f32> = load %frag_position_Input
+ %21:vec4<f32> = load %frag_loc0_Input
+ %22:Interface = construct %20, %21
+ %23:vec4<f32> = call %frag_inner, %22
+ store %frag_loc0_Output, %23
ret
}
}
@@ -805,18 +724,10 @@
color:vec4<f32> @offset(16)
}
-vert_BuiltinOutputsStruct = struct @align(16), @block {
- Outputs_position:vec4<f32> @offset(0), @builtin(position)
-}
-
-vert_LocationOutputsStruct = struct @align(16), @block {
- Outputs_color:vec4<f32> @offset(0), @location(0)
-}
-
%b1 = block { # root
%1:ptr<storage, Outputs, read> = var
- %vert_BuiltinOutputs:ptr<__out, vert_BuiltinOutputsStruct, write> = var
- %vert_LocationOutputs:ptr<__out, vert_LocationOutputsStruct, write> = var
+ %vert_position_Output:ptr<__out, vec4<f32>, write> = var @builtin(position)
+ %vert_loc0_Output:ptr<__out, vec4<f32>, write> = var @location(0)
}
%vert_inner = func():Outputs -> %b2 {
@@ -829,11 +740,9 @@
%b3 = block {
%7:Outputs = call %vert_inner
%8:vec4<f32> = access %7, 0u
- %9:ptr<__out, vec4<f32>, write> = access %vert_BuiltinOutputs, 0u
- store %9, %8
- %10:vec4<f32> = access %7, 1u
- %11:ptr<__out, vec4<f32>, write> = access %vert_LocationOutputs, 0u
- store %11, %10
+ store %vert_position_Output, %8
+ %9:vec4<f32> = access %7, 1u
+ store %vert_loc0_Output, %9
ret
}
}
@@ -894,22 +803,10 @@
mask:u32 @offset(4)
}
-foo_BuiltinInputsStruct = struct @align(4), @block {
- mask_in:array<u32, 1> @offset(0), @builtin(sample_mask)
-}
-
-foo_BuiltinOutputsStruct = struct @align(4), @block {
- Outputs_mask:array<u32, 1> @offset(0), @builtin(sample_mask)
-}
-
-foo_LocationOutputsStruct = struct @align(4), @block {
- Outputs_color:f32 @offset(0), @location(0)
-}
-
%b1 = block { # root
- %foo_BuiltinInputs:ptr<__in, foo_BuiltinInputsStruct, read> = var
- %foo_BuiltinOutputs:ptr<__out, foo_BuiltinOutputsStruct, write> = var
- %foo_LocationOutputs:ptr<__out, foo_LocationOutputsStruct, write> = var
+ %foo_sample_mask_Input:ptr<__in, array<u32, 1>, read> = var @builtin(sample_mask)
+ %foo_loc0_Output:ptr<__out, f32, write> = var @location(0)
+ %foo_sample_mask_Output:ptr<__out, array<u32, 1>, write> = var @builtin(sample_mask)
}
%foo_inner = func(%mask_in:u32):Outputs -> %b2 {
@@ -920,15 +817,14 @@
}
%foo = @fragment func():void -> %b3 {
%b3 = block {
- %8:ptr<__in, u32, read> = access %foo_BuiltinInputs, 0u, 0u
+ %8:ptr<__in, u32, read> = access %foo_sample_mask_Input, 0u
%9:u32 = load %8
%10:Outputs = call %foo_inner, %9
%11:f32 = access %10, 0u
- %12:ptr<__out, f32, write> = access %foo_LocationOutputs, 0u
- store %12, %11
- %13:u32 = access %10, 1u
- %14:ptr<__out, u32, write> = access %foo_BuiltinOutputs, 0u, 0u
- store %14, %13
+ store %foo_loc0_Output, %11
+ %12:u32 = access %10, 1u
+ %13:ptr<__out, u32, write> = access %foo_sample_mask_Output, 0u
+ store %13, %12
ret
}
}
@@ -984,22 +880,14 @@
depth:f32 @offset(4)
}
-foo_BuiltinOutputsStruct = struct @align(4), @block {
- Outputs_depth:f32 @offset(0), @builtin(frag_depth)
-}
-
-foo_LocationOutputsStruct = struct @align(4), @block {
- Outputs_color:f32 @offset(0), @location(0)
-}
-
FragDepthClampArgs = struct @align(4), @block {
min:f32 @offset(0)
max:f32 @offset(4)
}
%b1 = block { # root
- %foo_BuiltinOutputs:ptr<__out, foo_BuiltinOutputsStruct, write> = var
- %foo_LocationOutputs:ptr<__out, foo_LocationOutputsStruct, write> = var
+ %foo_loc0_Output:ptr<__out, f32, write> = var @location(0)
+ %foo_frag_depth_Output:ptr<__out, f32, write> = var @builtin(frag_depth)
%tint_frag_depth_clamp_args:ptr<push_constant, FragDepthClampArgs, read_write> = var
}
@@ -1013,15 +901,13 @@
%b3 = block {
%7:Outputs = call %foo_inner
%8:f32 = access %7, 0u
- %9:ptr<__out, f32, write> = access %foo_LocationOutputs, 0u
- store %9, %8
- %10:f32 = access %7, 1u
- %11:ptr<__out, f32, write> = access %foo_BuiltinOutputs, 0u
- %12:FragDepthClampArgs = load %tint_frag_depth_clamp_args
- %13:f32 = access %12, 0u
- %14:f32 = access %12, 1u
- %15:f32 = clamp %10, %13, %14
- store %11, %15
+ store %foo_loc0_Output, %8
+ %9:f32 = access %7, 1u
+ %10:FragDepthClampArgs = load %tint_frag_depth_clamp_args
+ %11:f32 = access %10, 0u
+ %12:f32 = access %10, 1u
+ %13:f32 = clamp %9, %11, %12
+ store %foo_frag_depth_Output, %13
ret
}
}
diff --git a/src/tint/lang/wgsl/writer/ir_to_program/rename_conflicts.cc b/src/tint/lang/wgsl/writer/ir_to_program/rename_conflicts.cc
index 0d61b8a..7ef8cf8 100644
--- a/src/tint/lang/wgsl/writer/ir_to_program/rename_conflicts.cc
+++ b/src/tint/lang/wgsl/writer/ir_to_program/rename_conflicts.cc
@@ -25,6 +25,7 @@
#include "src/tint/lang/core/ir/validator.h"
#include "src/tint/lang/core/ir/var.h"
#include "src/tint/lang/core/type/matrix.h"
+#include "src/tint/lang/core/type/pointer.h"
#include "src/tint/lang/core/type/scalar.h"
#include "src/tint/lang/core/type/struct.h"
#include "src/tint/lang/core/type/vector.h"