diff --git a/core/src/main/java/art/arcane/iris/generation/hydrology/engine/HydrologyRuntime.java b/core/src/main/java/art/arcane/iris/generation/hydrology/engine/HydrologyRuntime.java index 2f525c3d9..878113a13 100644 --- a/core/src/main/java/art/arcane/iris/generation/hydrology/engine/HydrologyRuntime.java +++ b/core/src/main/java/art/arcane/iris/generation/hydrology/engine/HydrologyRuntime.java @@ -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; diff --git a/core/src/main/java/art/arcane/iris/generation/hydrology/raster/ChunkWaterRaster.java b/core/src/main/java/art/arcane/iris/generation/hydrology/raster/ChunkWaterRaster.java index 524bcef74..02c963754 100644 --- a/core/src/main/java/art/arcane/iris/generation/hydrology/raster/ChunkWaterRaster.java +++ b/core/src/main/java/art/arcane/iris/generation/hydrology/raster/ChunkWaterRaster.java @@ -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 tiers, LongPalette features, Palette 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 { + 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); + } + } } diff --git a/core/src/main/java/art/arcane/iris/generation/hydrology/raster/RasterComposer.java b/core/src/main/java/art/arcane/iris/generation/hydrology/raster/RasterComposer.java index 36dec7012..bb377ec85 100644 --- a/core/src/main/java/art/arcane/iris/generation/hydrology/raster/RasterComposer.java +++ b/core/src/main/java/art/arcane/iris/generation/hydrology/raster/RasterComposer.java @@ -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 tierOut = new ChunkWaterRaster.Palette<>(new HydrologyTier[columns]); + ChunkWaterRaster.LongPalette featureOut = new ChunkWaterRaster.LongPalette(); + ChunkWaterRaster.Palette 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) {