From aa3b2cd57f84e414a4af0ccbc42d7eb35a8af17b Mon Sep 17 00:00:00 2001 From: nmzik Date: Thu, 8 Oct 2026 17:22:01 +0400 Subject: [PATCH] Implement AGC async rewind imports --- src/libs/agc.cpp | 8 ++ src/libs/agc.h | 1 + src/libs/libAgcDriver.cpp | 2 + tests/ShaderRecompilerComputeTests.cpp | 121 +++++++++++++++++++------ 4 files changed, 105 insertions(+), 27 deletions(-) diff --git a/src/libs/agc.cpp b/src/libs/agc.cpp index 21351de62..de431d44e 100644 --- a/src/libs/agc.cpp +++ b/src/libs/agc.cpp @@ -3217,6 +3217,14 @@ uint32_t KYTY_SYSV_ABI AgcAcbJumpGetSize() { return 0x10u; } +uint32_t* KYTY_SYSV_ABI AgcAcbRewind(CommandBuffer* buf, uint8_t initial_state, uint8_t offload) { + auto* cmd = AgcDcbRewind(buf, initial_state); + if (cmd != nullptr) { + cmd[1] |= (static_cast(offload & 0x1u) << 24u); + } + return cmd; +} + uint32_t* KYTY_SYSV_ABI AgcAcbWaitRegMem(CommandBuffer* buf, uint8_t size, uint8_t compare_function, uint8_t cache_policy, const volatile void* address, uint64_t reference, uint64_t mask, uint32_t poll_cycles) { diff --git a/src/libs/agc.h b/src/libs/agc.h index de13577d8..7eac1b193 100644 --- a/src/libs/agc.h +++ b/src/libs/agc.h @@ -206,6 +206,7 @@ uint32_t KYTY_SYSV_ABI AgcAcbCondExecGetSize(); uint32_t* KYTY_SYSV_ABI AgcAcbJump(CommandBuffer* buf, uint8_t cache_policy, const uint32_t* target, uint32_t size_in_dwords); uint32_t KYTY_SYSV_ABI AgcAcbJumpGetSize(); +uint32_t* KYTY_SYSV_ABI AgcAcbRewind(CommandBuffer* buf, uint8_t initial_state, uint8_t offload); uint32_t* KYTY_SYSV_ABI AgcAcbWaitRegMem(CommandBuffer* buf, uint8_t size, uint8_t compare_function, uint8_t cache_policy, const volatile void* address, uint64_t reference, uint64_t mask, uint32_t poll_cycles); diff --git a/src/libs/libAgcDriver.cpp b/src/libs/libAgcDriver.cpp index 04ab0d6d2..740c625a0 100644 --- a/src/libs/libAgcDriver.cpp +++ b/src/libs/libAgcDriver.cpp @@ -74,6 +74,7 @@ LIB_DEFINE(InitAgcDriver_1) { LIB_FUNC("ozKzBP4aki4", Gen5::AgcAcbCondExecGetSize); LIB_FUNC("e1DFTg+Sd8U", Gen5::AgcAcbJump); LIB_FUNC("b-oySn+G2tE", Gen5::AgcAcbJumpGetSize); + LIB_FUNC("DwICrVxerkY", Gen5::AgcAcbRewind); LIB_FUNC("htn36gPnBk4", Gen5::AgcAcbWaitRegMem); LIB_FUNC("idlaArvdXEs", Gen5::AgcAcbWaitOnAddressGetSize); LIB_FUNC("-RnpfpxIhec", Gen5::AgcAcbDmaData); @@ -156,6 +157,7 @@ LIB_DEFINE(InitAgcDriver_1) { LIB_FUNC("k-JpyR2dYAM", Gen5::AgcCondExecPatchSetEnd); LIB_FUNC("3ZWa3AoyWZQ", Gen5::AgcCondExecPatchSetCommandAddress); LIB_FUNC("ziVA3whp3p4", Gen5::AgcRewindPatchSetRewindState); + LIB_FUNC("eWaWyFegzgQ", Gen5::AgcRewindPatchSetRewindState); LIB_FUNC("YUeqkyT7mEQ", Gen5::AgcDcbSetFlip); } diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index c9250a918..49b9a2731 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -50,6 +50,7 @@ #include "libs/agc.h" #include "libs/dialog.h" #include "libs/errno.h" +#include "loader/symbolDatabase.h" #include "spirv-tools/libspirv.hpp" #if __has_include("graphics/host_gpu/renderer/renderTargetBarriers.h") @@ -104,6 +105,10 @@ #undef max #endif +namespace Libs { +void InitAgcDriver_1(Loader::SymbolDatabase *symbols); +} + namespace Libs::Graphics { template @@ -41645,35 +41650,97 @@ void CheckPm4WaitResume(RenderContext &renderer) { } void CheckPm4RewindResume(RenderContext &renderer) { + constexpr const char *name = "Pm4RewindResume"; + Loader::SymbolDatabase symbols; + Libs::InitAgcDriver_1(&symbols); + const auto *emit_symbol = symbols.FindByNid("DwICrVxerkY", Loader::SymbolType::Func); + const auto *patch_symbol = symbols.FindByNid("eWaWyFegzgQ", Loader::SymbolType::Func); + Require(name, "async exports", + emit_symbol != nullptr && patch_symbol != nullptr && + emit_symbol->vaddr == reinterpret_cast(&Gen5::AgcAcbRewind) && + patch_symbol->vaddr == + reinterpret_cast(&Gen5::AgcRewindPatchSetRewindState), + "async rewind imports did not resolve to the emitter and state patch"); + const auto async_patch = reinterpret_cast( + patch_symbol->vaddr); + + constexpr uint32_t sentinel = 0x12345678u; + std::array packet{sentinel, sentinel, sentinel, sentinel}; + CommandBufferLayout acb{packet.data(), packet.data() + packet.size(), + packet.data() + 1, packet.data() + 3, nullptr, nullptr, 0}; + auto *buffer = reinterpret_cast(&acb); + for (const uint8_t state : {0, 1}) { + for (const uint8_t offload : {0, 1}) { + acb.cursor_up = packet.data() + 1; + Require(name, "async packet", + Gen5::AgcAcbRewind(buffer, state, offload) == packet.data() + 1 && + acb.cursor_up == packet.data() + 3 && + packet[0] == sentinel && packet[3] == sentinel && + packet[1] == 0xc0005900u && + packet[2] == ((uint32_t{state} << 31u) | (uint32_t{offload} << 24u)), + "async rewind packet or two-DWORD allocation differs from the native command"); + } + } + const auto before = packet; + acb.cursor_up = packet.data() + 1; + acb.reserved_dw = 1; + Require(name, "bounded allocation", + Gen5::AgcAcbRewind(buffer, 1, 1) == nullptr && + Gen5::AgcAcbRewind(nullptr, 1, 1) == nullptr && + acb.cursor_up == packet.data() + 1 && packet == before, + "failed rewind allocation changed the cursor or command storage"); + + // Native patchers inspect only the opcode, preserving the header and low 31 bits. + std::array command{0xa5ff591fu, 0x0155aa55u}; + Require(name, "patch preserves fields", + async_patch(command.data(), 1) == 0 && command[0] == 0xa5ff591fu && + command[1] == 0x8155aa55u && async_patch(command.data(), 0) == 0 && + command[0] == 0xa5ff591fu && command[1] == 0x0155aa55u, + "rewind patch changed the offload bit, header, or unrelated payload bits"); + command[0] = 0xc0001000u; + const auto invalid = command; + Require(name, "wrong opcode", + async_patch(command.data(), 1) == static_cast(0x8a6c000cu) && + command == invalid, + "rewind patch accepted or modified a packet with the wrong opcode"); + GraphicsInitJmpTables(); CommandProcessor processor(renderer, 0); - - uint32_t suffix = 0; - const auto address = reinterpret_cast(&suffix); - std::array commands{}; - commands[0] = KYTY_PM4(2, Pm4::IT_REWIND, 0); - commands[1] = 0; - commands[2] = KYTY_PM4(5, Pm4::IT_WRITE_DATA, 0); - commands[3] = 0; - commands[4] = static_cast(address); - commands[5] = static_cast(address >> 32u); - commands[6] = 33; - - Pm4Execution execution; - Require("Pm4RewindResume", "pending", - processor.Process(execution, commands) == Pm4ProcessResult::Blocked && - suffix == 0, - "pending rewind did not preserve its command position"); - Require("Pm4RewindResume", "patch valid", - Gen5::AgcRewindPatchSetRewindState(commands.data(), 1) == 0 && - commands[1] == 0x80000000u, - "rewind state patch did not set the valid bit"); - Require("Pm4RewindResume", "resume", - processor.Process(execution, commands) == - Pm4ProcessResult::Complete && - suffix == 33, - "valid rewind did not resume at the following packet"); - std::printf("[host] %-32s ok\n", "Pm4RewindResume"); + for (const bool async : {false, true}) { + for (const uint8_t offload : {0, 1}) { + if (!async && offload != 0) { + continue; + } + uint32_t suffix = 0; + const auto address = reinterpret_cast(&suffix); + std::array commands{ + 0, 0, KYTY_PM4(5, Pm4::IT_WRITE_DATA, 0), 0, + static_cast(address), static_cast(address >> 32u), 33}; + CommandBufferLayout cb{commands.data(), commands.data() + 2, + commands.data(), commands.data() + 2, nullptr, nullptr, 0}; + auto *command_buffer = reinterpret_cast(&cb); + const auto *emitted = async ? Gen5::AgcAcbRewind(command_buffer, 0, offload) + : Gen5::AgcDcbRewind(command_buffer, 0); + const auto patch = async ? async_patch : &Gen5::AgcRewindPatchSetRewindState; + Pm4Execution execution; + Require(name, "pending", + emitted == commands.data() && + processor.Process(execution, commands) == Pm4ProcessResult::Blocked && + suffix == 0, + "pending rewind did not preserve its command position"); + Require(name, "still pending", + processor.Process(execution, commands) == Pm4ProcessResult::Blocked && + !execution.MadeProgress() && suffix == 0, + "pending rewind advanced before its state was patched"); + Require(name, "patch and resume", + patch(commands.data(), 1) == 0 && + commands[1] == (0x80000000u | (uint32_t{offload} << 24u)) && + processor.Process(execution, commands) == Pm4ProcessResult::Complete && + suffix == 33, + "patched rewind lost the offload bit or failed to resume"); + } + } + std::printf("[host] %-32s ok\n", name); } void CheckPm4CeCompletion(RenderContext &renderer) {