[ir][spirv-writer] Emit texture and sampler types
Bug: tint:1906
Change-Id: I938fe30f24b78069c52da396cc096beeef8cac65
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/139660
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: James Price <jrprice@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Auto-Submit: James Price <jrprice@google.com>
diff --git a/src/tint/writer/spirv/ir/generator_impl_ir.cc b/src/tint/writer/spirv/ir/generator_impl_ir.cc
index 7b96272..782725e 100644
--- a/src/tint/writer/spirv/ir/generator_impl_ir.cc
+++ b/src/tint/writer/spirv/ir/generator_impl_ir.cc
@@ -53,12 +53,19 @@
#include "src/tint/transform/manager.h"
#include "src/tint/type/array.h"
#include "src/tint/type/bool.h"
+#include "src/tint/type/depth_multisampled_texture.h"
+#include "src/tint/type/depth_texture.h"
#include "src/tint/type/f16.h"
#include "src/tint/type/f32.h"
#include "src/tint/type/i32.h"
#include "src/tint/type/matrix.h"
+#include "src/tint/type/multisampled_texture.h"
#include "src/tint/type/pointer.h"
+#include "src/tint/type/sampled_texture.h"
+#include "src/tint/type/sampler.h"
+#include "src/tint/type/storage_texture.h"
#include "src/tint/type/struct.h"
+#include "src/tint/type/texture.h"
#include "src/tint/type/type.h"
#include "src/tint/type/u32.h"
#include "src/tint/type/vector.h"
@@ -331,6 +338,18 @@
Type(ptr->StoreType(), ptr->AddressSpace())});
},
[&](const type::Struct* str) { EmitStructType(id, str, addrspace); },
+ [&](const type::Texture* tex) { EmitTextureType(id, tex); },
+ [&](const type::Sampler* s) {
+ module_.PushType(spv::Op::OpTypeSampler, {id});
+
+ // Register both of the sampler types, as they're the same in SPIR-V.
+ if (s->kind() == type::SamplerKind::kSampler) {
+ types_.Add(
+ ir_->Types().Get<type::Sampler>(type::SamplerKind::kComparisonSampler), id);
+ } else {
+ types_.Add(ir_->Types().Get<type::Sampler>(type::SamplerKind::kSampler), id);
+ }
+ },
[&](Default) {
TINT_ICE(Writer, diagnostics_) << "unhandled type: " << ty->FriendlyName();
});
@@ -451,6 +470,82 @@
}
}
+void GeneratorImplIr::EmitTextureType(uint32_t id, const type::Texture* texture) {
+ uint32_t sampled_type = Switch(
+ texture, //
+ [&](const type::DepthTexture*) { return Type(ir_->Types().f32()); },
+ [&](const type::DepthMultisampledTexture*) { return Type(ir_->Types().f32()); },
+ [&](const type::SampledTexture* t) { return Type(t->type()); },
+ [&](const type::MultisampledTexture* t) { return Type(t->type()); },
+ [&](const type::StorageTexture* t) { return Type(t->type()); });
+
+ uint32_t dim = SpvDimMax;
+ uint32_t array = 0u;
+ switch (texture->dim()) {
+ case type::TextureDimension::kNone: {
+ break;
+ }
+ case type::TextureDimension::k1d: {
+ dim = SpvDim1D;
+ if (texture->Is<type::SampledTexture>()) {
+ module_.PushCapability(SpvCapabilitySampled1D);
+ } else if (texture->Is<type::StorageTexture>()) {
+ module_.PushCapability(SpvCapabilityImage1D);
+ }
+ break;
+ }
+ case type::TextureDimension::k2d: {
+ dim = SpvDim2D;
+ break;
+ }
+ case type::TextureDimension::k2dArray: {
+ dim = SpvDim2D;
+ array = 1u;
+ break;
+ }
+ case type::TextureDimension::k3d: {
+ dim = SpvDim3D;
+ break;
+ }
+ case type::TextureDimension::kCube: {
+ dim = SpvDimCube;
+ break;
+ }
+ case type::TextureDimension::kCubeArray: {
+ dim = SpvDimCube;
+ array = 1u;
+ if (texture->IsAnyOf<type::SampledTexture, type::DepthTexture>()) {
+ module_.PushCapability(SpvCapabilitySampledCubeArray);
+ }
+ break;
+ }
+ }
+
+ // The Vulkan spec says: The "Depth" operand of OpTypeImage is ignored.
+ // In SPIRV, 0 means not depth, 1 means depth, and 2 means unknown.
+ // Using anything other than 0 is problematic on various Vulkan drivers.
+ uint32_t depth = 0u;
+
+ uint32_t ms = 0u;
+ if (texture->IsAnyOf<type::MultisampledTexture, type::DepthMultisampledTexture>()) {
+ ms = 1u;
+ }
+
+ uint32_t sampled = 2u;
+ if (texture->IsAnyOf<type::MultisampledTexture, type::SampledTexture, type::DepthTexture,
+ type::DepthMultisampledTexture>()) {
+ sampled = 1u;
+ }
+
+ uint32_t format = SpvImageFormat_::SpvImageFormatUnknown;
+ if (auto* st = texture->As<type::StorageTexture>()) {
+ format = TexelFormat(st->texel_format());
+ }
+
+ module_.PushType(spv::Op::OpTypeImage,
+ {id, sampled_type, dim, depth, array, ms, sampled, format});
+}
+
void GeneratorImplIr::EmitFunction(ir::Function* func) {
auto id = Value(func);
@@ -1187,4 +1282,51 @@
}
}
+uint32_t GeneratorImplIr::TexelFormat(const builtin::TexelFormat format) {
+ switch (format) {
+ case builtin::TexelFormat::kBgra8Unorm:
+ TINT_ICE(Writer, diagnostics_)
+ << "bgra8unorm should have been polyfilled to rgba8unorm";
+ return SpvImageFormatUnknown;
+ case builtin::TexelFormat::kR32Uint:
+ return SpvImageFormatR32ui;
+ case builtin::TexelFormat::kR32Sint:
+ return SpvImageFormatR32i;
+ case builtin::TexelFormat::kR32Float:
+ return SpvImageFormatR32f;
+ case builtin::TexelFormat::kRgba8Unorm:
+ return SpvImageFormatRgba8;
+ case builtin::TexelFormat::kRgba8Snorm:
+ return SpvImageFormatRgba8Snorm;
+ case builtin::TexelFormat::kRgba8Uint:
+ return SpvImageFormatRgba8ui;
+ case builtin::TexelFormat::kRgba8Sint:
+ return SpvImageFormatRgba8i;
+ case builtin::TexelFormat::kRg32Uint:
+ module_.PushCapability(SpvCapabilityStorageImageExtendedFormats);
+ return SpvImageFormatRg32ui;
+ case builtin::TexelFormat::kRg32Sint:
+ module_.PushCapability(SpvCapabilityStorageImageExtendedFormats);
+ return SpvImageFormatRg32i;
+ case builtin::TexelFormat::kRg32Float:
+ module_.PushCapability(SpvCapabilityStorageImageExtendedFormats);
+ return SpvImageFormatRg32f;
+ case builtin::TexelFormat::kRgba16Uint:
+ return SpvImageFormatRgba16ui;
+ case builtin::TexelFormat::kRgba16Sint:
+ return SpvImageFormatRgba16i;
+ case builtin::TexelFormat::kRgba16Float:
+ return SpvImageFormatRgba16f;
+ case builtin::TexelFormat::kRgba32Uint:
+ return SpvImageFormatRgba32ui;
+ case builtin::TexelFormat::kRgba32Sint:
+ return SpvImageFormatRgba32i;
+ case builtin::TexelFormat::kRgba32Float:
+ return SpvImageFormatRgba32f;
+ case builtin::TexelFormat::kUndefined:
+ return SpvImageFormatUnknown;
+ }
+ return SpvImageFormatUnknown;
+}
+
} // namespace tint::writer::spirv
diff --git a/src/tint/writer/spirv/ir/generator_impl_ir.h b/src/tint/writer/spirv/ir/generator_impl_ir.h
index e2a78bc..57a7932 100644
--- a/src/tint/writer/spirv/ir/generator_impl_ir.h
+++ b/src/tint/writer/spirv/ir/generator_impl_ir.h
@@ -54,6 +54,7 @@
} // namespace tint::ir
namespace tint::type {
class Struct;
+class Texture;
class Type;
} // namespace tint::type
@@ -100,6 +101,11 @@
/// @returns the enum value of the corresponding SPIR-V builtin
uint32_t Builtin(builtin::BuiltinValue builtin, builtin::AddressSpace addrspace);
+ /// Convert a texel format to the corresponding SPIR-V enum value, adding required capabilities.
+ /// @param format the format to convert
+ /// @returns the enum value of the corresponding SPIR-V texel format
+ uint32_t TexelFormat(const builtin::TexelFormat format);
+
/// Get the result ID of the constant `constant`, emitting its instruction if necessary.
/// @param constant the constant to get the ID for
/// @returns the result ID of the constant
@@ -138,6 +144,11 @@
const type::Struct* str,
builtin::AddressSpace addrspace = builtin::AddressSpace::kUndefined);
+ /// Emit a texture type.
+ /// @param id the result ID to use
+ /// @param texture the texture type to emit
+ void EmitTextureType(uint32_t id, const type::Texture* texture);
+
/// Emit a function.
/// @param func the function to emit
void EmitFunction(ir::Function* func);
diff --git a/src/tint/writer/spirv/ir/generator_impl_ir_type_test.cc b/src/tint/writer/spirv/ir/generator_impl_ir_type_test.cc
index 9caa027..4284884 100644
--- a/src/tint/writer/spirv/ir/generator_impl_ir_type_test.cc
+++ b/src/tint/writer/spirv/ir/generator_impl_ir_type_test.cc
@@ -13,9 +13,14 @@
// limitations under the License.
#include "src/tint/type/bool.h"
+#include "src/tint/type/depth_multisampled_texture.h"
+#include "src/tint/type/depth_texture.h"
#include "src/tint/type/f16.h"
#include "src/tint/type/f32.h"
#include "src/tint/type/i32.h"
+#include "src/tint/type/multisampled_texture.h"
+#include "src/tint/type/sampled_texture.h"
+#include "src/tint/type/storage_texture.h"
#include "src/tint/type/type.h"
#include "src/tint/type/u32.h"
#include "src/tint/type/void.h"
@@ -196,6 +201,172 @@
EXPECT_INST("%MyStruct = OpTypeStruct %mat3v3float %_arr__arr_mat2v4half_uint_4_uint_4");
}
+TEST_F(SpvGeneratorImplTest, Type_Sampler) {
+ generator_.Type(ty.sampler());
+
+ ASSERT_TRUE(Generate()) << Error() << output_;
+ EXPECT_INST("%1 = OpTypeSampler");
+}
+
+TEST_F(SpvGeneratorImplTest, Type_SamplerComparison) {
+ generator_.Type(ty.comparison_sampler());
+
+ ASSERT_TRUE(Generate()) << Error() << output_;
+ EXPECT_INST("%1 = OpTypeSampler");
+}
+
+TEST_F(SpvGeneratorImplTest, Type_Samplers_Dedup) {
+ auto id = generator_.Type(ty.sampler());
+ EXPECT_EQ(generator_.Type(ty.comparison_sampler()), id);
+
+ ASSERT_TRUE(Generate()) << Error() << output_;
+}
+
+using Dim = type::TextureDimension;
+struct TextureCase {
+ std::string result;
+ Dim dim;
+ TestElementType format = kF32;
+};
+
+using Type_SampledTexture = SpvGeneratorImplTestWithParam<TextureCase>;
+TEST_P(Type_SampledTexture, Emit) {
+ auto params = GetParam();
+ generator_.Type(ty.Get<type::SampledTexture>(params.dim, MakeScalarType(params.format)));
+
+ ASSERT_TRUE(Generate()) << Error() << output_;
+ EXPECT_INST(params.result);
+}
+INSTANTIATE_TEST_SUITE_P(
+ SpvGeneratorImplTest,
+ Type_SampledTexture,
+ testing::Values(
+ TextureCase{"%1 = OpTypeImage %float 1D 0 0 0 1 Unknown", Dim::k1d, kF32},
+ TextureCase{"%1 = OpTypeImage %float 2D 0 0 0 1 Unknown", Dim::k2d, kF32},
+ TextureCase{"%1 = OpTypeImage %float 2D 0 1 0 1 Unknown", Dim::k2dArray, kF32},
+ TextureCase{"%1 = OpTypeImage %float 3D 0 0 0 1 Unknown", Dim::k3d, kF32},
+ TextureCase{"%1 = OpTypeImage %float Cube 0 0 0 1 Unknown", Dim::kCube, kF32},
+ TextureCase{"%1 = OpTypeImage %float Cube 0 1 0 1 Unknown", Dim::kCubeArray, kF32},
+ TextureCase{"%1 = OpTypeImage %int 1D 0 0 0 1 Unknown", Dim::k1d, kI32},
+ TextureCase{"%1 = OpTypeImage %int 2D 0 0 0 1 Unknown", Dim::k2d, kI32},
+ TextureCase{"%1 = OpTypeImage %int 2D 0 1 0 1 Unknown", Dim::k2dArray, kI32},
+ TextureCase{"%1 = OpTypeImage %int 3D 0 0 0 1 Unknown", Dim::k3d, kI32},
+ TextureCase{"%1 = OpTypeImage %int Cube 0 0 0 1 Unknown", Dim::kCube, kI32},
+ TextureCase{"%1 = OpTypeImage %int Cube 0 1 0 1 Unknown", Dim::kCubeArray, kI32},
+ TextureCase{"%1 = OpTypeImage %uint 1D 0 0 0 1 Unknown", Dim::k1d, kU32},
+ TextureCase{"%1 = OpTypeImage %uint 2D 0 0 0 1 Unknown", Dim::k2d, kU32},
+ TextureCase{"%1 = OpTypeImage %uint 2D 0 1 0 1 Unknown", Dim::k2dArray, kU32},
+ TextureCase{"%1 = OpTypeImage %uint 3D 0 0 0 1 Unknown", Dim::k3d, kU32},
+ TextureCase{"%1 = OpTypeImage %uint Cube 0 0 0 1 Unknown", Dim::kCube, kU32},
+ TextureCase{"%1 = OpTypeImage %uint Cube 0 1 0 1 Unknown", Dim::kCubeArray, kU32}));
+
+using Type_MultisampledTexture = SpvGeneratorImplTestWithParam<TextureCase>;
+TEST_P(Type_MultisampledTexture, Emit) {
+ auto params = GetParam();
+ generator_.Type(ty.Get<type::MultisampledTexture>(params.dim, MakeScalarType(params.format)));
+
+ ASSERT_TRUE(Generate()) << Error() << output_;
+ EXPECT_INST(params.result);
+}
+INSTANTIATE_TEST_SUITE_P(
+ SpvGeneratorImplTest,
+ Type_MultisampledTexture,
+ testing::Values(TextureCase{"%1 = OpTypeImage %float 2D 0 0 1 1 Unknown", Dim::k2d, kF32},
+ TextureCase{"%1 = OpTypeImage %int 2D 0 0 1 1 Unknown", Dim::k2d, kI32},
+ TextureCase{"%1 = OpTypeImage %uint 2D 0 0 1 1 Unknown", Dim::k2d, kU32}));
+
+using Type_DepthTexture = SpvGeneratorImplTestWithParam<TextureCase>;
+TEST_P(Type_DepthTexture, Emit) {
+ auto params = GetParam();
+ generator_.Type(ty.Get<type::DepthTexture>(params.dim));
+
+ ASSERT_TRUE(Generate()) << Error() << output_;
+ EXPECT_INST(params.result);
+}
+INSTANTIATE_TEST_SUITE_P(
+ SpvGeneratorImplTest,
+ Type_DepthTexture,
+ testing::Values(TextureCase{"%1 = OpTypeImage %float 2D 0 0 0 1 Unknown", Dim::k2d},
+ TextureCase{"%1 = OpTypeImage %float 2D 0 1 0 1 Unknown", Dim::k2dArray},
+ TextureCase{"%1 = OpTypeImage %float Cube 0 0 0 1 Unknown", Dim::kCube},
+ TextureCase{"%1 = OpTypeImage %float Cube 0 1 0 1 Unknown", Dim::kCubeArray}));
+
+TEST_F(SpvGeneratorImplTest, Type_DepthMultiSampledTexture) {
+ generator_.Type(ty.Get<type::DepthMultisampledTexture>(Dim::k2d));
+
+ ASSERT_TRUE(Generate()) << Error() << output_;
+ EXPECT_INST("%1 = OpTypeImage %float 2D 0 0 1 1 Unknown");
+}
+
+using Format = builtin::TexelFormat;
+struct StorageTextureCase {
+ std::string result;
+ Dim dim;
+ Format format;
+};
+using Type_StorageTexture = SpvGeneratorImplTestWithParam<StorageTextureCase>;
+TEST_P(Type_StorageTexture, Emit) {
+ auto params = GetParam();
+ generator_.Type(
+ ty.Get<type::StorageTexture>(params.dim, params.format, builtin::Access::kWrite,
+ type::StorageTexture::SubtypeFor(params.format, mod.Types())));
+
+ ASSERT_TRUE(Generate()) << Error() << output_;
+ EXPECT_INST(params.result);
+ if (params.format == builtin::TexelFormat::kRg32Uint ||
+ params.format == builtin::TexelFormat::kRg32Sint ||
+ params.format == builtin::TexelFormat::kRg32Float) {
+ EXPECT_INST("OpCapability StorageImageExtendedFormats");
+ }
+}
+INSTANTIATE_TEST_SUITE_P(SpvGeneratorImplTest,
+ Type_StorageTexture,
+ testing::Values(
+ // Test all the dimensions with a single format.
+ StorageTextureCase{"%1 = OpTypeImage %float 1D 0 0 0 2 R32f", //
+ Dim::k1d, Format::kR32Float},
+ StorageTextureCase{"%1 = OpTypeImage %float 2D 0 0 0 2 R32f", //
+ Dim::k2d, Format::kR32Float},
+ StorageTextureCase{"%1 = OpTypeImage %float 2D 0 1 0 2 R32f", //
+ Dim::k2dArray, Format::kR32Float},
+ StorageTextureCase{"%1 = OpTypeImage %float 3D 0 0 0 2 R32f", //
+ Dim::k3d, Format::kR32Float},
+
+ // Test all the formats with 2D.
+ // TODO(jrprice): Enable this format when we polyfill it.
+ // StorageTextureCase{"%1 = OpTypeImage %float 2D 0 0 0 2 Bgra8Unorm",
+ // Dim::k2d, Format::kBgra8Unorm},
+ StorageTextureCase{"%1 = OpTypeImage %int 2D 0 0 0 2 R32i", //
+ Dim::k2d, Format::kR32Sint},
+ StorageTextureCase{"%1 = OpTypeImage %uint 2D 0 0 0 2 R32u", //
+ Dim::k2d, Format::kR32Uint},
+ StorageTextureCase{"%1 = OpTypeImage %float 2D 0 0 0 2 Rg32f", //
+ Dim::k2d, Format::kRg32Float},
+ StorageTextureCase{"%1 = OpTypeImage %int 2D 0 0 0 2 Rg32i", //
+ Dim::k2d, Format::kRg32Sint},
+ StorageTextureCase{"%1 = OpTypeImage %uint 2D 0 0 0 2 Rg32ui", //
+ Dim::k2d, Format::kRg32Uint},
+ StorageTextureCase{"%1 = OpTypeImage %float 2D 0 0 0 2 Rgba16f", //
+ Dim::k2d, Format::kRgba16Float},
+ StorageTextureCase{"%1 = OpTypeImage %int 2D 0 0 0 2 Rgba16i", //
+ Dim::k2d, Format::kRgba16Sint},
+ StorageTextureCase{"%1 = OpTypeImage %uint 2D 0 0 0 2 Rgba16ui", //
+ Dim::k2d, Format::kRgba16Uint},
+ StorageTextureCase{"%1 = OpTypeImage %float 2D 0 0 0 2 Rgba32f", //
+ Dim::k2d, Format::kRgba32Float},
+ StorageTextureCase{"%1 = OpTypeImage %int 2D 0 0 0 2 Rgba32i", //
+ Dim::k2d, Format::kRgba32Sint},
+ StorageTextureCase{"%1 = OpTypeImage %uint 2D 0 0 0 2 Rgba32ui", //
+ Dim::k2d, Format::kRgba32Uint},
+ StorageTextureCase{"%1 = OpTypeImage %int 2D 0 0 0 2 Rgba8i", //
+ Dim::k2d, Format::kRgba8Sint},
+ StorageTextureCase{"%1 = OpTypeImage %float 2D 0 0 0 2 Rgba8Snorm", //
+ Dim::k2d, Format::kRgba8Snorm},
+ StorageTextureCase{"%1 = OpTypeImage %uint 2D 0 0 0 2 Rgba8ui", //
+ Dim::k2d, Format::kRgba8Uint},
+ StorageTextureCase{"%1 = OpTypeImage %float 2D 0 0 0 2 Rgba8", //
+ Dim::k2d, Format::kRgba8Unorm}));
+
// Test that we can emit multiple types.
// Includes types with the same opcode but different parameters.
TEST_F(SpvGeneratorImplTest, Type_Multiple) {