diff --git a/src/graphics/guest_gpu/command_processor/pm4Dispatch.cpp b/src/graphics/guest_gpu/command_processor/pm4Dispatch.cpp index 9c1111087..c9d587ede 100644 --- a/src/graphics/guest_gpu/command_processor/pm4Dispatch.cpp +++ b/src/graphics/guest_gpu/command_processor/pm4Dispatch.cpp @@ -216,6 +216,7 @@ constexpr auto MakeOpcodeDispatchTable() { g_cp_op_func[Pm4::IT_COND_EXEC] = CpOpCondExec; g_cp_op_func[Pm4::IT_SET_PREDICATION] = CpOpSetPredication; g_cp_op_func[Pm4::IT_WRITE_DATA] = CpOpWriteData; + g_cp_op_func[Pm4::IT_WAIT_REG_MEM] = CpOpWaitRegMem32; g_cp_op_func[Pm4::IT_INDIRECT_BUFFER] = CpOpIndirectBuffer; g_cp_op_func[Pm4::IT_COPY_DATA] = CpOpCopyData; g_cp_op_func[Pm4::IT_EVENT_WRITE] = CpOpEventWrite; @@ -238,6 +239,7 @@ constexpr auto MakeOpcodeDispatchTable() { g_cp_op_func[Pm4::IT_WAIT_ON_CE_COUNTER] = CpOpWaitOnCeCounter; g_cp_op_func[Pm4::IT_WAIT_ON_DE_COUNTER_DIFF] = CpOpWaitOnDeCounterDiff; g_cp_op_func[Pm4::IT_GET_LOD_STATS] = CpOpGetLodStats; + g_cp_op_func[Pm4::IT_WAIT_REG_MEM_64] = CpOpWaitRegMem64; return g_cp_op_func; } @@ -246,12 +248,10 @@ constexpr auto MakeCustomOpcodeDispatchTable() { std::array g_cp_op_custom_func {}; g_cp_op_custom_func[Pm4::R_DISPATCH_RESET] = CpOpDispatchReset; - g_cp_op_custom_func[Pm4::R_WAIT_MEM_32] = CpOpWaitRegMem32; g_cp_op_custom_func[Pm4::R_WAIT_FLIP_DONE] = CpOpWaitFlipDone; g_cp_op_custom_func[Pm4::R_PUSH_MARKER] = CpOpPushMarker; g_cp_op_custom_func[Pm4::R_POP_MARKER] = CpOpPopMarker; g_cp_op_custom_func[Pm4::R_ACQUIRE_MEM] = CpOpAcquireMem; - g_cp_op_custom_func[Pm4::R_WAIT_MEM_64] = CpOpWaitRegMem64; g_cp_op_custom_func[Pm4::R_FLIP] = CpOpFlip; g_cp_op_custom_func[Pm4::R_RELEASE_MEM] = CpOpReleaseMem; g_cp_op_custom_func[Pm4::R_CONTEXT_STATE] = CpOpContextState; diff --git a/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp b/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp index 4a4689f49..40e9d7a6e 100644 --- a/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp +++ b/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp @@ -2866,6 +2866,10 @@ KYTY_CP_OP_PARSER(CpOpSetShaderReg) { KYTY_CP_OP_PARSER(CpOpSetUconfigReg) { KYTY_PROFILER_FUNCTION(); + if (Gen5::AgcIsWaitUserDataPacket(cmd_id, buffer)) { + return KYTY_PM4_LEN(cmd_id) - 1u; + } + auto raw_cmd_offset = buffer[0]; auto cmd_offset = NormalizeRegisterOffset(raw_cmd_offset); @@ -2956,19 +2960,21 @@ template static uint32_t CpOpWaitRegMemSized(CommandProcessor& cp, uint32_t cmd_id, const uint32_t* buffer) { static_assert(sizeof(T) == sizeof(uint32_t) || sizeof(T) == sizeof(uint64_t)); - constexpr auto value_dw = static_cast(sizeof(T) / sizeof(uint32_t)); + constexpr auto value_dw = static_cast(sizeof(T) / sizeof(uint32_t)); constexpr auto payload_dw = 4u + value_dw * 2u; constexpr auto packet_dw = payload_dw + 1u; - constexpr auto register_id = - (sizeof(T) == sizeof(uint32_t) ? Pm4::R_WAIT_MEM_32 : Pm4::R_WAIT_MEM_64); + constexpr auto opcode = + (sizeof(T) == sizeof(uint32_t) ? Pm4::IT_WAIT_REG_MEM : Pm4::IT_WAIT_REG_MEM_64); - EXIT_NOT_IMPLEMENTED(KYTY_PM4_R(cmd_id) != register_id); + EXIT_NOT_IMPLEMENTED(((cmd_id >> 8u) & 0xffu) != opcode || KYTY_PM4_R(cmd_id) != 0u); EXIT_NOT_IMPLEMENTED(KYTY_PM4_LEN(cmd_id) != packet_dw); - auto* addr = reinterpret_cast(buffer[0] | (static_cast(buffer[1]) << 32u)); - auto mask = CpOpWaitRegMemReadValue(buffer + 2u); - auto ref = CpOpWaitRegMemReadValue(buffer + 2u + value_dw); - auto ctrl = buffer[2u + value_dw * 2u]; + constexpr auto address_align_mask = (sizeof(T) == sizeof(uint32_t) ? 0x3u : 0x7u); + auto ctrl = buffer[0]; + auto* addr = reinterpret_cast((buffer[1] & ~address_align_mask) | + (static_cast(buffer[2] & 0x3ffffu) << 32u)); + auto ref = CpOpWaitRegMemReadValue(buffer + 3u); + auto mask = CpOpWaitRegMemReadValue(buffer + 3u + value_dw); auto poll = buffer[3u + value_dw * 2u]; EXIT_NOT_IMPLEMENTED((ctrl & 0x10u) == 0); diff --git a/src/graphics/guest_gpu/pm4.cpp b/src/graphics/guest_gpu/pm4.cpp index 8b9472b71..fa5cb816e 100644 --- a/src/graphics/guest_gpu/pm4.cpp +++ b/src/graphics/guest_gpu/pm4.cpp @@ -21,12 +21,10 @@ constexpr auto MakeRegisterNames() { names[R_DRAW_RESET] = "R_DRAW_RESET"; names[R_WAIT_FLIP_DONE] = "R_WAIT_FLIP_DONE"; names[R_DISPATCH_RESET] = "R_DISPATCH_RESET"; - names[R_WAIT_MEM_32] = "R_WAIT_MEM_32"; names[R_PUSH_MARKER] = "R_PUSH_MARKER"; names[R_POP_MARKER] = "R_POP_MARKER"; names[R_ACQUIRE_MEM] = "R_ACQUIRE_MEM"; names[R_WRITE_DATA] = "R_WRITE_DATA"; - names[R_WAIT_MEM_64] = "R_WAIT_MEM_64"; names[R_FLIP] = "R_FLIP"; names[R_RELEASE_MEM] = "R_RELEASE_MEM"; names[R_DMA_DATA] = "R_DMA_DATA"; @@ -62,6 +60,7 @@ constexpr auto MakeOpcodeNames() { names[IT_WRITE_DATA] = "IT_WRITE_DATA"; names[IT_MEM_SEMAPHORE] = "IT_MEM_SEMAPHORE"; names[IT_DRAW_INDEX_INDIRECT_MULTI] = "IT_DRAW_INDEX_INDIRECT_MULTI"; + names[IT_WAIT_REG_MEM] = "IT_WAIT_REG_MEM"; names[IT_INDIRECT_BUFFER] = "IT_INDIRECT_BUFFER"; names[IT_COPY_DATA] = "IT_COPY_DATA"; names[IT_CP_DMA] = "IT_CP_DMA"; @@ -91,6 +90,7 @@ constexpr auto MakeOpcodeNames() { names[IT_DISPATCH_DRAW_PREAMBLE] = "IT_DISPATCH_DRAW_PREAMBLE"; names[IT_DISPATCH_DRAW] = "IT_DISPATCH_DRAW"; names[IT_GET_LOD_STATS] = "IT_GET_LOD_STATS"; + names[IT_WAIT_REG_MEM_64] = "IT_WAIT_REG_MEM_64"; names[IT_SET_CONTEXT_REG_INDIRECT] = "IT_SET_CONTEXT_REG_INDIRECT"; return names; } diff --git a/src/graphics/guest_gpu/pm4.h b/src/graphics/guest_gpu/pm4.h index e11a11530..076761f51 100644 --- a/src/graphics/guest_gpu/pm4.h +++ b/src/graphics/guest_gpu/pm4.h @@ -42,6 +42,7 @@ constexpr uint32_t IT_WRITE_DATA = 0x37; constexpr uint32_t IT_MEM_SEMAPHORE = 0x39; constexpr uint32_t IT_DRAW_INDEX_INDIRECT_MULTI = 0x38; constexpr uint32_t IT_DISPATCH_DRAW_PREAMBLE = 0x3A; +constexpr uint32_t IT_WAIT_REG_MEM = 0x3C; constexpr uint32_t IT_INDIRECT_BUFFER = 0x3F; constexpr uint32_t IT_COPY_DATA = 0x40; constexpr uint32_t IT_CP_DMA = 0x41; @@ -70,6 +71,7 @@ constexpr uint32_t IT_WAIT_ON_CE_COUNTER = 0x86; constexpr uint32_t IT_WAIT_ON_DE_COUNTER_DIFF = 0x88; constexpr uint32_t IT_DISPATCH_DRAW = 0x8D; constexpr uint32_t IT_GET_LOD_STATS = 0x8E; +constexpr uint32_t IT_WAIT_REG_MEM_64 = 0x93; constexpr uint32_t IT_SET_CONTEXT_REG_INDIRECT = 0x9F; /* Custom codes. Implemented via IT_NOP */ @@ -78,12 +80,10 @@ constexpr uint32_t R_ZERO = 0x00; constexpr uint32_t R_DRAW_RESET = 0x05; constexpr uint32_t R_WAIT_FLIP_DONE = 0x06; constexpr uint32_t R_DISPATCH_RESET = 0x09; -constexpr uint32_t R_WAIT_MEM_32 = 0x0A; constexpr uint32_t R_PUSH_MARKER = 0x0B; constexpr uint32_t R_POP_MARKER = 0x0C; constexpr uint32_t R_ACQUIRE_MEM = 0x14; constexpr uint32_t R_WRITE_DATA = 0x15; -constexpr uint32_t R_WAIT_MEM_64 = 0x16; constexpr uint32_t R_FLIP = 0x17; constexpr uint32_t R_RELEASE_MEM = 0x18; constexpr uint32_t R_DMA_DATA = 0x19; diff --git a/src/libs/agc.cpp b/src/libs/agc.cpp index 87230aae3..6e0ada7b8 100644 --- a/src/libs/agc.cpp +++ b/src/libs/agc.cpp @@ -3705,6 +3705,33 @@ 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)) { + return nullptr; + } + + auto* wait = cmd + AGC_WAIT_USER_DATA_BEGIN_DW; + const auto op = (wait[0] >> 8u) & 0xffu; + const auto len = KYTY_PM4_LEN(wait[0]); + const auto tag = cmd[2] & 0xffff0000u; + if (KYTY_PM4_R(wait[0]) != 0u || + !((op == Pm4::IT_WAIT_REG_MEM && len == 7u && tag == AGC_WAIT_USER_DATA_BEGIN_32_TAG) || + (op == Pm4::IT_WAIT_REG_MEM_64 && len == 9u && + tag == AGC_WAIT_USER_DATA_BEGIN_64_TAG))) { + return nullptr; + } + + return wait; +} + int KYTY_SYSV_ABI AgcWaitRegMemPatchAddress(uint32_t* cmd, const volatile void* address) { PRINT_NAME(); @@ -3712,21 +3739,20 @@ int KYTY_SYSV_ABI AgcWaitRegMemPatchAddress(uint32_t* cmd, const volatile void* "\t address = 0x%016" PRIx64 "\n", reinterpret_cast(cmd), reinterpret_cast(address)); - EXIT_NOT_IMPLEMENTED(cmd == nullptr); - - auto vaddr = reinterpret_cast(address); - auto op = (cmd[0] >> 8u) & 0xffu; - - if (op == Pm4::IT_NOP && KYTY_PM4_R(cmd[0]) == Pm4::R_WAIT_MEM_32) { - cmd[1] = static_cast(vaddr) & ~0x3u; - cmd[2] = static_cast(vaddr >> 32u) & 0x3ffffu; - } else if (op == Pm4::IT_NOP && KYTY_PM4_R(cmd[0]) == Pm4::R_WAIT_MEM_64) { - cmd[1] = static_cast(vaddr) & ~0x7u; - cmd[2] = static_cast(vaddr >> 32u) & 0x3ffffu; - } else { - EXIT("unsupported waitOnAddress packet for address patch: 0x%08" PRIx32 "\n", cmd[0]); + auto* wait = get_agc_wait_packet(cmd); + if (wait == nullptr) { + return GRAPHICS5_ERROR_INVALID_PACKET; } + const auto vaddr = reinterpret_cast(address); + const auto op = (wait[0] >> 8u) & 0xffu; + const auto align_mask = (op == Pm4::IT_WAIT_REG_MEM ? 0x3u : 0x7u); + cmd[2] = (cmd[2] & 0xffff0000u) | static_cast((vaddr >> 32u) & 0xffffu); + cmd[3] = static_cast(vaddr); + wait[2] = (wait[2] & align_mask) | (static_cast(vaddr) & ~align_mask); + wait[3] = (wait[3] & 0xfffc0000u) | + (static_cast(vaddr >> 32u) & 0x3ffffu); + return OK; } @@ -3737,19 +3763,14 @@ int KYTY_SYSV_ABI AgcWaitRegMemPatchReference(uint32_t* cmd, uint64_t reference) "\t reference = 0x%016" PRIx64 "\n", reinterpret_cast(cmd), reference); - EXIT_NOT_IMPLEMENTED(cmd == nullptr); - - auto op = (cmd[0] >> 8u) & 0xffu; - - if (op == Pm4::IT_NOP && KYTY_PM4_R(cmd[0]) == Pm4::R_WAIT_MEM_32) { - cmd[4] = static_cast(reference & 0xffffffffu); - } else if (op == Pm4::IT_NOP && KYTY_PM4_R(cmd[0]) == Pm4::R_WAIT_MEM_64) { - cmd[5] = static_cast(reference & 0xffffffffu); - cmd[6] = static_cast((reference >> 32u) & 0xffffffffu); - } else { - EXIT("unsupported waitOnAddress packet for reference patch: 0x%08" PRIx32 "\n", cmd[0]); + auto* wait = get_agc_wait_packet(cmd); + if (wait == nullptr) { + return GRAPHICS5_ERROR_INVALID_PACKET; } + // The native patch helper changes only the low reference DWORD for both packet sizes. + wait[4] = static_cast(reference); + return OK; } @@ -3837,6 +3858,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) { + 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) { + 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; + } + 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); +} + uint32_t* KYTY_SYSV_ABI AgcDcbWaitRegMem(CommandBuffer* buf, uint8_t size, uint8_t compare_function, uint8_t op, uint8_t cache_policy, const volatile void* address, uint64_t reference, @@ -3872,39 +3909,53 @@ uint32_t* KYTY_SYSV_ABI AgcDcbWaitRegMem(CommandBuffer* buf, uint8_t size, uint8 auto address_value = reinterpret_cast(address); 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; - auto* cmd = buf->AllocateDW(wait32 ? 7 : 9); + auto* cmd = buf->AllocateDW(total_dw); if (cmd == nullptr) { return nullptr; } + cmd[0] = KYTY_PM4(AGC_WAIT_USER_DATA_BEGIN_DW, Pm4::IT_SET_UCONFIG_REG, 1u); + cmd[1] = AGC_WAIT_USER_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; + if (wait32) { - cmd[0] = KYTY_PM4(7, Pm4::IT_NOP, Pm4::R_WAIT_MEM_32); - cmd[1] = static_cast(address_value) & ~0x3u; - cmd[2] = static_cast(address_value >> 32u) & 0x3ffffu; - cmd[3] = static_cast(mask & 0xffffffffu); - cmd[4] = static_cast(reference & 0xffffffffu); - cmd[5] = wait_reg_mem32_control(compare_function, op, cache_policy); - cmd[6] = poll; + wait[0] = KYTY_PM4(7, Pm4::IT_WAIT_REG_MEM, 0u); + wait[1] = wait_reg_mem32_control(compare_function, op, cache_policy); + wait[2] = static_cast(address_value) & ~0x3u; + wait[3] = static_cast(address_value >> 32u) & 0x3ffffu; + wait[4] = static_cast(reference); + wait[5] = static_cast(mask); + wait[6] = poll; return cmd; } - cmd[0] = KYTY_PM4(9, Pm4::IT_NOP, Pm4::R_WAIT_MEM_64); - cmd[1] = static_cast(address_value) & ~0x7u; - cmd[2] = static_cast(address_value >> 32u) & 0x3ffffu; - cmd[3] = static_cast(reinterpret_cast(mask) & 0xffffffffu); - cmd[4] = static_cast((reinterpret_cast(mask) >> 32u) & 0xffffffffu); - cmd[5] = static_cast(reinterpret_cast(reference) & 0xffffffffu); - cmd[6] = static_cast((reinterpret_cast(reference) >> 32u) & 0xffffffffu); - cmd[7] = wait_reg_mem64_control(compare_function, op, cache_policy); - cmd[8] = poll; + wait[0] = KYTY_PM4(9, Pm4::IT_WAIT_REG_MEM_64, 0u); + wait[1] = wait_reg_mem64_control(compare_function, op, cache_policy); + wait[2] = static_cast(address_value) & ~0x7u; + wait[3] = static_cast(address_value >> 32u) & 0x3ffffu; + wait[4] = static_cast(reference); + wait[5] = static_cast(reference >> 32u); + wait[6] = static_cast(mask); + wait[7] = static_cast(mask >> 32u); + wait[8] = poll; return cmd; } -uint32_t KYTY_SYSV_ABI AgcDcbWaitOnAddressGetSize(uint32_t size) { +uint64_t KYTY_SYSV_ABI AgcDcbWaitOnAddressGetSize(uint32_t size) { PRINT_NAME(); switch (size) { diff --git a/src/libs/agc.h b/src/libs/agc.h index 592204c52..0aa044ac5 100644 --- a/src/libs/agc.h +++ b/src/libs/agc.h @@ -270,7 +270,8 @@ uint32_t* KYTY_SYSV_ABI AgcDcbWaitRegMem(CommandBuffer* buf, uint8_t size, uint8 uint8_t op, uint8_t cache_policy, const volatile void* address, uint64_t reference, uint64_t mask, uint32_t poll_cycles); -uint32_t KYTY_SYSV_ABI AgcDcbWaitOnAddressGetSize(uint32_t size); +uint64_t KYTY_SYSV_ABI AgcDcbWaitOnAddressGetSize(uint32_t size); +bool AgcIsWaitUserDataPacket(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/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index d98291d83..cde162f3c 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -2035,12 +2035,12 @@ public: commands[2] = static_cast(address(&prefix)); commands[3] = static_cast(address(&prefix) >> 32u); commands[4] = 11; - commands[5] = KYTY_PM4(7, Pm4::IT_NOP, Pm4::R_WAIT_MEM_32); - commands[6] = static_cast(address(&label)); - commands[7] = static_cast(address(&label) >> 32u); - commands[8] = UINT32_MAX; + commands[5] = KYTY_PM4(7, Pm4::IT_WAIT_REG_MEM, 0); + commands[6] = 0x10u | 3u; + commands[7] = static_cast(address(&label)); + commands[8] = static_cast(address(&label) >> 32u); commands[9] = 1; - commands[10] = 0x10u | 3u; + commands[10] = UINT32_MAX; commands[12] = KYTY_PM4(5, Pm4::IT_WRITE_DATA, 0); commands[13] = 0; commands[14] = static_cast(address(&suffix)); @@ -22184,24 +22184,159 @@ void CheckPm4PolygonOffsetRegisters(RenderContext &renderer) { std::printf("[host] %-32s ok\n", "Pm4PolygonOffset"); } +struct AgcCommandBufferLayout { + using Callback = KYTY_SYSV_ABI bool (*)(Gen5::CommandBuffer *, uint32_t, + void *); + + uint32_t *bottom; + uint32_t *top; + uint32_t *cursor_up; + uint32_t *cursor_down; + Callback callback; + void *user_data; + uint32_t reserved_dw; +}; +static_assert(offsetof(AgcCommandBufferLayout, cursor_up) == 0x10); +static_assert(offsetof(AgcCommandBufferLayout, reserved_dw) == 0x30); + +void CheckAgcWaitPackets(RenderContext &renderer) { + GraphicsInitJmpTables(); + CommandProcessor processor(renderer); + constexpr auto packet_mismatch = static_cast(0x8a6c000cu); + const auto execute = [&](uint32_t *packet, uint32_t size_dw) { + Pm4Execution execution; + return processor.Process(execution, packet, size_dw) == + Pm4ProcessResult::Complete; + }; + + alignas(8) uint32_t label32 = 0x11223344u; + std::array packet32{}; + AgcCommandBufferLayout dcb32{packet32.data(), + packet32.data() + packet32.size(), + packet32.data(), + packet32.data() + packet32.size(), + nullptr, + nullptr, + 0}; + const auto address32 = reinterpret_cast(&label32); + auto *emitted32 = Gen5::AgcDcbWaitRegMem( + reinterpret_cast(&dcb32), 0, 3, 0, 0, + &label32, label32, UINT32_MAX, 400); + Require( + "AgcWaitPackets", "native 32-bit packet", + Gen5::AgcDcbWaitOnAddressGetSize(0) == 56 && + emitted32 == packet32.data() && + dcb32.cursor_up == packet32.data() + 14 && + packet32[0] == 0xc0027904u && packet32[1] == 0x342u && + packet32[2] == + (0xc8010000u | + (static_cast(address32 >> 32u) & 0xffffu)) && + packet32[3] == static_cast(address32) && + packet32[4] == 0xc0053c00u && packet32[5] == 0x13u && + packet32[6] == (static_cast(address32) & ~0x3u) && + packet32[7] == + (static_cast(address32 >> 32u) & 0x3ffffu) && + packet32[8] == label32 && packet32[9] == UINT32_MAX && + packet32[10] == 25u && packet32[11] == 0xc0017904u && + packet32[12] == 0x342u && packet32[13] == 0xc8000000u && + execute(packet32.data(), 14), + "32-bit waitOnAddress stream differs from native AGC"); + + alignas(8) uint32_t patched_label32 = 0xaabbccddu; + const auto patched_address32 = reinterpret_cast(&patched_label32); + packet32[6] |= 0x2u; + packet32[7] |= 0xa5a40000u; + Require( + "AgcWaitPackets", "native 32-bit patch", + Gen5::AgcWaitRegMemPatchAddress(packet32.data(), &patched_label32) == 0 && + Gen5::AgcWaitRegMemPatchReference(packet32.data(), patched_label32) == 0 && + packet32[2] == + (0xc8010000u | + (static_cast(patched_address32 >> 32u) & 0xffffu)) && + packet32[3] == static_cast(patched_address32) && + packet32[6] == + ((static_cast(patched_address32) & ~0x3u) | 0x2u) && + packet32[7] == + ((static_cast(patched_address32 >> 32u) & 0x3ffffu) | + 0xa5a40000u) && + packet32[8] == patched_label32 && execute(packet32.data(), 14), + "32-bit native wait patch did not update metadata and hardware packet"); + + alignas(8) uint64_t label64 = 0x1122334455667788ull; + std::array packet64{}; + AgcCommandBufferLayout dcb64{packet64.data(), + packet64.data() + packet64.size(), + packet64.data(), + packet64.data() + packet64.size(), + nullptr, + nullptr, + 0}; + const auto address64 = reinterpret_cast(&label64); + auto *emitted64 = Gen5::AgcDcbWaitRegMem( + reinterpret_cast(&dcb64), 1, 3, 0, 0, + &label64, label64, UINT64_MAX, 400); + Require( + "AgcWaitPackets", "native 64-bit packet", + Gen5::AgcDcbWaitOnAddressGetSize(1) == 64 && + Gen5::AgcDcbWaitOnAddressGetSize(2) == 0 && + emitted64 == packet64.data() && + dcb64.cursor_up == packet64.data() + packet64.size() && + packet64[0] == 0xc0027904u && packet64[1] == 0x342u && + packet64[2] == + (0xc8020000u | + (static_cast(address64 >> 32u) & 0xffffu)) && + packet64[3] == static_cast(address64) && + packet64[4] == 0xc0079300u && packet64[5] == 0x13u && + packet64[6] == (static_cast(address64) & ~0x7u) && + packet64[7] == + (static_cast(address64 >> 32u) & 0x3ffffu) && + packet64[8] == static_cast(label64) && + packet64[9] == static_cast(label64 >> 32u) && + packet64[10] == UINT32_MAX && packet64[11] == UINT32_MAX && + packet64[12] == 25u && packet64[13] == 0xc0017904u && + packet64[14] == 0x342u && packet64[15] == 0xc8000000u && + execute(packet64.data(), packet64.size()), + "64-bit waitOnAddress stream differs from native AGC"); + + alignas(8) uint64_t patched_label64 = 0x11223344aabbccddull; + const auto patched_address64 = reinterpret_cast(&patched_label64); + packet64[6] |= 0x5u; + packet64[7] |= 0x5a580000u; + Require( + "AgcWaitPackets", "native 64-bit patch", + Gen5::AgcWaitRegMemPatchAddress(packet64.data(), &patched_label64) == 0 && + Gen5::AgcWaitRegMemPatchReference(packet64.data(), + 0xdeadbeefaabbccddull) == 0 && + packet64[2] == + (0xc8020000u | + (static_cast(patched_address64 >> 32u) & 0xffffu)) && + packet64[3] == static_cast(patched_address64) && + packet64[6] == + ((static_cast(patched_address64) & ~0x7u) | 0x5u) && + packet64[7] == + ((static_cast(patched_address64 >> 32u) & 0x3ffffu) | + 0x5a580000u) && + packet64[8] == 0xaabbccddu && packet64[9] == 0x11223344u && + execute(packet64.data(), packet64.size()), + "64-bit native wait patch did not preserve the native high reference DWORD"); + + std::array invalid{}; + const uint32_t short_payload = 0x342u; + Require("AgcWaitPackets", "invalid packet", + Gen5::AgcWaitRegMemPatchAddress(invalid.data(), &label32) == + packet_mismatch && + Gen5::AgcWaitRegMemPatchReference(invalid.data(), label32) == + packet_mismatch && + !Gen5::AgcIsWaitUserDataPacket( + 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 CheckPm4ContextStateOperations(RenderContext &renderer) { GraphicsInitJmpTables(); CommandProcessor processor(renderer); constexpr auto primitive_type = static_cast(0x35u); - struct AgcCommandBufferLayout { - using Callback = KYTY_SYSV_ABI bool (*)(Gen5::CommandBuffer *, uint32_t, - void *); - - uint32_t *bottom; - uint32_t *top; - uint32_t *cursor_up; - uint32_t *cursor_down; - Callback callback; - void *user_data; - uint32_t reserved_dw; - }; - static_assert(offsetof(AgcCommandBufferLayout, cursor_up) == 0x10); - static_assert(offsetof(AgcCommandBufferLayout, reserved_dw) == 0x30); HW::RenderControl original_control{}; original_control.depth_clear_enable = true; @@ -22366,12 +22501,12 @@ void CheckPm4WaitResume(RenderContext &renderer) { child[2] = static_cast(address(&child_observation)); child[3] = static_cast(address(&child_observation) >> 32u); child[4] = 0; - child[5] = KYTY_PM4(7, Pm4::IT_NOP, Pm4::R_WAIT_MEM_32); - child[6] = static_cast(address(&label)); - child[7] = static_cast(address(&label) >> 32u); - child[8] = UINT32_MAX; + child[5] = KYTY_PM4(7, Pm4::IT_WAIT_REG_MEM, 0); + child[6] = 0x10u | 3u; + child[7] = static_cast(address(&label)); + child[8] = static_cast(address(&label) >> 32u); child[9] = 1; - child[10] = 0x10u | 3u; + child[10] = UINT32_MAX; std::array nested{}; nested[0] = KYTY_PM4(4, Pm4::IT_INDIRECT_BUFFER, 0); @@ -22578,6 +22713,12 @@ int main(int argc, char **argv) { CheckPm4ContextStateOperations(vulkan.RuntimeRenderer()); return 0; } + if (argc == 2 && std::strcmp(argv[1], "--agc-wait-only") == 0) { + VulkanHarness vulkan; + CheckAgcWaitPackets(vulkan.RuntimeRenderer()); + CheckPm4WaitResume(vulkan.RuntimeRenderer()); + return 0; + } if (argc == 2 && std::strcmp(argv[1], "--rewind-only") == 0) { VulkanHarness vulkan; CheckPm4RewindResume(vulkan.RuntimeRenderer()); @@ -22741,6 +22882,7 @@ int main(int argc, char **argv) { CheckPm4DirectShaderRegisterFallback(vulkan.RuntimeRenderer()); CheckPm4GuardBandRegisterRanges(vulkan.RuntimeRenderer()); CheckPm4PolygonOffsetRegisters(vulkan.RuntimeRenderer()); + CheckAgcWaitPackets(vulkan.RuntimeRenderer()); CheckPm4ContextStateOperations(vulkan.RuntimeRenderer()); CheckPm4WaitResume(vulkan.RuntimeRenderer()); CheckPm4RewindResume(vulkan.RuntimeRenderer());