From a44dd88806c5c48a86e6ba14089eb92911dbea29 Mon Sep 17 00:00:00 2001 From: nmzik Date: Fri, 21 Aug 2026 02:54:39 +0200 Subject: [PATCH] agc: new ABI --- .../command_processor/pm4Handlers.cpp | 2 +- src/libs/agc.cpp | 114 ++++++++++++++---- src/libs/agc.h | 8 +- src/libs/libAgcDriver.cpp | 1 + tests/ShaderRecompilerComputeTests.cpp | 54 ++++++++- 5 files changed, 155 insertions(+), 24 deletions(-) diff --git a/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp b/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp index e00dd3c45..bd79fdb22 100644 --- a/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp +++ b/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp @@ -2837,7 +2837,7 @@ KYTY_CP_OP_PARSER(CpOpSetShaderReg) { KYTY_CP_OP_PARSER(CpOpSetUconfigReg) { KYTY_PROFILER_FUNCTION(); - if (Gen5::AgcIsWaitUserDataPacket(cmd_id, buffer)) { + if (Gen5::AgcIsInternalDataPacket(cmd_id, buffer)) { return KYTY_PM4_LEN(cmd_id) - 1u; } diff --git a/src/libs/agc.cpp b/src/libs/agc.cpp index fd83f597e..a72904706 100644 --- a/src/libs/agc.cpp +++ b/src/libs/agc.cpp @@ -2619,6 +2619,21 @@ static uint32_t decode_draw_index_initiator(uint64_t modifier) { return (static_cast(modifier) >> 3u) & 0x20u; } +static constexpr uint32_t AGC_INTERNAL_DATA_REGISTER = 0x342u; +static constexpr uint32_t AGC_DRAW_INDIRECT_MULTI_BEGIN_TAG = 0xc6000008u; +static constexpr uint32_t AGC_DRAW_INDIRECT_MULTI_END_TAG = 0xc6000000u; +static constexpr uint32_t AGC_WAIT_USER_DATA_BEGIN_32_TAG = 0xc8010000u; +static constexpr uint32_t AGC_WAIT_USER_DATA_BEGIN_64_TAG = 0xc8020000u; +static constexpr uint32_t AGC_WAIT_USER_DATA_END_TAG = 0xc8000000u; +static constexpr uint32_t AGC_WAIT_USER_DATA_BEGIN_DW = 4u; +static constexpr uint32_t AGC_INTERNAL_DATA_PACKET_SIZE_DW = 3u; + +static void write_agc_internal_data_packet(uint32_t* cmd, uint32_t tag) { + cmd[0] = KYTY_PM4(AGC_INTERNAL_DATA_PACKET_SIZE_DW, Pm4::IT_SET_UCONFIG_REG, 1u); + cmd[1] = AGC_INTERNAL_DATA_REGISTER; + cmd[2] = tag; +} + uint32_t* KYTY_SYSV_ABI AgcDcbDrawIndex(CommandBuffer* buf, uint32_t index_count, const volatile void* index_addr, uint64_t modifier) { PRINT_NAME(); @@ -2875,6 +2890,69 @@ uint32_t* KYTY_SYSV_ABI AgcDcbDrawIndirect(CommandBuffer* buf, uint32_t data_off return cmd; } +uint32_t* KYTY_SYSV_ABI AgcDcbDrawIndirectMulti(CommandBuffer* buf, + uint32_t data_offset_in_bytes, + uint32_t count_indirect, + uint32_t max_count_or_count, + const volatile void* count_addr, + uint32_t stride_in_bytes, uint64_t modifier) { + PRINT_NAME(); + + LOGF("\t data_offset = 0x%" PRIx32 "\n" + "\t count_indirect = 0x%" PRIx32 "\n" + "\t max_count_or_count = 0x%" PRIx32 "\n" + "\t count_addr = 0x%016" PRIx64 "\n" + "\t stride_in_bytes = 0x%" PRIx32 "\n" + "\t modifier = 0x%016" PRIx64 "\n", + data_offset_in_bytes, count_indirect, max_count_or_count, + reinterpret_cast(count_addr), stride_in_bytes, modifier); + + if (buf == nullptr) { + return nullptr; + } + + buf->DbgDump(); + + constexpr uint32_t draw_packet_size_dw = 10u; + constexpr uint32_t total_size_dw = + AGC_INTERNAL_DATA_PACKET_SIZE_DW + draw_packet_size_dw + + AGC_INTERNAL_DATA_PACKET_SIZE_DW; + auto* cmd = buf->AllocateDW(total_size_dw); + if (cmd == nullptr) { + return nullptr; + } + + write_agc_internal_data_packet(cmd, AGC_DRAW_INDIRECT_MULTI_BEGIN_TAG); + auto* draw = cmd + AGC_INTERNAL_DATA_PACKET_SIZE_DW; + auto* end = draw + draw_packet_size_dw; + write_agc_internal_data_packet(end, AGC_DRAW_INDIRECT_MULTI_END_TAG); + + const auto low = static_cast(modifier); + const auto sgpr_base = indirect_modifier_sgpr_base(low); + const auto patch_offsets = decode_indirect_modifier_patch_offsets(modifier, false); + const auto count_vaddr = reinterpret_cast(count_addr); + + uint32_t draw_index_location = 0x280u; + if ((low & 0x8u) != 0) { + draw_index_location = sgpr_base + extract_modifier_bits(low, 24u, 5u); + } + const auto draw_control = draw_index_location | ((low & 0x10u) << 23u) | + ((count_indirect & 0x1u) << 30u) | ((low & 0x8u) << 28u); + + draw[0] = KYTY_PM4(draw_packet_size_dw, Pm4::IT_DRAW_INDIRECT_MULTI, 0u); + draw[1] = data_offset_in_bytes; + draw[2] = static_cast(patch_offsets); + draw[3] = static_cast(patch_offsets >> 32u); + draw[4] = draw_control; + draw[5] = max_count_or_count; + draw[6] = static_cast(count_vaddr) & ~0x3u; + draw[7] = static_cast(count_vaddr >> 32u); + draw[8] = stride_in_bytes; + draw[9] = decode_indirect_draw_initiator(modifier); + + return cmd; +} + uint32_t* KYTY_SYSV_ABI AgcDcbDrawIndexIndirectMulti(CommandBuffer* buf, uint32_t data_offset_in_bytes, uint32_t count_indirect, @@ -3793,16 +3871,9 @@ uint32_t* KYTY_SYSV_ABI AgcDcbGetLodStats(CommandBuffer* buf, uint8_t cache_poli return cmd; } -static constexpr uint32_t AGC_WAIT_USER_DATA_REGISTER = 0x342u; -static constexpr uint32_t AGC_WAIT_USER_DATA_BEGIN_32_TAG = 0xc8010000u; -static constexpr uint32_t AGC_WAIT_USER_DATA_BEGIN_64_TAG = 0xc8020000u; -static constexpr uint32_t AGC_WAIT_USER_DATA_END_TAG = 0xc8000000u; -static constexpr uint32_t AGC_WAIT_USER_DATA_BEGIN_DW = 4u; -static constexpr uint32_t AGC_WAIT_USER_DATA_END_DW = 3u; - static uint32_t* get_agc_wait_packet(uint32_t* cmd) { if (cmd == nullptr || KYTY_PM4_LEN(cmd[0]) != AGC_WAIT_USER_DATA_BEGIN_DW || - !AgcIsWaitUserDataPacket(cmd[0], cmd + 1)) { + !AgcIsInternalDataPacket(cmd[0], cmd + 1)) { return nullptr; } @@ -3946,20 +4017,22 @@ static uint32_t wait_reg_mem64_control(uint8_t compare_function, uint8_t op, uin ((static_cast(cache_policy) & 0x3u) << 25u); } -bool AgcIsWaitUserDataPacket(uint32_t cmd_id, const uint32_t* payload) { +bool AgcIsInternalDataPacket(uint32_t cmd_id, const uint32_t* payload) { if (payload == nullptr || ((cmd_id >> 8u) & 0xffu) != Pm4::IT_SET_UCONFIG_REG || - KYTY_PM4_R(cmd_id) != 1u || payload[0] != AGC_WAIT_USER_DATA_REGISTER) { + KYTY_PM4_R(cmd_id) != 1u || payload[0] != AGC_INTERNAL_DATA_REGISTER) { return false; } const auto size_dw = KYTY_PM4_LEN(cmd_id); - if (size_dw != AGC_WAIT_USER_DATA_BEGIN_DW && size_dw != AGC_WAIT_USER_DATA_END_DW) { - return false; + if (size_dw == AGC_WAIT_USER_DATA_BEGIN_DW) { + const auto tag = payload[1] & 0xffff0000u; + return tag == AGC_WAIT_USER_DATA_BEGIN_32_TAG || tag == AGC_WAIT_USER_DATA_BEGIN_64_TAG; } - const auto tag = payload[1] & 0xffff0000u; - return (size_dw == AGC_WAIT_USER_DATA_BEGIN_DW && - (tag == AGC_WAIT_USER_DATA_BEGIN_32_TAG || tag == AGC_WAIT_USER_DATA_BEGIN_64_TAG)) || - (size_dw == AGC_WAIT_USER_DATA_END_DW && payload[1] == AGC_WAIT_USER_DATA_END_TAG); + + return size_dw == AGC_INTERNAL_DATA_PACKET_SIZE_DW && + (payload[1] == AGC_WAIT_USER_DATA_END_TAG || + payload[1] == AGC_DRAW_INDIRECT_MULTI_BEGIN_TAG || + payload[1] == AGC_DRAW_INDIRECT_MULTI_END_TAG); } uint32_t* KYTY_SYSV_ABI AgcDcbWaitRegMem(CommandBuffer* buf, uint8_t size, uint8_t compare_function, @@ -3998,7 +4071,8 @@ uint32_t* KYTY_SYSV_ABI AgcDcbWaitRegMem(CommandBuffer* buf, uint8_t size, uint8 bool wait32 = (size == 0); auto poll = wait_reg_mem_poll_cycles_to_packet(poll_cycles); const auto wait_dw = wait32 ? 7u : 9u; - const auto total_dw = AGC_WAIT_USER_DATA_BEGIN_DW + wait_dw + AGC_WAIT_USER_DATA_END_DW; + const auto total_dw = + AGC_WAIT_USER_DATA_BEGIN_DW + wait_dw + AGC_INTERNAL_DATA_PACKET_SIZE_DW; auto* cmd = buf->AllocateDW(total_dw); @@ -4007,16 +4081,14 @@ uint32_t* KYTY_SYSV_ABI AgcDcbWaitRegMem(CommandBuffer* buf, uint8_t size, uint8 } cmd[0] = KYTY_PM4(AGC_WAIT_USER_DATA_BEGIN_DW, Pm4::IT_SET_UCONFIG_REG, 1u); - cmd[1] = AGC_WAIT_USER_DATA_REGISTER; + cmd[1] = AGC_INTERNAL_DATA_REGISTER; cmd[2] = (wait32 ? AGC_WAIT_USER_DATA_BEGIN_32_TAG : AGC_WAIT_USER_DATA_BEGIN_64_TAG) | static_cast((address_value >> 32u) & 0xffffu); cmd[3] = static_cast(address_value); auto* wait = cmd + AGC_WAIT_USER_DATA_BEGIN_DW; auto* end = wait + wait_dw; - end[0] = KYTY_PM4(AGC_WAIT_USER_DATA_END_DW, Pm4::IT_SET_UCONFIG_REG, 1u); - end[1] = AGC_WAIT_USER_DATA_REGISTER; - end[2] = AGC_WAIT_USER_DATA_END_TAG; + write_agc_internal_data_packet(end, AGC_WAIT_USER_DATA_END_TAG); if (wait32) { wait[0] = KYTY_PM4(7, Pm4::IT_WAIT_REG_MEM, 0u); diff --git a/src/libs/agc.h b/src/libs/agc.h index cebd4d63a..75017d193 100644 --- a/src/libs/agc.h +++ b/src/libs/agc.h @@ -171,6 +171,12 @@ uint32_t* KYTY_SYSV_ABI AgcDcbSetBaseIndirectArgs(CommandBuffer* buf, uint32_t s uint32_t* KYTY_SYSV_ABI AgcDcbDrawIndirect(CommandBuffer* buf, uint32_t data_offset_in_bytes, uint64_t modifier); uint32_t KYTY_SYSV_ABI AgcDcbDrawIndirectGetSize(); +uint32_t* KYTY_SYSV_ABI AgcDcbDrawIndirectMulti(CommandBuffer* buf, + uint32_t data_offset_in_bytes, + uint32_t count_indirect, + uint32_t max_count_or_count, + const volatile void* count_addr, + uint32_t stride_in_bytes, uint64_t modifier); uint32_t* KYTY_SYSV_ABI AgcDcbDrawIndexIndirect(CommandBuffer* buf, uint32_t data_offset_in_bytes, uint64_t modifier); uint32_t* KYTY_SYSV_ABI AgcDcbDrawIndexIndirectMulti(CommandBuffer* buf, @@ -273,7 +279,7 @@ uint32_t* KYTY_SYSV_ABI AgcDcbWaitRegMem(CommandBuffer* buf, uint8_t size, uint8 const volatile void* address, uint64_t reference, uint64_t mask, uint32_t poll_cycles); uint64_t KYTY_SYSV_ABI AgcDcbWaitOnAddressGetSize(uint32_t size); -bool AgcIsWaitUserDataPacket(uint32_t cmd_id, const uint32_t* payload); +bool AgcIsInternalDataPacket(uint32_t cmd_id, const uint32_t* payload); uint32_t* KYTY_SYSV_ABI AgcDcbSetMarker(CommandBuffer* buf, const char* str, uint32_t color); uint32_t* KYTY_SYSV_ABI AgcDcbPushMarker(CommandBuffer* buf, const char* str, uint32_t color); uint32_t* KYTY_SYSV_ABI AgcDcbPopMarker(CommandBuffer* buf); diff --git a/src/libs/libAgcDriver.cpp b/src/libs/libAgcDriver.cpp index 7feb62d9b..94b3c6642 100644 --- a/src/libs/libAgcDriver.cpp +++ b/src/libs/libAgcDriver.cpp @@ -111,6 +111,7 @@ LIB_DEFINE(InitAgcDriver_1) { LIB_FUNC("RmaJwLtc8rY", Gen5::AgcDcbSetBaseIndirectArgs); LIB_FUNC("1q1titRBL6o", Gen5::AgcDcbDrawIndirect); LIB_FUNC("cxPZ4Wgvdj8", Gen5::AgcDcbDrawIndirectGetSize); + LIB_FUNC("kUlvghKs-mA", Gen5::AgcDcbDrawIndirectMulti); LIB_FUNC("t1vNu082-jM", Gen5::AgcDcbDrawIndexIndirect); LIB_FUNC("ypVBz4uPKcQ", Gen5::AgcDcbDrawIndexIndirectMulti); LIB_FUNC("CtB+A9-VxO0", Gen5::AgcDcbDispatchIndirect); diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 9e208f7dc..f05dea018 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -22835,12 +22835,58 @@ void CheckAgcWaitPackets(RenderContext &renderer) { packet_mismatch && Gen5::AgcWaitRegMemPatchReference(invalid.data(), label32) == packet_mismatch && - !Gen5::AgcIsWaitUserDataPacket( + !Gen5::AgcIsInternalDataPacket( KYTY_PM4(2, Pm4::IT_SET_UCONFIG_REG, 1), &short_payload), "native wait patch accepted a packet without the AGC metadata prefix"); std::printf("[host] %-32s ok\n", "AgcWaitPackets"); } +void CheckAgcDrawIndirectMultiPacket(RenderContext &renderer) { + GraphicsInitJmpTables(); + CommandProcessor processor(renderer); + const auto execute = [&](uint32_t *packet, uint32_t size_dw) { + Pm4Execution execution; + return processor.Process(execution, packet, size_dw) == + Pm4ProcessResult::Complete; + }; + + constexpr uint64_t modifier = 0x0000000069380b1dull; + constexpr auto packet_mismatch = static_cast(0x8a6c000cu); + const auto *count_address = reinterpret_cast( + static_cast(0x123456789abcdef3ull)); + constexpr std::array expected{ + 0xc0017904u, 0x00000342u, 0xc6000008u, 0xc0082c00u, + 0x11223344u, 0x00000111u, 0x00000113u, 0xc8000115u, + 0x55667788u, 0x9abcdef0u, 0x12345678u, 0xaabbccddu, + 0x00000022u, 0xc0017904u, 0x00000342u, 0xc6000000u}; + + std::array packet{}; + AgcCommandBufferLayout dcb{packet.data(), + packet.data() + packet.size(), + packet.data(), + packet.data() + packet.size(), + nullptr, + nullptr, + 0}; + auto *emitted = Gen5::AgcDcbDrawIndirectMulti( + reinterpret_cast(&dcb), 0x11223344u, 1u, + 0x55667788u, count_address, 0xaabbccddu, modifier); + + const uint32_t invalid_payload[]{0x342u, 0xc6000010u}; + Require( + "AgcDrawIndirectMulti", "native packet", + emitted == packet.data() && dcb.cursor_up == packet.data() + packet.size() && + packet == expected && execute(packet.data(), 3u) && + execute(packet.data() + 13u, 3u) && + Gen5::AgcWaitRegMemPatchAddress(packet.data(), count_address) == + packet_mismatch && + Gen5::AgcWaitRegMemPatchReference(packet.data(), 0x12345678u) == + packet_mismatch && + !Gen5::AgcIsInternalDataPacket(0xc0017904u, invalid_payload), + "drawIndirectMulti stream differs from native AGC"); + std::printf("[host] %-32s ok\n", "AgcDrawIndirectMulti"); +} + void CheckPm4ContextStateOperations(RenderContext &renderer) { GraphicsInitJmpTables(); CommandProcessor processor(renderer); @@ -23228,6 +23274,11 @@ int main(int argc, char **argv) { CheckPm4WaitResume(vulkan.RuntimeRenderer()); return 0; } + if (argc == 2 && std::strcmp(argv[1], "--agc-draw-multi-only") == 0) { + VulkanHarness vulkan; + CheckAgcDrawIndirectMultiPacket(vulkan.RuntimeRenderer()); + return 0; + } if (argc == 2 && std::strcmp(argv[1], "--rewind-only") == 0) { VulkanHarness vulkan; CheckPm4RewindResume(vulkan.RuntimeRenderer()); @@ -23393,6 +23444,7 @@ int main(int argc, char **argv) { CheckPm4BlendColorRegisterRanges(vulkan.RuntimeRenderer()); CheckPm4PolygonOffsetRegisters(vulkan.RuntimeRenderer()); CheckAgcWaitPackets(vulkan.RuntimeRenderer()); + CheckAgcDrawIndirectMultiPacket(vulkan.RuntimeRenderer()); CheckPm4ContextStateOperations(vulkan.RuntimeRenderer()); CheckPm4WaitResume(vulkan.RuntimeRenderer()); CheckPm4RewindResume(vulkan.RuntimeRenderer());