shader: decode every PS5 image_gather4 encoding

Co-authored-by: shadowbeat070 <70843316+shadowbeat070@users.noreply.github.com>
This commit is contained in:
nmzik
2026-10-27 01:13:46 +01:00
parent 1b70938052
commit ffa3992c1a
7 changed files with 249 additions and 130 deletions
@@ -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<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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
@@ -8,7 +8,7 @@ namespace Libs::Graphics::ShaderRecompiler::Decoder {
void DecodeMimg(uint32_t pc, std::span<const uint32_t> 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;
@@ -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:
@@ -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,
@@ -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);
+80 -22
View File
@@ -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<uint32_t>(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<u32>(16, 0xaae2b321u);
test.sampled_image_format = vk::Format::eR32Uint;
@@ -39144,7 +39138,7 @@ template <bool rg, bool snorm = false> TestCase ImageSamplePacked8() {
for (float value : expected) {
test.expected.push_back(std::bit_cast<u32>(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 <u32 mode> 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<u32>(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<u32>(0.6875f));
AppendVMovLiteral(&test.code, vaddr++, std::bit_cast<u32>(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<TestCase> 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;
}
+70 -2
View File
@@ -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();