From f3845a15fb128b2ccaeac01af56a2ee07240faa8 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Thu, 14 Nov 2024 17:10:00 -0800 Subject: [PATCH] Not a lut left - Prune empty layers from the lut to avoid it just getting larger as the player explores the world --- .../flywheel/backend/engine/LightLut.java | 128 ++++++++++++++++++ .../flywheel/backend/engine/LightStorage.java | 8 ++ 2 files changed, 136 insertions(+) diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightLut.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightLut.java index ffe0b2b4d..987ae674b 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightLut.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightLut.java @@ -23,6 +23,11 @@ public void add(long position, int index) { .set(z, index + 1); } + public void prune() { + // Maybe this could be done better incrementally? + indices.prune((middle) -> middle.prune(IntLayer::prune)); + } + public void remove(long section) { final var x = SectionPos.x(section); final var y = SectionPos.y(section); @@ -49,6 +54,11 @@ public IntArrayList flatten() { return out; } + @FunctionalInterface + public interface Prune { + boolean prune(T t); + } + public static final class Layer { private boolean hasBase = false; private int base = 0; @@ -135,6 +145,69 @@ public T computeIfAbsent(int i, Supplier ifAbsent) { return (T) out; } + /** + * @return {@code true} if the layer is now empty. + */ + public boolean prune(Prune inner) { + if (!hasBase) { + return true; + } + + // Prune the next layer before checking for leading/trailing zeros. + for (var i = 0; i < nextLayer.length; i++) { + var o = nextLayer[i]; + if (o != null && inner.prune((T) o)) { + nextLayer[i] = null; + } + } + + var leadingZeros = getLeadingZeros(); + + if (leadingZeros == nextLayer.length) { + return true; + } + + var trailingZeros = getTrailingZeros(); + + if (leadingZeros == 0 && trailingZeros == 0) { + return false; + } + + final var newIndices = new Object[nextLayer.length - leadingZeros - trailingZeros]; + + System.arraycopy(nextLayer, leadingZeros, newIndices, 0, newIndices.length); + nextLayer = newIndices; + base += leadingZeros; + + return false; + } + + private int getLeadingZeros() { + int out = 0; + + for (Object index : nextLayer) { + if (index == null) { + out++; + } else { + break; + } + } + return out; + } + + private int getTrailingZeros() { + int out = 0; + + for (int i = nextLayer.length - 1; i >= 0; i--) { + if (nextLayer[i] == null) { + out++; + } else { + break; + } + } + return out; + } + private void resize(int length) { final var newIndices = new Object[length]; System.arraycopy(nextLayer, 0, newIndices, 0, nextLayer.length); @@ -214,6 +287,61 @@ public void set(int i, int index) { indices[offset] = index; } + /** + * @return {@code true} if the layer is now empty. + */ + public boolean prune() { + if (!hasBase) { + return true; + } + + var leadingZeros = getLeadingZeros(); + + if (leadingZeros == indices.length) { + return true; + } + + var trailingZeros = getTrailingZeros(); + + if (leadingZeros == 0 && trailingZeros == 0) { + return false; + } + + final var newIndices = new int[indices.length - leadingZeros - trailingZeros]; + + System.arraycopy(indices, leadingZeros, newIndices, 0, newIndices.length); + indices = newIndices; + base += leadingZeros; + + return false; + } + + private int getTrailingZeros() { + int out = 0; + + for (int i = indices.length - 1; i >= 0; i--) { + if (indices[i] == 0) { + out++; + } else { + break; + } + } + return out; + } + + private int getLeadingZeros() { + int out = 0; + + for (int index : indices) { + if (index == 0) { + out++; + } else { + break; + } + } + return out; + } + public void clear(int i) { if (!hasBase) { return; diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightStorage.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightStorage.java index be9df4981..060cf6f7d 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightStorage.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/LightStorage.java @@ -171,6 +171,8 @@ private void removeUnusedSections() { return; } + boolean anyRemoved = false; + var entries = section2ArenaIndex.long2IntEntrySet(); var it = entries.iterator(); while (it.hasNext()) { @@ -181,8 +183,14 @@ private void removeUnusedSections() { arena.free(entry.getIntValue()); endTrackingSection(section); it.remove(); + anyRemoved = true; } } + + if (anyRemoved) { + lut.prune(); + needsLutRebuild = true; + } } private void beginTrackingSection(long section, int index) {