Lessen updates today!

- LightLut now does incremental updates java-side
- Still requires a full upload when changed, though it does not take up
  much space
- ShaderLightVisualStorage now actually triggers removal of light
  section from the lut
This commit is contained in:
Jozufozu 2024-11-10 11:59:05 -08:00
parent 5b97a56c8c
commit bedb92c73c
3 changed files with 85 additions and 36 deletions

View file

@ -3,17 +3,16 @@ package dev.engine_room.flywheel.backend.engine;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.jetbrains.annotations.Nullable;
import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.longs.Long2IntMap;
import net.minecraft.core.SectionPos; import net.minecraft.core.SectionPos;
// Massive kudos to RogueLogix for figuring out this LUT scheme.
public final class LightLut { public final class LightLut {
private final Layer<Layer<IntLayer>> indices = new Layer<>(); private final Layer<Layer<IntLayer>> indices = new Layer<>();
private LightLut() { public void add(long position, int index) {
}
private void add(long position, int index) {
final var x = SectionPos.x(position); final var x = SectionPos.x(position);
final var y = SectionPos.y(position); final var y = SectionPos.y(position);
final var z = SectionPos.z(position); final var z = SectionPos.z(position);
@ -23,31 +22,32 @@ public final class LightLut {
.set(z, index + 1); .set(z, index + 1);
} }
private IntArrayList toLut() { public void remove(long section) {
final var x = SectionPos.x(section);
final var y = SectionPos.y(section);
final var z = SectionPos.z(section);
var first = indices.get(x);
if (first == null) {
return;
}
var second = first.get(y);
if (second == null) {
return;
}
second.clear(z);
}
public IntArrayList flatten() {
final var out = new IntArrayList(); final var out = new IntArrayList();
indices.fillLut(out, (yIndices, lut) -> yIndices.fillLut(lut, IntLayer::fillLut)); indices.fillLut(out, (yIndices, lut) -> yIndices.fillLut(lut, IntLayer::fillLut));
return out; return out;
} }
// Massive kudos to RogueLogix for figuring out this LUT scheme.
// TODO: switch to y x z or x z y ordering
// DATA LAYOUT
// [0] : base chunk X, X index count, followed by linear indices of y blocks
// [yBlockIndex] : baseChunk Y, Y index count, followed by linear indices of z blocks for this x
// [zBlockIndex] : baseChunk Z, Z index count, followed by linear indices of lighting chunks
// this data layout allows a single buffer to represent the lighting volume, without requiring the entire 3d lookup volume to be allocated
public static IntArrayList buildLut(Long2IntMap sectionIndicesMaps) {
if (sectionIndicesMaps.isEmpty()) {
return new IntArrayList();
}
var out = new LightLut();
sectionIndicesMaps.forEach(out::add);
return out.toLut();
}
private static final class Layer<T> { private static final class Layer<T> {
private boolean hasBase = false; private boolean hasBase = false;
private int base = 0; private int base = 0;
@ -78,6 +78,25 @@ public final class LightLut {
} }
} }
@Nullable
public T get(int i) {
if (!hasBase) {
return null;
}
if (i < base) {
return null;
}
final var offset = i - base;
if (offset >= nextLayer.length) {
return null;
}
return (T) nextLayer[offset];
}
public T computeIfAbsent(int i, Supplier<T> ifAbsent) { public T computeIfAbsent(int i, Supplier<T> ifAbsent) {
if (!hasBase) { if (!hasBase) {
// We don't want to default to base 0, so we'll use the first value we get. // We don't want to default to base 0, so we'll use the first value we get.
@ -155,6 +174,24 @@ public final class LightLut {
indices[offset] = index; indices[offset] = index;
} }
public void clear(int i) {
if (!hasBase) {
return;
}
if (i < base) {
return;
}
final var offset = i - base;
if (offset >= indices.length) {
return;
}
indices[offset] = 0;
}
private void resize(int length) { private void resize(int length) {
final var newIndices = new int[length]; final var newIndices = new int[length];
System.arraycopy(indices, 0, newIndices, 0, indices.length); System.arraycopy(indices, 0, newIndices, 0, indices.length);

View file

@ -44,12 +44,9 @@ public class LightStorage {
private static final int INVALID_SECTION = -1; private static final int INVALID_SECTION = -1;
private final LevelAccessor level; private final LevelAccessor level;
private final LightLut lut;
private final CpuArena arena; private final CpuArena arena;
private final Long2IntMap section2ArenaIndex = new Long2IntOpenHashMap(); private final Long2IntMap section2ArenaIndex;
{
section2ArenaIndex.defaultReturnValue(INVALID_SECTION);
}
private final BitSet changed = new BitSet(); private final BitSet changed = new BitSet();
private boolean needsLutRebuild = false; private boolean needsLutRebuild = false;
@ -60,8 +57,10 @@ public class LightStorage {
public LightStorage(LevelAccessor level) { public LightStorage(LevelAccessor level) {
this.level = level; this.level = level;
lut = new LightLut();
arena = new CpuArena(SECTION_SIZE_BYTES, DEFAULT_ARENA_CAPACITY_SECTIONS); arena = new CpuArena(SECTION_SIZE_BYTES, DEFAULT_ARENA_CAPACITY_SECTIONS);
section2ArenaIndex = new Long2IntOpenHashMap();
section2ArenaIndex.defaultReturnValue(INVALID_SECTION);
} }
/** /**
@ -135,12 +134,22 @@ public class LightStorage {
if (!requestedSections.contains(section)) { if (!requestedSections.contains(section)) {
arena.free(entry.getIntValue()); arena.free(entry.getIntValue());
needsLutRebuild = true; endTrackingSection(section);
it.remove(); it.remove();
} }
} }
} }
private void beginTrackingSection(long section, int index) {
lut.add(section, index);
needsLutRebuild = true;
}
private void endTrackingSection(long section) {
lut.remove(section);
needsLutRebuild = true;
}
public int capacity() { public int capacity() {
return arena.capacity(); return arena.capacity();
} }
@ -374,7 +383,7 @@ 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);
needsLutRebuild = true; beginTrackingSection(section, out);
} }
return out; return out;
} }
@ -406,8 +415,7 @@ public class LightStorage {
} }
public IntArrayList createLut() { public IntArrayList createLut() {
// TODO: incremental lut updates return lut.flatten();
return LightLut.buildLut(section2ArenaIndex);
} }
private enum SectionEdge { private enum SectionEdge {

View file

@ -43,7 +43,11 @@ public class ShaderLightVisualStorage {
} }
public void remove(ShaderLightVisual visual) { public void remove(ShaderLightVisual visual) {
trackers.remove(visual); var tracker = trackers.remove(visual);
if (tracker != null) {
markDirty();
}
} }
public void clear() { public void clear() {