Cached more water rasters

- Stored raster tiers fluids and feature ids as per-chunk palettes
- Derived cave ceilings from terrain heights and water heads
- Raised the water raster cache from 1024 to 8192 chunks
This commit is contained in:
Brian Neumann-Fopiano committed 2026-10-30 20:09:02 -04:00
1 parent c700138b66
commit a879985875
3 files changed
+97 -34

No files matched your search

@@ -56,7 +56,7 @@ import java.util.function.Predicate;
import java.util.function.Supplier;
public final class HydrologyRuntime {
private static final int RASTER_CAPACITY = 1024;
private static final int RASTER_CAPACITY = 8192;
private static final int KEEP_SCOPES = 2;
private static final int CHUNKS_PER_WINDOW = WindowKey.SIZE >> 4;
public static final int MAX_PREGENERATION_REGIONS = 4096;
@@ -2,6 +2,8 @@ package art.arcane.iris.generation.hydrology.raster;
import art.arcane.iris.generation.hydrology.water.HydrologyTier;
import java.util.Arrays;
public final class ChunkWaterRaster {
public static final int MAX_SHORE_DISTANCE = 32;
public static final ChunkWaterRaster PROVISIONAL = new ChunkWaterRaster(true);
@@ -13,10 +15,13 @@ public final class ChunkWaterRaster {
private final int[] terrain;
private final int[] head;
private final byte[] role;
private final HydrologyTier[] tier;
private final long[] featureId;
private final String[] fluid;
private final int[] caveCeiling;
private final byte[] tier;
private final HydrologyTier[] tiers;
private final byte[] feature;
private final long[] features;
private final byte[] fluid;
private final String[] fluids;
private final boolean caveMask;
private final byte[] shoreDistance;
private ChunkWaterRaster(boolean provisional) {
@@ -25,26 +30,32 @@ public final class ChunkWaterRaster {
this.head = null;
this.role = null;
this.tier = null;
this.featureId = null;
this.tiers = null;
this.feature = null;
this.features = null;
this.fluid = null;
this.caveCeiling = null;
this.fluids = null;
this.caveMask = false;
this.shoreDistance = null;
}
ChunkWaterRaster(int[] terrain, int[] head, byte[] role, HydrologyTier[] tier, long[] featureId, String[] fluid, int[] caveCeiling,
byte[] shoreDistance) {
if (terrain.length != COLUMNS || head.length != COLUMNS || role.length != COLUMNS || tier.length != COLUMNS
|| featureId.length != COLUMNS || fluid.length != COLUMNS || caveCeiling.length != COLUMNS || shoreDistance.length != COLUMNS) {
ChunkWaterRaster(int[] terrain, int[] head, byte[] role, Palette<HydrologyTier> tiers, LongPalette features, Palette<String> fluids,
boolean caveMask, byte[] shoreDistance) {
if (terrain.length != COLUMNS || head.length != COLUMNS || role.length != COLUMNS || tiers.indices.length != COLUMNS
|| features.indices.length != COLUMNS || fluids.indices.length != COLUMNS || shoreDistance.length != COLUMNS) {
throw new IllegalArgumentException("A chunk water raster needs " + COLUMNS + " entries per array");
}
this.provisional = false;
this.terrain = terrain;
this.head = head;
this.role = role;
this.tier = tier;
this.featureId = featureId;
this.fluid = fluid;
this.caveCeiling = caveCeiling;
this.tier = tiers.indices;
this.tiers = tiers.values();
this.feature = features.indices;
this.features = features.values();
this.fluid = fluids.indices;
this.fluids = fluids.values();
this.caveMask = caveMask;
this.shoreDistance = shoreDistance;
}
@@ -68,22 +79,22 @@ public final class ChunkWaterRaster {
public HydrologyTier tier(int lx, int lz) {
int index = index(lx, lz);
return tier == null ? null : tier[index];
return tier == null ? null : tiers[tier[index] & 0xFF];
}
public long featureId(int lx, int lz) {
int index = index(lx, lz);
return featureId == null ? 0L : featureId[index];
return feature == null ? 0L : features[feature[index] & 0xFF];
}
public String fluid(int lx, int lz) {
int index = index(lx, lz);
return fluid == null ? null : fluid[index];
return fluid == null ? null : fluids[fluid[index] & 0xFF];
}
public int caveCeiling(int lx, int lz) {
int index = index(lx, lz);
return caveCeiling == null ? Integer.MAX_VALUE : caveCeiling[index];
return caveMask && head[index] != Integer.MIN_VALUE ? terrain[index] - 1 : Integer.MAX_VALUE;
}
public int shoreDistance(int lx, int lz) {
@@ -105,4 +116,53 @@ public final class ChunkWaterRaster {
}
return lz << 4 | lx;
}
static final class Palette<T> {
private final byte[] indices = new byte[COLUMNS];
private final T[] seen;
private int size;
Palette(T[] seen) {
if (seen.length != COLUMNS) {
throw new IllegalArgumentException("A raster palette needs room for " + COLUMNS + " values");
}
this.seen = seen;
}
void set(int column, T value) {
int found = 0;
while (found < size && seen[found] != value) {
found++;
}
if (found == size) {
seen[size++] = value;
}
indices[column] = (byte) found;
}
private T[] values() {
return Arrays.copyOf(seen, size);
}
}
static final class LongPalette {
private final byte[] indices = new byte[COLUMNS];
private final long[] seen = new long[COLUMNS];
private int size;
void set(int column, long value) {
int found = 0;
while (found < size && seen[found] != value) {
found++;
}
if (found == size) {
seen[size++] = value;
}
indices[column] = (byte) found;
}
private long[] values() {
return Arrays.copyOf(seen, size);
}
}
}
@@ -385,34 +385,37 @@ final class RasterComposer {
private ChunkWaterRaster pack() {
int columns = ChunkWaterRaster.COLUMNS;
boolean affected = false;
for (int lz = 0; lz < CHUNK && !affected; lz++) {
for (int lx = 0; lx < CHUNK && !affected; lx++) {
int cell = (lz + 1) * GRID + lx + 1;
affected = role[cell] != WaterRole.NONE || terrain[cell] != natural[cell] || shore(cell) < ChunkWaterRaster.MAX_SHORE_DISTANCE;
}
}
if (!affected) {
return ChunkWaterRaster.EMPTY;
}
int[] terrainOut = new int[columns];
int[] headOut = new int[columns];
byte[] roleOut = new byte[columns];
HydrologyTier[] tierOut = new HydrologyTier[columns];
long[] featureOut = new long[columns];
String[] fluidOut = new String[columns];
int[] caveOut = new int[columns];
ChunkWaterRaster.Palette<HydrologyTier> tierOut = new ChunkWaterRaster.Palette<>(new HydrologyTier[columns]);
ChunkWaterRaster.LongPalette featureOut = new ChunkWaterRaster.LongPalette();
ChunkWaterRaster.Palette<String> fluidOut = new ChunkWaterRaster.Palette<>(new String[columns]);
byte[] shoreOut = new byte[columns];
boolean caveMask = settings.deformation().caveMask();
boolean affected = false;
for (int lz = 0; lz < CHUNK; lz++) {
for (int lx = 0; lx < CHUNK; lx++) {
int cell = (lz + 1) * GRID + lx + 1;
int index = lz << 4 | lx;
int shore = shore(cell);
terrainOut[index] = terrain[cell];
headOut[index] = head[cell];
roleOut[index] = (byte) role[cell].ordinal();
tierOut[index] = tier[cell];
featureOut[index] = feature[cell];
fluidOut[index] = fluid[cell];
caveOut[index] = caveMask && head[cell] != Integer.MIN_VALUE ? terrain[cell] - 1 : Integer.MAX_VALUE;
shoreOut[index] = (byte) shore;
affected |= role[cell] != WaterRole.NONE || terrain[cell] != natural[cell] || shore < ChunkWaterRaster.MAX_SHORE_DISTANCE;
tierOut.set(index, tier[cell]);
featureOut.set(index, feature[cell]);
fluidOut.set(index, fluid[cell]);
shoreOut[index] = (byte) shore(cell);
}
}
return affected ? new ChunkWaterRaster(terrainOut, headOut, roleOut, tierOut, featureOut, fluidOut, caveOut, shoreOut)
: ChunkWaterRaster.EMPTY;
return new ChunkWaterRaster(terrainOut, headOut, roleOut, tierOut, featureOut, fluidOut, settings.deformation().caveMask(), shoreOut);
}
private int shore(int cell) {