Better instance deletions.

- Defer element removal until just before the model is drawn.
 - Use a modified version of ArrayList#removeIf to delete the instances and adjust the keys.
This commit is contained in:
JozsefA 2021-03-03 17:14:19 -08:00
parent 9d77f85b94
commit fe492c5d75

View File

@ -2,7 +2,7 @@ package com.simibubi.create.foundation.render.backend.instancing;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.*;
import java.util.function.Consumer;
import com.simibubi.create.foundation.render.backend.Backend;
@ -74,18 +74,6 @@ public abstract class InstancedModel<D extends InstanceData> extends BufferedMod
public synchronized void deleteInstance(InstanceKey<D> key) {
verifyKey(key);
int index = key.index;
keys.remove(index);
data.remove(index);
for (int i = index; i < keys.size(); i++) {
keys.get(i).index--;
}
maxIndexChanged = keys.size() - 1;
markIndexChanged(Math.min(maxIndexChanged, index));
key.invalidate();
}
@ -120,7 +108,9 @@ public abstract class InstancedModel<D extends InstanceData> extends BufferedMod
}
protected void renderSetup() {
if (minIndexChanged < 0 || data.isEmpty()) return;
boolean anyRemoved = doRemoval();
if (!anyRemoved && (minIndexChanged < 0 || data.isEmpty())) return;
VertexFormat instanceFormat = getInstanceFormat();
@ -140,11 +130,13 @@ public abstract class InstancedModel<D extends InstanceData> extends BufferedMod
int offset = minIndexChanged * stride;
int length = (1 + maxIndexChanged - minIndexChanged) * stride;
vbo.map(offset, length, buffer -> {
for (int i = minIndexChanged; i <= maxIndexChanged; i++) {
data.get(i).write(buffer);
}
});
if (length > 0) {
vbo.map(offset, length, buffer -> {
for (int i = minIndexChanged; i <= maxIndexChanged; i++) {
data.get(i).write(buffer);
}
});
}
if (newInstanceCount < glInstanceCount) {
int clearFrom = (maxIndexChanged + 1) * stride;
@ -170,6 +162,48 @@ public abstract class InstancedModel<D extends InstanceData> extends BufferedMod
maxIndexChanged = -1;
}
// copied from ArrayList#removeIf
protected boolean doRemoval() {
// figure out which elements are to be removed
// any exception thrown from the filter predicate at this stage
// will leave the collection unmodified
int removeCount = 0;
final int size = this.keys.size();
final BitSet removeSet = new BitSet(size);
for (int i=0; i < size; i++) {
final InstanceKey<D> element = this.keys.get(i);
if (!element.isValid()) {
removeSet.set(i);
removeCount++;
}
}
// shift surviving elements left over the spaces left by removed elements
final boolean anyToRemove = removeCount > 0;
if (anyToRemove) {
final int newSize = size - removeCount;
for (int i = 0, j = 0; (i < size) && (j < newSize); i++, j++) {
i = removeSet.nextClearBit(i);
keys.set(j, keys.get(i));
data.set(j, data.get(i));
}
keys.subList(newSize, size).clear();
data.subList(newSize, size).clear();
int firstChanged = removeSet.nextSetBit(0);
for (int i = firstChanged; i < newSize; i++) {
keys.get(i).index = i;
}
minIndexChanged = 0;
maxIndexChanged = newSize - 1;
}
return anyToRemove;
}
protected void markIndexChanged(int index) {
if (minIndexChanged < 0) {
minIndexChanged = index;