From d4313180220e1f8f2e53261bf294f14adf3c8314 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Fri, 20 Aug 2021 15:05:41 -0700 Subject: [PATCH] Errors and pools - GlException and error checking WIP - Put all models within a material in a ModelPool - Instancers directly accept IModels - ModelPools are very WIP --- .../backend/gl/attrib/VertexAttribSpec.java | 2 + .../backend/gl/buffer/MappedBufferRange.java | 9 + .../backend/gl/buffer/MappedFullBuffer.java | 11 + .../backend/gl/buffer/PersistentGlBuffer.java | 13 +- .../gl/buffer/PersistentMappedBuffer.java | 27 ++- .../flywheel/backend/gl/error/GlError.java | 38 +++ .../backend/gl/error/GlException.java | 38 +++ .../backend/gl/versioned/BufferStorage.java | 14 +- .../backend/gl/versioned/GlCompat.java | 3 + .../gl/versioned/instancing/BaseVertex.java | 62 +++++ .../versioned/instancing/DrawInstanced.java | 7 +- .../backend/instancing/Instancer.java | 54 ++--- .../backend/material/InstanceMaterial.java | 14 +- .../backend/material/MaterialManager.java | 1 - .../backend/material/MaterialRenderer.java | 11 +- .../flywheel/backend/model/BufferedModel.java | 10 +- .../backend/model/IBufferedModel.java | 8 +- .../flywheel/backend/model/ModelPool.java | 229 ++++++++++++++++++ .../jozufozu/flywheel/core/QuadConverter.java | 20 +- .../jozufozu/flywheel/util/StringUtil.java | 11 + 20 files changed, 513 insertions(+), 69 deletions(-) create mode 100644 src/main/java/com/jozufozu/flywheel/backend/gl/error/GlError.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/gl/error/GlException.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/gl/versioned/instancing/BaseVertex.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/model/ModelPool.java diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/attrib/VertexAttribSpec.java b/src/main/java/com/jozufozu/flywheel/backend/gl/attrib/VertexAttribSpec.java index 373f4cfb6..7a57032dd 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/attrib/VertexAttribSpec.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/attrib/VertexAttribSpec.java @@ -1,6 +1,8 @@ package com.jozufozu.flywheel.backend.gl.attrib; import org.lwjgl.opengl.GL20; +import org.lwjgl.opengl.GL44; +import org.lwjgl.opengl.GL46; import com.jozufozu.flywheel.backend.gl.GlNumericType; diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/MappedBufferRange.java b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/MappedBufferRange.java index af0485c1e..e9df6a807 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/MappedBufferRange.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/MappedBufferRange.java @@ -1,6 +1,9 @@ package com.jozufozu.flywheel.backend.gl.buffer; import com.jozufozu.flywheel.backend.Backend; +import com.jozufozu.flywheel.backend.gl.error.GlError; +import com.jozufozu.flywheel.backend.gl.error.GlException; +import com.jozufozu.flywheel.util.StringUtil; public class MappedBufferRange extends MappedBuffer { @@ -27,6 +30,12 @@ public class MappedBufferRange extends MappedBuffer { protected void checkAndMap() { if (!mapped) { setInternal(Backend.getInstance().compat.mapBufferRange.mapBuffer(owner.type, offset, length, access)); + + GlError error = GlError.poll(); + + if (error != null) { + throw new GlException(error, StringUtil.args("mapBufferRange", owner.type, offset, length, access)); + } mapped = true; } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/MappedFullBuffer.java b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/MappedFullBuffer.java index 36ebb41d2..4ed9f1fde 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/MappedFullBuffer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/MappedFullBuffer.java @@ -2,6 +2,10 @@ package com.jozufozu.flywheel.backend.gl.buffer; import org.lwjgl.opengl.GL15; +import com.jozufozu.flywheel.backend.gl.error.GlError; +import com.jozufozu.flywheel.backend.gl.error.GlException; +import com.jozufozu.flywheel.util.StringUtil; + public class MappedFullBuffer extends MappedBuffer { MappedBufferUsage usage; @@ -15,6 +19,13 @@ public class MappedFullBuffer extends MappedBuffer { protected void checkAndMap() { if (!mapped) { setInternal(GL15.glMapBuffer(owner.type.glEnum, usage.glEnum)); + + GlError error = GlError.poll(); + + if (error != null) { + throw new GlException(error, StringUtil.args("mapBuffer", owner.type, usage)); + } + mapped = true; } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/PersistentGlBuffer.java b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/PersistentGlBuffer.java index b58a9f43b..9bab9c607 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/PersistentGlBuffer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/PersistentGlBuffer.java @@ -6,6 +6,9 @@ import java.nio.ByteBuffer; import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.gl.GlFence; +import com.jozufozu.flywheel.backend.gl.error.GlError; +import com.jozufozu.flywheel.backend.gl.error.GlException; +import com.jozufozu.flywheel.util.StringUtil; public class PersistentGlBuffer extends GlBuffer { @@ -35,11 +38,19 @@ public class PersistentGlBuffer extends GlBuffer { if (buffer != null) { deleteInternal(handle()); _create(); + + bind(); } fence.clear(); - Backend.getInstance().compat.bufferStorage.bufferStorage(type.glEnum, size, flags); + Backend.getInstance().compat.bufferStorage.bufferStorage(type, size, flags); + + GlError error = GlError.poll(); + + if (error != null) { + throw new GlException(error, StringUtil.args("bufferStorage", type, size, flags)); + } buffer = new PersistentMappedBuffer(this); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/PersistentMappedBuffer.java b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/PersistentMappedBuffer.java index 9d3dbcfdb..e2f46728f 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/PersistentMappedBuffer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/PersistentMappedBuffer.java @@ -2,25 +2,42 @@ package com.jozufozu.flywheel.backend.gl.buffer; import java.nio.ByteBuffer; -import org.lwjgl.opengl.GL15; -import org.lwjgl.opengl.GL44; -import org.lwjgl.opengl.GL46; - import com.jozufozu.flywheel.backend.Backend; +import com.jozufozu.flywheel.backend.gl.error.GlError; +import com.jozufozu.flywheel.backend.gl.error.GlException; +import com.jozufozu.flywheel.util.StringUtil; public class PersistentMappedBuffer extends MappedBuffer { + private final long offset; + private final long length; PersistentGlBuffer owner; public PersistentMappedBuffer(PersistentGlBuffer buffer) { super(buffer); owner = buffer; + offset = 0; + length = owner.size; - ByteBuffer byteBuffer = Backend.getInstance().compat.mapBufferRange.mapBuffer(owner.type, 0, owner.size, owner.flags); + ByteBuffer byteBuffer = Backend.getInstance().compat.mapBufferRange.mapBuffer(owner.type, offset, length, owner.flags); + + GlError error = GlError.poll(); + + if (error != null) { + throw new GlException(error, StringUtil.args("mapBuffer", owner.type, offset, length, owner.flags)); + } setInternal(byteBuffer); } + @Override + public MappedBuffer position(int p) { + if (p < offset || p >= offset + length) { + throw new IndexOutOfBoundsException("Index " + p + " is not mapped"); + } + return super.position(p - (int) offset); + } + @Override public void flush() { diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/error/GlError.java b/src/main/java/com/jozufozu/flywheel/backend/gl/error/GlError.java new file mode 100644 index 000000000..f96cfd207 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/error/GlError.java @@ -0,0 +1,38 @@ +package com.jozufozu.flywheel.backend.gl.error; + +import org.lwjgl.opengl.GL20; +import org.lwjgl.opengl.GL30; + +import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; + +public enum GlError { + INVALID_ENUM(GL20.GL_INVALID_ENUM), + INVALID_VALUE(GL20.GL_INVALID_VALUE), + INVALID_OPERATION(GL20.GL_INVALID_OPERATION), + INVALID_FRAMEBUFFER_OPERATION(GL30.GL_INVALID_FRAMEBUFFER_OPERATION), + OUT_OF_MEMORY(GL20.GL_OUT_OF_MEMORY), + STACK_UNDERFLOW(GL20.GL_STACK_UNDERFLOW), + STACK_OVERFLOW(GL20.GL_STACK_OVERFLOW), + ; + + private static final Int2ObjectMap errorLookup = new Int2ObjectArrayMap<>(); + + static { + errorLookup.defaultReturnValue(null); + for (GlError value : values()) { + errorLookup.put(value.glEnum, value); + } + } + + final int glEnum; + + GlError(int glEnum) { + this.glEnum = glEnum; + } + + + public static GlError poll() { + return errorLookup.get(GL20.glGetError()); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/error/GlException.java b/src/main/java/com/jozufozu/flywheel/backend/gl/error/GlException.java new file mode 100644 index 000000000..9c080382a --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/error/GlException.java @@ -0,0 +1,38 @@ +package com.jozufozu.flywheel.backend.gl.error; + +public class GlException extends RuntimeException { + + final GlError errorCode; + + @Override + public String toString() { + String s = getClass().getName(); + String message = getLocalizedMessage(); + String withCode = s + ": " + errorCode; + return (message != null) ? (withCode + ": " + message) : withCode; + } + + public GlException(GlError errorCode) { + this.errorCode = errorCode; + } + + public GlException(GlError errorCode, String message) { + super(message); + this.errorCode = errorCode; + } + + public GlException(GlError errorCode, String message, Throwable cause) { + super(message, cause); + this.errorCode = errorCode; + } + + public GlException(GlError errorCode, Throwable cause) { + super(cause); + this.errorCode = errorCode; + } + + public GlException(GlError errorCode, String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + this.errorCode = errorCode; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/versioned/BufferStorage.java b/src/main/java/com/jozufozu/flywheel/backend/gl/versioned/BufferStorage.java index 53964f078..a1d787037 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/versioned/BufferStorage.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/versioned/BufferStorage.java @@ -4,6 +4,8 @@ import org.lwjgl.opengl.ARBBufferStorage; import org.lwjgl.opengl.GL44; import org.lwjgl.opengl.GLCapabilities; +import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; + public enum BufferStorage implements GlVersioned { GL44CORE { @@ -13,8 +15,8 @@ public enum BufferStorage implements GlVersioned { } @Override - public void bufferStorage(int target, long size, int flags) { - GL44.glBufferStorage(target, size, flags); + public void bufferStorage(GlBufferType target, long size, int flags) { + GL44.glBufferStorage(target.glEnum, size, flags); } }, ARB { @@ -24,8 +26,8 @@ public enum BufferStorage implements GlVersioned { } @Override - public void bufferStorage(int target, long size, int flags) { - ARBBufferStorage.glBufferStorage(target, size, flags); + public void bufferStorage(GlBufferType target, long size, int flags) { + ARBBufferStorage.glBufferStorage(target.glEnum, size, flags); } }, UNSUPPORTED { @@ -35,10 +37,10 @@ public enum BufferStorage implements GlVersioned { } @Override - public void bufferStorage(int target, long size, int flags) { + public void bufferStorage(GlBufferType target, long size, int flags) { throw new UnsupportedOperationException(); } }; - public abstract void bufferStorage(int target, long size, int flags); + public abstract void bufferStorage(GlBufferType target, long size, int flags); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/versioned/GlCompat.java b/src/main/java/com/jozufozu/flywheel/backend/gl/versioned/GlCompat.java index 348fa7d86..0ef1aae06 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/versioned/GlCompat.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/versioned/GlCompat.java @@ -12,6 +12,7 @@ import org.lwjgl.system.MemoryUtil; import com.jozufozu.flywheel.backend.gl.versioned.framebuffer.Blit; import com.jozufozu.flywheel.backend.gl.versioned.framebuffer.Framebuffer; import com.jozufozu.flywheel.backend.gl.versioned.instancing.DrawInstanced; +import com.jozufozu.flywheel.backend.gl.versioned.instancing.BaseVertex; import com.jozufozu.flywheel.backend.gl.versioned.instancing.InstancedArrays; import com.jozufozu.flywheel.backend.gl.versioned.instancing.VertexArrayObject; @@ -32,6 +33,7 @@ public class GlCompat { public final BufferStorage bufferStorage; public final RGPixelFormat pixelFormat; + public final BaseVertex baseVertex; public GlCompat(GLCapabilities caps) { mapBufferRange = getLatest(MapBufferRange.class, caps); @@ -39,6 +41,7 @@ public class GlCompat { vao = getLatest(VertexArrayObject.class, caps); instancedArrays = getLatest(InstancedArrays.class, caps); drawInstanced = getLatest(DrawInstanced.class, caps); + baseVertex = getLatest(BaseVertex.class, caps); blit = getLatest(Blit.class, caps); fbo = getLatest(Framebuffer.class, caps); bufferStorage = getLatest(BufferStorage.class, caps); diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/versioned/instancing/BaseVertex.java b/src/main/java/com/jozufozu/flywheel/backend/gl/versioned/instancing/BaseVertex.java new file mode 100644 index 000000000..1932032cc --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/versioned/instancing/BaseVertex.java @@ -0,0 +1,62 @@ +package com.jozufozu.flywheel.backend.gl.versioned.instancing; + +import org.lwjgl.opengl.ARBDrawElementsBaseVertex; +import org.lwjgl.opengl.ARBDrawInstanced; +import org.lwjgl.opengl.EXTDrawInstanced; +import org.lwjgl.opengl.GL32; +import org.lwjgl.opengl.GLCapabilities; + +import com.jozufozu.flywheel.backend.gl.GlNumericType; +import com.jozufozu.flywheel.backend.gl.GlPrimitive; +import com.jozufozu.flywheel.backend.gl.versioned.GlVersioned; + +public enum BaseVertex implements GlVersioned { + GL31_CORE { + @Override + public boolean supported(GLCapabilities caps) { + return caps.OpenGL31; + } + + @Override + public void drawElementsInstancedBaseVertex(GlPrimitive mode, int elementCount, GlNumericType type, long indices, int instanceCount, int baseVertex) { + GL32.glDrawElementsInstancedBaseVertex(mode.glEnum, elementCount, type.getGlEnum(), indices, instanceCount, baseVertex); + } + + @Override + public void drawElementsBaseVertex(GlPrimitive mode, int elementCount, GlNumericType type, long indices, int baseVertex) { + GL32.glDrawElementsBaseVertex(mode.glEnum, elementCount, type.getGlEnum(), indices, baseVertex); + } + }, + ARB { + @Override + public boolean supported(GLCapabilities caps) { + return caps.GL_ARB_draw_elements_base_vertex; + } + + @Override + public void drawElementsInstancedBaseVertex(GlPrimitive mode, int elementCount, GlNumericType type, long indices, int instanceCount, int baseVertex) { + ARBDrawElementsBaseVertex.glDrawElementsInstancedBaseVertex(mode.glEnum, elementCount, type.getGlEnum(), indices, instanceCount, baseVertex); + } + + @Override + public void drawElementsBaseVertex(GlPrimitive mode, int elementCount, GlNumericType type, long indices, int baseVertex) { + ARBDrawElementsBaseVertex.glDrawElementsBaseVertex(mode.glEnum, elementCount, type.getGlEnum(), indices, baseVertex); + } + }, + UNSUPPORTED { + @Override + public boolean supported(GLCapabilities caps) { + return true; + } + }; + + + public void drawElementsInstancedBaseVertex(GlPrimitive mode, int elementCount, GlNumericType type, long indices, int instanceCount, int baseVertex) { + throw new UnsupportedOperationException(); + } + + + public void drawElementsBaseVertex(GlPrimitive mode, int elementCount, GlNumericType type, long indices, int baseVertex) { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/versioned/instancing/DrawInstanced.java b/src/main/java/com/jozufozu/flywheel/backend/gl/versioned/instancing/DrawInstanced.java index c2d73e0c4..25f96a498 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/versioned/instancing/DrawInstanced.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/versioned/instancing/DrawInstanced.java @@ -3,6 +3,7 @@ package com.jozufozu.flywheel.backend.gl.versioned.instancing; import org.lwjgl.opengl.ARBDrawInstanced; import org.lwjgl.opengl.EXTDrawInstanced; import org.lwjgl.opengl.GL31; +import org.lwjgl.opengl.GL46; import org.lwjgl.opengl.GLCapabilities; import com.jozufozu.flywheel.backend.gl.GlNumericType; @@ -10,7 +11,7 @@ import com.jozufozu.flywheel.backend.gl.GlPrimitive; import com.jozufozu.flywheel.backend.gl.versioned.GlVersioned; public enum DrawInstanced implements GlVersioned { - GL31_DRAW_INSTANCED { + GL31_CORE { @Override public boolean supported(GLCapabilities caps) { return caps.OpenGL31; @@ -26,7 +27,7 @@ public enum DrawInstanced implements GlVersioned { GL31.glDrawElementsInstanced(mode.glEnum, elementCount, type.getGlEnum(), indices, primcount); } }, - ARB_DRAW_INSTANCED { + ARB { @Override public boolean supported(GLCapabilities caps) { return caps.GL_ARB_draw_instanced; @@ -42,7 +43,7 @@ public enum DrawInstanced implements GlVersioned { ARBDrawInstanced.glDrawElementsInstancedARB(mode.glEnum, elementCount, type.getGlEnum(), indices, primcount); } }, - EXT_DRAW_INSTANCED { + EXT { @Override public boolean supported(GLCapabilities caps) { return caps.GL_EXT_draw_instanced; diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/Instancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/Instancer.java index c85980c52..0b3d874c4 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/Instancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/Instancer.java @@ -2,7 +2,6 @@ package com.jozufozu.flywheel.backend.instancing; import java.util.ArrayList; import java.util.BitSet; -import java.util.function.Supplier; import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.gl.GlVertexArray; @@ -11,8 +10,8 @@ import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer; import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer; import com.jozufozu.flywheel.backend.material.MaterialSpec; +import com.jozufozu.flywheel.backend.model.ModelPool; import com.jozufozu.flywheel.backend.model.IBufferedModel; -import com.jozufozu.flywheel.backend.model.IndexedModel; import com.jozufozu.flywheel.core.model.IModel; import com.jozufozu.flywheel.util.AttribUtil; @@ -36,25 +35,27 @@ import com.jozufozu.flywheel.util.AttribUtil; */ public class Instancer { - protected final Supplier gen; - protected final VertexFormat instanceFormat; - protected final IInstanceFactory factory; + private final ModelPool modelAllocator; + private final IModel modelData; + private final VertexFormat instanceFormat; + private final IInstanceFactory factory; - protected IBufferedModel model; - protected GlVertexArray vao; - protected GlBuffer instanceVBO; - protected int glBufferSize = -1; - protected int glInstanceCount = 0; + private IBufferedModel model; + private GlVertexArray vao; + private GlBuffer instanceVBO; + private int glBufferSize = -1; + private int glInstanceCount = 0; private boolean deleted; private boolean initialized; - protected final ArrayList data = new ArrayList<>(); + private final ArrayList data = new ArrayList<>(); boolean anyToRemove; boolean anyToUpdate; - public Instancer(Supplier model, MaterialSpec spec) { - this.gen = model; + public Instancer(ModelPool modelAllocator, IModel model, MaterialSpec spec) { + this.modelAllocator = modelAllocator; + this.modelData = model; this.factory = spec.getInstanceFactory(); this.instanceFormat = spec.getInstanceFormat(); } @@ -80,7 +81,6 @@ public class Instancer { } public void render() { - if (!isInitialized()) init(); if (invalid()) return; vao.bind(); @@ -98,28 +98,28 @@ public class Instancer { return deleted || model == null; } - private void init() { + public void init() { + if (isInitialized()) return; + initialized = true; - IModel iModel = gen.get(); - if (iModel.empty()) { - return; - } - - model = new IndexedModel(iModel); vao = new GlVertexArray(); - instanceVBO = GlBuffer.requestPersistent(GlBufferType.ARRAY_BUFFER); + + model = modelAllocator.alloc(modelData) + .setReallocCallback(arenaModel -> { + vao.bind(); + + model.setupState(); + + vao.unbind(); + }); vao.bind(); - // bind the model's vbo to our vao - model.setupState(); - + instanceVBO = GlBuffer.requestPersistent(GlBufferType.ARRAY_BUFFER); AttribUtil.enableArrays(model.getAttributeCount() + instanceFormat.getAttributeCount()); vao.unbind(); - - model.clearState(); } public boolean isInitialized() { diff --git a/src/main/java/com/jozufozu/flywheel/backend/material/InstanceMaterial.java b/src/main/java/com/jozufozu/flywheel/backend/material/InstanceMaterial.java index a8c46059c..efe069d98 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/material/InstanceMaterial.java +++ b/src/main/java/com/jozufozu/flywheel/backend/material/InstanceMaterial.java @@ -1,7 +1,6 @@ package com.jozufozu.flywheel.backend.material; import java.util.concurrent.ExecutionException; -import java.util.function.Consumer; import java.util.function.Supplier; import com.google.common.cache.Cache; @@ -9,6 +8,7 @@ import com.google.common.cache.CacheBuilder; import com.jozufozu.flywheel.backend.RenderWork; import com.jozufozu.flywheel.backend.instancing.InstanceData; import com.jozufozu.flywheel.backend.instancing.Instancer; +import com.jozufozu.flywheel.backend.model.ModelPool; import com.jozufozu.flywheel.core.PartialModel; import com.jozufozu.flywheel.core.model.BlockModel; import com.jozufozu.flywheel.core.model.IModel; @@ -25,12 +25,14 @@ import net.minecraft.util.Direction; */ public class InstanceMaterial { + final ModelPool modelPool; protected final Cache> models; protected final MaterialSpec spec; public InstanceMaterial(MaterialSpec spec) { this.spec = spec; + modelPool = new ModelPool(spec.getModelFormat(), spec.getModelFormat().getStride() * 64); this.models = CacheBuilder.newBuilder() .removalListener(notification -> { Instancer instancer = (Instancer) notification.getValue(); @@ -48,7 +50,7 @@ public class InstanceMaterial { */ public Instancer model(Object key, Supplier modelSupplier) { try { - return models.get(key, () -> new Instancer<>(modelSupplier, spec)); + return models.get(key, () -> new Instancer<>(modelPool, modelSupplier.get(), spec)); } catch (ExecutionException e) { throw new RuntimeException("error creating instancer", e); } @@ -79,6 +81,7 @@ public class InstanceMaterial { public void delete() { models.invalidateAll(); + modelPool.delete(); } /** @@ -90,11 +93,4 @@ public class InstanceMaterial { .forEach(Instancer::clear); } - public void forEachInstancer(Consumer> f) { - for (Instancer model : models.asMap() - .values()) { - f.accept(model); - } - } - } diff --git a/src/main/java/com/jozufozu/flywheel/backend/material/MaterialManager.java b/src/main/java/com/jozufozu/flywheel/backend/material/MaterialManager.java index 869be4cff..cb33d618b 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/material/MaterialManager.java +++ b/src/main/java/com/jozufozu/flywheel/backend/material/MaterialManager.java @@ -17,7 +17,6 @@ import com.jozufozu.flywheel.core.shader.WorldProgram; import com.jozufozu.flywheel.util.WeakHashSet; import net.minecraft.client.renderer.ActiveRenderInfo; -import net.minecraft.client.renderer.RenderType; import net.minecraft.inventory.container.PlayerContainer; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; diff --git a/src/main/java/com/jozufozu/flywheel/backend/material/MaterialRenderer.java b/src/main/java/com/jozufozu/flywheel/backend/material/MaterialRenderer.java index bf4c54b0b..7b27259c7 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/material/MaterialRenderer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/material/MaterialRenderer.java @@ -1,5 +1,6 @@ package com.jozufozu.flywheel.backend.material; +import java.util.Collection; import java.util.function.Consumer; import java.util.function.Supplier; @@ -24,6 +25,14 @@ public class MaterialRenderer

{ public void render(Matrix4f viewProjection, double camX, double camY, double camZ) { if (material.nothingToRender()) return; + Collection> instancers = material.models.asMap() + .values(); + + // initialize all uninitialized instancers... + instancers.forEach(Instancer::init); + // ...and then flush the model arena in case anything was marked for upload + material.modelPool.flush(); + P program = this.program.get(); program.bind(); @@ -32,7 +41,7 @@ public class MaterialRenderer

{ setupFunc.accept(program); - material.forEachInstancer(Instancer::render); + instancers.forEach(Instancer::render); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/BufferedModel.java b/src/main/java/com/jozufozu/flywheel/backend/model/BufferedModel.java index 6c02ffe30..0f7f621e8 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/BufferedModel.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/BufferedModel.java @@ -2,6 +2,8 @@ package com.jozufozu.flywheel.backend.model; import static org.lwjgl.opengl.GL20.glDrawArrays; +import org.lwjgl.opengl.GL44; + import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.gl.GlPrimitive; import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; @@ -37,6 +39,10 @@ public class BufferedModel implements IBufferedModel { vbo.unbind(); } + public boolean isDeleted() { + return deleted; + } + public VertexFormat getFormat() { return model.format(); } @@ -45,10 +51,6 @@ public class BufferedModel implements IBufferedModel { return model.vertexCount(); } - public boolean valid() { - return getVertexCount() > 0 && !deleted; - } - /** * The VBO/VAO should be bound externally. */ diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/IBufferedModel.java b/src/main/java/com/jozufozu/flywheel/backend/model/IBufferedModel.java index 4ac73c572..6db741c06 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/IBufferedModel.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/IBufferedModel.java @@ -8,8 +8,6 @@ public interface IBufferedModel { int getVertexCount(); - boolean valid(); - /** * The VBO/VAO should be bound externally. */ @@ -24,8 +22,14 @@ public interface IBufferedModel { */ void drawInstances(int instanceCount); + boolean isDeleted(); + void delete(); + default boolean valid() { + return getVertexCount() > 0 && !isDeleted(); + } + default int getAttributeCount() { return getFormat().getAttributeCount(); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/ModelPool.java b/src/main/java/com/jozufozu/flywheel/backend/model/ModelPool.java new file mode 100644 index 000000000..7e687e1ef --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/model/ModelPool.java @@ -0,0 +1,229 @@ +package com.jozufozu.flywheel.backend.model; + +import java.util.ArrayList; +import java.util.List; + +import com.jozufozu.flywheel.backend.Backend; +import com.jozufozu.flywheel.backend.gl.GlPrimitive; +import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; +import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer; +import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; +import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer; +import com.jozufozu.flywheel.backend.gl.buffer.MappedGlBuffer; +import com.jozufozu.flywheel.core.model.IModel; +import com.jozufozu.flywheel.util.AttribUtil; +import com.jozufozu.flywheel.util.StringUtil; + +public class ModelPool { + + protected final VertexFormat format; + + private final List models = new ArrayList<>(); + + private final List pendingUpload = new ArrayList<>(); + + private final GlBuffer vbo; + private int bufferSize; + + private int vertices; + + private boolean dirty; + private boolean anyToRemove; + + public ModelPool(VertexFormat format, int initialSize) { + this.format = format; + bufferSize = initialSize; + + vbo = new MappedGlBuffer(GlBufferType.ARRAY_BUFFER); + + vbo.bind(); + vbo.alloc(bufferSize); + vbo.unbind(); + } + + /** + * Allocate a model in the arena. + * + * @param model The model to allocate. + * @return A handle to the allocated model. + */ + public PooledModel alloc(IModel model) { + PooledModel bufferedModel = new PooledModel(model, vertices); + vertices += model.vertexCount(); + models.add(bufferedModel); + pendingUpload.add(bufferedModel); + + setDirty(); + return bufferedModel; + } + + public void flush() { + if (dirty) { + if (anyToRemove) processDeletions(); + + vbo.bind(); + if (realloc()) { + uploadAll(); + } else { + uploadPending(); + } + vbo.unbind(); + + dirty = false; + pendingUpload.clear(); + } + } + + private void processDeletions() { + + // remove deleted models + models.removeIf(PooledModel::isDeleted); + + // re-evaluate first vertex for each model + int vertices = 0; + for (PooledModel model : models) { + if (model.first != vertices) + pendingUpload.add(model); + + model.first = vertices; + + vertices += model.getVertexCount(); + } + + this.vertices = vertices; + this.anyToRemove = false; + } + + /** + * Assumes vbo is bound. + * + * @return true if the buffer was reallocated + */ + private boolean realloc() { + int neededSize = vertices * format.getStride(); + if (neededSize > bufferSize) { + bufferSize = neededSize + 128; + vbo.alloc(bufferSize); + + return true; + } + + return false; + } + + private void uploadAll() { + MappedBuffer buffer = vbo.getBuffer(0, bufferSize); + + for (PooledModel model : models) { + model.model.buffer(buffer); + if (model.callback != null) + model.callback.invoke(model); + } + + buffer.flush(); + } + + private void uploadPending() { + MappedBuffer buffer = vbo.getBuffer(0, bufferSize); + + int stride = format.getStride(); + for (PooledModel model : pendingUpload) { + int pos = model.first * stride; + buffer.position(pos); + model.model.buffer(buffer); + if (model.callback != null) + model.callback.invoke(model); + } + pendingUpload.clear(); + + buffer.flush(); + } + + private void setDirty() { + dirty = true; + } + + public void delete() { + vbo.delete(); + } + + public class PooledModel implements IBufferedModel { + + private final ElementBuffer ebo; + private Callback callback; + + private final IModel model; + private int first; + + private boolean remove; + + public PooledModel(IModel model, int first) { + this.model = model; + this.first = first; + ebo = model.createEBO(); + } + + @Override + public VertexFormat getFormat() { + return model.format(); + } + + @Override + public int getVertexCount() { + return model.vertexCount(); + } + + @Override + public void setupState() { + vbo.bind(); + ebo.bind(); + AttribUtil.enableArrays(getAttributeCount()); + getFormat().vertexAttribPointers(0); + } + + @Override + public void clearState() { + AttribUtil.disableArrays(getAttributeCount()); + ebo.unbind(); + vbo.unbind(); + } + + @Override + public void drawCall() { + Backend.getInstance().compat.baseVertex.drawElementsBaseVertex(GlPrimitive.TRIANGLES, ebo.elementCount, ebo.eboIndexType, 0, first); + } + + @Override + public void drawInstances(int instanceCount) { + if (!valid()) return; + + ebo.bind(); + + Backend.log.info(StringUtil.args("drawElementsInstancedBaseVertex", GlPrimitive.TRIANGLES, ebo.elementCount, ebo.eboIndexType, 0, instanceCount, first)); + + Backend.getInstance().compat.baseVertex.drawElementsInstancedBaseVertex(GlPrimitive.TRIANGLES, ebo.elementCount, ebo.eboIndexType, 0, instanceCount, first); + } + + @Override + public boolean isDeleted() { + return false; + } + + @Override + public void delete() { + setDirty(); + anyToRemove = true; + remove = true; + } + + public PooledModel setReallocCallback(Callback callback) { + this.callback = callback; + return this; + } + } + + @FunctionalInterface + public interface Callback { + void invoke(PooledModel arenaModel); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/core/QuadConverter.java b/src/main/java/com/jozufozu/flywheel/core/QuadConverter.java index beb2ab7d2..bdff17230 100644 --- a/src/main/java/com/jozufozu/flywheel/core/QuadConverter.java +++ b/src/main/java/com/jozufozu/flywheel/core/QuadConverter.java @@ -30,14 +30,14 @@ import net.minecraftforge.fml.common.Mod; @Mod.EventBusSubscriber(Dist.CLIENT) public class QuadConverter { - public static final int STARTING_CAPACITY = 42; + public static final int STARTING_CAPACITY = 42; // 255 / 6 = 42 private static QuadConverter INSTANCE; @Nonnull public static QuadConverter getInstance() { if (INSTANCE == null) { - INSTANCE = new QuadConverter(STARTING_CAPACITY); // 255 / 6 = 42 + INSTANCE = new QuadConverter(STARTING_CAPACITY); } return INSTANCE; @@ -157,14 +157,14 @@ public class QuadConverter { * */ private static GlNumericType getSmallestIndexType(int indexCount) { - indexCount = indexCount >>> 8; - if (indexCount == 0) { - return GlNumericType.UBYTE; - } - indexCount = indexCount >>> 8; - if (indexCount == 0) { - return GlNumericType.USHORT; - } +// indexCount = indexCount >>> 8; +// if (indexCount == 0) { +// return GlNumericType.UBYTE; +// } +// indexCount = indexCount >>> 8; +// if (indexCount == 0) { +// return GlNumericType.USHORT; +// } return GlNumericType.UINT; } diff --git a/src/main/java/com/jozufozu/flywheel/util/StringUtil.java b/src/main/java/com/jozufozu/flywheel/util/StringUtil.java index e451a8d43..b4ee5a638 100644 --- a/src/main/java/com/jozufozu/flywheel/util/StringUtil.java +++ b/src/main/java/com/jozufozu/flywheel/util/StringUtil.java @@ -1,6 +1,17 @@ package com.jozufozu.flywheel.util; +import java.util.Arrays; +import java.util.stream.Collectors; + public class StringUtil { + + public static String args(String functionName, Object... args) { + + return functionName + '(' + Arrays.stream(args) + .map(Object::toString) + .collect(Collectors.joining(", ")) + ')'; + } + public static String trimEnd(String value) { int len = value.length(); int st = 0;