mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[Fix] (Buffer): refresh persistent read maps at glFinish and glClientWaitSync
This commit is contained in:
@@ -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__);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user