diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index 0f52966f..db2f1a6d 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -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()); }); diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index 18868b8e..f5d9b79c 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -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 g_backendFramebufferObjects; Array 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 g_fboSyncedObjectVersions = {0}; + Array + g_fboSyncedObjects = {}; } // namespace FramebufferImpl namespace PrgramImpl { @@ -2935,6 +2951,9 @@ namespace MobileGL::MG_Backend::DirectGLES { } m_uniformBlockBackendIndices[static_cast(i)] = static_cast(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(); diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.h b/MobileGL/MG_Backend/DirectGLES/Managers.h index 3e1e9b0a..9210387c 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.h +++ b/MobileGL/MG_Backend/DirectGLES/Managers.h @@ -408,6 +408,12 @@ namespace MobileGL::MG_Backend::DirectGLES { extern StateBackendObjectRegistry g_backendFramebufferObjects; extern Array 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 g_fboSyncedObjectVersions; + extern Array + g_fboSyncedObjects; } // namespace FramebufferImpl // Image uniforms take their unit from the layout(binding=N) qualifier baked into diff --git a/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp b/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp index a04beafd..30a955d7 100644 --- a/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp +++ b/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp @@ -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); } } diff --git a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp index cc36b65f..9a2a0ee3 100644 --- a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp +++ b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp @@ -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); }