From 068aa69037bfec04bd668e53febffc56532093a0 Mon Sep 17 00:00:00 2001 From: David Neto Date: Tue, 15 Jun 2021 22:23:37 +0000 Subject: [PATCH] spirv-reader: pipeline IO: handle sample_mask output No other outputs require signedness conversion, so we can simplify one part of the code Bug: tint:508 Change-Id: I71a111b312a0b44f780155fcf6b4420742cc9d88 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/54483 Auto-Submit: David Neto Kokoro: Kokoro Commit-Queue: David Neto Reviewed-by: James Price --- src/reader/spirv/function.cc | 19 +- .../spirv/parser_impl_module_var_test.cc | 228 +++++++++++++++++- 2 files changed, 236 insertions(+), 11 deletions(-) diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc index 5022c6f354..2024a2fb26 100644 --- a/src/reader/spirv/function.cc +++ b/src/reader/spirv/function.cc @@ -1057,16 +1057,23 @@ bool FunctionEmitter::EmitEntryPointAsWrapper() { // Form the member type. // Reuse the var name for the member name. They can't clash. ast::StructMember* return_member = create( - Source{}, var_sym, forced_store_type->Build(builder_), - std::move(out_decos)); + Source{}, var_sym, forced_store_type->Build(builder_), out_decos); return_members.push_back(return_member); ast::Expression* return_member_value = create(source, var_sym); - if (forced_store_type != store_type) { - // We need to cast from the variable store type to the member type. - return_member_value = create( - source, forced_store_type->Build(builder_), return_member_value); + if (HasBuiltinSampleMask(out_decos)) { + // In Vulkan SPIR-V, the sample mask is an array. In WGSL it's a scalar. + // Get the first element only. + return_member_value = create( + source, return_member_value, parser_impl_.MakeNullValue(ty_.I32())); + if (store_type->As()->type->IsSignedScalarOrVector()) { + // sample_mask is unsigned in WGSL. Bitcast it. + return_member_value = create( + source, forced_store_type->Build(builder_), return_member_value); + } + } else { + // No other builtin outputs need signedness conversion. } // Save the expression. return_exprs.push_back(return_member_value); diff --git a/src/reader/spirv/parser_impl_module_var_test.cc b/src/reader/spirv/parser_impl_module_var_test.cc index a755f13d2c..729d3293f6 100644 --- a/src/reader/spirv/parser_impl_module_var_test.cc +++ b/src/reader/spirv/parser_impl_module_var_test.cc @@ -4616,15 +4616,233 @@ TEST_F(SpvModuleScopeVarParserTest, } TEST_F(SpvModuleScopeVarParserTest, - DISABLED_EntryPointWrapping_BuiltinVar_SampleMask_Out_U) {} + EntryPointWrapping_BuiltinVar_SampleMask_Out_Unsigned_Initializer) { + // SampleMask is u32 in WGSL. + // Use unsigned array element in Vulkan. + const auto assembly = CommonCapabilities() + R"( + OpEntryPoint Fragment %main "main" %1 + OpExecutionMode %main OriginUpperLeft + OpDecorate %1 BuiltIn SampleMask +)" + CommonTypes() + + R"( + %arr = OpTypeArray %uint %uint_1 + %ptr_ty = OpTypePointer Output %arr + %zero = OpConstantNull %arr + %1 = OpVariable %ptr_ty Output %zero + + %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{sample_mask} + ]] x_1: __u32} + } + Variable{ + x_1 + private + undefined + __array__u32_1 + { + TypeConstructor[not set]{ + __array__u32_1 + ScalarConstructor[not set]{0u} + } + } + } + Function main_1 -> __void + () + { + Return{} + } + Function main -> __type_name_main_out + StageDecoration{fragment} + () + { + Call[not set]{ + Identifier[not set]{main_1} + ( + ) + } + Return{ + { + TypeConstructor[not set]{ + __type_name_main_out + ArrayAccessor[not set]{ + Identifier[not set]{x_1} + ScalarConstructor[not set]{0} + } + } + } + } + } +} +)"; + EXPECT_EQ(got, expected) << got; +} + TEST_F(SpvModuleScopeVarParserTest, - DISABLED_EntryPointWrapping_BuiltinVar_SampleMask_Out_S) {} + EntryPointWrapping_BuiltinVar_SampleMask_Out_Signed_Initializer) { + // SampleMask is u32 in WGSL. + // Use signed array element in Vulkan. + const auto assembly = CommonCapabilities() + R"( + OpEntryPoint Fragment %main "main" %1 + OpExecutionMode %main OriginUpperLeft + OpDecorate %1 BuiltIn SampleMask +)" + CommonTypes() + + R"( + %arr = OpTypeArray %int %uint_1 + %ptr_ty = OpTypePointer Output %arr + %zero = OpConstantNull %arr + %1 = OpVariable %ptr_ty Output %zero + + %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{sample_mask} + ]] x_1: __u32} + } + Variable{ + x_1 + private + undefined + __array__i32_1 + { + TypeConstructor[not set]{ + __array__i32_1 + ScalarConstructor[not set]{0} + } + } + } + Function main_1 -> __void + () + { + Return{} + } + Function main -> __type_name_main_out + StageDecoration{fragment} + () + { + Call[not set]{ + Identifier[not set]{main_1} + ( + ) + } + Return{ + { + TypeConstructor[not set]{ + __type_name_main_out + Bitcast[not set]<__u32>{ + ArrayAccessor[not set]{ + Identifier[not set]{x_1} + ScalarConstructor[not set]{0} + } + } + } + } + } + } +} +)"; + EXPECT_EQ(got, expected) << got; +} + +TEST_F(SpvModuleScopeVarParserTest, + EntryPointWrapping_BuiltinVar_FragDepth_Out_Initializer) { + // FragDepth does not require conversion, because it's f32. + // The member of the return type is just the identifier corresponding + // to the module-scope private variable. + const auto assembly = CommonCapabilities() + R"( + OpEntryPoint Fragment %main "main" %1 + OpExecutionMode %main OriginUpperLeft + OpDecorate %1 BuiltIn FragDepth +)" + CommonTypes() + + R"( + %ptr_ty = OpTypePointer Output %float + %1 = OpVariable %ptr_ty Output %float_0 + + %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{frag_depth} + ]] x_1: __f32} + } + Variable{ + x_1 + private + undefined + __f32 + { + ScalarConstructor[not set]{0.000000} + } + } + Function main_1 -> __void + () + { + Return{} + } + Function main -> __type_name_main_out + StageDecoration{fragment} + () + { + Call[not set]{ + Identifier[not set]{main_1} + ( + ) + } + Return{ + { + TypeConstructor[not set]{ + __type_name_main_out + Identifier[not set]{x_1} + } + } + } + } +} +)"; + EXPECT_EQ(got, expected) << got; +} // TODO(dneto): pipeline IO: flatten structures, and distribute locations -// TODO(dneto): Test passing pointer to SampleMask as function parameter, -// both input case and output case. - } // namespace } // namespace spirv } // namespace reader