From b108cc1f4a3e8318a0f83c666a47730f2ad0feae Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Mon, 28 Sep 2026 18:59:47 +0200 Subject: [PATCH 01/11] Scale Saturn's screens by their GUI scale setting Co-Authored-By: Claude Opus 5.5 --- common | 2 +- .../org/saturnclient/impl/ui/SaturnScreenFabric.java | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/common b/common index ef0ead6..a7e3e53 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit ef0ead6bf895f723a78ddf5426f3881ef13f426a +Subproject commit a7e3e537b180638beadc0adcdc642935311e99ee diff --git a/src/main/java/org/saturnclient/impl/ui/SaturnScreenFabric.java b/src/main/java/org/saturnclient/impl/ui/SaturnScreenFabric.java index 827db8a..bfaa041 100644 --- a/src/main/java/org/saturnclient/impl/ui/SaturnScreenFabric.java +++ b/src/main/java/org/saturnclient/impl/ui/SaturnScreenFabric.java @@ -86,8 +86,10 @@ public class SaturnScreenFabric extends Screen implements ScreenProvider { } long elapsed = Duration.between(screen.start, Instant.now()).toMillis(); - mouseX *= 2; - mouseY *= 2; + screen.updateScale(); + + mouseX = (int) (mouseX / screen.scale); + mouseY = (int) (mouseY / screen.scale); // From 1.21.6 the background, blur included, is drawn in renderBackground. //? if <1.21.6 { @@ -245,11 +247,11 @@ public class SaturnScreenFabric extends Screen implements ScreenProvider { @Override public int getWidth() { - return width * 2; + return width; } @Override public int getHeight() { - return height * 2; + return height; } } \ No newline at end of file From 68d3fb6062ed15d4cb806633a1aac24ffcff0465 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Mon, 28 Sep 2026 19:09:10 +0200 Subject: [PATCH 02/11] Point common at the title screen layout Co-Authored-By: Claude Opus 5.5 --- common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common b/common index a7e3e53..320b2fd 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit a7e3e537b180638beadc0adcdc642935311e99ee +Subproject commit 320b2fd9a56d2b1d8bb011250862bd81f5c5ce58 From 3315926f7ccbc49b7fe7f6dd6928a8adc4a6fe1a Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Mon, 28 Sep 2026 19:20:33 +0200 Subject: [PATCH 03/11] Point common at the title screen corners Co-Authored-By: Claude Opus 5.5 --- common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common b/common index 320b2fd..a7ad458 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 320b2fd9a56d2b1d8bb011250862bd81f5c5ce58 +Subproject commit a7ad4586274f34354d025e77aca7b7d24972a1ba From 463335856f03c72081a1c39929e3865c7198b262 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Mon, 28 Sep 2026 19:39:39 +0200 Subject: [PATCH 04/11] Point common at the title screen polish Co-Authored-By: Claude Opus 5.5 --- common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common b/common index a7ad458..adedd68 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit a7ad4586274f34354d025e77aca7b7d24972a1ba +Subproject commit adedd688a798d7afe1e0c248e9163399eb9ca524 From da3dfaee66977b4562351658692e53679214d4bb Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Mon, 28 Sep 2026 19:45:04 +0200 Subject: [PATCH 05/11] Point common at the faster color picker Co-Authored-By: Claude Opus 5.5 --- common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common b/common index adedd68..3436054 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit adedd688a798d7afe1e0c248e9163399eb9ca524 +Subproject commit 3436054112a3c71b840f0257dfa4424aa5b4d131 From e26ce6d0763b38d5d3071069e831cbaba3993383 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Mon, 28 Sep 2026 19:56:24 +0200 Subject: [PATCH 06/11] Point common at the theme alpha fix Co-Authored-By: Claude Opus 5.5 --- common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common b/common index 3436054..5c0912f 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 3436054112a3c71b840f0257dfa4424aa5b4d131 +Subproject commit 5c0912f0cf6f09e5776f330e559ddd8e38370f47 From c524451704550dfa616480c25259ba1f5ec53a1c Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Mon, 28 Sep 2026 20:43:44 +0200 Subject: [PATCH 07/11] Draw glass behind translucent panels on 1.21.11 A copy of the game behind the screen is blurred once a frame and drawn through each panel's rounded rectangle texture by a glass pipeline. Older versions keep blurring the whole background. Co-Authored-By: Claude Opus 5.5 --- common | 2 +- .../mixins/render/GameRendererMixin.java | 5 +- .../impl/provider/SaturnProviderImpl.java | 8 +++ .../saturnclient/impl/ui/GlassRenderer.java | 71 +++++++++++++++++++ .../saturnclient/impl/ui/RenderScopeImpl.java | 26 +++++++ .../saturnclient/shaders/core/glass.fsh | 31 ++++++++ 6 files changed, 140 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/saturnclient/impl/ui/GlassRenderer.java create mode 100644 src/main/resources/assets/saturnclient/shaders/core/glass.fsh diff --git a/common b/common index 5c0912f..4d9c629 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 5c0912f0cf6f09e5776f330e559ddd8e38370f47 +Subproject commit 4d9c629eaf4f5d315d772d7e07336ff237369390 diff --git a/src/main/java/org/saturnclient/impl/features/mixins/render/GameRendererMixin.java b/src/main/java/org/saturnclient/impl/features/mixins/render/GameRendererMixin.java index be65e23..324fe01 100644 --- a/src/main/java/org/saturnclient/impl/features/mixins/render/GameRendererMixin.java +++ b/src/main/java/org/saturnclient/impl/features/mixins/render/GameRendererMixin.java @@ -49,12 +49,13 @@ public class GameRendererMixin { //? if >=1.21.6 { // From 1.21.6 the menu blur's strength is the vanilla setting, read here every frame. While a - // Saturn screen is open, it's Saturn's background blur instead, faded in with the screen. + // Saturn screen is open, it's Saturn's background blur instead, faded in with the screen. Glass + // uses the same blur, so this holds even when the screen doesn't blur its whole background. @Redirect(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/option/GameOptions;getMenuBackgroundBlurrinessValue()I")) private int useSaturnBlur(GameOptions options) { if (MinecraftClient.getInstance().currentScreen instanceof SaturnScreenFabric saturnScreen) { - return saturnScreen.screen.getBackgroundBlur(); + return saturnScreen.screen.getBlurRadius(); } return options.getMenuBackgroundBlurrinessValue(); diff --git a/src/main/java/org/saturnclient/impl/provider/SaturnProviderImpl.java b/src/main/java/org/saturnclient/impl/provider/SaturnProviderImpl.java index fb9b976..f468ed4 100644 --- a/src/main/java/org/saturnclient/impl/provider/SaturnProviderImpl.java +++ b/src/main/java/org/saturnclient/impl/provider/SaturnProviderImpl.java @@ -76,6 +76,14 @@ public class SaturnProviderImpl implements SaturnProvider { return (IdentifierRef) (Object) playerSkin.get(); } + @Override + public boolean supportsGlass() { + //? if >=1.21.11 { + return true; + //?} else + //return false; + } + @Override public String getModVersion() { return FabricLoader.getInstance().getModContainer(SaturnClient.MOD_ID) diff --git a/src/main/java/org/saturnclient/impl/ui/GlassRenderer.java b/src/main/java/org/saturnclient/impl/ui/GlassRenderer.java new file mode 100644 index 0000000..1c2f589 --- /dev/null +++ b/src/main/java/org/saturnclient/impl/ui/GlassRenderer.java @@ -0,0 +1,71 @@ +package org.saturnclient.impl.ui; + +//? if >=1.21.11 { +import com.mojang.blaze3d.pipeline.RenderPipeline; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.textures.FilterMode; +import com.mojang.blaze3d.textures.GpuTextureView; + +import org.saturnclient.saturnclient.SaturnClient; + +import net.minecraft.client.gl.Framebuffer; +import net.minecraft.client.gl.GpuSampler; +import net.minecraft.client.gl.PostEffectProcessor; +import net.minecraft.client.gl.RenderPipelines; +import net.minecraft.client.gl.SimpleFramebuffer; +import net.minecraft.client.render.DefaultFramebufferSet; +import net.minecraft.client.texture.TextureSetup; +import net.minecraft.client.util.Pool; +import net.minecraft.util.Identifier; + +// Glass: a copy of the game behind the screen, blurred with vanilla's blur (as strong as Saturn's +// background blur), drawn through each panel's rounded rectangle texture (shaders/core/glass.fsh). +// The copy is made while the screen builds its GUI, before the GUI is drawn, so it holds the world +// or panorama alone. +// TODO: glass before 1.21.11 (#23). Those versions blur the whole background instead. +public final class GlassRenderer { + public static final RenderPipeline PIPELINE = RenderPipeline.builder(RenderPipelines.POSITION_TEX_COLOR_SNIPPET) + .withLocation(Identifier.of("saturnclient", "pipeline/glass")) + .withFragmentShader(Identifier.of("saturnclient", "core/glass")) + .withSampler("Sampler1") + .build(); + + private static final Identifier BLUR = Identifier.ofVanilla("blur"); + // For the blur's intermediate target, freed once unused for a few frames + private static final Pool POOL = new Pool(3); + + private static SimpleFramebuffer backdrop; + + private GlassRenderer() { + } + + // Copies the main framebuffer and blurs the copy. Call once a frame, before drawing glass. + public static void captureBackdrop() { + Framebuffer main = SaturnClient.client.getFramebuffer(); + int width = main.textureWidth; + int height = main.textureHeight; + + if (backdrop == null) { + backdrop = new SimpleFramebuffer("Saturn glass", width, height, false); + } else if (backdrop.textureWidth != width || backdrop.textureHeight != height) { + backdrop.resize(width, height); + } + + RenderSystem.getDevice().createCommandEncoder().copyTextureToTexture( + main.getColorAttachment(), backdrop.getColorAttachment(), 0, 0, 0, 0, 0, width, height); + + PostEffectProcessor blur = SaturnClient.client.getShaderLoader().loadPostEffect(BLUR, + DefaultFramebufferSet.MAIN_ONLY); + if (blur != null) { + blur.render(backdrop, POOL); + } + POOL.decrementLifespan(); + } + + // The mask, then the blurred copy, as the glass pipeline samples them + public static TextureSetup textures(GpuTextureView mask, GpuSampler maskSampler) { + return TextureSetup.of(mask, maskSampler, backdrop.getColorAttachmentView(), + RenderSystem.getSamplerCache().get(FilterMode.LINEAR)); + } +} +//?} diff --git a/src/main/java/org/saturnclient/impl/ui/RenderScopeImpl.java b/src/main/java/org/saturnclient/impl/ui/RenderScopeImpl.java index 3cdb564..5c8b123 100644 --- a/src/main/java/org/saturnclient/impl/ui/RenderScopeImpl.java +++ b/src/main/java/org/saturnclient/impl/ui/RenderScopeImpl.java @@ -61,6 +61,10 @@ public class RenderScopeImpl implements RenderScope { private ScissorStack scissorStack; private int opacity = 0xFF << 24; + //? if >=1.21.11 { + // Whether the glass backdrop was made for this scope's frame + private boolean backdropCaptured = false; + //?} public RenderScopeImpl(DrawContext context) { this(context.getMatrices(), ((DrawContextAccessor) (Object) context).getState()); @@ -230,6 +234,28 @@ public class RenderScopeImpl implements RenderScope { this.scissorStack.peekLast())); } + //? if >=1.21.11 { + @Override + public void drawGlass(IdentifierRef mask, int x, int y, int width, int height) { + int color = getColor(0xFFFFFFFF); + if (mask == null || color >>> 24 == 0) + return; + + if (!backdropCaptured) { + GlassRenderer.captureBackdrop(); + backdropCaptured = true; + } + + AbstractTexture maskTexture = SaturnClient.client.getTextureManager().getTexture((Identifier) (Object) mask); + this.state.addSimpleElement(new TexturedQuadGuiElementRenderState( + GlassRenderer.PIPELINE, + GlassRenderer.textures(maskTexture.getGlTextureView(), maskTexture.getSampler()), + new Matrix3x2f(this.matrices.stack), + x, y, x + width, y + height, 0, 1, 0, 1, color, + this.scissorStack.peekLast())); + } + //?} + // Scissor management @Override public void enableScissor(int x1, int y1, int x2, int y2) { diff --git a/src/main/resources/assets/saturnclient/shaders/core/glass.fsh b/src/main/resources/assets/saturnclient/shaders/core/glass.fsh new file mode 100644 index 0000000..fe6f02b --- /dev/null +++ b/src/main/resources/assets/saturnclient/shaders/core/glass.fsh @@ -0,0 +1,31 @@ +#version 330 + +// Saturn's glass: the blurred game behind the screen, cut to a panel's shape. + +layout(std140) uniform DynamicTransforms { + mat4 ModelViewMat; + vec4 ColorModulator; + vec3 ModelOffset; + mat4 TextureMat; +}; + +// The panel's rounded rectangle, the same texture its background is drawn with +uniform sampler2D Sampler0; +// The game behind the screen, blurred, the size of the framebuffer +uniform sampler2D Sampler1; + +in vec2 texCoord0; +in vec4 vertexColor; + +out vec4 fragColor; + +void main() { + float mask = texture(Sampler0, texCoord0).a * vertexColor.a; + if (mask == 0.0) { + discard; + } + + // The blurred copy lines up with the framebuffer being drawn to. + vec2 screenUv = gl_FragCoord.xy / vec2(textureSize(Sampler1, 0)); + fragColor = vec4(texture(Sampler1, screenUv).rgb, mask) * ColorModulator; +} From 4e00934353837d912861ec60ff4052f20211faeb Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Mon, 28 Sep 2026 21:35:23 +0200 Subject: [PATCH 08/11] Blur each glass strength separately on 1.21.11 Every blur strength in use gets its own copy of the game, blurred with vanilla's blur at that radius instead of the animated menu blur. Co-Authored-By: Claude Opus 5.5 --- common | 2 +- .../mixins/render/GameRendererMixin.java | 5 +- .../impl/provider/SaturnProviderImpl.java | 8 -- .../saturnclient/impl/ui/GlassRenderer.java | 87 +++++++++++++++---- .../saturnclient/impl/ui/RenderScopeImpl.java | 15 ++-- 5 files changed, 80 insertions(+), 37 deletions(-) diff --git a/common b/common index 4d9c629..7bc3081 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 4d9c629eaf4f5d315d772d7e07336ff237369390 +Subproject commit 7bc308155e882743badc71e8465ec8e913022c0a diff --git a/src/main/java/org/saturnclient/impl/features/mixins/render/GameRendererMixin.java b/src/main/java/org/saturnclient/impl/features/mixins/render/GameRendererMixin.java index 324fe01..be65e23 100644 --- a/src/main/java/org/saturnclient/impl/features/mixins/render/GameRendererMixin.java +++ b/src/main/java/org/saturnclient/impl/features/mixins/render/GameRendererMixin.java @@ -49,13 +49,12 @@ public class GameRendererMixin { //? if >=1.21.6 { // From 1.21.6 the menu blur's strength is the vanilla setting, read here every frame. While a - // Saturn screen is open, it's Saturn's background blur instead, faded in with the screen. Glass - // uses the same blur, so this holds even when the screen doesn't blur its whole background. + // Saturn screen is open, it's Saturn's background blur instead, faded in with the screen. @Redirect(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/option/GameOptions;getMenuBackgroundBlurrinessValue()I")) private int useSaturnBlur(GameOptions options) { if (MinecraftClient.getInstance().currentScreen instanceof SaturnScreenFabric saturnScreen) { - return saturnScreen.screen.getBlurRadius(); + return saturnScreen.screen.getBackgroundBlur(); } return options.getMenuBackgroundBlurrinessValue(); diff --git a/src/main/java/org/saturnclient/impl/provider/SaturnProviderImpl.java b/src/main/java/org/saturnclient/impl/provider/SaturnProviderImpl.java index f468ed4..fb9b976 100644 --- a/src/main/java/org/saturnclient/impl/provider/SaturnProviderImpl.java +++ b/src/main/java/org/saturnclient/impl/provider/SaturnProviderImpl.java @@ -76,14 +76,6 @@ public class SaturnProviderImpl implements SaturnProvider { return (IdentifierRef) (Object) playerSkin.get(); } - @Override - public boolean supportsGlass() { - //? if >=1.21.11 { - return true; - //?} else - //return false; - } - @Override public String getModVersion() { return FabricLoader.getInstance().getModContainer(SaturnClient.MOD_ID) diff --git a/src/main/java/org/saturnclient/impl/ui/GlassRenderer.java b/src/main/java/org/saturnclient/impl/ui/GlassRenderer.java index 1c2f589..cee6ba6 100644 --- a/src/main/java/org/saturnclient/impl/ui/GlassRenderer.java +++ b/src/main/java/org/saturnclient/impl/ui/GlassRenderer.java @@ -1,28 +1,38 @@ package org.saturnclient.impl.ui; //? if >=1.21.11 { +import java.io.Reader; +import java.util.HashMap; +import java.util.Map; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import com.mojang.blaze3d.pipeline.RenderPipeline; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.textures.FilterMode; import com.mojang.blaze3d.textures.GpuTextureView; +import com.mojang.serialization.JsonOps; import org.saturnclient.saturnclient.SaturnClient; import net.minecraft.client.gl.Framebuffer; import net.minecraft.client.gl.GpuSampler; +import net.minecraft.client.gl.PostEffectPipeline; import net.minecraft.client.gl.PostEffectProcessor; import net.minecraft.client.gl.RenderPipelines; import net.minecraft.client.gl.SimpleFramebuffer; import net.minecraft.client.render.DefaultFramebufferSet; +import net.minecraft.client.render.ProjectionMatrix2; import net.minecraft.client.texture.TextureSetup; import net.minecraft.client.util.Pool; import net.minecraft.util.Identifier; -// Glass: a copy of the game behind the screen, blurred with vanilla's blur (as strong as Saturn's -// background blur), drawn through each panel's rounded rectangle texture (shaders/core/glass.fsh). -// The copy is made while the screen builds its GUI, before the GUI is drawn, so it holds the world -// or panorama alone. -// TODO: glass before 1.21.11 (#23). Those versions blur the whole background instead. +// Glass: a copy of the game behind the screen, blurred, drawn through each panel's rounded rectangle +// texture (shaders/core/glass.fsh). There's a copy for each blur strength in use (menus and widgets +// have their own), made while the screen builds its GUI, before the GUI is drawn, so it holds the +// world or panorama alone, without the screen's own animated background blur. +// TODO: glass before 1.21.11 (#23). Those versions draw translucent panels without it. public final class GlassRenderer { public static final RenderPipeline PIPELINE = RenderPipeline.builder(RenderPipelines.POSITION_TEX_COLOR_SNIPPET) .withLocation(Identifier.of("saturnclient", "pipeline/glass")) @@ -30,23 +40,29 @@ public final class GlassRenderer { .withSampler("Sampler1") .build(); - private static final Identifier BLUR = Identifier.ofVanilla("blur"); - // For the blur's intermediate target, freed once unused for a few frames + private static final Identifier BLUR = Identifier.ofVanilla("post_effect/blur.json"); + private static final ProjectionMatrix2 PROJECTION = new ProjectionMatrix2("saturn glass", 0.1f, 1000f, false); + // For the blurs' intermediate targets, freed once unused for a few frames private static final Pool POOL = new Pool(3); - private static SimpleFramebuffer backdrop; + // By blur strength. A blur that failed to load is kept as null, so it isn't retried every frame. + private static final Map BLURS = new HashMap<>(); + private static final Map BACKDROPS = new HashMap<>(); private GlassRenderer() { } - // Copies the main framebuffer and blurs the copy. Call once a frame, before drawing glass. - public static void captureBackdrop() { + // Copies the main framebuffer and blurs the copy by blur. Call once a frame for each blur, before + // drawing glass with it. + public static void captureBackdrop(int blur) { Framebuffer main = SaturnClient.client.getFramebuffer(); int width = main.textureWidth; int height = main.textureHeight; + SimpleFramebuffer backdrop = BACKDROPS.get(blur); if (backdrop == null) { - backdrop = new SimpleFramebuffer("Saturn glass", width, height, false); + backdrop = new SimpleFramebuffer("Saturn glass " + blur, width, height, false); + BACKDROPS.put(blur, backdrop); } else if (backdrop.textureWidth != width || backdrop.textureHeight != height) { backdrop.resize(width, height); } @@ -54,18 +70,53 @@ public final class GlassRenderer { RenderSystem.getDevice().createCommandEncoder().copyTextureToTexture( main.getColorAttachment(), backdrop.getColorAttachment(), 0, 0, 0, 0, 0, width, height); - PostEffectProcessor blur = SaturnClient.client.getShaderLoader().loadPostEffect(BLUR, - DefaultFramebufferSet.MAIN_ONLY); - if (blur != null) { - blur.render(backdrop, POOL); + PostEffectProcessor processor = getBlur(blur); + if (processor != null) { + processor.render(backdrop, POOL); } POOL.decrementLifespan(); } - // The mask, then the blurred copy, as the glass pipeline samples them - public static TextureSetup textures(GpuTextureView mask, GpuSampler maskSampler) { - return TextureSetup.of(mask, maskSampler, backdrop.getColorAttachmentView(), + // The mask, then the copy blurred by blur, as the glass pipeline samples them + public static TextureSetup textures(GpuTextureView mask, GpuSampler maskSampler, int blur) { + return TextureSetup.of(mask, maskSampler, BACKDROPS.get(blur).getColorAttachmentView(), RenderSystem.getSamplerCache().get(FilterMode.LINEAR)); } + + private static PostEffectProcessor getBlur(int blur) { + if (!BLURS.containsKey(blur)) { + BLURS.put(blur, loadBlur(blur)); + } + return BLURS.get(blur); + } + + // Vanilla's blur, with its radius set to blur. Vanilla's own leaves it at 0, which makes the shader + // read the menu blur setting (Saturn's animated background blur while a Saturn screen is open). + private static PostEffectProcessor loadBlur(int blur) { + try (Reader reader = SaturnClient.client.getResourceManager().getResourceOrThrow(BLUR).getReader()) { + JsonElement json = JsonParser.parseReader(reader); + setRadius(json, blur); + + PostEffectPipeline pipeline = PostEffectPipeline.CODEC.parse(JsonOps.INSTANCE, json).getOrThrow(); + return PostEffectProcessor.parseEffect(pipeline, SaturnClient.client.getTextureManager(), + DefaultFramebufferSet.MAIN_ONLY, Identifier.of("saturnclient", "glass_blur_" + blur), PROJECTION); + } catch (Exception e) { + SaturnClient.LOGGER.error("Couldn't load the glass blur", e); + return null; + } + } + + // Sets every "Radius" uniform in the effect + private static void setRadius(JsonElement json, int radius) { + if (json.isJsonArray()) { + json.getAsJsonArray().forEach(element -> setRadius(element, radius)); + } else if (json.isJsonObject()) { + JsonObject object = json.getAsJsonObject(); + if (object.has("name") && object.get("name").getAsString().equals("Radius")) { + object.addProperty("value", (float) radius); + } + object.entrySet().forEach(entry -> setRadius(entry.getValue(), radius)); + } + } } //?} diff --git a/src/main/java/org/saturnclient/impl/ui/RenderScopeImpl.java b/src/main/java/org/saturnclient/impl/ui/RenderScopeImpl.java index 5c8b123..d9004bc 100644 --- a/src/main/java/org/saturnclient/impl/ui/RenderScopeImpl.java +++ b/src/main/java/org/saturnclient/impl/ui/RenderScopeImpl.java @@ -5,7 +5,9 @@ package org.saturnclient.impl.ui; //? if >=1.21.6 { import java.util.ArrayDeque; import java.util.Deque; +import java.util.HashSet; import java.util.Objects; +import java.util.Set; import org.jetbrains.annotations.Nullable; import org.joml.Matrix3x2f; @@ -62,8 +64,8 @@ public class RenderScopeImpl implements RenderScope { private ScissorStack scissorStack; private int opacity = 0xFF << 24; //? if >=1.21.11 { - // Whether the glass backdrop was made for this scope's frame - private boolean backdropCaptured = false; + // The glass blurs made for this scope's frame + private final Set capturedBlurs = new HashSet<>(); //?} public RenderScopeImpl(DrawContext context) { @@ -236,20 +238,19 @@ public class RenderScopeImpl implements RenderScope { //? if >=1.21.11 { @Override - public void drawGlass(IdentifierRef mask, int x, int y, int width, int height) { + public void drawGlass(IdentifierRef mask, int x, int y, int width, int height, int blur) { int color = getColor(0xFFFFFFFF); if (mask == null || color >>> 24 == 0) return; - if (!backdropCaptured) { - GlassRenderer.captureBackdrop(); - backdropCaptured = true; + if (capturedBlurs.add(blur)) { + GlassRenderer.captureBackdrop(blur); } AbstractTexture maskTexture = SaturnClient.client.getTextureManager().getTexture((Identifier) (Object) mask); this.state.addSimpleElement(new TexturedQuadGuiElementRenderState( GlassRenderer.PIPELINE, - GlassRenderer.textures(maskTexture.getGlTextureView(), maskTexture.getSampler()), + GlassRenderer.textures(maskTexture.getGlTextureView(), maskTexture.getSampler(), blur), new Matrix3x2f(this.matrices.stack), x, y, x + width, y + height, 0, 1, 0, 1, color, this.scissorStack.peekLast())); From 2b5bcf4098e0ed77d6f0660922251aa103743fa9 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Mon, 28 Sep 2026 21:55:47 +0200 Subject: [PATCH 09/11] Draw glass on every version 1.21.6 to 1.21.10 draw glass as 1.21.11 does, without samplers, and the glass shader imports vanilla's transforms block, whose layout differs between them. 1.21.4 and 1.21.5 draw straight into the framebuffer, so the game is saved before the screen blurs it, and glass is a render layer: with a pipeline on 1.21.5 and a shader program on 1.21.4. Co-Authored-By: Claude Opus 5.5 --- common | 2 +- .../saturnclient/impl/ui/GlassRenderer.java | 229 +++++++++++++++++- .../saturnclient/impl/ui/RenderScopeImpl.java | 38 ++- .../impl/ui/SaturnScreenFabric.java | 4 + .../saturnclient/shaders/core/glass.fsh | 11 +- .../saturnclient/shaders/core/glass.json | 13 + .../shaders/core/glass_pre_1_21_6.fsh | 27 +++ 7 files changed, 297 insertions(+), 27 deletions(-) create mode 100644 src/main/resources/assets/saturnclient/shaders/core/glass.json create mode 100644 src/main/resources/assets/saturnclient/shaders/core/glass_pre_1_21_6.fsh diff --git a/common b/common index 7bc3081..524fc36 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 7bc308155e882743badc71e8465ec8e913022c0a +Subproject commit 524fc36bf333f37796516e267e5e8de8d5030f7e diff --git a/src/main/java/org/saturnclient/impl/ui/GlassRenderer.java b/src/main/java/org/saturnclient/impl/ui/GlassRenderer.java index cee6ba6..f552152 100644 --- a/src/main/java/org/saturnclient/impl/ui/GlassRenderer.java +++ b/src/main/java/org/saturnclient/impl/ui/GlassRenderer.java @@ -1,6 +1,20 @@ package org.saturnclient.impl.ui; -//? if >=1.21.11 { +// Glass: a copy of the game behind the screen, blurred, drawn through each panel's rounded rectangle +// texture as a mask. There's a copy for each blur strength in use (menus and widgets have their own), +// blurred with vanilla's blur at that radius, so it holds the world or panorama without the screen's +// own animated background blur. +// +// The GUI rewrites split this into three implementations: +// - 1.21.6+: the GUI is drawn after the screen builds it, so the copy is taken from the main +// framebuffer while the screen builds its GUI, and glass is a GUI element with its own pipeline +// (shaders/core/glass.fsh). The blur is vanilla's effect rebuilt with the radius set. +// - 1.21.5: the screen draws straight into the framebuffer, blurring it first, so the game is saved +// before the blur (saveBackground). Glass is a render layer with its own pipeline +// (shaders/core/glass_pre_1_21_6.fsh), and the blur takes the radius as it runs. +// - 1.21.4: as 1.21.5, with GL framebuffers copied by blitting, and a shader program +// (shaders/core/glass.json) instead of a pipeline. +//? if >=1.21.6 { import java.io.Reader; import java.util.HashMap; import java.util.Map; @@ -17,22 +31,17 @@ import com.mojang.serialization.JsonOps; import org.saturnclient.saturnclient.SaturnClient; import net.minecraft.client.gl.Framebuffer; -import net.minecraft.client.gl.GpuSampler; import net.minecraft.client.gl.PostEffectPipeline; import net.minecraft.client.gl.PostEffectProcessor; import net.minecraft.client.gl.RenderPipelines; import net.minecraft.client.gl.SimpleFramebuffer; import net.minecraft.client.render.DefaultFramebufferSet; import net.minecraft.client.render.ProjectionMatrix2; +import net.minecraft.client.texture.AbstractTexture; import net.minecraft.client.texture.TextureSetup; import net.minecraft.client.util.Pool; import net.minecraft.util.Identifier; -// Glass: a copy of the game behind the screen, blurred, drawn through each panel's rounded rectangle -// texture (shaders/core/glass.fsh). There's a copy for each blur strength in use (menus and widgets -// have their own), made while the screen builds its GUI, before the GUI is drawn, so it holds the -// world or panorama alone, without the screen's own animated background blur. -// TODO: glass before 1.21.11 (#23). Those versions draw translucent panels without it. public final class GlassRenderer { public static final RenderPipeline PIPELINE = RenderPipeline.builder(RenderPipelines.POSITION_TEX_COLOR_SNIPPET) .withLocation(Identifier.of("saturnclient", "pipeline/glass")) @@ -52,8 +61,8 @@ public final class GlassRenderer { private GlassRenderer() { } - // Copies the main framebuffer and blurs the copy by blur. Call once a frame for each blur, before - // drawing glass with it. + // Copies the main framebuffer and blurs the copy by blur. Call once a frame for each blur, while + // the screen builds its GUI, before drawing glass with it. public static void captureBackdrop(int blur) { Framebuffer main = SaturnClient.client.getFramebuffer(); int width = main.textureWidth; @@ -66,6 +75,8 @@ public final class GlassRenderer { } else if (backdrop.textureWidth != width || backdrop.textureHeight != height) { backdrop.resize(width, height); } + //? if <1.21.11 + //backdrop.getColorAttachment().setTextureFilter(FilterMode.LINEAR, false); RenderSystem.getDevice().createCommandEncoder().copyTextureToTexture( main.getColorAttachment(), backdrop.getColorAttachment(), 0, 0, 0, 0, 0, width, height); @@ -78,9 +89,12 @@ public final class GlassRenderer { } // The mask, then the copy blurred by blur, as the glass pipeline samples them - public static TextureSetup textures(GpuTextureView mask, GpuSampler maskSampler, int blur) { - return TextureSetup.of(mask, maskSampler, BACKDROPS.get(blur).getColorAttachmentView(), - RenderSystem.getSamplerCache().get(FilterMode.LINEAR)); + public static TextureSetup textures(AbstractTexture mask, int blur) { + GpuTextureView backdrop = BACKDROPS.get(blur).getColorAttachmentView(); + //? if >=1.21.11 + return TextureSetup.of(mask.getGlTextureView(), mask.getSampler(), backdrop, RenderSystem.getSamplerCache().get(FilterMode.LINEAR)); + //? if <1.21.11 + //return TextureSetup.of(mask.getGlTextureView(), backdrop); } private static PostEffectProcessor getBlur(int blur) { @@ -119,4 +133,193 @@ public final class GlassRenderer { } } } -//?} +//?} else if >=1.21.5 { +/*import java.util.HashMap; +import java.util.Map; + +import com.mojang.blaze3d.pipeline.RenderPipeline; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.textures.FilterMode; + +import org.saturnclient.saturnclient.SaturnClient; + +import net.minecraft.client.gl.Framebuffer; +import net.minecraft.client.gl.PostEffectProcessor; +import net.minecraft.client.gl.RenderPipelines; +import net.minecraft.client.gl.SimpleFramebuffer; +import net.minecraft.client.render.DefaultFramebufferSet; +import net.minecraft.client.render.RenderLayer; +import net.minecraft.client.render.RenderPhase; +import net.minecraft.client.util.Pool; +import net.minecraft.util.Identifier; + +public final class GlassRenderer { + private static final RenderPipeline PIPELINE = RenderPipeline.builder(RenderPipelines.POSITION_TEX_COLOR_SNIPPET) + .withLocation(Identifier.of("saturnclient", "pipeline/glass")) + .withFragmentShader(Identifier.of("saturnclient", "core/glass_pre_1_21_6")) + .withSampler("Sampler1") + .build(); + + private static final Identifier BLUR = Identifier.ofVanilla("blur"); + // For the blur's intermediate target, freed once unused for a few frames + private static final Pool POOL = new Pool(3); + + private static final Map BACKDROPS = new HashMap<>(); + // By mask and blur + private static final Map LAYERS = new HashMap<>(); + // The game behind the screen before the screen blurs it, which glass is blurred from + private static SimpleFramebuffer background; + + private GlassRenderer() { + } + + // Saves the main framebuffer for glass. Call each frame before the screen blurs its background. + public static void saveBackground() { + background = copy(SaturnClient.client.getFramebuffer(), background, "Saturn glass background"); + } + + // Copies the saved background and blurs the copy by blur. Call once a frame for each blur, before + // drawing glass with it. + public static void captureBackdrop(int blur) { + Framebuffer from = background != null ? background : SaturnClient.client.getFramebuffer(); + SimpleFramebuffer backdrop = copy(from, BACKDROPS.get(blur), "Saturn glass " + blur); + BACKDROPS.put(blur, backdrop); + + PostEffectProcessor processor = SaturnClient.client.getShaderLoader().loadPostEffect(BLUR, + DefaultFramebufferSet.MAIN_ONLY); + if (processor != null) { + processor.render(backdrop, POOL, pass -> pass.setUniform("Radius", (float) blur)); + } + POOL.decrementLifespan(); + } + + // Draws glass through mask with the copy blurred by blur + public static RenderLayer getLayer(Identifier mask, int blur) { + return LAYERS.computeIfAbsent(mask + " " + blur, key -> RenderLayer.of("saturn_glass", 786432, PIPELINE, + RenderLayer.MultiPhaseParameters.builder() + .texture(new RenderPhase.TextureBase(() -> { + RenderSystem.setShaderTexture(0, + SaturnClient.client.getTextureManager().getTexture(mask).getGlTexture()); + RenderSystem.setShaderTexture(1, BACKDROPS.get(blur).getColorAttachment()); + }, () -> { + })) + .build(false))); + } + + // Copies from into to, made or resized to match + private static SimpleFramebuffer copy(Framebuffer from, SimpleFramebuffer to, String name) { + int width = from.textureWidth; + int height = from.textureHeight; + + if (to == null) { + to = new SimpleFramebuffer(name, width, height, false); + } else if (to.textureWidth != width || to.textureHeight != height) { + to.resize(width, height); + } + to.getColorAttachment().setTextureFilter(FilterMode.LINEAR, false); + + RenderSystem.getDevice().createCommandEncoder().copyTextureToTexture( + from.getColorAttachment(), to.getColorAttachment(), 0, 0, 0, 0, 0, width, height); + return to; + } +} +*///?} else { +/*import java.util.HashMap; +import java.util.Map; + +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL30; + +import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.systems.RenderSystem; + +import org.saturnclient.saturnclient.SaturnClient; + +import net.minecraft.client.gl.Defines; +import net.minecraft.client.gl.Framebuffer; +import net.minecraft.client.gl.PostEffectProcessor; +import net.minecraft.client.gl.ShaderProgramKey; +import net.minecraft.client.gl.SimpleFramebuffer; +import net.minecraft.client.render.DefaultFramebufferSet; +import net.minecraft.client.render.RenderLayer; +import net.minecraft.client.render.RenderPhase; +import net.minecraft.client.render.VertexFormat; +import net.minecraft.client.render.VertexFormats; +import net.minecraft.client.util.Pool; +import net.minecraft.util.Identifier; + +public final class GlassRenderer { + private static final ShaderProgramKey PROGRAM = new ShaderProgramKey(Identifier.of("saturnclient", "core/glass"), + VertexFormats.POSITION_TEXTURE_COLOR, Defines.EMPTY); + + private static final Identifier BLUR = Identifier.ofVanilla("blur"); + // For the blur's intermediate target, freed once unused for a few frames + private static final Pool POOL = new Pool(3); + + private static final Map BACKDROPS = new HashMap<>(); + // By mask and blur + private static final Map LAYERS = new HashMap<>(); + // The game behind the screen before the screen blurs it, which glass is blurred from + private static SimpleFramebuffer background; + + private GlassRenderer() { + } + + // Saves the main framebuffer for glass. Call each frame before the screen blurs its background. + public static void saveBackground() { + background = copy(SaturnClient.client.getFramebuffer(), background); + } + + // Copies the saved background and blurs the copy by blur. Call once a frame for each blur, before + // drawing glass with it. + public static void captureBackdrop(int blur) { + Framebuffer from = background != null ? background : SaturnClient.client.getFramebuffer(); + SimpleFramebuffer backdrop = copy(from, BACKDROPS.get(blur)); + BACKDROPS.put(blur, backdrop); + + PostEffectProcessor processor = SaturnClient.client.getShaderLoader().loadPostEffect(BLUR, + DefaultFramebufferSet.MAIN_ONLY); + if (processor != null) { + processor.setUniforms("Radius", blur); + processor.render(backdrop, POOL); + } + POOL.decrementLifespan(); + SaturnClient.client.getFramebuffer().beginWrite(false); + } + + // Draws glass through mask with the copy blurred by blur + public static RenderLayer getLayer(Identifier mask, int blur) { + return LAYERS.computeIfAbsent(mask + " " + blur, key -> RenderLayer.of("saturn_glass", + VertexFormats.POSITION_TEXTURE_COLOR, VertexFormat.DrawMode.QUADS, 786432, + RenderLayer.MultiPhaseParameters.builder() + .texture(new RenderPhase.TextureBase(() -> { + RenderSystem.setShaderTexture(0, mask); + RenderSystem.setShaderTexture(1, BACKDROPS.get(blur).getColorAttachment()); + }, () -> { + })) + .program(new RenderPhase.ShaderProgram(PROGRAM)) + .transparency(RenderPhase.TRANSLUCENT_TRANSPARENCY) + .build(false))); + } + + // Copies from into to, made or resized to match + private static SimpleFramebuffer copy(Framebuffer from, SimpleFramebuffer to) { + int width = from.textureWidth; + int height = from.textureHeight; + + if (to == null) { + to = new SimpleFramebuffer(width, height, false); + } else if (to.textureWidth != width || to.textureHeight != height) { + to.resize(width, height); + } + to.setTexFilter(GL11.GL_LINEAR); + + GlStateManager._glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, from.fbo); + GlStateManager._glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, to.fbo); + GlStateManager._glBlitFrameBuffer(0, 0, width, height, 0, 0, width, height, GL11.GL_COLOR_BUFFER_BIT, + GL11.GL_NEAREST); + SaturnClient.client.getFramebuffer().beginWrite(false); + return to; + } +} +*///?} diff --git a/src/main/java/org/saturnclient/impl/ui/RenderScopeImpl.java b/src/main/java/org/saturnclient/impl/ui/RenderScopeImpl.java index d9004bc..3398516 100644 --- a/src/main/java/org/saturnclient/impl/ui/RenderScopeImpl.java +++ b/src/main/java/org/saturnclient/impl/ui/RenderScopeImpl.java @@ -42,7 +42,6 @@ import net.minecraft.client.render.entity.state.EntityRenderState; import net.minecraft.client.render.item.KeyedItemRenderState; //?} else //import net.minecraft.client.render.item.ItemRenderState; -//? if >=1.21.11 import net.minecraft.client.texture.AbstractTexture; import net.minecraft.client.texture.Sprite; import net.minecraft.client.texture.TextureSetup; @@ -63,10 +62,8 @@ public class RenderScopeImpl implements RenderScope { private ScissorStack scissorStack; private int opacity = 0xFF << 24; - //? if >=1.21.11 { // The glass blurs made for this scope's frame private final Set capturedBlurs = new HashSet<>(); - //?} public RenderScopeImpl(DrawContext context) { this(context.getMatrices(), ((DrawContextAccessor) (Object) context).getState()); @@ -236,7 +233,6 @@ public class RenderScopeImpl implements RenderScope { this.scissorStack.peekLast())); } - //? if >=1.21.11 { @Override public void drawGlass(IdentifierRef mask, int x, int y, int width, int height, int blur) { int color = getColor(0xFFFFFFFF); @@ -250,12 +246,11 @@ public class RenderScopeImpl implements RenderScope { AbstractTexture maskTexture = SaturnClient.client.getTextureManager().getTexture((Identifier) (Object) mask); this.state.addSimpleElement(new TexturedQuadGuiElementRenderState( GlassRenderer.PIPELINE, - GlassRenderer.textures(maskTexture.getGlTextureView(), maskTexture.getSampler(), blur), + GlassRenderer.textures(maskTexture, blur), new Matrix3x2f(this.matrices.stack), x, y, x + width, y + height, 0, 1, 0, 1, color, this.scissorStack.peekLast())); } - //?} // Scissor management @Override @@ -395,7 +390,9 @@ public class RenderScopeImpl implements RenderScope { //?} else { /*import java.util.ArrayDeque; import java.util.Deque; +import java.util.HashSet; import java.util.Objects; +import java.util.Set; import java.util.function.Consumer; import org.jetbrains.annotations.Nullable; @@ -448,6 +445,8 @@ public class RenderScopeImpl implements RenderScope { public VertexConsumerProvider.Immediate vertexConsumers; private ScissorStack scissorStack = new ScissorStack(); private int opacity = 255 << 24; + // The glass blurs made for this scope's frame + private final Set capturedBlurs = new HashSet<>(); private final ItemRenderState itemRenderState; // ---------------------------- @@ -687,6 +686,33 @@ public class RenderScopeImpl implements RenderScope { matrices.pop(); } + @Override + public void drawGlass(IdentifierRef mask, int x, int y, int width, int height, int blur) { + int color = getColor(0xFFFFFFFF); + if (mask == null || color >>> 24 == 0) + return; + + if (capturedBlurs.add(blur)) { + GlassRenderer.captureBackdrop(blur); + } + + // In quarter units, as drawTexturedQuad draws + int x1 = x * 4; + int x2 = (x + width) * 4; + int y1 = y * 4; + int y2 = (y + height) * 4; + + matrices.push(); + matrices.scale(0.25f, 0.25f, 1.0f); + Matrix4f matrix4f = matrices.peek().getPositionMatrix(); + VertexConsumer vertexConsumer = vertexConsumers.getBuffer(GlassRenderer.getLayer((Identifier) (Object) mask, blur)); + vertexConsumer.vertex(matrix4f, x1, y1, 0).texture(0, 0).color(color); + vertexConsumer.vertex(matrix4f, x1, y2, 0).texture(0, 1).color(color); + vertexConsumer.vertex(matrix4f, x2, y2, 0).texture(1, 1).color(color); + vertexConsumer.vertex(matrix4f, x2, y1, 0).texture(1, 0).color(color); + matrices.pop(); + } + // ---------------------------- // Items // ---------------------------- diff --git a/src/main/java/org/saturnclient/impl/ui/SaturnScreenFabric.java b/src/main/java/org/saturnclient/impl/ui/SaturnScreenFabric.java index bfaa041..7f17d03 100644 --- a/src/main/java/org/saturnclient/impl/ui/SaturnScreenFabric.java +++ b/src/main/java/org/saturnclient/impl/ui/SaturnScreenFabric.java @@ -97,6 +97,10 @@ public class SaturnScreenFabric extends Screen implements ScreenProvider { ROTATING_PANORAMA_RENDERER.render(context, this.width, this.height, screen.backgroundOpacity, delta); } + // Glass is blurred from the game as it is before this screen blurs it. + context.draw(); + GlassRenderer.saveBackground(); + PostEffectProcessor postEffectProcessor = this.client.getShaderLoader().loadPostEffect( Identifier.ofVanilla("blur"), DefaultFramebufferSet.MAIN_ONLY); diff --git a/src/main/resources/assets/saturnclient/shaders/core/glass.fsh b/src/main/resources/assets/saturnclient/shaders/core/glass.fsh index fe6f02b..7d5ee7e 100644 --- a/src/main/resources/assets/saturnclient/shaders/core/glass.fsh +++ b/src/main/resources/assets/saturnclient/shaders/core/glass.fsh @@ -1,13 +1,10 @@ #version 330 -// Saturn's glass: the blurred game behind the screen, cut to a panel's shape. +// Saturn's glass: the blurred game behind the screen, cut to a panel's shape. For 1.21.6 and +// later; glass_pre_1_21_6.fsh (with glass.json, the program 1.21.4 loads) is for older versions. -layout(std140) uniform DynamicTransforms { - mat4 ModelViewMat; - vec4 ColorModulator; - vec3 ModelOffset; - mat4 TextureMat; -}; +// Vanilla's own, since its layout changes between versions (1.21.11 dropped LineWidth) +#moj_import // The panel's rounded rectangle, the same texture its background is drawn with uniform sampler2D Sampler0; diff --git a/src/main/resources/assets/saturnclient/shaders/core/glass.json b/src/main/resources/assets/saturnclient/shaders/core/glass.json new file mode 100644 index 0000000..6aaac85 --- /dev/null +++ b/src/main/resources/assets/saturnclient/shaders/core/glass.json @@ -0,0 +1,13 @@ +{ + "vertex": "minecraft:core/position_tex_color", + "fragment": "saturnclient:core/glass_pre_1_21_6", + "samplers": [ + { "name": "Sampler0" }, + { "name": "Sampler1" } + ], + "uniforms": [ + { "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, + { "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, + { "name": "ColorModulator", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] } + ] +} diff --git a/src/main/resources/assets/saturnclient/shaders/core/glass_pre_1_21_6.fsh b/src/main/resources/assets/saturnclient/shaders/core/glass_pre_1_21_6.fsh new file mode 100644 index 0000000..e529808 --- /dev/null +++ b/src/main/resources/assets/saturnclient/shaders/core/glass_pre_1_21_6.fsh @@ -0,0 +1,27 @@ +#version 150 + +// Saturn's glass (see glass.fsh), for 1.21.4 and 1.21.5, which set uniforms one by one instead of in +// blocks. + +uniform vec4 ColorModulator; + +// The panel's rounded rectangle, the same texture its background is drawn with +uniform sampler2D Sampler0; +// The game behind the screen, blurred, the size of the framebuffer +uniform sampler2D Sampler1; + +in vec2 texCoord0; +in vec4 vertexColor; + +out vec4 fragColor; + +void main() { + float mask = texture(Sampler0, texCoord0).a * vertexColor.a; + if (mask == 0.0) { + discard; + } + + // The blurred copy lines up with the framebuffer being drawn to. + vec2 screenUv = gl_FragCoord.xy / vec2(textureSize(Sampler1, 0)); + fragColor = vec4(texture(Sampler1, screenUv).rgb, mask) * ColorModulator; +} From 254a37d757339d72579b6ab94343f43989c56f7f Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Mon, 28 Sep 2026 21:57:10 +0200 Subject: [PATCH 10/11] Skip glass and blur work that draws nothing Before 1.21.6 the game is only saved for glass when glass can be drawn, and a background blur of 0 no longer runs the blur. Every version frees the copies (and from 1.21.6 the blurs) of glass strengths no longer set, instead of keeping one per strength ever used. Co-Authored-By: Claude Opus 5.5 --- common | 2 +- .../saturnclient/impl/ui/GlassRenderer.java | 52 +++++++++++++++++++ .../impl/ui/SaturnScreenFabric.java | 11 ++-- 3 files changed, 60 insertions(+), 5 deletions(-) diff --git a/common b/common index 524fc36..7cc37b7 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 524fc36bf333f37796516e267e5e8de8d5030f7e +Subproject commit 7cc37b7c7455caea920981f982823af16810898a diff --git a/src/main/java/org/saturnclient/impl/ui/GlassRenderer.java b/src/main/java/org/saturnclient/impl/ui/GlassRenderer.java index f552152..37da9f3 100644 --- a/src/main/java/org/saturnclient/impl/ui/GlassRenderer.java +++ b/src/main/java/org/saturnclient/impl/ui/GlassRenderer.java @@ -28,6 +28,7 @@ import com.mojang.blaze3d.textures.FilterMode; import com.mojang.blaze3d.textures.GpuTextureView; import com.mojang.serialization.JsonOps; +import org.saturnclient.config.Config; import org.saturnclient.saturnclient.SaturnClient; import net.minecraft.client.gl.Framebuffer; @@ -86,6 +87,7 @@ public final class GlassRenderer { processor.render(backdrop, POOL); } POOL.decrementLifespan(); + release(blur); } // The mask, then the copy blurred by blur, as the glass pipeline samples them @@ -97,6 +99,28 @@ public final class GlassRenderer { //return TextureSetup.of(mask.getGlTextureView(), backdrop); } + // Frees the copies and blurs of strengths that are no longer set, besides kept + private static void release(int kept) { + BACKDROPS.entrySet().removeIf(entry -> { + boolean unused = entry.getKey() != kept && !isSet(entry.getKey()); + if (unused) { + entry.getValue().delete(); + } + return unused; + }); + BLURS.entrySet().removeIf(entry -> { + boolean unused = entry.getKey() != kept && !isSet(entry.getKey()); + if (unused && entry.getValue() != null) { + entry.getValue().close(); + } + return unused; + }); + } + + private static boolean isSet(int blur) { + return blur == Config.menuBlur.value || blur == Config.widgetBlur.value; + } + private static PostEffectProcessor getBlur(int blur) { if (!BLURS.containsKey(blur)) { BLURS.put(blur, loadBlur(blur)); @@ -141,6 +165,7 @@ import com.mojang.blaze3d.pipeline.RenderPipeline; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.textures.FilterMode; +import org.saturnclient.config.Config; import org.saturnclient.saturnclient.SaturnClient; import net.minecraft.client.gl.Framebuffer; @@ -191,6 +216,7 @@ public final class GlassRenderer { processor.render(backdrop, POOL, pass -> pass.setUniform("Radius", (float) blur)); } POOL.decrementLifespan(); + release(blur); } // Draws glass through mask with the copy blurred by blur @@ -206,6 +232,18 @@ public final class GlassRenderer { .build(false))); } + // Frees the copies of strengths that are no longer set, besides kept + private static void release(int kept) { + BACKDROPS.entrySet().removeIf(entry -> { + boolean unused = entry.getKey() != kept && entry.getKey() != Config.menuBlur.value + && entry.getKey() != Config.widgetBlur.value; + if (unused) { + entry.getValue().delete(); + } + return unused; + }); + } + // Copies from into to, made or resized to match private static SimpleFramebuffer copy(Framebuffer from, SimpleFramebuffer to, String name) { int width = from.textureWidth; @@ -233,6 +271,7 @@ import org.lwjgl.opengl.GL30; import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.systems.RenderSystem; +import org.saturnclient.config.Config; import org.saturnclient.saturnclient.SaturnClient; import net.minecraft.client.gl.Defines; @@ -284,6 +323,7 @@ public final class GlassRenderer { processor.render(backdrop, POOL); } POOL.decrementLifespan(); + release(blur); SaturnClient.client.getFramebuffer().beginWrite(false); } @@ -302,6 +342,18 @@ public final class GlassRenderer { .build(false))); } + // Frees the copies of strengths that are no longer set, besides kept + private static void release(int kept) { + BACKDROPS.entrySet().removeIf(entry -> { + boolean unused = entry.getKey() != kept && entry.getKey() != Config.menuBlur.value + && entry.getKey() != Config.widgetBlur.value; + if (unused) { + entry.getValue().delete(); + } + return unused; + }); + } + // Copies from into to, made or resized to match private static SimpleFramebuffer copy(Framebuffer from, SimpleFramebuffer to) { int width = from.textureWidth; diff --git a/src/main/java/org/saturnclient/impl/ui/SaturnScreenFabric.java b/src/main/java/org/saturnclient/impl/ui/SaturnScreenFabric.java index 7f17d03..908ad91 100644 --- a/src/main/java/org/saturnclient/impl/ui/SaturnScreenFabric.java +++ b/src/main/java/org/saturnclient/impl/ui/SaturnScreenFabric.java @@ -3,6 +3,7 @@ package org.saturnclient.impl.ui; import java.time.Duration; import java.time.Instant; +import org.saturnclient.config.Config; import org.saturnclient.ui.RenderScope; import org.saturnclient.ui.SaturnScreen; import org.saturnclient.ui.SaturnScreen.ScreenProvider; @@ -98,15 +99,17 @@ public class SaturnScreenFabric extends Screen implements ScreenProvider { } // Glass is blurred from the game as it is before this screen blurs it. - context.draw(); - GlassRenderer.saveBackground(); + if (Config.usesGlass()) { + context.draw(); + GlassRenderer.saveBackground(); + } - PostEffectProcessor postEffectProcessor = this.client.getShaderLoader().loadPostEffect( + float radius = screen.getBackgroundBlur(); + PostEffectProcessor postEffectProcessor = radius < 1 ? null : this.client.getShaderLoader().loadPostEffect( Identifier.ofVanilla("blur"), DefaultFramebufferSet.MAIN_ONLY); if (postEffectProcessor != null) { - float radius = screen.getBackgroundBlur(); //? if >=1.21.5 postEffectProcessor.render(this.client.getFramebuffer(), this.pool, pass -> pass.setUniform("Radius", radius)); //? if <1.21.5 From 06190d0ef9a0586d0f07455c66dd38129ae644e9 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Mon, 28 Sep 2026 22:01:43 +0200 Subject: [PATCH 11/11] Point common at its master merge commit Co-Authored-By: Claude Opus 5.5 --- common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common b/common index 7cc37b7..7d0bfd2 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 7cc37b7c7455caea920981f982823af16810898a +Subproject commit 7d0bfd26c9f19294b082887beacb6efc6d4c0778