From a5ceb48184741668df8cdc3e234861c03cf7482e Mon Sep 17 00:00:00 2001 From: nmzik Date: Thu, 15 Oct 2026 23:34:42 +0200 Subject: [PATCH] agc: encode acquire-memory byte ranges in standard packets --- .../command_processor/pm4Dispatch.cpp | 1 - .../command_processor/pm4Handlers.cpp | 4 +- src/graphics/guest_gpu/pm4.cpp | 1 - src/graphics/guest_gpu/pm4.h | 1 - src/libs/agc.cpp | 52 +++--------- tests/ShaderRecompilerComputeTests.cpp | 84 +++++++++++++------ 6 files changed, 75 insertions(+), 68 deletions(-) diff --git a/src/graphics/guest_gpu/command_processor/pm4Dispatch.cpp b/src/graphics/guest_gpu/command_processor/pm4Dispatch.cpp index be6136938..12a7a6050 100644 --- a/src/graphics/guest_gpu/command_processor/pm4Dispatch.cpp +++ b/src/graphics/guest_gpu/command_processor/pm4Dispatch.cpp @@ -231,7 +231,6 @@ constexpr auto MakeCustomOpcodeDispatchTable() { 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_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 a8186d3cc..83147cdd8 100644 --- a/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp +++ b/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp @@ -1290,8 +1290,8 @@ KYTY_HW_UC_PARSER(HwUcSetGdsOaRegisters) { KYTY_CP_OP_PARSER(CpOpAcquireMem) { KYTY_PROFILER_FUNCTION(); - EXIT_NOT_IMPLEMENTED(cmd_id != 0xC0055800 && cmd_id != 0xc0061050); - return (cmd_id == 0xc0061050 ? 7 : 6); + EXIT_NOT_IMPLEMENTED(cmd_id != 0xc0055800 && cmd_id != 0xc0065800); + return KYTY_PM4_LEN(cmd_id) - 1; } KYTY_CP_OP_PARSER(CpOpDispatchDirect) { diff --git a/src/graphics/guest_gpu/pm4.cpp b/src/graphics/guest_gpu/pm4.cpp index 554b33255..a77efe018 100644 --- a/src/graphics/guest_gpu/pm4.cpp +++ b/src/graphics/guest_gpu/pm4.cpp @@ -23,7 +23,6 @@ constexpr auto MakeRegisterNames() { names[R_DISPATCH_RESET] = "R_DISPATCH_RESET"; 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_FLIP] = "R_FLIP"; names[R_RELEASE_MEM] = "R_RELEASE_MEM"; diff --git a/src/graphics/guest_gpu/pm4.h b/src/graphics/guest_gpu/pm4.h index 80d190226..186c8844c 100644 --- a/src/graphics/guest_gpu/pm4.h +++ b/src/graphics/guest_gpu/pm4.h @@ -86,7 +86,6 @@ constexpr uint32_t R_WAIT_FLIP_DONE = 0x06; constexpr uint32_t R_DISPATCH_RESET = 0x09; 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_FLIP = 0x17; constexpr uint32_t R_RELEASE_MEM = 0x18; diff --git a/src/libs/agc.cpp b/src/libs/agc.cpp index cb207642a..585243906 100644 --- a/src/libs/agc.cpp +++ b/src/libs/agc.cpp @@ -3022,39 +3022,8 @@ uint32_t* KYTY_SYSV_ABI AgcDcbAcquireMem(CommandBuffer* buf, uint8_t engine, uin engine, cb_db_op, gcr_cntl, reinterpret_cast(base), size_bytes, poll_cycles); } - bool no_size = (static_cast(size_bytes) == -1); - auto vaddr = reinterpret_cast(base); - EXIT_NOT_IMPLEMENTED(buf == nullptr); - static std::atomic warning_log_count {0}; - const bool log_warning = (warning_log_count.fetch_add(1) < 64); - if (!no_size && (size_bytes & 0xffu) != 0) { - if (log_warning) { - LOGF_COLOR(Log::Color::Red, "\t warning: size_bytes is not 256-byte aligned\n"); - } - } - if (!no_size && (size_bytes >> 40u) != 0) { - if (log_warning) { - LOGF_COLOR(Log::Color::Red, "\t warning: size_bytes is too large\n"); - } - } - if ((vaddr & 0xffu) != 0) { - if (log_warning) { - LOGF_COLOR(Log::Color::Red, "\t warning: base is not 256-byte aligned\n"); - } - } - if ((vaddr >> 40u) != 0) { - if (log_warning) { - LOGF_COLOR(Log::Color::Red, "\t warning: base is too high\n"); - } - } - if (engine > 1) { - if (log_warning) { - LOGF_COLOR(Log::Color::Red, "\t warning: unsupported engine 0x%02" PRIx8 "\n", engine); - } - } - auto* cmd = buf->AllocateDW(8); if (cmd == nullptr) { @@ -3062,14 +3031,19 @@ uint32_t* KYTY_SYSV_ABI AgcDcbAcquireMem(CommandBuffer* buf, uint8_t engine, uin return nullptr; } - cmd[0] = KYTY_PM4(8, Pm4::IT_NOP, Pm4::R_ACQUIRE_MEM); - cmd[1] = (static_cast(engine & 1u) << 31u) | cb_db_op; - cmd[2] = (no_size ? 0 : static_cast(size_bytes >> 8u)); - cmd[3] = 0; + constexpr uint64_t address_mask = 0xffffffffffffull; + const auto vaddr = reinterpret_cast(base); + // Cover the byte range in 256-byte units, bounded by the 48-bit address space. + const auto size = std::min((((vaddr & 0xffu) + size_bytes) & address_mask) + 0xffu, + (address_mask & ~uint64_t {0xff}) - vaddr) >> 8u; + cmd[0] = KYTY_PM4(8, Pm4::IT_ACQUIRE_MEM, 0u); + cmd[1] = (static_cast(engine) << 31u) | (cb_db_op & 0x06007fc0u); + cmd[2] = static_cast(size); + cmd[3] = static_cast((size >> 32u) & 0xffu); cmd[4] = static_cast(vaddr >> 8u); - cmd[5] = 0; - cmd[6] = poll_cycles / 40; - cmd[7] = gcr_cntl; + cmd[5] = static_cast(vaddr >> 40u); + cmd[6] = std::min(poll_cycles >> 4u, 0xffffu); + cmd[7] = gcr_cntl & 0x7ffffu; return cmd; } @@ -3083,7 +3057,7 @@ uint32_t KYTY_SYSV_ABI AgcDcbAcquireMemGetSize() { uint32_t* KYTY_SYSV_ABI AgcAcbAcquireMem(CommandBuffer* buf, uint32_t gcr_cntl, const volatile void* base, uint64_t size_bytes, uint32_t poll_cycles) { - return AgcDcbAcquireMem(buf, 1, 0, gcr_cntl, base, size_bytes, poll_cycles); + return AgcDcbAcquireMem(buf, 0, 0, gcr_cntl, base, size_bytes, poll_cycles); } uint32_t KYTY_SYSV_ABI AgcAcbAcquireMemGetSize() { diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 63940c81c..84fd0f40c 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -45742,29 +45742,6 @@ void CheckVulkan13FeatureRequirements() { std::printf("[host] %-32s ok\n", "Vulkan13FeatureRequirements"); } -void CheckPm4AcquireMemNoOp(RenderContext &renderer) { - GraphicsInitJmpTables(); - CommandProcessor processor(renderer, 0); - const std::array standard_payload{0x00400000u, 1u, 0u, - 0u, 0u, 10u}; - const std::array custom_payload{ - 0xdeadbeefu, 0xffffffffu, 0x80000000u, 0x13579bdfu, - 0x2468ace0u, 0xaaaaaaaau, 0x55555555u}; - const auto standard_before = standard_payload; - const auto custom_before = custom_payload; - Require("Pm4AcquireMemNoOp", "recognized packets", - CpOpAcquireMem(processor, 0xC0055800u, standard_payload.data(), 0, - 0) == 6 && - CpOpAcquireMem(processor, 0xc0061050u, custom_payload.data(), 0, - 0) == 7 && - standard_payload == standard_before && - custom_payload == custom_before, - "ACQUIRE_MEM did not consume both pinned packet forms as " - "side-effect-free " - "no-ops"); - std::printf("[host] %-32s ok\n", "Pm4AcquireMemNoOp"); -} - void CheckPm4SyntheticOcclusionCounterDump(RenderContext &renderer) { GraphicsInitJmpTables(); CommandProcessor processor(renderer, 0); @@ -46531,6 +46508,64 @@ struct CommandBufferLayout { static_assert(offsetof(CommandBufferLayout, cursor_up) == 0x10); static_assert(offsetof(CommandBufferLayout, reserved_dw) == 0x30); +void CheckPm4AcquireMemPackets(RenderContext &renderer) { + constexpr const char *name = "Pm4AcquireMemPackets"; + GraphicsInitJmpTables(); + CommandProcessor processor(renderer, 0); + // Byte ranges cover every touched 256-byte block, including partial first and last blocks. + struct Case { + const char *label; + bool async; + uint8_t engine; + uint32_t cb_db, gcr; + uint64_t base, size; + uint32_t poll; + std::array expected; + }; + const Case cases[]{ + {"captured byte range", false, 1, 0, 0x9000, 0x2036000308ull, 0x20, 400, + {0xc0065800, 0x80000000, 1, 0, 0x20360003, 0, 25, 0x9000}}, + {"crossing high address", false, 0, 0x7fc0, 0x20, 0xab12345678f0ull, 0x40, 400, + {0xc0065800, 0x7fc0, 2, 0, 0x12345678, 0xab, 25, 0x20}}, + {"full range", false, 0, 0x7fc0, 0x20, 0, UINT64_MAX, 400, + {0xc0065800, 0x7fc0, UINT32_MAX, 0xff, 0, 0, 25, 0x20}}, + {"wide range and saturated poll", false, 1, 0, 0x380, 0, 0x10000000000ull, UINT32_MAX, + {0xc0065800, 0x80000000, 0, 1, 0, 0, 0xffff, 0x380}}, + {"async byte range", true, 0, 0, 0x9000, 0x2036000308ull, 0x20, 400, + {0xc0065800, 0, 1, 0, 0x20360003, 0, 25, 0x9000}}, + }; + Require(name, "packet sizes", Gen5::AgcDcbAcquireMemGetSize() == 32 && + Gen5::AgcAcbAcquireMemGetSize() == 32, + "native single ACQUIRE_MEM packet size changed"); + for (const auto &test : cases) { + std::array packet{}; + CommandBufferLayout layout{packet.data(), packet.data() + packet.size(), + packet.data(), packet.data() + packet.size(), + nullptr, nullptr, 0}; + auto *buffer = reinterpret_cast(&layout); + const auto *base = reinterpret_cast(test.base); + auto *emitted = test.async + ? Gen5::AgcAcbAcquireMem(buffer, test.gcr, base, test.size, test.poll) + : Gen5::AgcDcbAcquireMem(buffer, test.engine, test.cb_db, test.gcr, + base, test.size, test.poll); + Require(name, test.label, emitted == packet.data() && + layout.cursor_up == packet.data() + packet.size() && packet == test.expected, + "ACQUIRE_MEM differs from the pinned native packet"); + const auto tick = renderer.GetCommandScheduler().CurrentTick(); + Pm4Execution execution; + Require(name, test.label, + processor.Process(execution, packet) == Pm4ProcessResult::Complete && + packet == test.expected && renderer.GetCommandScheduler().CurrentTick() == tick, + "native ACQUIRE_MEM was not consumed without mutation or queue submission"); + } + const std::array ps4_packet{0xc0055800, 0x00400000, 1, 0, 0, 0, 10}; + Pm4Execution execution; + Require(name, "native PS4 packet", + processor.Process(execution, ps4_packet) == Pm4ProcessResult::Complete, + "seven-DWORD ACQUIRE_MEM was not consumed"); + std::printf("[host] %-32s ok\n", name); +} + void CheckPm4WaitPackets(RenderContext &renderer) { GraphicsInitJmpTables(); CommandProcessor processor(renderer, 0); @@ -48240,6 +48275,7 @@ int main(int argc, char **argv) { } if (argc == 2 && std::strcmp(argv[1], "--pm4-wait-only") == 0) { VulkanHarness vulkan; + CheckPm4AcquireMemPackets(vulkan.RuntimeRenderer()); CheckPm4WaitPackets(vulkan.RuntimeRenderer()); CheckPm4WaitResume(vulkan.RuntimeRenderer()); return 0; @@ -48512,7 +48548,7 @@ int main(int argc, char **argv) { CheckErrorDialogLifecycle(); CheckSigninDialogLifecycle(); CheckVulkan13FeatureRequirements(); - CheckPm4AcquireMemNoOp(vulkan.RuntimeRenderer()); + CheckPm4AcquireMemPackets(vulkan.RuntimeRenderer()); CheckPm4SyntheticOcclusionCounterDump(vulkan.RuntimeRenderer()); CheckPm4Predication(vulkan.RuntimeRenderer()); CheckPm4StencilInfoValueLane(vulkan.RuntimeRenderer());