From 835c48c346a5e68642e8440f5799e4731d4417a8 Mon Sep 17 00:00:00 2001 From: Nathan Scheele Date: Mon, 27 Jul 2026 12:48:18 -0400 Subject: [PATCH 1/2] Clear the default framebuffer before compositing The scene blit only covers the scene viewport, and drawUi() preserves destination RGB wherever the UI texture is transparent. On macOS, rlawt recycles IOSurface back buffers without clearing them, so stale frame content could show through regions neither pass covers, e.g. the margins around the 3D viewport in fixed display mode. Clear the default framebuffer at the top of the composite path so every region starts from black. Fixes #1124. Co-Authored-By: Claude Fable 5 --- .../java/rs117/hd/renderer/zone/ZoneRenderer.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/rs117/hd/renderer/zone/ZoneRenderer.java b/src/main/java/rs117/hd/renderer/zone/ZoneRenderer.java index 01c1f2f0a3..4eb7b67fa7 100644 --- a/src/main/java/rs117/hd/renderer/zone/ZoneRenderer.java +++ b/src/main/java/rs117/hd/renderer/zone/ZoneRenderer.java @@ -1134,6 +1134,14 @@ public void draw(int overlayColor) { scenePass(); } + // Clear the default framebuffer before drawing anything. + // On macOS, rlawt may hand back a dirty back buffer, which would otherwise + // show through regions the scene blit and opaque UI don't cover + // (e.g. fixed-mode margins). See #1124 + glBindFramebuffer(GL_FRAMEBUFFER, plugin.awtContext.getFramebuffer(false)); + glClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT); + if (sceneFboValid && plugin.sceneResolution != null && plugin.sceneViewport != null) { glBindFramebuffer(GL_READ_FRAMEBUFFER, plugin.fboScene); if (plugin.fboSceneResolve != 0) { @@ -1161,10 +1169,6 @@ public void draw(int overlayColor) { GL_COLOR_BUFFER_BIT, config.sceneScalingMode().glFilter ); - } else { - glBindFramebuffer(GL_FRAMEBUFFER, plugin.awtContext.getFramebuffer(false)); - glClearColor(0, 0, 0, 1); - glClear(GL_COLOR_BUFFER_BIT); } plugin.drawUi(overlayColor); From 401937770e7586ad187f213c33a3b0c864635206 Mon Sep 17 00:00:00 2001 From: Hooder Date: Wed, 29 Jul 2026 18:26:30 +0200 Subject: [PATCH 2/2] Rework this slightly --- .../rs117/hd/renderer/zone/ZoneRenderer.java | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/main/java/rs117/hd/renderer/zone/ZoneRenderer.java b/src/main/java/rs117/hd/renderer/zone/ZoneRenderer.java index 4eb7b67fa7..68fadcf287 100644 --- a/src/main/java/rs117/hd/renderer/zone/ZoneRenderer.java +++ b/src/main/java/rs117/hd/renderer/zone/ZoneRenderer.java @@ -75,6 +75,7 @@ import static net.runelite.api.Perspective.*; import static org.lwjgl.opengl.GL33C.*; import static org.lwjgl.opengl.GL40.GL_DRAW_INDIRECT_BUFFER; +import static rs117.hd.HdPlugin.APPLE; import static rs117.hd.HdPlugin.COLOR_FILTER_FADE_DURATION; import static rs117.hd.HdPlugin.NEAR_PLANE; import static rs117.hd.HdPlugin.ORTHOGRAPHIC_ZOOM; @@ -1134,14 +1135,6 @@ public void draw(int overlayColor) { scenePass(); } - // Clear the default framebuffer before drawing anything. - // On macOS, rlawt may hand back a dirty back buffer, which would otherwise - // show through regions the scene blit and opaque UI don't cover - // (e.g. fixed-mode margins). See #1124 - glBindFramebuffer(GL_FRAMEBUFFER, plugin.awtContext.getFramebuffer(false)); - glClearColor(0, 0, 0, 1); - glClear(GL_COLOR_BUFFER_BIT); - if (sceneFboValid && plugin.sceneResolution != null && plugin.sceneViewport != null) { glBindFramebuffer(GL_READ_FRAMEBUFFER, plugin.fboScene); if (plugin.fboSceneResolve != 0) { @@ -1157,6 +1150,15 @@ public void draw(int overlayColor) { // Blit from the resolved FBO to the default FBO glBindFramebuffer(GL_DRAW_FRAMEBUFFER, plugin.awtContext.getFramebuffer(false)); + + if (APPLE && !client.isResized()) { + // On macOS, we need to ensure that the alpha channel is opaque to prevent whatever + // is beneath from leaking through. In fixed mode, the MSAA resolve alone is not + // sufficient, since the viewport only covers part of the screen. + glClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT); + } + glBlitFramebuffer( 0, 0, @@ -1169,6 +1171,10 @@ public void draw(int overlayColor) { GL_COLOR_BUFFER_BIT, config.sceneScalingMode().glFilter ); + } else { + glBindFramebuffer(GL_FRAMEBUFFER, plugin.awtContext.getFramebuffer(false)); + glClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT); } plugin.drawUi(overlayColor);