Implement textureNumSamples()

SPIR-V reader TODO

Bug: tint:140
Bug: tint:437
Change-Id: Id95855660680b12e3ff49b6340ef966dfa49f8e4
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/37848
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: David Neto <dneto@google.com>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
diff --git a/src/ast/intrinsic.cc b/src/ast/intrinsic.cc
index 2282650..6a16abb 100644
--- a/src/ast/intrinsic.cc
+++ b/src/ast/intrinsic.cc
@@ -215,6 +215,9 @@
     case Intrinsic::kTextureNumLevels:
       out << "textureNumLevels";
       return out;
+    case Intrinsic::kTextureNumSamples:
+      out << "textureNumSamples";
+      return out;
     case Intrinsic::kTextureSample:
       out << "textureSample";
       return out;
@@ -281,7 +284,9 @@
 
 bool IsImageQueryIntrinsic(Intrinsic i) {
   return i == ast::Intrinsic::kTextureDimensions ||
-         i == Intrinsic::kTextureNumLayers || i == Intrinsic::kTextureNumLevels;
+         i == Intrinsic::kTextureNumLayers ||
+         i == Intrinsic::kTextureNumLevels ||
+         i == Intrinsic::kTextureNumSamples;
 }
 
 }  // namespace intrinsic
diff --git a/src/ast/intrinsic.h b/src/ast/intrinsic.h
index bdf310e..90477bc 100644
--- a/src/ast/intrinsic.h
+++ b/src/ast/intrinsic.h
@@ -87,6 +87,7 @@
   kTextureLoad,
   kTextureNumLayers,
   kTextureNumLevels,
+  kTextureNumSamples,
   kTextureSample,
   kTextureSampleBias,
   kTextureSampleCompare,
diff --git a/src/ast/intrinsic_texture_helper_test.cc b/src/ast/intrinsic_texture_helper_test.cc
index fbeae35..00a2d05 100644
--- a/src/ast/intrinsic_texture_helper_test.cc
+++ b/src/ast/intrinsic_texture_helper_test.cc
@@ -712,6 +712,26 @@
           [](Builder* b) { return b->ExprList("texture"); },
       },
       {
+          ValidTextureOverload::kNumSamplesMultisampled2d,
+          "textureNumSamples(t : texture_multisampled_2d<f32>) -> i32",
+          TextureKind::kMultisampled,
+          type::SamplerKind::kSampler,
+          type::TextureDimension::k2d,
+          TextureDataType::kF32,
+          "textureNumSamples",
+          [](Builder* b) { return b->ExprList("texture"); },
+      },
+      {
+          ValidTextureOverload::kNumSamplesMultisampled2dArray,
+          "textureNumSamples(t : texture_multisampled_2d_array<f32>) -> i32",
+          TextureKind::kMultisampled,
+          type::SamplerKind::kSampler,
+          type::TextureDimension::k2dArray,
+          TextureDataType::kF32,
+          "textureNumSamples",
+          [](Builder* b) { return b->ExprList("texture"); },
+      },
+      {
           ValidTextureOverload::kSample1dF32,
           "textureSample(t      : texture_1d<f32>,\n"
           "              s      : sampler,\n"
diff --git a/src/ast/intrinsic_texture_helper_test.h b/src/ast/intrinsic_texture_helper_test.h
index e78f9e0..bd67b9f 100644
--- a/src/ast/intrinsic_texture_helper_test.h
+++ b/src/ast/intrinsic_texture_helper_test.h
@@ -86,6 +86,8 @@
   kNumLevelsDepth2dArray,
   kNumLevelsDepthCube,
   kNumLevelsDepthCubeArray,
+  kNumSamplesMultisampled2d,
+  kNumSamplesMultisampled2dArray,
   kSample1dF32,
   kSample1dArrayF32,
   kSample2dF32,
diff --git a/src/type_determiner.cc b/src/type_determiner.cc
index 84f9b6f..7019202 100644
--- a/src/type_determiner.cc
+++ b/src/type_determiner.cc
@@ -568,6 +568,7 @@
         break;
       case ast::Intrinsic::kTextureNumLayers:
       case ast::Intrinsic::kTextureNumLevels:
+      case ast::Intrinsic::kTextureNumSamples:
         param.idx.texture = param.count++;
         break;
       case ast::Intrinsic::kTextureLoad:
@@ -698,6 +699,7 @@
       }
       case ast::Intrinsic::kTextureNumLayers:
       case ast::Intrinsic::kTextureNumLevels:
+      case ast::Intrinsic::kTextureNumSamples:
         return_type = mod_->create<ast::type::I32>();
         break;
       case ast::Intrinsic::kTextureStore:
@@ -1038,6 +1040,8 @@
     ident->set_intrinsic(ast::Intrinsic::kTextureNumLayers);
   } else if (name == "textureNumLevels") {
     ident->set_intrinsic(ast::Intrinsic::kTextureNumLevels);
+  } else if (name == "textureNumSamples") {
+    ident->set_intrinsic(ast::Intrinsic::kTextureNumSamples);
   } else if (name == "textureLoad") {
     ident->set_intrinsic(ast::Intrinsic::kTextureLoad);
   } else if (name == "textureStore") {
diff --git a/src/type_determiner_test.cc b/src/type_determiner_test.cc
index 1a76ae4..b05d2ee 100644
--- a/src/type_determiner_test.cc
+++ b/src/type_determiner_test.cc
@@ -1766,6 +1766,7 @@
         IntrinsicData{"textureLoad", ast::Intrinsic::kTextureLoad},
         IntrinsicData{"textureNumLayers", ast::Intrinsic::kTextureNumLayers},
         IntrinsicData{"textureNumLevels", ast::Intrinsic::kTextureNumLevels},
+        IntrinsicData{"textureNumSamples", ast::Intrinsic::kTextureNumSamples},
         IntrinsicData{"textureSample", ast::Intrinsic::kTextureSample},
         IntrinsicData{"textureSampleBias", ast::Intrinsic::kTextureSampleBias},
         IntrinsicData{"textureSampleCompare",
@@ -2949,6 +2950,9 @@
     case ValidTextureOverload::kNumLevelsDepthCube:
     case ValidTextureOverload::kNumLevelsDepthCubeArray:
       return R"(textureNumLevels(texture))";
+    case ValidTextureOverload::kNumSamplesMultisampled2d:
+    case ValidTextureOverload::kNumSamplesMultisampled2dArray:
+      return R"(textureNumSamples(texture))";
     case ValidTextureOverload::kDimensions2dLevel:
     case ValidTextureOverload::kDimensions2dArrayLevel:
     case ValidTextureOverload::kDimensions3dLevel:
@@ -3206,6 +3210,8 @@
     EXPECT_EQ(call->result_type(), ty.i32);
   } else if (std::string(param.function) == "textureNumLevels") {
     EXPECT_EQ(call->result_type(), ty.i32);
+  } else if (std::string(param.function) == "textureNumSamples") {
+    EXPECT_EQ(call->result_type(), ty.i32);
   } else if (std::string(param.function) == "textureStore") {
     EXPECT_EQ(call->result_type(), ty.void_);
   } else {
diff --git a/src/writer/hlsl/generator_impl.cc b/src/writer/hlsl/generator_impl.cc
index 187020d..06791db 100644
--- a/src/writer/hlsl/generator_impl.cc
+++ b/src/writer/hlsl/generator_impl.cc
@@ -698,15 +698,9 @@
   switch (ident->intrinsic()) {
     case ast::Intrinsic::kTextureDimensions:
     case ast::Intrinsic::kTextureNumLayers:
-    case ast::Intrinsic::kTextureNumLevels: {
-      // Declare a variable to hold the queried texture info
-      auto dims = generate_name(kTempNamePrefix);
-
-      std::stringstream texture_name;
-      if (!EmitExpression(pre, texture_name, texture)) {
-        return false;
-      }
-
+    case ast::Intrinsic::kTextureNumLevels:
+    case ast::Intrinsic::kTextureNumSamples: {
+      // All of these intrinsics use the GetDimensions() method on the texture
       int num_dimensions = 0;
       const char* swizzle = "";
       bool add_mip_level_in = false;
@@ -783,18 +777,39 @@
               break;
           }
           break;
+        case ast::Intrinsic::kTextureNumSamples:
+          switch (texture_type->dim()) {
+            default:
+              error_ = "texture dimension does not support multisampling";
+              return false;
+            case ast::type::TextureDimension::k2d:
+              num_dimensions = 3;
+              swizzle = ".z";
+              break;
+            case ast::type::TextureDimension::k2dArray:
+              num_dimensions = 4;
+              swizzle = ".w";
+              break;
+          }
+          break;
         default:
           error_ = "unexpected intrinsic";
           return false;
       }
 
+      // Declare a variable to hold the queried texture info
+      auto dims = generate_name(kTempNamePrefix);
+
       if (num_dimensions == 1) {
         pre << "int " << dims << ";\n";
       } else {
         pre << "int" << num_dimensions << " " << dims << ";\n";
       }
 
-      pre << texture_name.str() << ".GetDimensions(";
+      if (!EmitExpression(pre, pre, texture)) {
+        return false;
+      }
+      pre << ".GetDimensions(";
       if (pidx.level != kNotUsed) {
         pre << pidx.level << ", ";
       } else if (add_mip_level_in) {
diff --git a/src/writer/hlsl/generator_impl_intrinsic_texture_test.cc b/src/writer/hlsl/generator_impl_intrinsic_texture_test.cc
index bbc5401..e0ec794 100644
--- a/src/writer/hlsl/generator_impl_intrinsic_texture_test.cc
+++ b/src/writer/hlsl/generator_impl_intrinsic_texture_test.cc
@@ -178,6 +178,20 @@
           "_tint_tmp.x, _tint_tmp.y, _tint_tmp.z, _tint_tmp.w);",
           "_tint_tmp.w",
       };
+    case ValidTextureOverload::kNumSamplesMultisampled2d:
+      return {
+          "int3 _tint_tmp;\n"
+          "texture_tint_0."
+          "GetDimensions(_tint_tmp.x, _tint_tmp.y, _tint_tmp.z);",
+          "_tint_tmp.z",
+      };
+    case ValidTextureOverload::kNumSamplesMultisampled2dArray:
+      return {
+          "int4 _tint_tmp;\n"
+          "texture_tint_0."
+          "GetDimensions(_tint_tmp.x, _tint_tmp.y, _tint_tmp.z, _tint_tmp.w);",
+          "_tint_tmp.w",
+      };
     case ValidTextureOverload::kSample1dF32:
       return R"(texture_tint_0.Sample(sampler_tint_0, 1.0f))";
     case ValidTextureOverload::kSample1dArrayF32:
diff --git a/src/writer/msl/generator_impl.cc b/src/writer/msl/generator_impl.cc
index f3a55e4..aa91eef 100644
--- a/src/writer/msl/generator_impl.cc
+++ b/src/writer/msl/generator_impl.cc
@@ -687,6 +687,14 @@
       out_ << ".get_num_mip_levels())";
       return true;
     }
+    case ast::Intrinsic::kTextureNumSamples: {
+      out_ << "int(";
+      if (!EmitExpression(params[pidx.texture])) {
+        return false;
+      }
+      out_ << ".get_num_samples())";
+      return true;
+    }
     default:
       break;
   }
diff --git a/src/writer/msl/generator_impl_intrinsic_texture_test.cc b/src/writer/msl/generator_impl_intrinsic_texture_test.cc
index ded1bfb..54c2a9e 100644
--- a/src/writer/msl/generator_impl_intrinsic_texture_test.cc
+++ b/src/writer/msl/generator_impl_intrinsic_texture_test.cc
@@ -90,6 +90,9 @@
     case ValidTextureOverload::kNumLevelsDepthCube:
     case ValidTextureOverload::kNumLevelsDepthCubeArray:
       return R"(int(texture_tint_0.get_num_mip_levels()))";
+    case ValidTextureOverload::kNumSamplesMultisampled2d:
+    case ValidTextureOverload::kNumSamplesMultisampled2dArray:
+      return R"(int(texture_tint_0.get_num_samples()))";
     case ValidTextureOverload::kSample1dF32:
       return R"(texture_tint_0.sample(sampler_tint_0, 1.0f))";
     case ValidTextureOverload::kSample1dArrayF32:
diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc
index 7f913a5..e6d11fd 100644
--- a/src/writer/spirv/builder.cc
+++ b/src/writer/spirv/builder.cc
@@ -2251,6 +2251,12 @@
       spirv_params.emplace_back(gen_param(pidx.texture));
       break;
     }
+    case ast::Intrinsic::kTextureNumSamples: {
+      op = spv::Op::OpImageQuerySamples;
+      append_result_type_and_id_to_spirv_params();
+      spirv_params.emplace_back(gen_param(pidx.texture));
+      break;
+    }
     case ast::Intrinsic::kTextureLoad: {
       op = texture_type->Is<ast::type::StorageTexture>()
                ? spv::Op::OpImageRead
diff --git a/src/writer/spirv/builder_intrinsic_texture_test.cc b/src/writer/spirv/builder_intrinsic_texture_test.cc
index bf4c87f..9c1e251 100644
--- a/src/writer/spirv/builder_intrinsic_texture_test.cc
+++ b/src/writer/spirv/builder_intrinsic_texture_test.cc
@@ -1074,6 +1074,42 @@
 OpCapability SampledCubeArray
 OpCapability ImageQuery
 )"};
+    case ValidTextureOverload::kNumSamplesMultisampled2d:
+      return {R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 2D 0 0 1 1 Unknown
+%2 = OpTypePointer UniformConstant %3
+%1 = OpVariable %2 UniformConstant
+%7 = OpTypeSampler
+%6 = OpTypePointer UniformConstant %7
+%5 = OpVariable %6 UniformConstant
+%9 = OpTypeInt 32 1
+)",
+              R"(
+%10 = OpLoad %3 %1
+%8 = OpImageQuerySamples %9 %10
+)",
+              R"(
+OpCapability ImageQuery
+)"};
+    case ValidTextureOverload::kNumSamplesMultisampled2dArray:
+      return {R"(
+%4 = OpTypeFloat 32
+%3 = OpTypeImage %4 2D 0 1 1 1 Unknown
+%2 = OpTypePointer UniformConstant %3
+%1 = OpVariable %2 UniformConstant
+%7 = OpTypeSampler
+%6 = OpTypePointer UniformConstant %7
+%5 = OpVariable %6 UniformConstant
+%9 = OpTypeInt 32 1
+)",
+              R"(
+%10 = OpLoad %3 %1
+%8 = OpImageQuerySamples %9 %10
+)",
+              R"(
+OpCapability ImageQuery
+)"};
     case ValidTextureOverload::kSample1dF32:
       return {
           R"(