Add intermediate framebuffer for gamma correction to prevent flickering#7589
Merged
Conversation
168859c to
99c3a28
Compare
… 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>
99c3a28 to
d4d0310
Compare
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
approved these changes
Jul 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
cc63bfa7("Gamma by Shader (#7484)") introduced a shader-based gamma-correction pass that samplesBack_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: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 viaglBlitFramebuffer, 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
glColorMaskfully disabled, and the Rocket UI material path never restores it, so the mask stays disabled intogr_opengl_flip. Beforecc63bfa7this was harmless — presentation was a pureglBlitFramebuffer, 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: addGammaBlit_framebuffer, created alongside the existingBack_framebufferinopengl_setup_scene_textures()and torn down inopengl_scene_texture_shutdown(). It normally reusesScene_ldr_textureas its color attachment (dead scratch by flip time — all its consumers write before reading withingr_opengl_post_process_end()), avoiding a new screen-sized allocation; a dedicatedGammaBlit_textureis only allocated in the edge case where the scene textures were clamped toGL_max_renderbuffer_size.code/graphics/opengl/gropengl.cpp:gr_opengl_flip()now draws the gamma-correction shader intoGammaBlit_framebuffer(samplingBack_textureas before), then presents viaglBlitFramebufferintoGL_BACKinstead 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, blittingBack_framebufferstraight toGL_BACK.Testing
cc63bfa7is the sole origin of both regressions; confirmed neither occurs on its parent commit.Fixes #7590
🤖 Generated with Claude Code