diff --git a/src/graphics/shader/recompiler/frontend/decode/ImageOps.cpp b/src/graphics/shader/recompiler/frontend/decode/ImageOps.cpp index 4469a6bcd..5d3edd9cf 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ImageOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/ImageOps.cpp @@ -14,12 +14,6 @@ struct MimgSampleInfo { uint32_t flags = 0; }; -struct MimgGatherInfo { - uint32_t encoding = 0; - Opcode decoded = Opcode::UNSUPPORTED; - uint32_t flags = 0; -}; - struct MimgAtomicInfo { uint32_t encoding = 0; Opcode decoded = Opcode::UNSUPPORTED; @@ -181,23 +175,72 @@ constexpr MimgSampleInfo MIMG_SAMPLE_OPCODE_LIST[] = { ImageSampleFlagOffset | ImageSampleFlagAdjust}, }; -constexpr MimgGatherInfo MIMG_GATHER_OPCODE_LIST[] = { - {0x44u, Opcode::IMAGE_GATHER4_L, ImageSampleFlagLod}, - {0x47u, Opcode::IMAGE_GATHER4_LZ, ImageSampleFlagLevelZero}, - {0x48u, Opcode::IMAGE_GATHER4_C, ImageSampleFlagCompare}, - {0x4cu, Opcode::IMAGE_GATHER4_C_L, ImageSampleFlagCompare | ImageSampleFlagLod}, - {0x4fu, Opcode::IMAGE_GATHER4_C_LZ, - ImageSampleFlagCompare | ImageSampleFlagLevelZero}, - {0x57u, Opcode::IMAGE_GATHER4_LZ_O, - ImageSampleFlagLevelZero | ImageSampleFlagOffset}, - {0x58u, Opcode::IMAGE_GATHER4_C_O, - ImageSampleFlagCompare | ImageSampleFlagOffset}, - {0x5fu, Opcode::IMAGE_GATHER4_C_LZ_O, +constexpr MimgSampleInfo MIMG_GATHER_OPCODE_LIST[] = { + {0x40u, "image_gather4", 0}, + {0x41u, "image_gather4_cl", ImageSampleFlagLodClamp}, + {0x44u, "image_gather4_l", ImageSampleFlagLod}, + {0x45u, "image_gather4_b", ImageSampleFlagBias}, + {0x46u, "image_gather4_b_cl", ImageSampleFlagBias | ImageSampleFlagLodClamp}, + {0x47u, "image_gather4_lz", ImageSampleFlagLevelZero}, + {0x48u, "image_gather4_c", ImageSampleFlagCompare}, + {0x49u, "image_gather4_c_cl", ImageSampleFlagCompare | ImageSampleFlagLodClamp}, + {0x4cu, "image_gather4_c_l", ImageSampleFlagCompare | ImageSampleFlagLod}, + {0x4du, "image_gather4_c_b", ImageSampleFlagCompare | ImageSampleFlagBias}, + {0x4eu, "image_gather4_c_b_cl", + ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagLodClamp}, + {0x4fu, "image_gather4_c_lz", ImageSampleFlagCompare | ImageSampleFlagLevelZero}, + {0x50u, "image_gather4_o", ImageSampleFlagOffset}, + {0x51u, "image_gather4_cl_o", ImageSampleFlagLodClamp | ImageSampleFlagOffset}, + {0x54u, "image_gather4_l_o", ImageSampleFlagLod | ImageSampleFlagOffset}, + {0x55u, "image_gather4_b_o", ImageSampleFlagBias | ImageSampleFlagOffset}, + {0x56u, "image_gather4_b_cl_o", + ImageSampleFlagBias | ImageSampleFlagLodClamp | ImageSampleFlagOffset}, + {0x57u, "image_gather4_lz_o", ImageSampleFlagLevelZero | ImageSampleFlagOffset}, + {0x58u, "image_gather4_c_o", ImageSampleFlagCompare | ImageSampleFlagOffset}, + {0x59u, "image_gather4_c_cl_o", + ImageSampleFlagCompare | ImageSampleFlagLodClamp | ImageSampleFlagOffset}, + {0x5cu, "image_gather4_c_l_o", + ImageSampleFlagCompare | ImageSampleFlagLod | ImageSampleFlagOffset}, + {0x5du, "image_gather4_c_b_o", + ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagOffset}, + {0x5eu, "image_gather4_c_b_cl_o", + ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagLodClamp | + ImageSampleFlagOffset}, + {0x5fu, "image_gather4_c_lz_o", ImageSampleFlagCompare | ImageSampleFlagLevelZero | ImageSampleFlagOffset}, - {0x61u, Opcode::IMAGE_GATHER4H, ImageSampleFlagGatherHorizontal}, - {0xc8u, Opcode::IMAGE_GATHER4_C, ImageSampleFlagCompare | ImageSampleFlagAdjust}, - {0xd8u, Opcode::IMAGE_GATHER4_C_O, + {0x61u, "image_gather4h", ImageSampleFlagGatherHorizontal}, + {0xc0u, "image_gather4_a", ImageSampleFlagAdjust}, + {0xc1u, "image_gather4_cl_a", ImageSampleFlagLodClamp | ImageSampleFlagAdjust}, + {0xc5u, "image_gather4_b_a", ImageSampleFlagBias | ImageSampleFlagAdjust}, + {0xc6u, "image_gather4_b_cl_a", + ImageSampleFlagBias | ImageSampleFlagLodClamp | ImageSampleFlagAdjust}, + {0xc8u, "image_gather4_c_a", ImageSampleFlagCompare | ImageSampleFlagAdjust}, + {0xc9u, "image_gather4_c_cl_a", + ImageSampleFlagCompare | ImageSampleFlagLodClamp | ImageSampleFlagAdjust}, + {0xcdu, "image_gather4_c_b_a", + ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagAdjust}, + {0xceu, "image_gather4_c_b_cl_a", + ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagLodClamp | + ImageSampleFlagAdjust}, + {0xd0u, "image_gather4_o_a", ImageSampleFlagOffset | ImageSampleFlagAdjust}, + {0xd1u, "image_gather4_cl_o_a", + ImageSampleFlagLodClamp | ImageSampleFlagOffset | ImageSampleFlagAdjust}, + {0xd5u, "image_gather4_b_o_a", + ImageSampleFlagBias | ImageSampleFlagOffset | ImageSampleFlagAdjust}, + {0xd6u, "image_gather4_b_cl_o_a", + ImageSampleFlagBias | ImageSampleFlagLodClamp | ImageSampleFlagOffset | + ImageSampleFlagAdjust}, + {0xd8u, "image_gather4_c_o_a", ImageSampleFlagCompare | ImageSampleFlagOffset | ImageSampleFlagAdjust}, + {0xd9u, "image_gather4_c_cl_o_a", + ImageSampleFlagCompare | ImageSampleFlagLodClamp | ImageSampleFlagOffset | + ImageSampleFlagAdjust}, + {0xddu, "image_gather4_c_b_o_a", + ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagOffset | + ImageSampleFlagAdjust}, + {0xdeu, "image_gather4_c_b_cl_o_a", + ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagLodClamp | + ImageSampleFlagOffset | ImageSampleFlagAdjust}, }; constexpr MimgAtomicInfo MIMG_ATOMIC_OPCODE_LIST[] = { @@ -219,13 +262,13 @@ constexpr auto MIMG_SAMPLE_OPS = Detail::MakeOpcodeTable<0x100>(MIMG_SAMPLE_OPCO constexpr auto MIMG_GATHER_OPS = Detail::MakeOpcodeTable<0x100>(MIMG_GATHER_OPCODE_LIST); constexpr auto MIMG_ATOMIC_OPS = Detail::MakeOpcodeTable<0x100>(MIMG_ATOMIC_OPCODE_LIST); -Opcode DecodeMimgOpcode(uint32_t opcode, const MimgSampleInfo* sample, const MimgGatherInfo* gather, +Opcode DecodeMimgOpcode(uint32_t opcode, const MimgSampleInfo* sample, const MimgSampleInfo* gather, const MimgAtomicInfo* atomic) { if (sample != nullptr) { return Opcode::IMAGE_SAMPLE; } if (gather != nullptr) { - return gather->decoded; + return Opcode::IMAGE_GATHER4; } if (atomic != nullptr) { return atomic->decoded; @@ -244,24 +287,10 @@ Opcode DecodeMimgOpcode(uint32_t opcode, const MimgSampleInfo* sample, const Mim } } -uint32_t DecodeMimgSampleFlags(const MimgSampleInfo* sample, const MimgGatherInfo* gather) { - if (sample != nullptr) { - return sample->flags; - } - if (gather != nullptr) { - return gather->flags; - } - return 0; -} - uint32_t DecodeMimgAddressComponents(uint32_t opcode, ImageDimension dimension, - const MimgSampleInfo* sample, const MimgGatherInfo* gather, - const MimgAtomicInfo* atomic) { - if (sample != nullptr) { - return ImageSampleAddressComponents(sample->flags, dimension); - } - if (gather != nullptr) { - return ImageSampleAddressComponents(gather->flags, dimension); + const MimgSampleInfo* sampled, const MimgAtomicInfo* atomic) { + if (sampled != nullptr) { + return ImageSampleAddressComponents(sampled->flags, dimension); } if (atomic != nullptr) { return ImageCoordComponents(dimension); @@ -329,16 +358,17 @@ void DecodeMimg(uint32_t pc, std::span code, uint32_t word_index const auto dimension = DecodeImageDimension((word0 >> 3u) & 0x7u); const uint32_t word_count = 2u + nsa_dwords; - const uint32_t vdata = (word1 >> 8u) & 0xffu; - const uint32_t vaddr = word1 & 0xffu; - const uint32_t srsrc = (word1 >> 16u) & 0x1fu; - const uint32_t ssamp = (word1 >> 21u) & 0x1fu; - const bool r128 = ((word0 >> 15u) & 0x1u) != 0u; - const bool a16 = ((word1 >> 30u) & 0x1u) != 0u; - const bool d16 = ((word1 >> 31u) & 0x1u) != 0u; - const auto* sample = Detail::FindOpcode(MIMG_SAMPLE_OPS, opcode); - const auto* gather = Detail::FindOpcode(MIMG_GATHER_OPS, opcode); - const auto* atomic = Detail::FindOpcode(MIMG_ATOMIC_OPS, opcode); + const uint32_t vdata = (word1 >> 8u) & 0xffu; + const uint32_t vaddr = word1 & 0xffu; + const uint32_t srsrc = (word1 >> 16u) & 0x1fu; + const uint32_t ssamp = (word1 >> 21u) & 0x1fu; + const bool r128 = ((word0 >> 15u) & 0x1u) != 0u; + const bool a16 = ((word1 >> 30u) & 0x1u) != 0u; + const bool d16 = ((word1 >> 31u) & 0x1u) != 0u; + const auto* sample = Detail::FindOpcode(MIMG_SAMPLE_OPS, opcode); + const auto* gather = Detail::FindOpcode(MIMG_GATHER_OPS, opcode); + const auto* atomic = Detail::FindOpcode(MIMG_ATOMIC_OPS, opcode); + const auto* sampled = sample != nullptr ? sample : gather; inst.pc = pc; inst.word_count = word_count; @@ -351,11 +381,11 @@ void DecodeMimg(uint32_t pc, std::span code, uint32_t word_index inst.data_dwords = d16 ? (inst.data_components + 1u) / 2u : inst.data_components; inst.glc = ((word0 >> 13u) & 1u) != 0; inst.slc = ((word0 >> 25u) & 1u) != 0; - inst.image_sample_flags = DecodeMimgSampleFlags(sample, gather); + inst.image_sample_flags = sampled != nullptr ? sampled->flags : 0u; if (a16) { inst.image_sample_flags |= ImageSampleFlagA16; } - if ((sample != nullptr || gather != nullptr) && (word0 & (1u << 12u)) != 0u) { + if (sampled != nullptr && (word0 & (1u << 12u)) != 0u) { inst.image_sample_flags |= ImageSampleFlagUnnormalized; } inst.image_dimension = dimension; @@ -364,8 +394,7 @@ void DecodeMimg(uint32_t pc, std::span code, uint32_t word_index for (uint32_t i = 0; i < nsa_dwords * 4u; i++) { inst.image_nsa_addr[i] = (code[word_index + 2u + i / 4u] >> ((i % 4u) * 8u)) & 0xffu; } - inst.image_address_components = - DecodeMimgAddressComponents(opcode, dimension, sample, gather, atomic); + inst.image_address_components = DecodeMimgAddressComponents(opcode, dimension, sampled, atomic); SetRawWords(inst, code, word_index, word_count); if (inst.opcode == Opcode::UNSUPPORTED) { @@ -389,8 +418,8 @@ void DecodeMimg(uint32_t pc, std::span code, uint32_t word_index } } } - const bool supports_d16 = sample != nullptr || gather != nullptr || opcode == 0x00u || - opcode == 0x01u || opcode == 0x08u || opcode == 0x09u; + const bool supports_d16 = sampled != nullptr || opcode == 0x00u || opcode == 0x01u || + opcode == 0x08u || opcode == 0x09u; if (d16 && !supports_d16) { SetUnsupported(inst, Family::MIMG, opcode, "MIMG opcode does not support D16 data"); } @@ -407,9 +436,10 @@ void DecodeMimg(uint32_t pc, std::span code, uint32_t word_index inst.src_count = 3; } -const char* MimgSampleOpcodeName(uint32_t opcode) { - const auto* sample = Detail::FindOpcode(MIMG_SAMPLE_OPS, opcode); - return sample != nullptr ? sample->name : nullptr; +const char* MimgSampledOpcodeName(uint32_t opcode) { + const auto* sample = Detail::FindOpcode(MIMG_SAMPLE_OPS, opcode); + const auto* sampled = sample != nullptr ? sample : Detail::FindOpcode(MIMG_GATHER_OPS, opcode); + return sampled != nullptr ? sampled->name : nullptr; } } // namespace Libs::Graphics::ShaderRecompiler::Decoder diff --git a/src/graphics/shader/recompiler/frontend/decode/ImageOps.h b/src/graphics/shader/recompiler/frontend/decode/ImageOps.h index 72dbdbb32..1bf06e703 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ImageOps.h +++ b/src/graphics/shader/recompiler/frontend/decode/ImageOps.h @@ -8,7 +8,7 @@ namespace Libs::Graphics::ShaderRecompiler::Decoder { void DecodeMimg(uint32_t pc, std::span code, uint32_t word_index, Instruction& inst); -const char* MimgSampleOpcodeName(uint32_t opcode); +const char* MimgSampledOpcodeName(uint32_t opcode); struct ImageAddressComponent { uint32_t bit_offset; diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp index 2a9611412..2792f4cf6 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp @@ -123,10 +123,9 @@ std::string ImageSampleFlagsToString(uint32_t flags) { } std::string FormatMimg(const Instruction& inst) { - const char* sample_name = - inst.opcode == Opcode::IMAGE_SAMPLE ? MimgSampleOpcodeName(inst.opcode_id) : nullptr; + const char* sampled_name = MimgSampledOpcodeName(inst.opcode_id); const std::string_view name = - sample_name != nullptr ? sample_name : magic_enum::enum_name(inst.opcode); + sampled_name != nullptr ? sampled_name : magic_enum::enum_name(inst.opcode); std::string text = fmt::format("0x{:08x}: {} {}, {}, {}, {} ; dmask=0x{:x} image_dim={}", inst.pc, name, OperandToString(inst.dst).c_str(), OperandToString(inst.src0).c_str(), @@ -138,22 +137,10 @@ std::string FormatMimg(const Instruction& inst) { if (inst.image_r128) { text += " r128=1"; } - 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_L: - case Opcode::IMAGE_GATHER4_C_LZ: - case Opcode::IMAGE_GATHER4_LZ_O: - case Opcode::IMAGE_GATHER4_C_O: - case Opcode::IMAGE_GATHER4_C_LZ_O: - case Opcode::IMAGE_GATHER4H: - text += fmt::format(" sample_flags={} addr_components={}", - ImageSampleFlagsToString(inst.image_sample_flags).c_str(), - inst.image_address_components); - break; - default: break; + if (sampled_name != nullptr) { + text += fmt::format(" sample_flags={} addr_components={}", + ImageSampleFlagsToString(inst.image_sample_flags).c_str(), + inst.image_address_components); } if (inst.image_nsa_dwords != 0) { text += fmt::format(" nsa_dwords={} nsa_addr=", inst.image_nsa_dwords); @@ -616,15 +603,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_L: - case Opcode::IMAGE_GATHER4_C_LZ: - case Opcode::IMAGE_GATHER4_LZ_O: - case Opcode::IMAGE_GATHER4_C_O: - case Opcode::IMAGE_GATHER4_C_LZ_O: - case Opcode::IMAGE_GATHER4H: return WithUnsupportedReason(inst, FormatMimg(inst)); + case Opcode::IMAGE_GATHER4: return WithUnsupportedReason(inst, FormatMimg(inst)); case Opcode::S_LOAD_DWORD: case Opcode::S_LOAD_DWORDX2: case Opcode::S_LOAD_DWORDX4: diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h index d207026fa..b62d4b89d 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h @@ -708,15 +708,7 @@ enum class Opcode { IMAGE_ATOMIC_FMIN, IMAGE_ATOMIC_FMAX, IMAGE_SAMPLE, - IMAGE_GATHER4_L, - IMAGE_GATHER4_LZ, - IMAGE_GATHER4_C, - IMAGE_GATHER4_C_L, - IMAGE_GATHER4_C_LZ, - IMAGE_GATHER4_LZ_O, - IMAGE_GATHER4_C_O, - IMAGE_GATHER4_C_LZ_O, - IMAGE_GATHER4H, + IMAGE_GATHER4, V_INTERP_P1_F32, V_INTERP_P2_F32, V_INTERP_MOV_F32, diff --git a/src/graphics/shader/recompiler/frontend/translate/Memory.cpp b/src/graphics/shader/recompiler/frontend/translate/Memory.cpp index bfc99bc3b..fd9a7f921 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Memory.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Memory.cpp @@ -1166,15 +1166,7 @@ void 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_L: - case Decoder::Opcode::IMAGE_GATHER4_C_LZ: - case Decoder::Opcode::IMAGE_GATHER4_LZ_O: - case Decoder::Opcode::IMAGE_GATHER4_C_O: - case Decoder::Opcode::IMAGE_GATHER4_C_LZ_O: - case Decoder::Opcode::IMAGE_GATHER4H: return IMAGE_GATHER(inst); + case Decoder::Opcode::IMAGE_GATHER4: return IMAGE_GATHER(inst); case Decoder::Opcode::DS_MIN_F32: return DS_ATOMIC(inst, IR::ValueOpcode::SharedAtomicFMin32, false); diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index df50c09da..b9d339e25 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -1723,7 +1723,7 @@ CompiledShader CompileCase(const TestCase &test, u32 host_subgroup_size = 64) { const auto uses_image = std::any_of(test.opcodes.begin(), test.opcodes.end(), [](auto op) { return op >= ShaderOpcode::IMAGE_GET_RESINFO && - op <= ShaderOpcode::IMAGE_GATHER4H; + op <= ShaderOpcode::IMAGE_GATHER4; }); if (uses_image && ((user_data[3] >> 28u) & 0xfu) == 0) { user_data[3] = static_cast(Prospero::ImageType::kColor2D) << 28u; @@ -14132,7 +14132,7 @@ public: test.initial.assign(test.expected.size(), 0xdeadbeefu); test.expected_mip_descriptors = 2u; test.required_spirv = {"OpImageDrefGather", "OpSwitch"}; - test.decoded_counts = {{"IMAGE_GATHER4_C_L", 6u}}; + test.decoded_counts = {{"image_gather4_c_l", 6u}}; const auto compiled = CompileCase(test, SubgroupSize()); Require(test.name, "comparison mip resource", compiled.program.info.images.size() == 1u && compiled.program.info.images[0].depth_compare, @@ -22771,13 +22771,7 @@ CoverageClass ClassifyOpcode(ShaderOpcode opcode, case Opcode::IMAGE_ATOMIC_FMIN: case Opcode::IMAGE_ATOMIC_FMAX: case Opcode::IMAGE_SAMPLE: - case Opcode::IMAGE_GATHER4_LZ: - case Opcode::IMAGE_GATHER4_C: - case Opcode::IMAGE_GATHER4_C_L: - case Opcode::IMAGE_GATHER4_C_LZ: - case Opcode::IMAGE_GATHER4_LZ_O: - case Opcode::IMAGE_GATHER4_C_O: - case Opcode::IMAGE_GATHER4_C_LZ_O: + case Opcode::IMAGE_GATHER4: return CoverageClass::NeedsImageCase; case Opcode::V_INTERP_P1_F32: @@ -39002,7 +38996,7 @@ TestCase ImageSamplePackedUintConvertsSampleAndGather() { test.code = std::move(code); test.expected = {0x2abu, 0x456u, 0x321u, 0x321u, 0x456u, 0x456u, 0x456u, 0x456u}; - test.opcodes = {O::V_MOV_B32, O::IMAGE_SAMPLE, O::IMAGE_GATHER4_LZ, + test.opcodes = {O::V_MOV_B32, O::IMAGE_SAMPLE, O::IMAGE_GATHER4, O::BUFFER_STORE_DWORD, O::S_ENDPGM}; test.sampled_image_rgba = std::vector(16, 0xaae2b321u); test.sampled_image_format = vk::Format::eR32Uint; @@ -39144,7 +39138,7 @@ template TestCase ImageSamplePacked8() { for (float value : expected) { test.expected.push_back(std::bit_cast(value)); } - test.opcodes = {O::V_MOV_B32, O::IMAGE_SAMPLE, O::IMAGE_GATHER4_LZ, + test.opcodes = {O::V_MOV_B32, O::IMAGE_SAMPLE, O::IMAGE_GATHER4, O::BUFFER_STORE_DWORD, O::S_ENDPGM}; return test; } @@ -39262,7 +39256,7 @@ TestCase ImageGather2DInstructionWith1DDescriptor() { test.name = "ImageGather2DInstructionWith1DDescriptor"; test.code = code; test.expected = {0x40000000u, 0x40400000u, 0x40400000u, 0x40000000u}; - test.opcodes = {O::V_MOV_B32, O::IMAGE_GATHER4_LZ, O::BUFFER_STORE_DWORD, + test.opcodes = {O::V_MOV_B32, O::IMAGE_GATHER4, O::BUFFER_STORE_DWORD, O::S_ENDPGM}; test.image_width = 4; test.image_height = 1; @@ -39531,9 +39525,8 @@ TestCase ImageSampleAndGather() { 0x3f800000u, 0}; test.opcodes = { - O::V_MOV_B32, O::IMAGE_SAMPLE, O::IMAGE_GET_LOD, - O::IMAGE_GATHER4_LZ, O::IMAGE_GATHER4_LZ_O, O::BUFFER_STORE_DWORD, - O::S_ENDPGM}; + O::V_MOV_B32, O::IMAGE_SAMPLE, O::IMAGE_GET_LOD, O::IMAGE_GATHER4, + O::BUFFER_STORE_DWORD, O::S_ENDPGM}; test.sampled_image_rgba = image; return test; } @@ -39601,7 +39594,7 @@ TestCase ImageGatherExplicitLod() { TestCase test; test.name = "ImageGatherExplicitLod"; test.code = std::move(code); - test.opcodes = {O::V_MOV_B32, O::IMAGE_GATHER4_L, O::IMAGE_SAMPLE, + test.opcodes = {O::V_MOV_B32, O::IMAGE_GATHER4, O::IMAGE_SAMPLE, O::BUFFER_STORE_DWORD, O::S_ENDPGM}; // A gather's single-mip descriptors must not replace the ordinary sample's // full mip chain when both instructions use the same guest descriptor. @@ -39726,7 +39719,7 @@ template TestCase ImageGatherLodPerLane() { } AppendEnd(&code); test.opcodes = {O::V_MOV_B32, O::V_LSHLREV_B32, O::BUFFER_LOAD_DWORD, - O::IMAGE_GATHER4_L, O::V_ADD_NC_U32, O::BUFFER_STORE_DWORD, + O::IMAGE_GATHER4, O::V_ADD_NC_U32, O::BUFFER_STORE_DWORD, O::S_ENDPGM}; test.user_data = MakeSampledTextureData(Prospero::BufferFormat::k32_32_32_32Float); test.user_data[1] |= (3u << 30u) | (mode == 3 ? 768u << 8u : 0u); @@ -39778,7 +39771,7 @@ TestCase ImageD16GatherPacksHalfPairs() { test.name = "ImageD16GatherPacksHalfPairs"; test.code = code; test.expected = {0x38003800u, 0x38003800u}; - test.opcodes = {O::V_MOV_B32, O::IMAGE_GATHER4_LZ, O::BUFFER_STORE_DWORD, + test.opcodes = {O::V_MOV_B32, O::IMAGE_GATHER4, O::BUFFER_STORE_DWORD, O::S_ENDPGM}; test.sampled_image_rgba = image; test.decoded_counts = {{"d16=1", 1}}; @@ -40022,6 +40015,63 @@ TestCase ImageSampleA16CompareBiasRdna2AddressOrder() { return test; } +TestCase ImageGatherAddressVariants() { + using O = ShaderOpcode; + struct Gather { + u32 opcode; + bool offset; + bool bias; + bool lod_or_clamp; + }; + // Plain, LOD clamp, bias, LOD and offset gathers, with and without gradient adjust. + constexpr Gather gathers[] = { + {0x40, false, false, false}, {0x41, false, false, true}, + {0x45, false, true, false}, {0x46, false, true, true}, + {0x50, true, false, false}, {0x51, true, false, true}, + {0x54, true, false, true}, {0x55, true, true, false}, + {0x56, true, true, true}, {0xc0, false, false, false}, + {0xc1, false, false, true}, {0xc5, false, true, false}, + {0xc6, false, true, true}, {0xd0, true, false, false}, + {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++) { + const auto &gather = gathers[index]; + // Zero bias, clamp and LOD keep the base level; a misplaced offset or bias moves the footprint. + u32 vaddr = 20; + if (gather.offset) AppendVMovU32(&test.code, vaddr++, 0x3fu); // x offset -1 + if (gather.bias) AppendVMovU32(&test.code, vaddr++, 0); + AppendVMovLiteral(&test.code, vaddr++, std::bit_cast(0.6875f)); + AppendVMovLiteral(&test.code, vaddr++, std::bit_cast(0.4375f)); + if (gather.lod_or_clamp) AppendVMovU32(&test.code, vaddr++, 0); + test.code.push_back(EncodeMimg0(gather.opcode, 0x1)); + test.code.push_back(EncodeMimg1(0, 20)); + for (u32 component = 0; component < 4u; component++) { + AppendStoreVgpr(&test.code, component, index * 4u + component); + } + // Footprint x 2..3, y 1..2, or x 1..2 with the offset, in (x0, y1), (x1, y1), (x1, y0), + // (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)); + } + } + AppendEnd(&test.code); + test.opcodes = {O::V_MOV_B32, O::IMAGE_GATHER4, O::BUFFER_STORE_DWORD, + O::S_ENDPGM}; + test.sampled_image_rgba = image; + return test; +} + TestCase ImageGatherCompareOpcodes() { using O = ShaderOpcode; @@ -40052,15 +40102,21 @@ TestCase ImageGatherCompareOpcodes() { AppendStoreVgpr(&code, 8, 1); AppendStoreVgpr(&code, 0, 2); AppendStoreVgpr(&code, 12, 3); + // The other compare gathers. + constexpr u32 variants[] = {0x49, 0x4c, 0x4d, 0x4e, 0x59, 0x5c, 0x5d, 0x5e, + 0xc8, 0xc9, 0xcd, 0xce, 0xd8, 0xd9, 0xdd, 0xde}; + for (u32 index = 0; index < std::size(variants); index++) { + code.push_back(EncodeMimg0(variants[index], 0x1)); + code.push_back(EncodeMimg1(40, 20)); + AppendStoreVgpr(&code, 40, 4u + index); + } AppendEnd(&code); TestCase test; test.name = "ImageGatherCompareOpcodes"; test.code = code; - test.opcodes = { - O::V_MOV_B32, O::IMAGE_GATHER4_C, O::IMAGE_GATHER4_C_LZ, - O::IMAGE_GATHER4_C_O, O::IMAGE_GATHER4_C_LZ_O, O::BUFFER_STORE_DWORD, - O::S_ENDPGM}; + test.opcodes = {O::V_MOV_B32, O::IMAGE_GATHER4, O::BUFFER_STORE_DWORD, + O::S_ENDPGM}; test.required_spirv = {"OpImageDrefGather", "ConstOffset"}; test.forbidden_spirv = {"OpBitFieldSExtract"}; test.compile_only = true; @@ -45822,6 +45878,7 @@ std::vector MakeCases() { AddCase(ImageGetResinfoDmaskWidthHeight); AddCase(ImageGetResinfoDmaskMipLevels); AddCase(ImageSampleAndGather); + AddCase(ImageGatherAddressVariants); AddCase(ImageCubeGradientsPreserveDerivatives); AddCase(ImageGatherExplicitLod); AddCase(ImageSampleLodClamp); @@ -52321,6 +52378,7 @@ int main(int argc, char **argv) { RunCase(&vulkan, ImageSampleA16OffsetKeepsTexelOffset32BitOnGpu<32>()); RunCase(&vulkan, ImageSampleA16OffsetKeepsTexelOffset32BitOnGpu<64>()); RunCase(&vulkan, ImageSampleAndGather()); + RunCase(&vulkan, ImageGatherAddressVariants()); RunCase(nullptr, ImageGatherCompareOpcodes()); return 0; } diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index c5ffb57ef..475f6fcb8 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -4715,6 +4715,73 @@ void TestImageAtomicWidthDecoder() { } } +void TestImageGatherDecoder() { + using namespace ShaderRecompiler::Decoder; + struct Gather { + uint32_t opcode; + const char *name; + uint32_t address_vgprs; + }; + // Every PS5 image_gather4 encoding, with the vaddr VGPR count of its 2D full-float form. + constexpr Gather gathers[] = { + {0x40, "image_gather4", 2}, {0x41, "image_gather4_cl", 3}, + {0x44, "image_gather4_l", 3}, {0x45, "image_gather4_b", 3}, + {0x46, "image_gather4_b_cl", 4}, {0x47, "image_gather4_lz", 2}, + {0x48, "image_gather4_c", 3}, {0x49, "image_gather4_c_cl", 4}, + {0x4c, "image_gather4_c_l", 4}, {0x4d, "image_gather4_c_b", 4}, + {0x4e, "image_gather4_c_b_cl", 5}, {0x4f, "image_gather4_c_lz", 3}, + {0x50, "image_gather4_o", 3}, {0x51, "image_gather4_cl_o", 4}, + {0x54, "image_gather4_l_o", 4}, {0x55, "image_gather4_b_o", 4}, + {0x56, "image_gather4_b_cl_o", 5}, {0x57, "image_gather4_lz_o", 3}, + {0x58, "image_gather4_c_o", 4}, {0x59, "image_gather4_c_cl_o", 5}, + {0x5c, "image_gather4_c_l_o", 5}, {0x5d, "image_gather4_c_b_o", 5}, + {0x5e, "image_gather4_c_b_cl_o", 6}, {0x5f, "image_gather4_c_lz_o", 4}, + {0x61, "image_gather4h", 2}, {0xc0, "image_gather4_a", 2}, + {0xc1, "image_gather4_cl_a", 3}, {0xc5, "image_gather4_b_a", 3}, + {0xc6, "image_gather4_b_cl_a", 4}, {0xc8, "image_gather4_c_a", 3}, + {0xc9, "image_gather4_c_cl_a", 4}, {0xcd, "image_gather4_c_b_a", 4}, + {0xce, "image_gather4_c_b_cl_a", 5}, {0xd0, "image_gather4_o_a", 3}, + {0xd1, "image_gather4_cl_o_a", 4}, {0xd5, "image_gather4_b_o_a", 4}, + {0xd6, "image_gather4_b_cl_o_a", 5}, {0xd8, "image_gather4_c_o_a", 4}, + {0xd9, "image_gather4_c_cl_o_a", 5}, {0xdd, "image_gather4_c_b_o_a", 5}, + {0xde, "image_gather4_c_b_cl_o_a", 6}, + }; + const auto expected_flags = [](std::string_view name) { + if (name == "image_gather4h") return uint32_t{ImageSampleFlagGatherHorizontal}; + uint32_t flags = 0; + for (size_t at = name.find('_', 6); at != std::string_view::npos;) { + const auto end = name.find('_', at + 1); + const auto modifier = name.substr(at + 1, end - at - 1); + if (modifier == "c") flags |= ImageSampleFlagCompare; + if (modifier == "b") flags |= ImageSampleFlagBias; + if (modifier == "cl") flags |= ImageSampleFlagLodClamp; + if (modifier == "l") flags |= ImageSampleFlagLod; + if (modifier == "lz") flags |= ImageSampleFlagLevelZero; + if (modifier == "o") flags |= ImageSampleFlagOffset; + if (modifier == "a") flags |= ImageSampleFlagAdjust; + at = end; + } + return flags; + }; + for (uint32_t opcode = 0; opcode < 0x100u; ++opcode) { + const auto *gather = std::ranges::find(gathers, opcode, &Gather::opcode); + const uint32_t words[] = {EncodeMimg0(opcode, 0x1), EncodeMimg1(8, 0, 1, 0)}; + Instruction decoded; + DecodeInstruction(words, 0, decoded); + if (gather == std::end(gathers)) { + Check(decoded.opcode != Opcode::IMAGE_GATHER4, + "a non-gather MIMG encoding decoded as image_gather4"); + continue; + } + Check(decoded.opcode == Opcode::IMAGE_GATHER4 && decoded.data_components == 4u && + decoded.image_sample_flags == expected_flags(gather->name) && + decoded.image_address_components == gather->address_vgprs && + InstructionToString(decoded).find(std::string(gather->name) + " ") != + std::string::npos, + "a PS5 gather encoding lost its opcode, name, modifiers or address count"); + } +} + void TestNewShaderDecoderArchitecture() { using namespace ShaderRecompiler::Decoder; @@ -5657,7 +5724,7 @@ void TestImageAddressOperands() { const uint32_t captured[] = {0xf130010au, 0x00a30013u, 0x00161a19u}; Decoder::Instruction gather; Decoder::DecodeInstruction(captured, 0, gather); - Check(gather.opcode == Decoder::Opcode::IMAGE_GATHER4_C_L && + Check(gather.opcode == Decoder::Opcode::IMAGE_GATHER4 && gather.opcode_id == 0x4cu && gather.word_count == 3u && gather.image_dimension == Decoder::ImageDimension::Dim2D && gather.dmask == 1u && gather.data_components == 4u && @@ -5671,7 +5738,7 @@ void TestImageAddressOperands() { gather.image_nsa_addr[2] == 22u && !gather.glc && !gather.slc && !gather.image_r128, "captured IMAGE_GATHER4_C_L lost compare/LOD, NSA, or result metadata"); - Check(Decoder::InstructionToString(gather).find("IMAGE_GATHER4_C_L") != std::string::npos, + Check(Decoder::InstructionToString(gather).find("image_gather4_c_l") != std::string::npos, "captured IMAGE_GATHER4_C_L is missing from the decoded dump"); struct Case { uint32_t opcode; @@ -17022,6 +17089,7 @@ int main() { // here. TestScalarAshrI64Decoder(); TestImageAtomicWidthDecoder(); + TestImageGatherDecoder(); TestDsReturningAtomic64Decoder(); TestDsCompareStore32Decoder(); TestDsFloatMinMaxDecoder();