From 68be13345a084613815e18be50fbb184796998d6 Mon Sep 17 00:00:00 2001 From: nmzik Date: Fri, 31 Jul 2026 11:30:48 +0200 Subject: [PATCH] fix(shader): support multisampled depth image loads --- .../host_gpu/renderer/image/imageView.h | 4 +- .../renderer/pipeline/descriptorCache.cpp | 4 + .../renderer/pipeline/descriptors.cpp | 112 +++++++++++++----- .../shader/recompiler/decompiler/ImageOps.cpp | 8 +- .../recompiler/decompiler/ShaderDecoder.cpp | 10 +- .../recompiler/decompiler/ShaderDecoder.h | 2 + .../recompiler/emitter/SpirvEmitter.cpp | 8 ++ .../emitter/spirvEmitterAnalysis.cpp | 16 ++- .../emitter/spirvEmitterImageOps.cpp | 30 +++-- .../recompiler/emitter/spirvEmitterInternal.h | 29 ++++- .../recompiler/emitter/spirvEmitterModule.cpp | 36 +++--- .../shader/recompiler/ir/BindingLayout.cpp | 47 +++++--- .../recompiler/ir/ResourceMaterialization.cpp | 13 +- src/graphics/shader/recompiler/ir/ShaderIR.h | 4 + tests/ShaderRecompilerComputeTests.cpp | 6 +- tests/shaderCfgTests.cpp | 50 +++++++- 16 files changed, 276 insertions(+), 103 deletions(-) diff --git a/src/graphics/host_gpu/renderer/image/imageView.h b/src/graphics/host_gpu/renderer/image/imageView.h index 59041ecf4..f8666e3f1 100644 --- a/src/graphics/host_gpu/renderer/image/imageView.h +++ b/src/graphics/host_gpu/renderer/image/imageView.h @@ -88,7 +88,9 @@ SelectSampledDepthView(vk::Format image_format, vk::Format view_format, uint32_t IsSupportedSampledDepthResource(const ShaderRecompiler::IR::ImageResource& resource) noexcept { return resource.kind == ShaderRecompiler::IR::ResourceKind::Image && (resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D || - resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DArray) && + resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DArray || + resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaa || + resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaaArray) && resource.mip_mode == ShaderRecompiler::IR::ImageMipMode::None && resource.read && !resource.written && !resource.atomic; } diff --git a/src/graphics/host_gpu/renderer/pipeline/descriptorCache.cpp b/src/graphics/host_gpu/renderer/pipeline/descriptorCache.cpp index fcd5917fd..25a90572f 100644 --- a/src/graphics/host_gpu/renderer/pipeline/descriptorCache.cpp +++ b/src/graphics/host_gpu/renderer/pipeline/descriptorCache.cpp @@ -26,11 +26,15 @@ bool IsSampledImage(BindingKind kind) { case BindingKind::Sampled1DArray: case BindingKind::Sampled2D: case BindingKind::Sampled2DArray: + case BindingKind::Sampled2DMsaa: + case BindingKind::Sampled2DMsaaArray: case BindingKind::Sampled3D: case BindingKind::SampledUint1D: case BindingKind::SampledUint1DArray: case BindingKind::SampledUint2D: case BindingKind::SampledUint2DArray: + case BindingKind::SampledUint2DMsaa: + case BindingKind::SampledUint2DMsaaArray: case BindingKind::SampledUint3D: return true; default: return false; } diff --git a/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp b/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp index 2babd3bcd..e397b10b5 100644 --- a/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp +++ b/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp @@ -73,6 +73,11 @@ static Prospero::ImageType TextureBaseType(Prospero::ImageType type) { } } +static bool IsMultisampledTexture(Prospero::ImageType type) { + return type == Prospero::ImageType::kColor2DMsaa || + type == Prospero::ImageType::kColor2DMsaaArray; +} + static BufferView NativeStorageBuffer(RenderContext& context, CommandBuffer& command_buffer, const ShaderBufferResource& descriptor, const ShaderRecompiler::IR::BufferResource& resource, @@ -159,6 +164,8 @@ static bool IsSupportedSampledColorResource(const ShaderRecompiler::IR::ImageRes case ShaderRecompiler::Decoder::ImageDimension::Dim1DArray: case ShaderRecompiler::Decoder::ImageDimension::Dim2D: case ShaderRecompiler::Decoder::ImageDimension::Dim2DArray: + case ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaa: + case ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaaArray: supported_dimension = true; break; default: break; @@ -195,6 +202,22 @@ TargetTextureViewInfo ResolveTargetTextureView(const ShaderRecompiler::IR::Image ? TargetTextureViewInfo {vk::ImageViewType::e2DArray, base_layer, image_layers - base_layer} : TargetTextureViewInfo {}; + case Prospero::ImageType::kColor2DMsaa: + return resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaa && + base_layer == 0 && image_layers == 1 + ? TargetTextureViewInfo {vk::ImageViewType::e2D, 0, 1} + : TargetTextureViewInfo {}; + case Prospero::ImageType::kColor2DMsaaArray: + if (resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaa && + base_layer == 0 && image_layers == 1) { + return {vk::ImageViewType::e2D, 0, 1}; + } + return resource.dimension == + ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaaArray && + base_layer < image_layers + ? TargetTextureViewInfo {vk::ImageViewType::e2DArray, base_layer, + image_layers - base_layer} + : TargetTextureViewInfo {}; default: return {}; } } @@ -209,10 +232,14 @@ bool IsSupportedSampledVideoOutView(const ShaderRecompiler::IR::ImageResource& r } bool IsSupportedDepthTargetDescriptor(const ShaderTextureResource& descriptor, const Image& image) { - const auto width = static_cast(descriptor.Width5()) + 1u; - const auto height = static_cast(descriptor.Height5()) + 1u; - const auto pitch = TileGetTexturePitch(descriptor.Format(), width, 1, descriptor.TileMode()); - const auto type = static_cast(descriptor.Type()); + const auto width = static_cast(descriptor.Width5()) + 1u; + const auto height = static_cast(descriptor.Height5()) + 1u; + const auto type = static_cast(descriptor.Type()); + const bool multisampled = IsMultisampledTexture(type); + const auto samples = multisampled ? 1u << descriptor.LastLevel() : 1u; + const auto pitch = + multisampled ? TileGetDepthPitch(width, image.info.bytes_per_block, descriptor.LastLevel()) + : TileGetTexturePitch(descriptor.Format(), width, 1, descriptor.TileMode()); const bool supported_2d = type == Prospero::ImageType::kColor2D && image.info.resources.layers == 1 && descriptor.Depth() == 0 && descriptor.BaseArray5() == 0; @@ -224,28 +251,45 @@ bool IsSupportedDepthTargetDescriptor(const ShaderTextureResource& descriptor, c image.info.resources.layers % 6u == 0 && static_cast(descriptor.Depth()) + 1u == image.info.resources.layers && descriptor.BaseArray5() == 0; + const bool supported_msaa_2d = type == Prospero::ImageType::kColor2DMsaa && + image.info.resources.layers == 1 && descriptor.Depth() == 0 && + descriptor.BaseArray5() == 0; + const bool supported_msaa_array = type == Prospero::ImageType::kColor2DMsaaArray && + descriptor.BaseArray5() <= descriptor.Depth() && + descriptor.Depth() < image.info.resources.layers; + const bool levels_ok = + multisampled + ? descriptor.BaseLevel() == 0 && descriptor.LastLevel() >= 1 && + descriptor.LastLevel() <= 3 && descriptor.MaxMip() == descriptor.LastLevel() && + image.info.resources.levels == 1 && image.info.samples == samples + : descriptor.BaseLevel() == 0 && descriptor.LastLevel() == 0 && + descriptor.MaxMip() == 0 && image.info.samples == 1; return image.info.IsDepth() && width == image.info.extent.width && height == image.info.extent.height && - (supported_2d || supported_array || supported_cube) && descriptor.BaseLevel() == 0 && - descriptor.LastLevel() == 0 && descriptor.MaxMip() == 0 && descriptor.MinLod() == 0 && + (supported_2d || supported_array || supported_cube || supported_msaa_2d || + supported_msaa_array) && + levels_ok && descriptor.MinLod() == 0 && descriptor.TileMode() == Prospero::GpuEnumValue(Prospero::TileMode::kDepth) && - descriptor.BCSwizzle() == 0 && !descriptor.MsaaDepth() && pitch >= width && - pitch == image.info.pitch; + descriptor.BCSwizzle() == 0 && descriptor.MsaaDepth() == multisampled && + pitch >= width && pitch == image.info.pitch; } bool IsSupportedDepthTextureEncoding(const ShaderTextureResource& descriptor, const Image& image) { constexpr uint32_t field1_reserved_mask = 0x200fff00u; constexpr uint32_t field2_reserved_mask = 0xf0003000u; - constexpr uint32_t field3_common = 0x01800000u; - constexpr uint32_t field5_expected = 0x00700000u; - const uint32_t field3_expected = - (descriptor.Type() << 28u) | field3_common | descriptor.DstSelXYZW(); - const uint32_t field4_expected = descriptor.Depth() | (descriptor.BaseArray5() << 16u); - const bool common = (descriptor.fields[1] & field1_reserved_mask) == 0 && - (descriptor.fields[2] & field2_reserved_mask) == 0 && - descriptor.fields[3] == field3_expected && - descriptor.fields[4] == field4_expected && - descriptor.fields[5] == field5_expected; + const uint32_t field3_expected = descriptor.DstSelXYZW() | + (static_cast(descriptor.BaseLevel()) << 12u) | + (static_cast(descriptor.LastLevel()) << 16u) | + (static_cast(descriptor.TileMode()) << 20u) | + (static_cast(descriptor.Type()) << 28u); + const uint32_t field4_expected = descriptor.Depth() | (descriptor.BaseArray5() << 16u); + const uint32_t field5_expected = + 0x00700000u | (static_cast(descriptor.MaxMip()) << 4u); + const bool common = (descriptor.fields[1] & field1_reserved_mask) == 0 && + (descriptor.fields[2] & field2_reserved_mask) == 0 && + descriptor.fields[3] == field3_expected && + descriptor.fields[4] == field4_expected && + descriptor.fields[5] == field5_expected; if (!common || (descriptor.fields[6] == 0 && descriptor.fields[7] != 0)) { return false; } @@ -253,8 +297,9 @@ bool IsSupportedDepthTextureEncoding(const ShaderTextureResource& descriptor, co return true; } constexpr uint32_t htile_control = 0x00280000u; - const auto metadata_addr = descriptor.MetaAddr() << 8u; - return (descriptor.fields[6] & 0x00ffffffu) == htile_control && metadata_addr != 0 && + const uint32_t expected_control = htile_control | (descriptor.MsaaDepth() ? (1u << 10u) : 0u); + const auto metadata_addr = descriptor.MetaAddr() << 8u; + return (descriptor.fields[6] & 0x00ffffffu) == expected_control && metadata_addr != 0 && descriptor.TileMode() == Prospero::GpuEnumValue(Prospero::TileMode::kDepth) && image.info.tile_mode == Prospero::GpuEnumValue(Prospero::TileMode::kDepth) && image.info.metadata.kind == ImageMetadataKind::Htile && @@ -520,6 +565,7 @@ static ImageViewInfo TextureViewInfo(const ShaderRecompiler::IR::ImageResource& view.layer_count = 1; break; case ShaderRecompiler::Decoder::ImageDimension::Dim2DArray: + case ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaaArray: view.type = vk::ImageViewType::e2DArray; view.base_layer = descriptor.BaseArray5(); if (view.base_layer >= image_layers) { @@ -528,6 +574,7 @@ static ImageViewInfo TextureViewInfo(const ShaderRecompiler::IR::ImageResource& view.layer_count = image_layers - view.base_layer; break; case ShaderRecompiler::Decoder::ImageDimension::Dim2D: + case ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaa: view.type = vk::ImageViewType::e2D; view.base_layer = descriptor.BaseArray5(); if (view.base_layer >= image_layers) { @@ -558,22 +605,23 @@ RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& reso return {id, nullptr, std::move(desc)}; } - const auto address = descriptor.Base40(); - const auto width = static_cast(descriptor.Width5()) + 1u; - const auto height = static_cast(descriptor.Height5()) + 1u; - const auto base_level = descriptor.BaseLevel(); - const auto last_level = descriptor.LastLevel(); - const auto type = TextureType(descriptor); - const bool multisampled = - type == Prospero::ImageType::kColor2DMsaa || type == Prospero::ImageType::kColor2DMsaaArray; - const auto levels = multisampled ? 1u : static_cast(descriptor.MaxMip()) + 1u; - const auto tile = descriptor.TileMode(); - const bool msaa_tile = tile == Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget); + const auto address = descriptor.Base40(); + const auto width = static_cast(descriptor.Width5()) + 1u; + const auto height = static_cast(descriptor.Height5()) + 1u; + const auto base_level = descriptor.BaseLevel(); + const auto last_level = descriptor.LastLevel(); + const auto type = TextureType(descriptor); + const bool multisampled = IsMultisampledTexture(type); + const auto levels = multisampled ? 1u : static_cast(descriptor.MaxMip()) + 1u; + const auto tile = descriptor.TileMode(); + const bool msaa_tile = + tile == Prospero::GpuEnumValue(descriptor.MsaaDepth() ? Prospero::TileMode::kDepth + : Prospero::TileMode::kRenderTarget); const bool msaa_array = type == Prospero::ImageType::kColor2DMsaaArray; if ((!multisampled && (base_level > last_level || last_level >= levels)) || (multisampled && (base_level != 0 || last_level == 0 || last_level > 3 || - descriptor.MaxMip() != last_level || !msaa_tile || descriptor.MsaaDepth() || + descriptor.MaxMip() != last_level || !msaa_tile || (!msaa_array && (descriptor.Depth() != 0 || descriptor.BaseArray5() != 0))))) { EXIT("unsupported texture mip view: base=%u last=%u levels=%u\n", base_level, last_level, levels); diff --git a/src/graphics/shader/recompiler/decompiler/ImageOps.cpp b/src/graphics/shader/recompiler/decompiler/ImageOps.cpp index 1aaa3230c..28e668dcc 100644 --- a/src/graphics/shader/recompiler/decompiler/ImageOps.cpp +++ b/src/graphics/shader/recompiler/decompiler/ImageOps.cpp @@ -35,9 +35,9 @@ constexpr ImageDimension DecodeImageDimension(uint32_t dim) { case 2u: return ImageDimension::Dim3D; case 3u: return ImageDimension::Dim2DArray; case 4u: return ImageDimension::Dim1DArray; - case 5u: - case 7u: return ImageDimension::Dim2DArray; - case 6u: return ImageDimension::Dim2D; + case 5u: return ImageDimension::Dim2DArray; + case 6u: return ImageDimension::Dim2DMsaa; + case 7u: return ImageDimension::Dim2DMsaaArray; default: return ImageDimension::Unknown; } } @@ -46,8 +46,10 @@ constexpr uint32_t ImageCoordComponents(ImageDimension dimension) { switch (dimension) { case ImageDimension::Dim1D: return 1u; case ImageDimension::Dim1DArray: return 2u; + case ImageDimension::Dim2DMsaa: case ImageDimension::Dim3D: case ImageDimension::Dim2DArray: return 3u; + case ImageDimension::Dim2DMsaaArray: return 4u; default: return 2u; } } diff --git a/src/graphics/shader/recompiler/decompiler/ShaderDecoder.cpp b/src/graphics/shader/recompiler/decompiler/ShaderDecoder.cpp index f79216ffd..d9d23a782 100644 --- a/src/graphics/shader/recompiler/decompiler/ShaderDecoder.cpp +++ b/src/graphics/shader/recompiler/decompiler/ShaderDecoder.cpp @@ -194,6 +194,8 @@ const char* ImageDimensionToString(ImageDimension dimension) { case ImageDimension::Dim2D: return "2d"; case ImageDimension::Dim3D: return "3d"; case ImageDimension::Dim2DArray: return "2d_array"; + case ImageDimension::Dim2DMsaa: return "2d_msaa"; + case ImageDimension::Dim2DMsaaArray: return "2d_msaa_array"; default: return "unknown"; } } @@ -220,9 +222,9 @@ bool DecodeScalarSource(uint32_t code, uint32_t pc, Operand& operand, std::strin } if (code >= 240u && code <= 247u) { constexpr float values[] = {0.5f, -0.5f, 1.0f, -1.0f, 2.0f, -2.0f, 4.0f, -4.0f}; - operand.kind = OperandKind::FloatInlineConstant; - operand.float_val = values[code - 240u]; - operand.value = FloatBits(operand.float_val); + operand.kind = OperandKind::FloatInlineConstant; + operand.float_val = values[code - 240u]; + operand.value = FloatBits(operand.float_val); return true; } if (code >= 256u && code <= 511u) { @@ -285,7 +287,7 @@ bool DecodeVectorGpr(uint32_t reg, Operand& operand, std::string* error) { SetError(error, "VGPR index is out of range"); return false; } - operand = {}; + operand = {}; operand.kind = OperandKind::Vgpr; operand.reg = reg; return true; diff --git a/src/graphics/shader/recompiler/decompiler/ShaderDecoder.h b/src/graphics/shader/recompiler/decompiler/ShaderDecoder.h index 8d25e2c18..f46374d74 100644 --- a/src/graphics/shader/recompiler/decompiler/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/decompiler/ShaderDecoder.h @@ -575,6 +575,8 @@ enum class ImageDimension : uint32_t { Dim2D, Dim3D, Dim2DArray, + Dim2DMsaa, + Dim2DMsaaArray, }; constexpr uint32_t MaxInstructionRawWords = 5u; diff --git a/src/graphics/shader/recompiler/emitter/SpirvEmitter.cpp b/src/graphics/shader/recompiler/emitter/SpirvEmitter.cpp index e47b662d4..d338310d8 100644 --- a/src/graphics/shader/recompiler/emitter/SpirvEmitter.cpp +++ b/src/graphics/shader/recompiler/emitter/SpirvEmitter.cpp @@ -30,10 +30,16 @@ bool ImageBinding(const IR::ImageResource& image, IR::DescriptorBindingKind& kin kind = integer ? Kind::SampledUint1DArray : Kind::Sampled1DArray; return true; case Dim::Dim2D: kind = integer ? Kind::SampledUint2D : Kind::Sampled2D; return true; + case Dim::Dim2DMsaa: + kind = integer ? Kind::SampledUint2DMsaa : Kind::Sampled2DMsaa; + return true; case Dim::Dim3D: kind = integer ? Kind::SampledUint3D : Kind::Sampled3D; return true; case Dim::Dim2DArray: kind = integer ? Kind::SampledUint2DArray : Kind::Sampled2DArray; return true; + case Dim::Dim2DMsaaArray: + kind = integer ? Kind::SampledUint2DMsaaArray : Kind::Sampled2DMsaaArray; + return true; case Dim::Unknown: return false; } } @@ -51,6 +57,8 @@ bool ImageBinding(const IR::ImageResource& image, IR::DescriptorBindingKind& kin case Dim::Dim2DArray: kind = uint_image ? Kind::StorageUint2DArray : Kind::Storage2DArray; return true; + case Dim::Dim2DMsaa: + case Dim::Dim2DMsaaArray: return false; case Dim::Unknown: return false; } return false; diff --git a/src/graphics/shader/recompiler/emitter/spirvEmitterAnalysis.cpp b/src/graphics/shader/recompiler/emitter/spirvEmitterAnalysis.cpp index 4cd2ee128..9fc861e78 100644 --- a/src/graphics/shader/recompiler/emitter/spirvEmitterAnalysis.cpp +++ b/src/graphics/shader/recompiler/emitter/spirvEmitterAnalysis.cpp @@ -129,7 +129,7 @@ uint32_t MaxCollectedVectorRegisterEnd(const std::vector& regis } void CollectMoveRelSourceRegisters(const IR::Program& program, - std::vector& registers) { + std::vector& registers) { const auto max_vector_end = MaxCollectedVectorRegisterEnd(registers); for (const auto& block: program.blocks) { for (const auto& inst: block.instructions) { @@ -276,8 +276,7 @@ void CopyProgramInputsAndOutputs(EmitterState& state, const IR::Program& program if (HasOutput(state.outputs, output.kind, output.index)) { continue; } - state.outputs.push_back( - {output.kind, output.index, output.location, 0, output.debug_name}); + state.outputs.push_back({output.kind, output.index, output.location, 0, output.debug_name}); } } @@ -576,6 +575,8 @@ ImageViewKind ImageViewKindFromDimension(Decoder::ImageDimension dimension) { case Decoder::ImageDimension::Dim1DArray: return ImageViewKind::Dim1DArray; case Decoder::ImageDimension::Dim2DArray: return ImageViewKind::Dim2DArray; case Decoder::ImageDimension::Dim3D: return ImageViewKind::Dim3D; + case Decoder::ImageDimension::Dim2DMsaa: return ImageViewKind::Dim2DMsaa; + case Decoder::ImageDimension::Dim2DMsaaArray: return ImageViewKind::Dim2DMsaaArray; default: return ImageViewKind::Dim2D; } } @@ -601,7 +602,9 @@ uint32_t ImageViewCoordinateComponents(ImageViewKind view) { case ImageViewKind::Dim1DArray: case ImageViewKind::Dim2D: return 2u; case ImageViewKind::Dim2DArray: + case ImageViewKind::Dim2DMsaaArray: case ImageViewKind::Dim3D: return 3u; + case ImageViewKind::Dim2DMsaa: return 2u; default: return 0u; } } @@ -611,7 +614,9 @@ uint32_t ImageViewSpatialComponents(ImageViewKind view) { case ImageViewKind::Dim1D: case ImageViewKind::Dim1DArray: return 1u; case ImageViewKind::Dim2D: - case ImageViewKind::Dim2DArray: return 2u; + case ImageViewKind::Dim2DArray: + case ImageViewKind::Dim2DMsaa: + case ImageViewKind::Dim2DMsaaArray: return 2u; case ImageViewKind::Dim3D: return 3u; default: return 0u; } @@ -663,8 +668,7 @@ uint32_t LoadSampledImageDescriptor(EmitterState& state, const IR::MemoryInfo& m uint32_t LoadSamplerDescriptor(EmitterState& state, uint32_t sampler, uint32_t use_pc) { (void)use_pc; - const auto binding = - ResourceForDescriptor(state, IR::DescriptorBindingKind::Samplers, sampler); + const auto binding = ResourceForDescriptor(state, IR::DescriptorBindingKind::Samplers, sampler); const auto pointer = DescriptorElementPointer( state, state.ptr_uniform_sampler, state.sampler_variable, binding.array_index, IR::DescriptorBindingKind::Samplers, sampler, "sampler descriptor array was not emitted"); diff --git a/src/graphics/shader/recompiler/emitter/spirvEmitterImageOps.cpp b/src/graphics/shader/recompiler/emitter/spirvEmitterImageOps.cpp index 3760f6175..ae263a239 100644 --- a/src/graphics/shader/recompiler/emitter/spirvEmitterImageOps.cpp +++ b/src/graphics/shader/recompiler/emitter/spirvEmitterImageOps.cpp @@ -35,9 +35,9 @@ uint32_t ConstantImageGatherHorizontalOffsets(EmitterState& state, ImageViewKind uint32_t LoadStorageImageDescriptorAtIndex(EmitterState& state, uint32_t resource, uint32_t array_index, bool uint_image, ImageViewKind view) { - const auto kind = StorageBindingKind(uint_image, view); + const auto kind = StorageBindingKind(uint_image, view); const auto& descriptors = state.storage_images[StorageImageIndex(uint_image, view)]; - const auto pointer = + const auto pointer = DescriptorElementPointer(state, descriptors.pointer_type, descriptors.variable, array_index, kind, resource, "storage image descriptor array was not emitted"); const auto image = state.builder.AllocateId(); @@ -133,10 +133,18 @@ void EmitImageLoad(EmitterState& state, const IR::Instruction& inst) { const bool integer = inst.memory.kind == IR::ResourceKind::ImageUint; const auto color = state.builder.AllocateId(); - state.builder.AddFunction({OpImageFetch, integer ? state.vec4_uint_type : state.vec4_float_type, - color, image, EmitImageLoadCoordU32(state, inst, view), - ImageOperandsLodMask, - EmitImageMipLodU32(state, inst, inst.src[0], view)}); + const auto coord = EmitImageLoadCoordU32(state, inst, view); + if (ImageSpirvMultisampled(view) != 0) { + const auto sample = EmitImageAddressValueLoad(state, inst, inst.src[0], + ImageViewCoordinateComponents(view)); + state.builder.AddFunction({OpImageFetch, + integer ? state.vec4_uint_type : state.vec4_float_type, color, + image, coord, ImageOperandsSampleMask, sample}); + } else { + state.builder.AddFunction( + {OpImageFetch, integer ? state.vec4_uint_type : state.vec4_float_type, color, image, + coord, ImageOperandsLodMask, EmitImageMipLodU32(state, inst, inst.src[0], view)}); + } const auto dmask = inst.memory.dmask != 0 ? inst.memory.dmask : 1u; uint32_t dst_index = 0; @@ -158,8 +166,8 @@ void EmitImageLoad(EmitterState& state, const IR::Instruction& inst) { void EmitImageStore(EmitterState& state, const IR::Instruction& inst) { const auto uint_image = inst.memory.kind == IR::ResourceKind::StorageImageUint; const auto view = StorageImageViewKind(state, inst.memory, uint_image, inst.pc); - const auto binding = ResourceForDescriptor(state, StorageBindingKind(uint_image, view), - inst.memory.resource); + const auto binding = + ResourceForDescriptor(state, StorageBindingKind(uint_image, view), inst.memory.resource); const auto image = LoadStorageImageDescriptorAtIndex(state, inst.memory.resource, binding.array_index, uint_image, view); @@ -261,9 +269,9 @@ void EmitImageSample(EmitterState& state, const IR::Instruction& inst) { } else if (integer) { result_type = state.vec4_uint_type; } - const auto explicit_lod = ImageSampleNeedsExplicitLod(state, inst); - const auto opcode = ImageSampleOpcode(state, inst); - std::vector words = {opcode, result_type, sample, sampled_image, base_coord}; + const auto explicit_lod = ImageSampleNeedsExplicitLod(state, inst); + const auto opcode = ImageSampleOpcode(state, inst); + std::vector words = {opcode, result_type, sample, sampled_image, base_coord}; if (dref) { words.push_back(EmitImageDrefF32(state, inst, layout)); } diff --git a/src/graphics/shader/recompiler/emitter/spirvEmitterInternal.h b/src/graphics/shader/recompiler/emitter/spirvEmitterInternal.h index 5b6d23744..71d161963 100644 --- a/src/graphics/shader/recompiler/emitter/spirvEmitterInternal.h +++ b/src/graphics/shader/recompiler/emitter/spirvEmitterInternal.h @@ -99,6 +99,7 @@ enum : uint32_t { ImageOperandsGradMask = 0x00000004u, ImageOperandsOffsetMask = 0x00000010u, ImageOperandsConstOffsetsMask = 0x00000020u, + ImageOperandsSampleMask = 0x00000040u, }; enum : uint32_t { @@ -150,7 +151,6 @@ enum : uint32_t { OpImageGather = 96, OpImageDrefGather = 97, OpImageWrite = 99, - OpImage = 100, OpImageQuerySizeLod = 103, OpImageQueryLod = 105, OpImageQueryLevels = 106, @@ -382,7 +382,7 @@ struct EmitterState { uint32_t ptr_workgroup_array = 0; uint32_t ptr_workgroup_uint = 0; uint32_t lds_variable = 0; - std::array sampled_images; + std::array sampled_images; std::array storage_images; uint32_t sampler_type = 0; uint32_t sampler_array_type = 0; @@ -453,17 +453,20 @@ enum class ImageViewKind { Dim2D, Dim2DArray, Dim3D, + Dim2DMsaa, + Dim2DMsaaArray, Count, }; -constexpr uint32_t ImageViewKindCount = static_cast(ImageViewKind::Count); +constexpr uint32_t SampledImageViewKindCount = static_cast(ImageViewKind::Count); +constexpr uint32_t StorageImageViewKindCount = static_cast(ImageViewKind::Dim2DMsaa); constexpr uint32_t SampledImageIndex(bool integer, ImageViewKind view) { - return static_cast(view) + (integer ? ImageViewKindCount : 0u); + return static_cast(view) + (integer ? SampledImageViewKindCount : 0u); } constexpr uint32_t StorageImageIndex(bool integer, ImageViewKind view) { - return static_cast(view) + (integer ? ImageViewKindCount : 0u); + return static_cast(view) + (integer ? StorageImageViewKindCount : 0u); } constexpr IR::DescriptorBindingKind SampledBindingKind(bool integer, ImageViewKind view) { @@ -474,6 +477,9 @@ constexpr IR::DescriptorBindingKind SampledBindingKind(bool integer, ImageViewKi case ImageViewKind::Dim2D: return IR::DescriptorBindingKind::SampledUint2D; case ImageViewKind::Dim2DArray: return IR::DescriptorBindingKind::SampledUint2DArray; case ImageViewKind::Dim3D: return IR::DescriptorBindingKind::SampledUint3D; + case ImageViewKind::Dim2DMsaa: return IR::DescriptorBindingKind::SampledUint2DMsaa; + case ImageViewKind::Dim2DMsaaArray: + return IR::DescriptorBindingKind::SampledUint2DMsaaArray; default: break; } } @@ -483,6 +489,8 @@ constexpr IR::DescriptorBindingKind SampledBindingKind(bool integer, ImageViewKi case ImageViewKind::Dim2D: return IR::DescriptorBindingKind::Sampled2D; case ImageViewKind::Dim2DArray: return IR::DescriptorBindingKind::Sampled2DArray; case ImageViewKind::Dim3D: return IR::DescriptorBindingKind::Sampled3D; + case ImageViewKind::Dim2DMsaa: return IR::DescriptorBindingKind::Sampled2DMsaa; + case ImageViewKind::Dim2DMsaaArray: return IR::DescriptorBindingKind::Sampled2DMsaaArray; default: break; } return IR::DescriptorBindingKind::Count; @@ -516,6 +524,8 @@ constexpr uint32_t ImageSpirvDimension(ImageViewKind view) { case ImageViewKind::Dim1DArray: return Dim1D; case ImageViewKind::Dim2D: case ImageViewKind::Dim2DArray: + case ImageViewKind::Dim2DMsaa: + case ImageViewKind::Dim2DMsaaArray: case ImageViewKind::Count: return Dim2D; case ImageViewKind::Dim3D: return Dim3D; } @@ -523,7 +533,14 @@ constexpr uint32_t ImageSpirvDimension(ImageViewKind view) { } constexpr uint32_t ImageSpirvArrayed(ImageViewKind view) { - return view == ImageViewKind::Dim1DArray || view == ImageViewKind::Dim2DArray ? 1u : 0u; + return view == ImageViewKind::Dim1DArray || view == ImageViewKind::Dim2DArray || + view == ImageViewKind::Dim2DMsaaArray + ? 1u + : 0u; +} + +constexpr uint32_t ImageSpirvMultisampled(ImageViewKind view) { + return view == ImageViewKind::Dim2DMsaa || view == ImageViewKind::Dim2DMsaaArray ? 1u : 0u; } struct AddCarryResult { diff --git a/src/graphics/shader/recompiler/emitter/spirvEmitterModule.cpp b/src/graphics/shader/recompiler/emitter/spirvEmitterModule.cpp index 89e7fbefb..cec1b73cc 100644 --- a/src/graphics/shader/recompiler/emitter/spirvEmitterModule.cpp +++ b/src/graphics/shader/recompiler/emitter/spirvEmitterModule.cpp @@ -334,15 +334,19 @@ void AddDescriptorAnnotationsAndNames(EmitterState& state) { "sampled_2d", "sampled_2d_array", "sampled_3d", + "sampled_2d_msaa", + "sampled_2d_msaa_array", "sampled_uint_1d", "sampled_uint_1d_array", "sampled_uint_2d", "sampled_uint_2d_array", - "sampled_uint_3d"}; + "sampled_uint_3d", + "sampled_uint_2d_msaa", + "sampled_uint_2d_msaa_array"}; for (uint32_t i = 0; i < state.sampled_images.size(); i++) { - const auto view = static_cast(i % ImageViewKindCount); + const auto view = static_cast(i % SampledImageViewKindCount); Decorate(state.sampled_images[i].variable, SampledNames[i], - SampledBindingKind(i >= ImageViewKindCount, view)); + SampledBindingKind(i >= SampledImageViewKindCount, view)); } constexpr const char* StorageNames[] = {"storage_1d", "storage_1d_array", @@ -355,9 +359,9 @@ void AddDescriptorAnnotationsAndNames(EmitterState& state) { "storage_uint_2d_array", "storage_uint_3d"}; for (uint32_t i = 0; i < state.storage_images.size(); i++) { - const auto view = static_cast(i % ImageViewKindCount); + const auto view = static_cast(i % StorageImageViewKindCount); Decorate(state.storage_images[i].variable, StorageNames[i], - StorageBindingKind(i >= ImageViewKindCount, view)); + StorageBindingKind(i >= StorageImageViewKindCount, view)); } if (state.sampler_variable != 0) { Decorate(state.sampler_variable, "samplers", IR::DescriptorBindingKind::Samplers); @@ -480,7 +484,8 @@ void EmitHeaderAndTypes(EmitterState& state) { if (state.needs_image_gather_extended) { state.builder.AddCapability({CapabilityImageGatherExtended}); } - if (std::any_of(state.storage_images.begin(), state.storage_images.begin() + ImageViewKindCount, + if (std::any_of(state.storage_images.begin(), + state.storage_images.begin() + StorageImageViewKindCount, [](const auto& image) { return image.variable != 0; })) { state.builder.AddCapability({CapabilityStorageImageReadWithoutFormat}); state.builder.AddCapability({CapabilityStorageImageWriteWithoutFormat}); @@ -724,11 +729,12 @@ void EmitHeaderAndTypes(EmitterState& state) { } for (uint32_t i = 0; i < state.sampled_images.size(); i++) { auto& image = state.sampled_images[i]; - const auto view = static_cast(i % ImageViewKindCount); - const bool integer = i >= ImageViewKindCount; + const auto view = static_cast(i % SampledImageViewKindCount); + const bool integer = i >= SampledImageViewKindCount; const auto component = integer ? state.uint_type : state.float_type; state.builder.AddType({OpTypeImage, image.image_type, component, ImageSpirvDimension(view), - 0, ImageSpirvArrayed(view), 0, 1, ImageFormatUnknown}); + 0, ImageSpirvArrayed(view), ImageSpirvMultisampled(view), 1, + ImageFormatUnknown}); state.builder.AddType({OpTypeSampledImage, image.sampled_image_type, image.image_type}); state.builder.AddType( {OpTypePointer, image.pointer_type, StorageClassUniformConstant, image.image_type}); @@ -756,8 +762,8 @@ void EmitHeaderAndTypes(EmitterState& state) { } for (uint32_t i = 0; i < state.storage_images.size(); i++) { auto& image = state.storage_images[i]; - const auto view = static_cast(i % ImageViewKindCount); - const bool integer = i >= ImageViewKindCount; + const auto view = static_cast(i % StorageImageViewKindCount); + const bool integer = i >= StorageImageViewKindCount; const auto component = integer ? state.uint_type : state.float_type; const auto format = integer ? ImageFormatR32ui : ImageFormatUnknown; state.builder.AddType({OpTypeImage, image.image_type, component, ImageSpirvDimension(view), @@ -808,15 +814,15 @@ void AllocateDescriptorVariables(EmitterState& state) { state.flattened_srt_variable = state.builder.AllocateId(); } for (uint32_t i = 0; i < state.sampled_images.size(); i++) { - const auto view = static_cast(i % ImageViewKindCount); - if (DescriptorBinding(state, SampledBindingKind(i >= ImageViewKindCount, view)) != + const auto view = static_cast(i % SampledImageViewKindCount); + if (DescriptorBinding(state, SampledBindingKind(i >= SampledImageViewKindCount, view)) != nullptr) { state.sampled_images[i].variable = state.builder.AllocateId(); } } for (uint32_t i = 0; i < state.storage_images.size(); i++) { - const auto view = static_cast(i % ImageViewKindCount); - if (DescriptorBinding(state, StorageBindingKind(i >= ImageViewKindCount, view)) != + const auto view = static_cast(i % StorageImageViewKindCount); + if (DescriptorBinding(state, StorageBindingKind(i >= StorageImageViewKindCount, view)) != nullptr) { state.storage_images[i].variable = state.builder.AllocateId(); } diff --git a/src/graphics/shader/recompiler/ir/BindingLayout.cpp b/src/graphics/shader/recompiler/ir/BindingLayout.cpp index f29d4a528..0667a13fb 100644 --- a/src/graphics/shader/recompiler/ir/BindingLayout.cpp +++ b/src/graphics/shader/recompiler/ir/BindingLayout.cpp @@ -13,16 +13,30 @@ namespace { constexpr uint32_t MaxPushConstantBytes = 128; constexpr std::array ImageBindingKinds = { - DescriptorBindingKind::Sampled1D, DescriptorBindingKind::Sampled1DArray, - DescriptorBindingKind::Sampled2D, DescriptorBindingKind::Sampled2DArray, - DescriptorBindingKind::Sampled3D, DescriptorBindingKind::SampledUint1D, - DescriptorBindingKind::SampledUint1DArray, DescriptorBindingKind::SampledUint2D, - DescriptorBindingKind::SampledUint2DArray, DescriptorBindingKind::SampledUint3D, - DescriptorBindingKind::Storage1D, DescriptorBindingKind::Storage1DArray, - DescriptorBindingKind::Storage2D, DescriptorBindingKind::Storage2DArray, - DescriptorBindingKind::Storage3D, DescriptorBindingKind::StorageUint1D, - DescriptorBindingKind::StorageUint1DArray, DescriptorBindingKind::StorageUint2D, - DescriptorBindingKind::StorageUint2DArray, DescriptorBindingKind::StorageUint3D, + DescriptorBindingKind::Sampled1D, + DescriptorBindingKind::Sampled1DArray, + DescriptorBindingKind::Sampled2D, + DescriptorBindingKind::Sampled2DArray, + DescriptorBindingKind::Sampled2DMsaa, + DescriptorBindingKind::Sampled2DMsaaArray, + DescriptorBindingKind::Sampled3D, + DescriptorBindingKind::SampledUint1D, + DescriptorBindingKind::SampledUint1DArray, + DescriptorBindingKind::SampledUint2D, + DescriptorBindingKind::SampledUint2DArray, + DescriptorBindingKind::SampledUint2DMsaa, + DescriptorBindingKind::SampledUint2DMsaaArray, + DescriptorBindingKind::SampledUint3D, + DescriptorBindingKind::Storage1D, + DescriptorBindingKind::Storage1DArray, + DescriptorBindingKind::Storage2D, + DescriptorBindingKind::Storage2DArray, + DescriptorBindingKind::Storage3D, + DescriptorBindingKind::StorageUint1D, + DescriptorBindingKind::StorageUint1DArray, + DescriptorBindingKind::StorageUint2D, + DescriptorBindingKind::StorageUint2DArray, + DescriptorBindingKind::StorageUint3D, }; bool ImageBinding(const ImageResource& image, DescriptorBindingKind& result) { @@ -36,6 +50,8 @@ bool ImageBinding(const ImageResource& image, DescriptorBindingKind& result) { case Dimension::Dim1DArray: result = Kind::Sampled1DArray; return true; case Dimension::Dim2D: result = Kind::Sampled2D; return true; case Dimension::Dim2DArray: result = Kind::Sampled2DArray; return true; + case Dimension::Dim2DMsaa: result = Kind::Sampled2DMsaa; return true; + case Dimension::Dim2DMsaaArray: result = Kind::Sampled2DMsaaArray; return true; case Dimension::Dim3D: result = Kind::Sampled3D; return true; default: return false; } @@ -45,6 +61,8 @@ bool ImageBinding(const ImageResource& image, DescriptorBindingKind& result) { case Dimension::Dim1DArray: result = Kind::SampledUint1DArray; return true; case Dimension::Dim2D: result = Kind::SampledUint2D; return true; case Dimension::Dim2DArray: result = Kind::SampledUint2DArray; return true; + case Dimension::Dim2DMsaa: result = Kind::SampledUint2DMsaa; return true; + case Dimension::Dim2DMsaaArray: result = Kind::SampledUint2DMsaaArray; return true; case Dimension::Dim3D: result = Kind::SampledUint3D; return true; default: return false; } @@ -71,7 +89,7 @@ bool ImageBinding(const ImageResource& image, DescriptorBindingKind& result) { } bool CollectValue(const ScalarProvenance& provenance, uint32_t id, std::vector& visited, - std::set& registers) { + std::set& registers) { if (id <= ScalarProvenance::Unknown) { return true; } @@ -104,7 +122,7 @@ bool CollectValue(const ScalarProvenance& provenance, uint32_t id, std::vector& visited, std::set& registers) { + std::vector& visited, std::set& registers) { if (allow_unknown && source == ScalarProvenance::Unknown) { return true; } @@ -165,8 +183,7 @@ bool CollectUserData(const Program& program, std::vector& result) { return false; } for (uint32_t i = 0; i < inst.src_count; i++) { - if (!CollectValue(program.provenance, inst.scalar_sources[i], visited, - registers)) { + if (!CollectValue(program.provenance, inst.scalar_sources[i], visited, registers)) { return false; } } @@ -199,7 +216,7 @@ bool AllocateBindings(Program& program, const BindingLayoutOptions& options, std if (!program.shader_info_complete || program.binding_layout_complete) { if (error != nullptr) { *error = !program.shader_info_complete ? "shader info is not ready" - : "binding layout already allocated"; + : "binding layout already allocated"; } return false; } diff --git a/src/graphics/shader/recompiler/ir/ResourceMaterialization.cpp b/src/graphics/shader/recompiler/ir/ResourceMaterialization.cpp index 752914a63..f686c94cb 100644 --- a/src/graphics/shader/recompiler/ir/ResourceMaterialization.cpp +++ b/src/graphics/shader/recompiler/ir/ResourceMaterialization.cpp @@ -15,7 +15,8 @@ constexpr uint64_t AddressMask = 0x0000ffffffffffffull; Decoder::ImageDimension DescriptorDimension(const DescriptorValue& descriptor, Decoder::ImageDimension requested) { const bool is_array = requested == Decoder::ImageDimension::Dim1DArray || - requested == Decoder::ImageDimension::Dim2DArray; + requested == Decoder::ImageDimension::Dim2DArray || + requested == Decoder::ImageDimension::Dim2DMsaaArray; switch (static_cast((descriptor.dwords[3] >> 28u) & 0xfu)) { case Prospero::ImageType::kColor1D: return Decoder::ImageDimension::Dim1D; case Prospero::ImageType::kColor1DArray: @@ -26,13 +27,17 @@ Decoder::ImageDimension DescriptorDimension(const DescriptorValue& descriptor, case Prospero::ImageType::kColor3D: return Decoder::ImageDimension::Dim3D; case Prospero::ImageType::kCube: return Decoder::ImageDimension::Dim2DArray; case Prospero::ImageType::kColor2DArray: - case Prospero::ImageType::kColor2DMsaaArray: if (is_array) { return Decoder::ImageDimension::Dim2DArray; } return Decoder::ImageDimension::Dim2D; - case Prospero::ImageType::kColor2D: - case Prospero::ImageType::kColor2DMsaa: return Decoder::ImageDimension::Dim2D; + case Prospero::ImageType::kColor2DMsaaArray: + if (is_array) { + return Decoder::ImageDimension::Dim2DMsaaArray; + } + return Decoder::ImageDimension::Dim2DMsaa; + case Prospero::ImageType::kColor2D: return Decoder::ImageDimension::Dim2D; + case Prospero::ImageType::kColor2DMsaa: return Decoder::ImageDimension::Dim2DMsaa; default: return Decoder::ImageDimension::Unknown; } } diff --git a/src/graphics/shader/recompiler/ir/ShaderIR.h b/src/graphics/shader/recompiler/ir/ShaderIR.h index 29a13b695..9f94601ca 100644 --- a/src/graphics/shader/recompiler/ir/ShaderIR.h +++ b/src/graphics/shader/recompiler/ir/ShaderIR.h @@ -679,11 +679,15 @@ enum class DescriptorBindingKind { Sampled1DArray, Sampled2D, Sampled2DArray, + Sampled2DMsaa, + Sampled2DMsaaArray, Sampled3D, SampledUint1D, SampledUint1DArray, SampledUint2D, SampledUint2DArray, + SampledUint2DMsaa, + SampledUint2DMsaaArray, SampledUint3D, Storage1D, Storage1DArray, diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 271ba3a96..d6fbd1a92 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -5774,7 +5774,7 @@ public: std::make_shared( *array_runtime.program); colliding_msaa_program->info.images[0].dimension = - ShaderRecompiler::Decoder::ImageDimension::Dim2D; + ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaa; auto colliding_msaa_snapshot = std::make_shared(); colliding_msaa_snapshot->images.push_back(colliding_msaa_descriptor); @@ -5836,7 +5836,7 @@ public: auto msaa_program = std::make_shared( *array_runtime.program); msaa_program->info.images[0].dimension = - ShaderRecompiler::Decoder::ImageDimension::Dim2D; + ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaa; auto msaa_snapshot = std::make_shared(); msaa_snapshot->images.push_back(msaa_descriptor); @@ -5877,7 +5877,7 @@ public: auto msaa_array_program = std::make_shared( *msaa_runtime.program); msaa_array_program->info.images[0].dimension = - ShaderRecompiler::Decoder::ImageDimension::Dim2DArray; + ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaaArray; auto msaa_array_snapshot = std::make_shared(); msaa_array_snapshot->images.push_back(msaa_array_descriptor); diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index 1e56230ca..e46b493f8 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -221,7 +221,7 @@ uint32_t SpirvExtInstCount(const std::vector& binary, uint32_t ext_ins } bool SpirvContainsTypeImage(const std::vector& binary, uint32_t dim, uint32_t arrayed, - uint32_t sampled) { + uint32_t sampled, uint32_t multisampled = 0) { for (size_t i = 5; i < binary.size();) { const uint32_t word = binary[i]; const uint32_t opcode = word & 0xffffu; @@ -230,7 +230,7 @@ bool SpirvContainsTypeImage(const std::vector& binary, uint32_t dim, u return false; } if (opcode == 25u && word_count >= 9u && binary[i + 3] == dim && binary[i + 5] == arrayed && - binary[i + 7] == sampled) { + binary[i + 6] == multisampled && binary[i + 7] == sampled) { return true; } i += word_count; @@ -2972,7 +2972,6 @@ void TestNewShaderRecompilerMemoryFamilyLowering() { Check(SpirvContainsOpcode(result.spirv, 61), "SPIR-V binary does not contain OpLoad"); Check(SpirvContainsOpcode(result.spirv, 62), "SPIR-V binary does not contain OpStore"); Check(SpirvContainsOpcode(result.spirv, 95), "SPIR-V binary does not contain OpImageFetch"); - Check(SpirvContainsOpcode(result.spirv, 100), "SPIR-V binary does not contain OpImage"); Check(SpirvContainsOpcode(result.spirv, 103), "SPIR-V binary does not contain OpImageQuerySizeLod"); Check(SpirvContainsOpcode(result.spirv, 88), @@ -3766,6 +3765,50 @@ void TestNewShaderRecompilerImageLoadVariants() { CheckSpirvBinaryValidates(result.spirv); } +void TestNewShaderRecompilerImageLoad2DMsaa() { + const uint32_t shader[] = { + 0xf0000130u, // image_load v3, v[5:7], s[0:7] dmask:x dim:2d_msaa + 0x00000305u, + 0xbf810000u, + }; + + auto user_data = ImageTestUserData(Prospero::ImageType::kColor2DMsaa); + user_data[3] |= 2u << 16u; + user_data[5] |= 2u << 4u; + user_data[6] |= 1u << 10u; + + ShaderRecompiler::CompileOptions options; + options.stage = ShaderType::Pixel; + options.dump_ir = true; + options.user_data = user_data.data(); + + ShaderRecompiler::CompileResult result; + std::string error; + Check(ShaderRecompiler::TryRecompile(shader, options, result, &error), error.c_str()); + Check(Common::ContainsStr(result.decoded_dump, "image_dim=2d_msaa") && + Common::ContainsStr(result.ir_dump, "image_dim=2d_msaa") && + Common::ContainsStr(result.ir_dump, "image_addr=3 image_mip=0"), + "RDNA2 2D-MSAA load did not preserve x, y, and fragment ID"); + Check(result.program.info.images.size() == 1 && + result.program.info.images[0].dimension == + ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaa, + "2D-MSAA descriptor specialization lost the multisample dimension"); + Check(ShaderRecompiler::IR::FindBinding( + result.program.bindings, + ShaderRecompiler::IR::DescriptorBindingKind::Sampled2DMsaa) != nullptr, + "2D-MSAA image did not receive a multisampled descriptor binding"); + Check(SpirvContainsTypeImage(result.spirv, 1, 0, 1, 1), + "SPIR-V binary does not contain a multisampled 2D image type"); + CheckSpirvBinaryValidates(result.spirv); + const auto source = DisassembleSpirvBinary(result.spirv); + Check(SpirvSourceHasInstructionUsing(source, "OpAccessChain", "sampled_2d_msaa"), + "2D-MSAA load did not access its multisampled descriptor"); + Check(SpirvSourceHasInstructionUsing(source, "OpImageFetch", " Sample "), + "2D-MSAA load did not emit the fragment ID as a SPIR-V Sample operand"); + Check(!SpirvSourceHasInstructionUsing(source, "OpImageFetch", " Lod "), + "2D-MSAA load incorrectly emitted its fragment ID as a mip level"); +} + void TestNewShaderRecompilerImageStoreLowering() { const uint32_t shader[] = { EncodeMimg0(0x08, 0xf), @@ -7018,6 +7061,7 @@ int main() { TestNewShaderRecompilerImageGatherVariants(); TestNewShaderRecompilerImageLoadA16UintCoords(); TestNewShaderRecompilerImageLoadVariants(); + TestNewShaderRecompilerImageLoad2DMsaa(); TestNewShaderRecompilerImageStoreLowering(); TestNewShaderRecompilerStorageImage3DDescriptorVariant(); TestNewShaderRecompilerStorageImage2DDescriptorOverridesMimg3D();