diff --git a/gradle/scripts/moddevgradle.gradle b/gradle/scripts/moddevgradle.gradle index 8b78218109..2b936141dd 100644 --- a/gradle/scripts/moddevgradle.gradle +++ b/gradle/scripts/moddevgradle.gradle @@ -42,6 +42,11 @@ neoForge { systemProperty 'neoforge.enabledGameTestNamespaces', project.mod_id } + clientEnforceLegacyBuffers { + client() + systemProperty "anvilcraft.enforceLegacyBuffers", "true" + } + server { server() programArgument '--nogui' diff --git a/src/main/java/dev/dubhe/anvilcraft/api/power/SimplePowerGrid.java b/src/main/java/dev/dubhe/anvilcraft/api/power/SimplePowerGrid.java index dd2e09e6f5..615df21e26 100644 --- a/src/main/java/dev/dubhe/anvilcraft/api/power/SimplePowerGrid.java +++ b/src/main/java/dev/dubhe/anvilcraft/api/power/SimplePowerGrid.java @@ -3,9 +3,9 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; import dev.dubhe.anvilcraft.AnvilCraft; -import dev.dubhe.anvilcraft.client.renderer.Line; import dev.dubhe.anvilcraft.client.support.PowerGridSupport; import dev.dubhe.anvilcraft.util.ColorUtil; +import dev.dubhe.anvilcraft.util.Line; import dev.dubhe.anvilcraft.util.ShapeUtil; import dev.dubhe.anvilcraft.util.VirtualThreadFactoryImpl; import lombok.Getter; diff --git a/src/main/java/dev/dubhe/anvilcraft/api/rendering/CompileResult.java b/src/main/java/dev/dubhe/anvilcraft/api/rendering/CompileResult.java deleted file mode 100644 index 5ee32609e0..0000000000 --- a/src/main/java/dev/dubhe/anvilcraft/api/rendering/CompileResult.java +++ /dev/null @@ -1,60 +0,0 @@ -package dev.dubhe.anvilcraft.api.rendering; - -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.vertex.VertexBuffer; -import com.mojang.blaze3d.vertex.VertexFormat; -import lombok.EqualsAndHashCode; -import net.minecraft.client.renderer.RenderType; -import org.lwjgl.opengl.GL15; -import org.lwjgl.opengl.GL15C; -import org.lwjgl.system.MemoryUtil; - -@EqualsAndHashCode -final class CompileResult { - private static final MemoryUtil.MemoryAllocator ALLOCATOR = MemoryUtil.getAllocator(false); - private final RenderType renderType; - private final int vertexCount; - private final int vertexSize; - private final long vertexBufferPtr; - final int indexCount; - private boolean freed = false; - - CompileResult( - RenderType renderType, - int vertexCount, - int vertexSize, - long vertexBufferPtr, - int indexCount - ) { - this.renderType = renderType; - this.vertexCount = vertexCount; - this.vertexSize = vertexSize; - this.vertexBufferPtr = vertexBufferPtr; - this.indexCount = indexCount; - } - - void upload(VertexBuffer vertexBuffer) { - if (freed) return; - VertexFormat.Mode mode = renderType.mode; - vertexBuffer.bind(); - if (vertexBuffer.format != null) { - vertexBuffer.format.clearBufferState(); - } - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vertexBuffer.vertexBufferId); - renderType.format.setupBufferState(); - vertexBuffer.format = renderType.format; - GL15C.nglBufferData(GL15.GL_ARRAY_BUFFER, (long) vertexCount * vertexSize, vertexBufferPtr, GL15.GL_STATIC_DRAW); - RenderSystem.AutoStorageIndexBuffer indexBuffer = RenderSystem.getSequentialBuffer(mode); - if (indexBuffer != vertexBuffer.sequentialIndices || !indexBuffer.hasStorage(indexCount)) { - indexBuffer.bind(indexCount); - } - vertexBuffer.sequentialIndices = indexBuffer; - VertexBuffer.unbind(); - } - - void free() { - if (freed) return; - ALLOCATOR.free(vertexBufferPtr); - freed = true; - } -} diff --git a/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/CompileResult.java b/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/CompileResult.java new file mode 100644 index 0000000000..850fe6d86b --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/CompileResult.java @@ -0,0 +1,48 @@ +package dev.dubhe.anvilcraft.api.rendering.foundation; + +import dev.dubhe.anvilcraft.api.rendering.foundation.buffer.vertex.GlVertexBuffer; +import dev.dubhe.anvilcraft.api.rendering.foundation.buffer.vertex.QuadSortingState; +import lombok.EqualsAndHashCode; +import net.minecraft.client.renderer.RenderType; +import org.jetbrains.annotations.Nullable; +import org.joml.Vector3f; +import org.lwjgl.system.MemoryUtil; + +@EqualsAndHashCode +public final class CompileResult implements Disposable { + private static final MemoryUtil.MemoryAllocator ALLOCATOR = MemoryUtil.getAllocator(false); + private final RenderType renderType; + private final int vertexCount; + private final int vertexSize; + private final long vertexBufferPtr; + private final int indexCount; + @Nullable + private final QuadSortingState sortingState; + private boolean freed = false; + + public CompileResult( + RenderType renderType, + int vertexCount, + int vertexSize, + long vertexBufferPtr, + int indexCount, + @Nullable QuadSortingState sortingState + ) { + this.renderType = renderType; + this.vertexCount = vertexCount; + this.vertexSize = vertexSize; + this.vertexBufferPtr = vertexBufferPtr; + this.indexCount = indexCount; + this.sortingState = sortingState; + } + + public void upload(GlVertexBuffer vertexBuffer) { + vertexBuffer.upload(vertexBufferPtr, vertexCount * vertexSize, vertexCount, indexCount, sortingState, new Vector3f(), this); + } + + public void dispose() { + if (freed) return; + ALLOCATOR.free(vertexBufferPtr); + freed = true; + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/Disposable.java b/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/Disposable.java new file mode 100644 index 0000000000..fbc9359e53 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/Disposable.java @@ -0,0 +1,8 @@ +package dev.dubhe.anvilcraft.api.rendering.foundation; + +public interface Disposable { + /** + * It is guaranteed to run on Render Thread. + */ + void dispose(); +} diff --git a/src/main/java/dev/dubhe/anvilcraft/api/rendering/FullyBufferedBufferSource.java b/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/FullyBufferedBufferSource.java similarity index 69% rename from src/main/java/dev/dubhe/anvilcraft/api/rendering/FullyBufferedBufferSource.java rename to src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/FullyBufferedBufferSource.java index 2d1241858d..4a8b207ab7 100644 --- a/src/main/java/dev/dubhe/anvilcraft/api/rendering/FullyBufferedBufferSource.java +++ b/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/FullyBufferedBufferSource.java @@ -1,30 +1,33 @@ -package dev.dubhe.anvilcraft.api.rendering; +package dev.dubhe.anvilcraft.api.rendering.foundation; import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.ByteBufferBuilder; import com.mojang.blaze3d.vertex.MeshData; -import com.mojang.blaze3d.vertex.VertexBuffer; import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.blaze3d.vertex.VertexFormat; +import dev.dubhe.anvilcraft.api.rendering.foundation.buffer.vertex.GlVertexBuffer; +import dev.dubhe.anvilcraft.api.rendering.foundation.buffer.vertex.QuadSortingState; import it.unimi.dsi.fastutil.objects.Reference2IntMap; +import it.unimi.dsi.fastutil.objects.Reference2IntMaps; import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; +import lombok.Getter; import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.RenderType; import org.lwjgl.system.MemoryUtil; import javax.annotation.ParametersAreNonnullByDefault; -import java.util.HashMap; import java.util.Map; -import java.util.function.Consumer; -import java.util.function.Function; +import java.util.concurrent.ConcurrentHashMap; @ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault -public class FullyBufferedBufferSource extends MultiBufferSource.BufferSource implements AutoCloseable { +public class FullyBufferedBufferSource extends MultiBufferSource.BufferSource implements Disposable { private static final MemoryUtil.MemoryAllocator ALLOCATOR = MemoryUtil.getAllocator(false); - private final Map byteBuffers = new HashMap<>(); - private final Map bufferBuilders = new HashMap<>(); - final Reference2IntMap indexCountMap = new Reference2IntOpenHashMap<>(); + private final Map byteBuffers = new ConcurrentHashMap<>(); + private final Map bufferBuilders = new ConcurrentHashMap<>(); + @Getter + private final Reference2IntMap indexCountMap = Reference2IntMaps.synchronize(new Reference2IntOpenHashMap<>()); public FullyBufferedBufferSource() { super(null, null); @@ -50,12 +53,9 @@ public boolean isEmpty() { public void endBatch(RenderType renderType) { } - public void upload( - Function vertexBufferGetter, - Consumer runner - ) { + public void upload(CompileContext context) { for (RenderType renderType : bufferBuilders.keySet()) { - runner.accept(() -> { + context.submitUploadTask(() -> { BufferBuilder bufferBuilder = bufferBuilders.get(renderType); ByteBufferBuilder byteBuffer = byteBuffers.get(renderType); long ptr = byteBuffer.pointer; @@ -64,19 +64,23 @@ public void upload( long allocated = ALLOCATOR.malloc(compiledVertices); MemoryUtil.memCopy(ptr, allocated, compiledVertices); MeshData mesh = bufferBuilder.build(); - if (mesh != null) { - mesh.close(); + if (mesh == null) return; + QuadSortingState state = null; + + if (renderType.sortOnUpload) { + state = QuadSortingState.fromMesh(mesh); } + mesh.close(); CompileResult compileResult = new CompileResult( renderType, bufferBuilder.vertices, renderType.format.getVertexSize(), allocated, - renderType.mode.indexCount(bufferBuilder.vertices) + renderType.mode.indexCount(bufferBuilder.vertices), + state ); indexCountMap.put(renderType, renderType.mode.indexCount(bufferBuilder.vertices)); - compileResult.upload(vertexBufferGetter.apply(renderType)); - compileResult.free(); + compileResult.upload(context.getOrCreateBuffer(renderType, mesh.drawState().indexType())); } byteBuffer.close(); bufferBuilders.remove(renderType); @@ -90,7 +94,13 @@ public void close(RenderType renderType) { builder.close(); } - public void close() { + public void dispose() { byteBuffers.keySet().forEach(this::close); } + + public interface CompileContext { + GlVertexBuffer getOrCreateBuffer(RenderType renderType, VertexFormat.IndexType indexType); + + void submitUploadTask(Runnable runnable); + } } diff --git a/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/QuadSorter.java b/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/QuadSorter.java new file mode 100644 index 0000000000..2c8fc89758 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/QuadSorter.java @@ -0,0 +1,19 @@ +package dev.dubhe.anvilcraft.api.rendering.foundation; + +import dev.dubhe.anvilcraft.api.rendering.foundation.buffer.vertex.QuadSortingState; +import it.unimi.dsi.fastutil.ints.IntArrays; +import org.joml.Vector3f; + +public class QuadSorter { + public static int[] buildSortedIndexByDistance(QuadSortingState state, Vector3f point){ + float[] distances = new float[state.quadCenters().length]; + int[] indexes = new int[state.quadCenters().length]; + + for (int i = 0; i < state.quadCenters().length; i++) { + distances[i] = state.quadCenters()[i].distanceSquared(point); + indexes[i] = i; + } + IntArrays.mergeSort(indexes, (a,b) -> Float.compare(distances[a], distances[b])); + return indexes; + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/BufferHost.java b/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/BufferHost.java new file mode 100644 index 0000000000..7b67bd026b --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/BufferHost.java @@ -0,0 +1,4 @@ +package dev.dubhe.anvilcraft.api.rendering.foundation.buffer; + +public interface BufferHost { +} diff --git a/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/GlBufferStorage.java b/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/GlBufferStorage.java new file mode 100644 index 0000000000..e5a76bb446 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/GlBufferStorage.java @@ -0,0 +1,53 @@ +package dev.dubhe.anvilcraft.api.rendering.foundation.buffer; + +import dev.dubhe.anvilcraft.api.rendering.foundation.Disposable; +import org.lwjgl.opengl.GL; + +import static org.lwjgl.opengl.GL45.*; + +public abstract class GlBufferStorage implements Disposable { + public static final boolean BUFFER_STORAGE_SUPPORT = GL.getCapabilities().GL_ARB_buffer_storage && System.getProperty("anvilcraft.enforceLegacyBuffers") == null; + protected final int glBufferId; + protected final int target; + protected boolean valid = true; + + static { + if (BUFFER_STORAGE_SUPPORT) { + System.out.println("Using GL_ARB_buffer_storage as buffer storage."); + } + } + + GlBufferStorage(int target, C configureContext) { + this.target = target; + this.glBufferId = glGenBuffers(); + } + + public abstract void setupBufferState(C configureContext); + + public void upload(long ptr, long size, Disposable uploadSrc) { + this.upload(ptr, size); + uploadSrc.dispose(); + } + + /** + * Runs on worker thread + */ + public abstract void upload(long ptr, long size); + + public void bind() { + glBindBuffer(target, glBufferId); + } + + @Override + public void dispose() { + if (!valid) return; + bind(); + glDeleteBuffers(glBufferId); + unbind(); + valid = false; + } + + public void unbind() { + glBindBuffer(target, 0); + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/GlBufferStorageLegacy.java b/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/GlBufferStorageLegacy.java new file mode 100644 index 0000000000..30d4cb7115 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/GlBufferStorageLegacy.java @@ -0,0 +1,40 @@ +package dev.dubhe.anvilcraft.api.rendering.foundation.buffer; + +import com.mojang.blaze3d.systems.RenderSystem; +import dev.dubhe.anvilcraft.api.rendering.foundation.Disposable; + +import static org.lwjgl.opengl.GL45.*; + +public abstract class GlBufferStorageLegacy extends GlBufferStorage { + + protected GlBufferStorageLegacy(int target,C configureContext) { + super(target, configureContext); + bind(); + this.setupBufferState(configureContext); + } + + @Override + public void upload(long ptr, long size, Disposable disposable) { + if (RenderSystem.isOnRenderThread()) { + nglBufferData(this.target, size, ptr, GL_STATIC_DRAW); + disposable.dispose(); + return; + } + RenderSystem.recordRenderCall(() -> { + nglBufferData(this.target, size, ptr, GL_STATIC_DRAW); + disposable.dispose(); + } + ); + } + + public void upload(long ptr, long size) { + if (RenderSystem.isOnRenderThread()) { + nglBufferData(this.target, size, ptr, GL_STATIC_DRAW); + return; + } + RenderSystem.recordRenderCall(() -> { + nglBufferData(this.target, size, ptr, GL_STATIC_DRAW); + } + ); + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/GlBufferStorageModern.java b/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/GlBufferStorageModern.java new file mode 100644 index 0000000000..bdef0d0bcf --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/GlBufferStorageModern.java @@ -0,0 +1,43 @@ +package dev.dubhe.anvilcraft.api.rendering.foundation.buffer; + +import net.minecraft.client.renderer.RenderType; +import org.lwjgl.opengl.ARBBufferStorage; +import org.lwjgl.system.MemoryUtil; + +import static org.lwjgl.opengl.GL45.*; + +public abstract class GlBufferStorageModern extends GlBufferStorage { + public static final long BUFFER_SIZE = RenderType.SMALL_BUFFER_SIZE; + public static final int FLAGS = ARBBufferStorage.GL_MAP_PERSISTENT_BIT + | ARBBufferStorage.GL_MAP_COHERENT_BIT + | GL_MAP_WRITE_BIT + | GL_DYNAMIC_STORAGE_BIT; + + private long clientPtr; + + protected GlBufferStorageModern(int target,C configureContext) { + super(target, configureContext); + bind(); + this.setupBufferState(configureContext); + ARBBufferStorage.glBufferStorage( + target, + BUFFER_SIZE, + FLAGS + ); + clientPtr = nglMapBufferRange(target, 0, BUFFER_SIZE, GL_MAP_WRITE_BIT); + } + + @Override + public void dispose() { + if (!valid)return; + bind(); + glUnmapBuffer(target); + clientPtr = 0; + super.dispose(); + } + + @Override + public void upload(long ptr, long size) { + MemoryUtil.memCopy(ptr, clientPtr, size); + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/ring/RingBuffer.java b/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/ring/RingBuffer.java new file mode 100644 index 0000000000..4f9d905fd0 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/ring/RingBuffer.java @@ -0,0 +1,65 @@ +package dev.dubhe.anvilcraft.api.rendering.foundation.buffer.ring; + +import dev.dubhe.anvilcraft.api.rendering.foundation.Disposable; +import dev.dubhe.anvilcraft.api.rendering.foundation.buffer.BufferHost; + +public class RingBuffer, C extends BufferHost> implements Disposable { + public static final int DEFAULT_SIZE = 5; + + private final int size; + private int currentUsing = 0; + private final C context; + private final RingBufferElement.Factory factory; + private final RingBufferElement[] elements; + + public RingBuffer(int size, C context, RingBufferElement.Factory factory) { + this.size = size; + this.context = context; + this.factory = factory; + elements = new RingBufferElement[size]; + } + + public RingBuffer(C context, RingBufferElement.Factory factory) { + this(DEFAULT_SIZE, context, factory); + } + + @SuppressWarnings("unchecked") + public T get() { + if (currentUsing < size - 1) { + T element = (T) elements[currentUsing++]; + if (element == null) { + elements[currentUsing] = element = factory.create(context); + } + element.waitForReady(); + element.setup(); + return element; + } + currentUsing = 0; + T element = (T) elements[0]; + if (element == null) { + elements[currentUsing] = element = factory.create(context); + } + element.waitForReady(); + element.setup(); + return element; + } + + @SuppressWarnings("unchecked") + public T current() { + T element = (T) elements[currentUsing]; + if (element == null) { + elements[currentUsing] = element = factory.create(context); + } + return element; + } + + + @Override + public void dispose() { + for (RingBufferElement element : elements) { + if (element != null) { + element.dispose(); + } + } + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/ring/RingBufferElement.java b/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/ring/RingBufferElement.java new file mode 100644 index 0000000000..8aff8ad028 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/ring/RingBufferElement.java @@ -0,0 +1,15 @@ +package dev.dubhe.anvilcraft.api.rendering.foundation.buffer.ring; + +import dev.dubhe.anvilcraft.api.rendering.foundation.Disposable; +import dev.dubhe.anvilcraft.api.rendering.foundation.buffer.BufferHost; + +public interface RingBufferElement extends Disposable { + + void waitForReady(); + + void setup(); + + public interface Factory, C extends BufferHost> { + T create(C context); + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/vertex/GlVertexBuffer.java b/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/vertex/GlVertexBuffer.java new file mode 100644 index 0000000000..45d9da6e95 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/vertex/GlVertexBuffer.java @@ -0,0 +1,76 @@ +package dev.dubhe.anvilcraft.api.rendering.foundation.buffer.vertex; + +import com.mojang.blaze3d.vertex.VertexFormat; +import dev.dubhe.anvilcraft.api.rendering.foundation.Disposable; +import dev.dubhe.anvilcraft.api.rendering.foundation.QuadSorter; +import dev.dubhe.anvilcraft.api.rendering.foundation.buffer.GlBufferStorage; +import dev.dubhe.anvilcraft.api.rendering.foundation.buffer.vertex.index.GlIndexBuffer; +import net.minecraft.client.renderer.RenderType; +import net.neoforged.fml.ModList; +import org.jetbrains.annotations.Nullable; +import org.joml.Vector3f; + +import static org.lwjgl.opengl.GL45.*; + +public class GlVertexBuffer implements Disposable { + private final int arrayObjectId; + private final GlBufferStorage vertexBuffer; + private final GlIndexBuffer indexBuffer; + private final RenderType renderType; + private final VertexFormat.IndexType indexType; + private QuadSortingState sortingState; + + public GlVertexBuffer(RenderType renderType) { + this(renderType, VertexFormat.IndexType.SHORT); + } + + public GlVertexBuffer(RenderType renderType, VertexFormat.IndexType indexType) { + if (renderType.mode != VertexFormat.Mode.QUADS) throw new UnsupportedOperationException(); + this.indexType = indexType; + this.renderType = renderType; + this.arrayObjectId = glGenVertexArrays(); + glBindVertexArray(arrayObjectId); + this.vertexBuffer = GlVertexBufferStorage.create(renderType.format); + this.indexBuffer = GlIndexBuffer.forQuad(indexType); + glBindVertexArray(0); + } + + public void upload(long ptr, int size,int vertexCount, int indexCount, Disposable uploadSrc) { + this.upload(ptr, size, vertexCount, indexCount, null, null, uploadSrc); + } + + public void upload(long ptr, int size, int vertexCount,int indexCount, @Nullable QuadSortingState sortingState, @Nullable Vector3f origin, Disposable uploadSrc) { + this.sortingState = sortingState; +// if (sortingState == null) { +// indexBuffer.fillContents(vertexCount); +// } else { +// resortVertices(origin); +// } + indexBuffer.fillContents(vertexCount, indexCount); + this.vertexBuffer.upload(ptr, size, uploadSrc); + } + + public void resortVertices(Vector3f origin) { + int[] indexes = QuadSorter.buildSortedIndexByDistance(sortingState, origin); + indexBuffer.fromSorted(indexes); + } + + public void bind() { + glBindVertexArray(arrayObjectId); + } + + public void unbind() { + glBindVertexArray(0); + } + + @Override + public void dispose() { + vertexBuffer.dispose(); + indexBuffer.dispose(); + glDeleteVertexArrays(arrayObjectId); + } + + public int getIndexType() { + return indexType.asGLType; + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/vertex/GlVertexBufferStorage.java b/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/vertex/GlVertexBufferStorage.java new file mode 100644 index 0000000000..cbe349f47e --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/vertex/GlVertexBufferStorage.java @@ -0,0 +1,48 @@ +package dev.dubhe.anvilcraft.api.rendering.foundation.buffer.vertex; + +import com.mojang.blaze3d.platform.GlConst; +import com.mojang.blaze3d.vertex.VertexFormat; +import dev.dubhe.anvilcraft.api.rendering.foundation.buffer.GlBufferStorage; +import dev.dubhe.anvilcraft.api.rendering.foundation.buffer.GlBufferStorageLegacy; +import dev.dubhe.anvilcraft.api.rendering.foundation.buffer.GlBufferStorageModern; +import lombok.Getter; + +public final class GlVertexBufferStorage { + + public static GlBufferStorage create(VertexFormat format) { + if (GlBufferStorage.BUFFER_STORAGE_SUPPORT) { + return new Modern(format); + } + return new Legacy(format); + } + + public static class Legacy extends GlBufferStorageLegacy { + @Getter + private final VertexFormat format; + + public Legacy(VertexFormat format) { + super(GlConst.GL_ARRAY_BUFFER, format); + this.format = format; + } + + @Override + public void setupBufferState(VertexFormat vertexFormat) { + vertexFormat.setupBufferState(); + } + } + + public static class Modern extends GlBufferStorageModern { + @Getter + private final VertexFormat format; + + public Modern(VertexFormat format) { + super(GlConst.GL_ARRAY_BUFFER, format); + this.format = format; + } + + @Override + public void setupBufferState(VertexFormat vertexFormat) { + vertexFormat.setupBufferState(); + } + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/vertex/QuadSortingState.java b/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/vertex/QuadSortingState.java new file mode 100644 index 0000000000..ea10530f8c --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/vertex/QuadSortingState.java @@ -0,0 +1,41 @@ +package dev.dubhe.anvilcraft.api.rendering.foundation.buffer.vertex; + +import com.mojang.blaze3d.vertex.MeshData; +import com.mojang.blaze3d.vertex.VertexFormat; +import com.mojang.blaze3d.vertex.VertexFormatElement; +import org.joml.Vector3f; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; + +public record QuadSortingState(Vector3f[] quadCenters) { + public static QuadSortingState fromMesh(MeshData meshData) { + ByteBuffer byteBuffer = meshData.vertexBuffer(); + int vertexCount = meshData.drawState().vertexCount(); + VertexFormat format = meshData.drawState().format(); + int offset = format.getOffset(VertexFormatElement.POSITION); + if (offset == -1) { + throw new IllegalArgumentException("Cannot identify quad centers with no position element"); + } else { + FloatBuffer floatbuffer = byteBuffer.asFloatBuffer(); + int j = format.getVertexSize() / 4; + int vertexSize = j * 4; + int quadCount = vertexCount / 4; + Vector3f[] avector3f = new Vector3f[quadCount]; + + for (int i = 0; i < quadCount; i++) { + int j1 = i * vertexSize + offset; + int k1 = j1 + j * 2; + float f = floatbuffer.get(j1 + 0); + float f1 = floatbuffer.get(j1 + 1); + float f2 = floatbuffer.get(j1 + 2); + float f3 = floatbuffer.get(k1 + 0); + float f4 = floatbuffer.get(k1 + 1); + float f5 = floatbuffer.get(k1 + 2); + avector3f[i] = new Vector3f((f + f3) / 2.0F, (f1 + f4) / 2.0F, (f2 + f5) / 2.0F); + } + + return new QuadSortingState(avector3f); + } + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/vertex/index/GlIndexBuffer.java b/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/vertex/index/GlIndexBuffer.java new file mode 100644 index 0000000000..7da75a1f1d --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/vertex/index/GlIndexBuffer.java @@ -0,0 +1,24 @@ +package dev.dubhe.anvilcraft.api.rendering.foundation.buffer.vertex.index; + +import com.mojang.blaze3d.vertex.VertexFormat; +import dev.dubhe.anvilcraft.api.rendering.foundation.Disposable; + +public interface GlIndexBuffer extends Disposable { + /** + * Called on worker threads + */ + void fillContents(int vertexCount, int indexCount); + + /** + * Called on worker threads= + */ + void fromSorted(int[] sortedIndex); + + void bind(); + + void unbind(); + + static GlIndexBuffer forQuad(VertexFormat.IndexType indexType){ + return new GlQuadIndexBuffer(indexType); + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/vertex/index/GlIndexBufferStorage.java b/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/vertex/index/GlIndexBufferStorage.java new file mode 100644 index 0000000000..8e2520e9e0 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/vertex/index/GlIndexBufferStorage.java @@ -0,0 +1,36 @@ +package dev.dubhe.anvilcraft.api.rendering.foundation.buffer.vertex.index; + +import dev.dubhe.anvilcraft.api.rendering.foundation.buffer.GlBufferStorage; +import dev.dubhe.anvilcraft.api.rendering.foundation.buffer.GlBufferStorageLegacy; +import dev.dubhe.anvilcraft.api.rendering.foundation.buffer.GlBufferStorageModern; +import org.lwjgl.opengl.GL45; + +public class GlIndexBufferStorage { + public static GlBufferStorage create(){ + if (GlBufferStorage.BUFFER_STORAGE_SUPPORT){ + return new Modern(); + } + return new Legacy(); + } + + public static class Legacy extends GlBufferStorageLegacy { + + public Legacy() { + super(GL45.GL_ELEMENT_ARRAY_BUFFER, null); + } + + @Override + public void setupBufferState(Void v) { + } + } + + public static class Modern extends GlBufferStorageModern { + public Modern() { + super(GL45.GL_ELEMENT_ARRAY_BUFFER, null); + } + + @Override + public void setupBufferState(Void v) { + } + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/vertex/index/GlQuadIndexBuffer.java b/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/vertex/index/GlQuadIndexBuffer.java new file mode 100644 index 0000000000..bec2aa3c5d --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/api/rendering/foundation/buffer/vertex/index/GlQuadIndexBuffer.java @@ -0,0 +1,92 @@ +package dev.dubhe.anvilcraft.api.rendering.foundation.buffer.vertex.index; + +import com.mojang.blaze3d.vertex.VertexFormat; +import dev.dubhe.anvilcraft.api.rendering.foundation.buffer.GlBufferStorage; +import org.lwjgl.system.MemoryUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class GlQuadIndexBuffer implements GlIndexBuffer { + private final Logger logger = LoggerFactory.getLogger("GlQuadIndexBuffer"); + private final MemoryUtil.MemoryAllocator alloc = MemoryUtil.getAllocator(); + private long ptr = alloc.calloc(1024, 4); + private final VertexFormat.IndexType indexType; + private final GlBufferStorage backedBuffer; + private int indexCount; + + protected GlQuadIndexBuffer(VertexFormat.IndexType indexType) { + this.indexType = indexType; + this.backedBuffer = GlIndexBufferStorage.create(); + } + + private void write(int index, int value) { + logger.info("{}: {} -> {}", this, index, value); + if (indexType == VertexFormat.IndexType.SHORT) { + MemoryUtil.memPutShort(ptr + index * 2L, (short) value); + return; + } + if (indexType == VertexFormat.IndexType.INT) { + MemoryUtil.memPutInt(ptr + index * 4L, value); + } + } + + private boolean hasEnoughStorage(int required) { + return indexCount >= required; + } + + private void ensureStorage(int required) { + alloc.free(ptr); + this.ptr = alloc.calloc(required, 4); + this.indexCount = required; + } + + + @Override + public void dispose() { + if (ptr != 0) { + alloc.free(ptr); + ptr = 0; + } + backedBuffer.dispose(); + } + + @SuppressWarnings("PointlessArithmeticExpression") + @Override + public void fillContents(int vertexCount, int indexCount) { + if (hasEnoughStorage(indexCount)) return; + ensureStorage(indexCount); + int quadCount = vertexCount / 4; + for (int i = 0, j = 0, k = 0; i < quadCount; i++, k += 4) { + write(j++, k + 0); + write(j++, k + 1); + write(j++, k + 2); + write(j++, k + 2); + write(j++, k + 3); + write(j++, k + 0); + } + backedBuffer.bind(); + backedBuffer.upload(ptr, (long) indexCount * indexType.bytes); + } + + @Override + public void fromSorted(int[] sortedIndex) { + if (!hasEnoughStorage(sortedIndex.length)) { + ensureStorage(sortedIndex.length); + } + for (int i = 0; i < sortedIndex.length; i++) { + write(i, sortedIndex[i]); + } + backedBuffer.bind(); + backedBuffer.upload(ptr, (long) indexCount * indexType.bytes); + } + + @Override + public void bind() { + backedBuffer.bind(); + } + + @Override + public void unbind() { + backedBuffer.unbind(); + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/api/rendering/CacheableBERenderingPipeline.java b/src/main/java/dev/dubhe/anvilcraft/api/rendering/pipeline/cached/CacheableBERenderingPipeline.java similarity index 95% rename from src/main/java/dev/dubhe/anvilcraft/api/rendering/CacheableBERenderingPipeline.java rename to src/main/java/dev/dubhe/anvilcraft/api/rendering/pipeline/cached/CacheableBERenderingPipeline.java index 9c93e3c165..6201733d6a 100644 --- a/src/main/java/dev/dubhe/anvilcraft/api/rendering/CacheableBERenderingPipeline.java +++ b/src/main/java/dev/dubhe/anvilcraft/api/rendering/pipeline/cached/CacheableBERenderingPipeline.java @@ -1,4 +1,4 @@ -package dev.dubhe.anvilcraft.api.rendering; +package dev.dubhe.anvilcraft.api.rendering.pipeline.cached; import lombok.Getter; import net.minecraft.client.multiplayer.ClientLevel; @@ -68,7 +68,7 @@ public void submitCompileTask(Runnable task) { } public void releaseBuffers() { - renderRegions.values().forEach(RenderRegion::releaseBuffers); + renderRegions.values().forEach(RenderRegion::dispose); valid = false; } diff --git a/src/main/java/dev/dubhe/anvilcraft/api/rendering/CacheableBlockEntityRenderer.java b/src/main/java/dev/dubhe/anvilcraft/api/rendering/pipeline/cached/CacheableBlockEntityRenderer.java similarity index 85% rename from src/main/java/dev/dubhe/anvilcraft/api/rendering/CacheableBlockEntityRenderer.java rename to src/main/java/dev/dubhe/anvilcraft/api/rendering/pipeline/cached/CacheableBlockEntityRenderer.java index 0bc1c0e225..56b63b9811 100644 --- a/src/main/java/dev/dubhe/anvilcraft/api/rendering/CacheableBlockEntityRenderer.java +++ b/src/main/java/dev/dubhe/anvilcraft/api/rendering/pipeline/cached/CacheableBlockEntityRenderer.java @@ -1,4 +1,4 @@ -package dev.dubhe.anvilcraft.api.rendering; +package dev.dubhe.anvilcraft.api.rendering.pipeline.cached; import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.renderer.MultiBufferSource; diff --git a/src/main/java/dev/dubhe/anvilcraft/api/rendering/CacheableBlockEntityRenderers.java b/src/main/java/dev/dubhe/anvilcraft/api/rendering/pipeline/cached/CacheableBlockEntityRenderers.java similarity index 94% rename from src/main/java/dev/dubhe/anvilcraft/api/rendering/CacheableBlockEntityRenderers.java rename to src/main/java/dev/dubhe/anvilcraft/api/rendering/pipeline/cached/CacheableBlockEntityRenderers.java index c0fc129ea4..c1544b8e92 100644 --- a/src/main/java/dev/dubhe/anvilcraft/api/rendering/CacheableBlockEntityRenderers.java +++ b/src/main/java/dev/dubhe/anvilcraft/api/rendering/pipeline/cached/CacheableBlockEntityRenderers.java @@ -1,4 +1,4 @@ -package dev.dubhe.anvilcraft.api.rendering; +package dev.dubhe.anvilcraft.api.rendering.pipeline.cached; import dev.dubhe.anvilcraft.client.renderer.laser.LaserRenderer; import dev.dubhe.anvilcraft.init.block.ModBlockEntities; diff --git a/src/main/java/dev/dubhe/anvilcraft/api/rendering/pipeline/cached/RegionBuffers.java b/src/main/java/dev/dubhe/anvilcraft/api/rendering/pipeline/cached/RegionBuffers.java new file mode 100644 index 0000000000..dddab8d1b8 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/api/rendering/pipeline/cached/RegionBuffers.java @@ -0,0 +1,79 @@ +package dev.dubhe.anvilcraft.api.rendering.pipeline.cached; + +import com.mojang.blaze3d.vertex.VertexFormat; +import dev.dubhe.anvilcraft.api.rendering.foundation.Disposable; +import dev.dubhe.anvilcraft.api.rendering.foundation.FullyBufferedBufferSource; +import dev.dubhe.anvilcraft.api.rendering.foundation.buffer.ring.RingBufferElement; +import dev.dubhe.anvilcraft.api.rendering.foundation.buffer.vertex.GlVertexBuffer; +import dev.dubhe.anvilcraft.api.rendering.util.SyncSupport; +import lombok.Getter; +import lombok.Setter; +import net.minecraft.MethodsReturnNonnullByDefault; +import net.minecraft.client.renderer.RenderType; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +public class RegionBuffers implements RingBufferElement, FullyBufferedBufferSource.CompileContext { + private final SyncSupport syncSupport = new SyncSupport(); + private final Map vertexBuffers = new ConcurrentHashMap<>(); + private final RenderRegion renderRegion; + + public RegionBuffers(RenderRegion renderRegion) { + this.renderRegion = renderRegion; + } + + @Override + public void waitForReady() { + if (!syncSupport.isSyncSet()){ + return; + } + if (!syncSupport.isSyncSignaled()){ + syncSupport.waitSync(); + } + syncSupport.deleteSync(); + syncSupport.reset(); + } + + @Override + public void setup() { + syncSupport.setSync(); + } + + @Override + public void dispose() { + for (GlVertexBuffer value : vertexBuffers.values()) { + value.dispose(); + } + } + + @Override + public GlVertexBuffer getOrCreateBuffer(RenderType renderType, VertexFormat.IndexType indexType) { + GlVertexBuffer buffer = vertexBuffers.get(renderType); + if (buffer != null) { + return buffer; + } + buffer = new GlVertexBuffer(renderType, indexType); + vertexBuffers.put(renderType, buffer); + return buffer; + } + + @Override + public void submitUploadTask(Runnable runnable) { + renderRegion.getPipeline().submitUploadTask(runnable); + } + + @Nullable + public GlVertexBuffer getBuffer(RenderType renderType) { + return vertexBuffers.get(renderType); + } + + public Collection allRenderPasses() { + return vertexBuffers.keySet(); + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/api/rendering/RenderRegion.java b/src/main/java/dev/dubhe/anvilcraft/api/rendering/pipeline/cached/RenderRegion.java similarity index 76% rename from src/main/java/dev/dubhe/anvilcraft/api/rendering/RenderRegion.java rename to src/main/java/dev/dubhe/anvilcraft/api/rendering/pipeline/cached/RenderRegion.java index 54cc20779a..b9a1c9176c 100644 --- a/src/main/java/dev/dubhe/anvilcraft/api/rendering/RenderRegion.java +++ b/src/main/java/dev/dubhe/anvilcraft/api/rendering/pipeline/cached/RenderRegion.java @@ -1,15 +1,20 @@ -package dev.dubhe.anvilcraft.api.rendering; +package dev.dubhe.anvilcraft.api.rendering.pipeline.cached; import com.mojang.blaze3d.platform.Window; import com.mojang.blaze3d.shaders.Uniform; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.blaze3d.vertex.VertexBuffer; import com.mojang.blaze3d.vertex.VertexFormat; +import dev.dubhe.anvilcraft.api.rendering.foundation.Disposable; +import dev.dubhe.anvilcraft.api.rendering.foundation.FullyBufferedBufferSource; +import dev.dubhe.anvilcraft.api.rendering.foundation.buffer.BufferHost; +import dev.dubhe.anvilcraft.api.rendering.foundation.buffer.ring.RingBuffer; +import dev.dubhe.anvilcraft.api.rendering.foundation.buffer.vertex.GlVertexBuffer; import dev.dubhe.anvilcraft.client.init.ModRenderTypes; import dev.dubhe.anvilcraft.client.renderer.RenderState; import it.unimi.dsi.fastutil.objects.Reference2IntMap; import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; +import lombok.Getter; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.ShaderInstance; @@ -22,20 +27,19 @@ import org.lwjgl.opengl.GL15; import java.util.Collection; -import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Set; -public class RenderRegion { +public class RenderRegion implements BufferHost, Disposable { public static final List BLOOM_RENDERTYPES = List.of( ModRenderTypes.LASER ); private final ChunkPos chunkPos; - private final Map buffers = new HashMap<>(); + private final RingBuffer buffers = new RingBuffer<>(this, RegionBuffers::new); private Reference2IntMap indexCountMap = new Reference2IntOpenHashMap<>(); private final Set blockEntityList = new HashSet<>(); + @Getter private final CacheableBERenderingPipeline pipeline; private final Minecraft minecraft = Minecraft.getInstance(); private RebuildTask lastRebuildTask; @@ -54,11 +58,11 @@ public void update(BlockEntity be) { blockEntityList.removeIf(BlockEntity::isRemoved); if (be.isRemoved()) { blockEntityList.remove(be); - pipeline.submitCompileTask(new RebuildTask()); + pipeline.submitCompileTask(new RebuildTask(buffers.get())); return; } blockEntityList.add(be); - pipeline.submitCompileTask(new RebuildTask()); + pipeline.submitCompileTask(new RebuildTask(buffers.get())); } public void blockRemoved(BlockEntity be) { @@ -67,7 +71,7 @@ public void blockRemoved(BlockEntity be) { } blockEntityList.remove(be); blockEntityList.removeIf(BlockEntity::isRemoved); - pipeline.submitCompileTask(new RebuildTask()); + pipeline.submitCompileTask(new RebuildTask(buffers.get())); } public void renderBloomed(Matrix4f frustumMatrix, Matrix4f projectionMatrix) { @@ -75,16 +79,7 @@ public void renderBloomed(Matrix4f frustumMatrix, Matrix4f projectionMatrix) { } public void render(Matrix4f frustumMatrix, Matrix4f projectionMatrix) { - renderInternal(frustumMatrix, projectionMatrix, buffers.keySet(), RenderState::levelStage); - } - - public VertexBuffer getBuffer(RenderType renderType) { - if (buffers.containsKey(renderType)) { - return buffers.get(renderType); - } - VertexBuffer vb = new VertexBuffer(VertexBuffer.Usage.STATIC); - buffers.put(renderType, vb); - return vb; + renderInternal(frustumMatrix, projectionMatrix, buffers.current().allRenderPasses(), RenderState::levelStage); } private void renderInternal( @@ -102,20 +97,21 @@ private void renderInternal( return; } for (RenderType renderType : renderTypes) { - VertexBuffer vb = buffers.get(renderType); + GlVertexBuffer vb = this.getPresentBuffer(renderType); if (vb == null) continue; stateSwitcher.run(); renderLayer(renderType, vb, frustumMatrix, projectionMatrix, cameraPosition, window); } } - public void releaseBuffers() { - buffers.values().forEach(VertexBuffer::close); + protected GlVertexBuffer getPresentBuffer(RenderType renderType) { + RegionBuffers current = buffers.current(); + return current.getBuffer(renderType); } private void renderLayer( RenderType renderType, - VertexBuffer vertexBuffer, + GlVertexBuffer vertexBuffer, Matrix4f frustumMatrix, Matrix4f projectionMatrix, Vec3 cameraPosition, @@ -137,16 +133,29 @@ private void renderLayer( uniform.upload(); } vertexBuffer.bind(); - GL11.glDrawElements(GL15.GL_TRIANGLES, indexCount, vertexBuffer.sequentialIndices.type().asGLType, 0L); - VertexBuffer.unbind(); + if (renderType.sortOnUpload) { + //vertexBuffer.resortVertices(cameraPosition.toVector3f()); + } + GL11.glDrawElements(GL15.GL_TRIANGLES, indexCount, vertexBuffer.getIndexType(), 0L); + vertexBuffer.unbind(); if (uniform != null) { uniform.set(0.0F, 0.0F, 0.0F); } renderType.clearRenderState(); } + @Override + public void dispose() { + buffers.dispose(); + } + private class RebuildTask implements Runnable { private boolean cancelled = false; + private final RegionBuffers buffers; + + private RebuildTask(RegionBuffers buffers) { + this.buffers = buffers; + } @Override public void run() { @@ -156,7 +165,7 @@ public void run() { FullyBufferedBufferSource bufferSource = new FullyBufferedBufferSource(); for (BlockEntity be : blockEntityList) { if (cancelled) { - bufferSource.close(); + bufferSource.dispose(); return; } CacheableBlockEntityRenderer renderer = CacheableBlockEntityRenderers.get(be.getType()); @@ -176,11 +185,8 @@ public void run() { poseStack.popPose(); } RenderRegion.this.isEmpty = bufferSource.isEmpty(); - bufferSource.upload( - RenderRegion.this::getBuffer, - pipeline::submitUploadTask - ); - RenderRegion.this.indexCountMap = bufferSource.indexCountMap; + bufferSource.upload(buffers); + RenderRegion.this.indexCountMap = bufferSource.getIndexCountMap(); lastRebuildTask = null; } diff --git a/src/main/java/dev/dubhe/anvilcraft/api/rendering/util/SyncSupport.java b/src/main/java/dev/dubhe/anvilcraft/api/rendering/util/SyncSupport.java new file mode 100644 index 0000000000..d77e9fd5a7 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/api/rendering/util/SyncSupport.java @@ -0,0 +1,32 @@ +package dev.dubhe.anvilcraft.api.rendering.util; + +import static org.lwjgl.opengl.GL45.*; + +public class SyncSupport { + private long syncObject = -1; + + public boolean isSyncSet() { + return syncObject != -1; + } + + public boolean isSyncSignaled() { + return glGetSynci(syncObject, GL_SYNC_STATUS, null) == GL_SIGNALED; + } + + public void waitSync() { + glClientWaitSync(syncObject, GL_SYNC_FLUSH_COMMANDS_BIT, Long.MAX_VALUE); + } + + public void setSync() { + syncObject = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + } + + public void deleteSync() { + glDeleteSync(syncObject); + } + + public void reset() { + this.syncObject = -1; + } + +} diff --git a/src/main/java/dev/dubhe/anvilcraft/block/entity/BaseLaserBlockEntity.java b/src/main/java/dev/dubhe/anvilcraft/block/entity/BaseLaserBlockEntity.java index cd26854c2f..13bda150ec 100644 --- a/src/main/java/dev/dubhe/anvilcraft/block/entity/BaseLaserBlockEntity.java +++ b/src/main/java/dev/dubhe/anvilcraft/block/entity/BaseLaserBlockEntity.java @@ -2,7 +2,7 @@ import dev.dubhe.anvilcraft.AnvilCraft; import dev.dubhe.anvilcraft.api.heat.HeaterManager; -import dev.dubhe.anvilcraft.api.rendering.CacheableBERenderingPipeline; +import dev.dubhe.anvilcraft.api.rendering.pipeline.cached.CacheableBERenderingPipeline; import dev.dubhe.anvilcraft.init.block.ModBlockTags; import dev.dubhe.anvilcraft.init.entity.ModDamageTypes; import dev.dubhe.anvilcraft.init.ModHeaterInfos; @@ -50,6 +50,7 @@ public abstract class BaseLaserBlockEntity extends BlockEntity { protected HashSet irradiateSelfLaserBlockSet = new HashSet<>(); protected boolean changed = false; + @Nullable @Getter protected BlockPos irradiateBlockPos = null; @Getter diff --git a/src/main/java/dev/dubhe/anvilcraft/client/renderer/laser/LaserRenderer.java b/src/main/java/dev/dubhe/anvilcraft/client/renderer/laser/LaserRenderer.java index ad0b40b719..a1b5f4c765 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/renderer/laser/LaserRenderer.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/renderer/laser/LaserRenderer.java @@ -1,7 +1,7 @@ package dev.dubhe.anvilcraft.client.renderer.laser; import com.mojang.blaze3d.vertex.PoseStack; -import dev.dubhe.anvilcraft.api.rendering.CacheableBlockEntityRenderer; +import dev.dubhe.anvilcraft.api.rendering.pipeline.cached.CacheableBlockEntityRenderer; import dev.dubhe.anvilcraft.block.entity.BaseLaserBlockEntity; import net.minecraft.client.renderer.MultiBufferSource; diff --git a/src/main/java/dev/dubhe/anvilcraft/client/renderer/laser/LaserState.java b/src/main/java/dev/dubhe/anvilcraft/client/renderer/laser/LaserState.java index 35827209e9..4f2e94aee6 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/renderer/laser/LaserState.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/renderer/laser/LaserState.java @@ -8,6 +8,7 @@ import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.core.BlockPos; import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.Nullable; import java.util.function.Function; @@ -21,6 +22,7 @@ public record LaserState( TextureAtlasSprite laserAtlasSprite, TextureAtlasSprite concreteAtlasSprite ) { + @Nullable public static LaserState create(BaseLaserBlockEntity blockEntity, PoseStack poseStack) { if (blockEntity.getIrradiateBlockPos() == null) return null; Function spriteGetter = Minecraft.getInstance() diff --git a/src/main/java/dev/dubhe/anvilcraft/client/support/PowerGridSupport.java b/src/main/java/dev/dubhe/anvilcraft/client/support/PowerGridSupport.java index 7d6500954d..50e15c147d 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/support/PowerGridSupport.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/support/PowerGridSupport.java @@ -6,7 +6,7 @@ import dev.dubhe.anvilcraft.client.AnvilCraftClient; import dev.dubhe.anvilcraft.client.init.ModRenderTargets; import dev.dubhe.anvilcraft.client.init.ModRenderTypes; -import dev.dubhe.anvilcraft.client.renderer.Line; +import dev.dubhe.anvilcraft.util.Line; import dev.dubhe.anvilcraft.client.renderer.RenderState; import dev.dubhe.anvilcraft.constant.Constant; import net.minecraft.client.Minecraft; diff --git a/src/main/java/dev/dubhe/anvilcraft/client/support/RenderThunderSupport.java b/src/main/java/dev/dubhe/anvilcraft/client/support/RenderThunderSupport.java index e1e1d02155..a0f44e272c 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/support/RenderThunderSupport.java +++ b/src/main/java/dev/dubhe/anvilcraft/client/support/RenderThunderSupport.java @@ -1,6 +1,6 @@ package dev.dubhe.anvilcraft.client.support; -import dev.dubhe.anvilcraft.client.renderer.Line; +import dev.dubhe.anvilcraft.util.Line; import net.minecraft.world.phys.Vec3; import java.util.HashSet; diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/GlDebugMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/GlDebugMixin.java new file mode 100644 index 0000000000..16bc0370c3 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/mixin/GlDebugMixin.java @@ -0,0 +1,23 @@ +package dev.dubhe.anvilcraft.mixin; + +import com.mojang.blaze3d.platform.GlDebug; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(GlDebug.class) +public class GlDebugMixin { + @Inject( + method = "printDebugLog", + at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;info(Ljava/lang/String;Ljava/lang/Object;)V", shift = At.Shift.BEFORE), + cancellable = true + ) + private static void avoidSpam(int source, int type, int id, int severity, int messageLength, long message, long userParam, CallbackInfo ci) { + if (severity != 37190) { + ci.cancel(); + return; + } + new Throwable().printStackTrace(); + } +} diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/LevelChunkMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/LevelChunkMixin.java index f17d02bbbd..b3fb4eaece 100644 --- a/src/main/java/dev/dubhe/anvilcraft/mixin/LevelChunkMixin.java +++ b/src/main/java/dev/dubhe/anvilcraft/mixin/LevelChunkMixin.java @@ -1,6 +1,7 @@ package dev.dubhe.anvilcraft.mixin; -import dev.dubhe.anvilcraft.api.rendering.CacheableBERenderingPipeline; +import com.mojang.blaze3d.systems.RenderSystem; +import dev.dubhe.anvilcraft.api.rendering.pipeline.cached.CacheableBERenderingPipeline; import dev.dubhe.anvilcraft.block.entity.BaseLaserBlockEntity; import net.minecraft.core.BlockPos; import net.minecraft.world.level.block.entity.BlockEntity; @@ -23,7 +24,9 @@ public abstract class LevelChunkMixin { void onBlockEntityRemoved(BlockPos pos, CallbackInfo ci) { BlockEntity be = getBlockEntity(pos); if (be instanceof BaseLaserBlockEntity laserStateAccess) { - CacheableBERenderingPipeline.getInstance().blockRemoved(laserStateAccess); + if (RenderSystem.isOnRenderThread()) { + CacheableBERenderingPipeline.getInstance().blockRemoved(laserStateAccess); + } } } } diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java index 0883c858b0..86cf91d4aa 100644 --- a/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java +++ b/src/main/java/dev/dubhe/anvilcraft/mixin/LevelRendererMixin.java @@ -11,7 +11,7 @@ import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.Tesselator; import com.mojang.blaze3d.vertex.VertexFormat; -import dev.dubhe.anvilcraft.api.rendering.CacheableBERenderingPipeline; +import dev.dubhe.anvilcraft.api.rendering.pipeline.cached.CacheableBERenderingPipeline; import dev.dubhe.anvilcraft.client.init.ModRenderTargets; import dev.dubhe.anvilcraft.client.init.ModShaders; import dev.dubhe.anvilcraft.client.renderer.RenderState; diff --git a/src/main/java/dev/dubhe/anvilcraft/mixin/MinecraftClientMixin.java b/src/main/java/dev/dubhe/anvilcraft/mixin/MinecraftClientMixin.java index 38dfb7606a..b049a75975 100644 --- a/src/main/java/dev/dubhe/anvilcraft/mixin/MinecraftClientMixin.java +++ b/src/main/java/dev/dubhe/anvilcraft/mixin/MinecraftClientMixin.java @@ -1,6 +1,6 @@ package dev.dubhe.anvilcraft.mixin; -import dev.dubhe.anvilcraft.api.rendering.CacheableBERenderingPipeline; +import dev.dubhe.anvilcraft.api.rendering.pipeline.cached.CacheableBERenderingPipeline; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; import org.spongepowered.asm.mixin.Mixin; diff --git a/src/main/java/dev/dubhe/anvilcraft/client/renderer/Line.java b/src/main/java/dev/dubhe/anvilcraft/util/Line.java similarity index 97% rename from src/main/java/dev/dubhe/anvilcraft/client/renderer/Line.java rename to src/main/java/dev/dubhe/anvilcraft/util/Line.java index a3de49df27..b9375c36c8 100644 --- a/src/main/java/dev/dubhe/anvilcraft/client/renderer/Line.java +++ b/src/main/java/dev/dubhe/anvilcraft/util/Line.java @@ -1,4 +1,4 @@ -package dev.dubhe.anvilcraft.client.renderer; +package dev.dubhe.anvilcraft.util; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; diff --git a/src/main/java/dev/dubhe/anvilcraft/util/algo/MinimumSpanningTree3D.java b/src/main/java/dev/dubhe/anvilcraft/util/algo/MinimumSpanningTree3D.java new file mode 100644 index 0000000000..6873ed4262 --- /dev/null +++ b/src/main/java/dev/dubhe/anvilcraft/util/algo/MinimumSpanningTree3D.java @@ -0,0 +1,68 @@ +package dev.dubhe.anvilcraft.util.algo; + +import dev.dubhe.anvilcraft.util.Line; +import net.minecraft.world.phys.Vec3; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class MinimumSpanningTree3D { + + private static class Edge implements Comparable { + int u, v; + double weight; + Edge(int u, int v, double weight) { this.u = u; this.v = v; this.weight = weight; } + @Override + public int compareTo(Edge o) { return Double.compare(this.weight, o.weight); } + } + + private static class UnionFind { + int[] parent, rank; + UnionFind(int n) { + parent = new int[n]; + rank = new int[n]; + for (int i = 0; i < n; i++) parent[i] = i; + } + int find(int x) { + if (parent[x] != x) parent[x] = find(parent[x]); + return parent[x]; + } + void union(int x, int y) { + int xr = find(x), yr = find(y); + if (xr == yr) return; + if (rank[xr] < rank[yr]) parent[xr] = yr; + else { + parent[yr] = xr; + if (rank[xr] == rank[yr]) rank[xr]++; + } + } + } + + public static List kruskalMST(List points) { + int n = points.size(); + List edges = new ArrayList<>(); + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + edges.add(new Edge(i, j, distance(points.get(i), points.get(j)))); + } + } + Collections.sort(edges); + UnionFind uf = new UnionFind(n); + List result = new ArrayList<>(); + for (Edge e : edges) { + int pu = uf.find(e.u), pv = uf.find(e.v); + if (pu != pv) { + uf.union(pu, pv); + result.add(new Line(points.get(e.u), points.get(e.v), (float) e.weight)); + if (result.size() == n - 1) break; + } + } + return result; + } + + private static double distance(Vec3 a, Vec3 b) { + double dx = a.x - b.x, dy = a.y - b.y, dz = a.z - b.z; + return Math.sqrt(dx * dx + dy * dy + dz * dz); + } +} \ No newline at end of file diff --git a/src/main/resources/anvilcraft.mixins.json b/src/main/resources/anvilcraft.mixins.json index aa3f7a9aa8..2daf4172b5 100644 --- a/src/main/resources/anvilcraft.mixins.json +++ b/src/main/resources/anvilcraft.mixins.json @@ -81,6 +81,7 @@ "ClientLevelMixin", "ClientPacketListenerMixin", "GameRendererMixin", + "GlDebugMixin", "GuiGraphicsMixin", "ItemFrameRendererMixin", "ItemInHandRendererMixin",