From fdb747b06d84b75668f697ed9ab2e5966af3dc32 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Tue, 13 Oct 2026 08:02:35 -0400 Subject: [PATCH] [Fix] (Remote): stop the adoption's falling edge from overwriting writes made through the adopted map --- MobileGL/MG_Pipe/PipeApply.cpp | 15 ++++++++++++--- .../MG_State/GLState/BufferState/BufferObject.cpp | 7 +++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/MobileGL/MG_Pipe/PipeApply.cpp b/MobileGL/MG_Pipe/PipeApply.cpp index e9ec9059..4f56e51a 100644 --- a/MobileGL/MG_Pipe/PipeApply.cpp +++ b/MobileGL/MG_Pipe/PipeApply.cpp @@ -2808,7 +2808,12 @@ namespace MobileGL::MG_Pipe { // kReplySlot. An unregistered table declines every acquisition, which is exactly the // answer a build with no migrated backend should give. if (g_resourceOps == nullptr || g_resourceOps->MapPersistent == nullptr) return nullptr; - return g_resourceOps->MapPersistent(handle.Handle, size, seedBytes); + void* base = g_resourceOps->MapPersistent(handle.Handle, size, seedBytes); + // An adopted store has no live host writer to announce: the client writes the store's + // own memory and tells the applier nothing (BufferObject::NotePersistentMapStateChanged + // sends no falling edge for it), so the rising edge the map published ends here. + if (base != nullptr) record->HasLiveHostWrites = false; + return base; } void MGPipeApplyUnmapPersistent(const MGPHandleOnly& handle) { @@ -2858,7 +2863,9 @@ namespace MobileGL::MG_Pipe { // monolith client writes its adopted map, and the applier is told nothing either way. PinNoLiveHostWrites(*record, handle.Handle, "adopt_external"); if (g_resourceOps == nullptr || g_resourceOps->ImportExternal == nullptr) return false; - return g_resourceOps->ImportExternal(handle.Handle, ahb, size); + if (!g_resourceOps->ImportExternal(handle.Handle, ahb, size)) return false; + record->HasLiveHostWrites = false; // as map_persistent's mint: the adoption ends the state + return true; } void* MGPipeApplyDonateInProcess(const MGPHandleOnly& handle) { @@ -2875,7 +2882,9 @@ namespace MobileGL::MG_Pipe { // directly, as a monolith client writes its adopted map. PinNoLiveHostWrites(*record, handle.Handle, "donate_in_process"); if (g_resourceOps == nullptr || g_resourceOps->DonateInProcess == nullptr) return nullptr; - return g_resourceOps->DonateInProcess(handle.Handle, record->Desc.Width); + void* base = g_resourceOps->DonateInProcess(handle.Handle, record->Desc.Width); + if (base != nullptr) record->HasLiveHostWrites = false; // as map_persistent's mint + return base; } #endif diff --git a/MobileGL/MG_State/GLState/BufferState/BufferObject.cpp b/MobileGL/MG_State/GLState/BufferState/BufferObject.cpp index b055286b..28ab4a72 100644 --- a/MobileGL/MG_State/GLState/BufferState/BufferObject.cpp +++ b/MobileGL/MG_State/GLState/BufferState/BufferObject.cpp @@ -488,6 +488,13 @@ namespace MobileGL::MG_State::GLState { !m_resource.IsGpuResident(); if (live == m_publishedLiveHostWrites) return; m_publishedLiveHostWrites = live; + // AN ADOPTION ENDS THE STATE WITHOUT A RECORD. The one falling edge a resident store + // takes is AcquireMemoryRange adopting the store under a live persistent map. From then + // on the application writes the store's own memory, so a block captured here would land + // on the far side AFTER writes made through the map since, and put back the bytes they + // replaced (the seed, zeros for a fresh store). The adoption itself clears the far + // side's bit (MGPipeApplyMapPersistent / AdoptExternal / DonateInProcess). + if (!live && m_resource.IsGpuResident()) return; // ONE BLOCK, NOT THE SPAN. On the falling edge the span's bytes have already shipped // on this same path (ReleaseMemory lands the staged writes and calls