[Fix] (DirectGLES): never skip a framebuffer bind on a stale version snapshot

BindCurrentFBO returned early when the framebuffer binding slot's version matched
g_fboBindVersions - but nothing on that path ever writes that entry. Only
ForceBindCurrentFBO stamps it, so the comparison was against an arbitrarily old
snapshot, and any later slot version that happened to land on the same 16-bit value
read as "already bound". The driver was then left on whatever framebuffer it had
last been given.

That is how KHR-GL32.packed_pixels.varied_rectangle.rg8i_format_rg_integer read its
gradient back out of the previous subtest's framebuffer, seeing 18 where 127 was
expected. It only shows up after a few thousand cases have gone by - long enough for
the counter to come back around - which is why it reproduced exactly under one
caselist and not at all in isolation.

Drop the fast path. Skipping redundant work is BindFramebufferId's job: it shadows
the driver's own draw and read bindings and drops the glBindFramebuffer when the
target already holds that id, which is where the cost actually is. What is left here
is one registry lookup.

Takes GL32 to 100% conformance; GL30, GL31 and GL33 stay at 100%.
This commit is contained in:
BZLZHH
2026-08-02 07:24:59 -04:00
parent 13bab780f2
commit 7105c2ebdc
+10 -2
View File
@@ -1196,8 +1196,16 @@ namespace MobileGL::MG_Backend::DirectGLES {
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif
auto& slot = MG_State::pGLContext->GetFramebufferBindingSlot(target);
if (slot.GetVersion() == FramebufferImpl::g_fboBindVersions[(SizeT)target]) return;
// No fast path on the binding slot's version. It is a 16-bit counter that only
// ForceBindCurrentFBO ever stamps here, so the comparison was against an arbitrarily old
// snapshot and any later slot version that happened to land on it - one wrap of the
// counter, or simply enough rebinds - read as "already bound" and left the driver on a
// completely different framebuffer. KHR-GL32.packed_pixels then read its gradient back
// out of the previous subtest's framebuffer.
//
// Skipping the work is BindFramebufferId's job anyway: it shadows the driver's own
// draw/read bindings and drops the glBindFramebuffer when the target already holds the id,
// which is where the cost actually is. What is left here is one registry lookup.
const auto& currentFBO = slot.GetBoundObject();
if (currentFBO && currentFBO != MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO) {
const auto& backendFBOIt = FramebufferImpl::g_backendFramebufferObjects.find(currentFBO.get());