[hlsl] Add `textureDimensions` support.
This Cl adds support to the HLSL IR backend for `textureDimensions`
Bug: 42251045
Change-Id: I579cdc337d0eb2c81c07fa5fa2d175ed47c4506f
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/197176
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: James Price <jrprice@google.com>
diff --git a/src/tint/lang/hlsl/writer/builtin_test.cc b/src/tint/lang/hlsl/writer/builtin_test.cc
index 12edc94..6cac608 100644
--- a/src/tint/lang/hlsl/writer/builtin_test.cc
+++ b/src/tint/lang/hlsl/writer/builtin_test.cc
@@ -30,6 +30,7 @@
#include "src/tint/lang/core/number.h"
#include "src/tint/lang/core/type/depth_multisampled_texture.h"
#include "src/tint/lang/core/type/sampled_texture.h"
+#include "src/tint/lang/core/type/texture_dimension.h"
#include "src/tint/lang/hlsl/writer/helper_test.h"
#include "gtest/gtest.h"
@@ -841,6 +842,116 @@
)");
}
+TEST_F(HlslWriterTest, BuiltinTextureDimension1D) {
+ auto* t = b.FunctionParam(
+ "t", ty.Get<core::type::SampledTexture>(core::type::TextureDimension::k1d, ty.f32()));
+
+ auto* func = b.Function("foo", ty.void_());
+ func->SetParams({t});
+
+ b.Append(func->Block(), [&] {
+ b.Let("d", b.Call(ty.u32(), core::BuiltinFn::kTextureDimensions, t));
+ b.Return(func);
+ });
+
+ ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
+ EXPECT_EQ(output_.hlsl, R"(
+void foo(Texture1D<float4> t) {
+ uint v = 0u;
+ t.GetDimensions(v);
+ uint d = v;
+}
+
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+}
+
+)");
+}
+
+TEST_F(HlslWriterTest, BuiltinTextureDimension2D) {
+ auto* t = b.FunctionParam(
+ "t", ty.Get<core::type::SampledTexture>(core::type::TextureDimension::k2d, ty.f32()));
+
+ auto* func = b.Function("foo", ty.void_());
+ func->SetParams({t});
+
+ b.Append(func->Block(), [&] {
+ b.Let("d", b.Call(ty.vec2<u32>(), core::BuiltinFn::kTextureDimensions, t));
+ b.Return(func);
+ });
+
+ ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
+ EXPECT_EQ(output_.hlsl, R"(
+void foo(Texture2D<float4> t) {
+ uint2 v = (0u).xx;
+ t.GetDimensions(v[0u], v[1u]);
+ uint2 d = v;
+}
+
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+}
+
+)");
+}
+
+TEST_F(HlslWriterTest, BuiltinTextureDimension2dLOD) {
+ auto* t = b.FunctionParam(
+ "t", ty.Get<core::type::SampledTexture>(core::type::TextureDimension::k2d, ty.f32()));
+
+ auto* func = b.Function("foo", ty.void_());
+ func->SetParams({t});
+
+ b.Append(func->Block(), [&] {
+ b.Let("d", b.Call(ty.vec2<u32>(), core::BuiltinFn::kTextureDimensions, t, 1_i));
+ b.Return(func);
+ });
+
+ ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
+ EXPECT_EQ(output_.hlsl, R"(
+void foo(Texture2D<float4> t) {
+ uint3 v = (0u).xxx;
+ t.GetDimensions(0u, v[0u], v[1u], v[2u]);
+ uint3 v_1 = (0u).xxx;
+ t.GetDimensions(uint(min(uint(1), (v.z - 1u))), v_1[0u], v_1[1u], v_1[2u]);
+ uint2 d = v_1.xy;
+}
+
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+}
+
+)");
+}
+
+TEST_F(HlslWriterTest, BuiltinTextureDimension3D) {
+ auto* t = b.FunctionParam(
+ "t", ty.Get<core::type::SampledTexture>(core::type::TextureDimension::k3d, ty.f32()));
+
+ auto* func = b.Function("foo", ty.void_());
+ func->SetParams({t});
+
+ b.Append(func->Block(), [&] {
+ b.Let("d", b.Call(ty.vec3<u32>(), core::BuiltinFn::kTextureDimensions, t));
+ b.Return(func);
+ });
+
+ ASSERT_TRUE(Generate()) << err_ << output_.hlsl;
+ EXPECT_EQ(output_.hlsl, R"(
+void foo(Texture3D<float4> t) {
+ uint3 v = (0u).xxx;
+ t.GetDimensions(v[0u], v[1u], v[2u]);
+ uint3 d = v;
+}
+
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+}
+
+)");
+}
+
TEST_F(HlslWriterTest, BuiltinTextureLayers2dArray) {
auto* t = b.FunctionParam(
"t", ty.Get<core::type::SampledTexture>(core::type::TextureDimension::k2dArray, ty.f32()));
diff --git a/src/tint/lang/hlsl/writer/raise/builtin_polyfill.cc b/src/tint/lang/hlsl/writer/raise/builtin_polyfill.cc
index 2fc9499..9dc5ea8 100644
--- a/src/tint/lang/hlsl/writer/raise/builtin_polyfill.cc
+++ b/src/tint/lang/hlsl/writer/raise/builtin_polyfill.cc
@@ -38,6 +38,8 @@
#include "src/tint/lang/core/type/manager.h"
#include "src/tint/lang/core/type/multisampled_texture.h"
#include "src/tint/lang/core/type/texture.h"
+#include "src/tint/lang/core/type/u32.h"
+#include "src/tint/lang/core/type/vector.h"
#include "src/tint/lang/hlsl/builtin_fn.h"
#include "src/tint/lang/hlsl/ir/builtin_call.h"
#include "src/tint/lang/hlsl/ir/member_builtin_call.h"
@@ -82,6 +84,7 @@
switch (call->Func()) {
case core::BuiltinFn::kSelect:
case core::BuiltinFn::kSign:
+ case core::BuiltinFn::kTextureDimensions:
case core::BuiltinFn::kTextureNumLayers:
case core::BuiltinFn::kTextureNumLevels:
case core::BuiltinFn::kTextureNumSamples:
@@ -121,6 +124,9 @@
case core::BuiltinFn::kSign:
Sign(call);
break;
+ case core::BuiltinFn::kTextureDimensions:
+ TextureDimensions(call);
+ break;
case core::BuiltinFn::kTextureNumLayers:
TextureNumLayers(call);
break;
@@ -465,6 +471,91 @@
call->Destroy();
}
+ void TextureDimensions(core::ir::CoreBuiltinCall* call) {
+ auto* tex = call->Args()[0];
+ auto* tex_type = tex->Type()->As<core::type::Texture>();
+ bool has_level = call->Args().Length() > 1;
+ bool is_ms =
+ tex_type
+ ->IsAnyOf<core::type::MultisampledTexture, core::type::DepthMultisampledTexture>();
+
+ Vector<uint32_t, 2> swizzle{};
+ uint32_t query_size = 0;
+ switch (tex_type->dim()) {
+ case core::type::TextureDimension::kNone:
+ TINT_ICE() << "texture dimension is kNone";
+ case core::type::TextureDimension::k1d:
+ query_size = 1;
+ break;
+ case core::type::TextureDimension::k2d:
+ if (is_ms) {
+ query_size = 3;
+ swizzle = {0_u, 1_u};
+ } else {
+ query_size = 2;
+ }
+ break;
+ case core::type::TextureDimension::k2dArray:
+ query_size = is_ms ? 4 : 3;
+ swizzle = {0_u, 1_u};
+ break;
+ case core::type::TextureDimension::k3d:
+ query_size = 3;
+ break;
+ case core::type::TextureDimension::kCube:
+ query_size = 2;
+ break;
+ case core::type::TextureDimension::kCubeArray:
+ query_size = 3;
+ swizzle = {0_u, 1_u};
+ break;
+ }
+
+ // Query with a `level` adds a `number_of_levels` output parameter
+ if (has_level) {
+ // If there was no swizzle, we will need to swizzle out the required query parameters as
+ // the query will increase by one item.
+ if (swizzle.IsEmpty()) {
+ for (uint32_t i = 0; i < query_size; ++i) {
+ swizzle.Push(i);
+ }
+ }
+ query_size += 1;
+ }
+
+ const core::type::Type* query_ty = ty.u32();
+ if (query_size > 1) {
+ query_ty = ty.vec(query_ty, query_size);
+ }
+
+ b.InsertBefore(call, [&] {
+ Vector<core::ir::Value*, 5> args;
+
+ // Push the level if needed
+ if (has_level) {
+ args.Push(b.Convert(ty.u32(), call->Args()[1])->Result(0));
+ }
+
+ core::ir::Instruction* query = b.Var(ty.ptr(function, query_ty));
+ if (query_size == 1) {
+ args.Push(query->Result(0));
+ } else {
+ for (uint32_t i = 0; i < query_size; ++i) {
+ args.Push(b.Access(ty.ptr<function, u32>(), query, u32(i))->Result(0));
+ }
+ }
+
+ b.MemberCall<hlsl::ir::MemberBuiltinCall>(ty.void_(), hlsl::BuiltinFn::kGetDimensions,
+ tex, args);
+ query = b.Load(query);
+ if (!swizzle.IsEmpty()) {
+ query = b.Swizzle(ty.vec2<u32>(), query, swizzle);
+ }
+ call->Result(0)->ReplaceAllUsesWith(query->Result(0));
+ });
+ call->Destroy();
+ }
+
void TextureNumSamples(core::ir::CoreBuiltinCall* call) {
auto* tex = call->Args()[0];
auto* tex_type = tex->Type()->As<core::type::Texture>();
diff --git a/src/tint/lang/hlsl/writer/raise/builtin_polyfill_test.cc b/src/tint/lang/hlsl/writer/raise/builtin_polyfill_test.cc
index 1cf2910..92d0469 100644
--- a/src/tint/lang/hlsl/writer/raise/builtin_polyfill_test.cc
+++ b/src/tint/lang/hlsl/writer/raise/builtin_polyfill_test.cc
@@ -578,5 +578,163 @@
EXPECT_EQ(expect, str());
}
+TEST_F(HlslWriter_BuiltinPolyfillTest, TextureDimensions_1d) {
+ auto* t = b.FunctionParam(
+ "t", ty.Get<core::type::SampledTexture>(core::type::TextureDimension::k1d, ty.f32()));
+ auto* func = b.Function("foo", ty.u32());
+ func->SetParams({t});
+ b.Append(func->Block(), [&] {
+ auto* result = b.Call<u32>(core::BuiltinFn::kTextureDimensions, t);
+ b.Return(func, result);
+ });
+
+ auto* src = R"(
+%foo = func(%t:texture_1d<f32>):u32 {
+ $B1: {
+ %3:u32 = textureDimensions %t
+ ret %3
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%t:texture_1d<f32>):u32 {
+ $B1: {
+ %3:ptr<function, u32, read_write> = var
+ %4:void = %t.GetDimensions %3
+ %5:u32 = load %3
+ ret %5
+ }
+}
+)";
+
+ capabilities = core::ir::Capabilities{core::ir::Capability::kAllowVectorElementPointer};
+ Run(BuiltinPolyfill);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(HlslWriter_BuiltinPolyfillTest, TextureDimensions_2d_WithoutLod) {
+ auto* t = b.FunctionParam(
+ "t", ty.Get<core::type::SampledTexture>(core::type::TextureDimension::k2d, ty.f32()));
+ auto* func = b.Function("foo", ty.vec2<u32>());
+ func->SetParams({t});
+ b.Append(func->Block(), [&] {
+ auto* result = b.Call<vec2<u32>>(core::BuiltinFn::kTextureDimensions, t);
+ b.Return(func, result);
+ });
+
+ auto* src = R"(
+%foo = func(%t:texture_2d<f32>):vec2<u32> {
+ $B1: {
+ %3:vec2<u32> = textureDimensions %t
+ ret %3
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%t:texture_2d<f32>):vec2<u32> {
+ $B1: {
+ %3:ptr<function, vec2<u32>, read_write> = var
+ %4:ptr<function, u32, read_write> = access %3, 0u
+ %5:ptr<function, u32, read_write> = access %3, 1u
+ %6:void = %t.GetDimensions %4, %5
+ %7:vec2<u32> = load %3
+ ret %7
+ }
+}
+)";
+
+ capabilities = core::ir::Capabilities{core::ir::Capability::kAllowVectorElementPointer};
+ Run(BuiltinPolyfill);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(HlslWriter_BuiltinPolyfillTest, TextureDimensions_2d_WithI32Lod) {
+ auto* t = b.FunctionParam(
+ "t", ty.Get<core::type::SampledTexture>(core::type::TextureDimension::k2d, ty.f32()));
+ auto* func = b.Function("foo", ty.vec2<u32>());
+ func->SetParams({t});
+ b.Append(func->Block(), [&] {
+ auto* result = b.Call<vec2<u32>>(core::BuiltinFn::kTextureDimensions, t, 3_i);
+ b.Return(func, result);
+ });
+
+ auto* src = R"(
+%foo = func(%t:texture_2d<f32>):vec2<u32> {
+ $B1: {
+ %3:vec2<u32> = textureDimensions %t, 3i
+ ret %3
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%t:texture_2d<f32>):vec2<u32> {
+ $B1: {
+ %3:u32 = convert 3i
+ %4:ptr<function, vec3<u32>, read_write> = var
+ %5:ptr<function, u32, read_write> = access %4, 0u
+ %6:ptr<function, u32, read_write> = access %4, 1u
+ %7:ptr<function, u32, read_write> = access %4, 2u
+ %8:void = %t.GetDimensions %3, %5, %6, %7
+ %9:vec3<u32> = load %4
+ %10:vec2<u32> = swizzle %9, xy
+ ret %10
+ }
+}
+)";
+
+ capabilities = core::ir::Capabilities{core::ir::Capability::kAllowVectorElementPointer};
+ Run(BuiltinPolyfill);
+
+ EXPECT_EQ(expect, str());
+}
+
+TEST_F(HlslWriter_BuiltinPolyfillTest, TextureDimensions_3d) {
+ auto* t = b.FunctionParam(
+ "t", ty.Get<core::type::SampledTexture>(core::type::TextureDimension::k3d, ty.f32()));
+ auto* func = b.Function("foo", ty.vec3<u32>());
+ func->SetParams({t});
+ b.Append(func->Block(), [&] {
+ auto* result = b.Call<vec3<u32>>(core::BuiltinFn::kTextureDimensions, t);
+ b.Return(func, result);
+ });
+
+ auto* src = R"(
+%foo = func(%t:texture_3d<f32>):vec3<u32> {
+ $B1: {
+ %3:vec3<u32> = textureDimensions %t
+ ret %3
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = func(%t:texture_3d<f32>):vec3<u32> {
+ $B1: {
+ %3:ptr<function, vec3<u32>, read_write> = var
+ %4:ptr<function, u32, read_write> = access %3, 0u
+ %5:ptr<function, u32, read_write> = access %3, 1u
+ %6:ptr<function, u32, read_write> = access %3, 2u
+ %7:void = %t.GetDimensions %4, %5, %6
+ %8:vec3<u32> = load %3
+ ret %8
+ }
+}
+)";
+
+ capabilities = core::ir::Capabilities{core::ir::Capability::kAllowVectorElementPointer};
+ Run(BuiltinPolyfill);
+
+ EXPECT_EQ(expect, str());
+}
+
} // namespace
} // namespace tint::hlsl::writer::raise