From e058a4507af232ecd13511f2f8eb4b46db1d0530 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Wed, 7 Oct 2026 18:21:12 -0400 Subject: [PATCH] [Perf] (MG_Remote): the apply loop hands its poll's ring head to the drain, so a woken drain does not poll the GPU-fault verdict twice (Espryt rd12 inproc apply thread 1.34 -> 0.78 ms/frame). --- MobileGL/MG_Remote/Server/ServerLoop.cpp | 17 +++++++++++------ MobileGL/MG_Remote/Server/ServerLoop.h | 4 +++- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/MobileGL/MG_Remote/Server/ServerLoop.cpp b/MobileGL/MG_Remote/Server/ServerLoop.cpp index 36351044..a5bc5d91 100644 --- a/MobileGL/MG_Remote/Server/ServerLoop.cpp +++ b/MobileGL/MG_Remote/Server/ServerLoop.cpp @@ -708,8 +708,14 @@ namespace MobileGL::MG_Remote::Server { // backend's verdict - the driver's GUILTY report on Espryt, the hang watch on Magma - is // asked HERE, before the drain, so that frame is never applied: the session latches // (DeviceLost, its client reads a lost context) and leaves below. Cheap by contract. - if (m_backend != nullptr && !SessionLatched()) (void)m_backend->LatchIfGpuFaulted(); - if (eventRingHasRoom()) DrainRing(); + // The head is loaded BEFORE the poll, so every record below it was in the ring when the + // poll ran and the drain need not poll for it again (DrainRing's `polledHead`). + std::uint64_t polledHead = 0; + if (m_backend != nullptr && !SessionLatched()) { + polledHead = signals.CmdHead != nullptr ? signals.CmdHead->load(std::memory_order_acquire) : 0; + (void)m_backend->LatchIfGpuFaulted(); + } + if (eventRingHasRoom()) DrainRing(polledHead); if (stopOrForfeit()) break; // The session-targeted device-loss debug knob (PipeSessionFail.h): asked here, after a // drain, so it reaches a client that never presents or reads back, once the session @@ -1013,7 +1019,7 @@ namespace MobileGL::MG_Remote::Server { return true; } - Uint64 ServerLoop::DrainRing() { + Uint64 ServerLoop::DrainRing(std::uint64_t polledHead) { // THE EMPTY-RING ANSWER COSTS TWO LOADS AND NOTHING ELSE, and that was AUDITED rather // than assumed (P5d round 3, package T item 2): the idle poll calls this on every // iteration, so a lock or a clock read in here would be the same defect @@ -1101,12 +1107,11 @@ namespace MobileGL::MG_Remote::Server { // previous record was applied and acknowledged, so after a reset the driver may already // report - used to be popped and applied unpolled, and a GUILTY context ran one more record // (ServerLoopEglLatchTest under CPU load). `polledHead` is the command ring's head as loaded - // just BEFORE the last poll: every record below it was visible when that poll ran. A record - // at or past it re-loads the head and polls first. While the client stays ahead this is one + // just BEFORE the last poll (the apply loop's own, handed in): every record below it was + // visible when that poll ran. A record at or past it re-loads the head and polls first. While the client stays ahead this is one // poll per batch the drain catches up to, never more than one per record. Transport::LinkSignals signals{}; if (auto* link = session.DataLink()) signals = link->Signals(); - std::uint64_t polledHead = 0; for (;;) { if (SessionLatched()) break; if (m_windowRequests.load(std::memory_order_acquire) != 0) { diff --git a/MobileGL/MG_Remote/Server/ServerLoop.h b/MobileGL/MG_Remote/Server/ServerLoop.h index b61658aa..632b4288 100644 --- a/MobileGL/MG_Remote/Server/ServerLoop.h +++ b/MobileGL/MG_Remote/Server/ServerLoop.h @@ -548,7 +548,9 @@ namespace MobileGL::MG_Remote::Server { // the probe's metadata is protected for the same lifetime as its frame. MobileGLResult PostSurfaceControlFrameWithCallerLock(SurfaceControlFrame& frame); // Pops and applies every record currently in the ring; returns how many it applied. - Uint64 DrainRing(); + // `polledHead`: the command ring's head as loaded just before the caller's own fault poll + // (0 when the caller did not poll) - records below it need no poll of their own. + Uint64 DrainRing(std::uint64_t polledHead = 0); void SignalExited(); ServerSession* m_session = nullptr;