Skip to content

fix(renderer): stop DecalRenderPass's GLStateGuard error spam (#895) - #915

Merged
drsnuggles8 merged 1 commit into
masterfrom
feature/decal-pass-gl-state-leak-895
Aug 23, 2026
Merged

fix(renderer): stop DecalRenderPass's GLStateGuard error spam (#895)#915
drsnuggles8 merged 1 commit into
masterfrom
feature/decal-pass-gl-state-leak-895

Conversation

@drsnuggles8

@drsnuggles8 drsnuggles8 commented Aug 22, 2026

Copy link
Copy Markdown
Owner

Fixes #895DecalRenderPass construted its GLStateGuard with the default Policy::Log, which reports every escaped GL state field at ERROR level and rolls nothing back. The pass's manual restore only covered DepthMask/BlendState(enable)/DepthFunc/Cull, so DepthTest, the four blend func factors, ActiveProgram and VAO escaped on every frame that drained a decal — 2723 ERROR lines in a couple of minutes on DecalModeMatrixTest.olo, on both rendering paths.

Closes #895

The fix

Switch to Policy::Restore. This is safe here — unlike the DDGI/fog pattern docs/agent-rules/render-pass-published-state.md warns about — because GLStateSnapshot::ApplyCore() restores only the core GL subset (depth/blend/stencil/cull/polygon-mode/viewport/scissor/FBO/program/VAO) and deliberately never touches per-slot texture bindings. So it fixes exactly the fields that were escaping without undoing this pass's texture publishes at TEX_POSTPROCESS_DEPTH / TEX_USER_0:

  • TEX_POSTPROCESS_DEPTH is re-published fresh by every downstream consumer (SSAO, Fog, DOF, MotionBlur, TAA, ToneMap all rebind it themselves before sampling) — nothing depends on DecalRenderPass leaving it bound.
  • TEX_USER_0-2 are documented pass-local-reuse slots (ShaderBindingLayout.h) — same contract.

Residual texture diffs still surface, just at TRACE instead of ERROR — quiet in steady state, discoverable for a future leak hunt. Same shape as PlanarReflectionRenderPass / OverdrawRenderPass / ShaderDebugDrawPass, which also replay geometry with their own programs/VAOs under Policy::Restore.

Also added the CommandDispatch::InvalidateRenderStateCache() call the non-OIT restore path was missing (the OIT branch already had it) — without it the render-state cache would go stale after the guard's raw restore GL calls bypass the tracked RenderCommand layer, risking a downstream pass eliding a call it actually needs to reissue.

Audit of other GLStateGuard( construction sites (in scope per the issue): every other site already explicitly chooses Policy::Ignore or Policy::RestoreDecalRenderPass was the only one left on the noisy default. No broader sweep needed.

Verification

  • GLStateGuardTest.* (37 tests) pass, including RestorePolicy_RestoresCoreStateOnDtor and RestorePolicy_DoesNotUnbindTexturesOrUbosButLogsLeak, which pin exactly the ApplyCore behaviour this fix relies on.
  • Live editor verification on DecalModeMatrixTest.olo (per CLAUDE.md — headless tests are not sufficient for a rendering change): played the scene on both Forward and Deferred paths.
    • Zero GLStateGuard[DecalRenderPass] ERROR lines across ~8000 logged frames on either path (down from 2723 in a couple of minutes).
    • Decals still render their authored colours correctly on both paths — screenshots attached below, consistent with PR fix(renderer): decal per-channel colour masks, and the four defects hiding behind them (#853) #886's recorded baseline (Albedo decal 1.0, 0.251, 0.149; the checkerboard arm is the known missing-texture placeholder, unrelated to this change).
    • The three remaining [error] lines in the session log are a pre-existing missing Checkerboard.png asset, unrelated to this fix.

Out of scope

Summary by CodeRabbit

  • Bug Fixes
    • Improved decal rendering state management to ensure graphics settings are restored correctly after decal passes.
    • Updated non-order-independent transparency rendering to keep subsequent rendering operations synchronized with the restored state.
    • Helps prevent visual inconsistencies and rendering artifacts that could occur after decals are processed.

DecalRenderPass constructed its GLStateGuard with the default Policy::Log,
which logs every escaped GL state mutation at ERROR and rolls nothing back.
The pass's manual restore only covered DepthMask/BlendState(enable)/
DepthFunc/Cull, so DepthTest, the four blend func factors, ActiveProgram
and VAO escaped on every frame that drained a decal -- 2723 ERROR lines in
a couple of minutes on DecalModeMatrixTest.olo, on both rendering paths.

Switch to Policy::Restore. GLStateSnapshot::ApplyCore() restores only the
core GL subset (depth/blend/stencil/cull/polygon-mode/viewport/scissor/
FBO/program/VAO) and deliberately never touches per-slot texture bindings,
so it fixes the escaping fields without undoing this pass's deliberate
texture publishes at TEX_POSTPROCESS_DEPTH / TEX_USER_0 -- both re-bound
fresh by whichever pass needs them next, same as every other
TEX_POSTPROCESS_DEPTH consumer. Residual texture diffs now log at TRACE
instead of ERROR, matching the PlanarReflectionRenderPass / OverdrawRenderPass
/ ShaderDebugDrawPass precedent for passes that replay geometry with their
own programs/VAOs.

Also add the CommandDispatch::InvalidateRenderStateCache() call the non-OIT
restore path was missing (the OIT path already had it), so the render-state
cache doesn't go stale after the guard's raw restore GL calls.

Verified live in the editor on DecalModeMatrixTest.olo: zero
GLStateGuard[DecalRenderPass] ERROR lines across ~8000 frames on both
Forward and Deferred, decals still render their authored colours correctly
on both paths.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@drsnuggles8

Copy link
Copy Markdown
Owner Author

Live verification screenshots (Forward and Deferred paths, DecalModeMatrixTest.olo) — sent to the user directly in this session's chat, since gh comments can't attach local files without an upload step. Both show all four decal modes rendering correctly with zero GLStateGuard[DecalRenderPass] ERROR lines in OloEngine.log.

@coderabbitai

coderabbitai Bot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 99a5c390-1b1f-4cca-bd41-de747e89c0ad

📥 Commits

Reviewing files that changed from the base of the PR and between bc5af38 and d0fea4a.

📒 Files selected for processing (1)
  • OloEngine/src/OloEngine/Renderer/Passes/DecalRenderPass.cpp

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The decal render pass now restores core GL state automatically. The non-OIT path also invalidates the render-state cache after decal cleanup.

Changes

Decal render state

Layer / File(s) Summary
Restore GL state and invalidate cache
OloEngine/src/OloEngine/Renderer/Passes/DecalRenderPass.cpp
DecalRenderPass::Execute uses GLStateGuard::Policy::Restore for core GL state. Non-OIT cleanup invalidates the render-state cache after restoring decal state.

Merge Risk: ⚪ Minimal · up to d0fea

The localized renderer change restores leaked core graphics state and refreshes the state cache, with supplied verification showing correct decal rendering and no remaining actionable merge-blocking risk beyond normal checks.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the renderer fix for DecalRenderPass GLStateGuard error spam, which matches the main change.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sonarqubecloud

Copy link
Copy Markdown

@drsnuggles8
drsnuggles8 merged commit 3a7da20 into master Aug 23, 2026
12 checks passed
@drsnuggles8
drsnuggles8 deleted the feature/decal-pass-gl-state-leak-895 branch August 23, 2026 06:29
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.

DecalRenderPass leaks 8-9 GL state fields every frame, reported at error level (2723 lines in minutes)

1 participant