Pool party!

- Move index buffer logic into IndexPool class, should let us avoid
  unnecessary uploads and make it easier to manage
This commit is contained in:
Jozufozu 2024-02-20 11:07:45 -06:00
parent aa46f6ceb1
commit c4dcebb203
2 changed files with 112 additions and 46 deletions

View file

@ -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<IndexSequence> indexCounts;
private final Reference2IntMap<IndexSequence> 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<IndexSequence> 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();
}
}

View file

@ -9,27 +9,24 @@ import java.util.Set;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.lwjgl.opengl.GL32; import org.lwjgl.opengl.GL32;
import com.jozufozu.flywheel.api.model.IndexSequence;
import com.jozufozu.flywheel.api.model.Mesh; import com.jozufozu.flywheel.api.model.Mesh;
import com.jozufozu.flywheel.api.vertex.VertexView; import com.jozufozu.flywheel.api.vertex.VertexView;
import com.jozufozu.flywheel.backend.InternalVertex; 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.GlPrimitive;
import com.jozufozu.flywheel.backend.gl.array.GlVertexArray; import com.jozufozu.flywheel.backend.gl.array.GlVertexArray;
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer; import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.lib.memory.MemoryBlock; 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; import it.unimi.dsi.fastutil.objects.ReferenceArraySet;
public class MeshPool { public class MeshPool {
private final VertexView vertexView; private final VertexView vertexView;
private final Map<Mesh, BufferedMesh> meshes = new HashMap<>(); private final Map<Mesh, BufferedMesh> meshes = new HashMap<>();
private final List<BufferedMesh> meshList = new ArrayList<>(); private final List<BufferedMesh> meshList = new ArrayList<>();
private final List<BufferedMesh> recentlyAllocated = new ArrayList<>();
private final GlBuffer vbo; private final GlBuffer vbo;
private final GlBuffer ebo; private final IndexPool indexPool;
private boolean dirty; private boolean dirty;
private boolean anyToRemove; private boolean anyToRemove;
@ -40,7 +37,7 @@ public class MeshPool {
public MeshPool() { public MeshPool() {
vertexView = InternalVertex.createVertexView(); vertexView = InternalVertex.createVertexView();
vbo = new GlBuffer(); vbo = new GlBuffer();
ebo = new GlBuffer(); indexPool = new IndexPool();
} }
/** /**
@ -56,6 +53,7 @@ public class MeshPool {
private BufferedMesh _alloc(Mesh m) { private BufferedMesh _alloc(Mesh m) {
BufferedMesh bufferedModel = new BufferedMesh(m); BufferedMesh bufferedModel = new BufferedMesh(m);
meshList.add(bufferedModel); meshList.add(bufferedModel);
recentlyAllocated.add(bufferedModel);
dirty = true; dirty = true;
return bufferedModel; return bufferedModel;
@ -74,9 +72,23 @@ public class MeshPool {
if (anyToRemove) { if (anyToRemove) {
anyToRemove = false; anyToRemove = false;
processDeletions(); 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; dirty = false;
} }
@ -93,41 +105,8 @@ public class MeshPool {
private void uploadAll() { private void uploadAll() {
long neededSize = 0; long neededSize = 0;
Reference2IntMap<IndexSequence> indexCounts = new Reference2IntOpenHashMap<>();
indexCounts.defaultReturnValue(0);
for (BufferedMesh mesh : meshList) { for (BufferedMesh mesh : meshList) {
neededSize += mesh.byteSize(); 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<IndexSequence> firstIndices = new Reference2IntOpenHashMap<>();
int firstIndex = 0;
for (Reference2IntMap.Entry<IndexSequence> 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); final var vertexBlock = MemoryBlock.malloc(neededSize);
@ -144,27 +123,25 @@ public class MeshPool {
byteIndex += mesh.byteSize(); byteIndex += mesh.byteSize();
baseVertex += mesh.vertexCount(); baseVertex += mesh.vertexCount();
mesh.firstIndex = firstIndices.getInt(mesh.mesh.indexSequence()); mesh.firstIndex = indexPool.firstIndex(mesh.mesh.indexSequence());
mesh.boundTo.clear(); mesh.boundTo.clear();
} }
vbo.upload(vertexBlock); vbo.upload(vertexBlock);
ebo.upload(indexBlock);
vertexBlock.free(); vertexBlock.free();
indexBlock.free();
} }
public void bind(GlVertexArray vertexArray) { public void bind(GlVertexArray vertexArray) {
vertexArray.setElementBuffer(ebo.handle()); indexPool.bind(vertexArray);
vertexArray.bindVertexBuffer(0, vbo.handle(), 0, InternalVertex.STRIDE); vertexArray.bindVertexBuffer(0, vbo.handle(), 0, InternalVertex.STRIDE);
vertexArray.bindAttributes(0, 0, InternalVertex.ATTRIBUTES); vertexArray.bindAttributes(0, 0, InternalVertex.ATTRIBUTES);
} }
public void delete() { public void delete() {
vbo.delete(); vbo.delete();
ebo.delete(); indexPool.delete();
meshes.clear(); meshes.clear();
meshList.clear(); meshList.clear();
} }
@ -231,7 +208,7 @@ public class MeshPool {
public void setup(GlVertexArray vao) { public void setup(GlVertexArray vao) {
if (boundTo.add(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.bindVertexBuffer(0, MeshPool.this.vbo.handle(), byteIndex, InternalVertex.STRIDE);
vao.bindAttributes(0, 0, InternalVertex.ATTRIBUTES); vao.bindAttributes(0, 0, InternalVertex.ATTRIBUTES);
} }