From dc40478e7b798ac834648d25230c729f4184da17 Mon Sep 17 00:00:00 2001 From: nmzik Date: Fri, 23 Oct 2026 22:14:55 +0200 Subject: [PATCH] gpu: deliver RELEASE_MEM interrupts with the slice flush instead of their own --- src/graphics/guest_gpu/graphicsRun.cpp | 2 +- tests/ShaderRecompilerComputeTests.cpp | 69 +++++++++++--------------- 2 files changed, 30 insertions(+), 41 deletions(-) diff --git a/src/graphics/guest_gpu/graphicsRun.cpp b/src/graphics/guest_gpu/graphicsRun.cpp index 38ee3fde4..381803349 100644 --- a/src/graphics/guest_gpu/graphicsRun.cpp +++ b/src/graphics/guest_gpu/graphicsRun.cpp @@ -988,12 +988,12 @@ void CommandProcessor::ReleaseMem(uint32_t data_sel, uint32_t interrupt_selector default: EXIT("unknown release_mem data selector: %" PRIu32 "\n", data_sel); } + // GuestGpu::Process's end-of-slice flush submits the work this interrupt waits on. if (interrupt) { GetScheduler().DeferPriorityOperation( [&renderer = m_renderer, event_id = m_interrupt_event_id, interrupt_context_id] { renderer.TriggerInterrupt(event_id, interrupt_context_id); }); - BufferFlush(); } } diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index b13d1c797..be95c3044 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -3041,7 +3041,7 @@ public: cache_event_did_not_submit && cb_db_release_did_not_submit && interrupt_result == Pm4ProcessResult::Complete && - gpu_scheduler.CurrentTick() == interrupt_tick + 1 && + gpu_scheduler.CurrentTick() == interrupt_tick && interrupt_only_gds_label == UINT64_MAX; }); parser_complete.release(); @@ -3066,8 +3066,8 @@ public: "CPU-only unmap submitted or waited for unrelated native work"); Require("GpuCommandLane", "nonblocking GPU packets", parser_did_not_wait_gpu && parser_kept_nonblocking_boundaries, - "a cache event, GL2 writeback, or interrupt boundary used the " - "wrong submission behavior"); + "a cache event, GL2 writeback, or interrupt used the wrong " + "submission behavior"); alignas(uint64_t) uint64_t release_label = 0; alignas(uint64_t) uint64_t gds_label = UINT64_MAX; @@ -3076,7 +3076,7 @@ public: processor->BufferInit(); // Every end-of-pipe and end-of-shader action, with or without GL2 - // writeback, writes each label size and splits for its interrupt. + // writeback, writes each label size and submits nothing for its interrupt. for (const uint32_t action : {0x04u, 0x14u, 0x28u, 0x2bu, 0x2du, 0x2fu, 0x30u}) { for (const uint32_t gcr : {0u, 1u << 9u}) { for (const uint32_t data_sel : {1u, 2u, 3u}) { @@ -3098,27 +3098,12 @@ public: Require("GpuCommandLane", "release with write-confirm interrupt", release_result == Pm4ProcessResult::Complete && label_written && - gpu_scheduler.CurrentTick() == release_tick + 1, - "an end-of-pipe action lost its label or interrupt split"); + gpu_scheduler.CurrentTick() == release_tick, + "an end-of-pipe action lost its label or submitted work"); } } } - for (const auto interrupt : {0u, 3u, 1u, 2u}) { - release_label = 0; - auto immediate = make_release_mem(1, interrupt, &release_label, 0x11223344u); - Pm4Execution immediate_execution; - const auto immediate_tick = gpu_scheduler.CurrentTick(); - const auto immediate_result = - processor->Process(immediate_execution, immediate); - Require("GpuCommandLane", "32-bit release boundary", - immediate_result == Pm4ProcessResult::Complete && - gpu_scheduler.CurrentTick() == - immediate_tick + (interrupt == 1 || interrupt == 2) && - release_label == (interrupt == 1 ? 0 : 0x11223344u), - "label-only release submitted work or interrupt release lost its boundary"); - } - auto gds = make_release_mem(5, 0, &gds_label, 1ull << 16u); Pm4Execution gds_execution; const auto gds_tick = gpu_scheduler.CurrentTick(); @@ -3126,14 +3111,6 @@ public: const bool gds_waited_once = gpu_scheduler.CurrentTick() == gds_tick + 1; - auto interrupt_only = make_release_mem(0, 4, nullptr, 0); - Pm4Execution interrupt_execution; - const auto interrupt_tick = gpu_scheduler.CurrentTick(); - const auto interrupt_result = - processor->Process(interrupt_execution, interrupt_only); - const bool interrupt_split_once = - gpu_scheduler.CurrentTick() == interrupt_tick + 1; - auto gds_interrupt = make_release_mem(5, 2, &gds_label, 1ull << 16u); Pm4Execution gds_interrupt_execution; const auto gds_interrupt_tick = gpu_scheduler.CurrentTick(); @@ -3143,9 +3120,7 @@ public: gpu_scheduler.CurrentTick() == gds_interrupt_tick + 1; release_mem_submission_counts = - gds_result == Pm4ProcessResult::Complete && - gds_waited_once && interrupt_result == Pm4ProcessResult::Complete && - interrupt_split_once && + gds_result == Pm4ProcessResult::Complete && gds_waited_once && gds_interrupt_result == Pm4ProcessResult::Complete && gds_interrupt_waited_once; }); @@ -3155,10 +3130,9 @@ public: }); Require("GpuCommandLane", "RELEASE_MEM submission counts", release_mem_submission_counts && - static_cast(release_label) == 0x11223344u && static_cast(gds_label) == 0, - "RELEASE_MEM lost its required split/readback or retained a " - "redundant GPU wait"); + "RELEASE_MEM lost its GDS readback or retained a redundant GPU " + "wait"); } alignas(uint32_t) uint32_t packet_marker_a = 0; @@ -3241,10 +3215,11 @@ public: packet[7] = context_id & 0x07ffffffu; return packet; }; + // Waits only for submitted work, so an interrupt arrives only through its + // end-of-slice flush. const auto finish_gpu = [&] { gpu.SendCommandSync([&] { auto &scheduler = context.GetCommandScheduler(); - scheduler.Finish(); scheduler.WaitPriorityOperations(scheduler.CurrentTick() - 1); }); }; @@ -3255,10 +3230,10 @@ public: interrupt_queue, &event, 1, &count, &timeout); }; + // kDataAfterWriteConfirm writes its label without an interrupt. uint32_t no_interrupt_graphics_value = 0; - std::array no_interrupt_graphics_commands{}; - write_packet(no_interrupt_graphics_commands.data(), - &no_interrupt_graphics_value, 55); + auto no_interrupt_graphics_commands = + make_interrupt_packet(1, 3, &no_interrupt_graphics_value, 55, 0); gpu.Submit(no_interrupt_graphics_commands); gpu.WaitForIdle(); finish_gpu(); @@ -3270,7 +3245,8 @@ public: Require("GpuCommandLane", "no implicit graphics interrupt", no_graphics_interrupt_wait == LibKernel::KERNEL_ERROR_ETIMEDOUT && interrupt_count == 0 && no_interrupt_graphics_value == 55, - "graphics submission completion synthesized an interrupt"); + "kDataAfterWriteConfirm or graphics submission completion raised an " + "interrupt"); auto live_interrupt_commands = make_interrupt_packet(0, 1, nullptr, 0, 0x234u); @@ -3373,6 +3349,19 @@ public: compute_done_label == compute_done_value, "CS_DONE lost its full 64-bit label write or write-confirm interrupt"); + auto send_context_commands = make_interrupt_packet(0, 4, nullptr, 0, 0x789u); + gpu.SubmitCompute(0x20, send_context_commands); + gpu.WaitForIdle(); + finish_gpu(); + + interrupt_count = 0; + const auto send_context_wait = + wait_for_interrupt(interrupt_event, interrupt_count); + Require("GpuCommandLane", "kSendIntCtx interrupt", + send_context_wait == 0 && interrupt_count == 1 && + interrupt_event.ident == 0x20 && interrupt_event.data == 0x789u, + "kSendIntCtx lost its interrupt or context id"); + interrupt_count = 0; const auto extra_interrupt_wait = wait_for_interrupt(interrupt_event, interrupt_count);