diff --git a/build.gradle b/build.gradle index d65d4f49d..b8364464a 100644 --- a/build.gradle +++ b/build.gradle @@ -43,6 +43,7 @@ minecraft { property 'forge.logging.markers', '' property 'forge.logging.console.level', 'debug' property 'mixin.debug.export', 'true' + property 'flw.dumpShaderSource', 'true' arg '-mixin.config=flywheel.mixins.json' diff --git a/src/main/java/com/jozufozu/flywheel/api/struct/InstancedStructType.java b/src/main/java/com/jozufozu/flywheel/api/struct/InstancedStructType.java index 3a6ac7440..1a809163a 100644 --- a/src/main/java/com/jozufozu/flywheel/api/struct/InstancedStructType.java +++ b/src/main/java/com/jozufozu/flywheel/api/struct/InstancedStructType.java @@ -1,6 +1,7 @@ package com.jozufozu.flywheel.api.struct; -import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; +import java.nio.ByteBuffer; + import com.jozufozu.flywheel.core.source.FileResolution; public interface InstancedStructType extends StructType { @@ -9,7 +10,7 @@ public interface InstancedStructType extends StructType { * * @param backing The buffer that the StructWriter will write to. */ - StructWriter getWriter(VecBuffer backing); + StructWriter getWriter(ByteBuffer backing); FileResolution getInstanceShader(); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/Backend.java b/src/main/java/com/jozufozu/flywheel/backend/Backend.java index 79f4b6c93..aaf2e4e51 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/Backend.java +++ b/src/main/java/com/jozufozu/flywheel/backend/Backend.java @@ -1,5 +1,7 @@ package com.jozufozu.flywheel.backend; +import java.lang.ref.Cleaner; + import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; @@ -17,6 +19,8 @@ import net.minecraft.world.level.LevelAccessor; public class Backend { public static final Logger LOGGER = LogUtils.getLogger(); + public static boolean dumpShaderSource = Boolean.getBoolean("flw.dumpShaderSource"); + private static BackendType backendType; private static ParallelTaskEngine taskEngine; @@ -105,4 +109,5 @@ public class Backend { private Backend() { throw new UnsupportedOperationException("Backend is a static class!"); } + } diff --git a/src/main/java/com/jozufozu/flywheel/backend/FlywheelDebug.java b/src/main/java/com/jozufozu/flywheel/backend/FlywheelDebug.java new file mode 100644 index 000000000..48caf3cb9 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/FlywheelDebug.java @@ -0,0 +1,32 @@ +package com.jozufozu.flywheel.backend; + +import org.lwjgl.opengl.GL43; +import org.lwjgl.opengl.GLDebugMessageCallback; + +import com.mojang.blaze3d.platform.GlDebug; + +public class FlywheelDebug { +// +// public static void setup() { +// GLDebugMessageCallback.create() +// GL43.glDebugMessageCallback(); +// } +// +// private static void printDebugLog(int p_84039_, int p_84040_, int p_84041_, int p_84042_, int p_84043_, long p_84044_, long p_84045_) { +// String s = GLDebugMessageCallback.getMessage(p_84043_, p_84044_); +// GlDebug.LogEntry gldebug$logentry; +// synchronized(MESSAGE_BUFFER) { +// gldebug$logentry = lastEntry; +// if (gldebug$logentry != null && gldebug$logentry.isSame(p_84039_, p_84040_, p_84041_, p_84042_, s)) { +// ++gldebug$logentry.count; +// } else { +// gldebug$logentry = new GlDebug.LogEntry(p_84039_, p_84040_, p_84041_, p_84042_, s); +// MESSAGE_BUFFER.add(gldebug$logentry); +// lastEntry = gldebug$logentry; +// } +// } +// +// LOGGER.info("OpenGL debug message: {}", (Object)gldebug$logentry); +// } + +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/FlywheelMemory.java b/src/main/java/com/jozufozu/flywheel/backend/FlywheelMemory.java new file mode 100644 index 000000000..3d3f8bd60 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/FlywheelMemory.java @@ -0,0 +1,57 @@ +package com.jozufozu.flywheel.backend; + +import java.lang.ref.Cleaner; +import java.nio.ByteBuffer; + +import org.lwjgl.system.MemoryUtil; + +public class FlywheelMemory { + + public static final Cleaner CLEANER = Cleaner.create(); + + private static int gpuMemory = 0; + private static int cpuMemory = 0; + + public static void _freeCPUMemory(long size) { + cpuMemory -= size; + } + + public static void _allocCPUMemory(long size) { + cpuMemory += size; + } + + public static void _freeGPUMemory(long size) { + gpuMemory -= size; + } + + public static void _allocGPUMemory(long size) { + gpuMemory += size; + } + + public static int getGPUMemory() { + return gpuMemory; + } + + public static int getCPUMemory() { + return cpuMemory; + } + + public static Cleaner.Cleanable track(Object owner, ByteBuffer buffer) { + return CLEANER.register(owner, new Tracked(buffer)); + } + + public static class Tracked implements Runnable { + + private final ByteBuffer buffer; + + public Tracked(ByteBuffer buffer) { + this.buffer = buffer; + _allocCPUMemory(buffer.capacity()); + } + + public void run() { + _freeCPUMemory(buffer.capacity()); + MemoryUtil.memFree(buffer); + } + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/GlObject.java b/src/main/java/com/jozufozu/flywheel/backend/gl/GlObject.java index 27956c87e..48904d20c 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/GlObject.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/GlObject.java @@ -1,5 +1,7 @@ package com.jozufozu.flywheel.backend.gl; +import com.jozufozu.flywheel.backend.RenderWork; + // Utility class for safely dealing with gl object handles. public abstract class GlObject { private static final int INVALID_HANDLE = Integer.MIN_VALUE; @@ -40,5 +42,4 @@ public abstract class GlObject { } protected abstract void deleteInternal(int handle); - } diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/GlStateTracker.java b/src/main/java/com/jozufozu/flywheel/backend/gl/GlStateTracker.java index eec0a4cf8..a352e9465 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/GlStateTracker.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/GlStateTracker.java @@ -40,7 +40,7 @@ public class GlStateTracker { return new State(buffers.clone(), vao, program); } - public static record State(int[] buffers, int vao, int program) { + public record State(int[] buffers, int vao, int program) implements AutoCloseable { public void restore() { GlBufferType[] values = GlBufferType.values(); @@ -58,5 +58,10 @@ public class GlStateTracker { GlStateManager._glUseProgram(program); } } + + @Override + public void close() { + restore(); + } } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/GlVertexArray.java b/src/main/java/com/jozufozu/flywheel/backend/gl/GlVertexArray.java index f5f54047b..7de9ef863 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/GlVertexArray.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/GlVertexArray.java @@ -3,6 +3,7 @@ package com.jozufozu.flywheel.backend.gl; import org.lwjgl.opengl.GL20; import org.lwjgl.opengl.GL32; +import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer; import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; import com.jozufozu.flywheel.backend.gl.versioned.GlCompat; import com.jozufozu.flywheel.core.layout.BufferLayout; @@ -61,16 +62,19 @@ public class GlVertexArray extends GlObject { GlStateManager._glBindVertexArray(0); } - public void bindAttributes(int startIndex, BufferLayout type) { - int boundBuffer = GlStateTracker.getBuffer(GlBufferType.ARRAY_BUFFER); - + public void bindAttributes(GlBuffer buffer, int startIndex, BufferLayout type) { bind(); + + int targetBuffer = buffer.handle(); + + GlBufferType.ARRAY_BUFFER.bind(targetBuffer); + int i = startIndex; int offset = 0; final int stride = type.getStride(); for (VertexAttribute attribute : type.getAttributes()) { - targets[i] = boundBuffer; + targets[i] = targetBuffer; attributes[i] = attribute; offsets[i] = offset; strides[i] = stride; diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/GlBuffer.java b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/GlBuffer.java index 55d8d7037..09c186a9d 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/GlBuffer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/GlBuffer.java @@ -29,12 +29,12 @@ public abstract class GlBuffer extends GlObject { } } - protected final GlBufferType type; + public final GlBufferType type; /** * The size (in bytes) of the buffer on the GPU. */ - protected long capacity; + protected long size; /** * How much extra room to give the buffer when we reallocate. @@ -42,7 +42,7 @@ public abstract class GlBuffer extends GlObject { protected int growthMargin; public GlBuffer(GlBufferType type) { - _create(); + setHandle(GL20.glGenBuffers()); this.type = type; } @@ -50,42 +50,14 @@ public abstract class GlBuffer extends GlObject { this.growthMargin = growthMargin; } - public long getCapacity() { - return capacity; + public long getSize() { + return size; } - public MappedBuffer getBuffer() { - return getBuffer(0, capacity); + public GlBufferType getType() { + return type; } - public abstract MappedBuffer getBuffer(long offset, long length); - - /** - * Ensure that the buffer has at least enough room to store size bytes. - * - * @return true if the buffer grew. - */ - public boolean ensureCapacity(long size) { - if (size > capacity) { - capacity = size + growthMargin; - alloc(capacity); - return true; - } - - return false; - } - - /** - * Call this after all draw calls using this buffer are complete. - */ - public void doneForThisFrame() { - - } - - protected abstract void alloc(long size); - - public abstract void upload(ByteBuffer directBuffer); - public void bind() { type.bind(handle()); } @@ -94,11 +66,31 @@ public abstract class GlBuffer extends GlObject { type.unbind(); } - protected void _create() { - setHandle(GL20.glGenBuffers()); + public abstract void upload(ByteBuffer directBuffer); + + public abstract MappedBuffer map(); + + /** + * Ensure that the buffer has at least enough room to store {@code size} bytes. + * + * @return {@code true} if the buffer moved. + */ + public abstract boolean ensureCapacity(long size); + + /** + * Call this after all draw calls using this buffer are complete. + */ + public void doneForThisFrame() { + } protected void deleteInternal(int handle) { GL20.glDeleteBuffers(handle); } + + /** + * Indicates that this buffer need not be #flush()'d for its contents to sync. + * @return true if this buffer is persistently mapped. + */ + public abstract boolean isPersistent(); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/GlBufferType.java b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/GlBufferType.java index 776ed6328..91553e7ca 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/GlBufferType.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/GlBufferType.java @@ -10,6 +10,7 @@ import org.lwjgl.opengl.GL43; import com.jozufozu.flywheel.backend.gl.GlStateTracker; import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.systems.RenderSystem; public enum GlBufferType { ARRAY_BUFFER(GL15C.GL_ARRAY_BUFFER), @@ -53,11 +54,13 @@ public enum GlBufferType { } public void bind(int buffer) { - GlStateManager._glBindBuffer(glEnum, buffer); + if (getBoundBuffer() != buffer) { + GlStateManager._glBindBuffer(glEnum, buffer); + } } public void unbind() { - GlStateManager._glBindBuffer(glEnum, 0); + bind(0); } public int getBoundBuffer() { 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 deleted file mode 100644 index 8c5cddc42..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/Mappable.java +++ /dev/null @@ -1,14 +0,0 @@ -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 25155a1d0..4ccdbe721 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 @@ -3,43 +3,69 @@ package com.jozufozu.flywheel.backend.gl.buffer; import java.nio.ByteBuffer; import org.lwjgl.opengl.GL15; +import org.lwjgl.system.MemoryUtil; -public class MappedBuffer extends VecBuffer implements AutoCloseable { +public class MappedBuffer implements AutoCloseable { - protected final long offset; - protected final long length; - protected final Mappable owner; + private final long offset; + private final long length; + private final GlBuffer owner; + private final boolean persistent; + private ByteBuffer internal; - public MappedBuffer(Mappable owner, ByteBuffer internal, long offset, long length) { + public MappedBuffer(GlBuffer owner, ByteBuffer internal, long offset, long length) { this.internal = internal; this.owner = owner; this.offset = offset; this.length = length; + persistent = owner.isPersistent(); } /** * Make the changes in client memory available to the GPU. */ public void flush() { - if (owner.isPersistent()) return; + if (persistent) return; if (internal == null) return; + owner.bind(); 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); + internal.position(p - (int) offset); return this; } @Override - public void close() throws Exception { + public void close() { flush(); } + + public ByteBuffer unwrap() { + return internal; + } + + public long getMemAddress() { + return MemoryUtil.memAddress(internal); + } + + public void clear(long clearStart, long clearLength) { + if (clearLength <= 0) { + return; + } + + if (clearStart < offset || clearStart + clearLength > offset + length) { + throw new IndexOutOfBoundsException("Clear range [" + clearStart + "," + (clearStart + clearLength) + "] is not mapped"); + } + + long addr = MemoryUtil.memAddress(unwrap()) + clearStart; + + MemoryUtil.memSet(addr, 0, clearLength); + } } 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 aef6e8ce5..6d0e5956b 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 @@ -2,13 +2,13 @@ package com.jozufozu.flywheel.backend.gl.buffer; import java.nio.ByteBuffer; -import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL30; +import org.lwjgl.opengl.GL32; import com.jozufozu.flywheel.backend.gl.error.GlError; import com.jozufozu.flywheel.backend.gl.error.GlException; -public class MappedGlBuffer extends GlBuffer implements Mappable { +public class MappedGlBuffer extends GlBuffer { protected final GlBufferUsage usage; @@ -21,27 +21,67 @@ public class MappedGlBuffer extends GlBuffer implements Mappable { this.usage = usage; } - protected void alloc(long size) { - GL15.glBufferData(type.glEnum, size, usage.glEnum); + @Override + public boolean ensureCapacity(long size) { + if (size < 0) { + throw new IllegalArgumentException("Size " + size + " < 0"); + } + + if (size == 0) { + return false; + } + + if (this.size == 0) { + this.size = size; + bind(); + GL32.glBufferData(type.glEnum, size, usage.glEnum); + + return true; + } + + if (size > this.size) { + var oldSize = this.size; + this.size = size + growthMargin; + + realloc(oldSize, this.size); + + return true; + } + + return false; } + private void realloc(long oldSize, long newSize) { + var oldHandle = handle(); + var newHandle = GL32.glGenBuffers(); + + GlBufferType.COPY_READ_BUFFER.bind(oldHandle); + type.bind(newHandle); + + GL32.glBufferData(type.glEnum, newSize, usage.glEnum); + GL32.glCopyBufferSubData(GlBufferType.COPY_READ_BUFFER.glEnum, type.glEnum, 0, 0, oldSize); + + delete(); + setHandle(newHandle); + } + + @Override public void upload(ByteBuffer directBuffer) { - GL15.glBufferData(type.glEnum, directBuffer, usage.glEnum); + bind(); + GL32.glBufferData(type.glEnum, directBuffer, usage.glEnum); + this.size = directBuffer.capacity(); } - public MappedBuffer getBuffer(long offset, long length) { - ByteBuffer byteBuffer = GL30.glMapBufferRange(type.glEnum, offset, length, GL30.GL_MAP_WRITE_BIT); + @Override + public MappedBuffer map() { + bind(); + ByteBuffer byteBuffer = GL30.glMapBufferRange(type.glEnum, 0, size, 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; + return new MappedBuffer(this, byteBuffer, 0, size); } @Override 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 ff81679c2..bf9293be6 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,20 +6,21 @@ import static org.lwjgl.opengl.GL44.GL_MAP_PERSISTENT_BIT; import java.nio.ByteBuffer; -import org.lwjgl.opengl.GL30; +import org.jetbrains.annotations.Nullable; +import org.lwjgl.opengl.GL32; +import org.lwjgl.system.MemoryUtil; 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.backend.gl.versioned.GlCompat; -public class PersistentGlBuffer extends GlBuffer implements Mappable { +public class PersistentGlBuffer extends GlBuffer { - private MappedBuffer buffer; + @Nullable + private MappedBuffer access; int flags; - - long size; - GlFence fence; + private final GlFence fence; public PersistentGlBuffer(GlBufferType type) { super(type); @@ -34,47 +35,98 @@ public class PersistentGlBuffer extends GlBuffer implements Mappable { } @Override - protected void alloc(long size) { - this.size = size; - - if (buffer != null) { - deleteInternal(handle()); - _create(); - - bind(); + public boolean ensureCapacity(long size) { + if (size < 0) { + throw new IllegalArgumentException("Size " + size + " < 0"); } - fence.clear(); + if (size == 0) { + return false; + } - GlCompat.getInstance().bufferStorage.bufferStorage(type, size, flags); + if (this.size == 0) { + this.size = size; + bind(); + GlCompat.getInstance().bufferStorage.bufferStorage(type, this.size, flags); + return true; + } - ByteBuffer byteBuffer = GL30.glMapBufferRange(type.glEnum, 0, size, flags); + if (size > this.size) { + var oldSize = this.size; + this.size = size + growthMargin; + + fence.clear(); + + realloc(this.size, oldSize); + + access = null; + return true; + } + + return false; + } + + @Override + public void upload(ByteBuffer directBuffer) { + ensureCapacity(directBuffer.capacity()); + + var access = getWriteAccess(); + + ByteBuffer ourBuffer = access.unwrap(); + + ourBuffer.reset(); + + MemoryUtil.memCopy(directBuffer, ourBuffer); + + int uploadSize = directBuffer.remaining(); + int ourSize = ourBuffer.capacity(); + + if (uploadSize < ourSize) { + long clearFrom = access.getMemAddress() + uploadSize; + MemoryUtil.memSet(clearFrom, 0, ourSize - uploadSize); + } + } + + private void mapToClientMemory() { + bind(); + ByteBuffer byteBuffer = GL32.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); + access = new MappedBuffer(this, byteBuffer, 0, size); + } + + private void realloc(long newSize, long oldSize) { + int oldHandle = handle(); + int newHandle = GL32.glGenBuffers(); + + GlBufferType.COPY_READ_BUFFER.bind(oldHandle); + type.bind(newHandle); + + GlCompat.getInstance().bufferStorage.bufferStorage(type, newSize, flags); + + GL32.glCopyBufferSubData(GlBufferType.COPY_READ_BUFFER.glEnum, type.glEnum, 0, 0, oldSize); + + delete(); + setHandle(newHandle); } @Override - public void upload(ByteBuffer directBuffer) { - throw new UnsupportedOperationException("FIXME: Nothing calls #upload on a persistent buffer as of 12/10/2021."); + public MappedBuffer map() { + return getWriteAccess() + .position(0); } - @Override - public MappedBuffer getBuffer(long offset, long length) { + private MappedBuffer getWriteAccess() { + if (access == null) { + mapToClientMemory(); + } else { + fence.waitSync(); // FIXME: Hangs too much, needs double/triple buffering + } - fence.waitSync(); - - buffer.position((int) offset); - - return buffer; - } - - @Override - public GlBufferType getType() { - return type; + return access; } @Override 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 deleted file mode 100644 index 8d5a14eb6..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/buffer/VecBuffer.java +++ /dev/null @@ -1,145 +0,0 @@ -package com.jozufozu.flywheel.backend.gl.buffer; - -import java.nio.Buffer; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.FloatBuffer; - -public class VecBuffer { - - protected ByteBuffer internal; - - public VecBuffer() { - } - - public VecBuffer(ByteBuffer internal) { - this.internal = internal; - } - - public static VecBuffer allocate(int bytes) { - ByteBuffer buffer = ByteBuffer.allocate(bytes); - buffer.order(ByteOrder.nativeOrder()); - return new VecBuffer(buffer); - } - - public ByteBuffer unwrap() { - return internal; - } - - public VecBuffer rewind() { - ((Buffer) internal).rewind(); - - return this; - } - - public VecBuffer putFloatArray(float[] floats) { - - internal.asFloatBuffer().put(floats); - internal.position(internal.position() + floats.length * 4); - - return this; - } - - public VecBuffer putByteArray(byte[] bytes) { - internal.put(bytes); - return this; - } - - public VecBuffer put(FloatBuffer floats) { - - int remainingBytes = floats.remaining() * 4; - internal.asFloatBuffer().put(floats); - internal.position(internal.position() + remainingBytes); - - return this; - } - - public int position() { - return internal.position(); - } - - /** - * Position this buffer relative to the 0-index in GPU memory. - * - * @return This buffer. - */ - public VecBuffer position(int p) { - internal.position(p); - return this; - } - - public VecBuffer putFloat(float f) { - internal.putFloat(f); - return this; - } - - public VecBuffer putInt(int i) { - internal.putInt(i); - return this; - } - - public VecBuffer putShort(short s) { - internal.putShort(s); - return this; - } - - public VecBuffer put(byte b) { - internal.put(b); - return this; - } - - public VecBuffer put(ByteBuffer b) { - internal.put(b); - return this; - } - - public VecBuffer putVec4(float x, float y, float z, float w) { - internal.putFloat(x); - internal.putFloat(y); - internal.putFloat(z); - internal.putFloat(w); - return this; - } - - public VecBuffer putColor(int r, int g, int b, int a) { - internal.put((byte) r); - internal.put((byte) g); - internal.put((byte) b); - internal.put((byte) a); - return this; - } - - public VecBuffer putColor(byte r, byte g, byte b, byte a) { - internal.put(r); - internal.put(g); - internal.put(b); - internal.put(a); - return this; - } - - public VecBuffer putVec3(float x, float y, float z) { - internal.putFloat(x); - internal.putFloat(y); - internal.putFloat(z); - return this; - } - - public VecBuffer putVec2(float x, float y) { - internal.putFloat(x); - internal.putFloat(y); - return this; - } - - public VecBuffer putVec3(byte x, byte y, byte z) { - internal.put(x); - internal.put(y); - internal.put(z); - return this; - } - - public VecBuffer putVec2(byte x, byte y) { - internal.put(x); - internal.put(y); - return this; - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlShader.java b/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlShader.java index a925d903d..c4195c68f 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlShader.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/shader/GlShader.java @@ -7,10 +7,11 @@ import java.util.stream.Collectors; import org.lwjgl.opengl.GL20; +import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.gl.GlObject; import com.jozufozu.flywheel.backend.gl.versioned.GlCompat; +import com.jozufozu.flywheel.core.compile.ShaderCompilationException; import com.jozufozu.flywheel.core.shader.ShaderConstants; -import com.jozufozu.flywheel.core.source.ShaderLoadingException; import net.minecraft.client.Minecraft; import net.minecraft.resources.ResourceLocation; @@ -30,18 +31,10 @@ public class GlShader extends GlObject { GlCompat.safeShaderSource(handle, source); GL20.glCompileShader(handle); - // TODO: control this via a JVM flag or other dumpSource(source, type); -// String log = GL20.glGetShaderInfoLog(handle); -// -// if (!log.isEmpty()) { -// System.out.println(log); -//// env.printShaderInfoLog(source, log, this.name); -// } - if (GL20.glGetShaderi(handle, GL20.GL_COMPILE_STATUS) != GL20.GL_TRUE) { - throw new ShaderLoadingException("Could not compile " + getName() + ". See log for details."); + throw new ShaderCompilationException("Could not compile " + getName(), handle); } setHandle(handle); @@ -61,6 +54,10 @@ public class GlShader extends GlObject { } private void dumpSource(String source, ShaderType type) { + if (!Backend.dumpShaderSource) { + return; + } + File dir = new File(Minecraft.getInstance().gameDirectory, "flywheel_sources"); dir.mkdirs(); File file = new File(dir, type.getFileName(getName())); 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 0aaffe754..059dd15b1 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 @@ -38,13 +38,7 @@ public class GlCompat { instancedArrays = getLatest(InstancedArrays.class, caps); bufferStorage = getLatest(BufferStorage.class, caps); - if (Util.getPlatform() == Util.OS.WINDOWS) { - String vendor = GL20C.glGetString(GL20C.GL_VENDOR); - // vendor string I got was "ATI Technologies Inc." - amd = vendor.contains("ATI") || vendor.contains("AMD"); - } else { - amd = false; - } + amd = _isAmdWindows(); } public boolean onAMDWindows() { @@ -82,7 +76,7 @@ public class GlCompat { /** * Copied from: - *
https://github.com/grondag/canvas/commit/820bf754092ccaf8d0c169620c2ff575722d7d96 + *
canvas * *

Identical in function to {@link GL20C#glShaderSource(int, CharSequence)} but * passes a null pointer for string length to force the driver to rely on the null @@ -107,5 +101,20 @@ public class GlCompat { stack.setPointer(stackPointer); } } + + private static boolean _isAmdWindows() { + if (Util.getPlatform() != Util.OS.WINDOWS) { + return false; + } + + String vendor = GL20C.glGetString(GL20C.GL_VENDOR); + + if (vendor == null) { + return false; + } + + // vendor string I got was "ATI Technologies Inc." + return vendor.contains("ATI") || vendor.contains("AMD"); + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/DrawBuffer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/DrawBuffer.java index 4166c1e59..a9759c368 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/DrawBuffer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/DrawBuffer.java @@ -1,7 +1,9 @@ package com.jozufozu.flywheel.backend.instancing; +import java.lang.ref.Cleaner; import java.nio.ByteBuffer; +import com.jozufozu.flywheel.backend.FlywheelMemory; import com.jozufozu.flywheel.backend.model.BufferBuilderExtension; import com.jozufozu.flywheel.backend.model.DirectVertexConsumer; import com.mojang.blaze3d.platform.MemoryTracker; @@ -14,11 +16,12 @@ import net.minecraft.client.renderer.RenderType; * * The number of vertices needs to be known ahead of time. */ -public class DrawBuffer { +public class DrawBuffer implements AutoCloseable { private final RenderType parent; private ByteBuffer backingBuffer; private int expectedVertices; + private Cleaner.Cleanable cleanable; public DrawBuffer(RenderType parent) { this.parent = parent; @@ -43,9 +46,12 @@ public class DrawBuffer { if (backingBuffer == null) { backingBuffer = MemoryTracker.create(byteSize); + cleanable = FlywheelMemory.track(this, backingBuffer); } if (byteSize > backingBuffer.capacity()) { + cleanable.clean(); backingBuffer = MemoryTracker.resize(backingBuffer, byteSize); + cleanable = FlywheelMemory.track(this, backingBuffer); } return new DirectVertexConsumer(backingBuffer, format, vertexCount); @@ -74,4 +80,11 @@ public class DrawBuffer { public void reset() { this.expectedVertices = 0; } + + @Override + public void close() { + if (cleanable != null) { + cleanable.clean(); + } + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java index 75a02685c..f2ccf13de 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstancedRenderDispatcher.java @@ -2,7 +2,6 @@ package com.jozufozu.flywheel.backend.instancing; import java.util.List; -import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.config.FlwCommands; import com.jozufozu.flywheel.config.FlwConfig; @@ -123,9 +122,6 @@ public class InstancedRenderDispatcher { } public static void getDebugString(List debug) { - debug.add(""); - debug.add("Flywheel: " + Flywheel.getVersion()); - if (Backend.isOn()) { InstanceWorld instanceWorld = instanceWorlds.get(Minecraft.getInstance().level); 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 2886260cd..6eae582eb 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 @@ -3,8 +3,6 @@ package com.jozufozu.flywheel.backend.instancing.instancing; import java.util.HashSet; import java.util.Set; -import org.lwjgl.system.MemoryUtil; - import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.struct.InstancedStructType; @@ -56,65 +54,48 @@ public class GPUInstancer extends AbstractInstancer { removeDeletedInstances(); } - vbo.bind(); - - if (!realloc()) { - + if (checkAndGrowBuffer()) { + // The instance vbo has moved, so we need to re-bind attributes boundTo.clear(); - - if (anyToRemove) { - clearBufferTail(); - } - - if (anyToUpdate) { - updateBuffer(); - } - - glInstanceCount = data.size(); } + if (anyToUpdate) { + clearAndUpdateBuffer(); + } + + glInstanceCount = data.size(); + if (boundTo.add(vao)) { bindInstanceAttributes(vao); } - vbo.unbind(); - anyToRemove = anyToUpdate = false; } - private void clearBufferTail() { - int size = data.size(); - final int offset = size * instanceFormat.getStride(); - final long length = vbo.getCapacity() - offset; - if (length > 0) { - try (MappedBuffer buf = vbo.getBuffer(offset, length)) { - MemoryUtil.memSet(MemoryUtil.memAddress(buf.unwrap()), 0, length); - } catch (Exception e) { - Flywheel.LOGGER.error("Error clearing buffer tail:", e); - } - } - } - - private void updateBuffer() { + private void clearAndUpdateBuffer() { final int size = data.size(); + final long clearStart = (long) size * instanceFormat.getStride(); + final long clearLength = vbo.getSize() - clearStart; - if (size <= 0) return; + try (MappedBuffer buf = vbo.map()) { + buf.clear(clearStart, clearLength); - try (MappedBuffer mapped = vbo.getBuffer()) { + if (size > 0) { - final StructWriter writer = instancedType.getWriter(mapped); + final StructWriter writer = instancedType.getWriter(buf.unwrap()); - boolean sequential = true; - for (int i = 0; i < size; i++) { - final D element = data.get(i); - if (element.checkDirtyAndClear()) { - if (!sequential) { - writer.seek(i); + boolean sequential = true; + for (int i = 0; i < size; i++) { + final D element = data.get(i); + if (element.checkDirtyAndClear()) { + if (!sequential) { + writer.seek(i); + } + writer.write(element); + sequential = true; + } else { + sequential = false; } - writer.write(element); - sequential = true; - } else { - sequential = false; } } } catch (Exception e) { @@ -122,33 +103,22 @@ public class GPUInstancer extends AbstractInstancer { } } - private boolean realloc() { + /** + * @return {@code true} if the buffer moved. + */ + private boolean checkAndGrowBuffer() { int size = this.data.size(); int stride = instanceFormat.getStride(); int requiredSize = size * stride; - if (vbo.ensureCapacity(requiredSize)) { - try (MappedBuffer buffer = vbo.getBuffer()) { - StructWriter writer = instancedType.getWriter(buffer); - for (D datum : data) { - writer.write(datum); - } - } catch (Exception e) { - Flywheel.LOGGER.error("Error reallocating GPUInstancer:", e); - } - - glInstanceCount = size; - - return true; - } - return false; + return vbo.ensureCapacity(requiredSize); } private void bindInstanceAttributes(GlVertexArray vao) { - vao.bindAttributes(attributeBaseIndex, instanceFormat); + vao.bindAttributes(this.vbo, this.attributeBaseIndex, this.instanceFormat); - for (int i = 0; i < instanceFormat.getAttributeCount(); i++) { - vao.setAttributeDivisor(attributeBaseIndex + i, 1); + for (int i = 0; i < this.instanceFormat.getAttributeCount(); i++) { + vao.setAttributeDivisor(this.attributeBaseIndex + i, 1); } } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedModel.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedModel.java index 6c54ec14d..18ef8a641 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedModel.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedModel.java @@ -5,6 +5,7 @@ import java.util.Map; import com.google.common.collect.ImmutableMap; import com.jozufozu.flywheel.api.InstanceData; import com.jozufozu.flywheel.api.material.Material; +import com.jozufozu.flywheel.backend.gl.GlStateTracker; import com.jozufozu.flywheel.backend.gl.GlVertexArray; import com.jozufozu.flywheel.backend.model.MeshPool; import com.jozufozu.flywheel.core.model.Mesh; @@ -44,7 +45,7 @@ public class InstancedModel { private Layer(MeshPool allocator, Material material, Mesh mesh) { this.material = material; vao = new GlVertexArray(); - bufferedMesh = allocator.alloc(mesh, vao); + bufferedMesh = allocator.alloc(mesh); instancer.attributeBaseIndex = bufferedMesh.getAttributeCount(); vao.enableArrays(bufferedMesh.getAttributeCount() + instancer.instanceFormat.getAttributeCount()); } @@ -53,16 +54,17 @@ public class InstancedModel { public void render() { if (invalid()) return; - vao.bind(); + try (var ignored = GlStateTracker.getRestoreState()) { - instancer.renderSetup(vao); + instancer.renderSetup(vao); - if (instancer.glInstanceCount > 0) { - bufferedMesh.drawInstances(instancer.glInstanceCount); + if (instancer.glInstanceCount > 0) { + bufferedMesh.drawInstances(vao, instancer.glInstanceCount); + } + + // persistent mapping sync point + instancer.vbo.doneForThisFrame(); } - - // persistent mapping sync point - instancer.vbo.doneForThisFrame(); } @Override diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/ArrayModelRenderer.java b/src/main/java/com/jozufozu/flywheel/backend/model/ArrayModelRenderer.java index 79809edf8..36890cb5c 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/ArrayModelRenderer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/ArrayModelRenderer.java @@ -10,7 +10,7 @@ public class ArrayModelRenderer { public ArrayModelRenderer(BlockMesh mesh, MeshPool meshPool) { this.vao = new GlVertexArray(); - this.mesh = meshPool.alloc(mesh, this.vao); + this.mesh = meshPool.alloc(mesh); } /** @@ -19,9 +19,7 @@ public class ArrayModelRenderer { public void draw() { if (mesh.isDeleted()) return; - vao.bind(); - - mesh.drawCall(); + mesh.drawCall(vao); } public void delete() { diff --git a/src/main/java/com/jozufozu/flywheel/backend/model/MeshPool.java b/src/main/java/com/jozufozu/flywheel/backend/model/MeshPool.java index 0bcaf66b1..8ea30abc4 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/model/MeshPool.java +++ b/src/main/java/com/jozufozu/flywheel/backend/model/MeshPool.java @@ -1,7 +1,9 @@ package com.jozufozu.flywheel.backend.model; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import org.lwjgl.opengl.GL32; @@ -42,7 +44,6 @@ public class MeshPool { vbo = new MappedGlBuffer(GlBufferType.ARRAY_BUFFER); - vbo.bind(); vbo.setGrowthMargin(stride * 64); } @@ -50,16 +51,15 @@ public class MeshPool { * Allocate a model in the arena. * * @param mesh The model to allocate. - * @param vao The vertex array object to attach the model to. * @return A handle to the allocated model. */ - public BufferedMesh alloc(Mesh mesh, GlVertexArray vao) { - BufferedMesh bufferedModel = new BufferedMesh(vao, mesh, vertices); + public BufferedMesh alloc(Mesh mesh) { + BufferedMesh bufferedModel = new BufferedMesh(mesh, vertices); vertices += mesh.getVertexCount(); models.add(bufferedModel); pendingUpload.add(bufferedModel); - setDirty(); + dirty = true; return bufferedModel; } @@ -67,13 +67,11 @@ public class MeshPool { if (dirty) { if (anyToRemove) processDeletions(); - vbo.bind(); if (realloc()) { uploadAll(); } else { uploadPending(); } - vbo.unbind(); dirty = false; pendingUpload.clear(); @@ -110,7 +108,7 @@ public class MeshPool { } private void uploadAll() { - try (MappedBuffer buffer = vbo.getBuffer()) { + try (MappedBuffer buffer = vbo.map()) { VertexWriter writer = vertexType.createWriter(buffer.unwrap()); int vertices = 0; @@ -128,7 +126,7 @@ public class MeshPool { } private void uploadPending() { - try (MappedBuffer buffer = vbo.getBuffer()) { + try (MappedBuffer buffer = vbo.map()) { VertexWriter writer = vertexType.createWriter(buffer.unwrap()); for (BufferedMesh model : pendingUpload) { model.buffer(writer); @@ -139,10 +137,6 @@ public class MeshPool { } } - private void setDirty() { - dirty = true; - } - public void delete() { vbo.delete(); } @@ -150,51 +144,60 @@ public class MeshPool { public class BufferedMesh { private final ElementBuffer ebo; - private final GlVertexArray vao; - private final Mesh mesh; private int first; private boolean deleted; - public BufferedMesh(GlVertexArray vao, Mesh mesh, int first) { - this.vao = vao; + private final Set boundTo = new HashSet<>(); + + public BufferedMesh(Mesh mesh, int first) { this.mesh = mesh; this.first = first; - ebo = mesh.createEBO(); + this.ebo = mesh.createEBO(); } - public void drawCall() { - ebo.bind(); - GL32.glDrawElementsBaseVertex(GlPrimitive.TRIANGLES.glEnum, ebo.elementCount, ebo.eboIndexType.getGlEnum(), 0, first); + public void drawCall(GlVertexArray vao) { + attachTo(vao); + vao.bind(); + this.ebo.bind(); + GL32.glDrawElementsBaseVertex(GlPrimitive.TRIANGLES.glEnum, this.ebo.elementCount, this.ebo.eboIndexType.getGlEnum(), 0, this.first); } - public void drawInstances(int instanceCount) { + public void drawInstances(GlVertexArray vao, int instanceCount) { if (mesh.getVertexCount() <= 0 || isDeleted()) return; - ebo.bind(); + attachTo(vao); + + vao.bind(); + this.ebo.bind(); //Backend.log.info(StringUtil.args("drawElementsInstancedBaseVertex", GlPrimitive.TRIANGLES, ebo.elementCount, ebo.eboIndexType, 0, instanceCount, first)); - GL32.glDrawElementsInstancedBaseVertex(GlPrimitive.TRIANGLES.glEnum, ebo.elementCount, ebo.eboIndexType.getGlEnum(), 0, instanceCount, first); + GL32.glDrawElementsInstancedBaseVertex(GlPrimitive.TRIANGLES.glEnum, this.ebo.elementCount, this.ebo.eboIndexType.getGlEnum(), 0, instanceCount, this.first); + } + + private void attachTo(GlVertexArray vao) { + if (this.boundTo.add(vao)) { + vao.enableArrays(getAttributeCount()); + vao.bindAttributes(MeshPool.this.vbo, 0, MeshPool.this.vertexType.getLayout()); + } } public boolean isDeleted() { - return deleted; + return this.deleted; } public void delete() { - setDirty(); - anyToRemove = true; - deleted = true; + MeshPool.this.dirty = true; + MeshPool.this.anyToRemove = true; + this.deleted = true; } private void buffer(VertexWriter writer) { - writer.seekToVertex(first); - writer.writeVertexList(mesh.getReader()); - - vao.enableArrays(getAttributeCount()); - vao.bindAttributes(0, vertexType.getLayout()); + writer.seekToVertex(this.first); + writer.writeVertexList(this.mesh.getReader()); + this.boundTo.clear(); } public int getAttributeCount() { diff --git a/src/main/java/com/jozufozu/flywheel/backend/struct/BufferWriter.java b/src/main/java/com/jozufozu/flywheel/backend/struct/BufferWriter.java index 31a23c472..5a0bc8fb1 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/struct/BufferWriter.java +++ b/src/main/java/com/jozufozu/flywheel/backend/struct/BufferWriter.java @@ -1,18 +1,19 @@ package com.jozufozu.flywheel.backend.struct; +import java.nio.ByteBuffer; + import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructWriter; -import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; public abstract class BufferWriter implements StructWriter { - protected final VecBuffer backingBuffer; + protected final ByteBuffer backingBuffer; protected final int stride; - protected BufferWriter(VecBuffer backingBuffer, StructType vertexType) { - this.backingBuffer = backingBuffer; + protected BufferWriter(StructType structType, ByteBuffer byteBuffer) { + this.backingBuffer = byteBuffer; - this.stride = vertexType.getLayout().getStride(); + this.stride = structType.getLayout().getStride(); } @Override diff --git a/src/main/java/com/jozufozu/flywheel/backend/struct/UnsafeBufferWriter.java b/src/main/java/com/jozufozu/flywheel/backend/struct/UnsafeBufferWriter.java index b7c74e7f6..40d489d54 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/struct/UnsafeBufferWriter.java +++ b/src/main/java/com/jozufozu/flywheel/backend/struct/UnsafeBufferWriter.java @@ -1,9 +1,10 @@ package com.jozufozu.flywheel.backend.struct; +import java.nio.ByteBuffer; + import org.lwjgl.system.MemoryUtil; import com.jozufozu.flywheel.api.struct.StructType; -import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; /** * This class copied/adapted from jellysquid's @@ -20,8 +21,8 @@ public abstract class UnsafeBufferWriter extends BufferWriter { */ protected long writePointer; - protected UnsafeBufferWriter(VecBuffer backingBuffer, StructType vertexType) { - super(backingBuffer, vertexType); + protected UnsafeBufferWriter(StructType structType, ByteBuffer byteBuffer) { + super(structType, byteBuffer); acquireWritePointer(); } @@ -38,6 +39,6 @@ public abstract class UnsafeBufferWriter extends BufferWriter { } private void acquireWritePointer() { - this.writePointer = MemoryUtil.memAddress(this.backingBuffer.unwrap(), this.backingBuffer.position()); + this.writePointer = MemoryUtil.memAddress(this.backingBuffer, this.backingBuffer.position()); } } diff --git a/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java b/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java index 7575111e0..18e487b4d 100644 --- a/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java +++ b/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java @@ -7,6 +7,7 @@ import org.jetbrains.annotations.NotNull; import com.jozufozu.flywheel.backend.Backend; import com.mojang.brigadier.Command; import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.IntegerArgumentType; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import net.minecraft.ChatFormatting; @@ -14,9 +15,12 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.player.LocalPlayer; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; +import net.minecraft.commands.arguments.coordinates.BlockPosArgument; +import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; import net.minecraft.network.chat.TextComponent; +import net.minecraft.world.entity.Entity; import net.minecraftforge.client.event.RegisterClientCommandsEvent; import net.minecraftforge.common.ForgeConfigSpec.ConfigValue; import net.minecraftforge.fml.ModList; @@ -87,6 +91,25 @@ public class FlwCommands { } )); + commandBuilder.command.then(Commands.literal("debugCrumble") + .then(Commands.argument("pos", BlockPosArgument.blockPos()) + .then(Commands.argument("stage", IntegerArgumentType.integer(0, 9)) + .executes(context -> { + BlockPos pos = BlockPosArgument.getLoadedBlockPos(context, "pos"); + int value = IntegerArgumentType.getInteger(context, "stage"); + + Entity executor = context.getSource() + .getEntity(); + + if (executor == null) { + return 0; + } + + executor.level.destroyBlockProgress(executor.getId(), pos, value); + + return 1; + })))); + commandBuilder.build(event.getDispatcher()); } diff --git a/src/main/java/com/jozufozu/flywheel/core/FullscreenQuad.java b/src/main/java/com/jozufozu/flywheel/core/FullscreenQuad.java index e319fdf2c..b5a34c28a 100644 --- a/src/main/java/com/jozufozu/flywheel/core/FullscreenQuad.java +++ b/src/main/java/com/jozufozu/flywheel/core/FullscreenQuad.java @@ -6,16 +6,22 @@ import static org.lwjgl.opengl.GL20.glVertexAttribPointer; import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.backend.gl.GlNumericType; +import com.jozufozu.flywheel.backend.gl.GlStateTracker; 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.core.layout.BufferLayout; +import com.jozufozu.flywheel.core.layout.CommonItems; import com.jozufozu.flywheel.util.Lazy; public class FullscreenQuad { public static final Lazy INSTANCE = Lazy.of(FullscreenQuad::new); + private static final BufferLayout LAYOUT = BufferLayout.builder() + .addItems(CommonItems.VEC4) + .build(); private static final float[] vertices = { // pos // tex @@ -29,24 +35,25 @@ public class FullscreenQuad { private final GlBuffer vbo; private FullscreenQuad() { - vbo = new MappedGlBuffer(GlBufferType.ARRAY_BUFFER); - vbo.bind(); - vbo.ensureCapacity(bufferSize); - try (MappedBuffer buffer = vbo.getBuffer()) { - buffer.putFloatArray(vertices); - } catch (Exception e) { - Flywheel.LOGGER.error("Could not create fullscreen quad.", e); + try (var restoreState = GlStateTracker.getRestoreState()) { + vbo = new MappedGlBuffer(GlBufferType.ARRAY_BUFFER); + vbo.ensureCapacity(bufferSize); + try (MappedBuffer buffer = vbo.map()) { + + buffer.unwrap() + .asFloatBuffer() + .put(vertices); + + } catch (Exception e) { + Flywheel.LOGGER.error("Could not create fullscreen quad.", e); + } + + vao = new GlVertexArray(); + + vao.enableArrays(1); + + vao.bindAttributes(vbo, 0, LAYOUT); } - - vao = new GlVertexArray(); - vao.bind(); - - vao.enableArrays(1); - - glVertexAttribPointer(0, 4, GlNumericType.FLOAT.getGlEnum(), false, 4 * 4, 0); - - GlVertexArray.unbind(); - vbo.unbind(); } public void draw() { diff --git a/src/main/java/com/jozufozu/flywheel/core/QuadConverter.java b/src/main/java/com/jozufozu/flywheel/core/QuadConverter.java index 100c2e168..537db4dd7 100644 --- a/src/main/java/com/jozufozu/flywheel/core/QuadConverter.java +++ b/src/main/java/com/jozufozu/flywheel/core/QuadConverter.java @@ -3,17 +3,14 @@ package com.jozufozu.flywheel.core; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.util.EnumMap; -import java.util.Map; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.lwjgl.system.MemoryStack; import org.lwjgl.system.MemoryUtil; import com.jozufozu.flywheel.backend.gl.GlNumericType; -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.backend.model.ElementBuffer; import com.jozufozu.flywheel.event.ReloadRenderersEvent; @@ -29,14 +26,12 @@ import net.minecraftforge.fml.common.Mod; @Mod.EventBusSubscriber(Dist.CLIENT) public class QuadConverter { - public static final int STARTING_CAPACITY = 42; // 255 / 6 = 42 - private static QuadConverter INSTANCE; @NotNull public static QuadConverter getInstance() { if (INSTANCE == null) { - INSTANCE = new QuadConverter(STARTING_CAPACITY); + INSTANCE = new QuadConverter(); } return INSTANCE; @@ -47,130 +42,80 @@ public class QuadConverter { return INSTANCE; } - Map ebos; - int[] capacities; + private MappedGlBuffer ebo; + private int quadCapacity; - public QuadConverter(int initialCapacity) { - this.ebos = new EnumMap<>(GlNumericType.class); - initCapacities(); - - fillBuffer(initialCapacity); + public QuadConverter() { + this.ebo = new MappedGlBuffer(GlBufferType.ELEMENT_ARRAY_BUFFER); + this.quadCapacity = 0; } public ElementBuffer quads2Tris(int quads) { int indexCount = quads * 6; - GlNumericType type = getSmallestIndexType(indexCount); - if (quads > getCapacity(type)) { - fillBuffer(quads, indexCount, type); + if (quads > quadCapacity) { + ebo.ensureCapacity((long) indexCount * GlNumericType.UINT.getByteWidth()); + + try (MappedBuffer map = ebo.map()) { + ByteBuffer indices = map.unwrap(); + + fillBuffer(indices, quads); + } + ebo.unbind(); + + this.quadCapacity = quads; } - return new ElementBuffer(getBuffer(type), indexCount, type); - } - - private void initCapacities() { - this.capacities = new int[GlNumericType.values().length]; - } - - private int getCapacity(GlNumericType type) { - return capacities[type.ordinal()]; - } - - private void updateCapacity(GlNumericType type, int capacity) { - if (getCapacity(type) < capacity) { - capacities[type.ordinal()] = capacity; - } + return new ElementBuffer(ebo, indexCount, GlNumericType.UINT); } public void delete() { - ebos.values() - .forEach(GlBuffer::delete); - ebos.clear(); - initCapacities(); + ebo.delete(); + this.quadCapacity = 0; } - private void fillBuffer(int quads) { - int indexCount = quads * 6; + private void fillBuffer(ByteBuffer indices, int quads) { + long addr = MemoryUtil.memAddress(indices); + int numVertices = 4 * quads; + int baseVertex = 0; + while (baseVertex < numVertices) { + // writeQuadIndices(indices, baseVertex); + writeQuadIndicesUnsafe(addr, baseVertex); - fillBuffer(quads, indexCount, getSmallestIndexType(indexCount)); - } - - private void fillBuffer(int quads, int indexCount, GlNumericType type) { - MemoryStack stack = MemoryStack.stackPush(); - int bytes = indexCount * type.getByteWidth(); - - ByteBuffer indices; - if (bytes > stack.getSize()) { - indices = MemoryUtil.memAlloc(bytes); // not enough space on the preallocated stack - } else { - stack.push(); - indices = stack.malloc(bytes); + baseVertex += 4; + addr += 6 * 4; } - - indices.order(ByteOrder.nativeOrder()); - - fillBuffer(indices, type, quads); - - GlBuffer buffer = getBuffer(type); - - buffer.bind(); - buffer.upload(indices); - buffer.unbind(); - - if (bytes > stack.getSize()) { - MemoryUtil.memFree(indices); - } else { - stack.pop(); - } - - updateCapacity(type, quads); + // ((Buffer) indices).flip(); } - private void fillBuffer(ByteBuffer indices, GlNumericType type, int quads) { - for (int i = 0, max = 4 * quads; i < max; i += 4) { - // triangle a - type.castAndBuffer(indices, i); - type.castAndBuffer(indices, i + 1); - type.castAndBuffer(indices, i + 2); - // triangle b - type.castAndBuffer(indices, i); - type.castAndBuffer(indices, i + 2); - type.castAndBuffer(indices, i + 3); - } - ((Buffer) indices).flip(); + private void writeQuadIndices(ByteBuffer indices, int baseVertex) { + // triangle a + indices.putInt(baseVertex); + indices.putInt(baseVertex + 1); + indices.putInt(baseVertex + 2); + // triangle b + indices.putInt(baseVertex); + indices.putInt(baseVertex + 2); + indices.putInt(baseVertex + 3); } - private GlBuffer getBuffer(GlNumericType type) { - return ebos.computeIfAbsent(type, $ -> new MappedGlBuffer(GlBufferType.ELEMENT_ARRAY_BUFFER)); - } - - /** - * Given the needed number of indices, what is the smallest bit width type that can index everything?
- * - *

-	 * | indexCount   | type  |
-	 * |--------------|-------|
-	 * | [0, 255)     | byte  |
-	 * | [256, 65536)	| short	|
-	 * | [65537, )	| int	|
-	 * 
- */ - private static GlNumericType getSmallestIndexType(int indexCount) { -// indexCount = indexCount >>> 8; -// if (indexCount == 0) { -// return GlNumericType.UBYTE; -// } -// indexCount = indexCount >>> 8; -// if (indexCount == 0) { -// return GlNumericType.USHORT; -// } - - return GlNumericType.UINT; + private void writeQuadIndicesUnsafe(long addr, int baseVertex) { + // triangle a + MemoryUtil.memPutInt(addr, baseVertex); + MemoryUtil.memPutInt(addr + 4, baseVertex + 1); + MemoryUtil.memPutInt(addr + 8, baseVertex + 2); + // triangle b + MemoryUtil.memPutInt(addr + 12, baseVertex); + MemoryUtil.memPutInt(addr + 16, baseVertex + 2); + MemoryUtil.memPutInt(addr + 20, baseVertex + 3); } // make sure this gets reset first so it has a chance to repopulate @SubscribeEvent(priority = EventPriority.HIGHEST) public static void onRendererReload(ReloadRenderersEvent event) { - if (INSTANCE != null) INSTANCE.delete(); + if (INSTANCE != null) { + INSTANCE.delete(); + INSTANCE = null; + } } } diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/FragmentCompiler.java b/src/main/java/com/jozufozu/flywheel/core/compile/FragmentCompiler.java index 0c925373f..8fd766456 100644 --- a/src/main/java/com/jozufozu/flywheel/core/compile/FragmentCompiler.java +++ b/src/main/java/com/jozufozu/flywheel/core/compile/FragmentCompiler.java @@ -7,7 +7,7 @@ import com.jozufozu.flywheel.backend.gl.shader.ShaderType; import com.jozufozu.flywheel.core.CoreShaderInfoMap.CoreShaderInfo.FogType; import com.jozufozu.flywheel.core.shader.ShaderConstants; import com.jozufozu.flywheel.core.shader.StateSnapshot; -import com.jozufozu.flywheel.core.source.FileIndexImpl; +import com.jozufozu.flywheel.core.source.FileIndex; import com.jozufozu.flywheel.core.source.FileResolution; import com.jozufozu.flywheel.core.source.SourceFile; @@ -33,7 +33,7 @@ public class FragmentCompiler extends Memoizer { shaderConstants.writeInto(finalSource); finalSource.append('\n'); - var index = new FileIndexImpl(); + var index = new FileIndex(); // LAYOUT @@ -64,7 +64,11 @@ public class VertexCompiler extends Memoizer { .orElseThrow(); finalSource.append(generateFooter(key.vertexType, instanceStruct)); - return new GlShader(finalSource.toString(), ShaderType.VERTEX, ImmutableList.of(layoutShader.name, instanceShader.name, materialShader.name, contextShaderSource.name), shaderConstants); + try { + return new GlShader(finalSource.toString(), ShaderType.VERTEX, ImmutableList.of(layoutShader.name, instanceShader.name, materialShader.name, contextShaderSource.name), shaderConstants); + } catch (ShaderCompilationException e) { + throw e.withErrorLog(index); + } } protected String generateFooter(VertexType vertexType, ShaderStruct instance) { diff --git a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java index 5a9906a34..9dc640d71 100644 --- a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java +++ b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java @@ -61,20 +61,19 @@ public class CrumblingRenderer { Int2ObjectMap> activeStages = getActiveStageBlockEntities(levelRenderer, level); if (activeStages.isEmpty()) return; - GlStateTracker.State restoreState = GlStateTracker.getRestoreState(); + try (var restoreState = GlStateTracker.getRestoreState()) { - Matrix4f viewProjection = poseStack.last() - .pose() - .copy(); - viewProjection.multiplyBackward(projectionMatrix); + Matrix4f viewProjection = poseStack.last() + .pose() + .copy(); + viewProjection.multiplyBackward(projectionMatrix); - State state = STATE.get(); - var instanceManager = state.instanceManager; - var engine = state.instancerManager; + State state = STATE.get(); + var instanceManager = state.instanceManager; + var engine = state.instancerManager; - renderCrumblingInner(activeStages, instanceManager, engine, level, poseStack, camera, viewProjection); - - restoreState.restore(); + renderCrumblingInner(activeStages, instanceManager, engine, level, poseStack, camera, viewProjection); + } } private static void renderCrumblingInner(Int2ObjectMap> activeStages, InstanceManager instanceManager, CrumblingEngine engine, ClientLevel level, PoseStack stack, Camera camera, Matrix4f viewProjection) { diff --git a/src/main/java/com/jozufozu/flywheel/core/hardcoded/ModelPart.java b/src/main/java/com/jozufozu/flywheel/core/hardcoded/ModelPart.java index a30b26547..4d2f94202 100644 --- a/src/main/java/com/jozufozu/flywheel/core/hardcoded/ModelPart.java +++ b/src/main/java/com/jozufozu/flywheel/core/hardcoded/ModelPart.java @@ -2,6 +2,8 @@ package com.jozufozu.flywheel.core.hardcoded; import java.util.List; +import org.lwjgl.system.MemoryStack; + import com.jozufozu.flywheel.api.vertex.VertexList; import com.jozufozu.flywheel.core.model.Mesh; import com.jozufozu.flywheel.core.vertex.Formats; @@ -26,12 +28,14 @@ public class ModelPart implements Mesh { this.vertices = vertices; } - PosTexNormalWriterUnsafe writer = getType().createWriter(MemoryTracker.create(size())); - for (PartBuilder.CuboidBuilder cuboid : cuboids) { - cuboid.buffer(writer); - } + try (var stack = MemoryStack.stackPush()) { + PosTexNormalWriterUnsafe writer = getType().createWriter(stack.malloc(size())); + for (PartBuilder.CuboidBuilder cuboid : cuboids) { + cuboid.buffer(writer); + } - reader = writer.intoReader(); + reader = writer.intoReader(); + } } public static PartBuilder builder(String name, int sizeU, int sizeV) { diff --git a/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java b/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java index 260a8adf6..8c5e4342b 100644 --- a/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java +++ b/src/main/java/com/jozufozu/flywheel/core/model/ModelUtil.java @@ -1,6 +1,7 @@ package com.jozufozu.flywheel.core.model; import java.lang.reflect.Field; +import java.nio.ByteBuffer; import java.util.EnumMap; import java.util.Random; @@ -10,6 +11,7 @@ import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.DefaultVertexFormat; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexFormat; +import com.mojang.datafixers.util.Pair; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.RenderType; @@ -52,24 +54,17 @@ public class ModelUtil { return new WorldModelBuilder(layer); } - public static ShadeSeparatedBufferBuilder getBufferBuilder(Bufferable object) { + public static ShadeSeparatedBufferBuilder getBufferBuilder(Bufferable bufferable) { ModelBlockRenderer blockRenderer = VANILLA_RENDERER.getModelRenderer(); ThreadLocalObjects objects = THREAD_LOCAL_OBJECTS.get(); - ShadeSeparatedBufferBuilder builder = new ShadeSeparatedBufferBuilder(512); + objects.begin(); - builder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK); - objects.unshadedBuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK); - objects.shadeSeparatingWrapper.prepare(builder, objects.unshadedBuilder); + bufferable.bufferInto(blockRenderer, objects.shadeSeparatingWrapper, objects.random); - object.bufferInto(blockRenderer, objects.shadeSeparatingWrapper, objects.random); + objects.end(); - objects.shadeSeparatingWrapper.clear(); - objects.unshadedBuilder.end(); - builder.appendUnshadedVertices(objects.unshadedBuilder); - builder.end(); - - return builder; + return objects.separatedBufferBuilder; } private static PoseStack createRotation(Direction facing) { @@ -96,7 +91,21 @@ public class ModelUtil { private static class ThreadLocalObjects { public final Random random = new Random(); public final ShadeSeparatingVertexConsumer shadeSeparatingWrapper = new ShadeSeparatingVertexConsumer(); + public final ShadeSeparatedBufferBuilder separatedBufferBuilder = new ShadeSeparatedBufferBuilder(512); public final BufferBuilder unshadedBuilder = new BufferBuilder(512); + + private void begin() { + this.separatedBufferBuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK); + this.unshadedBuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.BLOCK); + this.shadeSeparatingWrapper.prepare(this.separatedBufferBuilder, this.unshadedBuilder); + } + + private void end() { + this.shadeSeparatingWrapper.clear(); + this.unshadedBuilder.end(); + this.separatedBufferBuilder.appendUnshadedVertices(this.unshadedBuilder); + this.separatedBufferBuilder.end(); + } } } diff --git a/src/main/java/com/jozufozu/flywheel/core/source/FileIndex.java b/src/main/java/com/jozufozu/flywheel/core/source/FileIndex.java index 55d91a96a..b2c5a9b6e 100644 --- a/src/main/java/com/jozufozu/flywheel/core/source/FileIndex.java +++ b/src/main/java/com/jozufozu/flywheel/core/source/FileIndex.java @@ -1,21 +1,68 @@ package com.jozufozu.flywheel.core.source; +import java.util.ArrayList; +import java.util.List; + +import org.jetbrains.annotations.Nullable; + +import com.jozufozu.flywheel.core.source.error.ErrorBuilder; import com.jozufozu.flywheel.core.source.span.Span; -public interface FileIndex { +public class FileIndex { + public final List files = new ArrayList<>(); + /** * Returns an arbitrary file ID for use this compilation context, or generates one if missing. - * * @param sourceFile The file to retrieve the ID for. * @return A file ID unique to the given sourceFile. */ - int getFileID(SourceFile sourceFile); + public int getFileID(SourceFile sourceFile) { + int i = files.indexOf(sourceFile); + if (i != -1) { + return i; + } - boolean exists(SourceFile sourceFile); + int size = files.size(); + files.add(sourceFile); + return size; + } - SourceFile getFile(int fileID); + public boolean exists(SourceFile sourceFile) { + return files.contains(sourceFile); + } - default Span getLineSpan(int fileId, int lineNo) { + public SourceFile getFile(int fileId) { + return files.get(fileId); + } + + public String parseErrors(String log) { + List lines = log.lines() + .toList(); + + StringBuilder errors = new StringBuilder(); + for (String line : lines) { + ErrorBuilder builder = parseCompilerError(line); + + if (builder != null) { + errors.append(builder.build()); + } else { + errors.append(line).append('\n'); + } + } + return errors.toString(); + } + + @Nullable + private ErrorBuilder parseCompilerError(String line) { + try { + return ErrorBuilder.fromLogLine(this, line); + } catch (Exception ignored) { + } + + return null; + } + + public Span getLineSpan(int fileId, int lineNo) { return getFile(fileId).getLineSpanNoWhitespace(lineNo); } } diff --git a/src/main/java/com/jozufozu/flywheel/core/source/FileIndexImpl.java b/src/main/java/com/jozufozu/flywheel/core/source/FileIndexImpl.java deleted file mode 100644 index fcab6d684..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/source/FileIndexImpl.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.jozufozu.flywheel.core.source; - -import java.util.ArrayList; -import java.util.List; - -import org.jetbrains.annotations.Nullable; - -import com.jozufozu.flywheel.backend.Backend; -import com.jozufozu.flywheel.core.source.error.ErrorBuilder; -import com.jozufozu.flywheel.core.source.error.ErrorReporter; - -import net.minecraft.resources.ResourceLocation; - -public class FileIndexImpl implements FileIndex { - public final List files = new ArrayList<>(); - - /** - * Returns an arbitrary file ID for use this compilation context, or generates one if missing. - * @param sourceFile The file to retrieve the ID for. - * @return A file ID unique to the given sourceFile. - */ - @Override - public int getFileID(SourceFile sourceFile) { - int i = files.indexOf(sourceFile); - if (i != -1) { - return i; - } - - int size = files.size(); - files.add(sourceFile); - return size; - } - - @Override - public boolean exists(SourceFile sourceFile) { - return files.contains(sourceFile); - } - - @Override - public SourceFile getFile(int fileId) { - return files.get(fileId); - } - - public void printShaderInfoLog(String source, String log, ResourceLocation name) { - List lines = log.lines() - .toList(); - - boolean needsSourceDump = false; - - StringBuilder errors = new StringBuilder(); - for (String line : lines) { - ErrorBuilder builder = parseCompilerError(line); - - if (builder != null) { - errors.append(builder.build()); - } else { - errors.append(line).append('\n'); - needsSourceDump = true; - } - } - Backend.LOGGER.error("Errors compiling '" + name + "': \n" + errors); - if (needsSourceDump) { - // TODO: generated code gets its own "file" - ErrorReporter.printLines(source); - } - } - - @Nullable - private ErrorBuilder parseCompilerError(String line) { - try { - return ErrorBuilder.fromLogLine(this, line); - } catch (Exception ignored) { - } - - return null; - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/structs/BasicWriterUnsafe.java b/src/main/java/com/jozufozu/flywheel/core/structs/BasicWriterUnsafe.java index c29d3e057..5a7fa37c3 100644 --- a/src/main/java/com/jozufozu/flywheel/core/structs/BasicWriterUnsafe.java +++ b/src/main/java/com/jozufozu/flywheel/core/structs/BasicWriterUnsafe.java @@ -1,15 +1,16 @@ package com.jozufozu.flywheel.core.structs; +import java.nio.ByteBuffer; + import org.lwjgl.system.MemoryUtil; import com.jozufozu.flywheel.api.struct.StructType; -import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; import com.jozufozu.flywheel.backend.struct.UnsafeBufferWriter; public abstract class BasicWriterUnsafe extends UnsafeBufferWriter { - public BasicWriterUnsafe(VecBuffer backingBuffer, StructType vertexType) { - super(backingBuffer, vertexType); + public BasicWriterUnsafe(StructType structType, ByteBuffer byteBuffer) { + super(structType, byteBuffer); } @Override diff --git a/src/main/java/com/jozufozu/flywheel/core/structs/model/ModelType.java b/src/main/java/com/jozufozu/flywheel/core/structs/model/ModelType.java index e6547a3eb..660ebd611 100644 --- a/src/main/java/com/jozufozu/flywheel/core/structs/model/ModelType.java +++ b/src/main/java/com/jozufozu/flywheel/core/structs/model/ModelType.java @@ -1,9 +1,10 @@ package com.jozufozu.flywheel.core.structs.model; +import java.nio.ByteBuffer; + import com.jozufozu.flywheel.api.struct.BatchedStructType; import com.jozufozu.flywheel.api.struct.InstancedStructType; import com.jozufozu.flywheel.api.struct.StructWriter; -import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; import com.jozufozu.flywheel.core.layout.BufferLayout; import com.jozufozu.flywheel.core.layout.CommonItems; import com.jozufozu.flywheel.core.model.ModelTransformer; @@ -28,8 +29,8 @@ public class ModelType implements InstancedStructType, BatchedStructT } @Override - public StructWriter getWriter(VecBuffer backing) { - return new ModelWriterUnsafe(backing, this); + public StructWriter getWriter(ByteBuffer backing) { + return new ModelWriterUnsafe(this, backing); } @Override diff --git a/src/main/java/com/jozufozu/flywheel/core/structs/model/ModelWriterUnsafe.java b/src/main/java/com/jozufozu/flywheel/core/structs/model/ModelWriterUnsafe.java index b125b8d6c..26b12801a 100644 --- a/src/main/java/com/jozufozu/flywheel/core/structs/model/ModelWriterUnsafe.java +++ b/src/main/java/com/jozufozu/flywheel/core/structs/model/ModelWriterUnsafe.java @@ -1,14 +1,15 @@ package com.jozufozu.flywheel.core.structs.model; +import java.nio.ByteBuffer; + import com.jozufozu.flywheel.api.struct.StructType; -import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; import com.jozufozu.flywheel.core.structs.BasicWriterUnsafe; import com.jozufozu.flywheel.util.MatrixWrite; public class ModelWriterUnsafe extends BasicWriterUnsafe { - public ModelWriterUnsafe(VecBuffer backingBuffer, StructType vertexType) { - super(backingBuffer, vertexType); + public ModelWriterUnsafe(StructType structType, ByteBuffer byteBuffer) { + super(structType, byteBuffer); } @Override diff --git a/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedType.java b/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedType.java index 07428287b..0c00fe649 100644 --- a/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedType.java +++ b/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedType.java @@ -1,9 +1,10 @@ package com.jozufozu.flywheel.core.structs.oriented; +import java.nio.ByteBuffer; + import com.jozufozu.flywheel.api.struct.BatchedStructType; import com.jozufozu.flywheel.api.struct.InstancedStructType; import com.jozufozu.flywheel.api.struct.StructWriter; -import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; import com.jozufozu.flywheel.core.layout.BufferLayout; import com.jozufozu.flywheel.core.layout.CommonItems; import com.jozufozu.flywheel.core.model.ModelTransformer; @@ -29,8 +30,8 @@ public class OrientedType implements InstancedStructType, BatchedS } @Override - public StructWriter getWriter(VecBuffer backing) { - return new OrientedWriterUnsafe(backing, this); + public StructWriter getWriter(ByteBuffer backing) { + return new OrientedWriterUnsafe(this, backing); } @Override diff --git a/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedWriterUnsafe.java b/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedWriterUnsafe.java index 45730aca1..8f030f837 100644 --- a/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedWriterUnsafe.java +++ b/src/main/java/com/jozufozu/flywheel/core/structs/oriented/OrientedWriterUnsafe.java @@ -1,14 +1,15 @@ package com.jozufozu.flywheel.core.structs.oriented; +import java.nio.ByteBuffer; + import org.lwjgl.system.MemoryUtil; import com.jozufozu.flywheel.api.struct.StructType; -import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; import com.jozufozu.flywheel.core.structs.BasicWriterUnsafe; public class OrientedWriterUnsafe extends BasicWriterUnsafe { - public OrientedWriterUnsafe(VecBuffer backingBuffer, StructType vertexType) { - super(backingBuffer, vertexType); + public OrientedWriterUnsafe(StructType structType, ByteBuffer byteBuffer) { + super(structType, byteBuffer); } @Override diff --git a/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertexListUnsafe.java b/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertexListUnsafe.java index 7258b3af4..a3d6e5930 100644 --- a/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertexListUnsafe.java +++ b/src/main/java/com/jozufozu/flywheel/core/vertex/BlockVertexListUnsafe.java @@ -1,35 +1,24 @@ package com.jozufozu.flywheel.core.vertex; +import java.lang.ref.Cleaner; import java.nio.ByteBuffer; import org.lwjgl.system.MemoryUtil; import com.jozufozu.flywheel.api.vertex.ShadedVertexList; -import com.jozufozu.flywheel.api.vertex.VertexList; import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.util.RenderMath; -public class BlockVertexListUnsafe implements VertexList { - - private final ByteBuffer buffer; - private final int vertexCount; - private final long base; +public class BlockVertexListUnsafe extends TrackedVertexList { public BlockVertexListUnsafe(ByteBuffer buffer, int vertexCount) { - this.buffer = buffer; - this.base = MemoryUtil.memAddress(buffer); - this.vertexCount = vertexCount; + super(buffer, vertexCount); } private long ptr(long index) { return base + index * 32; } - @Override - public boolean isEmpty() { - return vertexCount == 0; - } - @Override public float getX(int index) { return MemoryUtil.memGetFloat(ptr(index)); @@ -95,11 +84,6 @@ public class BlockVertexListUnsafe implements VertexList { return RenderMath.f(MemoryUtil.memGetByte(ptr(index) + 30)); } - @Override - public int getVertexCount() { - return vertexCount; - } - @Override public VertexType getVertexType() { return Formats.BLOCK; diff --git a/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalVertexListUnsafe.java b/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalVertexListUnsafe.java index df6aa57cd..d01249a64 100644 --- a/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalVertexListUnsafe.java +++ b/src/main/java/com/jozufozu/flywheel/core/vertex/PosTexNormalVertexListUnsafe.java @@ -1,23 +1,20 @@ package com.jozufozu.flywheel.core.vertex; +import java.lang.ref.Cleaner; import java.nio.ByteBuffer; import org.lwjgl.system.MemoryUtil; import com.jozufozu.flywheel.api.vertex.VertexList; import com.jozufozu.flywheel.api.vertex.VertexType; +import com.jozufozu.flywheel.backend.FlywheelMemory; import com.jozufozu.flywheel.util.RenderMath; +import com.mojang.blaze3d.platform.MemoryTracker; -public class PosTexNormalVertexListUnsafe implements VertexList { - - private final ByteBuffer buffer; - private final int vertexCount; - private final long base; +public class PosTexNormalVertexListUnsafe extends TrackedVertexList { public PosTexNormalVertexListUnsafe(ByteBuffer buffer, int vertexCount) { - this.buffer = buffer; - this.vertexCount = vertexCount; - this.base = MemoryUtil.memAddress(buffer); + super(buffer, vertexCount); } private long ptr(long idx) { @@ -89,11 +86,6 @@ public class PosTexNormalVertexListUnsafe implements VertexList { return RenderMath.f(MemoryUtil.memGetByte(ptr(index) + 22)); } - @Override - public int getVertexCount() { - return vertexCount; - } - @Override public VertexType getVertexType() { return Formats.POS_TEX_NORMAL; diff --git a/src/main/java/com/jozufozu/flywheel/core/vertex/TrackedVertexList.java b/src/main/java/com/jozufozu/flywheel/core/vertex/TrackedVertexList.java new file mode 100644 index 000000000..00cb24eec --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/core/vertex/TrackedVertexList.java @@ -0,0 +1,41 @@ +package com.jozufozu.flywheel.core.vertex; + +import java.lang.ref.Cleaner; +import java.nio.Buffer; +import java.nio.ByteBuffer; + +import org.lwjgl.system.MemoryUtil; + +import com.jozufozu.flywheel.api.vertex.VertexList; +import com.jozufozu.flywheel.backend.FlywheelMemory; +import com.mojang.blaze3d.platform.MemoryTracker; + +public abstract class TrackedVertexList implements VertexList, AutoCloseable { + + protected final ByteBuffer contents; + protected final long base; + protected final int vertexCount; + private final Cleaner.Cleanable cleanable; + + protected TrackedVertexList(ByteBuffer copyFrom, int vertexCount) { + this.contents = MemoryTracker.create(copyFrom.capacity()); + this.contents.order(copyFrom.order()); + this.contents.put(copyFrom); + ((Buffer) this.contents).flip(); + + this.cleanable = FlywheelMemory.track(this, this.contents); + + this.base = MemoryUtil.memAddress(this.contents); + this.vertexCount = vertexCount; + } + + @Override + public void close() { + cleanable.clean(); + } + + @Override + public int getVertexCount() { + return vertexCount; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/event/ForgeEvents.java b/src/main/java/com/jozufozu/flywheel/event/ForgeEvents.java index 52e31ff9b..19ab783d8 100644 --- a/src/main/java/com/jozufozu/flywheel/event/ForgeEvents.java +++ b/src/main/java/com/jozufozu/flywheel/event/ForgeEvents.java @@ -1,6 +1,10 @@ package com.jozufozu.flywheel.event; +import java.util.ArrayList; + +import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.backend.Backend; +import com.jozufozu.flywheel.backend.FlywheelMemory; import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; import com.jozufozu.flywheel.light.LightUpdater; import com.jozufozu.flywheel.util.WorldAttached; @@ -21,7 +25,15 @@ public class ForgeEvents { if (Minecraft.getInstance().options.renderDebug) { - InstancedRenderDispatcher.getDebugString(event.getRight()); + ArrayList debug = event.getRight(); + debug.add(""); + debug.add("Flywheel: " + Flywheel.getVersion()); + + InstancedRenderDispatcher.getDebugString(debug); + + debug.add("Memory used:"); + debug.add("GPU: " + FlywheelMemory.getGPUMemory()); + debug.add("CPU: " + FlywheelMemory.getCPUMemory()); } } diff --git a/src/main/java/com/jozufozu/flywheel/mixin/FrustumMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/FrustumMixin.java index ab9eaabbb..f42f4b86f 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/FrustumMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/FrustumMixin.java @@ -20,9 +20,9 @@ public class FrustumMixin { @Inject(method = "prepare", at = @At("TAIL")) private void onPrepare(double x, double y, double z, CallbackInfo ci) { if (OptifineHandler.isShadowPass()) { - GlStateTracker.State restoreState = GlStateTracker.getRestoreState(); - MinecraftForge.EVENT_BUS.post(new BeginFrameEvent(Minecraft.getInstance().level, LastActiveCamera.getActiveCamera(), (Frustum) (Object) this)); - restoreState.restore(); + try (var restoreState = GlStateTracker.getRestoreState()) { + MinecraftForge.EVENT_BUS.post(new BeginFrameEvent(Minecraft.getInstance().level, LastActiveCamera.getActiveCamera(), (Frustum) (Object) this)); + } } } } diff --git a/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererMixin.java index f730bd77e..93fc64158 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererMixin.java @@ -46,20 +46,19 @@ public class LevelRendererMixin { Vec3 position = pCamera.getPosition(); RenderContext.CURRENT = new RenderContext(level, pPoseStack, RenderLayerEvent.createViewProjection(pPoseStack), renderBuffers, position.x, position.y, position.z); - GlStateTracker.State restoreState = GlStateTracker.getRestoreState(); - MinecraftForge.EVENT_BUS.post(new BeginFrameEvent(level, pCamera, null)); - restoreState.restore(); + try (var restoreState = GlStateTracker.getRestoreState()) { + MinecraftForge.EVENT_BUS.post(new BeginFrameEvent(level, pCamera, null)); + } } @Inject(at = @At("TAIL"), method = "renderChunkLayer") private void renderChunkLayer(RenderType pRenderType, PoseStack pPoseStack, double pCamX, double pCamY, double pCamZ, Matrix4f pProjectionMatrix, CallbackInfo ci) { - GlStateTracker.State restoreState = GlStateTracker.getRestoreState(); + try (var restoreState = GlStateTracker.getRestoreState()) { - // TODO: Is this necessary? - InstancedRenderDispatcher.renderSpecificType(RenderContext.CURRENT, pRenderType); - MinecraftForge.EVENT_BUS.post(new RenderLayerEvent(RenderContext.CURRENT, pRenderType)); - - restoreState.restore(); + // TODO: Is this necessary? + InstancedRenderDispatcher.renderSpecificType(RenderContext.CURRENT, pRenderType); + MinecraftForge.EVENT_BUS.post(new RenderLayerEvent(RenderContext.CURRENT, pRenderType)); + } } @Inject(at = @At("TAIL"), method = "renderLevel") diff --git a/src/main/java/com/jozufozu/flywheel/mixin/MultiBufferSourceMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/MultiBufferSourceMixin.java index 7af64e9c3..ae7d1a77a 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/MultiBufferSourceMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/MultiBufferSourceMixin.java @@ -19,22 +19,20 @@ public class MultiBufferSourceMixin { @Inject(method = "endBatch(Lnet/minecraft/client/renderer/RenderType;)V", at = @At("TAIL")) private void renderLayer(RenderType renderType, CallbackInfo ci) { if (RenderContext.CURRENT != null && Backend.isGameActive() && Backend.isOn()) { - GlStateTracker.State restoreState = GlStateTracker.getRestoreState(); + try (var restoreState = GlStateTracker.getRestoreState()) { - InstancedRenderDispatcher.renderSpecificType(RenderContext.CURRENT, renderType); + InstancedRenderDispatcher.renderSpecificType(RenderContext.CURRENT, renderType); - restoreState.restore(); + } } } @Inject(method = "endBatch()V", at = @At("TAIL")) private void endBatch(CallbackInfo ci) { if (RenderContext.CURRENT != null && Backend.isGameActive() && Backend.isOn()) { - GlStateTracker.State restoreState = GlStateTracker.getRestoreState(); - - InstancedRenderDispatcher.renderAllRemaining(RenderContext.CURRENT); - - restoreState.restore(); + try (var restoreState = GlStateTracker.getRestoreState()) { + InstancedRenderDispatcher.renderAllRemaining(RenderContext.CURRENT); + } } } } diff --git a/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix3fMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix3fMixin.java index a6057dc0c..b8cbadd0d 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix3fMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix3fMixin.java @@ -1,10 +1,11 @@ package com.jozufozu.flywheel.mixin.matrix; +import java.nio.ByteBuffer; + import org.lwjgl.system.MemoryUtil; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; -import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; import com.jozufozu.flywheel.util.MatrixWrite; import com.mojang.math.Matrix3f; @@ -35,7 +36,7 @@ public abstract class Matrix3fMixin implements MatrixWrite { } @Override - public void flywheel$write(VecBuffer buffer) { + public void flywheel$write(ByteBuffer buffer) { buffer.putFloat(m00); buffer.putFloat(m10); buffer.putFloat(m20); diff --git a/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix4fMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix4fMixin.java index e97fa3e8d..14f64c9b0 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix4fMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/matrix/Matrix4fMixin.java @@ -1,10 +1,11 @@ package com.jozufozu.flywheel.mixin.matrix; +import java.nio.ByteBuffer; + import org.lwjgl.system.MemoryUtil; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; -import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; import com.jozufozu.flywheel.util.MatrixWrite; import com.mojang.math.Matrix4f; @@ -49,7 +50,7 @@ public abstract class Matrix4fMixin implements MatrixWrite { } @Override - public void flywheel$write(VecBuffer buf) { + public void flywheel$write(ByteBuffer buf) { buf.putFloat(m00); buf.putFloat(m10); buf.putFloat(m20); diff --git a/src/main/java/com/jozufozu/flywheel/util/MatrixWrite.java b/src/main/java/com/jozufozu/flywheel/util/MatrixWrite.java index 0619d0793..9af8c92bc 100644 --- a/src/main/java/com/jozufozu/flywheel/util/MatrixWrite.java +++ b/src/main/java/com/jozufozu/flywheel/util/MatrixWrite.java @@ -1,6 +1,6 @@ package com.jozufozu.flywheel.util; -import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer; +import java.nio.ByteBuffer; /** * @see com.jozufozu.flywheel.mixin.matrix.Matrix3fMixin @@ -13,5 +13,5 @@ public interface MatrixWrite { */ void flywheel$writeUnsafe(long ptr); - void flywheel$write(VecBuffer buf); + void flywheel$write(ByteBuffer buf); } diff --git a/src/main/resources/assets/flywheel/flywheel/context/crumbling.frag b/src/main/resources/assets/flywheel/flywheel/context/crumbling.frag index a7dffc948..46cb1ad37 100644 --- a/src/main/resources/assets/flywheel/flywheel/context/crumbling.frag +++ b/src/main/resources/assets/flywheel/flywheel/context/crumbling.frag @@ -12,9 +12,10 @@ vec2 flattenedPos(vec3 pos, vec3 normal) { pos -= floor(pos) + vec3(0.5); float sinYRot = -normal.x; - float sqLength = normal.x * normal.x + normal.z * normal.z; + vec2 XZ = normal.xz; + float sqLength = dot(XZ, XZ); if (sqLength > 0) { - sinYRot /= sqrt(sqLength); + sinYRot *= inversesqrt(sqLength); sinYRot = clamp(sinYRot, -1, 1); }