From e862fde45cf55313fef2ee324193c6e7324c9751 Mon Sep 17 00:00:00 2001 From: Ryan Harrison Date: Tue, 3 Nov 2020 22:19:50 +0000 Subject: [PATCH] [inspector] Extract Read Only Storage Buffer data BUG=tint:257 Change-Id: Ie9486726b2a164971d720f4496a87a0bd118ebde Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/31760 Reviewed-by: dan sinclair Commit-Queue: Ryan Harrison --- src/inspector/inspector.cc | 70 ++++--- src/inspector/inspector.h | 8 + src/inspector/inspector_test.cc | 319 +++++++++++++++++++++++++++----- 3 files changed, 326 insertions(+), 71 deletions(-) diff --git a/src/inspector/inspector.cc b/src/inspector/inspector.cc index b6bdc4a5bb..0d1dcfb029 100644 --- a/src/inspector/inspector.cc +++ b/src/inspector/inspector.cc @@ -26,6 +26,7 @@ #include "src/ast/null_literal.h" #include "src/ast/scalar_constructor_expression.h" #include "src/ast/sint_literal.h" +#include "src/ast/type/access_control_type.h" #include "src/ast/type/struct_type.h" #include "src/ast/type/type.h" #include "src/ast/uint_literal.h" @@ -172,36 +173,13 @@ std::vector Inspector::GetUniformBufferResourceBindings( 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( - ast::type::MemoryLayout::kStorageBuffer); - - result.push_back(std::move(entry)); - } - - return result; + return GetStorageBufferResourceBindingsImpl(entry_point, false); } std::vector -Inspector::GetReadOnlyStorageBufferResourceBindings(const std::string&) { - error_ += "GetReadOnlyStorageBufferResourceBindings is not implemented!"; - return {}; +Inspector::GetReadOnlyStorageBufferResourceBindings( + const std::string& entry_point) { + return GetStorageBufferResourceBindingsImpl(entry_point, true); } ast::Function* Inspector::FindEntryPointByName(const std::string& name) { @@ -219,5 +197,43 @@ ast::Function* Inspector::FindEntryPointByName(const std::string& name) { return func; } +std::vector Inspector::GetStorageBufferResourceBindingsImpl( + const std::string& entry_point, + bool read_only) { + 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()->IsAccessControl()) { + continue; + } + + auto* ac_type = var->type()->AsAccessControl(); + if (read_only != ac_type->IsReadOnly()) { + continue; + } + + if (!ac_type->type()->IsStruct()) { + continue; + } + + entry.bind_group = binding_info.set->value(); + entry.binding = binding_info.binding->value(); + entry.min_buffer_binding_size = var->type()->MinBufferBindingSize( + ast::type::MemoryLayout::kStorageBuffer); + + result.push_back(std::move(entry)); + } + + return result; +} + } // namespace inspector } // namespace tint diff --git a/src/inspector/inspector.h b/src/inspector/inspector.h index 8ff1e2e63f..2a64b3b7eb 100644 --- a/src/inspector/inspector.h +++ b/src/inspector/inspector.h @@ -84,6 +84,14 @@ class Inspector { /// @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); + + /// @param entry_point name of the entry point to get information about. + /// @param read_only get only read only if true, otherwise get everything + /// else. + /// @returns vector of all of the bindings for the request storage buffers. + std::vector GetStorageBufferResourceBindingsImpl( + const std::string& entry_point, + bool read_only); }; } // namespace inspector diff --git a/src/inspector/inspector_test.cc b/src/inspector/inspector_test.cc index f4b69c0596..cd0e4c994c 100644 --- a/src/inspector/inspector_test.cc +++ b/src/inspector/inspector_test.cc @@ -39,6 +39,7 @@ #include "src/ast/struct_member.h" #include "src/ast/struct_member_decoration.h" #include "src/ast/struct_member_offset_decoration.h" +#include "src/ast/type/access_control_type.h" #include "src/ast/type/array_type.h" #include "src/ast/type/bool_type.h" #include "src/ast/type/f32_type.h" @@ -289,30 +290,52 @@ class InspectorHelper { return MakeStructType(name, members_info, true); } - /// Generates a struct type appropriate for using in an storage buffer + /// Generates a struct type appropriate for using in a 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( + std::tuple, + std::unique_ptr> + MakeStorageBufferStructType( const std::string& name, std::vector> members_info) { - return MakeStructType(name, members_info, false); + auto struct_type = MakeStructType(name, members_info, false); + auto access_type = std::make_unique( + ast::AccessControl::kReadWrite, struct_type.get()); + return {std::move(struct_type), std::move(access_type)}; + } + + /// Generates a struct type appropriate for using in a read only 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::tuple, + std::unique_ptr> + MakeReadOnlyStorageBufferStructType( + const std::string& name, + std::vector> members_info) { + auto struct_type = MakeStructType(name, members_info, false); + auto access_type = std::make_unique( + ast::AccessControl::kReadOnly, struct_type.get()); + return {std::move(struct_type), std::move(access_type)}; } /// 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 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) { + void AddBinding(const std::string& name, + ast::type::Type* type, + ast::StorageClass storage_class, + uint32_t set, + uint32_t binding) { auto var = std::make_unique( - std::make_unique(name, storage_class, struct_type)); + std::make_unique(name, storage_class, type)); ast::VariableDecorationList decorations; decorations.push_back( @@ -325,28 +348,26 @@ class InspectorHelper { /// Adds an uniform buffer variable to the module /// @param name the name of the variable - /// @param struct_type the type to use + /// @param type the type 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 AddUniformBuffer(const std::string& name, - ast::type::StructType* struct_type, + ast::type::Type* type, uint32_t set, uint32_t binding) { - AddStructBinding(name, struct_type, ast::StorageClass::kUniform, set, - binding); + AddBinding(name, type, ast::StorageClass::kUniform, set, binding); } /// Adds an storage buffer variable to the module /// @param name the name of the variable - /// @param struct_type the type to use + /// @param 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, + ast::type::Type* type, uint32_t set, uint32_t binding) { - AddStructBinding(name, struct_type, ast::StorageClass::kStorageBuffer, set, - binding); + AddBinding(name, type, ast::StorageClass::kStorageBuffer, set, binding); } /// Generates a function that references a specific struct variable @@ -427,8 +448,10 @@ class InspectorTest : public InspectorHelper, public testing::Test {}; class InspectorGetEntryPointTest : public InspectorTest {}; class InspectorGetConstantIDsTest : public InspectorTest {}; -class InspectorGetUniformBufferResourceBindings : public InspectorTest {}; -class InspectorGetStorageBufferResourceBindings : public InspectorTest {}; +class InspectorGetUniformBufferResourceBindingsTest : public InspectorTest {}; +class InspectorGetStorageBufferResourceBindingsTest : public InspectorTest {}; +class InspectorGetReadOnlyStorageBufferResourceBindingsTest + : public InspectorTest {}; TEST_F(InspectorGetEntryPointTest, NoFunctions) { auto result = inspector()->GetEntryPoints(); @@ -839,14 +862,14 @@ TEST_F(InspectorGetConstantIDsTest, Float) { EXPECT_FLOAT_EQ(15.0, result[4000].AsFloat()); } -TEST_F(InspectorGetUniformBufferResourceBindings, MissingEntryPoint) { +TEST_F(InspectorGetUniformBufferResourceBindingsTest, MissingEntryPoint) { auto result = inspector()->GetUniformBufferResourceBindings("ep_func"); ASSERT_TRUE(inspector()->has_error()); std::string error = inspector()->error(); EXPECT_TRUE(error.find("not found") != std::string::npos); } -TEST_F(InspectorGetUniformBufferResourceBindings, NonEntryPointFunc) { +TEST_F(InspectorGetUniformBufferResourceBindingsTest, NonEntryPointFunc) { auto foo_type = MakeUniformBufferStructType("foo_type", {{i32_type(), 0}}); AddUniformBuffer("foo_ub", foo_type.get(), 0, 0); @@ -866,7 +889,7 @@ TEST_F(InspectorGetUniformBufferResourceBindings, NonEntryPointFunc) { EXPECT_TRUE(error.find("not an entry point") != std::string::npos); } -TEST_F(InspectorGetUniformBufferResourceBindings, MissingBlockDeco) { +TEST_F(InspectorGetUniformBufferResourceBindingsTest, MissingBlockDeco) { ast::StructMemberList members; ast::StructMemberDecorationList deco; deco.push_back( @@ -901,7 +924,7 @@ TEST_F(InspectorGetUniformBufferResourceBindings, MissingBlockDeco) { EXPECT_EQ(0u, result.size()); } -TEST_F(InspectorGetUniformBufferResourceBindings, Simple) { +TEST_F(InspectorGetUniformBufferResourceBindingsTest, Simple) { auto foo_type = MakeUniformBufferStructType("foo_type", {{i32_type(), 0}}); AddUniformBuffer("foo_ub", foo_type.get(), 0, 0); @@ -925,7 +948,7 @@ TEST_F(InspectorGetUniformBufferResourceBindings, Simple) { EXPECT_EQ(16u, result[0].min_buffer_binding_size); } -TEST_F(InspectorGetUniformBufferResourceBindings, MultipleMembers) { +TEST_F(InspectorGetUniformBufferResourceBindingsTest, MultipleMembers) { auto foo_type = MakeUniformBufferStructType( "foo_type", {{i32_type(), 0}, {u32_type(), 4}, {f32_type(), 8}}); AddUniformBuffer("foo_ub", foo_type.get(), 0, 0); @@ -950,7 +973,7 @@ TEST_F(InspectorGetUniformBufferResourceBindings, MultipleMembers) { EXPECT_EQ(16u, result[0].min_buffer_binding_size); } -TEST_F(InspectorGetUniformBufferResourceBindings, MultipleUniformBuffers) { +TEST_F(InspectorGetUniformBufferResourceBindingsTest, MultipleUniformBuffers) { auto ub_type = MakeUniformBufferStructType( "ub_type", {{i32_type(), 0}, {u32_type(), 4}, {f32_type(), 8}}); AddUniformBuffer("ub_foo", ub_type.get(), 0, 0); @@ -1008,7 +1031,7 @@ TEST_F(InspectorGetUniformBufferResourceBindings, MultipleUniformBuffers) { EXPECT_EQ(16u, result[2].min_buffer_binding_size); } -TEST_F(InspectorGetUniformBufferResourceBindings, ContainingArray) { +TEST_F(InspectorGetUniformBufferResourceBindingsTest, ContainingArray) { auto foo_type = MakeUniformBufferStructType( "foo_type", {{i32_type(), 0}, {u32_array_type(4), 4}}); AddUniformBuffer("foo_ub", foo_type.get(), 0, 0); @@ -1033,9 +1056,12 @@ TEST_F(InspectorGetUniformBufferResourceBindings, ContainingArray) { EXPECT_EQ(32u, 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); +TEST_F(InspectorGetStorageBufferResourceBindingsTest, Simple) { + std::unique_ptr foo_struct_type; + std::unique_ptr foo_control_type; + std::tie(foo_struct_type, foo_control_type) = + MakeStorageBufferStructType("foo_type", {{i32_type(), 0}}); + AddStorageBuffer("foo_sb", foo_control_type.get(), 0, 0); auto sb_func = MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, i32_type()}}); @@ -1057,10 +1083,12 @@ TEST_F(InspectorGetStorageBufferResourceBindings, Simple) { EXPECT_EQ(4u, result[0].min_buffer_binding_size); } -TEST_F(InspectorGetStorageBufferResourceBindings, MultipleMembers) { - auto foo_type = MakeStorageBufferStructType( +TEST_F(InspectorGetStorageBufferResourceBindingsTest, MultipleMembers) { + std::unique_ptr foo_struct_type; + std::unique_ptr foo_control_type; + std::tie(foo_struct_type, foo_control_type) = MakeStorageBufferStructType( "foo_type", {{i32_type(), 0}, {u32_type(), 4}, {f32_type(), 8}}); - AddStorageBuffer("foo_sb", foo_type.get(), 0, 0); + AddStorageBuffer("foo_sb", foo_control_type.get(), 0, 0); auto sb_func = MakeStructVariableReferenceBodyFunction( "sb_func", "foo_sb", {{0, i32_type()}, {1, u32_type()}, {2, f32_type()}}); @@ -1082,12 +1110,14 @@ TEST_F(InspectorGetStorageBufferResourceBindings, MultipleMembers) { EXPECT_EQ(12u, result[0].min_buffer_binding_size); } -TEST_F(InspectorGetStorageBufferResourceBindings, MultipleStorageBuffers) { - auto sb_type = MakeStorageBufferStructType( +TEST_F(InspectorGetStorageBufferResourceBindingsTest, MultipleStorageBuffers) { + std::unique_ptr sb_struct_type; + std::unique_ptr sb_control_type; + std::tie(sb_struct_type, sb_control_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); + AddStorageBuffer("sb_foo", sb_control_type.get(), 0, 0); + AddStorageBuffer("sb_bar", sb_control_type.get(), 0, 1); + AddStorageBuffer("sb_baz", sb_control_type.get(), 2, 0); auto AddReferenceFunc = [this](const std::string& func_name, const std::string& var_name) { @@ -1140,10 +1170,12 @@ TEST_F(InspectorGetStorageBufferResourceBindings, MultipleStorageBuffers) { EXPECT_EQ(12u, result[2].min_buffer_binding_size); } -TEST_F(InspectorGetStorageBufferResourceBindings, ContainingArray) { - auto foo_type = MakeStorageBufferStructType( +TEST_F(InspectorGetStorageBufferResourceBindingsTest, ContainingArray) { + std::unique_ptr foo_struct_type; + std::unique_ptr foo_control_type; + std::tie(foo_struct_type, foo_control_type) = MakeStorageBufferStructType( "foo_type", {{i32_type(), 0}, {u32_array_type(4), 4}}); - AddStorageBuffer("foo_sb", foo_type.get(), 0, 0); + AddStorageBuffer("foo_sb", foo_control_type.get(), 0, 0); auto sb_func = MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, i32_type()}}); @@ -1165,10 +1197,12 @@ TEST_F(InspectorGetStorageBufferResourceBindings, ContainingArray) { EXPECT_EQ(20u, result[0].min_buffer_binding_size); } -TEST_F(InspectorGetStorageBufferResourceBindings, ContainingRuntimeArray) { - auto foo_type = MakeStorageBufferStructType( +TEST_F(InspectorGetStorageBufferResourceBindingsTest, ContainingRuntimeArray) { + std::unique_ptr foo_struct_type; + std::unique_ptr foo_control_type; + std::tie(foo_struct_type, foo_control_type) = MakeStorageBufferStructType( "foo_type", {{i32_type(), 0}, {u32_array_type(0), 4}}); - AddStorageBuffer("foo_sb", foo_type.get(), 0, 0); + AddStorageBuffer("foo_sb", foo_control_type.get(), 0, 0); auto sb_func = MakeStructVariableReferenceBodyFunction("sb_func", "foo_sb", {{0, i32_type()}}); @@ -1190,6 +1224,203 @@ TEST_F(InspectorGetStorageBufferResourceBindings, ContainingRuntimeArray) { EXPECT_EQ(8u, result[0].min_buffer_binding_size); } +TEST_F(InspectorGetStorageBufferResourceBindingsTest, SkipReadOnly) { + std::unique_ptr foo_struct_type; + std::unique_ptr foo_control_type; + std::tie(foo_struct_type, foo_control_type) = + MakeReadOnlyStorageBufferStructType("foo_type", {{i32_type(), 0}}); + AddStorageBuffer("foo_sb", foo_control_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(0u, result.size()); +} + +TEST_F(InspectorGetReadOnlyStorageBufferResourceBindingsTest, Simple) { + std::unique_ptr foo_struct_type; + std::unique_ptr foo_control_type; + std::tie(foo_struct_type, foo_control_type) = + MakeReadOnlyStorageBufferStructType("foo_type", {{i32_type(), 0}}); + AddStorageBuffer("foo_sb", foo_control_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()->GetReadOnlyStorageBufferResourceBindings("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(InspectorGetReadOnlyStorageBufferResourceBindingsTest, + MultipleStorageBuffers) { + std::unique_ptr sb_struct_type; + std::unique_ptr sb_control_type; + std::tie(sb_struct_type, sb_control_type) = + MakeReadOnlyStorageBufferStructType( + "sb_type", {{i32_type(), 0}, {u32_type(), 4}, {f32_type(), 8}}); + AddStorageBuffer("sb_foo", sb_control_type.get(), 0, 0); + AddStorageBuffer("sb_bar", sb_control_type.get(), 0, 1); + AddStorageBuffer("sb_baz", sb_control_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()->GetReadOnlyStorageBufferResourceBindings("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(InspectorGetReadOnlyStorageBufferResourceBindingsTest, ContainingArray) { + std::unique_ptr foo_struct_type; + std::unique_ptr foo_control_type; + std::tie(foo_struct_type, foo_control_type) = + MakeReadOnlyStorageBufferStructType( + "foo_type", {{i32_type(), 0}, {u32_array_type(4), 4}}); + AddStorageBuffer("foo_sb", foo_control_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()->GetReadOnlyStorageBufferResourceBindings("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(InspectorGetReadOnlyStorageBufferResourceBindingsTest, + ContainingRuntimeArray) { + std::unique_ptr foo_struct_type; + std::unique_ptr foo_control_type; + std::tie(foo_struct_type, foo_control_type) = + MakeReadOnlyStorageBufferStructType( + "foo_type", {{i32_type(), 0}, {u32_array_type(0), 4}}); + AddStorageBuffer("foo_sb", foo_control_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()->GetReadOnlyStorageBufferResourceBindings("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); +} + +TEST_F(InspectorGetReadOnlyStorageBufferResourceBindingsTest, SkipNonReadOnly) { + std::unique_ptr foo_struct_type; + std::unique_ptr foo_control_type; + std::tie(foo_struct_type, foo_control_type) = + MakeStorageBufferStructType("foo_type", {{i32_type(), 0}}); + AddStorageBuffer("foo_sb", foo_control_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()->GetReadOnlyStorageBufferResourceBindings("ep_func"); + ASSERT_FALSE(inspector()->has_error()); + ASSERT_EQ(0u, result.size()); +} + } // namespace } // namespace inspector } // namespace tint