diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GLUtil.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GLUtil.java index 65da9b96e9..50f65ef233 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GLUtil.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GLUtil.java @@ -31,6 +31,7 @@ class GLUtil private static final int ERR_LEN = 1024; private static final int[] buf = new int[1]; + private static final float[] fbuf = new float[1]; static int glGetInteger(GL4 gl, int pname) { @@ -38,6 +39,12 @@ class GLUtil return buf[0]; } + static float glGetFloat(GL4 gl, int pname) + { + gl.glGetFloatv(pname, fbuf, 0); + return fbuf[0]; + } + static int glGetShader(GL4 gl, int shader, int pname) { gl.glGetShaderiv(shader, pname, buf, 0); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java index ef9b60e6df..3d751d9427 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java @@ -243,6 +243,7 @@ public class GpuPlugin extends Plugin implements DrawCallbacks private int lastStretchedCanvasWidth; private int lastStretchedCanvasHeight; private AntiAliasingMode lastAntiAliasingMode; + private int lastAnisotropicFilteringLevel = -1; private int centerX; private int centerY; @@ -1106,6 +1107,15 @@ public class GpuPlugin extends Plugin implements DrawCallbacks int renderViewportHeight = viewportHeight; int renderViewportWidth = viewportWidth; + // Setup anisotropic filtering + final int anisotropicFilteringLevel = config.anisotropicFilteringLevel(); + + if (textureArrayId != -1 && lastAnisotropicFilteringLevel != anisotropicFilteringLevel) + { + textureManager.setAnisotropicFilteringLevel(textureArrayId, anisotropicFilteringLevel, gl); + lastAnisotropicFilteringLevel = anisotropicFilteringLevel; + } + if (client.isStretchedEnabled()) { Dimension dim = client.getStretchedDimensions(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPluginConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPluginConfig.java index d804624976..c432ba4ad7 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPluginConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPluginConfig.java @@ -108,4 +108,19 @@ public interface GpuPluginConfig extends Config { return true; } + + @Range( + min = 0, + max = 16 + ) + @ConfigItem( + keyName = "anisotropicFilteringLevel", + name = "Anisotropic Filtering", + description = "Configures the anisotropic filtering level.", + position = 7 + ) + default int anisotropicFilteringLevel() + { + return 0; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/TextureManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/TextureManager.java index 82caedafc5..351eba2823 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/TextureManager.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/TextureManager.java @@ -51,7 +51,7 @@ class TextureManager int textureArrayId = GLUtil.glGenTexture(gl); gl.glBindTexture(gl.GL_TEXTURE_2D_ARRAY, textureArrayId); - gl.glTexStorage3D(gl.GL_TEXTURE_2D_ARRAY, 1, gl.GL_RGBA8, TEXTURE_SIZE, TEXTURE_SIZE, textures.length); + gl.glTexStorage3D(gl.GL_TEXTURE_2D_ARRAY, 8, gl.GL_RGBA8, TEXTURE_SIZE, TEXTURE_SIZE, textures.length); gl.glTexParameteri(gl.GL_TEXTURE_2D_ARRAY, gl.GL_TEXTURE_MIN_FILTER, gl.GL_NEAREST); gl.glTexParameteri(gl.GL_TEXTURE_2D_ARRAY, gl.GL_TEXTURE_MAG_FILTER, gl.GL_NEAREST); @@ -68,11 +68,40 @@ class TextureManager gl.glActiveTexture(gl.GL_TEXTURE1); gl.glBindTexture(gl.GL_TEXTURE_2D_ARRAY, textureArrayId); + gl.glGenerateMipmap(gl.GL_TEXTURE_2D_ARRAY); gl.glActiveTexture(gl.GL_TEXTURE0); return textureArrayId; } + void setAnisotropicFilteringLevel(int textureArrayId, int level, GL4 gl) + { + gl.glBindTexture(gl.GL_TEXTURE_2D_ARRAY, textureArrayId); + + //level = 0 means no mipmaps and no anisotropic filtering + if (level == 0) + { + gl.glTexParameteri(gl.GL_TEXTURE_2D_ARRAY, gl.GL_TEXTURE_MIN_FILTER, gl.GL_NEAREST); + } + //level = 1 means with mipmaps but without anisotropic filtering GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT defaults to 1.0 which is off + //level > 1 enables anisotropic filtering. It's up to the vendor what the values mean + //Even if anisotropic filtering isn't supported, mipmaps will be enabled with any level >= 1 + else + { + // Set on GL_NEAREST_MIPMAP_LINEAR (bilinear filtering with mipmaps) since the pixel nature of the game means that nearest filtering + // looks best for objects up close but allows linear filtering to resolve possible aliasing and noise with mipmaps from far away objects. + gl.glTexParameteri(gl.GL_TEXTURE_2D_ARRAY, gl.GL_TEXTURE_MIN_FILTER, gl.GL_NEAREST_MIPMAP_LINEAR); + } + + if (gl.isExtensionAvailable("GL_EXT_texture_filter_anisotropic")) + { + final float maxSamples = GLUtil.glGetFloat(gl, gl.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT); + //Clamp from 1 to max GL says it supports. + final float anisoLevel = Math.max(1, Math.min(maxSamples, level)); + gl.glTexParameterf(gl.GL_TEXTURE_2D_ARRAY, gl.GL_TEXTURE_MAX_ANISOTROPY_EXT, anisoLevel); + } + } + void freeTextureArray(GL4 gl, int textureArrayId) { GLUtil.glDeleteTexture(gl, textureArrayId);