[Fix] (Buffer): refresh persistent read maps at glFinish and glClientWaitSync

This commit is contained in:
2026-10-12 08:32:52 -04:00
parent acad001b34
commit 1962e0c603
4 changed files with 64 additions and 4 deletions
@@ -22,6 +22,7 @@
#include "../Sync/GL_Sync.h"
#include "../Debug/GL_Debug.h"
#include <MG_State/GLState/Core.h>
#include <MG_State/GLState/BufferState/BufferObject.h>
#include <MG_Impl/EGLImpl/EGLImpl.h>
#define DECLARE_GL_FUNCTION_STUB_HEAD(type, name, ...) MOBILEGL_GL_API type gl##name(__VA_ARGS__) {
@@ -127,6 +128,11 @@ MOBILEGL_GL_API void glFinish() {
MobileGL::MG_Impl::EGLImpl::FlushSharedImageAccesses();
MobileGL::MG_Pipe::MGPipeClientFinish();
#endif
// Everything issued has completed, so a persistent read map now has to show the GPU's
// writes (GL 4.6 core 7.13).
if (MobileGL::MG_State::pGLContext != nullptr) {
MobileGL::MG_State::GLState::SyncPersistentReadMapsFromGpu();
}
}
MOBILEGL_GL_API void glFlush() {
MGLOG_D("Implementing function: %s(...)", __FUNCTION__);
+12 -4
View File
@@ -9,6 +9,7 @@
#include "GL_Sync.h"
#include <MG_Backend/BackendObjects.h>
#include <MG_State/GLState/Core.h>
#include <MG_State/GLState/BufferState/BufferObject.h>
#include <MG_Impl/Pipe/PipeFill.h>
// CONTRACT-P5.md §7 / ID-14: a null check on a GLFunctionsTable slot may not survive into the
// client under split - it becomes a caps-mirror read. SlotCaps.h carries the rule and the test
@@ -101,11 +102,18 @@ namespace MobileGL::MG_Impl::GLImpl {
return GL_WAIT_FAILED;
}
const auto backendClientWaitSync = MG_Backend::gBackendFunctionsTable.GL.ClientWaitSync;
if (!backendClientWaitSync || !syncObject->backendHandle) {
return GL_ALREADY_SIGNALED; // legacy always-signaled fallback
GLenum result = GL_ALREADY_SIGNALED; // legacy always-signaled fallback
if (backendClientWaitSync && syncObject->backendHandle) {
MGP_FILL(ClientWaitSync);
result = backendClientWaitSync(syncObject->backendHandle, flags, timeout);
}
MGP_FILL(ClientWaitSync);
return backendClientWaitSync(syncObject->backendHandle, flags, timeout);
// A satisfied wait is the point at which GPU writes issued before the fence become
// visible through a persistent read map (GL 4.6 core 7.13), and its pointer is the
// frontend shadow, which only an explicit pull refreshes.
if (result == GL_ALREADY_SIGNALED || result == GL_CONDITION_SATISFIED) {
MG_State::GLState::SyncPersistentReadMapsFromGpu();
}
return result;
}
void WaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) {
@@ -11,6 +11,7 @@
#include <Config.h>
#include <atomic>
#include <mutex>
#include <MG_Pipe/PipeMutation.h>
#if MOBILEGL_BUILD_DISAGGREGATED
// MG_State reaching into MG_Remote/Client is the layering ARCHITECTURE.md:575 names - the CLIENT
@@ -64,6 +65,33 @@ namespace MobileGL::MG_State::GLState {
}
}
namespace {
// The live persistent READ maps, keyed on the never-reused lifetime id. Recursive
// because SyncGpuWrites runs under it and must stay free to reach map-state code.
std::recursive_mutex g_persistentReadMapsMutex;
UnorderedMap<Uint64, BufferObject*> g_livePersistentReadMaps;
} // namespace
void BufferObject::NotePersistentReadMapStateChanged() {
const Bool live = m_isMapped && (m_mappingAccess & BufferMappingAccessBit::Persistent) &&
(m_mappingAccess & BufferMappingAccessBit::Read);
const std::lock_guard<std::recursive_mutex> lock(g_persistentReadMapsMutex);
if (live) {
g_livePersistentReadMaps[m_lifetimeId] = this;
} else {
g_livePersistentReadMaps.erase(m_lifetimeId);
}
}
void SyncPersistentReadMapsFromGpu() {
const std::lock_guard<std::recursive_mutex> lock(g_persistentReadMapsMutex);
if (g_livePersistentReadMaps.empty()) return;
for (const auto& [lifetimeId, buffer] : g_livePersistentReadMaps) {
(void)lifetimeId;
buffer->SyncGpuWrites();
}
}
Uint64 BufferObject::AllocateLifetimeId() {
return g_nextBufferLifetimeId.fetch_add(1, std::memory_order_relaxed);
}
@@ -86,6 +114,10 @@ namespace MobileGL::MG_State::GLState {
// outlives its object is the one failure this set must not have. Forget is a no-op
// for a buffer that was never a member.
MG_Record::PersistentMapTracker::Instance().Forget(*this);
{
const std::lock_guard<std::recursive_mutex> lock(g_persistentReadMapsMutex);
g_livePersistentReadMaps.erase(m_lifetimeId);
}
// P3a D-L: the buffer's death crosses as resource_destroy, which is the catalogue
// call for it - no seventh NotifyStateObjectDestroyed raiser is added, because that
// header exists for kinds that have no such call. The emit-then-free ORDER is fixed
@@ -426,6 +458,7 @@ namespace MobileGL::MG_State::GLState {
}
void BufferObject::NotePersistentMapStateChanged() {
NotePersistentReadMapStateChanged();
if (!MG_Record::PersistentMapTracker::PushIsArmed()) return;
MG_Record::PersistentMapTracker::Instance().NoteMapStateChanged(*this);
// Teardown must retire membership, but only the client publishes live-host-write state.
@@ -168,6 +168,10 @@ namespace MobileGL {
// call, no serial and no epoch, and the applier's IsBufferDrawCleanByHandle can
// no longer ask this object (there is no object on that side of a spawn).
void NotePersistentMapStateChanged();
// Keeps this buffer's membership in the live persistent READ map set current (see
// SyncPersistentReadMapsFromGpu). Called from NotePersistentMapStateChanged, so it
// rides every event that can move the predicate, on every transport.
void NotePersistentReadMapStateChanged();
// What MGPipeEmitResourceSubData and MGPipeEmitBufferSubDataResident write into
// MGPSubData::HasLiveHostWrites. The PUBLISHED value, not the live predicate: the
@@ -294,5 +298,14 @@ namespace MobileGL {
SizeT m_stagingBias = 0;
Bool m_ownsStagingData;
};
// GL 4.6 core 6.2 / 7.13: GPU writes to a buffer become visible through a persistent
// READ map once the client has waited for them - glFinish, or a glClientWaitSync on a
// fence issued after them (coherent maps; a non-coherent map adds a
// CLIENT_MAPPED_BUFFER_BARRIER_BIT barrier before that fence). The pointer such a map
// hands out is the frontend shadow, which nothing else pulls the GPU's bytes into, so
// these two waits refresh every live persistent read map that has a GPU write
// outstanding.
void SyncPersistentReadMapsFromGpu();
} // namespace MG_State::GLState
} // namespace MobileGL