From 60e33ec044c999c5b4ee7015860175d40519e901 Mon Sep 17 00:00:00 2001
From: JellySquid <jellysquid@pm.me>
Date: Sat, 18 Jan 2025 12:45:58 -0600
Subject: [PATCH] Use 8 bits of sub-texel precision and fix off-by-one error

---
 .../chunk/shader/DefaultShaderInterface.java  | 28 +++++++------------
 .../format/impl/CompactChunkVertex.java       |  4 +--
 2 files changed, 12 insertions(+), 20 deletions(-)

diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/shader/DefaultShaderInterface.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/shader/DefaultShaderInterface.java
index 5d0bda6be3..f2cb18fcb6 100644
--- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/shader/DefaultShaderInterface.java
+++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/shader/DefaultShaderInterface.java
@@ -5,6 +5,7 @@
 import net.caffeinemc.mods.sodium.client.gl.shader.uniform.GlUniformFloat3v;
 import net.caffeinemc.mods.sodium.client.gl.shader.uniform.GlUniformInt;
 import net.caffeinemc.mods.sodium.client.gl.shader.uniform.GlUniformMatrix4f;
+import net.caffeinemc.mods.sodium.client.render.chunk.vertex.format.impl.CompactChunkVertex;
 import net.caffeinemc.mods.sodium.client.util.TextureUtil;
 import net.caffeinemc.mods.sodium.mixin.core.render.texture.TextureAtlasAccessor;
 import net.minecraft.client.Minecraft;
@@ -20,22 +21,9 @@
  */
 public class DefaultShaderInterface implements ChunkShaderInterface {
     // Direct3D specifies at least 8 bits of sub-texel precision for texture fetches. OpenGL specifies at least
-    // 4 bits of sub-texel precision. Most OpenGL-capable graphics are Direct3D-capable as well, so we could
+    // 4 bits of sub-texel precision. Most OpenGL-capable graphics are Direct3D-capable as well, so we can
     // *probably* assume 8 bits of precision.
-    //
-    // However, in practice, this seems to be a complete mess. The rounding behavior for point-filtering seems to
-    // be defined inconsistently and depends on the shader compiler and hardware implementation. Apple's GL-on-Metal
-    // implementation is the worst of all of them, with a very large epsilon (1.0 / 32.0) being needed to cure
-    // texture seams between blocks.
-    //
-    // Unless we implemented texture filtering in the shader ourselves (i.e. using texelFetch(..)), it is unlikely
-    // we could avoid these issues. And that would not help much in the case of linear interpolation across
-    // mip layers.
-    //
-    // So in other words, this constant is the lowest common denominator we found through evaluation on the target
-    // hardware. It is rather pessimistic to accommodate for Apple's implementation, but does seem to reliably fix
-    // texture seams.
-    private static final int SUB_TEXEL_PRECISION_BITS = 5;
+    private static final int SUB_TEXEL_PRECISION_BITS = 8;
 
     private final Map<ChunkShaderTextureSlot, GlUniformInt> uniformTextures;
 
@@ -71,10 +59,14 @@ public void setupState() {
                 .getTexture(TextureAtlas.LOCATION_BLOCKS);
 
         // There is a limited amount of sub-texel precision when using hardware texture sampling. The mapped texture
-        // area must be "shrunk" by at least one sub-texel to avoid bleed between textures in the atlas.
+        // area must be "shrunk" by at least one sub-texel to avoid bleed between textures in the atlas. And since we
+        // offset texture coordinates in the vertex format by one texel, we also need to undo that here.
+        double subTexelPrecision = (1 << SUB_TEXEL_PRECISION_BITS);
+        double subTexelOffset = 1.0f / CompactChunkVertex.TEXTURE_MAX_VALUE;
+
         this.uniformTexCoordShrink.set(
-                (1.0f / textureAtlas.getWidth()) / (1 << SUB_TEXEL_PRECISION_BITS),
-                (1.0f / textureAtlas.getHeight()) / (1 << SUB_TEXEL_PRECISION_BITS)
+                (float) (subTexelOffset + ((1.0D / textureAtlas.getWidth()) / subTexelPrecision)),
+                (float) (subTexelOffset + ((1.0D / textureAtlas.getHeight()) / subTexelPrecision))
         );
 
         this.fogShader.setup();
diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/vertex/format/impl/CompactChunkVertex.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/vertex/format/impl/CompactChunkVertex.java
index 44c73a8719..410dc0a5c0 100644
--- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/vertex/format/impl/CompactChunkVertex.java
+++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/vertex/format/impl/CompactChunkVertex.java
@@ -18,8 +18,8 @@ public class CompactChunkVertex implements ChunkVertexType {
             .addElement(DefaultChunkMeshAttributes.LIGHT_MATERIAL_INDEX, ChunkShaderBindingPoints.ATTRIBUTE_LIGHT_MATERIAL_INDEX, 16)
             .build();
 
-    private static final int POSITION_MAX_VALUE = 1 << 20;
-    private static final int TEXTURE_MAX_VALUE = 1 << 15;
+    public static final int POSITION_MAX_VALUE = 1 << 20;
+    public static final int TEXTURE_MAX_VALUE = 1 << 15;
 
     private static final float MODEL_ORIGIN = 8.0f;
     private static final float MODEL_RANGE = 32.0f;