[ir][spirv-writer] Handle textureLoad builtin
This maps to OpImageFetch. Refactor some logic from TextureSample that
can be reused for TextureLoad.
Bug: tint:1906
Change-Id: Ie1ea9d707b43eee9bff0db5cc4ef9367305028f8
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/141541
Reviewed-by: Ben Clayton <bclayton@google.com>
Auto-Submit: James Price <jrprice@google.com>
Commit-Queue: James Price <jrprice@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
diff --git a/src/tint/ir/intrinsic_call.cc b/src/tint/ir/intrinsic_call.cc
index 81a4f4e..6a34103 100644
--- a/src/tint/ir/intrinsic_call.cc
+++ b/src/tint/ir/intrinsic_call.cc
@@ -76,6 +76,9 @@
case IntrinsicCall::Kind::kSpirvDot:
out << "spirv.dot";
break;
+ case IntrinsicCall::Kind::kSpirvImageFetch:
+ out << "spirv.image_fetch";
+ break;
case IntrinsicCall::Kind::kSpirvImageSampleImplicitLod:
out << "spirv.image_sample_implicit_lod";
break;
diff --git a/src/tint/ir/intrinsic_call.h b/src/tint/ir/intrinsic_call.h
index 7d667ca..674f8bf 100644
--- a/src/tint/ir/intrinsic_call.h
+++ b/src/tint/ir/intrinsic_call.h
@@ -43,6 +43,7 @@
kSpirvAtomicUMin,
kSpirvAtomicXor,
kSpirvDot,
+ kSpirvImageFetch,
kSpirvImageSampleImplicitLod,
kSpirvImageSampleExplicitLod,
kSpirvImageSampleDrefImplicitLod,
diff --git a/src/tint/ir/transform/builtin_polyfill_spirv.cc b/src/tint/ir/transform/builtin_polyfill_spirv.cc
index 274cfb3..9865126 100644
--- a/src/tint/ir/transform/builtin_polyfill_spirv.cc
+++ b/src/tint/ir/transform/builtin_polyfill_spirv.cc
@@ -22,6 +22,7 @@
#include "src/tint/type/builtin_structs.h"
#include "src/tint/type/depth_multisampled_texture.h"
#include "src/tint/type/depth_texture.h"
+#include "src/tint/type/multisampled_texture.h"
#include "src/tint/type/sampled_texture.h"
#include "src/tint/type/texture.h"
@@ -71,6 +72,7 @@
case builtin::Function::kAtomicXor:
case builtin::Function::kDot:
case builtin::Function::kSelect:
+ case builtin::Function::kTextureLoad:
case builtin::Function::kTextureSample:
case builtin::Function::kTextureSampleBias:
case builtin::Function::kTextureSampleCompare:
@@ -108,6 +110,9 @@
case builtin::Function::kSelect:
replacement = Select(builtin);
break;
+ case builtin::Function::kTextureLoad:
+ replacement = TextureLoad(builtin);
+ break;
case builtin::Function::kTextureSample:
case builtin::Function::kTextureSampleBias:
case builtin::Function::kTextureSampleCompare:
@@ -302,6 +307,94 @@
return call->Result();
}
+ /// ImageOperands represents the optional image operands for an image instruction.
+ struct ImageOperands {
+ /// Bias
+ Value* bias = nullptr;
+ /// Lod
+ Value* lod = nullptr;
+ /// Grad (dx)
+ Value* ddx = nullptr;
+ /// Grad (dy)
+ Value* ddy = nullptr;
+ /// ConstOffset
+ Value* offset = nullptr;
+ /// Sample
+ Value* sample = nullptr;
+ };
+
+ /// Append optional image operands to an image intrinsic argument list.
+ /// @param operands the operands
+ /// @param args the argument list
+ /// @param insertion_point the insertion point for new instructions
+ /// @param requires_float_lod true if the lod needs to be a floating point value
+ void AppendImageOperands(ImageOperands& operands,
+ utils::Vector<Value*, 8>& args,
+ Instruction* insertion_point,
+ bool requires_float_lod) {
+ // Add a placeholder argument for the image operand mask, which we will fill in when we have
+ // processed the image operands.
+ uint32_t image_operand_mask = 0u;
+ size_t mask_idx = args.Length();
+ args.Push(nullptr);
+
+ // Add each of the optional image operands if used, updating the image operand mask.
+ if (operands.bias) {
+ image_operand_mask |= SpvImageOperandsBiasMask;
+ args.Push(operands.bias);
+ }
+ if (operands.lod) {
+ image_operand_mask |= SpvImageOperandsLodMask;
+ if (requires_float_lod && operands.lod->Type()->is_integer_scalar()) {
+ auto* convert = b.Convert(ty.f32(), operands.lod);
+ convert->InsertBefore(insertion_point);
+ operands.lod = convert->Result();
+ }
+ args.Push(operands.lod);
+ }
+ if (operands.ddx) {
+ image_operand_mask |= SpvImageOperandsGradMask;
+ args.Push(operands.ddx);
+ args.Push(operands.ddy);
+ }
+ if (operands.offset) {
+ image_operand_mask |= SpvImageOperandsConstOffsetMask;
+ args.Push(operands.offset);
+ }
+ if (operands.sample) {
+ image_operand_mask |= SpvImageOperandsSampleMask;
+ args.Push(operands.sample);
+ }
+
+ // Replace the image operand mask with the final mask value, as a literal operand.
+ auto* literal = ir->constant_values.Get(u32(image_operand_mask));
+ args[mask_idx] = ir->values.Create<LiteralOperand>(literal);
+ }
+
+ /// Append an array index to a coordinate vector.
+ /// @param coords the coordinate vector
+ /// @param array_idx the array index
+ /// @param insertion_point the insertion point for new instructions
+ /// @returns the modified coordinate vector
+ Value* AppendArrayIndex(Value* coords, Value* array_idx, Instruction* insertion_point) {
+ auto* vec = coords->Type()->As<type::Vector>();
+ auto* element_ty = vec->type();
+
+ // Convert the index to match the coordinate type if needed.
+ if (array_idx->Type() != element_ty) {
+ auto* array_idx_converted = b.Convert(element_ty, array_idx);
+ array_idx_converted->InsertBefore(insertion_point);
+ array_idx = array_idx_converted->Result();
+ }
+
+ // Construct a new coordinate vector.
+ auto num_coords = vec->Width();
+ auto* coord_ty = ty.vec(element_ty, num_coords + 1);
+ auto* construct = b.Construct(coord_ty, utils::Vector{coords, array_idx});
+ construct->InsertBefore(insertion_point);
+ return construct->Result();
+ }
+
/// Handle a textureSample*() builtin.
/// @param builtin the builtin call instruction
/// @returns the replacement value
@@ -316,8 +409,6 @@
auto* sampler = next_arg();
auto* coords = next_arg();
auto* texture_ty = texture->Type()->As<type::Texture>();
- auto* array_idx = IsTextureArray(texture_ty->dim()) ? next_arg() : nullptr;
- Value* depth = nullptr;
// Use OpSampledImage to create an OpTypeSampledImage object.
auto* sampled_image =
@@ -326,29 +417,15 @@
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) {
- // Convert the index to an f32.
- auto* array_idx_f32 = b.Convert(ty.f32(), array_idx);
- array_idx_f32->InsertBefore(builtin);
-
- // Construct a new coordinate vector.
- auto num_coords = coords->Type()->As<type::Vector>()->Width();
- auto* coord_ty = ty.vec(ty.f32(), num_coords + 1);
- auto* construct = b.Construct(coord_ty, utils::Vector{coords, array_idx_f32->Result()});
- construct->InsertBefore(builtin);
- coords = construct->Result();
+ coords = AppendArrayIndex(coords, array_idx, builtin);
}
// Determine which SPIR-V intrinsic to use and which optional image operands are needed.
enum IntrinsicCall::Kind intrinsic;
- struct ImageOperands {
- Value* bias = nullptr;
- Value* lod = nullptr;
- Value* ddx = nullptr;
- Value* ddy = nullptr;
- Value* offset = nullptr;
- Value* sample = nullptr;
- } operands;
+ Value* depth = nullptr;
+ ImageOperands operands;
switch (builtin->Func()) {
case builtin::Function::kTextureSample:
intrinsic = IntrinsicCall::Kind::kSpirvImageSampleImplicitLod;
@@ -395,44 +472,8 @@
intrinsic_args.Push(depth);
}
- // Add a placeholder argument for the image operand mask, which we'll fill in when we've
- // processed the image operands.
- uint32_t image_operand_mask = 0u;
- size_t mask_idx = intrinsic_args.Length();
- intrinsic_args.Push(nullptr);
-
- // Add each of the optional image operands if used, updating the image operand mask.
- if (operands.bias) {
- image_operand_mask |= SpvImageOperandsBiasMask;
- intrinsic_args.Push(operands.bias);
- }
- if (operands.lod) {
- image_operand_mask |= SpvImageOperandsLodMask;
- if (operands.lod->Type()->is_integer_scalar()) {
- // Some builtins take the lod as an integer, but SPIR-V always requires an f32.
- auto* convert = b.Convert(ty.f32(), operands.lod);
- convert->InsertBefore(builtin);
- operands.lod = convert->Result();
- }
- intrinsic_args.Push(operands.lod);
- }
- if (operands.ddx) {
- image_operand_mask |= SpvImageOperandsGradMask;
- intrinsic_args.Push(operands.ddx);
- intrinsic_args.Push(operands.ddy);
- }
- if (operands.offset) {
- image_operand_mask |= SpvImageOperandsConstOffsetMask;
- intrinsic_args.Push(operands.offset);
- }
- if (operands.sample) {
- image_operand_mask |= SpvImageOperandsSampleMask;
- intrinsic_args.Push(operands.sample);
- }
-
- // Replace the image operand mask with the final mask value, as a literal operand.
- auto* literal = ir->constant_values.Get(u32(image_operand_mask));
- intrinsic_args[mask_idx] = ir->values.Create<LiteralOperand>(literal);
+ // Add the optional image operands, if any.
+ AppendImageOperands(operands, intrinsic_args, builtin, /* requires_float_lod */ true);
// Call the intrinsic.
// If this is a depth comparison, the result is always f32, otherwise vec4f.
@@ -452,6 +493,63 @@
return result;
}
+
+ /// Handle a textureLoad() builtin.
+ /// @param builtin the builtin call instruction
+ /// @returns the replacement value
+ Value* TextureLoad(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* texture = next_arg();
+ auto* coords = next_arg();
+ auto* texture_ty = texture->Type()->As<type::Texture>();
+
+ // 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);
+ }
+
+ // Start building the argument list for the intrinsic.
+ // The first two operands are always the texture and then the coordinates.
+ utils::Vector<Value*, 8> intrinsic_args;
+ intrinsic_args.Push(texture);
+ intrinsic_args.Push(coords);
+
+ // Add the optional image operands, if any.
+ ImageOperands operands;
+ if (texture_ty->IsAnyOf<type::MultisampledTexture, type::DepthMultisampledTexture>()) {
+ operands.sample = next_arg();
+ } else {
+ operands.lod = next_arg();
+ }
+ AppendImageOperands(operands, intrinsic_args, builtin, /* requires_float_lod */ false);
+
+ // Call the intrinsic.
+ // The result is always a vec4 in SPIR-V.
+ auto* result_ty = builtin->Result()->Type();
+ bool expects_scalar_result = result_ty->Is<type::Scalar>();
+ if (expects_scalar_result) {
+ result_ty = ty.vec4(result_ty);
+ }
+ auto* texture_call =
+ b.Call(result_ty, IntrinsicCall::Kind::kSpirvImageFetch, std::move(intrinsic_args));
+ texture_call->InsertBefore(builtin);
+ auto* result = texture_call->Result();
+
+ // If we are expecting a scalar result, extract the first component.
+ if (expects_scalar_result) {
+ auto* extract = b.Access(ty.f32(), result, 0_u);
+ extract->InsertBefore(builtin);
+ result = extract->Result();
+ }
+
+ return result;
+ }
};
void BuiltinPolyfillSpirv::Run(ir::Module* ir, const DataMap&, DataMap&) const {
diff --git a/src/tint/ir/transform/builtin_polyfill_spirv_test.cc b/src/tint/ir/transform/builtin_polyfill_spirv_test.cc
index e8ab19a..37c5988 100644
--- a/src/tint/ir/transform/builtin_polyfill_spirv_test.cc
+++ b/src/tint/ir/transform/builtin_polyfill_spirv_test.cc
@@ -20,6 +20,7 @@
#include "src/tint/type/atomic.h"
#include "src/tint/type/builtin_structs.h"
#include "src/tint/type/depth_texture.h"
+#include "src/tint/type/multisampled_texture.h"
#include "src/tint/type/sampled_texture.h"
namespace tint::ir::transform {
@@ -897,6 +898,199 @@
EXPECT_EQ(expect, str());
}
+TEST_F(IR_BuiltinPolyfillSpirvTest, TextureLoad_2D) {
+ auto* t =
+ b.FunctionParam("t", ty.Get<type::SampledTexture>(type::TextureDimension::k2d, ty.f32()));
+ auto* coords = b.FunctionParam("coords", ty.vec2<i32>());
+ auto* lod = b.FunctionParam("lod", ty.i32());
+ auto* func = b.Function("foo", ty.vec4<f32>());
+ func->SetParams({t, coords, lod});
+
+ b.With(func->Block(), [&] {
+ auto* result = b.Call(ty.vec4<f32>(), builtin::Function::kTextureLoad, t, coords, lod);
+ b.Return(func, result);
+ });
+
+ auto* src = R"(
+%foo = func(%t:texture_2d<f32>, %coords:vec2<i32>, %lod:i32):vec4<f32> -> %b1 {
+ %b1 = block {
+ %5:vec4<f32> = textureLoad %t, %coords, %lod
+ ret %5
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%t:texture_2d<f32>, %coords:vec2<i32>, %lod:i32):vec4<f32> -> %b1 {
+ %b1 = block {
+ %5:vec4<f32> = spirv.image_fetch %t, %coords, 2u, %lod
+ ret %5
+ }
+}
+)";
+
+ Run<BuiltinPolyfillSpirv>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_BuiltinPolyfillSpirvTest, TextureLoad_2DArray) {
+ auto* t = b.FunctionParam(
+ "t", ty.Get<type::SampledTexture>(type::TextureDimension::k2dArray, ty.f32()));
+ auto* coords = b.FunctionParam("coords", ty.vec2<i32>());
+ auto* array_idx = b.FunctionParam("array_idx", ty.i32());
+ auto* lod = b.FunctionParam("lod", ty.i32());
+ auto* func = b.Function("foo", ty.vec4<f32>());
+ func->SetParams({t, coords, array_idx, lod});
+
+ b.With(func->Block(), [&] {
+ auto* result =
+ b.Call(ty.vec4<f32>(), builtin::Function::kTextureLoad, t, coords, array_idx, lod);
+ b.Return(func, result);
+ });
+
+ auto* src = R"(
+%foo = func(%t:texture_2d_array<f32>, %coords:vec2<i32>, %array_idx:i32, %lod:i32):vec4<f32> -> %b1 {
+ %b1 = block {
+ %6:vec4<f32> = textureLoad %t, %coords, %array_idx, %lod
+ ret %6
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%t:texture_2d_array<f32>, %coords:vec2<i32>, %array_idx:i32, %lod:i32):vec4<f32> -> %b1 {
+ %b1 = block {
+ %6:vec3<i32> = construct %coords, %array_idx
+ %7:vec4<f32> = spirv.image_fetch %t, %6, 2u, %lod
+ ret %7
+ }
+}
+)";
+
+ Run<BuiltinPolyfillSpirv>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_BuiltinPolyfillSpirvTest, TextureLoad_2DArray_IndexDifferentType) {
+ auto* t = b.FunctionParam(
+ "t", ty.Get<type::SampledTexture>(type::TextureDimension::k2dArray, ty.f32()));
+ auto* coords = b.FunctionParam("coords", ty.vec2<i32>());
+ auto* array_idx = b.FunctionParam("array_idx", ty.u32());
+ auto* lod = b.FunctionParam("lod", ty.i32());
+ auto* func = b.Function("foo", ty.vec4<f32>());
+ func->SetParams({t, coords, array_idx, lod});
+
+ b.With(func->Block(), [&] {
+ auto* result =
+ b.Call(ty.vec4<f32>(), builtin::Function::kTextureLoad, t, coords, array_idx, lod);
+ b.Return(func, result);
+ });
+
+ auto* src = R"(
+%foo = func(%t:texture_2d_array<f32>, %coords:vec2<i32>, %array_idx:u32, %lod:i32):vec4<f32> -> %b1 {
+ %b1 = block {
+ %6:vec4<f32> = textureLoad %t, %coords, %array_idx, %lod
+ ret %6
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%t:texture_2d_array<f32>, %coords:vec2<i32>, %array_idx:u32, %lod:i32):vec4<f32> -> %b1 {
+ %b1 = block {
+ %6:i32 = convert %array_idx
+ %7:vec3<i32> = construct %coords, %6
+ %8:vec4<f32> = spirv.image_fetch %t, %7, 2u, %lod
+ ret %8
+ }
+}
+)";
+
+ Run<BuiltinPolyfillSpirv>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_BuiltinPolyfillSpirvTest, TextureLoad_Multisampled2D) {
+ auto* t = b.FunctionParam(
+ "t", ty.Get<type::MultisampledTexture>(type::TextureDimension::k2d, ty.f32()));
+ auto* coords = b.FunctionParam("coords", ty.vec2<i32>());
+ auto* sample_idx = b.FunctionParam("sample_idx", ty.i32());
+ auto* func = b.Function("foo", ty.vec4<f32>());
+ func->SetParams({t, coords, sample_idx});
+
+ b.With(func->Block(), [&] {
+ auto* result =
+ b.Call(ty.vec4<f32>(), builtin::Function::kTextureLoad, t, coords, sample_idx);
+ b.Return(func, result);
+ });
+
+ auto* src = R"(
+%foo = func(%t:texture_multisampled_2d<f32>, %coords:vec2<i32>, %sample_idx:i32):vec4<f32> -> %b1 {
+ %b1 = block {
+ %5:vec4<f32> = textureLoad %t, %coords, %sample_idx
+ ret %5
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%t:texture_multisampled_2d<f32>, %coords:vec2<i32>, %sample_idx:i32):vec4<f32> -> %b1 {
+ %b1 = block {
+ %5:vec4<f32> = spirv.image_fetch %t, %coords, 64u, %sample_idx
+ ret %5
+ }
+}
+)";
+
+ Run<BuiltinPolyfillSpirv>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_BuiltinPolyfillSpirvTest, TextureLoad_Depth2D) {
+ auto* t = b.FunctionParam("t", ty.Get<type::DepthTexture>(type::TextureDimension::k2d));
+ auto* coords = b.FunctionParam("coords", ty.vec2<i32>());
+ auto* lod = b.FunctionParam("lod", ty.i32());
+ auto* func = b.Function("foo", ty.f32());
+ func->SetParams({t, coords, lod});
+
+ b.With(func->Block(), [&] {
+ auto* result = b.Call(ty.f32(), builtin::Function::kTextureLoad, t, coords, lod);
+ b.Return(func, result);
+ });
+
+ auto* src = R"(
+%foo = func(%t:texture_depth_2d, %coords:vec2<i32>, %lod:i32):f32 -> %b1 {
+ %b1 = block {
+ %5:f32 = textureLoad %t, %coords, %lod
+ ret %5
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%t:texture_depth_2d, %coords:vec2<i32>, %lod:i32):f32 -> %b1 {
+ %b1 = block {
+ %5:vec4<f32> = spirv.image_fetch %t, %coords, 2u, %lod
+ %6:f32 = access %5, 0u
+ ret %6
+ }
+}
+)";
+
+ Run<BuiltinPolyfillSpirv>();
+
+ EXPECT_EQ(expect, str());
+}
+
TEST_F(IR_BuiltinPolyfillSpirvTest, TextureSample_1D) {
auto* t =
b.FunctionParam("t", ty.Get<type::SampledTexture>(type::TextureDimension::k1d, ty.f32()));
diff --git a/src/tint/writer/spirv/ir/generator_impl_ir.cc b/src/tint/writer/spirv/ir/generator_impl_ir.cc
index 95e82ee..276b4ea 100644
--- a/src/tint/writer/spirv/ir/generator_impl_ir.cc
+++ b/src/tint/writer/spirv/ir/generator_impl_ir.cc
@@ -1478,6 +1478,9 @@
case ir::IntrinsicCall::Kind::kSpirvDot:
op = spv::Op::OpDot;
break;
+ case ir::IntrinsicCall::Kind::kSpirvImageFetch:
+ op = spv::Op::OpImageFetch;
+ break;
case ir::IntrinsicCall::Kind::kSpirvImageSampleImplicitLod:
op = spv::Op::OpImageSampleImplicitLod;
break;
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 f931747..d38beb0 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
@@ -15,6 +15,7 @@
#include "src/tint/writer/spirv/ir/test_helper_ir.h"
#include "src/tint/builtin/function.h"
+#include "src/tint/type/depth_multisampled_texture.h"
using namespace tint::number_suffixes; // NOLINT
@@ -23,7 +24,9 @@
enum TextureType {
kSampledTexture,
+ kMultisampledTexture,
kDepthTexture,
+ kDepthMultisampledTexture,
};
enum SamplerUsage {
@@ -63,9 +66,15 @@
case kSampledTexture:
out << "SampleTexture";
break;
+ case kMultisampledTexture:
+ out << "MultisampleTexture";
+ break;
case kDepthTexture:
out << "DepthTexture";
break;
+ case kDepthMultisampledTexture:
+ out << "DepthMultisampledTexture";
+ break;
}
return out;
}
@@ -87,8 +96,12 @@
switch (type) {
case kSampledTexture:
return ty.Get<type::SampledTexture>(dim, MakeScalarType(texel_type));
+ case kMultisampledTexture:
+ return ty.Get<type::MultisampledTexture>(dim, MakeScalarType(texel_type));
case kDepthTexture:
return ty.Get<type::DepthTexture>(dim);
+ case kDepthMultisampledTexture:
+ return ty.Get<type::DepthMultisampledTexture>(dim);
}
return nullptr;
}
@@ -924,5 +937,124 @@
}),
PrintCase);
+////////////////////////////////////////////////////////////////
+//// textureLoad
+////////////////////////////////////////////////////////////////
+using TextureLoad = TextureBuiltinTest;
+TEST_P(TextureLoad, Emit) {
+ Run(builtin::Function::kTextureLoad, kNoSampler);
+}
+INSTANTIATE_TEST_SUITE_P(SpvGeneratorImplTest,
+ TextureLoad,
+ testing::Values(
+ TextureBuiltinTestCase{
+ kSampledTexture,
+ type::TextureDimension::k1d,
+ /* texel type */ kF32,
+ {{"coord", 1, kI32}, {"lod", 1, kI32}},
+ {"result", 4, kF32},
+ {
+ "OpImageFetch %v4float %t %coord Lod %lod",
+ },
+ },
+ TextureBuiltinTestCase{
+ kSampledTexture,
+ type::TextureDimension::k2d,
+ /* texel type */ kF32,
+ {{"coords", 2, kI32}, {"lod", 1, kI32}},
+ {"result", 4, kF32},
+ {
+ "OpImageFetch %v4float %t %coords Lod %lod",
+ },
+ },
+ TextureBuiltinTestCase{
+ kSampledTexture,
+ type::TextureDimension::k2dArray,
+ /* texel type */ kF32,
+ {{"coords", 2, kI32}, {"array_idx", 1, kI32}, {"lod", 1, kI32}},
+ {"result", 4, kF32},
+ {
+ "%10 = OpCompositeConstruct %v3int %coords %array_idx",
+ "OpImageFetch %v4float %t %10 Lod %lod",
+ },
+ },
+ TextureBuiltinTestCase{
+ kSampledTexture,
+ type::TextureDimension::k3d,
+ /* texel type */ kF32,
+ {{"coords", 3, kI32}, {"lod", 1, kI32}},
+ {"result", 4, kF32},
+ {
+ "OpImageFetch %v4float %t %coords Lod %lod",
+ },
+ },
+ TextureBuiltinTestCase{
+ kMultisampledTexture,
+ type::TextureDimension::k2d,
+ /* texel type */ kF32,
+ {{"coords", 2, kI32}, {"sample_idx", 1, kI32}},
+ {"result", 4, kF32},
+ {
+ "OpImageFetch %v4float %t %coords Sample %sample_idx",
+ },
+ },
+ TextureBuiltinTestCase{
+ kDepthTexture,
+ type::TextureDimension::k2d,
+ /* texel type */ kF32,
+ {{"coords", 2, kI32}, {"lod", 1, kI32}},
+ {"result", 1, kF32},
+ {
+ "OpImageFetch %v4float %t %coords Lod %lod",
+ "%result = OpCompositeExtract %float",
+ },
+ },
+ TextureBuiltinTestCase{
+ kDepthTexture,
+ type::TextureDimension::k2dArray,
+ /* texel type */ kF32,
+ {{"coords", 2, kI32}, {"array_idx", 1, kI32}, {"lod", 1, kI32}},
+ {"result", 1, kF32},
+ {
+ "%9 = OpCompositeConstruct %v3int %coords %array_idx",
+ "OpImageFetch %v4float %t %9 Lod %lod",
+ "%result = OpCompositeExtract %float",
+ },
+ },
+ TextureBuiltinTestCase{
+ kDepthMultisampledTexture,
+ type::TextureDimension::k2d,
+ /* texel type */ kF32,
+ {{"coords", 3, kI32}, {"sample_idx", 1, kI32}},
+ {"result", 1, kF32},
+ {
+ "OpImageFetch %v4float %t %coords Sample %sample_idx",
+ "%result = OpCompositeExtract %float",
+ },
+ },
+
+ // Test some textures with integer texel types.
+ TextureBuiltinTestCase{
+ kSampledTexture,
+ type::TextureDimension::k2d,
+ /* texel type */ kI32,
+ {{"coords", 2, kI32}, {"lod", 1, kI32}},
+ {"result", 4, kI32},
+ {
+ "OpImageFetch %v4int %t %coords Lod %lod",
+ },
+ },
+ TextureBuiltinTestCase{
+ kSampledTexture,
+ type::TextureDimension::k2d,
+ /* texel type */ kU32,
+ {{"coords", 2, kI32}, {"lod", 1, kI32}},
+ {"result", 4, kU32},
+ {
+ "OpImageFetch %v4uint %t %coords Lod %lod",
+ },
+ }),
+ PrintCase);
+
} // namespace
} // namespace tint::writer::spirv