diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterImage.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterImage.cpp index daa18b36d..52e0c56b5 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterImage.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterImage.cpp @@ -3,7 +3,9 @@ #include "graphics/shader/recompiler/frontend/decode/ImageOps.h" #include +#include #include +#include namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter { namespace { @@ -646,6 +648,13 @@ void EmitImage(ValueEmitContext& ctx, const IR::Inst& inst) { const auto coord = CoordF32(ctx, mem, *address, layout.coord, dimension_info.coordinate_components); if (op == IR::ValueOpcode::ImageGatherRaw) { + if (HasFlag(mem, Decoder::ImageSampleFlagLod)) { + static std::atomic_flag warned = ATOMIC_FLAG_INIT; + if (!warned.test_and_set(std::memory_order_relaxed)) { + std::fputs("Warning: approximating IMAGE_GATHER4_L at mip level 0; explicit LOD is ignored.\n", + stderr); + } + } if (dimension == ImageDimension::Dim1D) { if (dref || !HasFlag(mem, Decoder::ImageSampleFlagLevelZero) || HasFlag(mem, Decoder::ImageSampleFlagOffset) || diff --git a/src/graphics/shader/recompiler/frontend/decode/ImageOps.cpp b/src/graphics/shader/recompiler/frontend/decode/ImageOps.cpp index 1337f43ac..932df888a 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ImageOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/ImageOps.cpp @@ -175,6 +175,7 @@ constexpr MimgSampleInfo MIMG_SAMPLE_OPCODE_LIST[] = { }; constexpr MimgGatherInfo MIMG_GATHER_OPCODE_LIST[] = { + {0x44u, Opcode::IMAGE_GATHER4_L, ImageSampleFlagLod}, {0x47u, Opcode::IMAGE_GATHER4_LZ, ImageSampleFlagLevelZero}, {0x48u, Opcode::IMAGE_GATHER4_C, ImageSampleFlagCompare}, {0x4fu, Opcode::IMAGE_GATHER4_C_LZ, diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp index 1056cf258..028c715cc 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp @@ -144,6 +144,7 @@ std::string FormatMimg(const Instruction& inst) { } switch (inst.opcode) { case Opcode::IMAGE_SAMPLE: + case Opcode::IMAGE_GATHER4_L: case Opcode::IMAGE_GATHER4_LZ: case Opcode::IMAGE_GATHER4_C: case Opcode::IMAGE_GATHER4_C_LZ: @@ -580,6 +581,7 @@ std::string InstructionToString(const Instruction& inst) { case Opcode::IMAGE_LOAD_MIP: case Opcode::IMAGE_GET_RESINFO: case Opcode::IMAGE_GET_LOD: + case Opcode::IMAGE_GATHER4_L: case Opcode::IMAGE_GATHER4_LZ: case Opcode::IMAGE_GATHER4_C: case Opcode::IMAGE_GATHER4_C_LZ: diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h index 20e7d0661..08b0a3059 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h @@ -559,6 +559,7 @@ enum class Opcode { IMAGE_ATOMIC_OR, IMAGE_ATOMIC_XOR, IMAGE_SAMPLE, + IMAGE_GATHER4_L, IMAGE_GATHER4_LZ, IMAGE_GATHER4_C, IMAGE_GATHER4_C_LZ, diff --git a/src/graphics/shader/recompiler/frontend/translate/Memory.cpp b/src/graphics/shader/recompiler/frontend/translate/Memory.cpp index ad6cd4286..de40e4281 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Memory.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Memory.cpp @@ -1034,6 +1034,7 @@ bool Translator::EmitMemory(const Decoder::Instruction& inst) { case Decoder::Opcode::IMAGE_STORE: case Decoder::Opcode::IMAGE_STORE_MIP: return IMAGE_STORE(inst); case Decoder::Opcode::IMAGE_SAMPLE: return IMAGE_SAMPLE(inst); + case Decoder::Opcode::IMAGE_GATHER4_L: case Decoder::Opcode::IMAGE_GATHER4_LZ: case Decoder::Opcode::IMAGE_GATHER4_C: case Decoder::Opcode::IMAGE_GATHER4_C_LZ: diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index de0fa8181..3ade1f407 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -12271,7 +12271,7 @@ public: return ret; } - vk::Sampler CreateNearestSampler(const char *shader_name) { + vk::Sampler CreateNearestSampler(const char *shader_name, u32 mip_levels = 1) { vk::SamplerCreateInfo sampler_info{}; sampler_info.sType = vk::StructureType::eSamplerCreateInfo; sampler_info.magFilter = vk::Filter::eNearest; @@ -12281,7 +12281,7 @@ public: sampler_info.addressModeV = vk::SamplerAddressMode::eClampToEdge; sampler_info.addressModeW = vk::SamplerAddressMode::eClampToEdge; sampler_info.minLod = 0.0f; - sampler_info.maxLod = 0.0f; + sampler_info.maxLod = static_cast(mip_levels - 1u); vk::Sampler sampler = nullptr; RequireVk(shader_name, "dispatch", m_device.createSampler(&sampler_info, nullptr, &sampler), @@ -15484,7 +15484,7 @@ void RunCase(VulkanHarness *vulkan, const TestCase &test) { vk::ImageLayout::eGeneral); } if (needs_sampler) { - sampler = vulkan->CreateNearestSampler(test.name); + sampler = vulkan->CreateNearestSampler(test.name, sampled_image.mip_levels); } vulkan->Dispatch(test, compiled, buffer, needs_gds ? &gds_buffer : nullptr, @@ -25001,6 +25001,34 @@ TestCase ImageSampleAndGather() { return test; } +TestCase ImageGatherLodApproximatesLevelZero() { + using O = ShaderOpcode; + std::vector code; + AppendVMovLiteral(&code, 20, 0x3f000000u); + AppendVMovLiteral(&code, 21, 0x3f000000u); + AppendVMovLiteral(&code, 22, 0x3f800000u); + code.push_back(EncodeMimg0(0x44, 0x1)); + code.push_back(EncodeMimg1(0, 20)); + code.push_back(EncodeMimg0(0x24, 0x1)); + code.push_back(EncodeMimg1(4, 20)); + for (u32 i = 0; i < 5u; i++) { + AppendStoreVgpr(&code, i, i); + } + AppendEnd(&code); + + TestCase test; + test.name = "ImageGatherLodApproximatesLevelZero"; + test.code = std::move(code); + test.opcodes = {O::V_MOV_B32, O::IMAGE_GATHER4_L, O::IMAGE_SAMPLE, + O::BUFFER_STORE_DWORD, O::S_ENDPGM}; + // Explicit sampling proves mip 1 is accessible; the gather must still read mip 0. + test.expected = {0x3f800000u, 0x3f800000u, 0x3f800000u, 0x3f800000u, 0x41000000u}; + test.sampled_image_rgba_mips = {std::vector(4 * 4 * 4, 0x3f800000u), + std::vector(2 * 2 * 4, 0x41000000u)}; + test.required_spirv = {"OpImageGather", "OpImageSampleExplicitLod"}; + return test; +} + TestCase ImageD16GatherPacksHalfPairs() { using O = ShaderOpcode; @@ -26575,6 +26603,7 @@ std::vector MakeCases() { AddCase(ImageGetResinfoDmaskWidthHeight); AddCase(ImageGetResinfoDmaskMipLevels); AddCase(ImageSampleAndGather); + AddCase(ImageGatherLodApproximatesLevelZero); AddCase(ImageD16GatherPacksHalfPairs); AddCase(ImageSampleA16SamplerCoordsOnGpu); AddCase(ImageSampleOpcodeAliasUsesNormalCoords); @@ -31166,6 +31195,7 @@ int main(int argc, char **argv) { RunCase(&vulkan, BranchVccnzUsesWaveMask()); RunCase(&vulkan, BranchVccnzUsesCarryProducedWaveMask()); RunCase(&vulkan, ImageSampleAndGather()); + RunCase(&vulkan, ImageGatherLodApproximatesLevelZero()); RunCase(&vulkan, SharedReturnKeepsSelectedValues()); return 0; }