[Fix] (MG_Backend/DirectGLES, MG_Impl/GLImpl): draw/read-buffer state could land on the wrong FBO (glDrawBuffer's static-array latch; SyncToBackend emitting glDrawBuffers/glReadBuffer for the non-bound target; no resync on bound-FBO attachment/drawbuffer edits) leaving MC 26.3's OIT color clears as no-ops; apply per bound target and track the FBO object version

This commit is contained in:
2026-07-17 11:45:54 -04:00
parent c9fbf79d6a
commit c30bd0fabb
5 changed files with 71 additions and 8 deletions
+40 -4
View File
@@ -503,11 +503,22 @@ namespace MobileGL::MG_Backend::DirectGLES {
for (auto& target : fboTargets) {
auto& slot = MG_State::pGLContext->GetFramebufferBindingSlot(target);
auto version = slot.GetVersion();
if (version == g_fboBindVersions[SizeT(target)]) continue;
auto& currentFBO = slot.GetBoundObject();
// The slot version only tracks rebinds; attachment/drawbuffer edits on an
// already-bound FBO bump its object version and must re-sync it too (e.g.
// Minecraft 26.x reuses one FBO for depth-blit destinations with draw
// buffers NONE and for color clears with draw buffer 0 — dropping the
// glDrawBuffers change turns every offscreen clear into a no-op).
const Uint16 slotVersion = slot.GetVersion();
const Uint16 objectVersion = currentFBO ? currentFBO->GetObjectVersion() : 0;
auto* currentPtr = currentFBO.get();
if (slotVersion == g_fboBindVersions[SizeT(target)] &&
objectVersion == g_fboSyncedObjectVersions[SizeT(target)] &&
currentPtr == g_fboSyncedObjects[SizeT(target)]) {
continue;
}
if (!currentFBO) {
MGLOG_E("No FBO is currently bound, cannot sync current FBO.");
continue;
@@ -520,6 +531,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
if (currentFBO.get() == lastUpdatedFBO) {
MGLOG_D("Draw FBO and read FBO are the same, skipping sync.");
g_fboSyncedObjectVersions[SizeT(target)] = objectVersion;
g_fboSyncedObjects[SizeT(target)] = currentPtr;
continue;
}
@@ -531,6 +544,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
}
backendObj->SyncToBackend(currentFBO, target);
g_fboSyncedObjectVersions[SizeT(target)] = objectVersion;
g_fboSyncedObjects[SizeT(target)] = currentPtr;
lastUpdatedFBO = currentFBO.get();
}
}
@@ -1011,8 +1026,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif
auto& slot = MG_State::pGLContext->GetFramebufferBindingSlot(target);
SyncAndBindFramebufferObject(slot.GetBoundObject(), target);
const auto& fbo = slot.GetBoundObject();
SyncAndBindFramebufferObject(fbo, target);
FramebufferImpl::g_fboBindVersions[(SizeT)target] = slot.GetVersion();
FramebufferImpl::g_fboSyncedObjectVersions[(SizeT)target] = fbo ? fbo->GetObjectVersion() : 0;
FramebufferImpl::g_fboSyncedObjects[(SizeT)target] = fbo.get();
}
static void BindCurrentProgramWithResources();
@@ -1860,6 +1878,24 @@ namespace MobileGL::MG_Backend::DirectGLES {
MGLOG_D("ES %s(%d, %d, %d, %d, %d, %d, %d, %d, 0x%x, %s)", __func__, srcX0, srcY0, srcX1, srcY1,
dstX0, dstY0, dstX1, dstY1, mask, MG_Util::ConvertGLEnumToString(filter).c_str());
g_GLESFuncs.glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
// Debug-only diagnostics: which GLES depth texture did this blit write?
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG
if (mask & GL_DEPTH_BUFFER_BIT) {
static int diagCount = 0;
if ((diagCount++ % 600) < 4) {
GLint readFbo = 0, drawFbo = 0, readDepth = 0, drawDepth = 0;
g_GLESFuncs.glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &readFbo);
g_GLESFuncs.glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &drawFbo);
g_GLESFuncs.glGetFramebufferAttachmentParameteriv(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &readDepth);
g_GLESFuncs.glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &drawDepth);
MGLOG_D("DBLIT readFbo=%d(depth=%d) -> drawFbo=%d(depth=%d) rect=(%d,%d,%d,%d)->(%d,%d,%d,%d)",
readFbo, readDepth, drawFbo, drawDepth, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1,
dstY1);
}
}
#endif
DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__](auto err) {
MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str());
});
+22 -3
View File
@@ -2491,7 +2491,14 @@ namespace MobileGL::MG_Backend::DirectGLES {
drawBufferClean = true;
}
if (!drawBufferClean) {
// glDrawBuffers writes the state of the FBO bound to GL_DRAW_FRAMEBUFFER.
// When this object is only bound as the READ target the call would land on
// whatever framebuffer is draw-bound AND falsely stamp this object's memo,
// so the later draw-target sync skips as "clean" while the real state is
// stale (Minecraft 26.x OIT: the scratch clear-FBO kept draw buffers NONE
// from its blit-destination configuration, silently dropping every
// offscreen color clear).
if (!drawBufferClean && asTarget == FramebufferTarget::Draw) {
memcpy(m_frontendDrawBuffers, stateDrawBuffers.data(),
FramebufferObject::MAX_DRAW_BUFFERS * sizeof(FramebufferAttachmentType));
std::fill(m_backendDrawBuffers, m_backendDrawBuffers + FramebufferObject::MAX_DRAW_BUFFERS, GL_NONE);
@@ -2516,6 +2523,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
nEffectiveBuffers = i + 1;
}
g_GLESFuncs.glDrawBuffers(nEffectiveBuffers, m_backendDrawBuffers);
MGLOG_D("DBAPPLY beFbo=%u target=%d n=%d db0=0x%x feDb0=%d", m_backendFBOId, (int)asTarget,
nEffectiveBuffers, m_backendDrawBuffers[0], (int)stateDrawBuffers[0]);
}
if (asTarget == FramebufferTarget::Draw) {
@@ -2538,9 +2547,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
PrgramImpl::g_unormFallbackClampOutputMask = unormClampOutputMask;
}
// 2. Remap read buffer
// 2. Remap read buffer. glReadBuffer writes the READ-bound FBO's state, so
// only apply (and stamp the memo) when this object is bound as READ.
auto frontendReadBuf = stateFBOObject->GetReadBuffer();
if (frontendReadBuf != m_frontendReadBuffer) {
if (frontendReadBuf != m_frontendReadBuffer && asTarget == FramebufferTarget::Read) {
m_frontendReadBuffer = frontendReadBuf;
GLenum glBackendReadBuffer = GetBackendAttachmentType(frontendReadBuf);
@@ -2647,6 +2657,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
StateBackendObjectRegistry<MG_State::GLState::FramebufferObject, BackendFramebufferObject>
g_backendFramebufferObjects;
Array<Uint16, SizeT(FramebufferTarget::FramebufferTargetCount)> g_fboBindVersions = {0};
// Tracks the bound FBO's object version (bumped on any attachment/drawbuffer change)
// per target: re-attaching textures or changing draw buffers on an already-bound FBO
// must re-sync it even when the binding-slot version has not moved.
Array<Uint16, SizeT(FramebufferTarget::FramebufferTargetCount)> g_fboSyncedObjectVersions = {0};
Array<MG_State::GLState::FramebufferObject*, SizeT(FramebufferTarget::FramebufferTargetCount)>
g_fboSyncedObjects = {};
} // namespace FramebufferImpl
namespace PrgramImpl {
@@ -2935,6 +2951,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
}
m_uniformBlockBackendIndices[static_cast<SizeT>(i)] = static_cast<Int>(backendBlkIdx);
g_GLESFuncs.glUniformBlockBinding(m_backendProgramId, backendBlkIdx, lastUBOBinding);
MGLOG_D("CACHE prog=%u beProg=%u blk[%d]='%s' beIdx=%u -> bePoint=%u",
stateProgramObject->GetExternalIndex(), m_backendProgramId, i, name.c_str(), backendBlkIdx,
lastUBOBinding);
}
m_samplerUniformBindings.clear();
@@ -408,6 +408,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
extern StateBackendObjectRegistry<MG_State::GLState::FramebufferObject, BackendFramebufferObject>
g_backendFramebufferObjects;
extern Array<Uint16, SizeT(FramebufferTarget::FramebufferTargetCount)> g_fboBindVersions;
// Tracks the bound FBO's object version (bumped on any attachment/drawbuffer change)
// per target: re-attaching textures or changing draw buffers on an already-bound FBO
// must re-sync it even when the binding-slot version has not moved.
extern Array<Uint16, SizeT(FramebufferTarget::FramebufferTargetCount)> g_fboSyncedObjectVersions;
extern Array<MG_State::GLState::FramebufferObject*, SizeT(FramebufferTarget::FramebufferTargetCount)>
g_fboSyncedObjects;
} // namespace FramebufferImpl
// Image uniforms take their unit from the layout(binding=N) qualifier baked into
@@ -1177,7 +1177,7 @@ namespace MobileGL::MG_Impl::GLImpl {
auto& bindingSlot = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw);
auto& fbo = bindingSlot.GetBoundObject();
const bool isDefaultFBO = (fbo == FramebufferImpl::pDefaultFramebufferInfo->defaultFBO);
static GLenum bufs[] = {buf};
const GLenum bufs[] = {buf};
DrawBuffersForFramebuffer_State(fbo, isDefaultFBO, 1, bufs, true);
}
}
@@ -1406,6 +1406,7 @@ namespace MobileGL::MG_Impl::GLImpl {
}
const auto& index = programObject->GetUniformBlockIndex(uniformBlockName);
MGLOG_D("GBI prog=%u name='%s' -> %d", program, uniformBlockName ? uniformBlockName : "(null)", (Int)index);
return index;
}
@@ -1429,6 +1430,7 @@ namespace MobileGL::MG_Impl::GLImpl {
std::to_string(program) + "."));
return;
}
MGLOG_D("UBB prog=%u idx=%u binding=%u", program, uniformBlockIndex, uniformBlockBinding);
programObject->SetUniformBlockBinding(uniformBlockIndex, uniformBlockBinding);
}