diff --git a/cache/pom.xml b/cache/pom.xml index 5e2dff68969..c6d9f6da299 100644 --- a/cache/pom.xml +++ b/cache/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.11.23-SNAPSHOT + 1.12.0-SNAPSHOT cache diff --git a/cache/src/main/java/net/runelite/cache/definitions/NpcDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/NpcDefinition.java index f2397f4e864..69db696418d 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/NpcDefinition.java +++ b/cache/src/main/java/net/runelite/cache/definitions/NpcDefinition.java @@ -78,5 +78,7 @@ public class NpcDefinition public int height = -1; public int[] stats = {1, 1, 1, 1, 1, 1}; public int footprintSize = -1; + public boolean canHideForOverlap; + public int overlapTintHSL = 39188; public boolean unknown1 = false; } diff --git a/cache/src/main/java/net/runelite/cache/definitions/ObjectDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/ObjectDefinition.java index 0b1f07e50a4..3a93d10baee 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/ObjectDefinition.java +++ b/cache/src/main/java/net/runelite/cache/definitions/ObjectDefinition.java @@ -79,6 +79,7 @@ public class ObjectDefinition private int soundFadeOutDuration = 300; private int soundFadeInCurve; private int soundFadeOutCurve; + private int soundVisibility = 2; private int ambientSoundChangeTicksMin = 0; private int ambientSoundChangeTicksMax = 0; private boolean blocksProjectile = true; diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/NpcLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/NpcLoader.java index d76b6bbee83..726148d1385 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/loaders/NpcLoader.java +++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/NpcLoader.java @@ -383,6 +383,14 @@ else if (opcode == 129) { def.unknown1 = true; } + else if (opcode == 145) + { + def.canHideForOverlap = true; + } + else if (opcode == 146) + { + def.overlapTintHSL = stream.readUnsignedShort(); + } else if (opcode == 249) { length = stream.readUnsignedByte(); diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/ObjectLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/ObjectLoader.java index 15a8ec35d84..f11aab4ac8b 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/loaders/ObjectLoader.java +++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/ObjectLoader.java @@ -392,6 +392,10 @@ else if (opcode == 94) { def.setUnknown1(true); } + else if (opcode == 95) + { + def.setSoundVisibility(is.readUnsignedByte()); + } else if (opcode == 249) { int length = is.readUnsignedByte(); diff --git a/cache/src/main/java/net/runelite/cache/fs/Archive.java b/cache/src/main/java/net/runelite/cache/fs/Archive.java index b6b4105c97f..46b77ce5fd2 100644 --- a/cache/src/main/java/net/runelite/cache/fs/Archive.java +++ b/cache/src/main/java/net/runelite/cache/fs/Archive.java @@ -82,15 +82,7 @@ public byte[] decompress(byte[] data, int[] keys) throws IOException return null; } - byte[] encryptedData = data; - - Container container = Container.decompress(encryptedData, keys); - if (container == null) - { - logger.warn("Unable to decrypt archive {}", this); - return null; - } - + Container container = Container.decompress(data, keys); byte[] decompressedData = container.data; if (this.crc != container.crc) @@ -99,20 +91,20 @@ public byte[] decompress(byte[] data, int[] keys) throws IOException throw new IOException("CRC mismatch for " + index.getId() + "/" + this.getArchiveId()); } - if (container.revision != -1 && this.getRevision() != container.revision) + if (container.revision != -1 && this.revision != container.revision) { // compressed data doesn't always include a revision, but check it if it does logger.warn("revision mismatch for archive {}/{}, expected {} was {}", index.getId(), this.getArchiveId(), - this.getRevision(), container.revision); + this.revision, container.revision); // I've seen this happen with vanilla caches where the // revision in the index data differs from the revision // stored for the archive data on disk... I assume this // is more correct - this.setRevision(container.revision); + this.revision = container.revision; } - setCompression(container.compression); + this.compression = container.compression; return decompressedData; } diff --git a/cache/src/main/java/net/runelite/cache/fs/Container.java b/cache/src/main/java/net/runelite/cache/fs/Container.java index e528f941647..52635d19278 100644 --- a/cache/src/main/java/net/runelite/cache/fs/Container.java +++ b/cache/src/main/java/net/runelite/cache/fs/Container.java @@ -105,7 +105,6 @@ public static Container decompress(byte[] b, int[] keys) throws IOException crc32.update(b, 0, 5); // compression + length byte[] data; - int revision = -1; switch (compression) { case CompressionType.NONE: @@ -116,12 +115,6 @@ public static Container decompress(byte[] b, int[] keys) throws IOException crc32.update(encryptedData, 0, compressedLength); byte[] decryptedData = decrypt(encryptedData, encryptedData.length, keys); - if (stream.remaining() >= 2) - { - revision = stream.readUnsignedShort(); - assert revision != -1; - } - data = decryptedData; break; @@ -134,16 +127,10 @@ public static Container decompress(byte[] b, int[] keys) throws IOException crc32.update(encryptedData, 0, encryptedData.length); byte[] decryptedData = decrypt(encryptedData, encryptedData.length, keys); - if (stream.remaining() >= 2) - { - revision = stream.readUnsignedShort(); - assert revision != -1; - } - - stream = new InputStream(decryptedData); + InputStream decryptedStream = new InputStream(decryptedData); - int decompressedLength = stream.readInt(); - data = BZip2.decompress(stream.getRemaining(), compressedLength); + int decompressedLength = decryptedStream.readInt(); + data = BZip2.decompress(decryptedStream.getRemaining(), compressedLength); assert data.length == decompressedLength; break; @@ -156,22 +143,26 @@ public static Container decompress(byte[] b, int[] keys) throws IOException crc32.update(encryptedData, 0, encryptedData.length); byte[] decryptedData = decrypt(encryptedData, encryptedData.length, keys); - if (stream.remaining() >= 2) - { - revision = stream.readUnsignedShort(); - assert revision != -1; - } + InputStream decryptedStream = new InputStream(decryptedData); - stream = new InputStream(decryptedData); - - int decompressedLength = stream.readInt(); - data = GZip.decompress(stream.getRemaining(), compressedLength); + int decompressedLength = decryptedStream.readInt(); + data = GZip.decompress(decryptedStream.getRemaining(), compressedLength); assert data.length == decompressedLength; break; } default: - throw new RuntimeException("Unknown decompression type"); + throw new RuntimeException("Unknown compression type"); + } + + int revision = -1; + if (stream.remaining() >= 4) + { + revision = stream.readInt(); + } + else if (stream.remaining() >= 2) + { + revision = stream.readUnsignedShort(); } Container container = new Container(compression, revision); diff --git a/pom.xml b/pom.xml index 1ba106c7249..035871b94ea 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.11.23-SNAPSHOT + 1.12.0-SNAPSHOT pom RuneLite diff --git a/runelite-api/pom.xml b/runelite-api/pom.xml index 5c540048b26..7a3a3a3601a 100644 --- a/runelite-api/pom.xml +++ b/runelite-api/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.11.23-SNAPSHOT + 1.12.0-SNAPSHOT runelite-api diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml index eb5c87c18ea..3fe1c6c2621 100644 --- a/runelite-client/pom.xml +++ b/runelite-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.11.23-SNAPSHOT + 1.12.0-SNAPSHOT client @@ -41,7 +41,7 @@ nogit false false - 2.0.35 + 2.0.36 nogit diff --git a/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java b/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java index 7e997d21058..665a78188c4 100644 --- a/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java +++ b/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java @@ -38,7 +38,6 @@ import java.awt.image.VolatileImage; import java.io.PrintWriter; import java.io.StringWriter; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; @@ -115,6 +114,7 @@ public class Hooks implements Callbacks @Nullable private final RuntimeConfig runtimeConfig; private final boolean developerMode; + private final RenderCallbackManager renderCallbackManager; private Dimension lastStretchedDimensions; private VolatileImage stretchedImage; @@ -130,13 +130,21 @@ public class Hooks implements Callbacks private boolean rateLimitedError; private int errorBackoff = 1; + /** + * use {@link RenderCallbackManager} instead + */ @FunctionalInterface - public interface RenderableDrawListener + @Deprecated + public interface RenderableDrawListener extends RenderCallback { boolean draw(Renderable renderable, boolean ui); - } - private final List renderableDrawListeners = new ArrayList<>(); + @Override + default boolean drawEntity(Renderable renderable, boolean ui) + { + return draw(renderable, ui); + } + } /** * Get the Graphics2D for the MainBufferProvider image @@ -177,7 +185,8 @@ private Hooks( ClientUI clientUi, @Nullable TelemetryClient telemetryClient, @Nullable RuntimeConfig runtimeConfig, - @Named("developerMode") final boolean developerMode + @Named("developerMode") final boolean developerMode, + RenderCallbackManager renderCallbackManager ) { this.client = client; @@ -196,6 +205,7 @@ private Hooks( this.telemetryClient = telemetryClient; this.runtimeConfig = runtimeConfig; this.developerMode = developerMode; + this.renderCallbackManager = renderCallbackManager; eventBus.register(this); } @@ -577,14 +587,22 @@ public void onScriptCallbackEvent(ScriptCallbackEvent scriptCallbackEvent) eventBus.post(fakeXpDrop); } + /** + * use {@link RenderCallbackManager#register(RenderCallback)} instead + */ + @Deprecated public void registerRenderableDrawListener(RenderableDrawListener listener) { - renderableDrawListeners.add(listener); + renderCallbackManager.register(listener); } + /** + * use {@link RenderCallbackManager#unregister(RenderCallback)} instead + */ + @Deprecated public void unregisterRenderableDrawListener(RenderableDrawListener listener) { - renderableDrawListeners.remove(listener); + renderCallbackManager.unregister(listener); } @Override @@ -592,17 +610,11 @@ public boolean draw(Renderable renderable, boolean drawingUi) { try { - for (RenderableDrawListener renderableDrawListener : renderableDrawListeners) - { - if (!renderableDrawListener.draw(renderable, drawingUi)) - { - return false; - } - } + return renderCallbackManager.drawEntity(renderable, drawingUi); } catch (Exception ex) { - log.error("exception from renderable draw listener", ex); + log.error("exception from render callback", ex); } return true; } diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/to_screen.glsl b/runelite-client/src/main/java/net/runelite/client/callback/RenderCallback.java similarity index 52% rename from runelite-client/src/main/resources/net/runelite/client/plugins/gpu/to_screen.glsl rename to runelite-client/src/main/java/net/runelite/client/callback/RenderCallback.java index 530cb990cc9..9749178aa3e 100644 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/to_screen.glsl +++ b/runelite-client/src/main/java/net/runelite/client/callback/RenderCallback.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Adam + * Copyright (c) 2025, Adam * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -22,26 +22,50 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +package net.runelite.client.callback; -/* - * Convert a vertex to screen space - */ -vec3 toScreen(vec3 vertex, float cameraYaw, float cameraPitch, int centerX, int centerY, int zoom) { - float yawSin = sin(cameraYaw); - float yawCos = cos(cameraYaw); - - float pitchSin = sin(cameraPitch); - float pitchCos = cos(cameraPitch); - - float rotatedX = (vertex.z * yawSin) + (vertex.x * yawCos); - float rotatedZ = (vertex.z * yawCos) - (vertex.x * yawSin); +import net.runelite.api.Renderable; +import net.runelite.api.Scene; +import net.runelite.api.Tile; +import net.runelite.api.TileObject; - float var13 = (vertex.y * pitchCos) - (rotatedZ * pitchSin); - float var12 = (vertex.y * pitchSin) + (rotatedZ * pitchCos); +public interface RenderCallback +{ + /** + * Tests if an entity should be rendered. + * This is called multiple times per frame from the client thread. + * Entities are temporary entities (players, npcs, projectiles, spotanims, etc). + * @param renderable the entity + * @param ui true if this test is for drawing the ui (hitbars etc) + * @return + */ + default boolean drawEntity(Renderable renderable, boolean ui) + { + return true; + } - float x = rotatedX * zoom / var12 + centerX; - float y = var13 * zoom / var12 + centerY; - float z = -var12; // in OpenGL depth is negative + /** + * Test if a tile should be drawn. + * This is called on scene upload, by the maploader thread. + * @param scene + * @param tile + * @return + */ + default boolean drawTile(Scene scene, Tile tile) + { + return true; + } - return vec3(x, y, z); + /** + * Test if a tileobject should be drawn. + * This is called on scene upload, by the maploader thread, as well as + * each frame by the client thread for dynamic objects (animated objects) + * @param scene + * @param object + * @return + */ + default boolean drawObject(Scene scene, TileObject object) + { + return true; + } } diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp_common.glsl b/runelite-client/src/main/java/net/runelite/client/callback/RenderCallbackManager.java similarity index 51% rename from runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp_common.glsl rename to runelite-client/src/main/java/net/runelite/client/callback/RenderCallbackManager.java index 39dbed12fe3..8fc6a861001 100644 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp_common.glsl +++ b/runelite-client/src/main/java/net/runelite/client/callback/RenderCallbackManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Adam + * Copyright (c) 2025, Adam * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -22,61 +22,82 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +package net.runelite.client.callback; -#define PI 3.1415926535897932384626433832795f -#define UNIT PI / 1024.0f +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import javax.inject.Singleton; +import net.runelite.api.Renderable; +import net.runelite.api.Scene; +import net.runelite.api.Tile; +import net.runelite.api.TileObject; -layout(std140) uniform uniforms { - float cameraYaw; - float cameraPitch; - int centerX; - int centerY; - int zoom; - float cameraX; - float cameraY; - float cameraZ; -}; +@Singleton +public class RenderCallbackManager +{ + private final List callbacks = new CopyOnWriteArrayList<>(); -struct modelinfo { - int offset; // offset into vertex buffer - int toffset; // offset into texture buffer - int size; // length in faces - int idx; // write idx in target buffer - int flags; // buffer, hillskew, plane, orientation - int x; // scene position x - int y; // scene position y - int z; // scene position z -}; + public void register(RenderCallback cb) + { + callbacks.add(cb); + } -struct vert { - vec3 pos; - int ahsl; -}; + public void unregister(RenderCallback cb) + { + callbacks.remove(cb); + } -layout(std430, binding = 0) readonly buffer modelbuffer_in { - modelinfo ol[]; -}; + public boolean drawEntity(Renderable renderable, boolean ui) + { + if (callbacks.isEmpty()) + { + return true; + } -layout(std430, binding = 1) readonly buffer vertexbuffer_in { - vert vb[]; -}; + for (var cb : callbacks) + { + if (!cb.drawEntity(renderable, ui)) + { + return false; + } + } -layout(std430, binding = 2) readonly buffer tempvertexbuffer_in { - vert tempvb[]; -}; + return true; + } -layout(std430, binding = 3) writeonly buffer vertex_out { - vert vout[]; -}; + public boolean drawTile(Scene scene, Tile tile) + { + if (callbacks.isEmpty()) + { + return true; + } -layout(std430, binding = 4) writeonly buffer uv_out { - vec4 uvout[]; -}; + for (var cb : callbacks) + { + if (!cb.drawTile(scene, tile)) + { + return false; + } + } -layout(std430, binding = 5) readonly buffer texturebuffer_in { - vec4 texb[]; -}; + return true; + } -layout(std430, binding = 6) readonly buffer temptexturebuffer_in { - vec4 temptexb[]; -}; \ No newline at end of file + public boolean drawObject(Scene scene, TileObject object) + { + if (callbacks.isEmpty()) + { + return true; + } + + for (var cb : callbacks) + { + if (!cb.drawObject(scene, object)) + { + return false; + } + } + + return true; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/FacePrioritySorter.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/FacePrioritySorter.java new file mode 100644 index 00000000000..a82ca655a05 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/FacePrioritySorter.java @@ -0,0 +1,479 @@ +/* + * Copyright (c) 2018, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.gpu; + +import java.nio.IntBuffer; +import java.util.Arrays; +import javax.inject.Inject; +import javax.inject.Singleton; +import lombok.RequiredArgsConstructor; +import net.runelite.api.Client; +import net.runelite.api.Model; +import net.runelite.api.Perspective; +import net.runelite.api.Projection; + +@Singleton +@RequiredArgsConstructor(onConstructor = @__(@Inject)) +class FacePrioritySorter +{ + static final int[] distances; + static final char[] distanceFaceCount; + static final char[][] distanceToFaces; + + private static final float[] modelCanvasX; + private static final float[] modelCanvasY; + + private static final float[] modelLocalX; + private static final float[] modelLocalY; + private static final float[] modelLocalZ; + + static final int[] numOfPriority; + private static final int[] eq10; + private static final int[] eq11; + private static final int[] lt10; + static final int[][] orderedFaces; + + private static final int MAX_VERTEX_COUNT = SceneUploader.MAX_VERTEX_COUNT; + private static final int MAX_DIAMETER = 6000; + private static final int ZSORT_GROUP_SIZE = 1024; // was 512 + private static final int MAX_FACES_PER_PRIORITY = 4000; // was 2500 + + static + { + distances = new int[MAX_VERTEX_COUNT]; + distanceFaceCount = new char[MAX_DIAMETER]; + distanceToFaces = new char[MAX_DIAMETER][ZSORT_GROUP_SIZE]; + + modelCanvasX = new float[MAX_VERTEX_COUNT]; + modelCanvasY = new float[MAX_VERTEX_COUNT]; + + modelLocalX = SceneUploader.modelLocalX; + modelLocalY = SceneUploader.modelLocalY; + modelLocalZ = SceneUploader.modelLocalZ; + + numOfPriority = new int[12]; + eq10 = new int[MAX_FACES_PER_PRIORITY]; + eq11 = new int[MAX_FACES_PER_PRIORITY]; + lt10 = new int[12]; + orderedFaces = new int[12][MAX_FACES_PER_PRIORITY]; + } + + private final Client client; + + int uploadSortedModel(Projection proj, Model model, int orientation, int x, int y, int z, IntBuffer opaqueBuffer, IntBuffer alphaBuffer) + { + final int vertexCount = model.getVerticesCount(); + final float[] verticesX = model.getVerticesX(); + final float[] verticesY = model.getVerticesY(); + final float[] verticesZ = model.getVerticesZ(); + + final int faceCount = model.getFaceCount(); + final int[] indices1 = model.getFaceIndices1(); + final int[] indices2 = model.getFaceIndices2(); + final int[] indices3 = model.getFaceIndices3(); + + final int[] faceColors3 = model.getFaceColors3(); + final byte[] faceRenderPriorities = model.getFaceRenderPriorities(); + + final int centerX = client.getCenterX(); + final int centerY = client.getCenterY(); + final int zoom = client.get3dZoom(); + + float orientSine = 0; + float orientCosine = 0; + if (orientation != 0) + { + orientSine = Perspective.SINE[orientation] / 65536f; + orientCosine = Perspective.COSINE[orientation] / 65536f; + } + + float[] p = proj.project(x, y, z); + int zero = (int) p[2]; + + for (int v = 0; v < vertexCount; ++v) + { + float vertexX = verticesX[v]; + float vertexY = verticesY[v]; + float vertexZ = verticesZ[v]; + + if (orientation != 0) + { + float x0 = vertexX; + vertexX = vertexZ * orientSine + x0 * orientCosine; + vertexZ = vertexZ * orientCosine - x0 * orientSine; + } + + // move to local position + vertexX += x; + vertexY += y; + vertexZ += z; + + modelLocalX[v] = vertexX; + modelLocalY[v] = vertexY; + modelLocalZ[v] = vertexZ; + + p = proj.project(vertexX, vertexY, vertexZ); + if (p[2] < 50) + { + return 0; + } + + modelCanvasX[v] = centerX + p[0] * zoom / p[2]; + modelCanvasY[v] = centerY + p[1] * zoom / p[2]; + distances[v] = (int) p[2] - zero; + } + + final int diameter = model.getDiameter(); + final int radius = model.getRadius(); + if (diameter >= 6000) + { + return 0; + } + + Arrays.fill(distanceFaceCount, 0, diameter, (char) 0); + + for (char i = 0; i < faceCount; ++i) + { + if (faceColors3[i] != -2) + { + final int v1 = indices1[i]; + final int v2 = indices2[i]; + final int v3 = indices3[i]; + + final float + aX = modelCanvasX[v1], + aY = modelCanvasY[v1], + bX = modelCanvasX[v2], + bY = modelCanvasY[v2], + cX = modelCanvasX[v3], + cY = modelCanvasY[v3]; + + if ((aX - bX) * (cY - bY) - (cX - bX) * (aY - bY) > 0) + { + int distance = radius + (distances[v1] + distances[v2] + distances[v3]) / 3; + assert distance >= 0 && distance < diameter; + distanceToFaces[distance][distanceFaceCount[distance]++] = i; + } + } + } + + int len = 0; + if (faceRenderPriorities == null) + { + for (int i = diameter - 1; i >= 0; --i) + { + final int cnt = distanceFaceCount[i]; + if (cnt > 0) + { + final char[] faces = distanceToFaces[i]; + + for (int faceIdx = 0; faceIdx < cnt; ++faceIdx) + { + final int face = faces[faceIdx]; + len += pushFace(model, face, opaqueBuffer, alphaBuffer); + } + } + } + } + else + { + Arrays.fill(numOfPriority, 0); + Arrays.fill(lt10, 0); + + for (int i = diameter - 1; i >= 0; --i) + { + final int cnt = distanceFaceCount[i]; + if (cnt > 0) + { + final char[] faces = distanceToFaces[i]; + + for (int faceIdx = 0; faceIdx < cnt; ++faceIdx) + { + final int face = faces[faceIdx]; + final byte pri = faceRenderPriorities[face]; + final int distIdx = numOfPriority[pri]++; + + orderedFaces[pri][distIdx] = face; + if (pri < 10) + { + lt10[pri] += i; + } + else if (pri == 10) + { + eq10[distIdx] = i; + } + else + { + eq11[distIdx] = i; + } + } + } + } + + int avg12 = 0; + if (numOfPriority[1] > 0 || numOfPriority[2] > 0) + { + avg12 = (lt10[1] + lt10[2]) / (numOfPriority[1] + numOfPriority[2]); + } + + int avg34 = 0; + if (numOfPriority[3] > 0 || numOfPriority[4] > 0) + { + avg34 = (lt10[3] + lt10[4]) / (numOfPriority[3] + numOfPriority[4]); + } + + int avg68 = 0; + if (numOfPriority[6] > 0 || numOfPriority[8] > 0) + { + avg68 = (lt10[8] + lt10[6]) / (numOfPriority[8] + numOfPriority[6]); + } + + int drawnFaces = 0; + int numDynFaces = numOfPriority[10]; + int[] dynFaces = orderedFaces[10]; + int[] dynFaceDistances = eq10; + if (drawnFaces == numDynFaces) + { + drawnFaces = 0; + numDynFaces = numOfPriority[11]; + dynFaces = orderedFaces[11]; + dynFaceDistances = eq11; + } + + int currFaceDistance; + if (drawnFaces < numDynFaces) + { + currFaceDistance = dynFaceDistances[drawnFaces]; + } + else + { + currFaceDistance = -1000; + } + + for (int pri = 0; pri < 10; ++pri) + { + while (pri == 0 && currFaceDistance > avg12) + { + final int face = dynFaces[drawnFaces++]; + len += pushFace(model, face, opaqueBuffer, alphaBuffer); + + if (drawnFaces == numDynFaces && dynFaces != orderedFaces[11]) + { + drawnFaces = 0; + numDynFaces = numOfPriority[11]; + dynFaces = orderedFaces[11]; + dynFaceDistances = eq11; + } + + if (drawnFaces < numDynFaces) + { + currFaceDistance = dynFaceDistances[drawnFaces]; + } + else + { + currFaceDistance = -1000; + } + } + + while (pri == 3 && currFaceDistance > avg34) + { + final int face = dynFaces[drawnFaces++]; + len += pushFace(model, face, opaqueBuffer, alphaBuffer); + + if (drawnFaces == numDynFaces && dynFaces != orderedFaces[11]) + { + drawnFaces = 0; + numDynFaces = numOfPriority[11]; + dynFaces = orderedFaces[11]; + dynFaceDistances = eq11; + } + + if (drawnFaces < numDynFaces) + { + currFaceDistance = dynFaceDistances[drawnFaces]; + } + else + { + currFaceDistance = -1000; + } + } + + while (pri == 5 && currFaceDistance > avg68) + { + final int face = dynFaces[drawnFaces++]; + len += pushFace(model, face, opaqueBuffer, alphaBuffer); + + if (drawnFaces == numDynFaces && dynFaces != orderedFaces[11]) + { + drawnFaces = 0; + numDynFaces = numOfPriority[11]; + dynFaces = orderedFaces[11]; + dynFaceDistances = eq11; + } + + if (drawnFaces < numDynFaces) + { + currFaceDistance = dynFaceDistances[drawnFaces]; + } + else + { + currFaceDistance = -1000; + } + } + + final int priNum = numOfPriority[pri]; + final int[] priFaces = orderedFaces[pri]; + + for (int faceIdx = 0; faceIdx < priNum; ++faceIdx) + { + final int face = priFaces[faceIdx]; + len += pushFace(model, face, opaqueBuffer, alphaBuffer); + } + } + + while (currFaceDistance != -1000) + { + final int face = dynFaces[drawnFaces++]; + len += pushFace(model, face, opaqueBuffer, alphaBuffer); + + if (drawnFaces == numDynFaces && dynFaces != orderedFaces[11]) + { + drawnFaces = 0; + dynFaces = orderedFaces[11]; + numDynFaces = numOfPriority[11]; + dynFaceDistances = eq11; + } + + if (drawnFaces < numDynFaces) + { + currFaceDistance = dynFaceDistances[drawnFaces]; + } + else + { + currFaceDistance = -1000; + } + } + } + + return len; + } + + private int pushFace(Model model, int face, IntBuffer opaqueBuffer, IntBuffer alphaBuffer) + { + final int[] indices1 = model.getFaceIndices1(); + final int[] indices2 = model.getFaceIndices2(); + final int[] indices3 = model.getFaceIndices3(); + + final int[] faceColors1 = model.getFaceColors1(); + final int[] faceColors2 = model.getFaceColors2(); + final int[] faceColors3 = model.getFaceColors3(); + + final byte overrideAmount = model.getOverrideAmount(); + final byte overrideHue = model.getOverrideHue(); + final byte overrideSat = model.getOverrideSaturation(); + final byte overrideLum = model.getOverrideLuminance(); + + final short[] faceTextures = model.getFaceTextures(); + final byte[] textureFaces = model.getTextureFaces(); + final int[] texIndices1 = model.getTexIndices1(); + final int[] texIndices2 = model.getTexIndices2(); + final int[] texIndices3 = model.getTexIndices3(); + + final byte[] transparencies = model.getFaceTransparencies(); + final byte[] bias = model.getFaceBias(); + + final int triangleA = indices1[face]; + final int triangleB = indices2[face]; + final int triangleC = indices3[face]; + + int color1 = faceColors1[face]; + int color2 = faceColors2[face]; + int color3 = faceColors3[face]; + + boolean alpha = (transparencies != null && transparencies[face] != 0); + + if (color3 == -1) + { + color2 = color3 = color1; + } + + // HSL override is not applied to textured faces + if (faceTextures == null || faceTextures[face] == -1) + { + if (overrideAmount > 0) + { + color1 = SceneUploader.interpolateHSL(color1, overrideHue, overrideSat, overrideLum, overrideAmount); + color2 = SceneUploader.interpolateHSL(color2, overrideHue, overrideSat, overrideLum, overrideAmount); + color3 = SceneUploader.interpolateHSL(color3, overrideHue, overrideSat, overrideLum, overrideAmount); + } + } + + float vx1 = modelLocalX[triangleA]; + float vy1 = modelLocalY[triangleA]; + float vz1 = modelLocalZ[triangleA]; + + float vx2 = modelLocalX[triangleB]; + float vy2 = modelLocalY[triangleB]; + float vz2 = modelLocalZ[triangleB]; + + float vx3 = modelLocalX[triangleC]; + float vy3 = modelLocalY[triangleC]; + float vz3 = modelLocalZ[triangleC]; + + int texA, texB, texC; + + if (textureFaces != null && textureFaces[face] != -1) + { + int tface = textureFaces[face] & 0xff; + texA = texIndices1[tface]; + texB = texIndices2[tface]; + texC = texIndices3[tface]; + } + else + { + texA = triangleA; + texB = triangleB; + texC = triangleC; + } + + int alphaBias = 0; + alphaBias |= transparencies != null ? (transparencies[face] & 0xff) << 24 : 0; + alphaBias |= bias != null ? (bias[face] & 0xff) << 16 : 0; + int texture = faceTextures != null ? faceTextures[face] + 1 : 0; + + var vb = alpha ? alphaBuffer : opaqueBuffer; + + SceneUploader.putfff4(vb, vx1, vy1, vz1, alphaBias | color1); + SceneUploader.put2222(vb, texture, (int) modelLocalX[texA] - (int) vx1, (int) modelLocalY[texA] - (int) vy1, (int) modelLocalZ[texA] - (int) vz1); + + SceneUploader.putfff4(vb, vx2, vy2, vz2, alphaBias | color2); + SceneUploader.put2222(vb, texture, (int) modelLocalX[texB] - (int) vx2, (int) modelLocalY[texB] - (int) vy2, (int) modelLocalZ[texB] - (int) vz2); + + SceneUploader.putfff4(vb, vx3, vy3, vz3, alphaBias | color3); + SceneUploader.put2222(vb, texture, (int) modelLocalX[texC] - (int) vx3, (int) modelLocalY[texC] - (int) vy3, (int) modelLocalZ[texC] - (int) vz3); + + return 3; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GLBuffer.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GLBuffer.java index 3c183bb937b..9193a9aacd8 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GLBuffer.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GLBuffer.java @@ -29,7 +29,6 @@ class GLBuffer String name; int glBufferId = -1; int size = -1; - long clBuffer = -1; GLBuffer(String name) { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuFloatBuffer.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuFloatBuffer.java index 0d274f8bbed..8f27b4c48af 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuFloatBuffer.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuFloatBuffer.java @@ -30,11 +30,17 @@ class GpuFloatBuffer { - private FloatBuffer buffer = allocateDirect(65536); + private final FloatBuffer buffer; - void put(float s, float t, float p, float q) + GpuFloatBuffer(int size) { - buffer.put(s).put(t).put(p).put(q); + buffer = allocateDirect(size); + } + + GpuFloatBuffer put(float f) + { + buffer.put(f); + return this; } void flip() @@ -47,25 +53,6 @@ void clear() buffer.clear(); } - void ensureCapacity(int size) - { - int capacity = buffer.capacity(); - final int position = buffer.position(); - if ((capacity - position) < size) - { - do - { - capacity *= 2; - } - while ((capacity - position) < size); - - FloatBuffer newB = allocateDirect(capacity); - buffer.flip(); - newB.put(buffer); - buffer = newB; - } - } - FloatBuffer getBuffer() { return buffer; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuIntBuffer.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuIntBuffer.java index 9f8ab2eae83..0245935f1b9 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuIntBuffer.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuIntBuffer.java @@ -30,43 +30,34 @@ class GpuIntBuffer { - private IntBuffer buffer = allocateDirect(65536); + private final IntBuffer buffer; - void put(float x, float y, float z, int w) + GpuIntBuffer(IntBuffer ib) { - buffer.put(Float.floatToIntBits(x)) - .put(Float.floatToIntBits(y)) - .put(Float.floatToIntBits(z)) - .put(w); + buffer = ib; } - void flip() + void put22224(int x, int y, int z, int w) { - buffer.flip(); + buffer.put(((y & 0xffff) << 16) | (x & 0xffff)); + buffer.put(z & 0xffff); + buffer.put(w); } - void clear() + void put2222(int x, int y, int z, int w) { - buffer.clear(); + buffer.put(((y & 0xffff) << 16) | (x & 0xffff)); + buffer.put(((w & 0xffff) << 16) | (z & 0xffff)); } - void ensureCapacity(int size) + void flip() { - int capacity = buffer.capacity(); - final int position = buffer.position(); - if ((capacity - position) < size) - { - do - { - capacity *= 2; - } - while ((capacity - position) < size); + buffer.flip(); + } - IntBuffer newB = allocateDirect(capacity); - buffer.flip(); - newB.put(buffer); - buffer = newB; - } + void clear() + { + buffer.clear(); } IntBuffer getBuffer() 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 fc02c8df0b9..32fd818376f 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 @@ -24,13 +24,16 @@ */ package net.runelite.client.plugins.gpu; +import com.google.common.base.Stopwatch; import com.google.common.primitives.Ints; import com.google.inject.Provides; import lombok.extern.slf4j.Slf4j; import net.runelite.api.*; import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.PostClientTick; import net.runelite.api.hooks.DrawCallbacks; import net.runelite.client.callback.ClientThread; +import net.runelite.client.callback.RenderCallbackManager; import net.runelite.client.config.ConfigManager; import net.runelite.client.eventbus.Subscribe; import net.runelite.client.events.ConfigChanged; @@ -44,13 +47,11 @@ import net.runelite.client.plugins.microbot.Microbot; import net.runelite.client.ui.ClientUI; import net.runelite.client.ui.DrawManager; -import net.runelite.client.util.OSType; import net.runelite.rlawt.AWTContext; -import org.lwjgl.opencl.CL10; -import org.lwjgl.opencl.CL10GL; -import org.lwjgl.opencl.CL12; import org.lwjgl.opengl.GL; -import org.lwjgl.opengl.GL43C; +import static org.lwjgl.opengl.GL43C.*; +import static org.lwjgl.opengl.GL45C.GL_ZERO_TO_ONE; +import static org.lwjgl.opengl.GL45C.glClipControl; import org.lwjgl.opengl.GLCapabilities; import org.lwjgl.opengl.GLUtil; import org.lwjgl.system.Callback; @@ -67,7 +68,7 @@ @PluginDescriptor( name = "GPU", - description = "Utilizes the GPU", + description = "Offloads rendering to GPU", enabledByDefault = true, tags = {"fog", "draw distance"}, loadInSafeMode = false @@ -75,14 +76,12 @@ @Slf4j public class GpuPlugin extends Plugin implements DrawCallbacks { - // This is the maximum number of triangles the compute shaders support - static final int MAX_TRIANGLE = 6144; - static final int SMALL_TRIANGLE_COUNT = 512; - private static final int FLAG_SCENE_BUFFER = Integer.MIN_VALUE; static final int MAX_DISTANCE = 184; static final int MAX_FOG_DEPTH = 100; static final int SCENE_OFFSET = (Constants.EXTENDED_SCENE_SIZE - Constants.SCENE_SIZE) / 2; // offset for sxy -> msxy - private static final int GROUND_MIN_Y = 350; // how far below the ground models extend + private static final int UNIFORM_BUFFER_SIZE = 5 * Float.BYTES; + private static final int NUM_ZONES = Constants.EXTENDED_SCENE_SIZE >> 3; + private static final int MAX_WORLDVIEWS = 4096; @Inject private Client client; @@ -90,9 +89,6 @@ public class GpuPlugin extends Plugin implements DrawCallbacks @Inject private ClientUI clientUI; - @Inject - private OpenCLManager openCLManager; - @Inject private ClientThread clientThread; @@ -103,7 +99,10 @@ public class GpuPlugin extends Plugin implements DrawCallbacks private TextureManager textureManager; @Inject - private SceneUploader sceneUploader; + private RegionManager regionManager; + + @Inject + private FacePrioritySorter facePrioritySorter; @Inject private DrawManager drawManager; @@ -111,55 +110,28 @@ public class GpuPlugin extends Plugin implements DrawCallbacks @Inject private PluginManager pluginManager; - enum ComputeMode - { - NONE, - OPENGL, - OPENCL - } - - private ComputeMode computeMode = ComputeMode.NONE; + @Inject + private RenderCallbackManager renderCallbackManager; private Canvas canvas; private AWTContext awtContext; private Callback debugCallback; + private boolean lwjglInitted = false; private GLCapabilities glCapabilities; - static final String LINUX_VERSION_HEADER = - "#version 420\n" + - "#extension GL_ARB_compute_shader : require\n" + - "#extension GL_ARB_shader_storage_buffer_object : require\n" + - "#extension GL_ARB_explicit_attrib_location : require\n"; - static final String WINDOWS_VERSION_HEADER = "#version 430\n"; - static final Shader PROGRAM = new Shader() - .add(GL43C.GL_VERTEX_SHADER, "vert.glsl") - .add(GL43C.GL_GEOMETRY_SHADER, "geom.glsl") - .add(GL43C.GL_FRAGMENT_SHADER, "frag.glsl"); - - static final Shader COMPUTE_PROGRAM = new Shader() - .add(GL43C.GL_COMPUTE_SHADER, "comp.glsl"); - - static final Shader SMALL_COMPUTE_PROGRAM = new Shader() - .add(GL43C.GL_COMPUTE_SHADER, "comp.glsl"); - - static final Shader UNORDERED_COMPUTE_PROGRAM = new Shader() - .add(GL43C.GL_COMPUTE_SHADER, "comp_unordered.glsl"); + .add(GL_VERTEX_SHADER, "vert.glsl") + .add(GL_GEOMETRY_SHADER, "geom.glsl") + .add(GL_FRAGMENT_SHADER, "frag.glsl"); static final Shader UI_PROGRAM = new Shader() - .add(GL43C.GL_VERTEX_SHADER, "vertui.glsl") - .add(GL43C.GL_FRAGMENT_SHADER, "fragui.glsl"); + .add(GL_VERTEX_SHADER, "vertui.glsl") + .add(GL_FRAGMENT_SHADER, "fragui.glsl"); - private int glProgram; - private int glComputeProgram; - private int glSmallComputeProgram; - private int glUnorderedComputeProgram; + static int glProgram; private int glUiProgram; - private int vaoCompute; - private int vaoTemp; - private int interfaceTexture; private int interfacePbo; @@ -167,113 +139,122 @@ enum ComputeMode private int vboUiHandle; private int fboScene; + private boolean sceneFboValid; private int rboColorBuffer; - - private final GLBuffer sceneVertexBuffer = new GLBuffer("scene vertex buffer"); - private final GLBuffer sceneUvBuffer = new GLBuffer("scene tex buffer"); - private final GLBuffer tmpVertexBuffer = new GLBuffer("tmp vertex buffer"); - private final GLBuffer tmpUvBuffer = new GLBuffer("tmp tex buffer"); - private final GLBuffer tmpModelBufferLarge = new GLBuffer("model buffer large"); - private final GLBuffer tmpModelBufferSmall = new GLBuffer("model buffer small"); - private final GLBuffer tmpModelBufferUnordered = new GLBuffer("model buffer unordered"); - private final GLBuffer tmpOutBuffer = new GLBuffer("out vertex buffer"); - private final GLBuffer tmpOutUvBuffer = new GLBuffer("out tex buffer"); + private int rboDepthBuffer; private int textureArrayId; - private int tileHeightTex; - - private final GLBuffer uniformBuffer = new GLBuffer("uniform buffer"); - private GpuIntBuffer vertexBuffer; - private GpuFloatBuffer uvBuffer; + private final GLBuffer glUniformBuffer = new GLBuffer("uniform buffer"); - private GpuIntBuffer modelBufferUnordered; - private GpuIntBuffer modelBufferSmall; - private GpuIntBuffer modelBuffer; + private int lastCanvasWidth; + private int lastCanvasHeight; + private int lastStretchedCanvasWidth; + private int lastStretchedCanvasHeight; + private AntiAliasingMode lastAntiAliasingMode; + private int lastAnisotropicFilteringLevel = -1; - private int unorderedModels; + private GpuFloatBuffer uniformBuffer; - /** - * number of models in small buffer - */ - private int smallModels; + private int cameraX, cameraY, cameraZ; + private int cameraYaw, cameraPitch; + private int minLevel, level, maxLevel; + private Set hideRoofIds; - /** - * number of models in large buffer - */ - private int largeModels; + private VAOList vaoO; + private VAOList vaoA; + private VAOList vaoPO; - /** - * offset in the target buffer for model - */ - private int targetBufferOffset; + static class SceneContext + { + final int sizeX, sizeZ; + Zone[][] zones; - /** - * offset into the temporary scene vertex buffer - */ - private int tempOffset; + SceneContext(int sizeX, int sizeZ) + { + this.sizeX = sizeX; + this.sizeZ = sizeZ; + zones = new Zone[sizeX][sizeZ]; + for (int x = 0; x < sizeX; ++x) + { + for (int z = 0; z < sizeZ; ++z) + { + zones[x][z] = new Zone(); + } + } + } - /** - * offset into the temporary scene uv buffer - */ - private int tempUvOffset; + void free() + { + for (int x = 0; x < sizeX; ++x) + { + for (int z = 0; z < sizeZ; ++z) + { + zones[x][z].free(); + } + } + } + } - private int lastCanvasWidth; - private int lastCanvasHeight; - private int lastStretchedCanvasWidth; - private int lastStretchedCanvasHeight; - private AntiAliasingMode lastAntiAliasingMode; - private int lastAnisotropicFilteringLevel = -1; + SceneContext context(Scene scene) + { + int wvid = scene.getWorldViewId(); + if (wvid == -1) + { + return root; + } + return subs[wvid]; + } - private double cameraX, cameraY, cameraZ; - private double cameraYaw, cameraPitch; + SceneContext context(WorldView wv) + { + int wvid = wv.getId(); + if (wvid == -1) + { + return root; + } + return subs[wvid]; + } - private int viewportOffsetX; - private int viewportOffsetY; - private int viewportWidth; - private int viewportHeight; + private SceneContext root; + private SceneContext[] subs; + private Zone[][] nextZones; + private Map nextRoofChanges; // Uniforms - private int uniColorBlindMode; - private int uniUiColorBlindMode; private int uniUseFog; private int uniFogColor; private int uniFogDepth; private int uniDrawDistance; private int uniExpandedMapLoadingChunks; - private int uniProjectionMatrix; + private int uniWorldProj; + private static int uniEntityProj; + static int uniEntityTint; private int uniBrightness; private int uniTex; - private int uniTexSamplingMode; private int uniTexSourceDimensions; private int uniTexTargetDimensions; private int uniUiAlphaOverlay; private int uniTextures; private int uniTextureAnimations; - private int uniBlockSmall; - private int uniBlockLarge; private int uniBlockMain; - private int uniSmoothBanding; private int uniTextureLightMode; private int uniTick; + static int uniBase; - private boolean lwjglInitted = false; - - private int sceneId; - private int nextSceneId; - private GpuIntBuffer nextSceneVertexBuffer; - private GpuFloatBuffer nextSceneTexBuffer; + private static Projection lastProjection; @Override protected void startUp() { + root = new SceneContext(NUM_ZONES, NUM_ZONES); + subs = new SceneContext[MAX_WORLDVIEWS]; clientThread.invoke(() -> { try { - fboScene = rboColorBuffer = -1; - targetBufferOffset = 0; - unorderedModels = smallModels = largeModels = 0; + fboScene = -1; + lastAnisotropicFilteringLevel = -1; AWTContext.loadNatives(); @@ -294,35 +275,20 @@ protected void startUp() canvas.setIgnoreRepaint(true); - computeMode = config.useComputeShaders() - ? (OSType.getOSType() == OSType.MacOS ? ComputeMode.OPENCL : ComputeMode.OPENGL) - : ComputeMode.NONE; - // lwjgl defaults to lwjgl- + user.name, but this breaks if the username would cause an invalid path // to be created. Configuration.SHARED_LIBRARY_EXTRACT_DIRECTORY.set("lwjgl-rl"); glCapabilities = GL.createCapabilities(); - log.info("Using device: {}", GL43C.glGetString(GL43C.GL_RENDERER)); - log.info("Using driver: {}", GL43C.glGetString(GL43C.GL_VERSION)); + log.info("Using device: {}", glGetString(GL_RENDERER)); + log.info("Using driver: {}", glGetString(GL_VERSION)); if (!glCapabilities.OpenGL31) { throw new RuntimeException("OpenGL 3.1 is required but not available"); } - if (!glCapabilities.OpenGL43 && computeMode == ComputeMode.OPENGL) - { - log.info("disabling compute shaders because OpenGL 4.3 is not available"); - computeMode = ComputeMode.NONE; - } - - if (computeMode == ComputeMode.NONE) - { - sceneUploader.initSortingBuffers(); - } - lwjglInitted = true; checkGLErrors(); @@ -331,52 +297,43 @@ protected void startUp() debugCallback = GLUtil.setupDebugMessageCallback(); if (debugCallback != null) { - // GLDebugEvent[ id 0x20071 - // type Warning: generic - // severity Unknown (0x826b) - // source GL API - // msg Buffer detailed info: Buffer object 11 (bound to GL_ARRAY_BUFFER_ARB, and GL_SHADER_STORAGE_BUFFER (4), usage hint is GL_STREAM_DRAW) will use VIDEO memory as the source for buffer object operations. - GL43C.glDebugMessageControl(GL43C.GL_DEBUG_SOURCE_API, GL43C.GL_DEBUG_TYPE_OTHER, - GL43C.GL_DONT_CARE, 0x20071, false); - - // GLDebugMessageHandler: GLDebugEvent[ id 0x20052 - // type Warning: implementation dependent performance - // severity Medium: Severe performance/deprecation/other warnings - // source GL API - // msg Pixel-path performance warning: Pixel transfer is synchronized with 3D rendering. - GL43C.glDebugMessageControl(GL43C.GL_DEBUG_SOURCE_API, GL43C.GL_DEBUG_TYPE_PERFORMANCE, - GL43C.GL_DONT_CARE, 0x20052, false); + // [LWJGL] OpenGL debug message + // ID: 0x20071 + // Source: API + // Type: OTHER + // Severity: NOTIFICATION + // Message: Buffer detailed info: Buffer object 2 (bound to GL_PIXEL_UNPACK_BUFFER_ARB, usage hint is GL_STREAM_DRAW) has been mapped WRITE_ONLY in SYSTEM HEAP memory (fast). + glDebugMessageControl(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_OTHER, + GL_DONT_CARE, 0x20071, false); + + // [LWJGL] OpenGL debug message + // ID: 0x20052 + // Source: API + // Type: PERFORMANCE + // Severity: MEDIUM + // Message: Pixel-path performance warning: Pixel transfer is synchronized with 3D rendering. + glDebugMessageControl(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_PERFORMANCE, + GL_DONT_CARE, 0x20052, false); } } - vertexBuffer = new GpuIntBuffer(); - uvBuffer = new GpuFloatBuffer(); - - modelBufferUnordered = new GpuIntBuffer(); - modelBufferSmall = new GpuIntBuffer(); - modelBuffer = new GpuIntBuffer(); - setupSyncMode(); initBuffers(); initVao(); - try - { - initProgram(); - } - catch (ShaderException ex) + initProgram(); + initInterfaceTexture(); + if (glCapabilities.OpenGL45) { - throw new RuntimeException(ex); + glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE); // 1 near 0 far } - initInterfaceTexture(); - initUniformBuffer(); client.setDrawCallbacks(this); client.setGpuFlags(DrawCallbacks.GPU - | (computeMode == ComputeMode.NONE ? 0 : DrawCallbacks.HILLSKEW) | (config.removeVertexSnapping() ? DrawCallbacks.NO_VERTEX_SNAPPING : 0) + | DrawCallbacks.ZBUF ); - client.setExpandedMapLoading(config.expandedMapLoadingChunks()); + client.setExpandedMapLoading(config.expandedMapLoadingZones()); // force rebuild of main buffer provider to enable alpha channel client.resizeCanvas(); @@ -389,9 +346,7 @@ protected void startUp() if (client.getGameState() == GameState.LOGGED_IN) { - Scene scene = client.getScene(); - loadScene(scene); - swapScene(scene); + startupWorldLoad(); } checkGLErrors(); @@ -419,6 +374,22 @@ protected void startUp() }); } + private void startupWorldLoad() + { + WorldView root = client.getTopLevelWorldView(); + Scene scene = root.getScene(); + loadScene(root, scene); + swapScene(scene); + + for (WorldEntity subEntity : root.worldEntities()) + { + WorldView sub = subEntity.getWorldView(); + log.debug("WorldView loading: {}", sub.getId()); + loadSubScene(sub, sub.getScene()); + swapSub(sub.getScene()); + } + } + @Override protected void shutDown() { @@ -429,8 +400,6 @@ protected void shutDown() client.setUnlockedFps(false); client.setExpandedMapLoading(0); - sceneUploader.releaseSortingBuffers(); - if (lwjglInitted) { if (textureArrayId != -1) @@ -439,13 +408,7 @@ protected void shutDown() textureArrayId = -1; } - if (tileHeightTex != 0) - { - GL43C.glDeleteTextures(tileHeightTex); - tileHeightTex = 0; - } - - destroyGlBuffer(uniformBuffer); + root.free(); shutdownInterfaceTexture(); shutdownProgram(); @@ -454,9 +417,6 @@ protected void shutDown() shutdownFbo(); } - // this must shutdown after the clgl buffers are freed - openCLManager.cleanup(); - if (awtContext != null) { awtContext.destroy(); @@ -471,15 +431,6 @@ protected void shutDown() glCapabilities = null; - vertexBuffer = null; - uvBuffer = null; - - modelBufferSmall = null; - modelBuffer = null; - modelBufferUnordered = null; - - lastAnisotropicFilteringLevel = -1; - // force main buffer provider rebuild to turn off alpha channel client.resizeCanvas(); }); @@ -507,7 +458,7 @@ else if (configChanged.getKey().equals("expandedMapLoadingChunks")) { clientThread.invokeLater(() -> { - client.setExpandedMapLoading(config.expandedMapLoadingChunks()); + client.setExpandedMapLoading(config.expandedMapLoadingZones()); if (client.getGameState() == GameState.LOGGED_IN) { client.setGameState(GameState.LOADING); @@ -518,10 +469,19 @@ else if (configChanged.getKey().equals("removeVertexSnapping")) { log.debug("Toggle {}", configChanged.getKey()); client.setGpuFlags(DrawCallbacks.GPU - | (computeMode == ComputeMode.NONE ? 0 : DrawCallbacks.HILLSKEW) | (config.removeVertexSnapping() ? DrawCallbacks.NO_VERTEX_SNAPPING : 0) + | DrawCallbacks.ZBUF ); } + else if (configChanged.getKey().equals("uiScalingMode") || configChanged.getKey().equals("colorBlindMode")) + { + clientThread.invokeLater(() -> + { + log.debug("Recompiling shaders"); + shutdownProgram(); + initProgram(); + }); + } } } @@ -559,24 +519,19 @@ private void setupSyncMode() checkGLErrors(); } - private Template createTemplate(int threadCount, int facesPerThread) + private Template createTemplate() { - String versionHeader = OSType.getOSType() == OSType.Linux ? LINUX_VERSION_HEADER : WINDOWS_VERSION_HEADER; Template template = new Template(); template.add(key -> { - if ("version_header".equals(key)) - { - return versionHeader; - } - if ("thread_config".equals(key)) + switch (key) { - return "#define THREAD_COUNT " + threadCount + "\n" + - "#define FACES_PER_THREAD " + facesPerThread + "\n"; - } - if ("texture_config".equals(key)) - { - return "#define TEXTURE_COUNT " + TextureManager.TEXTURE_COUNT + "\n"; + case "texture_config": + return "#define TEXTURE_COUNT " + TextureManager.TEXTURE_COUNT + "\n"; + case "sampling_mode": + return "#define SAMPLING_MODE " + config.uiScalingMode().ordinal() + "\n"; + case "colorblind_mode": + return "#define COLORBLIND_MODE " + config.colorBlindMode().ordinal() + "\n"; } return null; }); @@ -586,228 +541,151 @@ private Template createTemplate(int threadCount, int facesPerThread) private void initProgram() throws ShaderException { - Template template = createTemplate(-1, -1); + // macOS core profile has no default VAO, so the shaders won't validate unless a VAO is bound + glBindVertexArray(vaoUiHandle); + + Template template = createTemplate(); glProgram = PROGRAM.compile(template); glUiProgram = UI_PROGRAM.compile(template); - if (computeMode == ComputeMode.OPENGL) - { - glComputeProgram = COMPUTE_PROGRAM.compile(createTemplate(1024, 6)); - glSmallComputeProgram = SMALL_COMPUTE_PROGRAM.compile(createTemplate(512, 1)); - glUnorderedComputeProgram = UNORDERED_COMPUTE_PROGRAM.compile(template); - } - else if (computeMode == ComputeMode.OPENCL) - { - openCLManager.init(awtContext); - } + glBindVertexArray(0); initUniforms(); } private void initUniforms() { - uniProjectionMatrix = GL43C.glGetUniformLocation(glProgram, "projectionMatrix"); - uniBrightness = GL43C.glGetUniformLocation(glProgram, "brightness"); - uniSmoothBanding = GL43C.glGetUniformLocation(glProgram, "smoothBanding"); - uniUseFog = GL43C.glGetUniformLocation(glProgram, "useFog"); - uniFogColor = GL43C.glGetUniformLocation(glProgram, "fogColor"); - uniFogDepth = GL43C.glGetUniformLocation(glProgram, "fogDepth"); - uniDrawDistance = GL43C.glGetUniformLocation(glProgram, "drawDistance"); - uniExpandedMapLoadingChunks = GL43C.glGetUniformLocation(glProgram, "expandedMapLoadingChunks"); - uniColorBlindMode = GL43C.glGetUniformLocation(glProgram, "colorBlindMode"); - uniTextureLightMode = GL43C.glGetUniformLocation(glProgram, "textureLightMode"); - uniTick = GL43C.glGetUniformLocation(glProgram, "tick"); - uniBlockMain = GL43C.glGetUniformBlockIndex(glProgram, "uniforms"); - uniTextures = GL43C.glGetUniformLocation(glProgram, "textures"); - uniTextureAnimations = GL43C.glGetUniformLocation(glProgram, "textureAnimations"); - - uniTex = GL43C.glGetUniformLocation(glUiProgram, "tex"); - uniTexSamplingMode = GL43C.glGetUniformLocation(glUiProgram, "samplingMode"); - uniTexTargetDimensions = GL43C.glGetUniformLocation(glUiProgram, "targetDimensions"); - uniTexSourceDimensions = GL43C.glGetUniformLocation(glUiProgram, "sourceDimensions"); - uniUiColorBlindMode = GL43C.glGetUniformLocation(glUiProgram, "colorBlindMode"); - uniUiAlphaOverlay = GL43C.glGetUniformLocation(glUiProgram, "alphaOverlay"); - - if (computeMode == ComputeMode.OPENGL) - { - uniBlockSmall = GL43C.glGetUniformBlockIndex(glSmallComputeProgram, "uniforms"); - uniBlockLarge = GL43C.glGetUniformBlockIndex(glComputeProgram, "uniforms"); - } + uniWorldProj = glGetUniformLocation(glProgram, "worldProj"); + uniEntityProj = glGetUniformLocation(glProgram, "entityProj"); + uniEntityTint = glGetUniformLocation(glProgram, "entityTint"); + uniBrightness = glGetUniformLocation(glProgram, "brightness"); + uniUseFog = glGetUniformLocation(glProgram, "useFog"); + uniFogColor = glGetUniformLocation(glProgram, "fogColor"); + uniFogDepth = glGetUniformLocation(glProgram, "fogDepth"); + uniDrawDistance = glGetUniformLocation(glProgram, "drawDistance"); + uniExpandedMapLoadingChunks = glGetUniformLocation(glProgram, "expandedMapLoadingChunks"); + uniTextureLightMode = glGetUniformLocation(glProgram, "textureLightMode"); + uniTick = glGetUniformLocation(glProgram, "tick"); + uniBlockMain = glGetUniformBlockIndex(glProgram, "uniforms"); + uniTextures = glGetUniformLocation(glProgram, "textures"); + uniTextureAnimations = glGetUniformLocation(glProgram, "textureAnimations"); + uniBase = glGetUniformLocation(glProgram, "base"); + + uniTex = glGetUniformLocation(glUiProgram, "tex"); + uniTexTargetDimensions = glGetUniformLocation(glUiProgram, "targetDimensions"); + uniTexSourceDimensions = glGetUniformLocation(glUiProgram, "sourceDimensions"); + uniUiAlphaOverlay = glGetUniformLocation(glUiProgram, "alphaOverlay"); } private void shutdownProgram() { - GL43C.glDeleteProgram(glProgram); - glProgram = -1; + glDeleteProgram(glProgram); + glProgram = 0; - GL43C.glDeleteProgram(glComputeProgram); - glComputeProgram = -1; - - GL43C.glDeleteProgram(glSmallComputeProgram); - glSmallComputeProgram = -1; - - GL43C.glDeleteProgram(glUnorderedComputeProgram); - glUnorderedComputeProgram = -1; - - GL43C.glDeleteProgram(glUiProgram); - glUiProgram = -1; + glDeleteProgram(glUiProgram); + glUiProgram = 0; } private void initVao() { - // Create compute VAO - vaoCompute = GL43C.glGenVertexArrays(); - GL43C.glBindVertexArray(vaoCompute); - - GL43C.glEnableVertexAttribArray(0); - GL43C.glBindBuffer(GL43C.GL_ARRAY_BUFFER, tmpOutBuffer.glBufferId); - GL43C.glVertexAttribPointer(0, 3, GL43C.GL_FLOAT, false, 16, 0); - - GL43C.glEnableVertexAttribArray(1); - GL43C.glBindBuffer(GL43C.GL_ARRAY_BUFFER, tmpOutBuffer.glBufferId); - GL43C.glVertexAttribIPointer(1, 1, GL43C.GL_INT, 16, 12); - - GL43C.glEnableVertexAttribArray(2); - GL43C.glBindBuffer(GL43C.GL_ARRAY_BUFFER, tmpOutUvBuffer.glBufferId); - GL43C.glVertexAttribPointer(2, 4, GL43C.GL_FLOAT, false, 0, 0); - - // Create temp VAO - vaoTemp = GL43C.glGenVertexArrays(); - GL43C.glBindVertexArray(vaoTemp); - - GL43C.glEnableVertexAttribArray(0); - GL43C.glBindBuffer(GL43C.GL_ARRAY_BUFFER, tmpVertexBuffer.glBufferId); - GL43C.glVertexAttribPointer(0, 3, GL43C.GL_FLOAT, false, 16, 0); - - GL43C.glEnableVertexAttribArray(1); - GL43C.glBindBuffer(GL43C.GL_ARRAY_BUFFER, tmpVertexBuffer.glBufferId); - GL43C.glVertexAttribIPointer(1, 1, GL43C.GL_INT, 16, 12); - - GL43C.glEnableVertexAttribArray(2); - GL43C.glBindBuffer(GL43C.GL_ARRAY_BUFFER, tmpUvBuffer.glBufferId); - GL43C.glVertexAttribPointer(2, 4, GL43C.GL_FLOAT, false, 0, 0); - // Create UI VAO - vaoUiHandle = GL43C.glGenVertexArrays(); + vaoUiHandle = glGenVertexArrays(); // Create UI buffer - vboUiHandle = GL43C.glGenBuffers(); - GL43C.glBindVertexArray(vaoUiHandle); + vboUiHandle = glGenBuffers(); + glBindVertexArray(vaoUiHandle); FloatBuffer vboUiBuf = GpuFloatBuffer.allocateDirect(5 * 4); vboUiBuf.put(new float[]{ // positions // texture coords - 1f, 1f, 0.0f, 1.0f, 0f, // top right - 1f, -1f, 0.0f, 1.0f, 1f, // bottom right - -1f, -1f, 0.0f, 0.0f, 1f, // bottom left - -1f, 1f, 0.0f, 0.0f, 0f // top left + 1f, 1f, 0f, 1f, 0f, // top right + 1f, -1f, 0f, 1f, 1f, // bottom right + -1f, -1f, 0f, 0f, 1f, // bottom left + -1f, 1f, 0f, 0f, 0f // top left }); vboUiBuf.rewind(); - GL43C.glBindBuffer(GL43C.GL_ARRAY_BUFFER, vboUiHandle); - GL43C.glBufferData(GL43C.GL_ARRAY_BUFFER, vboUiBuf, GL43C.GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, vboUiHandle); + glBufferData(GL_ARRAY_BUFFER, vboUiBuf, GL_STATIC_DRAW); // position attribute - GL43C.glVertexAttribPointer(0, 3, GL43C.GL_FLOAT, false, 5 * Float.BYTES, 0); - GL43C.glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, false, 5 * Float.BYTES, 0); + glEnableVertexAttribArray(0); // texture coord attribute - GL43C.glVertexAttribPointer(1, 2, GL43C.GL_FLOAT, false, 5 * Float.BYTES, 3 * Float.BYTES); - GL43C.glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, false, 5 * Float.BYTES, 3 * Float.BYTES); + glEnableVertexAttribArray(1); - // unbind VBO - GL43C.glBindBuffer(GL43C.GL_ARRAY_BUFFER, 0); + // unbind VAO/VBO + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); } private void shutdownVao() { - GL43C.glDeleteVertexArrays(vaoCompute); - vaoCompute = -1; - - GL43C.glDeleteVertexArrays(vaoTemp); - vaoTemp = -1; + glDeleteBuffers(vboUiHandle); + vboUiHandle = 0; - GL43C.glDeleteBuffers(vboUiHandle); - vboUiHandle = -1; - - GL43C.glDeleteVertexArrays(vaoUiHandle); - vaoUiHandle = -1; + glDeleteVertexArrays(vaoUiHandle); + vaoUiHandle = 0; } private void initBuffers() { - initGlBuffer(sceneVertexBuffer); - initGlBuffer(sceneUvBuffer); - initGlBuffer(tmpVertexBuffer); - initGlBuffer(tmpUvBuffer); - initGlBuffer(tmpModelBufferLarge); - initGlBuffer(tmpModelBufferSmall); - initGlBuffer(tmpModelBufferUnordered); - initGlBuffer(tmpOutBuffer); - initGlBuffer(tmpOutUvBuffer); + uniformBuffer = new GpuFloatBuffer(UNIFORM_BUFFER_SIZE); + initGlBuffer(glUniformBuffer); + Zone.initBuffer(); + + vaoO = new VAOList(); + vaoA = new VAOList(); + vaoPO = new VAOList(); } private void initGlBuffer(GLBuffer glBuffer) { - glBuffer.glBufferId = GL43C.glGenBuffers(); + glBuffer.glBufferId = glGenBuffers(); } private void shutdownBuffers() { - destroyGlBuffer(sceneVertexBuffer); - destroyGlBuffer(sceneUvBuffer); - - destroyGlBuffer(tmpVertexBuffer); - destroyGlBuffer(tmpUvBuffer); - destroyGlBuffer(tmpModelBufferLarge); - destroyGlBuffer(tmpModelBufferSmall); - destroyGlBuffer(tmpModelBufferUnordered); - destroyGlBuffer(tmpOutBuffer); - destroyGlBuffer(tmpOutUvBuffer); + destroyGlBuffer(glUniformBuffer); + uniformBuffer = null; + Zone.freeBuffer(); + + vaoO.free(); + vaoA.free(); + vaoPO.free(); + vaoO = vaoA = vaoPO = null; } private void destroyGlBuffer(GLBuffer glBuffer) { if (glBuffer.glBufferId != -1) { - GL43C.glDeleteBuffers(glBuffer.glBufferId); + glDeleteBuffers(glBuffer.glBufferId); glBuffer.glBufferId = -1; } glBuffer.size = -1; - - if (glBuffer.clBuffer != -1) - { - CL12.clReleaseMemObject(glBuffer.clBuffer); - glBuffer.clBuffer = -1; - } } private void initInterfaceTexture() { - interfacePbo = GL43C.glGenBuffers(); - - interfaceTexture = GL43C.glGenTextures(); - GL43C.glBindTexture(GL43C.GL_TEXTURE_2D, interfaceTexture); - GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_WRAP_S, GL43C.GL_CLAMP_TO_EDGE); - GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_WRAP_T, GL43C.GL_CLAMP_TO_EDGE); - GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MIN_FILTER, GL43C.GL_LINEAR); - GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MAG_FILTER, GL43C.GL_LINEAR); - GL43C.glBindTexture(GL43C.GL_TEXTURE_2D, 0); + interfacePbo = glGenBuffers(); + + interfaceTexture = glGenTextures(); + glBindTexture(GL_TEXTURE_2D, interfaceTexture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glBindTexture(GL_TEXTURE_2D, 0); } private void shutdownInterfaceTexture() { - GL43C.glDeleteBuffers(interfacePbo); - GL43C.glDeleteTextures(interfaceTexture); + glDeleteBuffers(interfacePbo); + glDeleteTextures(interfaceTexture); interfaceTexture = -1; } - private void initUniformBuffer() - { - initGlBuffer(uniformBuffer); - - updateBuffer(uniformBuffer, GL43C.GL_UNIFORM_BUFFER, 8 * Integer.BYTES, GL43C.GL_DYNAMIC_DRAW, CL12.CL_MEM_READ_ONLY); - GL43C.glBindBuffer(GL43C.GL_UNIFORM_BUFFER, 0); - } - private void initFbo(int width, int height, int aaSamples) { final GraphicsConfiguration graphicsConfiguration = clientUI.getGraphicsConfiguration(); @@ -818,335 +696,128 @@ private void initFbo(int width, int height, int aaSamples) if (aaSamples > 0) { - GL43C.glEnable(GL43C.GL_MULTISAMPLE); + glEnable(GL_MULTISAMPLE); } else { - GL43C.glDisable(GL43C.GL_MULTISAMPLE); + glDisable(GL_MULTISAMPLE); } // Create and bind the FBO - fboScene = GL43C.glGenFramebuffers(); - GL43C.glBindFramebuffer(GL43C.GL_FRAMEBUFFER, fboScene); - - // Create color render buffer - rboColorBuffer = GL43C.glGenRenderbuffers(); - GL43C.glBindRenderbuffer(GL43C.GL_RENDERBUFFER, rboColorBuffer); - GL43C.glRenderbufferStorageMultisample(GL43C.GL_RENDERBUFFER, aaSamples, GL43C.GL_RGBA, width, height); - GL43C.glFramebufferRenderbuffer(GL43C.GL_FRAMEBUFFER, GL43C.GL_COLOR_ATTACHMENT0, GL43C.GL_RENDERBUFFER, rboColorBuffer); - - int status = GL43C.glCheckFramebufferStatus(GL43C.GL_FRAMEBUFFER); - if (status != GL43C.GL_FRAMEBUFFER_COMPLETE) + fboScene = glGenFramebuffers(); + glBindFramebuffer(GL_FRAMEBUFFER, fboScene); + + // Color render buffer + rboColorBuffer = glGenRenderbuffers(); + glBindRenderbuffer(GL_RENDERBUFFER, rboColorBuffer); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, aaSamples, GL_RGBA, width, height); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rboColorBuffer); + + // Depth render buffer + rboDepthBuffer = glGenRenderbuffers(); + glBindRenderbuffer(GL_RENDERBUFFER, rboDepthBuffer); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, aaSamples, GL_DEPTH_COMPONENT32F, width, height); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboDepthBuffer); + + int status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { throw new RuntimeException("FBO is incomplete. status: " + status); } // Reset - GL43C.glBindFramebuffer(GL43C.GL_FRAMEBUFFER, awtContext.getFramebuffer(false)); - GL43C.glBindRenderbuffer(GL43C.GL_RENDERBUFFER, 0); + glBindFramebuffer(GL_FRAMEBUFFER, awtContext.getFramebuffer(false)); + glBindRenderbuffer(GL_RENDERBUFFER, 0); } private void shutdownFbo() { if (fboScene != -1) { - GL43C.glDeleteFramebuffers(fboScene); + glDeleteFramebuffers(fboScene); fboScene = -1; } - if (rboColorBuffer != -1) + if (rboColorBuffer != 0) { - GL43C.glDeleteRenderbuffers(rboColorBuffer); - rboColorBuffer = -1; + glDeleteRenderbuffers(rboColorBuffer); + rboColorBuffer = 0; } - } - - @Override - public void drawScene(double cameraX, double cameraY, double cameraZ, double cameraPitch, double cameraYaw, int plane) - { - this.cameraX = cameraX; - this.cameraY = cameraY; - this.cameraZ = cameraZ; - this.cameraPitch = cameraPitch; - this.cameraYaw = cameraYaw; - viewportOffsetX = client.getViewportXOffset(); - viewportOffsetY = client.getViewportYOffset(); - viewportWidth = client.getViewportWidth(); - viewportHeight = client.getViewportHeight(); - - final Scene scene = client.getScene(); - scene.setDrawDistance(getDrawDistance()); - // Only reset the target buffer offset right before drawing the scene. That way if there are frames - // after this that don't involve a scene draw, like during LOADING/HOPPING/CONNECTION_LOST, we can - // still redraw the previous frame's scene to emulate the client behavior of not painting over the - // viewport buffer. - targetBufferOffset = 0; - - // UBO. - // We can reuse the vertex buffer since it isn't used yet. - vertexBuffer.clear(); - vertexBuffer.ensureCapacity(32); - IntBuffer uniformBuf = vertexBuffer.getBuffer(); - uniformBuf - .put(Float.floatToIntBits((float) cameraYaw)) - .put(Float.floatToIntBits((float) cameraPitch)) - .put(client.getCenterX()) - .put(client.getCenterY()) - .put(client.getScale()) - .put(Float.floatToIntBits((float) cameraX)) - .put(Float.floatToIntBits((float) cameraY)) - .put(Float.floatToIntBits((float) cameraZ)); - uniformBuf.flip(); - - updateBuffer(uniformBuffer, GL43C.GL_UNIFORM_BUFFER, uniformBuf, GL43C.GL_DYNAMIC_DRAW, CL12.CL_MEM_READ_ONLY); - GL43C.glBindBuffer(GL43C.GL_UNIFORM_BUFFER, 0); - - GL43C.glBindBufferBase(GL43C.GL_UNIFORM_BUFFER, 0, uniformBuffer.glBufferId); - uniformBuf.clear(); - - checkGLErrors(); - } - - @Override - public void postDrawScene() - { - if (computeMode == ComputeMode.NONE) + if (rboDepthBuffer != 0) { - // Upload buffers - vertexBuffer.flip(); - uvBuffer.flip(); - - IntBuffer vertexBuffer = this.vertexBuffer.getBuffer(); - FloatBuffer uvBuffer = this.uvBuffer.getBuffer(); - - updateBuffer(tmpVertexBuffer, GL43C.GL_ARRAY_BUFFER, vertexBuffer, GL43C.GL_DYNAMIC_DRAW, 0L); - updateBuffer(tmpUvBuffer, GL43C.GL_ARRAY_BUFFER, uvBuffer, GL43C.GL_DYNAMIC_DRAW, 0L); - - checkGLErrors(); - return; - } - - // Upload buffers - vertexBuffer.flip(); - uvBuffer.flip(); - modelBuffer.flip(); - modelBufferSmall.flip(); - modelBufferUnordered.flip(); - - IntBuffer vertexBuffer = this.vertexBuffer.getBuffer(); - FloatBuffer uvBuffer = this.uvBuffer.getBuffer(); - IntBuffer modelBuffer = this.modelBuffer.getBuffer(); - IntBuffer modelBufferSmall = this.modelBufferSmall.getBuffer(); - IntBuffer modelBufferUnordered = this.modelBufferUnordered.getBuffer(); - - // temp buffers - updateBuffer(tmpVertexBuffer, GL43C.GL_ARRAY_BUFFER, vertexBuffer, GL43C.GL_DYNAMIC_DRAW, CL12.CL_MEM_READ_ONLY); - updateBuffer(tmpUvBuffer, GL43C.GL_ARRAY_BUFFER, uvBuffer, GL43C.GL_DYNAMIC_DRAW, CL12.CL_MEM_READ_ONLY); - - // model buffers - updateBuffer(tmpModelBufferLarge, GL43C.GL_ARRAY_BUFFER, modelBuffer, GL43C.GL_DYNAMIC_DRAW, CL12.CL_MEM_READ_ONLY); - updateBuffer(tmpModelBufferSmall, GL43C.GL_ARRAY_BUFFER, modelBufferSmall, GL43C.GL_DYNAMIC_DRAW, CL12.CL_MEM_READ_ONLY); - updateBuffer(tmpModelBufferUnordered, GL43C.GL_ARRAY_BUFFER, modelBufferUnordered, GL43C.GL_DYNAMIC_DRAW, CL12.CL_MEM_READ_ONLY); - - // Output buffers - updateBuffer(tmpOutBuffer, - GL43C.GL_ARRAY_BUFFER, - targetBufferOffset * 16, // each element is an ivec4, which is 16 bytes - GL43C.GL_STREAM_DRAW, - CL12.CL_MEM_WRITE_ONLY); - updateBuffer(tmpOutUvBuffer, - GL43C.GL_ARRAY_BUFFER, - targetBufferOffset * 16, // each element is a vec4, which is 16 bytes - GL43C.GL_STREAM_DRAW, - CL12.CL_MEM_WRITE_ONLY); - - if (computeMode == ComputeMode.OPENCL) - { - // The docs for clEnqueueAcquireGLObjects say all pending GL operations must be completed before calling - // clEnqueueAcquireGLObjects, and recommends calling glFinish() as the only portable way to do that. - // However no issues have been observed from not calling it, and so will leave disabled for now. - // GL43C.glFinish(); - - openCLManager.compute( - unorderedModels, smallModels, largeModels, - sceneVertexBuffer, sceneUvBuffer, - tmpVertexBuffer, tmpUvBuffer, - tmpModelBufferUnordered, tmpModelBufferSmall, tmpModelBufferLarge, - tmpOutBuffer, tmpOutUvBuffer, - uniformBuffer); - - checkGLErrors(); - return; + glDeleteRenderbuffers(rboDepthBuffer); + rboDepthBuffer = 0; } - - /* - * Compute is split into three separate programs: 'unordered', 'small', and 'large' - * to save on GPU resources. Small will sort <= 512 faces, large will do <= 6144. - */ - - // Bind UBO to compute programs - GL43C.glUniformBlockBinding(glSmallComputeProgram, uniBlockSmall, 0); - GL43C.glUniformBlockBinding(glComputeProgram, uniBlockLarge, 0); - - // unordered - GL43C.glUseProgram(glUnorderedComputeProgram); - - GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 0, tmpModelBufferUnordered.glBufferId); - GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 1, sceneVertexBuffer.glBufferId); - GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 2, tmpVertexBuffer.glBufferId); - GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 3, tmpOutBuffer.glBufferId); - GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 4, tmpOutUvBuffer.glBufferId); - GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 5, sceneUvBuffer.glBufferId); - GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 6, tmpUvBuffer.glBufferId); - - GL43C.glDispatchCompute(unorderedModels, 1, 1); - - // small - GL43C.glUseProgram(glSmallComputeProgram); - - GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 0, tmpModelBufferSmall.glBufferId); - GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 1, sceneVertexBuffer.glBufferId); - GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 2, tmpVertexBuffer.glBufferId); - GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 3, tmpOutBuffer.glBufferId); - GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 4, tmpOutUvBuffer.glBufferId); - GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 5, sceneUvBuffer.glBufferId); - GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 6, tmpUvBuffer.glBufferId); - - GL43C.glDispatchCompute(smallModels, 1, 1); - - // large - GL43C.glUseProgram(glComputeProgram); - - GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 0, tmpModelBufferLarge.glBufferId); - GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 1, sceneVertexBuffer.glBufferId); - GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 2, tmpVertexBuffer.glBufferId); - GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 3, tmpOutBuffer.glBufferId); - GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 4, tmpOutUvBuffer.glBufferId); - GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 5, sceneUvBuffer.glBufferId); - GL43C.glBindBufferBase(GL43C.GL_SHADER_STORAGE_BUFFER, 6, tmpUvBuffer.glBufferId); - - GL43C.glDispatchCompute(largeModels, 1, 1); - - checkGLErrors(); } - @Override - public void drawScenePaint(Scene scene, SceneTilePaint paint, int plane, int tileX, int tileY) + static void updateEntityProjection(Projection projection) { - if (computeMode == ComputeMode.NONE) - { - targetBufferOffset += sceneUploader.upload(scene, paint, - plane, tileX, tileY, - vertexBuffer, uvBuffer, - tileX << Perspective.LOCAL_COORD_BITS, - tileY << Perspective.LOCAL_COORD_BITS, - true - ); - } - else if (paint.getBufferLen() > 0) + if (lastProjection != projection) { - final int localX = tileX << Perspective.LOCAL_COORD_BITS; - final int localY = 0; - final int localZ = tileY << Perspective.LOCAL_COORD_BITS; - - GpuIntBuffer b = modelBufferUnordered; - ++unorderedModels; - - b.ensureCapacity(8); - IntBuffer buffer = b.getBuffer(); - buffer.put(paint.getBufferOffset()); - buffer.put(paint.getUvBufferOffset()); - buffer.put(2); - buffer.put(targetBufferOffset); - buffer.put(FLAG_SCENE_BUFFER); - buffer.put(localX).put(localY).put(localZ); - - targetBufferOffset += 2 * 3; + float[] p = projection instanceof FloatProjection ? ((FloatProjection) projection).getProjection() : Mat4.identity(); + glUniformMatrix4fv(uniEntityProj, false, p); + lastProjection = projection; } } @Override - public void drawSceneTileModel(Scene scene, SceneTileModel model, int tileX, int tileY) + public void preSceneDraw(Scene scene, + float cameraX, float cameraY, float cameraZ, float cameraPitch, float cameraYaw, + int minLevel, int level, int maxLevel, Set hideRoofIds) { - if (computeMode == ComputeMode.NONE) + this.cameraX = (int) cameraX; + this.cameraY = (int) cameraY; + this.cameraZ = (int) cameraZ; + this.cameraYaw = client.getCameraYaw(); + this.cameraPitch = client.getCameraPitch(); + this.minLevel = minLevel; + this.level = level; + this.maxLevel = maxLevel; + this.hideRoofIds = hideRoofIds; + + if (scene.getWorldViewId() == WorldView.TOPLEVEL) { - targetBufferOffset += sceneUploader.upload(model, - 0, 0, - vertexBuffer, uvBuffer, - true); + preSceneDrawToplevel(scene, cameraX, cameraY, cameraZ, cameraPitch, cameraYaw); } - else if (model.getBufferLen() > 0) + else { - final int localX = tileX << Perspective.LOCAL_COORD_BITS; - final int localY = 0; - final int localZ = tileY << Perspective.LOCAL_COORD_BITS; - - GpuIntBuffer b = modelBufferUnordered; - ++unorderedModels; - - b.ensureCapacity(8); - IntBuffer buffer = b.getBuffer(); - buffer.put(model.getBufferOffset()); - buffer.put(model.getUvBufferOffset()); - buffer.put(model.getBufferLen() / 3); - buffer.put(targetBufferOffset); - buffer.put(FLAG_SCENE_BUFFER); - buffer.put(localX).put(localY).put(localZ); - - targetBufferOffset += model.getBufferLen(); + Scene toplevel = client.getScene(); + vaoO.addRange(null, toplevel); + vaoPO.addRange(null, toplevel); + glUniform4i(uniEntityTint, scene.getOverrideHue(), scene.getOverrideSaturation(), scene.getOverrideLuminance(), scene.getOverrideAmount()); } } - private void prepareInterfaceTexture(int canvasWidth, int canvasHeight) + private void preSceneDrawToplevel(Scene scene, + float cameraX, float cameraY, float cameraZ, float cameraPitch, float cameraYaw) { - if (canvasWidth != lastCanvasWidth || canvasHeight != lastCanvasHeight) - { - lastCanvasWidth = canvasWidth; - lastCanvasHeight = canvasHeight; - - GL43C.glBindBuffer(GL43C.GL_PIXEL_UNPACK_BUFFER, interfacePbo); - GL43C.glBufferData(GL43C.GL_PIXEL_UNPACK_BUFFER, canvasWidth * canvasHeight * 4L, GL43C.GL_STREAM_DRAW); - GL43C.glBindBuffer(GL43C.GL_PIXEL_UNPACK_BUFFER, 0); + scene.setDrawDistance(getDrawDistance()); - GL43C.glBindTexture(GL43C.GL_TEXTURE_2D, interfaceTexture); - GL43C.glTexImage2D(GL43C.GL_TEXTURE_2D, 0, GL43C.GL_RGBA, canvasWidth, canvasHeight, 0, GL43C.GL_BGRA, GL43C.GL_UNSIGNED_BYTE, 0); - GL43C.glBindTexture(GL43C.GL_TEXTURE_2D, 0); - } + // UBO + uniformBuffer.clear(); + uniformBuffer + .put(cameraYaw) + .put(cameraPitch) + .put(cameraX) + .put(cameraY) + .put(cameraZ); + uniformBuffer.flip(); - final BufferProvider bufferProvider = client.getBufferProvider(); - final int[] pixels = bufferProvider.getPixels(); - final int width = bufferProvider.getWidth(); - final int height = bufferProvider.getHeight(); + glBindBuffer(GL_UNIFORM_BUFFER, glUniformBuffer.glBufferId); + glBufferData(GL_UNIFORM_BUFFER, uniformBuffer.getBuffer(), GL_DYNAMIC_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + uniformBuffer.clear(); - GL43C.glBindBuffer(GL43C.GL_PIXEL_UNPACK_BUFFER, interfacePbo); - ByteBuffer interfaceBuf = GL43C.glMapBuffer(GL43C.GL_PIXEL_UNPACK_BUFFER, GL43C.GL_WRITE_ONLY); - if (interfaceBuf != null) - { - interfaceBuf - .asIntBuffer() - .put(pixels, 0, width * height); - GL43C.glUnmapBuffer(GL43C.GL_PIXEL_UNPACK_BUFFER); - } - GL43C.glBindTexture(GL43C.GL_TEXTURE_2D, interfaceTexture); - GL43C.glTexSubImage2D(GL43C.GL_TEXTURE_2D, 0, 0, 0, width, height, GL43C.GL_BGRA, GL43C.GL_UNSIGNED_INT_8_8_8_8_REV, 0); - GL43C.glBindBuffer(GL43C.GL_PIXEL_UNPACK_BUFFER, 0); - GL43C.glBindTexture(GL43C.GL_TEXTURE_2D, 0); - } + glBindBufferBase(GL_UNIFORM_BUFFER, 0, glUniformBuffer.glBufferId); - @Override - public void draw(int overlayColor) - { - final GameState gameState = client.getGameState(); - if (gameState == GameState.STARTING) - { - return; - } + checkGLErrors(); final int canvasHeight = client.getCanvasHeight(); final int canvasWidth = client.getCanvasWidth(); - prepareInterfaceTexture(canvasWidth, canvasHeight); + final int viewportHeight = client.getViewportHeight(); + final int viewportWidth = client.getViewportWidth(); // Setup FBO and anti-aliasing { @@ -1164,9 +835,9 @@ public void draw(int overlayColor) shutdownFbo(); // Bind default FBO to check whether anti-aliasing is forced - GL43C.glBindFramebuffer(GL43C.GL_FRAMEBUFFER, awtContext.getFramebuffer(false)); - final int forcedAASamples = GL43C.glGetInteger(GL43C.GL_SAMPLES); - final int maxSamples = GL43C.glGetInteger(GL43C.GL_MAX_SAMPLES); + glBindFramebuffer(GL_FRAMEBUFFER, awtContext.getFramebuffer(false)); + final int forcedAASamples = glGetInteger(GL_SAMPLES); + final int maxSamples = glGetInteger(GL_MAX_SAMPLES); final int samples = forcedAASamples != 0 ? forcedAASamples : Math.min(antiAliasingMode.getSamples(), maxSamples); @@ -1179,171 +850,512 @@ public void draw(int overlayColor) lastAntiAliasingMode = antiAliasingMode; } - GL43C.glBindFramebuffer(GL43C.GL_DRAW_FRAMEBUFFER, fboScene); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboScene); } // Clear scene int sky = client.getSkyboxColor(); - GL43C.glClearColor((sky >> 16 & 0xFF) / 255f, (sky >> 8 & 0xFF) / 255f, (sky & 0xFF) / 255f, 1f); - GL43C.glClear(GL43C.GL_COLOR_BUFFER_BIT); + glClearColor((sky >> 16 & 0xFF) / 255f, (sky >> 8 & 0xFF) / 255f, (sky & 0xFF) / 255f, 1f); + glClearDepthf(0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // Setup anisotropic filtering + final int anisotropicFilteringLevel = config.anisotropicFilteringLevel(); - // Draw 3d scene - if (gameState.getState() >= GameState.LOADING.getState()) + if (textureArrayId != -1 && lastAnisotropicFilteringLevel != anisotropicFilteringLevel) { - final TextureProvider textureProvider = client.getTextureProvider(); - if (textureArrayId == -1) - { - // lazy init textures as they may not be loaded at plugin start. - // this will return -1 and retry if not all textures are loaded yet, too. - textureArrayId = textureManager.initTextureArray(textureProvider); - if (textureArrayId > -1) - { - // if texture upload is successful, compute and set texture animations - float[] texAnims = textureManager.computeTextureAnimations(textureProvider); - GL43C.glUseProgram(glProgram); - GL43C.glUniform2fv(uniTextureAnimations, texAnims); - GL43C.glUseProgram(0); - } - } + textureManager.setAnisotropicFilteringLevel(textureArrayId, anisotropicFilteringLevel); + lastAnisotropicFilteringLevel = anisotropicFilteringLevel; + } - int renderWidthOff = viewportOffsetX; - int renderHeightOff = viewportOffsetY; - int renderCanvasHeight = canvasHeight; - int renderViewportHeight = viewportHeight; - int renderViewportWidth = viewportWidth; + // Setup viewport + int renderWidthOff = client.getViewportXOffset(); + int renderHeightOff = client.getViewportYOffset(); + int renderCanvasHeight = canvasHeight; + int renderViewportHeight = viewportHeight; + int renderViewportWidth = viewportWidth; + if (client.isStretchedEnabled()) + { + Dimension dim = client.getStretchedDimensions(); + renderCanvasHeight = dim.height; - // Setup anisotropic filtering - final int anisotropicFilteringLevel = config.anisotropicFilteringLevel(); + double scaleFactorY = dim.getHeight() / canvasHeight; + double scaleFactorX = dim.getWidth() / canvasWidth; - if (textureArrayId != -1 && lastAnisotropicFilteringLevel != anisotropicFilteringLevel) - { - textureManager.setAnisotropicFilteringLevel(textureArrayId, anisotropicFilteringLevel); - lastAnisotropicFilteringLevel = anisotropicFilteringLevel; - } + // Pad the viewport a little because having ints for our viewport dimensions can introduce off-by-one errors. + final int padding = 1; - if (client.isStretchedEnabled()) - { - Dimension dim = client.getStretchedDimensions(); - renderCanvasHeight = dim.height; + // Ceil the sizes because even if the size is 599.1 we want to treat it as size 600 (i.e. render to the x=599 pixel). + renderViewportHeight = (int) Math.ceil(scaleFactorY * (renderViewportHeight)) + padding * 2; + renderViewportWidth = (int) Math.ceil(scaleFactorX * (renderViewportWidth)) + padding * 2; - double scaleFactorY = dim.getHeight() / canvasHeight; - double scaleFactorX = dim.getWidth() / canvasWidth; + // Floor the offsets because even if the offset is 4.9, we want to render to the x=4 pixel anyway. + renderHeightOff = (int) Math.floor(scaleFactorY * (renderHeightOff)) - padding; + renderWidthOff = (int) Math.floor(scaleFactorX * (renderWidthOff)) - padding; + } - // Pad the viewport a little because having ints for our viewport dimensions can introduce off-by-one errors. - final int padding = 1; + glDpiAwareViewport(renderWidthOff, renderCanvasHeight - renderViewportHeight - renderHeightOff, renderViewportWidth, renderViewportHeight); - // Ceil the sizes because even if the size is 599.1 we want to treat it as size 600 (i.e. render to the x=599 pixel). - renderViewportHeight = (int) Math.ceil(scaleFactorY * (renderViewportHeight)) + padding * 2; - renderViewportWidth = (int) Math.ceil(scaleFactorX * (renderViewportWidth)) + padding * 2; + glUseProgram(glProgram); - // Floor the offsets because even if the offset is 4.9, we want to render to the x=4 pixel anyway. - renderHeightOff = (int) Math.floor(scaleFactorY * (renderHeightOff)) - padding; - renderWidthOff = (int) Math.floor(scaleFactorX * (renderWidthOff)) - padding; - } + // Setup uniforms + final int drawDistance = getDrawDistance(); + final int fogDepth = config.fogDepth(); + glUniform1i(uniUseFog, fogDepth > 0 ? 1 : 0); + glUniform4f(uniFogColor, (sky >> 16 & 0xFF) / 255f, (sky >> 8 & 0xFF) / 255f, (sky & 0xFF) / 255f, 1f); + glUniform1i(uniFogDepth, fogDepth); + glUniform1i(uniDrawDistance, drawDistance * Perspective.LOCAL_TILE_SIZE); + glUniform1i(uniExpandedMapLoadingChunks, client.getExpandedMapLoading()); - glDpiAwareViewport(renderWidthOff, renderCanvasHeight - renderViewportHeight - renderHeightOff, renderViewportWidth, renderViewportHeight); + // Brightness happens to also be stored in the texture provider, so we use that + TextureProvider textureProvider = client.getTextureProvider(); + glUniform1f(uniBrightness, (float) textureProvider.getBrightness()); + glUniform1f(uniTextureLightMode, config.brightTextures() ? 1f : 0f); + if (client.getGameState() == GameState.LOGGED_IN) + { + // avoid textures animating during loading + glUniform1i(uniTick, client.getGameCycle() & 127); + } - GL43C.glUseProgram(glProgram); + // Calculate projection matrix + float[] projectionMatrix = Mat4.scale(client.getScale(), client.getScale(), 1); + Mat4.mul(projectionMatrix, Mat4.projection(viewportWidth, viewportHeight, 50)); + Mat4.mul(projectionMatrix, Mat4.rotateX(cameraPitch)); + Mat4.mul(projectionMatrix, Mat4.rotateY(cameraYaw)); + Mat4.mul(projectionMatrix, Mat4.translate(-cameraX, -cameraY, -cameraZ)); + glUniformMatrix4fv(uniWorldProj, false, projectionMatrix); - final int drawDistance = getDrawDistance(); - final int fogDepth = config.fogDepth(); - GL43C.glUniform1i(uniUseFog, fogDepth > 0 ? 1 : 0); - GL43C.glUniform4f(uniFogColor, (sky >> 16 & 0xFF) / 255f, (sky >> 8 & 0xFF) / 255f, (sky & 0xFF) / 255f, 1f); - GL43C.glUniform1i(uniFogDepth, fogDepth); - GL43C.glUniform1i(uniDrawDistance, drawDistance * Perspective.LOCAL_TILE_SIZE); - GL43C.glUniform1i(uniExpandedMapLoadingChunks, client.getExpandedMapLoading()); + projectionMatrix = Mat4.identity(); + glUniformMatrix4fv(uniEntityProj, false, projectionMatrix); - // Brightness happens to also be stored in the texture provider, so we use that - GL43C.glUniform1f(uniBrightness, (float) textureProvider.getBrightness()); - GL43C.glUniform1f(uniSmoothBanding, config.smoothBanding() ? 0f : 1f); - GL43C.glUniform1i(uniColorBlindMode, config.colorBlindMode().ordinal()); - GL43C.glUniform1f(uniTextureLightMode, config.brightTextures() ? 1f : 0f); - if (gameState == GameState.LOGGED_IN) - { - // avoid textures animating during loading - GL43C.glUniform1i(uniTick, client.getGameCycle() & 127); - } + glUniform4i(uniEntityTint, 0, 0, 0, 0); - // Calculate projection matrix - float[] projectionMatrix = Mat4.scale(client.getScale(), client.getScale(), 1); - Mat4.mul(projectionMatrix, Mat4.projection(viewportWidth, viewportHeight, 50)); - Mat4.mul(projectionMatrix, Mat4.rotateX((float) cameraPitch)); - Mat4.mul(projectionMatrix, Mat4.rotateY((float) cameraYaw)); - Mat4.mul(projectionMatrix, Mat4.translate((float) -cameraX, (float) -cameraY, (float) -cameraZ)); - GL43C.glUniformMatrix4fv(uniProjectionMatrix, false, projectionMatrix); + // Bind uniforms + glUniformBlockBinding(glProgram, uniBlockMain, 0); + glUniform1i(uniTextures, 1); // texture sampler array is bound to texture1 - // Bind uniforms - GL43C.glUniformBlockBinding(glProgram, uniBlockMain, 0); - GL43C.glUniform1i(uniTextures, 1); // texture sampler array is bound to texture1 + // Enable face culling + glEnable(GL_CULL_FACE); - // We just allow the GL to do face culling. Note this requires the priority renderer - // to have logic to disregard culled faces in the priority depth testing. - GL43C.glEnable(GL43C.GL_CULL_FACE); + // Enable blending + glEnable(GL_BLEND); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE); - // Enable blending for alpha - GL43C.glEnable(GL43C.GL_BLEND); - GL43C.glBlendFuncSeparate(GL43C.GL_SRC_ALPHA, GL43C.GL_ONE_MINUS_SRC_ALPHA, GL43C.GL_ONE, GL43C.GL_ONE); + // Enable depth testing + glDepthFunc(GL_GREATER); + glEnable(GL_DEPTH_TEST); - // Draw buffers - if (computeMode != ComputeMode.NONE) - { - if (computeMode == ComputeMode.OPENGL) - { - // Before reading the SSBOs written to from postDrawScene() we must insert a barrier - GL43C.glMemoryBarrier(GL43C.GL_SHADER_STORAGE_BARRIER_BIT); - } - else - { - // Wait for the command queue to finish, so that we know the compute is done - openCLManager.finish(); - } + checkGLErrors(); + } - // Draw using the output buffer of the compute - GL43C.glBindVertexArray(vaoCompute); - } - else - { - // Only use the temporary buffers, which will contain the full scene - GL43C.glBindVertexArray(vaoTemp); - } + @Override + public void postSceneDraw(Scene scene) + { + if (scene.getWorldViewId() == WorldView.TOPLEVEL) + { + postDrawToplevel(); + } + else + { + glUniform4i(uniEntityTint, 0, 0, 0, 0); + } + } - GL43C.glDrawArrays(GL43C.GL_TRIANGLES, 0, targetBufferOffset); + private void postDrawToplevel() + { + glDisable(GL_BLEND); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); - GL43C.glDisable(GL43C.GL_BLEND); - GL43C.glDisable(GL43C.GL_CULL_FACE); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, awtContext.getFramebuffer(false)); + sceneFboValid = true; + } - GL43C.glUseProgram(0); - } + private void blitSceneFbo() + { + int width = lastStretchedCanvasWidth; + int height = lastStretchedCanvasHeight; - // Blit FBO - { - int width = lastStretchedCanvasWidth; - int height = lastStretchedCanvasHeight; + final GraphicsConfiguration graphicsConfiguration = clientUI.getGraphicsConfiguration(); + final AffineTransform transform = graphicsConfiguration.getDefaultTransform(); - final GraphicsConfiguration graphicsConfiguration = clientUI.getGraphicsConfiguration(); - final AffineTransform transform = graphicsConfiguration.getDefaultTransform(); + width = getScaledValue(transform.getScaleX(), width); + height = getScaledValue(transform.getScaleY(), height); - width = getScaledValue(transform.getScaleX(), width); - height = getScaledValue(transform.getScaleY(), height); + int defaultFbo = awtContext.getFramebuffer(false); + glBindFramebuffer(GL_READ_FRAMEBUFFER, fboScene); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, defaultFbo); + glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, + GL_COLOR_BUFFER_BIT, GL_NEAREST); - GL43C.glBindFramebuffer(GL43C.GL_READ_FRAMEBUFFER, fboScene); - GL43C.glBindFramebuffer(GL43C.GL_DRAW_FRAMEBUFFER, awtContext.getFramebuffer(false)); - GL43C.glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, - GL43C.GL_COLOR_BUFFER_BIT, GL43C.GL_NEAREST); + // Reset + glBindFramebuffer(GL_READ_FRAMEBUFFER, defaultFbo); - // Reset - GL43C.glBindFramebuffer(GL43C.GL_READ_FRAMEBUFFER, awtContext.getFramebuffer(false)); - } + checkGLErrors(); + } - vertexBuffer.clear(); - uvBuffer.clear(); - modelBuffer.clear(); - modelBufferSmall.clear(); - modelBufferUnordered.clear(); + @Override + public void drawZoneOpaque(Projection entityProjection, Scene scene, int zx, int zz) + { + updateEntityProjection(entityProjection); - smallModels = largeModels = unorderedModels = 0; - tempOffset = 0; - tempUvOffset = 0; + SceneContext ctx = context(scene); + if (ctx == null) + { + return; + } + + Zone z = ctx.zones[zx][zz]; + if (!z.initialized) + { + return; + } + + int offset = scene.getWorldViewId() == -1 ? (SCENE_OFFSET >> 3) : 0; + z.renderOpaque(zx - offset, zz - offset, minLevel, level, maxLevel, hideRoofIds); + + checkGLErrors(); + } + + @Override + public void drawZoneAlpha(Projection entityProjection, Scene scene, int level, int zx, int zz) + { + updateEntityProjection(entityProjection); + + SceneContext ctx = context(scene); + if (ctx == null) + { + return; + } + + // this is a noop after the first zone + vaoA.unmap(); + + Zone z = ctx.zones[zx][zz]; + if (!z.initialized) + { + return; + } + + int offset = scene.getWorldViewId() == -1 ? (SCENE_OFFSET >> 3) : 0; + if (level == 0) + { + z.alphaSort(zx - offset, zz - offset, cameraX, cameraY, cameraZ); + z.multizoneLocs(scene, zx - offset, zz - offset, cameraX, cameraZ, ctx.zones); + } + + glDepthMask(false); + z.renderAlpha(zx - offset, zz - offset, cameraYaw, cameraPitch, minLevel, this.level, maxLevel, level, hideRoofIds); + glDepthMask(true); + + checkGLErrors(); + } + + @Override + public void drawPass(Projection projection, Scene scene, int pass) + { + SceneContext ctx = context(scene); + if (ctx == null) + { + return; + } + + updateEntityProjection(projection); + + if (pass == DrawCallbacks.PASS_OPAQUE) + { + vaoO.addRange(projection, scene); + vaoPO.addRange(projection, scene); + + if (scene.getWorldViewId() == -1) + { + glProgramUniform3i(glProgram, uniBase, 0, 0, 0); + + var vaos = vaoO.unmap(); + for (VAO vao : vaos) + { + vao.draw(); + vao.reset(); + } + + vaos = vaoPO.unmap(); + glDepthMask(false); + for (VAO vao : vaos) + { + vao.draw(); + } + glDepthMask(true); + + glColorMask(false, false, false, false); + for (VAO vao : vaos) + { + vao.draw(); + vao.reset(); + } + glColorMask(true, true, true, true); + } + } + else if (pass == DrawCallbacks.PASS_ALPHA) + { + for (int x = 0; x < ctx.sizeX; ++x) + { + for (int z = 0; z < ctx.sizeZ; ++z) + { + Zone zone = ctx.zones[x][z]; + zone.removeTemp(); + } + } + } + + checkGLErrors(); + } + + @Override + public void drawDynamic(Projection worldProjection, Scene scene, TileObject tileObject, Renderable r, Model m, int orient, int x, int y, int z) + { + SceneContext ctx = context(scene); + if (ctx == null) + { + return; + } + + if (!renderCallbackManager.drawObject(scene, tileObject)) + { + return; + } + + int size = m.getFaceCount() * 3 * VAO.VERT_SIZE; + if (m.getFaceTransparencies() == null) + { + VAO o = vaoO.get(size); + SceneUploader.uploadTempModel(m, orient, x, y, z, o.vbo.vb); + } + else + { + m.calculateBoundsCylinder(); + VAO o = vaoO.get(size), a = vaoA.get(size); + int start = a.vbo.vb.position(); + facePrioritySorter.uploadSortedModel(worldProjection, m, orient, x, y, z, o.vbo.vb, a.vbo.vb); + int end = a.vbo.vb.position(); + + if (end > start) + { + int offset = scene.getWorldViewId() == -1 ? SCENE_OFFSET : 0; + int zx = (x >> 10) + (offset >> 3); + int zz = (z >> 10) + (offset >> 3); + Zone zone = ctx.zones[zx][zz]; + + // level is checked prior to this callback being run, in order to cull clickboxes, but + // tileObject.getPlane()>maxLevel if visbelow is set - lower the object to the max level + int plane = Math.min(maxLevel, tileObject.getPlane()); + // renderable modelheight is typically not set here because DynamicObject doesn't compute it on the returned model + zone.addTempAlphaModel(a.vao, start, end, plane, x & 1023, y, z & 1023); + } + } + } + + @Override + public void drawTemp(Projection worldProjection, Scene scene, GameObject gameObject, Model m) + { + SceneContext ctx = context(scene); + if (ctx == null) + { + return; + } + + int size = m.getFaceCount() * 3 * VAO.VERT_SIZE; + if (gameObject.getRenderable() instanceof Player || m.getFaceTransparencies() != null) + { + // opaque player faces have their own vao and are drawn in a separate pass from normal opaque faces + // because they are not depth tested. transparent player faces don't need their own vao because normal + // transparent faces are already not depth tested + VAO o = gameObject.getRenderable() instanceof Player ? vaoPO.get(size) : vaoO.get(size); + VAO a = vaoA.get(size); + + int start = a.vbo.vb.position(); + m.calculateBoundsCylinder(); + try + { + facePrioritySorter.uploadSortedModel(worldProjection, m, gameObject.getModelOrientation(), gameObject.getX(), gameObject.getZ(), gameObject.getY(), o.vbo.vb, a.vbo.vb); + } + catch (Exception ex) + { + log.debug("error drawing entity", ex); + } + int end = a.vbo.vb.position(); + + if (end > start) + { + int offset = scene.getWorldViewId() == -1 ? (SCENE_OFFSET >> 3) : 0; + int zx = (gameObject.getX() >> 10) + offset; + int zz = (gameObject.getY() >> 10) + offset; + Zone zone = ctx.zones[zx][zz]; + zone.addTempAlphaModel(a.vao, start, end, gameObject.getPlane(), gameObject.getX() & 1023, gameObject.getZ() - gameObject.getRenderable().getModelHeight() /* to render players over locs */, gameObject.getY() & 1023); + } + } + else + { + VAO o = vaoO.get(size); + SceneUploader.uploadTempModel(m, gameObject.getModelOrientation(), gameObject.getX(), gameObject.getZ(), gameObject.getY(), o.vbo.vb); + } + } + + @Override + public void invalidateZone(Scene scene, int zx, int zz) + { + SceneContext ctx = context(scene); + Zone z = ctx.zones[zx][zz]; + if (!z.invalidate) + { + z.invalidate = true; + log.debug("Zone invalidated: wx={} x={} z={}", scene.getWorldViewId(), zx, zz); + } + } + + @Subscribe + public void onPostClientTick(PostClientTick event) + { + WorldView wv = client.getTopLevelWorldView(); + if (wv == null) + { + return; + } + + rebuild(wv); + for (WorldEntity we : wv.worldEntities()) + { + wv = we.getWorldView(); + rebuild(wv); + } + } + + private void rebuild(WorldView wv) + { + SceneContext ctx = context(wv); + if (ctx == null) + { + return; + } + + for (int x = 0; x < ctx.sizeX; ++x) + { + for (int z = 0; z < ctx.sizeZ; ++z) + { + Zone zone = ctx.zones[x][z]; + if (!zone.invalidate) + { + continue; + } + + assert zone.initialized; + zone.free(); + zone = ctx.zones[x][z] = new Zone(); + + Scene scene = wv.getScene(); + SceneUploader sceneUploader = injector.getInstance(SceneUploader.class); + sceneUploader.zoneSize(scene, zone, x, z); + + VBO o = null, a = null; + int sz = zone.sizeO * Zone.VERT_SIZE * 3; + if (sz > 0) + { + o = new VBO(sz); + o.init(GL_STATIC_DRAW); + o.map(); + } + + sz = zone.sizeA * Zone.VERT_SIZE * 3; + if (sz > 0) + { + a = new VBO(sz); + a.init(GL_STATIC_DRAW); + a.map(); + } + + zone.init(o, a); + + sceneUploader.uploadZone(scene, zone, x, z); + + zone.unmap(); + zone.initialized = true; + zone.dirty = true; + + log.debug("Rebuilt zone wv={} x={} z={}", wv.getId(), x, z); + } + } + } + + private void prepareInterfaceTexture(int canvasWidth, int canvasHeight) + { + if (canvasWidth != lastCanvasWidth || canvasHeight != lastCanvasHeight) + { + lastCanvasWidth = canvasWidth; + lastCanvasHeight = canvasHeight; + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, interfacePbo); + glBufferData(GL_PIXEL_UNPACK_BUFFER, canvasWidth * canvasHeight * 4L, GL_STREAM_DRAW); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + + glBindTexture(GL_TEXTURE_2D, interfaceTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, canvasWidth, canvasHeight, 0, GL_BGRA, GL_UNSIGNED_BYTE, 0); + glBindTexture(GL_TEXTURE_2D, 0); + } + + final BufferProvider bufferProvider = client.getBufferProvider(); + final int[] pixels = bufferProvider.getPixels(); + final int width = bufferProvider.getWidth(); + final int height = bufferProvider.getHeight(); + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, interfacePbo); + ByteBuffer interfaceBuf = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY); + if (interfaceBuf != null) + { + interfaceBuf + .asIntBuffer() + .put(pixels, 0, width * height); + glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + } + glBindTexture(GL_TEXTURE_2D, interfaceTexture); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 0); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + glBindTexture(GL_TEXTURE_2D, 0); + } + + @Override + public void draw(int overlayColor) + { + final GameState gameState = client.getGameState(); + if (gameState == GameState.STARTING) + { + return; + } + + final TextureProvider textureProvider = client.getTextureProvider(); + if (textureArrayId == -1 && textureProvider != null) + { + // lazy init textures as they may not be loaded at plugin start. + // this will return -1 and retry if not all textures are loaded yet, too. + textureArrayId = textureManager.initTextureArray(textureProvider); + if (textureArrayId > -1) + { + // if texture upload is successful, compute and set texture animations + float[] texAnims = textureManager.computeTextureAnimations(textureProvider); + glProgramUniform2fv(glProgram, uniTextureAnimations, texAnims); + } + } + + final int canvasHeight = client.getCanvasHeight(); + final int canvasWidth = client.getCanvasWidth(); + + prepareInterfaceTexture(canvasWidth, canvasHeight); + + glClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT); + + if (sceneFboValid) + { + blitSceneFbo(); + } // Texture on UI drawUi(overlayColor, canvasHeight, canvasWidth); @@ -1383,25 +1395,23 @@ public void draw(int overlayColor) drawManager.processDrawComplete(this::screenshot); - GL43C.glBindFramebuffer(GL43C.GL_FRAMEBUFFER, awtContext.getFramebuffer(false)); + glBindFramebuffer(GL_FRAMEBUFFER, awtContext.getFramebuffer(false)); checkGLErrors(); } private void drawUi(final int overlayColor, final int canvasHeight, final int canvasWidth) { - GL43C.glEnable(GL43C.GL_BLEND); - GL43C.glBlendFunc(GL43C.GL_ONE, GL43C.GL_ONE_MINUS_SRC_ALPHA); - GL43C.glBindTexture(GL43C.GL_TEXTURE_2D, interfaceTexture); + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glBindTexture(GL_TEXTURE_2D, interfaceTexture); // Use the texture bound in the first pass final UIScalingMode uiScalingMode = config.uiScalingMode(); - GL43C.glUseProgram(glUiProgram); - GL43C.glUniform1i(uniTex, 0); - GL43C.glUniform1i(uniTexSamplingMode, uiScalingMode.getMode()); - GL43C.glUniform2i(uniTexSourceDimensions, canvasWidth, canvasHeight); - GL43C.glUniform1i(uniUiColorBlindMode, config.colorBlindMode().ordinal()); - GL43C.glUniform4f(uniUiAlphaOverlay, + glUseProgram(glUiProgram); + glUniform1i(uniTex, 0); + glUniform2i(uniTexSourceDimensions, canvasWidth, canvasHeight); + glUniform4f(uniUiAlphaOverlay, (overlayColor >> 16 & 0xFF) / 255f, (overlayColor >> 8 & 0xFF) / 255f, (overlayColor & 0xFF) / 255f, @@ -1412,32 +1422,32 @@ private void drawUi(final int overlayColor, final int canvasHeight, final int ca { Dimension dim = client.getStretchedDimensions(); glDpiAwareViewport(0, 0, dim.width, dim.height); - GL43C.glUniform2i(uniTexTargetDimensions, dim.width, dim.height); + glUniform2i(uniTexTargetDimensions, dim.width, dim.height); } else { glDpiAwareViewport(0, 0, canvasWidth, canvasHeight); - GL43C.glUniform2i(uniTexTargetDimensions, canvasWidth, canvasHeight); + glUniform2i(uniTexTargetDimensions, canvasWidth, canvasHeight); } // Set the sampling function used when stretching the UI. // This is probably better done with sampler objects instead of texture parameters, but this is easier and likely more portable. // See https://www.khronos.org/opengl/wiki/Sampler_Object for details. - // GL_NEAREST makes sampling for bicubic/xBR simpler, so it should be used whenever linear isn't - final int function = uiScalingMode == UIScalingMode.LINEAR ? GL43C.GL_LINEAR : GL43C.GL_NEAREST; - GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MIN_FILTER, function); - GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MAG_FILTER, function); + // GL_NEAREST makes sampling for bicubic/xBR simpler, so it should be used whenever linear/hybrid isn't + final int function = uiScalingMode == UIScalingMode.LINEAR || uiScalingMode == UIScalingMode.HYBRID ? GL_LINEAR : GL_NEAREST; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, function); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, function); // Texture on UI - GL43C.glBindVertexArray(vaoUiHandle); - GL43C.glDrawArrays(GL43C.GL_TRIANGLE_FAN, 0, 4); + glBindVertexArray(vaoUiHandle); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); // Reset - GL43C.glBindTexture(GL43C.GL_TEXTURE_2D, 0); - GL43C.glBindVertexArray(0); - GL43C.glUseProgram(0); - GL43C.glBlendFunc(GL43C.GL_SRC_ALPHA, GL43C.GL_ONE_MINUS_SRC_ALPHA); - GL43C.glDisable(GL43C.GL_BLEND); + glBindTexture(GL_TEXTURE_2D, 0); + glBindVertexArray(0); + glUseProgram(0); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_BLEND); } /** @@ -1465,8 +1475,8 @@ private Image screenshot() ByteBuffer buffer = ByteBuffer.allocateDirect(width * height * 4) .order(ByteOrder.nativeOrder()); - GL43C.glReadBuffer(awtContext.getBufferMode()); - GL43C.glReadPixels(0, 0, width, height, GL43C.GL_RGBA, GL43C.GL_UNSIGNED_BYTE, buffer); + glReadBuffer(awtContext.getBufferMode()); + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer); BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); @@ -1487,21 +1497,17 @@ private Image screenshot() return image; } - @Override - public void animate(Texture texture, int diff) - { - // texture animation happens on gpu - } - @Subscribe public void onGameStateChanged(GameStateChanged gameStateChanged) { - if (gameStateChanged.getGameState() == GameState.LOGIN_SCREEN) + GameState state = gameStateChanged.getGameState(); + if (state.getState() < GameState.LOADING.getState()) { - // Avoid drawing the last frame's buffer during LOADING after LOGIN_SCREEN - targetBufferOffset = 0; + // this is to avoid scene fbo blit when going from =loading, + // but keep it when doing >loading to loading + sceneFboValid = false; } - if (gameStateChanged.getGameState() == GameState.STARTING) + if (state == GameState.STARTING) { if (textureArrayId != -1) { @@ -1513,345 +1519,449 @@ public void onGameStateChanged(GameStateChanged gameStateChanged) } @Override - public void loadScene(Scene scene) + public void loadScene(WorldView worldView, Scene scene) { - if (computeMode == ComputeMode.NONE) + if (scene.getWorldViewId() > -1) { + loadSubScene(worldView, scene); return; } - GpuIntBuffer vertexBuffer = new GpuIntBuffer(); - GpuFloatBuffer uvBuffer = new GpuFloatBuffer(); + assert scene.getWorldViewId() == -1; + if (nextZones != null) + { + throw new RuntimeException("Double zone load!"); + } + + SceneContext ctx = root; + Scene prev = client.getTopLevelWorldView().getScene(); - sceneUploader.upload(scene, vertexBuffer, uvBuffer); + regionManager.prepare(scene); - vertexBuffer.flip(); - uvBuffer.flip(); + int dx = scene.getBaseX() - prev.getBaseX() >> 3; + int dy = scene.getBaseY() - prev.getBaseY() >> 3; - nextSceneVertexBuffer = vertexBuffer; - nextSceneTexBuffer = uvBuffer; - nextSceneId = sceneUploader.sceneId; - } + final int SCENE_ZONES = NUM_ZONES; - private void uploadTileHeights(Scene scene) - { - if (tileHeightTex != 0) + // initially mark every zone as needing culled + for (int x = 0; x < SCENE_ZONES; ++x) { - GL43C.glDeleteTextures(tileHeightTex); - tileHeightTex = 0; + for (int z = 0; z < SCENE_ZONES; ++z) + { + ctx.zones[x][z].cull = true; + } } - final int TILEHEIGHT_BUFFER_SIZE = Constants.MAX_Z * Constants.EXTENDED_SCENE_SIZE * Constants.EXTENDED_SCENE_SIZE * Short.BYTES; - ShortBuffer tileBuffer = ByteBuffer - .allocateDirect(TILEHEIGHT_BUFFER_SIZE) - .order(ByteOrder.nativeOrder()) - .asShortBuffer(); - - int[][][] tileHeights = scene.getTileHeights(); - for (int z = 0; z < Constants.MAX_Z; ++z) + // find zones which overlap and copy them + Zone[][] newZones = new Zone[SCENE_ZONES][SCENE_ZONES]; + final GameState gameState = client.getGameState(); + if (prev.isInstance() == scene.isInstance() + && prev.getRoofRemovalMode() == scene.getRoofRemovalMode() + && gameState == GameState.LOGGED_IN) { - for (int y = 0; y < Constants.EXTENDED_SCENE_SIZE; ++y) + int[][][] prevTemplates = prev.getInstanceTemplateChunks(); + int[][][] curTemplates = scene.getInstanceTemplateChunks(); + + for (int x = 0; x < SCENE_ZONES; ++x) { - for (int x = 0; x < Constants.EXTENDED_SCENE_SIZE; ++x) + next: + for (int z = 0; z < SCENE_ZONES; ++z) { - int h = tileHeights[z][x][y]; - assert (h & 0b111) == 0; - h >>= 3; - tileBuffer.put((short) h); + int ox = x + dx; + int oz = z + dy; + + // Reused the old zone if it is also in the new scene, except for the edges, to work around + // tile blending, (edge) shadows, sharelight, etc. + if (canReuse(ctx.zones, ox, oz)) + { + if (scene.isInstance()) + { + // Convert from modified chunk coordinates to Jagex chunk coordinates + int jx = x - (SCENE_OFFSET / 8); + int jz = z - (SCENE_OFFSET / 8); + int jox = ox - (SCENE_OFFSET / 8); + int joz = oz - (SCENE_OFFSET / 8); + // Check Jagex chunk coordinates are within the Jagex scene + if (jx >= 0 && jx < Constants.SCENE_SIZE / 8 && jz >= 0 && jz < Constants.SCENE_SIZE / 8) + { + if (jox >= 0 && jox < Constants.SCENE_SIZE / 8 && joz >= 0 && joz < Constants.SCENE_SIZE / 8) + { + for (int level = 0; level < 4; ++level) + { + int prevTemplate = prevTemplates[level][jox][joz]; + int curTemplate = curTemplates[level][jx][jz]; + if (prevTemplate != curTemplate) + { + log.error("Instance template reuse mismatch! prev={} cur={}", prevTemplate, curTemplate); + continue next; + } + } + } + } + } + + Zone old = ctx.zones[ox][oz]; + assert old.initialized; + + if (old.dirty) + { + continue; + } + + assert old.sizeO > 0 || old.sizeA > 0; + + assert old.cull; + old.cull = false; + + newZones[x][z] = old; + } } } } - tileBuffer.flip(); - - tileHeightTex = GL43C.glGenTextures(); - GL43C.glBindTexture(GL43C.GL_TEXTURE_3D, tileHeightTex); - GL43C.glTexParameteri(GL43C.GL_TEXTURE_3D, GL43C.GL_TEXTURE_MIN_FILTER, GL43C.GL_NEAREST); - GL43C.glTexParameteri(GL43C.GL_TEXTURE_3D, GL43C.GL_TEXTURE_MAG_FILTER, GL43C.GL_NEAREST); - GL43C.glTexParameteri(GL43C.GL_TEXTURE_3D, GL43C.GL_TEXTURE_WRAP_S, GL43C.GL_CLAMP_TO_EDGE); - GL43C.glTexParameteri(GL43C.GL_TEXTURE_3D, GL43C.GL_TEXTURE_WRAP_T, GL43C.GL_CLAMP_TO_EDGE); - GL43C.glTexImage3D(GL43C.GL_TEXTURE_3D, 0, GL43C.GL_R16I, - Constants.EXTENDED_SCENE_SIZE, Constants.EXTENDED_SCENE_SIZE, Constants.MAX_Z, - 0, GL43C.GL_RED_INTEGER, GL43C.GL_SHORT, tileBuffer); - GL43C.glBindTexture(GL43C.GL_TEXTURE_3D, 0); - - // bind to texture 2 - GL43C.glActiveTexture(GL43C.GL_TEXTURE2); - GL43C.glBindTexture(GL43C.GL_TEXTURE_3D, tileHeightTex); // binding = 2 in the shader - GL43C.glActiveTexture(GL43C.GL_TEXTURE0); - } - @Override - public void swapScene(Scene scene) - { - if (computeMode == ComputeMode.NONE) + // Fill out any zones that weren't copied + for (int x = 0; x < SCENE_ZONES; ++x) { - return; + for (int z = 0; z < SCENE_ZONES; ++z) + { + if (newZones[x][z] == null) + { + newZones[x][z] = new Zone(); + } + } } - if (computeMode == ComputeMode.OPENCL) - { - openCLManager.uploadTileHeights(scene); - } - else + // size the zones which require upload + SceneUploader sceneUploader = injector.getInstance(SceneUploader.class); + Stopwatch sw = Stopwatch.createStarted(); + int len = 0, lena = 0; + int reused = 0, newzones = 0; + for (int x = 0; x < NUM_ZONES; ++x) { - assert computeMode == ComputeMode.OPENGL; - uploadTileHeights(scene); - } - - sceneId = nextSceneId; - updateBuffer(sceneVertexBuffer, GL43C.GL_ARRAY_BUFFER, nextSceneVertexBuffer.getBuffer(), GL43C.GL_STATIC_COPY, CL12.CL_MEM_READ_ONLY); - updateBuffer(sceneUvBuffer, GL43C.GL_ARRAY_BUFFER, nextSceneTexBuffer.getBuffer(), GL43C.GL_STATIC_COPY, CL12.CL_MEM_READ_ONLY); - - nextSceneVertexBuffer = null; - nextSceneTexBuffer = null; - nextSceneId = -1; - - checkGLErrors(); - } - - @Override - public boolean tileInFrustum(Scene scene, float pitchSin, float pitchCos, float yawSin, float yawCos, int cameraX, int cameraY, int cameraZ, int plane, int msx, int msy) - { - int[][][] tileHeights = scene.getTileHeights(); - int x = ((msx - SCENE_OFFSET) << Perspective.LOCAL_COORD_BITS) + 64 - cameraX; - int z = ((msy - SCENE_OFFSET) << Perspective.LOCAL_COORD_BITS) + 64 - cameraZ; - int y = Math.max( - Math.max(tileHeights[plane][msx][msy], tileHeights[plane][msx][msy + 1]), - Math.max(tileHeights[plane][msx + 1][msy], tileHeights[plane][msx + 1][msy + 1]) - ) + GROUND_MIN_Y - cameraY; - - int radius = 96; // ~ 64 * sqrt(2) - - int zoom = client.get3dZoom(); - int Rasterizer3D_clipMidX2 = client.getRasterizer3D_clipMidX2(); - int Rasterizer3D_clipNegativeMidX = client.getRasterizer3D_clipNegativeMidX(); - int Rasterizer3D_clipNegativeMidY = client.getRasterizer3D_clipNegativeMidY(); - - float var11 = yawCos * z - yawSin * x; - float var12 = pitchSin * y + pitchCos * var11; - float var13 = pitchCos * radius; - float depth = var12 + var13; - if (depth > 50) - { - float rx = z * yawSin + yawCos * x; - float var16 = (rx - radius) * zoom; - float var17 = (rx + radius) * zoom; - // left && right - if (var16 < Rasterizer3D_clipMidX2 * depth && var17 > Rasterizer3D_clipNegativeMidX * depth) + for (int z = 0; z < NUM_ZONES; ++z) { - float ry = pitchCos * y - var11 * pitchSin; - float ybottom = pitchSin * radius; - float var20 = (ry + ybottom) * zoom; - // top - if (var20 > Rasterizer3D_clipNegativeMidY * depth) + Zone zone = newZones[x][z]; + if (!zone.initialized) + { + assert zone.glVao == 0; + assert zone.glVaoA == 0; + sceneUploader.zoneSize(scene, zone, x, z); + len += zone.sizeO; + lena += zone.sizeA; + newzones++; + } + else { - // we don't test the bottom so we don't have to find the height of all the models on the tile - return true; + reused++; } } } - return false; - } + log.debug("Scene size time {} reused {} new {} len opaque {} size opaque {}kb len alpha {} size alpha {}kb", + sw, reused, newzones, + len, (len * Zone.VERT_SIZE * 3) / 1024, + lena, (lena * Zone.VERT_SIZE * 3) / 1024); - /** - * Check is a model is visible and should be drawn. - */ - private boolean isVisible(Model model, float pitchSin, float pitchCos, float yawSin, float yawCos, int x, int y, int z) - { - final int xzMag = model.getXYZMag(); - final int bottomY = model.getBottomY(); - final int zoom = client.get3dZoom(); - final int modelHeight = model.getModelHeight(); - - int Rasterizer3D_clipMidX2 = client.getRasterizer3D_clipMidX2(); // width / 2 - int Rasterizer3D_clipNegativeMidX = client.getRasterizer3D_clipNegativeMidX(); // -width / 2 - int Rasterizer3D_clipNegativeMidY = client.getRasterizer3D_clipNegativeMidY(); // -height / 2 - int Rasterizer3D_clipMidY2 = client.getRasterizer3D_clipMidY2(); // height / 2 - - float var11 = yawCos * z - yawSin * x; - float var12 = pitchSin * y + pitchCos * var11; - float var13 = pitchCos * xzMag; - float depth = var12 + var13; - if (depth > 50) - { - float rx = z * yawSin + yawCos * x; - float var16 = (rx - xzMag) * zoom; - if (var16 / depth < Rasterizer3D_clipMidX2) + // allocate buffers for zones which require upload + CountDownLatch latch = new CountDownLatch(1); + clientThread.invoke(() -> + { + for (int x = 0; x < Constants.EXTENDED_SCENE_SIZE >> 3; ++x) { - float var17 = (rx + xzMag) * zoom; - if (var17 / depth > Rasterizer3D_clipNegativeMidX) + for (int z = 0; z < Constants.EXTENDED_SCENE_SIZE >> 3; ++z) { - float ry = pitchCos * y - var11 * pitchSin; - float yheight = pitchSin * xzMag; - float ybottom = (pitchCos * bottomY) + yheight; // use bottom height instead of y pos for height - float var20 = (ry + ybottom) * zoom; - if (var20 / depth > Rasterizer3D_clipNegativeMidY) + Zone zone = newZones[x][z]; + + if (zone.initialized) + { + continue; + } + + VBO o = null, a = null; + int sz = zone.sizeO * Zone.VERT_SIZE * 3; + if (sz > 0) + { + o = new VBO(sz); + o.init(GL_STATIC_DRAW); + o.map(); + } + + sz = zone.sizeA * Zone.VERT_SIZE * 3; + if (sz > 0) { - float ytop = (pitchCos * modelHeight) + yheight; - float var22 = (ry - ytop) * zoom; - return var22 / depth < Rasterizer3D_clipMidY2; + a = new VBO(sz); + a.init(GL_STATIC_DRAW); + a.map(); } + + zone.init(o, a); } } + + latch.countDown(); + }); + try + { + latch.await(); + } + catch (InterruptedException e) + { + throw new RuntimeException(e); } - return false; - } - /** - * Draw a renderable in the scene - */ - @Override - public void draw(Projection projection, Scene scene, Renderable renderable, int orientation, int x, int y, int z, long hash) - { - Model model, offsetModel; - if (renderable instanceof Model) + // upload zones + sw = Stopwatch.createStarted(); + for (int x = 0; x < Constants.EXTENDED_SCENE_SIZE >> 3; ++x) { - model = (Model) renderable; - offsetModel = model.getUnskewedModel(); - if (offsetModel == null) + for (int z = 0; z < Constants.EXTENDED_SCENE_SIZE >> 3; ++z) { - offsetModel = model; + Zone zone = newZones[x][z]; + + if (!zone.initialized) + { + sceneUploader.uploadZone(scene, zone, x, z); + } } } - else + log.debug("Scene upload time {}", sw); + + // Roof ids aren't consistent between scenes, so build a mapping of old -> new roof ids + Map roofChanges; { - model = renderable.getModel(); - if (model == null) + int[][][] prids = prev.getRoofs(); + int[][][] nrids = scene.getRoofs(); + dx <<= 3; + dy <<= 3; + roofChanges = new HashMap<>(); + + sw = Stopwatch.createStarted(); + for (int level = 0; level < 4; ++level) { - return; + for (int x = 0; x < Constants.EXTENDED_SCENE_SIZE; ++x) + { + for (int z = 0; z < Constants.EXTENDED_SCENE_SIZE; ++z) + { + int ox = x + dx; + int oz = z + dy; + + // old zone still in scene? + if (ox >= 0 && oz >= 0 && ox < Constants.EXTENDED_SCENE_SIZE && oz < Constants.EXTENDED_SCENE_SIZE) + { + int prid = prids[level][ox][oz]; + int nrid = nrids[level][x][z]; + if (prid > 0 && nrid > 0 && prid != nrid) + { + Integer old = roofChanges.putIfAbsent(prid, nrid); + if (old == null) + { + log.trace("Roof change: {} -> {}", prid, nrid); + } + else if (old != nrid) + { + log.debug("Roof change mismatch: {} -> {} vs {}", prid, nrid, old); + } + } + } + } + } } - offsetModel = model; + sw.stop(); + + log.debug("Roof remapping time {}", sw); } - if (computeMode == ComputeMode.NONE) + nextZones = newZones; + nextRoofChanges = roofChanges; + } + + private static boolean canReuse(Zone[][] zones, int zx, int zz) + { + // For tile blending, sharelight, and shadows to work correctly, the zones surrounding + // the zone must be valid. + for (int x = zx - 1; x <= zx + 1; ++x) { - // Apply height to renderable from the model - if (model != renderable) + if (x < 0 || x >= NUM_ZONES) { - renderable.setModelHeight(model.getModelHeight()); + return false; } - - model.calculateBoundsCylinder(); - - if (projection instanceof IntProjection) + for (int z = zz - 1; z <= zz + 1; ++z) { - IntProjection p = (IntProjection) projection; - if (!isVisible(model, p.getPitchSin(), p.getPitchCos(), p.getYawSin(), p.getYawCos(), x - p.getCameraX(), y - p.getCameraY(), z - p.getCameraZ())) + if (z < 0 || z >= NUM_ZONES) + { + return false; + } + Zone zone = zones[x][z]; + if (!zone.initialized) + { + return false; + } + if (zone.sizeO == 0 && zone.sizeA == 0) { - return; + return false; } } + } + return true; + } - client.checkClickbox(projection, model, orientation, x, y, z, hash); + private void loadSubScene(WorldView worldView, Scene scene) + { + int worldViewId = scene.getWorldViewId(); + assert worldViewId != -1; - targetBufferOffset += sceneUploader.pushSortedModel( - projection, - model, orientation, - x, y, z, - vertexBuffer, uvBuffer); - } - // Model may be in the scene buffer - else if (offsetModel.getSceneId() == sceneId) + log.debug("Loading world view {}", worldViewId); + + SceneContext ctx0 = subs[worldViewId]; + if (ctx0 != null) { - assert model == renderable; + throw new RuntimeException("Reload of an already loaded worldview?"); + } - model.calculateBoundsCylinder(); + final SceneContext ctx = new SceneContext(worldView.getSizeX() >> 3, worldView.getSizeY() >> 3); + subs[worldViewId] = ctx; - if (projection instanceof IntProjection) + SceneUploader sceneUploader = injector.getInstance(SceneUploader.class); + for (int x = 0; x < ctx.sizeX; ++x) + { + for (int z = 0; z < ctx.sizeZ; ++z) { - IntProjection p = (IntProjection) projection; - if (!isVisible(model, p.getPitchSin(), p.getPitchCos(), p.getYawSin(), p.getYawCos(), x - p.getCameraX(), y - p.getCameraY(), z - p.getCameraZ())) - { - return; - } + Zone zone = ctx.zones[x][z]; + sceneUploader.zoneSize(scene, zone, x, z); } + } - client.checkClickbox(projection, model, orientation, x, y, z, hash); + // allocate buffers for zones which require upload + CountDownLatch latch = new CountDownLatch(1); + clientThread.invoke(() -> + { + for (int x = 0; x < ctx.sizeX; ++x) + { + for (int z = 0; z < ctx.sizeZ; ++z) + { + Zone zone = ctx.zones[x][z]; - int tc = Math.min(MAX_TRIANGLE, offsetModel.getFaceCount()); - int uvOffset = offsetModel.getUvBufferOffset(); - int plane = (int) ((hash >> TileObject.HASH_PLANE_SHIFT) & 3); - boolean hillskew = offsetModel != model; + VBO o = null, a = null; + int sz = zone.sizeO * Zone.VERT_SIZE * 3; + if (sz > 0) + { + o = new VBO(sz); + o.init(GL_STATIC_DRAW); + o.map(); + } - GpuIntBuffer b = bufferForTriangles(tc); + sz = zone.sizeA * Zone.VERT_SIZE * 3; + if (sz > 0) + { + a = new VBO(sz); + a.init(GL_STATIC_DRAW); + a.map(); + } - b.ensureCapacity(8); - IntBuffer buffer = b.getBuffer(); - buffer.put(offsetModel.getBufferOffset()); - buffer.put(uvOffset); - buffer.put(tc); - buffer.put(targetBufferOffset); - buffer.put(FLAG_SCENE_BUFFER | (hillskew ? (1 << 26) : 0) | (plane << 24) | orientation); - buffer.put(x).put(y).put(z); + zone.init(o, a); + } + } - targetBufferOffset += tc * 3; + latch.countDown(); + }); + try + { + latch.await(); } - else + catch (InterruptedException e) { - // Temporary model (animated or otherwise not a static Model on the scene) + throw new RuntimeException(e); + } - // Apply height to renderable from the model - if (model != renderable) + for (int x = 0; x < ctx.sizeX; ++x) + { + for (int z = 0; z < ctx.sizeZ; ++z) { - renderable.setModelHeight(model.getModelHeight()); + Zone zone = ctx.zones[x][z]; + + sceneUploader.uploadZone(scene, zone, x, z); } + } + } - model.calculateBoundsCylinder(); + @Override + public void despawnWorldView(WorldView worldView) + { + int worldViewId = worldView.getId(); + if (worldViewId > -1) + { + log.debug("WorldView despawn: {}", worldViewId); + subs[worldViewId].free(); + subs[worldViewId] = null; + } + } + + @Override + public void swapScene(Scene scene) + { + if (scene.getWorldViewId() > -1) + { + swapSub(scene); + return; + } - if (projection instanceof IntProjection) + SceneContext ctx = root; + for (int x = 0; x < ctx.sizeX; ++x) + { + for (int z = 0; z < ctx.sizeZ; ++z) { - IntProjection p = (IntProjection) projection; - if (!isVisible(model, p.getPitchSin(), p.getPitchCos(), p.getYawSin(), p.getYawCos(), x - p.getCameraX(), y - p.getCameraY(), z - p.getCameraZ())) + Zone zone = ctx.zones[x][z]; + + if (zone.cull) + { + zone.free(); + } + else { - return; + // reused zone + zone.updateRoofs(nextRoofChanges); } } + } + nextRoofChanges = null; - client.checkClickbox(projection, model, orientation, x, y, z, hash); - - boolean hasUv = model.getFaceTextures() != null; - - int len = sceneUploader.pushModel(model, vertexBuffer, uvBuffer); - - GpuIntBuffer b = bufferForTriangles(len / 3); - - b.ensureCapacity(8); - IntBuffer buffer = b.getBuffer(); - buffer.put(tempOffset); - buffer.put(hasUv ? tempUvOffset : -1); - buffer.put(len / 3); - buffer.put(targetBufferOffset); - buffer.put(orientation); - buffer.put(x).put(y).put(z); + ctx.zones = nextZones; + nextZones = null; - tempOffset += len; - if (hasUv) + // setup vaos + for (int x = 0; x < ctx.zones.length; ++x) // NOPMD: ForLoopCanBeForeach + { + for (int z = 0; z < ctx.zones[0].length; ++z) { - tempUvOffset += len; - } + Zone zone = ctx.zones[x][z]; - targetBufferOffset += len; + if (!zone.initialized) + { + zone.unmap(); + zone.initialized = true; + } + } } + + checkGLErrors(); } - /** - * returns the correct buffer based on triangle count and updates model count - * - * @param triangles - * @return - */ - private GpuIntBuffer bufferForTriangles(int triangles) + private void swapSub(Scene scene) { - if (triangles <= SMALL_TRIANGLE_COUNT) + SceneContext ctx = context(scene); + // setup vaos + for (int x = 0; x < ctx.sizeX; ++x) { - ++smallModels; - return modelBufferSmall; - } - else - { - ++largeModels; - return modelBuffer; + for (int z = 0; z < ctx.sizeZ; ++z) + { + Zone zone = ctx.zones[x][z]; + + if (!zone.initialized) + { + zone.unmap(); + zone.initialized = true; + } + } } + log.debug("WorldView ready: {}", scene.getWorldViewId()); } private int getScaledValue(final double scale, final int value) @@ -1863,7 +1973,7 @@ private void glDpiAwareViewport(final int x, final int y, final int width, final { final GraphicsConfiguration graphicsConfiguration = clientUI.getGraphicsConfiguration(); final AffineTransform t = graphicsConfiguration.getDefaultTransform(); - GL43C.glViewport( + glViewport( getScaledValue(t.getScaleX(), x), getScaledValue(t.getScaleY(), y), getScaledValue(t.getScaleX(), width), @@ -1875,71 +1985,6 @@ private int getDrawDistance() return Ints.constrainToRange(config.drawDistance(), 0, MAX_DISTANCE); } - private void updateBuffer(@Nonnull GLBuffer glBuffer, int target, @Nonnull IntBuffer data, int usage, long clFlags) - { - int size = data.remaining() << 2; - updateBuffer(glBuffer, target, size, usage, clFlags); - GL43C.glBufferSubData(target, 0, data); - } - - private void updateBuffer(@Nonnull GLBuffer glBuffer, int target, @Nonnull FloatBuffer data, int usage, long clFlags) - { - int size = data.remaining() << 2; - updateBuffer(glBuffer, target, size, usage, clFlags); - GL43C.glBufferSubData(target, 0, data); - } - - private void updateBuffer(@Nonnull GLBuffer glBuffer, int target, int size, int usage, long clFlags) - { - GL43C.glBindBuffer(target, glBuffer.glBufferId); - if (glCapabilities.glInvalidateBufferData != 0L) - { - // https://www.khronos.org/opengl/wiki/Buffer_Object_Streaming suggests buffer re-specification is useful - // to avoid implicit synching. We always need to trash the whole buffer anyway so this can't hurt. - GL43C.glInvalidateBufferData(glBuffer.glBufferId); - } - if (size > glBuffer.size) - { - int newSize = Math.max(1024, nextPowerOfTwo(size)); - log.trace("Buffer resize: {} {} -> {}", glBuffer.name, glBuffer.size, newSize); - - glBuffer.size = newSize; - GL43C.glBufferData(target, newSize, usage); - recreateCLBuffer(glBuffer, clFlags); - } - } - - private static int nextPowerOfTwo(int v) - { - v--; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - v++; - return v; - } - - private void recreateCLBuffer(GLBuffer glBuffer, long clFlags) - { - if (computeMode == ComputeMode.OPENCL) - { - if (glBuffer.clBuffer != -1) - { - CL10.clReleaseMemObject(glBuffer.clBuffer); - } - if (glBuffer.size == 0) - { - glBuffer.clBuffer = -1; - } - else - { - glBuffer.clBuffer = CL10GL.clCreateFromGLBuffer(openCLManager.context, clFlags, glBuffer.glBufferId, (int[]) null); - } - } - } - private void checkGLErrors() { if (!log.isDebugEnabled()) @@ -1949,8 +1994,8 @@ private void checkGLErrors() for (; ; ) { - int err = GL43C.glGetError(); - if (err == GL43C.GL_NO_ERROR) + int err = glGetError(); + if (err == GL_NO_ERROR) { return; } @@ -1958,16 +2003,16 @@ private void checkGLErrors() String errStr; switch (err) { - case GL43C.GL_INVALID_ENUM: + case GL_INVALID_ENUM: errStr = "INVALID_ENUM"; break; - case GL43C.GL_INVALID_VALUE: + case GL_INVALID_VALUE: errStr = "INVALID_VALUE"; break; - case GL43C.GL_INVALID_OPERATION: + case GL_INVALID_OPERATION: errStr = "INVALID_OPERATION"; break; - case GL43C.GL_INVALID_FRAMEBUFFER_OPERATION: + case GL_INVALID_FRAMEBUFFER_OPERATION: errStr = "INVALID_FRAMEBUFFER_OPERATION"; break; default: 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 e39057ec1d5..2f34fa8cf3f 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 @@ -73,22 +73,11 @@ default boolean hideUnrelatedMaps() description = "Extra map area to load, in 8 tile chunks.", position = 1 ) - default int expandedMapLoadingChunks() + default int expandedMapLoadingZones() { return 3; } - @ConfigItem( - keyName = "smoothBanding", - name = "Remove color banding", - description = "Smooths out the color banding that is present in the CPU renderer.", - position = 2 - ) - default boolean smoothBanding() - { - return true; - } - @ConfigItem( keyName = "antiAliasingMode", name = "Anti aliasing", @@ -108,7 +97,7 @@ default AntiAliasingMode antiAliasingMode() ) default UIScalingMode uiScalingMode() { - return UIScalingMode.LINEAR; + return UIScalingMode.HYBRID; } @Range( @@ -125,18 +114,6 @@ default int fogDepth() return 0; } - @ConfigItem( - keyName = "useComputeShaders", - name = "Compute shaders", - description = "Offloads face sorting to GPU. Requires plugin restart.", - warning = "This feature requires OpenGL 4.3 to use. Please check that your GPU supports this.\nRestart the plugin for changes to take effect.", - position = 6 - ) - default boolean useComputeShaders() - { - return true; - } - @Range( min = 0, max = 16 diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/OpenCLManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/OpenCLManager.java deleted file mode 100644 index 4d12d88ef1c..00000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/OpenCLManager.java +++ /dev/null @@ -1,661 +0,0 @@ -/* - * Copyright (c) 2021, Adam - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.runelite.client.plugins.gpu; - -import java.nio.ByteBuffer; -import java.nio.IntBuffer; -import java.nio.LongBuffer; -import java.nio.ShortBuffer; -import javax.inject.Singleton; -import lombok.extern.slf4j.Slf4j; -import net.runelite.api.Constants; -import net.runelite.api.Scene; -import net.runelite.client.plugins.gpu.template.Template; -import net.runelite.client.util.OSType; -import net.runelite.rlawt.AWTContext; -import org.lwjgl.PointerBuffer; -import org.lwjgl.opencl.APPLEGLSharing; -import static org.lwjgl.opencl.APPLEGLSharing.CL_CGL_DEVICE_FOR_CURRENT_VIRTUAL_SCREEN_APPLE; -import static org.lwjgl.opencl.APPLEGLSharing.clGetGLContextInfoAPPLE; -import org.lwjgl.opencl.CL; -import static org.lwjgl.opencl.CL10.CL_CONTEXT_PLATFORM; -import static org.lwjgl.opencl.CL10.CL_DEVICE_EXTENSIONS; -import static org.lwjgl.opencl.CL10.CL_DEVICE_NAME; -import static org.lwjgl.opencl.CL10.CL_DEVICE_PROFILE; -import static org.lwjgl.opencl.CL10.CL_DEVICE_VENDOR; -import static org.lwjgl.opencl.CL10.CL_DEVICE_VERSION; -import static org.lwjgl.opencl.CL10.CL_DRIVER_VERSION; -import static org.lwjgl.opencl.CL10.CL_PROGRAM_BUILD_LOG; -import static org.lwjgl.opencl.CL10.CL_PROGRAM_BUILD_OPTIONS; -import static org.lwjgl.opencl.CL10.CL_PROGRAM_BUILD_STATUS; -import static org.lwjgl.opencl.CL10.CL_SUCCESS; -import static org.lwjgl.opencl.CL10.clGetPlatformInfo; -import static org.lwjgl.opencl.CL10.clGetProgramBuildInfo; -import org.lwjgl.opencl.CL10GL; -import static org.lwjgl.opencl.CL11.CL_DEVICE_ADDRESS_BITS; -import static org.lwjgl.opencl.CL11.CL_DEVICE_AVAILABLE; -import static org.lwjgl.opencl.CL11.CL_DEVICE_COMPILER_AVAILABLE; -import static org.lwjgl.opencl.CL11.CL_DEVICE_MAX_CLOCK_FREQUENCY; -import static org.lwjgl.opencl.CL11.CL_DEVICE_MAX_COMPUTE_UNITS; -import static org.lwjgl.opencl.CL11.CL_DEVICE_MAX_WORK_GROUP_SIZE; -import static org.lwjgl.opencl.CL11.CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS; -import static org.lwjgl.opencl.CL11.CL_DEVICE_QUEUE_PROPERTIES; -import static org.lwjgl.opencl.CL11.CL_DEVICE_TYPE; -import static org.lwjgl.opencl.CL11.CL_DEVICE_TYPE_GPU; -import static org.lwjgl.opencl.CL11.CL_DEVICE_VENDOR_ID; -import static org.lwjgl.opencl.CL11.CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE; -import static org.lwjgl.opencl.CL11.clCreateContext; -import static org.lwjgl.opencl.CL11.clGetDeviceIDs; -import static org.lwjgl.opencl.CL11.clGetDeviceInfo; -import static org.lwjgl.opencl.CL11.clGetPlatformIDs; -import org.lwjgl.opencl.CL12; -import static org.lwjgl.opencl.CL12.CL_PROGRAM_BINARY_TYPE; -import org.lwjgl.opencl.CLCapabilities; -import org.lwjgl.opencl.CLContextCallback; -import org.lwjgl.opencl.CLImageFormat; -import static org.lwjgl.opencl.KHRGLSharing.CL_GLX_DISPLAY_KHR; -import static org.lwjgl.opencl.KHRGLSharing.CL_GL_CONTEXT_KHR; -import static org.lwjgl.opencl.KHRGLSharing.CL_WGL_HDC_KHR; -import org.lwjgl.system.Configuration; -import org.lwjgl.system.MemoryStack; -import org.lwjgl.system.MemoryUtil; -import static org.lwjgl.system.MemoryUtil.NULL; -import static org.lwjgl.system.MemoryUtil.memASCII; -import static org.lwjgl.system.MemoryUtil.memUTF8; - -@Singleton -@Slf4j -class OpenCLManager -{ - private static final String KERNEL_NAME_UNORDERED = "computeUnordered"; - private static final String KERNEL_NAME_LARGE = "computeLarge"; - - private static final int MIN_WORK_GROUP_SIZE = 256; - private static final int SMALL_SIZE = GpuPlugin.SMALL_TRIANGLE_COUNT; - private static final int LARGE_SIZE = GpuPlugin.MAX_TRIANGLE; - // struct shared_data { - // int totalNum[12]; - // int totalDistance[12]; - // int totalMappedNum[18]; - // int min10; - // int renderPris[0]; - // }; - private static final int SHARED_SIZE = 12 + 12 + 18 + 1; // in ints - - private boolean initialized; - - // The number of faces each worker processes in the two kernels - private int largeFaceCount; - private int smallFaceCount; - - private long device; - long context; - private long commandQueue; - - private long programUnordered; - private long programSmall; - private long programLarge; - - private long kernelUnordered; - private long kernelSmall; - private long kernelLarge; - - private long tileHeightImage; - - static - { - Configuration.OPENCL_EXPLICIT_INIT.set(true); - } - - void init(AWTContext awtContext) - { - device = context = commandQueue = 0L; - programUnordered = programSmall = programLarge = 0L; - kernelUnordered = kernelSmall = kernelLarge = 0L; - tileHeightImage = 0L; - - CL.create(); - initialized = true; - - try (var stack = MemoryStack.stackPush()) - { - if (OSType.getOSType() == OSType.MacOS) - { - initContextMacOS(awtContext, stack); - } - else - { - initContext(awtContext, stack); - } - - ensureMinWorkGroupSize(); - initQueue(); - compilePrograms(stack); - } - } - - void cleanup() - { - if (!initialized) - { - return; - } - - try - { - if (tileHeightImage != 0L) - { - CL12.clReleaseMemObject(tileHeightImage); - } - - CL12.clReleaseKernel(kernelUnordered); - CL12.clReleaseKernel(kernelSmall); - CL12.clReleaseKernel(kernelLarge); - - CL12.clReleaseProgram(programUnordered); - CL12.clReleaseProgram(programSmall); - CL12.clReleaseProgram(programLarge); - - CL12.clReleaseCommandQueue(commandQueue); - - CL12.clReleaseContext(context); - - CL12.clReleaseDevice(device); - } - finally - { - CL.destroy(); - initialized = false; - } - } - - private void initContext(AWTContext awtContext, MemoryStack stack) - { - IntBuffer pi = stack.mallocInt(1); - checkCLError(clGetPlatformIDs(null, pi)); - if (pi.get(0) == 0) - { - throw new RuntimeException("No OpenCL platforms found."); - } - - PointerBuffer platforms = stack.mallocPointer(pi.get(0)); - checkCLError(clGetPlatformIDs(platforms, (IntBuffer) null)); - - PointerBuffer ctxProps = stack.mallocPointer(7); - if (OSType.getOSType() == OSType.Windows) - { - ctxProps - .put(CL_CONTEXT_PLATFORM) - .put(0) - .put(CL_GL_CONTEXT_KHR) - .put(awtContext.getGLContext()) - .put(CL_WGL_HDC_KHR) - .put(awtContext.getWGLHDC()) - .put(0) - .flip(); - } - else if (OSType.getOSType() == OSType.Linux) - { - ctxProps - .put(CL_CONTEXT_PLATFORM) - .put(0) - .put(CL_GL_CONTEXT_KHR) - .put(awtContext.getGLContext()) - .put(CL_GLX_DISPLAY_KHR) - .put(awtContext.getGLXDisplay()) - .put(0) - .flip(); - } - else - { - throw new RuntimeException("unsupported platform"); - } - - for (int p = 0; p < platforms.capacity(); p++) - { - long platform = platforms.get(p); - ctxProps.put(1, platform); - - try - { - CLCapabilities platformCaps = CL.createPlatformCapabilities(platform); - - log.debug("Platform profile: {}", getPlatformInfoStringUTF8(platform, CL12.CL_PLATFORM_PROFILE)); - log.debug("Platform version: {}", getPlatformInfoStringUTF8(platform, CL12.CL_PLATFORM_VERSION)); - log.debug("Platform name: {}", getPlatformInfoStringUTF8(platform, CL12.CL_PLATFORM_NAME)); - log.debug("Platform vendor: {}", getPlatformInfoStringUTF8(platform, CL12.CL_PLATFORM_VENDOR)); - log.debug("Platform extensions: {}", getPlatformInfoStringUTF8(platform, CL12.CL_PLATFORM_EXTENSIONS)); - - checkCLError(clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, null, pi)); - - PointerBuffer devices = stack.mallocPointer(pi.get(0)); - checkCLError(clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, devices, (IntBuffer) null)); - - for (int d = 0; d < devices.capacity(); d++) - { - long device = devices.get(d); - - try - { - CLCapabilities deviceCaps = CL.createDeviceCapabilities(device, platformCaps); - - log.debug("Device id {}", device); - log.debug("\tCL_DEVICE_NAME: {}", getDeviceInfoStringUTF8(device, CL_DEVICE_NAME)); - log.debug("\tCL_DEVICE_VENDOR: {}", getDeviceInfoStringUTF8(device, CL_DEVICE_VENDOR)); - log.debug("\tCL_DRIVER_VERSION: {}", getDeviceInfoStringUTF8(device, CL_DRIVER_VERSION)); - log.debug("\tCL_DEVICE_PROFILE: {}", getDeviceInfoStringUTF8(device, CL_DEVICE_PROFILE)); - log.debug("\tCL_DEVICE_VERSION: {}", getDeviceInfoStringUTF8(device, CL_DEVICE_VERSION)); - log.debug("\tCL_DEVICE_EXTENSIONS: {}", getDeviceInfoStringUTF8(device, CL_DEVICE_EXTENSIONS)); - log.debug("\tCL_DEVICE_TYPE: {}", getDeviceInfoLong(device, CL_DEVICE_TYPE)); - log.debug("\tCL_DEVICE_VENDOR_ID: {}", getDeviceInfoInt(device, CL_DEVICE_VENDOR_ID)); - log.debug("\tCL_DEVICE_MAX_COMPUTE_UNITS: {}", getDeviceInfoInt(device, CL_DEVICE_MAX_COMPUTE_UNITS)); - log.debug("\tCL_DEVICE_MAX_WORK_ITEM_DIMENSIONS: {}", getDeviceInfoInt(device, CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS)); - log.debug("\tCL_DEVICE_MAX_WORK_GROUP_SIZE: {}", getDeviceInfoPointer(device, CL_DEVICE_MAX_WORK_GROUP_SIZE)); - log.debug("\tCL_DEVICE_MAX_CLOCK_FREQUENCY: {}", getDeviceInfoInt(device, CL_DEVICE_MAX_CLOCK_FREQUENCY)); - log.debug("\tCL_DEVICE_ADDRESS_BITS: {}", getDeviceInfoInt(device, CL_DEVICE_ADDRESS_BITS)); - log.debug("\tCL_DEVICE_AVAILABLE: {}", getDeviceInfoInt(device, CL_DEVICE_AVAILABLE) != 0); - log.debug("\tCL_DEVICE_COMPILER_AVAILABLE: {}", getDeviceInfoInt(device, CL_DEVICE_COMPILER_AVAILABLE) != 0); - - if (!deviceCaps.cl_khr_gl_sharing && !deviceCaps.cl_APPLE_gl_sharing) - { - continue; - } - - IntBuffer errcode_ret = stack.callocInt(1); - long context = clCreateContext(ctxProps, device, CLContextCallback.create((errinfo, private_info, cb, user_data) -> - log.error("[LWJGL] cl_context_callback: {}", memUTF8(errinfo))), NULL, errcode_ret); - checkCLError(errcode_ret); - - this.device = device; - this.context = context; - return; - } - catch (Exception ex) - { - log.error("error checking device", ex); - } - } - } - catch (Exception ex) - { - log.error("error checking platform", ex); - } - } - - throw new RuntimeException("Unable to find compute platform"); - } - - private void initContextMacOS(AWTContext awtContext, MemoryStack stack) - { - PointerBuffer ctxProps = stack.mallocPointer(3); - ctxProps - .put(APPLEGLSharing.CL_CONTEXT_PROPERTY_USE_CGL_SHAREGROUP_APPLE) - .put(awtContext.getCGLShareGroup()) - .put(0) - .flip(); - - IntBuffer errcode_ret = stack.callocInt(1); - var devices = stack.mallocPointer(0); - long context = clCreateContext(ctxProps, devices, CLContextCallback.create((errinfo, private_info, cb, user_data) -> - log.error("[LWJGL] cl_context_callback: {}", memUTF8(errinfo))), NULL, errcode_ret); - checkCLError(errcode_ret); - - var deviceBuf = stack.mallocPointer(1); - checkCLError(clGetGLContextInfoAPPLE(context, awtContext.getGLContext(), CL_CGL_DEVICE_FOR_CURRENT_VIRTUAL_SCREEN_APPLE, deviceBuf, null)); - long device = deviceBuf.get(0); - - log.debug("Got macOS CLGL compute device {}", device); - this.context = context; - this.device = device; - } - - private void ensureMinWorkGroupSize() - { - long[] maxWorkGroupSize = new long[1]; - CL12.clGetDeviceInfo(device, CL_DEVICE_MAX_WORK_GROUP_SIZE, maxWorkGroupSize, null); - log.debug("Device CL_DEVICE_MAX_WORK_GROUP_SIZE: {}", maxWorkGroupSize[0]); - - if (maxWorkGroupSize[0] < MIN_WORK_GROUP_SIZE) - { - throw new RuntimeException("Compute device does not support min work group size " + MIN_WORK_GROUP_SIZE); - } - - // Largest power of 2 less than or equal to maxWorkGroupSize - int groupSize = 0x80000000 >>> Integer.numberOfLeadingZeros((int) maxWorkGroupSize[0]); - largeFaceCount = LARGE_SIZE / (Math.min(groupSize, LARGE_SIZE)); - smallFaceCount = SMALL_SIZE / (Math.min(groupSize, SMALL_SIZE)); - - log.debug("Face counts: small: {}, large: {}", smallFaceCount, largeFaceCount); - } - - private void initQueue() - { - long[] l = new long[1]; - CL12.clGetDeviceInfo(device, CL_DEVICE_QUEUE_PROPERTIES, l, null); - - commandQueue = CL12.clCreateCommandQueue(context, device, l[0] & CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE, (int[]) null); - log.debug("Created command_queue {}, properties {}", commandQueue, l[0] & CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE); - } - - private long compileProgram(MemoryStack stack, String programSource) - { - log.trace("Compiling program:\n {}", programSource); - IntBuffer errcode_ret = stack.callocInt(1); - long program = CL12.clCreateProgramWithSource(context, programSource, errcode_ret); - checkCLError(errcode_ret); - - int err = CL12.clBuildProgram(program, device, "", null, 0); - if (err != CL_SUCCESS) - { - String errstr = getProgramBuildInfoStringASCII(program, device, CL_PROGRAM_BUILD_LOG); - throw new RuntimeException(errstr); - } - - log.debug("Build status: {}", getProgramBuildInfoInt(program, device, CL_PROGRAM_BUILD_STATUS)); - log.debug("Binary type: {}", getProgramBuildInfoInt(program, device, CL_PROGRAM_BINARY_TYPE)); - log.debug("Build options: {}", getProgramBuildInfoStringASCII(program, device, CL_PROGRAM_BUILD_OPTIONS)); - log.debug("Build log: {}", getProgramBuildInfoStringASCII(program, device, CL_PROGRAM_BUILD_LOG)); - return program; - } - - private long getKernel(MemoryStack stack, long program, String kernelName) - { - IntBuffer errcode_ret = stack.callocInt(1); - long kernel = CL12.clCreateKernel(program, kernelName, errcode_ret); - checkCLError(errcode_ret); - log.debug("Loaded kernel {} for program {}", kernelName, program); - return kernel; - } - - private void compilePrograms(MemoryStack stack) - { - Template templateSmall = new Template() - .addInclude(OpenCLManager.class) - .add(key -> key.equals("FACE_COUNT") ? ("#define FACE_COUNT " + smallFaceCount) : null); - Template templateLarge = new Template() - .addInclude(OpenCLManager.class) - .add(key -> key.equals("FACE_COUNT") ? ("#define FACE_COUNT " + largeFaceCount) : null); - - String unordered = new Template() - .addInclude(OpenCLManager.class) - .load("comp_unordered.cl"); - String small = templateSmall.load("comp.cl"); - String large = templateLarge.load("comp.cl"); - - programUnordered = compileProgram(stack, unordered); - programSmall = compileProgram(stack, small); - programLarge = compileProgram(stack, large); - - kernelUnordered = getKernel(stack, programUnordered, KERNEL_NAME_UNORDERED); - kernelSmall = getKernel(stack, programSmall, KERNEL_NAME_LARGE); - kernelLarge = getKernel(stack, programLarge, KERNEL_NAME_LARGE); - } - - void uploadTileHeights(Scene scene) - { - if (tileHeightImage != 0L) - { - CL12.clReleaseMemObject(tileHeightImage); - tileHeightImage = 0L; - } - - final int TILEHEIGHT_BUFFER_SIZE = Constants.MAX_Z * Constants.EXTENDED_SCENE_SIZE * Constants.EXTENDED_SCENE_SIZE * Short.BYTES; - ShortBuffer tileBuffer = MemoryUtil.memAllocShort(TILEHEIGHT_BUFFER_SIZE); - int[][][] tileHeights = scene.getTileHeights(); - for (int z = 0; z < Constants.MAX_Z; ++z) - { - for (int y = 0; y < Constants.EXTENDED_SCENE_SIZE; ++y) - { - for (int x = 0; x < Constants.EXTENDED_SCENE_SIZE; ++x) - { - int h = tileHeights[z][x][y]; - assert (h & 0b111) == 0; - h >>= 3; - tileBuffer.put((short) h); - } - } - } - tileBuffer.flip(); - - try (MemoryStack stack = MemoryStack.stackPush()) - { - CLImageFormat imageFormat = CLImageFormat.calloc(stack); - imageFormat.image_channel_order(CL12.CL_R); - imageFormat.image_channel_data_type(CL12.CL_SIGNED_INT16); - - IntBuffer errcode_ret = stack.callocInt(1); - tileHeightImage = CL12.clCreateImage3D(context, CL12.CL_MEM_READ_ONLY | CL12.CL_MEM_COPY_HOST_PTR, imageFormat, - Constants.EXTENDED_SCENE_SIZE, Constants.EXTENDED_SCENE_SIZE, Constants.MAX_Z, - 0L, 0L, - tileBuffer, - errcode_ret); - checkCLError(errcode_ret); - } - - MemoryUtil.memFree(tileBuffer); - } - - void compute(int unorderedModels, int smallModels, int largeModels, - GLBuffer sceneVertexBuffer, - GLBuffer sceneUvBuffer, - GLBuffer vertexBuffer, - GLBuffer uvBuffer, - GLBuffer unorderedBuffer, - GLBuffer smallBuffer, - GLBuffer largeBuffer, - GLBuffer outVertexBuffer, - GLBuffer outUvBuffer, - GLBuffer uniformBuffer - ) - { - try (MemoryStack stack = MemoryStack.stackPush()) - { - PointerBuffer glBuffers = stack.mallocPointer(10); - glBuffers.put(sceneVertexBuffer.clBuffer); - glBuffers.put(sceneUvBuffer.clBuffer); - glBuffers.put(unorderedBuffer.clBuffer); - glBuffers.put(smallBuffer.clBuffer); - glBuffers.put(largeBuffer.clBuffer); - glBuffers.put(vertexBuffer.clBuffer); - glBuffers.put(uvBuffer.clBuffer); - glBuffers.put(outVertexBuffer.clBuffer); - glBuffers.put(outUvBuffer.clBuffer); - glBuffers.put(uniformBuffer.clBuffer); - glBuffers.flip(); - - PointerBuffer acquireEvent = stack.mallocPointer(1); - CL10GL.clEnqueueAcquireGLObjects(commandQueue, glBuffers, null, acquireEvent); - - var computeEvents = stack.mallocPointer(3); - if (unorderedModels > 0) - { - CL12.clSetKernelArg1p(kernelUnordered, 0, unorderedBuffer.clBuffer); - CL12.clSetKernelArg1p(kernelUnordered, 1, sceneVertexBuffer.clBuffer); - CL12.clSetKernelArg1p(kernelUnordered, 2, vertexBuffer.clBuffer); - CL12.clSetKernelArg1p(kernelUnordered, 3, sceneUvBuffer.clBuffer); - CL12.clSetKernelArg1p(kernelUnordered, 4, uvBuffer.clBuffer); - CL12.clSetKernelArg1p(kernelUnordered, 5, outVertexBuffer.clBuffer); - CL12.clSetKernelArg1p(kernelUnordered, 6, outUvBuffer.clBuffer); - - // queue compute call after acquireGLBuffers - CL12.clEnqueueNDRangeKernel(commandQueue, kernelUnordered, 1, null, - stack.pointers(unorderedModels * 6L), stack.pointers(6), - acquireEvent, computeEvents); - computeEvents.position(computeEvents.position() + 1); - } - - if (smallModels > 0) - { - CL12.clSetKernelArg(kernelSmall, 0, (SHARED_SIZE + SMALL_SIZE) * Integer.BYTES); - CL12.clSetKernelArg1p(kernelSmall, 1, smallBuffer.clBuffer); - CL12.clSetKernelArg1p(kernelSmall, 2, sceneVertexBuffer.clBuffer); - CL12.clSetKernelArg1p(kernelSmall, 3, vertexBuffer.clBuffer); - CL12.clSetKernelArg1p(kernelSmall, 4, sceneUvBuffer.clBuffer); - CL12.clSetKernelArg1p(kernelSmall, 5, uvBuffer.clBuffer); - CL12.clSetKernelArg1p(kernelSmall, 6, outVertexBuffer.clBuffer); - CL12.clSetKernelArg1p(kernelSmall, 7, outUvBuffer.clBuffer); - CL12.clSetKernelArg1p(kernelSmall, 8, uniformBuffer.clBuffer); - CL12.clSetKernelArg1l(kernelSmall, 9, tileHeightImage); - - CL12.clEnqueueNDRangeKernel(commandQueue, kernelSmall, 1, null, - stack.pointers(smallModels * (SMALL_SIZE / smallFaceCount)), stack.pointers(SMALL_SIZE / smallFaceCount), - acquireEvent, computeEvents); - computeEvents.position(computeEvents.position() + 1); - } - - if (largeModels > 0) - { - CL12.clSetKernelArg(kernelLarge, 0, (SHARED_SIZE + LARGE_SIZE) * Integer.BYTES); - CL12.clSetKernelArg1p(kernelLarge, 1, largeBuffer.clBuffer); - CL12.clSetKernelArg1p(kernelLarge, 2, sceneVertexBuffer.clBuffer); - CL12.clSetKernelArg1p(kernelLarge, 3, vertexBuffer.clBuffer); - CL12.clSetKernelArg1p(kernelLarge, 4, sceneUvBuffer.clBuffer); - CL12.clSetKernelArg1p(kernelLarge, 5, uvBuffer.clBuffer); - CL12.clSetKernelArg1p(kernelLarge, 6, outVertexBuffer.clBuffer); - CL12.clSetKernelArg1p(kernelLarge, 7, outUvBuffer.clBuffer); - CL12.clSetKernelArg1p(kernelLarge, 8, uniformBuffer.clBuffer); - CL12.clSetKernelArg1l(kernelLarge, 9, tileHeightImage); - - CL12.clEnqueueNDRangeKernel(commandQueue, kernelLarge, 1, null, - stack.pointers(largeModels * (LARGE_SIZE / largeFaceCount)), stack.pointers(LARGE_SIZE / largeFaceCount), - acquireEvent, computeEvents); - computeEvents.position(computeEvents.position() + 1); - } - - if (computeEvents.position() == 0) - { - CL10GL.clEnqueueReleaseGLObjects(commandQueue, glBuffers, null, null); - } - else - { - computeEvents.flip(); - CL10GL.clEnqueueReleaseGLObjects(commandQueue, glBuffers, computeEvents, null); - } - } - } - - void finish() - { - CL12.clFinish(commandQueue); - } - - private static String getPlatformInfoStringUTF8(long cl_platform_id, int param_name) - { - try (MemoryStack stack = MemoryStack.stackPush()) - { - PointerBuffer pp = stack.mallocPointer(1); - checkCLError(clGetPlatformInfo(cl_platform_id, param_name, (ByteBuffer) null, pp)); - int bytes = (int) pp.get(0); - - ByteBuffer buffer = stack.malloc(bytes); - checkCLError(clGetPlatformInfo(cl_platform_id, param_name, buffer, null)); - - return memUTF8(buffer, bytes - 1); - } - } - - private static long getDeviceInfoLong(long cl_device_id, int param_name) - { - try (MemoryStack stack = MemoryStack.stackPush()) - { - LongBuffer pl = stack.mallocLong(1); - checkCLError(clGetDeviceInfo(cl_device_id, param_name, pl, null)); - return pl.get(0); - } - } - - private static int getDeviceInfoInt(long cl_device_id, int param_name) - { - try (MemoryStack stack = MemoryStack.stackPush()) - { - IntBuffer pl = stack.mallocInt(1); - checkCLError(clGetDeviceInfo(cl_device_id, param_name, pl, null)); - return pl.get(0); - } - } - - private static long getDeviceInfoPointer(long cl_device_id, int param_name) - { - try (MemoryStack stack = MemoryStack.stackPush()) - { - PointerBuffer pp = stack.mallocPointer(1); - checkCLError(clGetDeviceInfo(cl_device_id, param_name, pp, null)); - return pp.get(0); - } - } - - private static String getDeviceInfoStringUTF8(long cl_device_id, int param_name) - { - try (MemoryStack stack = MemoryStack.stackPush()) - { - PointerBuffer pp = stack.mallocPointer(1); - checkCLError(clGetDeviceInfo(cl_device_id, param_name, (ByteBuffer) null, pp)); - int bytes = (int) pp.get(0); - - ByteBuffer buffer = stack.malloc(bytes); - checkCLError(clGetDeviceInfo(cl_device_id, param_name, buffer, null)); - - return memUTF8(buffer, bytes - 1); - } - } - - private static int getProgramBuildInfoInt(long cl_program_id, long cl_device_id, int param_name) - { - try (MemoryStack stack = MemoryStack.stackPush()) - { - IntBuffer pl = stack.mallocInt(1); - checkCLError(clGetProgramBuildInfo(cl_program_id, cl_device_id, param_name, pl, null)); - return pl.get(0); - } - } - - private static String getProgramBuildInfoStringASCII(long cl_program_id, long cl_device_id, int param_name) - { - try (MemoryStack stack = MemoryStack.stackPush()) - { - PointerBuffer pp = stack.mallocPointer(1); - checkCLError(clGetProgramBuildInfo(cl_program_id, cl_device_id, param_name, (ByteBuffer) null, pp)); - int bytes = (int) pp.get(0); - - ByteBuffer buffer = stack.malloc(bytes); - checkCLError(clGetProgramBuildInfo(cl_program_id, cl_device_id, param_name, buffer, null)); - - return memASCII(buffer, bytes - 1); - } - } - - private static void checkCLError(IntBuffer errcode) - { - checkCLError(errcode.get(errcode.position())); - } - - private static void checkCLError(int errcode) - { - if (errcode != CL_SUCCESS) - { - throw new RuntimeException(String.format("OpenCL error [%d]", errcode)); - } - } -} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/RegionManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/RegionManager.java new file mode 100644 index 00000000000..9d72380782a --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/RegionManager.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2025, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.gpu; + +import java.io.IOException; +import javax.inject.Inject; +import javax.inject.Singleton; +import net.runelite.api.Constants; +import net.runelite.api.Scene; +import net.runelite.api.Tile; +import net.runelite.client.plugins.gpu.regions.Regions; + +@Singleton +class RegionManager +{ + private final GpuPluginConfig gpuConfig; + private final Regions regions; + + @Inject + RegionManager( + GpuPluginConfig config + ) + { + this.gpuConfig = config; + + try (var in = SceneUploader.class.getResourceAsStream("regions/regions.txt")) + { + regions = new Regions(in, "regions.txt"); + } + catch (IOException ex) + { + throw new RuntimeException(ex); + } + } + + // remove tiles from the scene that are outside the current region + void prepare(Scene scene) + { + if (scene.isInstance() || !gpuConfig.hideUnrelatedMaps()) + { + return; + } + + int baseX = scene.getBaseX() / 8; + int baseY = scene.getBaseY() / 8; + int centerX = baseX + 6; + int centerY = baseY + 6; + int centerId = regions.getRegionId(centerX, centerY); + + int r = Constants.EXTENDED_SCENE_SIZE / 16; + for (int offx = -r; offx <= r; ++offx) + { + for (int offy = -r; offy <= r; ++offy) + { + int cx = centerX + offx; + int cy = centerY + offy; + int id = regions.getRegionId(cx, cy); + if (id != centerId) + { + removeZone(scene, cx, cy); + } + } + } + } + + private static void removeZone(Scene scene, int cx, int cy) + { + int wx = cx * 8; + int wy = cy * 8; + int sx = wx - scene.getBaseX(); + int sy = wy - scene.getBaseY(); + int cmsx = sx + GpuPlugin.SCENE_OFFSET; + int cmsy = sy + GpuPlugin.SCENE_OFFSET; + Tile[][][] tiles = scene.getExtendedTiles(); + for (int x = 0; x < 8; ++x) + { + for (int y = 0; y < 8; ++y) + { + int msx = cmsx + x; + int msy = cmsy + y; + if (msx >= 0 && msx < Constants.EXTENDED_SCENE_SIZE && msy >= 0 && msy < Constants.EXTENDED_SCENE_SIZE) + { + for (int z = 0; z < Constants.MAX_Z; ++z) + { + Tile tile = tiles[z][msx][msy]; + if (tile != null) + { + scene.removeTile(tile); + } + } + } + } + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/SceneUploader.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/SceneUploader.java index eee5eea53aa..2054466a695 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/SceneUploader.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/SceneUploader.java @@ -24,196 +24,301 @@ */ package net.runelite.client.plugins.gpu; -import com.google.common.base.Stopwatch; -import java.io.IOException; -import java.util.Arrays; +import java.nio.IntBuffer; +import java.util.HashSet; +import java.util.Set; import javax.inject.Inject; -import javax.inject.Singleton; import lombok.extern.slf4j.Slf4j; -import net.runelite.api.Client; import net.runelite.api.Constants; import net.runelite.api.DecorativeObject; +import net.runelite.api.DynamicObject; import net.runelite.api.GameObject; import net.runelite.api.GroundObject; import net.runelite.api.Model; import net.runelite.api.Perspective; import net.runelite.api.Point; -import net.runelite.api.Projection; import net.runelite.api.Renderable; import net.runelite.api.Scene; import net.runelite.api.SceneTileModel; import net.runelite.api.SceneTilePaint; import net.runelite.api.Tile; import net.runelite.api.WallObject; -import net.runelite.client.plugins.gpu.regions.Regions; +import net.runelite.client.callback.RenderCallbackManager; -@Singleton @Slf4j class SceneUploader { - private final Client client; - private final GpuPluginConfig gpuConfig; - - private final Regions regions; - - int sceneId = (int) System.nanoTime(); - private int offset; - private int uvoffset; - private int uniqueModels; + private final RenderCallbackManager renderCallbackManager; + private int basex, basez, rid, level; @Inject - SceneUploader( - Client client, - GpuPluginConfig config - ) + SceneUploader(RenderCallbackManager renderCallbackManager) { - this.client = client; - this.gpuConfig = config; - - try (var in = SceneUploader.class.getResourceAsStream("regions/regions.txt")) - { - regions = new Regions(in, "regions.txt"); - } - catch (IOException ex) - { - throw new RuntimeException(ex); - } + this.renderCallbackManager = renderCallbackManager; } - void upload(Scene scene, GpuIntBuffer vertexBuffer, GpuFloatBuffer uvBuffer) + void zoneSize(Scene scene, Zone zone, int mzx, int mzz) { - ++sceneId; - offset = 0; - uvoffset = 0; - uniqueModels = 0; - vertexBuffer.clear(); - uvBuffer.clear(); - - Stopwatch stopwatch = Stopwatch.createStarted(); - prepare(scene); - stopwatch.stop(); - log.debug("Scene preparation time: {}", stopwatch); - - stopwatch = Stopwatch.createStarted(); - for (int z = 0; z < Constants.MAX_Z; ++z) + Tile[][][] tiles = scene.getExtendedTiles(); + + for (int z = 3; z >= 0; --z) { - for (int x = 0; x < Constants.EXTENDED_SCENE_SIZE; ++x) + for (int xoff = 0; xoff < 8; ++xoff) { - for (int y = 0; y < Constants.EXTENDED_SCENE_SIZE; ++y) + for (int zoff = 0; zoff < 8; ++zoff) { - Tile tile = scene.getExtendedTiles()[z][x][y]; - if (tile != null) + Tile t = tiles[z][(mzx << 3) + xoff][(mzz << 3) + zoff]; + if (t != null) { - upload(scene, tile, vertexBuffer, uvBuffer); + zoneSize(zone, t); } } } } - - stopwatch.stop(); - log.debug("Scene upload time: {} unique models: {} length: {}KB", stopwatch, uniqueModels, (offset * 16) / 1024); } - private void upload(Scene scene, Tile tile, GpuIntBuffer vertexBuffer, GpuFloatBuffer uvBuffer) + void uploadZone(Scene scene, Zone zone, int mzx, int mzz) { - Tile bridge = tile.getBridge(); - if (bridge != null) + int[][][] roofs = scene.getRoofs(); + Set roofIds = new HashSet<>(); + + var vb = zone.vboO != null ? new GpuIntBuffer(zone.vboO.vb) : null; + var ab = zone.vboA != null ? new GpuIntBuffer(zone.vboA.vb) : null; + + for (int level = 0; level <= 3; ++level) { - upload(scene, bridge, vertexBuffer, uvBuffer); + for (int xoff = 0; xoff < 8; ++xoff) + { + for (int zoff = 0; zoff < 8; ++zoff) + { + int rid = roofs[level][(mzx << 3) + xoff][(mzz << 3) + zoff]; + if (rid > 0) + { + roofIds.add(rid); + } + } + } } - SceneTilePaint sceneTilePaint = tile.getSceneTilePaint(); - if (sceneTilePaint != null) + zone.rids = new int[4][roofIds.size()]; + zone.roofStart = new int[4][roofIds.size()]; + zone.roofEnd = new int[4][roofIds.size()]; + + for (int level = 0; level <= 3; ++level) { - sceneTilePaint.setBufferOffset(offset); - if (sceneTilePaint.getTexture() != -1) + this.level = level; + + if (level == 0) { - sceneTilePaint.setUvBufferOffset(uvoffset); + uploadZoneLevel(scene, zone, mzx, mzz, level, false, roofIds, vb, ab); + uploadZoneLevel(scene, zone, mzx, mzz, level, true, roofIds, vb, ab); + uploadZoneLevel(scene, zone, mzx, mzz, 1, true, roofIds, vb, ab); + uploadZoneLevel(scene, zone, mzx, mzz, 2, true, roofIds, vb, ab); + uploadZoneLevel(scene, zone, mzx, mzz, 3, true, roofIds, vb, ab); } else { - sceneTilePaint.setUvBufferOffset(-1); + uploadZoneLevel(scene, zone, mzx, mzz, level, false, roofIds, vb, ab); } - Point tilePoint = tile.getSceneLocation(); - int len = upload(scene, sceneTilePaint, - tile.getRenderLevel(), tilePoint.getX(), tilePoint.getY(), - vertexBuffer, uvBuffer, - 0, 0, false); - sceneTilePaint.setBufferLen(len); - offset += len; - if (sceneTilePaint.getTexture() != -1) + + if (zone.vboO != null) { - uvoffset += len; + int pos = zone.vboO.vb.position(); + zone.levelOffsets[level] = pos; } } + } + + private void uploadZoneLevel(Scene scene, Zone zone, int mzx, int mzz, int level, boolean visbelow, Set roofIds, GpuIntBuffer vb, GpuIntBuffer ab) + { + int ridx = 0; - SceneTileModel sceneTileModel = tile.getSceneTileModel(); - if (sceneTileModel != null) + // upload the roofs and save their positions + for (int id : roofIds) { - sceneTileModel.setBufferOffset(offset); - if (sceneTileModel.getTriangleTextureId() != null) - { - sceneTileModel.setUvBufferOffset(uvoffset); - } - else + int pos = zone.vboO != null ? zone.vboO.vb.position() : 0; + + uploadZoneLevelRoof(scene, zone, mzx, mzz, level, id, visbelow, vb, ab); + + int endpos = zone.vboO != null ? zone.vboO.vb.position() : 0; + + if (endpos > pos) { - sceneTileModel.setUvBufferOffset(-1); + zone.rids[level][ridx] = id; + zone.roofStart[level][ridx] = pos; + zone.roofEnd[level][ridx] = endpos; + ++ridx; } - Point tilePoint = tile.getSceneLocation(); - int len = upload(sceneTileModel, - tilePoint.getX() << Perspective.LOCAL_COORD_BITS, tilePoint.getY() << Perspective.LOCAL_COORD_BITS, - vertexBuffer, uvBuffer, false); - sceneTileModel.setBufferLen(len); - offset += len; - if (sceneTileModel.getTriangleTextureId() != null) + } + + // upload everything else + uploadZoneLevelRoof(scene, zone, mzx, mzz, level, 0, visbelow, vb, ab); + } + + private void uploadZoneLevelRoof(Scene scene, Zone zone, int mzx, int mzz, int level, int roofId, boolean visbelow, GpuIntBuffer vb, GpuIntBuffer ab) + { + byte[][][] settings = scene.getExtendedTileSettings(); + int[][][] roofs = scene.getRoofs(); + Tile[][][] tiles = scene.getExtendedTiles(); + + int offset = scene.getWorldViewId() == -1 ? GpuPlugin.SCENE_OFFSET >> 3 : 0; + this.basex = (mzx - offset) << 10; + this.basez = (mzz - offset) << 10; + + for (int xoff = 0; xoff < 8; ++xoff) + { + for (int zoff = 0; zoff < 8; ++zoff) { - uvoffset += len; + int msx = (mzx << 3) + xoff; + int msz = (mzz << 3) + zoff; + + boolean isbridge = (settings[1][msx][msz] & Constants.TILE_FLAG_BRIDGE) != 0; + int maplevel = level; + if (isbridge) + { + ++maplevel; + } + + boolean isvisbelow = maplevel <= 3 && (settings[maplevel][msx][msz] & Constants.TILE_FLAG_VIS_BELOW) != 0; + if (isvisbelow != visbelow) + { + continue; + } + + int rid; + if (isvisbelow || maplevel == 0) + { + rid = 0; + } + else + { + rid = roofs[maplevel - 1][msx][msz]; + } + + if (rid == roofId) + { + Tile t = tiles[level][msx][msz]; + if (t != null) + { + this.rid = rid; + uploadZoneTile(scene, zone, t, vb, ab); + } + } } } + } - WallObject wallObject = tile.getWallObject(); + private void zoneSize(Zone z, Tile t) + { + SceneTilePaint paint = t.getSceneTilePaint(); + if (paint != null) + { + z.sizeO += 2; + } + + SceneTileModel model = t.getSceneTileModel(); + if (model != null) + { + z.sizeO += model.getFaceX().length; + } + + WallObject wallObject = t.getWallObject(); if (wallObject != null) { - Renderable renderable1 = wallObject.getRenderable1(); - if (renderable1 instanceof Model) + zoneRenderableSize(z, wallObject.getRenderable1()); + zoneRenderableSize(z, wallObject.getRenderable2()); + } + + DecorativeObject decorativeObject = t.getDecorativeObject(); + if (decorativeObject != null) + { + zoneRenderableSize(z, decorativeObject.getRenderable()); + zoneRenderableSize(z, decorativeObject.getRenderable2()); + } + + GroundObject groundObject = t.getGroundObject(); + if (groundObject != null) + { + zoneRenderableSize(z, groundObject.getRenderable()); + } + + GameObject[] gameObjects = t.getGameObjects(); + for (GameObject gameObject : gameObjects) + { + if (gameObject == null) { - uploadSceneModel((Model) renderable1, vertexBuffer, uvBuffer); + continue; } - Renderable renderable2 = wallObject.getRenderable2(); - if (renderable2 instanceof Model) + if (!gameObject.getSceneMinLocation().equals(t.getSceneLocation())) { - uploadSceneModel((Model) renderable2, vertexBuffer, uvBuffer); + continue; } + + Renderable renderable = gameObject.getRenderable(); + zoneRenderableSize(z, renderable); } - GroundObject groundObject = tile.getGroundObject(); - if (groundObject != null) + Tile bridge = t.getBridge(); + if (bridge != null) { - Renderable renderable = groundObject.getRenderable(); - if (renderable instanceof Model) - { - uploadSceneModel((Model) renderable, vertexBuffer, uvBuffer); - } + zoneSize(z, bridge); } + } - DecorativeObject decorativeObject = tile.getDecorativeObject(); - if (decorativeObject != null) + private int uploadZoneTile(Scene scene, Zone zone, Tile t, GpuIntBuffer vertexBuffer, GpuIntBuffer ab) + { + int len = 0; + boolean drawTile = renderCallbackManager.drawTile(scene, t); + + SceneTilePaint paint = t.getSceneTilePaint(); + if (paint != null && drawTile) + { + Point tilePoint = t.getSceneLocation(); + len = upload(scene, paint, + t.getRenderLevel(), tilePoint.getX(), tilePoint.getY(), + vertexBuffer, + tilePoint.getX() * 128 - basex, tilePoint.getY() * 128 - basez + ); + } + + SceneTileModel model = t.getSceneTileModel(); + if (model != null && drawTile) + { + len += upload(model, basex, basez, vertexBuffer); + } + + WallObject wallObject = t.getWallObject(); + if (wallObject != null && renderCallbackManager.drawObject(scene, wallObject)) + { + Renderable renderable1 = wallObject.getRenderable1(); + uploadZoneRenderable(renderable1, zone, 0, wallObject.getX(), wallObject.getZ(), wallObject.getY(), -1, -1, -1, -1, wallObject.getId(), vertexBuffer, ab); + + Renderable renderable2 = wallObject.getRenderable2(); + uploadZoneRenderable(renderable2, zone, 0, wallObject.getX(), wallObject.getZ(), wallObject.getY(), -1, -1, -1, -1, wallObject.getId(), vertexBuffer, ab); + } + + DecorativeObject decorativeObject = t.getDecorativeObject(); + if (decorativeObject != null && renderCallbackManager.drawObject(scene, decorativeObject)) { Renderable renderable = decorativeObject.getRenderable(); - if (renderable instanceof Model) - { - uploadSceneModel((Model) renderable, vertexBuffer, uvBuffer); - } + uploadZoneRenderable(renderable, zone, 0, decorativeObject.getX() + decorativeObject.getXOffset(), decorativeObject.getZ(), decorativeObject.getY() + decorativeObject.getYOffset(), -1, -1, -1, -1, decorativeObject.getId(), vertexBuffer, ab); Renderable renderable2 = decorativeObject.getRenderable2(); - if (renderable2 instanceof Model) - { - uploadSceneModel((Model) renderable2, vertexBuffer, uvBuffer); - } + uploadZoneRenderable(renderable2, zone, 0, decorativeObject.getX(), decorativeObject.getZ(), decorativeObject.getY(), -1, -1, -1, -1, decorativeObject.getId(), vertexBuffer, ab); } - GameObject[] gameObjects = tile.getGameObjects(); + GroundObject groundObject = t.getGroundObject(); + if (groundObject != null && renderCallbackManager.drawObject(scene, groundObject)) + { + Renderable renderable = groundObject.getRenderable(); + uploadZoneRenderable(renderable, zone, 0, groundObject.getX(), groundObject.getZ(), groundObject.getY(), + -1, -1, -1, -1, + groundObject.getId(), + vertexBuffer, ab); + } + + GameObject[] gameObjects = t.getGameObjects(); for (GameObject gameObject : gameObjects) { if (gameObject == null) @@ -221,19 +326,114 @@ private void upload(Scene scene, Tile tile, GpuIntBuffer vertexBuffer, GpuFloatB continue; } + Point min = gameObject.getSceneMinLocation(), max = gameObject.getSceneMaxLocation(); + + if (!min.equals(t.getSceneLocation())) + { + continue; + } + + if (!renderCallbackManager.drawObject(scene, gameObject)) + { + continue; + } + Renderable renderable = gameObject.getRenderable(); - if (renderable instanceof Model) + uploadZoneRenderable(renderable, zone, gameObject.getModelOrientation(), gameObject.getX(), gameObject.getZ(), gameObject.getY(), + min.getX(), min.getY(), max.getX(), max.getY(), + gameObject.getId(), + vertexBuffer, ab); + } + + Tile bridge = t.getBridge(); + if (bridge != null) + { + len += uploadZoneTile(scene, zone, bridge, vertexBuffer, ab); + } + + return len; + } + + private void zoneRenderableSize(Zone z, Renderable r) + { + Model m = null; + if (r instanceof Model) + { + m = (Model) r; + } + else if (r instanceof DynamicObject) + { + m = ((DynamicObject) r).getModelZbuf(); + } + if (m == null) + { + return; + } + + byte[] transparencies = m.getFaceTransparencies(); + int faceCount = m.getFaceCount(); + if (transparencies != null) + { + for (int face = 0; face < faceCount; ++face) { - uploadSceneModel((Model) gameObject.getRenderable(), vertexBuffer, uvBuffer); + boolean alpha = transparencies[face] != 0; + if (alpha) + { + z.sizeA++; + } + else + { + z.sizeO++; + } } + return; } + z.sizeO += faceCount; } - int upload(Scene scene, SceneTilePaint tile, int tileZ, int tileX, int tileY, GpuIntBuffer vertexBuffer, GpuFloatBuffer uvBuffer, - int lx, int lz, boolean stream) + private void uploadZoneRenderable(Renderable r, Zone zone, int orient, int x, int y, int z, int lx, int lz, int ux, int uz, int id, GpuIntBuffer vb, GpuIntBuffer ab) { - tileX += GpuPlugin.SCENE_OFFSET; - tileY += GpuPlugin.SCENE_OFFSET; + int pos = zone.vboA != null ? zone.vboA.vb.position() : 0; + Model model = null; + if (r instanceof Model) + { + model = (Model) r; + uploadStaticModel(model, orient, x - basex, y, z - basez, vb, ab); + } + else if (r instanceof DynamicObject) + { + model = ((DynamicObject) r).getModelZbuf(); + if (model != null) + { + uploadStaticModel(model, orient, x - basex, y, z - basez, vb, ab); + } + } + int endpos = zone.vboA != null ? zone.vboA.vb.position() : 0; + if (endpos > pos) + { + assert model != null; + if (lx > -1) + { + lx -= basex >> 7; + lz -= basez >> 7; + ux -= basex >> 7; + uz -= basez >> 7; + assert lx >= 0 : lx; + assert lz >= 0 : lz; + assert ux < 25 : ux; // largest object? + assert uz < 25 : uz; + } + zone.addAlphaModel(zone.glVaoA, model, pos, endpos, + x - basex, y, z - basez, + lx, lz, ux, uz, + rid, level, id); + } + } + + private int upload(Scene scene, SceneTilePaint tile, int tileZ, int tileX, int tileY, GpuIntBuffer vertexBuffer, int lx, int lz) + { + tileX += scene.getWorldViewId() == -1 ? GpuPlugin.SCENE_OFFSET : 0; + tileY += scene.getWorldViewId() == -1 ? GpuPlugin.SCENE_OFFSET : 0; final int[][][] tileHeights = scene.getTileHeights(); final int swHeight = tileHeights[tileZ][tileX][tileY]; @@ -251,9 +451,6 @@ int upload(Scene scene, SceneTilePaint tile, int tileZ, int tileX, int tileY, Gp return 0; } - vertexBuffer.ensureCapacity(24); - uvBuffer.ensureCapacity(24); - // 0,0 final int lx0 = lx; final int ly0 = swHeight; @@ -278,40 +475,52 @@ int upload(Scene scene, SceneTilePaint tile, int tileZ, int tileX, int tileY, Gp final int lz3 = lz + Perspective.LOCAL_TILE_SIZE; final int hsl3 = nwColor; - vertexBuffer.put((float) lx2, ly2, lz2, hsl2); - vertexBuffer.put((float) lx3, ly3, lz3, hsl3); - vertexBuffer.put((float) lx1, ly1, lz1, hsl1); + int tex = tile.getTexture() + 1; - vertexBuffer.put((float) lx0, ly0, lz0, hsl0); - vertexBuffer.put((float) lx1, ly1, lz1, hsl1); - vertexBuffer.put((float) lx3, ly3, lz3, hsl3); + vertexBuffer.put22224(lx2, ly2, lz2, hsl2); + if (tile.isFlat()) + { + vertexBuffer.put2222(tex, lx0 - lx2, ly0 - ly2, lz0 - lz2); + } + else + { + vertexBuffer.put2222(tex, lx2 - lx2, ly2 - ly2, lz2 - lz2); + } - if (stream || tile.getTexture() != -1) + vertexBuffer.put22224(lx3, ly3, lz3, hsl3); + if (tile.isFlat()) { - int tex = tile.getTexture() + 1; - if (tile.isFlat()) - { - uvBuffer.put(tex, lx0, ly0, lz0); - uvBuffer.put(tex, lx1, ly1, lz1); - uvBuffer.put(tex, lx3, ly3, lz3); - } - else - { - uvBuffer.put(tex, lx2, ly2, lz2); - uvBuffer.put(tex, lx3, ly3, lz3); - uvBuffer.put(tex, lx1, ly1, lz1); - } + vertexBuffer.put2222(tex, lx1 - lx3, ly1 - ly3, lz1 - lz3); + } + else + { + vertexBuffer.put2222(tex, lx3 - lx3, ly3 - ly3, lz3 - lz3); + } + - uvBuffer.put(tex, lx0, ly0, lz0); - uvBuffer.put(tex, lx1, ly1, lz1); - uvBuffer.put(tex, lx3, ly3, lz3); + vertexBuffer.put22224(lx1, ly1, lz1, hsl1); + if (tile.isFlat()) + { + vertexBuffer.put2222(tex, lx3 - lx1, ly3 - ly1, lz3 - lz1); + } + else + { + vertexBuffer.put2222(tex, lx1 - lx1, ly1 - ly1, lz1 - lz1); } + vertexBuffer.put22224(lx0, ly0, lz0, hsl0); + vertexBuffer.put2222(tex, lx0 - lx0, ly0 - ly0, lz0 - lz0); + + vertexBuffer.put22224(lx1, ly1, lz1, hsl1); + vertexBuffer.put2222(tex, lx1 - lx1, ly1 - ly1, lz1 - lz1); + + vertexBuffer.put22224(lx3, ly3, lz3, hsl3); + vertexBuffer.put2222(tex, lx3 - lx3, ly3 - ly3, lz3 - lz3); + return 6; } - int upload(SceneTileModel sceneTileModel, int lx, int lz, - GpuIntBuffer vertexBuffer, GpuFloatBuffer uvBuffer, boolean stream) + private int upload(SceneTileModel sceneTileModel, int lx, int lz, GpuIntBuffer vertexBuffer) { final int[] faceX = sceneTileModel.getFaceX(); final int[] faceY = sceneTileModel.getFaceY(); @@ -329,9 +538,6 @@ int upload(SceneTileModel sceneTileModel, int lx, int lz, final int faceCount = faceX.length; - vertexBuffer.ensureCapacity(faceCount * 12); - uvBuffer.ensureCapacity(faceCount * 12); - int cnt = 0; for (int i = 0; i < faceCount; ++i) { @@ -363,80 +569,46 @@ int upload(SceneTileModel sceneTileModel, int lx, int lz, int ly2 = vertexY[vertex2]; int lz2 = vertexZ[vertex2] - lz; - vertexBuffer.put((float) lx0, ly0, lz0, hsl0); - vertexBuffer.put((float) lx1, ly1, lz1, hsl1); - vertexBuffer.put((float) lx2, ly2, lz2, hsl2); - - if (stream || triangleTextures != null) + int tex = triangleTextures != null ? triangleTextures[i] + 1 : 0; + vertexBuffer.put22224(lx0, ly0, lz0, hsl0); + if (sceneTileModel.isFlat()) { - if (triangleTextures != null && triangleTextures[i] != -1) - { - int tex = triangleTextures[i] + 1; - if (sceneTileModel.isFlat()) - { - uvBuffer.put(tex, vertexX[0] - lx, vertexY[0], vertexZ[0] - lz); - uvBuffer.put(tex, vertexX[1] - lx, vertexY[1], vertexZ[1] - lz); - uvBuffer.put(tex, vertexX[3] - lx, vertexY[3], vertexZ[3] - lz); - } - else - { - uvBuffer.put(tex, vertexX[vertex0] - lx, vertexY[vertex0], vertexZ[vertex0] - lz); - uvBuffer.put(tex, vertexX[vertex1] - lx, vertexY[vertex1], vertexZ[vertex1] - lz); - uvBuffer.put(tex, vertexX[vertex2] - lx, vertexY[vertex2], vertexZ[vertex2] - lz); - } - } - else - { - uvBuffer.put(0, 0, 0, 0); - uvBuffer.put(0, 0, 0, 0); - uvBuffer.put(0, 0, 0, 0); - } + vertexBuffer.put2222(tex, vertexX[0] - lx - lx0, vertexY[0] - ly0, vertexZ[0] - lz - lz0); + } + else + { + vertexBuffer.put2222(tex, vertexX[vertex0] - lx - lx0, vertexY[vertex0] - ly0, vertexZ[vertex0] - lz - lz0); } - } - - return cnt; - } - - private void uploadSceneModel(Model model, GpuIntBuffer vertexBuffer, GpuFloatBuffer uvBuffer) - { - // deduplicate hillskewed models - if (model.getUnskewedModel() != null) - { - model = model.getUnskewedModel(); - } - if (model.getSceneId() == sceneId) - { - return; // model has already been uploaded - } + vertexBuffer.put22224(lx1, ly1, lz1, hsl1); + if (sceneTileModel.isFlat()) + { + vertexBuffer.put2222(tex, vertexX[1] - lx - lx1, vertexY[1] - ly1, vertexZ[1] - lz - lz1); + } + else + { + vertexBuffer.put2222(tex, vertexX[vertex1] - lx - lx1, vertexY[vertex1] - ly1, vertexZ[vertex1] - lz - lz1); + } - model.setBufferOffset(offset); - if (model.getFaceTextures() != null) - { - model.setUvBufferOffset(uvoffset); - } - else - { - model.setUvBufferOffset(-1); + vertexBuffer.put22224(lx2, ly2, lz2, hsl2); + if (sceneTileModel.isFlat()) + { + vertexBuffer.put2222(tex, vertexX[3] - lx - lx2, vertexY[3] - ly2, vertexZ[3] - lz - lz2); + } + else + { + vertexBuffer.put2222(tex, vertexX[vertex2] - lx - lx2, vertexY[vertex2] - ly2, vertexZ[vertex2] - lz - lz2); + } } - model.setSceneId(sceneId); - ++uniqueModels; - - int len = pushModel(model, vertexBuffer, uvBuffer); - offset += len; - if (model.getFaceTextures() != null) - { - uvoffset += len; - } + return cnt; } - public int pushModel(Model model, GpuIntBuffer vertexBuffer, GpuFloatBuffer uvBuffer) + // scene upload + private int uploadStaticModel(Model model, int orient, int x, int y, int z, GpuIntBuffer vb, GpuIntBuffer ab) { - final int triangleCount = Math.min(model.getFaceCount(), GpuPlugin.MAX_TRIANGLE); - - vertexBuffer.ensureCapacity(triangleCount * 12); - uvBuffer.ensureCapacity(triangleCount * 12); + final int vertexCount = model.getVerticesCount(); + final int triangleCount = model.getFaceCount(); final float[] vertexX = model.getVerticesX(); final float[] vertexY = model.getVerticesY(); @@ -457,12 +629,37 @@ public int pushModel(Model model, GpuIntBuffer vertexBuffer, GpuFloatBuffer uvBu final int[] texIndices3 = model.getTexIndices3(); final byte[] transparencies = model.getFaceTransparencies(); - final byte[] facePriorities = model.getFaceRenderPriorities(); + final byte[] bias = model.getFaceBias(); - final byte overrideAmount = model.getOverrideAmount(); - final byte overrideHue = model.getOverrideHue(); - final byte overrideSat = model.getOverrideSaturation(); - final byte overrideLum = model.getOverrideLuminance(); + int orientSin = 0; + int orientCos = 0; + if (orient != 0) + { + orientSin = Perspective.SINE[orient]; + orientCos = Perspective.COSINE[orient]; + } + + for (int v = 0; v < vertexCount; ++v) + { + int vx = (int) vertexX[v]; + int vy = (int) vertexY[v]; + int vz = (int) vertexZ[v]; + + if (orient != 0) + { + int x0 = vx; + vx = vz * orientSin + x0 * orientCos >> 16; + vz = vz * orientCos - x0 * orientSin >> 16; + } + + vx += x; + vy += y; + vz += z; + + modelLocalXI[v] = vx; + modelLocalYI[v] = vy; + modelLocalZI[v] = vz; + } int len = 0; for (int face = 0; face < triangleCount; ++face) @@ -471,167 +668,100 @@ public int pushModel(Model model, GpuIntBuffer vertexBuffer, GpuFloatBuffer uvBu int color2 = color2s[face]; int color3 = color3s[face]; + boolean alpha = (transparencies != null && transparencies[face] != 0); + if (color3 == -1) { color2 = color3 = color1; } else if (color3 == -2) { - vertexBuffer.put(0, 0, 0, 0); - vertexBuffer.put(0, 0, 0, 0); - vertexBuffer.put(0, 0, 0, 0); - - if (faceTextures != null) - { - uvBuffer.put(0, 0, 0, 0); - uvBuffer.put(0, 0, 0, 0); - uvBuffer.put(0, 0, 0, 0); - } - - len += 3; continue; } - // HSL override is not applied to textured faces - if (faceTextures == null || faceTextures[face] == -1) - { - if (overrideAmount > 0) - { - color1 = interpolateHSL(color1, overrideHue, overrideSat, overrideLum, overrideAmount); - color2 = interpolateHSL(color2, overrideHue, overrideSat, overrideLum, overrideAmount); - color3 = interpolateHSL(color3, overrideHue, overrideSat, overrideLum, overrideAmount); - } - } - - int packAlphaPriority = packAlphaPriority(faceTextures, transparencies, facePriorities, face); - int triangleA = indices1[face]; int triangleB = indices2[face]; int triangleC = indices3[face]; - vertexBuffer.put(vertexX[triangleA], vertexY[triangleA], vertexZ[triangleA], packAlphaPriority | color1); - vertexBuffer.put(vertexX[triangleB], vertexY[triangleB], vertexZ[triangleB], packAlphaPriority | color2); - vertexBuffer.put(vertexX[triangleC], vertexY[triangleC], vertexZ[triangleC], packAlphaPriority | color3); - - if (faceTextures != null) - { - if (faceTextures[face] != -1) - { - int texA, texB, texC; - - if (textureFaces != null && textureFaces[face] != -1) - { - int tface = textureFaces[face] & 0xff; - texA = texIndices1[tface]; - texB = texIndices2[tface]; - texC = texIndices3[tface]; - } - else - { - texA = triangleA; - texB = triangleB; - texC = triangleC; - } - - int texture = faceTextures[face] + 1; - uvBuffer.put(texture, vertexX[texA], vertexY[texA], vertexZ[texA]); - uvBuffer.put(texture, vertexX[texB], vertexY[texB], vertexZ[texB]); - uvBuffer.put(texture, vertexX[texC], vertexY[texC], vertexZ[texC]); - } - else - { - uvBuffer.put(0, 0, 0, 0); - uvBuffer.put(0, 0, 0, 0); - uvBuffer.put(0, 0, 0, 0); - } - } - - len += 3; - } - - return len; - } + int vx1 = modelLocalXI[triangleA]; + int vy1 = modelLocalYI[triangleA]; + int vz1 = modelLocalZI[triangleA]; - private static int[] distances; - private static char[] distanceFaceCount; - private static char[][] distanceToFaces; + int vx2 = modelLocalXI[triangleB]; + int vy2 = modelLocalYI[triangleB]; + int vz2 = modelLocalZI[triangleB]; - private static float[] modelCanvasX; - private static float[] modelCanvasY; + int vx3 = modelLocalXI[triangleC]; + int vy3 = modelLocalYI[triangleC]; + int vz3 = modelLocalZI[triangleC]; - private static float[] modelLocalX; - private static float[] modelLocalY; - private static float[] modelLocalZ; + int texA, texB, texC; - private static int[] numOfPriority; - private static int[] eq10; - private static int[] eq11; - private static int[] lt10; - private static int[][] orderedFaces; + if (textureFaces != null && textureFaces[face] != -1) + { + int tface = textureFaces[face] & 0xff; + texA = texIndices1[tface]; + texB = texIndices2[tface]; + texC = texIndices3[tface]; + } + else + { + texA = triangleA; + texB = triangleB; + texC = triangleC; + } - void initSortingBuffers() - { - int MAX_VERTEX_COUNT = 6500; - int MAX_DIAMETER = 6000; - int ZSORT_GROUP_SIZE = 1024; // was 512 - int MAX_FACES_PER_PRIORITY = 4000; // was 2000 + int alphaBias = 0; + alphaBias |= transparencies != null ? (transparencies[face] & 0xff) << 24 : 0; + alphaBias |= bias != null ? (bias[face] & 0xff) << 16 : 0; + int texture = faceTextures != null ? faceTextures[face] + 1 : 0; + GpuIntBuffer buf = alpha ? ab : vb; - distances = new int[MAX_VERTEX_COUNT]; - distanceFaceCount = new char[MAX_DIAMETER]; - distanceToFaces = new char[MAX_DIAMETER][ZSORT_GROUP_SIZE]; + buf.put22224(vx1, vy1, vz1, alphaBias | color1); + buf.put2222(texture, modelLocalXI[texA] - vx1, modelLocalYI[texA] - vy1, modelLocalZI[texA] - vz1); - modelCanvasX = new float[MAX_VERTEX_COUNT]; - modelCanvasY = new float[MAX_VERTEX_COUNT]; + buf.put22224(vx2, vy2, vz2, alphaBias | color2); + buf.put2222(texture, modelLocalXI[texB] - vx2, modelLocalYI[texB] - vy2, modelLocalZI[texB] - vz2); - modelLocalX = new float[MAX_VERTEX_COUNT]; - modelLocalY = new float[MAX_VERTEX_COUNT]; - modelLocalZ = new float[MAX_VERTEX_COUNT]; + buf.put22224(vx3, vy3, vz3, alphaBias | color3); + buf.put2222(texture, modelLocalXI[texC] - vx3, modelLocalYI[texC] - vy3, modelLocalZI[texC] - vz3); - numOfPriority = new int[12]; - eq10 = new int[MAX_FACES_PER_PRIORITY]; - eq11 = new int[MAX_FACES_PER_PRIORITY]; - lt10 = new int[12]; - orderedFaces = new int[12][MAX_FACES_PER_PRIORITY]; - } + len += 3; + } - void releaseSortingBuffers() - { - distances = null; - distanceFaceCount = null; - distanceToFaces = null; - - modelCanvasX = null; - modelCanvasY = null; - - modelLocalX = null; - modelLocalY = null; - modelLocalZ = null; - - numOfPriority = null; - eq10 = null; - eq11 = null; - lt10 = null; - orderedFaces = null; + return len; } - int pushSortedModel(Projection proj, Model model, int orientation, int x, int y, int z, GpuIntBuffer vertexBuffer, GpuFloatBuffer uvBuffer) + // temp draw + static int uploadTempModel(Model model, int orientation, int x, int y, int z, IntBuffer opaqueBuffer) { + final int triangleCount = model.getFaceCount(); final int vertexCount = model.getVerticesCount(); + final float[] verticesX = model.getVerticesX(); final float[] verticesY = model.getVerticesY(); final float[] verticesZ = model.getVerticesZ(); - final int faceCount = model.getFaceCount(); final int[] indices1 = model.getFaceIndices1(); final int[] indices2 = model.getFaceIndices2(); final int[] indices3 = model.getFaceIndices3(); - final int[] faceColors3 = model.getFaceColors3(); - final byte[] faceRenderPriorities = model.getFaceRenderPriorities(); + final int[] color1s = model.getFaceColors1(); + final int[] color2s = model.getFaceColors2(); + final int[] color3s = model.getFaceColors3(); - final int centerX = client.getCenterX(); - final int centerY = client.getCenterY(); - final int zoom = client.get3dZoom(); + final short[] faceTextures = model.getFaceTextures(); + final byte[] textureFaces = model.getTextureFaces(); + final int[] texIndices1 = model.getTexIndices1(); + final int[] texIndices2 = model.getTexIndices2(); + final int[] texIndices3 = model.getTexIndices3(); + + final byte[] bias = model.getFaceBias(); + + final byte overrideAmount = model.getOverrideAmount(); + final byte overrideHue = model.getOverrideHue(); + final byte overrideSat = model.getOverrideSaturation(); + final byte overrideLum = model.getOverrideLuminance(); float orientSine = 0; float orientCosine = 0; @@ -641,9 +771,6 @@ int pushSortedModel(Projection proj, Model model, int orientation, int x, int y, orientCosine = Perspective.COSINE[orientation] / 65536f; } - float[] p = proj.project(x, y, z); - int zero = (int) p[2]; - for (int v = 0; v < vertexCount; ++v) { float vertexX = verticesX[v]; @@ -657,7 +784,6 @@ int pushSortedModel(Projection proj, Model model, int orientation, int x, int y, vertexZ = vertexZ * orientCosine - x0 * orientSine; } - // move to local position vertexX += x; vertexY += y; vertexZ += z; @@ -665,354 +791,123 @@ int pushSortedModel(Projection proj, Model model, int orientation, int x, int y, modelLocalX[v] = vertexX; modelLocalY[v] = vertexY; modelLocalZ[v] = vertexZ; - - p = proj.project(vertexX, vertexY, vertexZ); - modelCanvasX[v] = centerX + p[0] * zoom / p[2]; - modelCanvasY[v] = centerY + p[1] * zoom / p[2]; - distances[v] = (int) p[2] - zero; } - final int diameter = model.getDiameter(); - final int radius = model.getRadius(); - if (diameter >= 6000) + int len = 0; + for (int face = 0; face < triangleCount; ++face) { - return 0; - } - - Arrays.fill(distanceFaceCount, 0, diameter, (char) 0); + int color1 = color1s[face]; + int color2 = color2s[face]; + int color3 = color3s[face]; - for (char i = 0; i < faceCount; ++i) - { - if (faceColors3[i] != -2) + if (color3 == -1) { - final int v1 = indices1[i]; - final int v2 = indices2[i]; - final int v3 = indices3[i]; - - final float - aX = modelCanvasX[v1], - aY = modelCanvasY[v1], - bX = modelCanvasX[v2], - bY = modelCanvasY[v2], - cX = modelCanvasX[v3], - cY = modelCanvasY[v3]; - - if ((aX - bX) * (cY - bY) - (cX - bX) * (aY - bY) > 0) - { - int distance = radius + (distances[v1] + distances[v2] + distances[v3]) / 3; - assert distance >= 0 && distance < diameter; - distanceToFaces[distance][distanceFaceCount[distance]++] = i; - } + color2 = color3 = color1; } - } - - vertexBuffer.ensureCapacity(12 * faceCount); - uvBuffer.ensureCapacity(12 * faceCount); - - int len = 0; - if (faceRenderPriorities == null) - { - for (int i = diameter - 1; i >= 0; --i) + else if (color3 == -2) { - final int cnt = distanceFaceCount[i]; - if (cnt > 0) - { - final char[] faces = distanceToFaces[i]; - - for (int faceIdx = 0; faceIdx < cnt; ++faceIdx) - { - final int face = faces[faceIdx]; - len += pushFace(model, face, vertexBuffer, uvBuffer); - } - } + continue; } - } - else - { - Arrays.fill(numOfPriority, 0); - Arrays.fill(lt10, 0); - for (int i = diameter - 1; i >= 0; --i) + // HSL override is not applied to textured faces + if (faceTextures == null || faceTextures[face] == -1) { - final int cnt = distanceFaceCount[i]; - if (cnt > 0) + if (overrideAmount > 0) { - final char[] faces = distanceToFaces[i]; - - for (int faceIdx = 0; faceIdx < cnt; ++faceIdx) - { - final int face = faces[faceIdx]; - final byte pri = faceRenderPriorities[face]; - final int distIdx = numOfPriority[pri]++; - - orderedFaces[pri][distIdx] = face; - if (pri < 10) - { - lt10[pri] += i; - } - else if (pri == 10) - { - eq10[distIdx] = i; - } - else - { - eq11[distIdx] = i; - } - } + color1 = interpolateHSL(color1, overrideHue, overrideSat, overrideLum, overrideAmount); + color2 = interpolateHSL(color2, overrideHue, overrideSat, overrideLum, overrideAmount); + color3 = interpolateHSL(color3, overrideHue, overrideSat, overrideLum, overrideAmount); } } - int avg12 = 0; - if (numOfPriority[1] > 0 || numOfPriority[2] > 0) - { - avg12 = (lt10[1] + lt10[2]) / (numOfPriority[1] + numOfPriority[2]); - } + int triangleA = indices1[face]; + int triangleB = indices2[face]; + int triangleC = indices3[face]; - int avg34 = 0; - if (numOfPriority[3] > 0 || numOfPriority[4] > 0) - { - avg34 = (lt10[3] + lt10[4]) / (numOfPriority[3] + numOfPriority[4]); - } + float vx1 = modelLocalX[triangleA]; + float vy1 = modelLocalY[triangleA]; + float vz1 = modelLocalZ[triangleA]; - int avg68 = 0; - if (numOfPriority[6] > 0 || numOfPriority[8] > 0) - { - avg68 = (lt10[8] + lt10[6]) / (numOfPriority[8] + numOfPriority[6]); - } + float vx2 = modelLocalX[triangleB]; + float vy2 = modelLocalY[triangleB]; + float vz2 = modelLocalZ[triangleB]; - int drawnFaces = 0; - int numDynFaces = numOfPriority[10]; - int[] dynFaces = orderedFaces[10]; - int[] dynFaceDistances = eq10; - if (drawnFaces == numDynFaces) - { - drawnFaces = 0; - numDynFaces = numOfPriority[11]; - dynFaces = orderedFaces[11]; - dynFaceDistances = eq11; - } + float vx3 = modelLocalX[triangleC]; + float vy3 = modelLocalY[triangleC]; + float vz3 = modelLocalZ[triangleC]; - int currFaceDistance; - if (drawnFaces < numDynFaces) + int texA, texB, texC; + + if (textureFaces != null && textureFaces[face] != -1) { - currFaceDistance = dynFaceDistances[drawnFaces]; + int tface = textureFaces[face] & 0xff; + texA = texIndices1[tface]; + texB = texIndices2[tface]; + texC = texIndices3[tface]; } else { - currFaceDistance = -1000; + texA = triangleA; + texB = triangleB; + texC = triangleC; } - for (int pri = 0; pri < 10; ++pri) - { - while (pri == 0 && currFaceDistance > avg12) - { - final int face = dynFaces[drawnFaces++]; - len += pushFace(model, face, vertexBuffer, uvBuffer); + int alphaBias = 0; + alphaBias |= bias != null ? (bias[face] & 0xff) << 16 : 0; + int texture = faceTextures != null ? faceTextures[face] + 1 : 0; - if (drawnFaces == numDynFaces && dynFaces != orderedFaces[11]) - { - drawnFaces = 0; - numDynFaces = numOfPriority[11]; - dynFaces = orderedFaces[11]; - dynFaceDistances = eq11; - } + putfff4(opaqueBuffer, vx1, vy1, vz1, alphaBias | color1); + put2222(opaqueBuffer, texture, (int) modelLocalX[texA] - (int) vx1, (int) modelLocalY[texA] - (int) vy1, (int) modelLocalZ[texA] - (int) vz1); - if (drawnFaces < numDynFaces) - { - currFaceDistance = dynFaceDistances[drawnFaces]; - } - else - { - currFaceDistance = -1000; - } - } - - while (pri == 3 && currFaceDistance > avg34) - { - final int face = dynFaces[drawnFaces++]; - len += pushFace(model, face, vertexBuffer, uvBuffer); - - if (drawnFaces == numDynFaces && dynFaces != orderedFaces[11]) - { - drawnFaces = 0; - numDynFaces = numOfPriority[11]; - dynFaces = orderedFaces[11]; - dynFaceDistances = eq11; - } - - if (drawnFaces < numDynFaces) - { - currFaceDistance = dynFaceDistances[drawnFaces]; - } - else - { - currFaceDistance = -1000; - } - } - - while (pri == 5 && currFaceDistance > avg68) - { - final int face = dynFaces[drawnFaces++]; - len += pushFace(model, face, vertexBuffer, uvBuffer); - - if (drawnFaces == numDynFaces && dynFaces != orderedFaces[11]) - { - drawnFaces = 0; - numDynFaces = numOfPriority[11]; - dynFaces = orderedFaces[11]; - dynFaceDistances = eq11; - } - - if (drawnFaces < numDynFaces) - { - currFaceDistance = dynFaceDistances[drawnFaces]; - } - else - { - currFaceDistance = -1000; - } - } - - final int priNum = numOfPriority[pri]; - final int[] priFaces = orderedFaces[pri]; - - for (int faceIdx = 0; faceIdx < priNum; ++faceIdx) - { - final int face = priFaces[faceIdx]; - len += pushFace(model, face, vertexBuffer, uvBuffer); - } - } + putfff4(opaqueBuffer, vx2, vy2, vz2, alphaBias | color2); + put2222(opaqueBuffer, texture, (int) modelLocalX[texB] - (int) vx2, (int) modelLocalY[texB] - (int) vy2, (int) modelLocalZ[texB] - (int) vz2); - while (currFaceDistance != -1000) - { - final int face = dynFaces[drawnFaces++]; - len += pushFace(model, face, vertexBuffer, uvBuffer); - - if (drawnFaces == numDynFaces && dynFaces != orderedFaces[11]) - { - drawnFaces = 0; - dynFaces = orderedFaces[11]; - numDynFaces = numOfPriority[11]; - dynFaceDistances = eq11; - } + putfff4(opaqueBuffer, vx3, vy3, vz3, alphaBias | color3); + put2222(opaqueBuffer, texture, (int) modelLocalX[texC] - (int) vx3, (int) modelLocalY[texC] - (int) vy3, (int) modelLocalZ[texC] - (int) vz3); - if (drawnFaces < numDynFaces) - { - currFaceDistance = dynFaceDistances[drawnFaces]; - } - else - { - currFaceDistance = -1000; - } - } + len += 3; } return len; } - private int pushFace(Model model, int face, GpuIntBuffer vertexBuffer, GpuFloatBuffer uvBuffer) + static void put2222(IntBuffer vb, int x, int y, int z, int w) { - final int[] indices1 = model.getFaceIndices1(); - final int[] indices2 = model.getFaceIndices2(); - final int[] indices3 = model.getFaceIndices3(); - - final int[] faceColors1 = model.getFaceColors1(); - final int[] faceColors2 = model.getFaceColors2(); - final int[] faceColors3 = model.getFaceColors3(); - - final byte overrideAmount = model.getOverrideAmount(); - final byte overrideHue = model.getOverrideHue(); - final byte overrideSat = model.getOverrideSaturation(); - final byte overrideLum = model.getOverrideLuminance(); - - final short[] faceTextures = model.getFaceTextures(); - final byte[] textureFaces = model.getTextureFaces(); - final int[] texIndices1 = model.getTexIndices1(); - final int[] texIndices2 = model.getTexIndices2(); - final int[] texIndices3 = model.getTexIndices3(); - - final byte[] faceRenderPriorities = model.getFaceRenderPriorities(); - final byte[] transparencies = model.getFaceTransparencies(); - - final int packAlphaPriority = packAlphaPriority(faceTextures, transparencies, faceRenderPriorities, face); - - final int triangleA = indices1[face]; - final int triangleB = indices2[face]; - final int triangleC = indices3[face]; - - int color1 = faceColors1[face]; - int color2 = faceColors2[face]; - int color3 = faceColors3[face]; + vb.put(((y & 0xffff) << 16) | (x & 0xffff)); + vb.put(((w & 0xffff) << 16) | (z & 0xffff)); + } - if (color3 == -1) - { - color2 = color3 = color1; - } + static void putfff4(IntBuffer vb, float x, float y, float z, int w) + { + vb.put(Float.floatToIntBits(x)); + vb.put(Float.floatToIntBits(y)); + vb.put(Float.floatToIntBits(z)); + vb.put(w); + } - // HSL override is not applied to textured faces - if (faceTextures == null || faceTextures[face] == -1) - { - if (overrideAmount > 0) - { - color1 = interpolateHSL(color1, overrideHue, overrideSat, overrideLum, overrideAmount); - color2 = interpolateHSL(color2, overrideHue, overrideSat, overrideLum, overrideAmount); - color3 = interpolateHSL(color3, overrideHue, overrideSat, overrideLum, overrideAmount); - } - } + static float[] modelLocalX; + static float[] modelLocalY; + static float[] modelLocalZ; - vertexBuffer.put(modelLocalX[triangleA], modelLocalY[triangleA], modelLocalZ[triangleA], packAlphaPriority | color1); - vertexBuffer.put(modelLocalX[triangleB], modelLocalY[triangleB], modelLocalZ[triangleB], packAlphaPriority | color2); - vertexBuffer.put(modelLocalX[triangleC], modelLocalY[triangleC], modelLocalZ[triangleC], packAlphaPriority | color3); + // uploadStaticModel runs on the maploader thread, so requires its own buffers + private final static int[] modelLocalXI; + private final static int[] modelLocalYI; + private final static int[] modelLocalZI; - if (faceTextures != null && faceTextures[face] != -1) - { - int texA, texB, texC; + static final int MAX_VERTEX_COUNT = 6500; - if (textureFaces != null && textureFaces[face] != -1) - { - int tfaceIdx = textureFaces[face] & 0xff; - texA = texIndices1[tfaceIdx]; - texB = texIndices2[tfaceIdx]; - texC = texIndices3[tfaceIdx]; - } - else - { - texA = triangleA; - texB = triangleB; - texC = triangleC; - } - - int texture = faceTextures[face] + 1; - uvBuffer.put(texture, modelLocalX[texA], modelLocalY[texA], modelLocalZ[texA]); - uvBuffer.put(texture, modelLocalX[texB], modelLocalY[texB], modelLocalZ[texB]); - uvBuffer.put(texture, modelLocalX[texC], modelLocalY[texC], modelLocalZ[texC]); - } - else - { - uvBuffer.put(0, 0, 0, 0); - uvBuffer.put(0, 0, 0, 0); - uvBuffer.put(0, 0, 0, 0); - } - - return 3; - } - - private static int packAlphaPriority(short[] faceTextures, byte[] faceTransparencies, byte[] facePriorities, int face) + static { - int alpha = 0; - if (faceTransparencies != null) - { - alpha = (faceTransparencies[face] & 0xFF) << 24; - } - int priority = 0; - if (facePriorities != null) - { - priority = (facePriorities[face] & 0xff) << 16; - } - return alpha | priority; + modelLocalX = new float[MAX_VERTEX_COUNT]; + modelLocalY = new float[MAX_VERTEX_COUNT]; + modelLocalZ = new float[MAX_VERTEX_COUNT]; + + modelLocalXI = new int[MAX_VERTEX_COUNT]; + modelLocalYI = new int[MAX_VERTEX_COUNT]; + modelLocalZI = new int[MAX_VERTEX_COUNT]; } - private static int interpolateHSL(int hsl, byte hue2, byte sat2, byte lum2, byte lerp) + static int interpolateHSL(int hsl, byte hue2, byte sat2, byte lum2, byte lerp) { int hue = hsl >> 10 & 63; int sat = hsl >> 7 & 7; @@ -1035,64 +930,4 @@ private static int interpolateHSL(int hsl, byte hue2, byte sat2, byte lum2, byte return (hue << 10 | sat << 7 | lum) & 65535; } - - // remove tiles from the scene that are outside the current region - private void prepare(Scene scene) - { - if (scene.isInstance() || !gpuConfig.hideUnrelatedMaps()) - { - return; - } - - int baseX = scene.getBaseX() / 8; - int baseY = scene.getBaseY() / 8; - int centerX = baseX + 6; - int centerY = baseY + 6; - int centerId = regions.getRegionId(centerX, centerY); - - int r = Constants.EXTENDED_SCENE_SIZE / 16; - for (int offx = -r; offx <= r; ++offx) - { - for (int offy = -r; offy <= r; ++offy) - { - int cx = centerX + offx; - int cy = centerY + offy; - int id = regions.getRegionId(cx, cy); - if (id != centerId) - { - removeChunk(scene, cx, cy); - } - } - } - } - - private static void removeChunk(Scene scene, int cx, int cy) - { - int wx = cx * 8; - int wy = cy * 8; - int sx = wx - scene.getBaseX(); - int sy = wy - scene.getBaseY(); - int cmsx = sx + GpuPlugin.SCENE_OFFSET; - int cmsy = sy + GpuPlugin.SCENE_OFFSET; - Tile[][][] tiles = scene.getExtendedTiles(); - for (int x = 0; x < 8; ++x) - { - for (int y = 0; y < 8; ++y) - { - int msx = cmsx + x; - int msy = cmsy + y; - if (msx >= 0 && msx < Constants.EXTENDED_SCENE_SIZE && msy >= 0 && msy < Constants.EXTENDED_SCENE_SIZE) - { - for (int z = 0; z < Constants.MAX_Z; ++z) - { - Tile tile = tiles[z][msx][msy]; - if (tile != null) - { - scene.removeTile(tile); - } - } - } - } - } - } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/Shader.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/Shader.java index 559c3ccc0a8..72617a68c90 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/Shader.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/Shader.java @@ -30,10 +30,12 @@ import java.util.List; import lombok.Getter; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import net.runelite.client.plugins.gpu.template.Template; -import org.lwjgl.opengl.GL43C; +import static org.lwjgl.opengl.GL43C.*; -public class Shader +@Slf4j +class Shader { @VisibleForTesting final List units = new ArrayList<>(); @@ -55,9 +57,9 @@ public Shader add(int type, String name) return this; } - public int compile(Template template) throws ShaderException + int compile(Template template) throws ShaderException { - int program = GL43C.glCreateProgram(); + int program = glCreateProgram(); int[] shaders = new int[units.size()]; int i = 0; boolean ok = false; @@ -66,39 +68,40 @@ public int compile(Template template) throws ShaderException while (i < shaders.length) { Unit unit = units.get(i); - int shader = GL43C.glCreateShader(unit.type); + int shader = glCreateShader(unit.type); if (shader == 0) { throw new ShaderException("Unable to create shader of type " + unit.type); } String source = template.load(unit.filename); - GL43C.glShaderSource(shader, source); - GL43C.glCompileShader(shader); + glShaderSource(shader, source); + glCompileShader(shader); - if (GL43C.glGetShaderi(shader, GL43C.GL_COMPILE_STATUS) != GL43C.GL_TRUE) + if (glGetShaderi(shader, GL_COMPILE_STATUS) != GL_TRUE) { - String err = GL43C.glGetShaderInfoLog(shader); - GL43C.glDeleteShader(shader); + String err = glGetShaderInfoLog(shader); + glDeleteShader(shader); + logShaderSource(source); throw new ShaderException(err); } - GL43C.glAttachShader(program, shader); + glAttachShader(program, shader); shaders[i++] = shader; } - GL43C.glLinkProgram(program); + glLinkProgram(program); - if (GL43C.glGetProgrami(program, GL43C.GL_LINK_STATUS) == GL43C.GL_FALSE) + if (glGetProgrami(program, GL_LINK_STATUS) == GL_FALSE) { - String err = GL43C.glGetProgramInfoLog(program); + String err = glGetProgramInfoLog(program); throw new ShaderException(err); } - GL43C.glValidateProgram(program); + glValidateProgram(program); - if (GL43C.glGetProgrami(program, GL43C.GL_VALIDATE_STATUS) == GL43C.GL_FALSE) + if (glGetProgrami(program, GL_VALIDATE_STATUS) == GL_FALSE) { - String err = GL43C.glGetProgramInfoLog(program); + String err = glGetProgramInfoLog(program); throw new ShaderException(err); } @@ -109,16 +112,25 @@ public int compile(Template template) throws ShaderException while (i > 0) { int shader = shaders[--i]; - GL43C.glDetachShader(program, shader); - GL43C.glDeleteShader(shader); + glDetachShader(program, shader); + glDeleteShader(shader); } if (!ok) { - GL43C.glDeleteProgram(program); + glDeleteProgram(program); } } return program; } + + private static void logShaderSource(String source) + { + int lineNum = 1; + for (String line : source.split("\n")) + { + log.error("{}: {}", lineNum++, line); + } + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/ShaderException.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/ShaderException.java index 78a36eaa34a..3d59a80cfd2 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/ShaderException.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/ShaderException.java @@ -24,7 +24,7 @@ */ package net.runelite.client.plugins.gpu; -class ShaderException extends Exception +class ShaderException extends RuntimeException { ShaderException(String message) { 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 dc795731207..a9e8784e9d8 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 @@ -31,7 +31,7 @@ import net.runelite.api.TextureProvider; import org.lwjgl.opengl.EXTTextureFilterAnisotropic; import org.lwjgl.opengl.GL; -import org.lwjgl.opengl.GL43C; +import static org.lwjgl.opengl.GL43C.*; @Singleton @Slf4j @@ -49,26 +49,26 @@ int initTextureArray(TextureProvider textureProvider) Texture[] textures = textureProvider.getTextures(); - int textureArrayId = GL43C.glGenTextures(); - GL43C.glBindTexture(GL43C.GL_TEXTURE_2D_ARRAY, textureArrayId); + int textureArrayId = glGenTextures(); + glBindTexture(GL_TEXTURE_2D_ARRAY, textureArrayId); if (GL.getCapabilities().glTexStorage3D != 0) { - GL43C.glTexStorage3D(GL43C.GL_TEXTURE_2D_ARRAY, 8, GL43C.GL_RGBA8, TEXTURE_SIZE, TEXTURE_SIZE, textures.length); + glTexStorage3D(GL_TEXTURE_2D_ARRAY, 8, GL_RGBA8, TEXTURE_SIZE, TEXTURE_SIZE, textures.length); } else { int size = TEXTURE_SIZE; for (int i = 0; i < 8; i++) { - GL43C.glTexImage3D(GL43C.GL_TEXTURE_2D_ARRAY, i, GL43C.GL_RGBA8, size, size, textures.length, 0, GL43C.GL_RGBA, GL43C.GL_UNSIGNED_BYTE, 0); + glTexImage3D(GL_TEXTURE_2D_ARRAY, i, GL_RGBA8, size, size, textures.length, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); size /= 2; } } - GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D_ARRAY, GL43C.GL_TEXTURE_MIN_FILTER, GL43C.GL_NEAREST); - GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D_ARRAY, GL43C.GL_TEXTURE_MAG_FILTER, GL43C.GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D_ARRAY, GL43C.GL_TEXTURE_WRAP_S, GL43C.GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // Set brightness to 1.0d to upload unmodified textures to GPU double save = textureProvider.getBrightness(); @@ -78,22 +78,22 @@ int initTextureArray(TextureProvider textureProvider) textureProvider.setBrightness(save); - GL43C.glActiveTexture(GL43C.GL_TEXTURE1); - GL43C.glBindTexture(GL43C.GL_TEXTURE_2D_ARRAY, textureArrayId); - GL43C.glGenerateMipmap(GL43C.GL_TEXTURE_2D_ARRAY); - GL43C.glActiveTexture(GL43C.GL_TEXTURE0); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D_ARRAY, textureArrayId); + glGenerateMipmap(GL_TEXTURE_2D_ARRAY); + glActiveTexture(GL_TEXTURE0); return textureArrayId; } void setAnisotropicFilteringLevel(int textureArrayId, int level) { - GL43C.glBindTexture(GL43C.GL_TEXTURE_2D_ARRAY, textureArrayId); + glBindTexture(GL_TEXTURE_2D_ARRAY, textureArrayId); //level = 0 means no mipmaps and no anisotropic filtering if (level == 0) { - GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D_ARRAY, GL43C.GL_TEXTURE_MIN_FILTER, GL43C.GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, 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 @@ -102,21 +102,21 @@ void setAnisotropicFilteringLevel(int textureArrayId, int level) { // 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. - GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D_ARRAY, GL43C.GL_TEXTURE_MIN_FILTER, GL43C.GL_NEAREST_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR); } if (GL.getCapabilities().GL_EXT_texture_filter_anisotropic) { - final float maxSamples = GL43C.glGetFloat(EXTTextureFilterAnisotropic.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT); + final float maxSamples = glGetFloat(EXTTextureFilterAnisotropic.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)); - GL43C.glTexParameterf(GL43C.GL_TEXTURE_2D_ARRAY, EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT, anisoLevel); + glTexParameterf(GL_TEXTURE_2D_ARRAY, EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT, anisoLevel); } } void freeTextureArray(int textureArrayId) { - GL43C.glDeleteTextures(textureArrayId); + glDeleteTextures(textureArrayId); } /** @@ -153,7 +153,7 @@ private void updateTextures(TextureProvider textureProvider, int textureArrayId) { Texture[] textures = textureProvider.getTextures(); - GL43C.glBindTexture(GL43C.GL_TEXTURE_2D_ARRAY, textureArrayId); + glBindTexture(GL_TEXTURE_2D_ARRAY, textureArrayId); int cnt = 0; for (int textureId = 0; textureId < textures.length; textureId++) @@ -182,8 +182,8 @@ private void updateTextures(TextureProvider textureProvider, int textureArrayId) ByteBuffer pixelBuffer = ByteBuffer.allocateDirect(pixels.length); pixelBuffer.put(pixels); pixelBuffer.flip(); - GL43C.glTexSubImage3D(GL43C.GL_TEXTURE_2D_ARRAY, 0, 0, 0, textureId, TEXTURE_SIZE, TEXTURE_SIZE, - 1, GL43C.GL_RGBA, GL43C.GL_UNSIGNED_BYTE, pixelBuffer); + glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, textureId, TEXTURE_SIZE, TEXTURE_SIZE, + 1, GL_RGBA, GL_UNSIGNED_BYTE, pixelBuffer); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/VAO.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/VAO.java new file mode 100644 index 00000000000..f50a010c4b4 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/VAO.java @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2025, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.gpu; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Projection; +import net.runelite.api.Scene; +import static net.runelite.client.plugins.gpu.GpuPlugin.uniEntityTint; +import static net.runelite.client.plugins.gpu.GpuPlugin.updateEntityProjection; +import static org.lwjgl.opengl.GL11.GL_TRIANGLES; +import static org.lwjgl.opengl.GL11.glDrawArrays; +import static org.lwjgl.opengl.GL11C.GL_FLOAT; +import static org.lwjgl.opengl.GL11C.GL_INT; +import static org.lwjgl.opengl.GL11C.GL_SHORT; +import static org.lwjgl.opengl.GL15.GL_DYNAMIC_DRAW; +import static org.lwjgl.opengl.GL15C.GL_ARRAY_BUFFER; +import static org.lwjgl.opengl.GL15C.glBindBuffer; +import static org.lwjgl.opengl.GL20C.glEnableVertexAttribArray; +import static org.lwjgl.opengl.GL20C.glUniform4i; +import static org.lwjgl.opengl.GL20C.glVertexAttribPointer; +import static org.lwjgl.opengl.GL30C.glBindVertexArray; +import static org.lwjgl.opengl.GL30C.glDeleteVertexArrays; +import static org.lwjgl.opengl.GL30C.glGenVertexArrays; +import static org.lwjgl.opengl.GL30C.glVertexAttribIPointer; + +class VAO +{ + // Temporary vertex format + // index 0: vec3(x, y, z) + // index 1: int abhsl + // index 2: short vec4(id, x, y, z) + static final int VERT_SIZE = 24; + + final VBO vbo; + int vao; + + VAO(int size) + { + vbo = new VBO(size); + } + + void init() + { + vao = glGenVertexArrays(); + glBindVertexArray(vao); + + vbo.init(GL_DYNAMIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, vbo.bufId); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, false, VERT_SIZE, 0); + + glEnableVertexAttribArray(1); + glVertexAttribIPointer(1, 1, GL_INT, VERT_SIZE, 12); + + glEnableVertexAttribArray(2); + glVertexAttribIPointer(2, 4, GL_SHORT, VERT_SIZE, 16); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + } + + void destroy() + { + vbo.destroy(); + glDeleteVertexArrays(vao); + vao = 0; + } + + int[] lengths = new int[4]; + Projection[] projs = new Projection[4]; + Scene[] scenes = new Scene[4]; + int off = 0; + + void addRange(Projection projection, Scene scene) + { + assert vbo.mapped; + + if (off > 0 && lengths[off - 1] == vbo.vb.position()) + { + return; + } + + if (lengths.length == off) + { + int l = lengths.length << 1; + lengths = Arrays.copyOf(lengths, l); + projs = Arrays.copyOf(projs, l); + scenes = Arrays.copyOf(scenes, l); + } + + lengths[off] = vbo.vb.position(); + projs[off] = projection; + scenes[off] = scene; + off++; + } + + void draw() + { + assert !vbo.mapped; + + int start = 0; + for (int i = 0; i < off; ++i) + { + int end = lengths[i]; + Projection p = projs[i]; + Scene scene = scenes[i]; + + int count = end - start; + + updateEntityProjection(p); + glUniform4i(uniEntityTint, scene.getOverrideHue(), scene.getOverrideSaturation(), scene.getOverrideLuminance(), scene.getOverrideAmount()); + glBindVertexArray(vao); + glDrawArrays(GL_TRIANGLES, start / (VERT_SIZE / 4), count / (VAO.VERT_SIZE / 4)); + + start = end; + } + } + + void reset() + { + Arrays.fill(projs, 0, off, null); + Arrays.fill(scenes, 0, off, null); + off = 0; + } +} + +@Slf4j +class VAOList +{ + // this needs to be larger than the largest single model + private static final int VAO_SIZE = 4 * 1024 * 1024; + + private int curIdx; + private final List vaos = new ArrayList<>(); + + VAO get(int size) + { + assert size <= VAO_SIZE; + + while (curIdx < vaos.size()) + { + VAO vao = vaos.get(curIdx); + if (!vao.vbo.mapped) + { + vao.vbo.map(); + } + + int rem = vao.vbo.vb.remaining() * Integer.BYTES; + if (size <= rem) + { + return vao; + } + + curIdx++; + } + + VAO vao = new VAO(VAO_SIZE); + vao.init(); + vao.vbo.map(); + vaos.add(vao); + log.debug("Allocated VAO {} request {}", vao.vao, size); + return vao; + } + + List unmap() + { + int sz = 0; + for (VAO vao : vaos) + { + if (vao.vbo.mapped) + { + ++sz; + vao.vbo.unmap(); + } + } + curIdx = 0; + return vaos.subList(0, sz); + } + + void free() + { + for (VAO vao : vaos) + { + vao.destroy(); + } + vaos.clear(); + curIdx = 0; + } + + void addRange(Projection projection, Scene scene) + { + for (int i = 0; i <= curIdx && i < vaos.size(); ++i) + { + VAO vao = vaos.get(i); + if (vao.vbo.mapped) + { + vao.addRange(projection, scene); + } + } + } + + void debug() + { + log.debug("{} vaos allocated", vaos.size()); + for (VAO vao : vaos) + { + log.debug("vao {} mapped: {} num ranges: {} length: {}", vao, vao.vbo.mapped, vao.off, vao.vbo.mapped ? vao.vbo.vb.position() : -1); + if (vao.off > 1) + { + for (int i = 0; i < vao.off; ++i) + { + log.debug(" {} {} {}", vao.lengths[i], vao.projs[i], vao.scenes[i]); + } + } + } + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/VBO.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/VBO.java new file mode 100644 index 00000000000..62ed7103b95 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/VBO.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2025, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.gpu; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import static org.lwjgl.opengl.GL15.GL_WRITE_ONLY; +import static org.lwjgl.opengl.GL15.glMapBuffer; +import static org.lwjgl.opengl.GL15C.GL_ARRAY_BUFFER; +import static org.lwjgl.opengl.GL15C.glBindBuffer; +import static org.lwjgl.opengl.GL15C.glBufferData; +import static org.lwjgl.opengl.GL15C.glDeleteBuffers; +import static org.lwjgl.opengl.GL15C.glGenBuffers; +import static org.lwjgl.opengl.GL15C.glUnmapBuffer; + +class VBO +{ + final int size; + int bufId; + private ByteBuffer buffer; + IntBuffer vb; + int len; + boolean mapped; + + VBO(int size) + { + this.size = size; + } + + void init(int usage) + { + bufId = glGenBuffers(); + + glBindBuffer(GL_ARRAY_BUFFER, bufId); + glBufferData(GL_ARRAY_BUFFER, size, usage); + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + void destroy() + { + if (mapped) + { + glBindBuffer(GL_ARRAY_BUFFER, bufId); + glUnmapBuffer(GL_ARRAY_BUFFER); + glBindBuffer(GL_ARRAY_BUFFER, 0); + mapped = false; + } + glDeleteBuffers(bufId); + bufId = 0; + } + + void map() + { + assert !mapped; + glBindBuffer(GL_ARRAY_BUFFER, bufId); + buffer = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY, buffer); + if (buffer == null) + { + throw new RuntimeException("unable to map GL buffer " + bufId + " size " + size); + } + this.vb = buffer.asIntBuffer(); + glBindBuffer(GL_ARRAY_BUFFER, 0); + mapped = true; + } + + void unmap() + { + assert mapped; + len = vb.position(); + vb = null; + + glBindBuffer(GL_ARRAY_BUFFER, bufId); + glUnmapBuffer(GL_ARRAY_BUFFER); + glBindBuffer(GL_ARRAY_BUFFER, 0); + mapped = false; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/Zone.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/Zone.java new file mode 100644 index 00000000000..c3db9ee0f1a --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/Zone.java @@ -0,0 +1,794 @@ +/* + * Copyright (c) 2025, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.gpu; + +import java.nio.IntBuffer; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.Set; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Model; +import net.runelite.api.Perspective; +import net.runelite.api.Scene; +import static net.runelite.client.plugins.gpu.FacePrioritySorter.distanceFaceCount; +import static net.runelite.client.plugins.gpu.FacePrioritySorter.distanceToFaces; +import static net.runelite.client.plugins.gpu.GpuPlugin.glProgram; // NOPMD: UnnecessaryImport +import static net.runelite.client.plugins.gpu.GpuPlugin.uniBase; +import org.lwjgl.BufferUtils; +import static org.lwjgl.opengl.GL11.GL_UNSIGNED_INT; +import static org.lwjgl.opengl.GL11.glDrawElements; +import static org.lwjgl.opengl.GL11C.GL_TRIANGLES; +import static org.lwjgl.opengl.GL14.glMultiDrawArrays; +import static org.lwjgl.opengl.GL15C.GL_ELEMENT_ARRAY_BUFFER; +import static org.lwjgl.opengl.GL15C.GL_STREAM_DRAW; +import static org.lwjgl.opengl.GL15C.glBufferData; +import static org.lwjgl.opengl.GL15C.glDeleteBuffers; +import static org.lwjgl.opengl.GL15C.glGenBuffers; +import static org.lwjgl.opengl.GL20C.glVertexAttribPointer; +import static org.lwjgl.opengl.GL30C.GL_ARRAY_BUFFER; +import static org.lwjgl.opengl.GL30C.GL_INT; +import static org.lwjgl.opengl.GL30C.GL_SHORT; +import static org.lwjgl.opengl.GL30C.glBindBuffer; +import static org.lwjgl.opengl.GL30C.glBindVertexArray; +import static org.lwjgl.opengl.GL30C.glDeleteVertexArrays; +import static org.lwjgl.opengl.GL30C.glEnableVertexAttribArray; +import static org.lwjgl.opengl.GL30C.glGenVertexArrays; +import static org.lwjgl.opengl.GL30C.glVertexAttribIPointer; +import static org.lwjgl.opengl.GL41C.glProgramUniform3i; + +@Slf4j +@RequiredArgsConstructor +class Zone +{ + private static final boolean USE_STATIC_UNSORTED = false; + + // Zone vertex format + // index 0: short vec3(x, y, z) + // index 1: int abhsl + // index 2: short vec4(id, x, y, z) + static final int VERT_SIZE = 20; + + int glVao; + int bufLen; + + int glVaoA; + int bufLenA; + + int sizeO, sizeA; + VBO vboO, vboA; + + boolean initialized; // whether the zone vao and vbos are ready + boolean cull; // whether the zone is queued for deletion + boolean dirty; // whether the zone has temporary modifications + boolean invalidate; // whether the zone needs rebuilding + + int[] levelOffsets = new int[4]; // buffer pos in ints for the end of the level + + int[][] rids; + int[][] roofStart; + int[][] roofEnd; + + final List alphaModels = new ArrayList<>(0); + + void init(VBO o, VBO a) + { + assert glVao == 0; + assert glVaoA == 0; + + if (o != null) + { + vboO = o; + glVao = glGenVertexArrays(); + setupVao(glVao, o.bufId); + } + + if (a != null) + { + vboA = a; + glVaoA = glGenVertexArrays(); + setupVao(glVaoA, a.bufId); + } + } + + void free() + { + if (vboO != null) + { + vboO.destroy(); + vboO = null; + } + + if (vboA != null) + { + vboA.destroy(); + vboA = null; + } + + if (glVao != 0) + { + glDeleteVertexArrays(glVao); + glVao = 0; + } + + if (glVaoA != 0) + { + glDeleteVertexArrays(glVaoA); + glVaoA = 0; + } + + // don't add permanent alphamodels to the cache as permanent alphamodels are always allocated + // to avoid having to synchronize the cache + alphaModels.clear(); + } + + void unmap() + { + if (vboO != null) + { + vboO.unmap(); + } + if (vboA != null) + { + vboA.unmap(); + } + + if (vboO != null) + { + this.bufLen = vboO.len / (VERT_SIZE / 4); + } + + if (vboA != null) + { + this.bufLenA = vboA.len / (VERT_SIZE / 4); + } + } + + private void setupVao(int vao, int buffer) + { + glBindVertexArray(vao); + glBindBuffer(GL_ARRAY_BUFFER, buffer); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_SHORT, false, VERT_SIZE, 0); + + glEnableVertexAttribArray(1); + glVertexAttribIPointer(1, 1, GL_INT, VERT_SIZE, 8); + + glEnableVertexAttribArray(2); + glVertexAttribIPointer(2, 4, GL_SHORT, VERT_SIZE, 12); + + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + void updateRoofs(Map updates) + { + for (int level = 0; level < 4; ++level) + { + for (int i = 0; i < rids[level].length; ++i) + { + rids[level][i] = updates.getOrDefault(rids[level][i], rids[level][i]); + } + } + + for (AlphaModel m : alphaModels) + { + m.rid = (short) (int) updates.getOrDefault((int) m.rid, (int) m.rid); + } + } + + private static final int NUM_DRAW_RANGES = 512; + private static final IntBuffer drawOff = BufferUtils.createIntBuffer(NUM_DRAW_RANGES); + private static final IntBuffer drawEnd = BufferUtils.createIntBuffer(NUM_DRAW_RANGES); + + private void convertForDraw(int vertSize) + { + assert drawOff.position() == drawEnd.position(); + + drawOff.flip(); + drawEnd.flip(); + + for (int i = 0; i < drawOff.limit(); ++i) + { + int off = drawOff.get(i); + int end = drawEnd.get(i); + + assert end >= off; + + // convert from bytes to verts + off /= vertSize >> 2; + end /= vertSize >> 2; + + end -= off; // convert from end pos to length + + drawOff.put(i, off); + drawEnd.put(i, end); + } + } + + void renderOpaque(int zx, int zz, int minLevel, int currentLevel, int maxLevel, Set hiddenRoofIds) + { + drawOff.clear(); + drawEnd.clear(); + + for (int level = minLevel; level <= maxLevel; ++level) + { + int[] rids = this.rids[level]; + int[] roofStart = this.roofStart[level]; + int[] roofEnd = this.roofEnd[level]; + + if (rids.length == 0 || hiddenRoofIds.isEmpty() || level <= currentLevel) + { + // draw the whole level + int start = level == 0 ? 0 : this.levelOffsets[level - 1]; + int end = this.levelOffsets[level]; + pushRange(start, end); + continue; + } + + for (int roofIdx = 0; roofIdx < rids.length; ++roofIdx) + { + int rid = rids[roofIdx]; + if (rid > 0 && !hiddenRoofIds.contains(rid)) + { + // draw the roof + assert roofEnd[roofIdx] >= roofStart[roofIdx]; + if (roofEnd[roofIdx] > roofStart[roofIdx]) + { + pushRange(roofStart[roofIdx], roofEnd[roofIdx]); + } + } + } + + // push from the end of the last roof to the end of the level + int endpos = level == 0 ? 0 : this.levelOffsets[level - 1]; + for (int roofIdx = rids.length - 1; roofIdx >= 0; --roofIdx) + { + int rid = rids[roofIdx]; + if (rid > 0) + { + endpos = roofEnd[roofIdx]; + break; + } + } + // draw the non roofs + pushRange(endpos, this.levelOffsets[level]); + } + + convertForDraw(VERT_SIZE); + + glProgramUniform3i(glProgram, uniBase, zx << 10, 0, zz << 10); + glBindVertexArray(glVao); + glMultiDrawArrays(GL_TRIANGLES, drawOff, drawEnd); + } + + private static void pushRange(int start, int end) + { + assert end >= start; + + int idx = drawEnd.position(); + if (idx > 0 && drawEnd.get(idx - 1) == start) + { + drawEnd.put(idx - 1, end); + } + else if (!drawEnd.hasRemaining()) + { + log.debug("draw ranges exhausted"); + } + else + { + drawOff.put(start); + drawEnd.put(end); + } + } + + static class AlphaModel + { + int id; + int startpos, endpos; + short x, y, z; // local position + short rid; + int vao; + byte level; + byte lx, lz, ux, uz; // lower/upper zone coords + byte zofx, zofz; // for temp alpha models, offset of source zone from target zone + byte flags; + + // only set for static geometry as they require sorting + int radius; + int[] packedFaces; + byte[] renderPriorities; + + static final int SKIP = 1; // temporary model is in a closer zone + static final int TEMP = 2; // temporary model added to a closer zone + + boolean isTemp() + { + return packedFaces == null; + } + } + + static final Queue modelCache = new ArrayDeque<>(); + + void addAlphaModel(int vao, Model model, int startpos, int endpos, int x, int y, int z, int lx, int lz, int ux, int uz, int rid, int level, int id) + { + AlphaModel m = new AlphaModel(); + m.id = id; + m.startpos = startpos; + m.endpos = endpos; + m.x = (short) x; + m.y = (short) y; + m.z = (short) z; + m.vao = vao; + m.rid = (short) rid; + m.level = (byte) level; + if (lx > -1) + { + m.lx = (byte) lx; + m.lz = (byte) lz; + m.ux = (byte) ux; + m.uz = (byte) uz; + } + else + { + m.lx = m.lz = m.ux = m.uz = -1; + } + + int faceCount = model.getFaceCount(); + int[] color3 = model.getFaceColors3(); + byte[] transparencies = model.getFaceTransparencies(); + float[] vertexX = model.getVerticesX(); + float[] vertexY = model.getVerticesY(); + float[] vertexZ = model.getVerticesZ(); + int[] indices1 = model.getFaceIndices1(); + int[] indices2 = model.getFaceIndices2(); + int[] indices3 = model.getFaceIndices3(); + + int minX = Integer.MAX_VALUE, minY = minX, minZ = minY; + int maxX = Integer.MIN_VALUE, maxY = maxX, maxZ = maxY; + + for (int f = 0; f < faceCount; ++f) + { + if (color3[f] == -2 || transparencies[f] == 0) + { + continue; + } + + int fx = (int) (vertexX[indices1[f]] + vertexX[indices2[f]] + vertexX[indices3[f]]); + int fy = (int) (vertexY[indices1[f]] + vertexY[indices2[f]] + vertexY[indices3[f]]); + int fz = (int) (vertexZ[indices1[f]] + vertexZ[indices2[f]] + vertexZ[indices3[f]]); + + minX = Math.min(minX, fx); + maxX = Math.max(maxX, fx); + minY = Math.min(minY, fy); + maxY = Math.max(maxY, fy); + minZ = Math.min(minZ, fz); + maxZ = Math.max(maxZ, fz); + } + + int cx = (minX + maxX) / 6; + int cy = (minY + maxY) / 6; + int cz = (minZ + maxZ) / 6; + + int size = Math.max(Math.max( + Math.max(maxX / 3 - cx, minX / -3 - cx), + Math.max(maxY / 3 - cy, minY / -3 - cy) * 2), + Math.max(maxZ / 3 - cz, minZ / -3 - cz)); + + int shift = 0; + // 10 bits because we need a sign bit + for (int v = size >> 10; v > 0; v >>= 1) + { + shift++; + } + + int[] packedFaces = m.packedFaces = new int[(endpos - startpos) / ((3 * VERT_SIZE) >> 2)]; + int radius = 0; + char bufferIdx = 0; + for (int f = 0; f < faceCount; ++f) + { + if (color3[f] == -2 || transparencies[f] == 0) + { + continue; + } + + int fx = (((int) (vertexX[indices1[f]] + vertexX[indices2[f]] + vertexX[indices3[f]]) / 3) - cx) >> shift; + int fy = (((int) (vertexY[indices1[f]] + vertexY[indices2[f]] + vertexY[indices3[f]]) / 3) - cy) >> shift; + int fz = (((int) (vertexZ[indices1[f]] + vertexZ[indices2[f]] + vertexZ[indices3[f]]) / 3) - cz) >> shift; + + radius = Math.max(radius, fx * fx + fy * fy + fz * fz); + + packedFaces[bufferIdx] = ((fx & ((1 << 11) - 1)) << 21) + | ((fy & ((1 << 10) - 1)) << 11) + | (fz & ((1 << 11) - 1)); + bufferIdx++; + } + + assert radius >= 0; + + m.renderPriorities = model.getFaceRenderPriorities(); + m.radius = 2 + (int) Math.sqrt(radius); + + assert packedFaces.length > 0; + assert bufferIdx == packedFaces.length; + + alphaModels.add(m); + } + + void addTempAlphaModel(int vao, int startpos, int endpos, int level, int x, int y, int z) + { + AlphaModel m = modelCache.poll(); + if (m == null) + { + m = new AlphaModel(); + } + m.id = -1; + m.startpos = startpos; + m.endpos = endpos; + m.x = (short) x; + m.y = (short) y; + m.z = (short) z; + m.vao = vao; + m.rid = -1; + m.level = (byte) level; + m.lx = m.lz = m.ux = m.uz = -1; + m.flags = 0; + m.zofx = m.zofz = 0; + alphaModels.add(m); + } + + void removeTemp() + { + for (int i = alphaModels.size() - 1; i >= 0; --i) + { + AlphaModel m = alphaModels.get(i); + if (m.isTemp() || (m.flags & AlphaModel.TEMP) != 0) + { + alphaModels.remove(i); + m.packedFaces = null; + m.renderPriorities = null; + modelCache.add(m); + } + m.flags &= ~AlphaModel.SKIP; + } + } + + // this needs to be larger than the max model alpha face count * 3 + private static final IntBuffer alphaElements = BufferUtils.createIntBuffer(16384); + + private static final int STATIC = 1; + private static final int TEMP = 2; + private static final int STATIC_UNSORTED = 3; + + private static int lastDrawMode; + private static int lastVao; + private static int lastzx, lastzz; + + private static int elementBufferId; + + private static final int[] numOfPriority = FacePrioritySorter.numOfPriority; + private static final int[][] orderedFaces = FacePrioritySorter.orderedFaces; + + static void initBuffer() + { + elementBufferId = glGenBuffers(); + } + + static void freeBuffer() + { + glDeleteBuffers(elementBufferId); + elementBufferId = 0; + } + + void alphaSort(int zx, int zz, int cx, int cy, int cz) + { + alphaModels.sort(Comparator.comparingInt((AlphaModel m) -> + { + final int mx = (m.x + ((zx - m.zofx) << 10)); + final int mz = (m.z + ((zz - m.zofz) << 10)); + return (mx - cx) * (mx - cx) + + (m.y - cy) * (m.y - cy) + + (mz - cz) * (mz - cz); + } + ) + .reversed() + ); + } + + void renderAlpha(int zx, int zz, int cyaw, int cpitch, int minLevel, int currentLevel, int maxLevel, int level, Set hiddenRoofIds) + { + drawOff.clear(); + drawEnd.clear(); + alphaElements.clear(); + lastDrawMode = lastVao = 0; + lastzx = zx; + lastzz = zz; + + int yawsin = Perspective.SINE[cyaw]; + int yawcos = Perspective.COSINE[cyaw]; + int pitchsin = Perspective.SINE[cpitch]; + int pitchcos = Perspective.COSINE[cpitch]; + for (AlphaModel m : alphaModels) + { + if ((m.flags & AlphaModel.SKIP) != 0) continue; + if (m.level != level) continue; + + boolean ok = false; + if (level >= minLevel && level <= maxLevel) + { + if (level <= currentLevel || !hiddenRoofIds.contains((int) m.rid)) + { + ok = true; + } + } + if (!ok) + { + continue; + } + + if (lastVao != m.vao + || lastzx != (zx - m.zofx) || lastzz != (zz - m.zofz) + ) + { + flush(); + } + + lastVao = m.vao; + lastzx = zx - m.zofx; + lastzz = zz - m.zofz; + + if (m.isTemp()) + { + // these are already sorted and so just requires a glMultiDrawArrays() from the active vao + lastDrawMode = TEMP; + pushRange(m.startpos, m.endpos); + continue; + } + + if (USE_STATIC_UNSORTED) + { + lastDrawMode = STATIC_UNSORTED; + pushRange(m.startpos, m.endpos); + continue; + } + + lastDrawMode = STATIC; + + final int radius = m.radius; + int diameter = 1 + radius * 2; + final int[] packedFaces = m.packedFaces; + if (diameter >= 6000) + { + continue; + } + + Arrays.fill(distanceFaceCount, 0, diameter, (char) 0); + + for (int i = 0; i < packedFaces.length; ++i) + { + int pack = packedFaces[i]; + + int x = pack >> 21; + int y = (pack << 11) >> 22; + int z = (pack << 21) >> 21; + + int t = z * yawcos - x * yawsin >> 16; + int fz = y * pitchsin + t * pitchcos >> 16; + fz += radius; + + assert fz >= 0 && fz < diameter : fz; + distanceToFaces[fz][distanceFaceCount[fz]++] = (char) i; + } + + if (packedFaces.length * 3 > alphaElements.remaining()) + { + flush(); + } + + byte[] faceRenderPriorities = m.renderPriorities; + final int start = m.startpos / (VERT_SIZE >> 2); // ints to verts + if (faceRenderPriorities == null) + { + for (int i = diameter - 1; i >= 0; --i) + { + final int cnt = distanceFaceCount[i]; + if (cnt > 0) + { + final char[] faces = distanceToFaces[i]; + + for (int faceIdx = 0; faceIdx < cnt; ++faceIdx) + { + int face = faces[faceIdx]; + face *= 3; + face += start; + alphaElements.put(face++); + alphaElements.put(face++); + alphaElements.put(face++); + } + } + } + } + else + { + // Vanilla uses priority draw order for alpha faces and not depth draw order + // And since we don't have the full model here, only the alpha faces, we can't compute the + // 10/11 insertion points either. Just ignore those since I think they are mostly for players, + // which are rendered differently anyway. + Arrays.fill(numOfPriority, 0); + + for (int i = diameter - 1; i >= 0; --i) + { + final int cnt = distanceFaceCount[i]; + if (cnt > 0) + { + final char[] faces = distanceToFaces[i]; + + for (int faceIdx = 0; faceIdx < cnt; ++faceIdx) + { + final int face = faces[faceIdx]; + final byte pri = faceRenderPriorities[face]; + final int distIdx = numOfPriority[pri]++; + + orderedFaces[pri][distIdx] = face; + } + } + } + + for (int pri = 0; pri < 12; ++pri) + { + final int priNum = numOfPriority[pri]; + final int[] priFaces = orderedFaces[pri]; + + for (int faceIdx = 0; faceIdx < priNum; ++faceIdx) + { + final int face = priFaces[faceIdx]; + int idx = face * 3 + start; + alphaElements.put(idx++); + alphaElements.put(idx++); + alphaElements.put(idx++); + } + } + } + } + + flush(); + } + + private void flush() + { + if (lastDrawMode == TEMP) + { + convertForDraw(VAO.VERT_SIZE); + glProgramUniform3i(glProgram, uniBase, 0, 0, 0); + glBindVertexArray(lastVao); + glMultiDrawArrays(GL_TRIANGLES, drawOff, drawEnd); + drawOff.clear(); + drawEnd.clear(); + } + else if (lastDrawMode == STATIC) + { + alphaElements.flip(); + glProgramUniform3i(glProgram, uniBase, lastzx << 10, 0, lastzz << 10); + glBindVertexArray(lastVao); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBufferId); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, alphaElements, GL_STREAM_DRAW); + glDrawElements(GL_TRIANGLES, alphaElements.limit(), GL_UNSIGNED_INT, 0L); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + alphaElements.clear(); + } + else if (lastDrawMode == STATIC_UNSORTED) + { + convertForDraw(VERT_SIZE); + glProgramUniform3i(glProgram, uniBase, lastzx << 10, 0, lastzz << 10); + glBindVertexArray(lastVao); + glMultiDrawArrays(GL_TRIANGLES, drawOff, drawEnd); + drawOff.clear(); + drawEnd.clear(); + } + } + + void multizoneLocs(Scene scene, int zx, int zz, int cx, int cz, Zone[][] zones) + { + int offset = scene.getWorldViewId() == -1 ? GpuPlugin.SCENE_OFFSET >> 3 : 0; + for (AlphaModel m : alphaModels) + { + if (m.lx == -1) + { + continue; + } + + // calculate which zone this model should be drawn from + // TODO fix for boats + int max = Integer.MAX_VALUE; + int closestZoneX = -50, closestZoneZ = -50; + for (int x = m.lx >> 3; x <= m.ux >> 3; ++x) + { + for (int z = m.lz >> 3; z <= m.uz >> 3; ++z) + { + int centerX = (zx - m.zofx + x) * 8 + 4 << 7; + int centerZ = (zz - m.zofz + z) * 8 + 4 << 7; + int distance = (centerX - cx) * (centerX - cx) + + (centerZ - cz) * (centerZ - cz); + if (distance < max) + { + max = distance; + closestZoneX = centerX >> 10; + closestZoneZ = centerZ >> 10; + } + } + } + assert closestZoneX != -50; + if (closestZoneX != zx || closestZoneZ != zz) + { + assert (m.flags & AlphaModel.TEMP) == 0; + + assert closestZoneX + offset >= 0 : closestZoneX; + assert closestZoneX + offset < zones.length : closestZoneX; + assert closestZoneZ + offset >= 0 : closestZoneZ; + assert closestZoneZ + offset < zones[0].length : closestZoneZ; + + Zone z = zones[closestZoneX + offset][closestZoneZ + offset]; + assert z != null; + assert z != this; + + AlphaModel m2 = modelCache.poll(); + if (m2 == null) + { + m2 = new AlphaModel(); + } + m2.id = m.id; + m2.startpos = m.startpos; + m2.endpos = m.endpos; + m2.x = m.x; + m2.y = m.y; + m2.z = m.z; + m2.vao = m.vao; + m2.rid = m.rid; + m2.level = m.level; + m2.lx = m.lx; + m2.lz = m.lz; + m2.ux = m.ux; + m2.uz = m.uz; + m2.zofx = (byte) (closestZoneX - zx); + m2.zofz = (byte) (closestZoneZ - zz); + + m2.packedFaces = m.packedFaces; + m2.renderPriorities = m.renderPriorities; + m2.radius = m.radius; + + m2.flags = AlphaModel.TEMP; + m.flags |= AlphaModel.SKIP; + + z.alphaModels.add(m2); + } + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/config/UIScalingMode.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/config/UIScalingMode.java index 04c01697c51..a78694d152f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/config/UIScalingMode.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/config/UIScalingMode.java @@ -31,14 +31,14 @@ @RequiredArgsConstructor public enum UIScalingMode { - NEAREST("Nearest Neighbor", 0), - LINEAR("Bilinear", 0), - MITCHELL("Bicubic (Mitchell)", 1), - CATMULL_ROM("Bicubic (Catmull-Rom)", 2), - XBR("xBR", 3); + NEAREST("Nearest Neighbor"), + LINEAR("Bilinear"), + MITCHELL("Bicubic (Mitchell)"), + CATMULL_ROM("Bicubic (Catmull-Rom)"), + XBR("xBR"), + HYBRID("Hybrid"); private final String name; - private final int mode; @Override public String toString() diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerConfig.java index 4d090771aa9..3ac5320cda0 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerConfig.java @@ -86,7 +86,7 @@ default boolean showPriceType() name = "Remember loot", description = "Saves loot between client sessions." ) - default boolean syncPanel() + default boolean rememberLoot() { return true; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java index 3491df9c6e8..fec21a553ed 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java @@ -37,7 +37,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Deque; -import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.function.Predicate; @@ -534,11 +533,8 @@ void rebuild() } else { - // Loop in reverse insertion order so the most recent kill is first on the UI - Iterator it = sessionRecords.descendingIterator(); - while (it.hasNext()) + for (LootTrackerRecord r : sessionRecords) { - LootTrackerRecord r = it.next(); if (!hideIgnoredItems || !plugin.isEventIgnored(r.getTitle())) { buildBox(r); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java index d5b81f66824..4eeb01b751f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java @@ -487,7 +487,7 @@ private void switchProfile(String profileKey) log.debug("Switched to profile {}", profileKey); - if (!config.syncPanel()) + if (!config.rememberLoot()) { return; } @@ -650,7 +650,7 @@ public void onGameStateChanged(final GameStateChanged event) // if there is unloaded loot for a type+name, load it in private void initLoot(LootRecordType type, String name) { - if (panel.hasRecord(type, name)) + if (panel.hasRecord(type, name) || !config.rememberLoot()) { return; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/minimap/MinimapPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/minimap/MinimapPlugin.java index d403563bcae..b41c8c893e2 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/minimap/MinimapPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/minimap/MinimapPlugin.java @@ -142,6 +142,11 @@ public void onScriptPostFired(ScriptPostFired scriptPostFired) private void updateMinimapWidgetVisibility(boolean hide) { + if (client.getGameState() != GameState.LOGGED_IN) + { + return; + } + boolean vanillaHideMinimap = client.getVarbitValue(VarbitID.MINIMAP_TOGGLE) == 1; setHidden(InterfaceID.ToplevelOsrsStretch.MAP_MINIMAP, hide || vanillaHideMinimap); diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/cl_types.cl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/cl_types.cl deleted file mode 100644 index ce9bb7111b7..00000000000 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/cl_types.cl +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2021, Adam - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -struct uniform { - float cameraYaw; - float cameraPitch; - int centerX; - int centerY; - int zoom; - float cameraX; - float cameraY; - float cameraZ; -}; - -struct shared_data { - int totalNum[12]; // number of faces with a given priority - int totalDistance[12]; // sum of distances to faces of a given priority - int totalMappedNum[18]; // number of faces with a given adjusted priority - int min10; // minimum distance to a face of priority 10 - int renderPris[0]; // priority for face draw order -}; - -struct modelinfo { - int offset; // offset into vertex buffer - int toffset; // offset into texture buffer - int size; // length in faces - int idx; // write idx in target buffer - int flags; // buffer, hillskew, plane, orientation - int x; // scene position x - int y; // scene position y - int z; // scene position z -}; - -struct vert { - float x; - float y; - float z; - int ahsl; -}; \ No newline at end of file diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/colorblind.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/colorblind.glsl index aa7068703e2..6421a3dc90b 100644 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/colorblind.glsl +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/colorblind.glsl @@ -3,7 +3,6 @@ // https://web.archive.org/web/20090731011248/http://scien.stanford.edu/class/psych221/projects/05/ofidaner/project_report.pdf // -#define NONE 0 #define PROTAN 1 #define DEUTERAN 2 #define TRITAN 3 @@ -18,19 +17,19 @@ const mat3 lms2lmst = mat3(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(-0.395 const mat3 corrections = mat3(vec3(0.0, 0.0, 0.0), vec3(0.7, 1.0, 0.0), vec3(0.7, 0.0, 1.0)); -vec3 colorblind(int mode, vec3 color) { +vec3 colorblind(vec3 color) { vec3 LMS = color * rgb2lms; vec3 lms; - if (mode == PROTAN) { - lms = LMS * lms2lmsp; // red deficiency - } else if (mode == DEUTERAN) { - lms = LMS * lms2lmsd; // green deficiency - } else if (mode == TRITAN) { - lms = LMS * lms2lmst; // blue deficiency - } else { - return color; - } +#if COLORBLIND_MODE == PROTAN + lms = LMS * lms2lmsp; // red deficiency +#elif COLORBLIND_MODE == DEUTERAN + lms = LMS * lms2lmsd; // green deficiency +#elif COLORBLIND_MODE == TRITAN + lms = LMS * lms2lmst; // blue deficiency +#else +#error 1 +#endif // LMS to RGB matrix conversion mat3 lms2rgb = inverse(rgb2lms); diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/common.cl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/common.cl deleted file mode 100644 index 8b949b20ccc..00000000000 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/common.cl +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2021, Adam - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#define PI 3.1415926535897932384626433832795f -#define UNIT PI / 1024.0f - -float3 toScreen(float3 vertex, float cameraYaw, float cameraPitch, int centerX, int centerY, int zoom) { - float yawSin = sin(cameraYaw); - float yawCos = cos(cameraYaw); - - float pitchSin = sin(cameraPitch); - float pitchCos = cos(cameraPitch); - - float rotatedX = (vertex.z * yawSin) + (vertex.x * yawCos); - float rotatedZ = (vertex.z * yawCos) - (vertex.x * yawSin); - - float var13 = (vertex.y * pitchCos) - (rotatedZ * pitchSin); - float var12 = (vertex.y * pitchSin) + (rotatedZ * pitchCos); - - float x = rotatedX * zoom / var12 + centerX; - float y = var13 * zoom / var12 + centerY; - float z = -var12; // in OpenGL depth is negative - - return (float3)(x, y, z); -} - -/* - * Rotate a vertex by a given orientation in JAU - */ -float4 rotate_vertex(float4 vertex, int orientation) { - float rad = orientation * UNIT; - float s = sin(rad); - float c = cos(rad); - float x = vertex.z * s + vertex.x * c; - float z = vertex.z * c - vertex.x * s; - return (float4)(x, vertex.y, z, vertex.w); -} - -/* - * Calculate the distance to a vertex given the camera angle - */ -float vertex_distance(float4 vertex, float cameraYaw, float cameraPitch) { - float yawSin = sin(cameraYaw); - float yawCos = cos(cameraYaw); - - float pitchSin = sin(cameraPitch); - float pitchCos = cos(cameraPitch); - - float j = vertex.z * yawCos - vertex.x * yawSin; - float l = vertex.y * pitchSin + j * pitchCos; - - return l; -} - -/* - * Calculate the distance to a face - */ -float face_distance(float4 vA, float4 vB, float4 vC, float cameraYaw, float cameraPitch) { - float dvA = vertex_distance(vA, cameraYaw, cameraPitch); - float dvB = vertex_distance(vB, cameraYaw, cameraPitch); - float dvC = vertex_distance(vC, cameraYaw, cameraPitch); - float faceDistance = (dvA + dvB + dvC) / 3; - return (int)faceDistance; -} - -/* - * Test if a face is visible (not backward facing) - */ -bool face_visible(__constant struct uniform *uni, float3 vA, float3 vB, float3 vC, int4 position) { - // Move model to scene location, and account for camera offset - float3 cameraPos = (float3)(uni->cameraX, uni->cameraY, uni->cameraZ); - float3 modelPos = convert_float3(position.xyz); - - float3 lA = vA + modelPos - cameraPos; - float3 lB = vB + modelPos - cameraPos; - float3 lC = vC + modelPos - cameraPos; - - float3 sA = toScreen(lA, uni->cameraYaw, uni->cameraPitch, uni->centerX, uni->centerY, uni->zoom); - float3 sB = toScreen(lB, uni->cameraYaw, uni->cameraPitch, uni->centerX, uni->centerY, uni->zoom); - float3 sC = toScreen(lC, uni->cameraYaw, uni->cameraPitch, uni->centerX, uni->centerY, uni->zoom); - - return (sA.x - sB.x) * (sC.y - sB.y) - (sC.x - sB.x) * (sA.y - sB.y) > 0; -} diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/common.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/common.glsl deleted file mode 100644 index 14ceab09d2b..00000000000 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/common.glsl +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2018, Adam - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "to_screen.glsl" - -/* - * Rotate a vertex by a given orientation in JAU - */ -vec4 rotate_vertex(vec4 vertex, int orientation) { - float rad = orientation * UNIT; - float s = sin(rad); - float c = cos(rad); - // clang-format off - mat4 m = mat4( - c, 0, s, 0, - 0, 1, 0, 0, - -s, 0, c, 0, - 0, 0, 0, 1 - ); - // clang-format on - return vertex * m; -} - -/* - * Calculate the distance to a vertex given the camera angle - */ -float distance(vec4 vertex, float cameraYaw, float cameraPitch) { - float yawSin = sin(cameraYaw); - float yawCos = cos(cameraYaw); - - float pitchSin = sin(cameraPitch); - float pitchCos = cos(cameraPitch); - - float j = vertex.z * yawCos - vertex.x * yawSin; - float l = vertex.y * pitchSin + j * pitchCos; - - return l; -} - -/* - * Calculate the distance to a face - */ -int face_distance(vec4 vA, vec4 vB, vec4 vC, float cameraYaw, float cameraPitch) { - float dvA = distance(vA, cameraYaw, cameraPitch); - float dvB = distance(vB, cameraYaw, cameraPitch); - float dvC = distance(vC, cameraYaw, cameraPitch); - float faceDistance = (dvA + dvB + dvC) / 3; - return int(faceDistance); -} - -/* - * Test if a face is visible (not backward facing) - */ -bool face_visible(vec3 vA, vec3 vB, vec3 vC, ivec4 position) { - // Move model to scene location, and account for camera offset - vec3 cameraPos = vec3(cameraX, cameraY, cameraZ); - - vec3 lA = vA + position.xyz - cameraPos; - vec3 lB = vB + position.xyz - cameraPos; - vec3 lC = vC + position.xyz - cameraPos; - - vec3 sA = toScreen(lA, cameraYaw, cameraPitch, centerX, centerY, zoom); - vec3 sB = toScreen(lB, cameraYaw, cameraPitch, centerX, centerY, zoom); - vec3 sC = toScreen(lC, cameraYaw, cameraPitch, centerX, centerY, zoom); - - return (sA.x - sB.x) * (sC.y - sB.y) - (sC.x - sB.x) * (sA.y - sB.y) > 0; -} diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp.cl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp.cl deleted file mode 100644 index 9049cd29a50..00000000000 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp.cl +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2021, Adam - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include FACE_COUNT - -#include "cl_types.cl" -#include "common.cl" -#include "priority_render.cl" - -__kernel __attribute__((work_group_size_hint(256, 1, 1))) void computeLarge(__local struct shared_data *shared, __global const struct modelinfo *ol, - __global const struct vert *vb, __global const struct vert *tempvb, - __global const float4 *texb, __global const float4 *temptexb, - __global struct vert *vout, __global float4 *uvout, __constant struct uniform *uni, - read_only image3d_t tileHeightImage) { - size_t groupId = get_group_id(0); - size_t localId = get_local_id(0) * FACE_COUNT; - struct modelinfo minfo = ol[groupId]; - int4 pos = (int4)(minfo.x, minfo.y, minfo.z, 0); - - if (localId == 0) { - shared->min10 = 6000; - for (int i = 0; i < 12; ++i) { - shared->totalNum[i] = 0; - shared->totalDistance[i] = 0; - } - for (int i = 0; i < 18; ++i) { - shared->totalMappedNum[i] = 0; - } - } - - int prio[FACE_COUNT]; - int dis[FACE_COUNT]; - struct vert v1[FACE_COUNT]; - struct vert v2[FACE_COUNT]; - struct vert v3[FACE_COUNT]; - - for (int i = 0; i < FACE_COUNT; i++) { - get_face(shared, vb, tempvb, localId + i, minfo, uni->cameraYaw, uni->cameraPitch, &prio[i], &dis[i], &v1[i], &v2[i], &v3[i]); - } - - barrier(CLK_LOCAL_MEM_FENCE); - - for (int i = 0; i < FACE_COUNT; i++) { - add_face_prio_distance(shared, uni, localId + i, minfo, v1[i], v2[i], v3[i], prio[i], dis[i], pos); - } - - barrier(CLK_LOCAL_MEM_FENCE); - - int prioAdj[FACE_COUNT]; - int idx[FACE_COUNT]; - for (int i = 0; i < FACE_COUNT; i++) { - idx[i] = map_face_priority(shared, localId + i, minfo, prio[i], dis[i], &prioAdj[i]); - } - - barrier(CLK_LOCAL_MEM_FENCE); - - for (int i = 0; i < FACE_COUNT; i++) { - insert_face(shared, localId + i, minfo, prioAdj[i], dis[i], idx[i]); - } - - barrier(CLK_LOCAL_MEM_FENCE); - - for (int i = 0; i < FACE_COUNT; i++) { - sort_and_insert(shared, uni, texb, temptexb, vout, uvout, localId + i, minfo, prioAdj[i], dis[i], v1[i], v2[i], v3[i], tileHeightImage); - } -} diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp.glsl deleted file mode 100644 index edf7d3ff2fe..00000000000 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp.glsl +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (c) 2018, Adam - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include version_header - -#include thread_config - -shared int totalNum[12]; // number of faces with a given priority -shared int totalDistance[12]; // sum of distances to faces of a given priority - -shared int totalMappedNum[18]; // number of faces with a given adjusted priority - -shared int min10; // minimum distance to a face of priority 10 -shared int renderPris[THREAD_COUNT * FACES_PER_THREAD]; // priority for face draw order - -#include "comp_common.glsl" - -layout(local_size_x = THREAD_COUNT) in; - -#include "common.glsl" -#include "priority_render.glsl" - -void main() { - uint groupId = gl_WorkGroupID.x; - uint localId = gl_LocalInvocationID.x * FACES_PER_THREAD; - modelinfo minfo = ol[groupId]; - ivec4 pos = ivec4(minfo.x, minfo.y, minfo.z, 0); - - if (localId == 0) { - min10 = 6000; - for (int i = 0; i < 12; ++i) { - totalNum[i] = 0; - totalDistance[i] = 0; - } - for (int i = 0; i < 18; ++i) { - totalMappedNum[i] = 0; - } - } - - int prio[FACES_PER_THREAD]; - int dis[FACES_PER_THREAD]; - vert vA[FACES_PER_THREAD]; - vert vB[FACES_PER_THREAD]; - vert vC[FACES_PER_THREAD]; - - for (int i = 0; i < FACES_PER_THREAD; i++) { - get_face(localId + i, minfo, cameraYaw, cameraPitch, prio[i], dis[i], vA[i], vB[i], vC[i]); - } - - memoryBarrierShared(); - barrier(); - - for (int i = 0; i < FACES_PER_THREAD; i++) { - add_face_prio_distance(localId + i, minfo, vA[i], vB[i], vC[i], prio[i], dis[i], pos); - } - - memoryBarrierShared(); - barrier(); - - int prioAdj[FACES_PER_THREAD]; - int idx[FACES_PER_THREAD]; - for (int i = 0; i < FACES_PER_THREAD; i++) { - idx[i] = map_face_priority(localId + i, minfo, prio[i], dis[i], prioAdj[i]); - } - - memoryBarrierShared(); - barrier(); - - for (int i = 0; i < FACES_PER_THREAD; i++) { - insert_face(localId + i, minfo, prioAdj[i], dis[i], idx[i]); - } - - memoryBarrierShared(); - barrier(); - - for (int i = 0; i < FACES_PER_THREAD; i++) { - sort_and_insert(localId + i, minfo, prioAdj[i], dis[i], vA[i], vB[i], vC[i]); - } -} diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp_unordered.cl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp_unordered.cl deleted file mode 100644 index 738967216e9..00000000000 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp_unordered.cl +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2021, Adam - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "cl_types.cl" - -__kernel __attribute__((reqd_work_group_size(6, 1, 1))) void computeUnordered(__global const struct modelinfo *ol, __global const struct vert *vb, - __global const struct vert *tempvb, __global const float4 *texb, - __global const float4 *temptexb, __global struct vert *vout, - __global float4 *uvout) { - size_t groupId = get_group_id(0); - size_t localId = get_local_id(0); - struct modelinfo minfo = ol[groupId]; - - int offset = minfo.offset; - int size = minfo.size; - int outOffset = minfo.idx; - int toffset = minfo.toffset; - int flags = minfo.flags; - - if (localId >= size) { - return; - } - - uint ssboOffset = localId; - struct vert thisA, thisB, thisC; - - // Grab triangle vertices from the correct buffer - if (flags < 0) { - thisA = vb[offset + ssboOffset * 3]; - thisB = vb[offset + ssboOffset * 3 + 1]; - thisC = vb[offset + ssboOffset * 3 + 2]; - } else { - thisA = tempvb[offset + ssboOffset * 3]; - thisB = tempvb[offset + ssboOffset * 3 + 1]; - thisC = tempvb[offset + ssboOffset * 3 + 2]; - } - - uint myOffset = localId; - float3 pos = convert_float3((int3)(minfo.x, minfo.y, minfo.z)); - float4 texPos = (float4)(0, minfo.x, minfo.y, minfo.z); - - float3 vertA = (float3)(thisA.x, thisA.y, thisA.z) + pos; - float3 vertB = (float3)(thisB.x, thisB.y, thisB.z) + pos; - float3 vertC = (float3)(thisC.x, thisC.y, thisC.z) + pos; - - // position vertices in scene and write to out buffer - vout[outOffset + myOffset * 3] = (struct vert){vertA.x, vertA.y, vertA.z, thisA.ahsl}; - vout[outOffset + myOffset * 3 + 1] = (struct vert){vertB.x, vertB.y, vertB.z, thisB.ahsl}; - vout[outOffset + myOffset * 3 + 2] = (struct vert){vertC.x, vertC.y, vertC.z, thisC.ahsl}; - - if (toffset < 0) { - uvout[outOffset + myOffset * 3] = (float4)(0.0f, 0.0f, 0.0f, 0.0f); - uvout[outOffset + myOffset * 3 + 1] = (float4)(0.0f, 0.0f, 0.0f, 0.0f); - uvout[outOffset + myOffset * 3 + 2] = (float4)(0.0f, 0.0f, 0.0f, 0.0f); - } else if (flags >= 0) { - uvout[outOffset + myOffset * 3] = texPos + temptexb[toffset + localId * 3]; - uvout[outOffset + myOffset * 3 + 1] = texPos + temptexb[toffset + localId * 3 + 1]; - uvout[outOffset + myOffset * 3 + 2] = texPos + temptexb[toffset + localId * 3 + 2]; - } else { - uvout[outOffset + myOffset * 3] = texPos + texb[toffset + localId * 3]; - uvout[outOffset + myOffset * 3 + 1] = texPos + texb[toffset + localId * 3 + 1]; - uvout[outOffset + myOffset * 3 + 2] = texPos + texb[toffset + localId * 3 + 2]; - } -} diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp_unordered.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp_unordered.glsl deleted file mode 100644 index a4eb42d23ad..00000000000 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp_unordered.glsl +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2018, Adam - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include version_header - -#include "comp_common.glsl" - -layout(local_size_x = 6) in; - -void main() { - uint groupId = gl_WorkGroupID.x; - uint localId = gl_LocalInvocationID.x; - modelinfo minfo = ol[groupId]; - - int offset = minfo.offset; - int size = minfo.size; - int outOffset = minfo.idx; - int toffset = minfo.toffset; - int flags = minfo.flags; - - if (localId >= size) { - return; - } - - uint ssboOffset = localId; - vert thisA, thisB, thisC; - - // Grab triangle vertices from the correct buffer - if (flags < 0) { - thisA = vb[offset + ssboOffset * 3]; - thisB = vb[offset + ssboOffset * 3 + 1]; - thisC = vb[offset + ssboOffset * 3 + 2]; - } else { - thisA = tempvb[offset + ssboOffset * 3]; - thisB = tempvb[offset + ssboOffset * 3 + 1]; - thisC = tempvb[offset + ssboOffset * 3 + 2]; - } - - uint myOffset = localId; - vec3 pos = vec3(minfo.x, minfo.y, minfo.z); - ivec4 texPos = ivec4(0, pos); - - vec3 vertA = thisA.pos + pos; - vec3 vertB = thisB.pos + pos; - vec3 vertC = thisC.pos + pos; - - // position vertices in scene and write to out buffer - vout[outOffset + myOffset * 3] = vert(vertA, thisA.ahsl); - vout[outOffset + myOffset * 3 + 1] = vert(vertB, thisB.ahsl); - vout[outOffset + myOffset * 3 + 2] = vert(vertC, thisC.ahsl); - - if (toffset < 0) { - uvout[outOffset + myOffset * 3] = vec4(0); - uvout[outOffset + myOffset * 3 + 1] = vec4(0); - uvout[outOffset + myOffset * 3 + 2] = vec4(0); - } else if (flags >= 0) { - uvout[outOffset + myOffset * 3] = texPos + temptexb[toffset + localId * 3]; - uvout[outOffset + myOffset * 3 + 1] = texPos + temptexb[toffset + localId * 3 + 1]; - uvout[outOffset + myOffset * 3 + 2] = texPos + temptexb[toffset + localId * 3 + 2]; - } else { - uvout[outOffset + myOffset * 3] = texPos + texb[toffset + localId * 3]; - uvout[outOffset + myOffset * 3 + 1] = texPos + texb[toffset + localId * 3 + 1]; - uvout[outOffset + myOffset * 3 + 2] = texPos + texb[toffset + localId * 3 + 2]; - } -} diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/frag.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/frag.glsl index a8916f301fb..18c98a03e8e 100644 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/frag.glsl +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/frag.glsl @@ -25,13 +25,13 @@ #version 330 //#define FRAG_UVS -//#define ZBUF +//#define ZBUF_DEBUG + +#include colorblind_mode uniform sampler2DArray textures; uniform float brightness; -uniform float smoothBanding; uniform vec4 fogColor; -uniform int colorBlindMode; uniform float textureLightMode; in vec4 fColor; @@ -39,16 +39,19 @@ noperspective centroid in float fHsl; flat in int fTextureId; in vec2 fUv; in float fFogAmount; -#ifdef ZBUF +#ifdef ZBUF_DEBUG in float fDepth; #endif out vec4 FragColor; #include "hsl_to_rgb.glsl" + +#if COLORBLIND_MODE > 0 #include "colorblind.glsl" +#endif -#ifdef ZBUF +#ifdef ZBUF_DEBUG float linear_depth(float depth) { // depth is computed as 100/z, solve for z float z = 100 / depth; @@ -63,21 +66,26 @@ void main() { int textureIdx = fTextureId - 1; vec4 textureColor = texture(textures, vec3(fUv, float(textureIdx))); - vec4 textureColorBrightness = pow(textureColor, vec4(brightness, brightness, brightness, 1.0f)); + vec4 textureColor0 = textureLod(textures, vec3(fUv, float(textureIdx)), 0.f); + + if (textureColor0.a < 1.f) + discard; + + textureColor = vec4(textureColor.rgb, 1.f); + + textureColor = pow(textureColor, vec4(brightness, brightness, brightness, 1.f)); // textured triangles hsl is a 7 bit lightness 2-126 float light = fHsl / 127.f; vec3 mul = (1.f - textureLightMode) * vec3(light) + textureLightMode * fColor.rgb; - c = textureColorBrightness * vec4(mul, fColor.a); + c = textureColor * vec4(mul, fColor.a); } else { - // pick interpolated hsl or rgb depending on smooth banding setting - vec3 rgb = hslToRgb(int(fHsl)) * smoothBanding + fColor.rgb * (1.f - smoothBanding); - c = vec4(rgb, fColor.a); + c = fColor; } - if (colorBlindMode > 0) { - c.rgb = colorblind(colorBlindMode, c.rgb); - } +#if COLORBLIND_MODE > 0 + c.rgb = colorblind(c.rgb); +#endif vec3 mixedColor = mix(c.rgb, fogColor.rgb, fFogAmount); FragColor = vec4(mixedColor, c.a); @@ -88,7 +96,7 @@ void main() { } #endif -#ifdef ZBUF +#ifdef ZBUF_DEBUG float dc = linear_depth(fDepth); if (dc > 1.0) { FragColor = vec4(1, 0, 0, 1); diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/fragui.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/fragui.glsl index 449dd16c2cb..b4611875e24 100644 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/fragui.glsl +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/fragui.glsl @@ -24,24 +24,36 @@ */ #version 330 -#define SAMPLING_MITCHELL 1 -#define SAMPLING_CATROM 2 -#define SAMPLING_XBR 3 +#include sampling_mode +#include colorblind_mode + +#define SAMPLING_NEAREST 0 +#define SAMPLING_LINEAR 1 +#define SAMPLING_MITCHELL 2 +#define SAMPLING_CATROM 3 +#define SAMPLING_XBR 4 +#define SAMPLING_HYBRID 5 uniform sampler2D tex; -uniform int samplingMode; uniform ivec2 sourceDimensions; uniform ivec2 targetDimensions; -uniform int colorBlindMode; uniform vec4 alphaOverlay; -#include "scale/bicubic.glsl" -#include "scale/xbr_lv2_frag.glsl" +#if COLORBLIND_MODE > 0 #include "colorblind.glsl" +#endif in vec2 TexCoord; +#if SAMPLING_MODE == SAMPLING_MITCHELL || SAMPLING_MODE == SAMPLING_CATROM +#include "scale/bicubic.glsl" +#elif SAMPLING_MODE == SAMPLING_XBR +#include "scale/xbr_lv2_frag.glsl" + in XBRTable xbrTable; +#elif SAMPLING_MODE == SAMPLING_HYBRID +#include "scale/hybrid.glsl" +#endif out vec4 FragColor; @@ -52,16 +64,21 @@ vec4 alphaBlend(vec4 src, vec4 dst) { void main() { vec4 c; - if (samplingMode == SAMPLING_CATROM || samplingMode == SAMPLING_MITCHELL) { - c = textureCubic(tex, TexCoord, samplingMode); - } else if (samplingMode == SAMPLING_XBR) { - c = textureXBR(tex, TexCoord, xbrTable, ceil(1.0 * targetDimensions.x / sourceDimensions.x)); - } else { // NEAREST or LINEAR, which uses GL_TEXTURE_MIN_FILTER/GL_TEXTURE_MAG_FILTER to affect sampling - c = texture(tex, TexCoord); - } +#if SAMPLING_MODE == SAMPLING_MITCHELL || SAMPLING_MODE == SAMPLING_CATROM + c = textureCubic(tex, TexCoord); +#elif SAMPLING_MODE == SAMPLING_XBR + c = textureXBR(tex, TexCoord, xbrTable, ceil(1.0 * targetDimensions.x / sourceDimensions.x)); +#elif SAMPLING_MODE == SAMPLING_HYBRID + c = textureHybrid(tex, TexCoord); +#else + // NEAREST or LINEAR, which uses GL_TEXTURE_MIN_FILTER/GL_TEXTURE_MAG_FILTER to affect sampling + c = texture(tex, TexCoord); +#endif c = alphaBlend(c, alphaOverlay); - c.rgb = colorblind(colorBlindMode, c.rgb); +#if COLORBLIND_MODE > 0 + c.rgb = colorblind(c.rgb); +#endif FragColor = c; } diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/geom.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/geom.glsl index 8c72591a4c3..6846c6f097b 100644 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/geom.glsl +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/geom.glsl @@ -27,7 +27,8 @@ #include texture_config -//#define ZBUF +//#define ZBUF_DEBUG +//#define BIAS_DEBUG // smallest unit of the texture which can be moved per tick. textures are all // 128x128px - so this is equivalent to +1px @@ -39,9 +40,6 @@ layout(triangle_strip, max_vertices = 3) out; layout(std140) uniform uniforms { float cameraYaw; float cameraPitch; - int centerX; - int centerY; - int zoom; float cameraX; float cameraY; float cameraZ; @@ -51,21 +49,22 @@ layout(std140) uniform uniforms { uniform vec2 textureAnimations[TEXTURE_COUNT]; uniform int tick; -uniform mat4 projectionMatrix; +uniform mat4 worldProj; -in vec3 gVertex[3]; +in vec4 gVertex[3]; in vec4 gColor[3]; in float gHsl[3]; in int gTextureId[3]; -in vec3 gTexPos[3]; +in vec4 gTexPos[3]; in float gFogAmount[3]; +in int gBias[3]; out vec4 fColor; noperspective centroid out float fHsl; flat out int fTextureId; out vec2 fUv; out float fFogAmount; -#ifdef ZBUF +#ifdef ZBUF_DEBUG out float fDepth; #endif @@ -75,7 +74,7 @@ void main() { if (textureId > 0) { vec3 cameraPos = vec3(cameraX, cameraY, cameraZ); - compute_uv(cameraPos, gVertex[0], gVertex[1], gVertex[2], gTexPos[0], gTexPos[1], gTexPos[2], uv[0], uv[1], uv[2]); + compute_uv(cameraPos, gVertex[0].xyz, gVertex[1].xyz, gVertex[2].xyz, gTexPos[0].xyz, gTexPos[1].xyz, gTexPos[2].xyz, uv[0], uv[1], uv[2]); vec2 textureAnim = textureAnimations[min(textureId - 1, TEXTURE_COUNT - 1)]; for (int i = 0; i < 3; ++i) { @@ -88,16 +87,21 @@ void main() { } for (int i = 0; i < 3; ++i) { +#ifdef BIAS_DEBUG + fColor = vec4(clamp(gBias[i], 0, 12) / 12.0, 0.0, 0.0, 1.0); +#else fColor = gColor[i]; +#endif fHsl = gHsl[i]; fTextureId = gTextureId[i]; fUv = uv[i]; fFogAmount = gFogAmount[i]; - vec4 pos = projectionMatrix * vec4(gVertex[i], 1); -#ifdef ZBUF + vec4 pos = worldProj * gVertex[i]; +#ifdef ZBUF_DEBUG fDepth = pos.z / pos.w; #endif + pos.z += gBias[i] / 128.0; gl_Position = pos; EmitVertex(); diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/hsl_to_rgb.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/hsl_to_rgb.glsl index 218974247df..8d7d0bd2c15 100644 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/hsl_to_rgb.glsl +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/hsl_to_rgb.glsl @@ -23,69 +23,63 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -vec3 hslToRgb(int hsl) { - int var5 = hsl / 128; - float var6 = float(var5 >> 3) / 64.0f + 0.0078125f; - float var8 = float(var5 & 7) / 8.0f + 0.0625f; +vec3 hslToRgb(vec3 hsl) { + float hue = hsl.x / 64.0f + 0.0078125f; + float sat = hsl.y / 8.0f + 0.0625f; + float lum = hsl.z; - int var10 = hsl % 128; + float var11 = lum / 128.0f; + float r = var11; + float g = var11; + float b = var11; - float var11 = float(var10) / 128.0f; - float var13 = var11; - float var15 = var11; - float var17 = var11; - - if (var8 != 0.0f) { - float var19; - if (var11 < 0.5f) { - var19 = var11 * (1.0f + var8); - } else { - var19 = var11 + var8 - var11 * var8; - } - - float var21 = 2.0f * var11 - var19; - float var23 = var6 + 0.3333333333333333f; - if (var23 > 1.0f) { - var23 -= 1.f; - } + float var19; + if (var11 < 0.5f) { + var19 = var11 * (1.0f + sat); + } else { + var19 = var11 + sat - var11 * sat; + } - float var27 = var6 - 0.3333333333333333f; - if (var27 < 0.0f) { - var27 += 1.f; - } + float var21 = 2.0f * var11 - var19; + float var23 = hue + 0.3333333333333333f; + if (var23 > 1.0f) { + var23 -= 1.f; + } - if (6.0f * var23 < 1.0f) { - var13 = var21 + (var19 - var21) * 6.0f * var23; - } else if (2.0f * var23 < 1.0f) { - var13 = var19; - } else if (3.0f * var23 < 2.0f) { - var13 = var21 + (var19 - var21) * (0.6666666666666666f - var23) * 6.0f; - } else { - var13 = var21; - } + float var27 = hue - 0.3333333333333333f; + if (var27 < 0.0f) { + var27 += 1.f; + } - if (6.0f * var6 < 1.0f) { - var15 = var21 + (var19 - var21) * 6.0f * var6; - } else if (2.0f * var6 < 1.0f) { - var15 = var19; - } else if (3.0f * var6 < 2.0f) { - var15 = var21 + (var19 - var21) * (0.6666666666666666f - var6) * 6.0f; - } else { - var15 = var21; - } + if (6.0f * var23 < 1.0f) { + r = var21 + (var19 - var21) * 6.0f * var23; + } else if (2.0f * var23 < 1.0f) { + r = var19; + } else if (3.0f * var23 < 2.0f) { + r = var21 + (var19 - var21) * (0.6666666666666666f - var23) * 6.0f; + } else { + r = var21; + } - if (6.0f * var27 < 1.0f) { - var17 = var21 + (var19 - var21) * 6.0f * var27; - } else if (2.0f * var27 < 1.0f) { - var17 = var19; - } else if (3.0f * var27 < 2.0f) { - var17 = var21 + (var19 - var21) * (0.6666666666666666f - var27) * 6.0f; - } else { - var17 = var21; - } + if (6.0f * hue < 1.0f) { + g = var21 + (var19 - var21) * 6.0f * hue; + } else if (2.0f * hue < 1.0f) { + g = var19; + } else if (3.0f * hue < 2.0f) { + g = var21 + (var19 - var21) * (0.6666666666666666f - hue) * 6.0f; + } else { + g = var21; } - vec3 rgb = vec3(pow(var13, brightness), pow(var15, brightness), pow(var17, brightness)); + if (6.0f * var27 < 1.0f) { + b = var21 + (var19 - var21) * 6.0f * var27; + } else if (2.0f * var27 < 1.0f) { + b = var19; + } else if (3.0f * var27 < 2.0f) { + b = var21 + (var19 - var21) * (0.6666666666666666f - var27) * 6.0f; + } else { + b = var21; + } - return rgb; + return vec3(pow(r, brightness), pow(g, brightness), pow(b, brightness)); } \ No newline at end of file diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/priority_render.cl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/priority_render.cl deleted file mode 100644 index 7aa764af880..00000000000 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/priority_render.cl +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright (c) 2021, Adam - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -// Calculate adjusted priority for a face with a given priority, distance, and -// model global min10 and face distance averages. This allows positioning faces -// with priorities 10/11 into the correct 'slots' resulting in 18 possible -// adjusted priorities -int priority_map(int p, int distance, int _min10, int avg1, int avg2, int avg3) { - // (10, 11) 0 1 2 (10, 11) 3 4 (10, 11) 5 6 7 8 9 (10, 11) - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 - switch (p) { - case 0: - return 2; - case 1: - return 3; - case 2: - return 4; - case 3: - return 7; - case 4: - return 8; - case 5: - return 11; - case 6: - return 12; - case 7: - return 13; - case 8: - return 14; - case 9: - return 15; - case 10: - if (distance > avg1) { - return 0; - } else if (distance > avg2) { - return 5; - } else if (distance > avg3) { - return 9; - } else { - return 16; - } - case 11: - if (distance > avg1 && _min10 > avg1) { - return 1; - } else if (distance > avg2 && (_min10 > avg1 || _min10 > avg2)) { - return 6; - } else if (distance > avg3 && (_min10 > avg1 || _min10 > avg2 || _min10 > avg3)) { - return 10; - } else { - return 17; - } - default: - // this can't happen unless an invalid priority is sent. just assume 0. - return 0; - } -} - -// calculate the number of faces with a lower adjusted priority than -// the given adjusted priority -int count_prio_offset(__local struct shared_data *shared, int priority) { - // this shouldn't ever be outside of (0, 17) because it is the return value from priority_map - priority = clamp(priority, 0, 17); - int total = 0; - for (int i = 0; i < priority; i++) { - total += shared->totalMappedNum[i]; - } - return total; -} - -void get_face(__local struct shared_data *shared, __global const struct vert *vb, __global const struct vert *tempvb, uint localId, struct modelinfo minfo, - float cameraYaw, float cameraPitch, - /* out */ int *prio, int *dis, struct vert *o1, struct vert *o2, struct vert *o3) { - int size = minfo.size; - int offset = minfo.offset; - int flags = minfo.flags; - uint ssboOffset; - - if (localId < size) { - ssboOffset = localId; - } else { - ssboOffset = 0; - } - - struct vert thisA; - struct vert thisB; - struct vert thisC; - - // Grab triangle vertices from the correct buffer - if (flags < 0) { - thisA = vb[offset + ssboOffset * 3]; - thisB = vb[offset + ssboOffset * 3 + 1]; - thisC = vb[offset + ssboOffset * 3 + 2]; - } else { - thisA = tempvb[offset + ssboOffset * 3]; - thisB = tempvb[offset + ssboOffset * 3 + 1]; - thisC = tempvb[offset + ssboOffset * 3 + 2]; - } - - if (localId < size) { - int orientation = flags & 0x7ff; - - // rotate for model orientation - float4 thisrvA = rotate_vertex((float4)(thisA.x, thisA.y, thisA.z, 0), orientation); - float4 thisrvB = rotate_vertex((float4)(thisB.x, thisB.y, thisB.z, 0), orientation); - float4 thisrvC = rotate_vertex((float4)(thisC.x, thisC.y, thisC.z, 0), orientation); - - // calculate distance to face - int thisPriority = (thisA.ahsl >> 16) & 0xff; // all vertices on the face have the same priority - int thisDistance = face_distance(thisrvA, thisrvB, thisrvC, cameraYaw, cameraPitch); - - *o1 = (struct vert){thisrvA.x, thisrvA.y, thisrvA.z, thisA.ahsl}; - *o2 = (struct vert){thisrvB.x, thisrvB.y, thisrvB.z, thisB.ahsl}; - *o3 = (struct vert){thisrvC.x, thisrvC.y, thisrvC.z, thisC.ahsl}; - - *prio = thisPriority; - *dis = thisDistance; - } else { - *o1 = (struct vert){0, 0, 0, 0}; - *o2 = (struct vert){0, 0, 0, 0}; - *o3 = (struct vert){0, 0, 0, 0}; - *prio = 0; - *dis = 0; - } -} - -void add_face_prio_distance(__local struct shared_data *shared, __constant struct uniform *uni, uint localId, struct modelinfo minfo, struct vert thisrvA, - struct vert thisrvB, struct vert thisrvC, int thisPriority, int thisDistance, int4 pos) { - if (localId < minfo.size) { - // if the face is not culled, it is calculated into priority distance averages - float3 posA = (float3)(thisrvA.x, thisrvA.y, thisrvA.z); - float3 posB = (float3)(thisrvB.x, thisrvB.y, thisrvB.z); - float3 posC = (float3)(thisrvC.x, thisrvC.y, thisrvC.z); - if (face_visible(uni, posA, posB, posC, pos)) { - atomic_add(&shared->totalNum[thisPriority], 1); - atomic_add(&shared->totalDistance[thisPriority], thisDistance); - - // calculate minimum distance to any face of priority 10 for positioning the 11 faces later - if (thisPriority == 10) { - atomic_min(&shared->min10, thisDistance); - } - } - } -} - -int map_face_priority(__local struct shared_data *shared, uint localId, struct modelinfo minfo, int thisPriority, int thisDistance, int *prio) { - int size = minfo.size; - - // Compute average distances for 0/2, 3/4, and 6/8 - - if (localId < size) { - int avg1 = -6000; - int avg2 = -6000; - int avg3 = -6000; - - if (shared->totalNum[1] > 0 || shared->totalNum[2] > 0) { - avg1 = (shared->totalDistance[1] + shared->totalDistance[2]) / (shared->totalNum[1] + shared->totalNum[2]); - } - - if (shared->totalNum[3] > 0 || shared->totalNum[4] > 0) { - avg2 = (shared->totalDistance[3] + shared->totalDistance[4]) / (shared->totalNum[3] + shared->totalNum[4]); - } - - if (shared->totalNum[6] > 0 || shared->totalNum[8] > 0) { - avg3 = (shared->totalDistance[6] + shared->totalDistance[8]) / (shared->totalNum[6] + shared->totalNum[8]); - } - - int adjPrio = priority_map(thisPriority, thisDistance, shared->min10, avg1, avg2, avg3); - int prioIdx = atomic_add(&shared->totalMappedNum[adjPrio], 1); - - *prio = adjPrio; - return prioIdx; - } - - *prio = 0; - return 0; -} - -void insert_face(__local struct shared_data *shared, uint localId, struct modelinfo minfo, int adjPrio, int distance, int prioIdx) { - int size = minfo.size; - - if (localId < size) { - // calculate base offset into renderPris based on number of faces with a lower priority - int baseOff = count_prio_offset(shared, adjPrio); - // the furthest faces draw first, and have the highest priority. - // if two faces have the same distance, the one with the - // lower id draws first. - shared->renderPris[baseOff + prioIdx] = distance << 16 | (int)(~localId & 0xffffu); - } -} - -int tile_height(read_only image3d_t tileHeightImage, int z, int x, int y) { -#define ESCENE_OFFSET 40 // (184-104)/2 - const sampler_t tileHeightSampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE; - int4 coord = (int4)(x + ESCENE_OFFSET, y + ESCENE_OFFSET, z, 0); - return read_imagei(tileHeightImage, tileHeightSampler, coord).x << 3; -} - -float4 hillskew_vertexf(read_only image3d_t tileHeightImage, float4 v, int hillskew, int y, int plane) { - if (hillskew == 1) { - float fx = v.x / 128; - float fz = v.z / 128; - int sx = (int)(floor(fx)); - int sz = (int)(floor(fz)); - float it; - float h1 = mix((float)tile_height(tileHeightImage, plane, sx, sz), (float)tile_height(tileHeightImage, plane, sx + 1, sz), fract(fx, &it)); - float h2 = mix((float)tile_height(tileHeightImage, plane, sx, sz + 1), (float)tile_height(tileHeightImage, plane, sx + 1, sz + 1), fract(fx, &it)); - float h3 = mix(h1, h2, fract(fz, &it)); - return (float4)(v.x, v.y + h3 - y, v.z, v.w); - } else { - return v; - } -} - -void sort_and_insert(__local struct shared_data *shared, __constant struct uniform *uni, __global const float4 *texb, __global const float4 *temptexb, - __global struct vert *vout, __global float4 *uvout, uint localId, struct modelinfo minfo, int thisPriority, int thisDistance, - struct vert thisrvA, struct vert thisrvB, struct vert thisrvC, read_only image3d_t tileHeightImage) { - int size = minfo.size; - - if (localId < size) { - int outOffset = minfo.idx; - int toffset = minfo.toffset; - int flags = minfo.flags; - - // we only have to order faces against others of the same priority - const int priorityOffset = count_prio_offset(shared, thisPriority); - const int numOfPriority = shared->totalMappedNum[thisPriority]; - const int start = priorityOffset; // index of first face with this priority - const int end = priorityOffset + numOfPriority; // index of last face with this priority - const int renderPriority = thisDistance << 16 | (int)(~localId & 0xffffu); - int myOffset = priorityOffset; - - // calculate position this face will be in - for (int i = start; i < end; ++i) { - if (renderPriority < shared->renderPris[i]) { - ++myOffset; - } - } - - // position into scene - float4 pos = (float4)(minfo.x, minfo.y, minfo.z, 0); - float4 vertA = (float4)(thisrvA.x, thisrvA.y, thisrvA.z, 0) + pos; - float4 vertB = (float4)(thisrvB.x, thisrvB.y, thisrvB.z, 0) + pos; - float4 vertC = (float4)(thisrvC.x, thisrvC.y, thisrvC.z, 0) + pos; - - // apply hillskew - int plane = (flags >> 24) & 3; - int hillskew = (flags >> 26) & 1; - vertA = hillskew_vertexf(tileHeightImage, vertA, hillskew, minfo.y, plane); - vertB = hillskew_vertexf(tileHeightImage, vertB, hillskew, minfo.y, plane); - vertC = hillskew_vertexf(tileHeightImage, vertC, hillskew, minfo.y, plane); - - // write to out buffer - vout[outOffset + myOffset * 3] = (struct vert){vertA.x, vertA.y, vertA.z, thisrvA.ahsl}; - vout[outOffset + myOffset * 3 + 1] = (struct vert){vertB.x, vertB.y, vertB.z, thisrvB.ahsl}; - vout[outOffset + myOffset * 3 + 2] = (struct vert){vertC.x, vertC.y, vertC.z, thisrvC.ahsl}; - - if (toffset < 0) { - uvout[outOffset + myOffset * 3] = (float4)(0, 0, 0, 0); - uvout[outOffset + myOffset * 3 + 1] = (float4)(0, 0, 0, 0); - uvout[outOffset + myOffset * 3 + 2] = (float4)(0, 0, 0, 0); - } else { - float4 texA, texB, texC; - - if (flags >= 0) { - texA = temptexb[toffset + localId * 3]; - texB = temptexb[toffset + localId * 3 + 1]; - texC = temptexb[toffset + localId * 3 + 2]; - } else { - texA = texb[toffset + localId * 3]; - texB = texb[toffset + localId * 3 + 1]; - texC = texb[toffset + localId * 3 + 2]; - } - - int orientation = flags & 0x7ff; - // swizzle from (tex,x,y,z) to (x,y,z,tex) for rotate and hillskew - texA = texA.yzwx; - texB = texB.yzwx; - texC = texC.yzwx; - // rotate - texA = rotate_vertex(texA, orientation); - texB = rotate_vertex(texB, orientation); - texC = rotate_vertex(texC, orientation); - // position - texA += pos; - texB += pos; - texC += pos; - // hillskew - texA = hillskew_vertexf(tileHeightImage, texA, hillskew, minfo.y, plane); - texB = hillskew_vertexf(tileHeightImage, texB, hillskew, minfo.y, plane); - texC = hillskew_vertexf(tileHeightImage, texC, hillskew, minfo.y, plane); - uvout[outOffset + myOffset * 3] = texA.wxyz; - uvout[outOffset + myOffset * 3 + 1] = texB.wxyz; - uvout[outOffset + myOffset * 3 + 2] = texC.wxyz; - } - } -} diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/priority_render.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/priority_render.glsl deleted file mode 100644 index e8767766767..00000000000 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/priority_render.glsl +++ /dev/null @@ -1,320 +0,0 @@ -/* - * Copyright (c) 2018, Adam - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -layout(binding = 2) uniform isampler3D tileHeightSampler; - -// Calculate adjusted priority for a face with a given priority, distance, and -// model global min10 and face distance averages. This allows positioning faces -// with priorities 10/11 into the correct 'slots' resulting in 18 possible -// adjusted priorities -int priority_map(int p, int distance, int _min10, int avg1, int avg2, int avg3) { - // (10, 11) 0 1 2 (10, 11) 3 4 (10, 11) 5 6 7 8 9 (10, 11) - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 - switch (p) { - case 0: - return 2; - case 1: - return 3; - case 2: - return 4; - case 3: - return 7; - case 4: - return 8; - case 5: - return 11; - case 6: - return 12; - case 7: - return 13; - case 8: - return 14; - case 9: - return 15; - case 10: - if (distance > avg1) { - return 0; - } else if (distance > avg2) { - return 5; - } else if (distance > avg3) { - return 9; - } else { - return 16; - } - case 11: - if (distance > avg1 && _min10 > avg1) { - return 1; - } else if (distance > avg2 && (_min10 > avg1 || _min10 > avg2)) { - return 6; - } else if (distance > avg3 && (_min10 > avg1 || _min10 > avg2 || _min10 > avg3)) { - return 10; - } else { - return 17; - } - default: - // this can't happen unless an invalid priority is sent. just assume 0. - return 0; - } -} - -// calculate the number of faces with a lower adjusted priority than -// the given adjusted priority -int count_prio_offset(int priority) { - // this shouldn't ever be outside of (0, 17) because it is the return value from priority_map - priority = clamp(priority, 0, 17); - int total = 0; - for (int i = 0; i < priority; i++) { - total += totalMappedNum[i]; - } - return total; -} - -void get_face(uint localId, modelinfo minfo, float cameraYaw, float cameraPitch, out int prio, out int dis, out vert o1, out vert o2, out vert o3) { - int size = minfo.size; - int offset = minfo.offset; - int flags = minfo.flags; - uint ssboOffset; - - if (localId < size) { - ssboOffset = localId; - } else { - ssboOffset = 0; - } - - vert thisA; - vert thisB; - vert thisC; - - // Grab triangle vertices from the correct buffer - if (flags < 0) { - thisA = vb[offset + ssboOffset * 3]; - thisB = vb[offset + ssboOffset * 3 + 1]; - thisC = vb[offset + ssboOffset * 3 + 2]; - } else { - thisA = tempvb[offset + ssboOffset * 3]; - thisB = tempvb[offset + ssboOffset * 3 + 1]; - thisC = tempvb[offset + ssboOffset * 3 + 2]; - } - - if (localId < size) { - int orientation = flags & 0x7ff; - - // rotate for model orientation - vec4 thisrvA = rotate_vertex(vec4(thisA.pos, 0), orientation); - vec4 thisrvB = rotate_vertex(vec4(thisB.pos, 0), orientation); - vec4 thisrvC = rotate_vertex(vec4(thisC.pos, 0), orientation); - - // calculate distance to face - int thisPriority = (thisA.ahsl >> 16) & 0xff; // all vertices on the face have the same priority - int thisDistance = face_distance(thisrvA, thisrvB, thisrvC, cameraYaw, cameraPitch); - - o1.pos = thisrvA.xyz; - o1.ahsl = thisA.ahsl; - - o2.pos = thisrvB.xyz; - o2.ahsl = thisB.ahsl; - - o3.pos = thisrvC.xyz; - o3.ahsl = thisC.ahsl; - - prio = thisPriority; - dis = thisDistance; - } else { - o1.pos = vec3(0); - o1.ahsl = 0; - - o2.pos = vec3(0); - o2.ahsl = 0; - - o3.pos = vec3(0); - o3.ahsl = 0; - - prio = 0; - dis = 0; - } -} - -void add_face_prio_distance(uint localId, modelinfo minfo, vert thisrvA, vert thisrvB, vert thisrvC, int thisPriority, int thisDistance, ivec4 pos) { - if (localId < minfo.size) { - // if the face is not culled, it is calculated into priority distance averages - if (face_visible(thisrvA.pos, thisrvB.pos, thisrvC.pos, pos)) { - atomicAdd(totalNum[thisPriority], 1); - atomicAdd(totalDistance[thisPriority], thisDistance); - - // calculate minimum distance to any face of priority 10 for positioning the 11 faces later - if (thisPriority == 10) { - atomicMin(min10, thisDistance); - } - } - } -} - -int map_face_priority(uint localId, modelinfo minfo, int thisPriority, int thisDistance, out int prio) { - int size = minfo.size; - - // Compute average distances for 0/2, 3/4, and 6/8 - - if (localId < size) { - int avg1 = -6000; - int avg2 = -6000; - int avg3 = -6000; - - if (totalNum[1] > 0 || totalNum[2] > 0) { - avg1 = (totalDistance[1] + totalDistance[2]) / (totalNum[1] + totalNum[2]); - } - - if (totalNum[3] > 0 || totalNum[4] > 0) { - avg2 = (totalDistance[3] + totalDistance[4]) / (totalNum[3] + totalNum[4]); - } - - if (totalNum[6] > 0 || totalNum[8] > 0) { - avg3 = (totalDistance[6] + totalDistance[8]) / (totalNum[6] + totalNum[8]); - } - - int adjPrio = priority_map(thisPriority, thisDistance, min10, avg1, avg2, avg3); - int prioIdx = atomicAdd(totalMappedNum[adjPrio], 1); - - prio = adjPrio; - return prioIdx; - } - - prio = 0; - return 0; -} - -void insert_face(uint localId, modelinfo minfo, int adjPrio, int distance, int prioIdx) { - int size = minfo.size; - - if (localId < size) { - // calculate base offset into renderPris based on number of faces with a lower priority - int baseOff = count_prio_offset(adjPrio); - // the furthest faces draw first, and have the highest priority. - // if two faces have the same distance, the one with the - // lower id draws first. - renderPris[baseOff + prioIdx] = distance << 16 | int(~localId & 0xffffu); - } -} - -int tile_height(int z, int x, int y) { -#define ESCENE_OFFSET 40 // (184-104)/2 - return texelFetch(tileHeightSampler, ivec3(x + ESCENE_OFFSET, y + ESCENE_OFFSET, z), 0).r << 3; -} - -vec4 hillskew_vertexf(vec4 v, int hillskew, int y, int plane) { - if (hillskew == 1) { - float fx = v.x / 128; - float fz = v.z / 128; - int sx = int(floor(fx)); - int sz = int(floor(fz)); - float h1 = mix(tile_height(plane, sx, sz), tile_height(plane, sx + 1, sz), fract(fx)); - float h2 = mix(tile_height(plane, sx, sz + 1), tile_height(plane, sx + 1, sz + 1), fract(fx)); - float h3 = mix(h1, h2, fract(fz)); - return vec4(v.x, v.y + h3 - y, v.z, v.w); - } else { - return v; - } -} - -void sort_and_insert(uint localId, modelinfo minfo, int thisPriority, int thisDistance, vert thisrvA, vert thisrvB, vert thisrvC) { - int size = minfo.size; - - if (localId < size) { - int outOffset = minfo.idx; - int toffset = minfo.toffset; - int flags = minfo.flags; - - // we only have to order faces against others of the same priority - const int priorityOffset = count_prio_offset(thisPriority); - const int numOfPriority = totalMappedNum[thisPriority]; - const int start = priorityOffset; // index of first face with this priority - const int end = priorityOffset + numOfPriority; // index of last face with this priority - const int renderPriority = thisDistance << 16 | int(~localId & 0xffffu); - int myOffset = priorityOffset; - - // calculate position this face will be in - for (int i = start; i < end; ++i) { - if (renderPriority < renderPris[i]) { - ++myOffset; - } - } - - // position into scene - vec4 pos = vec4(minfo.x, minfo.y, minfo.z, 0); - vec4 vertA = vec4(thisrvA.pos, 0) + pos; - vec4 vertB = vec4(thisrvB.pos, 0) + pos; - vec4 vertC = vec4(thisrvC.pos, 0) + pos; - - // apply hillskew - int plane = (flags >> 24) & 3; - int hillskew = (flags >> 26) & 1; - vertA = hillskew_vertexf(vertA, hillskew, minfo.y, plane); - vertB = hillskew_vertexf(vertB, hillskew, minfo.y, plane); - vertC = hillskew_vertexf(vertC, hillskew, minfo.y, plane); - - // write to out buffer - vout[outOffset + myOffset * 3] = vert(vertA.xyz, thisrvA.ahsl); - vout[outOffset + myOffset * 3 + 1] = vert(vertB.xyz, thisrvB.ahsl); - vout[outOffset + myOffset * 3 + 2] = vert(vertC.xyz, thisrvC.ahsl); - - if (toffset < 0) { - uvout[outOffset + myOffset * 3] = vec4(0); - uvout[outOffset + myOffset * 3 + 1] = vec4(0); - uvout[outOffset + myOffset * 3 + 2] = vec4(0); - } else { - vec4 texA, texB, texC; - - if (flags >= 0) { - texA = temptexb[toffset + localId * 3]; - texB = temptexb[toffset + localId * 3 + 1]; - texC = temptexb[toffset + localId * 3 + 2]; - } else { - texA = texb[toffset + localId * 3]; - texB = texb[toffset + localId * 3 + 1]; - texC = texb[toffset + localId * 3 + 2]; - } - - int orientation = flags & 0x7ff; - // swizzle from (tex,x,y,z) to (x,y,z,tex) for rotate and hillskew - texA = texA.yzwx; - texB = texB.yzwx; - texC = texC.yzwx; - // rotate - texA = rotate_vertex(texA, orientation); - texB = rotate_vertex(texB, orientation); - texC = rotate_vertex(texC, orientation); - // position - texA += pos; - texB += pos; - texC += pos; - // hillskew - texA = hillskew_vertexf(texA, hillskew, minfo.y, plane); - texB = hillskew_vertexf(texB, hillskew, minfo.y, plane); - texC = hillskew_vertexf(texC, hillskew, minfo.y, plane); - uvout[outOffset + myOffset * 3] = texA.wxyz; - uvout[outOffset + myOffset * 3 + 1] = texB.wxyz; - uvout[outOffset + myOffset * 3 + 2] = texC.wxyz; - } - } -} diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/scale/bicubic.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/scale/bicubic.glsl index 9027c6e0f53..9fcfaf09afa 100644 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/scale/bicubic.glsl +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/scale/bicubic.glsl @@ -89,7 +89,7 @@ float d(vec2 pt1, vec2 pt2) { } // Samples a texture using a 4x4 kernel. -vec4 textureCubic(sampler2D sampler, vec2 texCoords, int mode) { +vec4 textureCubic(sampler2D sampler, vec2 texCoords) { vec2 texSize = textureSize(sampler, 0); vec2 texelSize = 1.0 / texSize; vec2 texelFCoords = texCoords * texSize; @@ -103,53 +103,55 @@ vec4 textureCubic(sampler2D sampler, vec2 texCoords, int mode) { vec4 c; - if (mode == SAMPLING_CATROM) { - // catrom benefits from anti-ringing, which requires knowledge of the minimum and maximum samples in the kernel - vec4 min_sample = vec4(FLT_MAX); - vec4 max_sample = vec4(FLT_MIN); - for (int m = -1; m <= 2; m++) { - for (int n = -1; n <= 2; n++) { - // this would use texelFetch, but that would require manual implementation of texture wrapping - vec4 vecData = texture(sampler, texCoords + vec2(m, n) * texelSize); - - // update min and max as we go - min_sample = min(min_sample, vecData); - max_sample = max(max_sample, vecData); - - // calculate weight based on distance of the current texel offset from the sub-texel position of the sampling location - float w = catmull_rom(d(vec2(m, n), coordFract)); - - // build the weighted average - nSum += vecData * w; - nDenom += w; - } +#if SAMPLING_MODE == SAMPLING_CATROM + // catrom benefits from anti-ringing, which requires knowledge of the minimum and maximum samples in the kernel + vec4 min_sample = vec4(FLT_MAX); + vec4 max_sample = vec4(FLT_MIN); + for (int m = -1; m <= 2; m++) { + for (int n = -1; n <= 2; n++) { + // this would use texelFetch, but that would require manual implementation of texture wrapping + vec4 vecData = texture(sampler, texCoords + vec2(m, n) * texelSize); + + // update min and max as we go + min_sample = min(min_sample, vecData); + max_sample = max(max_sample, vecData); + + // calculate weight based on distance of the current texel offset from the sub-texel position of the sampling location + float w = catmull_rom(d(vec2(m, n), coordFract)); + + // build the weighted average + nSum += vecData * w; + nDenom += w; } - // calculate weighted average - c = nSum / nDenom; - - // store value before anti-ringing - vec4 aux = c; - // anti-ringing: clamp the color value so that it cannot exceed values already present in the kernel area - c = clamp(c, min_sample, max_sample); - // mix according to anti-ringing strength - c = mix(aux, c, CR_AR_STRENGTH); - } else if (mode == SAMPLING_MITCHELL) { - for (int m = -1; m <= 2; m++) { - for (int n = -1; n <= 2; n++) { - // this would use texelFetch, but that would require manual implementation of texture wrapping - vec4 vecData = texture(sampler, texCoords + vec2(m, n) * texelSize); - - // calculate weight based on distance of the current texel offset from the sub-texel position of the sampling location - float w = mitchell(d(vec2(m, n), coordFract)); - - // build the weighted average - nSum += vecData * w; - nDenom += w; - } + } + // calculate weighted average + c = nSum / nDenom; + + // store value before anti-ringing + vec4 aux = c; + // anti-ringing: clamp the color value so that it cannot exceed values already present in the kernel area + c = clamp(c, min_sample, max_sample); + // mix according to anti-ringing strength + c = mix(aux, c, CR_AR_STRENGTH); +#elif SAMPLING_MODE == SAMPLING_MITCHELL + for (int m = -1; m <= 2; m++) { + for (int n = -1; n <= 2; n++) { + // this would use texelFetch, but that would require manual implementation of texture wrapping + vec4 vecData = texture(sampler, texCoords + vec2(m, n) * texelSize); + + // calculate weight based on distance of the current texel offset from the sub-texel position of the sampling location + float w = mitchell(d(vec2(m, n), coordFract)); + + // build the weighted average + nSum += vecData * w; + nDenom += w; } - // calculate weighted average - c = nSum / nDenom; } + // calculate weighted average + c = nSum / nDenom; +#else +#error 1 +#endif // return the weighted average return c; diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/scale/hybrid.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/scale/hybrid.glsl new file mode 100644 index 00000000000..4f45d609914 --- /dev/null +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/scale/hybrid.glsl @@ -0,0 +1,17 @@ +/* + * Written in 2025 by Casey Zwicker + * To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights + * to this software to the public domain worldwide. This software is distributed without any warranty. + * You should have received a copy of the CC0 Public Domain Dedication along with this software. + * If not, see . + */ + +// Anti-aliased UI scaling that respects pixel sharpness +// Approach taken from https://colececil.dev/blog/2017/scaling-pixel-art-without-destroying-it/ +vec4 textureHybrid(sampler2D tex, vec2 uv) { + uv *= sourceDimensions; + vec2 texelUv = fract(uv); + vec2 pixelsPerTexel = vec2(targetDimensions) / vec2(sourceDimensions); + vec2 interpolationAmount = min(texelUv * pixelsPerTexel, .5) - min((1 - texelUv) * pixelsPerTexel, .5); + return texture(tex, (floor(uv) + .5 + interpolationAmount) / sourceDimensions); +} \ No newline at end of file diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/uv.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/uv.glsl index 9e170e34e81..7818e59b2de 100644 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/uv.glsl +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/uv.glsl @@ -24,14 +24,13 @@ */ void compute_uv(vec3 cameraPos, vec3 f1, vec3 f2, vec3 f3, vec3 t1, vec3 t2, vec3 t3, out vec2 uv1, out vec2 uv2, out vec2 uv3) { - vec3 v1 = t1; - vec3 v2 = t2 - v1; - vec3 v3 = t3 - v1; + vec3 tangent = t2 - t1; + vec3 bitangent = t3 - t1; - vec3 texNormal = cross(v2, v3); + vec3 texNormal = cross(tangent, bitangent); vec3 vertexToCamera; - // Set the tri vertex position to the intersection point on the tex tri plane of the tri vertex + // Set the tri vertex position to the intersection point on the tex tri plane // along the vector from the tri vertex to the camera. // Point of intersection p, from https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection#Algebraic_form: // d = ((p_0 - l_0) ⋅ n) / (l ⋅ n) @@ -49,25 +48,25 @@ void compute_uv(vec3 cameraPos, vec3 f1, vec3 f2, vec3 f3, vec3 t1, vec3 t2, vec vertexToCamera = cameraPos - f3; f3 += vertexToCamera * dot(t3 - f3, texNormal) / dot(vertexToCamera, texNormal); - vec3 v4 = f1 - v1; - vec3 v5 = f2 - v1; - vec3 v6 = f3 - v1; + vec3 v4 = f1 - t1; + vec3 v5 = f2 - t1; + vec3 v6 = f3 - t1; - vec3 v8 = cross(v3, texNormal); - float d = 1.0f / dot(v8, v2); + vec3 v8 = cross(bitangent, texNormal); + float d = 1.0f / dot(v8, tangent); - float u0 = dot(v8, v4) * d; - float u1 = dot(v8, v5) * d; - float u2 = dot(v8, v6) * d; + float u1 = dot(v8, v4) * d; + float u2 = dot(v8, v5) * d; + float u3 = dot(v8, v6) * d; - v8 = cross(v2, texNormal); - d = 1.0f / dot(v8, v3); + v8 = cross(tangent, texNormal); + d = 1.0f / dot(v8, bitangent); - float v0_ = dot(v8, v4) * d; - float v1_ = dot(v8, v5) * d; - float v2_ = dot(v8, v6) * d; + float v1 = dot(v8, v4) * d; + float v2 = dot(v8, v5) * d; + float v3 = dot(v8, v6) * d; - uv1 = vec2(u0, v0_); - uv2 = vec2(u1, v1_); - uv3 = vec2(u2, v2_); + uv1 = vec2(u1, v1); + uv2 = vec2(u2, v2); + uv3 = vec2(u3, v3); } \ No newline at end of file diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/vert.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/vert.glsl index 7c37d22d7d9..ad8f5e19c69 100644 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/vert.glsl +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/vert.glsl @@ -32,33 +32,34 @@ #define FOG_CORNER_ROUNDING 1.5 #define FOG_CORNER_ROUNDING_SQUARED (FOG_CORNER_ROUNDING * FOG_CORNER_ROUNDING) -layout(location = 0) in vec3 vert; -layout(location = 1) in int ahsl; -layout(location = 2) in vec4 uv; +layout(location = 0) in vec3 vertf; +layout(location = 1) in int abhsl; +layout(location = 2) in ivec4 tex; layout(std140) uniform uniforms { float cameraYaw; float cameraPitch; - int centerX; - int centerY; - int zoom; float cameraX; float cameraY; float cameraZ; }; +uniform mat4 entityProj; +uniform ivec4 entityTint; uniform float brightness; uniform int useFog; uniform int fogDepth; uniform int drawDistance; uniform int expandedMapLoadingChunks; +uniform ivec3 base; -out vec3 gVertex; +out vec4 gVertex; out vec4 gColor; out float gHsl; out int gTextureId; -out vec3 gTexPos; +out vec4 gTexPos; out float gFogAmount; +out int gBias; #include "hsl_to_rgb.glsl" @@ -67,28 +68,28 @@ float fogFactorLinear(const float dist, const float start, const float end) { } void main() { - int hsl = ahsl & 0xffff; - float a = float(ahsl >> 24 & 0xff) / 255.f; + vec4 vert = vec4(vertf + base, 1); + float a = float(abhsl >> 24 & 0xff) / 255.f; + vec3 hsl = vec3(abhsl >> 10 & 63, abhsl >> 7 & 7, abhsl & 127); + hsl += ((entityTint.xyz - hsl) * entityTint.w) / 128; vec3 rgb = hslToRgb(hsl); - gVertex = vert; + gVertex = entityProj * vert; gColor = vec4(rgb, 1.f - a); - gHsl = float(hsl); + gHsl = abhsl & 0xffff; // only used for texture lighting, which isn't affected by tint - gTextureId = int(uv.x); // the texture id + 1; - gTexPos = uv.yzw; + gTextureId = tex.x; // the texture id + 1 + gTexPos = entityProj * (trunc(vert) + vec4(tex.yzw, 0)); - // the client draws one less tile to the north and east than it does to the south - // and west, so subtract a tiles width from the north and east edges. float fogWest = max(FOG_SCENE_EDGE_MIN, cameraX - drawDistance); - float fogEast = min(FOG_SCENE_EDGE_MAX, cameraX + drawDistance - TILE_SIZE); + float fogEast = min(FOG_SCENE_EDGE_MAX, cameraX + drawDistance); float fogSouth = max(FOG_SCENE_EDGE_MIN, cameraZ - drawDistance); - float fogNorth = min(FOG_SCENE_EDGE_MAX, cameraZ + drawDistance - TILE_SIZE); + float fogNorth = min(FOG_SCENE_EDGE_MAX, cameraZ + drawDistance); // Calculate distance from the scene edge - float xDist = min(vert.x - fogWest, fogEast - vert.x); - float zDist = min(vert.z - fogSouth, fogNorth - vert.z); + float xDist = min(gVertex.x - fogWest, fogEast - gVertex.x); + float zDist = min(gVertex.z - fogSouth, fogNorth - gVertex.z); float nearestEdgeDistance = min(xDist, zDist); float secondNearestEdgeDistance = max(xDist, zDist); float fogDistance = @@ -96,4 +97,6 @@ void main() { max(0.f, (nearestEdgeDistance + FOG_CORNER_ROUNDING_SQUARED) / (secondNearestEdgeDistance + FOG_CORNER_ROUNDING_SQUARED)); gFogAmount = fogFactorLinear(fogDistance, 0, fogDepth * TILE_SIZE) * useFog; + + gBias = (abhsl >> 16) & 0xff; } diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/vertui.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/vertui.glsl index 0bb514448e1..ae3294055e5 100644 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/vertui.glsl +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/vertui.glsl @@ -24,27 +24,33 @@ */ #version 330 -#define SAMPLING_DEFAULT 0 -#define SAMPLING_MITCHELL 1 -#define SAMPLING_CATROM 2 -#define SAMPLING_XBR 3 +#include sampling_mode + +#define SAMPLING_NEAREST 0 +#define SAMPLING_LINEAR 1 +#define SAMPLING_MITCHELL 2 +#define SAMPLING_CATROM 3 +#define SAMPLING_XBR 4 +#define SAMPLING_HYBRID 5 -uniform int samplingMode; uniform ivec2 sourceDimensions; uniform ivec2 targetDimensions; -#include "scale/xbr_lv2_vert.glsl" - layout(location = 0) in vec3 aPos; layout(location = 1) in vec2 aTexCoord; out vec2 TexCoord; +#if SAMPLING_MODE == SAMPLING_XBR +#include "scale/xbr_lv2_vert.glsl" + out XBRTable xbrTable; +#endif void main() { gl_Position = vec4(aPos, 1.0); TexCoord = aTexCoord; - if (samplingMode == SAMPLING_XBR) - xbrTable = xbr_vert(TexCoord, sourceDimensions); +#if SAMPLING_MODE == SAMPLING_XBR + xbrTable = xbr_vert(TexCoord, sourceDimensions); +#endif } diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/transports.tsv b/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/transports.tsv index 4e011021b58..aa5f7602a2a 100644 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/transports.tsv +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/transports.tsv @@ -5188,4 +5188,6 @@ # Isle of Souls 2310 2919 0 2167 9308 0 Enter;Cave;40736 -2167 9308 0 2310 2919 0 Exit;Opening;40737 \ No newline at end of file +2167 9308 0 2310 2919 0 Exit;Opening;40737 +2763 2951 0 2763 2951 1 Climb-up;Ladder;16683 +2763 2952 1 2763 2951 0 Climb-down;Ladder;16679 \ No newline at end of file diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/gpu/ShaderTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/gpu/ShaderTest.java index 13fe5341f38..cb40d7d6718 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/gpu/ShaderTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/gpu/ShaderTest.java @@ -30,6 +30,8 @@ import java.nio.file.Files; import java.util.ArrayList; import java.util.List; +import net.runelite.client.plugins.gpu.config.ColorBlindMode; +import net.runelite.client.plugins.gpu.config.UIScalingMode; import net.runelite.client.plugins.gpu.template.Template; import org.junit.Assert; import org.junit.Assume; @@ -54,20 +56,17 @@ public void testShaders() throws Exception .addInclude(GpuPlugin.class) .add(key -> { - if ("version_header".equals(key)) + if ("texture_config".equals(key)) { - return GpuPlugin.WINDOWS_VERSION_HEADER; + return "#define TEXTURE_COUNT " + TextureManager.TEXTURE_COUNT + "\n"; } - if ("thread_config".equals(key)) + if ("sampling_mode".equals(key)) { - int threadCount = 512; - int facesPerThread = 1; - return "#define THREAD_COUNT " + threadCount + "\n" + - "#define FACES_PER_THREAD " + facesPerThread + "\n"; + return "#define SAMPLING_MODE " + UIScalingMode.XBR.ordinal() + "\n"; } - if ("texture_config".equals(key)) + if ("colorblind_mode".equals(key)) { - return "#define TEXTURE_COUNT " + TextureManager.TEXTURE_COUNT + "\n"; + return "#define COLORBLIND_MODE " + ColorBlindMode.DEUTERANOPE.ordinal() + "\n"; } return null; }), @@ -75,8 +74,6 @@ public void testShaders() throws Exception Shader[] shaders = { GpuPlugin.PROGRAM, - GpuPlugin.COMPUTE_PROGRAM, - GpuPlugin.UNORDERED_COMPUTE_PROGRAM, GpuPlugin.UI_PROGRAM, }; diff --git a/runelite-jshell/pom.xml b/runelite-jshell/pom.xml index 7294e1f65a8..828a20170d4 100644 --- a/runelite-jshell/pom.xml +++ b/runelite-jshell/pom.xml @@ -30,7 +30,7 @@ net.runelite runelite-parent - 1.11.23-SNAPSHOT + 1.12.0-SNAPSHOT jshell diff --git a/runelite-maven-plugin/pom.xml b/runelite-maven-plugin/pom.xml index 83c08704f03..26a1ad5525f 100644 --- a/runelite-maven-plugin/pom.xml +++ b/runelite-maven-plugin/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.11.23-SNAPSHOT + 1.12.0-SNAPSHOT runelite-maven-plugin