Skip to content

#14714 Release offscreen FBO OpenGL resources on context loss/recreation - #14715

Closed
magnesj wants to merge 2 commits into
OPM:devfrom
magnesj:14714-fbo-context-loss
Closed

#14714 Release offscreen FBO OpenGL resources on context loss/recreation#14715
magnesj wants to merge 2 commits into
OPM:devfrom
magnesj:14714-fbo-context-loss

Conversation

@magnesj

@magnesj magnesj commented Sep 9, 2026

Copy link
Copy Markdown
Member

Fixes #14714.

Problem

caf::Viewer relied on a QOpenGLContext::aboutToBeDestroyed connection to release the offscreen FBO's OpenGL resources, but the connection was dead code:

  • Slot ordering: OpenGLWidget's own aboutToBeDestroyed slot ran first and nulled the cvf OpenGL context, so deleteFboOpenGLResources() became a no-op.
  • One-shot connection: the connection was only ever made once (onWidgetOpenGLReady()), so it did not fire again after a context was recreated (e.g. when a dock widget is reparented across top-level windows, which Qt::AA_ShareOpenGLContexts enables).

As a result m_offscreenFbo kept OpenGL ids belonging to a destroyed context. FramebufferObject::applyOpenGL() only regenerates ids that are still 0, so a stale non-zero id caused glBindFramebuffer to silently create a fresh, attachment-less framebuffer. Attachments are only reapplied when the viewport size changes, so a view re-docked at an unchanged size stayed incomplete and rendered black — this is the reproducible symptom in #14708 (tiling two ensemble contour maps at the same size blacks one out), and likely the underlying cause of the RHEL8 completely-black-view reports.

Fix

Added explicit, well-documented lifecycle hooks to cvfqt::OpenGLWidget:

  • onWidgetOpenGLAboutToBeShutdown() — called before context teardown, with the old context still current.
  • onWidgetOpenGLReinitialized() — called from initializeGL() on reinit, with the new context current (replaces the bare update() call).

caf::Viewer overrides both to release/reset the offscreen FBO's cached OpenGL resources, so they get rebuilt correctly in the new context regardless of which lifecycle event actually fires on a given platform.

…eation

caf::Viewer relied on a QOpenGLContext::aboutToBeDestroyed connection to
release the offscreen FBO's OpenGL resources, but the connection was
dead code: OpenGLWidget's own aboutToBeDestroyed slot ran first and
nulled the cvf OpenGL context, so deleteFboOpenGLResources() became a
no-op. The connection was also only ever made once, so it did not fire
again after a context was recreated (e.g. when a dock widget was
reparented).

As a result m_offscreenFbo kept OpenGL ids belonging to a destroyed
context. FramebufferObject::applyOpenGL() only regenerates ids that are
still 0, so a stale non-zero id caused glBindFramebuffer to silently
create a fresh, attachment-less framebuffer, which rendered black until
a size change forced the attachments to be reapplied.

Add explicit onWidgetOpenGLAboutToBeShutdown()/onWidgetOpenGLReinitialized()
hooks to cvfqt::OpenGLWidget, called with a well-defined current OpenGL
context (old context before teardown, new context after
reinitialization), and override them in caf::Viewer to release/reset
the offscreen FBO's cached OpenGL resources in both directions.
Review follow-up on the offscreen FBO context loss fix.

makeCurrent() silently does nothing when the widget surface is already
gone, which is exactly what happens during the teardown this code
targets. Issuing OpenGL calls in that case dereferences a NULL current
context, or worse, deletes framebuffer names in whatever context happens
to be current. Only notify derived classes when the context really did
become current.

FramebufferObject::deleteFramebuffer() cleared the color attachment
version ticks while the attachments themselves were kept, so the next
applyOpenGL() indexed an empty vector. Reset the ticks instead, keeping
the array in sync with the attachment count.

The offscreen textures and renderbuffers live in the shared context
group (AA_ShareOpenGLContexts) and outlive the widget context, but
onWidgetOpenGLAboutToBeShutdown() is never called during widget
destruction since the base class disconnects from aboutToBeDestroyed
first. Release them in the Viewer destructor, where the derived object
and the Qt context are both still alive.

Framebuffer, texture and renderbuffer names are per context, so ids
minted in a destroyed context must be dropped rather than deleted
through the new one. Add forgetCurrentOpenGLResources() to
FramebufferObject and use it when reinitializing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B5zb3uXFHUva7Wrb7zZBp1
@magnesj magnesj closed this Sep 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Offscreen FBO is never released on context loss (likely cause of black 3D view / #14708)

1 participant