From 494e82dc8a575249b042c346423c98ad969afd6a Mon Sep 17 00:00:00 2001 From: James Price Date: Wed, 31 Mar 2021 15:42:17 +0000 Subject: [PATCH] [resolver] Track pipeline stage usages for structs This will be used by the generators to determine how to handle location decorations. Change-Id: Ib0e0ce852a5da3819781b402c5625a440c4c9544 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/46400 Auto-Submit: James Price Kokoro: Kokoro Reviewed-by: Ben Clayton Commit-Queue: Ben Clayton --- src/CMakeLists.txt | 1 + src/resolver/resolver.cc | 51 ++++- src/resolver/resolver.h | 2 + .../struct_pipeline_stage_use_test.cc | 204 ++++++++++++++++++ src/semantic/sem_struct.cc | 6 +- src/semantic/struct.h | 20 +- test/BUILD.gn | 1 + 7 files changed, 281 insertions(+), 4 deletions(-) create mode 100644 src/resolver/struct_pipeline_stage_use_test.cc diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b5cc0d0772..8a27981ac1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -483,6 +483,7 @@ if(${TINT_BUILD_TESTS}) resolver/resolver_test_helper.h resolver/resolver_test.cc resolver/struct_layout_test.cc + resolver/struct_pipeline_stage_use_test.cc resolver/struct_storage_class_use_test.cc resolver/type_constructor_validation_test.cc resolver/type_validation_test.cc diff --git a/src/resolver/resolver.cc b/src/resolver/resolver.cc index 3c8dfccda2..d7bb477a11 100644 --- a/src/resolver/resolver.cc +++ b/src/resolver/resolver.cc @@ -293,6 +293,54 @@ bool Resolver::Function(ast::Function* func) { variable_stack_.push_scope(); for (auto* param : func->params()) { variable_stack_.set(param->symbol(), CreateVariableInfo(param)); + + if (auto* str = + param->declared_type()->UnwrapAliasIfNeeded()->As()) { + auto* info = Structure(str); + if (!info) { + return false; + } + switch (func->pipeline_stage()) { + case ast::PipelineStage::kVertex: + info->pipeline_stage_uses.emplace( + semantic::PipelineStageUsage::kVertexInput); + break; + case ast::PipelineStage::kFragment: + info->pipeline_stage_uses.emplace( + semantic::PipelineStageUsage::kFragmentInput); + break; + case ast::PipelineStage::kCompute: + info->pipeline_stage_uses.emplace( + semantic::PipelineStageUsage::kComputeInput); + break; + case ast::PipelineStage::kNone: + break; + } + } + } + + if (auto* str = + func->return_type()->UnwrapAliasIfNeeded()->As()) { + auto* info = Structure(str); + if (!info) { + return false; + } + switch (func->pipeline_stage()) { + case ast::PipelineStage::kVertex: + info->pipeline_stage_uses.emplace( + semantic::PipelineStageUsage::kVertexOutput); + break; + case ast::PipelineStage::kFragment: + info->pipeline_stage_uses.emplace( + semantic::PipelineStageUsage::kFragmentOutput); + break; + case ast::PipelineStage::kCompute: + info->pipeline_stage_uses.emplace( + semantic::PipelineStageUsage::kComputeOutput); + break; + case ast::PipelineStage::kNone: + break; + } } if (!BlockStatement(func->body())) { @@ -1359,7 +1407,8 @@ void Resolver::CreateSemanticNodes() const { builder_->Sem().Add( str, builder_->create( str, std::move(info->members), info->align, info->size, - info->size_no_padding, info->storage_class_usage)); + info->size_no_padding, info->storage_class_usage, + info->pipeline_stage_uses)); } } diff --git a/src/resolver/resolver.h b/src/resolver/resolver.h index 2aa09874f7..be29997854 100644 --- a/src/resolver/resolver.h +++ b/src/resolver/resolver.h @@ -24,6 +24,7 @@ #include "src/intrinsic_table.h" #include "src/program_builder.h" #include "src/scope_stack.h" +#include "src/semantic/struct.h" #include "src/utils/unique_vector.h" namespace tint { @@ -139,6 +140,7 @@ class Resolver { uint32_t size = 0; uint32_t size_no_padding = 0; std::unordered_set storage_class_usage; + std::unordered_set pipeline_stage_uses; }; /// Structure holding semantic information about a block (i.e. scope), such as diff --git a/src/resolver/struct_pipeline_stage_use_test.cc b/src/resolver/struct_pipeline_stage_use_test.cc new file mode 100644 index 0000000000..6804e47589 --- /dev/null +++ b/src/resolver/struct_pipeline_stage_use_test.cc @@ -0,0 +1,204 @@ +// Copyright 2021 The Tint Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "src/resolver/resolver.h" + +#include "gmock/gmock.h" +#include "src/ast/stage_decoration.h" +#include "src/resolver/resolver_test_helper.h" +#include "src/semantic/struct.h" + +using ::testing::UnorderedElementsAre; + +namespace tint { +namespace resolver { +namespace { + +using ResolverPipelineStageUseTest = ResolverTest; + +TEST_F(ResolverPipelineStageUseTest, UnusedStruct) { + auto* s = Structure( + "S", {Member("a", ty.f32(), {create(0)})}); + + ASSERT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(s); + ASSERT_NE(sem, nullptr); + EXPECT_TRUE(sem->PipelineStageUses().empty()); +} + +TEST_F(ResolverPipelineStageUseTest, StructUsedAsNonEntryPointParam) { + auto* s = Structure( + "S", {Member("a", ty.f32(), {create(0)})}); + + Func("foo", {Const("param", s)}, ty.void_(), {}, {}); + + ASSERT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(s); + ASSERT_NE(sem, nullptr); + EXPECT_TRUE(sem->PipelineStageUses().empty()); +} + +TEST_F(ResolverPipelineStageUseTest, StructUsedAsNonEntryPointReturnType) { + auto* s = Structure( + "S", {Member("a", ty.f32(), {create(0)})}); + + Func("foo", {}, s, {Return(Construct(s, Expr(0.f)))}, {}); + + ASSERT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(s); + ASSERT_NE(sem, nullptr); + EXPECT_TRUE(sem->PipelineStageUses().empty()); +} + +TEST_F(ResolverPipelineStageUseTest, StructUsedAsVertexShaderParam) { + auto* s = Structure( + "S", {Member("a", ty.f32(), {create(0)})}); + + Func("main", {Const("param", s)}, ty.void_(), {}, + {create(ast::PipelineStage::kVertex)}); + + ASSERT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(s); + ASSERT_NE(sem, nullptr); + EXPECT_THAT(sem->PipelineStageUses(), + UnorderedElementsAre(semantic::PipelineStageUsage::kVertexInput)); +} + +TEST_F(ResolverPipelineStageUseTest, StructUsedAsVertexShaderReturnType) { + auto* s = Structure( + "S", {Member("a", ty.f32(), {create(0)})}); + + Func("main", {}, s, {Return(Construct(s, Expr(0.f)))}, + {create(ast::PipelineStage::kVertex)}); + + ASSERT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(s); + ASSERT_NE(sem, nullptr); + EXPECT_THAT( + sem->PipelineStageUses(), + UnorderedElementsAre(semantic::PipelineStageUsage::kVertexOutput)); +} + +TEST_F(ResolverPipelineStageUseTest, StructUsedAsFragmentShaderParam) { + auto* s = Structure( + "S", {Member("a", ty.f32(), {create(0)})}); + + Func("main", {Const("param", s)}, ty.void_(), {}, + {create(ast::PipelineStage::kFragment)}); + + ASSERT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(s); + ASSERT_NE(sem, nullptr); + EXPECT_THAT( + sem->PipelineStageUses(), + UnorderedElementsAre(semantic::PipelineStageUsage::kFragmentInput)); +} + +TEST_F(ResolverPipelineStageUseTest, StructUsedAsFragmentShaderReturnType) { + auto* s = Structure( + "S", {Member("a", ty.f32(), {create(0)})}); + + Func("main", {}, s, {Return(Construct(s, Expr(0.f)))}, + {create(ast::PipelineStage::kFragment)}); + + ASSERT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(s); + ASSERT_NE(sem, nullptr); + EXPECT_THAT( + sem->PipelineStageUses(), + UnorderedElementsAre(semantic::PipelineStageUsage::kFragmentOutput)); +} + +TEST_F(ResolverPipelineStageUseTest, StructUsedAsComputeShaderParam) { + auto* s = Structure("S", {Member("a", ty.u32(), + {create( + ast::Builtin::kLocalInvocationIndex)})}); + + Func("main", {Const("param", s)}, ty.void_(), {}, + {create(ast::PipelineStage::kCompute)}); + + ASSERT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(s); + ASSERT_NE(sem, nullptr); + EXPECT_THAT( + sem->PipelineStageUses(), + UnorderedElementsAre(semantic::PipelineStageUsage::kComputeInput)); +} + +TEST_F(ResolverPipelineStageUseTest, StructUsedMultipleStages) { + auto* s = Structure( + "S", {Member("a", ty.f32(), {create(0)})}); + + Func("vert_main", {Const("param", s)}, s, {Return(Construct(s, Expr(0.f)))}, + {create(ast::PipelineStage::kVertex)}); + + Func("frag_main", {Const("param", s)}, ty.void_(), {}, + {create(ast::PipelineStage::kFragment)}); + + ASSERT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(s); + ASSERT_NE(sem, nullptr); + EXPECT_THAT( + sem->PipelineStageUses(), + UnorderedElementsAre(semantic::PipelineStageUsage::kVertexInput, + semantic::PipelineStageUsage::kVertexOutput, + semantic::PipelineStageUsage::kFragmentInput)); +} + +TEST_F(ResolverPipelineStageUseTest, StructUsedAsShaderParamViaAlias) { + auto* s = Structure( + "S", {Member("a", ty.f32(), {create(0)})}); + auto* s_alias = ty.alias("S_alias", s); + + Func("main", {Const("param", s_alias)}, ty.void_(), {}, + {create(ast::PipelineStage::kFragment)}); + + ASSERT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(s); + ASSERT_NE(sem, nullptr); + EXPECT_THAT( + sem->PipelineStageUses(), + UnorderedElementsAre(semantic::PipelineStageUsage::kFragmentInput)); +} + +TEST_F(ResolverPipelineStageUseTest, StructUsedAsShaderReturnTypeViaAlias) { + auto* s = Structure( + "S", {Member("a", ty.f32(), {create(0)})}); + auto* s_alias = ty.alias("S_alias", s); + + Func("main", {}, s_alias, {Return(Construct(s_alias, Expr(0.f)))}, + {create(ast::PipelineStage::kFragment)}); + + ASSERT_TRUE(r()->Resolve()) << r()->error(); + + auto* sem = Sem().Get(s); + ASSERT_NE(sem, nullptr); + EXPECT_THAT( + sem->PipelineStageUses(), + UnorderedElementsAre(semantic::PipelineStageUsage::kFragmentOutput)); +} + +} // namespace +} // namespace resolver +} // namespace tint diff --git a/src/semantic/sem_struct.cc b/src/semantic/sem_struct.cc index 4201517838..78c272a093 100644 --- a/src/semantic/sem_struct.cc +++ b/src/semantic/sem_struct.cc @@ -25,13 +25,15 @@ Struct::Struct(type::Struct* type, uint32_t align, uint32_t size, uint32_t size_no_padding, - std::unordered_set storage_class_usage) + std::unordered_set storage_class_usage, + std::unordered_set pipeline_stage_uses) : type_(type), members_(std::move(members)), align_(align), size_(size), size_no_padding_(size_no_padding), - storage_class_usage_(std::move(storage_class_usage)) {} + storage_class_usage_(std::move(storage_class_usage)), + pipeline_stage_uses_(std::move(pipeline_stage_uses)) {} Struct::~Struct() = default; diff --git a/src/semantic/struct.h b/src/semantic/struct.h index 70c315b504..58cd91edfd 100644 --- a/src/semantic/struct.h +++ b/src/semantic/struct.h @@ -40,6 +40,16 @@ class StructMember; /// A vector of StructMember pointers. using StructMemberList = std::vector; +/// Metadata to capture how a structure is used in a shader module. +enum class PipelineStageUsage { + kVertexInput, + kVertexOutput, + kFragmentInput, + kFragmentOutput, + kComputeInput, + kComputeOutput, +}; + /// Struct holds the semantic information for structures. class Struct : public Castable { public: @@ -51,12 +61,14 @@ class Struct : public Castable { /// @param size_no_padding size of the members without the end of structure /// alignment padding /// @param storage_class_usage a set of all the storage class usages + /// @param pipeline_stage_uses a set of all the pipeline stage uses Struct(type::Struct* type, StructMemberList members, uint32_t align, uint32_t size, uint32_t size_no_padding, - std::unordered_set storage_class_usage); + std::unordered_set storage_class_usage, + std::unordered_set pipeline_stage_uses); /// Destructor ~Struct() override; @@ -105,6 +117,11 @@ class Struct : public Castable { return false; } + /// @returns the set of entry point uses of this structure + const std::unordered_set& PipelineStageUses() const { + return pipeline_stage_uses_; + } + private: type::Struct* const type_; StructMemberList const members_; @@ -112,6 +129,7 @@ class Struct : public Castable { uint32_t const size_; uint32_t const size_no_padding_; std::unordered_set const storage_class_usage_; + std::unordered_set const pipeline_stage_uses_; }; /// StructMember holds the semantic information for structure members. diff --git a/test/BUILD.gn b/test/BUILD.gn index c7f99146a3..a806af6025 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -181,6 +181,7 @@ source_set("tint_unittests_core_src") { "../src/resolver/resolver_test_helper.cc", "../src/resolver/resolver_test_helper.h", "../src/resolver/struct_layout_test.cc", + "../src/resolver/struct_pipeline_stage_use_test.cc", "../src/resolver/struct_storage_class_use_test.cc", "../src/resolver/type_constructor_validation_test.cc", "../src/resolver/type_validation_test.cc",