mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 02:47:02 +02:00
shader: add missing 64-bit integer image atomics
Co-authored-by: shadowbeat070 <70843316+shadowbeat070@users.noreply.github.com>
This commit is contained in:
@@ -33646,20 +33646,68 @@ TestCase ImageAtomicVariants() {
|
||||
return test;
|
||||
}
|
||||
|
||||
template <bool wide> TestCase ImageAtomicUMaxGlcAndExec() {
|
||||
// Input and old texel: high DWORD ordering, unsigned sign boundary, and
|
||||
// equal-high-DWORD ordering must all survive the RG32_UINT -> R64_UINT view.
|
||||
constexpr uint64_t values[][2] = {
|
||||
{0x0000000100000001ull, 0x00000000ffffffffull},
|
||||
{0x00000000ffffffffull, 0x0000000100000001ull},
|
||||
{0x8000000000000000ull, 0x7fffffffffffffffull},
|
||||
{0x12345678ffffffffull, 0x1234567800000001ull},
|
||||
{0xffffffffffffffffull, 0x8000000000000000ull},
|
||||
};
|
||||
constexpr u32 texel_dwords = wide ? 2 : 1;
|
||||
// Input and old texel cover high-DWORD ordering, carry/wrap, the unsigned sign
|
||||
// boundary, and equal-high-DWORD ordering through the RG32_UINT -> R64_UINT view.
|
||||
constexpr uint64_t ImageAtomicInputs[][2] = {
|
||||
{0x0000000100000001ull, 0x00000000ffffffffull},
|
||||
{0x00000000ffffffffull, 0x0000000100000001ull},
|
||||
{0x8000000000000000ull, 0x7fffffffffffffffull},
|
||||
{0x12345678ffffffffull, 0x1234567800000001ull},
|
||||
{0xffffffffffffffffull, 0x8000000000000000ull},
|
||||
};
|
||||
|
||||
struct ImageAtomicIntegerCase {
|
||||
u32 encoding;
|
||||
ShaderOpcode opcode;
|
||||
const char *name32;
|
||||
const char *name64;
|
||||
const char *spirv;
|
||||
uint64_t expected64[std::size(ImageAtomicInputs)];
|
||||
u32 expected32[std::size(ImageAtomicInputs)];
|
||||
};
|
||||
|
||||
constexpr ImageAtomicIntegerCase ImageAtomicIntegerCases[] = {
|
||||
{0x0fu, ShaderOpcode::IMAGE_ATOMIC_SWAP,
|
||||
"ImageAtomicSwap32GlcAndExec", "ImageAtomicSwap64GlcAndExec", "OpAtomicExchange",
|
||||
{0x0000000100000001ull, 0x00000000ffffffffull, 0x8000000000000000ull,
|
||||
0x12345678ffffffffull, 0xffffffffffffffffull},
|
||||
{0x00000001u, 0xffffffffu, 0x00000000u, 0xffffffffu, 0xffffffffu}},
|
||||
{0x11u, ShaderOpcode::IMAGE_ATOMIC_ADD,
|
||||
"ImageAtomicAdd32GlcAndExec", "ImageAtomicAdd64GlcAndExec", "OpAtomicIAdd",
|
||||
{0x0000000200000000ull, 0x0000000200000000ull, 0xffffffffffffffffull,
|
||||
0x2468acf100000000ull, 0x7fffffffffffffffull},
|
||||
{0x00000000u, 0x00000000u, 0xffffffffu, 0x00000000u, 0xffffffffu}},
|
||||
{0x15u, ShaderOpcode::IMAGE_ATOMIC_UMIN,
|
||||
"ImageAtomicUMin32GlcAndExec", "ImageAtomicUMin64GlcAndExec", "OpAtomicUMin",
|
||||
{0x00000000ffffffffull, 0x00000000ffffffffull, 0x7fffffffffffffffull,
|
||||
0x1234567800000001ull, 0x8000000000000000ull},
|
||||
{0x00000001u, 0x00000001u, 0x00000000u, 0x00000001u, 0x00000000u}},
|
||||
{0x17u, ShaderOpcode::IMAGE_ATOMIC_UMAX,
|
||||
"ImageAtomicUMax32GlcAndExec", "ImageAtomicUMax64GlcAndExec", "OpAtomicUMax",
|
||||
{0x0000000100000001ull, 0x0000000100000001ull, 0x8000000000000000ull,
|
||||
0x12345678ffffffffull, 0xffffffffffffffffull},
|
||||
{0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu}},
|
||||
{0x18u, ShaderOpcode::IMAGE_ATOMIC_AND,
|
||||
"ImageAtomicAnd32GlcAndExec", "ImageAtomicAnd64GlcAndExec", "OpAtomicAnd",
|
||||
{0x0000000000000001ull, 0x0000000000000001ull, 0x0000000000000000ull,
|
||||
0x1234567800000001ull, 0x8000000000000000ull},
|
||||
{0x00000001u, 0x00000001u, 0x00000000u, 0x00000001u, 0x00000000u}},
|
||||
{0x19u, ShaderOpcode::IMAGE_ATOMIC_OR,
|
||||
"ImageAtomicOr32GlcAndExec", "ImageAtomicOr64GlcAndExec", "OpAtomicOr",
|
||||
{0x00000001ffffffffull, 0x00000001ffffffffull, 0xffffffffffffffffull,
|
||||
0x12345678ffffffffull, 0xffffffffffffffffull},
|
||||
{0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu}},
|
||||
{0x1au, ShaderOpcode::IMAGE_ATOMIC_XOR,
|
||||
"ImageAtomicXor32GlcAndExec", "ImageAtomicXor64GlcAndExec", "OpAtomicXor",
|
||||
{0x00000001fffffffeull, 0x00000001fffffffeull, 0xffffffffffffffffull,
|
||||
0x00000000fffffffeull, 0x7fffffffffffffffull},
|
||||
{0xfffffffeu, 0xfffffffeu, 0xffffffffu, 0xfffffffeu, 0xffffffffu}},
|
||||
};
|
||||
|
||||
TestCase ImageAtomicIntegerGlcAndExec(const ImageAtomicIntegerCase &atomic, bool wide) {
|
||||
const u32 texel_dwords = wide ? 2 : 1;
|
||||
TestCase test;
|
||||
test.name = wide ? "ImageAtomicUMax64CapturedGlcAndExec"
|
||||
: "ImageAtomicUMax32GlcAndExec";
|
||||
test.name = wide ? atomic.name64 : atomic.name32;
|
||||
test.user_data = MakeStorageTextureData(wide ? Prospero::BufferFormat::k32_32UInt
|
||||
: Prospero::BufferFormat::k32UInt);
|
||||
test.has_user_data = true;
|
||||
@@ -33667,25 +33715,21 @@ template <bool wide> TestCase ImageAtomicUMaxGlcAndExec() {
|
||||
test.expected_storage_image_r32ui = test.storage_image_r32ui;
|
||||
auto &code = test.code;
|
||||
code.push_back(EncodeSop1(0x04, 12, 126)); // Preserve EXEC.
|
||||
for (u32 i = 0; i < std::size(values); ++i) {
|
||||
const auto input = values[i][0];
|
||||
const auto old = values[i][1];
|
||||
for (u32 i = 0; i < std::size(ImageAtomicInputs); ++i) {
|
||||
const auto input = ImageAtomicInputs[i][0];
|
||||
const auto old = ImageAtomicInputs[i][1];
|
||||
const bool glc = i != 0 && i != 4;
|
||||
AppendVMovU32(&code, 0, i & 3u);
|
||||
AppendVMovU32(&code, 1, i >> 2u);
|
||||
AppendVMovLiteral(&code, 2, static_cast<u32>(input));
|
||||
AppendVMovLiteral(&code, 3, static_cast<u32>(input >> 32u));
|
||||
// Captured PS5 instruction: f05c0308 00000200, DMASK=3, VDATA=v[2:3].
|
||||
code.insert(code.end(), {0xf05c0108u | (wide ? 0x200u : 0u) |
|
||||
(glc ? 0x2000u : 0u),
|
||||
0x00000200u});
|
||||
code.push_back(EncodeMimg0(atomic.encoding, wide ? 3u : 1u, 0, glc));
|
||||
code.push_back(EncodeMimg1(2, 0));
|
||||
AppendStoreVgpr(&code, 2, i * 2);
|
||||
AppendStoreVgpr(&code, 3, i * 2 + 1);
|
||||
test.expected.push_back(static_cast<u32>(glc ? old : input));
|
||||
test.expected.push_back(static_cast<u32>((wide && glc ? old : input) >> 32u));
|
||||
const uint64_t result = wide ? std::max(input, old)
|
||||
: std::max(static_cast<u32>(input),
|
||||
static_cast<u32>(old));
|
||||
const uint64_t result = wide ? atomic.expected64[i] : atomic.expected32[i];
|
||||
for (u32 word = 0; word < texel_dwords; ++word) {
|
||||
test.storage_image_r32ui[i * texel_dwords + word] =
|
||||
static_cast<u32>(old >> (word * 32u));
|
||||
@@ -33693,23 +33737,30 @@ template <bool wide> TestCase ImageAtomicUMaxGlcAndExec() {
|
||||
static_cast<u32>(result >> (word * 32u));
|
||||
}
|
||||
}
|
||||
// A leaked update must change the inactive texel for every operation.
|
||||
const bool clear_bits = atomic.opcode == ShaderOpcode::IMAGE_ATOMIC_AND ||
|
||||
atomic.opcode == ShaderOpcode::IMAGE_ATOMIC_UMIN;
|
||||
const u32 inactive = clear_bits ? 0xffffffffu : 0u;
|
||||
for (u32 word = 0; word < texel_dwords; ++word) {
|
||||
test.storage_image_r32ui[5u * texel_dwords + word] = inactive;
|
||||
test.expected_storage_image_r32ui[5u * texel_dwords + word] = inactive;
|
||||
}
|
||||
AppendVMovU32(&code, 0, 1);
|
||||
AppendVMovU32(&code, 1, 1); // Untouched zero texel, so an EXEC leak is observable.
|
||||
AppendVMovU32(&code, 1, 1);
|
||||
AppendVMovLiteral(&code, 2, 0xabcdef01u);
|
||||
AppendVMovLiteral(&code, 3, 0x87654321u);
|
||||
code.push_back(EncodeSop1(0x04, 126, InlineU32(0)));
|
||||
code.insert(code.end(), {0xf05c2108u | (wide ? 0x200u : 0u), 0x00000200u});
|
||||
code.push_back(EncodeMimg0(atomic.encoding, wide ? 3u : 1u, 0, true));
|
||||
code.push_back(EncodeMimg1(2, 0));
|
||||
code.push_back(EncodeSop1(0x04, 126, 12));
|
||||
AppendStoreVgpr(&code, 2, std::size(values) * 2);
|
||||
AppendStoreVgpr(&code, 3, std::size(values) * 2 + 1);
|
||||
AppendStoreVgpr(&code, 2, std::size(ImageAtomicInputs) * 2);
|
||||
AppendStoreVgpr(&code, 3, std::size(ImageAtomicInputs) * 2 + 1);
|
||||
test.expected.insert(test.expected.end(), {0xabcdef01u, 0x87654321u});
|
||||
AppendEnd(&code);
|
||||
test.opcodes = {ShaderOpcode::V_MOV_B32, ShaderOpcode::S_MOV_B64,
|
||||
ShaderOpcode::IMAGE_ATOMIC_UMAX, ShaderOpcode::BUFFER_STORE_DWORD,
|
||||
ShaderOpcode::S_ENDPGM};
|
||||
test.required_spirv = {"OpAtomicUMax", "OpImageTexelPointer",
|
||||
wide ? "R64ui" : "R32ui"};
|
||||
if constexpr (wide) {
|
||||
atomic.opcode, ShaderOpcode::BUFFER_STORE_DWORD, ShaderOpcode::S_ENDPGM};
|
||||
test.required_spirv = {atomic.spirv, "OpImageTexelPointer", wide ? "R64ui" : "R32ui"};
|
||||
if (wide) {
|
||||
test.required_spirv.push_back("Int64ImageEXT");
|
||||
test.required_spirv.push_back("SPV_EXT_shader_image_int64");
|
||||
}
|
||||
@@ -34802,8 +34853,10 @@ std::vector<TestCase> MakeCases() {
|
||||
AddCase(ImageAtomicSwapReturnsPreviousTexel);
|
||||
AddCase(ImageStoreAndAtomicUseSeparateBindings);
|
||||
AddCase(ImageAtomicVariants);
|
||||
AddCase(ImageAtomicUMaxGlcAndExec<false>);
|
||||
AddCase(ImageAtomicUMaxGlcAndExec<true>);
|
||||
for (const auto &atomic : ImageAtomicIntegerCases) {
|
||||
cases.push_back(ImageAtomicIntegerGlcAndExec(atomic, false));
|
||||
cases.push_back(ImageAtomicIntegerGlcAndExec(atomic, true));
|
||||
}
|
||||
AddCase(ImageAtomicFloatGlcAndExec<true>);
|
||||
AddCase(ImageAtomicFloatGlcAndExec<false>);
|
||||
AddCase(ImageAtomicFloatSpecialValues<true>);
|
||||
@@ -40121,8 +40174,10 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--image-atomic64-only") == 0) {
|
||||
VulkanHarness vulkan;
|
||||
RunCase(&vulkan, ImageAtomicUMaxGlcAndExec<false>());
|
||||
RunCase(&vulkan, ImageAtomicUMaxGlcAndExec<true>());
|
||||
for (const auto &atomic : ImageAtomicIntegerCases) {
|
||||
RunCase(&vulkan, ImageAtomicIntegerGlcAndExec(atomic, false));
|
||||
RunCase(&vulkan, ImageAtomicIntegerGlcAndExec(atomic, true));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--ds-atomics-only") == 0) {
|
||||
|
||||
+22
-15
@@ -4413,25 +4413,32 @@ void TestImageAtomicWidthDecoder() {
|
||||
decoded.dst.reg == 2u && decoded.dmask == 3u && !decoded.glc,
|
||||
"captured image atomic lost its 64-bit data width");
|
||||
|
||||
for (const auto opcode : {0x10u, 0x17u}) {
|
||||
for (const auto opcode : {0x0fu, 0x10u, 0x11u, 0x14u, 0x15u, 0x16u,
|
||||
0x17u, 0x18u, 0x19u, 0x1au, 0x1eu, 0x1fu}) {
|
||||
const bool compare_swap = opcode == 0x10u;
|
||||
const bool supports_64 = opcode != 0x10u && opcode != 0x14u &&
|
||||
opcode != 0x16u && opcode != 0x1eu && opcode != 0x1fu;
|
||||
for (uint32_t mask = 0; mask < 16u; ++mask) {
|
||||
const uint32_t words[] = {EncodeMimg0(opcode, mask), captured[1]};
|
||||
DecodeInstruction(words, 0, decoded);
|
||||
const bool compare_swap = opcode == 0x10u;
|
||||
const bool supported = mask == (compare_swap ? 3u : 1u) ||
|
||||
(!compare_swap && mask == 3u);
|
||||
Check((decoded.opcode != Opcode::UNSUPPORTED) == supported,
|
||||
"image atomic accepted an invalid or unsupported width mask");
|
||||
if (supported) {
|
||||
Check(decoded.data_bits == (!compare_swap && mask == 3u ? 64u : 32u),
|
||||
"image atomic DMASK selected the wrong data width");
|
||||
for (const bool glc : {false, true}) {
|
||||
const uint32_t words[] = {EncodeMimg0(opcode, mask, glc), captured[1]};
|
||||
DecodeInstruction(words, 0, decoded);
|
||||
const bool supported = mask == (compare_swap ? 3u : 1u) ||
|
||||
(supports_64 && mask == 3u);
|
||||
Check((decoded.opcode != Opcode::UNSUPPORTED) == supported,
|
||||
"image atomic accepted an invalid or unsupported width mask");
|
||||
if (supported) {
|
||||
const auto bits = !compare_swap && mask == 3u ? 64u : 32u;
|
||||
Check(decoded.data_bits == bits && decoded.glc == glc &&
|
||||
decoded.data_dwords == (compare_swap ? 2u : bits / 32u),
|
||||
"image atomic DMASK selected the wrong data width or return mode");
|
||||
}
|
||||
const uint32_t d16_words[] = {words[0], words[1] | (1u << 31u)};
|
||||
DecodeInstruction(d16_words, 0, decoded);
|
||||
Check(decoded.opcode == Opcode::UNSUPPORTED,
|
||||
"image atomic incorrectly accepted D16 data");
|
||||
}
|
||||
}
|
||||
}
|
||||
const uint32_t unsupported[] = {EncodeMimg0(0x11u, 3u), captured[1]};
|
||||
DecodeInstruction(unsupported, 0, decoded);
|
||||
Check(decoded.opcode == Opcode::UNSUPPORTED && decoded.data_bits == 64u,
|
||||
"unsupported 64-bit image atomic silently decoded as 32-bit");
|
||||
}
|
||||
|
||||
void TestNewShaderDecoderArchitecture() {
|
||||
|
||||
Reference in New Issue
Block a user