Chunk-scoped cave lookups

- Subterrain cells for carve and decoration resolve through a per-chunk sampler with a column height hull instead of per-block engine dispatch
- IrisEngine subterrain lookups read the thread frame and runtime once and memoize the dimension gate, removing the per-call disabled-component list scan
- Unrouted workers resolve cave biomes through the captured carving snapshot and column cache with subterrain biomes checked first
- Captured cave biome lookups bypass the per-voxel hash cache
- Internal carve walls come from word-level column masks instead of hashed neighbor puts, painted after cheap solidity and surface checks
- Floor and ceiling layers are memoized per column when the biome layers do not depend on the zone height
- Zone scanning walks carved runs, the decoration scan memoizes block solidity and reuses the previous block, and waterlogging normalization skips snapshots without hydrology
- Boundary walls read own cells straight from the snapshot; the unused snapshot face builder and face accessors are removed
This commit is contained in:
Brian Neumann-Fopiano committed 2026-10-22 06:42:35 -04:00
1 parent af90765a92
commit 0bdb022f56
16 files changed
+790 -197

No files matched your search

@@ -57,10 +57,20 @@ public class CaveTerrainSnapshotTest {
for (TerrainMatterView.Face face : TerrainMatterView.Face.values()) {
for (int height : new int[]{0, 1, 37, SECTIONS * 16, SECTIONS * 16 + 9}) {
MatterCavern[] expected = TerrainMatterView.getComposedFace(chunk, face, height);
MatterCavern[] actual = snapshot.composedFace(face, height);
assertEquals(expected.length, actual.length);
assertEquals(height * 16, expected.length);
for (int index = 0; index < expected.length; index++) {
assertSame(face + " " + index, expected[index], actual[index]);
int offset = index & 15;
int x = switch (face) {
case WEST -> 0;
case EAST -> 15;
case NORTH, SOUTH -> offset;
};
int z = switch (face) {
case NORTH -> 0;
case SOUTH -> 15;
case WEST, EAST -> offset;
};
assertSame(face + " " + index, expected[index], snapshot.composedCavern(x, index >> 4, z));
}
}
}
@@ -9,6 +9,7 @@ import art.arcane.iris.generation.runtime.Engine;
import art.arcane.iris.generation.runtime.EnginePlatformHooks;
import art.arcane.iris.generation.runtime.IrisComplex;
import art.arcane.iris.generation.runtime.IrisEngine;
import art.arcane.iris.generation.runtime.SubterrainChunkCells;
import art.arcane.iris.generation.terrain.IrisDimension;
import art.arcane.iris.generation.terrain.IrisDimensionCarvingEntry;
import art.arcane.iris.generation.terrain.IrisDimensionCarvingResolver;
@@ -148,6 +149,41 @@ public class IrisCarveModifierCaveInputsTest {
}
}
@Test
public void unroutedWorkerCapturesColumnsLikeTheEngineIncludingSubterrainBiomes() {
try (Fixture fixture = new Fixture()) {
when(fixture.engine.hasGenerationRuntimeScope()).thenReturn(false);
when(fixture.engine.hasGenerationHistoryRouting()).thenReturn(false);
doReturn(SubterrainChunkCells.delegated(fixture.engine)).when(fixture.engine).subterrainChunkCells(anyInt(), anyInt());
IrisBiome feature = biome("feature", 0);
doReturn(feature).when(fixture.engine).getSubterrainBiome(-20, 33, 18);
doReturn(feature).when(fixture.scalar).getSubterrainBiome(-20, 33, 18);
IrisCarveModifier.CaveInputs inputs = IrisCarveModifier.CaveInputs.forContentChunk(fixture.engine, -2, 1);
IrisDimensionCarvingResolver.State state = new IrisDimensionCarvingResolver.State();
for (int x = -35; x <= -13; x++) {
for (int z = 13; z <= 35; z += 2) {
for (int y : new int[]{0, 30, 33, 34, 39, 40}) {
assertSame(x + "," + y + "," + z, fixture.scalar.getCaveBiome(x, y, z, state), inputs.resolve(x, y, z));
}
}
}
assertSame(feature, inputs.resolve(-20, 33, 18));
assertTrue(fixture.samples.get() > 0);
}
}
@Test
public void routedOrBoundUnscopedEnginesKeepCoordinateDispatch() {
try (Fixture fixture = new Fixture()) {
when(fixture.engine.hasGenerationRuntimeScope()).thenReturn(false);
when(fixture.engine.hasGenerationHistoryRouting()).thenReturn(true);
IrisBiome historical = biome("historical", 0);
when(fixture.engine.getCaveBiome(anyInt(), anyInt(), anyInt(), any(IrisDimensionCarvingResolver.State.class))).thenReturn(historical);
assertSame(historical, IrisCarveModifier.CaveInputs.forTerrainChunk(fixture.engine, -2, 1).resolve(-20, 20, 18));
assertEquals(0, fixture.samples.get());
}
}
private static IrisBiome biome(String key, int depth) {
IrisBiome biome = new IrisBiome();
biome.setLoadKey(key);
@@ -67,7 +67,8 @@ public class IrisCarveModifierInferenceIsolationTest {
int.class,
IrisCarveModifier.CaveInputs.class,
Long2ObjectOpenHashMap.class,
Map.class
Map.class,
CaveLayerMemo.class
);
paintBoundaryZone.setAccessible(true);
paintBoundaryZone.invoke(
@@ -83,7 +84,8 @@ public class IrisCarveModifierInferenceIsolationTest {
1,
new IrisCarveModifier.CaveInputs(engine),
new Long2ObjectOpenHashMap<IrisBiome>(),
customBiomes
customBiomes,
new CaveLayerMemo()
);
IrisBiome fallback = new IrisBiome();
@@ -0,0 +1,95 @@
package art.arcane.iris.generation.stage;
import art.arcane.iris.generation.biome.IrisBiome;
import art.arcane.iris.generation.biome.IrisBiomePaletteLayer;
import art.arcane.iris.generation.biome.IrisSurfaceLayerFallback;
import art.arcane.iris.generation.runtime.IrisComplex;
import art.arcane.iris.generation.terrain.IrisDimension;
import art.arcane.iris.generation.terrain.IrisSlopeClip;
import art.arcane.iris.pack.loading.IrisData;
import art.arcane.volmlib.nativelib.terrain.NativeBlockState;
import art.arcane.volmlib.util.collection.KList;
import art.arcane.volmlib.util.math.RNG;
import art.arcane.volmlib.util.noise.CNG;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyDouble;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class IrisCarveModifierLayerMemoTest {
private static final Map<Integer, NativeBlockState> BLOCKS = new HashMap<>();
@Test
public void heightFreeFloorLayersIgnoreTheZoneFloor() {
IrisData data = mock(IrisData.class);
IrisComplex complex = mock(IrisComplex.class);
when(complex.hasTerrain3D()).thenReturn(true);
when(complex.terrainSurfaceSlope(anyInt(), anyInt(), anyInt())).thenAnswer(call -> (double) (int) call.getArgument(1));
RNG rng = new RNG(31L);
IrisBiomePaletteLayer top = layer(data, 2, new IrisSlopeClip());
IrisBiomePaletteLayer under = layer(data, 3, new IrisSlopeClip());
IrisBiome biome = new IrisBiome().setLayers(new KList<IrisBiomePaletteLayer>().qadd(top).qadd(under));
IrisDimension dimension = new IrisDimension().setSurfaceLayerFallback(IrisSurfaceLayerFallback.TOP_LAYER);
assertTrue(IrisCarveModifier.hasHeightFreeFloorLayers(biome));
KList<NativeBlockState> reference = biome.generateLayers(dimension, 14D, -9D, rng, 3, 0, data, complex);
for (int floor = -64; floor < 320; floor += 7) {
assertEquals(reference, biome.generateLayers(dimension, 14D, -9D, rng, 3, floor, data, complex));
}
biome.setLockLayers(true);
assertFalse(IrisCarveModifier.hasHeightFreeFloorLayers(biome));
biome.setLockLayers(false);
biome.setLayers(new KList<IrisBiomePaletteLayer>().qadd(top).qadd(layer(data, 1, new IrisSlopeClip(0D, 2D))));
assertFalse(IrisCarveModifier.hasHeightFreeFloorLayers(biome));
}
@Test
public void memoAnswersOnlyTheBiomesOfTheCurrentColumn() {
CaveLayerMemo memo = new CaveLayerMemo();
IrisBiome first = new IrisBiome();
IrisBiome second = new IrisBiome();
KList<NativeBlockState> floor = new KList<>();
KList<NativeBlockState> ceiling = new KList<>();
memo.rememberFloor(first, floor);
memo.rememberCeiling(second, ceiling);
assertSame(floor, memo.floor(first));
assertNull(memo.floor(second));
assertSame(ceiling, memo.ceiling(second));
assertNull(memo.ceiling(first));
memo.reset();
assertNull(memo.floor(first));
assertNull(memo.ceiling(second));
}
private static IrisBiomePaletteLayer layer(IrisData data, int thickness, IrisSlopeClip slope) {
IrisBiomePaletteLayer layer = mock(IrisBiomePaletteLayer.class);
CNG height = mock(CNG.class);
when(layer.getZoom()).thenReturn(1D);
when(layer.getMinHeight()).thenReturn(thickness);
when(layer.getMaxHeight()).thenReturn(thickness);
when(layer.getSlopeCondition()).thenReturn(slope);
when(layer.getHeightGenerator(any(RNG.class), same(data))).thenReturn(height);
when(height.fit(anyInt(), anyInt(), anyDouble(), anyDouble())).thenReturn(thickness);
when(layer.get(any(RNG.class), anyInt(), anyDouble(), anyDouble(), anyDouble(), same(data)))
.thenAnswer(call -> block((int) call.getArgument(1)));
return layer;
}
private static NativeBlockState block(int signature) {
return BLOCKS.computeIfAbsent(signature, key -> mock(NativeBlockState.class));
}
}
@@ -112,39 +112,15 @@ public class IrisCarveModifierZoneParityTest {
private List<String> bitsetZones(CarveColumnMask columnMask, int maxHeight) {
List<String> zones = new ArrayList<>();
int firstHeight = columnMask.nextSetBit(0);
if (firstHeight < 0) {
return zones;
}
int floor = firstHeight;
int ceiling = -1;
int buf = firstHeight - 1;
int y = firstHeight;
while (y >= 0) {
if (y >= 0 && y <= maxHeight) {
if (y == buf + 1) {
buf = y;
ceiling = buf;
} else if (isValidZone(floor, ceiling, maxHeight)) {
zones.add(zoneKey(floor, ceiling));
floor = y;
ceiling = -1;
buf = y;
} else {
floor = y;
ceiling = -1;
buf = y;
}
int floor = columnMask.nextSetBit(0);
while (floor >= 0 && floor <= maxHeight) {
int end = columnMask.nextClearBit(floor) - 1;
int ceiling = Math.min(end, maxHeight);
if (isValidZone(floor, ceiling, maxHeight)) {
zones.add(zoneKey(floor, ceiling));
}
y = columnMask.nextSetBit(y + 1);
floor = columnMask.nextSetBit(end + 1);
}
if (isValidZone(floor, ceiling, maxHeight)) {
zones.add(zoneKey(floor, ceiling));
}
return zones;
}
@@ -5,7 +5,10 @@ import art.arcane.volmlib.util.matter.MatterCavern;
import org.junit.Test;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -65,6 +68,54 @@ public class IrisCarveScratchTest {
assertEquals(0, wallCount[0]);
}
@Test
public void maskWallsMatchTheNeighborScanOfCarvedCells() {
Random random = new Random(52_117L);
for (int round = 0; round < 40; round++) {
CarveColumnMask[] columns = new CarveColumnMask[256];
CarveColumnMask[] walls = new CarveColumnMask[256];
for (int index = 0; index < 256; index++) {
columns[index] = new CarveColumnMask();
walls[index] = new CarveColumnMask();
walls[index].add(random.nextInt(700));
}
int cells = random.nextInt(4000);
int height = 64 + random.nextInt(700);
for (int cell = 0; cell < cells; cell++) {
columns[random.nextInt(256)].add(1 + random.nextInt(height - 1));
}
Set<String> expected = new HashSet<>();
for (int index = 0; index < 256; index++) {
int rx = index >> 4;
int rz = index & 15;
for (int y = columns[index].nextSetBit(0); y >= 0; y = columns[index].nextSetBit(y + 1)) {
if (rz < 15 && !columns[index + 1].contains(y)) {
expected.add(key(rx, y, rz + 1));
}
if (rx < 15 && !columns[index + 16].contains(y)) {
expected.add(key(rx + 1, y, rz));
}
if (rz > 0 && !columns[index - 1].contains(y)) {
expected.add(key(rx, y, rz - 1));
}
if (rx > 0 && !columns[index - 16].contains(y)) {
expected.add(key(rx - 1, y, rz));
}
}
}
IrisCarveModifier.collectInternalWalls(walls, columns);
Set<String> actual = new HashSet<>();
for (int index = 0; index < 256; index++) {
for (int y = walls[index].nextSetBit(0); y >= 0; y = walls[index].nextSetBit(y + 1)) {
actual.add(key(index >> 4, y, index & 15));
assertFalse(columns[index].contains(y));
}
assertEquals(walls[index].nextSetBit(0) < 0, walls[index].isEmpty());
}
assertEquals(expected, actual);
}
}
private static String key(int x, int y, int z) {
return x + ":" + y + ":" + z;
}
@@ -376,6 +376,7 @@ public class IrisNaturalCaveScanTest {
chunk = mock(MantleChunk.class);
doReturn(engine).when(modifier).getEngine();
doReturn(complex).when(modifier).getComplex();
doReturn(complex).when(engine).getComplex();
doReturn(32).when(engine).getHeight();
doReturn(new SeedManager(1337L)).when(engine).getSeedManager();
doReturn(engineMantle).when(engine).getMantle();
@@ -638,6 +638,48 @@ public class SubterrainPlannerTest {
assertTrue(original.sample(anchor.x(), anchor.y(), anchor.z()).occupied());
}
@Test
public void chunkSamplersMatchCoordinateSamplingInsideAndAroundTheirChunk() {
for (IrisSubterrainFamily family : IrisSubterrainFamily.values()) {
int length = family == IrisSubterrainFamily.TECTONIC_FAULT ? 208 : 64;
SubterrainPlanner planner = planner(compactFeature(family).setLength(length).setProbability(0.6));
SubterrainPlanner reference = planner(compactFeature(family).setLength(length).setProbability(0.6));
SubterrainPlan plan = first(planner);
int centerChunkX = plan.centerX() >> 4;
int centerChunkZ = plan.centerZ() >> 4;
int occupied = 0;
int emptyChunks = 0;
for (int chunkX = centerChunkX - 3; chunkX <= centerChunkX + 3; chunkX += 2) {
for (int chunkZ = centerChunkZ - 3; chunkZ <= centerChunkZ + 3; chunkZ += 2) {
SubterrainPlanner.ChunkSampler sampler = planner.chunkSampler(chunkX, chunkZ);
List<SubterrainPlan> chunkPlans = reference.plansForBounds((chunkX << 4) - 1, (chunkZ << 4) - 1,
(chunkX << 4) + 16, (chunkZ << 4) + 16);
if (sampler.isEmpty()) {
emptyChunks++;
}
for (int x = (chunkX << 4) - 3; x < (chunkX << 4) + 19; x++) {
for (int z = (chunkZ << 4) - 3; z < (chunkZ << 4) + 19; z++) {
for (int y = -66; y <= 99; y++) {
SubterrainCell expected = reference.sample(x, y, z);
assertEquals(family + " " + x + "," + y + "," + z, expected, sampler.sample(x, y, z));
assertEquals(family + " candidates " + x + "," + y + "," + z,
reference.sample(chunkPlans, x, y, z), sampler.sampleCandidates(x, y, z));
if (expected.occupied()) {
occupied++;
}
}
}
}
}
}
assertTrue(family + " occupied", occupied > 100);
assertTrue(family + " empty chunks", emptyChunks < 16);
}
SubterrainPlanner empty = new SubterrainPlanner(new SubterrainPlanner.Options(List.of(), 7123, -64, 97, (x, z) -> "test-region"));
assertTrue(empty.chunkSampler(3, -5).isEmpty());
assertEquals(SubterrainCell.OUTSIDE, empty.chunkSampler(3, -5).sample(50, 10, -70));
}
@Test(expected = IllegalArgumentException.class)
public void duplicateIdentifiersAreRejected() {
IrisSubterrainFeature feature = feature(IrisSubterrainFamily.CENOTE);