diff --git a/src/libs/agc.cpp b/src/libs/agc.cpp index dbe00bd74..c116706a8 100644 --- a/src/libs/agc.cpp +++ b/src/libs/agc.cpp @@ -3714,6 +3714,20 @@ int KYTY_SYSV_ABI AgcSetPacketPredication(uint32_t* packet, uint32_t predication return OK; } +int KYTY_SYSV_ABI AgcSetNop(uint32_t* packet) { + PRINT_NAME(); + + const auto size = AgcGetPacketSize(packet); + + packet[0] = (packet[0] & 0xffff0003u) | (Pm4::IT_NOP << 8u); + // Prevent the disabled payload from being treated as an executable marker. + if (size > 1 && (packet[1] & 0xffff0000u) == 0x68750000u) { + packet[1] &= 0xffffu; + } + + return OK; +} + int KYTY_SYSV_ABI AgcSetRangePredication(uint32_t* start, const volatile uint32_t* end, uint32_t predication) { PRINT_NAME(); diff --git a/src/libs/agc.h b/src/libs/agc.h index bac31c03a..2244a5b22 100644 --- a/src/libs/agc.h +++ b/src/libs/agc.h @@ -270,6 +270,7 @@ uint32_t KYTY_SYSV_ABI AgcGetPacketSize(uint32_t* packet); int KYTY_SYSV_ABI AgcSetPacketPredication(uint32_t* packet, uint32_t predication); int KYTY_SYSV_ABI AgcSetRangePredication(uint32_t* start, const volatile uint32_t* end, uint32_t predication); +int KYTY_SYSV_ABI AgcSetNop(uint32_t* packet); int KYTY_SYSV_ABI AgcRewindPatchSetRewindState(uint32_t* cmd, uint8_t state); int KYTY_SYSV_ABI AgcCondExecPatchSetEnd(uint32_t* cmd, const volatile uint32_t* buffer); int KYTY_SYSV_ABI AgcCondExecPatchSetCommandAddress(uint32_t* cmd, diff --git a/src/libs/libAgcDriver.cpp b/src/libs/libAgcDriver.cpp index 0e0ceddb3..5cb83dc81 100644 --- a/src/libs/libAgcDriver.cpp +++ b/src/libs/libAgcDriver.cpp @@ -143,6 +143,7 @@ LIB_DEFINE(InitAgcDriver_1) { LIB_FUNC("Lkf86B98qPc", Gen5::AgcGetPacketSize); LIB_FUNC("w6Dj1VJt5qY", Gen5::AgcSetPacketPredication); LIB_FUNC("n8vgpaQg6dA", Gen5::AgcSetRangePredication); + LIB_FUNC("K2mciNVxUCE", Gen5::AgcSetNop); LIB_FUNC("i1jyy49AjXU", Gen5::AgcDcbWriteData); LIB_FUNC("p9tI+yTvx68", Gen5::AgcDcbWriteDataGetSize); LIB_FUNC("vuSXe69VILM", Gen5::AgcDcbGetLodStats); diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 22a735b01..a2f39402f 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -42630,6 +42630,43 @@ void CheckPm4IndirectControlFlow(RenderContext &renderer) { Gen5::AgcCbNop(reinterpret_cast(&payload_dcb), 3) == payload.data() && payload == std::array{0xc0011000u, 0x12345678u, 0x9abcdef0u}, "NOP emitter overwrote the caller's reserved payload"); + alignas(8) uint64_t released = 0; + std::array disabled_commands{}; + const auto disabled_write = write(&returned, 99); + std::copy(disabled_write.begin(), disabled_write.end(), disabled_commands.begin()); + disabled_commands[5] = KYTY_PM4(2, Pm4::IT_NUM_INSTANCES, 0); + disabled_commands[6] = 0x68750123u; + CommandBufferLayout release_dcb{disabled_commands.data() + 7, disabled_commands.data() + 15, + disabled_commands.data() + 7, disabled_commands.data() + 15, nullptr, nullptr, 0}; + auto* release = Gen5::AgcCbReleaseMem( + reinterpret_cast(&release_dcb), 0x28, 0, 0, 0, + reinterpret_cast(&released), 2, 77, 0, 0, 0, 0); + const auto enabled_write = write(&selected, 88); + std::copy(enabled_write.begin(), enabled_write.end(), disabled_commands.begin() + 15); + const auto unpatched_commands = disabled_commands; + Require(name, "SetNop packets", + release == disabled_commands.data() + 7 && + Gen5::AgcSetNop(disabled_commands.data()) == 0 && + Gen5::AgcSetNop(disabled_commands.data() + 5) == 0 && Gen5::AgcSetNop(release) == 0 && + disabled_commands[0] == ((disabled_write[0] & 0xffff00ffu) | (Pm4::IT_NOP << 8u)) && + disabled_commands[5] == KYTY_PM4(2, Pm4::IT_NOP, Pm4::R_ZERO) && + disabled_commands[6] == 0x123u && + release[0] == KYTY_PM4(8, Pm4::IT_NOP, Pm4::R_ZERO) && + unpatched_commands[7] == KYTY_PM4(8, Pm4::IT_NOP, Pm4::R_RELEASE_MEM) && + Gen5::AgcGetPacketSize(disabled_commands.data()) == disabled_write.size() && + Gen5::AgcGetPacketSize(disabled_commands.data() + 5) == 2 && + Gen5::AgcGetPacketSize(release) == 8 && + std::equal(disabled_commands.begin() + 1, disabled_commands.begin() + 5, + unpatched_commands.begin() + 1) && + std::equal(disabled_commands.begin() + 8, disabled_commands.end(), + unpatched_commands.begin() + 8), + "SetNop changed a packet length, kept its operation or marker, or touched its payload"); + selected = returned = 0; + Pm4Execution disabled_execution; + Require(name, "SetNop execution", + processor.Process(disabled_execution, disabled_commands) == Pm4ProcessResult::Complete && + returned == 0 && released == 0 && selected == 88, + "a disabled packet still executed or hid the packet after it"); const auto then_commands = write(&selected, 11); const auto else_commands = write(&selected, 33); const auto branch_suffix = write(&selected, 44);