mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2024-12-27 07:26:48 +01:00
More smarter less upload
- Only write draw calls when they change. - Update baseInstance in the apply shader. - Simplify object upload logic.
This commit is contained in:
parent
d4a9b51c8b
commit
abca3a2389
7 changed files with 55 additions and 39 deletions
|
@ -15,7 +15,6 @@ public abstract class AbstractInstancer<I extends Instance> implements Instancer
|
|||
protected final ArrayList<I> instances = new ArrayList<>();
|
||||
protected final ArrayList<InstanceHandleImpl> handles = new ArrayList<>();
|
||||
|
||||
// TODO: atomic bitset?
|
||||
protected final BitSet changed = new BitSet();
|
||||
protected final BitSet deleted = new BitSet();
|
||||
|
||||
|
@ -45,6 +44,7 @@ public abstract class AbstractInstancer<I extends Instance> implements Instancer
|
|||
if (index < 0 || index >= getInstanceCount()) {
|
||||
return;
|
||||
}
|
||||
// TODO: Atomic bitset. Synchronizing here blocks the task executor and causes massive overhead.
|
||||
synchronized (lock) {
|
||||
changed.set(index);
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||
private final List<IndirectDraw> indirectDraws = new ArrayList<>();
|
||||
private final Map<RenderStage, List<MultiDraw>> multiDraws = new EnumMap<>(RenderStage.class);
|
||||
private boolean needsDrawBarrier;
|
||||
private boolean needsSortDraws;
|
||||
private boolean hasNewDraws;
|
||||
private int instanceCountThisFrame;
|
||||
|
||||
IndirectCullingGroup(InstanceType<I> instanceType) {
|
||||
|
@ -72,15 +72,23 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||
|
||||
buffers.updateCounts(instanceCountThisFrame, indirectModels.size(), indirectDraws.size());
|
||||
|
||||
if (needsSortDraws) {
|
||||
sortDraws();
|
||||
needsSortDraws = false;
|
||||
}
|
||||
|
||||
// Must flush the mesh pool first so everything else has the right baseVertex and baseIndex.
|
||||
meshPool.flush(stagingBuffer);
|
||||
|
||||
// Upload only objects that have changed.
|
||||
uploadObjects(stagingBuffer);
|
||||
|
||||
// We need to upload the models every frame to reset the instance count.
|
||||
uploadModels(stagingBuffer);
|
||||
uploadDraws(stagingBuffer);
|
||||
|
||||
if (hasNewDraws) {
|
||||
sortDraws();
|
||||
// Draws, however, only need to be updated when we get new ones.
|
||||
// The instanceCount and baseInstance will be updated by the applyProgram,
|
||||
// and all other fields are constant to the lifetime of the draw.
|
||||
uploadDraws(stagingBuffer);
|
||||
hasNewDraws = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void dispatchCull() {
|
||||
|
@ -161,7 +169,7 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||
indirectDraws.add(new IndirectDraw(indirectModel, entry.getKey(), bufferedMesh, stage));
|
||||
}
|
||||
|
||||
needsSortDraws = true;
|
||||
hasNewDraws = true;
|
||||
}
|
||||
|
||||
public void submit(RenderStage stage) {
|
||||
|
@ -193,9 +201,9 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||
|
||||
private void uploadObjects(StagingBuffer stagingBuffer) {
|
||||
long pos = 0;
|
||||
for (IndirectModel batch : indirectModels) {
|
||||
var instanceCount = batch.instancer.getInstanceCount();
|
||||
batch.writeObjects(stagingBuffer, pos, buffers.object.handle());
|
||||
for (IndirectModel model : indirectModels) {
|
||||
var instanceCount = model.instancer.getInstanceCount();
|
||||
model.writeObjects(stagingBuffer, pos, buffers.object.handle());
|
||||
|
||||
pos += instanceCount * objectStride;
|
||||
}
|
||||
|
@ -216,15 +224,15 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||
}
|
||||
|
||||
private void writeModels(long writePtr) {
|
||||
for (var batch : indirectModels) {
|
||||
batch.write(writePtr);
|
||||
for (var model : indirectModels) {
|
||||
model.write(writePtr);
|
||||
writePtr += IndirectBuffers.MODEL_STRIDE;
|
||||
}
|
||||
}
|
||||
|
||||
private void writeCommands(long writePtr) {
|
||||
for (var batch : indirectDraws) {
|
||||
batch.write(writePtr);
|
||||
for (var draw : indirectDraws) {
|
||||
draw.write(writePtr);
|
||||
writePtr += IndirectBuffers.DRAW_COMMAND_STRIDE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,9 +47,9 @@ public class IndirectDraw {
|
|||
MemoryUtil.memPutInt(ptr + 4, 0); // instanceCount - to be set by the apply shader
|
||||
MemoryUtil.memPutInt(ptr + 8, mesh.firstIndex()); // firstIndex
|
||||
MemoryUtil.memPutInt(ptr + 12, mesh.baseVertex()); // baseVertex
|
||||
MemoryUtil.memPutInt(ptr + 16, model.baseInstance()); // baseInstance
|
||||
MemoryUtil.memPutInt(ptr + 16, 0); // baseInstance - to be set by the apply shader
|
||||
|
||||
MemoryUtil.memPutInt(ptr + 20, model.index); // modelIndex
|
||||
MemoryUtil.memPutInt(ptr + 20, model.index); // modelIndex - never changes
|
||||
|
||||
MemoryUtil.memPutInt(ptr + 24, materialVertexIndex); // materialVertexIndex
|
||||
MemoryUtil.memPutInt(ptr + 28, materialFragmentIndex); // materialFragmentIndex
|
||||
|
|
|
@ -12,6 +12,8 @@ public class IndirectInstancer<I extends Instance> extends AbstractInstancer<I>
|
|||
private final InstanceWriter<I> writer;
|
||||
private int modelIndex;
|
||||
|
||||
private long lastStartPos = -1;
|
||||
|
||||
public IndirectInstancer(InstanceType<I> type) {
|
||||
super(type);
|
||||
long instanceStride = type.getLayout()
|
||||
|
@ -24,21 +26,38 @@ public class IndirectInstancer<I extends Instance> extends AbstractInstancer<I>
|
|||
removeDeletedInstances();
|
||||
}
|
||||
|
||||
public void writeChanged(StagingBuffer stagingBuffer, long start, int dstVbo) {
|
||||
public void write(StagingBuffer stagingBuffer, long startPos, int dstVbo) {
|
||||
if (shouldWriteAll(startPos)) {
|
||||
writeAll(stagingBuffer, startPos, dstVbo);
|
||||
} else {
|
||||
writeChanged(stagingBuffer, startPos, dstVbo);
|
||||
}
|
||||
|
||||
changed.clear();
|
||||
lastStartPos = startPos;
|
||||
}
|
||||
|
||||
private boolean shouldWriteAll(long startPos) {
|
||||
// If enough of the buffer has changed, write the whole thing to avoid the overhead of a bunch of small writes.
|
||||
return startPos != lastStartPos || moreThanTwoThirdsChanged();
|
||||
}
|
||||
|
||||
private boolean moreThanTwoThirdsChanged() {
|
||||
return (changed.cardinality() * 3) > (instances.size() * 2);
|
||||
}
|
||||
|
||||
private void writeChanged(StagingBuffer stagingBuffer, long start, int dstVbo) {
|
||||
int count = instances.size();
|
||||
for (int i = changed.nextSetBit(0); i >= 0 && i < count; i = changed.nextSetBit(i + 1)) {
|
||||
var instance = instances.get(i);
|
||||
stagingBuffer.enqueueCopy(objectStride, dstVbo, start + i * objectStride, ptr -> writeOne(ptr, instance));
|
||||
}
|
||||
changed.clear();
|
||||
}
|
||||
|
||||
public void writeAll(StagingBuffer stagingBuffer, long start, int dstVbo) {
|
||||
private void writeAll(StagingBuffer stagingBuffer, long start, int dstVbo) {
|
||||
long totalSize = objectStride * instances.size();
|
||||
|
||||
stagingBuffer.enqueueCopy(totalSize, dstVbo, start, this::writeAll);
|
||||
|
||||
changed.clear();
|
||||
}
|
||||
|
||||
private void writeAll(long ptr) {
|
||||
|
@ -49,9 +68,7 @@ public class IndirectInstancer<I extends Instance> extends AbstractInstancer<I>
|
|||
}
|
||||
|
||||
private void writeOne(long ptr, I instance) {
|
||||
// write modelID
|
||||
MemoryUtil.memPutInt(ptr, modelIndex);
|
||||
// write object
|
||||
writer.write(ptr + IndirectBuffers.INT_SIZE, instance);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ public class IndirectModel {
|
|||
private final Vector4fc boundingSphere;
|
||||
|
||||
private int baseInstance = -1;
|
||||
private boolean needsFullWrite = true;
|
||||
|
||||
public IndirectModel(IndirectInstancer<?> instancer, int index, Vector4fc boundingSphere) {
|
||||
this.instancer = instancer;
|
||||
|
@ -23,20 +22,11 @@ public class IndirectModel {
|
|||
|
||||
public void prepare(int baseInstance) {
|
||||
instancer.update();
|
||||
if (baseInstance == this.baseInstance) {
|
||||
needsFullWrite = false;
|
||||
return;
|
||||
}
|
||||
this.baseInstance = baseInstance;
|
||||
needsFullWrite = true;
|
||||
}
|
||||
|
||||
public void writeObjects(StagingBuffer stagingBuffer, long start, int dstVbo) {
|
||||
if (needsFullWrite) {
|
||||
instancer.writeAll(stagingBuffer, start, dstVbo);
|
||||
} else {
|
||||
instancer.writeChanged(stagingBuffer, start, dstVbo);
|
||||
}
|
||||
instancer.write(stagingBuffer, start, dstVbo);
|
||||
}
|
||||
|
||||
public void write(long ptr) {
|
||||
|
|
|
@ -14,7 +14,8 @@ import com.jozufozu.flywheel.lib.memory.MemoryBlock;
|
|||
import it.unimi.dsi.fastutil.PriorityQueue;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayFIFOQueue;
|
||||
|
||||
// https://github.com/CaffeineMC/sodium-fabric/blob/dev/src/main/java/me/jellysquid/mods/sodium/client/gl/arena/staging/MappedStagingBuffer.java
|
||||
// Used https://github.com/CaffeineMC/sodium-fabric/blob/dev/src/main/java/me/jellysquid/mods/sodium/client/gl/arena/staging/MappedStagingBuffer.java
|
||||
// as a reference for implementation. Modified to be less safe and to allow for writing directly into the staging buffer.
|
||||
public class StagingBuffer {
|
||||
private static final long DEFAULT_CAPACITY = 1024 * 1024 * 16;
|
||||
private static final int STORAGE_FLAGS = GL45C.GL_MAP_PERSISTENT_BIT | GL45C.GL_MAP_WRITE_BIT | GL45C.GL_CLIENT_STORAGE_BIT;
|
||||
|
|
|
@ -21,6 +21,6 @@ void main() {
|
|||
}
|
||||
|
||||
uint modelIndex = drawCommands[drawIndex].modelIndex;
|
||||
uint instanceCount = models[modelIndex].instanceCount;
|
||||
drawCommands[drawIndex].instanceCount = instanceCount;
|
||||
drawCommands[drawIndex].instanceCount = models[modelIndex].instanceCount;
|
||||
drawCommands[drawIndex].baseInstance = models[modelIndex].baseInstance;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue