[ir][spirv-writer] Handle texture sample builtins
Use the BuiltinPolyfillSpirv transform to replace texture sample
builtins with calls to SPIR-V intrinsic functions that will create the
`OpSampledImage` and then execute an `OpImageSample*` instruction.
A `LiteralOperand` subclass of `ir::Constant` is used to represent the
literal 'image operands' operand, and a `SampledImage` subclass of
`type::Type` is used to represent the `OpSampledImage` type.
Bug: tint:1906
Change-Id: Id3fd166f1cf5772fd75aed5cbeb8c3c02ea65197
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/141230
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>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index 467d112..729d601 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -2088,6 +2088,7 @@
"writer/spirv/ir/generator_impl_ir_switch_test.cc",
"writer/spirv/ir/generator_impl_ir_swizzle_test.cc",
"writer/spirv/ir/generator_impl_ir_test.cc",
+ "writer/spirv/ir/generator_impl_ir_texture_builtin_test.cc",
"writer/spirv/ir/generator_impl_ir_type_test.cc",
"writer/spirv/ir/generator_impl_ir_unary_test.cc",
"writer/spirv/ir/generator_impl_ir_var_test.cc",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index b53cffd..e7b6cb2 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -1350,6 +1350,7 @@
writer/spirv/ir/generator_impl_ir_switch_test.cc
writer/spirv/ir/generator_impl_ir_swizzle_test.cc
writer/spirv/ir/generator_impl_ir_test.cc
+ writer/spirv/ir/generator_impl_ir_texture_builtin_test.cc
writer/spirv/ir/generator_impl_ir_type_test.cc
writer/spirv/ir/generator_impl_ir_unary_test.cc
writer/spirv/ir/generator_impl_ir_var_test.cc
diff --git a/src/tint/ir/intrinsic_call.cc b/src/tint/ir/intrinsic_call.cc
index 4063213..1170418 100644
--- a/src/tint/ir/intrinsic_call.cc
+++ b/src/tint/ir/intrinsic_call.cc
@@ -40,6 +40,21 @@
case IntrinsicCall::Kind::kSpirvSelect:
out << "spirv.select";
break;
+ case IntrinsicCall::Kind::kSpirvSampledImage:
+ out << "spirv.sampled_image";
+ break;
+ case IntrinsicCall::Kind::kSpirvImageSampleImplicitLod:
+ out << "spirv.image_sample_implicit_lod";
+ break;
+ case IntrinsicCall::Kind::kSpirvImageSampleExplicitLod:
+ out << "spirv.image_sample_explicit_lod";
+ break;
+ case IntrinsicCall::Kind::kSpirvImageSampleDrefImplicitLod:
+ out << "spirv.image_sample_dref_implicit_lod";
+ break;
+ case IntrinsicCall::Kind::kSpirvImageSampleDrefExplicitLod:
+ out << "spirv.image_sample_dref_implicit_lod";
+ break;
}
return out;
}
diff --git a/src/tint/ir/intrinsic_call.h b/src/tint/ir/intrinsic_call.h
index 33cbb03..f535972 100644
--- a/src/tint/ir/intrinsic_call.h
+++ b/src/tint/ir/intrinsic_call.h
@@ -31,6 +31,11 @@
// SPIR-V backend intrinsics.
kSpirvDot,
kSpirvSelect,
+ kSpirvSampledImage,
+ kSpirvImageSampleImplicitLod,
+ kSpirvImageSampleExplicitLod,
+ kSpirvImageSampleDrefImplicitLod,
+ kSpirvImageSampleDrefExplicitLod,
};
/// Constructor
diff --git a/src/tint/ir/transform/builtin_polyfill_spirv.cc b/src/tint/ir/transform/builtin_polyfill_spirv.cc
index 1aa82e5..4833847 100644
--- a/src/tint/ir/transform/builtin_polyfill_spirv.cc
+++ b/src/tint/ir/transform/builtin_polyfill_spirv.cc
@@ -16,10 +16,17 @@
#include <utility>
+#include "spirv/unified1/spirv.h"
#include "src/tint/ir/builder.h"
#include "src/tint/ir/module.h"
+#include "src/tint/type/depth_multisampled_texture.h"
+#include "src/tint/type/depth_texture.h"
+#include "src/tint/type/sampled_texture.h"
+#include "src/tint/type/texture.h"
TINT_INSTANTIATE_TYPEINFO(tint::ir::transform::BuiltinPolyfillSpirv);
+TINT_INSTANTIATE_TYPEINFO(tint::ir::transform::BuiltinPolyfillSpirv::LiteralOperand);
+TINT_INSTANTIATE_TYPEINFO(tint::ir::transform::BuiltinPolyfillSpirv::SampledImage);
using namespace tint::number_suffixes; // NOLINT
@@ -52,6 +59,12 @@
switch (builtin->Func()) {
case builtin::Function::kDot:
case builtin::Function::kSelect:
+ case builtin::Function::kTextureSample:
+ case builtin::Function::kTextureSampleBias:
+ case builtin::Function::kTextureSampleCompare:
+ case builtin::Function::kTextureSampleCompareLevel:
+ case builtin::Function::kTextureSampleGrad:
+ case builtin::Function::kTextureSampleLevel:
worklist.Push(builtin);
break;
default:
@@ -70,6 +83,14 @@
case builtin::Function::kSelect:
replacement = Select(builtin);
break;
+ case builtin::Function::kTextureSample:
+ case builtin::Function::kTextureSampleBias:
+ case builtin::Function::kTextureSampleCompare:
+ case builtin::Function::kTextureSampleCompareLevel:
+ case builtin::Function::kTextureSampleGrad:
+ case builtin::Function::kTextureSampleLevel:
+ replacement = TextureSample(builtin);
+ break;
default:
break;
}
@@ -152,10 +173,178 @@
call->InsertBefore(builtin);
return call->Result();
}
+
+ /// Handle a textureSample*() builtin.
+ /// @param builtin the builtin call instruction
+ /// @returns the replacement value
+ Value* TextureSample(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* 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 =
+ 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.
+ 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();
+ }
+
+ // 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;
+ switch (builtin->Func()) {
+ case builtin::Function::kTextureSample:
+ intrinsic = IntrinsicCall::Kind::kSpirvImageSampleImplicitLod;
+ operands.offset = next_arg();
+ break;
+ case builtin::Function::kTextureSampleBias:
+ intrinsic = IntrinsicCall::Kind::kSpirvImageSampleImplicitLod;
+ operands.bias = next_arg();
+ operands.offset = next_arg();
+ break;
+ case builtin::Function::kTextureSampleCompare:
+ intrinsic = IntrinsicCall::Kind::kSpirvImageSampleDrefImplicitLod;
+ depth = next_arg();
+ operands.offset = next_arg();
+ break;
+ case builtin::Function::kTextureSampleCompareLevel:
+ intrinsic = IntrinsicCall::Kind::kSpirvImageSampleDrefExplicitLod;
+ depth = next_arg();
+ operands.lod = b.Constant(0_f);
+ operands.offset = next_arg();
+ break;
+ case builtin::Function::kTextureSampleGrad:
+ intrinsic = IntrinsicCall::Kind::kSpirvImageSampleExplicitLod;
+ operands.ddx = next_arg();
+ operands.ddy = next_arg();
+ operands.offset = next_arg();
+ break;
+ case builtin::Function::kTextureSampleLevel:
+ intrinsic = IntrinsicCall::Kind::kSpirvImageSampleExplicitLod;
+ operands.lod = 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
+ // the depth reference if used.
+ utils::Vector<Value*, 8> intrinsic_args;
+ intrinsic_args.Push(sampled_image->Result());
+ intrinsic_args.Push(coords);
+ if (depth) {
+ 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);
+
+ // Call the intrinsic.
+ // If this is a depth comparison, the result is always f32, otherwise vec4f.
+ auto* result_ty = depth ? static_cast<const type::Type*>(ty.f32()) : ty.vec4<f32>();
+ auto* texture_call = b.Call(result_ty, intrinsic, std::move(intrinsic_args));
+ texture_call->InsertBefore(builtin);
+
+ auto* result = texture_call->Result();
+
+ // If this is not a depth comparison but we are sampling a depth texture, extract the first
+ // component to get the scalar f32 that SPIR-V expects.
+ if (!depth && texture_ty->IsAnyOf<type::DepthTexture, type::DepthMultisampledTexture>()) {
+ 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 {
State{ir}.Process();
}
+BuiltinPolyfillSpirv::LiteralOperand::LiteralOperand(const constant::Value* value) : Base(value) {}
+
+BuiltinPolyfillSpirv::LiteralOperand::~LiteralOperand() = default;
+
+BuiltinPolyfillSpirv::SampledImage::SampledImage(const type::Type* image)
+ : Base(static_cast<size_t>(
+ utils::Hash(utils::TypeInfo::Of<BuiltinPolyfillSpirv::SampledImage>().full_hashcode,
+ image)),
+ type::Flags{}),
+ image_(image) {}
+
+BuiltinPolyfillSpirv::SampledImage* BuiltinPolyfillSpirv::SampledImage::Clone(
+ type::CloneContext& ctx) const {
+ auto* image = image_->Clone(ctx);
+ return ctx.dst.mgr->Get<BuiltinPolyfillSpirv::SampledImage>(image);
+}
+
} // namespace tint::ir::transform
diff --git a/src/tint/ir/transform/builtin_polyfill_spirv.h b/src/tint/ir/transform/builtin_polyfill_spirv.h
index 8909263..d323f68 100644
--- a/src/tint/ir/transform/builtin_polyfill_spirv.h
+++ b/src/tint/ir/transform/builtin_polyfill_spirv.h
@@ -15,7 +15,16 @@
#ifndef SRC_TINT_IR_TRANSFORM_BUILTIN_POLYFILL_SPIRV_H_
#define SRC_TINT_IR_TRANSFORM_BUILTIN_POLYFILL_SPIRV_H_
+#include <string>
+
+#include "src/tint/ir/constant.h"
#include "src/tint/ir/transform/transform.h"
+#include "src/tint/type/type.h"
+
+// Forward declarations
+namespace tint::type {
+class Texture;
+} // namespace tint::type
namespace tint::ir::transform {
@@ -31,6 +40,44 @@
/// @copydoc Transform::Run
void Run(ir::Module* module, const DataMap& inputs, DataMap& outputs) const override;
+ /// LiteralOperand is a type of constant value that is intended to be emitted as a literal in
+ /// the SPIR-V instruction stream.
+ class LiteralOperand final : public utils::Castable<LiteralOperand, ir::Constant> {
+ public:
+ /// Constructor
+ /// @param value the operand value
+ explicit LiteralOperand(const constant::Value* value);
+ /// Destructor
+ ~LiteralOperand() override;
+ };
+
+ /// SampledImage represents an OpTypeSampledImage in SPIR-V.
+ class SampledImage final : public utils::Castable<SampledImage, type::Type> {
+ public:
+ /// Constructor
+ /// @param image the image type
+ explicit SampledImage(const type::Type* image);
+
+ /// @param other the other node to compare against
+ /// @returns true if the this type is equal to @p other
+ bool Equals(const UniqueNode& other) const override {
+ return &other.TypeInfo() == &TypeInfo();
+ }
+
+ /// @returns the friendly name for this type
+ std::string FriendlyName() const override { return "spirv.sampled_image"; }
+
+ /// @param ctx the clone context
+ /// @returns a clone of this type
+ SampledImage* Clone(type::CloneContext& ctx) const override;
+
+ /// @returns the image type
+ const type::Type* Image() const { return image_; }
+
+ private:
+ const type::Type* image_;
+ };
+
private:
struct State;
};
diff --git a/src/tint/ir/transform/builtin_polyfill_spirv_test.cc b/src/tint/ir/transform/builtin_polyfill_spirv_test.cc
index 007a702..6f655c4 100644
--- a/src/tint/ir/transform/builtin_polyfill_spirv_test.cc
+++ b/src/tint/ir/transform/builtin_polyfill_spirv_test.cc
@@ -17,6 +17,8 @@
#include <utility>
#include "src/tint/ir/transform/test_helper.h"
+#include "src/tint/type/depth_texture.h"
+#include "src/tint/type/sampled_texture.h"
namespace tint::ir::transform {
namespace {
@@ -260,5 +262,785 @@
EXPECT_EQ(expect, str());
}
+TEST_F(IR_BuiltinPolyfillSpirvTest, TextureSample_1D) {
+ auto* t =
+ b.FunctionParam("t", ty.Get<type::SampledTexture>(type::TextureDimension::k1d, ty.f32()));
+ auto* s = b.FunctionParam("s", ty.sampler());
+ auto* coords = b.FunctionParam("coords", ty.f32());
+ auto* func = b.Function("foo", ty.vec4<f32>());
+ func->SetParams({t, s, coords});
+
+ b.With(func->Block(), [&] {
+ auto* result = b.Call(ty.f32(), builtin::Function::kTextureSample, t, s, coords);
+ b.Return(func, result);
+ });
+
+ auto* src = R"(
+%foo = func(%t:texture_1d<f32>, %s:sampler, %coords:f32):vec4<f32> -> %b1 {
+ %b1 = block {
+ %5:f32 = textureSample %t, %s, %coords
+ ret %5
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%t:texture_1d<f32>, %s:sampler, %coords:f32):vec4<f32> -> %b1 {
+ %b1 = block {
+ %5:spirv.sampled_image = spirv.sampled_image %t, %s
+ %6:vec4<f32> = spirv.image_sample_implicit_lod %5, %coords, 0u
+ ret %6
+ }
+}
+)";
+
+ Run<BuiltinPolyfillSpirv>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_BuiltinPolyfillSpirvTest, TextureSample_2D) {
+ auto* t =
+ b.FunctionParam("t", ty.Get<type::SampledTexture>(type::TextureDimension::k2d, ty.f32()));
+ 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.f32(), builtin::Function::kTextureSample, t, s, coords);
+ b.Return(func, result);
+ });
+
+ auto* src = R"(
+%foo = func(%t:texture_2d<f32>, %s:sampler, %coords:vec2<f32>):vec4<f32> -> %b1 {
+ %b1 = block {
+ %5:f32 = textureSample %t, %s, %coords
+ ret %5
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%t:texture_2d<f32>, %s:sampler, %coords:vec2<f32>):vec4<f32> -> %b1 {
+ %b1 = block {
+ %5:spirv.sampled_image = spirv.sampled_image %t, %s
+ %6:vec4<f32> = spirv.image_sample_implicit_lod %5, %coords, 0u
+ ret %6
+ }
+}
+)";
+
+ Run<BuiltinPolyfillSpirv>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_BuiltinPolyfillSpirvTest, TextureSample_2D_Offset) {
+ auto* t =
+ b.FunctionParam("t", ty.Get<type::SampledTexture>(type::TextureDimension::k2d, ty.f32()));
+ 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::kTextureSample, 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, %coords:vec2<f32>):vec4<f32> -> %b1 {
+ %b1 = block {
+ %5:vec4<f32> = textureSample %t, %s, %coords, vec2<i32>(1i)
+ ret %5
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%t:texture_2d<f32>, %s:sampler, %coords:vec2<f32>):vec4<f32> -> %b1 {
+ %b1 = block {
+ %5:spirv.sampled_image = spirv.sampled_image %t, %s
+ %6:vec4<f32> = spirv.image_sample_implicit_lod %5, %coords, 8u, vec2<i32>(1i)
+ ret %6
+ }
+}
+)";
+
+ Run<BuiltinPolyfillSpirv>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_BuiltinPolyfillSpirvTest, TextureSample_2DArray_Offset) {
+ auto* t = b.FunctionParam(
+ "t", ty.Get<type::SampledTexture>(type::TextureDimension::k2dArray, ty.f32()));
+ auto* s = b.FunctionParam("s", ty.sampler());
+ 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, coords, array_idx});
+
+ b.With(func->Block(), [&] {
+ auto* result = b.Call(
+ ty.vec4<f32>(), builtin::Function::kTextureSample, 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, %coords:vec2<f32>, %array_idx:i32):vec4<f32> -> %b1 {
+ %b1 = block {
+ %6:vec4<f32> = textureSample %t, %s, %coords, %array_idx, vec2<i32>(1i)
+ ret %6
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%t:texture_2d_array<f32>, %s:sampler, %coords:vec2<f32>, %array_idx:i32):vec4<f32> -> %b1 {
+ %b1 = block {
+ %6:spirv.sampled_image = spirv.sampled_image %t, %s
+ %7:f32 = convert %array_idx
+ %8:vec3<f32> = construct %coords, %7
+ %9:vec4<f32> = spirv.image_sample_implicit_lod %6, %8, 8u, vec2<i32>(1i)
+ ret %9
+ }
+}
+)";
+
+ Run<BuiltinPolyfillSpirv>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_BuiltinPolyfillSpirvTest, TextureSampleBias_2D) {
+ auto* t =
+ b.FunctionParam("t", ty.Get<type::SampledTexture>(type::TextureDimension::k2d, ty.f32()));
+ auto* s = b.FunctionParam("s", ty.sampler());
+ auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
+ auto* bias = b.FunctionParam("bias", ty.f32());
+ auto* func = b.Function("foo", ty.vec4<f32>());
+ func->SetParams({t, s, coords, bias});
+
+ b.With(func->Block(), [&] {
+ auto* result = b.Call(ty.f32(), builtin::Function::kTextureSampleBias, t, s, coords, bias);
+ b.Return(func, result);
+ });
+
+ auto* src = R"(
+%foo = func(%t:texture_2d<f32>, %s:sampler, %coords:vec2<f32>, %bias:f32):vec4<f32> -> %b1 {
+ %b1 = block {
+ %6:f32 = textureSampleBias %t, %s, %coords, %bias
+ ret %6
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%t:texture_2d<f32>, %s:sampler, %coords:vec2<f32>, %bias:f32):vec4<f32> -> %b1 {
+ %b1 = block {
+ %6:spirv.sampled_image = spirv.sampled_image %t, %s
+ %7:vec4<f32> = spirv.image_sample_implicit_lod %6, %coords, 1u, %bias
+ ret %7
+ }
+}
+)";
+
+ Run<BuiltinPolyfillSpirv>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_BuiltinPolyfillSpirvTest, TextureSampleBias_2D_Offset) {
+ auto* t =
+ b.FunctionParam("t", ty.Get<type::SampledTexture>(type::TextureDimension::k2d, ty.f32()));
+ auto* s = b.FunctionParam("s", ty.sampler());
+ auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
+ auto* bias = b.FunctionParam("bias", ty.f32());
+ auto* func = b.Function("foo", ty.vec4<f32>());
+ func->SetParams({t, s, coords, bias});
+
+ b.With(func->Block(), [&] {
+ auto* result = b.Call(
+ ty.vec4<f32>(), builtin::Function::kTextureSampleBias, t, s, coords, bias,
+ 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, %coords:vec2<f32>, %bias:f32):vec4<f32> -> %b1 {
+ %b1 = block {
+ %6:vec4<f32> = textureSampleBias %t, %s, %coords, %bias, vec2<i32>(1i)
+ ret %6
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%t:texture_2d<f32>, %s:sampler, %coords:vec2<f32>, %bias:f32):vec4<f32> -> %b1 {
+ %b1 = block {
+ %6:spirv.sampled_image = spirv.sampled_image %t, %s
+ %7:vec4<f32> = spirv.image_sample_implicit_lod %6, %coords, 9u, %bias, vec2<i32>(1i)
+ ret %7
+ }
+}
+)";
+
+ Run<BuiltinPolyfillSpirv>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_BuiltinPolyfillSpirvTest, TextureSampleBias_2DArray_Offset) {
+ auto* t = b.FunctionParam(
+ "t", ty.Get<type::SampledTexture>(type::TextureDimension::k2dArray, ty.f32()));
+ auto* s = b.FunctionParam("s", ty.sampler());
+ auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
+ auto* array_idx = b.FunctionParam("array_idx", ty.i32());
+ auto* bias = b.FunctionParam("bias", ty.f32());
+ auto* func = b.Function("foo", ty.vec4<f32>());
+ func->SetParams({t, s, coords, array_idx, bias});
+
+ b.With(func->Block(), [&] {
+ auto* result = b.Call(
+ ty.vec4<f32>(), builtin::Function::kTextureSampleBias, t, s, coords, array_idx, bias,
+ 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, %coords:vec2<f32>, %array_idx:i32, %bias:f32):vec4<f32> -> %b1 {
+ %b1 = block {
+ %7:vec4<f32> = textureSampleBias %t, %s, %coords, %array_idx, %bias, vec2<i32>(1i)
+ ret %7
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%t:texture_2d_array<f32>, %s:sampler, %coords:vec2<f32>, %array_idx:i32, %bias: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_sample_implicit_lod %7, %9, 9u, %bias, vec2<i32>(1i)
+ ret %10
+ }
+}
+)";
+
+ Run<BuiltinPolyfillSpirv>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_BuiltinPolyfillSpirvTest, TextureSampleCompare_2D) {
+ 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* dref = b.FunctionParam("dref", ty.f32());
+ auto* func = b.Function("foo", ty.f32());
+ func->SetParams({t, s, coords, dref});
+
+ b.With(func->Block(), [&] {
+ auto* result =
+ b.Call(ty.f32(), builtin::Function::kTextureSampleCompare, t, s, coords, dref);
+ b.Return(func, result);
+ });
+
+ auto* src = R"(
+%foo = func(%t:texture_depth_2d, %s:sampler, %coords:vec2<f32>, %dref:f32):f32 -> %b1 {
+ %b1 = block {
+ %6:f32 = textureSampleCompare %t, %s, %coords, %dref
+ ret %6
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%t:texture_depth_2d, %s:sampler, %coords:vec2<f32>, %dref:f32):f32 -> %b1 {
+ %b1 = block {
+ %6:spirv.sampled_image = spirv.sampled_image %t, %s
+ %7:f32 = spirv.image_sample_dref_implicit_lod %6, %coords, %dref, 0u
+ ret %7
+ }
+}
+)";
+
+ Run<BuiltinPolyfillSpirv>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_BuiltinPolyfillSpirvTest, TextureSampleCompare_2D_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* dref = b.FunctionParam("dref", ty.f32());
+ auto* func = b.Function("foo", ty.f32());
+ func->SetParams({t, s, coords, dref});
+
+ b.With(func->Block(), [&] {
+ auto* result = b.Call(
+ ty.f32(), builtin::Function::kTextureSampleCompare, t, s, coords, dref,
+ 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>, %dref:f32):f32 -> %b1 {
+ %b1 = block {
+ %6:f32 = textureSampleCompare %t, %s, %coords, %dref, vec2<i32>(1i)
+ ret %6
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%t:texture_depth_2d, %s:sampler, %coords:vec2<f32>, %dref:f32):f32 -> %b1 {
+ %b1 = block {
+ %6:spirv.sampled_image = spirv.sampled_image %t, %s
+ %7:f32 = spirv.image_sample_dref_implicit_lod %6, %coords, %dref, 8u, vec2<i32>(1i)
+ ret %7
+ }
+}
+)";
+
+ Run<BuiltinPolyfillSpirv>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_BuiltinPolyfillSpirvTest, TextureSampleCompare_2DArray_Offset) {
+ 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.i32());
+ auto* bias = b.FunctionParam("bias", ty.f32());
+ auto* func = b.Function("foo", ty.f32());
+ func->SetParams({t, s, coords, array_idx, bias});
+
+ b.With(func->Block(), [&] {
+ auto* result = b.Call(
+ ty.f32(), builtin::Function::kTextureSampleCompare, t, s, coords, array_idx, bias,
+ 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_array, %s:sampler, %coords:vec2<f32>, %array_idx:i32, %bias:f32):f32 -> %b1 {
+ %b1 = block {
+ %7:f32 = textureSampleCompare %t, %s, %coords, %array_idx, %bias, vec2<i32>(1i)
+ ret %7
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%t:texture_depth_2d_array, %s:sampler, %coords:vec2<f32>, %array_idx:i32, %bias:f32):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:f32 = spirv.image_sample_dref_implicit_lod %7, %9, %bias, 8u, vec2<i32>(1i)
+ ret %10
+ }
+}
+)";
+
+ Run<BuiltinPolyfillSpirv>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_BuiltinPolyfillSpirvTest, TextureSampleCompareLevel_2D) {
+ 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* dref = b.FunctionParam("dref", ty.f32());
+ auto* func = b.Function("foo", ty.f32());
+ func->SetParams({t, s, coords, dref});
+
+ b.With(func->Block(), [&] {
+ auto* result =
+ b.Call(ty.f32(), builtin::Function::kTextureSampleCompareLevel, t, s, coords, dref);
+ b.Return(func, result);
+ });
+
+ auto* src = R"(
+%foo = func(%t:texture_depth_2d, %s:sampler, %coords:vec2<f32>, %dref:f32):f32 -> %b1 {
+ %b1 = block {
+ %6:f32 = textureSampleCompareLevel %t, %s, %coords, %dref
+ ret %6
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%t:texture_depth_2d, %s:sampler, %coords:vec2<f32>, %dref:f32):f32 -> %b1 {
+ %b1 = block {
+ %6:spirv.sampled_image = spirv.sampled_image %t, %s
+ %7:f32 = spirv.image_sample_dref_implicit_lod %6, %coords, %dref, 2u, 0.0f
+ ret %7
+ }
+}
+)";
+
+ Run<BuiltinPolyfillSpirv>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_BuiltinPolyfillSpirvTest, TextureSampleCompareLevel_2D_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* dref = b.FunctionParam("dref", ty.f32());
+ auto* func = b.Function("foo", ty.f32());
+ func->SetParams({t, s, coords, dref});
+
+ b.With(func->Block(), [&] {
+ auto* result = b.Call(
+ ty.f32(), builtin::Function::kTextureSampleCompareLevel, t, s, coords, dref,
+ 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>, %dref:f32):f32 -> %b1 {
+ %b1 = block {
+ %6:f32 = textureSampleCompareLevel %t, %s, %coords, %dref, vec2<i32>(1i)
+ ret %6
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%t:texture_depth_2d, %s:sampler, %coords:vec2<f32>, %dref:f32):f32 -> %b1 {
+ %b1 = block {
+ %6:spirv.sampled_image = spirv.sampled_image %t, %s
+ %7:f32 = spirv.image_sample_dref_implicit_lod %6, %coords, %dref, 10u, 0.0f, vec2<i32>(1i)
+ ret %7
+ }
+}
+)";
+
+ Run<BuiltinPolyfillSpirv>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_BuiltinPolyfillSpirvTest, TextureSampleCompareLevel_2DArray_Offset) {
+ 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.i32());
+ auto* bias = b.FunctionParam("bias", ty.f32());
+ auto* func = b.Function("foo", ty.f32());
+ func->SetParams({t, s, coords, array_idx, bias});
+
+ b.With(func->Block(), [&] {
+ auto* result = b.Call(
+ ty.f32(), builtin::Function::kTextureSampleCompareLevel, t, s, coords, array_idx, bias,
+ 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_array, %s:sampler, %coords:vec2<f32>, %array_idx:i32, %bias:f32):f32 -> %b1 {
+ %b1 = block {
+ %7:f32 = textureSampleCompareLevel %t, %s, %coords, %array_idx, %bias, vec2<i32>(1i)
+ ret %7
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%t:texture_depth_2d_array, %s:sampler, %coords:vec2<f32>, %array_idx:i32, %bias:f32):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:f32 = spirv.image_sample_dref_implicit_lod %7, %9, %bias, 10u, 0.0f, vec2<i32>(1i)
+ ret %10
+ }
+}
+)";
+
+ Run<BuiltinPolyfillSpirv>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_BuiltinPolyfillSpirvTest, TextureSampleGrad_2D) {
+ auto* t =
+ b.FunctionParam("t", ty.Get<type::SampledTexture>(type::TextureDimension::k2d, ty.f32()));
+ auto* s = b.FunctionParam("s", ty.sampler());
+ auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
+ auto* ddx = b.FunctionParam("ddx", ty.vec2<f32>());
+ auto* ddy = b.FunctionParam("ddy", ty.vec2<f32>());
+ auto* func = b.Function("foo", ty.vec4<f32>());
+ func->SetParams({t, s, coords, ddx, ddy});
+
+ b.With(func->Block(), [&] {
+ auto* result =
+ b.Call(ty.f32(), builtin::Function::kTextureSampleBias, t, s, coords, ddx, ddy);
+ b.Return(func, result);
+ });
+
+ auto* src = R"(
+%foo = func(%t:texture_2d<f32>, %s:sampler, %coords:vec2<f32>, %ddx:vec2<f32>, %ddy:vec2<f32>):vec4<f32> -> %b1 {
+ %b1 = block {
+ %7:f32 = textureSampleBias %t, %s, %coords, %ddx, %ddy
+ ret %7
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%t:texture_2d<f32>, %s:sampler, %coords:vec2<f32>, %ddx:vec2<f32>, %ddy:vec2<f32>):vec4<f32> -> %b1 {
+ %b1 = block {
+ %7:spirv.sampled_image = spirv.sampled_image %t, %s
+ %8:vec4<f32> = spirv.image_sample_implicit_lod %7, %coords, 9u, %ddx, %ddy
+ ret %8
+ }
+}
+)";
+
+ Run<BuiltinPolyfillSpirv>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_BuiltinPolyfillSpirvTest, TextureSampleGrad_2D_Offset) {
+ auto* t =
+ b.FunctionParam("t", ty.Get<type::SampledTexture>(type::TextureDimension::k2d, ty.f32()));
+ auto* s = b.FunctionParam("s", ty.sampler());
+ auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
+ auto* ddx = b.FunctionParam("ddx", ty.vec2<f32>());
+ auto* ddy = b.FunctionParam("ddy", ty.vec2<f32>());
+ auto* func = b.Function("foo", ty.vec4<f32>());
+ func->SetParams({t, s, coords, ddx, ddy});
+
+ b.With(func->Block(), [&] {
+ auto* result = b.Call(
+ ty.vec4<f32>(), builtin::Function::kTextureSampleBias, t, s, coords, ddx, ddy,
+ 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, %coords:vec2<f32>, %ddx:vec2<f32>, %ddy:vec2<f32>):vec4<f32> -> %b1 {
+ %b1 = block {
+ %7:vec4<f32> = textureSampleBias %t, %s, %coords, %ddx, %ddy, vec2<i32>(1i)
+ ret %7
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%t:texture_2d<f32>, %s:sampler, %coords:vec2<f32>, %ddx:vec2<f32>, %ddy:vec2<f32>):vec4<f32> -> %b1 {
+ %b1 = block {
+ %7:spirv.sampled_image = spirv.sampled_image %t, %s
+ %8:vec4<f32> = spirv.image_sample_implicit_lod %7, %coords, 9u, %ddx, %ddy
+ ret %8
+ }
+}
+)";
+
+ Run<BuiltinPolyfillSpirv>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_BuiltinPolyfillSpirvTest, TextureSampleGrad_2DArray_Offset) {
+ auto* t = b.FunctionParam(
+ "t", ty.Get<type::SampledTexture>(type::TextureDimension::k2dArray, ty.f32()));
+ auto* s = b.FunctionParam("s", ty.sampler());
+ auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
+ auto* array_idx = b.FunctionParam("array_idx", ty.i32());
+ auto* ddx = b.FunctionParam("ddx", ty.vec2<f32>());
+ auto* ddy = b.FunctionParam("ddy", ty.vec2<f32>());
+ auto* func = b.Function("foo", ty.vec4<f32>());
+ func->SetParams({t, s, coords, array_idx, ddx, ddy});
+
+ b.With(func->Block(), [&] {
+ auto* result = b.Call(
+ ty.vec4<f32>(), builtin::Function::kTextureSampleBias, t, s, coords, array_idx, ddx,
+ ddy,
+ 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, %coords:vec2<f32>, %array_idx:i32, %ddx:vec2<f32>, %ddy:vec2<f32>):vec4<f32> -> %b1 {
+ %b1 = block {
+ %8:vec4<f32> = textureSampleBias %t, %s, %coords, %array_idx, %ddx, %ddy, vec2<i32>(1i)
+ ret %8
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%t:texture_2d_array<f32>, %s:sampler, %coords:vec2<f32>, %array_idx:i32, %ddx:vec2<f32>, %ddy:vec2<f32>):vec4<f32> -> %b1 {
+ %b1 = block {
+ %8:spirv.sampled_image = spirv.sampled_image %t, %s
+ %9:f32 = convert %array_idx
+ %10:vec3<f32> = construct %coords, %9
+ %11:vec4<f32> = spirv.image_sample_implicit_lod %8, %10, 9u, %ddx, %ddy
+ ret %11
+ }
+}
+)";
+
+ Run<BuiltinPolyfillSpirv>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_BuiltinPolyfillSpirvTest, TextureSampleLevel_2D) {
+ auto* t =
+ b.FunctionParam("t", ty.Get<type::SampledTexture>(type::TextureDimension::k2d, ty.f32()));
+ auto* s = b.FunctionParam("s", ty.sampler());
+ auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
+ auto* lod = b.FunctionParam("lod", ty.f32());
+ auto* func = b.Function("foo", ty.vec4<f32>());
+ func->SetParams({t, s, coords, lod});
+
+ b.With(func->Block(), [&] {
+ auto* result = b.Call(ty.f32(), builtin::Function::kTextureSampleLevel, t, s, coords, lod);
+ b.Return(func, result);
+ });
+
+ auto* src = R"(
+%foo = func(%t:texture_2d<f32>, %s:sampler, %coords:vec2<f32>, %lod:f32):vec4<f32> -> %b1 {
+ %b1 = block {
+ %6:f32 = textureSampleLevel %t, %s, %coords, %lod
+ ret %6
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%t:texture_2d<f32>, %s:sampler, %coords:vec2<f32>, %lod:f32):vec4<f32> -> %b1 {
+ %b1 = block {
+ %6:spirv.sampled_image = spirv.sampled_image %t, %s
+ %7:vec4<f32> = spirv.image_sample_explicit_lod %6, %coords, 2u, %lod
+ ret %7
+ }
+}
+)";
+
+ Run<BuiltinPolyfillSpirv>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_BuiltinPolyfillSpirvTest, TextureSampleLevel_2D_Offset) {
+ auto* t =
+ b.FunctionParam("t", ty.Get<type::SampledTexture>(type::TextureDimension::k2d, ty.f32()));
+ auto* s = b.FunctionParam("s", ty.sampler());
+ auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
+ auto* lod = b.FunctionParam("lod", ty.f32());
+ auto* func = b.Function("foo", ty.vec4<f32>());
+ func->SetParams({t, s, coords, lod});
+
+ b.With(func->Block(), [&] {
+ auto* result = b.Call(
+ ty.vec4<f32>(), builtin::Function::kTextureSampleLevel, t, s, coords, lod,
+ 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, %coords:vec2<f32>, %lod:f32):vec4<f32> -> %b1 {
+ %b1 = block {
+ %6:vec4<f32> = textureSampleLevel %t, %s, %coords, %lod, vec2<i32>(1i)
+ ret %6
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%t:texture_2d<f32>, %s:sampler, %coords:vec2<f32>, %lod:f32):vec4<f32> -> %b1 {
+ %b1 = block {
+ %6:spirv.sampled_image = spirv.sampled_image %t, %s
+ %7:vec4<f32> = spirv.image_sample_explicit_lod %6, %coords, 10u, %lod, vec2<i32>(1i)
+ ret %7
+ }
+}
+)";
+
+ Run<BuiltinPolyfillSpirv>();
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(IR_BuiltinPolyfillSpirvTest, TextureSampleLevel_2DArray_Offset) {
+ auto* t = b.FunctionParam(
+ "t", ty.Get<type::SampledTexture>(type::TextureDimension::k2dArray, ty.f32()));
+ auto* s = b.FunctionParam("s", ty.sampler());
+ auto* coords = b.FunctionParam("coords", ty.vec2<f32>());
+ auto* array_idx = b.FunctionParam("array_idx", ty.i32());
+ auto* lod = b.FunctionParam("lod", ty.f32());
+ auto* func = b.Function("foo", ty.vec4<f32>());
+ func->SetParams({t, s, coords, array_idx, lod});
+
+ b.With(func->Block(), [&] {
+ auto* result = b.Call(
+ ty.vec4<f32>(), builtin::Function::kTextureSampleLevel, t, s, coords, array_idx, lod,
+ 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, %coords:vec2<f32>, %array_idx:i32, %lod:f32):vec4<f32> -> %b1 {
+ %b1 = block {
+ %7:vec4<f32> = textureSampleLevel %t, %s, %coords, %array_idx, %lod, vec2<i32>(1i)
+ ret %7
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%t:texture_2d_array<f32>, %s:sampler, %coords:vec2<f32>, %array_idx:i32, %lod: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_sample_explicit_lod %7, %9, 10u, %lod, vec2<i32>(1i)
+ ret %10
+ }
+}
+)";
+
+ Run<BuiltinPolyfillSpirv>();
+
+ EXPECT_EQ(expect, str());
+}
+
} // namespace
} // namespace tint::ir::transform
diff --git a/src/tint/writer/spirv/ir/generator_impl_ir.cc b/src/tint/writer/spirv/ir/generator_impl_ir.cc
index 024aa59..479cbd9 100644
--- a/src/tint/writer/spirv/ir/generator_impl_ir.cc
+++ b/src/tint/writer/spirv/ir/generator_impl_ir.cc
@@ -147,6 +147,15 @@
return s;
},
+ // Dedup a SampledImage if its underlying image will be deduped.
+ [&](const ir::transform::BuiltinPolyfillSpirv::SampledImage* si) -> const type::Type* {
+ auto* img = DedupType(si->Image(), types);
+ if (img != si->Image()) {
+ return types.Get<ir::transform::BuiltinPolyfillSpirv::SampledImage>(img);
+ }
+ return si;
+ },
+
[&](Default) { return ty; });
}
@@ -236,6 +245,11 @@
}
uint32_t GeneratorImplIr::Constant(ir::Constant* constant) {
+ // If it is a literal operand, just return the value.
+ if (auto* literal = constant->As<ir::transform::BuiltinPolyfillSpirv::LiteralOperand>()) {
+ return literal->Value()->ValueAs<uint32_t>();
+ }
+
auto id = Constant(constant->Value());
// Set the name for the SPIR-V result ID if provided in the module.
@@ -375,6 +389,9 @@
[&](const type::Struct* str) { EmitStructType(id, str, addrspace); },
[&](const type::Texture* tex) { EmitTextureType(id, tex); },
[&](const type::Sampler*) { module_.PushType(spv::Op::OpTypeSampler, {id}); },
+ [&](const ir::transform::BuiltinPolyfillSpirv::SampledImage* s) {
+ module_.PushType(spv::Op::OpTypeSampledImage, {id, Type(s->Image())});
+ },
[&](Default) {
TINT_ICE(Writer, diagnostics_) << "unhandled type: " << ty->FriendlyName();
});
@@ -1403,6 +1420,21 @@
case ir::IntrinsicCall::Kind::kSpirvSelect:
op = spv::Op::OpSelect;
break;
+ case ir::IntrinsicCall::Kind::kSpirvSampledImage:
+ op = spv::Op::OpSampledImage;
+ break;
+ case ir::IntrinsicCall::Kind::kSpirvImageSampleImplicitLod:
+ op = spv::Op::OpImageSampleImplicitLod;
+ break;
+ case ir::IntrinsicCall::Kind::kSpirvImageSampleExplicitLod:
+ op = spv::Op::OpImageSampleExplicitLod;
+ break;
+ case ir::IntrinsicCall::Kind::kSpirvImageSampleDrefImplicitLod:
+ op = spv::Op::OpImageSampleDrefImplicitLod;
+ break;
+ case ir::IntrinsicCall::Kind::kSpirvImageSampleDrefExplicitLod:
+ op = spv::Op::OpImageSampleDrefExplicitLod;
+ break;
}
OperandList operands = {Type(call->Result()->Type()), id};
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
new file mode 100644
index 0000000..90711e7
--- /dev/null
+++ b/src/tint/writer/spirv/ir/generator_impl_ir_texture_builtin_test.cc
@@ -0,0 +1,597 @@
+// Copyright 2023 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/writer/spirv/ir/test_helper_ir.h"
+
+#include "src/tint/builtin/function.h"
+
+using namespace tint::number_suffixes; // NOLINT
+
+namespace tint::writer::spirv {
+namespace {
+
+/// An additional argument to a texture builtin.
+struct Arg {
+ /// The argument name.
+ const char* name;
+ /// The vector width of the argument (1 means scalar).
+ uint32_t width;
+ /// The element type of the argument.
+ TestElementType type;
+};
+
+/// A parameterized texture builtin function test case.
+struct TextureBuiltinTestCase {
+ /// The builtin function.
+ enum builtin::Function function;
+ /// The builtin function arguments.
+ utils::Vector<Arg, 4> optional_args;
+ /// The expected SPIR-V instruction string for the texture call.
+ const char* texture_call;
+};
+
+std::string PrintCase(testing::TestParamInfo<TextureBuiltinTestCase> cc) {
+ utils::StringStream ss;
+ ss << cc.param.function;
+ for (const auto& arg : cc.param.optional_args) {
+ ss << "_" << arg.name;
+ }
+ return ss.str();
+}
+
+class TextureBuiltinTest : public SpvGeneratorImplTestWithParam<TextureBuiltinTestCase> {
+ protected:
+ void Run(const type::Texture* texture_ty,
+ const type::Sampler* sampler_ty,
+ const type::Type* coord_ty,
+ const type::Type* return_ty) {
+ auto params = GetParam();
+
+ auto* t = b.FunctionParam("t", texture_ty);
+ auto* s = b.FunctionParam("s", sampler_ty);
+ auto* coord = b.FunctionParam("coords", coord_ty);
+ auto* func = b.Function("foo", return_ty);
+ func->SetParams({t, s, coord});
+
+ b.With(func->Block(), [&] {
+ utils::Vector<ir::Value*, 4> args = {t, s, coord};
+ uint32_t arg_value = 1;
+ for (const auto& arg : params.optional_args) {
+ auto* value = MakeScalarValue(arg.type, arg_value++);
+ if (arg.width > 1) {
+ value = b.Constant(mod.constant_values.Splat(ty.vec(value->Type(), arg.width),
+ value->Value(), arg.width));
+ }
+ args.Push(value);
+ mod.SetName(value, arg.name);
+ }
+ auto* result = b.Call(return_ty, params.function, std::move(args));
+ b.Return(func, result);
+ mod.SetName(result, "result");
+ });
+
+ ASSERT_TRUE(Generate()) << Error() << output_;
+ EXPECT_INST(params.texture_call);
+ }
+};
+
+using Texture1D = TextureBuiltinTest;
+TEST_P(Texture1D, Emit) {
+ Run(ty.Get<type::SampledTexture>(type::TextureDimension::k1d, ty.f32()),
+ ty.sampler(), // sampler type
+ ty.f32(), // coord type
+ ty.vec4<f32>() // return type
+ );
+ EXPECT_INST("%11 = OpSampledImage %12 %t %s");
+}
+INSTANTIATE_TEST_SUITE_P(SpvGeneratorImplTest,
+ Texture1D,
+ testing::Values(TextureBuiltinTestCase{
+ builtin::Function::kTextureSample,
+ {},
+ "OpImageSampleImplicitLod %v4float %11 %coords None",
+ }),
+ PrintCase);
+
+using Texture2D = TextureBuiltinTest;
+TEST_P(Texture2D, Emit) {
+ Run(ty.Get<type::SampledTexture>(type::TextureDimension::k2d, ty.f32()),
+ ty.sampler(), // sampler type
+ ty.vec2<f32>(), // coord type
+ ty.vec4<f32>() // return type
+ );
+ EXPECT_INST("%12 = OpSampledImage %13 %t %s");
+}
+INSTANTIATE_TEST_SUITE_P(
+ SpvGeneratorImplTest,
+ Texture2D,
+ testing::Values(
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSample,
+ {},
+ "OpImageSampleImplicitLod %v4float %12 %coords None",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSample,
+ {{"offset", 2, kI32}},
+ "OpImageSampleImplicitLod %v4float %12 %coords ConstOffset %offset",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleBias,
+ {{"bias", 1, kF32}},
+ "OpImageSampleImplicitLod %v4float %12 %coords Bias %bias",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleBias,
+ {{"bias", 1, kF32}, {"offset", 2, kI32}},
+ "OpImageSampleImplicitLod %v4float %12 %coords Bias|ConstOffset %bias %offset",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleGrad,
+ {{"ddx", 2, kF32}, {"ddy", 2, kF32}},
+ "OpImageSampleExplicitLod %v4float %12 %coords Grad %ddx %ddy",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleGrad,
+ {{"ddx", 2, kF32}, {"ddy", 2, kF32}, {"offset", 2, kI32}},
+ "OpImageSampleExplicitLod %v4float %12 %coords Grad|ConstOffset %ddx %ddy %offset",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleLevel,
+ {{"lod", 1, kF32}},
+ "OpImageSampleExplicitLod %v4float %12 %coords Lod %lod",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleLevel,
+ {{"lod", 1, kF32}, {"offset", 2, kI32}},
+ "OpImageSampleExplicitLod %v4float %12 %coords Lod|ConstOffset %lod %offset",
+ }),
+ PrintCase);
+
+using Texture2DArray = TextureBuiltinTest;
+TEST_P(Texture2DArray, Emit) {
+ Run(ty.Get<type::SampledTexture>(type::TextureDimension::k2dArray, ty.f32()),
+ ty.sampler(), // sampler type
+ ty.vec2<f32>(), // coord type
+ ty.vec4<f32>() // return type
+ );
+ EXPECT_INST("%12 = OpSampledImage %13 %t %s");
+ EXPECT_INST("%14 = OpConvertSToF %float %array_idx");
+ EXPECT_INST("%18 = OpCompositeConstruct %v3float %coords %14");
+}
+INSTANTIATE_TEST_SUITE_P(
+ SpvGeneratorImplTest,
+ Texture2DArray,
+ testing::Values(
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSample,
+ {{"array_idx", 1, kI32}},
+ "OpImageSampleImplicitLod %v4float %12 %18 None",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSample,
+ {{"array_idx", 1, kI32}, {"offset", 2, kI32}},
+ "OpImageSampleImplicitLod %v4float %12 %18 ConstOffset %offset",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleBias,
+ {{"array_idx", 1, kI32}, {"bias", 1, kF32}},
+ "OpImageSampleImplicitLod %v4float %12 %18 Bias %bias",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleBias,
+ {{"array_idx", 1, kI32}, {"bias", 1, kF32}, {"offset", 2, kI32}},
+ "OpImageSampleImplicitLod %v4float %12 %18 Bias|ConstOffset %bias %offset",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleGrad,
+ {{"array_idx", 1, kI32}, {"ddx", 2, kF32}, {"ddy", 2, kF32}},
+ "OpImageSampleExplicitLod %v4float %12 %18 Grad %ddx %ddy",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleGrad,
+ {{"array_idx", 1, kI32}, {"ddx", 2, kF32}, {"ddy", 2, kF32}, {"offset", 2, kI32}},
+ "OpImageSampleExplicitLod %v4float %12 %18 Grad|ConstOffset %ddx %ddy %offset",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleLevel,
+ {{"array_idx", 1, kI32}, {"lod", 1, kF32}},
+ "OpImageSampleExplicitLod %v4float %12 %18 Lod %lod",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleLevel,
+ {{"array_idx", 1, kI32}, {"lod", 1, kF32}, {"offset", 2, kI32}},
+ "OpImageSampleExplicitLod %v4float %12 %18 Lod|ConstOffset %lod %offset",
+ }),
+ PrintCase);
+
+using Texture3D = TextureBuiltinTest;
+TEST_P(Texture3D, Emit) {
+ Run(ty.Get<type::SampledTexture>(type::TextureDimension::k3d, ty.f32()),
+ ty.sampler(), // sampler type
+ ty.vec3<f32>(), // coord type
+ ty.vec4<f32>() // return type
+ );
+ EXPECT_INST("%12 = OpSampledImage %13 %t %s");
+}
+INSTANTIATE_TEST_SUITE_P(
+ SpvGeneratorImplTest,
+ Texture3D,
+ testing::Values(
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSample,
+ {},
+ "OpImageSampleImplicitLod %v4float %12 %coords None",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSample,
+ {{"offset", 3, kI32}},
+ "OpImageSampleImplicitLod %v4float %12 %coords ConstOffset %offset",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleBias,
+ {{"bias", 1, kF32}},
+ "OpImageSampleImplicitLod %v4float %12 %coords Bias %bias",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleBias,
+ {{"bias", 1, kF32}, {"offset", 3, kI32}},
+ "OpImageSampleImplicitLod %v4float %12 %coords Bias|ConstOffset %bias %offset",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleGrad,
+ {{"ddx", 3, kF32}, {"ddy", 3, kF32}},
+ "OpImageSampleExplicitLod %v4float %12 %coords Grad %ddx %ddy",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleGrad,
+ {{"ddx", 3, kF32}, {"ddy", 3, kF32}, {"offset", 3, kI32}},
+ "OpImageSampleExplicitLod %v4float %12 %coords Grad|ConstOffset %ddx %ddy %offset",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleLevel,
+ {{"lod", 1, kF32}},
+ "OpImageSampleExplicitLod %v4float %12 %coords Lod %lod",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleLevel,
+ {{"lod", 1, kF32}, {"offset", 3, kI32}},
+ "OpImageSampleExplicitLod %v4float %12 %coords Lod|ConstOffset %lod %offset",
+ }),
+ PrintCase);
+
+using TextureCube = TextureBuiltinTest;
+TEST_P(TextureCube, Emit) {
+ Run(ty.Get<type::SampledTexture>(type::TextureDimension::kCube, ty.f32()),
+ ty.sampler(), // sampler type
+ ty.vec3<f32>(), // coord type
+ ty.vec4<f32>() // return type
+ );
+ EXPECT_INST("%12 = OpSampledImage %13 %t %s");
+}
+INSTANTIATE_TEST_SUITE_P(SpvGeneratorImplTest,
+ TextureCube,
+ testing::Values(
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSample,
+ {},
+ "OpImageSampleImplicitLod %v4float %12 %coords None",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleBias,
+ {{"bias", 1, kF32}},
+ "OpImageSampleImplicitLod %v4float %12 %coords Bias %bias",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleGrad,
+ {{"ddx", 3, kF32}, {"ddy", 3, kF32}},
+ "OpImageSampleExplicitLod %v4float %12 %coords Grad %ddx %ddy",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleLevel,
+ {{"lod", 1, kF32}},
+ "OpImageSampleExplicitLod %v4float %12 %coords Lod %lod",
+ }),
+ PrintCase);
+
+using TextureCubeArray = TextureBuiltinTest;
+TEST_P(TextureCubeArray, Emit) {
+ Run(ty.Get<type::SampledTexture>(type::TextureDimension::kCubeArray, ty.f32()),
+ ty.sampler(), // sampler type
+ ty.vec3<f32>(), // coord type
+ ty.vec4<f32>() // return type
+ );
+ EXPECT_INST("%12 = OpSampledImage %13 %t %s");
+ EXPECT_INST("%14 = OpConvertSToF %float %array_idx");
+ EXPECT_INST("%17 = OpCompositeConstruct %v4float %coords %14");
+}
+INSTANTIATE_TEST_SUITE_P(SpvGeneratorImplTest,
+ TextureCubeArray,
+ testing::Values(
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSample,
+ {{"array_idx", 1, kI32}},
+ "OpImageSampleImplicitLod %v4float %12 %17 None",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleBias,
+ {{"array_idx", 1, kI32}, {"bias", 1, kF32}},
+ "OpImageSampleImplicitLod %v4float %12 %17 Bias %bias",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleGrad,
+ {{"array_idx", 1, kI32}, {"ddx", 3, kF32}, {"ddy", 3, kF32}},
+ "OpImageSampleExplicitLod %v4float %12 %17 Grad %ddx %ddy",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleLevel,
+ {{"array_idx", 1, kI32}, {"lod", 1, kF32}},
+ "OpImageSampleExplicitLod %v4float %12 %17 Lod %lod",
+ }),
+ PrintCase);
+
+using TextureDepth2D = TextureBuiltinTest;
+TEST_P(TextureDepth2D, Emit) {
+ Run(ty.Get<type::DepthTexture>(type::TextureDimension::k2d),
+ ty.sampler(), // sampler type
+ ty.vec2<f32>(), // coord type
+ ty.f32() // return type
+ );
+ EXPECT_INST("%11 = OpSampledImage %12 %t %s");
+ EXPECT_INST("%result = OpCompositeExtract %float");
+}
+INSTANTIATE_TEST_SUITE_P(
+ SpvGeneratorImplTest,
+ TextureDepth2D,
+ testing::Values(
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSample,
+ {},
+ "OpImageSampleImplicitLod %v4float %11 %coords None",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSample,
+ {{"offset", 2, kI32}},
+ "OpImageSampleImplicitLod %v4float %11 %coords ConstOffset %offset",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleLevel,
+ {{"lod", 1, kI32}},
+ "OpImageSampleExplicitLod %v4float %11 %coords Lod %13",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleLevel,
+ {{"lod", 1, kI32}, {"offset", 2, kI32}},
+ "OpImageSampleExplicitLod %v4float %11 %coords Lod|ConstOffset %13 %offset",
+ }),
+ PrintCase);
+
+using TextureDepth2D_DepthComparison = TextureBuiltinTest;
+TEST_P(TextureDepth2D_DepthComparison, Emit) {
+ Run(ty.Get<type::DepthTexture>(type::TextureDimension::k2d),
+ ty.comparison_sampler(), // sampler type
+ ty.vec2<f32>(), // coord type
+ ty.f32() // return type
+ );
+ EXPECT_INST("%11 = OpSampledImage %12 %t %s");
+}
+INSTANTIATE_TEST_SUITE_P(
+ SpvGeneratorImplTest,
+ TextureDepth2D_DepthComparison,
+ testing::Values(
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleCompare,
+ {{"depth", 1, kF32}},
+ "OpImageSampleDrefImplicitLod %float %11 %coords %depth",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleCompare,
+ {{"depth", 1, kF32}, {"offset", 2, kI32}},
+ "OpImageSampleDrefImplicitLod %float %11 %coords %depth ConstOffset %offset",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleCompareLevel,
+ {{"depth_l0", 1, kF32}},
+ "OpImageSampleDrefExplicitLod %float %11 %coords %depth_l0 Lod %float_0",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleCompareLevel,
+ {{"depth_l0", 1, kF32}, {"offset", 2, kI32}},
+ "OpImageSampleDrefExplicitLod %float %11 %coords %depth_l0 Lod|ConstOffset %float_0 "
+ "%offset",
+ }),
+ PrintCase);
+
+using TextureDepth2DArray = TextureBuiltinTest;
+TEST_P(TextureDepth2DArray, Emit) {
+ Run(ty.Get<type::DepthTexture>(type::TextureDimension::k2dArray),
+ ty.sampler(), // sampler type
+ ty.vec2<f32>(), // coord type
+ ty.f32() // return type
+ );
+ EXPECT_INST("%11 = OpSampledImage %12 %t %s");
+ EXPECT_INST("%13 = OpConvertSToF %float %array_idx");
+ EXPECT_INST("%17 = OpCompositeConstruct %v3float %coords %13");
+ EXPECT_INST("%result = OpCompositeExtract %float");
+}
+INSTANTIATE_TEST_SUITE_P(
+ SpvGeneratorImplTest,
+ TextureDepth2DArray,
+ testing::Values(
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSample,
+ {{"array_idx", 1, kI32}},
+ "OpImageSampleImplicitLod %v4float %11 %17 None",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSample,
+ {{"array_idx", 1, kI32}, {"offset", 2, kI32}},
+ "OpImageSampleImplicitLod %v4float %11 %17 ConstOffset %offset",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleLevel,
+ {{"array_idx", 1, kI32}, {"lod", 1, kI32}},
+ "OpImageSampleExplicitLod %v4float %11 %17 Lod %18",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleLevel,
+ {{"array_idx", 1, kI32}, {"lod", 1, kI32}, {"offset", 2, kI32}},
+ "OpImageSampleExplicitLod %v4float %11 %17 Lod|ConstOffset %18 %offset",
+ }),
+ PrintCase);
+
+using TextureDepth2DArray_DepthComparison = TextureBuiltinTest;
+TEST_P(TextureDepth2DArray_DepthComparison, Emit) {
+ Run(ty.Get<type::DepthTexture>(type::TextureDimension::k2dArray),
+ ty.comparison_sampler(), // sampler type
+ ty.vec2<f32>(), // coord type
+ ty.f32() // return type
+ );
+ EXPECT_INST("%11 = OpSampledImage %12 %t %s");
+ EXPECT_INST("%13 = OpConvertSToF %float %array_idx");
+ EXPECT_INST("%17 = OpCompositeConstruct %v3float %coords %13");
+}
+INSTANTIATE_TEST_SUITE_P(
+ SpvGeneratorImplTest,
+ TextureDepth2DArray_DepthComparison,
+ testing::Values(
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleCompare,
+ {{"array_idx", 1, kI32}, {"depth", 1, kF32}},
+ "OpImageSampleDrefImplicitLod %float %11 %17 %depth",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleCompare,
+ {{"array_idx", 1, kI32}, {"depth", 1, kF32}, {"offset", 2, kI32}},
+ "OpImageSampleDrefImplicitLod %float %11 %17 %depth ConstOffset %offset",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleCompareLevel,
+ {{"array_idx", 1, kI32}, {"depth_l0", 1, kF32}},
+ "OpImageSampleDrefExplicitLod %float %11 %17 %depth_l0 Lod %float_0",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleCompareLevel,
+ {{"array_idx", 1, kI32}, {"depth_l0", 1, kF32}, {"offset", 2, kI32}},
+ "OpImageSampleDrefExplicitLod %float %11 %17 %depth_l0 Lod|ConstOffset %float_0 "
+ "%offset",
+ }),
+ PrintCase);
+
+using TextureDepthCube = TextureBuiltinTest;
+TEST_P(TextureDepthCube, Emit) {
+ Run(ty.Get<type::DepthTexture>(type::TextureDimension::kCube),
+ ty.sampler(), // sampler type
+ ty.vec3<f32>(), // coord type
+ ty.f32() // return type
+ );
+ EXPECT_INST("%11 = OpSampledImage %12 %t %s");
+ EXPECT_INST("%result = OpCompositeExtract %float");
+}
+INSTANTIATE_TEST_SUITE_P(SpvGeneratorImplTest,
+ TextureDepthCube,
+ testing::Values(
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSample,
+ {},
+ "OpImageSampleImplicitLod %v4float %11 %coords None",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleLevel,
+ {{"lod", 1, kI32}},
+ "OpImageSampleExplicitLod %v4float %11 %coords Lod %13",
+ }),
+ PrintCase);
+
+using TextureDepthCube_DepthComparison = TextureBuiltinTest;
+TEST_P(TextureDepthCube_DepthComparison, Emit) {
+ Run(ty.Get<type::DepthTexture>(type::TextureDimension::kCube),
+ ty.comparison_sampler(), // sampler typea
+ ty.vec3<f32>(), // coord type
+ ty.f32() // return type
+ );
+ EXPECT_INST("%11 = OpSampledImage %12 %t %s");
+}
+INSTANTIATE_TEST_SUITE_P(
+ SpvGeneratorImplTest,
+ TextureDepthCube_DepthComparison,
+ testing::Values(
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleCompare,
+ {{"depth", 1, kF32}},
+ "OpImageSampleDrefImplicitLod %float %11 %coords %depth",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleCompareLevel,
+ {{"depth_l0", 1, kF32}},
+ "OpImageSampleDrefExplicitLod %float %11 %coords %depth_l0 Lod %float_0",
+ }),
+ PrintCase);
+
+using TextureDepthCubeArray = TextureBuiltinTest;
+TEST_P(TextureDepthCubeArray, Emit) {
+ Run(ty.Get<type::DepthTexture>(type::TextureDimension::kCubeArray),
+ ty.sampler(), // sampler type
+ ty.vec3<f32>(), // coord type
+ ty.f32() // return type
+ );
+ EXPECT_INST("%11 = OpSampledImage %12 %t %s");
+ EXPECT_INST("%13 = OpConvertSToF %float %array_idx");
+ EXPECT_INST("%17 = OpCompositeConstruct %v4float %coords %13");
+ EXPECT_INST("%result = OpCompositeExtract %float");
+}
+INSTANTIATE_TEST_SUITE_P(SpvGeneratorImplTest,
+ TextureDepthCubeArray,
+ testing::Values(
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSample,
+ {{"array_idx", 1, kI32}},
+ "OpImageSampleImplicitLod %v4float %11 %17 None",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleLevel,
+ {{"array_idx", 1, kI32}, {"lod", 1, kI32}},
+ "OpImageSampleExplicitLod %v4float %11 %17 Lod %18",
+ }),
+ PrintCase);
+
+using TextureDepthCubeArray_DepthComparison = TextureBuiltinTest;
+TEST_P(TextureDepthCubeArray_DepthComparison, Emit) {
+ Run(ty.Get<type::DepthTexture>(type::TextureDimension::kCubeArray),
+ ty.comparison_sampler(), // sampler type
+ ty.vec3<f32>(), // coord type
+ ty.f32() // return type
+ );
+ EXPECT_INST("%11 = OpSampledImage %12 %t %s");
+ EXPECT_INST("%13 = OpConvertSToF %float %array_idx");
+ EXPECT_INST("%17 = OpCompositeConstruct %v4float %coords %13");
+}
+INSTANTIATE_TEST_SUITE_P(
+ SpvGeneratorImplTest,
+ TextureDepthCubeArray_DepthComparison,
+ testing::Values(
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleCompare,
+ {{"array_idx", 1, kI32}, {"depth", 1, kF32}},
+ "OpImageSampleDrefImplicitLod %float %11 %17 %depth",
+ },
+ TextureBuiltinTestCase{
+ builtin::Function::kTextureSampleCompareLevel,
+ {{"array_idx", 1, kI32}, {"depth_l0", 1, kF32}},
+ "OpImageSampleDrefExplicitLod %float %11 %17 %depth_l0 Lod %float_0",
+ }),
+ PrintCase);
+
+} // namespace
+} // namespace tint::writer::spirv
diff --git a/src/tint/writer/spirv/ir/test_helper_ir.h b/src/tint/writer/spirv/ir/test_helper_ir.h
index 3d86819..54af5ad 100644
--- a/src/tint/writer/spirv/ir/test_helper_ir.h
+++ b/src/tint/writer/spirv/ir/test_helper_ir.h
@@ -176,19 +176,20 @@
/// Helper to make a scalar value with the scalar type `type`.
/// @param type the element type
+ /// @param value the optional value to use
/// @returns the scalar value
- ir::Value* MakeScalarValue(TestElementType type) {
+ ir::Constant* MakeScalarValue(TestElementType type, uint32_t value = 1) {
switch (type) {
case kBool:
return b.Constant(true);
case kI32:
- return b.Constant(i32(1));
+ return b.Constant(i32(value));
case kU32:
- return b.Constant(u32(1));
+ return b.Constant(u32(value));
case kF32:
- return b.Constant(f32(1));
+ return b.Constant(f32(value));
case kF16:
- return b.Constant(f16(1));
+ return b.Constant(f16(value));
}
return nullptr;
}
@@ -196,7 +197,7 @@
/// Helper to make a vector value with an element type of `type`.
/// @param type the element type
/// @returns the vector value
- ir::Value* MakeVectorValue(TestElementType type) {
+ ir::Constant* MakeVectorValue(TestElementType type) {
switch (type) {
case kBool:
return b.Constant(mod.constant_values.Composite(