diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc index a0de6f829a..3a6707ff1d 100644 --- a/src/reader/spirv/function.cc +++ b/src/reader/spirv/function.cc @@ -1041,7 +1041,7 @@ bool FunctionEmitter::EmitEntryPointAsWrapper() { // The SPIR-V gl_PerVertex variable has already been remapped to // a gl_Position variable. // Substitute the type. - param_type = ty_.Vector(ty_.F32(), 4); + store_type = param_type = ty_.Vector(ty_.F32(), 4); out_decos.emplace_back( create(source, ast::Builtin::kPosition)); } else { diff --git a/src/reader/spirv/parser_impl.cc b/src/reader/spirv/parser_impl.cc index 04e2e5d363..95e50ad5b5 100644 --- a/src/reader/spirv/parser_impl.cc +++ b/src/reader/spirv/parser_impl.cc @@ -1250,6 +1250,8 @@ bool ParserImpl::EmitModuleScopeVariables() { (spirv_storage_class == SpvStorageClassOutput))) { // Skip emitting gl_PerVertex. builtin_position_.per_vertex_var_id = var.result_id(); + builtin_position_.per_vertex_var_init_id = + var.NumInOperands() > 1 ? var.GetSingleWordInOperand(1) : 0u; continue; } switch (enum_converter_.ToStorageClass(spirv_storage_class)) { @@ -1322,13 +1324,33 @@ bool ParserImpl::EmitModuleScopeVariables() { decos.push_back( create(Source{}, ast::Builtin::kPosition)); } - auto* var = MakeVariable( + ast::Expression* ast_constructor = nullptr; + if (builtin_position_.per_vertex_var_init_id) { + // The initializer is complex. + const auto* init = + def_use_mgr_->GetDef(builtin_position_.per_vertex_var_init_id); + switch (init->opcode()) { + case SpvOpConstantComposite: + case SpvOpSpecConstantComposite: + ast_constructor = MakeConstantExpression( + init->GetSingleWordInOperand( + builtin_position_.position_member_index)) + .expr; + break; + default: + return Fail() << "gl_PerVertex initializer too complex. only " + "OpCompositeConstruct and OpSpecConstantComposite " + "are supported: " + << init->PrettyPrint(); + } + } + auto* ast_var = MakeVariable( builtin_position_.per_vertex_var_id, enum_converter_.ToStorageClass(builtin_position_.storage_class), - ConvertType(builtin_position_.position_member_type_id), false, nullptr, - std::move(decos)); + ConvertType(builtin_position_.position_member_type_id), false, + ast_constructor, std::move(decos)); - builder_.AST().AddGlobalVariable(var); + builder_.AST().AddGlobalVariable(ast_var); } return success_; } diff --git a/src/reader/spirv/parser_impl.h b/src/reader/spirv/parser_impl.h index 062cb58c87..d303bc026c 100644 --- a/src/reader/spirv/parser_impl.h +++ b/src/reader/spirv/parser_impl.h @@ -478,6 +478,8 @@ class ParserImpl : Reader { /// The ID of the gl_PerVertex variable, if it was declared. /// We'll use this for the gl_Position variable instead. uint32_t per_vertex_var_id = 0; + /// The ID of the initializer to gl_PerVertex, if any. + uint32_t per_vertex_var_init_id = 0; }; /// @returns info about the gl_Position builtin variable. const BuiltInPositionInfo& GetBuiltInPositionInfo() { diff --git a/src/reader/spirv/parser_impl_module_var_test.cc b/src/reader/spirv/parser_impl_module_var_test.cc index 11f2328619..e3ff63ce64 100644 --- a/src/reader/spirv/parser_impl_module_var_test.cc +++ b/src/reader/spirv/parser_impl_module_var_test.cc @@ -4899,7 +4899,106 @@ TEST_F(SpvModuleScopeVarParserTest, EXPECT_EQ(got, expected) << got; } -// TODO(dneto): pipeline IO: gl_Position, with initializer +TEST_F(SpvModuleScopeVarParserTest, + BuiltinPosition_BuiltIn_Position_Initializer) { + const std::string assembly = R"( + OpCapability Shader + OpMemoryModel Logical Simple + OpEntryPoint Vertex %main "main" %1 + + OpMemberDecorate %10 0 BuiltIn Position + OpMemberDecorate %10 1 BuiltIn PointSize + OpMemberDecorate %10 2 BuiltIn ClipDistance + OpMemberDecorate %10 3 BuiltIn CullDistance + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %arr = OpTypeArray %float %uint_1 + %10 = OpTypeStruct %v4float %float %arr %arr + %11 = OpTypePointer Output %10 + + %float_1 = OpConstant %float 1 + %float_2 = OpConstant %float 2 + %float_3 = OpConstant %float 3 + %float_4 = OpConstant %float 4 + %float_5 = OpConstant %float 5 + %float_6 = OpConstant %float 6 + %float_7 = OpConstant %float 7 + + %init_pos = OpConstantComposite %v4float %float_1 %float_2 %float_3 %float_4 + %init_clip = OpConstantComposite %arr %float_6 + %init_cull = OpConstantComposite %arr %float_7 + %init_per_vertex = OpConstantComposite %10 %init_pos %float_5 %init_clip %init_cull + + %1 = OpVariable %11 Output %init_per_vertex + + %main = OpFunction %void None %voidfn + %entry = OpLabel + OpReturn + OpFunctionEnd +)"; + auto p = parser(test::Assemble(assembly)); + + // TODO(crbug.com/tint/508): Remove this when everything is converted + // to HLSL style pipeline IO. + p->SetHLSLStylePipelineIO(); + + ASSERT_TRUE(p->Parse()) << p->error() << assembly; + EXPECT_TRUE(p->error().empty()); + + const auto got = p->program().to_str(); + const std::string expected = R"(Module{ + Struct main_out { + StructMember{[[ BuiltinDecoration{position} + ]] gl_Position: __vec_4__f32} + } + Variable{ + gl_Position + private + undefined + __vec_4__f32 + { + TypeConstructor[not set]{ + __vec_4__f32 + ScalarConstructor[not set]{1.000000} + ScalarConstructor[not set]{2.000000} + ScalarConstructor[not set]{3.000000} + ScalarConstructor[not set]{4.000000} + } + } + } + Function main_1 -> __void + () + { + Return{} + } + Function main -> __type_name_main_out + StageDecoration{vertex} + () + { + Call[not set]{ + Identifier[not set]{main_1} + ( + ) + } + Return{ + { + TypeConstructor[not set]{ + __type_name_main_out + Identifier[not set]{gl_Position} + } + } + } + } +} +)"; + EXPECT_EQ(got, expected) << got; +} + // TODO(dneto): pipeline IO: read PointSize // TODO(dneto): pipeline IO: write PointSize