#include "graphics/shader/recompiler/ImageOps.h" #include #include #include namespace Libs::Graphics::ShaderRecompiler::Decoder { namespace { struct MimgSampleInfo { uint32_t opcode = 0; const char* name = nullptr; uint32_t flags = 0; uint32_t address_components = 3; }; struct MimgGatherInfo { uint32_t opcode = 0; const char* name = nullptr; Opcode decoded = Opcode::Unsupported; uint32_t flags = 0; uint32_t address_components = 3; }; struct MimgAtomicInfo { uint32_t opcode = 0; const char* name = nullptr; Opcode decoded = Opcode::Unsupported; }; constexpr ImageDimension DecodeImageDimension(uint32_t dim) { switch (dim) { case 1u: return ImageDimension::Dim2D; case 2u: return ImageDimension::Dim3D; case 3u: return ImageDimension::Dim2DArray; case 5u: case 7u: return ImageDimension::Dim2DArray; case 6u: return ImageDimension::Dim2D; default: return ImageDimension::Unknown; } } constexpr uint32_t ImageCoordComponents(ImageDimension dimension) { switch (dimension) { case ImageDimension::Dim3D: case ImageDimension::Dim2DArray: return 3u; default: return 2u; } } constexpr uint32_t ImageSampleAddressComponents(uint32_t flags, ImageDimension dimension) { const auto coord_components = ImageCoordComponents(dimension); uint32_t components = coord_components; if ((flags & ImageSampleFlagOffset) != 0) { components++; } if ((flags & ImageSampleFlagCompare) != 0) { components++; } if ((flags & ImageSampleFlagBias) != 0) { components++; } if ((flags & ImageSampleFlagLod) != 0) { components++; } if ((flags & ImageSampleFlagDerivative) != 0) { components += 4u; } return components; } constexpr MimgSampleInfo SampleInfo(uint32_t opcode, const char* name, uint32_t flags) { return {opcode, name, flags, ImageSampleAddressComponents(flags, ImageDimension::Dim2D)}; } constexpr MimgSampleInfo MIMG_SAMPLE_OPS[] = { SampleInfo(0x20u, "image_sample", 0), SampleInfo(0x21u, "image_sample_cl", ImageSampleFlagLodClamp), SampleInfo(0x22u, "image_sample_d", ImageSampleFlagDerivative), SampleInfo(0x23u, "image_sample_d_cl", ImageSampleFlagDerivative | ImageSampleFlagLodClamp), SampleInfo(0x24u, "image_sample_l", ImageSampleFlagLod), SampleInfo(0x25u, "image_sample_b", ImageSampleFlagBias), SampleInfo(0x26u, "image_sample_b_cl", ImageSampleFlagBias | ImageSampleFlagLodClamp), SampleInfo(0x27u, "image_sample_lz", ImageSampleFlagLevelZero), SampleInfo(0x28u, "image_sample_c", ImageSampleFlagCompare), SampleInfo(0x29u, "image_sample_c_cl", ImageSampleFlagCompare | ImageSampleFlagLodClamp), SampleInfo(0x2au, "image_sample_c_d", ImageSampleFlagCompare | ImageSampleFlagDerivative), SampleInfo(0x2bu, "image_sample_c_d_cl", ImageSampleFlagCompare | ImageSampleFlagDerivative | ImageSampleFlagLodClamp), SampleInfo(0x2cu, "image_sample_c_l", ImageSampleFlagCompare | ImageSampleFlagLod), SampleInfo(0x2du, "image_sample_c_b", ImageSampleFlagCompare | ImageSampleFlagBias), SampleInfo(0x2eu, "image_sample_c_b_cl", ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagLodClamp), SampleInfo(0x2fu, "image_sample_c_lz", ImageSampleFlagCompare | ImageSampleFlagLevelZero), SampleInfo(0x30u, "image_sample_o", ImageSampleFlagOffset), SampleInfo(0x31u, "image_sample_cl_o", ImageSampleFlagLodClamp | ImageSampleFlagOffset), SampleInfo(0x32u, "image_sample_d_o", ImageSampleFlagDerivative | ImageSampleFlagOffset), SampleInfo(0x33u, "image_sample_d_cl_o", ImageSampleFlagDerivative | ImageSampleFlagLodClamp | ImageSampleFlagOffset), SampleInfo(0x34u, "image_sample_l_o", ImageSampleFlagLod | ImageSampleFlagOffset), SampleInfo(0x35u, "image_sample_b_o", ImageSampleFlagBias | ImageSampleFlagOffset), SampleInfo(0x36u, "image_sample_b_cl_o", ImageSampleFlagBias | ImageSampleFlagLodClamp | ImageSampleFlagOffset), SampleInfo(0x37u, "image_sample_lz_o", ImageSampleFlagLevelZero | ImageSampleFlagOffset), SampleInfo(0x38u, "image_sample_c_o", ImageSampleFlagCompare | ImageSampleFlagOffset), SampleInfo(0x39u, "image_sample_c_cl_o", ImageSampleFlagCompare | ImageSampleFlagLodClamp | ImageSampleFlagOffset), SampleInfo(0x3au, "image_sample_c_d_o", ImageSampleFlagCompare | ImageSampleFlagDerivative | ImageSampleFlagOffset), SampleInfo(0x3bu, "image_sample_c_d_cl_o", ImageSampleFlagCompare | ImageSampleFlagDerivative | ImageSampleFlagLodClamp | ImageSampleFlagOffset), SampleInfo(0x3cu, "image_sample_c_l_o", ImageSampleFlagCompare | ImageSampleFlagLod | ImageSampleFlagOffset), SampleInfo(0x3du, "image_sample_c_b_o", ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagOffset), SampleInfo(0x3eu, "image_sample_c_b_cl_o", ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagLodClamp | ImageSampleFlagOffset), SampleInfo(0x3fu, "image_sample_c_lz_o", ImageSampleFlagCompare | ImageSampleFlagLevelZero | ImageSampleFlagOffset), SampleInfo(0x68u, "image_sample_cd", ImageSampleFlagDerivative | ImageSampleFlagCd), SampleInfo(0x69u, "image_sample_cd_cl", ImageSampleFlagDerivative | ImageSampleFlagCd | ImageSampleFlagLodClamp), SampleInfo(0x6au, "image_sample_c_cd", ImageSampleFlagCompare | ImageSampleFlagDerivative | ImageSampleFlagCd), SampleInfo(0x6bu, "image_sample_c_cd_cl", ImageSampleFlagCompare | ImageSampleFlagDerivative | ImageSampleFlagCd | ImageSampleFlagLodClamp), SampleInfo(0x6cu, "image_sample_cd_o", ImageSampleFlagDerivative | ImageSampleFlagCd | ImageSampleFlagOffset), SampleInfo(0x6du, "image_sample_cd_cl_o", ImageSampleFlagDerivative | ImageSampleFlagCd | ImageSampleFlagLodClamp | ImageSampleFlagOffset), SampleInfo(0x6eu, "image_sample_c_cd_o", ImageSampleFlagCompare | ImageSampleFlagDerivative | ImageSampleFlagCd | ImageSampleFlagOffset), SampleInfo(0x6fu, "image_sample_c_cd_cl_o", ImageSampleFlagCompare | ImageSampleFlagDerivative | ImageSampleFlagCd | ImageSampleFlagLodClamp | ImageSampleFlagOffset), SampleInfo(0xa0u, "image_sample_a", 0), SampleInfo(0xa1u, "image_sample_cl_a", ImageSampleFlagLodClamp), SampleInfo(0xa5u, "image_sample_b_a", ImageSampleFlagBias), SampleInfo(0xa6u, "image_sample_b_cl_a", ImageSampleFlagBias | ImageSampleFlagLodClamp), SampleInfo(0xa8u, "image_sample_c_a", ImageSampleFlagCompare), SampleInfo(0xa9u, "image_sample_c_cl_a", ImageSampleFlagCompare | ImageSampleFlagLodClamp), SampleInfo(0xadu, "image_sample_c_b_a", ImageSampleFlagCompare | ImageSampleFlagBias), SampleInfo(0xaeu, "image_sample_c_b_cl_a", ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagLodClamp), SampleInfo(0xb0u, "image_sample_o_a", ImageSampleFlagOffset), SampleInfo(0xb1u, "image_sample_cl_o_a", ImageSampleFlagLodClamp | ImageSampleFlagOffset), SampleInfo(0xb5u, "image_sample_b_o_a", ImageSampleFlagBias | ImageSampleFlagOffset), SampleInfo(0xb6u, "image_sample_b_cl_o_a", ImageSampleFlagBias | ImageSampleFlagLodClamp | ImageSampleFlagOffset), SampleInfo(0xb8u, "image_sample_c_o_a", ImageSampleFlagCompare | ImageSampleFlagOffset), SampleInfo(0xb9u, "image_sample_c_cl_o_a", ImageSampleFlagCompare | ImageSampleFlagLodClamp | ImageSampleFlagOffset), SampleInfo(0xbdu, "image_sample_c_b_o_a", ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagOffset), SampleInfo(0xbeu, "image_sample_c_b_cl_o_a", ImageSampleFlagCompare | ImageSampleFlagBias | ImageSampleFlagLodClamp | ImageSampleFlagOffset), }; constexpr MimgGatherInfo MIMG_GATHER_OPS[] = { {0x47u, "image_gather4_lz", Opcode::ImageGather4Lz, ImageSampleFlagLevelZero, 2u}, {0x48u, "image_gather4_c", Opcode::ImageGather4C, ImageSampleFlagCompare, 3u}, {0x4fu, "image_gather4_c_lz", Opcode::ImageGather4CLz, ImageSampleFlagCompare | ImageSampleFlagLevelZero, 3u}, {0x57u, "image_gather4_lz_o", Opcode::ImageGather4LzO, ImageSampleFlagLevelZero | ImageSampleFlagOffset, 3u}, {0x58u, "image_gather4_c_o", Opcode::ImageGather4CO, ImageSampleFlagCompare | ImageSampleFlagOffset, 4u}, {0x5fu, "image_gather4_c_lz_o", Opcode::ImageGather4CLzO, ImageSampleFlagCompare | ImageSampleFlagLevelZero | ImageSampleFlagOffset, 4u}, {0x61u, "image_gather4h", Opcode::ImageGather4H, ImageSampleFlagGatherHorizontal, 2u}, }; constexpr MimgAtomicInfo MIMG_ATOMIC_OPS[] = { {0x11u, "image_atomic_add", Opcode::ImageAtomicAdd}, {0x15u, "image_atomic_umin", Opcode::ImageAtomicUMin}, {0x17u, "image_atomic_umax", Opcode::ImageAtomicUMax}, {0x18u, "image_atomic_and", Opcode::ImageAtomicAnd}, {0x19u, "image_atomic_or", Opcode::ImageAtomicOr}, {0x1au, "image_atomic_xor", Opcode::ImageAtomicXor}, }; const MimgSampleInfo* LookupSample(uint32_t opcode) { for (const auto& info: MIMG_SAMPLE_OPS) { if (info.opcode == opcode) { return &info; } } return nullptr; } const MimgGatherInfo* LookupGather(uint32_t opcode) { for (const auto& info: MIMG_GATHER_OPS) { if (info.opcode == opcode) { return &info; } } return nullptr; } const MimgAtomicInfo* LookupAtomic(uint32_t opcode) { for (const auto& info: MIMG_ATOMIC_OPS) { if (info.opcode == opcode) { return &info; } } return nullptr; } uint32_t CountDmaskComponents(uint32_t dmask) { uint32_t count = 0; for (uint32_t i = 0; i < 4u; i++) { count += (dmask >> i) & 1u; } return count != 0 ? count : 1u; } bool IsSingleDmaskBit(uint32_t dmask) { return dmask != 0u && (dmask & (dmask - 1u)) == 0u; } } // namespace bool DecodeMimg(uint32_t pc, std::span code, uint32_t word_index, Instruction* inst, std::string* error) { if (word_index + 1u >= code.size()) { if (error != nullptr) { *error = fmt::format("truncated MIMG instruction at pc 0x{:08x}", pc); } return false; } const uint32_t word0 = code[word_index]; const uint32_t word1 = code[word_index + 1u]; const uint32_t opcode = ((word0 >> 18u) & 0x7fu) | ((word0 & 1u) << 7u); const uint32_t nsa_dwords = (word0 >> 1u) & 0x3u; const auto dimension = DecodeImageDimension((word0 >> 3u) & 0x7u); const uint32_t word_count = 2u + nsa_dwords; if (word_index + word_count > code.size()) { if (error != nullptr) { *error = fmt::format("truncated MIMG NSA instruction at pc 0x{:08x}", pc); } return false; } 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 a16 = ((word1 >> 30u) & 0x1u) != 0u; const auto* sample = LookupSample(opcode); const auto* gather = LookupGather(opcode); const auto* atomic = LookupAtomic(opcode); inst->pc = pc; inst->word = word0; inst->word_count = word_count; inst->family = Family::MIMG; inst->opcode_id = opcode; inst->opcode = (sample != nullptr ? Opcode::ImageSample : gather != nullptr ? gather->decoded : atomic != nullptr ? atomic->decoded : opcode == 0x00u ? Opcode::ImageLoad : opcode == 0x01u ? Opcode::ImageLoadMip : opcode == 0x08u ? Opcode::ImageStore : opcode == 0x09u ? Opcode::ImageStoreMip : opcode == 0x0eu ? Opcode::ImageGetResinfo : opcode == 0x60u ? Opcode::ImageGetLod : Opcode::Unsupported); inst->dmask = (word0 >> 8u) & 0xfu; inst->data_dwords = gather != nullptr ? 4u : CountDmaskComponents(inst->dmask); inst->glc = ((word0 >> 13u) & 1u) != 0; inst->slc = ((word0 >> 25u) & 1u) != 0; inst->image_sample_flags = (sample != nullptr ? sample->flags : gather != nullptr ? gather->flags : 0u) | (a16 ? ImageSampleFlagA16 : 0u); inst->image_dimension = dimension; inst->image_nsa_dwords = nsa_dwords; for (uint32_t i = 0; i < nsa_dwords * 4u && i < MaxImageNsaAddressComponents; i++) { inst->image_nsa_addr[i] = (code[word_index + 2u + i / 4u] >> ((i % 4u) * 8u)) & 0xffu; } inst->image_address_components = sample != nullptr ? ImageSampleAddressComponents(sample->flags, dimension) : gather != nullptr ? ImageSampleAddressComponents(gather->flags, dimension) : atomic != nullptr ? 3u : opcode == 0x60u ? ImageCoordComponents(dimension) : opcode == 0x01u ? ImageCoordComponents(dimension) + 1u : opcode == 0x00u ? ImageCoordComponents(dimension) : (opcode == 0x09u ? ImageCoordComponents(dimension) + 1u : opcode == 0x08u ? ImageCoordComponents(dimension) : 0u); SetRawWords(inst, code, word_index, word_count); if (inst->opcode == Opcode::Unsupported) { SetUnsupported(inst, Family::MIMG, opcode, "MIMG opcode is not implemented"); } if (gather != nullptr && !IsSingleDmaskBit(inst->dmask)) { SetUnsupported(inst, Family::MIMG, opcode, "MIMG image gather requires exactly one dmask bit"); } DecodeVectorGpr(vdata, &inst->dst, nullptr); DecodeVectorGpr(vaddr, &inst->src0, nullptr); DecodeScalarSource(srsrc * 4u, pc, &inst->src1, nullptr); DecodeScalarSource(ssamp * 4u, pc, &inst->src2, nullptr); inst->src_count = 3; return true; } const char* MimgSampleOpcodeName(uint32_t opcode) { const auto* sample = LookupSample(opcode); return sample != nullptr ? sample->name : nullptr; } uint32_t MimgSampleOpcodeFlags(uint32_t opcode) { const auto* sample = LookupSample(opcode); return sample != nullptr ? sample->flags : 0u; } uint32_t MimgSampleAddressComponents(uint32_t opcode) { const auto* sample = LookupSample(opcode); return sample != nullptr ? sample->address_components : 0u; } } // namespace Libs::Graphics::ShaderRecompiler::Decoder