[hlsl] Support `textureNumSamples`

This CL adds support to the HLSL IR backend for `textureNumSamples`.

Bug: 42251045
Change-Id: Idb325fbd573b6911c548181009c31f2ef2bbc8ca
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/197357
Reviewed-by: James Price <jrprice@google.com>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
diff --git a/src/tint/lang/hlsl/writer/builtin_test.cc b/src/tint/lang/hlsl/writer/builtin_test.cc
index 099b78a..12edc94 100644
--- a/src/tint/lang/hlsl/writer/builtin_test.cc
+++ b/src/tint/lang/hlsl/writer/builtin_test.cc
@@ -28,6 +28,7 @@
 #include "src/tint/lang/core/fluent_types.h"
 #include "src/tint/lang/core/ir/function.h"
 #include "src/tint/lang/core/number.h"
+#include "src/tint/lang/core/type/depth_multisampled_texture.h"
 #include "src/tint/lang/core/type/sampled_texture.h"
 #include "src/tint/lang/hlsl/writer/helper_test.h"
 
@@ -894,5 +895,32 @@
 )");
 }
 
+TEST_F(HlslWriterTest, BuiltinTextureNumSamples) {
+    auto* t = b.FunctionParam(
+        "t", ty.Get<core::type::DepthMultisampledTexture>(core::type::TextureDimension::k2d));
+
+    auto* func = b.Function("foo", ty.void_());
+    func->SetParams({t});
+
+    b.Append(func->Block(), [&] {
+        b.Let("d", b.Call(ty.u32(), core::BuiltinFn::kTextureNumSamples, t));
+        b.Return(func);
+    });
+
+    ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
+    EXPECT_EQ(output_.hlsl, R"(
+void foo(Texture2DMS<float4> t) {
+  uint3 v = (0u).xxx;
+  t.GetDimensions(v[0u], v[1u], v[2u]);
+  uint d = v.z;
+}
+
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+}
+
+)");
+}
+
 }  // namespace
 }  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/raise/builtin_polyfill.cc b/src/tint/lang/hlsl/writer/raise/builtin_polyfill.cc
index 34fbe0c..2fc9499 100644
--- a/src/tint/lang/hlsl/writer/raise/builtin_polyfill.cc
+++ b/src/tint/lang/hlsl/writer/raise/builtin_polyfill.cc
@@ -34,7 +34,9 @@
 #include "src/tint/lang/core/ir/builder.h"
 #include "src/tint/lang/core/ir/module.h"
 #include "src/tint/lang/core/ir/validator.h"
+#include "src/tint/lang/core/type/depth_multisampled_texture.h"
 #include "src/tint/lang/core/type/manager.h"
+#include "src/tint/lang/core/type/multisampled_texture.h"
 #include "src/tint/lang/core/type/texture.h"
 #include "src/tint/lang/hlsl/builtin_fn.h"
 #include "src/tint/lang/hlsl/ir/builtin_call.h"
@@ -82,6 +84,7 @@
                     case core::BuiltinFn::kSign:
                     case core::BuiltinFn::kTextureNumLayers:
                     case core::BuiltinFn::kTextureNumLevels:
+                    case core::BuiltinFn::kTextureNumSamples:
                     case core::BuiltinFn::kTrunc:
                         call_worklist.Push(call);
                         break;
@@ -124,6 +127,9 @@
                 case core::BuiltinFn::kTextureNumLevels:
                     TextureNumLevels(call);
                     break;
+                case core::BuiltinFn::kTextureNumSamples:
+                    TextureNumSamples(call);
+                    break;
                 case core::BuiltinFn::kTrunc:
                     Trunc(call);
                     break;
@@ -458,6 +464,31 @@
         });
         call->Destroy();
     }
+
+    void TextureNumSamples(core::ir::CoreBuiltinCall* call) {
+        auto* tex = call->Args()[0];
+        auto* tex_type = tex->Type()->As<core::type::Texture>();
+
+        TINT_ASSERT(tex_type->dim() == core::type::TextureDimension::k2d);
+        TINT_ASSERT((tex_type->IsAnyOf<core::type::DepthMultisampledTexture,
+                                       core::type::MultisampledTexture>()));
+
+        const core::type::Type* query_ty = ty.vec(ty.u32(), 3);
+        b.InsertBefore(call, [&] {
+            core::ir::Instruction* out = b.Var(ty.ptr(function, query_ty));
+
+            b.MemberCall<hlsl::ir::MemberBuiltinCall>(
+                ty.void_(), hlsl::BuiltinFn::kGetDimensions, tex,
+                Vector<core::ir::Value*, 3>{
+                    b.Access(ty.ptr<function, u32>(), out, 0_u)->Result(0),
+                    b.Access(ty.ptr<function, u32>(), out, 1_u)->Result(0),
+                    b.Access(ty.ptr<function, u32>(), out, 2_u)->Result(0)});
+
+            out = b.Swizzle(ty.u32(), out, {2_u});
+            call->Result(0)->ReplaceAllUsesWith(out->Result(0));
+        });
+        call->Destroy();
+    }
 };
 
 }  // namespace
diff --git a/src/tint/lang/hlsl/writer/raise/builtin_polyfill_test.cc b/src/tint/lang/hlsl/writer/raise/builtin_polyfill_test.cc
index 829c9a3..1cf2910 100644
--- a/src/tint/lang/hlsl/writer/raise/builtin_polyfill_test.cc
+++ b/src/tint/lang/hlsl/writer/raise/builtin_polyfill_test.cc
@@ -32,6 +32,7 @@
 #include "src/tint/lang/core/ir/transform/helper_test.h"
 #include "src/tint/lang/core/number.h"
 #include "src/tint/lang/core/type/builtin_structs.h"
+#include "src/tint/lang/core/type/multisampled_texture.h"
 #include "src/tint/lang/core/type/sampled_texture.h"
 #include "src/tint/lang/core/type/storage_texture.h"
 
@@ -537,5 +538,45 @@
     EXPECT_EQ(expect, str());
 }
 
+TEST_F(HlslWriter_BuiltinPolyfillTest, TextureNumSamples) {
+    auto* t = b.FunctionParam(
+        "t", ty.Get<core::type::MultisampledTexture>(core::type::TextureDimension::k2d, ty.f32()));
+    auto* func = b.Function("foo", ty.u32());
+    func->SetParams({t});
+    b.Append(func->Block(), [&] {
+        auto* result = b.Call<u32>(core::BuiltinFn::kTextureNumSamples, t);
+        b.Return(func, result);
+    });
+
+    auto* src = R"(
+%foo = func(%t:texture_multisampled_2d<f32>):u32 {
+  $B1: {
+    %3:u32 = textureNumSamples %t
+    ret %3
+  }
+}
+)";
+    EXPECT_EQ(src, str());
+
+    auto* expect = R"(
+%foo = func(%t:texture_multisampled_2d<f32>):u32 {
+  $B1: {
+    %3:ptr<function, vec3<u32>, read_write> = var
+    %4:ptr<function, u32, read_write> = access %3, 0u
+    %5:ptr<function, u32, read_write> = access %3, 1u
+    %6:ptr<function, u32, read_write> = access %3, 2u
+    %7:void = %t.GetDimensions %4, %5, %6
+    %8:u32 = swizzle %3, z
+    ret %8
+  }
+}
+)";
+
+    capabilities = core::ir::Capabilities{core::ir::Capability::kAllowVectorElementPointer};
+    Run(BuiltinPolyfill);
+
+    EXPECT_EQ(expect, str());
+}
+
 }  // namespace
 }  // namespace tint::hlsl::writer::raise