Repository navigation
Reset texture and framebuffer bindings immediately on invalidation - #2994
riccardobl merged 7 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.
Preserve immediate render-state reset, framebuffer lifecycle fixes and the GLES MSAA resolve path. Restore unknown read/draw bindings around public attachment updates, including failure and matching stale-name cases. Keep nonzero presentation framebuffers and unsupported-FBO validation correct, and cover the integration with regression tests.
Application.destroy() runs before native context teardown and SDL subsystem shutdown. Join the render thread in finally so subsequent scenarios and screenshot tests cannot initialize SDL while the previous context is being released. Keep the existing rendering and MSAA assertions intact.
Follow the eager-reset approach of jMonkeyEngine#2993: select texture unit zero and the platform framebuffer, force its configured sRGB write state, and synchronize the ordinary cache. Remove unknown-binding flags, sentinel handling and restoration queries. Preserve main overrides without allocating them during cleanup or context reset. Update regression tests and document the new reset contract.
jaime-jmebot
left a comment
There was a problem hiding this comment.
The revised implementation restores native texture and framebuffer defaults immediately, while keeping the documented override and cleanup behavior intact.
- The binding-state machinery from the intermediate revision is removed; invalidation and cached state now align with the stated contract.
- Focused coverage exercises invalidation, nonzero default framebuffers, texture operations, and copy/attachment restoration.
- I found no additional blocking issues in this pass.
Problem
Renderer.invalidateState()must not reset the Java cache to assumed defaults while leaving the corresponding native bindings unchanged. In particular, an external renderer can leave texture unit 3 active, so a subsequent jME texture operation on unit 0 affects the wrong unit. Framebuffer selection and temporary attachment/copy operations must also have a consistent binding after invalidation.Change
This revision follows the immediate-reset approach merged in #2993 instead of introducing unknown-binding state:
GLRenderer.invalidateState()unconditionally selects texture unit zero and, when framebuffer objects are supported, binds the platform'sdefaultFBOfor both reading and drawing. The default can be nonzero.RenderContextand record the same native defaults. Texture references are forgotten so textures are rebound when used; there is no native loop unbinding every texture unit.RenderContextnow matches the base branch exactly.The existing #2993 draw-state reset remains unchanged. The previous SDL screenshot-test teardown fix is retained.
Contract and lifecycle
Invalidation now immediately replaces external read/draw framebuffer bindings with the platform default; it does not preserve those external bindings. A configured main-framebuffer override is retained, but is not selected or allocated by invalidation. This is important because
cleanup()andresetGLObjects()also call invalidation and must not recreate resources they just deleted/reset. CallsetFrameBuffer(null)to select a configured override before resuming rendering, or explicitly select another target.The public API documentation describes this contract and requires a current graphics context on the rendering thread. Viewport/scissor resets, external mutation of texture/attachment contents, and unrelated OpenGL state categories are outside this change.
Cost and compatibility
At invalidation: one active-texture call, one framebuffer bind when supported, and one sRGB enable/disable when supported. The existing two desktop draw/read-buffer-selection queries are retained and run after binding the platform framebuffer. No new binding queries are introduced, including in copies and attachment updates after invalidation. Repeated texture/framebuffer operations retain ordinary cached call elision.
Public/protected
GLRenderersignatures were compared usingjavapand are unchanged from the base.RenderContextis source-identical to the base, andRendererchanges only documentation.Validation
Implementation commit:
db744371d0b75dcc4ae52a86f962c3d2bd707c5e. Heade436c07e8be6112966829e877d0a49eb7b2644a7only wraps a test method signature for Checkstyle; full unit suites and test Checkstyle were rerun successfully on that head.GLRenderer(8abcb3ee) produce 39 failures and 26 passing controls; all 65 pass with this implementation.--release 8and the focused tests run through JUnit.A local native
TestPresentationMsaaattempt could not create the SDL window (Could not get EGL display), before renderer initialization; the devbox's standaloneglxinfoalso cannot find a GLX visual. This is not counted as a passing native test. Backend rendering validation belongs to the new CI runs: Build jMonkeyEngine 41053318909 and Android Screenshot Test 41053318578. Read their current status rather than relying on the previous revision's green runs.