[spirv-reader][ir] Support unused position in a structure.
Make sure to emit a default write to the `position` builtin if it's in a
structure and unused.
Bug: 42250952
Change-Id: If02a5babd91cf22ddacb4eb8935e549ad5fa985e
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/246074
Reviewed-by: James Price <jrprice@google.com>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
diff --git a/src/tint/lang/spirv/reader/parser/function_test.cc b/src/tint/lang/spirv/reader/parser/function_test.cc
index 98a2aea..0a835dd 100644
--- a/src/tint/lang/spirv/reader/parser/function_test.cc
+++ b/src/tint/lang/spirv/reader/parser/function_test.cc
@@ -225,6 +225,92 @@
)");
}
+TEST_F(SpirvParserTest, VertexShader_PositionUnused_Struct) {
+ EXPECT_IR(R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %main "main" %1
+ OpDecorate %str Block
+ OpMemberDecorate %str 0 BuiltIn Position
+ %void = OpTypeVoid
+ %float = OpTypeFloat 32
+ %ep_type = OpTypeFunction %void
+ %v4float = OpTypeVector %float 4
+ %str = OpTypeStruct %v4float
+ %str_ptr = OpTypePointer Output %str
+ %1 = OpVariable %str_ptr Output
+ %main = OpFunction %void None %ep_type
+ %main_start = OpLabel
+ OpReturn
+ OpFunctionEnd
+)",
+ R"(
+tint_symbol_1 = struct @align(16) {
+ tint_symbol:vec4<f32> @offset(0), @builtin(position)
+}
+
+$B1: { # root
+ %1:ptr<__out, tint_symbol_1, read_write> = var undef
+}
+
+%main = @vertex func():void {
+ $B2: {
+ %3:ptr<__out, vec4<f32>, read_write> = access %1, 0u
+ store %3, vec4<f32>(0.0f)
+ ret
+ }
+}
+)");
+}
+
+TEST_F(SpirvParserTest, VertexShader_PositionUsed_Struct) {
+ EXPECT_IR(R"(
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Vertex %main "main" %1
+ OpDecorate %str Block
+ OpMemberDecorate %str 0 BuiltIn Position
+ %void = OpTypeVoid
+ %float = OpTypeFloat 32
+ %uint = OpTypeInt 32 0
+ %ep_type = OpTypeFunction %void
+ %v4float = OpTypeVector %float 4
+ %str = OpTypeStruct %v4float
+ %str_ptr = OpTypePointer Output %str
+%ptr_v4float = OpTypePointer Output %v4float
+ %uint_0 = OpConstant %uint 0
+ %one = OpConstant %float 1
+ %two = OpConstant %float 2
+ %three = OpConstant %float 3
+ %four = OpConstant %float 4
+ %3 = OpConstantComposite %v4float %one %two %three %four
+ %1 = OpVariable %str_ptr Output
+ %main = OpFunction %void None %ep_type
+ %main_start = OpLabel
+ %2 = OpAccessChain %ptr_v4float %1 %uint_0
+ OpStore %2 %3
+ OpReturn
+ OpFunctionEnd
+)",
+ R"(
+tint_symbol_1 = struct @align(16) {
+ tint_symbol:vec4<f32> @offset(0), @builtin(position)
+}
+
+$B1: { # root
+ %1:ptr<__out, tint_symbol_1, read_write> = var undef
+}
+
+%main = @vertex func():void {
+ $B2: {
+ %3:ptr<__out, vec4<f32>, read_write> = access %1, 0u
+ store %3, vec4<f32>(1.0f, 2.0f, 3.0f, 4.0f)
+ ret
+ }
+}
+)");
+}
+
TEST_F(SpirvParserTest, VertexShader_PositionUnused) {
EXPECT_IR(R"(
OpCapability Shader
diff --git a/src/tint/lang/spirv/reader/parser/parser.cc b/src/tint/lang/spirv/reader/parser/parser.cc
index 6a82af2..a725e62 100644
--- a/src/tint/lang/spirv/reader/parser/parser.cc
+++ b/src/tint/lang/spirv/reader/parser/parser.cc
@@ -1125,6 +1125,16 @@
current_spirv_function_ = nullptr;
}
+ std::optional<uint32_t> Position(const core::type::Struct* str) {
+ for (auto* mem : str->Members()) {
+ auto builtin = mem->Attributes().builtin;
+ if (builtin.has_value() && builtin.value() == core::BuiltinValue::kPosition) {
+ return {mem->Index()};
+ }
+ }
+ return {};
+ }
+
/// Emit entry point attributes.
void EmitEntryPointAttributes() {
// Handle OpEntryPoint declarations.
@@ -1166,17 +1176,65 @@
auto* var = inst_result->Instruction()->As<core::ir::Var>();
TINT_ASSERT(var);
- auto builtin = var->Builtin();
- if (!builtin.has_value() || builtin.value() != core::BuiltinValue::kPosition) {
- continue;
- }
+ if (auto* str = var->Result()->Type()->UnwrapPtr()->As<core::type::Struct>()) {
+ auto p = Position(str);
+ if (!p.has_value()) {
+ continue;
+ }
- auto refs = referenced.TransitiveReferences(func);
- // The referenced variables does not contain the var which has the `position`
- // builtin, then we need to create a fake store.
- if (!refs.Contains(var)) {
- auto* store = b_.Store(var, b_.Zero(var->Result()->Type()->UnwrapPtr()));
- store->InsertBefore(func->Block()->Front());
+ auto refs = referenced.TransitiveReferences(func);
+ if (refs.Contains(var)) {
+ // The `var` is referenced, but we need to determine if there is an
+ // `access` usage which references `position` which is then used in a
+ // `store` instruction.
+
+ bool accessed = false;
+ for (auto& usage : var->Result()->UsagesUnsorted()) {
+ if (auto* access = usage->instruction->As<core::ir::Access>()) {
+ if (access->Indices().IsEmpty()) {
+ continue;
+ }
+
+ auto* cnst = access->Indices()[0]->As<core::ir::Constant>();
+ TINT_ASSERT(cnst);
+
+ if (cnst->Value()->ValueAs<uint32_t>() == p.value()) {
+ accessed = true;
+ break;
+ }
+ }
+ }
+
+ if (accessed) {
+ break;
+ }
+ }
+
+ auto* mem_ty = str->Members()[p.value()]->Type();
+ auto* type = ty_.ptr(
+ var->Result()->Type()->As<core::type::Pointer>()->AddressSpace(),
+ mem_ty);
+ auto* access = b_.Access(type, var, u32(p.value()));
+ access->InsertBefore(func->Block()->Front());
+
+ auto* store = b_.Store(access, b_.Zero(mem_ty));
+ store->InsertAfter(access);
+
+ } else {
+ auto builtin = var->Builtin();
+ if (!builtin.has_value() ||
+ builtin.value() != core::BuiltinValue::kPosition) {
+ continue;
+ }
+
+ auto refs = referenced.TransitiveReferences(func);
+ // The referenced variables does not contain the var which has the
+ // `position` builtin, then we need to create a fake store.
+ if (!refs.Contains(var)) {
+ auto* store =
+ b_.Store(var, b_.Zero(var->Result()->Type()->UnwrapPtr()));
+ store->InsertBefore(func->Block()->Front());
+ }
}
// There should only be one `position` so we're done if we've processed the