diff --git a/src/libs/agc.cpp b/src/libs/agc.cpp index de431d44e..49a327c4a 100644 --- a/src/libs/agc.cpp +++ b/src/libs/agc.cpp @@ -830,6 +830,8 @@ enum class RegIndirectPacket : uint32_t { Uc, }; +static constexpr uint32_t RegIndirectPacketSizeDw = 5; + static uint32_t reg_indirect_native_op(RegIndirectPacket type) { switch (type) { case RegIndirectPacket::Cx: return Pm4::IT_SET_CONTEXT_REG_INDIRECT; @@ -853,7 +855,7 @@ static uint32_t reg_indirect_pm4_r(RegIndirectPacket type) { static void reg_indirect_write_packet(uint32_t* cmd, uint64_t vaddr, uint32_t num_regs, RegIndirectPacket type) { - cmd[0] = KYTY_PM4(5, reg_indirect_native_op(type), reg_indirect_pm4_r(type)); + cmd[0] = KYTY_PM4(RegIndirectPacketSizeDw, reg_indirect_native_op(type), reg_indirect_pm4_r(type)); cmd[1] = static_cast(vaddr) & 0xfffffffcu; cmd[2] = static_cast(vaddr >> 32u); cmd[3] = 0x80000000u; @@ -1789,6 +1791,33 @@ uint32_t KYTY_SYSV_ABI AgcCbDispatchGetSize() { return 20; } +static constexpr uint32_t BranchPacketSizeDw = 14; + +static void write_branch_target(uint32_t* words, uint64_t address, uint8_t cache_policy, + uint32_t size_in_dwords) { + words[0] = (words[0] & 0x3u) | (static_cast(address) & ~0x3u); + words[1] = static_cast(address >> 32u); + words[2] = (words[2] & 0xcff00000u) | (size_in_dwords & 0xfffffu) | + ((static_cast(cache_policy) & 0x3u) << 28u); +} + +uint64_t KYTY_SYSV_ABI AgcCbBranchGetSize() { + PRINT_NAME(); + return BranchPacketSizeDw * sizeof(uint32_t); +} + +int KYTY_SYSV_ABI AgcBranchPatchSetThenTarget(uint32_t* cmd, uint8_t cache_policy, + const volatile uint32_t* target, + uint32_t size_in_dwords) { + PRINT_NAME(); + EXIT_NOT_IMPLEMENTED(cmd == nullptr); + if (((cmd[0] >> 8u) & 0xffu) != Pm4::IT_INDIRECT_BUFFER) { + return GRAPHICS5_ERROR_INVALID_PACKET; + } + write_branch_target(cmd + 8, reinterpret_cast(target), cache_policy, size_in_dwords); + return OK; +} + uint32_t* KYTY_SYSV_ABI AgcCbBranch(CommandBuffer* buf, uint8_t mode, uint8_t compare_function, const volatile uint64_t* compare_addr, uint64_t mask, uint64_t reference, uint8_t cache_policy1, @@ -1814,7 +1843,7 @@ uint32_t* KYTY_SYSV_ABI AgcCbBranch(CommandBuffer* buf, uint8_t mode, uint8_t co EXIT_NOT_IMPLEMENTED(buf == nullptr); - auto* cmd = buf->AllocateDW(14); + auto* cmd = buf->AllocateDW(BranchPacketSizeDw); if (cmd == nullptr) { LOGF_COLOR(Log::Color::Red, "\t failed to allocate branch packet\n"); @@ -1825,7 +1854,7 @@ uint32_t* KYTY_SYSV_ABI AgcCbBranch(CommandBuffer* buf, uint8_t mode, uint8_t co const auto then_vaddr = reinterpret_cast(buffer1); const auto else_vaddr = reinterpret_cast(buffer2); - cmd[0] = KYTY_PM4(14, Pm4::IT_INDIRECT_BUFFER, 0u); + cmd[0] = KYTY_PM4(BranchPacketSizeDw, Pm4::IT_INDIRECT_BUFFER, 0u); cmd[1] = (mode & 0x3u) | ((static_cast(compare_function) & 0x7u) << 8u); cmd[2] = static_cast(compare_vaddr & 0xfffffff8u); cmd[3] = static_cast((compare_vaddr >> 32u) & 0xffffffffu); @@ -1833,12 +1862,9 @@ uint32_t* KYTY_SYSV_ABI AgcCbBranch(CommandBuffer* buf, uint8_t mode, uint8_t co cmd[5] = static_cast((mask >> 32u) & 0xffffffffu); cmd[6] = static_cast(reference & 0xffffffffu); cmd[7] = static_cast((reference >> 32u) & 0xffffffffu); - cmd[8] = static_cast(then_vaddr & 0xfffffffcu); - cmd[9] = static_cast((then_vaddr >> 32u) & 0xffffffffu); - cmd[10] = (size_in_dwords1 & 0xfffffu) | ((static_cast(cache_policy1) & 0x3u) << 28u); - cmd[11] = static_cast(else_vaddr & 0xfffffffcu); - cmd[12] = static_cast((else_vaddr >> 32u) & 0xffffffffu); - cmd[13] = (size_in_dwords2 & 0xfffffu) | ((static_cast(cache_policy2) & 0x3u) << 28u); + std::fill(cmd + 8, cmd + BranchPacketSizeDw, 0u); + write_branch_target(cmd + 8, then_vaddr, cache_policy1, size_in_dwords1); + write_branch_target(cmd + 11, else_vaddr, cache_policy2, size_in_dwords2); return cmd; } @@ -2353,7 +2379,7 @@ uint32_t* KYTY_SYSV_ABI AgcDcbSetCxRegistersIndirect(CommandBuffer* buf->DbgDump(); - auto* cmd = buf->AllocateDW(5); + auto* cmd = buf->AllocateDW(RegIndirectPacketSizeDw); EXIT_NOT_IMPLEMENTED(cmd == nullptr); @@ -2364,6 +2390,11 @@ uint32_t* KYTY_SYSV_ABI AgcDcbSetCxRegistersIndirect(CommandBuffer* return cmd; } +uint64_t KYTY_SYSV_ABI AgcDcbSetCxRegistersIndirectGetSize() { + PRINT_NAME(); + return RegIndirectPacketSizeDw * sizeof(uint32_t); +} + uint32_t* KYTY_SYSV_ABI AgcDcbSetShRegistersIndirect(CommandBuffer* buf, const volatile ShaderRegister* regs, uint32_t num_regs) { @@ -2377,7 +2408,7 @@ uint32_t* KYTY_SYSV_ABI AgcDcbSetShRegistersIndirect(CommandBuffer* buf->DbgDump(); - auto* cmd = buf->AllocateDW(5); + auto* cmd = buf->AllocateDW(RegIndirectPacketSizeDw); EXIT_NOT_IMPLEMENTED(cmd == nullptr); @@ -2391,7 +2422,7 @@ uint32_t* KYTY_SYSV_ABI AgcDcbSetShRegistersIndirect(CommandBuffer* uint64_t KYTY_SYSV_ABI AgcDcbSetShRegistersIndirectGetSize() { PRINT_NAME(); - return 5u * sizeof(uint32_t); + return RegIndirectPacketSizeDw * sizeof(uint32_t); } uint32_t* KYTY_SYSV_ABI AgcDcbSetUcRegistersIndirect(CommandBuffer* buf, @@ -2407,7 +2438,7 @@ uint32_t* KYTY_SYSV_ABI AgcDcbSetUcRegistersIndirect(CommandBuffer* buf->DbgDump(); - auto* cmd = buf->AllocateDW(5); + auto* cmd = buf->AllocateDW(RegIndirectPacketSizeDw); EXIT_NOT_IMPLEMENTED(cmd == nullptr); diff --git a/src/libs/agc.h b/src/libs/agc.h index 7eac1b193..712bf1ad9 100644 --- a/src/libs/agc.h +++ b/src/libs/agc.h @@ -101,6 +101,10 @@ uint32_t* KYTY_SYSV_ABI AgcCbBranch(CommandBuffer* buf, uint8_t mode, uint8_t co const volatile uint32_t* buffer1, uint32_t size_in_dwords1, uint8_t cache_policy2, const volatile uint32_t* buffer2, uint32_t size_in_dwords2); +uint64_t KYTY_SYSV_ABI AgcCbBranchGetSize(); +int KYTY_SYSV_ABI AgcBranchPatchSetThenTarget(uint32_t* cmd, uint8_t cache_policy, + const volatile uint32_t* target, + uint32_t size_in_dwords); uint32_t* KYTY_SYSV_ABI AgcCbSetShRegisterRangeDirect(CommandBuffer* buf, uint32_t offset, const uint32_t* values, uint32_t num_values); uint32_t KYTY_SYSV_ABI AgcCbSetShRegisterRangeDirectGetSize(uint32_t num_values); @@ -134,6 +138,7 @@ uint32_t* KYTY_SYSV_ABI AgcDcbSetUcRegisterDirect(CommandBuffer* buf, ShaderRegi uint32_t* KYTY_SYSV_ABI AgcDcbSetCxRegistersIndirect(CommandBuffer* buf, const volatile ShaderRegister* regs, uint32_t num_regs); +uint64_t KYTY_SYSV_ABI AgcDcbSetCxRegistersIndirectGetSize(); uint32_t* KYTY_SYSV_ABI AgcDcbSetShRegistersIndirect(CommandBuffer* buf, const volatile ShaderRegister* regs, uint32_t num_regs); diff --git a/src/libs/libAgcDriver.cpp b/src/libs/libAgcDriver.cpp index 740c625a0..0e0ceddb3 100644 --- a/src/libs/libAgcDriver.cpp +++ b/src/libs/libAgcDriver.cpp @@ -59,6 +59,8 @@ LIB_DEFINE(InitAgcDriver_1) { LIB_FUNC("k3GhuSNmBLU", Gen5::AgcCbDispatch); LIB_FUNC("Abendgtz+3o", Gen5::AgcCbDispatchGetSize); LIB_FUNC("w1KFAHVqpaU", Gen5::AgcCbBranch); + LIB_FUNC("uZW-mqsxkrM", Gen5::AgcCbBranchGetSize); + LIB_FUNC("7Wa3aeJgeVU", Gen5::AgcBranchPatchSetThenTarget); LIB_FUNC("n2fD4A+pb+g", Gen5::AgcCbSetShRegisterRangeDirect); LIB_FUNC("bxGoVxpdSPQ", Gen5::AgcCbSetShRegisterRangeDirectGetSize); LIB_FUNC("UZbQjYAwwXM", Gen5::AgcCbSetShRegistersDirect); @@ -95,6 +97,7 @@ LIB_DEFINE(InitAgcDriver_1) { LIB_FUNC("1DeUNpRIDDA", Gen5::AgcDcbSetCxRegisterDirectGetSize); LIB_FUNC("w4-d0n60hdo", Gen5::AgcDcbSetUcRegisterDirect); LIB_FUNC("ZvwO9euwYzc", Gen5::AgcDcbSetCxRegistersIndirect); + LIB_FUNC("GBCh3zCihoU", Gen5::AgcDcbSetCxRegistersIndirectGetSize); LIB_FUNC("-HOOCn0JY48", Gen5::AgcDcbSetShRegistersIndirect); LIB_FUNC("nNlUtdDDvZ0", Gen5::AgcDcbSetShRegistersIndirectGetSize); LIB_FUNC("hvUfkUIQcOE", Gen5::AgcDcbSetUcRegistersIndirect); diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 654b24cf5..870dfd4d8 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -41818,6 +41818,56 @@ void CheckPm4IndirectControlFlow(RenderContext &renderer) { } } + std::array cx_packet{}; + CommandBufferLayout cx_dcb{cx_packet.data(), cx_packet.data() + cx_packet.size(), + cx_packet.data(), cx_packet.data() + cx_packet.size(), + nullptr, nullptr, 0}; + const ShaderRegister cx_register{Pm4::CB_TARGET_MASK, 0x76543210u}; + Require(name, "indirect context packet size", + Gen5::AgcDcbSetCxRegistersIndirectGetSize() == sizeof(cx_packet) && + Gen5::AgcDcbSetCxRegistersIndirect( + reinterpret_cast(&cx_dcb), &cx_register, 1) == + cx_packet.data() && cx_dcb.cursor_up == cx_dcb.top, + "indirect Cx size did not match the emitted command buffer"); + + std::array patched_branch{}; + CommandBufferLayout branch_dcb{ + patched_branch.data(), patched_branch.data() + patched_branch.size(), + patched_branch.data(), patched_branch.data() + patched_branch.size(), + nullptr, nullptr, 0}; + Require(name, "branch packet size", + Gen5::AgcCbBranchGetSize() == sizeof(patched_branch) && + Gen5::AgcCbBranch(reinterpret_cast(&branch_dcb), + 1, 0, &condition, 0, 0, 0, nullptr, 0, + 3, else_commands.data(), else_commands.size()) == + patched_branch.data() && branch_dcb.cursor_up == branch_dcb.top, + "branch size did not match the emitted command buffer"); + patched_branch[8] = 3; + patched_branch[10] = 0xc5a00000u; + const auto unpatched = patched_branch; + Require(name, "patched indirect context branch", + Gen5::AgcBranchPatchSetThenTarget(patched_branch.data(), 2, + cx_packet.data(), cx_packet.size()) == 0 && + (patched_branch[8] & 3u) == 3u && + patched_branch[10] == 0xe5a00005u && + std::equal(patched_branch.begin(), patched_branch.begin() + 8, + unpatched.begin()) && + std::equal(patched_branch.begin() + 11, patched_branch.end(), + unpatched.begin() + 11), + "branch target patch lost reserved fields or changed the condition/else target"); + Pm4Execution patched_execution; + Require(name, "patched branch execution", + processor.Process(patched_execution, patched_branch) == Pm4ProcessResult::Complete && + processor.GetCtx().GetRenderTargetMask() == cx_register.value, + "patched branch did not fetch and apply its indirect context registers"); + patched_branch[0] = KYTY_PM4(14, Pm4::IT_NOP, 0); + const auto mismatched = patched_branch; + Require(name, "branch packet mismatch", + Gen5::AgcBranchPatchSetThenTarget(patched_branch.data(), 0, + then_commands.data(), then_commands.size()) == + static_cast(0x8a6c000cu) && patched_branch == mismatched, + "branch patch accepted or changed a different packet type"); + // This stream exceeds native recursion capacity, while chains need one fetcher cursor. std::vector> links(65536); std::span target = then_commands;