Add support fo extracting information about Storage Textures

BUG=tint:489

Change-Id: I28e4b0e568aea463971e9d2f5a04f04e942e2564
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/41600
Auto-Submit: Ryan Harrison <rharrison@chromium.org>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: Ben Clayton <bclayton@google.com>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
diff --git a/src/inspector/inspector.cc b/src/inspector/inspector.cc
index e41d2b3..95f8565 100644
--- a/src/inspector/inspector.cc
+++ b/src/inspector/inspector.cc
@@ -332,6 +332,18 @@
   return GetSampledTextureResourceBindingsImpl(entry_point, true);
 }
 
+std::vector<ResourceBinding>
+Inspector::GetReadOnlyStorageTextureResourceBindings(
+    const std::string& entry_point) {
+  return GetStorageTextureResourceBindingsImpl(entry_point, true);
+}
+
+std::vector<ResourceBinding>
+Inspector::GetWriteOnlyStorageTextureResourceBindings(
+    const std::string& entry_point) {
+  return GetStorageTextureResourceBindingsImpl(entry_point, false);
+}
+
 ast::Function* Inspector::FindEntryPointByName(const std::string& name) {
   auto* func = program_->AST().Functions().Find(program_->Symbols().Get(name));
   if (!func) {
@@ -357,7 +369,7 @@
 
   auto* func_sem = program_->Sem().Get(func);
   std::vector<ResourceBinding> result;
-  for (auto& rsv : func_sem->ReferencedStoragebufferVariables()) {
+  for (auto& rsv : func_sem->ReferencedStorageBufferVariables()) {
     auto* var = rsv.first;
     auto* decl = var->Declaration();
     auto binding_info = rsv.second;
@@ -477,5 +489,42 @@
   return result;
 }
 
+std::vector<ResourceBinding> Inspector::GetStorageTextureResourceBindingsImpl(
+    const std::string& entry_point,
+    bool read_only) {
+  auto* func = FindEntryPointByName(entry_point);
+  if (!func) {
+    return {};
+  }
+
+  auto* func_sem = program_->Sem().Get(func);
+  std::vector<ResourceBinding> result;
+  for (auto& ref : func_sem->ReferencedStorageTextureVariables()) {
+    auto* var = ref.first;
+    auto* decl = var->Declaration();
+    auto binding_info = ref.second;
+
+    auto* ac_type = decl->type()->As<type::AccessControl>();
+    if (ac_type == nullptr) {
+      continue;
+    }
+
+    if (read_only != ac_type->IsReadOnly()) {
+      continue;
+    }
+
+    ResourceBinding entry;
+    entry.resource_type =
+        read_only ? ResourceBinding::ResourceType::kReadOnlyStorageTexture
+                  : ResourceBinding::ResourceType::kWriteOnlyStorageTexture;
+    entry.bind_group = binding_info.group->value();
+    entry.binding = binding_info.binding->value();
+
+    result.push_back(entry);
+  }
+
+  return result;
+}
+
 }  // namespace inspector
 }  // namespace tint
diff --git a/src/inspector/inspector.h b/src/inspector/inspector.h
index 8e09e3f..38e59a1 100644
--- a/src/inspector/inspector.h
+++ b/src/inspector/inspector.h
@@ -63,7 +63,9 @@
     kSampler,
     kComparisonSampler,
     kSampledTexture,
-    kMulitsampledTexture
+    kMulitsampledTexture,
+    kReadOnlyStorageTexture,
+    kWriteOnlyStorageTexture,
   };
 
   /// Type of resource that is bound.
@@ -146,6 +148,16 @@
   std::vector<ResourceBinding> GetMultisampledTextureResourceBindings(
       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 read-only storage textures.
+  std::vector<ResourceBinding> GetReadOnlyStorageTextureResourceBindings(
+      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 write-only storage textures.
+  std::vector<ResourceBinding> GetWriteOnlyStorageTextureResourceBindings(
+      const std::string& entry_point);
+
  private:
   const Program* program_;
   std::string error_;
@@ -156,9 +168,9 @@
   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.
+  /// @param read_only if true get only read-only bindings, if false get
+  ///                  write-only bindings.
+  /// @returns vector of all of the bindings for the requested storage buffers.
   std::vector<ResourceBinding> GetStorageBufferResourceBindingsImpl(
       const std::string& entry_point,
       bool read_only);
@@ -170,6 +182,14 @@
   std::vector<ResourceBinding> GetSampledTextureResourceBindingsImpl(
       const std::string& entry_point,
       bool multisampled_only);
+
+  /// @param entry_point name of the entry point to get information about.
+  /// @param read_only if true get only read-only bindings, otherwise get
+  ///                  write-only bindings.
+  /// @returns vector of all of the bindings for the requested storage textures.
+  std::vector<ResourceBinding> GetStorageTextureResourceBindingsImpl(
+      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 982e12e..f52e688 100644
--- a/src/inspector/inspector_test.cc
+++ b/src/inspector/inspector_test.cc
@@ -601,6 +601,85 @@
     return nullptr;
   }
 
+  /// Generates appropriate types for a StorageTexture
+  /// @param dim the texture dimension of the storage texture
+  /// @param format the image format of the storage texture
+  /// @returns the storage texture type and subtype
+  std::tuple<type::StorageTexture*, type::Type*> MakeStorageTextureTypes(
+      type::TextureDimension dim,
+      type::ImageFormat format) {
+    type::Type* subtype = type::StorageTexture::SubtypeFor(format, Types());
+    return {create<type::StorageTexture>(dim, format, subtype), subtype};
+  }
+
+  /// Generates appropriate types for a Read-Only StorageTexture
+  /// @param dim the texture dimension of the storage texture
+  /// @param format the image format of the storage texture
+  /// @param read_only should the access type be read only, otherwise write only
+  /// @returns the storage texture type, subtype & access control type
+  std::tuple<type::StorageTexture*, type::Type*, type::AccessControl*>
+  MakeStorageTextureTypes(type::TextureDimension dim,
+                          type::ImageFormat format,
+                          bool read_only) {
+    type::StorageTexture* texture_type;
+    type::Type* subtype;
+    std::tie(texture_type, subtype) = MakeStorageTextureTypes(dim, format);
+    auto* access_control =
+        create<type::AccessControl>(read_only ? ast::AccessControl::kReadOnly
+                                              : ast::AccessControl::kWriteOnly,
+                                    texture_type);
+    return {texture_type, subtype, access_control};
+  }
+
+  /// Generates appropriate types for a Write-Only StorageTexture
+  /// @param dim the texture dimension of the storage texture
+  /// @param format the image format of the storage texture
+  /// @returns the storage texture type, subtype & access control type
+  std::tuple<type::StorageTexture*, type::Type*, type::AccessControl*>
+  MakeWriteOnlyStorageTextureTypes(type::TextureDimension dim,
+                                   type::ImageFormat format) {
+    type::StorageTexture* texture_type;
+    type::Type* subtype;
+    std::tie(texture_type, subtype) = MakeStorageTextureTypes(dim, format);
+    auto* access_control = create<type::AccessControl>(
+        ast::AccessControl::kWriteOnly, texture_type);
+    return {texture_type, subtype, access_control};
+  }
+
+  /// Adds a storage texture variable to the program
+  /// @param name the name of the variable
+  /// @param type the type to use
+  /// @param group the binding/group to use for the sampled texture
+  /// @param binding the binding number to use for the sampled texture
+  void AddStorageTexture(const std::string& name,
+                         type::Type* type,
+                         uint32_t group,
+                         uint32_t binding) {
+    AddBinding(name, type, ast::StorageClass::kUniformConstant, group, binding);
+  }
+
+  /// Generates a function that references a storage texture variable.
+  /// @param func_name name of the function created
+  /// @param st_name name of the storage texture to use
+  /// @param dim_type type expected by textureDimensons to return
+  /// @param decorations the function decorations
+  /// @returns a function that references all of the values specified
+  ast::Function* MakeStorageTextureBodyFunction(
+      const std::string& func_name,
+      const std::string& st_name,
+      type::Type* dim_type,
+      ast::FunctionDecorationList decorations) {
+    ast::StatementList stmts;
+
+    stmts.emplace_back(create<ast::VariableDeclStatement>(
+        Var("dim", ast::StorageClass::kFunction, dim_type)));
+    stmts.emplace_back(create<ast::AssignmentStatement>(
+        Expr("dim"), Call("textureDimensions", st_name)));
+    stmts.emplace_back(create<ast::ReturnStatement>());
+
+    return Func(func_name, ast::VariableList(), ty.void_(), stmts, decorations);
+  }
+
   Inspector& Build() {
     if (inspector_) {
       return *inspector_;
@@ -694,6 +773,13 @@
 class InspectorGetMultisampledTextureResourceBindingsTestWithParam
     : public InspectorHelper,
       public testing::TestWithParam<GetMultisampledTextureTestParams> {};
+class InspectorGetStorageTextureResourceBindingsTest : public InspectorHelper,
+                                                       public testing::Test {};
+typedef std::tuple<bool, type::TextureDimension, type::ImageFormat>
+    GetStorageTextureTestParams;
+class InspectorGetStorageTextureResourceBindingsTestWithParam
+    : public InspectorHelper,
+      public testing::TestWithParam<GetStorageTextureTestParams> {};
 
 TEST_F(InspectorGetEntryPointTest, NoFunctions) {
   Inspector& inspector = Build();
@@ -2417,6 +2503,126 @@
             inspector::ResourceBinding::TextureDimension::k2dArray,
             inspector::ResourceBinding::SampledKind::kUInt}));
 
+TEST_F(InspectorGetStorageTextureResourceBindingsTest, Empty) {
+  MakeEmptyBodyFunction(
+      "ep", ast::FunctionDecorationList{
+                create<ast::StageDecoration>(ast::PipelineStage::kVertex),
+            });
+
+  Inspector& inspector = Build();
+
+  auto result = inspector.GetReadOnlyStorageTextureResourceBindings("ep");
+  ASSERT_FALSE(inspector.has_error()) << inspector.error();
+  EXPECT_EQ(0u, result.size());
+
+  result = inspector.GetWriteOnlyStorageTextureResourceBindings("ep");
+  ASSERT_FALSE(inspector.has_error()) << inspector.error();
+  EXPECT_EQ(0u, result.size());
+}
+
+TEST_P(InspectorGetStorageTextureResourceBindingsTestWithParam, Simple) {
+  bool read_only;
+  type::TextureDimension dim;
+  type::ImageFormat format;
+  std::tie(read_only, dim, format) = GetParam();
+
+  type::StorageTexture* st_type;
+  type::Type* st_subtype;
+  type::AccessControl* ac;
+  std::tie(st_type, st_subtype, ac) =
+      MakeStorageTextureTypes(dim, format, read_only);
+  AddStorageTexture("st_var", ac, 0, 0);
+
+  type::Type* dim_type = nullptr;
+  switch (dim) {
+    case type::TextureDimension::k1d:
+    case type::TextureDimension::k1dArray:
+      dim_type = ty.i32();
+      break;
+    case type::TextureDimension::k2d:
+    case type::TextureDimension::k2dArray:
+      dim_type = ty.vec2<i32>();
+      break;
+    case type::TextureDimension::k3d:
+      dim_type = ty.vec3<i32>();
+      break;
+    default:
+      break;
+  }
+
+  ASSERT_FALSE(dim_type == nullptr);
+
+  MakeStorageTextureBodyFunction(
+      "ep", "st_var", dim_type,
+      ast::FunctionDecorationList{
+          create<ast::StageDecoration>(ast::PipelineStage::kVertex)});
+
+  Inspector& inspector = Build();
+
+  auto result =
+      read_only ? inspector.GetReadOnlyStorageTextureResourceBindings("ep")
+                : inspector.GetWriteOnlyStorageTextureResourceBindings("ep");
+  ASSERT_FALSE(inspector.has_error()) << inspector.error();
+  ASSERT_EQ(1u, result.size());
+
+  EXPECT_EQ(read_only ? ResourceBinding::ResourceType::kReadOnlyStorageTexture
+                      : ResourceBinding::ResourceType::kWriteOnlyStorageTexture,
+            result[0].resource_type);
+  EXPECT_EQ(0u, result[0].bind_group);
+  EXPECT_EQ(0u, result[0].binding);
+
+  result = read_only
+               ? inspector.GetWriteOnlyStorageTextureResourceBindings("ep")
+               : inspector.GetReadOnlyStorageTextureResourceBindings("ep");
+  ASSERT_FALSE(inspector.has_error()) << inspector.error();
+  ASSERT_EQ(0u, result.size());
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    InspectorGetStorageTextureResourceBindingsTest,
+    InspectorGetStorageTextureResourceBindingsTestWithParam,
+    testing::Combine(testing::Bool(),
+                     testing::Values(type::TextureDimension::k1d,
+                                     type::TextureDimension::k1dArray,
+                                     type::TextureDimension::k2d,
+                                     type::TextureDimension::k2dArray,
+                                     type::TextureDimension::k3d),
+                     testing::Values(type::ImageFormat::kR8Uint,
+                                     type::ImageFormat::kR16Uint,
+                                     type::ImageFormat::kRg8Uint,
+                                     type::ImageFormat::kR32Uint,
+                                     type::ImageFormat::kRg16Uint,
+                                     type::ImageFormat::kRgba8Uint,
+                                     type::ImageFormat::kRg32Uint,
+                                     type::ImageFormat::kRgba16Uint,
+                                     type::ImageFormat::kRgba32Uint,
+                                     type::ImageFormat::kR8Sint,
+                                     type::ImageFormat::kR16Sint,
+                                     type::ImageFormat::kRg8Sint,
+                                     type::ImageFormat::kR32Sint,
+                                     type::ImageFormat::kRg16Sint,
+                                     type::ImageFormat::kRgba8Sint,
+                                     type::ImageFormat::kRg32Sint,
+                                     type::ImageFormat::kRgba16Sint,
+                                     type::ImageFormat::kRgba32Sint,
+                                     type::ImageFormat::kR8Unorm,
+                                     type::ImageFormat::kRg8Unorm,
+                                     type::ImageFormat::kRgba8Unorm,
+                                     type::ImageFormat::kRgba8UnormSrgb,
+                                     type::ImageFormat::kBgra8Unorm,
+                                     type::ImageFormat::kBgra8UnormSrgb,
+                                     type::ImageFormat::kRgb10A2Unorm,
+                                     type::ImageFormat::kR8Snorm,
+                                     type::ImageFormat::kRg8Snorm,
+                                     type::ImageFormat::kRgba8Snorm,
+                                     type::ImageFormat::kR16Float,
+                                     type::ImageFormat::kR32Float,
+                                     type::ImageFormat::kRg16Float,
+                                     type::ImageFormat::kRg11B10Float,
+                                     type::ImageFormat::kRg32Float,
+                                     type::ImageFormat::kRgba16Float,
+                                     type::ImageFormat::kRgba32Float)));
+
 }  // namespace
 }  // namespace inspector
 }  // namespace tint
diff --git a/src/semantic/function.h b/src/semantic/function.h
index 652e14d..9dc200f 100644
--- a/src/semantic/function.h
+++ b/src/semantic/function.h
@@ -95,7 +95,7 @@
   /// must be decorated with both binding and group decorations.
   /// @returns the referenced storagebuffers
   const std::vector<std::pair<const Variable*, BindingInfo>>
-  ReferencedStoragebufferVariables() const;
+  ReferencedStorageBufferVariables() const;
 
   /// Retrieves any referenced regular Sampler variables. Note, the
   /// variables must be decorated with both binding and group decorations.
@@ -121,6 +121,12 @@
   const std::vector<std::pair<const Variable*, BindingInfo>>
   ReferencedMultisampledTextureVariables() const;
 
+  /// Retrieves any referenced storage texture variables. Note, the variables
+  /// must be decorated with both binding and group decorations.
+  /// @returns the referenced storage textures
+  const std::vector<std::pair<const Variable*, BindingInfo>>
+  ReferencedStorageTextureVariables() const;
+
   /// Retrieves any locally referenced builtin variables
   /// @returns the <variable, decoration> pairs.
   const std::vector<std::pair<const Variable*, ast::BuiltinDecoration*>>
diff --git a/src/semantic/sem_function.cc b/src/semantic/sem_function.cc
index 2ebf820..9862075 100644
--- a/src/semantic/sem_function.cc
+++ b/src/semantic/sem_function.cc
@@ -24,6 +24,7 @@
 #include "src/semantic/variable.h"
 #include "src/type/multisampled_texture_type.h"
 #include "src/type/sampled_texture_type.h"
+#include "src/type/storage_texture_type.h"
 #include "src/type/texture_type.h"
 
 TINT_INSTANTIATE_CLASS_ID(tint::semantic::Function);
@@ -98,7 +99,7 @@
 }
 
 const std::vector<std::pair<const Variable*, Function::BindingInfo>>
-Function::ReferencedStoragebufferVariables() const {
+Function::ReferencedStorageBufferVariables() const {
   std::vector<std::pair<const Variable*, Function::BindingInfo>> ret;
 
   for (auto* var : ReferencedModuleVariables()) {
@@ -159,6 +160,35 @@
   return ReferencedSampledTextureVariablesImpl(true);
 }
 
+const std::vector<std::pair<const Variable*, Function::BindingInfo>>
+Function::ReferencedStorageTextureVariables() const {
+  std::vector<std::pair<const Variable*, Function::BindingInfo>> ret;
+
+  for (auto* var : ReferencedModuleVariables()) {
+    auto* unwrapped_type = var->Declaration()->type()->UnwrapIfNeeded();
+    auto* storage_texture = unwrapped_type->As<type::StorageTexture>();
+    if (storage_texture == nullptr) {
+      continue;
+    }
+
+    ast::BindingDecoration* binding = nullptr;
+    ast::GroupDecoration* group = nullptr;
+    for (auto* deco : var->Declaration()->decorations()) {
+      if (auto* b = deco->As<ast::BindingDecoration>()) {
+        binding = b;
+      } else if (auto* s = deco->As<ast::GroupDecoration>()) {
+        group = s;
+      }
+    }
+    if (binding == nullptr || group == nullptr) {
+      continue;
+    }
+
+    ret.push_back({var, BindingInfo{binding, group}});
+  }
+  return ret;
+}
+
 const std::vector<std::pair<const Variable*, ast::BuiltinDecoration*>>
 Function::LocalReferencedBuiltinVariables() const {
   std::vector<std::pair<const Variable*, ast::BuiltinDecoration*>> ret;
diff --git a/src/writer/hlsl/generator_impl.cc b/src/writer/hlsl/generator_impl.cc
index b170fbd..f1d34e0 100644
--- a/src/writer/hlsl/generator_impl.cc
+++ b/src/writer/hlsl/generator_impl.cc
@@ -1626,7 +1626,7 @@
   }
 
   bool emitted_storagebuffer = false;
-  for (auto data : func_sem->ReferencedStoragebufferVariables()) {
+  for (auto data : func_sem->ReferencedStorageBufferVariables()) {
     auto* var = data.first;
     auto* decl = var->Declaration();
     auto* binding = data.second.binding;
diff --git a/src/writer/msl/generator_impl.cc b/src/writer/msl/generator_impl.cc
index 5421a64..7fb6803 100644
--- a/src/writer/msl/generator_impl.cc
+++ b/src/writer/msl/generator_impl.cc
@@ -546,7 +546,7 @@
     out_ << program_->Symbols().NameFor(var->Declaration()->symbol());
   }
 
-  for (const auto& data : func_sem->ReferencedStoragebufferVariables()) {
+  for (const auto& data : func_sem->ReferencedStorageBufferVariables()) {
     auto* var = data.first;
     if (!first) {
       out_ << ", ";
@@ -1384,7 +1384,7 @@
     out_ << "& " << program_->Symbols().NameFor(var->Declaration()->symbol());
   }
 
-  for (const auto& data : func_sem->ReferencedStoragebufferVariables()) {
+  for (const auto& data : func_sem->ReferencedStorageBufferVariables()) {
     auto* var = data.first;
     if (!first) {
       out_ << ", ";
@@ -1551,7 +1551,7 @@
          << " [[buffer(" << binding->value() << ")]]";
   }
 
-  for (auto data : func_sem->ReferencedStoragebufferVariables()) {
+  for (auto data : func_sem->ReferencedStorageBufferVariables()) {
     if (!first) {
       out_ << ", ";
     }