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.Supplier;
import org.jetbrains.annotations.Nullable;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.longs.Long2IntMap;
import net.minecraft.core.SectionPos;
// Massive kudos to RogueLogix for figuring out this LUT scheme.
public final class LightLut {
private final Layer<Layer<IntLayer>> indices = new Layer<>();
private LightLut() {
}
private void add(long position, int index) {
public void add(long position, int index) {
final var x = SectionPos.x(position);
final var y = SectionPos.y(position);
final var z = SectionPos.z(position);
@ -23,31 +22,32 @@ public final class LightLut {
.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();
indices.fillLut(out, (yIndices, lut) -> yIndices.fillLut(lut, IntLayer::fillLut));
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 boolean hasBase = false;
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) {
if (!hasBase) {
// 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;
}
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) {
final var newIndices = new int[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 final LevelAccessor level;
private final LightLut lut;
private final CpuArena arena;
private final Long2IntMap section2ArenaIndex = new Long2IntOpenHashMap();
{
section2ArenaIndex.defaultReturnValue(INVALID_SECTION);
}
private final Long2IntMap section2ArenaIndex;
private final BitSet changed = new BitSet();
private boolean needsLutRebuild = false;
@ -60,8 +57,10 @@ public class LightStorage {
public LightStorage(LevelAccessor level) {
this.level = level;
lut = new LightLut();
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)) {
arena.free(entry.getIntValue());
needsLutRebuild = true;
endTrackingSection(section);
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() {
return arena.capacity();
}
@ -374,7 +383,7 @@ public class LightStorage {
if (out == INVALID_SECTION) {
out = arena.alloc();
section2ArenaIndex.put(section, out);
needsLutRebuild = true;
beginTrackingSection(section, out);
}
return out;
}
@ -406,8 +415,7 @@ public class LightStorage {
}
public IntArrayList createLut() {
// TODO: incremental lut updates
return LightLut.buildLut(section2ArenaIndex);
return lut.flatten();
}
private enum SectionEdge {

View File

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