[Perf] (MG_State/FastSTL): make object deletion cheap again

- GLContext::MarkBufferObjectForDeletion now detaches the deleted buffer
  only from the currently bound VAO (GL 4.6 5.1.2 semantics; other VAOs
  keep their shared_ptr attachments alive). The old every-VAO scan was
  O(VAOs) per delete - with one VAO per chunk section, vanilla chunk
  churn made it dominate the render thread and FPS decay over minutes.
- Bump FastSTL: erase(key) destroys in place instead of building the
  discarded successor iterator (a linear bucket-array scan), and switch
  the buffer/framebuffer/renderbuffer deletion paths to the key overload.

Together these removed the 34% render-thread deletion overhead measured
in aged vanilla sessions (simpleperf, Adreno 830 / DirectVulkan).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-09 12:12:50 +00:00
co-authored by Claude Fable 5
parent bb3a18c627
commit 08e808ef20
5 changed files with 15 additions and 8 deletions
@@ -64,7 +64,9 @@ namespace MobileGL::MG_State::GLState {
}
}
}
m_bufferObjects.erase(it);
// Key-based erase skips FastSTL's successor-iterator scan, which is
// pure overhead here and dominates delete-heavy frames.
m_bufferObjects.erase(index);
}
m_indexGenerator.Delete(index);
}
+9 -4
View File
@@ -80,11 +80,16 @@ namespace MobileGL::MG_State {
void GLContext::MarkBufferObjectForDeletion(Uint index) {
if (ValidateBufferObject(index)) {
// GL semantics: deleting a buffer detaches it only from the CURRENT
// context's bindings, including the currently bound VAO's attachment
// points; attachments in other VAOs must survive (the shared_ptr keeps
// the data object alive, matching the spec's deferred deletion). The
// previous every-VAO scan was wrong per spec and O(VAOs) per delete —
// with one VAO per chunk section, vanilla's steady buffer churn made it
// dominate the render thread and FPS decay over session time.
auto bufferObject = m_bufferState.GetBufferObject(index);
auto& vaos = m_vertexArrayState.GetAllVertexArrays();
for (auto& vao : vaos) {
if (vao == nullptr) continue;
const auto& vao = m_vertexArrayState.GetBoundVertexArray();
if (vao != nullptr) {
if (vao->GetIndexBufferBindingSlot().GetBoundObject() == bufferObject) {
vao->GetIndexBufferBindingSlot().Bind(nullptr);
}
@@ -65,7 +65,7 @@ namespace MobileGL::MG_State::GLState {
bindingSlot.Bind(GetFramebufferObject(0));
}
}
m_framebufferObjects.erase(it);
m_framebufferObjects.erase(index);
}
m_indexGenerator.Delete(index);
}
@@ -57,7 +57,7 @@ namespace MobileGL::MG_State::GLState {
bindingSlot.Bind(nullptr);
}
}
m_renderbufferObjects.erase(it);
m_renderbufferObjects.erase(index);
}
m_indexGenerator.Delete(index);
}