mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 02:47:02 +02:00
shader: decode and translate V_FRACT_F16 (VOP1 0x5f) and V_CMPX_LT_U16 (VOPC 0xb9) (#582)
shader: support V_FRACT_F16 and V_CMPX_LT_U16 Decode compact, SDWA, and VOP3 forms using the existing arithmetic and comparison paths. Preserve EXEC masking and VCC for the integer compare, and reject unsupported DPP encoding. Add captured-instruction, operand-selection, floating-point edge, and execution-mask regression coverage. Co-authored-by: nmzik <Nmzik@mail.ru>
This commit is contained in:
@@ -17835,6 +17835,91 @@ TestCase VectorSpecialF16Ops() {
|
||||
O::BUFFER_STORE_DWORD, O::S_ENDPGM}};
|
||||
}
|
||||
|
||||
TestCase VectorFractF16CapturedAndEdges() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
// RDNA2 section 12.8: x - floor(x), rounded to half precision.
|
||||
// Load at runtime to exercise the emitted operation, including half subnormals.
|
||||
const std::array<u32, 18> inputs{
|
||||
0x3d00u, 0xbd00u, 0x0000u, 0x8000u, 0x0001u, 0x8001u,
|
||||
0x03ffu, 0x83ffu, 0x7bffu, 0xfbffu, 0x3800u, 0xb800u,
|
||||
0x8c00u, 0x8c01u, 0x7c00u, 0xfc00u, 0x7e55u, 0x7d01u};
|
||||
const std::array<u32, 18> fractions{
|
||||
0x3400u, 0x3a00u, 0x0000u, 0x0000u, 0x0001u, 0x3c00u,
|
||||
0x03ffu, 0x3c00u, 0x0000u, 0x0000u, 0x3800u, 0x3800u,
|
||||
0x3c00u, 0x3bffu, 0x7e00u, 0x7e00u, 0x7e00u, 0x7e00u};
|
||||
TestCase test;
|
||||
test.name = "VectorFractF16CapturedAndEdges";
|
||||
for (u32 bits : inputs) {
|
||||
test.initial.push_back(0x42000000u | bits); // Distinct high half must be ignored.
|
||||
}
|
||||
test.expected = test.initial;
|
||||
auto &code = test.code;
|
||||
for (u32 i = 0; i < inputs.size(); i++) {
|
||||
AppendVMovU32(&code, 30, i * 4u);
|
||||
AppendBufferLoadDword(&code, 7, 30);
|
||||
AppendVMovLiteral(&code, 9, 0xa5a51234u);
|
||||
code.push_back(0x7e12bf07u); // Captured v_fract_f16 v9, v7.
|
||||
if (i >= 14u) {
|
||||
// Check quiet NaN class without constraining its sign or payload.
|
||||
code.push_back(EncodeVop2(0x1b, 9, 255u, 9));
|
||||
code.push_back(0xffff7e00u);
|
||||
}
|
||||
AppendStoreVgpr(&code, 9, static_cast<u32>(inputs.size()) + i);
|
||||
test.expected.push_back(0xa5a50000u | fractions[i]);
|
||||
}
|
||||
AppendEnd(&code);
|
||||
test.opcodes = {O::V_MOV_B32, O::BUFFER_LOAD_DWORD, O::V_FRACT_F16,
|
||||
O::V_AND_B32, O::BUFFER_STORE_DWORD, O::S_ENDPGM};
|
||||
test.decoded_counts = {{"V_FRACT_F16 v9, v7", inputs.size()}};
|
||||
test.ir_counts = {{" = FPFract32 ", inputs.size()}};
|
||||
test.required_spirv = {" Fract ", " PackHalf2x16 ", " UnpackHalf2x16 "};
|
||||
test.forbidden_spirv = {"OpCapability Float16"};
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase VectorFractF16Modifiers() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
std::vector<u32> code;
|
||||
AppendVMovLiteral(&code, 7, 0xbd003d00u); // high=-1.25h, low=+1.25h
|
||||
for (u32 dst = 10; dst <= 19; dst++) {
|
||||
AppendVMovLiteral(&code, dst, 0xabcd1234u);
|
||||
}
|
||||
code.push_back(EncodeVop1(0x5f, 10, 249));
|
||||
code.push_back(EncodeVop1Sdwa(7, 5, 2, 5)); // high -> high, preserve low
|
||||
code.push_back(EncodeVop1(0x5f, 11, 249));
|
||||
code.push_back(EncodeVop1Sdwa(7, 4, 0, 5, 0, 1, 1)); // -abs(-1.25), pad high
|
||||
code.push_back(EncodeVop1(0x5f, 12, 249));
|
||||
code.push_back(EncodeVop1Sdwa(7, 4, 2, 5, 0, 0, 1)); // abs(-1.25)
|
||||
code.push_back(EncodeVop1(0x5f, 13, 249));
|
||||
code.push_back(EncodeVop1Sdwa(7, 4, 2, 5, 0, 0, 0, 0, 1, 1)); // *2, clamp
|
||||
code.push_back(EncodeVop1(0x5f, 14, 250));
|
||||
code.push_back(EncodeVop1Dpp(7, 0x0e4) | (1u << 20u)); // DPP identity, negate
|
||||
AppendVop3(&code, 0x1df, 15, Vgpr(7), 0, 0, 1, 0, true, 1, 1);
|
||||
code.push_back(EncodeVop1(0x5f, 16, 255));
|
||||
code.push_back(0x4200bd00u); // literal -1.25h
|
||||
code.push_back(EncodeVop1(0x5f, 17, 240)); // inline +0.5, interpreted as FP16
|
||||
AppendSMovLiteral(&code, 20, 0x4200bd00u);
|
||||
code.push_back(EncodeVop1(0x5f, 18, 20)); // SGPR source
|
||||
AppendVop3(&code, 0x1df, 19, Vgpr(7), 0, 0, 0, 0, false, 3); // *0.5
|
||||
for (u32 i = 0; i < 10; i++) {
|
||||
AppendStoreVgpr(&code, 10 + i, i);
|
||||
}
|
||||
AppendEnd(&code);
|
||||
|
||||
TestCase test{"VectorFractF16Modifiers", code, {},
|
||||
{0x3a001234u, 0x00003a00u, 0xabcd3400u, 0xabcd3c00u,
|
||||
0xabcd3a00u, 0xabcd3c00u, 0xabcd3a00u, 0xabcd3800u,
|
||||
0xabcd3a00u, 0xabcd3000u},
|
||||
{O::V_MOV_B32, O::S_MOV_B32, O::V_FRACT_F16,
|
||||
O::BUFFER_STORE_DWORD, O::S_ENDPGM}};
|
||||
test.decoded_counts = {{"V_FRACT_F16", 10}};
|
||||
test.ir_counts = {{" = FPFract32 ", 10}};
|
||||
test.required_spirv = {" Fract ", " PackHalf2x16 "};
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase VectorCosF16CapturedSdwaAndEdges() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
@@ -19593,6 +19678,69 @@ TestCase VectorVopcCmpxGtU16CapturedSdwaExecMask() {
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase VectorVopcCmpxLtU16CapturedSdwaExecMask() {
|
||||
using O = ShaderOpcode;
|
||||
struct CompareCase {
|
||||
u32 lhs;
|
||||
u32 rhs;
|
||||
u32 incoming_exec;
|
||||
u32 expected_exec;
|
||||
u32 encoding = 0; // Captured SDWA, compact, VOP3, WORD_1 src0/src1.
|
||||
};
|
||||
const std::array<CompareCase, 12> cases{{
|
||||
{0xffff0000u, 0x12340001u, 1, 1}, // Ignore high halves.
|
||||
{0xabcd0001u, 0x12340001u, 1, 0}, // Strict comparison: equal is false.
|
||||
{0x0000ffffu, 0x12340001u, 1, 0}, // Unsigned, not signed -1.
|
||||
{0x00007fffu, 0x12348000u, 1, 1},
|
||||
{0x00008000u, 0x12347fffu, 1, 0},
|
||||
{0x12340000u, 0x56780000u, 1, 0},
|
||||
{0x0000fffeu, 0x1234ffffu, 1, 1},
|
||||
{0x00000000u, 0x12340001u, 0, 0}, // CMPX cannot reactivate an inactive lane.
|
||||
{0x00008000u, 0x0000ffffu, 1, 1, 1},
|
||||
{0x0000ffffu, 0x00008000u, 1, 0, 2},
|
||||
{0x0001ffffu, 0x12340002u, 1, 1, 3},
|
||||
{0x00000001u, 0x00020000u, 1, 1, 4},
|
||||
}};
|
||||
constexpr u32 vcc_hi = 0x89abcdefu;
|
||||
TestCase test;
|
||||
test.name = "VectorVopcCmpxLtU16CapturedSdwaExecMask";
|
||||
for (const auto &entry : cases) {
|
||||
test.initial.push_back(entry.lhs);
|
||||
}
|
||||
test.expected = test.initial;
|
||||
auto &code = test.code;
|
||||
for (u32 i = 0; i < cases.size(); ++i) {
|
||||
const auto &entry = cases[i];
|
||||
AppendVMovU32(&code, 30, i * 4u);
|
||||
AppendBufferLoadDword(&code, 0, 30); // Runtime input prevents constant folding.
|
||||
AppendVMovU32(&code, 1, entry.rhs);
|
||||
AppendSMovLiteral(&code, 106, entry.rhs);
|
||||
AppendSMovLiteral(&code, 107, vcc_hi);
|
||||
code.push_back(EncodeSMovB32(126, InlineU32(entry.incoming_exec)));
|
||||
switch (entry.encoding) {
|
||||
case 1: code.push_back(0x7d720300u); break; // compact v0, v1
|
||||
case 2: code.insert(code.end(), {0xd4b9007eu, 0x00020300u}); break;
|
||||
case 3: code.insert(code.end(), {0x7d72d4f9u, 0x86050000u}); break;
|
||||
case 4: code.insert(code.end(), {0x7d72d4f9u, 0x85060000u}); break;
|
||||
default: code.insert(code.end(), {0x7d72d4f9u, 0x86060000u}); break;
|
||||
}
|
||||
code.push_back(EncodeSMovB32(20, 126)); // Snapshot EXEC before restoring it.
|
||||
code.push_back(EncodeSMovB32(21, 127));
|
||||
code.push_back(EncodeSMovB32(126, InlineU32(1)));
|
||||
const u32 out = static_cast<u32>(cases.size()) + i * 4u;
|
||||
AppendStoreSgprPair(&code, 20, out);
|
||||
AppendStoreSgprPair(&code, 106, out + 2u); // CMPX must not overwrite either VCC half.
|
||||
test.expected.insert(test.expected.end(),
|
||||
{entry.expected_exec, 0u, entry.rhs, vcc_hi});
|
||||
}
|
||||
AppendEnd(&code);
|
||||
test.opcodes = {O::V_MOV_B32, O::S_MOV_B32, O::BUFFER_LOAD_DWORD,
|
||||
O::V_CMPX_LT_U16, O::BUFFER_STORE_DWORD, O::S_ENDPGM};
|
||||
test.decoded_counts = {{"V_CMPX_LT_U16", cases.size()}};
|
||||
test.required_spirv = {"OpULessThan"};
|
||||
return test;
|
||||
}
|
||||
|
||||
TestCase VectorVopcCmpxNgtF16CapturedSdwaExecMask() {
|
||||
using O = ShaderOpcode;
|
||||
|
||||
@@ -24928,6 +25076,8 @@ std::vector<TestCase> MakeCases() {
|
||||
AddCase(NativeAndSdwa16BitDestinationWrites);
|
||||
AddCase(VectorMinMaxMed3F16Ops);
|
||||
AddCase(VectorSpecialF16Ops);
|
||||
AddCase(VectorFractF16CapturedAndEdges);
|
||||
AddCase(VectorFractF16Modifiers);
|
||||
AddCase(VectorCosF16CapturedSdwaAndEdges);
|
||||
AddCase(VectorSinF16SdwaAndEdges);
|
||||
AddCase(VectorWritelaneIgnoresExecMask);
|
||||
@@ -24980,6 +25130,7 @@ std::vector<TestCase> MakeCases() {
|
||||
AddCase(VectorVop3CmpxWritesExecMask);
|
||||
AddCase(VectorVopcSdwaCmpxWritesExecMask);
|
||||
AddCase(VectorVopcCmpxGtU16CapturedSdwaExecMask);
|
||||
AddCase(VectorVopcCmpxLtU16CapturedSdwaExecMask);
|
||||
AddCase(VectorVopcCmpxNgtF16CapturedSdwaExecMask);
|
||||
AddCase(VectorCompareInvertedMaskSelect);
|
||||
AddCase(BranchSelect);
|
||||
@@ -29204,6 +29355,26 @@ int main(int argc, char **argv) {
|
||||
std::setvbuf(stdout, nullptr, _IONBF, 0);
|
||||
EnsureConfigInitialized();
|
||||
CheckLeastRecentlyUsedCacheOrdering();
|
||||
if (argc == 2 && std::strcmp(argv[1], "--cmpx-lt-u16-only") == 0) {
|
||||
VulkanHarness vulkan;
|
||||
RunCase(&vulkan, VectorVopcCmpxLtU16CapturedSdwaExecMask());
|
||||
RunCase(&vulkan, VectorVopcCmpxGtU16CapturedSdwaExecMask());
|
||||
RunCase(&vulkan, VectorVopcSdwaCmpxWritesExecMask());
|
||||
RunCase(&vulkan, VectorVop3CmpxWritesExecMask());
|
||||
RunCase(&vulkan, VectorVopcSdwaCmpxClassF32CapturedExecMask());
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--fract-f16-only") == 0) {
|
||||
VulkanHarness vulkan;
|
||||
RunCase(&vulkan, VectorFractF16CapturedAndEdges());
|
||||
RunCase(&vulkan, VectorFractF16Modifiers());
|
||||
RunCase(&vulkan, NativeAndSdwa16BitDestinationWrites());
|
||||
RunCase(&vulkan, VectorSpecialF16Ops());
|
||||
RunCase(&vulkan, VectorCosF16CapturedSdwaAndEdges());
|
||||
RunCase(&vulkan, VectorSinF16SdwaAndEdges());
|
||||
RunCase(&vulkan, VectorFloatConversionOps());
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--packed-texture-only") == 0) {
|
||||
VulkanHarness vulkan;
|
||||
vulkan.CheckPackedTextureComponents();
|
||||
|
||||
Reference in New Issue
Block a user