[hlsl] Emit `textureGather` in HLSL IR
This CL adds support for the `textureGather` intrinsic to the
HLSL IR backend.
Bug: 42251045
Change-Id: I7467bb03f7e5ae9d66133e1a7d0476e993ae507d
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/200247
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: James Price <jrprice@google.com>
diff --git a/src/tint/lang/hlsl/writer/builtin_test.cc b/src/tint/lang/hlsl/writer/builtin_test.cc
index 5f4283b..4e6ebc6 100644
--- a/src/tint/lang/hlsl/writer/builtin_test.cc
+++ b/src/tint/lang/hlsl/writer/builtin_test.cc
@@ -1545,6 +1545,316 @@
)");
}
+TEST_F(HlslWriterTest, BuiltinTextureGather_Alpha) {
+ core::ir::Var* tex = nullptr;
+ core::ir::Var* sampler = nullptr;
+ b.Append(b.ir.root_block, [&] {
+ tex = b.Var(ty.ptr(handle, ty.Get<core::type::SampledTexture>(
+ core::type::TextureDimension::k2d, ty.i32())));
+ tex->SetBindingPoint(0, 0);
+
+ sampler =
+ b.Var(ty.ptr(handle, ty.Get<core::type::Sampler>(core::type::SamplerKind::kSampler)));
+ sampler->SetBindingPoint(0, 1);
+ });
+
+ auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+ b.Append(func->Block(), [&] {
+ auto* coords = b.Construct(ty.vec2<f32>(), b.Value(1_f), b.Value(2_f));
+
+ auto* t = b.Load(tex);
+ auto* s = b.Load(sampler);
+ b.Let("x", b.Call<vec4<i32>>(core::BuiltinFn::kTextureGather, 3_u, t, s, coords));
+ b.Return(func);
+ });
+
+ Options opts;
+ opts.disable_robustness = true;
+ ASSERT_TRUE(Generate(opts)) << err_ << output_.hlsl;
+ EXPECT_EQ(output_.hlsl, R"(
+Texture2D<int4> v : register(t0);
+SamplerState v_1 : register(s1);
+void foo() {
+ float2 v_2 = float2(1.0f, 2.0f);
+ int4 x = v.GatherAlpha(v_1, v_2);
+}
+
+)");
+}
+TEST_F(HlslWriterTest, BuiltinTextureGather_RedOffset) {
+ core::ir::Var* tex = nullptr;
+ core::ir::Var* sampler = nullptr;
+ b.Append(b.ir.root_block, [&] {
+ tex = b.Var(ty.ptr(handle, ty.Get<core::type::SampledTexture>(
+ core::type::TextureDimension::k2d, ty.i32())));
+ tex->SetBindingPoint(0, 0);
+
+ sampler =
+ b.Var(ty.ptr(handle, ty.Get<core::type::Sampler>(core::type::SamplerKind::kSampler)));
+ sampler->SetBindingPoint(0, 1);
+ });
+
+ auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+ b.Append(func->Block(), [&] {
+ auto* coords = b.Construct(ty.vec2<f32>(), b.Value(1_f), b.Value(2_f));
+ auto* offset = b.Composite<vec2<i32>>(1_i, 3_i);
+
+ auto* t = b.Load(tex);
+ auto* s = b.Load(sampler);
+ b.Let("x", b.Call<vec4<i32>>(core::BuiltinFn::kTextureGather, 0_u, t, s, coords, offset));
+ b.Return(func);
+ });
+
+ Options opts;
+ opts.disable_robustness = true;
+ ASSERT_TRUE(Generate(opts)) << err_ << output_.hlsl;
+ EXPECT_EQ(output_.hlsl, R"(
+Texture2D<int4> v : register(t0);
+SamplerState v_1 : register(s1);
+void foo() {
+ float2 v_2 = float2(1.0f, 2.0f);
+ int4 x = v.GatherRed(v_1, v_2, int2(1, 3));
+}
+
+)");
+}
+TEST_F(HlslWriterTest, BuiltinTextureGather_GreenArray) {
+ core::ir::Var* tex = nullptr;
+ core::ir::Var* sampler = nullptr;
+ b.Append(b.ir.root_block, [&] {
+ tex = b.Var(ty.ptr(handle, ty.Get<core::type::SampledTexture>(
+ core::type::TextureDimension::k2dArray, ty.i32())));
+ tex->SetBindingPoint(0, 0);
+
+ sampler =
+ b.Var(ty.ptr(handle, ty.Get<core::type::Sampler>(core::type::SamplerKind::kSampler)));
+ sampler->SetBindingPoint(0, 1);
+ });
+
+ auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+ b.Append(func->Block(), [&] {
+ auto* coords = b.Construct(ty.vec2<f32>(), b.Value(1_f), b.Value(2_f));
+ auto* array_idx = b.Value(1_u);
+
+ auto* t = b.Load(tex);
+ auto* s = b.Load(sampler);
+ b.Let("x",
+ b.Call<vec4<i32>>(core::BuiltinFn::kTextureGather, 1_u, t, s, coords, array_idx));
+ b.Return(func);
+ });
+
+ Options opts;
+ opts.disable_robustness = true;
+ ASSERT_TRUE(Generate(opts)) << err_ << output_.hlsl;
+ EXPECT_EQ(output_.hlsl, R"(
+Texture2DArray<int4> v : register(t0);
+SamplerState v_1 : register(s1);
+void foo() {
+ float2 v_2 = float2(1.0f, 2.0f);
+ Texture2DArray<int4> v_3 = v;
+ SamplerState v_4 = v_1;
+ int4 x = v_3.GatherGreen(v_4, float3(v_2, float(1u)));
+}
+
+)");
+}
+
+TEST_F(HlslWriterTest, BuiltinTextureGather_BlueArrayOffset) {
+ core::ir::Var* tex = nullptr;
+ core::ir::Var* sampler = nullptr;
+ b.Append(b.ir.root_block, [&] {
+ tex = b.Var(ty.ptr(handle, ty.Get<core::type::SampledTexture>(
+ core::type::TextureDimension::k2dArray, ty.i32())));
+ tex->SetBindingPoint(0, 0);
+
+ sampler =
+ b.Var(ty.ptr(handle, ty.Get<core::type::Sampler>(core::type::SamplerKind::kSampler)));
+ sampler->SetBindingPoint(0, 1);
+ });
+
+ auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+ b.Append(func->Block(), [&] {
+ auto* coords = b.Construct(ty.vec2<f32>(), b.Value(1_f), b.Value(2_f));
+ auto* array_idx = b.Value(1_i);
+ auto* offset = b.Composite<vec2<i32>>(1_i, 2_i);
+
+ auto* t = b.Load(tex);
+ auto* s = b.Load(sampler);
+ b.Let("x", b.Call<vec4<i32>>(core::BuiltinFn::kTextureGather, 2_u, t, s, coords, array_idx,
+ offset));
+ b.Return(func);
+ });
+
+ Options opts;
+ opts.disable_robustness = true;
+ ASSERT_TRUE(Generate(opts)) << err_ << output_.hlsl;
+ EXPECT_EQ(output_.hlsl, R"(
+Texture2DArray<int4> v : register(t0);
+SamplerState v_1 : register(s1);
+void foo() {
+ float2 v_2 = float2(1.0f, 2.0f);
+ Texture2DArray<int4> v_3 = v;
+ SamplerState v_4 = v_1;
+ int4 x = v_3.GatherBlue(v_4, float3(v_2, float(1)), int2(1, 2));
+}
+
+)");
+}
+
+TEST_F(HlslWriterTest, BuiltinTextureGather_Depth) {
+ core::ir::Var* tex = nullptr;
+ core::ir::Var* sampler = nullptr;
+ b.Append(b.ir.root_block, [&] {
+ tex = b.Var(
+ ty.ptr(handle, ty.Get<core::type::DepthTexture>(core::type::TextureDimension::k2d)));
+ tex->SetBindingPoint(0, 0);
+
+ sampler =
+ b.Var(ty.ptr(handle, ty.Get<core::type::Sampler>(core::type::SamplerKind::kSampler)));
+ sampler->SetBindingPoint(0, 1);
+ });
+
+ auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+ b.Append(func->Block(), [&] {
+ auto* coords = b.Construct(ty.vec2<f32>(), b.Value(1_f), b.Value(2_f));
+
+ auto* t = b.Load(tex);
+ auto* s = b.Load(sampler);
+ b.Let("x", b.Call<vec4<f32>>(core::BuiltinFn::kTextureGather, t, s, coords));
+ b.Return(func);
+ });
+
+ Options opts;
+ opts.disable_robustness = true;
+ ASSERT_TRUE(Generate(opts)) << err_ << output_.hlsl;
+ EXPECT_EQ(output_.hlsl, R"(
+Texture2D v : register(t0);
+SamplerState v_1 : register(s1);
+void foo() {
+ float2 v_2 = float2(1.0f, 2.0f);
+ float4 x = v.Gather(v_1, v_2);
+}
+
+)");
+}
+TEST_F(HlslWriterTest, BuiltinTextureGather_DepthOffset) {
+ core::ir::Var* tex = nullptr;
+ core::ir::Var* sampler = nullptr;
+ b.Append(b.ir.root_block, [&] {
+ tex = b.Var(
+ ty.ptr(handle, ty.Get<core::type::DepthTexture>(core::type::TextureDimension::k2d)));
+ tex->SetBindingPoint(0, 0);
+
+ sampler =
+ b.Var(ty.ptr(handle, ty.Get<core::type::Sampler>(core::type::SamplerKind::kSampler)));
+ sampler->SetBindingPoint(0, 1);
+ });
+
+ auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+ b.Append(func->Block(), [&] {
+ auto* coords = b.Construct(ty.vec2<f32>(), b.Value(1_f), b.Value(2_f));
+ auto* offset = b.Composite<vec2<i32>>(3_i, 4_i);
+
+ auto* t = b.Load(tex);
+ auto* s = b.Load(sampler);
+ b.Let("x", b.Call<vec4<f32>>(core::BuiltinFn::kTextureGather, t, s, coords, offset));
+ b.Return(func);
+ });
+
+ Options opts;
+ opts.disable_robustness = true;
+ ASSERT_TRUE(Generate(opts)) << err_ << output_.hlsl;
+ EXPECT_EQ(output_.hlsl, R"(
+Texture2D v : register(t0);
+SamplerState v_1 : register(s1);
+void foo() {
+ float2 v_2 = float2(1.0f, 2.0f);
+ float4 x = v.Gather(v_1, v_2, int2(3, 4));
+}
+
+)");
+}
+TEST_F(HlslWriterTest, BuiltinTextureGather_DepthArray) {
+ core::ir::Var* tex = nullptr;
+ core::ir::Var* sampler = nullptr;
+ b.Append(b.ir.root_block, [&] {
+ tex = b.Var(ty.ptr(
+ handle, ty.Get<core::type::DepthTexture>(core::type::TextureDimension::k2dArray)));
+ tex->SetBindingPoint(0, 0);
+
+ sampler =
+ b.Var(ty.ptr(handle, ty.Get<core::type::Sampler>(core::type::SamplerKind::kSampler)));
+ sampler->SetBindingPoint(0, 1);
+ });
+
+ auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+ b.Append(func->Block(), [&] {
+ auto* coords = b.Construct(ty.vec2<f32>(), b.Value(1_f), b.Value(2_f));
+ auto* array_idx = b.Value(4_i);
+
+ auto* t = b.Load(tex);
+ auto* s = b.Load(sampler);
+ b.Let("x", b.Call<vec4<f32>>(core::BuiltinFn::kTextureGather, t, s, coords, array_idx));
+ b.Return(func);
+ });
+
+ Options opts;
+ opts.disable_robustness = true;
+ ASSERT_TRUE(Generate(opts)) << err_ << output_.hlsl;
+ EXPECT_EQ(output_.hlsl, R"(
+Texture2DArray v : register(t0);
+SamplerState v_1 : register(s1);
+void foo() {
+ float2 v_2 = float2(1.0f, 2.0f);
+ Texture2DArray v_3 = v;
+ SamplerState v_4 = v_1;
+ float4 x = v_3.Gather(v_4, float3(v_2, float(4)));
+}
+
+)");
+}
+TEST_F(HlslWriterTest, BuiltinTextureGather_DepthArrayOffset) {
+ core::ir::Var* tex = nullptr;
+ core::ir::Var* sampler = nullptr;
+ b.Append(b.ir.root_block, [&] {
+ tex = b.Var(ty.ptr(
+ handle, ty.Get<core::type::DepthTexture>(core::type::TextureDimension::k2dArray)));
+ tex->SetBindingPoint(0, 0);
+
+ sampler =
+ b.Var(ty.ptr(handle, ty.Get<core::type::Sampler>(core::type::SamplerKind::kSampler)));
+ sampler->SetBindingPoint(0, 1);
+ });
+
+ auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+ b.Append(func->Block(), [&] {
+ auto* coords = b.Construct(ty.vec2<f32>(), b.Value(1_f), b.Value(2_f));
+ auto* array_idx = b.Value(4_u);
+ auto* offset = b.Composite<vec2<i32>>(4_i, 5_i);
+
+ auto* t = b.Load(tex);
+ auto* s = b.Load(sampler);
+ b.Let("x",
+ b.Call<vec4<f32>>(core::BuiltinFn::kTextureGather, t, s, coords, array_idx, offset));
+ b.Return(func);
+ });
+
+ Options opts;
+ opts.disable_robustness = true;
+ ASSERT_TRUE(Generate(opts)) << err_ << output_.hlsl;
+ EXPECT_EQ(output_.hlsl, R"(
+Texture2DArray v : register(t0);
+SamplerState v_1 : register(s1);
+void foo() {
+ float2 v_2 = float2(1.0f, 2.0f);
+ Texture2DArray v_3 = v;
+ SamplerState v_4 = v_1;
+ float4 x = v_3.Gather(v_4, float3(v_2, float(4u)), int2(4, 5));
+}
+
+)");
+}
+
TEST_F(HlslWriterTest, BuiltinQuantizeToF16) {
auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
b.Append(func->Block(), [&] {
diff --git a/src/tint/lang/hlsl/writer/raise/builtin_polyfill.cc b/src/tint/lang/hlsl/writer/raise/builtin_polyfill.cc
index b29d173..41947b1 100644
--- a/src/tint/lang/hlsl/writer/raise/builtin_polyfill.cc
+++ b/src/tint/lang/hlsl/writer/raise/builtin_polyfill.cc
@@ -115,6 +115,7 @@
case core::BuiltinFn::kSelect:
case core::BuiltinFn::kSign:
case core::BuiltinFn::kTextureDimensions:
+ case core::BuiltinFn::kTextureGather:
case core::BuiltinFn::kTextureGatherCompare:
case core::BuiltinFn::kTextureLoad:
case core::BuiltinFn::kTextureNumLayers:
@@ -242,6 +243,9 @@
case core::BuiltinFn::kTextureDimensions:
TextureDimensions(call);
break;
+ case core::BuiltinFn::kTextureGather:
+ TextureGather(call);
+ break;
case core::BuiltinFn::kTextureGatherCompare:
TextureGatherCompare(call);
break;
@@ -1003,6 +1007,81 @@
call->Destroy();
}
+ void TextureGather(core::ir::CoreBuiltinCall* call) {
+ auto args = call->Args();
+ b.InsertBefore(call, [&] {
+ core::ir::Value* tex = nullptr;
+ hlsl::BuiltinFn fn = hlsl::BuiltinFn::kGather;
+
+ core::ir::Value* coords = nullptr;
+
+ Vector<core::ir::Value*, 4> params;
+
+ uint32_t idx = 0;
+ if (!args[idx]->Type()->Is<core::type::Texture>()) {
+ auto* comp = args[idx++]->As<core::ir::Constant>();
+ TINT_ASSERT(comp);
+
+ switch (comp->Value()->ValueAs<int32_t>()) {
+ case 0:
+ fn = hlsl::BuiltinFn::kGatherRed;
+ break;
+ case 1:
+ fn = hlsl::BuiltinFn::kGatherGreen;
+ break;
+ case 2:
+ fn = hlsl::BuiltinFn::kGatherBlue;
+ break;
+ case 3:
+ fn = hlsl::BuiltinFn::kGatherAlpha;
+ break;
+ default:
+ TINT_UNREACHABLE();
+ }
+ }
+
+ tex = args[idx++];
+
+ auto* tex_type = tex->Type()->As<core::type::Texture>();
+ TINT_ASSERT(tex_type);
+
+ bool is_depth = tex_type->Is<core::type::DepthTexture>();
+
+ params.Push(args[idx++]); // sampler
+ coords = args[idx++];
+
+ uint32_t offset_idx = 0;
+
+ switch (tex_type->dim()) {
+ case core::type::TextureDimension::k2d:
+ params.Push(coords);
+ offset_idx = is_depth ? 3 : 4;
+ break;
+ case core::type::TextureDimension::k2dArray:
+ params.Push(b.Construct(ty.vec3<f32>(), coords, b.Convert<f32>(args[idx++]))
+ ->Result(0));
+ offset_idx = is_depth ? 4 : 5;
+ break;
+ case core::type::TextureDimension::kCube:
+ params.Push(coords);
+ break;
+ case core::type::TextureDimension::kCubeArray:
+ params.Push(b.Construct(ty.vec4<f32>(), coords, b.Convert<f32>(args[idx++]))
+ ->Result(0));
+ break;
+ default:
+ TINT_UNREACHABLE();
+ }
+ if (offset_idx > 0 && args.Length() > offset_idx) {
+ params.Push(args[offset_idx]);
+ }
+
+ b.MemberCallWithResult<hlsl::ir::MemberBuiltinCall>(call->DetachResult(), fn, tex,
+ params);
+ });
+ call->Destroy();
+ }
+
void TextureGatherCompare(core::ir::CoreBuiltinCall* call) {
auto args = call->Args();
b.InsertBefore(call, [&] {
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 13de540..3beca97 100644
--- a/src/tint/lang/hlsl/writer/raise/builtin_polyfill_test.cc
+++ b/src/tint/lang/hlsl/writer/raise/builtin_polyfill_test.cc
@@ -1459,6 +1459,540 @@
EXPECT_EQ(expect, str());
}
+TEST_F(HlslWriter_BuiltinPolyfillTest, TextureGather_Alpha) {
+ core::ir::Var* tex = nullptr;
+ core::ir::Var* sampler = nullptr;
+ b.Append(b.ir.root_block, [&] {
+ tex = b.Var(ty.ptr(handle, ty.Get<core::type::SampledTexture>(
+ core::type::TextureDimension::k2d, ty.i32())));
+ tex->SetBindingPoint(0, 0);
+
+ sampler =
+ b.Var(ty.ptr(handle, ty.Get<core::type::Sampler>(core::type::SamplerKind::kSampler)));
+ sampler->SetBindingPoint(0, 1);
+ });
+
+ auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+ b.Append(func->Block(), [&] {
+ auto* coords = b.Construct(ty.vec2<f32>(), b.Value(1_f), b.Value(2_f));
+
+ auto* t = b.Load(tex);
+ auto* s = b.Load(sampler);
+ b.Let("x", b.Call<vec4<i32>>(core::BuiltinFn::kTextureGather, 3_u, t, s, coords));
+ b.Return(func);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %1:ptr<handle, texture_2d<i32>, read> = var @binding_point(0, 0)
+ %2:ptr<handle, sampler, read> = var @binding_point(0, 1)
+}
+
+%foo = @fragment func():void {
+ $B2: {
+ %4:vec2<f32> = construct 1.0f, 2.0f
+ %5:texture_2d<i32> = load %1
+ %6:sampler = load %2
+ %7:vec4<i32> = textureGather 3u, %5, %6, %4
+ %x:vec4<i32> = let %7
+ ret
+ }
+}
+)";
+ ASSERT_EQ(src, str());
+
+ auto* expect = R"(
+$B1: { # root
+ %1:ptr<handle, texture_2d<i32>, read> = var @binding_point(0, 0)
+ %2:ptr<handle, sampler, read> = var @binding_point(0, 1)
+}
+
+%foo = @fragment func():void {
+ $B2: {
+ %4:vec2<f32> = construct 1.0f, 2.0f
+ %5:texture_2d<i32> = load %1
+ %6:sampler = load %2
+ %7:vec4<i32> = %5.GatherAlpha %6, %4
+ %x:vec4<i32> = let %7
+ ret
+ }
+}
+)";
+
+ Run(BuiltinPolyfill);
+
+ EXPECT_EQ(expect, str());
+}
+TEST_F(HlslWriter_BuiltinPolyfillTest, TextureGather_RedOffset) {
+ core::ir::Var* tex = nullptr;
+ core::ir::Var* sampler = nullptr;
+ b.Append(b.ir.root_block, [&] {
+ tex = b.Var(ty.ptr(handle, ty.Get<core::type::SampledTexture>(
+ core::type::TextureDimension::k2d, ty.i32())));
+ tex->SetBindingPoint(0, 0);
+
+ sampler =
+ b.Var(ty.ptr(handle, ty.Get<core::type::Sampler>(core::type::SamplerKind::kSampler)));
+ sampler->SetBindingPoint(0, 1);
+ });
+
+ auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+ b.Append(func->Block(), [&] {
+ auto* coords = b.Construct(ty.vec2<f32>(), b.Value(1_f), b.Value(2_f));
+ auto* offset = b.Composite<vec2<i32>>(1_i, 3_i);
+
+ auto* t = b.Load(tex);
+ auto* s = b.Load(sampler);
+ b.Let("x", b.Call<vec4<i32>>(core::BuiltinFn::kTextureGather, 0_u, t, s, coords, offset));
+ b.Return(func);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %1:ptr<handle, texture_2d<i32>, read> = var @binding_point(0, 0)
+ %2:ptr<handle, sampler, read> = var @binding_point(0, 1)
+}
+
+%foo = @fragment func():void {
+ $B2: {
+ %4:vec2<f32> = construct 1.0f, 2.0f
+ %5:texture_2d<i32> = load %1
+ %6:sampler = load %2
+ %7:vec4<i32> = textureGather 0u, %5, %6, %4, vec2<i32>(1i, 3i)
+ %x:vec4<i32> = let %7
+ ret
+ }
+}
+)";
+ ASSERT_EQ(src, str());
+
+ auto* expect = R"(
+$B1: { # root
+ %1:ptr<handle, texture_2d<i32>, read> = var @binding_point(0, 0)
+ %2:ptr<handle, sampler, read> = var @binding_point(0, 1)
+}
+
+%foo = @fragment func():void {
+ $B2: {
+ %4:vec2<f32> = construct 1.0f, 2.0f
+ %5:texture_2d<i32> = load %1
+ %6:sampler = load %2
+ %7:vec4<i32> = %5.GatherRed %6, %4, vec2<i32>(1i, 3i)
+ %x:vec4<i32> = let %7
+ ret
+ }
+}
+)";
+
+ Run(BuiltinPolyfill);
+
+ EXPECT_EQ(expect, str());
+}
+TEST_F(HlslWriter_BuiltinPolyfillTest, TextureGather_GreenArray) {
+ core::ir::Var* tex = nullptr;
+ core::ir::Var* sampler = nullptr;
+ b.Append(b.ir.root_block, [&] {
+ tex = b.Var(ty.ptr(handle, ty.Get<core::type::SampledTexture>(
+ core::type::TextureDimension::k2dArray, ty.i32())));
+ tex->SetBindingPoint(0, 0);
+
+ sampler =
+ b.Var(ty.ptr(handle, ty.Get<core::type::Sampler>(core::type::SamplerKind::kSampler)));
+ sampler->SetBindingPoint(0, 1);
+ });
+
+ auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+ b.Append(func->Block(), [&] {
+ auto* coords = b.Construct(ty.vec2<f32>(), b.Value(1_f), b.Value(2_f));
+ auto* array_idx = b.Value(1_u);
+
+ auto* t = b.Load(tex);
+ auto* s = b.Load(sampler);
+ b.Let("x",
+ b.Call<vec4<i32>>(core::BuiltinFn::kTextureGather, 1_u, t, s, coords, array_idx));
+ b.Return(func);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %1:ptr<handle, texture_2d_array<i32>, read> = var @binding_point(0, 0)
+ %2:ptr<handle, sampler, read> = var @binding_point(0, 1)
+}
+
+%foo = @fragment func():void {
+ $B2: {
+ %4:vec2<f32> = construct 1.0f, 2.0f
+ %5:texture_2d_array<i32> = load %1
+ %6:sampler = load %2
+ %7:vec4<i32> = textureGather 1u, %5, %6, %4, 1u
+ %x:vec4<i32> = let %7
+ ret
+ }
+}
+)";
+ ASSERT_EQ(src, str());
+
+ auto* expect = R"(
+$B1: { # root
+ %1:ptr<handle, texture_2d_array<i32>, read> = var @binding_point(0, 0)
+ %2:ptr<handle, sampler, read> = var @binding_point(0, 1)
+}
+
+%foo = @fragment func():void {
+ $B2: {
+ %4:vec2<f32> = construct 1.0f, 2.0f
+ %5:texture_2d_array<i32> = load %1
+ %6:sampler = load %2
+ %7:f32 = convert 1u
+ %8:vec3<f32> = construct %4, %7
+ %9:vec4<i32> = %5.GatherGreen %6, %8
+ %x:vec4<i32> = let %9
+ ret
+ }
+}
+)";
+
+ Run(BuiltinPolyfill);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(HlslWriter_BuiltinPolyfillTest, TextureGather_BlueArrayOffset) {
+ core::ir::Var* tex = nullptr;
+ core::ir::Var* sampler = nullptr;
+ b.Append(b.ir.root_block, [&] {
+ tex = b.Var(ty.ptr(handle, ty.Get<core::type::SampledTexture>(
+ core::type::TextureDimension::k2dArray, ty.i32())));
+ tex->SetBindingPoint(0, 0);
+
+ sampler =
+ b.Var(ty.ptr(handle, ty.Get<core::type::Sampler>(core::type::SamplerKind::kSampler)));
+ sampler->SetBindingPoint(0, 1);
+ });
+
+ auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+ b.Append(func->Block(), [&] {
+ auto* coords = b.Construct(ty.vec2<f32>(), b.Value(1_f), b.Value(2_f));
+ auto* array_idx = b.Value(1_i);
+ auto* offset = b.Composite<vec2<i32>>(1_i, 2_i);
+
+ auto* t = b.Load(tex);
+ auto* s = b.Load(sampler);
+ b.Let("x", b.Call<vec4<i32>>(core::BuiltinFn::kTextureGather, 2_u, t, s, coords, array_idx,
+ offset));
+ b.Return(func);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %1:ptr<handle, texture_2d_array<i32>, read> = var @binding_point(0, 0)
+ %2:ptr<handle, sampler, read> = var @binding_point(0, 1)
+}
+
+%foo = @fragment func():void {
+ $B2: {
+ %4:vec2<f32> = construct 1.0f, 2.0f
+ %5:texture_2d_array<i32> = load %1
+ %6:sampler = load %2
+ %7:vec4<i32> = textureGather 2u, %5, %6, %4, 1i, vec2<i32>(1i, 2i)
+ %x:vec4<i32> = let %7
+ ret
+ }
+}
+)";
+ ASSERT_EQ(src, str());
+
+ auto* expect = R"(
+$B1: { # root
+ %1:ptr<handle, texture_2d_array<i32>, read> = var @binding_point(0, 0)
+ %2:ptr<handle, sampler, read> = var @binding_point(0, 1)
+}
+
+%foo = @fragment func():void {
+ $B2: {
+ %4:vec2<f32> = construct 1.0f, 2.0f
+ %5:texture_2d_array<i32> = load %1
+ %6:sampler = load %2
+ %7:f32 = convert 1i
+ %8:vec3<f32> = construct %4, %7
+ %9:vec4<i32> = %5.GatherBlue %6, %8, vec2<i32>(1i, 2i)
+ %x:vec4<i32> = let %9
+ ret
+ }
+}
+)";
+
+ Run(BuiltinPolyfill);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(HlslWriter_BuiltinPolyfillTest, TextureGather_Depth) {
+ core::ir::Var* tex = nullptr;
+ core::ir::Var* sampler = nullptr;
+ b.Append(b.ir.root_block, [&] {
+ tex = b.Var(
+ ty.ptr(handle, ty.Get<core::type::DepthTexture>(core::type::TextureDimension::k2d)));
+ tex->SetBindingPoint(0, 0);
+
+ sampler =
+ b.Var(ty.ptr(handle, ty.Get<core::type::Sampler>(core::type::SamplerKind::kSampler)));
+ sampler->SetBindingPoint(0, 1);
+ });
+
+ auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+ b.Append(func->Block(), [&] {
+ auto* coords = b.Construct(ty.vec2<f32>(), b.Value(1_f), b.Value(2_f));
+
+ auto* t = b.Load(tex);
+ auto* s = b.Load(sampler);
+ b.Let("x", b.Call<vec4<f32>>(core::BuiltinFn::kTextureGather, t, s, coords));
+ b.Return(func);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %1:ptr<handle, texture_depth_2d, read> = var @binding_point(0, 0)
+ %2:ptr<handle, sampler, read> = var @binding_point(0, 1)
+}
+
+%foo = @fragment func():void {
+ $B2: {
+ %4:vec2<f32> = construct 1.0f, 2.0f
+ %5:texture_depth_2d = load %1
+ %6:sampler = load %2
+ %7:vec4<f32> = textureGather %5, %6, %4
+ %x:vec4<f32> = let %7
+ ret
+ }
+}
+)";
+ ASSERT_EQ(src, str());
+
+ auto* expect = R"(
+$B1: { # root
+ %1:ptr<handle, texture_depth_2d, read> = var @binding_point(0, 0)
+ %2:ptr<handle, sampler, read> = var @binding_point(0, 1)
+}
+
+%foo = @fragment func():void {
+ $B2: {
+ %4:vec2<f32> = construct 1.0f, 2.0f
+ %5:texture_depth_2d = load %1
+ %6:sampler = load %2
+ %7:vec4<f32> = %5.Gather %6, %4
+ %x:vec4<f32> = let %7
+ ret
+ }
+}
+)";
+
+ Run(BuiltinPolyfill);
+
+ EXPECT_EQ(expect, str());
+}
+TEST_F(HlslWriter_BuiltinPolyfillTest, TextureGather_DepthOffset) {
+ core::ir::Var* tex = nullptr;
+ core::ir::Var* sampler = nullptr;
+ b.Append(b.ir.root_block, [&] {
+ tex = b.Var(
+ ty.ptr(handle, ty.Get<core::type::DepthTexture>(core::type::TextureDimension::k2d)));
+ tex->SetBindingPoint(0, 0);
+
+ sampler =
+ b.Var(ty.ptr(handle, ty.Get<core::type::Sampler>(core::type::SamplerKind::kSampler)));
+ sampler->SetBindingPoint(0, 1);
+ });
+
+ auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+ b.Append(func->Block(), [&] {
+ auto* coords = b.Construct(ty.vec2<f32>(), b.Value(1_f), b.Value(2_f));
+ auto* offset = b.Composite<vec2<i32>>(3_i, 4_i);
+
+ auto* t = b.Load(tex);
+ auto* s = b.Load(sampler);
+ b.Let("x", b.Call<vec4<f32>>(core::BuiltinFn::kTextureGather, t, s, coords, offset));
+ b.Return(func);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %1:ptr<handle, texture_depth_2d, read> = var @binding_point(0, 0)
+ %2:ptr<handle, sampler, read> = var @binding_point(0, 1)
+}
+
+%foo = @fragment func():void {
+ $B2: {
+ %4:vec2<f32> = construct 1.0f, 2.0f
+ %5:texture_depth_2d = load %1
+ %6:sampler = load %2
+ %7:vec4<f32> = textureGather %5, %6, %4, vec2<i32>(3i, 4i)
+ %x:vec4<f32> = let %7
+ ret
+ }
+}
+)";
+ ASSERT_EQ(src, str());
+
+ auto* expect = R"(
+$B1: { # root
+ %1:ptr<handle, texture_depth_2d, read> = var @binding_point(0, 0)
+ %2:ptr<handle, sampler, read> = var @binding_point(0, 1)
+}
+
+%foo = @fragment func():void {
+ $B2: {
+ %4:vec2<f32> = construct 1.0f, 2.0f
+ %5:texture_depth_2d = load %1
+ %6:sampler = load %2
+ %7:vec4<f32> = %5.Gather %6, %4, vec2<i32>(3i, 4i)
+ %x:vec4<f32> = let %7
+ ret
+ }
+}
+)";
+
+ Run(BuiltinPolyfill);
+
+ EXPECT_EQ(expect, str());
+}
+TEST_F(HlslWriter_BuiltinPolyfillTest, TextureGather_DepthArray) {
+ core::ir::Var* tex = nullptr;
+ core::ir::Var* sampler = nullptr;
+ b.Append(b.ir.root_block, [&] {
+ tex = b.Var(ty.ptr(
+ handle, ty.Get<core::type::DepthTexture>(core::type::TextureDimension::k2dArray)));
+ tex->SetBindingPoint(0, 0);
+
+ sampler =
+ b.Var(ty.ptr(handle, ty.Get<core::type::Sampler>(core::type::SamplerKind::kSampler)));
+ sampler->SetBindingPoint(0, 1);
+ });
+
+ auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+ b.Append(func->Block(), [&] {
+ auto* coords = b.Construct(ty.vec2<f32>(), b.Value(1_f), b.Value(2_f));
+ auto* array_idx = b.Value(4_i);
+
+ auto* t = b.Load(tex);
+ auto* s = b.Load(sampler);
+ b.Let("x", b.Call<vec4<f32>>(core::BuiltinFn::kTextureGather, t, s, coords, array_idx));
+ b.Return(func);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %1:ptr<handle, texture_depth_2d_array, read> = var @binding_point(0, 0)
+ %2:ptr<handle, sampler, read> = var @binding_point(0, 1)
+}
+
+%foo = @fragment func():void {
+ $B2: {
+ %4:vec2<f32> = construct 1.0f, 2.0f
+ %5:texture_depth_2d_array = load %1
+ %6:sampler = load %2
+ %7:vec4<f32> = textureGather %5, %6, %4, 4i
+ %x:vec4<f32> = let %7
+ ret
+ }
+}
+)";
+ ASSERT_EQ(src, str());
+
+ auto* expect = R"(
+$B1: { # root
+ %1:ptr<handle, texture_depth_2d_array, read> = var @binding_point(0, 0)
+ %2:ptr<handle, sampler, read> = var @binding_point(0, 1)
+}
+
+%foo = @fragment func():void {
+ $B2: {
+ %4:vec2<f32> = construct 1.0f, 2.0f
+ %5:texture_depth_2d_array = load %1
+ %6:sampler = load %2
+ %7:f32 = convert 4i
+ %8:vec3<f32> = construct %4, %7
+ %9:vec4<f32> = %5.Gather %6, %8
+ %x:vec4<f32> = let %9
+ ret
+ }
+}
+)";
+
+ Run(BuiltinPolyfill);
+
+ EXPECT_EQ(expect, str());
+}
+TEST_F(HlslWriter_BuiltinPolyfillTest, TextureGather_DepthArrayOffset) {
+ core::ir::Var* tex = nullptr;
+ core::ir::Var* sampler = nullptr;
+ b.Append(b.ir.root_block, [&] {
+ tex = b.Var(ty.ptr(
+ handle, ty.Get<core::type::DepthTexture>(core::type::TextureDimension::k2dArray)));
+ tex->SetBindingPoint(0, 0);
+
+ sampler =
+ b.Var(ty.ptr(handle, ty.Get<core::type::Sampler>(core::type::SamplerKind::kSampler)));
+ sampler->SetBindingPoint(0, 1);
+ });
+
+ auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+ b.Append(func->Block(), [&] {
+ auto* coords = b.Construct(ty.vec2<f32>(), b.Value(1_f), b.Value(2_f));
+ auto* array_idx = b.Value(4_u);
+ auto* offset = b.Composite<vec2<i32>>(4_i, 5_i);
+
+ auto* t = b.Load(tex);
+ auto* s = b.Load(sampler);
+ b.Let("x",
+ b.Call<vec4<f32>>(core::BuiltinFn::kTextureGather, t, s, coords, array_idx, offset));
+ b.Return(func);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %1:ptr<handle, texture_depth_2d_array, read> = var @binding_point(0, 0)
+ %2:ptr<handle, sampler, read> = var @binding_point(0, 1)
+}
+
+%foo = @fragment func():void {
+ $B2: {
+ %4:vec2<f32> = construct 1.0f, 2.0f
+ %5:texture_depth_2d_array = load %1
+ %6:sampler = load %2
+ %7:vec4<f32> = textureGather %5, %6, %4, 4u, vec2<i32>(4i, 5i)
+ %x:vec4<f32> = let %7
+ ret
+ }
+}
+)";
+ ASSERT_EQ(src, str());
+
+ auto* expect = R"(
+$B1: { # root
+ %1:ptr<handle, texture_depth_2d_array, read> = var @binding_point(0, 0)
+ %2:ptr<handle, sampler, read> = var @binding_point(0, 1)
+}
+
+%foo = @fragment func():void {
+ $B2: {
+ %4:vec2<f32> = construct 1.0f, 2.0f
+ %5:texture_depth_2d_array = load %1
+ %6:sampler = load %2
+ %7:f32 = convert 4u
+ %8:vec3<f32> = construct %4, %7
+ %9:vec4<f32> = %5.Gather %6, %8, vec2<i32>(4i, 5i)
+ %x:vec4<f32> = let %9
+ ret
+ }
+}
+)";
+
+ Run(BuiltinPolyfill);
+
+ EXPECT_EQ(expect, str());
+}
+
TEST_F(HlslWriter_BuiltinPolyfillTest, QuantizeToF16) {
auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
b.Append(func->Block(), [&] {