diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/ImageAddressDimension.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/ImageAddressDimension.cpp index 0910f962a..afc758033 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/ImageAddressDimension.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/ImageAddressDimension.cpp @@ -25,12 +25,15 @@ constexpr std::uint32_t Size = 16; constexpr std::uint32_t Levels = 5; constexpr std::uint32_t Format8888UNorm = 56; constexpr std::uint32_t Type2D = 9; +constexpr std::uint32_t Type3D = 10; +constexpr std::uint32_t Depth = 2; constexpr std::uint32_t ArrayInput = 0; constexpr std::uint32_t ArrayResult = 8; constexpr std::uint32_t PlainInput = 16; constexpr std::uint32_t PlainResult = 24; alignas(256) std::array Buffer{}; alignas(256) std::array Texels{}; +alignas(256) std::array Volume{}; alignas(256) constexpr std::array Code{ 0x34020087, 0xe0301000, 0x80000201, 0xe0301004, 0x80000301, 0xe0301008, 0x80000401, 0xe030100c, @@ -44,6 +47,12 @@ alignas(256) constexpr std::array Code{ 0xe0701074, 0x80000d01, 0xe0701078, 0x80000e01, 0xe070107c, 0x80000f01, 0xbf810000, }; +alignas(256) constexpr std::array VolumeCode{ + 0x34020087, 0xe030104c, 0x80001301, 0xe0301050, 0x80001401, 0xbf8c3f70, 0xf0001f08, 0x00010c13, + 0xe0701070, 0x80000c01, 0xe0701074, 0x80000d01, 0xe0701078, 0x80000e01, 0xe070107c, 0x80000f01, + 0xbf810000, +}; + alignas(256) constexpr std::array NarrowCode{ 0x34020087, 0xe0301040, 0x80001001, 0xe0301044, 0x80001101, 0xbf8c3f70, 0xf0900f00, 0x00610c10, 0xe0701070, 0x80000c01, 0xbf810000, @@ -117,14 +126,49 @@ std::array TextureDescriptor(const void* data) { }; } +std::array VolumeDescriptor() { + const auto address = static_cast(reinterpret_cast(Volume.data())); + return { + static_cast(address >> 8u), + static_cast((address >> 40u) & 0xffu) | (Format8888UNorm << 20u) | (((Size - 1u) & 3u) << 30u), + ((Size - 1u) >> 2u) | ((Size - 1u) << 14u), + 0xfacu | (Type3D << 28u), + Depth - 1u, + 0u, + 0u, + 0u, + }; +} + +std::uint8_t VolumeSliceMarker(std::uint32_t z) { + return static_cast(z * 100u + 7u); +} + +void FillVolume() { + const auto geometry = AgcDriver::Graphics::DescribeSurface(AgcDriver::Graphics::DecodeTextureResource(VolumeDescriptor())); + Require(geometry.guestBytes <= Volume.size(), "image address dimension: the volume does not fit the texel storage"); + const auto& mip = geometry.mips.at(0); + Volume.fill(0xeeu); + for (std::uint32_t z = 0; z < Depth; ++z) { + for (std::uint32_t y = 0; y < Size; ++y) { + for (std::uint32_t x = 0; x < Size; ++x) { + auto* texel = &Volume[geometry.GuestLayerOffset(z) + mip.tiledOffset + static_cast(y) * mip.pitchBytes + x * 4u]; + texel[0] = VolumeSliceMarker(z); + texel[1] = static_cast(x); + texel[2] = static_cast(y); + texel[3] = 255u; + } + } + } +} + std::array SamplerDescriptor() { return {0u, 0xfffu << 12u, 1u << 26u, 0u}; } -void Run(AgcDriver::VulkanDevice& device, std::span code) { +void Run(AgcDriver::VulkanDevice& device, std::span code, const std::array& texture) { std::vector userData(16, 0u); const auto buffer = BufferDescriptor(Buffer.data(), static_cast(Buffer.size() * 4u)); - const auto texture = TextureDescriptor(Texels.data()); const auto sampler = SamplerDescriptor(); std::copy(buffer.begin(), buffer.end(), userData.begin()); std::copy(texture.begin(), texture.end(), userData.begin() + 4); @@ -158,10 +202,21 @@ void Check() { } } +void CheckVolume() { + for (std::uint32_t tid = 0; tid < Threads; ++tid) { + const auto sample = SampleOf(tid); + const std::array expected{VolumeSliceMarker(0), sample.x >> sample.level, sample.y >> sample.level, 255u}; + for (std::uint32_t component = 0; component < 4u; ++component) { + const float value = std::bit_cast(Buffer[tid * Words + PlainResult + 4u + component]) * 255.0f; + Require(std::lround(value) == static_cast(expected[component]), "image_load 2d on a 3D texture: thread " + std::to_string(tid) + " component " + std::to_string(component) + " is " + std::to_string(value) + ", expected " + std::to_string(expected[component])); + } + } +} + void RequireRefused(AgcDriver::VulkanDevice& device) { std::string refusal; try { - Run(device, NarrowCode); + Run(device, NarrowCode, TextureDescriptor(Texels.data())); } catch (const std::exception& error) { refusal = error.what(); } @@ -176,8 +231,11 @@ int main() { if (!device) return VulkanTestSkipped; FillInput(); FillTexture(); - Run(*device, Code); + Run(*device, Code, TextureDescriptor(Texels.data())); Check(); + FillVolume(); + Run(*device, VolumeCode, VolumeDescriptor()); + CheckVolume(); RequireRefused(*device); std::puts("image address dimension tests passed"); return 0; diff --git a/core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp b/core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp index 9c8599186..bf83d37f2 100644 --- a/core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp +++ b/core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp @@ -190,17 +190,19 @@ std::uint32_t CoordF32(SpirvValueEmitContext& ctx, const ImageEmitAccess& access std::uint32_t CoordU32(SpirvValueEmitContext& ctx, const ImageEmitAccess& access) { const auto components = RdnaImageDimensionInfoFor(access.image.dimension).coordinateComponents; - if (AddressDimension(access).coordinateComponents < components || access.mem.imageAddressComponents < components) { + const auto encoded = std::min(components, AddressDimension(access).coordinateComponents); + if (access.mem.imageAddressComponents < encoded) { ctx.Fail(access.inst, "has an image address with too few coordinate components"); } - const auto x = AddressU32(ctx, access, 0); + const auto component = [&](std::uint32_t index) { return index < encoded ? AddressU32(ctx, access, index) : ConstantU32(ctx.state, 0); }; + const auto x = component(0); if (components == 1u) { return x; } - const auto y = AddressU32(ctx, access, 1); + const auto y = component(1); const auto result = ctx.state.module.AllocateId(); if (components == 3u) { - const auto z = AddressU32(ctx, access, 2); + const auto z = component(2); ctx.state.module.AddFunction(spv::OpCompositeConstruct, TypeU32Vector(ctx.state, 3), result, x, y, z); } else { ctx.state.module.AddFunction(spv::OpCompositeConstruct, TypeU32Vector(ctx.state, 2), result, x, y);