diff --git a/src/main/java/rs117/hd/HdPlugin.java b/src/main/java/rs117/hd/HdPlugin.java index 01f211c80d..0a58d067a0 100644 --- a/src/main/java/rs117/hd/HdPlugin.java +++ b/src/main/java/rs117/hd/HdPlugin.java @@ -39,7 +39,6 @@ import java.awt.image.DataBufferInt; import java.io.IOException; import java.nio.ByteBuffer; -import java.nio.FloatBuffer; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.ArrayList; @@ -76,6 +75,7 @@ import org.lwjgl.opengl.*; import org.lwjgl.system.Callback; import org.lwjgl.system.Configuration; +import org.lwjgl.system.MemoryStack; import rs117.hd.config.ColorFilter; import rs117.hd.config.DynamicLights; import rs117.hd.config.SeasonalHemisphere; @@ -83,6 +83,11 @@ import rs117.hd.config.ShadingMode; import rs117.hd.config.ShadowMode; import rs117.hd.config.VanillaShadowMode; +import rs117.hd.opengl.GLVao; +import rs117.hd.opengl.GLVertexLayout; +import rs117.hd.opengl.GLVertexLayout.ArrayField; +import rs117.hd.opengl.GLVertexLayout.ComponentType; +import rs117.hd.opengl.GLVertexLayout.FormatType; import rs117.hd.opengl.shader.ShaderException; import rs117.hd.opengl.shader.ShaderIncludes; import rs117.hd.opengl.shader.TiledLightingShaderProgram; @@ -160,6 +165,7 @@ public class HdPlugin extends Plugin { public static int MAX_TEXTURE_UNITS; public static int TEXTURE_UNIT_COUNT = 0; + public static final int TEXTURE_UNIT_UNUSED = GL_TEXTURE0 + TEXTURE_UNIT_COUNT++; public static final int TEXTURE_UNIT_UI = GL_TEXTURE0 + TEXTURE_UNIT_COUNT++; public static final int TEXTURE_UNIT_GAME = GL_TEXTURE0 + TEXTURE_UNIT_COUNT++; public static final int TEXTURE_UNIT_SHADOW_MAP = GL_TEXTURE0 + TEXTURE_UNIT_COUNT++; @@ -344,11 +350,13 @@ public class HdPlugin extends Plugin { private static final ResourcePath SHADER_PATH = Props .getFolder("rlhd.shader-path", () -> path(HdPlugin.class)); - public int vaoQuad; - private int vboQuad; + private static final GLVertexLayout FULLSCREEN_VERTEX_LAYOUT = new GLVertexLayout("FULLSCREEN_VERTEX_LAYOUT") + .edit(ArrayField.VERTEX_FIELD_0).enabled().component(ComponentType.RG).format(FormatType.FLOAT).stride(16).offset(0) + .edit(ArrayField.VERTEX_FIELD_1).enabled().component(ComponentType.RG).format(FormatType.FLOAT).stride(16).offset(8) + .finish(); - public int vaoTri; - private int vboTri; + public GLVao quadVao; + public GLVao triVao; @Getter @Nullable @@ -564,7 +572,7 @@ protected void startUp() { INTEL_GPU = glRenderer.contains("Intel"); NVIDIA_GPU = glRenderer.toLowerCase().contains("nvidia"); - SUPPORTS_INDIRECT_DRAW = NVIDIA_GPU && !APPLE || config.forceIndirectDraw(); + SUPPORTS_INDIRECT_DRAW = false; //NVIDIA_GPU && !APPLE || config.forceIndirectDraw(); TODO: This isn't working at the moment, not sure why just yet renderer = config.legacyRenderer() ? injector.getInstance(LegacyRenderer.class) : @@ -927,7 +935,7 @@ private void initializeShaders() throws ShaderException, IOException { var includes = getShaderIncludes(); // Bind a valid VAO, otherwise validation may fail on older Intel-based Macs - glBindVertexArray(vaoTri); + triVao.bind(); renderer.initializeShaders(includes); uiProgram.compile(includes); @@ -1018,76 +1026,56 @@ public void recompilePrograms() { } private void initializeVaos() { - { - // Create quad VAO - vaoQuad = glGenVertexArrays(); - vboQuad = glGenBuffers(); - glBindVertexArray(vaoQuad); - - FloatBuffer vboQuadData = BufferUtils.createFloatBuffer(16) - .put(new float[] { - // x, y, u, v - 1, 1, 1, 1, // top right - -1, 1, 0, 1, // top left - -1, -1, 0, 0, // bottom left - 1, -1, 1, 0 // bottom right - }) - .flip(); - glBindBuffer(GL_ARRAY_BUFFER, vboQuad); - glBufferData(GL_ARRAY_BUFFER, vboQuadData, GL_STATIC_DRAW); - - // position attribute - glVertexAttribPointer(0, 2, GL_FLOAT, false, 4 * Float.BYTES, 0); - glEnableVertexAttribArray(0); - - // texture coord attribute - glVertexAttribPointer(1, 2, GL_FLOAT, false, 4 * Float.BYTES, 2 * Float.BYTES); - glEnableVertexAttribArray(1); - } + try (MemoryStack stack = MemoryStack.stackPush()) { + quadVao = new GLVao("FullscreenQuad::VAO", FULLSCREEN_VERTEX_LAYOUT); + quadVao.setBufferRange( + new GLBuffer("FullscreenQuad::VBO", GL_ARRAY_BUFFER, GL_STATIC_DRAW) + .initialize() + .upload(stack + .mallocFloat(16) + .put(new float[] { + // x, y, u, v + 1, 1, 1, 1, // top right + -1, 1, 0, 1, // top left + -1, -1, 0, 0, // bottom left + 1, -1, 1, 0 // bottom right + }) + .flip() + ), + true, + ArrayField.VERTEX_FIELD_0, + ArrayField.VERTEX_FIELD_1 + ); - { - // Create tri VAO - vaoTri = glGenVertexArrays(); - vboTri = glGenBuffers(); - glBindVertexArray(vaoTri); - - FloatBuffer vboTriData = BufferUtils.createFloatBuffer(12) - .put(new float[] { - // x, y, u, v - -1, -1, 0, 0, // bottom left - 3, -1, 2, 0, // bottom right (off-screen) - -1, 3, 0, 2 // top left (off-screen) - }) - .flip(); - glBindBuffer(GL_ARRAY_BUFFER, vboTri); - glBufferData(GL_ARRAY_BUFFER, vboTriData, GL_STATIC_DRAW); - - // position attribute - glVertexAttribPointer(0, 2, GL_FLOAT, false, 4 * Float.BYTES, 0); - glEnableVertexAttribArray(0); - - // texture coord attribute - glVertexAttribPointer(1, 2, GL_FLOAT, false, 4 * Float.BYTES, 2 * Float.BYTES); - glEnableVertexAttribArray(1); + triVao = new GLVao("FullscreenQuad::VAO", FULLSCREEN_VERTEX_LAYOUT); + triVao.setBufferRange( + new GLBuffer("FullscreenQuad::VBO", GL_ARRAY_BUFFER, GL_STATIC_DRAW) + .initialize() + .upload(stack + .mallocFloat(16) + .put(new float[] { + // x, y, u, v + -1, -1, 0, 0, // bottom left + 3, -1, 2, 0, // bottom right (off-screen) + -1, 3, 0, 2 // top left (off-screen) + }) + .flip() + ), + true, + ArrayField.VERTEX_FIELD_0, + ArrayField.VERTEX_FIELD_1 + ); } } private void destroyVaos() { - if (vboQuad != 0) - glDeleteBuffers(vboQuad); - vboQuad = 0; + if (quadVao != null) + quadVao.destroy(); + quadVao = null; - if (vaoQuad != 0) - glDeleteVertexArrays(vaoQuad); - vaoQuad = 0; - - if (vboTri != 0) - glDeleteBuffers(vboTri); - vboTri = 0; - - if (vaoTri != 0) - glDeleteVertexArrays(vaoTri); - vaoTri = 0; + if (triVao != null) + triVao.destroy(); + triVao = null; } private void initializeUbos() { @@ -1141,6 +1129,9 @@ private void initializeUiTexture() { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glActiveTexture(TEXTURE_UNIT_UNUSED); + glBindTexture(GL_TEXTURE_2D, 0); + checkGLErrors(); } @@ -1202,6 +1193,9 @@ public void updateTiledLightingFbo() { glBindFramebuffer(GL_FRAMEBUFFER, awtContext.getFramebuffer(false)); + glActiveTexture(TEXTURE_UNIT_UNUSED); + glBindTexture(GL_TEXTURE_2D_ARRAY, 0); + checkGLErrors(); uboGlobal.tiledLightingResolution.set(tiledLightingResolution); @@ -1401,8 +1395,17 @@ private void initializeShadowMapFbo() { glDrawBuffer(GL_NONE); glReadBuffer(GL_NONE); + // Check framebuffer completeness + int status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + log.error("Shadow map framebuffer is not complete: 0x{}", Integer.toHexString(status)); + throw new RuntimeException("Failed to create shadow map framebuffer: status 0x" + Integer.toHexString(status)); + } + // Reset FBO glBindFramebuffer(GL_FRAMEBUFFER, awtContext.getFramebuffer(false)); + glActiveTexture(TEXTURE_UNIT_UNUSED); + glBindTexture(GL_TEXTURE_2D, 0); } private void initializeDummyShadowMap() { @@ -1415,6 +1418,9 @@ private void initializeDummyShadowMap() { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + + glActiveTexture(TEXTURE_UNIT_UNUSED); + glBindTexture(GL_TEXTURE_2D, 0); } private void destroyShadowMapFbo() { @@ -1447,7 +1453,6 @@ public void prepareInterfaceTexture() { if (resize) { uiResolution = resolution; - glActiveTexture(TEXTURE_UNIT_UI); glBindTexture(GL_TEXTURE_2D, texUi); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, uiResolution[0], uiResolution[1], 0, GL_BGRA, GL_UNSIGNED_BYTE, 0); } @@ -1520,7 +1525,6 @@ public void drawUi(int overlayColor) { // 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/pixel isn't final int function = config.uiScalingMode().glSamplingFunction; - glActiveTexture(TEXTURE_UNIT_UI); glBindTexture(GL_TEXTURE_2D, texUi); if (uiCopyJob != null) { @@ -1544,8 +1548,10 @@ public void drawUi(int overlayColor) { glEnable(GL_BLEND); glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); - glBindVertexArray(vaoTri); + + triVao.bind(); glDrawArrays(GL_TRIANGLES, 0, 3); + triVao.unbind(); shadowMapOverlay.render(); gammaCalibrationOverlay.render(); diff --git a/src/main/java/rs117/hd/opengl/GLState.java b/src/main/java/rs117/hd/opengl/GLState.java index 5775c232e0..6f095827ff 100644 --- a/src/main/java/rs117/hd/opengl/GLState.java +++ b/src/main/java/rs117/hd/opengl/GLState.java @@ -1,9 +1,8 @@ package rs117.hd.opengl; import java.util.Arrays; -import java.util.HashSet; import java.util.Objects; -import java.util.Set; +import lombok.Getter; public abstract class GLState { protected boolean hasValue; @@ -22,6 +21,11 @@ public void apply() { } abstract void internalApply(); + public abstract void setDefault(); + + public void printState(StringBuffer sb) { + sb.append(getClass().getSimpleName()).append(": ").append(hasApplied ? "applied " : " "); + } public abstract static class Bool extends GLState { private boolean value; @@ -41,6 +45,12 @@ void internalApply() { } protected abstract void applyValue(boolean value); + + @Override + public void printState(StringBuffer sb) { + super.printState(sb); + sb.append("(").append(appliedValue).append(") \n"); + } } public abstract static class Int extends GLState { @@ -61,10 +71,17 @@ void internalApply() { } protected abstract void applyValue(int value); + + @Override + public void printState(StringBuffer sb) { + super.printState(sb); + sb.append("(").append(appliedValue).append(") \n"); + } } public abstract static class Object extends GLState { private T value; + @Getter private T appliedValue; public final void set(T v) { @@ -81,6 +98,12 @@ void internalApply() { } protected abstract void applyValue(T value); + + @Override + public void printState(StringBuffer sb) { + super.printState(sb); + sb.append("(").append(appliedValue).append(") \n"); + } } public abstract static class IntArray extends GLState { @@ -106,6 +129,17 @@ void internalApply() { } protected abstract void applyValues(int[] values); + + @Override + public void printState(StringBuffer sb) { + super.printState(sb); + sb.append("("); + for(int i = 0; i < value.length; i++) { + if(i > 0) sb.append(", "); + sb.append(value[i]); + } + sb.append(") \n"); + } } public abstract static class BoolArray extends GLState { @@ -131,33 +165,16 @@ void internalApply() { } protected abstract void applyValues(boolean[] values); - } - - public abstract static class IntSet extends GLState { - private final Set targets = new HashSet<>(); - - public void add(int target) { - hasValue = true; - targets.add(target); - } - - public void remove(int target) { - targets.remove(target); - hasApplied = !targets.isEmpty(); - } @Override - void internalApply() { - for (int t : targets) applyTarget(t); - targets.clear(); - } - - @Override - public void reset() { - super.reset(); - targets.clear(); + public void printState(StringBuffer sb) { + super.printState(sb); + sb.append("("); + for(int i = 0; i < value.length; i++) { + if(i > 0) sb.append(", "); + sb.append(value[i]); + } + sb.append(") \n"); } - - protected abstract void applyTarget(int target); } } diff --git a/src/main/java/rs117/hd/opengl/GLVao.java b/src/main/java/rs117/hd/opengl/GLVao.java new file mode 100644 index 0000000000..bd287f331b --- /dev/null +++ b/src/main/java/rs117/hd/opengl/GLVao.java @@ -0,0 +1,195 @@ +package rs117.hd.opengl; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import rs117.hd.utils.buffer.GLBuffer; + +import static org.lwjgl.opengl.GL33C.*; +import static rs117.hd.HdPlugin.checkGLErrors; + +@RequiredArgsConstructor +@Slf4j +public class GLVao { + private static GLVao previousVao; + + private final String name; + private final GLVertexLayout layout; + private final GLBuffer[] buffers = new GLBuffer[GLVertexLayout.MAX_ATTRIBUTES]; + private final long[] builtData = new long[GLVertexLayout.MAX_ATTRIBUTES]; + + private int glVAO; + private int layoutVersion; + + private static long packBufferData(int bufferId, boolean owned) { + return ((long) bufferId << 1) | (owned ? 1L : 0L); + } + + private static int unpackBufferId(long data) { + return (int) (data >>> 1); + } + + private static boolean unpackOwnership(long data) { + return (data & 1L) != 0; + } + + public void setBuffer(GLBuffer buffer, boolean takeOwnership, GLVertexLayout.ArrayField field) { + buffers[field.ordinal()] = buffer; + // Note: ownership will be stored in builtData during bind/ensureBuilt + layoutVersion = -1; + } + + public void setBufferRange(GLBuffer buffer, boolean takeOwnership, GLVertexLayout.ArrayField start, GLVertexLayout.ArrayField end) { + for (int i = start.ordinal(); i <= end.ordinal(); i++) { + buffers[i] = buffer; + } + layoutVersion = -1; + } + + public void setBuffers(GLBuffer buffer, boolean takeOwnership, GLVertexLayout.ArrayField... fields) { + for (int i = 0; i < fields.length; i++) { + buffers[fields[i].ordinal()] = buffer; + } + layoutVersion = -1; + } + + public void associateBuffer(GLBuffer buffer, GLVertexLayout.ArrayField field) { + setBuffer(buffer, false, field); + } + + public void associateBuffers(GLBuffer buffer, GLVertexLayout.ArrayField... fields) { + setBuffers(buffer, false, fields); + } + + public void associateBufferRange(GLBuffer buffer, GLVertexLayout.ArrayField start, GLVertexLayout.ArrayField end) { + setBufferRange(buffer, false, start, end); + } + + public void remove(GLBuffer buffer) { + boolean found = false; + for (int i = 0; i < buffers.length; i++) { + GLBuffer b = buffers[i]; + if (b == buffer) { + buffers[i] = null; + found = true; + } + } + if (found) + layoutVersion = -1; + } + + public void bind() { + if (previousVao != null && previousVao != this) + log.warn("Binding VAO: {} when it was already bound by: {}", name, previousVao.name); + + if (glVAO == 0) + glVAO = glGenVertexArrays(); + + glBindVertexArray(glVAO); + previousVao = this; + + ensureBuilt(); + + final GLBuffer eboBuffer = buffers[GLVertexLayout.ArrayField.ELEMENT_BUFFER.ordinal()]; + final int newEboValue = eboBuffer != null ? eboBuffer.id : 0; + final int boundEboId = unpackBufferId(builtData[0]); + if (boundEboId != newEboValue) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, newEboValue); + builtData[0] = packBufferData(newEboValue, false); + } + } + + private void ensureBuilt() { + boolean hasArrayBuffersChanged = false; + for (int i = 1; i < buffers.length; i++) { + GLBuffer buffer = buffers[i]; + if (buffer != null && buffer.id != unpackBufferId(builtData[i])) { + hasArrayBuffersChanged = true; + break; + } + } + + if (layoutVersion == layout.getVersion() && !hasArrayBuffersChanged) + return; + + GLVertexLayout.Attribute[] attributes = layout.getAttributes(); + int prevBufferId = 0; + for (int i = 0; i < attributes.length; i++) { + final GLVertexLayout.Attribute attrib = attributes[i]; + if (!attrib.isEnabled()) { + glDisableVertexAttribArray(i); + continue; + } + + final GLBuffer arrayBuffer = buffers[i + 1]; + final int arrayBufferId = arrayBuffer != null ? arrayBuffer.id : 0; + if (arrayBuffer == null) { + log.warn( + "ArrayField: {} is enabled but no buffer is associated, expect erroneous behaviour", + GLVertexLayout.ARRAY_FIELD_NAMES[i + 1] + ); + } else if (arrayBuffer.target != GL_ARRAY_BUFFER) { + log.warn( + "ArrayField: {} is enabled but buffer is not an array buffer, expect erroneous behaviour", + GLVertexLayout.ARRAY_FIELD_NAMES[i + 1] + ); + } + + if (prevBufferId != arrayBufferId) { + glBindBuffer(GL_ARRAY_BUFFER, arrayBufferId); + prevBufferId = arrayBufferId; + } + + glEnableVertexAttribArray(i); + if (attrib.divisor > 0) + glVertexAttribDivisor(i, attrib.divisor); + + if (attrib.isInteger()) { + assert attrib.format.ordinal() >= GLVertexLayout.FormatType.INT.ordinal(); + glVertexAttribIPointer(i, attrib.component.size, attrib.format.glFormatType, attrib.stride, attrib.offset); + } else { + glVertexAttribPointer( + i, + attrib.component.size, + attrib.format.glFormatType, + attrib.isNormalized(), + attrib.stride, + attrib.offset + ); + } + builtData[i + 1] = packBufferData(arrayBufferId, false); + } + + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glBindVertexArray(glVAO); + + checkGLErrors(() -> "Building VAO: " + name + " with layout: " + layout); + layoutVersion = layout.getVersion(); + } + + public void unbind() { + if (previousVao != null && previousVao != this) + log.warn("Unbinding VAO: {} when it was bound by: {}", name, previousVao.name); + glBindVertexArray(0); + if (unpackBufferId(builtData[0]) != 0) + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + previousVao = null; + } + + public void destroy() { + if (glVAO != 0) { + glDeleteVertexArrays(glVAO); + glVAO = 0; + } + + for (int i = 0; i < buffers.length; i++) { + if (buffers[i] == null) + continue; + + if (unpackOwnership(builtData[i])) + buffers[i].destroy(); + buffers[i] = null; + } + } +} diff --git a/src/main/java/rs117/hd/opengl/GLVertexLayout.java b/src/main/java/rs117/hd/opengl/GLVertexLayout.java new file mode 100644 index 0000000000..0f2be76f45 --- /dev/null +++ b/src/main/java/rs117/hd/opengl/GLVertexLayout.java @@ -0,0 +1,215 @@ +package rs117.hd.opengl; + +import java.util.Arrays; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import static org.lwjgl.opengl.GL33C.*; + +@Slf4j +public class GLVertexLayout { + public static final int MAX_ATTRIBUTES = 12; + public static final String[] ARRAY_FIELD_NAMES = Arrays.stream(ArrayField.values()).map(ArrayField::name).toArray(String[]::new); + + @Getter + private final String name; + @Getter + private final Attribute[] attributes = new Attribute[MAX_ATTRIBUTES]; + @Getter + private int version; + private int editIdx; + + public GLVertexLayout(String name) { + this.name = name; + for (int i = 0; i < MAX_ATTRIBUTES; i++) + attributes[i] = new Attribute(); + } + + public GLVertexLayout edit(ArrayField field) { + assert field != ArrayField.ELEMENT_BUFFER : "Element buffer cannot be edited"; + editIdx = field.field; + return this; + } + + public GLVertexLayout enabled() { + attributes[editIdx].setEnabled(true); + return this; + } + + public GLVertexLayout disabled() { + attributes[editIdx].setEnabled(false); + return this; + } + + public GLVertexLayout normalized(boolean isNormalized) { + attributes[editIdx].setNormalized(isNormalized); + return this; + } + + public GLVertexLayout asFloat() { + attributes[editIdx].setInteger(false); + return this; + } + + public GLVertexLayout asInteger() { + attributes[editIdx].setInteger(true); + return this; + } + + public GLVertexLayout component(ComponentType component) { + attributes[editIdx].component = component; + return this; + } + + public GLVertexLayout format(FormatType format) { + attributes[editIdx].format = format; + return this; + } + + public GLVertexLayout offset(long offset) { + attributes[editIdx].offset = offset; + return this; + } + + public GLVertexLayout stride(int stride) { + attributes[editIdx].stride = stride; + return this; + } + + public GLVertexLayout divisor(int divisor) { + attributes[editIdx].divisor = divisor; + return this; + } + + public GLVertexLayout finish() { + editIdx = -1; + version++; + log.debug("{}", this); + return this; + } + + public String toString() { + StringBuilder str = new StringBuilder(); + str.append("\nGLVertexLayout - ").append(name).append(" - Version: ").append(version).append("\n"); + for (int i = 0; i < MAX_ATTRIBUTES; i++) { + Attribute attr = attributes[i]; + str.append(" * ARRAY_FIELD_").append(i).append(": "); + if (attr.isEnabled()) { + str.append("ENABLED, isInteger: ") + .append(attr.isInteger()) + .append(", isNormalized: ") + .append(attr.isNormalized()) + .append(", component: ") + .append(attr.component) + .append(", format: ") + .append(attr.format) + .append(", stride: ") + .append(attr.stride) + .append(", divisor: ") + .append(attr.divisor) + .append(", offset: ") + .append(attr.offset) + .append("\n"); + } else { + str.append("DISABLED\n"); + } + } + return str.toString(); + } + + @RequiredArgsConstructor + public enum ArrayField { + ELEMENT_BUFFER(-1), + VERTEX_FIELD_0(0), + VERTEX_FIELD_1(1), + VERTEX_FIELD_2(2), + VERTEX_FIELD_3(3), + VERTEX_FIELD_4(4), + VERTEX_FIELD_5(5), + VERTEX_FIELD_6(6), + VERTEX_FIELD_7(7), + VERTEX_FIELD_8(8), + VERTEX_FIELD_9(9), + VERTEX_FIELD_10(10), + VERTEX_FIELD_11(11); + + public final int field; + } + + @RequiredArgsConstructor + public enum ComponentType { + R(1), + RG(2), + RGB(3), + RGBA(4); + + public final int size; + } + + @RequiredArgsConstructor + public enum FormatType { + FLOAT(GL_FLOAT), + DOUBLE(GL_DOUBLE), + HALF_FLOAT(GL_HALF_FLOAT), + INT(GL_INT), + SHORT(GL_SHORT), + UNSIGNED_INT(GL_UNSIGNED_INT), + UNSIGNED_SHORT(GL_UNSIGNED_SHORT), + BYTE(GL_BYTE), + UNSIGNED_BYTE(GL_UNSIGNED_BYTE), + ; + + public final int glFormatType; + } + + public static final class Attribute { + private static final byte FLAG_ENABLED = 1 << 0; + private static final byte FLAG_INTEGER = 1 << 1; + private static final byte FLAG_NORMALIZED = 1 << 2; + + public ComponentType component; + public FormatType format; + public int stride; + public int divisor; + public long offset; + + private byte flags; + + public boolean isEnabled() { + return (flags & FLAG_ENABLED) != 0; + } + + public void setEnabled(boolean enabled) { + if (enabled) { + flags |= FLAG_ENABLED; + } else { + flags &= ~FLAG_ENABLED; + } + } + + public boolean isInteger() { + return (flags & FLAG_INTEGER) != 0; + } + + public void setInteger(boolean integer) { + if (integer) { + flags |= FLAG_INTEGER; + } else { + flags &= ~FLAG_INTEGER; + } + } + + public boolean isNormalized() { + return (flags & FLAG_NORMALIZED) != 0; + } + + public void setNormalized(boolean normalized) { + if (normalized) { + flags |= FLAG_NORMALIZED; + } else { + flags &= ~FLAG_NORMALIZED; + } + } + } +} diff --git a/src/main/java/rs117/hd/overlays/ShaderOverlay.java b/src/main/java/rs117/hd/overlays/ShaderOverlay.java index f01799b15b..54b4676070 100644 --- a/src/main/java/rs117/hd/overlays/ShaderOverlay.java +++ b/src/main/java/rs117/hd/overlays/ShaderOverlay.java @@ -411,11 +411,13 @@ protected void renderShader() { glEnable(GL_BLEND); glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); if (fullscreen) { - glBindVertexArray(plugin.vaoTri); + plugin.triVao.bind(); glDrawArrays(GL_TRIANGLES, 0, 3); + plugin.triVao.unbind(); } else { - glBindVertexArray(plugin.vaoQuad); + plugin.quadVao.bind(); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + plugin.quadVao.unbind(); } } diff --git a/src/main/java/rs117/hd/renderer/legacy/LegacyRenderer.java b/src/main/java/rs117/hd/renderer/legacy/LegacyRenderer.java index 32394d9568..941486d72b 100644 --- a/src/main/java/rs117/hd/renderer/legacy/LegacyRenderer.java +++ b/src/main/java/rs117/hd/renderer/legacy/LegacyRenderer.java @@ -67,6 +67,7 @@ import static rs117.hd.HdPlugin.NEAR_PLANE; import static rs117.hd.HdPlugin.ORTHOGRAPHIC_ZOOM; import static rs117.hd.HdPlugin.TEXTURE_UNIT_TILE_HEIGHT_MAP; +import static rs117.hd.HdPlugin.TEXTURE_UNIT_UNUSED; import static rs117.hd.HdPlugin.checkGLErrors; import static rs117.hd.HdPluginConfig.*; import static rs117.hd.utils.MathUtils.*; @@ -505,6 +506,8 @@ public void initializeTileHeightMap(Scene scene) { Constants.EXTENDED_SCENE_SIZE, Constants.EXTENDED_SCENE_SIZE, Constants.MAX_Z, 0, GL_RED_INTEGER, GL_SHORT, tileBuffer ); + glActiveTexture(TEXTURE_UNIT_UNUSED); + glBindTexture(GL_TEXTURE_3D, 0); } public void destroyTileHeightMap() { @@ -752,7 +755,7 @@ public void drawScene(double cameraX, double cameraY, double cameraZ, double cam glViewport(0, 0, plugin.tiledLightingResolution[0], plugin.tiledLightingResolution[1]); glBindFramebuffer(GL_FRAMEBUFFER, plugin.fboTiledLighting); - glBindVertexArray(plugin.vaoTri); + plugin.triVao.bind(); if (plugin.tiledLightingImageStoreProgram.isValid()) { plugin.tiledLightingImageStoreProgram.use(); @@ -768,6 +771,8 @@ public void drawScene(double cameraX, double cameraY, double cameraZ, double cam } } + plugin.triVao.unbind(); + frameTimer.end(Timer.RENDER_TILED_LIGHTING); frameTimer.end(Timer.DRAW_TILED_LIGHTING); } diff --git a/src/main/java/rs117/hd/renderer/zone/DynamicModelVAO.java b/src/main/java/rs117/hd/renderer/zone/DynamicModelVAO.java index 1b243e66c7..4890b1611f 100644 --- a/src/main/java/rs117/hd/renderer/zone/DynamicModelVAO.java +++ b/src/main/java/rs117/hd/renderer/zone/DynamicModelVAO.java @@ -6,6 +6,11 @@ import java.util.concurrent.atomic.AtomicInteger; import javax.annotation.Nonnull; import lombok.extern.slf4j.Slf4j; +import rs117.hd.opengl.GLVao; +import rs117.hd.opengl.GLVertexLayout; +import rs117.hd.opengl.GLVertexLayout.ArrayField; +import rs117.hd.opengl.GLVertexLayout.ComponentType; +import rs117.hd.opengl.GLVertexLayout.FormatType; import rs117.hd.utils.CommandBuffer; import rs117.hd.utils.buffer.GLBuffer; import rs117.hd.utils.buffer.GLMappedBufferIntWriter; @@ -14,7 +19,6 @@ import static org.lwjgl.opengl.GL33C.*; import static rs117.hd.HdPlugin.GL_CAPS; -import static rs117.hd.HdPlugin.NVIDIA_GPU; import static rs117.hd.HdPlugin.SUPPORTS_INDIRECT_DRAW; import static rs117.hd.renderer.zone.ZoneRenderer.TEXTURE_UNIT_TEXTURED_FACES; import static rs117.hd.utils.MathUtils.*; @@ -38,7 +42,18 @@ class DynamicModelVAO { // dummy sceneOffset ivec2 for macOS workaround static final int METADATA_SIZE = 12; - int vao; + public static final GLVertexLayout DYNAMIC_MODEL_VERTEX_LAYOUT = new GLVertexLayout("DYNAMIC_MODEL_VERTEX_LAYOUT") + // Mesh Data + .edit(ArrayField.VERTEX_FIELD_0).enabled().component(ComponentType.RGB).format(FormatType.FLOAT).stride(VERT_SIZE).offset(0) + .edit(ArrayField.VERTEX_FIELD_1).enabled().component(ComponentType.RGB).format(FormatType.HALF_FLOAT).stride(VERT_SIZE).offset(12) + .edit(ArrayField.VERTEX_FIELD_2).enabled().component(ComponentType.RGB).format(FormatType.SHORT).stride(VERT_SIZE).offset(18) + .edit(ArrayField.VERTEX_FIELD_3).enabled().component(ComponentType.R).format(FormatType.INT).stride(VERT_SIZE).offset(24).asInteger() + // Meta Data + .edit(ArrayField.VERTEX_FIELD_6).enabled().component(ComponentType.R).format(FormatType.INT).stride(METADATA_SIZE).offset(0).asInteger().divisor(1) + .edit(ArrayField.VERTEX_FIELD_7).enabled().component(ComponentType.RG).format(FormatType.INT).stride(METADATA_SIZE).offset(4).asInteger().divisor(1) + .finish(); + + GLVao vao; boolean used; private final GLBuffer vboRender; @@ -86,55 +101,17 @@ class DynamicModelVAO { public boolean hasStagingBuffer() { return vboRender != vboStaging; } void initialize() { - vao = glGenVertexArrays(); tbo.initialize(INITIAL_SIZE); vboRender.initialize(INITIAL_SIZE); if (vboRender != vboStaging) { vboStaging.initialize(INITIAL_SIZE); } - - bindRenderVAO(); + vao = new GLVao("DynamicModel::VAO", DYNAMIC_MODEL_VERTEX_LAYOUT); + vao.associateBufferRange(vboRender, ArrayField.VERTEX_FIELD_0, ArrayField.VERTEX_FIELD_3); } public void bindMetadataVAO(@Nonnull GLBuffer vboMetadata) { - glBindVertexArray(vao); - glBindBuffer(GL_ARRAY_BUFFER, vboMetadata.id); - - // WorldView index (not ID) - glEnableVertexAttribArray(6); - glVertexAttribDivisor(6, 1); - glVertexAttribIPointer(6, 1, GL_INT, METADATA_SIZE, 0); - - if (!NVIDIA_GPU) { - // Workaround for incorrect implementations of disabled vertex attribs, particularly on macOS - glEnableVertexAttribArray(7); - glVertexAttribDivisor(7, 1); - glVertexAttribIPointer(7, 2, GL_INT, METADATA_SIZE, 4); - } - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindVertexArray(0); - } - - void bindRenderVAO() { - glBindVertexArray(vao); - glBindBuffer(GL_ARRAY_BUFFER, vboRender.id); - - // Position - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 3, GL_FLOAT, false, VERT_SIZE, 0); - - // UVs - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 3, GL_HALF_FLOAT, false, VERT_SIZE, 12); - - // Normals - glEnableVertexAttribArray(2); - glVertexAttribPointer(2, 3, GL_SHORT, false, VERT_SIZE, 18); - - // TextureFaceIdx - glEnableVertexAttribArray(3); - glVertexAttribIPointer(3, 1, GL_INT, VERT_SIZE, 24); + vao.associateBufferRange(vboMetadata, ArrayField.VERTEX_FIELD_6, ArrayField.VERTEX_FIELD_7); } void map() { @@ -145,7 +122,6 @@ void map() { } synchronized void unmap(boolean coalesce) { - final int renderVBOId = vboRender.id; long vboWrittenBytes = vboWriter.flush(); tboWriter.flush(); @@ -178,16 +154,12 @@ synchronized void unmap(boolean coalesce) { } } } - - if (renderVBOId != vboRender.id) - bindRenderVAO(); } void destroy() { vboStaging.destroy(); tbo.destroy(); - glDeleteVertexArrays(vao); - vao = 0; + vao.destroy(); } synchronized View beginDraw(int faceCount) { @@ -269,7 +241,7 @@ void reset() { public final class View { public ReservedView vbo; public ReservedView tbo; - public int vao; + public GLVao vao; public int tboTexId; private int drawIdx; diff --git a/src/main/java/rs117/hd/renderer/zone/SceneUploader.java b/src/main/java/rs117/hd/renderer/zone/SceneUploader.java index d0b06b884d..7b6e389913 100644 --- a/src/main/java/rs117/hd/renderer/zone/SceneUploader.java +++ b/src/main/java/rs117/hd/renderer/zone/SceneUploader.java @@ -768,7 +768,7 @@ private void uploadZoneRenderable( zone.addAlphaModel( plugin, materialManager, - zone.glVaoA, + zone.alphaVao, zone.tboF.getTexId(), model, modelOverride, alphaStart, alphaEnd, x - basex, y, z - basez, diff --git a/src/main/java/rs117/hd/renderer/zone/Zone.java b/src/main/java/rs117/hd/renderer/zone/Zone.java index 24dc3abba1..6bf87466d1 100644 --- a/src/main/java/rs117/hd/renderer/zone/Zone.java +++ b/src/main/java/rs117/hd/renderer/zone/Zone.java @@ -17,6 +17,10 @@ import net.runelite.api.*; import org.lwjgl.system.MemoryStack; import rs117.hd.HdPlugin; +import rs117.hd.opengl.GLVao; +import rs117.hd.opengl.GLVertexLayout; +import rs117.hd.opengl.GLVertexLayout.ArrayField; +import rs117.hd.opengl.GLVertexLayout.FormatType; import rs117.hd.scene.MaterialManager; import rs117.hd.scene.SceneContext; import rs117.hd.scene.materials.Material; @@ -31,8 +35,9 @@ import static org.lwjgl.opengl.GL33C.*; import static rs117.hd.HdPlugin.GL_CAPS; import static rs117.hd.HdPlugin.SUPPORTS_INDIRECT_DRAW; -import static rs117.hd.HdPlugin.checkGLErrors; +import static rs117.hd.opengl.GLVertexLayout.ComponentType; import static rs117.hd.renderer.zone.ZoneRenderer.TEXTURE_UNIT_TEXTURED_FACES; +import static rs117.hd.renderer.zone.ZoneRenderer.eboAlpha; import static rs117.hd.utils.MathUtils.*; @Slf4j @@ -57,16 +62,27 @@ public class Zone { // sceneOffset int vec2(x, y) public static final int METADATA_SIZE = 12; + public static final GLVertexLayout ZONE_VERTEX_LAYOUT = new GLVertexLayout("ZONE_VERTEX_LAYOUT") + // Mesh Data + .edit(ArrayField.VERTEX_FIELD_0).enabled().component(ComponentType.RGB).format(FormatType.SHORT).stride(VERT_SIZE).offset(0) + .edit(ArrayField.VERTEX_FIELD_1).enabled().component(ComponentType.RGB).format(FormatType.HALF_FLOAT).stride(VERT_SIZE).offset(6) + .edit(ArrayField.VERTEX_FIELD_2).enabled().component(ComponentType.RGB).format(FormatType.SHORT).stride(VERT_SIZE).offset(12) + .edit(ArrayField.VERTEX_FIELD_3).enabled().component(ComponentType.R).format(FormatType.INT).stride(VERT_SIZE).offset(20).asInteger() + // Meta Data + .edit(ArrayField.VERTEX_FIELD_6).enabled().component(ComponentType.R).format(FormatType.INT).stride(METADATA_SIZE).offset(0).asInteger().divisor(1) + .edit(ArrayField.VERTEX_FIELD_7).enabled().component(ComponentType.RG).format(FormatType.INT).stride(METADATA_SIZE).offset(4).asInteger().divisor(1) + .finish(); + public static final int LEVEL_WATER_SURFACE = 4; public static final BlockingDeque VBO_PENDING_DELETION = new LinkedBlockingDeque<>(); - public static final BlockingDeque VAO_PENDING_DELETION = new LinkedBlockingDeque<>(); + public static final BlockingDeque VAO_PENDING_DELETION = new LinkedBlockingDeque<>(); - public int glVao; + public GLVao opaqueVao; int bufLen; int dist; - public int glVaoA; + public GLVao alphaVao; public int bufLenA; public int sortedFacesLen; @@ -99,25 +115,24 @@ public class Zone { final List playerModels = new ArrayList<>(0); final ConcurrentLinkedQueue pendingModelJobs = new ConcurrentLinkedQueue<>(); - public void initialize(GLBuffer o, GLBuffer a, GLTextureBuffer f, int eboShared) { - assert glVao == 0; - assert glVaoA == 0; + public void initialize(GLBuffer o, GLBuffer a, GLTextureBuffer f) { if (o == null && a == null || f == null) return; vboM = new GLBuffer("ZoneMetadata", GL_ARRAY_BUFFER, GL_DYNAMIC_DRAW, 0); vboM.initialize(METADATA_SIZE); - if (o != null) { - vboO = o; - glVao = glGenVertexArrays(); - setupVao(glVao, o.id, vboM.id, eboShared); + if ((vboO = o) != null) { + opaqueVao = new GLVao("Zone::Opaque::VAO", ZONE_VERTEX_LAYOUT); + opaqueVao.associateBufferRange(vboO, ArrayField.VERTEX_FIELD_0, ArrayField.VERTEX_FIELD_3); + opaqueVao.associateBufferRange(vboM, ArrayField.VERTEX_FIELD_6, ArrayField.VERTEX_FIELD_7); } - if (a != null) { - vboA = a; - glVaoA = glGenVertexArrays(); - setupVao(glVaoA, a.id, vboM.id, eboShared); + if ((vboA = a) != null) { + alphaVao = new GLVao("Zone::Alpha::VAO", ZONE_VERTEX_LAYOUT); + alphaVao.associateBufferRange(vboA, ArrayField.VERTEX_FIELD_0, ArrayField.VERTEX_FIELD_3); + alphaVao.associateBufferRange(vboM, ArrayField.VERTEX_FIELD_6, ArrayField.VERTEX_FIELD_7); + alphaVao.associateBuffer(eboAlpha, ArrayField.ELEMENT_BUFFER); } tboF = f; @@ -154,14 +169,14 @@ public void free() { tboF = null; } - if (glVao != 0) { - glDeleteVertexArrays(glVao); - glVao = 0; + if (opaqueVao != null) { + opaqueVao.destroy(); + opaqueVao = null; } - if (glVaoA != 0) { - glDeleteVertexArrays(glVaoA); - glVaoA = 0; + if (alphaVao != null) { + alphaVao.destroy(); + alphaVao = null; } if (uploadJob != null) { @@ -202,9 +217,9 @@ public static void processPendingDeletions() { leakCount++; } - Integer vao; + GLVao vao; while ((vao = VAO_PENDING_DELETION.poll()) != null) { - glDeleteVertexArrays(vao); + vao.destroy(); leakCount++; } @@ -232,14 +247,14 @@ protected void finalize() { vboM = null; } - if (glVao != 0) { - VAO_PENDING_DELETION.add(glVao); - glVao = 0; + if (opaqueVao != null) { + VAO_PENDING_DELETION.add(opaqueVao); + opaqueVao = null; } - if (glVaoA != 0) { - VAO_PENDING_DELETION.add(glVaoA); - glVaoA = 0; + if (alphaVao != null) { + VAO_PENDING_DELETION.add(alphaVao); + alphaVao = null; } } @@ -262,47 +277,6 @@ public void unmap() { } } - private void setupVao(int vao, int buffer, int metadata, int ebo) { - glBindVertexArray(vao); - glBindBuffer(GL_ARRAY_BUFFER, buffer); - - // The element buffer is part of VAO state - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); - - // Position - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 3, GL_SHORT, false, VERT_SIZE, 0); - - // UVs - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 3, GL_HALF_FLOAT, false, VERT_SIZE, 6); - - // Normals - glEnableVertexAttribArray(2); - glVertexAttribPointer(2, 3, GL_SHORT, false, VERT_SIZE, 12); - - // TextureFaceIdx - glEnableVertexAttribArray(3); - glVertexAttribIPointer(3, 1, GL_INT, VERT_SIZE, 20); - - glBindBuffer(GL_ARRAY_BUFFER, metadata); - - // WorldView index (not ID) - glEnableVertexAttribArray(6); - glVertexAttribDivisor(6, 1); - glVertexAttribIPointer(6, 1, GL_INT, METADATA_SIZE, 0); - - // Scene offset - glEnableVertexAttribArray(7); - glVertexAttribDivisor(7, 1); - glVertexAttribIPointer(7, 2, GL_INT, METADATA_SIZE, 4); - - checkGLErrors(); - - glBindVertexArray(0); - glBindBuffer(GL_ARRAY_BUFFER, 0); - } - public void setMetadata(WorldViewContext viewContext, SceneContext sceneContext, int mx, int mz) { if (vboM == null) return; @@ -407,7 +381,7 @@ void renderOpaque(CommandBuffer cmd, WorldViewContext ctx, boolean roofShadows) return; lastDrawMode = STATIC_UNSORTED; - lastVao = glVao; + lastVao = opaqueVao; lastTboF = tboF.getTexId(); flush(cmd); } @@ -421,7 +395,7 @@ void renderOpaqueLevel(CommandBuffer cmd, int level) { return; lastDrawMode = STATIC_UNSORTED; - lastVao = glVao; + lastVao = opaqueVao; lastTboF = tboF.getTexId(); flush(cmd); } @@ -446,7 +420,7 @@ public static class AlphaModel { int startpos, endpos; short x, y, z; // local position short rid; - int vao; + GLVao vao; int tboF; byte level; byte lx, lz, ux, uz; // lower/upper zone coords @@ -485,7 +459,7 @@ boolean isTemp() { void addAlphaModel( HdPlugin plugin, MaterialManager materialManager, - int vao, + GLVao vao, int tboF, Model model, ModelOverride modelOverride, @@ -717,7 +691,7 @@ private void cleanAlphaModels(List alphaModels) { private static int alphaFaceCount; private static int lastDrawMode; - private static int lastVao; + private static GLVao lastVao; private static int lastTboF; private static int lastzx, lastzz; @@ -875,7 +849,7 @@ void renderAlpha( if (m.sortedFaces == null || m.sortedFacesLen <= 0 || !ZoneRenderer.eboAlphaMapped.isMapped()) continue; - if ((long) (ZoneRenderer.eboAlphaOffset + m.sortedFacesLen) * Integer.BYTES < ZoneRenderer.eboAlpha.size) { + if ((long) (ZoneRenderer.eboAlphaOffset + m.sortedFacesLen) * Integer.BYTES < eboAlpha.size) { lastDrawMode = STATIC; m.eboOffset = ZoneRenderer.eboAlphaOffset - ZoneRenderer.eboAlphaPrevOffset; alphaFaceCount += m.sortedFacesLen / 3; diff --git a/src/main/java/rs117/hd/renderer/zone/ZoneRenderer.java b/src/main/java/rs117/hd/renderer/zone/ZoneRenderer.java index d9efbc7fd2..66f5e7affd 100644 --- a/src/main/java/rs117/hd/renderer/zone/ZoneRenderer.java +++ b/src/main/java/rs117/hd/renderer/zone/ZoneRenderer.java @@ -660,9 +660,10 @@ private void tiledLightingPass() { frameTimer.begin(Timer.DRAW_TILED_LIGHTING); frameTimer.begin(Timer.RENDER_TILED_LIGHTING); - renderState.framebuffer.set(GL_FRAMEBUFFER, plugin.fboTiledLighting); + renderState.setDefaults(); + renderState.drawFramebuffer.set(plugin.fboTiledLighting); renderState.viewport.set(0, 0, plugin.tiledLightingResolution[0], plugin.tiledLightingResolution[1]); - renderState.vao.set(plugin.vaoTri); + renderState.vao.set(plugin.triVao); if (plugin.tiledLightingImageStoreProgram.isValid()) { renderState.program.set(plugin.tiledLightingImageStoreProgram); @@ -674,7 +675,7 @@ private void tiledLightingPass() { int layerCount = plugin.configDynamicLights.getTiledLightingLayers(); for (int layer = 0; layer < layerCount; layer++) { renderState.program.set(plugin.tiledLightingShaderPrograms.get(layer)); - renderState.framebufferTextureLayer.set(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, plugin.texTiledLighting, 0, layer); + renderState.framebufferTextureLayer.set(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, plugin.texTiledLighting, 0, layer); renderState.apply(); glDrawArrays(GL_TRIANGLES, 0, 3); } @@ -691,39 +692,33 @@ private void directionalShadowPass() { frameTimer.begin(Timer.RENDER_SHADOWS); // Render to the shadow depth map - renderState.framebuffer.set(GL_FRAMEBUFFER, plugin.fboShadowMap); + renderState.setDefaults(); + renderState.drawFramebuffer.set(plugin.fboShadowMap); renderState.viewport.set(0, 0, plugin.shadowMapResolution, plugin.shadowMapResolution); - renderState.ido.set(indirectDrawCmds.id); + renderState.ido.set(indirectDrawCmds); renderState.apply(); glClearDepth(1); glClear(GL_DEPTH_BUFFER_BIT); - renderState.enable.set(GL_DEPTH_TEST); - renderState.disable.set(GL_CULL_FACE); + renderState.depthTest.set(true); + renderState.depthMask.set(true); + renderState.cullFace.set(false); renderState.depthFunc.set(GL_LEQUAL); CommandBuffer.SKIP_DEPTH_MASKING = true; directionalCmd.execute(); CommandBuffer.SKIP_DEPTH_MASKING = false; - renderState.disable.set(GL_DEPTH_TEST); - frameTimer.end(Timer.RENDER_SHADOWS); } private void scenePass() { - sceneProgram.use(); - frameTimer.begin(Timer.DRAW_SCENE); - renderState.framebuffer.set(GL_DRAW_FRAMEBUFFER, plugin.fboScene); - if (plugin.msaaSamples > 1) { - renderState.enable.set(GL_MULTISAMPLE); - } else { - renderState.disable.set(GL_MULTISAMPLE); - } + renderState.setDefaults(); + renderState.drawFramebuffer.set(plugin.fboScene); + renderState.multisample.set(plugin.msaaSamples > 1); renderState.viewport.set(0, 0, plugin.sceneResolution[0], plugin.sceneResolution[1]); - renderState.ido.set(indirectDrawCmds.id); renderState.apply(); // Clear scene @@ -743,24 +738,21 @@ private void scenePass() { frameTimer.begin(Timer.RENDER_SCENE); - renderState.enable.set(GL_BLEND); - renderState.enable.set(GL_CULL_FACE); - renderState.enable.set(GL_DEPTH_TEST); + renderState.blend.set(true); + renderState.cullFace.set(true); + renderState.depthTest.set(true); + renderState.depthMask.set(true); renderState.depthFunc.set(GL_GEQUAL); renderState.blendFunc.set(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); + renderState.ido.set(indirectDrawCmds); + renderState.program.set(sceneProgram); + renderState.apply(); // Render the scene sceneCmd.execute(); // TODO: Filler tiles frameTimer.end(Timer.RENDER_SCENE); - - // Done rendering the scene - renderState.disable.set(GL_BLEND); - renderState.disable.set(GL_CULL_FACE); - renderState.disable.set(GL_DEPTH_TEST); - renderState.apply(); - frameTimer.end(Timer.DRAW_SCENE); } @@ -1023,23 +1015,26 @@ public void draw(int overlayColor) { tiledLightingPass(); directionalShadowPass(); scenePass(); + renderState.setDefaults(); } if (sceneFboValid && plugin.sceneResolution != null && plugin.sceneViewport != null) { - glBindFramebuffer(GL_READ_FRAMEBUFFER, plugin.fboScene); + renderState.readFramebuffer.set(plugin.fboScene); if (plugin.fboSceneResolve != 0) { // Blit from the scene FBO to the multisample resolve FBO - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, plugin.fboSceneResolve); + renderState.drawFramebuffer.set(plugin.fboSceneResolve); + renderState.apply(); glBlitFramebuffer( 0, 0, plugin.sceneResolution[0], plugin.sceneResolution[1], 0, 0, plugin.sceneResolution[0], plugin.sceneResolution[1], GL_COLOR_BUFFER_BIT, GL_NEAREST ); - glBindFramebuffer(GL_READ_FRAMEBUFFER, plugin.fboSceneResolve); + renderState.readFramebuffer.set(plugin.fboSceneResolve); } // Blit from the resolved FBO to the default FBO - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, plugin.awtContext.getFramebuffer(false)); + renderState.drawFramebuffer.set(plugin.awtContext.getFramebuffer(false)); + renderState.apply(); glBlitFramebuffer( 0, 0, @@ -1053,7 +1048,8 @@ public void draw(int overlayColor) { config.sceneScalingMode().glFilter ); } else { - glBindFramebuffer(GL_FRAMEBUFFER, plugin.awtContext.getFramebuffer(false)); + renderState.framebuffer.set(plugin.awtContext.getFramebuffer(false)); + renderState.apply(); glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT); } diff --git a/src/main/java/rs117/hd/renderer/zone/ZoneUploadJob.java b/src/main/java/rs117/hd/renderer/zone/ZoneUploadJob.java index a9e2ed6508..3b0ea0ab77 100644 --- a/src/main/java/rs117/hd/renderer/zone/ZoneUploadJob.java +++ b/src/main/java/rs117/hd/renderer/zone/ZoneUploadJob.java @@ -8,7 +8,6 @@ import rs117.hd.utils.jobs.Job; import static org.lwjgl.opengl.GL33C.*; -import static rs117.hd.renderer.zone.ZoneRenderer.eboAlpha; import static rs117.hd.utils.buffer.GLBuffer.MAP_WRITE; @Slf4j @@ -77,7 +76,7 @@ private void mapZoneVertexBuffers() { f.map(MAP_WRITE); } - zone.initialize(o, a, f, eboAlpha.id); + zone.initialize(o, a, f); zone.setMetadata(viewContext, sceneContext, x, z); } catch (Throwable ex) { log.warn( diff --git a/src/main/java/rs117/hd/utils/CommandBuffer.java b/src/main/java/rs117/hd/utils/CommandBuffer.java index 410711c79b..c8a4229578 100644 --- a/src/main/java/rs117/hd/utils/CommandBuffer.java +++ b/src/main/java/rs117/hd/utils/CommandBuffer.java @@ -8,9 +8,11 @@ import lombok.extern.slf4j.Slf4j; import org.lwjgl.system.MemoryStack; import rs117.hd.opengl.GLFence; +import rs117.hd.opengl.GLVao; import rs117.hd.opengl.shader.ShaderProgram; import rs117.hd.overlays.FrameTimer; import rs117.hd.overlays.Timer; +import rs117.hd.utils.buffer.GLBuffer; import rs117.hd.utils.buffer.GpuIntBuffer; import static org.lwjgl.opengl.GL33C.*; @@ -32,7 +34,6 @@ public class CommandBuffer { private static final int GL_DRAW_CALL_TYPE_COUNT = 6; private static final int GL_BIND_VERTEX_ARRAY_TYPE = 6; - private static final int GL_BIND_ELEMENTS_ARRAY_TYPE = 7; private static final int GL_BIND_INDIRECT_ARRAY_TYPE = 8; private static final int GL_BIND_TEXTURE_UNIT_TYPE = 9; private static final int GL_DEPTH_MASK_TYPE = 10; @@ -89,9 +90,9 @@ public boolean isEmpty() { return writeHead == 0; } - public void BindVertexArray(int vao) { + public void BindVertexArray(GLVao vao) { ensureCapacity(1); - cmd[writeHead++] = GL_BIND_VERTEX_ARRAY_TYPE & 0xFF | (long) vao << 8; + cmd[writeHead++] = GL_BIND_VERTEX_ARRAY_TYPE & 0xFF | (long) writeObject(vao) << 8; } public void FenceSync(GLFence fence, int condition) { @@ -100,14 +101,9 @@ public void FenceSync(GLFence fence, int condition) { cmd[writeHead++] = writeObject(fence); } - public void BindElementsArray(int ebo) { + public void BindIndirectArray(GLBuffer ido) { ensureCapacity(1); - cmd[writeHead++] = GL_BIND_ELEMENTS_ARRAY_TYPE & 0xFF | (long) ebo << 8; - } - - public void BindIndirectArray(int ido) { - ensureCapacity(1); - cmd[writeHead++] = GL_BIND_INDIRECT_ARRAY_TYPE & 0xFF | (long) ido << 8; + cmd[writeHead++] = GL_BIND_INDIRECT_ARRAY_TYPE & 0xFF | (long) writeObject(ido) << 8; } public void BindTextureUnit(int type, int texId, int bindingIndex) { @@ -263,18 +259,17 @@ public void MultiDrawArraysIndirect(int mode, int[] vertexOffsets, int[] vertexC cmd[writeHead++] = (long) indirectOffset * Integer.BYTES; } - public void Enable(int capability) { - Toggle(capability, true); + public void Enable(RenderState.GLToggle toggle) { + Toggle(toggle, true); } - public void Disable(int capability) { - Toggle(capability, false); + public void Disable(RenderState.GLToggle toggle) { + Toggle(toggle, false); } - public void Toggle(int capability, boolean enabled) { - ensureCapacity(2); - cmd[writeHead++] = GL_TOGGLE_TYPE; - cmd[writeHead++] = (enabled ? 1L : 0) << 32 | capability & INT_MASK; + public void Toggle(RenderState.GLToggle toggle, boolean enabled) { + ensureCapacity(1); + cmd[writeHead++] = GL_TOGGLE_TYPE | (enabled ? 1L : 0) << 8 | (long) writeObject(toggle) << 9; } public void append(CommandBuffer other) { @@ -316,15 +311,13 @@ public void execute() { break; } case GL_BIND_VERTEX_ARRAY_TYPE: { - renderState.vao.set((int) (data >> 8)); - break; - } - case GL_BIND_ELEMENTS_ARRAY_TYPE: { - renderState.ebo.set((int) (data >> 8)); + int objectIdx = (int) (data >> 8); + renderState.vao.set((GLVao) objects[objectIdx]); break; } case GL_BIND_INDIRECT_ARRAY_TYPE: { - renderState.ido.set((int) (data >> 8)); + int objectIdx = (int) (data >> 8); + renderState.ido.set((GLBuffer) objects[objectIdx]); break; } case GL_BIND_TEXTURE_UNIT_TYPE: { @@ -343,13 +336,9 @@ public void execute() { break; } case GL_TOGGLE_TYPE: { - long packed = cmd[readHead++]; - int capability = (int) (packed & INT_MASK); - if ((packed >> 32) != 0) { - renderState.enable.set(capability); - } else { - renderState.disable.set(capability); - } + int objectIdx = (int) (data >> 9); + RenderState.GLToggle capability = (RenderState.GLToggle) objects[objectIdx]; + capability.set(((data >> 9) & 1) == 1); break; } case GL_FENCE_SYNC: { diff --git a/src/main/java/rs117/hd/utils/RenderState.java b/src/main/java/rs117/hd/utils/RenderState.java index 009a60eba1..31ac1d4e77 100644 --- a/src/main/java/rs117/hd/utils/RenderState.java +++ b/src/main/java/rs117/hd/utils/RenderState.java @@ -3,54 +3,94 @@ import java.util.ArrayList; import java.util.List; import java.util.function.Supplier; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import rs117.hd.opengl.GLState; +import rs117.hd.opengl.GLVao; import rs117.hd.opengl.shader.ShaderProgram; +import rs117.hd.utils.buffer.GLBuffer; import static org.lwjgl.opengl.GL33C.*; import static org.lwjgl.opengl.GL40.GL_DRAW_INDIRECT_BUFFER; +@Slf4j public final class RenderState { private final List states = new ArrayList<>(); - public final GLBindFramebuffer framebuffer = addState(GLBindFramebuffer::new); - public final GLFramebufferTextureLayer framebufferTextureLayer = addState(GLFramebufferTextureLayer::new); - public final GLDrawBuffer drawBuffer = addState(GLDrawBuffer::new); - public final GLShaderProgram program = addState(GLShaderProgram::new); - public final GLViewport viewport = addState(GLViewport::new); - public final GLBindVAO vao = addState(GLBindVAO::new); - public final GLBindEBO ebo = addState(GLBindEBO::new); - public final GLBindIDO ido = addState(GLBindIDO::new); - public final GLBindUBO ubo = addState(GLBindUBO::new); - public final GLDepthMask depthMask = addState(GLDepthMask::new); - public final GLDepthFunc depthFunc = addState(GLDepthFunc::new); - public final GLColorMask colorMask = addState(GLColorMask::new); - public final GLBlendFunc blendFunc = addState(GLBlendFunc::new); - public final GLEnable enable = addState(GLEnable::new); - public final GLDisable disable = addState(GLDisable::new); + public final GLBindFramebuffer framebuffer = createState(GLBindFramebuffer::new); + public final GLBindDrawFramebuffer drawFramebuffer = createState(GLBindDrawFramebuffer::new); + public final GLBindReadFramebuffer readFramebuffer = createState(GLBindReadFramebuffer::new); + public final GLFramebufferTextureLayer framebufferTextureLayer = createState(GLFramebufferTextureLayer::new); + public final GLDrawBuffer drawBuffer = createState(GLDrawBuffer::new); + public final GLShaderProgram program = createState(GLShaderProgram::new); + public final GLViewport viewport = createState(GLViewport::new); + public final GLBindVAO vao = createState(GLBindVAO::new); + public final GLBindIDO ido = createState(GLBindIDO::new); + public final GLBindUBO ubo = createState(GLBindUBO::new); + public final GLDepthMask depthMask = createState(GLDepthMask::new); + public final GLDepthFunc depthFunc = createState(GLDepthFunc::new); + public final GLColorMask colorMask = createState(GLColorMask::new); + public final GLBlendFunc blendFunc = createState(GLBlendFunc::new); + public final GLToggle blend = addState(new GLToggle(GL_BLEND, false)); + public final GLToggle cullFace = addState(new GLToggle(GL_CULL_FACE, true)); + public final GLToggle depthTest = addState(new GLToggle(GL_DEPTH_TEST, false)); + public final GLToggle multisample = addState(new GLToggle(GL_MULTISAMPLE, false)); public void apply() { for (GLState state : states) state.apply(); } + public void setDefaults(){ + for (GLState state : states) + state.setDefault(); + } + public void reset() { for (GLState state : states) state.reset(); } - private T addState(Supplier supplier) { + public void printState() { + StringBuffer sb = new StringBuffer(); + for (GLState state : states) + state.printState(sb); + log.debug("GLRenderState:\n{}", sb.toString().trim()); + } + + private T addState(T state) { + states.add(state); + return state; + } + + private T createState(Supplier supplier) { T state = supplier.get(); states.add(state); return state; } - public static final class GLBindFramebuffer extends GLState.IntArray { - private GLBindFramebuffer() { - super(2); - } + public static class GLBindFramebuffer extends GLState.Int { + @Override + protected void applyValue(int value) { glBindFramebuffer(GL_FRAMEBUFFER, value); } + + @Override + public void setDefault() { set(0); } + } + + public static class GLBindReadFramebuffer extends GLState.Int { + @Override + protected void applyValue(int value) { glBindFramebuffer(GL_READ_FRAMEBUFFER, value); } + + @Override + public void setDefault() { set(0); } + } + + public static class GLBindDrawFramebuffer extends GLState.Int { + @Override + protected void applyValue(int value) { glBindFramebuffer(GL_DRAW_FRAMEBUFFER, value); } @Override - protected void applyValues(int[] values) { glBindFramebuffer(values[0], values[1]); } + public void setDefault() { set(0); } } public static final class GLFramebufferTextureLayer extends GLState.IntArray { @@ -58,57 +98,85 @@ public static final class GLFramebufferTextureLayer extends GLState.IntArray { @Override protected void applyValues(int[] values) { + if (values[0] == 0) + return; glFramebufferTextureLayer(values[0], values[1], values[2], values[3], values[4]); } + + @Override + public void setDefault() { set(0, 0, 0, 0, 0); } } public static final class GLViewport extends GLState.IntArray { private GLViewport() { super(4); } - @Override protected void applyValues(int[] values) { glViewport(values[0], values[1], values[2], values[3]); } + @Override + public void setDefault() { set(0, 0, 0, 0); } } public static final class GLShaderProgram extends GLState.Object { @Override - protected void applyValue(ShaderProgram program) { program.use(); } + protected void applyValue(ShaderProgram program) { + if (program != null) { + program.use(); + } else { + glUseProgram(0); + } + } + @Override + public void setDefault() { set(null); } } public static final class GLDrawBuffer extends GLState.Int { @Override protected void applyValue(int buf) { glDrawBuffer(buf); } + @Override + public void setDefault() {} } - public static final class GLBindVAO extends GLState.Int { + public static final class GLBindVAO extends GLState.Object { @Override - protected void applyValue(int vao) { glBindVertexArray(vao); } - } + protected void applyValue(GLVao vao) { + if (getAppliedValue() != null) + getAppliedValue().unbind(); + + if (vao != null) + vao.bind(); - public static final class GLBindEBO extends GLState.Int { + } @Override - protected void applyValue(int ebo) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); } + public void setDefault() { set(null); } } - public static final class GLBindIDO extends GLState.Int { + public static final class GLBindIDO extends GLState.Object { + @Override + protected void applyValue(GLBuffer ido) { glBindBuffer(GL_DRAW_INDIRECT_BUFFER, ido != null ? ido.id : 0); } @Override - protected void applyValue(int ebo) { glBindBuffer(GL_DRAW_INDIRECT_BUFFER, ebo); } + public void setDefault() { set(null); } } public static final class GLBindUBO extends GLState.Int { @Override protected void applyValue(int ubo) { glBindBuffer(GL_UNIFORM_BUFFER, ubo); } + @Override + public void setDefault() { set(0); } } public static final class GLDepthMask extends GLState.Bool { @Override protected void applyValue(boolean enabled) { glDepthMask(enabled); } + @Override + public void setDefault() { set(true); } } public static final class GLDepthFunc extends GLState.Int { @Override protected void applyValue(int func) { glDepthFunc(func); } + @Override + public void setDefault() { set(GL_LESS); } } public static final class GLBlendFunc extends GLState.IntArray { @@ -118,6 +186,8 @@ private GLBlendFunc() { @Override protected void applyValues(int[] values) { glBlendFuncSeparate(values[0], values[1], values[2], values[3]); } + @Override + public void setDefault() { set(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO); } } public static final class GLColorMask extends GLState.BoolArray { @@ -127,25 +197,25 @@ private GLColorMask() { @Override protected void applyValues(boolean[] values) { glColorMask(values[0], values[1], values[2], values[3]); } + @Override + public void setDefault() { set(true, true, true, true); } } - public final class GLEnable extends GLState.IntSet { - @Override - protected void applyTarget(int target) { glEnable(target); } + @RequiredArgsConstructor + public static final class GLToggle extends GLState.Bool { + private final int target; + private final boolean defaultState; - public void set(int target) { - add(target); - disable.remove(target); + @Override + protected void applyValue(boolean value) { + if (value) { + glEnable(target); + } else { + glDisable(target); + } } - } - public final class GLDisable extends GLState.IntSet { @Override - protected void applyTarget(int target) { glDisable(target); } - - public void set(int target) { - add(target); - enable.remove(target); - } + public void setDefault() { set(defaultState); } } } diff --git a/src/main/java/rs117/hd/utils/buffer/GLBuffer.java b/src/main/java/rs117/hd/utils/buffer/GLBuffer.java index 971839fc68..58d027a43b 100644 --- a/src/main/java/rs117/hd/utils/buffer/GLBuffer.java +++ b/src/main/java/rs117/hd/utils/buffer/GLBuffer.java @@ -288,8 +288,9 @@ public void upload(IntBuffer data, long byteOffset) { )); } - public void upload(FloatBuffer data) { + public GLBuffer upload(FloatBuffer data) { upload(data, 0); + return this; } public void upload(FloatBuffer data, long byteOffset) {