writer/spirv: Emit NonRead/Writable decoration of storage texture vars

Fixes: tint:366
Change-Id: I03e1312841d5b86d1192382bf4cdf0ad1bcfc43d
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/33921
Auto-Submit: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: David Neto <dneto@google.com>
Reviewed-by: David Neto <dneto@google.com>
diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc
index 0d18d84..7b74ecb 100644
--- a/src/writer/spirv/builder.cc
+++ b/src/writer/spirv/builder.cc
@@ -710,7 +710,25 @@
                      Operand::Int(ConvertStorageClass(sc))};
   if (var->has_constructor()) {
     ops.push_back(Operand::Int(init_id));
-  } else if (!type->IsTexture() && !type->IsSampler()) {
+  } else if (type->IsTexture()) {
+    // Decorate storage texture variables with NonRead/Writeable if needed.
+    if (type->AsTexture()->IsStorage()) {
+      switch (type->AsTexture()->AsStorage()->access()) {
+        case ast::AccessControl::kWriteOnly:
+          push_annot(
+              spv::Op::OpDecorate,
+              {Operand::Int(var_id), Operand::Int(SpvDecorationNonReadable)});
+          break;
+        case ast::AccessControl::kReadOnly:
+          push_annot(
+              spv::Op::OpDecorate,
+              {Operand::Int(var_id), Operand::Int(SpvDecorationNonWritable)});
+          break;
+        case ast::AccessControl::kReadWrite:
+          break;
+      }
+    }
+  } else if (!type->IsSampler()) {
     // Certain cases require us to generate a constructor value.
     //
     // 1- ConstantId's must be attached to the OpConstant, if we have a
diff --git a/src/writer/spirv/builder_global_variable_test.cc b/src/writer/spirv/builder_global_variable_test.cc
index 97ed8ff..9cfbd0f 100644
--- a/src/writer/spirv/builder_global_variable_test.cc
+++ b/src/writer/spirv/builder_global_variable_test.cc
@@ -620,6 +620,46 @@
 )");
 }
 
+TEST_F(BuilderTest, GlobalVar_TextureStorageReadOnly) {
+  // var<uniform_constant> a : texture_storage_ro_2d<r32uint>;
+  ast::type::StorageTextureType type(ast::type::TextureDimension::k2d,
+                                     ast::AccessControl::kReadOnly,
+                                     ast::type::ImageFormat::kR32Uint);
+  ASSERT_TRUE(td.DetermineStorageTextureSubtype(&type)) << td.error();
+
+  ast::Variable var_a("a", ast::StorageClass::kUniformConstant, &type);
+
+  EXPECT_TRUE(b.GenerateGlobalVariable(&var_a)) << b.error();
+
+  EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %1 NonWritable
+)");
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 0
+%3 = OpTypeImage %4 2D 0 0 0 2 R32ui
+%2 = OpTypePointer UniformConstant %3
+%1 = OpVariable %2 UniformConstant
+)");
+}
+
+TEST_F(BuilderTest, GlobalVar_TextureStorageWriteOnly) {
+  // var<uniform_constant> a : texture_storage_wo_2d<r32uint>;
+  ast::type::StorageTextureType type(ast::type::TextureDimension::k2d,
+                                     ast::AccessControl::kWriteOnly,
+                                     ast::type::ImageFormat::kR32Uint);
+  ASSERT_TRUE(td.DetermineStorageTextureSubtype(&type)) << td.error();
+
+  ast::Variable var_a("a", ast::StorageClass::kUniformConstant, &type);
+
+  EXPECT_TRUE(b.GenerateGlobalVariable(&var_a)) << b.error();
+
+  EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %1 NonReadable
+)");
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeVoid
+%3 = OpTypeImage %4 2D 0 0 0 2 R32ui
+%2 = OpTypePointer UniformConstant %3
+%1 = OpVariable %2 UniformConstant
+)");
+}
+
 }  // namespace
 }  // namespace spirv
 }  // namespace writer