diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/Mappable.java b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/Mappable.java new file mode 100644 index 000000000..8c5cddc42 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/Mappable.java @@ -0,0 +1,14 @@ +package com.jozufozu.flywheel.backend.gl.buffer; + +/** + * Interface for generically dealing with mapped buffers. + */ +public interface Mappable { + GlBufferType getType(); + + /** + * Indicates that this buffer need not be #flush()'d for its contents to sync. + * @return true if this buffer is persistently mapped. + */ + boolean isPersistent(); +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/MappedBuffer.java b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/MappedBuffer.java index 60c5e85f8..25155a1d0 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/MappedBuffer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/MappedBuffer.java @@ -1,133 +1,45 @@ package com.jozufozu.flywheel.backend.gl.buffer; import java.nio.ByteBuffer; -import java.nio.FloatBuffer; import org.lwjgl.opengl.GL15; -import org.lwjgl.system.MemoryUtil; -public abstract class MappedBuffer extends VecBuffer implements AutoCloseable { +public class MappedBuffer extends VecBuffer implements AutoCloseable { - protected boolean mapped; - protected final GlBuffer owner; + protected final long offset; + protected final long length; + protected final Mappable owner; - public MappedBuffer(GlBuffer owner) { + public MappedBuffer(Mappable owner, ByteBuffer internal, long offset, long length) { + this.internal = internal; this.owner = owner; + this.offset = offset; + this.length = length; } - public long addr() { - return MemoryUtil.memAddress(this.internal, internal.position()); - } - - protected abstract void checkAndMap(); - /** * Make the changes in client memory available to the GPU. */ public void flush() { - if (mapped) { - GL15.glUnmapBuffer(owner.type.glEnum); - mapped = false; - setInternal(null); + if (owner.isPersistent()) return; + + if (internal == null) return; + + GL15.glUnmapBuffer(owner.getType().glEnum); + internal = null; + } + + @Override + public MappedBuffer position(int p) { + if (p < offset || p >= offset + length) { + throw new IndexOutOfBoundsException("Index " + p + " is not mapped"); } + super.position(p - (int) offset); + return this; } @Override public void close() throws Exception { flush(); } - - public MappedBuffer putFloatArray(float[] floats) { - checkAndMap(); - super.putFloatArray(floats); - return this; - } - - public MappedBuffer putByteArray(byte[] bytes) { - checkAndMap(); - super.putByteArray(bytes); - return this; - } - - public MappedBuffer put(FloatBuffer floats) { - checkAndMap(); - super.put(floats); - return this; - } - - public int position() { - checkAndMap(); - return super.position(); - } - - /** - * Position this buffer relative to the 0-index in GPU memory. - * - * @return This buffer. - */ - public MappedBuffer position(int p) { - checkAndMap(); - super.position(p); - return this; - } - - public MappedBuffer putFloat(float f) { - checkAndMap(); - super.putFloat(f); - return this; - } - - public MappedBuffer putInt(int i) { - checkAndMap(); - super.putInt(i); - return this; - } - - public MappedBuffer putShort(short s) { - checkAndMap(); - super.putShort(s); - return this; - } - - public MappedBuffer put(byte b) { - checkAndMap(); - super.put(b); - return this; - } - - public MappedBuffer put(ByteBuffer b) { - checkAndMap(); - super.put(b); - return this; - } - - public MappedBuffer putVec4(float x, float y, float z, float w) { - checkAndMap(); - super.putVec4(x, y, z, w); - return this; - } - - public MappedBuffer putVec3(float x, float y, float z) { - checkAndMap(); - super.putVec3(x, y, z); - return this; - } - - public MappedBuffer putVec2(float x, float y) { - checkAndMap(); - super.putVec2(x, y); - return this; - } - - public MappedBuffer putVec3(byte x, byte y, byte z) { - checkAndMap(); - super.putVec3(x, y, z); - return this; - } - - public MappedBuffer putVec2(byte x, byte y) { - checkAndMap(); - super.putVec2(x, y); - return this; - } } 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 deleted file mode 100644 index 9385e3efd..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/MappedBufferRange.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.jozufozu.flywheel.backend.gl.buffer; - -import org.lwjgl.opengl.GL30; - -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; -import com.mojang.blaze3d.platform.GlStateManager; - -public class MappedBufferRange extends MappedBuffer { - - long offset, length; - int access; - - public MappedBufferRange(GlBuffer buffer, long offset, long length, int access) { - super(buffer); - this.offset = offset; - this.length = length; - this.access = access; - } - - @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 - protected void checkAndMap() { - if (!mapped) { - setInternal(GL30.glMapBufferRange(owner.type.glEnum, offset, length, access)); - - GlError.pollAndThrow(() -> StringUtil.args("mapBufferRange", owner.type, offset, length, access)); - mapped = true; - } - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/MappedGlBuffer.java b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/MappedGlBuffer.java index 06ee17cfa..8fe3ba243 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/MappedGlBuffer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/MappedGlBuffer.java @@ -5,7 +5,11 @@ import java.nio.ByteBuffer; import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL30; -public class MappedGlBuffer extends GlBuffer { +import com.jozufozu.flywheel.backend.gl.error.GlError; +import com.jozufozu.flywheel.backend.gl.error.GlException; +import com.jozufozu.flywheel.util.StringUtil; + +public class MappedGlBuffer extends GlBuffer implements Mappable { protected final GlBufferUsage usage; @@ -27,6 +31,22 @@ public class MappedGlBuffer extends GlBuffer { } public MappedBuffer getBuffer(int offset, int length) { - return new MappedBufferRange(this, offset, length, GL30.GL_MAP_WRITE_BIT); + ByteBuffer byteBuffer = GL30.glMapBufferRange(type.glEnum, offset, length, GL30.GL_MAP_WRITE_BIT); + + if (byteBuffer == null) { + throw new GlException(GlError.poll(), "Could not map buffer"); + } + + return new MappedBuffer(this, byteBuffer, offset, length); + } + + @Override + public GlBufferType getType() { + return type; + } + + @Override + public boolean isPersistent() { + return false; } } 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 e5dd4348d..1a6b6f646 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,16 +6,19 @@ import static org.lwjgl.opengl.GL44.GL_MAP_PERSISTENT_BIT; import java.nio.ByteBuffer; +import org.lwjgl.opengl.GL30; + 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; +import com.jozufozu.flywheel.util.Unimplemented; -public class PersistentGlBuffer extends GlBuffer { +public class PersistentGlBuffer extends GlBuffer implements Mappable { - private PersistentMappedBuffer buffer; + private MappedBuffer buffer; int flags; long size; @@ -48,14 +51,25 @@ public class PersistentGlBuffer extends GlBuffer { Backend.getInstance().compat.bufferStorage.bufferStorage(type, size, flags); - GlError.pollAndThrow(() -> StringUtil.args("bufferStorage", type, size, flags)); + GlError error = GlError.poll(); + if (error != null) { + // If this error is being thrown but everything seems fine, + // GlError.poll() might be returning an error from something earlier. + throw new GlException(error, StringUtil.args("bufferStorage", type, size, flags)); + } - buffer = new PersistentMappedBuffer(this); + ByteBuffer byteBuffer = GL30.glMapBufferRange(type.glEnum, 0, size, flags); + + if (byteBuffer == null) { + throw new GlException(GlError.poll(), "Could not map buffer"); + } + + buffer = new MappedBuffer(this, byteBuffer, 0, size); } @Override public void upload(ByteBuffer directBuffer) { - + throw new Unimplemented("FIXME: Nothing calls #upload on a persistent buffer as of 12/10/2021."); } @Override @@ -67,4 +81,14 @@ public class PersistentGlBuffer extends GlBuffer { return buffer; } + + @Override + public GlBufferType getType() { + return type; + } + + @Override + public boolean isPersistent() { + return true; + } } 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 deleted file mode 100644 index 124c3bf58..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/PersistentMappedBuffer.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.jozufozu.flywheel.backend.gl.buffer; - -import java.nio.ByteBuffer; - -import org.lwjgl.opengl.GL30; - -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 = GL30.glMapBufferRange(owner.type.glEnum, offset, length, owner.flags); - - GlError.pollAndThrow(() -> 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() { - - } - - @Override - protected void checkAndMap() { - - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/VecBuffer.java b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/VecBuffer.java index 9d36890f1..ab957169c 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/VecBuffer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/VecBuffer.java @@ -24,10 +24,6 @@ public class VecBuffer { return new VecBuffer(buffer); } - protected void setInternal(@Nullable ByteBuffer internal) { - this.internal = internal; - } - public ByteBuffer unwrap() { return internal; } 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 index 9c080382a..ac856a5f1 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/error/GlException.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/error/GlException.java @@ -4,35 +4,12 @@ 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); + super(updateMessage(errorCode, 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; + private static String updateMessage(GlError error, String message) { + return String.format("%s: %s", error, message); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java index 556671492..efc77ce9a 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java @@ -2,6 +2,7 @@ package com.jozufozu.flywheel.backend.instancing.instancing; import java.util.BitSet; +import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.gl.GlVertexArray; import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; @@ -136,9 +137,11 @@ public class GPUInstancer extends AbstractInstancer { final int offset = size * instanceFormat.getStride(); final int length = glBufferSize - offset; if (length > 0) { - instanceVBO.getBuffer(offset, length) - .putByteArray(new byte[length]) - .flush(); + try (MappedBuffer buf = instanceVBO.getBuffer(offset, length)) { + buf.putByteArray(new byte[length]); + } catch (Exception e) { + Flywheel.log.error("Error clearing buffer tail:", e); + } } } @@ -159,16 +162,19 @@ public class GPUInstancer extends AbstractInstancer { final int length = (1 + lastDirty - firstDirty) * stride; if (length > 0) { - MappedBuffer mapped = instanceVBO.getBuffer(offset, length); + try (MappedBuffer mapped = instanceVBO.getBuffer(offset, length)) { - StructWriter writer = type.asInstanced().getWriter(mapped); + StructWriter writer = type.asInstanced() + .getWriter(mapped); - dirtySet.stream() - .forEach(i -> { - writer.seek(i); - writer.write(data.get(i)); - }); - mapped.flush(); + dirtySet.stream() + .forEach(i -> { + writer.seek(i); + writer.write(data.get(i)); + }); + } catch (Exception e) { + Flywheel.log.error("Error updating GPUInstancer:", e); + } } } @@ -180,12 +186,15 @@ public class GPUInstancer extends AbstractInstancer { glBufferSize = requiredSize + stride * 16; instanceVBO.alloc(glBufferSize); - MappedBuffer buffer = instanceVBO.getBuffer(0, glBufferSize); - StructWriter writer = type.asInstanced().getWriter(buffer); - for (D datum : data) { - writer.write(datum); + try (MappedBuffer buffer = instanceVBO.getBuffer(0, glBufferSize)) { + StructWriter writer = type.asInstanced() + .getWriter(buffer); + for (D datum : data) { + writer.write(datum); + } + } catch (Exception e) { + Flywheel.log.error("Error reallocating GPUInstancer:", e); } - buffer.flush(); glInstanceCount = size; 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 14fe216ac..98d2ef6dc 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/BufferedModel.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/BufferedModel.java @@ -4,6 +4,7 @@ import static org.lwjgl.opengl.GL11.glDrawArrays; import org.lwjgl.opengl.GL31; +import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.backend.gl.GlPrimitive; import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer; @@ -32,9 +33,11 @@ public class BufferedModel implements IBufferedModel { vbo.alloc(model.size()); // mirror it in system memory so we can write to it, and upload our model. - MappedBuffer buffer = vbo.getBuffer(0, model.size()); - model.buffer(new VecBufferWriter(buffer)); - buffer.flush(); + try (MappedBuffer buffer = vbo.getBuffer(0, model.size())) { + model.buffer(new VecBufferWriter(buffer)); + } catch (Exception e) { + Flywheel.log.error(String.format("Error uploading model '%s':", model.name()), e); + } vbo.unbind(); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/ModelPool.java b/src/main/java/com/jozufozu/flywheel/backend/model/ModelPool.java index 645858ede..5a1d033f6 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/ModelPool.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/ModelPool.java @@ -5,6 +5,7 @@ import java.util.List; import org.lwjgl.opengl.GL32; +import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.backend.gl.GlPrimitive; import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat; import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer; @@ -115,34 +116,35 @@ public class ModelPool implements ModelAllocator { } private void uploadAll() { - MappedBuffer buffer = vbo.getBuffer(0, bufferSize); + try (MappedBuffer buffer = vbo.getBuffer(0, bufferSize)) { - VecBufferWriter consumer = new VecBufferWriter(buffer); + VecBufferWriter consumer = new VecBufferWriter(buffer); - for (PooledModel model : models) { - model.model.buffer(consumer); - if (model.callback != null) - model.callback.onAlloc(model); + for (PooledModel model : models) { + model.model.buffer(consumer); + if (model.callback != null) model.callback.onAlloc(model); + } + + } catch (Exception e) { + Flywheel.log.error("Error uploading pooled models:", e); } - - buffer.flush(); } private void uploadPending() { - MappedBuffer buffer = vbo.getBuffer(0, bufferSize); - VecBufferWriter consumer = new VecBufferWriter(buffer); + try (MappedBuffer buffer = vbo.getBuffer(0, bufferSize)) { + VecBufferWriter consumer = new VecBufferWriter(buffer); - int stride = format.getStride(); - for (PooledModel model : pendingUpload) { - int pos = model.first * stride; - buffer.position(pos); - model.model.buffer(consumer); - if (model.callback != null) - model.callback.onAlloc(model); + int stride = format.getStride(); + for (PooledModel model : pendingUpload) { + int pos = model.first * stride; + buffer.position(pos); + model.model.buffer(consumer); + if (model.callback != null) model.callback.onAlloc(model); + } + pendingUpload.clear(); + } catch (Exception e) { + Flywheel.log.error("Error uploading pooled models:", e); } - pendingUpload.clear(); - - buffer.flush(); } private void setDirty() { diff --git a/src/main/java/com/jozufozu/flywheel/core/FullscreenQuad.java b/src/main/java/com/jozufozu/flywheel/core/FullscreenQuad.java index d3635718d..a1269b613 100644 --- a/src/main/java/com/jozufozu/flywheel/core/FullscreenQuad.java +++ b/src/main/java/com/jozufozu/flywheel/core/FullscreenQuad.java @@ -1,11 +1,15 @@ package com.jozufozu.flywheel.core; +import static org.lwjgl.opengl.GL20.*; + import org.lwjgl.opengl.GL20; +import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.backend.gl.GlNumericType; import com.jozufozu.flywheel.backend.gl.GlVertexArray; 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.util.Lazy; @@ -28,16 +32,18 @@ public class FullscreenQuad { vbo = new MappedGlBuffer(GlBufferType.ARRAY_BUFFER); vbo.bind(); vbo.alloc(bufferSize); - vbo.getBuffer(0, bufferSize) - .putFloatArray(vertices) - .flush(); + try (MappedBuffer buffer = vbo.getBuffer(0, bufferSize)) { + buffer.putFloatArray(vertices); + } catch (Exception e) { + Flywheel.log.error("Could not create fullscreen quad.", e); + } vao = new GlVertexArray(); vao.bind(); - GL20.glEnableVertexAttribArray(0); + glEnableVertexAttribArray(0); - GL20.glVertexAttribPointer(0, 4, GlNumericType.FLOAT.getGlEnum(), false, 4 * 4, 0); + glVertexAttribPointer(0, 4, GlNumericType.FLOAT.getGlEnum(), false, 4 * 4, 0); GlVertexArray.unbind(); vbo.unbind(); @@ -45,7 +51,7 @@ public class FullscreenQuad { public void draw() { vao.bind(); - GL20.glDrawArrays(GL20.GL_TRIANGLES, 0, 6); + glDrawArrays(GL_TRIANGLES, 0, 6); GlVertexArray.unbind(); } diff --git a/src/main/java/com/jozufozu/flywheel/util/Unimplemented.java b/src/main/java/com/jozufozu/flywheel/util/Unimplemented.java new file mode 100644 index 000000000..0743ddc4c --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/util/Unimplemented.java @@ -0,0 +1,7 @@ +package com.jozufozu.flywheel.util; + +public class Unimplemented extends RuntimeException { + public Unimplemented(String message) { + super(message); + } +}