Missing section misdirection

- Avoid adding all sections every frame
- Remove sections when they are no longer needed
- Rebuild the lut when sections are removed
- Properly detect missing sections by writing 1-based indices to the lut
This commit is contained in:
Jozufozu 2024-06-29 19:01:48 -07:00
parent cf2c11a37c
commit 2a0694ccaf
5 changed files with 50 additions and 16 deletions

View file

@ -46,7 +46,8 @@ public class EngineImpl implements Engine {
@Override @Override
public Plan<RenderContext> createFramePlan() { public Plan<RenderContext> createFramePlan() {
return lightStorage.createFramePlan().then(SyncedPlan.of(this::flush)); return lightStorage.createFramePlan()
.then(SyncedPlan.of(this::flush));
} }
@Override @Override

View file

@ -80,7 +80,8 @@ public class LightLut {
zLookup.ensureCapacity(zIndex + 3); zLookup.ensureCapacity(zIndex + 3);
zLookup.size(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; return indices;
} }

View file

@ -12,6 +12,7 @@ import dev.engine_room.flywheel.lib.task.SimplePlan;
import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.longs.Long2IntMap; import it.unimi.dsi.fastutil.longs.Long2IntMap;
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongSet;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos; import net.minecraft.core.SectionPos;
import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelAccessor;
@ -47,7 +48,7 @@ public class LightStorage {
} }
private final BitSet changed = new BitSet(); private final BitSet changed = new BitSet();
private boolean newSections = false; private boolean needsLutRebuild = false;
public LightStorage(LevelAccessor level, EnvironmentStorage environmentStorage) { public LightStorage(LevelAccessor level, EnvironmentStorage environmentStorage) {
this.level = level; this.level = level;
@ -58,11 +59,34 @@ public class LightStorage {
public Plan<RenderContext> createFramePlan() { public Plan<RenderContext> createFramePlan() {
return SimplePlan.of(() -> { return SimplePlan.of(() -> {
var longs = environmentStorage.allLightSections(); var allLightSections = environmentStorage.allLightSections();
longs.forEach(this::addSection);
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() { public int capacity() {
return arena.capacity(); return arena.capacity();
} }
@ -179,15 +203,11 @@ public class LightStorage {
if (out == INVALID_SECTION) { if (out == INVALID_SECTION) {
out = arena.alloc(); out = arena.alloc();
section2ArenaIndex.put(section, out); section2ArenaIndex.put(section, out);
newSections = true; needsLutRebuild = true;
} }
return out; return out;
} }
public void removeSection(long section) {
}
public void delete() { public void delete() {
arena.delete(); arena.delete();
} }
@ -196,8 +216,10 @@ public class LightStorage {
return !changed.isEmpty(); return !changed.isEmpty();
} }
public boolean hasNewSections() { public boolean checkNeedsLutRebuildAndClear() {
return newSections; var out = needsLutRebuild;
needsLutRebuild = false;
return out;
} }
public void uploadChangedSections(StagingBuffer staging, int dstVbo) { public void uploadChangedSections(StagingBuffer staging, int dstVbo) {
@ -208,6 +230,7 @@ public class LightStorage {
} }
public IntArrayList createLut() { public IntArrayList createLut() {
// TODO: incremental lut updates
return LightLut.buildLut(section2ArenaIndex); return LightLut.buildLut(section2ArenaIndex);
} }
} }

View file

@ -22,7 +22,7 @@ public class LightBuffers {
lightArena.ensureCapacity(capacity); lightArena.ensureCapacity(capacity);
light.uploadChangedSections(staging, lightArena.handle()); light.uploadChangedSections(staging, lightArena.handle());
if (light.hasNewSections()) { if (light.checkNeedsLutRebuildAndClear()) {
var lut = light.createLut(); var lut = light.createLut();
this.lut.ensureCapacity(lut.size()); this.lut.ensureCapacity(lut.size());

View file

@ -42,15 +42,24 @@ bool _flw_nextLut(uint base, int coord, out uint next) {
bool _flw_chunkCoordToSectionIndex(ivec3 sectionPos, out uint index) { bool _flw_chunkCoordToSectionIndex(ivec3 sectionPos, out uint index) {
uint y; uint y;
if (_flw_nextLut(0, sectionPos.x, y)) { if (_flw_nextLut(0, sectionPos.x, y) || y == 0) {
return true; return true;
} }
uint z; uint z;
if (_flw_nextLut(y, sectionPos.y, z)) { if (_flw_nextLut(y, sectionPos.y, z) || z == 0) {
return true; 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) { vec2 _flw_lightAt(uint sectionOffset, uvec3 blockInSectionPos) {