Import Tint changes from Dawn
Changes:
- 21d23ec2345ddf06502438ca1376b937059b8413 [ir][spirv-writer] Scalarize quantizeToF16 by James Price <jrprice@google.com>
- 72643887aebc7688a846e12962cfa0689abd2bba [ir][spirv-writer] Emit NonReadable and NonWritable by James Price <jrprice@google.com>
- 757dd563d2ad9b636cd6772f95bc948430c80bdc [ir][spirv-writer] Emit vertex point size builtin by James Price <jrprice@google.com>
- fef99889810618448a4bc0d22b43a5129434998e [ir][spirv-writer] Support dual-source blending by James Price <jrprice@google.com>
- 0c1f1c9c141a049fa585962c0c205a855bf14f72 [ir] Support subgroup builtin inputs by James Price <jrprice@google.com>
- d69b6f39129c8591c32de12ae9241268aa7c6443 [ir] Strip interpolation attributes when invalid by James Price <jrprice@google.com>
- 86f780dfb461a1aedc8e0b34d92c18c67f656d01 [ir][spirv-writer] Emit shader IO without structs by James Price <jrprice@google.com>
- b27bce431140c1c01cb3690dfe24265e4a635300 D3D12: Replace deprecated IDxcCompiler with IDxcCompiler3 by Jiawei Shao <jiawei.shao@intel.com>
GitOrigin-RevId: 21d23ec2345ddf06502438ca1376b937059b8413
Change-Id: I4add5e60c99aeb68b297010d8e11d03eda85ef63
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/151687
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@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/function_param.cc b/src/tint/lang/core/ir/function_param.cc
index 1b76ff6..877364e 100644
--- a/src/tint/lang/core/ir/function_param.cc
+++ b/src/tint/lang/core/ir/function_param.cc
@@ -50,6 +50,10 @@
return "sample_index";
case FunctionParam::Builtin::kSampleMask:
return "sample_mask";
+ case FunctionParam::Builtin::kSubgroupInvocationId:
+ return "subgroup_invocation_id";
+ case FunctionParam::Builtin::kSubgroupSize:
+ return "subgroup_size";
}
return "<unknown>";
}
diff --git a/src/tint/lang/core/ir/function_param.h b/src/tint/lang/core/ir/function_param.h
index f8f83d4..d9bf75c 100644
--- a/src/tint/lang/core/ir/function_param.h
+++ b/src/tint/lang/core/ir/function_param.h
@@ -53,6 +53,10 @@
kSampleIndex,
/// Builtin Sample mask
kSampleMask,
+ /// Builtin Subgroup invocation id
+ kSubgroupInvocationId,
+ /// Builtin Subgroup size
+ kSubgroupSize,
};
/// Constructor
diff --git a/src/tint/lang/core/ir/transform/shader_io.cc b/src/tint/lang/core/ir/transform/shader_io.cc
index f279b86..3edc00d 100644
--- a/src/tint/lang/core/ir/transform/shader_io.cc
+++ b/src/tint/lang/core/ir/transform/shader_io.cc
@@ -52,6 +52,10 @@
return core::BuiltinValue::kSampleIndex;
case FunctionParam::Builtin::kSampleMask:
return core::BuiltinValue::kSampleMask;
+ case FunctionParam::Builtin::kSubgroupInvocationId:
+ return core::BuiltinValue::kSubgroupInvocationId;
+ case FunctionParam::Builtin::kSubgroupSize:
+ return core::BuiltinValue::kSubgroupSize;
}
return core::BuiltinValue::kUndefined;
}
@@ -100,6 +104,15 @@
// Process the parameters and return value to prepare for building a wrapper function.
GatherInputs();
GatherOutput();
+
+ // Add an output for the vertex point size if needed.
+ std::optional<uint32_t> vertex_point_size_index;
+ if (func->Stage() == Function::PipelineStage::kVertex && backend->NeedsVertexPointSize()) {
+ vertex_point_size_index =
+ backend->AddOutput(ir->symbols.New("vertex_point_size"), ty.f32(),
+ {{}, {}, {BuiltinValue::kPointSize}, {}, false});
+ }
+
auto new_params = backend->FinalizeInputs();
auto* new_ret_val = backend->FinalizeOutputs();
@@ -124,6 +137,9 @@
auto inner_call_args = BuildInnerCallArgs(wrapper);
auto* inner_result = wrapper.Call(func->ReturnType(), func, std::move(inner_call_args));
SetOutputs(wrapper, inner_result->Result());
+ if (vertex_point_size_index) {
+ backend->SetOutput(wrapper, vertex_point_size_index.value(), b.Constant(1_f));
+ }
// Return the new result.
wrapper.Return(ep, new_ret_val);
@@ -135,8 +151,12 @@
if (auto* str = param->Type()->As<core::type::Struct>()) {
for (auto* member : str->Members()) {
auto name = str->Name().Name() + "_" + member->Name().Name();
- backend->AddInput(ir->symbols.Register(name), member->Type(),
- member->Attributes());
+ auto attributes = member->Attributes();
+ if (attributes.interpolation &&
+ func->Stage() != Function::PipelineStage::kFragment) {
+ attributes.interpolation = {};
+ }
+ backend->AddInput(ir->symbols.Register(name), member->Type(), attributes);
members_to_strip.Add(member);
}
} else {
@@ -144,7 +164,7 @@
core::type::StructMemberAttributes attributes;
if (auto loc = param->Location()) {
attributes.location = loc->value;
- if (loc->interpolation) {
+ if (loc->interpolation && func->Stage() == Function::PipelineStage::kFragment) {
attributes.interpolation = *loc->interpolation;
}
param->ClearLocation();
@@ -170,8 +190,11 @@
if (auto* str = func->ReturnType()->As<core::type::Struct>()) {
for (auto* member : str->Members()) {
auto name = str->Name().Name() + "_" + member->Name().Name();
- backend->AddOutput(ir->symbols.Register(name), member->Type(),
- member->Attributes());
+ auto attributes = member->Attributes();
+ if (attributes.interpolation && func->Stage() != Function::PipelineStage::kVertex) {
+ attributes.interpolation = {};
+ }
+ backend->AddOutput(ir->symbols.Register(name), member->Type(), attributes);
members_to_strip.Add(member);
}
} else {
@@ -179,6 +202,9 @@
core::type::StructMemberAttributes attributes;
if (auto loc = func->ReturnLocation()) {
attributes.location = loc->value;
+ if (loc->interpolation && func->Stage() == Function::PipelineStage::kVertex) {
+ attributes.interpolation = *loc->interpolation;
+ }
func->ClearReturnLocation();
} else if (auto builtin = func->ReturnBuiltin()) {
attributes.builtin = ReturnBuiltin(*builtin);
diff --git a/src/tint/lang/core/ir/transform/shader_io.h b/src/tint/lang/core/ir/transform/shader_io.h
index e3e7194..22210c0 100644
--- a/src/tint/lang/core/ir/transform/shader_io.h
+++ b/src/tint/lang/core/ir/transform/shader_io.h
@@ -37,20 +37,24 @@
/// @param name the name of the input
/// @param type the type of the input
/// @param attributes the IO attributes
- virtual void AddInput(Symbol name,
- const core::type::Type* type,
- core::type::StructMemberAttributes attributes) {
+ /// @returns the index of the input
+ virtual uint32_t AddInput(Symbol name,
+ const core::type::Type* type,
+ core::type::StructMemberAttributes attributes) {
inputs.Push({name, type, std::move(attributes)});
+ return uint32_t(inputs.Length() - 1);
}
/// Add an output.
/// @param name the name of the output
/// @param type the type of the output
/// @param attributes the IO attributes
- virtual void AddOutput(Symbol name,
- const core::type::Type* type,
- core::type::StructMemberAttributes attributes) {
+ /// @returns the index of the output
+ virtual uint32_t AddOutput(Symbol name,
+ const core::type::Type* type,
+ core::type::StructMemberAttributes attributes) {
outputs.Push({name, type, std::move(attributes)});
+ return uint32_t(outputs.Length() - 1);
}
/// Finalize the shader inputs and create any state needed for the new entry point function.
@@ -73,6 +77,9 @@
/// @param value the value to set
virtual void SetOutput(Builder& builder, uint32_t idx, Value* value) = 0;
+ /// @returns true if a vertex point size builtin should be added
+ virtual bool NeedsVertexPointSize() const { return false; }
+
protected:
/// The IR module.
Module* ir = nullptr;
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/hlsl/validate/hlsl.cc b/src/tint/lang/hlsl/validate/hlsl.cc
index af19505..6b5a759 100644
--- a/src/tint/lang/hlsl/validate/hlsl.cc
+++ b/src/tint/lang/hlsl/validate/hlsl.cc
@@ -71,7 +71,7 @@
// Match Dawn's compile flags
// See dawn\src\dawn_native\d3d12\RenderPipelineD3D12.cpp
- // and dawn_native\d3d12\ShaderModuleD3D12.cpp (GetDXCArguments)
+ // and dawn_native\d3d\ShaderUtils.cpp (GetDXCArguments)
auto res = dxc(
"-T " + std::string(stage_prefix) + "_" + std::string(shader_model_version), // Profile
"-HV 2018", // Use HLSL 2018
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/function_test.cc b/src/tint/lang/spirv/writer/function_test.cc
index 0f41ed6..dcbb23c 100644
--- a/src/tint/lang/spirv/writer/function_test.cc
+++ b/src/tint/lang/spirv/writer/function_test.cc
@@ -300,5 +300,75 @@
EXPECT_INST("%result = OpFunctionCall %void %foo");
}
+TEST_F(SpirvWriterTest, Function_ShaderIO_VertexPointSize) {
+ auto* func = b.Function("main", ty.vec4<f32>(), core::ir::Function::PipelineStage::kVertex);
+ func->SetReturnBuiltin(core::ir::Function::ReturnBuiltin::kPosition);
+ b.Append(func->Block(), [&] { //
+ b.Return(func, b.Construct(ty.vec4<f32>(), 0.5_f));
+ });
+
+ Options options;
+ options.emit_vertex_point_size = true;
+ ASSERT_TRUE(Generate(options)) << Error() << output_;
+ EXPECT_INST(
+ R"(OpEntryPoint Vertex %main "main" %main_position_Output %main___point_size_Output)");
+ EXPECT_INST(R"(
+ OpDecorate %main_position_Output BuiltIn Position
+ OpDecorate %main___point_size_Output BuiltIn PointSize
+)");
+ EXPECT_INST(R"(
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%main_position_Output = OpVariable %_ptr_Output_v4float Output
+%_ptr_Output_float = OpTypePointer Output %float
+%main___point_size_Output = OpVariable %_ptr_Output_float Output
+)");
+ EXPECT_INST(R"(
+ %main = OpFunction %void None %14
+ %15 = OpLabel
+ %16 = OpFunctionCall %v4float %main_inner
+ OpStore %main_position_Output %16
+ OpStore %main___point_size_Output %float_1
+ OpReturn
+ OpFunctionEnd
+)");
+}
+
+TEST_F(SpirvWriterTest, Function_ShaderIO_DualSourceBlend) {
+ auto* outputs = ty.Struct(mod.symbols.New("Outputs"),
+ {
+ {mod.symbols.Register("a"), ty.f32(), {0u, 0u, {}, {}, false}},
+ {mod.symbols.Register("b"), ty.f32(), {0u, 1u, {}, {}, false}},
+ });
+
+ auto* func = b.Function("main", outputs, core::ir::Function::PipelineStage::kFragment);
+ b.Append(func->Block(), [&] { //
+ b.Return(func, b.Construct(outputs, 0.5_f, 0.6_f));
+ });
+
+ ASSERT_TRUE(Generate()) << Error() << output_;
+ EXPECT_INST(R"(OpEntryPoint Fragment %main "main" %main_loc0_Output %main_loc0_Output_0)");
+ EXPECT_INST(R"(
+ OpDecorate %main_loc0_Output Location 0
+ OpDecorate %main_loc0_Output Index 0
+ OpDecorate %main_loc0_Output_0 Location 0
+ OpDecorate %main_loc0_Output_0 Index 1
+ )");
+ EXPECT_INST(R"(
+%main_loc0_Output = OpVariable %_ptr_Output_float Output
+%main_loc0_Output_0 = OpVariable %_ptr_Output_float Output
+ )");
+ EXPECT_INST(R"(
+ %main = OpFunction %void None %14
+ %15 = OpLabel
+ %16 = OpFunctionCall %Outputs %main_inner
+ %17 = OpCompositeExtract %float %16 0
+ OpStore %main_loc0_Output %17
+ %18 = OpCompositeExtract %float %16 1
+ OpStore %main_loc0_Output_0 %18
+ OpReturn
+ OpFunctionEnd
+)");
+}
+
} // namespace
} // namespace tint::spirv::writer
diff --git a/src/tint/lang/spirv/writer/printer/printer.cc b/src/tint/lang/spirv/writer/printer/printer.cc
index b098a86..d3a4b6e 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,9 +1778,55 @@
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.index) {
+ module_.PushAnnot(spv::Op::OpDecorate, {id, U32Operand(SpvDecorationIndex), *attrs.index});
+ }
+ 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>();
+ auto* store_ty = ptr->StoreType();
auto ty = Type(ptr);
switch (ptr->AddressSpace()) {
@@ -1848,13 +1837,14 @@
current_function_.push_inst(spv::Op::OpStore, {id, Value(var->Initializer())});
} else {
current_function_.push_var(
- {ty, id, U32Operand(SpvStorageClassFunction), ConstantNull(ptr->StoreType())});
+ {ty, id, U32Operand(SpvStorageClassFunction), ConstantNull(store_ty)});
}
break;
}
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: {
@@ -1864,7 +1854,7 @@
TINT_ASSERT(var->Initializer()->Is<core::ir::Constant>());
operands.push_back(Value(var->Initializer()));
} else {
- operands.push_back(ConstantNull(ptr->StoreType()));
+ operands.push_back(ConstantNull(store_ty));
}
module_.PushType(spv::Op::OpVariable, operands);
break;
@@ -1878,6 +1868,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:
@@ -1891,6 +1882,19 @@
{id, U32Operand(SpvDecorationDescriptorSet), bp.group});
module_.PushAnnot(spv::Op::OpDecorate,
{id, U32Operand(SpvDecorationBinding), bp.binding});
+
+ // Add NonReadable and NonWritable decorations to storage textures and buffers.
+ auto* st = store_ty->As<core::type::StorageTexture>();
+ if (st || store_ty->Is<core::type::Struct>()) {
+ auto access = st ? st->access() : ptr->Access();
+ if (access == core::Access::kRead) {
+ module_.PushAnnot(spv::Op::OpDecorate,
+ {id, U32Operand(SpvDecorationNonWritable)});
+ } else if (access == core::Access::kWrite) {
+ module_.PushAnnot(spv::Op::OpDecorate,
+ {id, U32Operand(SpvDecorationNonReadable)});
+ }
+ }
break;
}
case core::AddressSpace::kWorkgroup: {
@@ -1899,7 +1903,7 @@
if (zero_init_workgroup_memory_) {
// If requested, use the VK_KHR_zero_initialize_workgroup_memory to zero-initialize
// the workgroup variable using an null constant initializer.
- operands.push_back(ConstantNull(ptr->StoreType()));
+ operands.push_back(ConstantNull(store_ty));
}
module_.PushType(spv::Op::OpVariable, operands);
break;
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/builtin_polyfill.cc b/src/tint/lang/spirv/writer/raise/builtin_polyfill.cc
index 7a3bd38..fea3909 100644
--- a/src/tint/lang/spirv/writer/raise/builtin_polyfill.cc
+++ b/src/tint/lang/spirv/writer/raise/builtin_polyfill.cc
@@ -91,6 +91,11 @@
case core::Function::kTextureStore:
worklist.Push(builtin);
break;
+ case core::Function::kQuantizeToF16:
+ if (builtin->Result()->Type()->Is<core::type::Vector>()) {
+ worklist.Push(builtin);
+ }
+ break;
default:
break;
}
@@ -147,6 +152,9 @@
case core::Function::kTextureStore:
replacement = TextureStore(builtin);
break;
+ case core::Function::kQuantizeToF16:
+ replacement = QuantizeToF16Vec(builtin);
+ break;
default:
break;
}
@@ -818,6 +826,29 @@
extract->InsertBefore(builtin);
return extract->Result();
}
+
+ /// Scalarize the vector form of a `quantizeToF16()` builtin.
+ /// See crbug.com/tint/1741.
+ /// @param builtin the builtin call instruction
+ /// @returns the replacement value
+ core::ir::Value* QuantizeToF16Vec(core::ir::CoreBuiltinCall* builtin) {
+ auto* arg = builtin->Args()[0];
+ auto* vec = arg->Type()->As<core::type::Vector>();
+ TINT_ASSERT(vec);
+
+ // Replace the builtin call with a call to the spirv.dot intrinsic.
+ Vector<core::ir::Value*, 4> args;
+ for (uint32_t i = 0; i < vec->Width(); i++) {
+ auto* el = b.Access(ty.f32(), arg, u32(i));
+ auto* scalar_call = b.Call(ty.f32(), core::Function::kQuantizeToF16, el);
+ args.Push(scalar_call->Result());
+ el->InsertBefore(builtin);
+ scalar_call->InsertBefore(builtin);
+ }
+ auto* construct = b.Construct(vec, std::move(args));
+ construct->InsertBefore(builtin);
+ return construct->Result();
+ }
};
} // namespace
diff --git a/src/tint/lang/spirv/writer/raise/builtin_polyfill_test.cc b/src/tint/lang/spirv/writer/raise/builtin_polyfill_test.cc
index 0fa6eaf..d20393f 100644
--- a/src/tint/lang/spirv/writer/raise/builtin_polyfill_test.cc
+++ b/src/tint/lang/spirv/writer/raise/builtin_polyfill_test.cc
@@ -2804,5 +2804,74 @@
EXPECT_EQ(expect, str());
}
+TEST_F(SpirvWriter_BuiltinPolyfillTest, QuantizeToF16_Scalar) {
+ auto* arg = b.FunctionParam("arg", ty.f32());
+ auto* func = b.Function("foo", ty.f32());
+ func->SetParams({arg});
+
+ b.Append(func->Block(), [&] {
+ auto* result = b.Call(ty.f32(), core::Function::kQuantizeToF16, arg);
+ b.Return(func, result);
+ });
+
+ auto* src = R"(
+%foo = func(%arg:f32):f32 -> %b1 {
+ %b1 = block {
+ %3:f32 = quantizeToF16 %arg
+ ret %3
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = src;
+
+ Run(BuiltinPolyfill);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(SpirvWriter_BuiltinPolyfillTest, QuantizeToF16_Vector) {
+ auto* arg = b.FunctionParam("arg", ty.vec4<f32>());
+ auto* func = b.Function("foo", ty.vec4<f32>());
+ func->SetParams({arg});
+
+ b.Append(func->Block(), [&] {
+ auto* result = b.Call(ty.vec4<f32>(), core::Function::kQuantizeToF16, arg);
+ b.Return(func, result);
+ });
+
+ auto* src = R"(
+%foo = func(%arg:vec4<f32>):vec4<f32> -> %b1 {
+ %b1 = block {
+ %3:vec4<f32> = quantizeToF16 %arg
+ ret %3
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%arg:vec4<f32>):vec4<f32> -> %b1 {
+ %b1 = block {
+ %3:f32 = access %arg, 0u
+ %4:f32 = quantizeToF16 %3
+ %5:f32 = access %arg, 1u
+ %6:f32 = quantizeToF16 %5
+ %7:f32 = access %arg, 2u
+ %8:f32 = quantizeToF16 %7
+ %9:f32 = access %arg, 3u
+ %10:f32 = quantizeToF16 %9
+ %11:vec4<f32> = construct %4, %6, %8, %10
+ ret %11
+ }
+}
+)";
+
+ Run(BuiltinPolyfill);
+
+ EXPECT_EQ(expect, str());
+}
+
} // namespace
} // namespace tint::spirv::writer::raise
diff --git a/src/tint/lang/spirv/writer/raise/raise.cc b/src/tint/lang/spirv/writer/raise/raise.cc
index 71edb73..c4be7ea 100644
--- a/src/tint/lang/spirv/writer/raise/raise.cc
+++ b/src/tint/lang/spirv/writer/raise/raise.cc
@@ -78,7 +78,8 @@
RUN_TRANSFORM(ExpandImplicitSplats, module);
RUN_TRANSFORM(HandleMatrixArithmetic, module);
RUN_TRANSFORM(MergeReturn, module);
- RUN_TRANSFORM(ShaderIO, module, ShaderIOConfig{options.clamp_frag_depth});
+ RUN_TRANSFORM(ShaderIO, module,
+ ShaderIOConfig{options.clamp_frag_depth, options.emit_vertex_point_size});
RUN_TRANSFORM(core::ir::transform::Std140, module);
RUN_TRANSFORM(VarForDynamicIndex, module);
diff --git a/src/tint/lang/spirv/writer/raise/shader_io.cc b/src/tint/lang/spirv/writer/raise/shader_io.cc
index 2716b67..0c7a3b2 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);
}
@@ -213,6 +189,9 @@
.Call(ty.f32(), core::Function::kClamp, frag_depth, frag_depth_min, frag_depth_max)
->Result();
}
+
+ /// @copydoc ShaderIO::BackendState::NeedsVertexPointSize
+ bool NeedsVertexPointSize() const override { return config.emit_vertex_point_size; }
};
} // namespace
diff --git a/src/tint/lang/spirv/writer/raise/shader_io.h b/src/tint/lang/spirv/writer/raise/shader_io.h
index 5924c12..41a2c64 100644
--- a/src/tint/lang/spirv/writer/raise/shader_io.h
+++ b/src/tint/lang/spirv/writer/raise/shader_io.h
@@ -30,10 +30,12 @@
struct ShaderIOConfig {
/// true if frag_depth builtin outputs should be clamped
bool clamp_frag_depth = false;
+ /// true if a vertex point size builtin output should be added
+ bool emit_vertex_point_size = 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..e4edbf9 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,157 @@
}
%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
+ }
+}
+)";
+
+ ShaderIOConfig config;
+ config.clamp_frag_depth = false;
+ Run(ShaderIO, config);
+
+ EXPECT_EQ(expect, str());
+}
+
+// Test that interpolation attributes are stripped from vertex inputs and fragment outputs.
+TEST_F(SpirvWriter_ShaderIOTest, InterpolationOnVertexInputOrFragmentOutput) {
+ auto* str_ty = ty.Struct(mod.symbols.New("MyStruct"),
+ {
+ {
+ mod.symbols.New("color"),
+ ty.f32(),
+ {1u,
+ {},
+ {},
+ core::Interpolation{core::InterpolationType::kLinear,
+ core::InterpolationSampling::kSample},
+ false},
+ },
+ });
+
+ // Vertex shader.
+ {
+ auto* ep = b.Function("vert", ty.vec4<f32>());
+ ep->SetReturnBuiltin(core::ir::Function::ReturnBuiltin::kPosition);
+ ep->SetReturnInvariant(true);
+ ep->SetStage(core::ir::Function::PipelineStage::kVertex);
+
+ auto* str_param = b.FunctionParam("input", str_ty);
+ auto* ival = b.FunctionParam("ival", ty.i32());
+ ival->SetLocation(1, core::Interpolation{core::InterpolationType::kFlat});
+ ep->SetParams({str_param, ival});
+
+ b.Append(ep->Block(), [&] { //
+ b.Return(ep, b.Construct(ty.vec4<f32>(), 0.5_f));
+ });
+ }
+
+ // Fragment shader with struct output.
+ {
+ auto* ep = b.Function("frag1", str_ty);
+ ep->SetStage(core::ir::Function::PipelineStage::kFragment);
+
+ b.Append(ep->Block(), [&] { //
+ b.Return(ep, b.Construct(str_ty, 0.5_f));
+ });
+ }
+
+ // Fragment shader with non-struct output.
+ {
+ auto* ep = b.Function("frag2", ty.i32());
+ ep->SetStage(core::ir::Function::PipelineStage::kFragment);
+ ep->SetReturnLocation(0, core::Interpolation{core::InterpolationType::kFlat});
+
+ b.Append(ep->Block(), [&] { //
+ b.Return(ep, b.Constant(42_i));
+ });
+ }
+
+ auto* src = R"(
+MyStruct = struct @align(4) {
+ color:f32 @offset(0), @location(1), @interpolate(linear, sample)
+}
+
+%vert = @vertex func(%input:MyStruct, %ival:i32 [@location(1), @interpolate(flat)]):vec4<f32> [@invariant, @position] -> %b1 {
+ %b1 = block {
+ %4:vec4<f32> = construct 0.5f
+ ret %4
+ }
+}
+%frag1 = @fragment func():MyStruct -> %b2 {
+ %b2 = block {
+ %6:MyStruct = construct 0.5f
+ ret %6
+ }
+}
+%frag2 = @fragment func():i32 [@location(0), @interpolate(flat)] -> %b3 {
+ %b3 = block {
+ ret 42i
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+MyStruct = struct @align(4) {
+ color:f32 @offset(0)
+}
+
+%b1 = block { # root
+ %vert_loc1_Input:ptr<__in, f32, read> = var @location(1)
+ %vert_loc1_Input_1:ptr<__in, i32, read> = var @location(1) # %vert_loc1_Input_1: 'vert_loc1_Input'
+ %vert_position_Output:ptr<__out, vec4<f32>, write> = var @invariant @builtin(position)
+ %frag1_loc1_Output:ptr<__out, f32, write> = var @location(1)
+ %frag2_loc0_Output:ptr<__out, i32, write> = var @location(0)
+}
+
+%vert_inner = func(%input:MyStruct, %ival:i32):vec4<f32> -> %b2 {
+ %b2 = block {
+ %9:vec4<f32> = construct 0.5f
+ ret %9
+ }
+}
+%frag1_inner = func():MyStruct -> %b3 {
+ %b3 = block {
+ %11:MyStruct = construct 0.5f
+ ret %11
+ }
+}
+%frag2_inner = func():i32 -> %b4 {
+ %b4 = block {
+ ret 42i
+ }
+}
+%vert = @vertex func():void -> %b5 {
+ %b5 = block {
+ %14:f32 = load %vert_loc1_Input
+ %15:MyStruct = construct %14
+ %16:i32 = load %vert_loc1_Input_1
+ %17:vec4<f32> = call %vert_inner, %15, %16
+ store %vert_position_Output, %17
+ ret
+ }
+}
+%frag1 = @fragment func():void -> %b6 {
+ %b6 = block {
+ %19:MyStruct = call %frag1_inner
+ %20:f32 = access %19, 0u
+ store %frag1_loc1_Output, %20
+ ret
+ }
+}
+%frag2 = @fragment func():void -> %b7 {
+ %b7 = block {
+ %22:i32 = call %frag2_inner
+ store %frag2_loc0_Output, %22
ret
}
}
@@ -984,22 +1023,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 +1044,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
}
}
@@ -1034,5 +1063,53 @@
EXPECT_EQ(expect, str());
}
+TEST_F(SpirvWriter_ShaderIOTest, EmitVertexPointSize) {
+ auto* ep = b.Function("foo", ty.vec4<f32>());
+ ep->SetStage(core::ir::Function::PipelineStage::kVertex);
+ ep->SetReturnBuiltin(core::ir::Function::ReturnBuiltin::kPosition);
+
+ b.Append(ep->Block(), [&] { //
+ b.Return(ep, b.Construct(ty.vec4<f32>(), 0.5_f));
+ });
+
+ auto* src = R"(
+%foo = @vertex func():vec4<f32> [@position] -> %b1 {
+ %b1 = block {
+ %2:vec4<f32> = construct 0.5f
+ ret %2
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%b1 = block { # root
+ %foo_position_Output:ptr<__out, vec4<f32>, write> = var @builtin(position)
+ %foo___point_size_Output:ptr<__out, f32, write> = var @builtin(__point_size)
+}
+
+%foo_inner = func():vec4<f32> -> %b2 {
+ %b2 = block {
+ %4:vec4<f32> = construct 0.5f
+ ret %4
+ }
+}
+%foo = @vertex func():void -> %b3 {
+ %b3 = block {
+ %6:vec4<f32> = call %foo_inner
+ store %foo_position_Output, %6
+ store %foo___point_size_Output, 1.0f
+ ret
+ }
+}
+)";
+
+ ShaderIOConfig config;
+ config.emit_vertex_point_size = true;
+ Run(ShaderIO, config);
+
+ EXPECT_EQ(expect, str());
+}
+
} // namespace
} // namespace tint::spirv::writer::raise
diff --git a/src/tint/lang/spirv/writer/var_test.cc b/src/tint/lang/spirv/writer/var_test.cc
index c73fa13..2e37175 100644
--- a/src/tint/lang/spirv/writer/var_test.cc
+++ b/src/tint/lang/spirv/writer/var_test.cc
@@ -175,8 +175,8 @@
EXPECT_INST("%v = OpVariable %_ptr_Workgroup_int Workgroup %4");
}
-TEST_F(SpirvWriterTest, StorageVar) {
- auto* v = b.Var("v", ty.ptr<storage, i32>());
+TEST_F(SpirvWriterTest, StorageVar_ReadOnly) {
+ auto* v = b.Var("v", ty.ptr<storage, i32, read>());
v->SetBindingPoint(0, 0);
b.RootBlock()->Append(v);
@@ -185,6 +185,7 @@
OpDecorate %tint_symbol_1 Block
OpDecorate %1 DescriptorSet 0
OpDecorate %1 Binding 0
+ OpDecorate %1 NonWritable
)");
EXPECT_INST(R"(
%tint_symbol_1 = OpTypeStruct %int
@@ -194,7 +195,7 @@
}
TEST_F(SpirvWriterTest, StorageVar_LoadAndStore) {
- auto* v = b.Var("v", ty.ptr<storage, i32>());
+ auto* v = b.Var("v", ty.ptr<storage, i32, read_write>());
v->SetBindingPoint(0, 0);
b.RootBlock()->Append(v);
@@ -219,6 +220,31 @@
)");
}
+TEST_F(SpirvWriterTest, StorageVar_WriteOnly) {
+ auto* v = b.Var("v", ty.ptr<storage, i32, write>());
+ v->SetBindingPoint(0, 0);
+ b.RootBlock()->Append(v);
+
+ auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kCompute,
+ std::array{1u, 1u, 1u});
+ b.Append(func->Block(), [&] {
+ b.Store(v, 42_i);
+ b.Return(func);
+ });
+
+ ASSERT_TRUE(Generate()) << Error() << output_;
+ EXPECT_INST(R"(
+ OpDecorate %tint_symbol_1 Block
+ OpDecorate %1 DescriptorSet 0
+ OpDecorate %1 Binding 0
+ OpDecorate %1 NonReadable
+)");
+ EXPECT_INST(R"(
+ %9 = OpAccessChain %_ptr_StorageBuffer_int %1 %uint_0
+ OpStore %9 %int_42
+)");
+}
+
TEST_F(SpirvWriterTest, UniformVar) {
auto* v = b.Var("v", ty.ptr<uniform, i32>());
v->SetBindingPoint(0, 0);
@@ -363,5 +389,73 @@
EXPECT_INST("%load = OpLoad %3 %v");
}
+TEST_F(SpirvWriterTest, ReadOnlyStorageTextureVar) {
+ auto format = core::TexelFormat::kRgba8Unorm;
+ auto* v = b.Var("v", ty.ptr(core::AddressSpace::kHandle,
+ ty.Get<core::type::StorageTexture>(
+ core::type::TextureDimension::k2d, format, read,
+ core::type::StorageTexture::SubtypeFor(format, ty)),
+ core::Access::kRead));
+ v->SetBindingPoint(0, 0);
+ b.RootBlock()->Append(v);
+
+ ASSERT_TRUE(Generate()) << Error() << output_;
+ EXPECT_INST(R"(
+ OpDecorate %v DescriptorSet 0
+ OpDecorate %v Binding 0
+ OpDecorate %v NonWritable
+)");
+ EXPECT_INST(R"(
+ %3 = OpTypeImage %float 2D 0 0 0 2 Rgba8
+%_ptr_UniformConstant_3 = OpTypePointer UniformConstant %3
+ %v = OpVariable %_ptr_UniformConstant_3 UniformConstant
+)");
+}
+
+TEST_F(SpirvWriterTest, ReadWriteStorageTextureVar) {
+ auto format = core::TexelFormat::kRgba8Unorm;
+ auto* v = b.Var("v", ty.ptr(core::AddressSpace::kHandle,
+ ty.Get<core::type::StorageTexture>(
+ core::type::TextureDimension::k2d, format, read_write,
+ core::type::StorageTexture::SubtypeFor(format, ty)),
+ core::Access::kRead));
+ v->SetBindingPoint(0, 0);
+ b.RootBlock()->Append(v);
+
+ ASSERT_TRUE(Generate()) << Error() << output_;
+ EXPECT_INST(R"(
+ OpDecorate %v DescriptorSet 0
+ OpDecorate %v Binding 0
+)");
+ EXPECT_INST(R"(
+ %3 = OpTypeImage %float 2D 0 0 0 2 Rgba8
+%_ptr_UniformConstant_3 = OpTypePointer UniformConstant %3
+ %v = OpVariable %_ptr_UniformConstant_3 UniformConstant
+)");
+}
+
+TEST_F(SpirvWriterTest, WriteOnlyStorageTextureVar) {
+ auto format = core::TexelFormat::kRgba8Unorm;
+ auto* v = b.Var("v", ty.ptr(core::AddressSpace::kHandle,
+ ty.Get<core::type::StorageTexture>(
+ core::type::TextureDimension::k2d, format, write,
+ core::type::StorageTexture::SubtypeFor(format, ty)),
+ core::Access::kRead));
+ v->SetBindingPoint(0, 0);
+ b.RootBlock()->Append(v);
+
+ ASSERT_TRUE(Generate()) << Error() << output_;
+ EXPECT_INST(R"(
+ OpDecorate %v DescriptorSet 0
+ OpDecorate %v Binding 0
+ OpDecorate %v NonReadable
+)");
+ EXPECT_INST(R"(
+ %3 = OpTypeImage %float 2D 0 0 0 2 Rgba8
+%_ptr_UniformConstant_3 = OpTypePointer UniformConstant %3
+ %v = OpVariable %_ptr_UniformConstant_3 UniformConstant
+)");
+}
+
} // namespace
} // namespace tint::spirv::writer
diff --git a/src/tint/lang/wgsl/reader/program_to_ir/program_to_ir.cc b/src/tint/lang/wgsl/reader/program_to_ir/program_to_ir.cc
index 1464464..4850db3 100644
--- a/src/tint/lang/wgsl/reader/program_to_ir/program_to_ir.cc
+++ b/src/tint/lang/wgsl/reader/program_to_ir/program_to_ir.cc
@@ -421,6 +421,14 @@
param->SetBuiltin(
core::ir::FunctionParam::Builtin::kSampleMask);
break;
+ case core::BuiltinValue::kSubgroupInvocationId:
+ param->SetBuiltin(
+ core::ir::FunctionParam::Builtin::kSubgroupInvocationId);
+ break;
+ case core::BuiltinValue::kSubgroupSize:
+ param->SetBuiltin(
+ core::ir::FunctionParam::Builtin::kSubgroupSize);
+ break;
default:
TINT_ICE() << "Unknown builtin value in parameter attributes "
<< ident_sem->Value();
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"