From fcbc6efa8fd37bb6dd727a8ee897511cca9b2977 Mon Sep 17 00:00:00 2001 From: Ryan Harrison Date: Tue, 27 Oct 2020 18:03:09 +0000 Subject: [PATCH] [inspector] Extract storage-buffer information BUG=tint:257 Change-Id: Ib85d76a3b972226b401e57ba2a632d6252e95f8f Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/31080 Commit-Queue: Ryan Harrison Reviewed-by: dan sinclair --- src/ast/type/array_type.cc | 10 +- src/inspector/inspector.cc | 56 ++++++- src/inspector/inspector.h | 20 ++- src/inspector/inspector_test.cc | 276 ++++++++++++++++++++++++++++---- 4 files changed, 317 insertions(+), 45 deletions(-) diff --git a/src/ast/type/array_type.cc b/src/ast/type/array_type.cc index 179f6de3dd..776ac0764e 100644 --- a/src/ast/type/array_type.cc +++ b/src/ast/type/array_type.cc @@ -34,15 +34,15 @@ bool ArrayType::IsArray() const { } uint64_t ArrayType::MinBufferBindingSize() const { - if (IsRuntimeArray()) { - return array_stride(); - } + // RTAs have a size_ = 0, but the value that is wanted from this call is the + // minimum size, so assuming atleast 1 element in the RTA. + uint32_t size = IsRuntimeArray() ? 1 : size_; if (has_array_stride()) { - return size_ * array_stride(); + return size * array_stride(); } - return size_ * type()->MinBufferBindingSize(); + return size * type()->MinBufferBindingSize(); } uint32_t ArrayType::array_stride() const { diff --git a/src/inspector/inspector.cc b/src/inspector/inspector.cc index 239dcbb613..9a5e90add3 100644 --- a/src/inspector/inspector.cc +++ b/src/inspector/inspector.cc @@ -137,14 +137,8 @@ std::map Inspector::GetConstantIDs() { std::vector Inspector::GetUniformBufferResourceBindings( const std::string& entry_point) { - auto* func = module_.FindFunctionByName(entry_point); + auto* func = FindEntryPointByName(entry_point); if (!func) { - error_ += entry_point + " was not found!"; - return {}; - } - - if (!func->IsEntryPoint()) { - error_ += entry_point + " is not an entry point!"; return {}; } @@ -173,5 +167,53 @@ std::vector Inspector::GetUniformBufferResourceBindings( return result; } +std::vector Inspector::GetStorageBufferResourceBindings( + const std::string& entry_point) { + auto* func = FindEntryPointByName(entry_point); + if (!func) { + return {}; + } + + std::vector result; + for (auto& ruv : func->referenced_storagebuffer_variables()) { + ResourceBinding entry; + ast::Variable* var = nullptr; + ast::Function::BindingInfo binding_info; + std::tie(var, binding_info) = ruv; + if (!var->type()->IsStruct()) { + continue; + } + + entry.bind_group = binding_info.set->value(); + entry.binding = binding_info.binding->value(); + entry.min_buffer_binding_size = var->type()->MinBufferBindingSize(); + + result.push_back(std::move(entry)); + } + + return result; +} + +std::vector +Inspector::GetReadOnlyStorageBufferResourceBindings(const std::string&) { + error_ += "GetReadOnlyStorageBufferResourceBindings is not implemented!"; + return {}; +} + +ast::Function* Inspector::FindEntryPointByName(const std::string& name) { + auto* func = module_.FindFunctionByName(name); + if (!func) { + error_ += name + " was not found!"; + return nullptr; + } + + if (!func->IsEntryPoint()) { + error_ += name + " is not an entry point!"; + return nullptr; + } + + return func; +} + } // namespace inspector } // namespace tint diff --git a/src/inspector/inspector.h b/src/inspector/inspector.h index a0d82f2a91..8ff1e2e63f 100644 --- a/src/inspector/inspector.h +++ b/src/inspector/inspector.h @@ -59,13 +59,31 @@ class Inspector { std::map GetConstantIDs(); /// @param entry_point name of the entry point to get information about. - /// @returns vector of all of the bindings for Uniform buffers. + /// @returns vector of all of the bindings for uniform buffers. std::vector GetUniformBufferResourceBindings( const std::string& entry_point); + /// @param entry_point name of the entry point to get information about. + /// @returns vector of all of the bindings for storage buffers. + std::vector GetStorageBufferResourceBindings( + const std::string& entry_point); + + // TODO(rharrison): Implement once + // https://dawn-review.googlesource.com/c/tint/+/31060 lands. + /// @param entry_point name of the entry point to get information about. + /// @returns an empty vector and sets the error string. In the future it will + /// return of all of the bindings for read-only storage buffers. + std::vector GetReadOnlyStorageBufferResourceBindings( + const std::string& entry_point); + private: const ast::Module& module_; std::string error_; + + /// @param name name of the entry point to find + /// @returns a pointer to the entry point if it exists, otherwise returns + /// nullptr and sets the error string. + ast::Function* FindEntryPointByName(const std::string& name); }; } // namespace inspector diff --git a/src/inspector/inspector_test.cc b/src/inspector/inspector_test.cc index 6906ae8bed..91e69800da 100644 --- a/src/inspector/inspector_test.cc +++ b/src/inspector/inspector_test.cc @@ -254,14 +254,16 @@ class InspectorHelper { return std::to_string(idx) + type->type_name(); } - /// Generates a struct type appropriate for using in an uniform buffer + /// Generates a struct type /// @param name name for the type /// @param members_info a vector of {type, offset} where each entry is the /// type and offset of a member of the struct - /// @returns a struct type suitable to use for an uniform buffer - std::unique_ptr MakeUniformBufferStructType( + /// @param is_block whether or not to decorate as a Block + /// @returns a struct type + std::unique_ptr MakeStructType( const std::string& name, - std::vector> members_info) { + std::vector> members_info, + bool is_block) { ast::StructMemberList members; for (auto& member_info : members_info) { ast::type::Type* type; @@ -275,8 +277,11 @@ class InspectorHelper { members.push_back(std::make_unique( StructMemberName(members.size(), type), type, std::move(deco))); } + ast::StructDecorationList decos; - decos.push_back(ast::StructDecoration::kBlock); + if (is_block) { + decos.push_back(ast::StructDecoration::kBlock); + } auto str = std::make_unique(std::move(decos), std::move(members)); @@ -284,6 +289,50 @@ class InspectorHelper { return std::make_unique(name, std::move(str)); } + /// Generates a struct type appropriate for using in an uniform buffer + /// @param name name for the type + /// @param members_info a vector of {type, offset} where each entry is the + /// type and offset of a member of the struct + /// @returns a struct type suitable to use for an uniform buffer + std::unique_ptr MakeUniformBufferStructType( + const std::string& name, + std::vector> members_info) { + return MakeStructType(name, members_info, true); + } + + /// Generates a struct type appropriate for using in an storage buffer + /// @param name name for the type + /// @param members_info a vector of {type, offset} where each entry is the + /// type and offset of a member of the struct + /// @returns a struct type suitable to use for an uniform buffer + std::unique_ptr MakeStorageBufferStructType( + const std::string& name, + std::vector> members_info) { + return MakeStructType(name, members_info, false); + } + + /// Adds a binding variable with a struct type to the module + /// @param name the name of the variable + /// @param struct_type the type to use + /// @param storage_class the storage class to use + /// @param set the binding group/set to use for the uniform buffer + /// @param binding the binding number to use for the uniform buffer + void AddStructBinding(const std::string& name, + ast::type::StructType* struct_type, + ast::StorageClass storage_class, + uint32_t set, + uint32_t binding) { + auto var = std::make_unique( + std::make_unique(name, storage_class, struct_type)); + ast::VariableDecorationList decorations; + + decorations.push_back(std::make_unique(binding)); + decorations.push_back(std::make_unique(set)); + var->set_decorations(std::move(decorations)); + + mod()->AddGlobalVariable(std::move(var)); + } + /// Adds an uniform buffer variable to the module /// @param name the name of the variable /// @param struct_type the type to use @@ -293,26 +342,31 @@ class InspectorHelper { ast::type::StructType* struct_type, uint32_t set, uint32_t binding) { - auto var = std::make_unique( - std::make_unique(name, ast::StorageClass::kUniform, - struct_type)); - ast::VariableDecorationList decorations; - - decorations.push_back(std::make_unique(binding)); - decorations.push_back(std::make_unique(set)); - var->set_decorations(std::move(decorations)); - - mod()->AddGlobalVariable(std::move(var)); + AddStructBinding(name, struct_type, ast::StorageClass::kUniform, set, + binding); } - /// Generates a function that references a specific uniform buffer + /// Adds an storage buffer variable to the module + /// @param name the name of the variable + /// @param struct_type the type to use + /// @param set the binding group/set to use for the storage buffer + /// @param binding the binding number to use for the storage buffer + void AddStorageBuffer(const std::string& name, + ast::type::StructType* struct_type, + uint32_t set, + uint32_t binding) { + AddStructBinding(name, struct_type, ast::StorageClass::kStorageBuffer, set, + binding); + } + + /// Generates a function that references a specific struct variable /// @param func_name name of the function created - /// @param ub_name name of the uniform buffer to be accessed + /// @param struct_name name of the struct variabler to be accessed /// @param members list of members to access, by index and type - /// @returns a function that references all of the ub members specified - std::unique_ptr MakeUniformBufferReferenceBodyFunction( + /// @returns a function that references all of the members specified + std::unique_ptr MakeStructVariableReferenceBodyFunction( std::string func_name, - std::string ub_name, + std::string struct_name, std::vector> members) { auto body = std::make_unique(); @@ -334,7 +388,7 @@ class InspectorHelper { body->append(std::make_unique( std::make_unique("local" + member_name), std::make_unique( - std::make_unique(ub_name), + std::make_unique(struct_name), std::make_unique(member_name)))); } @@ -381,6 +435,7 @@ class InspectorTest : public InspectorHelper, public testing::Test {}; class InspectorGetEntryPointTest : public InspectorTest {}; class InspectorGetConstantIDsTest : public InspectorTest {}; class InspectorGetUniformBufferResourceBindings : public InspectorTest {}; +class InspectorGetStorageBufferResourceBindings : public InspectorTest {}; TEST_F(InspectorGetEntryPointTest, NoFunctions) { auto result = inspector()->GetEntryPoints(); @@ -801,8 +856,8 @@ TEST_F(InspectorGetUniformBufferResourceBindings, NonEntryPointFunc) { auto foo_type = MakeUniformBufferStructType("foo_type", {{i32_type(), 0}}); AddUniformBuffer("foo_ub", foo_type.get(), 0, 0); - auto ub_func = MakeUniformBufferReferenceBodyFunction("ub_func", "foo_ub", - {{0, i32_type()}}); + auto ub_func = MakeStructVariableReferenceBodyFunction("ub_func", "foo_ub", + {{0, i32_type()}}); mod()->AddFunction(std::move(ub_func)); auto ep_func = MakeCallerBodyFunction("ep_func", "ub_func"); @@ -835,8 +890,8 @@ TEST_F(InspectorGetUniformBufferResourceBindings, MissingBlockDeco) { AddUniformBuffer("foo_ub", foo_type.get(), 0, 0); - auto ub_func = MakeUniformBufferReferenceBodyFunction("ub_func", "foo_ub", - {{0, i32_type()}}); + auto ub_func = MakeStructVariableReferenceBodyFunction("ub_func", "foo_ub", + {{0, i32_type()}}); mod()->AddFunction(std::move(ub_func)); auto ep_func = MakeCallerBodyFunction("ep_func", "ub_func"); @@ -855,8 +910,8 @@ TEST_F(InspectorGetUniformBufferResourceBindings, Simple) { auto foo_type = MakeUniformBufferStructType("foo_type", {{i32_type(), 0}}); AddUniformBuffer("foo_ub", foo_type.get(), 0, 0); - auto ub_func = MakeUniformBufferReferenceBodyFunction("ub_func", "foo_ub", - {{0, i32_type()}}); + auto ub_func = MakeStructVariableReferenceBodyFunction("ub_func", "foo_ub", + {{0, i32_type()}}); mod()->AddFunction(std::move(ub_func)); auto ep_func = MakeCallerBodyFunction("ep_func", "ub_func"); @@ -880,7 +935,7 @@ TEST_F(InspectorGetUniformBufferResourceBindings, MultipleMembers) { "foo_type", {{i32_type(), 0}, {u32_type(), 4}, {f32_type(), 8}}); AddUniformBuffer("foo_ub", foo_type.get(), 0, 0); - auto ub_func = MakeUniformBufferReferenceBodyFunction( + auto ub_func = MakeStructVariableReferenceBodyFunction( "ub_func", "foo_ub", {{0, i32_type()}, {1, u32_type()}, {2, f32_type()}}); mod()->AddFunction(std::move(ub_func)); @@ -900,7 +955,7 @@ TEST_F(InspectorGetUniformBufferResourceBindings, MultipleMembers) { EXPECT_EQ(12u, result[0].min_buffer_binding_size); } -TEST_F(InspectorGetUniformBufferResourceBindings, MultipleUniformBufferS) { +TEST_F(InspectorGetUniformBufferResourceBindings, MultipleUniformBuffers) { auto ub_type = MakeUniformBufferStructType( "ub_type", {{i32_type(), 0}, {u32_type(), 4}, {f32_type(), 8}}); AddUniformBuffer("ub_foo", ub_type.get(), 0, 0); @@ -909,7 +964,7 @@ TEST_F(InspectorGetUniformBufferResourceBindings, MultipleUniformBufferS) { auto AddReferenceFunc = [this](const std::string& func_name, const std::string& var_name) { - auto ub_func = MakeUniformBufferReferenceBodyFunction( + auto ub_func = MakeStructVariableReferenceBodyFunction( func_name, var_name, {{0, i32_type()}, {1, u32_type()}, {2, f32_type()}}); mod()->AddFunction(std::move(ub_func)); @@ -963,8 +1018,8 @@ TEST_F(InspectorGetUniformBufferResourceBindings, ContainingArray) { "foo_type", {{i32_type(), 0}, {u32_array_type(4), 4}}); AddUniformBuffer("foo_ub", foo_type.get(), 0, 0); - auto ub_func = MakeUniformBufferReferenceBodyFunction("ub_func", "foo_ub", - {{0, i32_type()}}); + auto ub_func = MakeStructVariableReferenceBodyFunction("ub_func", "foo_ub", + {{0, i32_type()}}); mod()->AddFunction(std::move(ub_func)); auto ep_func = MakeCallerBodyFunction("ep_func", "ub_func"); @@ -983,6 +1038,163 @@ TEST_F(InspectorGetUniformBufferResourceBindings, ContainingArray) { EXPECT_EQ(20u, result[0].min_buffer_binding_size); } +TEST_F(InspectorGetStorageBufferResourceBindings, Simple) { + auto foo_type = MakeStorageBufferStructType("foo_type", {{i32_type(), 0}}); + AddStorageBuffer("foo_sb", foo_type.get(), 0, 0); + + auto sb_func = MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", + {{0, i32_type()}}); + mod()->AddFunction(std::move(sb_func)); + + auto ep_func = MakeCallerBodyFunction("ep_func", "sb_func"); + ep_func->add_decoration( + std::make_unique(ast::PipelineStage::kVertex)); + mod()->AddFunction(std::move(ep_func)); + + ASSERT_TRUE(td()->Determine()) << td()->error(); + + auto result = inspector()->GetStorageBufferResourceBindings("ep_func"); + ASSERT_FALSE(inspector()->has_error()); + ASSERT_EQ(1u, result.size()); + + EXPECT_EQ(0u, result[0].bind_group); + EXPECT_EQ(0u, result[0].binding); + EXPECT_EQ(4u, result[0].min_buffer_binding_size); +} + +TEST_F(InspectorGetStorageBufferResourceBindings, MultipleMembers) { + auto foo_type = MakeStorageBufferStructType( + "foo_type", {{i32_type(), 0}, {u32_type(), 4}, {f32_type(), 8}}); + AddStorageBuffer("foo_sb", foo_type.get(), 0, 0); + + auto sb_func = MakeStructVariableReferenceBodyFunction( + "sb_func", "foo_sb", {{0, i32_type()}, {1, u32_type()}, {2, f32_type()}}); + mod()->AddFunction(std::move(sb_func)); + + auto ep_func = MakeCallerBodyFunction("ep_func", "sb_func"); + ep_func->add_decoration( + std::make_unique(ast::PipelineStage::kVertex)); + mod()->AddFunction(std::move(ep_func)); + + ASSERT_TRUE(td()->Determine()) << td()->error(); + + auto result = inspector()->GetStorageBufferResourceBindings("ep_func"); + ASSERT_FALSE(inspector()->has_error()); + ASSERT_EQ(1u, result.size()); + + EXPECT_EQ(0u, result[0].bind_group); + EXPECT_EQ(0u, result[0].binding); + EXPECT_EQ(12u, result[0].min_buffer_binding_size); +} + +TEST_F(InspectorGetStorageBufferResourceBindings, MultipleStorageBuffers) { + auto sb_type = MakeStorageBufferStructType( + "sb_type", {{i32_type(), 0}, {u32_type(), 4}, {f32_type(), 8}}); + AddStorageBuffer("sb_foo", sb_type.get(), 0, 0); + AddStorageBuffer("sb_bar", sb_type.get(), 0, 1); + AddStorageBuffer("sb_baz", sb_type.get(), 2, 0); + + auto AddReferenceFunc = [this](const std::string& func_name, + const std::string& var_name) { + auto sb_func = MakeStructVariableReferenceBodyFunction( + func_name, var_name, + {{0, i32_type()}, {1, u32_type()}, {2, f32_type()}}); + mod()->AddFunction(std::move(sb_func)); + }; + AddReferenceFunc("sb_foo_func", "sb_foo"); + AddReferenceFunc("sb_bar_func", "sb_bar"); + AddReferenceFunc("sb_baz_func", "sb_baz"); + + auto AddFuncCall = [](ast::BlockStatement* body, const std::string& callee) { + auto ident_expr = std::make_unique(callee); + auto call_expr = std::make_unique( + std::move(ident_expr), ast::ExpressionList()); + body->append(std::make_unique(std::move(call_expr))); + }; + auto body = std::make_unique(); + + AddFuncCall(body.get(), "sb_foo_func"); + AddFuncCall(body.get(), "sb_bar_func"); + AddFuncCall(body.get(), "sb_baz_func"); + + body->append(std::make_unique()); + std::unique_ptr func = std::make_unique( + "ep_func", ast::VariableList(), void_type()); + func->set_body(std::move(body)); + + func->add_decoration( + std::make_unique(ast::PipelineStage::kVertex)); + mod()->AddFunction(std::move(func)); + + ASSERT_TRUE(td()->Determine()) << td()->error(); + + auto result = inspector()->GetStorageBufferResourceBindings("ep_func"); + ASSERT_FALSE(inspector()->has_error()); + ASSERT_EQ(3u, result.size()); + + EXPECT_EQ(0u, result[0].bind_group); + EXPECT_EQ(0u, result[0].binding); + EXPECT_EQ(12u, result[0].min_buffer_binding_size); + + EXPECT_EQ(0u, result[1].bind_group); + EXPECT_EQ(1u, result[1].binding); + EXPECT_EQ(12u, result[1].min_buffer_binding_size); + + EXPECT_EQ(2u, result[2].bind_group); + EXPECT_EQ(0u, result[2].binding); + EXPECT_EQ(12u, result[2].min_buffer_binding_size); +} + +TEST_F(InspectorGetStorageBufferResourceBindings, ContainingArray) { + auto foo_type = MakeStorageBufferStructType( + "foo_type", {{i32_type(), 0}, {u32_array_type(4), 4}}); + AddStorageBuffer("foo_sb", foo_type.get(), 0, 0); + + auto sb_func = MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", + {{0, i32_type()}}); + mod()->AddFunction(std::move(sb_func)); + + auto ep_func = MakeCallerBodyFunction("ep_func", "sb_func"); + ep_func->add_decoration( + std::make_unique(ast::PipelineStage::kVertex)); + mod()->AddFunction(std::move(ep_func)); + + ASSERT_TRUE(td()->Determine()) << td()->error(); + + auto result = inspector()->GetStorageBufferResourceBindings("ep_func"); + ASSERT_FALSE(inspector()->has_error()); + ASSERT_EQ(1u, result.size()); + + EXPECT_EQ(0u, result[0].bind_group); + EXPECT_EQ(0u, result[0].binding); + EXPECT_EQ(20u, result[0].min_buffer_binding_size); +} + +TEST_F(InspectorGetStorageBufferResourceBindings, ContainingRuntimeArray) { + auto foo_type = MakeStorageBufferStructType( + "foo_type", {{i32_type(), 0}, {u32_array_type(0), 4}}); + AddStorageBuffer("foo_sb", foo_type.get(), 0, 0); + + auto sb_func = MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", + {{0, i32_type()}}); + mod()->AddFunction(std::move(sb_func)); + + auto ep_func = MakeCallerBodyFunction("ep_func", "sb_func"); + ep_func->add_decoration( + std::make_unique(ast::PipelineStage::kVertex)); + mod()->AddFunction(std::move(ep_func)); + + ASSERT_TRUE(td()->Determine()) << td()->error(); + + auto result = inspector()->GetStorageBufferResourceBindings("ep_func"); + ASSERT_FALSE(inspector()->has_error()); + ASSERT_EQ(1u, result.size()); + + EXPECT_EQ(0u, result[0].bind_group); + EXPECT_EQ(0u, result[0].binding); + EXPECT_EQ(8u, result[0].min_buffer_binding_size); +} + } // namespace } // namespace inspector } // namespace tint