fix(renderer): stop DecalRenderPass's GLStateGuard error spam (#895) - #915
Conversation
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>
|
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. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughThe decal render pass now restores core GL state automatically. The non-OIT path also invalidates the render-state cache after decal cleanup. ChangesDecal render state
Merge Risk: ⚪ Minimal · up to 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)
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. Comment |
|



Fixes #895 —
DecalRenderPassconstruted itsGLStateGuardwith the defaultPolicy::Log, which reports every escaped GL state field at ERROR level and rolls nothing back. The pass's manual restore only coveredDepthMask/BlendState(enable)/DepthFunc/Cull, soDepthTest, the four blend func factors,ActiveProgramandVAOescaped on every frame that drained a decal — 2723 ERROR lines in a couple of minutes onDecalModeMatrixTest.olo, on both rendering paths.Closes #895The fix
Switch to
Policy::Restore. This is safe here — unlike the DDGI/fog patterndocs/agent-rules/render-pass-published-state.mdwarns about — becauseGLStateSnapshot::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 atTEX_POSTPROCESS_DEPTH/TEX_USER_0:TEX_POSTPROCESS_DEPTHis re-published fresh by every downstream consumer (SSAO, Fog, DOF, MotionBlur, TAA, ToneMap all rebind it themselves before sampling) — nothing depends onDecalRenderPassleaving it bound.TEX_USER_0-2are 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 underPolicy::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 trackedRenderCommandlayer, 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 choosesPolicy::IgnoreorPolicy::Restore—DecalRenderPasswas the only one left on the noisy default. No broader sweep needed.Verification
GLStateGuardTest.*(37 tests) pass, includingRestorePolicy_RestoresCoreStateOnDtorandRestorePolicy_DoesNotUnbindTexturesOrUbosButLogsLeak, which pin exactly the ApplyCore behaviour this fix relies on.DecalModeMatrixTest.olo(perCLAUDE.md— headless tests are not sufficient for a rendering change): played the scene on both Forward and Deferred paths.GLStateGuard[DecalRenderPass]ERROR lines across ~8000 logged frames on either path (down from 2723 in a couple of minutes).1.0, 0.251, 0.149; the checkerboard arm is the known missing-texture placeholder, unrelated to this change).[error]lines in the session log are a pre-existing missingCheckerboard.pngasset, unrelated to this fix.Out of scope
DecalComponenttexture references lost on scene round-trip) — same area, deliberately unclaimed this round per the issue.Summary by CodeRabbit