[spirv-writer] Add multisampled textureLoad test.

This CL adds a test for multisampled textureLoad and fixes up a few type
determiner issues for multisampled textures.

Bug: tint:237
Change-Id: I5c33797b197b6f092842b22aa93d2076b0779766
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/28582
Reviewed-by: Ryan Harrison <rharrison@chromium.org>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
diff --git a/src/type_determiner.cc b/src/type_determiner.cc
index 479382f..948b04c 100644
--- a/src/type_determiner.cc
+++ b/src/type_determiner.cc
@@ -43,6 +43,7 @@
 #include "src/ast/type/f32_type.h"
 #include "src/ast/type/i32_type.h"
 #include "src/ast/type/matrix_type.h"
+#include "src/ast/type/multisampled_texture_type.h"
 #include "src/ast/type/pointer_type.h"
 #include "src/ast/type/sampled_texture_type.h"
 #include "src/ast/type/storage_texture_type.h"
@@ -617,16 +618,25 @@
     ast::type::TextureType* texture =
         texture_param->result_type()->UnwrapPtrIfNeeded()->AsTexture();
 
-    if (!texture->IsStorage() && !texture->IsSampled()) {
+    if (!texture->IsStorage() &&
+        !(texture->IsSampled() || texture->IsMultisampled())) {
       set_error(expr->source(), "invalid texture for " + name);
       return false;
     }
 
+    ast::type::Type* type = nullptr;
+    if (texture->IsStorage()) {
+      type = texture->AsStorage()->type();
+    } else if (texture->IsSampled()) {
+      type = texture->AsSampled()->type();
+    } else if (texture->IsMultisampled()) {
+      type = texture->AsMultisampled()->type();
+    } else {
+      set_error(expr->source(), "unknown texture type for texture sampling");
+      return false;
+    }
     expr->func()->set_result_type(
-        ctx_.type_mgr().Get(std::make_unique<ast::type::VectorType>(
-            texture->IsStorage() ? texture->AsStorage()->type()
-                                 : texture->AsSampled()->type(),
-            4)));
+        ctx_.type_mgr().Get(std::make_unique<ast::type::VectorType>(type, 4)));
     return true;
   }
   if (name == "dot") {
diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc
index 795e16d..5bfdd9f 100644
--- a/src/writer/spirv/builder.cc
+++ b/src/writer/spirv/builder.cc
@@ -1538,12 +1538,16 @@
   // TODO: Remove the LOD param from textureLoad on storage textures when
   // https://github.com/gpuweb/gpuweb/pull/1032 gets merged.
   if (name == "textureLoad") {
-    auto spirv_params = {std::move(wgsl_params[0]),
-                         std::move(wgsl_params[1]),
-                         std::move(wgsl_params[2]),
-                         std::move(wgsl_params[3]),
-                         Operand::Int(SpvImageOperandsLodMask),
-                         std::move(wgsl_params[4])};
+    std::vector<Operand> spirv_params = {
+        std::move(wgsl_params[0]), std::move(wgsl_params[1]),
+        std::move(wgsl_params[2]), std::move(wgsl_params[3])};
+    if (texture_type->IsMultisampled()) {
+      spirv_params.push_back(Operand::Int(SpvImageOperandsSampleMask));
+    } else {
+      spirv_params.push_back(Operand::Int(SpvImageOperandsLodMask));
+    }
+    spirv_params.push_back(std::move(wgsl_params[4]));
+
     auto op = spv::Op::OpImageFetch;
     if (texture_type->IsStorage()) {
       op = spv::Op::OpImageRead;
diff --git a/src/writer/spirv/builder_intrinsic_test.cc b/src/writer/spirv/builder_intrinsic_test.cc
index 0efcdf3..1fbc36b 100644
--- a/src/writer/spirv/builder_intrinsic_test.cc
+++ b/src/writer/spirv/builder_intrinsic_test.cc
@@ -25,6 +25,7 @@
 #include "src/ast/type/f32_type.h"
 #include "src/ast/type/i32_type.h"
 #include "src/ast/type/matrix_type.h"
+#include "src/ast/type/multisampled_texture_type.h"
 #include "src/ast/type/sampled_texture_type.h"
 #include "src/ast/type/sampler_type.h"
 #include "src/ast/type/u32_type.h"
@@ -633,6 +634,63 @@
 )");
 }
 
+TEST_F(BuilderTest, Call_TextureLoad_Multisampled_2d) {
+  ast::type::F32Type f32;
+  ast::type::I32Type i32;
+  ast::type::VectorType vec2(&f32, 2);
+  ast::type::MultisampledTextureType s(ast::type::TextureDimension::k2d, &f32);
+
+  Context ctx;
+  ast::Module mod;
+  TypeDeterminer td(&ctx, &mod);
+  Builder b(&mod);
+
+  b.push_function(Function{});
+
+  ast::Variable tex("texture", ast::StorageClass::kNone, &s);
+  td.RegisterVariableForTesting(&tex);
+  ASSERT_TRUE(b.GenerateGlobalVariable(&tex)) << b.error();
+
+  ast::ExpressionList vals;
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.0)));
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 2.0)));
+
+  ast::ExpressionList call_params;
+  call_params.push_back(std::make_unique<ast::IdentifierExpression>("texture"));
+  call_params.push_back(
+      std::make_unique<ast::TypeConstructorExpression>(&vec2, std::move(vals)));
+  call_params.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::SintLiteral>(&i32, 2)));
+
+  ast::CallExpression expr(
+      std::make_unique<ast::IdentifierExpression>("textureLoad"),
+      std::move(call_params));
+
+  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+  EXPECT_EQ(b.GenerateExpression(&expr), 5u) << b.error();
+
+  EXPECT_EQ(DumpInstructions(b.types()),
+            R"(%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 2D 0 0 1 1 Unknown
+%2 = OpTypePointer Private %3
+%1 = OpVariable %2 Private
+%6 = OpTypeVector %4 4
+%8 = OpTypeVector %4 2
+%9 = OpConstant %4 1
+%10 = OpConstant %4 2
+%11 = OpConstantComposite %8 %9 %10
+%12 = OpTypeInt 32 1
+%13 = OpConstant %12 2
+)");
+
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%7 = OpLoad %3 %1
+%5 = OpImageFetch %6 %7 %11 Sample %13
+)");
+}
+
 TEST_F(BuilderTest, Call_TextureSample_1d) {
   ast::type::F32Type f32;