spirv-reader: Support multisampled textures

Only ImageFetch is supported.

Converts the signedness of the sample operand as needed.

Bug: tint:109
Bug: dawn:399
Change-Id: I1d00ff4452af123457bb1841d872afcf2c591c48
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/35540
Auto-Submit: David Neto <dneto@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: David Neto <dneto@google.com>
diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc
index be68f8e..ec024bb 100644
--- a/src/reader/spirv/function.cc
+++ b/src/reader/spirv/function.cc
@@ -3947,10 +3947,23 @@
   }
   if (arg_index < num_args &&
       (image_operands_mask & SpvImageOperandsConstOffsetMask)) {
+    // TODO(dneto): convert to signed integer if needed
     params.push_back(MakeOperand(inst, arg_index).expr);
     image_operands_mask ^= SpvImageOperandsConstOffsetMask;
     arg_index++;
   }
+  if (arg_index < num_args &&
+      (image_operands_mask & SpvImageOperandsSampleMask)) {
+    TypedExpression sample = MakeOperand(inst, arg_index);
+    if (!sample.type->Is<ast::type::I32>()) {
+      sample.expr = ast_module_.create<ast::TypeConstructorExpression>(
+          ast_module_.create<ast::type::I32>(),
+          ast::ExpressionList{sample.expr});
+    }
+    params.push_back(sample.expr);
+    image_operands_mask ^= SpvImageOperandsSampleMask;
+    arg_index++;
+  }
   if (image_operands_mask) {
     return Fail() << "unsupported image operands (" << image_operands_mask
                   << "): " << inst.PrettyPrint();
diff --git a/src/reader/spirv/parser_impl_handle_test.cc b/src/reader/spirv/parser_impl_handle_test.cc
index 693d845..7c3fc65 100644
--- a/src/reader/spirv/parser_impl_handle_test.cc
+++ b/src/reader/spirv/parser_impl_handle_test.cc
@@ -2934,6 +2934,7 @@
       }
     })"},
         // OpImageFetch with ConstOffset
+        // TODO(dneto): Seems this is not valid in WGSL.
         {"%float 2D 0 0 0 1 Unknown",
          "%99 = OpImageFetch %v4float %im %vu12 ConstOffset %offsets2d",
          R"(Variable{
@@ -2964,6 +2965,118 @@
     })"}}));
 
 INSTANTIATE_TEST_SUITE_P(
+    ImageFetch_Multisampled,
+    SpvParserTest_ImageAccessTest,
+    ::testing::ValuesIn(std::vector<ImageAccessCase>{
+        // SPIR-V requires a Sample image operand when operating on a
+        // multisampled image.
+
+        // ImageFetch non-arrayed
+        {"%float 2D 0 0 1 1 Unknown",
+         "%99 = OpImageFetch %v4float %im %vi12 Sample %i1",
+         R"(Variable{
+    Decorations{
+      SetDecoration{2}
+      BindingDecoration{1}
+    }
+    x_20
+    uniform_constant
+    __multisampled_texture_2d__f32
+  })",
+         R"(VariableDeclStatement{
+      VariableConst{
+        x_99
+        none
+        __vec_4__f32
+        {
+          Call[not set]{
+            Identifier[not set]{textureLoad}
+            (
+              Identifier[not set]{x_20}
+              Identifier[not set]{vi12}
+              Identifier[not set]{i1}
+            )
+          }
+        }
+      }
+    })"},
+        // ImageFetch arrayed
+        {"%float 2D 0 1 1 1 Unknown",
+         "%99 = OpImageFetch %v4float %im %vi123 Sample %i1",
+         R"(Variable{
+    Decorations{
+      SetDecoration{2}
+      BindingDecoration{1}
+    }
+    x_20
+    uniform_constant
+    __multisampled_texture_2d_array__f32
+  })",
+         R"(VariableDeclStatement{
+      VariableConst{
+        x_99
+        none
+        __vec_4__f32
+        {
+          Call[not set]{
+            Identifier[not set]{textureLoad}
+            (
+              Identifier[not set]{x_20}
+              MemberAccessor[not set]{
+                Identifier[not set]{vi123}
+                Identifier[not set]{xy}
+              }
+              TypeConstructor[not set]{
+                __i32
+                MemberAccessor[not set]{
+                  Identifier[not set]{vi123}
+                  Identifier[not set]{z}
+                }
+              }
+              Identifier[not set]{i1}
+            )
+          }
+        }
+      }
+    })"}}));
+
+INSTANTIATE_TEST_SUITE_P(
+    ImageFetch_Multisampled_ConvertSampleOperand,
+    SpvParserTest_ImageAccessTest,
+    ::testing::ValuesIn(std::vector<ImageAccessCase>{
+        {"%float 2D 0 0 1 1 Unknown",
+         "%99 = OpImageFetch %v4float %im %vi12 Sample %u1",
+         R"(Variable{
+    Decorations{
+      SetDecoration{2}
+      BindingDecoration{1}
+    }
+    x_20
+    uniform_constant
+    __multisampled_texture_2d__f32
+  })",
+         R"(VariableDeclStatement{
+      VariableConst{
+        x_99
+        none
+        __vec_4__f32
+        {
+          Call[not set]{
+            Identifier[not set]{textureLoad}
+            (
+              Identifier[not set]{x_20}
+              Identifier[not set]{vi12}
+              TypeConstructor[not set]{
+                __i32
+                Identifier[not set]{u1}
+              }
+            )
+          }
+        }
+      }
+    })"}}));
+
+INSTANTIATE_TEST_SUITE_P(
     ConvertResultSignedness,
     SpvParserTest_SampledImageAccessTest,
     ::testing::ValuesIn(std::vector<ImageAccessCase>{