Skip to content

fix(motionblur): stop per-frame ShaderGroup leak and edge-row artifacts - #161

Merged
gaoyu06 merged 1 commit into
mainfrom
fix/motionblur-shader-leak
Jul 2, 2026
Merged

fix(motionblur): stop per-frame ShaderGroup leak and edge-row artifacts#161
gaoyu06 merged 1 commit into
mainfrom
fix/motionblur-shader-leak

Conversation

@gaoyu06

@gaoyu06 gaoyu06 commented Jul 2, 2026

Copy link
Copy Markdown
Member

Problem

Users report intermittent screen corruption with MotionBlur enabled: a few garbage pixel rows, or the whole top half of the screen rendering garbage.

Root cause (New mode, the default): renderOverlay reloaded both post shaders every time isUsingShader() returned false, and vanilla EntityRenderer.loadShader never deletes the previous ShaderGroup. The GLSL 150 variant (motionblur_core) mixes a #version 120 vertex shader (vanilla sobel.vsh) with a #version 150 fragment shader and cannot compile in the legacy GL context 1.8.9 gets on macOS. When it fails, useShader stays false, the name check in isUsingShader() (which only accepted the core variant's name) never matches, and the module reloads both shaders every frame — leaking two fullscreen framebuffers (color + depth) per frame until VRAM exhaustion corrupts rendering. Even where both variants load, one full ShaderGroup leaked on every activation / respawn / dimension change.

Fix

  • Delete the old group (stopUseShader()) before loading, load only the GLSL 120 variant, and never retry after a failed load (log instead).
  • Rebind the main framebuffer after loadShader, which leaves FB 0 bound mid-frame.
  • isUsingShader() now checks for the shader that is actually loaded.
  • Remove the motionblur_core assets (unreferenced now; the 120 pipeline runs everywhere 1.8.9 runs).
  • Cache the listShaders reflection Field with a searge-name (field_148031_d) fallback so the Strength setting also works in the remapped production jar, and log instead of silently swallowing reflection failures.
  • Old mode: fullscreen quads used integer division (width / scaleFactor), leaving up to scaleFactor-1 unprocessed pixel rows/columns at the bottom/right edge ("a few abnormal pixel rows"); now float division. Also push/pop the projection/modelview matrices around blur() instead of clobbering them.

Testing

  • ./gradlew compileJava passes.
  • Vanilla behavior verified against decompiled 1.8.9 (EntityRenderer.loadShader / ShaderGroup / ShaderLoader): load-shader leak, compile-failure path, and searge field name confirmed.

🤖 Generated with Claude Code

New mode reloaded both motionblur post shaders whenever the active group
name did not match, and vanilla loadShader never deletes the previous
ShaderGroup. On systems where the GLSL 150 variant fails to compile
(macOS legacy GL context), this leaked two fullscreen framebuffers every
frame until VRAM exhaustion corrupted the screen (garbage pixel rows /
top-half artifacts).

- delete the old shader group (stopUseShader) before loading, load only
  the GLSL 120 variant, and never retry after a failed load
- rebind the main framebuffer after loadShader, which leaves FB 0 bound
- accept the loaded group's own name in isUsingShader instead of the
  motionblur_core name, which could never match the 120 variant
- drop the motionblur_core assets: 1.8.9 runs a compatibility context
  and the program mixed a #version 120 vertex shader with a #version
  150 fragment shader, which is link-error territory anyway
- cache the listShaders reflection Field and fall back to the searge
  name so Strength works in the remapped production jar
- Old mode: use float division for the fullscreen quad size so the
  bottom/right remainder rows are covered, and push/pop the projection
  and modelview matrices around blur()

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@gaoyu06
gaoyu06 merged commit 9b847eb into main Jul 2, 2026
2 checks passed
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.

1 participant