From 4aec5621a855716f76894c4b22921c2e13aca4a2 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Tue, 29 Sep 2026 19:32:19 -0400 Subject: [PATCH] [Fix] (MG_State, P11 B2): glDeleteBuffers moves the bind-point generation of every indexed target it unbinds, and a transform-feedback object swap moves it too - the server's window no longer names a deleted buffer's handle after its slot is reused --- .../MG_State/GLState/BufferState/BufferState.cpp | 12 ++++++++++++ MobileGL/MG_State/GLState/Core.cpp | 7 +++++++ 2 files changed, 19 insertions(+) diff --git a/MobileGL/MG_State/GLState/BufferState/BufferState.cpp b/MobileGL/MG_State/GLState/BufferState/BufferState.cpp index e43ee75c..ddf308cd 100644 --- a/MobileGL/MG_State/GLState/BufferState/BufferState.cpp +++ b/MobileGL/MG_State/GLState/BufferState/BufferState.cpp @@ -64,6 +64,18 @@ namespace MobileGL::MG_State::GLState { if (bindingPoint.GetBoundObject() == it->second) { bindingPoint.Bind(nullptr); bindingPoint.ClearRange(); +#if MOBILEGL_PIPE_PUSH + // An UNBIND, and the one writer of an indexed point that is not an + // entry point (GL_Buffer.cpp's BindBuffer{Base,Range}_State and + // Core.cpp's transform-feedback writers bump their own): without the + // bump the target's window is never re-sent, the server goes on naming + // this buffer's handle, and once its slot is reused at a newer + // generation the next draw or dispatch that walks the window asks the + // backend for the dead one - Espryt dies with Fatal{ProtocolCorruption, + // "BackendSlotTable.Generation"} (DeletedBoundBufferScenario). + NoteBindPointChanged(BufferBindPointTargets[static_cast( + &bindingPointArray - m_bufferBindPointTargets.data())]); +#endif } } } diff --git a/MobileGL/MG_State/GLState/Core.cpp b/MobileGL/MG_State/GLState/Core.cpp index 9939b443..49c1064a 100644 --- a/MobileGL/MG_State/GLState/Core.cpp +++ b/MobileGL/MG_State/GLState/Core.cpp @@ -1325,6 +1325,13 @@ namespace MobileGL::MG_State { } void GLContext::RestoreBoundTransformFeedbackState() { +#if MOBILEGL_PIPE_PUSH + // The FOURTH writer of the transform-feedback binding points (a bind or a delete of + // the bound transform-feedback object swaps the whole set in): it moves the generation + // like the other three, so bit 17 cannot miss a swap whose two objects happen to carry + // the same capture-span generation. + NoteBufferBindPointChanged(BufferTarget::TransformFeedback); +#endif const auto& object = m_transformFeedbackObjects[m_boundTransformFeedback]; for (Uint i = 0; i < MAX_TRANSFORM_FEEDBACK_BUFFERS; ++i) { auto& point = m_bufferState.GetBindingPoint(BufferTarget::TransformFeedback, i);