[Perf] (MG_Backend/DirectGLES): replace per-draw global-UBO glBufferSubData with a persistent-mapped ring allocator (fence-watermark reclaimed, MOBILEGL_DISABLE_UBO_RING opt-out); scrub stale indexed-binding shadow on glDeleteBuffers

This commit is contained in:
2026-07-16 00:10:12 -04:00
parent 305701326c
commit 2e7073a890
5 changed files with 350 additions and 7 deletions
+48 -7
View File
@@ -1076,14 +1076,52 @@ namespace MobileGL::MG_Backend::DirectGLES {
ZoneScopedNC("UpdateGlobalUBO", TRACY_ZONECOLOR_BACKEND);
#endif
const Uint32 uboContentVersion = currentProgram->GetUBOContentVersion();
if (backendProgram.GetLastUploadedGlobalUboVersion() != uboContentVersion) {
g_GLESFuncs.glBindBuffer(GL_UNIFORM_BUFFER, backendProgram.GetBackendGlobalUBOId());
g_GLESFuncs.glBufferSubData(GL_UNIFORM_BUFFER, 0, currentProgram->GetUBOSize(),
currentProgram->MapUBO());
g_GLESFuncs.glBindBuffer(GL_UNIFORM_BUFFER, 0);
backendProgram.SetLastUploadedGlobalUboVersion(uboContentVersion);
const SizeT uboSize = static_cast<SizeT>(currentProgram->GetUBOSize());
// Preferred path: write changed contents into a fresh slot of the
// shared persistent-mapped ring and bind it as a range. The GPU
// never reads bytes the CPU is writing, so the driver has no
// write-after-read hazard to resolve — the in-place glBufferSubData
// below forced Adreno into a ghost/stall on every uniform-dirtying
// draw (MC dirties uniforms every draw), which dominated frame time.
Bool ringBound = false;
if (BufferImpl::UboRingAvailable()) {
const SizeT bindSize =
std::max(uboSize, static_cast<SizeT>(backendProgram.GetGlobalUboBackendBlockSize()));
const Uint64 frameSerial = CurrentFrameSerial();
auto& ringSlot = backendProgram.GetGlobalUboRingAllocation();
Bool slotValid = ringSlot.ringGeneration == BufferImpl::UboRingGeneration() &&
ringSlot.frameSerial == frameSerial &&
ringSlot.contentVersion == uboContentVersion;
if (!slotValid) {
SizeT offset = 0;
if (BufferImpl::UboRingAllocate(bindSize, offset)) {
std::memcpy(static_cast<Uint8*>(BufferImpl::UboRingMappedPtr()) + offset,
currentProgram->MapUBO(), uboSize);
ringSlot = {uboContentVersion, BufferImpl::UboRingGeneration(), frameSerial,
offset};
slotValid = true;
}
}
if (slotValid) {
BufferImpl::BindBufferRangeCached(GL_UNIFORM_BUFFER, 0, BufferImpl::UboRingBufferId(),
static_cast<GLintptr>(ringSlot.offset),
static_cast<GLsizeiptr>(bindSize));
ringBound = true;
}
}
if (!ringBound) {
// Fallback (no EXT_buffer_storage / fences, or ring creation
// failed): the original in-place upload.
if (backendProgram.GetLastUploadedGlobalUboVersion() != uboContentVersion) {
g_GLESFuncs.glBindBuffer(GL_UNIFORM_BUFFER, backendProgram.GetBackendGlobalUBOId());
g_GLESFuncs.glBufferSubData(GL_UNIFORM_BUFFER, 0, currentProgram->GetUBOSize(),
currentProgram->MapUBO());
g_GLESFuncs.glBindBuffer(GL_UNIFORM_BUFFER, 0);
backendProgram.SetLastUploadedGlobalUboVersion(uboContentVersion);
}
BufferImpl::BindBufferBaseCached(GL_UNIFORM_BUFFER, 0,
backendProgram.GetBackendGlobalUBOId());
}
BufferImpl::BindBufferBaseCached(GL_UNIFORM_BUFFER, 0, backendProgram.GetBackendGlobalUBOId());
}
{
@@ -4129,6 +4167,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
g_completedFrameSerial.store(completed, std::memory_order_relaxed);
}
// After the watermark advanced: retire grown-away UBO-ring stores and record
// the frame's ring high-water mark for slot reclamation.
BufferImpl::UboRingOnPresent();
BufferImpl::TrimBufferPool();
}