mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-07 19:58:32 +09:00
[Fix] (MG_Backend/DirectGLES): survive ES context recreation in buffer ops
Track context generation + synced change serial per resource; re-register ops on MakeCurrent. Fixes frozen buffer contents after the trace replayer's probe context teardown. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -3485,6 +3485,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
return false;
|
||||
}
|
||||
t_backendContextCurrent = true;
|
||||
// The ops table may have been unregistered when a previous ES context was
|
||||
// destroyed (e.g. a probe context); re-register now that GL is usable.
|
||||
BufferImpl::RegisterBufferBackendOps();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3511,7 +3514,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
}
|
||||
|
||||
void DestroyEGLContext() {
|
||||
BufferImpl::UnregisterBufferBackendOps();
|
||||
BufferImpl::OnBackendContextDestroyed();
|
||||
t_backendContextCurrent = false;
|
||||
if (g_Display != EGL_NO_DISPLAY) {
|
||||
g_EGLFuncs.eglMakeCurrent(g_Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
|
||||
@@ -123,6 +123,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
Uint g_boundArrayBufferId = 0;
|
||||
Bool g_boundArrayBufferKnown = false;
|
||||
|
||||
// Bumped whenever the backend ES context is destroyed; resources with
|
||||
// an older generation hold ids from a dead context.
|
||||
Uint g_bufferContextGeneration = 1;
|
||||
|
||||
// Resources whose owning BufferObject died; ids deleted at the next
|
||||
// sync point with a current ES context.
|
||||
Vector<SharedPtr<BackendBufferResource>> g_deferredBufferReleases;
|
||||
@@ -133,7 +137,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
}
|
||||
|
||||
Bool CanTouchGLNow() {
|
||||
return false && DirectGLES::IsBackendContextCurrentOnThisThread(); // BISECT-EXPERIMENT
|
||||
return DirectGLES::IsBackendContextCurrentOnThisThread();
|
||||
}
|
||||
|
||||
// (Re)specify backend storage from the shadow copy: glBufferData.
|
||||
@@ -151,6 +155,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
resource.storageInitialized = true;
|
||||
resource.pendingRespecify = false;
|
||||
resource.pendingRanges.clear();
|
||||
resource.syncedChangeSerial = bufferObject.GetChangeSerial();
|
||||
}
|
||||
|
||||
Bool StorageMatches(const GLESBufferResource& resource, const BufferObject& bufferObject) {
|
||||
@@ -171,7 +176,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
void Ops_Respecify(BufferObject& bufferObject) {
|
||||
auto* resource = ResourceOf(bufferObject);
|
||||
if (!resource) return; // lazy: EnsureBufferResource full-uploads on creation
|
||||
if (!CanTouchGLNow() || resource->id == 0) {
|
||||
if (!CanTouchGLNow() || resource->id == 0 ||
|
||||
resource->contextGeneration != g_bufferContextGeneration) {
|
||||
resource->pendingRespecify = true;
|
||||
resource->pendingRanges.clear();
|
||||
return;
|
||||
@@ -190,11 +196,14 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
auto* resource = ResourceOf(bufferObject);
|
||||
if (!resource) return;
|
||||
if (resource->pendingRespecify) return; // full re-upload pending anyway
|
||||
if (!CanTouchGLNow() || resource->id == 0 || !StorageMatches(*resource, bufferObject)) {
|
||||
if (!CanTouchGLNow() || resource->id == 0 ||
|
||||
resource->contextGeneration != g_bufferContextGeneration ||
|
||||
!StorageMatches(*resource, bufferObject)) {
|
||||
resource->pendingRanges.Add({offset, offset + size});
|
||||
return;
|
||||
}
|
||||
UploadRangeNow(*resource, bufferObject, offset, offset + size);
|
||||
resource->syncedChangeSerial = bufferObject.GetChangeSerial();
|
||||
}
|
||||
|
||||
void Ops_FlushMappedRange(BufferObject& bufferObject, Range1D range,
|
||||
@@ -202,7 +211,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
auto* resource = ResourceOf(bufferObject);
|
||||
if (!resource) return;
|
||||
if (resource->pendingRespecify) return;
|
||||
if (!CanTouchGLNow() || resource->id == 0 || !StorageMatches(*resource, bufferObject)) {
|
||||
if (!CanTouchGLNow() || resource->id == 0 ||
|
||||
resource->contextGeneration != g_bufferContextGeneration ||
|
||||
!StorageMatches(*resource, bufferObject)) {
|
||||
resource->pendingRanges.Add(range);
|
||||
return;
|
||||
}
|
||||
@@ -227,17 +238,23 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
Memcpy(mappedData, bufferObject.GetDataReadOnly()->data() + range.start,
|
||||
range.end - range.start);
|
||||
g_GLESFuncs.glUnmapBuffer(TempBufferTarget);
|
||||
resource->syncedChangeSerial = bufferObject.GetChangeSerial();
|
||||
return;
|
||||
}
|
||||
MGLOG_E("Failed to map buffer with ID: %u for flush, falling back to glBufferSubData",
|
||||
resource->id);
|
||||
}
|
||||
UploadRangeNow(*resource, bufferObject, range.start, range.end);
|
||||
resource->syncedChangeSerial = bufferObject.GetChangeSerial();
|
||||
}
|
||||
|
||||
void Ops_OnDestroy(SharedPtr<BackendBufferResource>&& resource) {
|
||||
if (!resource) return;
|
||||
auto* glesResource = static_cast<GLESBufferResource*>(resource.get());
|
||||
if (glesResource->contextGeneration != g_bufferContextGeneration) {
|
||||
glesResource->id = 0; // id belonged to a destroyed context
|
||||
return;
|
||||
}
|
||||
if (CanTouchGLNow()) {
|
||||
if (glesResource->id != 0) {
|
||||
if (g_boundArrayBufferKnown && g_boundArrayBufferId == glesResource->id) {
|
||||
@@ -274,6 +291,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
g_deferredBufferReleases.clear();
|
||||
}
|
||||
|
||||
void OnBackendContextDestroyed() {
|
||||
UnregisterBufferBackendOps();
|
||||
++g_bufferContextGeneration;
|
||||
}
|
||||
|
||||
void ProcessDeferredBufferReleases() {
|
||||
if (!CanTouchGLNow()) return;
|
||||
Vector<SharedPtr<BackendBufferResource>> releases;
|
||||
@@ -283,6 +305,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
}
|
||||
for (auto& resource : releases) {
|
||||
auto* glesResource = static_cast<GLESBufferResource*>(resource.get());
|
||||
if (glesResource->contextGeneration != g_bufferContextGeneration) {
|
||||
glesResource->id = 0;
|
||||
continue;
|
||||
}
|
||||
if (glesResource->id != 0) {
|
||||
if (g_boundArrayBufferKnown && g_boundArrayBufferId == glesResource->id) {
|
||||
InvalidateArrayBufferBindingCache();
|
||||
@@ -312,6 +338,16 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
bufferObject->SetBackendResource(std::move(newResource));
|
||||
}
|
||||
|
||||
if (resource->contextGeneration != g_bufferContextGeneration) {
|
||||
// The id (if any) belonged to a destroyed ES context.
|
||||
resource->id = 0;
|
||||
resource->storageInitialized = false;
|
||||
resource->storageSize = 0;
|
||||
resource->pendingRespecify = true;
|
||||
resource->pendingRanges.clear();
|
||||
resource->contextGeneration = g_bufferContextGeneration;
|
||||
}
|
||||
|
||||
if (resource->id == 0) {
|
||||
g_GLESFuncs.glGenBuffers(1, &resource->id);
|
||||
if (resource->id == 0) {
|
||||
@@ -340,6 +376,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
UploadRangeNow(*resource, *bufferObject, std::min(range.start, end), end);
|
||||
}
|
||||
resource->pendingRanges.clear();
|
||||
resource->syncedChangeSerial = bufferObject->GetChangeSerial();
|
||||
} else if (resource->syncedChangeSerial != bufferObject->GetChangeSerial()) {
|
||||
// Ops could not track some writes (e.g. the ops table was
|
||||
// unregistered between contexts); re-upload everything.
|
||||
RespecifyStorageNow(*resource, *bufferObject);
|
||||
}
|
||||
return resource;
|
||||
}
|
||||
@@ -348,7 +389,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
#ifdef TRACY_ENABLE
|
||||
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
|
||||
#endif
|
||||
if (false && target == GL_ARRAY_BUFFER) { // BISECT2: cache disabled
|
||||
if (target == GL_ARRAY_BUFFER) {
|
||||
if (g_boundArrayBufferKnown && g_boundArrayBufferId == id) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -126,15 +126,27 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
Uint id = 0;
|
||||
SizeT storageSize = 0;
|
||||
Bool storageInitialized = false;
|
||||
// ES context generation this resource's id belongs to; ids from a
|
||||
// destroyed context are invalid and must not be deleted or reused.
|
||||
Uint contextGeneration = 0;
|
||||
// Frontend change serial the backend storage reflects. When immediate
|
||||
// ops cannot run (ops unregistered, no current context), this lags and
|
||||
// EnsureBufferResource falls back to a full re-upload.
|
||||
Uint64 syncedChangeSerial = 0;
|
||||
// Ops that arrived while no ES context was current on the calling thread
|
||||
// (or before storage existed); replayed by EnsureBufferResource.
|
||||
Bool pendingRespecify = false;
|
||||
VecRange1D pendingRanges;
|
||||
};
|
||||
|
||||
// Registered as the frontend's BufferBackendOps at backend init.
|
||||
// Registered as the frontend's BufferBackendOps at backend init and on
|
||||
// every MakeCurrent (the ES context can be destroyed and recreated, e.g.
|
||||
// by the trace replayer's probe context).
|
||||
void RegisterBufferBackendOps();
|
||||
void UnregisterBufferBackendOps();
|
||||
// The ES context died: unregister ops, invalidate all outstanding GL ids
|
||||
// (they belonged to the dead context) and drop deferred deletes.
|
||||
void OnBackendContextDestroyed();
|
||||
|
||||
// Get-or-create the backend resource and bring its storage up to date
|
||||
// (creates the GL buffer, replays pending ops, pushes persistent-mapped
|
||||
|
||||
Reference in New Issue
Block a user