diff --git a/src/graphics/shader/recompiler/frontend/decode/ImageOps.cpp b/src/graphics/shader/recompiler/frontend/decode/ImageOps.cpp index 5d3edd9cf..fb55948ec 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ImageOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/ImageOps.cpp @@ -320,27 +320,15 @@ uint32_t CountDmaskComponents(uint32_t dmask) { } // namespace ImageAddressComponent ImageAddressComponentLayout(uint32_t flags, uint32_t component) { - const auto width = [flags](uint32_t index) { - if ((flags & ImageSampleFlagA16) == 0u) return 32u; - uint32_t cursor = 0; - if ((flags & ImageSampleFlagOffset) != 0u) { - if (index == cursor++) return 32u; - } - if ((flags & ImageSampleFlagBias) != 0u) { - if (index == cursor++) return 16u; - } - if ((flags & ImageSampleFlagCompare) != 0u && index == cursor) return 32u; - return 16u; - }; - uint32_t offset = 0; - for (uint32_t index = 0; index < component; index++) { - const auto bits = width(index); - if (bits == 32u) offset = (offset + 31u) & ~31u; - offset += bits; - } - const auto bits = width(component); - if (bits == 32u) offset = (offset + 31u) & ~31u; - return {offset, bits}; + if ((flags & ImageSampleFlagA16) == 0u) return {component * 32u, 32u}; + // A16 offsets, bias and PCF reference each take a whole VGPR, the bias as a half in its low + // 16 bits; the components after them pack two per VGPR. + const bool has_bias = (flags & ImageSampleFlagBias) != 0u; + const uint32_t bias_index = (flags & ImageSampleFlagOffset) != 0u ? 1u : 0u; + const uint32_t whole_vgprs = std::popcount( + flags & (ImageSampleFlagOffset | ImageSampleFlagBias | ImageSampleFlagCompare)); + if (component >= whole_vgprs) return {(whole_vgprs + component) * 16u, 16u}; + return {component * 32u, has_bias && component == bias_index ? 16u : 32u}; } uint32_t ImageAddressDwordCount(uint32_t flags, uint32_t components) { diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index b9d339e25..3eb5df97d 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -40015,6 +40015,46 @@ TestCase ImageSampleA16CompareBiasRdna2AddressOrder() { return test; } +u32 RampTexel(u32 x, u32 y) { return std::bit_cast(float(1u + x + y * 4u)); } + +std::vector MakeRampImage() { + auto image = MakeRgbaImage(4, 4); + for (u32 y = 0; y < 4u; y++) { + for (u32 x = 0; x < 4u; x++) { + SetRgbaPixel(&image, 4, x, y, RampTexel(x, y), 0, 0, 0); + } + } + return image; +} + +TestCase ImageA16BiasTakesWholeVgpr() { + using O = ShaderOpcode; + TestCase test; + test.name = "ImageA16BiasTakesWholeVgpr"; + // The high half of the bias VGPR holds 0.0625, which would move the coordinates if read as one. + AppendVMovLiteral(&test.code, 20, 0x2c000000u); // bias 0 + AppendVMovLiteral(&test.code, 21, 0x36003900u); // x=0.625, y=0.375: texel (2, 1) + test.code.push_back(EncodeMimg0(0x25, 0x1)); // image_sample_b + test.code.push_back(EncodeMimg1(0, 20, 0, 0, true)); + AppendStoreVgpr(&test.code, 0, 0); + test.expected.push_back(RampTexel(2, 1)); + AppendVMovLiteral(&test.code, 22, 0x2c000000u); // bias 0 + AppendVMovLiteral(&test.code, 23, 0x37003980u); // x=0.6875, y=0.4375: x 2..3, y 1..2 + test.code.push_back(EncodeMimg0(0x45, 0x1)); // image_gather4_b + test.code.push_back(EncodeMimg1(4, 22, 0, 0, true)); + for (u32 component = 0; component < 4u; component++) { + AppendStoreVgpr(&test.code, 4u + component, 1u + component); + } + for (const auto [x, y] : {std::pair{2u, 2u}, {3u, 2u}, {3u, 1u}, {2u, 1u}}) { + test.expected.push_back(RampTexel(x, y)); + } + AppendEnd(&test.code); + test.opcodes = {O::V_MOV_B32, O::IMAGE_SAMPLE, O::IMAGE_GATHER4, O::BUFFER_STORE_DWORD, + O::S_ENDPGM}; + test.sampled_image_rgba = MakeRampImage(); + return test; +} + TestCase ImageGatherAddressVariants() { using O = ShaderOpcode; struct Gather { @@ -40035,13 +40075,6 @@ TestCase ImageGatherAddressVariants() { {0xd1, true, false, true}, {0xd5, true, true, false}, {0xd6, true, true, true}, }; - const auto texel = [](u32 x, u32 y) { return std::bit_cast(float(1u + x + y * 4u)); }; - auto image = MakeRgbaImage(4, 4); - for (u32 y = 0; y < 4u; y++) { - for (u32 x = 0; x < 4u; x++) { - SetRgbaPixel(&image, 4, x, y, texel(x, y), 0, 0, 0); - } - } TestCase test; test.name = "ImageGatherAddressVariants"; for (u32 index = 0; index < std::size(gathers); index++) { @@ -40062,13 +40095,13 @@ TestCase ImageGatherAddressVariants() { // (x0, y0) order. const u32 x0 = gather.offset ? 1u : 2u; for (const auto [x, y] : {std::pair{x0, 2u}, {x0 + 1u, 2u}, {x0 + 1u, 1u}, {x0, 1u}}) { - test.expected.push_back(texel(x, y)); + test.expected.push_back(RampTexel(x, y)); } } AppendEnd(&test.code); test.opcodes = {O::V_MOV_B32, O::IMAGE_GATHER4, O::BUFFER_STORE_DWORD, O::S_ENDPGM}; - test.sampled_image_rgba = image; + test.sampled_image_rgba = MakeRampImage(); return test; } @@ -45901,6 +45934,7 @@ std::vector MakeCases() { AddCase(ImageSampleOffsetsReadFourNeighbors<32>); AddCase(ImageSampleOffsetsReadFourNeighbors<64>); AddCase(ImageSampleA16CompareBiasRdna2AddressOrder); + AddCase(ImageA16BiasTakesWholeVgpr); AddCase(ImageGatherCompareOpcodes); AddCase(ImageStoreVariants); AddCase(ImageD16StoreUnpacksHalfPairs); @@ -52379,6 +52413,7 @@ int main(int argc, char **argv) { RunCase(&vulkan, ImageSampleA16OffsetKeepsTexelOffset32BitOnGpu<64>()); RunCase(&vulkan, ImageSampleAndGather()); RunCase(&vulkan, ImageGatherAddressVariants()); + RunCase(&vulkan, ImageA16BiasTakesWholeVgpr()); RunCase(nullptr, ImageGatherCompareOpcodes()); return 0; }