mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2024-11-13 05:54:01 +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
@ -15,7 +15,6 @@ public abstract class AbstractInstancer<I extends Instance> implements Instancer
|
|||||||
protected final ArrayList<I> instances = new ArrayList<>();
|
protected final ArrayList<I> instances = new ArrayList<>();
|
||||||
protected final ArrayList<InstanceHandleImpl> handles = new ArrayList<>();
|
protected final ArrayList<InstanceHandleImpl> handles = new ArrayList<>();
|
||||||
|
|
||||||
// TODO: atomic bitset?
|
|
||||||
protected final BitSet changed = new BitSet();
|
protected final BitSet changed = new BitSet();
|
||||||
protected final BitSet deleted = 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()) {
|
if (index < 0 || index >= getInstanceCount()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// TODO: Atomic bitset. Synchronizing here blocks the task executor and causes massive overhead.
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
changed.set(index);
|
changed.set(index);
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||||||
private final List<IndirectDraw> indirectDraws = new ArrayList<>();
|
private final List<IndirectDraw> indirectDraws = new ArrayList<>();
|
||||||
private final Map<RenderStage, List<MultiDraw>> multiDraws = new EnumMap<>(RenderStage.class);
|
private final Map<RenderStage, List<MultiDraw>> multiDraws = new EnumMap<>(RenderStage.class);
|
||||||
private boolean needsDrawBarrier;
|
private boolean needsDrawBarrier;
|
||||||
private boolean needsSortDraws;
|
private boolean hasNewDraws;
|
||||||
private int instanceCountThisFrame;
|
private int instanceCountThisFrame;
|
||||||
|
|
||||||
IndirectCullingGroup(InstanceType<I> instanceType) {
|
IndirectCullingGroup(InstanceType<I> instanceType) {
|
||||||
@ -72,15 +72,23 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||||||
|
|
||||||
buffers.updateCounts(instanceCountThisFrame, indirectModels.size(), indirectDraws.size());
|
buffers.updateCounts(instanceCountThisFrame, indirectModels.size(), indirectDraws.size());
|
||||||
|
|
||||||
if (needsSortDraws) {
|
// Must flush the mesh pool first so everything else has the right baseVertex and baseIndex.
|
||||||
sortDraws();
|
|
||||||
needsSortDraws = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
meshPool.flush(stagingBuffer);
|
meshPool.flush(stagingBuffer);
|
||||||
|
|
||||||
|
// Upload only objects that have changed.
|
||||||
uploadObjects(stagingBuffer);
|
uploadObjects(stagingBuffer);
|
||||||
|
|
||||||
|
// We need to upload the models every frame to reset the instance count.
|
||||||
uploadModels(stagingBuffer);
|
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() {
|
public void dispatchCull() {
|
||||||
@ -161,7 +169,7 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||||||
indirectDraws.add(new IndirectDraw(indirectModel, entry.getKey(), bufferedMesh, stage));
|
indirectDraws.add(new IndirectDraw(indirectModel, entry.getKey(), bufferedMesh, stage));
|
||||||
}
|
}
|
||||||
|
|
||||||
needsSortDraws = true;
|
hasNewDraws = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void submit(RenderStage stage) {
|
public void submit(RenderStage stage) {
|
||||||
@ -193,9 +201,9 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||||||
|
|
||||||
private void uploadObjects(StagingBuffer stagingBuffer) {
|
private void uploadObjects(StagingBuffer stagingBuffer) {
|
||||||
long pos = 0;
|
long pos = 0;
|
||||||
for (IndirectModel batch : indirectModels) {
|
for (IndirectModel model : indirectModels) {
|
||||||
var instanceCount = batch.instancer.getInstanceCount();
|
var instanceCount = model.instancer.getInstanceCount();
|
||||||
batch.writeObjects(stagingBuffer, pos, buffers.object.handle());
|
model.writeObjects(stagingBuffer, pos, buffers.object.handle());
|
||||||
|
|
||||||
pos += instanceCount * objectStride;
|
pos += instanceCount * objectStride;
|
||||||
}
|
}
|
||||||
@ -216,15 +224,15 @@ public class IndirectCullingGroup<I extends Instance> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void writeModels(long writePtr) {
|
private void writeModels(long writePtr) {
|
||||||
for (var batch : indirectModels) {
|
for (var model : indirectModels) {
|
||||||
batch.write(writePtr);
|
model.write(writePtr);
|
||||||
writePtr += IndirectBuffers.MODEL_STRIDE;
|
writePtr += IndirectBuffers.MODEL_STRIDE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeCommands(long writePtr) {
|
private void writeCommands(long writePtr) {
|
||||||
for (var batch : indirectDraws) {
|
for (var draw : indirectDraws) {
|
||||||
batch.write(writePtr);
|
draw.write(writePtr);
|
||||||
writePtr += IndirectBuffers.DRAW_COMMAND_STRIDE;
|
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 + 4, 0); // instanceCount - to be set by the apply shader
|
||||||
MemoryUtil.memPutInt(ptr + 8, mesh.firstIndex()); // firstIndex
|
MemoryUtil.memPutInt(ptr + 8, mesh.firstIndex()); // firstIndex
|
||||||
MemoryUtil.memPutInt(ptr + 12, mesh.baseVertex()); // baseVertex
|
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 + 24, materialVertexIndex); // materialVertexIndex
|
||||||
MemoryUtil.memPutInt(ptr + 28, materialFragmentIndex); // materialFragmentIndex
|
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 final InstanceWriter<I> writer;
|
||||||
private int modelIndex;
|
private int modelIndex;
|
||||||
|
|
||||||
|
private long lastStartPos = -1;
|
||||||
|
|
||||||
public IndirectInstancer(InstanceType<I> type) {
|
public IndirectInstancer(InstanceType<I> type) {
|
||||||
super(type);
|
super(type);
|
||||||
long instanceStride = type.getLayout()
|
long instanceStride = type.getLayout()
|
||||||
@ -24,21 +26,38 @@ public class IndirectInstancer<I extends Instance> extends AbstractInstancer<I>
|
|||||||
removeDeletedInstances();
|
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();
|
int count = instances.size();
|
||||||
for (int i = changed.nextSetBit(0); i >= 0 && i < count; i = changed.nextSetBit(i + 1)) {
|
for (int i = changed.nextSetBit(0); i >= 0 && i < count; i = changed.nextSetBit(i + 1)) {
|
||||||
var instance = instances.get(i);
|
var instance = instances.get(i);
|
||||||
stagingBuffer.enqueueCopy(objectStride, dstVbo, start + i * objectStride, ptr -> writeOne(ptr, instance));
|
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();
|
long totalSize = objectStride * instances.size();
|
||||||
|
|
||||||
stagingBuffer.enqueueCopy(totalSize, dstVbo, start, this::writeAll);
|
stagingBuffer.enqueueCopy(totalSize, dstVbo, start, this::writeAll);
|
||||||
|
|
||||||
changed.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeAll(long ptr) {
|
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) {
|
private void writeOne(long ptr, I instance) {
|
||||||
// write modelID
|
|
||||||
MemoryUtil.memPutInt(ptr, modelIndex);
|
MemoryUtil.memPutInt(ptr, modelIndex);
|
||||||
// write object
|
|
||||||
writer.write(ptr + IndirectBuffers.INT_SIZE, instance);
|
writer.write(ptr + IndirectBuffers.INT_SIZE, instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,6 @@ public class IndirectModel {
|
|||||||
private final Vector4fc boundingSphere;
|
private final Vector4fc boundingSphere;
|
||||||
|
|
||||||
private int baseInstance = -1;
|
private int baseInstance = -1;
|
||||||
private boolean needsFullWrite = true;
|
|
||||||
|
|
||||||
public IndirectModel(IndirectInstancer<?> instancer, int index, Vector4fc boundingSphere) {
|
public IndirectModel(IndirectInstancer<?> instancer, int index, Vector4fc boundingSphere) {
|
||||||
this.instancer = instancer;
|
this.instancer = instancer;
|
||||||
@ -23,20 +22,11 @@ public class IndirectModel {
|
|||||||
|
|
||||||
public void prepare(int baseInstance) {
|
public void prepare(int baseInstance) {
|
||||||
instancer.update();
|
instancer.update();
|
||||||
if (baseInstance == this.baseInstance) {
|
|
||||||
needsFullWrite = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.baseInstance = baseInstance;
|
this.baseInstance = baseInstance;
|
||||||
needsFullWrite = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeObjects(StagingBuffer stagingBuffer, long start, int dstVbo) {
|
public void writeObjects(StagingBuffer stagingBuffer, long start, int dstVbo) {
|
||||||
if (needsFullWrite) {
|
instancer.write(stagingBuffer, start, dstVbo);
|
||||||
instancer.writeAll(stagingBuffer, start, dstVbo);
|
|
||||||
} else {
|
|
||||||
instancer.writeChanged(stagingBuffer, start, dstVbo);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void write(long ptr) {
|
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.PriorityQueue;
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArrayFIFOQueue;
|
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 {
|
public class StagingBuffer {
|
||||||
private static final long DEFAULT_CAPACITY = 1024 * 1024 * 16;
|
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;
|
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 modelIndex = drawCommands[drawIndex].modelIndex;
|
||||||
uint instanceCount = models[modelIndex].instanceCount;
|
drawCommands[drawIndex].instanceCount = models[modelIndex].instanceCount;
|
||||||
drawCommands[drawIndex].instanceCount = instanceCount;
|
drawCommands[drawIndex].baseInstance = models[modelIndex].baseInstance;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user