[ir][spirv-writer] Handle textureGather{Compare}
These map to OpImageGather and OpImageDrefGather. Unlike other texture
builtins, the texture argument is not always first, so needs some
special casing in tests.
Bug: tint:1906
Change-Id: I6046b61073ca6b3f77c50b37501874cdcec7903f
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/141902
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
Auto-Submit: James Price <jrprice@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/ir/intrinsic_call.cc b/src/tint/ir/intrinsic_call.cc
index 4982e79..5f51bc1 100644
--- a/src/tint/ir/intrinsic_call.cc
+++ b/src/tint/ir/intrinsic_call.cc
@@ -79,6 +79,12 @@
case IntrinsicCall::Kind::kSpirvImageFetch:
out << "spirv.image_fetch";
break;
+ case IntrinsicCall::Kind::kSpirvImageGather:
+ out << "spirv.image_gather";
+ break;
+ case IntrinsicCall::Kind::kSpirvImageDrefGather:
+ out << "spirv.image_dref_gather";
+ break;
case IntrinsicCall::Kind::kSpirvImageQuerySize:
out << "spirv.image_query_size";
break;
diff --git a/src/tint/ir/intrinsic_call.h b/src/tint/ir/intrinsic_call.h
index ed90316..9c10969 100644
--- a/src/tint/ir/intrinsic_call.h
+++ b/src/tint/ir/intrinsic_call.h
@@ -44,6 +44,8 @@
kSpirvAtomicXor,
kSpirvDot,
kSpirvImageFetch,
+ kSpirvImageGather,
+ kSpirvImageDrefGather,
kSpirvImageQuerySize,
kSpirvImageQuerySizeLod,
kSpirvImageSampleImplicitLod,
diff --git a/src/tint/ir/transform/builtin_polyfill_spirv.cc b/src/tint/ir/transform/builtin_polyfill_spirv.cc
index 433c9be..1a12495 100644
--- a/src/tint/ir/transform/builtin_polyfill_spirv.cc
+++ b/src/tint/ir/transform/builtin_polyfill_spirv.cc
@@ -74,6 +74,8 @@
case builtin::Function::kDot:
case builtin::Function::kSelect:
case builtin::Function::kTextureDimensions:
+ case builtin::Function::kTextureGather:
+ case builtin::Function::kTextureGatherCompare:
case builtin::Function::kTextureLoad:
case builtin::Function::kTextureSample:
case builtin::Function::kTextureSampleBias:
@@ -116,6 +118,10 @@
case builtin::Function::kTextureDimensions:
replacement = TextureDimensions(builtin);
break;
+ case builtin::Function::kTextureGather:
+ case builtin::Function::kTextureGatherCompare:
+ replacement = TextureGather(builtin);
+ break;
case builtin::Function::kTextureLoad:
replacement = TextureLoad(builtin);
break;
@@ -503,6 +509,80 @@
return result;
}
+ /// Handle a textureGather*() builtin.
+ /// @param builtin the builtin call instruction
+ /// @returns the replacement value
+ Value* TextureGather(CoreBuiltinCall* builtin) {
+ // Helper to get the next argument from the call, or nullptr if there are no more arguments.
+ uint32_t arg_idx = 0;
+ auto next_arg = [&]() {
+ return arg_idx < builtin->Args().Length() ? builtin->Args()[arg_idx++] : nullptr;
+ };
+
+ auto* component = next_arg();
+ if (!component->Type()->is_integer_scalar()) {
+ // The first argument wasn't the component, so it must be the texture instead.
+ // Use constant zero for the component.
+ component = b.Constant(0_u);
+ arg_idx--;
+ }
+ auto* texture = next_arg();
+ auto* sampler = next_arg();
+ auto* coords = next_arg();
+ auto* texture_ty = texture->Type()->As<type::Texture>();
+
+ // Use OpSampledImage to create an OpTypeSampledImage object.
+ auto* sampled_image =
+ b.Call(ty.Get<SampledImage>(texture_ty), IntrinsicCall::Kind::kSpirvSampledImage,
+ utils::Vector{texture, sampler});
+ sampled_image->InsertBefore(builtin);
+
+ // Append the array index to the coordinates if provided.
+ auto* array_idx = IsTextureArray(texture_ty->dim()) ? next_arg() : nullptr;
+ if (array_idx) {
+ coords = AppendArrayIndex(coords, array_idx, builtin);
+ }
+
+ // Determine which SPIR-V intrinsic to use and which optional image operands are needed.
+ enum IntrinsicCall::Kind intrinsic;
+ Value* depth = nullptr;
+ ImageOperands operands;
+ switch (builtin->Func()) {
+ case builtin::Function::kTextureGather:
+ intrinsic = IntrinsicCall::Kind::kSpirvImageGather;
+ operands.offset = next_arg();
+ break;
+ case builtin::Function::kTextureGatherCompare:
+ intrinsic = IntrinsicCall::Kind::kSpirvImageDrefGather;
+ depth = next_arg();
+ operands.offset = next_arg();
+ break;
+ default:
+ return nullptr;
+ }
+
+ // Start building the argument list for the intrinsic.
+ // The first two operands are always the sampled image and then the coordinates, followed by
+ // either the depth reference or the component.
+ utils::Vector<Value*, 8> intrinsic_args;
+ intrinsic_args.Push(sampled_image->Result());
+ intrinsic_args.Push(coords);
+ if (depth) {
+ intrinsic_args.Push(depth);
+ } else {
+ intrinsic_args.Push(component);
+ }
+
+ // Add the optional image operands, if any.
+ AppendImageOperands(operands, intrinsic_args, builtin, /* requires_float_lod */ true);
+
+ // Call the intrinsic.
+ auto* result_ty = builtin->Result()->Type();
+ auto* texture_call = b.Call(result_ty, intrinsic, std::move(intrinsic_args));
+ texture_call->InsertBefore(builtin);
+ return texture_call->Result();
+ }
+
/// Handle a textureLoad() builtin.
/// @param builtin the builtin call instruction
/// @returns the replacement value
diff --git a/src/tint/ir/transform/builtin_polyfill_spirv_test.cc b/src/tint/ir/transform/builtin_polyfill_spirv_test.cc
index a3a09793..8f26e9d 100644
--- a/src/tint/ir/transform/builtin_polyfill_spirv_test.cc
+++ b/src/tint/ir/transform/builtin_polyfill_spirv_test.cc
@@ -1874,6 +1874,289 @@
EXPECT_EQ(expect, str());
}
+TEST_F(IR_BuiltinPolyfillSpirvTest, TextureGather_2D) {
+ auto* t =
+ b.FunctionParam("t", ty.Get<type::SampledTexture>(type::TextureDimension::k2d, ty.f32()));
+ auto* s = b.FunctionParam("s", ty.sampler());
+ auto* component = b.FunctionParam("component", ty.i32());
+ auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
+ auto* func = b.Function("foo", ty.vec4<f32>());
+ func->SetParams({component, t, s, coords});
+
+ b.With(func->Block(), [&] {
+ auto* result =
+ b.Call(ty.vec4<f32>(), builtin::Function::kTextureGather, component, t, s, coords);
+ b.Return(func, result);
+ });
+
+ auto* src = R"(
+%foo = func(%component:i32, %t:texture_2d<f32>, %s:sampler, %coords:vec2<f32>):vec4<f32> -> %b1 {
+ %b1 = block {
+ %6:vec4<f32> = textureGather %component, %t, %s, %coords
+ ret %6
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%component:i32, %t:texture_2d<f32>, %s:sampler, %coords:vec2<f32>):vec4<f32> -> %b1 {
+ %b1 = block {
+ %6:spirv.sampled_image = spirv.sampled_image %t, %s
+ %7:vec4<f32> = spirv.image_gather %6, %coords, %component, 0u
+ ret %7
+ }
+}
+)";
+
+ Run<BuiltinPolyfillSpirv>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_BuiltinPolyfillSpirvTest, TextureGather_2D_Offset) {
+ auto* t =
+ b.FunctionParam("t", ty.Get<type::SampledTexture>(type::TextureDimension::k2d, ty.f32()));
+ auto* s = b.FunctionParam("s", ty.sampler());
+ auto* component = b.FunctionParam("component", ty.i32());
+ auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
+ auto* func = b.Function("foo", ty.vec4<f32>());
+ func->SetParams({t, s, component, coords});
+
+ b.With(func->Block(), [&] {
+ auto* result = b.Call(
+ ty.vec4<f32>(), builtin::Function::kTextureGather, component, t, s, coords,
+ b.Constant(mod.constant_values.Splat(ty.vec2<i32>(), mod.constant_values.Get(1_i), 2)));
+ b.Return(func, result);
+ });
+
+ auto* src = R"(
+%foo = func(%t:texture_2d<f32>, %s:sampler, %component:i32, %coords:vec2<f32>):vec4<f32> -> %b1 {
+ %b1 = block {
+ %6:vec4<f32> = textureGather %component, %t, %s, %coords, vec2<i32>(1i)
+ ret %6
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%t:texture_2d<f32>, %s:sampler, %component:i32, %coords:vec2<f32>):vec4<f32> -> %b1 {
+ %b1 = block {
+ %6:spirv.sampled_image = spirv.sampled_image %t, %s
+ %7:vec4<f32> = spirv.image_gather %6, %coords, %component, 8u, vec2<i32>(1i)
+ ret %7
+ }
+}
+)";
+
+ Run<BuiltinPolyfillSpirv>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_BuiltinPolyfillSpirvTest, TextureGather_2DArray_Offset) {
+ auto* t = b.FunctionParam(
+ "t", ty.Get<type::SampledTexture>(type::TextureDimension::k2dArray, ty.f32()));
+ auto* s = b.FunctionParam("s", ty.sampler());
+ auto* component = b.FunctionParam("component", ty.i32());
+ auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
+ auto* array_idx = b.FunctionParam("array_idx", ty.i32());
+ auto* func = b.Function("foo", ty.vec4<f32>());
+ func->SetParams({t, s, component, coords, array_idx});
+
+ b.With(func->Block(), [&] {
+ auto* result = b.Call(
+ ty.vec4<f32>(), builtin::Function::kTextureGather, component, t, s, coords, array_idx,
+ b.Constant(mod.constant_values.Splat(ty.vec2<i32>(), mod.constant_values.Get(1_i), 2)));
+ b.Return(func, result);
+ });
+
+ auto* src = R"(
+%foo = func(%t:texture_2d_array<f32>, %s:sampler, %component:i32, %coords:vec2<f32>, %array_idx:i32):vec4<f32> -> %b1 {
+ %b1 = block {
+ %7:vec4<f32> = textureGather %component, %t, %s, %coords, %array_idx, vec2<i32>(1i)
+ ret %7
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%t:texture_2d_array<f32>, %s:sampler, %component:i32, %coords:vec2<f32>, %array_idx:i32):vec4<f32> -> %b1 {
+ %b1 = block {
+ %7:spirv.sampled_image = spirv.sampled_image %t, %s
+ %8:f32 = convert %array_idx
+ %9:vec3<f32> = construct %coords, %8
+ %10:vec4<f32> = spirv.image_gather %7, %9, %component, 8u, vec2<i32>(1i)
+ ret %10
+ }
+}
+)";
+
+ Run<BuiltinPolyfillSpirv>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_BuiltinPolyfillSpirvTest, TextureGather_Depth2D) {
+ auto* t = b.FunctionParam("t", ty.Get<type::DepthTexture>(type::TextureDimension::k2d));
+ auto* s = b.FunctionParam("s", ty.sampler());
+ auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
+ auto* func = b.Function("foo", ty.vec4<f32>());
+ func->SetParams({t, s, coords});
+
+ b.With(func->Block(), [&] {
+ auto* result = b.Call(ty.vec4<f32>(), builtin::Function::kTextureGather, t, s, coords);
+ b.Return(func, result);
+ });
+
+ auto* src = R"(
+%foo = func(%t:texture_depth_2d, %s:sampler, %coords:vec2<f32>):vec4<f32> -> %b1 {
+ %b1 = block {
+ %5:vec4<f32> = textureGather %t, %s, %coords
+ ret %5
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%t:texture_depth_2d, %s:sampler, %coords:vec2<f32>):vec4<f32> -> %b1 {
+ %b1 = block {
+ %5:spirv.sampled_image = spirv.sampled_image %t, %s
+ %6:vec4<f32> = spirv.image_gather %5, %coords, 0u, 0u
+ ret %6
+ }
+}
+)";
+
+ Run<BuiltinPolyfillSpirv>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_BuiltinPolyfillSpirvTest, TextureGatherCompare_Depth2D) {
+ auto* t = b.FunctionParam("t", ty.Get<type::DepthTexture>(type::TextureDimension::k2d));
+ auto* s = b.FunctionParam("s", ty.sampler());
+ auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
+ auto* depth = b.FunctionParam("depth", ty.f32());
+ auto* func = b.Function("foo", ty.vec4<f32>());
+ func->SetParams({t, s, coords, depth});
+
+ b.With(func->Block(), [&] {
+ auto* result =
+ b.Call(ty.vec4<f32>(), builtin::Function::kTextureGatherCompare, t, s, coords, depth);
+ b.Return(func, result);
+ });
+
+ auto* src = R"(
+%foo = func(%t:texture_depth_2d, %s:sampler, %coords:vec2<f32>, %depth:f32):vec4<f32> -> %b1 {
+ %b1 = block {
+ %6:vec4<f32> = textureGatherCompare %t, %s, %coords, %depth
+ ret %6
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%t:texture_depth_2d, %s:sampler, %coords:vec2<f32>, %depth:f32):vec4<f32> -> %b1 {
+ %b1 = block {
+ %6:spirv.sampled_image = spirv.sampled_image %t, %s
+ %7:vec4<f32> = spirv.image_dref_gather %6, %coords, %depth, 0u
+ ret %7
+ }
+}
+)";
+
+ Run<BuiltinPolyfillSpirv>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_BuiltinPolyfillSpirvTest, TextureGatherCompare_Depth2D_Offset) {
+ auto* t = b.FunctionParam("t", ty.Get<type::DepthTexture>(type::TextureDimension::k2d));
+ auto* s = b.FunctionParam("s", ty.sampler());
+ auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
+ auto* depth = b.FunctionParam("depth", ty.f32());
+ auto* func = b.Function("foo", ty.vec4<f32>());
+ func->SetParams({t, s, coords, depth});
+
+ b.With(func->Block(), [&] {
+ auto* result = b.Call(
+ ty.vec4<f32>(), builtin::Function::kTextureGatherCompare, t, s, coords, depth,
+ b.Constant(mod.constant_values.Splat(ty.vec2<i32>(), mod.constant_values.Get(1_i), 2)));
+ b.Return(func, result);
+ });
+
+ auto* src = R"(
+%foo = func(%t:texture_depth_2d, %s:sampler, %coords:vec2<f32>, %depth:f32):vec4<f32> -> %b1 {
+ %b1 = block {
+ %6:vec4<f32> = textureGatherCompare %t, %s, %coords, %depth, vec2<i32>(1i)
+ ret %6
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%t:texture_depth_2d, %s:sampler, %coords:vec2<f32>, %depth:f32):vec4<f32> -> %b1 {
+ %b1 = block {
+ %6:spirv.sampled_image = spirv.sampled_image %t, %s
+ %7:vec4<f32> = spirv.image_dref_gather %6, %coords, %depth, 8u, vec2<i32>(1i)
+ ret %7
+ }
+}
+)";
+
+ Run<BuiltinPolyfillSpirv>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_BuiltinPolyfillSpirvTest, TextureGatherCompare_Depth2DArray) {
+ auto* t = b.FunctionParam("t", ty.Get<type::DepthTexture>(type::TextureDimension::k2dArray));
+ auto* s = b.FunctionParam("s", ty.sampler());
+ auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
+ auto* array_idx = b.FunctionParam("array_idx", ty.u32());
+ auto* depth = b.FunctionParam("depth", ty.f32());
+ auto* func = b.Function("foo", ty.vec4<f32>());
+ func->SetParams({t, s, coords, array_idx, depth});
+
+ b.With(func->Block(), [&] {
+ auto* result = b.Call(ty.vec4<f32>(), builtin::Function::kTextureGatherCompare, t, s,
+ coords, array_idx, depth);
+ b.Return(func, result);
+ });
+
+ auto* src = R"(
+%foo = func(%t:texture_depth_2d_array, %s:sampler, %coords:vec2<f32>, %array_idx:u32, %depth:f32):vec4<f32> -> %b1 {
+ %b1 = block {
+ %7:vec4<f32> = textureGatherCompare %t, %s, %coords, %array_idx, %depth
+ ret %7
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%t:texture_depth_2d_array, %s:sampler, %coords:vec2<f32>, %array_idx:u32, %depth:f32):vec4<f32> -> %b1 {
+ %b1 = block {
+ %7:spirv.sampled_image = spirv.sampled_image %t, %s
+ %8:f32 = convert %array_idx
+ %9:vec3<f32> = construct %coords, %8
+ %10:vec4<f32> = spirv.image_dref_gather %7, %9, %depth, 0u
+ ret %10
+ }
+}
+)";
+
+ Run<BuiltinPolyfillSpirv>();
+
+ EXPECT_EQ(expect, str());
+}
+
TEST_F(IR_BuiltinPolyfillSpirvTest, TextureStore_2D) {
auto format = builtin::TexelFormat::kR32Float;
auto* t = b.FunctionParam("t", ty.Get<type::StorageTexture>(
diff --git a/src/tint/writer/spirv/ir/generator_impl_ir.cc b/src/tint/writer/spirv/ir/generator_impl_ir.cc
index 99c13c8..31ac619 100644
--- a/src/tint/writer/spirv/ir/generator_impl_ir.cc
+++ b/src/tint/writer/spirv/ir/generator_impl_ir.cc
@@ -1509,6 +1509,12 @@
case ir::IntrinsicCall::Kind::kSpirvImageFetch:
op = spv::Op::OpImageFetch;
break;
+ case ir::IntrinsicCall::Kind::kSpirvImageGather:
+ op = spv::Op::OpImageGather;
+ break;
+ case ir::IntrinsicCall::Kind::kSpirvImageDrefGather:
+ op = spv::Op::OpImageDrefGather;
+ break;
case ir::IntrinsicCall::Kind::kSpirvImageQuerySize:
module_.PushCapability(SpvCapabilityImageQuery);
op = spv::Op::OpImageQuerySize;
diff --git a/src/tint/writer/spirv/ir/generator_impl_ir_texture_builtin_test.cc b/src/tint/writer/spirv/ir/generator_impl_ir_texture_builtin_test.cc
index 13c153c..831e963 100644
--- a/src/tint/writer/spirv/ir/generator_impl_ir_texture_builtin_test.cc
+++ b/src/tint/writer/spirv/ir/generator_impl_ir_texture_builtin_test.cc
@@ -156,12 +156,21 @@
func->SetParams(std::move(func_params));
b.With(func->Block(), [&] {
- utils::Vector<ir::Value*, 4> args = {t};
+ uint32_t arg_value = 1;
+
+ utils::Vector<ir::Value*, 4> args;
+ if (function == builtin::Function::kTextureGather &&
+ params.texture_type != kDepthTexture) {
+ // Special case for textureGather, which has a component argument first.
+ auto* component = MakeScalarValue(kU32, arg_value++);
+ args.Push(component);
+ mod.SetName(component, "component");
+ }
+ args.Push(t);
if (s) {
args.Push(s);
}
- uint32_t arg_value = 1;
for (const auto& arg : params.args) {
auto* value = MakeScalarValue(arg.type, arg_value++);
if (arg.width > 1) {
@@ -966,6 +975,272 @@
PrintCase);
////////////////////////////////////////////////////////////////
+//// textureGather
+////////////////////////////////////////////////////////////////
+using TextureGather = TextureBuiltinTest;
+TEST_P(TextureGather, Emit) {
+ Run(builtin::Function::kTextureGather, kSampler);
+}
+INSTANTIATE_TEST_SUITE_P(
+ SpvGeneratorImplTest,
+ TextureGather,
+ testing::Values(
+ TextureBuiltinTestCase{
+ kSampledTexture,
+ type::TextureDimension::k2d,
+ /* texel type */ kF32,
+ {{"coords", 2, kF32}},
+ {"result", 4, kF32},
+ {
+ "%10 = OpSampledImage %11 %t %s",
+ "%result = OpImageGather %v4float %10 %coords %component None",
+ },
+ },
+ TextureBuiltinTestCase{
+ kSampledTexture,
+ type::TextureDimension::k2d,
+ /* texel type */ kF32,
+ {{"coords", 2, kF32}, {"offset", 2, kI32}},
+ {"result", 4, kF32},
+ {
+ "%10 = OpSampledImage %11 %t %s",
+ "%result = OpImageGather %v4float %10 %coords %component ConstOffset %offset",
+ },
+ },
+ TextureBuiltinTestCase{
+ kSampledTexture,
+ type::TextureDimension::k2dArray,
+ /* texel type */ kF32,
+ {{"coords", 2, kF32}, {"array_idx", 1, kI32}},
+ {"result", 4, kF32},
+ {
+ "%10 = OpSampledImage %11 %t %s",
+ "%12 = OpConvertSToF %float %array_idx",
+ "%16 = OpCompositeConstruct %v3float %coords %12",
+ "%result = OpImageGather %v4float %10 %16 %component None",
+ },
+ },
+ TextureBuiltinTestCase{
+ kSampledTexture,
+ type::TextureDimension::k2dArray,
+ /* texel type */ kF32,
+ {{"coords", 2, kF32}, {"array_idx", 1, kI32}, {"offset", 2, kI32}},
+ {"result", 4, kF32},
+ {
+ "%10 = OpSampledImage %11 %t %s",
+ "%12 = OpConvertSToF %float %array_idx",
+ "%16 = OpCompositeConstruct %v3float %coords %12",
+ "%result = OpImageGather %v4float %10 %16 %component ConstOffset %offset",
+ },
+ },
+ TextureBuiltinTestCase{
+ kSampledTexture,
+ type::TextureDimension::kCube,
+ /* texel type */ kF32,
+ {{"coords", 3, kF32}},
+ {"result", 4, kF32},
+ {
+ "%10 = OpSampledImage %11 %t %s",
+ "%result = OpImageGather %v4float %10 %coords %component None",
+ },
+ },
+ TextureBuiltinTestCase{
+ kSampledTexture,
+ type::TextureDimension::kCubeArray,
+ /* texel type */ kF32,
+ {{"coords", 3, kF32}, {"array_idx", 1, kI32}},
+ {"result", 4, kF32},
+ {
+ "%10 = OpSampledImage %11 %t %s",
+ "%12 = OpConvertSToF %float %array_idx",
+ "%15 = OpCompositeConstruct %v4float %coords %12",
+ "%result = OpImageGather %v4float %10 %15 %component None",
+ },
+ },
+ TextureBuiltinTestCase{
+ kDepthTexture,
+ type::TextureDimension::k2d,
+ /* texel type */ kF32,
+ {{"coords", 2, kF32}},
+ {"result", 4, kF32},
+ {
+ "%10 = OpSampledImage %11 %t %s",
+ "%result = OpImageGather %v4float %10 %coords %uint_0 None",
+ },
+ },
+ TextureBuiltinTestCase{
+ kDepthTexture,
+ type::TextureDimension::k2d,
+ /* texel type */ kF32,
+ {{"coords", 2, kF32}, {"offset", 2, kI32}},
+ {"result", 4, kF32},
+ {
+ "%10 = OpSampledImage %11 %t %s",
+ "%result = OpImageGather %v4float %10 %coords %uint_0 ConstOffset %offset",
+ },
+ },
+ TextureBuiltinTestCase{
+ kDepthTexture,
+ type::TextureDimension::kCube,
+ /* texel type */ kF32,
+ {{"coords", 3, kF32}},
+ {"result", 4, kF32},
+ {
+ "%10 = OpSampledImage %11 %t %s",
+ "%result = OpImageGather %v4float %10 %coords %uint_0 None",
+ },
+ },
+ TextureBuiltinTestCase{
+ kDepthTexture,
+ type::TextureDimension::k2dArray,
+ /* texel type */ kF32,
+ {{"coords", 2, kF32}, {"array_idx", 1, kI32}},
+ {"result", 4, kF32},
+ {
+ "%10 = OpSampledImage %11 %t %s",
+ "%12 = OpConvertSToF %float %array_idx",
+ "%16 = OpCompositeConstruct %v3float %coords %12",
+ "%result = OpImageGather %v4float %10 %16 %uint_0 None",
+ },
+ },
+ TextureBuiltinTestCase{
+ kDepthTexture,
+ type::TextureDimension::k2dArray,
+ /* texel type */ kF32,
+ {{"coords", 2, kF32}, {"array_idx", 1, kI32}, {"offset", 2, kI32}},
+ {"result", 4, kF32},
+ {
+ "%10 = OpSampledImage %11 %t %s",
+ "%12 = OpConvertSToF %float %array_idx",
+ "%16 = OpCompositeConstruct %v3float %coords %12",
+ "%result = OpImageGather %v4float %10 %16 %uint_0 ConstOffset %offset",
+ },
+ },
+ TextureBuiltinTestCase{
+ kDepthTexture,
+ type::TextureDimension::kCubeArray,
+ /* texel type */ kF32,
+ {{"coords", 3, kF32}, {"array_idx", 1, kI32}},
+ {"result", 4, kF32},
+ {
+ "%10 = OpSampledImage %11 %t %s",
+ "%12 = OpConvertSToF %float %array_idx",
+ "%15 = OpCompositeConstruct %v4float %coords %12",
+ "%result = OpImageGather %v4float %10 %15 %uint_0 None",
+ },
+ },
+
+ // Test some textures with integer texel types.
+ TextureBuiltinTestCase{
+ kSampledTexture,
+ type::TextureDimension::k2d,
+ /* texel type */ kI32,
+ {{"coords", 2, kF32}},
+ {"result", 4, kI32},
+ {
+ "%10 = OpSampledImage %11 %t %s",
+ "%result = OpImageGather %v4int %10 %coords %component None",
+ },
+ },
+ TextureBuiltinTestCase{
+ kSampledTexture,
+ type::TextureDimension::k2d,
+ /* texel type */ kU32,
+ {{"coords", 2, kF32}},
+ {"result", 4, kU32},
+ {
+ "%10 = OpSampledImage %11 %t %s",
+ "%result = OpImageGather %v4uint %10 %coords %component None",
+ },
+ }),
+ PrintCase);
+
+////////////////////////////////////////////////////////////////
+//// textureGatherCompare
+////////////////////////////////////////////////////////////////
+using TextureGatherCompare = TextureBuiltinTest;
+TEST_P(TextureGatherCompare, Emit) {
+ Run(builtin::Function::kTextureGatherCompare, kComparisonSampler);
+}
+INSTANTIATE_TEST_SUITE_P(
+ SpvGeneratorImplTest,
+ TextureGatherCompare,
+ testing::Values(
+ TextureBuiltinTestCase{
+ kDepthTexture,
+ type::TextureDimension::k2d,
+ /* texel type */ kF32,
+ {{"coords", 2, kF32}, {"depth", 1, kF32}},
+ {"result", 4, kF32},
+ {
+ "%10 = OpSampledImage %11 %t %s",
+ "%result = OpImageDrefGather %v4float %10 %coords %depth None",
+ },
+ },
+ TextureBuiltinTestCase{
+ kDepthTexture,
+ type::TextureDimension::k2d,
+ /* texel type */ kF32,
+ {{"coords", 2, kF32}, {"depth", 1, kF32}, {"offset", 2, kI32}},
+ {"result", 4, kF32},
+ {
+ "%10 = OpSampledImage %11 %t %s",
+ "%result = OpImageDrefGather %v4float %10 %coords %depth ConstOffset %offset",
+ },
+ },
+ TextureBuiltinTestCase{
+ kDepthTexture,
+ type::TextureDimension::k2dArray,
+ /* texel type */ kF32,
+ {{"coords", 2, kF32}, {"array_idx", 1, kI32}, {"depth", 1, kF32}},
+ {"result", 4, kF32},
+ {
+ "%10 = OpSampledImage %11 %t %s",
+ "%12 = OpConvertSToF %float %array_idx",
+ "%16 = OpCompositeConstruct %v3float %coords %12",
+ "%result = OpImageDrefGather %v4float %10 %16 %depth None",
+ },
+ },
+ TextureBuiltinTestCase{
+ kDepthTexture,
+ type::TextureDimension::k2dArray,
+ /* texel type */ kF32,
+ {{"coords", 2, kF32}, {"array_idx", 1, kI32}, {"depth", 1, kF32}, {"offset", 2, kI32}},
+ {"result", 4, kF32},
+ {
+ "%10 = OpSampledImage %11 %t %s",
+ "%12 = OpConvertSToF %float %array_idx",
+ "%16 = OpCompositeConstruct %v3float %coords %12",
+ "%result = OpImageDrefGather %v4float %10 %16 %depth ConstOffset %offset",
+ },
+ },
+ TextureBuiltinTestCase{
+ kDepthTexture,
+ type::TextureDimension::kCube,
+ /* texel type */ kF32,
+ {{"coords", 3, kF32}, {"depth", 1, kF32}},
+ {"result", 4, kF32},
+ {
+ "%10 = OpSampledImage %11 %t %s",
+ "%result = OpImageDrefGather %v4float %10 %coords %depth None",
+ },
+ },
+ TextureBuiltinTestCase{
+ kDepthTexture,
+ type::TextureDimension::kCubeArray,
+ /* texel type */ kF32,
+ {{"coords", 3, kF32}, {"array_idx", 1, kI32}, {"depth", 1, kF32}},
+ {"result", 4, kF32},
+ {
+ "%10 = OpSampledImage %11 %t %s",
+ "%12 = OpConvertSToF %float %array_idx",
+ "%15 = OpCompositeConstruct %v4float %coords %12",
+ "%result = OpImageDrefGather %v4float %10 %15 %depth None",
+ },
+ }),
+ PrintCase);
+
+////////////////////////////////////////////////////////////////
//// textureLoad
////////////////////////////////////////////////////////////////
using TextureLoad = TextureBuiltinTest;