diff --git a/src/graphics/guest_gpu/command_processor/commandProcessor.h b/src/graphics/guest_gpu/command_processor/commandProcessor.h index b1e15c8a2..84092b2b1 100644 --- a/src/graphics/guest_gpu/command_processor/commandProcessor.h +++ b/src/graphics/guest_gpu/command_processor/commandProcessor.h @@ -112,6 +112,7 @@ public: void SetCeComplete(bool complete) { m_ce_complete = complete; } void WaitCe(); void WaitDeDiff(uint32_t diff); + void WaitForRewind(bool valid); void IncrementDe(); void IncrementCe(); diff --git a/src/graphics/guest_gpu/command_processor/pm4Dispatch.cpp b/src/graphics/guest_gpu/command_processor/pm4Dispatch.cpp index 7a180041f..aec5e58b2 100644 --- a/src/graphics/guest_gpu/command_processor/pm4Dispatch.cpp +++ b/src/graphics/guest_gpu/command_processor/pm4Dispatch.cpp @@ -224,6 +224,7 @@ constexpr auto MakeOpcodeDispatchTable() { g_cp_op_func[Pm4::IT_EVENT_WRITE_EOS] = CpOpEventWriteEos; g_cp_op_func[Pm4::IT_DMA_DATA] = CpOpDmaData; g_cp_op_func[Pm4::IT_ACQUIRE_MEM] = CpOpAcquireMem; + g_cp_op_func[Pm4::IT_REWIND] = CpOpRewind; g_cp_op_func[Pm4::IT_SET_CONTEXT_REG] = CpOpSetContextReg; g_cp_op_func[Pm4::IT_SET_SH_REG] = CpOpSetShaderReg; g_cp_op_func[Pm4::IT_SET_UCONFIG_REG] = CpOpSetUconfigReg; diff --git a/src/graphics/guest_gpu/command_processor/pm4Dispatch.h b/src/graphics/guest_gpu/command_processor/pm4Dispatch.h index 8f9bff81d..63dae6768 100644 --- a/src/graphics/guest_gpu/command_processor/pm4Dispatch.h +++ b/src/graphics/guest_gpu/command_processor/pm4Dispatch.h @@ -133,6 +133,7 @@ uint32_t CpOpDrawIndirectMulti(CommandProcessor&, uint32_t, const uint32_t*, uin uint32_t CpOpDrawIndex(CommandProcessor&, uint32_t, const uint32_t*, uint32_t, uint32_t); uint32_t CpOpDrawIndexOffset(CommandProcessor&, uint32_t, const uint32_t*, uint32_t, uint32_t); uint32_t CpOpPfpSyncMe(CommandProcessor&, uint32_t, const uint32_t*, uint32_t, uint32_t); +uint32_t CpOpRewind(CommandProcessor&, uint32_t, const uint32_t*, uint32_t, uint32_t); uint32_t CpOpIndexType(CommandProcessor&, uint32_t, const uint32_t*, uint32_t, uint32_t); uint32_t CpOpNumInstances(CommandProcessor&, uint32_t, const uint32_t*, uint32_t, uint32_t); uint32_t CpOpDrawIndexAuto(CommandProcessor&, uint32_t, const uint32_t*, uint32_t, uint32_t); diff --git a/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp b/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp index ebbdf4ff4..66b381c68 100644 --- a/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp +++ b/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp @@ -1830,6 +1830,17 @@ KYTY_CP_OP_PARSER(CpOpPfpSyncMe) { return 1; } +KYTY_CP_OP_PARSER(CpOpRewind) { + KYTY_PROFILER_FUNCTION(); + + EXIT_NOT_IMPLEMENTED(cmd_id != 0xc0005900); + EXIT_NOT_IMPLEMENTED((buffer[0] & ~0x81000000u) != 0); + + cp.WaitForRewind((buffer[0] & 0x80000000u) != 0); + + return 1; +} + KYTY_CP_OP_PARSER(CpOpSetPredication) { KYTY_PROFILER_FUNCTION(); diff --git a/src/graphics/guest_gpu/graphicsRun.cpp b/src/graphics/guest_gpu/graphicsRun.cpp index 49ab334dd..efb583ed9 100644 --- a/src/graphics/guest_gpu/graphicsRun.cpp +++ b/src/graphics/guest_gpu/graphicsRun.cpp @@ -374,6 +374,12 @@ void CommandProcessor::WaitDeDiff(uint32_t diff) { } } +void CommandProcessor::WaitForRewind(bool valid) { + if (!valid) { + SuspendPm4(); + } +} + void CommandProcessor::IncrementDe() { m_de_count++; } diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index d78c0d0a9..9e755b729 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -21096,6 +21096,39 @@ void CheckPm4WaitResume(RenderContext &renderer) { std::printf("[host] %-32s ok\n", "Pm4WaitResume"); } +void CheckPm4RewindResume(RenderContext &renderer) { + GraphicsInitJmpTables(); + CommandProcessor processor(renderer); + + 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.data(), commands.size()) == + 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.data(), commands.size()) == + Pm4ProcessResult::Complete && + suffix == 33, + "valid rewind did not resume at the following packet"); + std::printf("[host] %-32s ok\n", "Pm4RewindResume"); +} + void CheckPm4CeCompletion(RenderContext &renderer) { GraphicsInitJmpTables(); CommandProcessor processor(renderer); @@ -21223,6 +21256,11 @@ int main(int argc, char **argv) { CheckPm4ContextStateOperations(vulkan.RuntimeRenderer()); return 0; } + if (argc == 2 && std::strcmp(argv[1], "--rewind-only") == 0) { + VulkanHarness vulkan; + CheckPm4RewindResume(vulkan.RuntimeRenderer()); + return 0; + } if (argc == 2 && std::strcmp(argv[1], "--image-overlap-only") == 0) { CheckDepthAttachmentWrites(); CheckDynamicRenderingState(); @@ -21381,6 +21419,7 @@ int main(int argc, char **argv) { CheckPm4PolygonOffsetRegisters(vulkan.RuntimeRenderer()); CheckPm4ContextStateOperations(vulkan.RuntimeRenderer()); CheckPm4WaitResume(vulkan.RuntimeRenderer()); + CheckPm4RewindResume(vulkan.RuntimeRenderer()); CheckPm4CeCompletion(vulkan.RuntimeRenderer()); CheckEmbeddedFetchVertexOffset(); CheckEmbeddedFetchLaneSpill();