diff --git a/src/main/java/com/jozufozu/flywheel/Flywheel.java b/src/main/java/com/jozufozu/flywheel/Flywheel.java
index 1b1638a59..145d5f661 100644
--- a/src/main/java/com/jozufozu/flywheel/Flywheel.java
+++ b/src/main/java/com/jozufozu/flywheel/Flywheel.java
@@ -23,7 +23,6 @@ import com.jozufozu.flywheel.lib.material.MaterialIndices;
import com.jozufozu.flywheel.lib.material.Materials;
import com.jozufozu.flywheel.lib.model.Models;
import com.jozufozu.flywheel.lib.model.PartialModel;
-import com.jozufozu.flywheel.lib.util.QuadConverter;
import com.jozufozu.flywheel.lib.util.ShadersModHandler;
import com.jozufozu.flywheel.lib.vertex.VertexTypes;
import com.jozufozu.flywheel.mixin.PausedPartialTickAccessor;
@@ -35,7 +34,6 @@ import net.minecraft.commands.synchronization.EmptyArgumentSerializer;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.common.MinecraftForge;
-import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.fml.IExtensionPoint;
@@ -79,7 +77,6 @@ public class Flywheel {
forgeEventBus.addListener(BackendManagerImpl::onReloadRenderers);
- forgeEventBus.addListener(EventPriority.HIGHEST, QuadConverter::onReloadRenderers);
forgeEventBus.addListener(Models::onReloadRenderers);
forgeEventBus.addListener(DrawBuffer::onReloadRenderers);
forgeEventBus.addListener(UniformBuffer::onReloadRenderers);
diff --git a/src/main/java/com/jozufozu/flywheel/api/model/IndexSequence.java b/src/main/java/com/jozufozu/flywheel/api/model/IndexSequence.java
new file mode 100644
index 000000000..6844417bf
--- /dev/null
+++ b/src/main/java/com/jozufozu/flywheel/api/model/IndexSequence.java
@@ -0,0 +1,13 @@
+package com.jozufozu.flywheel.api.model;
+
+/**
+ * Represents a sequence of unsigned integer vertex indices.
+ */
+public interface IndexSequence {
+ /**
+ * Populate the given memory region with indices.
+ *
+ * Do not write outside the range {@code [ptr, ptr + count * 4]}.
+ */
+ void fill(long ptr, int count);
+}
diff --git a/src/main/java/com/jozufozu/flywheel/api/model/Mesh.java b/src/main/java/com/jozufozu/flywheel/api/model/Mesh.java
index ef8494a89..16fd1486a 100644
--- a/src/main/java/com/jozufozu/flywheel/api/model/Mesh.java
+++ b/src/main/java/com/jozufozu/flywheel/api/model/Mesh.java
@@ -4,56 +4,64 @@ import org.joml.Vector4fc;
import com.jozufozu.flywheel.api.vertex.MutableVertexList;
import com.jozufozu.flywheel.api.vertex.VertexType;
-import com.jozufozu.flywheel.gl.buffer.ElementBuffer;
/**
* A holder for arbitrary vertex data that can be written to memory or a vertex list.
*/
public interface Mesh {
- VertexType getVertexType();
+ VertexType vertexType();
/**
* @return The number of vertices this mesh has.
*/
- int getVertexCount();
+ int vertexCount();
/**
* Is there nothing to render?
* @return true if there are no vertices.
*/
default boolean isEmpty() {
- return getVertexCount() == 0;
+ return vertexCount() == 0;
}
/**
* The size in bytes that this mesh's data takes up.
*/
default int size() {
- return getVertexType().getLayout().getStride() * getVertexCount();
+ return vertexType().getLayout()
+ .getStride() * vertexCount();
}
/**
- * Write this mesh into memory. The written data will use the format defined by {@link #getVertexType()} and the amount of
+ * Write this mesh into memory. The written data will use the format defined by {@link #vertexType()} and the amount of
* bytes written will be the same as the return value of {@link #size()}.
+ *
* @param ptr The address to which data is written to.
*/
void write(long ptr);
/**
- * Write this mesh into a vertex list. Vertices with index {@literal <}0 or {@literal >=}{@link #getVertexCount()} will not be
+ * Write this mesh into a vertex list. Vertices with index {@literal <}0 or {@literal >=}{@link #vertexCount()} will not be
* read or modified.
+ *
* @param vertexList The vertex list to which data is written to.
*/
void write(MutableVertexList vertexList);
+ IndexSequence indexSequence();
+
+ int indexCount();
+
/**
- * Create an element buffer object that indexes the vertices of this mesh.
- * @return an element buffer object indexing this model's vertices.
+ * Get a vec4 representing this mesh's bounding sphere in the format (x, y, z, radius).
+ *
+ * @return A vec4 view.
*/
- ElementBuffer createEBO();
-
- Vector4fc getBoundingSphere();
+ Vector4fc boundingSphere();
+ /**
+ * Free this mesh's resources, memory, etc.
+ */
void delete();
/**
diff --git a/src/main/java/com/jozufozu/flywheel/api/model/Model.java b/src/main/java/com/jozufozu/flywheel/api/model/Model.java
index 6998040c2..d3d4234e6 100644
--- a/src/main/java/com/jozufozu/flywheel/api/model/Model.java
+++ b/src/main/java/com/jozufozu/flywheel/api/model/Model.java
@@ -12,7 +12,7 @@ public interface Model {
default int getVertexCount() {
int size = 0;
for (Mesh mesh : getMeshes().values()) {
- size += mesh.getVertexCount();
+ size += mesh.vertexCount();
}
return size;
}
diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/batching/BatchedMeshPool.java b/src/main/java/com/jozufozu/flywheel/backend/engine/batching/BatchedMeshPool.java
index 6987c2a78..8bc6dd946 100644
--- a/src/main/java/com/jozufozu/flywheel/backend/engine/batching/BatchedMeshPool.java
+++ b/src/main/java/com/jozufozu/flywheel/backend/engine/batching/BatchedMeshPool.java
@@ -160,8 +160,8 @@ public class BatchedMeshPool {
private BufferedMesh(Mesh mesh, long byteIndex) {
this.mesh = mesh;
- vertexCount = mesh.getVertexCount();
- boundingSphere = mesh.getBoundingSphere();
+ vertexCount = mesh.vertexCount();
+ boundingSphere = mesh.boundingSphere();
byteSize = vertexCount * vertexFormat.getVertexSize();
this.byteIndex = byteIndex;
}
@@ -174,7 +174,7 @@ public class BatchedMeshPool {
return vertexCount;
}
- public Vector4fc getBoundingSphere() {
+ public Vector4fc boundingSphere() {
return boundingSphere;
}
diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/batching/TransformCall.java b/src/main/java/com/jozufozu/flywheel/backend/engine/batching/TransformCall.java
index c0bcb6d7b..12358edfc 100644
--- a/src/main/java/com/jozufozu/flywheel/backend/engine/batching/TransformCall.java
+++ b/src/main/java/com/jozufozu/flywheel/backend/engine/batching/TransformCall.java
@@ -35,7 +35,7 @@ public class TransformCall {
MaterialVertexTransformer materialVertexTransformer = material.getVertexTransformer();
meshVertexCount = mesh.getVertexCount();
- Vector4fc meshBoundingSphere = mesh.getBoundingSphere();
+ Vector4fc meshBoundingSphere = mesh.boundingSphere();
drawPlan = ForEachPlan.of(instancer::getAll, (instance, ctx) -> {
var boundingSphere = new Vector4f(meshBoundingSphere);
diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectCullingGroup.java b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectCullingGroup.java
index 31fe39c52..8ad9fbec7 100644
--- a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectCullingGroup.java
+++ b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectCullingGroup.java
@@ -8,40 +8,28 @@ import static org.lwjgl.opengl.GL43.glDispatchCompute;
import com.jozufozu.flywheel.api.event.RenderStage;
import com.jozufozu.flywheel.api.instance.Instance;
import com.jozufozu.flywheel.api.instance.InstanceType;
-import com.jozufozu.flywheel.api.layout.BufferLayout;
+import com.jozufozu.flywheel.api.material.Material;
+import com.jozufozu.flywheel.api.model.Mesh;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.compile.IndirectPrograms;
import com.jozufozu.flywheel.backend.engine.UniformBuffer;
-import com.jozufozu.flywheel.gl.array.GlVertexArray;
import com.jozufozu.flywheel.gl.shader.GlProgram;
import com.jozufozu.flywheel.lib.context.Contexts;
-import com.jozufozu.flywheel.lib.util.QuadConverter;
public class IndirectCullingGroup {
-
private static final int BARRIER_BITS = GL_SHADER_STORAGE_BARRIER_BIT | GL_COMMAND_BARRIER_BIT;
- final GlProgram compute;
- final GlProgram draw;
- private final VertexType vertexType;
+ private final GlProgram compute;
+ private final GlProgram draw;
private final long instanceStride;
-
- final IndirectBuffers buffers;
-
- final IndirectMeshPool meshPool;
- private final int elementBuffer;
-
- GlVertexArray vertexArray;
-
- final IndirectDrawSet drawSet = new IndirectDrawSet<>();
-
+ private final IndirectBuffers buffers;
+ public final IndirectMeshPool meshPool;
+ public final IndirectDrawSet drawSet = new IndirectDrawSet<>();
private boolean hasCulledThisFrame;
private boolean needsMemoryBarrier;
private int instanceCountThisFrame;
IndirectCullingGroup(InstanceType instanceType, VertexType vertexType) {
- this.vertexType = vertexType;
-
instanceStride = instanceType.getLayout()
.getStride();
buffers = new IndirectBuffers(instanceStride);
@@ -49,33 +37,24 @@ public class IndirectCullingGroup {
buffers.createObjectStorage(128);
buffers.createDrawStorage(2);
- meshPool = new IndirectMeshPool(vertexType, 1024);
-
- vertexArray = GlVertexArray.create();
-
- elementBuffer = QuadConverter.getInstance()
- .quads2Tris(2048).glBuffer;
- setupVertexArray();
+ meshPool = new IndirectMeshPool(vertexType);
var indirectPrograms = IndirectPrograms.get();
compute = indirectPrograms.getCullingProgram(instanceType);
draw = indirectPrograms.getIndirectProgram(vertexType, instanceType, Contexts.WORLD);
}
- private void setupVertexArray() {
- vertexArray.setElementBuffer(elementBuffer);
- BufferLayout type = vertexType.getLayout();
- vertexArray.bindVertexBuffer(0, meshPool.vbo, 0, type.getStride());
- vertexArray.bindAttributes(0, 0, type.attributes());
+ public void add(IndirectInstancer instancer, RenderStage stage, Material material, Mesh mesh) {
+ drawSet.add(instancer, material, stage, meshPool.alloc(mesh));
}
- void beginFrame() {
+ public void beginFrame() {
hasCulledThisFrame = false;
needsMemoryBarrier = true;
instanceCountThisFrame = calculateTotalInstanceCountAndPrepareBatches();
}
- void submit(RenderStage stage) {
+ public void submit(RenderStage stage) {
if (drawSet.isEmpty()) {
return;
}
@@ -112,7 +91,7 @@ public class IndirectCullingGroup {
}
UniformBuffer.syncAndBind(draw);
- vertexArray.bindForDraw();
+ meshPool.bindForDraw();
buffers.bindForDraw();
memoryBarrier();
@@ -164,7 +143,6 @@ public class IndirectCullingGroup {
}
public void delete() {
- vertexArray.delete();
buffers.delete();
meshPool.delete();
}
diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectDraw.java b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectDraw.java
index 798c28411..245fb79a4 100644
--- a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectDraw.java
+++ b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectDraw.java
@@ -64,12 +64,12 @@ public class IndirectDraw {
}
public void writeIndirectCommand(long ptr) {
- var boundingSphere = mesh.getMesh().getBoundingSphere();
+ var boundingSphere = mesh.boundingSphere();
- MemoryUtil.memPutInt(ptr, mesh.getIndexCount()); // count
+ MemoryUtil.memPutInt(ptr, mesh.indexCount()); // count
MemoryUtil.memPutInt(ptr + 4, 0); // instanceCount - to be incremented by the compute shader
- MemoryUtil.memPutInt(ptr + 8, 0); // firstIndex - all models share the same index buffer
- MemoryUtil.memPutInt(ptr + 12, mesh.getBaseVertex()); // baseVertex
+ MemoryUtil.memPutInt(ptr + 8, mesh.firstIndex); // firstIndex
+ MemoryUtil.memPutInt(ptr + 12, mesh.baseVertex); // baseVertex
MemoryUtil.memPutInt(ptr + 16, baseInstance); // baseInstance
boundingSphere.getToAddress(ptr + 20); // boundingSphere
diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectDrawManager.java b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectDrawManager.java
index 832cd14e2..0235357d9 100644
--- a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectDrawManager.java
+++ b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectDrawManager.java
@@ -64,9 +64,9 @@ public class IndirectDrawManager {
var material = entry.getKey();
var mesh = entry.getValue();
- var indirectList = (IndirectCullingGroup) renderLists.computeIfAbsent(Pair.of(instancer.type, mesh.getVertexType()), p -> new IndirectCullingGroup<>(p.first(), p.second()));
+ var indirectList = (IndirectCullingGroup) renderLists.computeIfAbsent(Pair.of(instancer.type, mesh.vertexType()), p -> new IndirectCullingGroup<>(p.first(), p.second()));
- indirectList.drawSet.add(instancer, material, stage, indirectList.meshPool.alloc(mesh));
+ indirectList.add(instancer, stage, material, mesh);
break; // TODO: support multiple meshes per model
}
diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectMeshPool.java b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectMeshPool.java
index c966ab56b..2472d0c96 100644
--- a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectMeshPool.java
+++ b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectMeshPool.java
@@ -1,21 +1,21 @@
package com.jozufozu.flywheel.backend.engine.indirect;
-import static org.lwjgl.opengl.GL15.glDeleteBuffers;
-import static org.lwjgl.opengl.GL44.GL_DYNAMIC_STORAGE_BIT;
-import static org.lwjgl.opengl.GL45.glCreateBuffers;
-import static org.lwjgl.opengl.GL45.glNamedBufferStorage;
-import static org.lwjgl.opengl.GL45.nglNamedBufferSubData;
-
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jetbrains.annotations.Nullable;
+import org.joml.Vector4fc;
+import com.jozufozu.flywheel.api.layout.BufferLayout;
import com.jozufozu.flywheel.api.model.Mesh;
import com.jozufozu.flywheel.api.vertex.VertexType;
+import com.jozufozu.flywheel.gl.GlNumericType;
+import com.jozufozu.flywheel.gl.array.GlVertexArray;
+import com.jozufozu.flywheel.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.lib.memory.MemoryBlock;
+import com.jozufozu.flywheel.lib.model.QuadIndexSequence;
public class IndirectMeshPool {
private final VertexType vertexType;
@@ -23,20 +23,25 @@ public class IndirectMeshPool {
private final Map meshes = new HashMap<>();
private final List meshList = new ArrayList<>();
- final int vbo;
- private final MemoryBlock clientStorage;
+ final GlVertexArray vertexArray;
+ final GlBuffer vbo;
+ final GlBuffer ebo;
private boolean dirty;
/**
* Create a new mesh pool.
*/
- public IndirectMeshPool(VertexType type, int vertexCapacity) {
+ public IndirectMeshPool(VertexType type) {
vertexType = type;
- vbo = glCreateBuffers();
- var byteCapacity = type.getLayout().getStride() * vertexCapacity;
- glNamedBufferStorage(vbo, byteCapacity, GL_DYNAMIC_STORAGE_BIT);
- clientStorage = MemoryBlock.malloc(byteCapacity);
+ vbo = new GlBuffer();
+ ebo = new GlBuffer();
+ vertexArray = GlVertexArray.create();
+
+ vertexArray.setElementBuffer(ebo.handle());
+ BufferLayout layout = vertexType.getLayout();
+ vertexArray.bindVertexBuffer(0, vbo.handle(), 0, layout.getStride());
+ vertexArray.bindAttributes(0, 0, layout.attributes());
}
public VertexType getVertexType() {
@@ -72,64 +77,98 @@ public class IndirectMeshPool {
}
private void uploadAll() {
- final long ptr = clientStorage.ptr();
+ long neededSize = 0;
+ int maxQuadIndexCount = 0;
+ int nonQuadIndexCount = 0;
+ for (BufferedMesh mesh : meshList) {
+ neededSize += mesh.size();
+
+ if (mesh.mesh.indexSequence() == QuadIndexSequence.INSTANCE) {
+ maxQuadIndexCount = Math.max(maxQuadIndexCount, mesh.mesh.indexCount());
+ } else {
+ nonQuadIndexCount += mesh.mesh.indexCount();
+ }
+ }
+
+ final long totalIndexCount = maxQuadIndexCount + nonQuadIndexCount;
+
+ final var vertexBlock = MemoryBlock.malloc(neededSize);
+ final var indexBlock = MemoryBlock.malloc(totalIndexCount * GlNumericType.UINT.byteWidth());
+
+ final long vertexPtr = vertexBlock.ptr();
+ final long indexPtr = indexBlock.ptr();
int byteIndex = 0;
int baseVertex = 0;
+ int firstIndex = maxQuadIndexCount;
for (BufferedMesh mesh : meshList) {
mesh.byteIndex = byteIndex;
mesh.baseVertex = baseVertex;
- mesh.buffer(ptr);
+ mesh.buffer(vertexPtr);
byteIndex += mesh.size();
- baseVertex += mesh.mesh.getVertexCount();
+ baseVertex += mesh.mesh.vertexCount();
+
+ var indexFiller = mesh.mesh.indexSequence();
+ if (indexFiller == QuadIndexSequence.INSTANCE) {
+ mesh.firstIndex = 0;
+ } else {
+ var indexCount = mesh.mesh.indexCount();
+ mesh.firstIndex = firstIndex;
+ indexFiller.fill(indexPtr + (long) firstIndex * GlNumericType.UINT.byteWidth(), indexCount);
+
+ firstIndex += indexCount;
+ }
}
- nglNamedBufferSubData(vbo, 0, byteIndex, ptr);
+ if (maxQuadIndexCount > 0) {
+ QuadIndexSequence.INSTANCE.fill(indexPtr, maxQuadIndexCount);
+ }
+
+ vbo.upload(vertexBlock);
+ ebo.upload(indexBlock);
+
+ vertexBlock.free();
+ indexBlock.free();
+ }
+
+ public void bindForDraw() {
+ vertexArray.bindForDraw();
}
public void delete() {
- clientStorage.free();
- glDeleteBuffers(vbo);
+ vertexArray.delete();
+ vbo.delete();
+ ebo.delete();
meshes.clear();
meshList.clear();
}
- public class BufferedMesh {
+ public static class BufferedMesh {
private final Mesh mesh;
- private final int vertexCount;
-
- private long byteIndex;
- private int baseVertex;
+ public long byteIndex;
+ public int firstIndex;
+ public int baseVertex;
private BufferedMesh(Mesh mesh) {
this.mesh = mesh;
- vertexCount = mesh.getVertexCount();
- }
-
- public Mesh getMesh() {
- return mesh;
}
public int size() {
return mesh.size();
}
- public int getBaseVertex() {
- return baseVertex;
- }
-
- public int getIndexCount() {
- return vertexCount * 6 / 4;
- }
-
- public VertexType getVertexType() {
- return vertexType;
+ public int indexCount() {
+ return mesh.indexCount();
}
private void buffer(long ptr) {
mesh.write(ptr + byteIndex);
}
+
+ public Vector4fc boundingSphere() {
+ return mesh.boundingSphere();
+ }
}
}
diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/EBOCache.java b/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/EBOCache.java
new file mode 100644
index 000000000..66f7cc5f2
--- /dev/null
+++ b/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/EBOCache.java
@@ -0,0 +1,80 @@
+package com.jozufozu.flywheel.backend.engine.instancing;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.jetbrains.annotations.NotNull;
+import org.lwjgl.system.MemoryUtil;
+
+import com.jozufozu.flywheel.api.model.IndexSequence;
+import com.jozufozu.flywheel.gl.GlNumericType;
+import com.jozufozu.flywheel.gl.buffer.GlBuffer;
+import com.jozufozu.flywheel.gl.buffer.GlBufferUsage;
+import com.jozufozu.flywheel.lib.memory.FlwMemoryTracker;
+import com.jozufozu.flywheel.lib.model.QuadIndexSequence;
+import com.mojang.blaze3d.platform.GlStateManager;
+
+import it.unimi.dsi.fastutil.objects.Object2ReferenceMap;
+import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap;
+
+public class EBOCache {
+ private final List quads = new ArrayList<>();
+ private final Object2ReferenceMap others = new Object2ReferenceOpenHashMap<>();
+
+ public void delete() {
+ quads.forEach(Entry::delete);
+ others.values()
+ .forEach(Entry::delete);
+ }
+
+ public int get(IndexSequence indexSequence, int indexCount) {
+ if (indexSequence == QuadIndexSequence.INSTANCE) {
+ return getQuads(indexCount);
+ } else {
+ return others.computeIfAbsent(new Key(indexSequence, indexCount), Key::create).ebo;
+ }
+ }
+
+ private int getQuads(int indexCount) {
+ // Use an existing quad EBO if there's one big enough.
+ for (Entry quadEBO : quads) {
+ if (quadEBO.gpuSize >= indexCount * GlNumericType.UINT.byteWidth()) {
+ return quadEBO.ebo;
+ }
+ }
+ // If not, create a new one.
+ var out = Entry.create(QuadIndexSequence.INSTANCE, indexCount);
+ quads.add(out);
+ return out.ebo;
+ }
+
+ private record Key(IndexSequence provider, int indexCount) {
+ private Entry create() {
+ return Entry.create(provider, indexCount);
+ }
+ }
+
+ private record Entry(int ebo, int gpuSize) {
+
+ @NotNull
+ private static Entry create(IndexSequence provider, int indexCount) {
+ int byteSize = indexCount * GlNumericType.UINT.byteWidth();
+ var ebo = GlBuffer.IMPL.create();
+
+ final long ptr = MemoryUtil.nmemAlloc(byteSize);
+ provider.fill(ptr, indexCount);
+
+ GlBuffer.IMPL.data(ebo, byteSize, ptr, GlBufferUsage.STATIC_DRAW.glEnum);
+ FlwMemoryTracker._allocGPUMemory(byteSize);
+
+ MemoryUtil.nmemFree(ptr);
+
+ return new Entry(ebo, byteSize);
+ }
+
+ private void delete() {
+ GlStateManager._glDeleteBuffers(this.ebo);
+ FlwMemoryTracker._freeGPUMemory(this.gpuSize);
+ }
+ }
+}
diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancedDrawManager.java b/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancedDrawManager.java
index b07ba7c72..e48d7d816 100644
--- a/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancedDrawManager.java
+++ b/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancedDrawManager.java
@@ -28,6 +28,7 @@ public class InstancedDrawManager {
private final List> initializedInstancers = new ArrayList<>();
private final Map drawSets = new EnumMap<>(RenderStage.class);
private final Map meshPools = new HashMap<>();
+ private final EBOCache eboCache = new EBOCache();
public DrawSet get(RenderStage stage) {
return drawSets.getOrDefault(stage, DrawSet.EMPTY);
@@ -69,6 +70,8 @@ public class InstancedDrawManager {
initializedInstancers.forEach(InstancedInstancer::delete);
initializedInstancers.clear();
+
+ eboCache.delete();
}
public void clearInstancers() {
@@ -89,8 +92,8 @@ public class InstancedDrawManager {
}
private InstancedMeshPool.BufferedMesh alloc(Mesh mesh) {
- return meshPools.computeIfAbsent(mesh.getVertexType(), InstancedMeshPool::new)
- .alloc(mesh);
+ return meshPools.computeIfAbsent(mesh.vertexType(), InstancedMeshPool::new)
+ .alloc(mesh, eboCache);
}
public static class DrawSet implements Iterable>> {
diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancedMeshPool.java b/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancedMeshPool.java
index 5e00a0604..d2901ada9 100644
--- a/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancedMeshPool.java
+++ b/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancedMeshPool.java
@@ -16,7 +16,6 @@ import com.jozufozu.flywheel.api.model.Mesh;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.gl.GlPrimitive;
import com.jozufozu.flywheel.gl.array.GlVertexArray;
-import com.jozufozu.flywheel.gl.buffer.ElementBuffer;
import com.jozufozu.flywheel.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.gl.buffer.MappedBuffer;
@@ -50,16 +49,17 @@ public class InstancedMeshPool {
/**
* Allocate a mesh in the arena.
*
- * @param mesh The mesh to allocate.
+ * @param mesh The mesh to allocate.
+ * @param eboCache The EBO cache to use.
* @return A handle to the allocated mesh.
*/
- public BufferedMesh alloc(Mesh mesh) {
+ public BufferedMesh alloc(Mesh mesh, EBOCache eboCache) {
return meshes.computeIfAbsent(mesh, m -> {
- if (m.getVertexType() != vertexType) {
+ if (m.vertexType() != vertexType) {
throw new IllegalArgumentException("Mesh has wrong vertex type");
}
- BufferedMesh bufferedMesh = new BufferedMesh(m, byteSize);
+ BufferedMesh bufferedMesh = new BufferedMesh(m, byteSize, eboCache);
byteSize += bufferedMesh.size();
allBuffered.add(bufferedMesh);
pendingUpload.add(bufferedMesh);
@@ -145,16 +145,16 @@ public class InstancedMeshPool {
public class BufferedMesh {
private final Mesh mesh;
- private final ElementBuffer ebo;
+ private final int ebo;
private long byteIndex;
private boolean deleted;
private final Set boundTo = new HashSet<>();
- private BufferedMesh(Mesh mesh, long byteIndex) {
+ private BufferedMesh(Mesh mesh, long byteIndex, EBOCache eboCache) {
this.mesh = mesh;
this.byteIndex = byteIndex;
- this.ebo = mesh.createEBO();
+ this.ebo = eboCache.get(mesh.indexSequence(), mesh.indexCount());
}
public int size() {
@@ -188,15 +188,15 @@ public class InstancedMeshPool {
BufferLayout type = vertexType.getLayout();
vao.bindVertexBuffer(0, InstancedMeshPool.this.vbo.handle(), byteIndex, type.getStride());
vao.bindAttributes(0, 0, type.attributes());
- vao.setElementBuffer(ebo.glBuffer);
+ vao.setElementBuffer(ebo);
}
}
public void draw(int instanceCount) {
if (instanceCount > 1) {
- GL32.glDrawElementsInstanced(GlPrimitive.TRIANGLES.glEnum, ebo.getElementCount(), ebo.getEboIndexType().asGLType, 0, instanceCount);
+ GL32.glDrawElementsInstanced(GlPrimitive.TRIANGLES.glEnum, mesh.indexCount(), GL32.GL_UNSIGNED_INT, 0, instanceCount);
} else {
- GL32.glDrawElements(GlPrimitive.TRIANGLES.glEnum, ebo.getElementCount(), ebo.getEboIndexType().asGLType, 0);
+ GL32.glDrawElements(GlPrimitive.TRIANGLES.glEnum, mesh.indexCount(), GL32.GL_UNSIGNED_INT, 0);
}
}
diff --git a/src/main/java/com/jozufozu/flywheel/gl/buffer/ElementBuffer.java b/src/main/java/com/jozufozu/flywheel/gl/buffer/ElementBuffer.java
deleted file mode 100644
index 8ee468976..000000000
--- a/src/main/java/com/jozufozu/flywheel/gl/buffer/ElementBuffer.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.jozufozu.flywheel.gl.buffer;
-
-import com.mojang.blaze3d.vertex.VertexFormat;
-
-public class ElementBuffer {
- protected final int elementCount;
- protected final VertexFormat.IndexType eboIndexType;
- public final int glBuffer;
-
- public ElementBuffer(int backing, int elementCount, VertexFormat.IndexType indexType) {
- this.elementCount = elementCount;
- this.eboIndexType = indexType;
- this.glBuffer = backing;
- }
-
- public int getElementCount() {
- return elementCount;
- }
-
- public VertexFormat.IndexType getEboIndexType() {
- return eboIndexType;
- }
-}
diff --git a/src/main/java/com/jozufozu/flywheel/lib/model/QuadIndexSequence.java b/src/main/java/com/jozufozu/flywheel/lib/model/QuadIndexSequence.java
new file mode 100644
index 000000000..b6a00bc0f
--- /dev/null
+++ b/src/main/java/com/jozufozu/flywheel/lib/model/QuadIndexSequence.java
@@ -0,0 +1,31 @@
+package com.jozufozu.flywheel.lib.model;
+
+import org.lwjgl.system.MemoryUtil;
+
+import com.jozufozu.flywheel.api.model.IndexSequence;
+
+public class QuadIndexSequence implements IndexSequence {
+ public static final QuadIndexSequence INSTANCE = new QuadIndexSequence();
+
+ private QuadIndexSequence() {
+ }
+
+ @Override
+ public void fill(long ptr, int count) {
+ int numVertices = 4 * (count / 6);
+ int baseVertex = 0;
+ while (baseVertex < numVertices) {
+ // triangle a
+ MemoryUtil.memPutInt(ptr, baseVertex);
+ MemoryUtil.memPutInt(ptr + 4, baseVertex + 1);
+ MemoryUtil.memPutInt(ptr + 8, baseVertex + 2);
+ // triangle b
+ MemoryUtil.memPutInt(ptr + 12, baseVertex);
+ MemoryUtil.memPutInt(ptr + 16, baseVertex + 2);
+ MemoryUtil.memPutInt(ptr + 20, baseVertex + 3);
+
+ baseVertex += 4;
+ ptr += 24;
+ }
+ }
+}
diff --git a/src/main/java/com/jozufozu/flywheel/lib/model/QuadMesh.java b/src/main/java/com/jozufozu/flywheel/lib/model/QuadMesh.java
index 2c46b040f..7549a9dc5 100644
--- a/src/main/java/com/jozufozu/flywheel/lib/model/QuadMesh.java
+++ b/src/main/java/com/jozufozu/flywheel/lib/model/QuadMesh.java
@@ -1,23 +1,16 @@
package com.jozufozu.flywheel.lib.model;
+import com.jozufozu.flywheel.api.model.IndexSequence;
import com.jozufozu.flywheel.api.model.Mesh;
-import com.jozufozu.flywheel.gl.buffer.ElementBuffer;
-import com.jozufozu.flywheel.lib.util.QuadConverter;
public interface QuadMesh extends Mesh {
- /**
- * Create an element buffer object that indexes the vertices of this mesh.
- *
- *
- * Very often models in minecraft are made up of sequential quads, which is a very predictable pattern.
- * The default implementation accommodates this, however this can be overridden to change the behavior and
- * support more complex models.
- *
- * @return an element buffer object indexing this model's vertices.
- */
@Override
- default ElementBuffer createEBO() {
- return QuadConverter.getInstance()
- .quads2Tris(getVertexCount() / 4);
+ default IndexSequence indexSequence() {
+ return QuadIndexSequence.INSTANCE;
+ }
+
+ @Override
+ default int indexCount() {
+ return vertexCount() / 2 * 3;
}
}
diff --git a/src/main/java/com/jozufozu/flywheel/lib/model/SimpleLazyModel.java b/src/main/java/com/jozufozu/flywheel/lib/model/SimpleLazyModel.java
index 86424149b..90d9b5c9c 100644
--- a/src/main/java/com/jozufozu/flywheel/lib/model/SimpleLazyModel.java
+++ b/src/main/java/com/jozufozu/flywheel/lib/model/SimpleLazyModel.java
@@ -29,7 +29,7 @@ public class SimpleLazyModel implements Model {
}
public int getVertexCount() {
- return supplier.map(Mesh::getVertexCount)
+ return supplier.map(Mesh::vertexCount)
.orElse(0);
}
diff --git a/src/main/java/com/jozufozu/flywheel/lib/model/SimpleMesh.java b/src/main/java/com/jozufozu/flywheel/lib/model/SimpleMesh.java
index 197b6ee7f..28a967263 100644
--- a/src/main/java/com/jozufozu/flywheel/lib/model/SimpleMesh.java
+++ b/src/main/java/com/jozufozu/flywheel/lib/model/SimpleMesh.java
@@ -28,7 +28,7 @@ public class SimpleMesh implements QuadMesh {
}
vertexCount = bytes / stride;
- vertexList = getVertexType().createVertexList();
+ vertexList = vertexType().createVertexList();
vertexList.ptr(contents.ptr());
vertexList.vertexCount(vertexCount);
@@ -36,12 +36,12 @@ public class SimpleMesh implements QuadMesh {
}
@Override
- public VertexType getVertexType() {
+ public VertexType vertexType() {
return vertexType;
}
@Override
- public int getVertexCount() {
+ public int vertexCount() {
return vertexCount;
}
@@ -56,7 +56,7 @@ public class SimpleMesh implements QuadMesh {
}
@Override
- public Vector4fc getBoundingSphere() {
+ public Vector4fc boundingSphere() {
return boundingSphere;
}
diff --git a/src/main/java/com/jozufozu/flywheel/lib/modelpart/ModelPart.java b/src/main/java/com/jozufozu/flywheel/lib/modelpart/ModelPart.java
index e6b1f544c..c5596bf8b 100644
--- a/src/main/java/com/jozufozu/flywheel/lib/modelpart/ModelPart.java
+++ b/src/main/java/com/jozufozu/flywheel/lib/modelpart/ModelPart.java
@@ -32,7 +32,7 @@ public class ModelPart implements QuadMesh {
cuboid.write(writer);
}
- vertexList = getVertexType().createVertexList();
+ vertexList = vertexType().createVertexList();
vertexList.ptr(ptr);
vertexList.vertexCount(vertexCount);
@@ -44,12 +44,12 @@ public class ModelPart implements QuadMesh {
}
@Override
- public PosTexNormalVertex getVertexType() {
+ public PosTexNormalVertex vertexType() {
return VertexTypes.POS_TEX_NORMAL;
}
@Override
- public int getVertexCount() {
+ public int vertexCount() {
return vertexCount;
}
@@ -64,7 +64,7 @@ public class ModelPart implements QuadMesh {
}
@Override
- public Vector4fc getBoundingSphere() {
+ public Vector4fc boundingSphere() {
return boundingSphere;
}
diff --git a/src/main/java/com/jozufozu/flywheel/lib/util/QuadConverter.java b/src/main/java/com/jozufozu/flywheel/lib/util/QuadConverter.java
deleted file mode 100644
index 0d01923c1..000000000
--- a/src/main/java/com/jozufozu/flywheel/lib/util/QuadConverter.java
+++ /dev/null
@@ -1,108 +0,0 @@
-package com.jozufozu.flywheel.lib.util;
-
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-import org.lwjgl.system.MemoryUtil;
-
-import com.jozufozu.flywheel.api.event.ReloadRenderersEvent;
-import com.jozufozu.flywheel.gl.GlNumericType;
-import com.jozufozu.flywheel.gl.buffer.ElementBuffer;
-import com.jozufozu.flywheel.gl.buffer.GlBuffer;
-import com.jozufozu.flywheel.gl.buffer.GlBufferUsage;
-import com.mojang.blaze3d.platform.GlStateManager;
-import com.mojang.blaze3d.vertex.VertexFormat;
-
-import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
-import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
-
-/**
- * A class to manage EBOs that index quads as triangles.
- */
-public class QuadConverter {
- private static QuadConverter INSTANCE;
-
- @NotNull
- public static QuadConverter getInstance() {
- if (INSTANCE == null) {
- INSTANCE = new QuadConverter();
- }
-
- return INSTANCE;
- }
-
- @Nullable
- public static QuadConverter getNullable() {
- return INSTANCE;
- }
-
- private final Int2ReferenceMap cache = new Int2ReferenceArrayMap<>();
- private final int ebo;
- private int quadCapacity;
-
- public QuadConverter() {
- this.ebo = GlBuffer.IMPL.create();
- this.quadCapacity = 0;
- }
-
- public ElementBuffer quads2Tris(int quads) {
- if (quads > quadCapacity) {
- grow(quads * 2);
- }
-
- return cache.computeIfAbsent(quads, this::createElementBuffer);
- }
-
- @NotNull
- private ElementBuffer createElementBuffer(int quads) {
- return new ElementBuffer(ebo, quads * 6, VertexFormat.IndexType.INT);
- }
-
- private void grow(int quads) {
- int byteSize = quads * 6 * GlNumericType.UINT.byteWidth();
- final long ptr = MemoryUtil.nmemAlloc(byteSize);
-
- fillBuffer(ptr, quads);
-
- GlBuffer.IMPL.data(ebo, byteSize, ptr, GlBufferUsage.STATIC_DRAW.glEnum);
-
- MemoryUtil.nmemFree(ptr);
-
- this.quadCapacity = quads;
- }
-
- public void delete() {
- GlStateManager._glDeleteBuffers(ebo);
- this.cache.clear();
- this.quadCapacity = 0;
- }
-
- private void fillBuffer(long ptr, int quads) {
- int numVertices = 4 * quads;
- int baseVertex = 0;
- while (baseVertex < numVertices) {
- writeQuadIndicesUnsafe(ptr, baseVertex);
-
- baseVertex += 4;
- ptr += 6 * 4;
- }
- }
-
- private void writeQuadIndicesUnsafe(long ptr, int baseVertex) {
- // triangle a
- MemoryUtil.memPutInt(ptr, baseVertex);
- MemoryUtil.memPutInt(ptr + 4, baseVertex + 1);
- MemoryUtil.memPutInt(ptr + 8, baseVertex + 2);
- // triangle b
- MemoryUtil.memPutInt(ptr + 12, baseVertex);
- MemoryUtil.memPutInt(ptr + 16, baseVertex + 2);
- MemoryUtil.memPutInt(ptr + 20, baseVertex + 3);
- }
-
- // make sure this gets reset first, so it has a chance to repopulate
- public static void onReloadRenderers(ReloadRenderersEvent event) {
- if (INSTANCE != null) {
- INSTANCE.delete();
- INSTANCE = null;
- }
- }
-}