spirv-reader: support Flat decoration

Bug: tint:935
Change-Id: Ie6f97d8d9a273fd8099d8e9807ffa189ba3031a0
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/56820
Auto-Submit: David Neto <dneto@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
This commit is contained in:
David Neto 2021-07-05 13:08:27 +00:00 committed by Tint LUCI CQ
parent 0e41747281
commit 5cb31a8a7f
2 changed files with 516 additions and 15 deletions

View File

@ -21,6 +21,7 @@
#include "source/opt/build_module.h"
#include "src/ast/bitcast_expression.h"
#include "src/ast/interpolate_decoration.h"
#include "src/ast/override_decoration.h"
#include "src/ast/struct_block_decoration.h"
#include "src/ast/type_name.h"
@ -1093,21 +1094,28 @@ const Type* ParserImpl::ConvertType(
bool is_non_writable = false;
ast::DecorationList ast_member_decorations;
for (auto& decoration : GetDecorationsForMember(type_id, member_index)) {
if (decoration[0] == SpvDecorationNonWritable) {
// WGSL doesn't represent individual members as non-writable. Instead,
// apply the ReadOnly access control to the containing struct if all
// the members are non-writable.
is_non_writable = true;
} else if (decoration[0] == SpvDecorationLocation) {
// Location decorations are handled when emitting the entry point.
} else {
auto* ast_member_decoration =
ConvertMemberDecoration(type_id, member_index, decoration);
if (!success_) {
return nullptr;
}
if (ast_member_decoration) {
ast_member_decorations.push_back(ast_member_decoration);
switch (decoration[0]) {
case SpvDecorationNonWritable:
// WGSL doesn't represent individual members as non-writable. Instead,
// apply the ReadOnly access control to the containing struct if all
// the members are non-writable.
is_non_writable = true;
break;
case SpvDecorationLocation:
case SpvDecorationFlat:
// IO decorations are handled when emitting the entry point.
break;
default: {
auto* ast_member_decoration =
ConvertMemberDecoration(type_id, member_index, decoration);
if (!success_) {
return nullptr;
}
if (ast_member_decoration) {
ast_member_decorations.push_back(ast_member_decoration);
}
break;
}
}
}
@ -1635,6 +1643,17 @@ bool ParserImpl::ConvertDecorationsForVariable(uint32_t id,
create<ast::LocationDecoration>(Source{}, deco[1]));
}
}
if (deco[0] == SpvDecorationFlat) {
if (transfer_pipeline_io) {
// In WGSL, integral types are always flat, and so the decoration
// is never specified.
if (!(*store_type)->IsIntegerScalarOrVector()) {
decorations->emplace_back(create<ast::InterpolateDecoration>(
Source{}, ast::InterpolationType::kFlat,
ast::InterpolationSampling::kNone));
}
}
}
if (deco[0] == SpvDecorationDescriptorSet) {
if (deco.size() == 1) {
return Fail() << "malformed DescriptorSet decoration on ID " << id

View File

@ -87,6 +87,7 @@ std::string CommonTypes() {
%v2uint = OpTypeVector %uint 2
%v2int = OpTypeVector %int 2
%v2float = OpTypeVector %float 2
%v4float = OpTypeVector %float 4
%m3v2float = OpTypeMatrix %v2float 3
%arr2uint = OpTypeArray %uint %uint_2
@ -7278,6 +7279,487 @@ TEST_F(SpvModuleScopeVarParserTest, FlattenStruct_LocOnStruct) {
"or member of a structure type"));
}
TEST_F(SpvModuleScopeVarParserTest,
EntryPointWrapping_Interpolation_Flat_Vertex_In) {
// Flat decorations are dropped for integral
const auto assembly = CommonCapabilities() + R"(
OpEntryPoint Vertex %main "main" %1 %2 %3 %4 %5 %6 %10
OpDecorate %1 Location 1
OpDecorate %2 Location 2
OpDecorate %3 Location 3
OpDecorate %4 Location 4
OpDecorate %5 Location 5
OpDecorate %6 Location 6
OpDecorate %1 Flat
OpDecorate %2 Flat
OpDecorate %3 Flat
OpDecorate %4 Flat
OpDecorate %5 Flat
OpDecorate %6 Flat
OpDecorate %10 BuiltIn Position
)" + CommonTypes() +
R"(
%ptr_in_uint = OpTypePointer Input %uint
%ptr_in_v2uint = OpTypePointer Input %v2uint
%ptr_in_int = OpTypePointer Input %int
%ptr_in_v2int = OpTypePointer Input %v2int
%ptr_in_float = OpTypePointer Input %float
%ptr_in_v2float = OpTypePointer Input %v2float
%1 = OpVariable %ptr_in_uint Input
%2 = OpVariable %ptr_in_v2uint Input
%3 = OpVariable %ptr_in_int Input
%4 = OpVariable %ptr_in_v2int Input
%5 = OpVariable %ptr_in_float Input
%6 = OpVariable %ptr_in_v2float Input
%ptr_out_v4float = OpTypePointer Output %v4float
%10 = OpVariable %ptr_out_v4float Output
%main = OpFunction %void None %voidfn
%entry = OpLabel
OpReturn
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
ASSERT_TRUE(p->BuildAndParseInternalModule());
EXPECT_TRUE(p->error().empty());
const auto got = p->program().to_str();
const std::string expected =
R"(Module{
Struct main_out {
StructMember{[[ BuiltinDecoration{position}
]] x_10_1: __vec_4__f32}
}
Variable{
x_1
private
undefined
__u32
}
Variable{
x_2
private
undefined
__vec_2__u32
}
Variable{
x_3
private
undefined
__i32
}
Variable{
x_4
private
undefined
__vec_2__i32
}
Variable{
x_5
private
undefined
__f32
}
Variable{
x_6
private
undefined
__vec_2__f32
}
Variable{
x_10
private
undefined
__vec_4__f32
}
Function main_1 -> __void
()
{
Return{}
}
Function main -> __type_name_main_out
StageDecoration{vertex}
(
VariableConst{
Decorations{
LocationDecoration{1}
}
x_1_param
none
undefined
__u32
}
VariableConst{
Decorations{
LocationDecoration{2}
}
x_2_param
none
undefined
__vec_2__u32
}
VariableConst{
Decorations{
LocationDecoration{3}
}
x_3_param
none
undefined
__i32
}
VariableConst{
Decorations{
LocationDecoration{4}
}
x_4_param
none
undefined
__vec_2__i32
}
VariableConst{
Decorations{
LocationDecoration{5}
InterpolateDecoration{flat none}
}
x_5_param
none
undefined
__f32
}
VariableConst{
Decorations{
LocationDecoration{6}
InterpolateDecoration{flat none}
}
x_6_param
none
undefined
__vec_2__f32
}
)
{
Assignment{
Identifier[not set]{x_1}
Identifier[not set]{x_1_param}
}
Assignment{
Identifier[not set]{x_2}
Identifier[not set]{x_2_param}
}
Assignment{
Identifier[not set]{x_3}
Identifier[not set]{x_3_param}
}
Assignment{
Identifier[not set]{x_4}
Identifier[not set]{x_4_param}
}
Assignment{
Identifier[not set]{x_5}
Identifier[not set]{x_5_param}
}
Assignment{
Identifier[not set]{x_6}
Identifier[not set]{x_6_param}
}
Call[not set]{
Identifier[not set]{main_1}
(
)
}
Return{
{
TypeConstructor[not set]{
__type_name_main_out
Identifier[not set]{x_10}
}
}
}
}
}
)";
EXPECT_EQ(got, expected) << got;
}
TEST_F(SpvModuleScopeVarParserTest,
EntryPointWrapping_Interpolation_Flat_Vertex_Output) {
// Flat decorations are dropped for integral
const auto assembly = CommonCapabilities() + R"(
OpEntryPoint Vertex %main "main" %1 %2 %3 %4 %5 %6 %10
OpDecorate %1 Location 1
OpDecorate %2 Location 2
OpDecorate %3 Location 3
OpDecorate %4 Location 4
OpDecorate %5 Location 5
OpDecorate %6 Location 6
OpDecorate %1 Flat
OpDecorate %2 Flat
OpDecorate %3 Flat
OpDecorate %4 Flat
OpDecorate %5 Flat
OpDecorate %6 Flat
OpDecorate %10 BuiltIn Position
)" + CommonTypes() +
R"(
%ptr_out_uint = OpTypePointer Output %uint
%ptr_out_v2uint = OpTypePointer Output %v2uint
%ptr_out_int = OpTypePointer Output %int
%ptr_out_v2int = OpTypePointer Output %v2int
%ptr_out_float = OpTypePointer Output %float
%ptr_out_v2float = OpTypePointer Output %v2float
%1 = OpVariable %ptr_out_uint Output
%2 = OpVariable %ptr_out_v2uint Output
%3 = OpVariable %ptr_out_int Output
%4 = OpVariable %ptr_out_v2int Output
%5 = OpVariable %ptr_out_float Output
%6 = OpVariable %ptr_out_v2float Output
%ptr_out_v4float = OpTypePointer Output %v4float
%10 = OpVariable %ptr_out_v4float Output
%main = OpFunction %void None %voidfn
%entry = OpLabel
OpReturn
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
ASSERT_TRUE(p->BuildAndParseInternalModule());
EXPECT_TRUE(p->error().empty());
const auto got = p->program().to_str();
const std::string expected =
R"(Module{
Struct main_out {
StructMember{[[ LocationDecoration{1}
]] x_1_1: __u32}
StructMember{[[ LocationDecoration{2}
]] x_2_1: __vec_2__u32}
StructMember{[[ LocationDecoration{3}
]] x_3_1: __i32}
StructMember{[[ LocationDecoration{4}
]] x_4_1: __vec_2__i32}
StructMember{[[ LocationDecoration{5}
InterpolateDecoration{flat none}
]] x_5_1: __f32}
StructMember{[[ LocationDecoration{6}
InterpolateDecoration{flat none}
]] x_6_1: __vec_2__f32}
StructMember{[[ BuiltinDecoration{position}
]] x_10_1: __vec_4__f32}
}
Variable{
x_1
private
undefined
__u32
}
Variable{
x_2
private
undefined
__vec_2__u32
}
Variable{
x_3
private
undefined
__i32
}
Variable{
x_4
private
undefined
__vec_2__i32
}
Variable{
x_5
private
undefined
__f32
}
Variable{
x_6
private
undefined
__vec_2__f32
}
Variable{
x_10
private
undefined
__vec_4__f32
}
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]{x_1}
Identifier[not set]{x_2}
Identifier[not set]{x_3}
Identifier[not set]{x_4}
Identifier[not set]{x_5}
Identifier[not set]{x_6}
Identifier[not set]{x_10}
}
}
}
}
}
)";
EXPECT_EQ(got, expected) << got;
}
TEST_F(SpvModuleScopeVarParserTest,
EntryPointWrapping_Flatten_Interpolation_Flat_Fragment_In) {
// Flat decorations are dropped for integral
const auto assembly = CommonCapabilities() + R"(
OpEntryPoint Fragment %main "main" %1 %2
OpExecutionMode %main OriginUpperLeft
OpDecorate %1 Location 1
OpDecorate %2 Location 5
OpDecorate %1 Flat
OpDecorate %2 Flat
)" + CommonTypes() +
R"(
%arr = OpTypeArray %float %uint_2
%strct = OpTypeStruct %float %float
%ptr_in_arr = OpTypePointer Input %arr
%ptr_in_strct = OpTypePointer Input %strct
%1 = OpVariable %ptr_in_arr Input
%2 = OpVariable %ptr_in_strct Input
%main = OpFunction %void None %voidfn
%entry = OpLabel
OpReturn
OpFunctionEnd
)";
auto p = parser(test::Assemble(assembly));
ASSERT_TRUE(p->BuildAndParseInternalModule());
EXPECT_TRUE(p->error().empty());
const auto got = p->program().to_str();
const std::string expected =
R"(Module{
Struct S {
StructMember{field0: __f32}
StructMember{field1: __f32}
}
Variable{
x_1
private
undefined
__array__f32_2
}
Variable{
x_2
private
undefined
__type_name_S
}
Function main_1 -> __void
()
{
Return{}
}
Function main -> __void
StageDecoration{fragment}
(
VariableConst{
Decorations{
LocationDecoration{1}
InterpolateDecoration{flat none}
}
x_1_param
none
undefined
__f32
}
VariableConst{
Decorations{
LocationDecoration{2}
InterpolateDecoration{flat none}
}
x_1_param_1
none
undefined
__f32
}
VariableConst{
Decorations{
LocationDecoration{5}
InterpolateDecoration{flat none}
}
x_2_param
none
undefined
__f32
}
VariableConst{
Decorations{
LocationDecoration{6}
InterpolateDecoration{flat none}
}
x_2_param_1
none
undefined
__f32
}
)
{
Assignment{
ArrayAccessor[not set]{
Identifier[not set]{x_1}
ScalarConstructor[not set]{0}
}
Identifier[not set]{x_1_param}
}
Assignment{
ArrayAccessor[not set]{
Identifier[not set]{x_1}
ScalarConstructor[not set]{1}
}
Identifier[not set]{x_1_param_1}
}
Assignment{
MemberAccessor[not set]{
Identifier[not set]{x_2}
Identifier[not set]{field0}
}
Identifier[not set]{x_2_param}
}
Assignment{
MemberAccessor[not set]{
Identifier[not set]{x_2}
Identifier[not set]{field1}
}
Identifier[not set]{x_2_param_1}
}
Call[not set]{
Identifier[not set]{main_1}
(
)
}
}
}
)";
EXPECT_EQ(got, expected) << got;
}
} // namespace
} // namespace spirv
} // namespace reader