mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-02-03 16:54:57 +01:00
A whole lut of refactors
- Replace monolithic lut building function with a class representing a layer of the lut - Actually need 2 classes because int[] and Object[] aren't trivial to make a type parameter, and we don't really want to be boxing ints here - No longer need sorted inputs - Should fix index out of bounds crash caused by reserving space for the wrong index layer - This will make it much easier to change the coordinate ordering scheme
This commit is contained in:
parent
e6fecc60b5
commit
376ac76ac2
1 changed files with 138 additions and 106 deletions
|
@ -1,32 +1,34 @@
|
||||||
package dev.engine_room.flywheel.backend.engine;
|
package dev.engine_room.flywheel.backend.engine;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||||
import it.unimi.dsi.fastutil.ints.IntObjectImmutablePair;
|
|
||||||
import it.unimi.dsi.fastutil.ints.IntObjectPair;
|
|
||||||
import it.unimi.dsi.fastutil.longs.Long2IntMap;
|
import it.unimi.dsi.fastutil.longs.Long2IntMap;
|
||||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
|
||||||
import it.unimi.dsi.fastutil.longs.LongComparator;
|
|
||||||
import it.unimi.dsi.fastutil.objects.ReferenceArrayList;
|
|
||||||
import net.minecraft.core.SectionPos;
|
import net.minecraft.core.SectionPos;
|
||||||
|
|
||||||
public final class LightLut {
|
public final class LightLut {
|
||||||
private static final LongComparator SECTION_X_THEN_Y_THEN_Z = (long a, long b) -> {
|
private final Layer<Layer<IntLayer>> indices = new Layer<>();
|
||||||
final var xComp = Integer.compare(SectionPos.x(a), SectionPos.x(b));
|
|
||||||
if (xComp != 0) {
|
|
||||||
return xComp;
|
|
||||||
}
|
|
||||||
var yComp = Integer.compare(SectionPos.y(a), SectionPos.y(b));
|
|
||||||
if (yComp != 0) {
|
|
||||||
return yComp;
|
|
||||||
}
|
|
||||||
return Integer.compare(SectionPos.z(a), SectionPos.z(b));
|
|
||||||
};
|
|
||||||
|
|
||||||
private LightLut() {
|
private LightLut() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void add(long position, int index) {
|
||||||
|
final var x = SectionPos.x(position);
|
||||||
|
final var y = SectionPos.y(position);
|
||||||
|
final var z = SectionPos.z(position);
|
||||||
|
|
||||||
|
indices.computeIfAbsent(x, Layer::new)
|
||||||
|
.computeIfAbsent(y, IntLayer::new)
|
||||||
|
.set(z, index + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IntArrayList toLut() {
|
||||||
|
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.
|
// Massive kudos to RogueLogix for figuring out this LUT scheme.
|
||||||
// TODO: switch to y x z or x z y ordering
|
// TODO: switch to y x z or x z y ordering
|
||||||
// DATA LAYOUT
|
// DATA LAYOUT
|
||||||
|
@ -38,105 +40,135 @@ public final class LightLut {
|
||||||
if (sectionIndicesMaps.isEmpty()) {
|
if (sectionIndicesMaps.isEmpty()) {
|
||||||
return new IntArrayList();
|
return new IntArrayList();
|
||||||
}
|
}
|
||||||
final var positions = sortedKeys(sectionIndicesMaps);
|
|
||||||
final var baseX = SectionPos.x(positions.getLong(0));
|
|
||||||
|
|
||||||
return buildLut(baseX, buildIndices(sectionIndicesMaps, positions, baseX));
|
var out = new LightLut();
|
||||||
|
|
||||||
|
sectionIndicesMaps.forEach(out::add);
|
||||||
|
|
||||||
|
return out.toLut();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ReferenceArrayList<IntObjectPair<ReferenceArrayList<IntArrayList>>> buildIndices(Long2IntMap sectionIndicesMaps, LongArrayList positions, int baseX) {
|
private static final class Layer<T> {
|
||||||
final var indices = new ReferenceArrayList<IntObjectPair<ReferenceArrayList<IntArrayList>>>();
|
private boolean hasBase = false;
|
||||||
for (long position : positions) {
|
private int base = 0;
|
||||||
final var x = SectionPos.x(position);
|
private Object[] nextLayer = new Object[0];
|
||||||
final var y = SectionPos.y(position);
|
|
||||||
final var z = SectionPos.z(position);
|
|
||||||
|
|
||||||
final var xIndex = x - baseX;
|
public void fillLut(IntArrayList lut, BiConsumer<T, IntArrayList> inner) {
|
||||||
if (indices.size() <= xIndex) {
|
lut.add(base);
|
||||||
indices.ensureCapacity(xIndex + 1);
|
lut.add(nextLayer.length);
|
||||||
indices.size(xIndex + 1);
|
|
||||||
}
|
|
||||||
var yLookup = indices.get(xIndex);
|
|
||||||
if (yLookup == null) {
|
|
||||||
//noinspection SuspiciousNameCombination
|
|
||||||
yLookup = new IntObjectImmutablePair<>(y, new ReferenceArrayList<>());
|
|
||||||
indices.set(xIndex, yLookup);
|
|
||||||
}
|
|
||||||
|
|
||||||
final var yIndices = yLookup.right();
|
int innerIndexBase = lut.size();
|
||||||
final var yIndex = y - yLookup.leftInt();
|
|
||||||
if (yIndices.size() <= yIndex) {
|
|
||||||
yIndices.ensureCapacity(yIndex + 1);
|
|
||||||
yIndices.size(yIndex + 1);
|
|
||||||
}
|
|
||||||
var zLookup = yIndices.get(yIndex);
|
|
||||||
if (zLookup == null) {
|
|
||||||
zLookup = new IntArrayList();
|
|
||||||
zLookup.add(z);
|
|
||||||
zLookup.add(0); // this value will be filled in later
|
|
||||||
yIndices.set(yIndex, zLookup);
|
|
||||||
}
|
|
||||||
|
|
||||||
final var zIndex = z - zLookup.getInt(0);
|
// Reserve space for the inner indices...
|
||||||
if ((zLookup.size() - 2) <= zIndex) {
|
lut.size(innerIndexBase + nextLayer.length);
|
||||||
zLookup.ensureCapacity(zIndex + 3);
|
|
||||||
zLookup.size(zIndex + 3);
|
|
||||||
}
|
|
||||||
// Add 1 to the actual index so that 0 indicates a missing section.
|
|
||||||
zLookup.set(zIndex + 2, sectionIndicesMaps.get(position) + 1);
|
|
||||||
}
|
|
||||||
return indices;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static @NotNull LongArrayList sortedKeys(Long2IntMap sectionIndicesMaps) {
|
for (int i = 0; i < nextLayer.length; i++) {
|
||||||
final var out = new LongArrayList(sectionIndicesMaps.keySet());
|
final var innerIndices = (T) nextLayer[i];
|
||||||
out.unstableSort(SECTION_X_THEN_Y_THEN_Z);
|
if (innerIndices == null) {
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IntArrayList buildLut(int baseX, ReferenceArrayList<IntObjectPair<ReferenceArrayList<IntArrayList>>> indices) {
|
|
||||||
final var out = new IntArrayList();
|
|
||||||
out.add(baseX);
|
|
||||||
out.add(indices.size());
|
|
||||||
for (int i = 0; i < indices.size(); i++) {
|
|
||||||
out.add(0);
|
|
||||||
}
|
|
||||||
for (int x = 0; x < indices.size(); x++) {
|
|
||||||
final var yLookup = indices.get(x);
|
|
||||||
if (yLookup == null) {
|
|
||||||
out.set(x + 2, 0);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// ensure that the base position and size dont cross a (64 byte) cache line
|
|
||||||
if ((out.size() & 0xF) == 0xF) {
|
|
||||||
out.add(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
final var baseYIndex = out.size();
|
|
||||||
out.set(x + 2, baseYIndex);
|
|
||||||
|
|
||||||
final var yIndices = yLookup.right();
|
|
||||||
out.add(yLookup.leftInt());
|
|
||||||
out.add(yIndices.size());
|
|
||||||
for (int i = 0; i < indices.size(); i++) {
|
|
||||||
out.add(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int y = 0; y < yIndices.size(); y++) {
|
|
||||||
final var zLookup = yIndices.get(y);
|
|
||||||
if (zLookup == null) {
|
|
||||||
out.set(baseYIndex + y + 2, 0);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// ensure that the base position and size dont cross a (64 byte) cache line
|
|
||||||
if ((out.size() & 0xF) == 0xF) {
|
int layerPosition = lut.size();
|
||||||
out.add(0);
|
|
||||||
}
|
// ...so we can write in their actual positions later.
|
||||||
out.set(baseYIndex + y + 2, out.size());
|
lut.set(innerIndexBase + i, layerPosition);
|
||||||
zLookup.set(1, zLookup.size() - 2);
|
|
||||||
out.addAll(zLookup);
|
// Append the next layer to the lut.
|
||||||
|
inner.accept(innerIndices, lut);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return out;
|
|
||||||
|
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.
|
||||||
|
base = i;
|
||||||
|
hasBase = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < base) {
|
||||||
|
rebase(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
final var offset = i - base;
|
||||||
|
|
||||||
|
if (offset >= nextLayer.length) {
|
||||||
|
resize(offset + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var out = nextLayer[offset];
|
||||||
|
|
||||||
|
if (out == null) {
|
||||||
|
out = ifAbsent.get();
|
||||||
|
nextLayer[offset] = out;
|
||||||
|
}
|
||||||
|
return (T) out;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resize(int length) {
|
||||||
|
final var newIndices = new Object[length];
|
||||||
|
System.arraycopy(nextLayer, 0, newIndices, 0, nextLayer.length);
|
||||||
|
nextLayer = newIndices;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void rebase(int newBase) {
|
||||||
|
final var growth = base - newBase;
|
||||||
|
|
||||||
|
final var newIndices = new Object[nextLayer.length + growth];
|
||||||
|
// Shift the existing elements to the end of the new array to maintain their offset with the new base.
|
||||||
|
System.arraycopy(nextLayer, 0, newIndices, growth, nextLayer.length);
|
||||||
|
|
||||||
|
nextLayer = newIndices;
|
||||||
|
base = newBase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class IntLayer {
|
||||||
|
private boolean hasBase = false;
|
||||||
|
private int base = 0;
|
||||||
|
private int[] indices = new int[0];
|
||||||
|
|
||||||
|
public void fillLut(IntArrayList lut) {
|
||||||
|
lut.add(base);
|
||||||
|
lut.add(indices.length);
|
||||||
|
|
||||||
|
for (int index : indices) {
|
||||||
|
lut.add(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(int i, int index) {
|
||||||
|
if (!hasBase) {
|
||||||
|
base = i;
|
||||||
|
hasBase = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < base) {
|
||||||
|
rebase(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
final var offset = i - base;
|
||||||
|
|
||||||
|
if (offset >= indices.length) {
|
||||||
|
resize(offset + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
indices[offset] = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resize(int length) {
|
||||||
|
final var newIndices = new int[length];
|
||||||
|
System.arraycopy(indices, 0, newIndices, 0, indices.length);
|
||||||
|
indices = newIndices;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void rebase(int newBase) {
|
||||||
|
final var growth = base - newBase;
|
||||||
|
|
||||||
|
final var newIndices = new int[indices.length + growth];
|
||||||
|
System.arraycopy(indices, 0, newIndices, growth, indices.length);
|
||||||
|
|
||||||
|
indices = newIndices;
|
||||||
|
base = newBase;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue