mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2024-11-14 22:43:56 +01:00
On-call paging
- Only update the page table when an allocation is resized - Only upload the page table after it's uploaded - Combine various setters for InstancePager.Allocation and IndirectInstancer - Free pages when an allocation is deleted
This commit is contained in:
parent
1138208e31
commit
e83a308a46
@ -127,7 +127,7 @@ public abstract class AbstractInstancer<I extends Instance> implements Instancer
|
||||
|
||||
// We definitely shouldn't consider the deleted instances as changed though,
|
||||
// else we might try some out of bounds accesses later.
|
||||
clearChangedRange(newSize, oldSize);
|
||||
changed.clear(newSize, oldSize);
|
||||
|
||||
// Punch out the deleted instances, shifting over surviving instances to fill their place.
|
||||
for (int scanPos = writePos; (scanPos < oldSize) && (writePos < newSize); scanPos++, writePos++) {
|
||||
@ -155,10 +155,6 @@ public abstract class AbstractInstancer<I extends Instance> implements Instancer
|
||||
.clear();
|
||||
}
|
||||
|
||||
protected void clearChangedRange(int start, int end) {
|
||||
changed.clear(start, end);
|
||||
}
|
||||
|
||||
protected void setRangeChanged(int start, int end) {
|
||||
changed.set(start, end);
|
||||
}
|
||||
|
@ -83,13 +83,13 @@ public class IndirectBuffers {
|
||||
draw.ensureCapacity(drawCount);
|
||||
|
||||
final long ptr = multiBindBlock.ptr();
|
||||
MemoryUtil.memPutInt(ptr + INSTANCE_HANDLE_OFFSET, pageFile.storage.handle());
|
||||
MemoryUtil.memPutInt(ptr + INSTANCE_HANDLE_OFFSET, pageFile.objects.handle());
|
||||
MemoryUtil.memPutInt(ptr + TARGET_HANDLE_OFFSET, target.handle());
|
||||
MemoryUtil.memPutInt(ptr + MODEL_INDEX_HANDLE_OFFSET, pageFile.pageTable.handle());
|
||||
MemoryUtil.memPutInt(ptr + MODEL_HANDLE_OFFSET, model.handle());
|
||||
MemoryUtil.memPutInt(ptr + DRAW_HANDLE_OFFSET, draw.handle());
|
||||
|
||||
MemoryUtil.memPutAddress(ptr + INSTANCE_SIZE_OFFSET, pageFile.storage.capacity());
|
||||
MemoryUtil.memPutAddress(ptr + INSTANCE_SIZE_OFFSET, pageFile.objects.capacity());
|
||||
MemoryUtil.memPutAddress(ptr + TARGET_SIZE_OFFSET, INT_SIZE * instanceCount);
|
||||
MemoryUtil.memPutAddress(ptr + MODEL_INDEX_SIZE_OFFSET, pageFile.pageTable.capacity());
|
||||
MemoryUtil.memPutAddress(ptr + MODEL_SIZE_OFFSET, MODEL_STRIDE * modelCount);
|
||||
|
@ -74,8 +74,7 @@ public class IndirectCullingGroup<I extends Instance> {
|
||||
continue;
|
||||
}
|
||||
|
||||
instancer.modelIndex(modelIndex);
|
||||
instancer.baseInstance(instanceCountThisFrame);
|
||||
instancer.postUpdate(modelIndex, instanceCountThisFrame);
|
||||
instanceCountThisFrame += instanceCount;
|
||||
|
||||
modelIndex++;
|
||||
@ -173,8 +172,8 @@ public class IndirectCullingGroup<I extends Instance> {
|
||||
}
|
||||
|
||||
public void add(IndirectInstancer<I> instancer, InstancerKey<I> key, MeshPool meshPool) {
|
||||
instancer.pageFile = buffers.pageFile.createPage();
|
||||
instancer.modelIndex(instancers.size());
|
||||
instancer.pageFile = buffers.pageFile.createAllocation();
|
||||
instancer.postUpdate(instancers.size(), -1);
|
||||
|
||||
instancers.add(instancer);
|
||||
|
||||
@ -246,7 +245,7 @@ public class IndirectCullingGroup<I extends Instance> {
|
||||
|
||||
private void uploadInstances(StagingBuffer stagingBuffer) {
|
||||
for (var instancer : instancers) {
|
||||
instancer.uploadInstances(stagingBuffer, buffers.pageFile.storage.handle());
|
||||
instancer.uploadInstances(stagingBuffer, buffers.pageFile.objects.handle());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,13 +52,6 @@ public class IndirectInstancer<I extends Instance> extends AbstractInstancer<I>
|
||||
changedPages.set(InstancePager.object2Page(start), InstancePager.object2Page(end) + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void clearChangedRange(int start, int end) {
|
||||
super.clearChangedRange(start, end);
|
||||
|
||||
// changedPages.clear(pageFile.object2Page(start), pageFile);
|
||||
}
|
||||
|
||||
public void addDraw(IndirectDraw draw) {
|
||||
associatedDraws.add(draw);
|
||||
}
|
||||
@ -69,8 +62,12 @@ public class IndirectInstancer<I extends Instance> extends AbstractInstancer<I>
|
||||
|
||||
public void update() {
|
||||
removeDeletedInstances();
|
||||
}
|
||||
|
||||
pageFile.activeCount(instanceCount());
|
||||
public void postUpdate(int modelIndex, int baseInstance) {
|
||||
this.modelIndex = modelIndex;
|
||||
this.baseInstance = baseInstance;
|
||||
pageFile.update(modelIndex, instanceCount());
|
||||
}
|
||||
|
||||
public void writeModel(long ptr) {
|
||||
@ -117,21 +114,14 @@ public class IndirectInstancer<I extends Instance> extends AbstractInstancer<I>
|
||||
for (IndirectDraw draw : draws()) {
|
||||
draw.delete();
|
||||
}
|
||||
}
|
||||
|
||||
public void modelIndex(int modelIndex) {
|
||||
this.modelIndex = modelIndex;
|
||||
pageFile.modelIndex(modelIndex);
|
||||
pageFile.delete();
|
||||
}
|
||||
|
||||
public int modelIndex() {
|
||||
return modelIndex;
|
||||
}
|
||||
|
||||
public void baseInstance(int baseInstance) {
|
||||
this.baseInstance = baseInstance;
|
||||
}
|
||||
|
||||
public int baseInstance() {
|
||||
return baseInstance;
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
package dev.engine_room.flywheel.backend.engine.indirect;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
@ -17,24 +15,20 @@ public class InstancePager extends AbstractArena {
|
||||
|
||||
public static final int INITIAL_PAGES_ALLOCATED = 4;
|
||||
|
||||
private final long objectSizeBytes;
|
||||
|
||||
private MemoryBlock pageData;
|
||||
|
||||
public final ResizableStorageBuffer storage;
|
||||
private MemoryBlock pageTableData;
|
||||
public final ResizableStorageBuffer objects;
|
||||
public final ResizableStorageBuffer pageTable;
|
||||
|
||||
private final List<Allocation> allocations = new ArrayList<>();
|
||||
private boolean needsUpload = false;
|
||||
|
||||
public InstancePager(long objectSizeBytes) {
|
||||
super(PAGE_SIZE * objectSizeBytes);
|
||||
this.objectSizeBytes = objectSizeBytes;
|
||||
|
||||
this.storage = new ResizableStorageBuffer();
|
||||
this.objects = new ResizableStorageBuffer();
|
||||
this.pageTable = new ResizableStorageBuffer();
|
||||
|
||||
pageData = MemoryBlock.malloc(INITIAL_PAGES_ALLOCATED * Integer.BYTES);
|
||||
storage.ensureCapacity(INITIAL_PAGES_ALLOCATED * elementSizeBytes);
|
||||
pageTableData = MemoryBlock.malloc(INITIAL_PAGES_ALLOCATED * Integer.BYTES);
|
||||
objects.ensureCapacity(INITIAL_PAGES_ALLOCATED * elementSizeBytes);
|
||||
pageTable.ensureCapacity(INITIAL_PAGES_ALLOCATED * Integer.BYTES);
|
||||
}
|
||||
|
||||
@ -46,79 +40,122 @@ public class InstancePager extends AbstractArena {
|
||||
return pageIndex << LOG_2_PAGE_SIZE;
|
||||
}
|
||||
|
||||
public Allocation createPage() {
|
||||
var out = new Allocation();
|
||||
allocations.add(out);
|
||||
return out;
|
||||
public Allocation createAllocation() {
|
||||
return new Allocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long byteCapacity() {
|
||||
return storage.capacity();
|
||||
return objects.capacity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void free(int i) {
|
||||
super.free(i);
|
||||
MemoryUtil.memPutInt(ptrForPage(i), 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void grow() {
|
||||
pageData = pageData.realloc(pageData.size() * 2);
|
||||
storage.ensureCapacity(storage.capacity() * 2);
|
||||
pageTableData = pageTableData.realloc(pageTableData.size() * 2);
|
||||
objects.ensureCapacity(objects.capacity() * 2);
|
||||
pageTable.ensureCapacity(pageTable.capacity() * 2);
|
||||
}
|
||||
|
||||
public void uploadTable(StagingBuffer stagingBuffer) {
|
||||
for (Allocation allocation : allocations) {
|
||||
allocation.updatePageTable();
|
||||
if (!needsUpload) {
|
||||
return;
|
||||
}
|
||||
stagingBuffer.enqueueCopy(pageData.ptr(), pageData.size(), pageTable.handle(), 0);
|
||||
// We could be smarter about which spans are uploaded but this thing is so small it's probably not worth it.
|
||||
stagingBuffer.enqueueCopy(pageTableData.ptr(), pageTableData.size(), pageTable.handle(), 0);
|
||||
needsUpload = false;
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
storage.delete();
|
||||
objects.delete();
|
||||
pageTable.delete();
|
||||
pageData.free();
|
||||
pageTableData.free();
|
||||
}
|
||||
|
||||
private long ptrForPage(int page) {
|
||||
return pageTableData.ptr() + (long) page * Integer.BYTES;
|
||||
}
|
||||
|
||||
public class Allocation {
|
||||
public int[] pages = new int[0];
|
||||
public static final int[] EMPTY_ALLOCATION = new int[0];
|
||||
public int[] pages = EMPTY_ALLOCATION;
|
||||
|
||||
private int modelIndex = -1;
|
||||
private int activeCount = 0;
|
||||
private int objectCount = 0;
|
||||
|
||||
public void modelIndex(int modelIndex) {
|
||||
if (this.modelIndex != modelIndex) {
|
||||
this.modelIndex = modelIndex;
|
||||
/**
|
||||
* Calculates the page descriptor for the given page index.
|
||||
* Runs under the assumption than all pages are full except maybe the last one.
|
||||
*/
|
||||
private int calculatePageDescriptor(int pageIndex) {
|
||||
int countInPage;
|
||||
if (objectCount % PAGE_SIZE != 0 && pageIndex == pages.length - 1) {
|
||||
// Last page && it isn't full -> use the remainder.
|
||||
countInPage = objectCount & PAGE_MASK;
|
||||
} else if (objectCount > 0) {
|
||||
// Full page.
|
||||
countInPage = PAGE_SIZE;
|
||||
} else {
|
||||
// Empty page, this shouldn't be reachable because we eagerly free empty pages.
|
||||
countInPage = 0;
|
||||
}
|
||||
return (modelIndex & 0x3FFFFF) | (countInPage << 26);
|
||||
}
|
||||
|
||||
private void updatePageTable() {
|
||||
if (pages.length == 0) {
|
||||
public void update(int modelIndex, int objectCount) {
|
||||
boolean incremental = this.modelIndex == modelIndex;
|
||||
|
||||
if (incremental && objectCount == this.objectCount) {
|
||||
// Nothing will change.
|
||||
return;
|
||||
}
|
||||
|
||||
var ptr = pageData.ptr();
|
||||
InstancePager.this.needsUpload = true;
|
||||
|
||||
int fullPage = (modelIndex & 0x3FFFFF) | (32 << 26);
|
||||
|
||||
int remainder = activeCount;
|
||||
|
||||
for (int i = 0; i < pages.length - 1; i++) {
|
||||
int page = pages[i];
|
||||
MemoryUtil.memPutInt(ptr + page * Integer.BYTES, fullPage);
|
||||
remainder -= PAGE_SIZE;
|
||||
}
|
||||
|
||||
MemoryUtil.memPutInt(ptr + pages[pages.length - 1] * Integer.BYTES, (modelIndex & 0x3FFFFF) | (remainder << 26));
|
||||
}
|
||||
|
||||
public void activeCount(int objectCount) {
|
||||
var neededPages = object2Page((objectCount + PAGE_MASK));
|
||||
activeCount = objectCount;
|
||||
this.modelIndex = modelIndex;
|
||||
this.objectCount = objectCount;
|
||||
|
||||
var oldLength = pages.length;
|
||||
var newLength = object2Page((objectCount + PAGE_MASK));
|
||||
|
||||
if (oldLength > neededPages) {
|
||||
shrink(oldLength, neededPages);
|
||||
} else if (oldLength < neededPages) {
|
||||
grow(neededPages, oldLength);
|
||||
if (oldLength > newLength) {
|
||||
// Eagerly free the now unnecessary pages.
|
||||
// shrink will zero out the pageTable entries for the freed pages.
|
||||
shrink(oldLength, newLength);
|
||||
|
||||
if (incremental) {
|
||||
// Only update the last page, everything else is unchanged.
|
||||
updateRange(newLength - 1, newLength);
|
||||
}
|
||||
} else if (oldLength < newLength) {
|
||||
// Allocate new pages to fit the new object count.
|
||||
grow(newLength, oldLength);
|
||||
|
||||
if (incremental) {
|
||||
// Update the old last page + all new pages
|
||||
updateRange(oldLength - 1, newLength);
|
||||
}
|
||||
} else {
|
||||
if (incremental) {
|
||||
// Only update the last page.
|
||||
updateRange(oldLength - 1, oldLength);
|
||||
}
|
||||
}
|
||||
|
||||
if (!incremental) {
|
||||
// Update all pages.
|
||||
updateRange(0, newLength);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateRange(int start, int oldLength) {
|
||||
for (int i = start; i < oldLength; i++) {
|
||||
MemoryUtil.memPutInt(ptrForPage(pages[i]), calculatePageDescriptor(i));
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,7 +163,8 @@ public class InstancePager extends AbstractArena {
|
||||
pages = Arrays.copyOf(pages, neededPages);
|
||||
|
||||
for (int i = oldLength; i < neededPages; i++) {
|
||||
pages[i] = InstancePager.this.alloc();
|
||||
var page = InstancePager.this.alloc();
|
||||
pages[i] = page;
|
||||
}
|
||||
}
|
||||
|
||||
@ -134,7 +172,6 @@ public class InstancePager extends AbstractArena {
|
||||
for (int i = oldLength - 1; i >= neededPages; i--) {
|
||||
var page = pages[i];
|
||||
InstancePager.this.free(page);
|
||||
MemoryUtil.memPutInt(pageData.ptr() + page * Integer.BYTES, 0);
|
||||
}
|
||||
|
||||
pages = Arrays.copyOf(pages, neededPages);
|
||||
@ -151,5 +188,14 @@ public class InstancePager extends AbstractArena {
|
||||
public long page2ByteOffset(int page) {
|
||||
return InstancePager.this.byteOffsetOf(pages[page]);
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
for (int page : pages) {
|
||||
InstancePager.this.free(page);
|
||||
}
|
||||
pages = EMPTY_ALLOCATION;
|
||||
modelIndex = -1;
|
||||
objectCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user