Skip to content

Add intermediate framebuffer for gamma correction to prevent flickering#7589

Merged
BMagnu merged 4 commits into
scp-fs2open:masterfrom
The-E:fix/flickering-in-briefing
Jul 14, 2026
Merged

Add intermediate framebuffer for gamma correction to prevent flickering#7589
BMagnu merged 4 commits into
scp-fs2open:masterfrom
The-E:fix/flickering-in-briefing

Conversation

@The-E

@The-E The-E commented Jul 9, 2026

Copy link
Copy Markdown
Member

Summary

cc63bfa7 ("Gamma by Shader (#7484)") introduced a shader-based gamma-correction pass that samples Back_texture (the just-rendered scene) and draws the corrected result directly into the window-system default framebuffer. This caused two SCPUI regressions, both fixed by this PR:

  1. Flicker on state transitions (e.g. loading screen → briefing): on some drivers, presenting straight from a shader draw that samples a just-rendered texture isn't reliably synchronized with the swap chain, causing the screen to flicker between the outgoing and incoming screen's content for several frames.
  2. Frozen briefing screen (Briefing screen (SCPUI) stops updating except while hovering a briefing icon #7590): the briefing stops visually updating (camera pan, stage text, map) except while hovering a briefing icon.

Fixes

Flicker: Route the gamma pass through an intermediate offscreen framebuffer/texture (GammaBlit_framebuffer / GammaBlit_texture), matching how every other post-process pass in the renderer works (FBO → FBO draw). The corrected result is then handed to the screen via glBlitFramebuffer, the same mechanism used for presentation before the gamma shader was introduced. This keeps gamma correction intact while removing the flicker.

Freeze: A masked draw pass inside the briefing map render leaves glColorMask fully disabled, and the Rocket UI material path never restores it, so the mask stays disabled into gr_opengl_flip. Before cc63bfa7 this was harmless — presentation was a pure glBlitFramebuffer, which ignores the color mask. The gamma pass turned presentation into a regular draw, which respects the mask, so every flip wrote nothing and the screen froze on stale swapchain content while the engine kept rendering correctly underneath. (Hovering a briefing icon appends draws whose materials re-enable color writes, which is why exactly those frames presented correctly.) The flip now neutralizes color mask and stencil state before the gamma draw, matching the blend/depth neutralization already done there. Full analysis in #7590.

Changes

  • code/graphics/opengl/gropengldraw.cpp / .h: add GammaBlit_framebuffer, created alongside the existing Back_framebuffer in opengl_setup_scene_textures() and torn down in opengl_scene_texture_shutdown(). It normally reuses Scene_ldr_texture as its color attachment (dead scratch by flip time — all its consumers write before reading within gr_opengl_post_process_end()), avoiding a new screen-sized allocation; a dedicated GammaBlit_texture is only allocated in the edge case where the scene textures were clamped to GL_max_renderbuffer_size.
  • code/graphics/opengl/gropengl.cpp: gr_opengl_flip() now draws the gamma-correction shader into GammaBlit_framebuffer (sampling Back_texture as before), then presents via glBlitFramebuffer into GL_BACK instead of drawing directly to the default framebuffer; it also resets color mask and stencil state before the gamma draw. When the effective gamma is 1.0 the shader pass is an identity transform and is skipped entirely, blitting Back_framebuffer straight to GL_BACK.

Testing

  • Reproduced the flicker on loading screen → briefing and other state transitions before the fix; verified flicker-free transitions after, with gamma correction still visibly applied (gamma slider in options still affects brightness).
  • Reproduced the briefing freeze and root-caused it with per-frame GL state/pixel probes (see Briefing screen (SCPUI) stops updating except while hovering a briefing icon #7590); verified after the fix that camera pans, stage text, and map animations update normally without hovering.
  • Bisected to confirm cc63bfa7 is the sole origin of both regressions; confirmed neither occurs on its parent commit.

Fixes #7590

🤖 Generated with Claude Code

@The-E The-E force-pushed the fix/flickering-in-briefing branch from 168859c to 99c3a28 Compare July 9, 2026 16:52
@wookieejedi wookieejedi added cleanup A modification or rewrite of code to make it more understandable or easier to maintain. graphics A feature or issue related to graphics (2d and 3d) labels Jul 12, 2026
The-E and others added 2 commits July 14, 2026 07:17
… pass

A masked draw pass inside the briefing map render leaves the GL color
mask fully disabled, and the Rocket UI material path never restores it.
Before cc63bfa this didn't matter: the frame was presented with a pure
glBlitFramebuffer, which ignores the color mask. The gamma-by-shader
change turned presentation into a regular draw, which respects the
mask, so every flip wrote nothing and the screen froze on stale
swapchain content while the engine kept rendering correctly underneath.
Hovering a briefing icon appended draws whose materials re-enabled
color writes, which is why the screen only updated while hovering.

Neutralize color mask and stencil state before the gamma draw, matching
the blend and depth-buffer neutralization already done there.

Fixes scp-fs2open#7590

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@The-E The-E force-pushed the fix/flickering-in-briefing branch from 99c3a28 to d4d0310 Compare July 14, 2026 06:16
The-E and others added 2 commits July 14, 2026 08:21
The gamma shader is an identity transform at gamma 1.0, so present
Back_framebuffer directly via glBlitFramebuffer in that case (the same
present path used before the gamma shader was introduced), saving a
fullscreen draw per frame. The shader pass through GammaBlit_framebuffer
only runs when the effective gamma actually differs from 1.0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Scene_ldr_texture is dead scratch by flip time: all of its consumers
(tonemap, FXAA/SMAA, the final post-process pass) write before reading
within gr_opengl_post_process_end(), which completes before the flip,
and the gamma pass fully overwrites it before blitting it to the
window. Attaching it to GammaBlit_framebuffer instead of allocating a
dedicated texture saves a screen-sized RGBA8 allocation (~8 MB at
1080p, ~33 MB at 4K).

The dedicated allocation remains as a fallback for the case where the
scene textures were clamped to GL_max_renderbuffer_size and don't cover
the screen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@BMagnu BMagnu enabled auto-merge (squash) July 14, 2026 07:09
@BMagnu BMagnu merged commit 583192b into scp-fs2open:master Jul 14, 2026
18 checks passed
@The-E The-E deleted the fix/flickering-in-briefing branch July 14, 2026 07:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cleanup A modification or rewrite of code to make it more understandable or easier to maintain. graphics A feature or issue related to graphics (2d and 3d)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Briefing screen (SCPUI) stops updating except while hovering a briefing icon

3 participants