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 <dneto@google.com> Kokoro: Kokoro <noreply+kokoro@google.com> Commit-Queue: David Neto <dneto@google.com> Reviewed-by: James Price <jrprice@google.com>
This commit is contained in:
parent
4daac0e64b
commit
068aa69037
|
@ -1057,16 +1057,23 @@ bool FunctionEmitter::EmitEntryPointAsWrapper() {
|
||||||
// Form the member type.
|
// Form the member type.
|
||||||
// Reuse the var name for the member name. They can't clash.
|
// Reuse the var name for the member name. They can't clash.
|
||||||
ast::StructMember* return_member = create<ast::StructMember>(
|
ast::StructMember* return_member = create<ast::StructMember>(
|
||||||
Source{}, var_sym, forced_store_type->Build(builder_),
|
Source{}, var_sym, forced_store_type->Build(builder_), out_decos);
|
||||||
std::move(out_decos));
|
|
||||||
return_members.push_back(return_member);
|
return_members.push_back(return_member);
|
||||||
|
|
||||||
ast::Expression* return_member_value =
|
ast::Expression* return_member_value =
|
||||||
create<ast::IdentifierExpression>(source, var_sym);
|
create<ast::IdentifierExpression>(source, var_sym);
|
||||||
if (forced_store_type != store_type) {
|
if (HasBuiltinSampleMask(out_decos)) {
|
||||||
// We need to cast from the variable store type to the member type.
|
// In Vulkan SPIR-V, the sample mask is an array. In WGSL it's a scalar.
|
||||||
return_member_value = create<ast::BitcastExpression>(
|
// Get the first element only.
|
||||||
source, forced_store_type->Build(builder_), return_member_value);
|
return_member_value = create<ast::ArrayAccessorExpression>(
|
||||||
|
source, return_member_value, parser_impl_.MakeNullValue(ty_.I32()));
|
||||||
|
if (store_type->As<Array>()->type->IsSignedScalarOrVector()) {
|
||||||
|
// sample_mask is unsigned in WGSL. Bitcast it.
|
||||||
|
return_member_value = create<ast::BitcastExpression>(
|
||||||
|
source, forced_store_type->Build(builder_), return_member_value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No other builtin outputs need signedness conversion.
|
||||||
}
|
}
|
||||||
// Save the expression.
|
// Save the expression.
|
||||||
return_exprs.push_back(return_member_value);
|
return_exprs.push_back(return_member_value);
|
||||||
|
|
|
@ -4616,15 +4616,233 @@ TEST_F(SpvModuleScopeVarParserTest,
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
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): 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
|
||||||
} // namespace spirv
|
} // namespace spirv
|
||||||
} // namespace reader
|
} // namespace reader
|
||||||
|
|
Loading…
Reference in New Issue