Repository navigation
Restore texture and framebuffer bindings after invalidation - #2994
toaster0123 wants to merge 3 commits into
Conversation
|
Nice work — the diagnosis is right: resetting the cache to "unit 0 / main framebuffer" without restoring the native binding is exactly the kind of thing that bites users after they embed another GL renderer. Moving "we don't know" into A few things I'd like tightened before this lands: 1. boundTextureUnit = -1;
frameBufferBindingValid = false;
boundFB = null; // cleared
// boundFBO keeps the old FBO name
boundFBO = 0;That means 2. The new public setter is a footgun of the same kind this PR fixes
public void setFrameBufferBinding(FrameBuffer fb, int fbo) {
this.boundFB = fb;
this.boundFBO = fbo;
this.frameBufferBindingValid = true;
}Symmetry point: 3. Quick check: does Only 4. sRGB follow-up, understood as out of scope With an unknown binding, both toggles in 5. Small test nit that would have real value The recording driver already guards against bogus framebuffer IDs: assertTrue(framebuffer >= 0, "Unknown framebuffer ID passed to GL");Adding the same check for the active unit closes the symmetric hole and turns a leaked if (name.equals("glActiveTexture")) {
int unit = (Integer) args[0] - GL.GL_TEXTURE0;
assertTrue(unit >= 0, "Unknown texture unit passed to GL");
activeUnit = unit;
}Overall the approach looks right and the control set (fresh defaults, repeated invalidation, explicit targets, nonzero presentation target, copies with separate read/draw bindings) is solid. |
jaime-jmebot
left a comment
There was a problem hiding this comment.
- The root cause and the direction of the fix look right: carrying "unknown" binding knowledge in
RenderContextand only eliding a bind when the cache is genuinely valid is the clean way to solve it. - Fresh-context defaults and repeated-call elision are preserved, and the control set around them (explicit targets, nonzero presentation target, copies with separate read/draw bindings) gives good confidence there's no extra-bind regression.
- Small hygiene items before merge: clear
boundFBOalongsideboundFBininvalidateBindings(), consider replacingsetFrameBufferBindingValid(boolean)with a single call that records framebuffer + name + validity together, and confirmreset()clears the validity flag too. Details and a suggested test guard are in my comment above. - sRGB after an unknown binding is intentionally left out of scope — just want the "is the cached
mainFrameBufferSrgbre-applied on the next main-target bind?" question tracked.
|
@jaime-jmebot Addressed the recording-helper and test suggestions in 809b988.
Validation on this exact revision: 49 focused tests, 610 scoped module tests (two existing skips), 662 composed core tests (one skip), style/Javadoc, renewed independent review, and native Mesa state/pixel controls pass. Unchanged selections still elide calls. Standalone nonzero-default initialization can make one conservative initial bind; the #2984 composition already aligns that cache. Current-head CI still awaits maintainer approval: Build 37032810713 and Android 37032810846. I am not treating earlier workflow status as validation of this revision. |
jaime-jmebot
left a comment
There was a problem hiding this comment.
Checked 809b988 against the points from my earlier comment — all substantive ones are addressed, and I agree with the reasoning on the rest:
init()(reached viareset()) now setsframeBufferBindingValid = truenext to theboundFB/boundFBOdefaults, so a reset context is a genuinely known one and no stale "valid but no Java association" state can survive a reset.- The two-value boolean setter is gone in favour of a single method that records object, native name and validity together, plus a framebuffer-only invalidator so copy restore keeps texture knowledge. That removes the "declare it valid without a bind" footgun.
- I accept your call on
boundFBOstaying untrusted while invalid. A nullboundFBgenuinely can coexist with a nonzero native name (nonzero presentation target, externally managed FBO), so zeroing the int wouldn't make ignoring the validity flag safe — it would just be a different lie. The documented "untrusted" contract plus tests is the honest version of that. - The recording driver now rejects a negative active unit, which is exactly the symmetric guard I wanted.
No further code comments from me. Two non-blocking follow-ups worth tracking when there's time: whether the cached mainFrameBufferSrgb gets re-applied when the main target is bound again, and the one conservative initial bind on standalone nonzero-default init that only disappears once composed with #2984. Nice work on the control set — fresh defaults and unchanged-selection elision are both still pinned.
Problem
After another OpenGL renderer finishes, an application can call
invalidateState()as documented and still get the wrong texture or render target on the next jME operation.Two concrete cases:
setTexture(0, greenTexture)binds the green texture on unit 3, while a shader sampling unit 0 still sees its old red texturesetFrameBuffer(null)returns without selecting the main framebuffer, so the following clear and draw affect the external target. This also happens when the platform's main framebuffer has a nonzero nameThe cache was reset to unit 0 and a null/default framebuffer without restoring those native bindings. Matching those assumed values was then treated as a reason to skip the required GL calls.
Fix
Keep unknown binding knowledge in
RenderContext:The other consumers of this cache are handled too. A copy made while the binding is unknown snapshots native read/draw targets before attachment updates, restores them separately, and leaves the cache unknown. Known copies keep their existing query-free path. Known framebuffer metadata is recorded together; framebuffer-only invalidation preserves texture knowledge. Override, deletion and immediate main-target configuration decisions do not trust an unknown cached value or mistake an unrelated native target for the main framebuffer.
Validation
This branch is based directly on current upstream. A separate tested composition includes #2983 (
e7c87ee), #2984 (f1646c1) and #2993 (4c89905); it preserves their storage, lifecycle, nonzero-default and render-state changes. Composition requires retainingcontext.invalidate()followed byinvalidateBindings(), combining the validity guards with #2984's default-target handling, and keeping #2983's mipmap handling inside #2984's attachment-update logic.Scope and limits
On standalone nonzero-default initialization, the stricter native-name check can add one conservative initial bind because the baseline cache still starts at zero. Repeated selections remain elided. The #2984 composition already synchronizes that initial native name.
This fixes binding selection, not external texture/attachment mutation or every OpenGL state category. General sRGB-state invalidation and exception-time copy restoration are outside this patch. The copy tests verify safe use of unknown bindings; the public API does not promise preservation of arbitrary external state.
Native evidence uses Mesa llvmpipe through a test-only JNA bridge, not every GPU or a production backend. The whole-repository local build is blocked by the missing Android SDK. The completed scoped checks above do not claim that whole-repository build passed.