Skip to content

Prevent leaked menu soft-focus blur from rendering during live gameplay - #172

Closed
jm2 wants to merge 1 commit into
themuffinator:mainfrom
jm2:fix/join-soft-focus-leak
Closed

jm2 wants to merge 1 commit into
themuffinator:mainfrom
jm2:fix/join-soft-focus-leak

Conversation

@jm2

@jm2 jm2 commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #171

Problem

When navigating the main menu or join game screens, Quake 4 enables a cinematic soft-focus distance blur pass (SPECIAL_EFFECT_BLUR). In certain transitions into active gameplay (or after level disconnect/reconnect), the blur bitmask and distance blur parameters remained active in tr.specialEffectsEnabled, causing 3D world geometry to render permanently blurred with an unwanted depth-of-field effect during live gameplay.

Solution

  • Add explicit checks for join/menu soft-focus parameters (isJoinSoftFocus: low focus distance, large distance scale, high strength) across the render passes (RenderSystem, draw_common, ScenePackets, and vk_GuiExecutor).
  • Invalidate and strip SPECIAL_EFFECT_BLUR whenever active 3D gameplay is rendering (viewDef->renderView.viewID > 0 or !session->IsGUIActive()).
  • Add cvar r_specialEffects (default 1) and expand r_forceSpecialEffects (-1 to force all off) for diagnostics and user control.

Validation

  • Verified on macOS arm64 (Vulkan backend) and Windows.
  • Confirmed that entering live SP/MP gameplay completely clears the menu blur effect, leaving world rendering sharp.
  • Contract test suites pass cleanly.

Repository owner deleted a comment from chatgpt-codex-connector Bot Sep 21, 2026

@themuffinator themuffinator left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for digging into this — but I don't think this change does what the description says, and as written I believe it removes a feature rather than fixing a leak. Three things, in order of how much they matter.

1. viewID > 0 is every normal player view, including the join screen's own

idPlayer::CalculateRenderView sets renderView->viewID = entityNumber + 1 for the first-person view (openQ4-game/src/game/Player.cpp:13519 and :13539). So parms->renderView.viewID > 0 is true for ordinary gameplay and for the live map that the join card is deliberately drawn over. The join screen has no separate view — the panel is a GUI on top of the player's view.

That means the new guard strips SPECIAL_EFFECT_BLUR on the first frame the join card is up, which is exactly the soft focus the card is supposed to have. It looks like a fix because the effect does stop appearing, but the reason is that the feature has been switched off.

The arena presentation survives only by accident: idMultiplayerGame sets view->viewID = 0 for its presentation cameras (MultiplayerGame.cpp:14254, :14346), and its strength parm is clamped to 0.45, just under the >= 0.5f in the heuristic. A small retune of either would take the ceremony DoF out with it.

2. The parm test is a fingerprint of game-module constants, read from the engine

focus < 0.02f && distanceScale >= 256.0f && strength >= 0.5f is not a general description of "menu soft focus" — it is the numeric signature of four constants that live in the game DLL:

JOIN_SCREEN_DOF_FOCUS          = 0.004f
JOIN_SCREEN_DOF_STRENGTH       = 0.85f
JOIN_SCREEN_DOF_DISTANCE_SCALE = 512.0f

(openQ4-game/src/mpgame/MultiplayerGame.cpp:65-68). The engine has no way to know those numbers are special, and openQ4 ships its own game modules that are free to retune them. Anyone changing the join card's look would silently un-fix this, with no build error and no test failure.

3. The leak this targets is already guarded on the game side

idMultiplayerGame::Clear() and ::ClearMap() both call SetJoinScreenSoftFocus( false ) before resetting joinScreenSoftFocusEnabled, specifically so an interrupted handoff cannot leave the renderer's pass enabled on the next screen or map. HandleGuiCommands clears it when the player answers the offer. If you have found a path that escapes all of those, that path is the bug and it belongs in the game module's ownership bookkeeping — please post the repro and we will fix it there.

Smaller points

  • R_AddSpecialEffects now writes to tr.specialEffectsEnabled itself. That global is the game's state, not the view walk's; clearing it from inside a per-view function means the game and the renderer disagree afterwards, and SetJoinScreenSoftFocus( false ) becomes a no-op because its own joinScreenSoftFocusEnabled guard still reads true.
  • The same heuristic is copy-pasted into four places. If a rule like this is ever needed it should be one predicate, next to the state it describes.
  • r_specialEffects as a new archived cvar is reasonable on its own, and so is honouring r_skipPostProcess in these paths. Those parts I would take happily in a separate PR.

On the link to #171

The description here is about a menu blur leaking into gameplay, but #171 is titled "distant rendering very blurry/double pixelated" and its log is a fresh boot with no map and no multiplayer session, on the Apple GL 2.1 compatibility path. Those read like two different problems to me. The log also shows a Retina display reporting contentScale 1.00 with pd=2.00, which is the shape of a drawable-size mismatch — render at point size, present into a 2x pixel drawable — and that would look exactly like "double pixelated" softness in the distance.

I have asked on #171 for the detail that separates the two. If it turns out the join soft focus really is leaking on your machine, I would still want the fix in the game module's ownership, not a parm fingerprint in the renderer.

Happy to keep this open while you rework it.

@jm2

jm2 commented Sep 22, 2026

Copy link
Copy Markdown
Contributor Author

Closing in favour of #178.

The review holds. viewID > 0 matches every player view, so this PR switched the join card's soft focus off rather than fixing a leak, and the parm test fingerprints game-module constants. The game-side bookkeeping is also correct: answering the card clears joinScreenSoftFocusEnabled and SPECIAL_EFFECT_BLUR.

The leak itself is real, though, and it is renderer-side. With the card answered and both of those cleared, the GL backend kept compositing the blur. R_AddSpecialEffects stops issuing RB_DrawSpecialEffects once no effect is on, so rbRVSpecialActiveMask and the depth capture keep their last values, and RB_DisplaySpecialEffects never checked their age.

#178 fixes that in six lines, with a repro and before/after measurements. It needs no game-module change and leaves the card's soft focus intact. The r_specialEffects cvar and r_skipPostProcess gating are not carried forward.

@jm2 jm2 closed this Sep 22, 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.

[macOS rendering]: distant rendering very blurry/double pixelated

2 participants