diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/EngineImpl.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/EngineImpl.java index 4b17aa5c8..1912bbf23 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/EngineImpl.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/EngineImpl.java @@ -46,7 +46,8 @@ public class EngineImpl implements Engine { @Override public Plan createFramePlan() { - return lightStorage.createFramePlan().then(SyncedPlan.of(this::flush)); + return lightStorage.createFramePlan() + .then(SyncedPlan.of(this::flush)); } @Override diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/embed/LightLut.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/embed/LightLut.java index cfe4ced2f..736811c5a 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/embed/LightLut.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/embed/LightLut.java @@ -80,7 +80,8 @@ public class LightLut { zLookup.ensureCapacity(zIndex + 3); zLookup.size(zIndex + 3); } - zLookup.set(zIndex + 2, sectionIndicesMaps.get(position)); + // Add 1 to the actual index so that 0 indicates a missing section. + zLookup.set(zIndex + 2, sectionIndicesMaps.get(position) + 1); } return indices; } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/embed/LightStorage.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/embed/LightStorage.java index 242c595f3..8095e7cdb 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/embed/LightStorage.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/embed/LightStorage.java @@ -12,6 +12,7 @@ import dev.engine_room.flywheel.lib.task.SimplePlan; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.longs.Long2IntMap; import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; +import it.unimi.dsi.fastutil.longs.LongSet; import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; import net.minecraft.world.level.LevelAccessor; @@ -47,7 +48,7 @@ public class LightStorage { } private final BitSet changed = new BitSet(); - private boolean newSections = false; + private boolean needsLutRebuild = false; public LightStorage(LevelAccessor level, EnvironmentStorage environmentStorage) { this.level = level; @@ -58,11 +59,34 @@ public class LightStorage { public Plan createFramePlan() { return SimplePlan.of(() -> { - var longs = environmentStorage.allLightSections(); - longs.forEach(this::addSection); + var allLightSections = environmentStorage.allLightSections(); + + removeUnusedSections(allLightSections); + + // Only add the new sections. + allLightSections.removeAll(section2ArenaIndex.keySet()); + + for (var section : allLightSections) { + addSection(section); + } }); } + private void removeUnusedSections(LongSet allLightSections) { + var entries = section2ArenaIndex.long2IntEntrySet(); + var it = entries.iterator(); + while (it.hasNext()) { + var entry = it.next(); + var section = entry.getLongKey(); + + if (!allLightSections.contains(section)) { + arena.free(entry.getIntValue()); + needsLutRebuild = true; + it.remove(); + } + } + } + public int capacity() { return arena.capacity(); } @@ -179,15 +203,11 @@ public class LightStorage { if (out == INVALID_SECTION) { out = arena.alloc(); section2ArenaIndex.put(section, out); - newSections = true; + needsLutRebuild = true; } return out; } - public void removeSection(long section) { - - } - public void delete() { arena.delete(); } @@ -196,8 +216,10 @@ public class LightStorage { return !changed.isEmpty(); } - public boolean hasNewSections() { - return newSections; + public boolean checkNeedsLutRebuildAndClear() { + var out = needsLutRebuild; + needsLutRebuild = false; + return out; } public void uploadChangedSections(StagingBuffer staging, int dstVbo) { @@ -208,6 +230,7 @@ public class LightStorage { } public IntArrayList createLut() { + // TODO: incremental lut updates return LightLut.buildLut(section2ArenaIndex); } } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/LightBuffers.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/LightBuffers.java index 76e3d200c..506e453f8 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/LightBuffers.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/LightBuffers.java @@ -22,7 +22,7 @@ public class LightBuffers { lightArena.ensureCapacity(capacity); light.uploadChangedSections(staging, lightArena.handle()); - if (light.hasNewSections()) { + if (light.checkNeedsLutRebuildAndClear()) { var lut = light.createLut(); this.lut.ensureCapacity(lut.size()); diff --git a/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/main.frag b/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/main.frag index b51f490a6..0179a846f 100644 --- a/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/main.frag +++ b/common/src/backend/resources/assets/flywheel/flywheel/internal/indirect/main.frag @@ -42,15 +42,24 @@ bool _flw_nextLut(uint base, int coord, out uint next) { bool _flw_chunkCoordToSectionIndex(ivec3 sectionPos, out uint index) { uint y; - if (_flw_nextLut(0, sectionPos.x, y)) { + if (_flw_nextLut(0, sectionPos.x, y) || y == 0) { return true; } uint z; - if (_flw_nextLut(y, sectionPos.y, z)) { + if (_flw_nextLut(y, sectionPos.y, z) || z == 0) { return true; } - return _flw_nextLut(z, sectionPos.z, index); + + uint sectionIndex; + if (_flw_nextLut(z, sectionPos.z, index) || index == 0) { + return true; + } + + // The index is written as 1-based so we can properly detect missing sections. + index = sectionIndex - 1; + + return false; } vec2 _flw_lightAt(uint sectionOffset, uvec3 blockInSectionPos) {