mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-27 16:02:37 +00:00
shader: decode every PS5 image_gather4 encoding
Co-authored-by: shadowbeat070 <70843316+shadowbeat070@users.noreply.github.com>
This commit is contained in:
1 parent
1b70938052
commit
ffa3992c1a
7 files changed
+249
-130
No files matched your search
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in new issue
Block a user