Bookkeeping

- Mappings drop pages when they write zero validity bits
- Instancer only updates pages that changed
This commit is contained in:
Jozufozu 2024-11-01 12:47:15 -07:00
parent 20b3f78b9c
commit fac63168c1
2 changed files with 69 additions and 44 deletions

View File

@ -168,7 +168,7 @@ public class IndirectInstancer<I extends Instance> extends AbstractInstancer<I>
int otherValid = other.valid.get(); int otherValid = other.valid.get();
// If the other page is empty, or we're full, we're done. // If the other page is empty, or we're full, we're done.
if (otherValid == 0 || valid == 0xFFFFFFFF) { if (isEmpty(otherValid) || isFull(valid)) {
return valid; return valid;
} }
@ -223,17 +223,29 @@ public class IndirectInstancer<I extends Instance> extends AbstractInstancer<I>
public void update(int modelIndex, int baseInstance) { public void update(int modelIndex, int baseInstance) {
this.baseInstance = baseInstance; this.baseInstance = baseInstance;
if (this.modelIndex == modelIndex && changedPages.isEmpty()) { var sameModelIndex = this.modelIndex == modelIndex;
if (sameModelIndex && changedPages.isEmpty()) {
// Nothing to do!
return; return;
} }
this.modelIndex = modelIndex; this.modelIndex = modelIndex;
var pages = this.pages.get(); var pages = this.pages.get();
mapping.updateCount(pages.length); mapping.updateCount(pages.length);
if (sameModelIndex) {
// Only need to update the changed pages.
for (int page = changedPages.nextSetBit(0); page >= 0 && page < pages.length; page = changedPages.nextSetBit(page + 1)) {
mapping.updatePage(page, modelIndex, pages[page].valid.get());
}
} else {
// Need to update all pages since the model index changed.
for (int i = 0; i < pages.length; i++) { for (int i = 0; i < pages.length; i++) {
mapping.updatePage(i, modelIndex, pages[i].valid.get()); mapping.updatePage(i, modelIndex, pages[i].valid.get());
} }
} }
}
public void writeModel(long ptr) { public void writeModel(long ptr) {
MemoryUtil.memPutInt(ptr, 0); // instanceCount - to be incremented by the cull shader MemoryUtil.memPutInt(ptr, 0); // instanceCount - to be incremented by the cull shader
@ -291,46 +303,17 @@ public class IndirectInstancer<I extends Instance> extends AbstractInstancer<I>
} }
public void parallelUpdate() { public void parallelUpdate() {
if (true) { // TODO: Merge pages when they're less than half full.
// FIXME: infinite loop when the page in readpos doesn't have enough to fill the page in writepos
return;
}
var pages = this.pages.get();
// If there are at least 2 pages with space, we can consolidate.
if (fullPages.cardinality() > (pages.length - 2)) {
return;
}
// Note this runs after visuals are updated so we don't really have to take care for thread safety.
int writePos = 0;
while (true) {
writePos = fullPages.nextClearBit(writePos);
int readPos = fullPages.nextClearBit(writePos + 1);
if (writePos >= pages.length || readPos >= pages.length) {
break;
}
InstancePage writeTo = pages[writePos];
InstancePage readFrom = pages[readPos];
int validNow = writeTo.takeFrom(readFrom);
if (isFull(validNow)) {
fullPages.set(writePos);
writePos = readPos;
}
}
} }
private static boolean isFull(int valid) { private static boolean isFull(int valid) {
return valid == 0xFFFFFFFF; return valid == 0xFFFFFFFF;
} }
private static boolean isEmpty(int otherValid) {
return otherValid == 0;
}
@Override @Override
public void delete() { public void delete() {
for (IndirectDraw draw : draws()) { for (IndirectDraw draw : draws()) {

View File

@ -13,6 +13,8 @@ public class ObjectStorage extends AbstractArena {
public static final int PAGE_SIZE = 1 << LOG_2_PAGE_SIZE; public static final int PAGE_SIZE = 1 << LOG_2_PAGE_SIZE;
public static final int PAGE_MASK = PAGE_SIZE - 1; public static final int PAGE_MASK = PAGE_SIZE - 1;
public static final int INVALID_PAGE = -1;
public static final int INITIAL_PAGES_ALLOCATED = 4; public static final int INITIAL_PAGES_ALLOCATED = 4;
public static final int DESCRIPTOR_SIZE_BYTES = Integer.BYTES * 2; public static final int DESCRIPTOR_SIZE_BYTES = Integer.BYTES * 2;
@ -53,8 +55,13 @@ public class ObjectStorage extends AbstractArena {
@Override @Override
public void free(int i) { public void free(int i) {
if (i == INVALID_PAGE) {
return;
}
super.free(i); super.free(i);
MemoryUtil.memPutInt(ptrForPage(i), 0); var ptr = ptrForPage(i);
MemoryUtil.memPutInt(ptr, 0);
MemoryUtil.memPutInt(ptr + 4, 0);
} }
@Override @Override
@ -98,14 +105,49 @@ public class ObjectStorage extends AbstractArena {
private static final int[] EMPTY_ALLOCATION = new int[0]; private static final int[] EMPTY_ALLOCATION = new int[0];
private int[] pages = EMPTY_ALLOCATION; private int[] pages = EMPTY_ALLOCATION;
public void updatePage(int i, int modelIndex, int i1) { public void updatePage(int index, int modelIndex, int validBits) {
var ptr = ptrForPage(pages[i]); if (validBits == 0) {
holePunch(index);
return;
}
var page = pages[index];
if (page == INVALID_PAGE) {
// Un-holed punch.
page = unHolePunch(index);
}
var ptr = ptrForPage(page);
MemoryUtil.memPutInt(ptr, modelIndex); MemoryUtil.memPutInt(ptr, modelIndex);
MemoryUtil.memPutInt(ptr + 4, i1); MemoryUtil.memPutInt(ptr + 4, validBits);
ObjectStorage.this.needsUpload = true; ObjectStorage.this.needsUpload = true;
} }
/**
* Free a page on the inside of the mapping, maintaining the same virtual mapping size.
*
* @param index The index of the page to free.
*/
public void holePunch(int index) {
ObjectStorage.this.free(pages[index]);
pages[index] = INVALID_PAGE;
ObjectStorage.this.needsUpload = true;
}
/**
* Allocate a new page on the inside of the mapping, maintaining the same virtual mapping size.
*
* @param index The index of the page to allocate.
* @return The allocated page.
*/
private int unHolePunch(int index) {
int page = ObjectStorage.this.alloc();
pages[index] = page;
return page;
}
public void updateCount(int newLength) { public void updateCount(int newLength) {
var oldLength = pages.length; var oldLength = pages.length;
if (oldLength > newLength) { if (oldLength > newLength) {
@ -122,8 +164,8 @@ public class ObjectStorage extends AbstractArena {
return pages.length; return pages.length;
} }
public long page2ByteOffset(int page) { public long page2ByteOffset(int index) {
return ObjectStorage.this.byteOffsetOf(pages[page]); return ObjectStorage.this.byteOffsetOf(pages[index]);
} }
public void delete() { public void delete() {