mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-26 22:10:18 +00:00
Planner reads memoize in bake
- Hydrology planner height and natural samples memoize columns in the bake like other reads - Batched planner base heights publish into the base plane - Removed the direct-mapped sparse base height memo - Only bounds lattice biome reads stay out of the bake
This commit is contained in:
1 parent
a25233edbc
commit
81f2d2332f
4 files changed
+15
-117
No files matched your search
@@ -4,7 +4,6 @@ import art.arcane.iris.generation.cache.ConcurrentClockCache;
|
|||||||
import art.arcane.iris.generation.stream.ProvisionalSampling;
|
import art.arcane.iris.generation.stream.ProvisionalSampling;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public final class ChunkBakeCache implements BakeCache {
|
public final class ChunkBakeCache implements BakeCache {
|
||||||
private static final ThreadLocal<int[]> SPARSE = ThreadLocal.withInitial(() -> new int[1]);
|
private static final ThreadLocal<int[]> SPARSE = ThreadLocal.withInitial(() -> new int[1]);
|
||||||
@@ -130,15 +129,6 @@ public final class ChunkBakeCache implements BakeCache {
|
|||||||
return planes;
|
return planes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> T sparse(Supplier<T> action) {
|
|
||||||
enterSparse();
|
|
||||||
try {
|
|
||||||
return action.get();
|
|
||||||
} finally {
|
|
||||||
exitSparse();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void enterSparse() {
|
public static void enterSparse() {
|
||||||
SPARSE.get()[0]++;
|
SPARSE.get()[0]++;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<Node> 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) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -25,7 +25,6 @@ import art.arcane.iris.generation.bake.ChunkPlanes;
|
|||||||
import art.arcane.iris.generation.bake.ColumnBake;
|
import art.arcane.iris.generation.bake.ColumnBake;
|
||||||
import art.arcane.iris.generation.bake.GeneratorEvaluations;
|
import art.arcane.iris.generation.bake.GeneratorEvaluations;
|
||||||
import art.arcane.iris.generation.bake.PlaneSource;
|
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.bake.TerrainProgram;
|
||||||
import art.arcane.iris.generation.noise.TerrainBatchScratch;
|
import art.arcane.iris.generation.noise.TerrainBatchScratch;
|
||||||
import art.arcane.iris.generation.simd.SimdSupport;
|
import art.arcane.iris.generation.simd.SimdSupport;
|
||||||
@@ -133,15 +132,14 @@ import java.nio.file.Path;
|
|||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
@Data
|
@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"})
|
@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", "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", "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 {
|
public class IrisComplex implements DataProvider {
|
||||||
private static final NoiseBounds ZERO_NOISE_BOUNDS = new NoiseBounds(0D, 0D);
|
private static final NoiseBounds ZERO_NOISE_BOUNDS = new NoiseBounds(0D, 0D);
|
||||||
private static final AtomicLong lastBoundsFailureLog = new AtomicLong(0L);
|
private static final AtomicLong lastBoundsFailureLog = new AtomicLong(0L);
|
||||||
private static final int GRID_BOUNDS_CACHE_SIZE = 8192;
|
private static final int GRID_BOUNDS_CACHE_SIZE = 8192;
|
||||||
private static final int STUDIO_NOISE_CACHE_SIZE = 32_768;
|
private static final int STUDIO_NOISE_CACHE_SIZE = 32_768;
|
||||||
private static final int TERRAIN_COLUMN_CACHE_SIZE = 524_288;
|
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;
|
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. */
|
/** 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;
|
private static final int SHARED_CORNER_BOUNDS_CAPACITY = 1 << 20;
|
||||||
@@ -176,8 +174,6 @@ public class IrisComplex implements DataProvider {
|
|||||||
@Getter(AccessLevel.NONE)
|
@Getter(AccessLevel.NONE)
|
||||||
private final transient ChunkBakeCache bakeCache;
|
private final transient ChunkBakeCache bakeCache;
|
||||||
@Getter(AccessLevel.NONE)
|
@Getter(AccessLevel.NONE)
|
||||||
private final transient SparseHeightMemo sparseBaseHeights = new SparseHeightMemo(SPARSE_BASE_HEIGHT_CAPACITY);
|
|
||||||
@Getter(AccessLevel.NONE)
|
|
||||||
private final transient BakeRegistry<IrisBiome> biomeRegistry = new BakeRegistry<>();
|
private final transient BakeRegistry<IrisBiome> biomeRegistry = new BakeRegistry<>();
|
||||||
@Getter(AccessLevel.NONE)
|
@Getter(AccessLevel.NONE)
|
||||||
private final transient BakeRegistry<IrisRegion> regionRegistry = new BakeRegistry<>();
|
private final transient BakeRegistry<IrisRegion> regionRegistry = new BakeRegistry<>();
|
||||||
@@ -467,7 +463,7 @@ public class IrisComplex implements DataProvider {
|
|||||||
(x, z) -> getHeight(engine, x, z, engine.getSeedManager().getHeight()),
|
(x, z) -> getHeight(engine, x, z, engine.getSeedManager().getHeight()),
|
||||||
engine.getDimension().getTerrainSamplingStep());
|
engine.getDimension().getTerrainSamplingStep());
|
||||||
baseTerrainHeightStream = new BakedDoubleStream(bakeCache, BakePlane.BASE_HEIGHT,
|
baseTerrainHeightStream = new BakedDoubleStream(bakeCache, BakePlane.BASE_HEIGHT,
|
||||||
ProceduralStream.ofDouble(this::sparseBaseTerrainHeight));
|
ProceduralStream.ofDouble(this::sampleUnblendedNaturalTerrainHeight));
|
||||||
boolean terrain3DEnabled = false;
|
boolean terrain3DEnabled = false;
|
||||||
for (IrisBiome biome : generatorBiomes) {
|
for (IrisBiome biome : generatorBiomes) {
|
||||||
if (biome.getTerrain3D() != null) {
|
if (biome.getTerrain3D() != null) {
|
||||||
@@ -503,9 +499,9 @@ public class IrisComplex implements DataProvider {
|
|||||||
engine.getHeight(),
|
engine.getHeight(),
|
||||||
engine.getDimension(),
|
engine.getDimension(),
|
||||||
data,
|
data,
|
||||||
(x, z, naturalHeight) -> ChunkBakeCache.sparse(() -> sampleHydrologyNatural(engine, x, z, naturalHeight)),
|
(x, z, naturalHeight) -> sampleHydrologyNatural(engine, x, z, naturalHeight),
|
||||||
new HydrologyHeightProvider(),
|
new HydrologyHeightProvider(),
|
||||||
(x, z) -> ChunkBakeCache.sparse(() -> describeNaturalHeight(engine, x, z)),
|
(x, z) -> describeNaturalHeight(engine, x, z),
|
||||||
this::sampleNaturalOcean,
|
this::sampleNaturalOcean,
|
||||||
footprint -> new MantleHydrologyCaveVoxelView(engine, this, footprint),
|
footprint -> new MantleHydrologyCaveVoxelView(engine, this, footprint),
|
||||||
() -> engine.getPlatformHooks().isMainThread(),
|
() -> engine.getPlatformHooks().isMainThread(),
|
||||||
@@ -991,23 +987,6 @@ public class IrisComplex implements DataProvider {
|
|||||||
return true;
|
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) {
|
private void prefetchBaseHeights(int chunkX, int chunkZ, long[] naturalMissing) {
|
||||||
ChunkPlanes.Plane base = bakeCache.plane(BakePlane.BASE_HEIGHT, chunkX, chunkZ);
|
ChunkPlanes.Plane base = bakeCache.plane(BakePlane.BASE_HEIGHT, chunkX, chunkZ);
|
||||||
int originX = chunkX << 4;
|
int originX = chunkX << 4;
|
||||||
@@ -1023,11 +1002,6 @@ public class IrisComplex implements DataProvider {
|
|||||||
if (terrain3D != null && !terrain3D.requiresBaseHeight(x, z)) {
|
if (terrain3D != null && !terrain3D.requiresBaseHeight(x, z)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
double memoized = sparseBaseHeights.get(x, z);
|
|
||||||
if (!Double.isNaN(memoized)) {
|
|
||||||
base.storeDouble(index, memoized);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
compute[index >>> 6] |= 1L << (index & 63);
|
compute[index >>> 6] |= 1L << (index & 63);
|
||||||
any = true;
|
any = true;
|
||||||
}
|
}
|
||||||
@@ -1071,21 +1045,6 @@ public class IrisComplex implements DataProvider {
|
|||||||
return program;
|
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) {
|
private void sampleNaturalBatch(long[] coordinates, int count, double[] heights) {
|
||||||
long[] required = new long[count];
|
long[] required = new long[count];
|
||||||
int requiredCount = 0;
|
int requiredCount = 0;
|
||||||
@@ -1094,17 +1053,20 @@ public class IrisComplex implements DataProvider {
|
|||||||
int z = RiverFootprint.unpackZ(coordinates[index]);
|
int z = RiverFootprint.unpackZ(coordinates[index]);
|
||||||
if (bakedColumn(BakePlane.NATURAL_HEIGHT, x, z)
|
if (bakedColumn(BakePlane.NATURAL_HEIGHT, x, z)
|
||||||
|| terrain3D != null && !terrain3D.requiresBaseHeight(x, z)
|
|| terrain3D != null && !terrain3D.requiresBaseHeight(x, z)
|
||||||
|| bakedColumn(BakePlane.BASE_HEIGHT, x, z)
|
|| bakedColumn(BakePlane.BASE_HEIGHT, x, z)) {
|
||||||
|| !Double.isNaN(sparseBaseHeights.get(x, z))) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
required[requiredCount++] = coordinates[index];
|
required[requiredCount++] = coordinates[index];
|
||||||
}
|
}
|
||||||
if (requiredCount > 0) {
|
if (requiredCount > 0) {
|
||||||
double[] baseHeights = new double[requiredCount];
|
double[] baseHeights = new double[requiredCount];
|
||||||
|
long revision = ProvisionalSampling.revision();
|
||||||
resolveBaseHeightBatch(required, requiredCount, baseHeights);
|
resolveBaseHeightBatch(required, requiredCount, baseHeights);
|
||||||
for (int index = 0; index < requiredCount; index++) {
|
if (bakeCache.memoAllowed()) {
|
||||||
sparseBaseHeights.put(RiverFootprint.unpackX(required[index]), RiverFootprint.unpackZ(required[index]), baseHeights[index]);
|
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++) {
|
for (int index = 0; index < count; index++) {
|
||||||
@@ -1126,12 +1088,7 @@ public class IrisComplex implements DataProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double sample(int blockX, int blockZ) {
|
public double sample(int blockX, int blockZ) {
|
||||||
ChunkBakeCache.enterSparse();
|
return naturalHeightStream.getDouble(blockX, blockZ);
|
||||||
try {
|
|
||||||
return naturalHeightStream.getDouble(blockX, blockZ);
|
|
||||||
} finally {
|
|
||||||
ChunkBakeCache.exitSparse();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1145,12 +1102,7 @@ public class IrisComplex implements DataProvider {
|
|||||||
IrisHydrologyNaturalHeightProvider.super.sampleBatch(coordinates, count, heights);
|
IrisHydrologyNaturalHeightProvider.super.sampleBatch(coordinates, count, heights);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ChunkBakeCache.enterSparse();
|
sampleNaturalBatch(coordinates, count, heights);
|
||||||
try {
|
|
||||||
sampleNaturalBatch(coordinates, count, heights);
|
|
||||||
} finally {
|
|
||||||
ChunkBakeCache.exitSparse();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1160,7 +1112,7 @@ public class IrisComplex implements DataProvider {
|
|||||||
switch (plane) {
|
switch (plane) {
|
||||||
case REGION -> objectPlane(regionSource, chunkX, chunkZ, missing, (Object[]) values);
|
case REGION -> objectPlane(regionSource, chunkX, chunkZ, missing, (Object[]) values);
|
||||||
case BASE_BIOME -> objectPlane(baseBiomeSource, 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_HEIGHT -> naturalPlane(chunkX, chunkZ, missing, (double[]) values);
|
||||||
case NATURAL_TRUE_BIOME -> objectPlane(naturalTrueBiomeSource, chunkX, chunkZ, missing, (Object[]) values);
|
case NATURAL_TRUE_BIOME -> objectPlane(naturalTrueBiomeSource, chunkX, chunkZ, missing, (Object[]) values);
|
||||||
case HEIGHT -> heightPlane(chunkX, chunkZ, missing, (double[]) values);
|
case HEIGHT -> heightPlane(chunkX, chunkZ, missing, (double[]) values);
|
||||||
@@ -3052,7 +3004,6 @@ public class IrisComplex implements DataProvider {
|
|||||||
}
|
}
|
||||||
releaseHydrologyNoiseCaches();
|
releaseHydrologyNoiseCaches();
|
||||||
bakeCache.clear();
|
bakeCache.clear();
|
||||||
sparseBaseHeights.clear();
|
|
||||||
if (transitionDisplacement != null) {
|
if (transitionDisplacement != null) {
|
||||||
transitionDisplacement.clear();
|
transitionDisplacement.clear();
|
||||||
}
|
}
|
||||||
|
|||||||
-2
@@ -4,7 +4,6 @@ import art.arcane.iris.configuration.IrisSettings;
|
|||||||
import art.arcane.iris.world.pregen.PregenPerformanceProfile;
|
import art.arcane.iris.world.pregen.PregenPerformanceProfile;
|
||||||
import art.arcane.iris.generation.bake.ChunkBakeCache;
|
import art.arcane.iris.generation.bake.ChunkBakeCache;
|
||||||
import art.arcane.iris.generation.bake.PlaneSource;
|
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.biome.IrisBiome;
|
||||||
import art.arcane.iris.generation.hydrology.HydrologyPlanner;
|
import art.arcane.iris.generation.hydrology.HydrologyPlanner;
|
||||||
import art.arcane.iris.generation.hydrology.HydrologyPlannerSettings;
|
import art.arcane.iris.generation.hydrology.HydrologyPlannerSettings;
|
||||||
@@ -309,7 +308,6 @@ public class IrisComplexHydrologyNoiseCacheTest {
|
|||||||
complex.setNaturalHeightStream(height);
|
complex.setNaturalHeightStream(height);
|
||||||
complex.setHeightStream(ProceduralStream.ofDouble((x, z) -> 47D));
|
complex.setHeightStream(ProceduralStream.ofDouble((x, z) -> 47D));
|
||||||
setField(complex, "bakeCache", bakes);
|
setField(complex, "bakeCache", bakes);
|
||||||
setField(complex, "sparseBaseHeights", new SparseHeightMemo(16));
|
|
||||||
when(engine.getComplex()).thenReturn(complex);
|
when(engine.getComplex()).thenReturn(complex);
|
||||||
complex.setBaseBiomeStream(biomes);
|
complex.setBaseBiomeStream(biomes);
|
||||||
complex.setRegionStream(regions);
|
complex.setRegionStream(regions);
|
||||||
|
|||||||
Reference in new issue
Block a user