From 83119040d21e114881e5dcc1928574f8c3856dc9 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Tue, 20 Feb 2024 11:07:45 -0600 Subject: [PATCH] Pool party! - Move index buffer logic into IndexPool class, should let us avoid unnecessary uploads and make it easier to manage --- .../flywheel/backend/engine/IndexPool.java | 89 +++++++++++++++++++ .../flywheel/backend/engine/MeshPool.java | 69 +++++--------- 2 files changed, 112 insertions(+), 46 deletions(-) create mode 100644 src/main/java/com/jozufozu/flywheel/backend/engine/IndexPool.java diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/IndexPool.java b/src/main/java/com/jozufozu/flywheel/backend/engine/IndexPool.java new file mode 100644 index 000000000..3b3a35e6f --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/IndexPool.java @@ -0,0 +1,89 @@ +package com.jozufozu.flywheel.backend.engine; + +import com.jozufozu.flywheel.api.model.IndexSequence; +import com.jozufozu.flywheel.backend.gl.GlNumericType; +import com.jozufozu.flywheel.backend.gl.array.GlVertexArray; +import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer; +import com.jozufozu.flywheel.lib.memory.MemoryBlock; + +import it.unimi.dsi.fastutil.objects.Reference2IntMap; +import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; + +public class IndexPool { + private final GlBuffer ebo; + + private final Reference2IntMap indexCounts; + private final Reference2IntMap firstIndices; + + private boolean dirty; + + public IndexPool() { + ebo = new GlBuffer(); + + indexCounts = new Reference2IntOpenHashMap<>(); + firstIndices = new Reference2IntOpenHashMap<>(); + + indexCounts.defaultReturnValue(0); + } + + public int firstIndex(IndexSequence sequence) { + return firstIndices.getInt(sequence); + } + + public void reset() { + indexCounts.clear(); + firstIndices.clear(); + dirty = true; + } + + public void updateCount(IndexSequence sequence, int indexCount) { + int oldCount = indexCounts.getInt(sequence); + int newCount = Math.max(oldCount, indexCount); + + if (newCount > oldCount) { + indexCounts.put(sequence, newCount); + dirty = true; + } + } + + public void flush() { + if (!dirty) { + return; + } + + firstIndices.clear(); + dirty = false; + + long totalIndexCount = 0; + + for (int count : indexCounts.values()) { + totalIndexCount += count; + } + + final var indexBlock = MemoryBlock.malloc(totalIndexCount * GlNumericType.UINT.byteWidth()); + final long indexPtr = indexBlock.ptr(); + + int firstIndex = 0; + for (Reference2IntMap.Entry entries : indexCounts.reference2IntEntrySet()) { + var indexSequence = entries.getKey(); + var indexCount = entries.getIntValue(); + + firstIndices.put(indexSequence, firstIndex); + + indexSequence.fill(indexPtr + (long) firstIndex * GlNumericType.UINT.byteWidth(), indexCount); + + firstIndex += indexCount; + } + + ebo.upload(indexBlock); + indexBlock.free(); + } + + public void bind(GlVertexArray vertexArray) { + vertexArray.setElementBuffer(ebo.handle()); + } + + public void delete() { + ebo.delete(); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/MeshPool.java b/src/main/java/com/jozufozu/flywheel/backend/engine/MeshPool.java index 9cf6b84f9..c204ae65c 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/MeshPool.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/MeshPool.java @@ -9,27 +9,24 @@ import java.util.Set; import org.jetbrains.annotations.Nullable; import org.lwjgl.opengl.GL32; -import com.jozufozu.flywheel.api.model.IndexSequence; import com.jozufozu.flywheel.api.model.Mesh; import com.jozufozu.flywheel.api.vertex.VertexView; import com.jozufozu.flywheel.backend.InternalVertex; -import com.jozufozu.flywheel.backend.gl.GlNumericType; import com.jozufozu.flywheel.backend.gl.GlPrimitive; import com.jozufozu.flywheel.backend.gl.array.GlVertexArray; import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer; import com.jozufozu.flywheel.lib.memory.MemoryBlock; -import it.unimi.dsi.fastutil.objects.Reference2IntMap; -import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.ReferenceArraySet; public class MeshPool { private final VertexView vertexView; private final Map meshes = new HashMap<>(); private final List meshList = new ArrayList<>(); + private final List recentlyAllocated = new ArrayList<>(); private final GlBuffer vbo; - private final GlBuffer ebo; + private final IndexPool indexPool; private boolean dirty; private boolean anyToRemove; @@ -40,7 +37,7 @@ public class MeshPool { public MeshPool() { vertexView = InternalVertex.createVertexView(); vbo = new GlBuffer(); - ebo = new GlBuffer(); + indexPool = new IndexPool(); } /** @@ -56,6 +53,7 @@ public class MeshPool { private BufferedMesh _alloc(Mesh m) { BufferedMesh bufferedModel = new BufferedMesh(m); meshList.add(bufferedModel); + recentlyAllocated.add(bufferedModel); dirty = true; return bufferedModel; @@ -74,9 +72,23 @@ public class MeshPool { if (anyToRemove) { anyToRemove = false; processDeletions(); + + // Might want to shrink the index pool if something was removed. + indexPool.reset(); + for (BufferedMesh mesh : meshList) { + indexPool.updateCount(mesh.mesh.indexSequence(), mesh.indexCount()); + } + } else { + // Otherwise, just update the index with the new counts. + for (BufferedMesh mesh : recentlyAllocated) { + indexPool.updateCount(mesh.mesh.indexSequence(), mesh.indexCount()); + } } - uploadAll(); + // Always need to flush the index pool. + indexPool.flush(); + + uploadAll(); dirty = false; } @@ -93,41 +105,8 @@ public class MeshPool { private void uploadAll() { long neededSize = 0; - Reference2IntMap indexCounts = new Reference2IntOpenHashMap<>(); - indexCounts.defaultReturnValue(0); - for (BufferedMesh mesh : meshList) { neededSize += mesh.byteSize(); - - int count = indexCounts.getInt(mesh.mesh.indexSequence()); - int newCount = Math.max(count, mesh.indexCount()); - - if (newCount > count) { - indexCounts.put(mesh.mesh.indexSequence(), newCount); - } - } - - long totalIndexCount = 0; - - for (int count : indexCounts.values()) { - totalIndexCount += count; - } - - final var indexBlock = MemoryBlock.malloc(totalIndexCount * GlNumericType.UINT.byteWidth()); - final long indexPtr = indexBlock.ptr(); - - Reference2IntMap firstIndices = new Reference2IntOpenHashMap<>(); - - int firstIndex = 0; - for (Reference2IntMap.Entry entries : indexCounts.reference2IntEntrySet()) { - var indexSequence = entries.getKey(); - var indexCount = entries.getIntValue(); - - firstIndices.put(indexSequence, firstIndex); - - indexSequence.fill(indexPtr + (long) firstIndex * GlNumericType.UINT.byteWidth(), indexCount); - - firstIndex += indexCount; } final var vertexBlock = MemoryBlock.malloc(neededSize); @@ -144,27 +123,25 @@ public class MeshPool { byteIndex += mesh.byteSize(); baseVertex += mesh.vertexCount(); - mesh.firstIndex = firstIndices.getInt(mesh.mesh.indexSequence()); + mesh.firstIndex = indexPool.firstIndex(mesh.mesh.indexSequence()); mesh.boundTo.clear(); } vbo.upload(vertexBlock); - ebo.upload(indexBlock); vertexBlock.free(); - indexBlock.free(); } public void bind(GlVertexArray vertexArray) { - vertexArray.setElementBuffer(ebo.handle()); + indexPool.bind(vertexArray); vertexArray.bindVertexBuffer(0, vbo.handle(), 0, InternalVertex.STRIDE); vertexArray.bindAttributes(0, 0, InternalVertex.ATTRIBUTES); } public void delete() { vbo.delete(); - ebo.delete(); + indexPool.delete(); meshes.clear(); meshList.clear(); } @@ -231,7 +208,7 @@ public class MeshPool { public void setup(GlVertexArray vao) { if (boundTo.add(vao)) { - vao.setElementBuffer(MeshPool.this.ebo.handle()); + MeshPool.this.indexPool.bind(vao); vao.bindVertexBuffer(0, MeshPool.this.vbo.handle(), byteIndex, InternalVertex.STRIDE); vao.bindAttributes(0, 0, InternalVertex.ATTRIBUTES); }