diff --git a/core/src/main/java/art/arcane/iris/generation/bake/ChunkBakeCache.java b/core/src/main/java/art/arcane/iris/generation/bake/ChunkBakeCache.java index 999848025..b1466b5a7 100644 --- a/core/src/main/java/art/arcane/iris/generation/bake/ChunkBakeCache.java +++ b/core/src/main/java/art/arcane/iris/generation/bake/ChunkBakeCache.java @@ -4,7 +4,6 @@ import art.arcane.iris.generation.cache.ConcurrentClockCache; import art.arcane.iris.generation.stream.ProvisionalSampling; import java.util.Objects; -import java.util.function.Supplier; public final class ChunkBakeCache implements BakeCache { private static final ThreadLocal SPARSE = ThreadLocal.withInitial(() -> new int[1]); @@ -130,15 +129,6 @@ public final class ChunkBakeCache implements BakeCache { return planes; } - public static T sparse(Supplier action) { - enterSparse(); - try { - return action.get(); - } finally { - exitSparse(); - } - } - public static void enterSparse() { SPARSE.get()[0]++; } diff --git a/core/src/main/java/art/arcane/iris/generation/bake/SparseHeightMemo.java b/core/src/main/java/art/arcane/iris/generation/bake/SparseHeightMemo.java deleted file mode 100644 index d11d33be4..000000000 --- a/core/src/main/java/art/arcane/iris/generation/bake/SparseHeightMemo.java +++ /dev/null @@ -1,41 +0,0 @@ -package art.arcane.iris.generation.bake; - -import art.arcane.volmlib.util.cache.CacheKey; - -import java.util.concurrent.atomic.AtomicReferenceArray; - -public final class SparseHeightMemo { - private final AtomicReferenceArray nodes; - private final int mask; - - public SparseHeightMemo(int capacity) { - if (capacity <= 0 || Integer.bitCount(capacity) != 1) { - throw new IllegalArgumentException("Sparse height memo capacity must be a power of two"); - } - nodes = new AtomicReferenceArray<>(capacity); - mask = capacity - 1; - } - - public double get(int x, int z) { - long key = ((long) x << 32) ^ (z & 0xffffffffL); - Node node = nodes.getAcquire((int) CacheKey.mix(key) & mask); - return node != null && node.key == key ? node.height : Double.NaN; - } - - public void put(int x, int z, double height) { - if (!Double.isFinite(height)) { - return; - } - long key = ((long) x << 32) ^ (z & 0xffffffffL); - nodes.setRelease((int) CacheKey.mix(key) & mask, new Node(key, height)); - } - - public void clear() { - for (int index = 0; index < nodes.length(); index++) { - nodes.setRelease(index, null); - } - } - - private record Node(long key, double height) { - } -} diff --git a/core/src/main/java/art/arcane/iris/generation/runtime/IrisComplex.java b/core/src/main/java/art/arcane/iris/generation/runtime/IrisComplex.java index 61c918c25..296ae4ee6 100644 --- a/core/src/main/java/art/arcane/iris/generation/runtime/IrisComplex.java +++ b/core/src/main/java/art/arcane/iris/generation/runtime/IrisComplex.java @@ -25,7 +25,6 @@ import art.arcane.iris.generation.bake.ChunkPlanes; import art.arcane.iris.generation.bake.ColumnBake; import art.arcane.iris.generation.bake.GeneratorEvaluations; import art.arcane.iris.generation.bake.PlaneSource; -import art.arcane.iris.generation.bake.SparseHeightMemo; import art.arcane.iris.generation.bake.TerrainProgram; import art.arcane.iris.generation.noise.TerrainBatchScratch; import art.arcane.iris.generation.simd.SimdSupport; @@ -133,15 +132,14 @@ import java.nio.file.Path; import java.util.function.BiFunction; @Data -@EqualsAndHashCode(exclude = {"data", "gridBoundsCache", "sharedCornerBounds", "terrainProgram", "bakeCache", "sparseBaseHeights", "biomeRegistry", "regionRegistry", "blockRegistry", "regionSource", "baseBiomeSource", "caveSource", "rockSource", "fluidSource", "childStyleSeed", "inferredByType", "naturalHeightSource", "naturalTrueBiomeSource", "trueBiomeSource", "frozenInterpolators", "frozenGenerators", "inferredBiomeStreams", "hydrologyRuntime", "imageMapRuntime", "terrainEngine", "terrain3D", "hydrologyBanks3D"}) -@ToString(exclude = {"data", "gridBoundsCache", "sharedCornerBounds", "terrainProgram", "bakeCache", "sparseBaseHeights", "biomeRegistry", "regionRegistry", "blockRegistry", "regionSource", "baseBiomeSource", "caveSource", "rockSource", "fluidSource", "childStyleSeed", "inferredByType", "naturalHeightSource", "naturalTrueBiomeSource", "trueBiomeSource", "frozenInterpolators", "frozenGenerators", "inferredBiomeStreams", "hydrologyRuntime", "imageMapRuntime", "terrainEngine", "terrain3D", "hydrologyBanks3D"}) +@EqualsAndHashCode(exclude = {"data", "gridBoundsCache", "sharedCornerBounds", "terrainProgram", "bakeCache", "biomeRegistry", "regionRegistry", "blockRegistry", "regionSource", "baseBiomeSource", "caveSource", "rockSource", "fluidSource", "childStyleSeed", "inferredByType", "naturalHeightSource", "naturalTrueBiomeSource", "trueBiomeSource", "frozenInterpolators", "frozenGenerators", "inferredBiomeStreams", "hydrologyRuntime", "imageMapRuntime", "terrainEngine", "terrain3D", "hydrologyBanks3D"}) +@ToString(exclude = {"data", "gridBoundsCache", "sharedCornerBounds", "terrainProgram", "bakeCache", "biomeRegistry", "regionRegistry", "blockRegistry", "regionSource", "baseBiomeSource", "caveSource", "rockSource", "fluidSource", "childStyleSeed", "inferredByType", "naturalHeightSource", "naturalTrueBiomeSource", "trueBiomeSource", "frozenInterpolators", "frozenGenerators", "inferredBiomeStreams", "hydrologyRuntime", "imageMapRuntime", "terrainEngine", "terrain3D", "hydrologyBanks3D"}) public class IrisComplex implements DataProvider { private static final NoiseBounds ZERO_NOISE_BOUNDS = new NoiseBounds(0D, 0D); private static final AtomicLong lastBoundsFailureLog = new AtomicLong(0L); private static final int GRID_BOUNDS_CACHE_SIZE = 8192; private static final int STUDIO_NOISE_CACHE_SIZE = 32_768; private static final int TERRAIN_COLUMN_CACHE_SIZE = 524_288; - private static final int SPARSE_BASE_HEIGHT_CAPACITY = 1 << 17; private static final int BAKE_CHUNKS_PER_NOISE_CHUNK = 8; /** One million corners: about 20 MB, roughly a 4000 by 4000 block area at the 4-block grid. */ private static final int SHARED_CORNER_BOUNDS_CAPACITY = 1 << 20; @@ -176,8 +174,6 @@ public class IrisComplex implements DataProvider { @Getter(AccessLevel.NONE) private final transient ChunkBakeCache bakeCache; @Getter(AccessLevel.NONE) - private final transient SparseHeightMemo sparseBaseHeights = new SparseHeightMemo(SPARSE_BASE_HEIGHT_CAPACITY); - @Getter(AccessLevel.NONE) private final transient BakeRegistry biomeRegistry = new BakeRegistry<>(); @Getter(AccessLevel.NONE) private final transient BakeRegistry regionRegistry = new BakeRegistry<>(); @@ -467,7 +463,7 @@ public class IrisComplex implements DataProvider { (x, z) -> getHeight(engine, x, z, engine.getSeedManager().getHeight()), engine.getDimension().getTerrainSamplingStep()); baseTerrainHeightStream = new BakedDoubleStream(bakeCache, BakePlane.BASE_HEIGHT, - ProceduralStream.ofDouble(this::sparseBaseTerrainHeight)); + ProceduralStream.ofDouble(this::sampleUnblendedNaturalTerrainHeight)); boolean terrain3DEnabled = false; for (IrisBiome biome : generatorBiomes) { if (biome.getTerrain3D() != null) { @@ -503,9 +499,9 @@ public class IrisComplex implements DataProvider { engine.getHeight(), engine.getDimension(), data, - (x, z, naturalHeight) -> ChunkBakeCache.sparse(() -> sampleHydrologyNatural(engine, x, z, naturalHeight)), + (x, z, naturalHeight) -> sampleHydrologyNatural(engine, x, z, naturalHeight), new HydrologyHeightProvider(), - (x, z) -> ChunkBakeCache.sparse(() -> describeNaturalHeight(engine, x, z)), + (x, z) -> describeNaturalHeight(engine, x, z), this::sampleNaturalOcean, footprint -> new MantleHydrologyCaveVoxelView(engine, this, footprint), () -> engine.getPlatformHooks().isMainThread(), @@ -991,23 +987,6 @@ public class IrisComplex implements DataProvider { return true; } - private void basePlane(int chunkX, int chunkZ, long[] missing, double[] values) { - int originX = chunkX << 4; - int originZ = chunkZ << 4; - long[] compute = missing.clone(); - for (int index = 0; index < 256; index++) { - if ((compute[index >>> 6] & (1L << (index & 63))) == 0L) { - continue; - } - double memoized = sparseBaseHeights.get(originX + (index & 15), originZ + (index >> 4)); - if (!Double.isNaN(memoized)) { - values[index] = memoized; - compute[index >>> 6] &= ~(1L << (index & 63)); - } - } - bakeBaseHeights(chunkX, chunkZ, compute, values); - } - private void prefetchBaseHeights(int chunkX, int chunkZ, long[] naturalMissing) { ChunkPlanes.Plane base = bakeCache.plane(BakePlane.BASE_HEIGHT, chunkX, chunkZ); int originX = chunkX << 4; @@ -1023,11 +1002,6 @@ public class IrisComplex implements DataProvider { if (terrain3D != null && !terrain3D.requiresBaseHeight(x, z)) { continue; } - double memoized = sparseBaseHeights.get(x, z); - if (!Double.isNaN(memoized)) { - base.storeDouble(index, memoized); - continue; - } compute[index >>> 6] |= 1L << (index & 63); any = true; } @@ -1071,21 +1045,6 @@ public class IrisComplex implements DataProvider { return program; } - private double sparseBaseTerrainHeight(double x, double z) { - int blockX = (int) x; - int blockZ = (int) z; - if (x != blockX || z != blockZ) { - return sampleUnblendedNaturalTerrainHeight(x, z); - } - double memoized = sparseBaseHeights.get(blockX, blockZ); - if (!Double.isNaN(memoized)) { - return memoized; - } - double height = sampleUnblendedNaturalTerrainHeight(x, z); - sparseBaseHeights.put(blockX, blockZ, height); - return height; - } - private void sampleNaturalBatch(long[] coordinates, int count, double[] heights) { long[] required = new long[count]; int requiredCount = 0; @@ -1094,17 +1053,20 @@ public class IrisComplex implements DataProvider { int z = RiverFootprint.unpackZ(coordinates[index]); if (bakedColumn(BakePlane.NATURAL_HEIGHT, x, z) || terrain3D != null && !terrain3D.requiresBaseHeight(x, z) - || bakedColumn(BakePlane.BASE_HEIGHT, x, z) - || !Double.isNaN(sparseBaseHeights.get(x, z))) { + || bakedColumn(BakePlane.BASE_HEIGHT, x, z)) { continue; } required[requiredCount++] = coordinates[index]; } if (requiredCount > 0) { double[] baseHeights = new double[requiredCount]; + long revision = ProvisionalSampling.revision(); resolveBaseHeightBatch(required, requiredCount, baseHeights); - for (int index = 0; index < requiredCount; index++) { - sparseBaseHeights.put(RiverFootprint.unpackX(required[index]), RiverFootprint.unpackZ(required[index]), baseHeights[index]); + if (bakeCache.memoAllowed()) { + for (int index = 0; index < requiredCount; index++) { + bakeCache.storeDouble(BakePlane.BASE_HEIGHT, RiverFootprint.unpackX(required[index]), + RiverFootprint.unpackZ(required[index]), baseHeights[index], revision); + } } } for (int index = 0; index < count; index++) { @@ -1126,12 +1088,7 @@ public class IrisComplex implements DataProvider { @Override public double sample(int blockX, int blockZ) { - ChunkBakeCache.enterSparse(); - try { - return naturalHeightStream.getDouble(blockX, blockZ); - } finally { - ChunkBakeCache.exitSparse(); - } + return naturalHeightStream.getDouble(blockX, blockZ); } @Override @@ -1145,12 +1102,7 @@ public class IrisComplex implements DataProvider { IrisHydrologyNaturalHeightProvider.super.sampleBatch(coordinates, count, heights); return; } - ChunkBakeCache.enterSparse(); - try { - sampleNaturalBatch(coordinates, count, heights); - } finally { - ChunkBakeCache.exitSparse(); - } + sampleNaturalBatch(coordinates, count, heights); } } @@ -1160,7 +1112,7 @@ public class IrisComplex implements DataProvider { switch (plane) { case REGION -> objectPlane(regionSource, chunkX, chunkZ, missing, (Object[]) values); case BASE_BIOME -> objectPlane(baseBiomeSource, chunkX, chunkZ, missing, (Object[]) values); - case BASE_HEIGHT -> basePlane(chunkX, chunkZ, missing, (double[]) values); + case BASE_HEIGHT -> bakeBaseHeights(chunkX, chunkZ, missing, (double[]) values); case NATURAL_HEIGHT -> naturalPlane(chunkX, chunkZ, missing, (double[]) values); case NATURAL_TRUE_BIOME -> objectPlane(naturalTrueBiomeSource, chunkX, chunkZ, missing, (Object[]) values); case HEIGHT -> heightPlane(chunkX, chunkZ, missing, (double[]) values); @@ -3052,7 +3004,6 @@ public class IrisComplex implements DataProvider { } releaseHydrologyNoiseCaches(); bakeCache.clear(); - sparseBaseHeights.clear(); if (transitionDisplacement != null) { transitionDisplacement.clear(); } diff --git a/core/src/test/java/art/arcane/iris/generation/runtime/IrisComplexHydrologyNoiseCacheTest.java b/core/src/test/java/art/arcane/iris/generation/runtime/IrisComplexHydrologyNoiseCacheTest.java index 39576e976..52e143e0b 100644 --- a/core/src/test/java/art/arcane/iris/generation/runtime/IrisComplexHydrologyNoiseCacheTest.java +++ b/core/src/test/java/art/arcane/iris/generation/runtime/IrisComplexHydrologyNoiseCacheTest.java @@ -4,7 +4,6 @@ import art.arcane.iris.configuration.IrisSettings; import art.arcane.iris.world.pregen.PregenPerformanceProfile; import art.arcane.iris.generation.bake.ChunkBakeCache; import art.arcane.iris.generation.bake.PlaneSource; -import art.arcane.iris.generation.bake.SparseHeightMemo; import art.arcane.iris.generation.biome.IrisBiome; import art.arcane.iris.generation.hydrology.HydrologyPlanner; import art.arcane.iris.generation.hydrology.HydrologyPlannerSettings; @@ -309,7 +308,6 @@ public class IrisComplexHydrologyNoiseCacheTest { complex.setNaturalHeightStream(height); complex.setHeightStream(ProceduralStream.ofDouble((x, z) -> 47D)); setField(complex, "bakeCache", bakes); - setField(complex, "sparseBaseHeights", new SparseHeightMemo(16)); when(engine.getComplex()).thenReturn(complex); complex.setBaseBiomeStream(biomes); complex.setRegionStream(regions);