Drop stale cave snapshots

- dimension stack metadata clearing and continuation restore rewrite cavern cells after sCave, so they drop the context snapshot and decoration re-reads the mantle
- test that decoration reuses only its own chunk's snapshot
This commit is contained in:
Brian Neumann-Fopiano
2026-09-28 13:55:18 -04:00
parent 195f1862af
commit 55e4a70b3b
4 changed files with 31 additions and 2 deletions
@@ -17,8 +17,9 @@ import java.util.function.IntFunction;
/** /**
* The terrain view of one mantle chunk's cavern and hydrology cells (journaled pre-object values win), * The terrain view of one mantle chunk's cavern and hydrology cells (journaled pre-object values win),
* copied once under the chunk monitor. Terrain components finish before any content write reaches a * copied once under the chunk monitor. Terrain components finish before any content write reaches a
* chunk and every later write journals the value it replaces, so this view never goes stale while the * chunk and every mantle-writer write journals the value it replaces, so the view holds while the chunk
* chunk generates. * generates; the raw writers that run after sCave (dimension stack clearing, continuation restore) drop
* it from the chunk context instead.
*/ */
public final class CaveTerrainSnapshot { public final class CaveTerrainSnapshot {
private static final int SECTION_VOLUME = 4096; private static final int SECTION_VOLUME = 4096;
@@ -218,6 +218,9 @@ public interface EngineMode extends Staged {
IrisDimensionStackActuator.clearHostNaturalMetadata(getEngine(), x, z, biomes.getHeight(), ctx); IrisDimensionStackActuator.clearHostNaturalMetadata(getEngine(), x, z, biomes.getHeight(), ctx);
ctx.beginContent(); ctx.beginContent();
List<Matter> continuations = restoreObjectContinuations(x, z); List<Matter> continuations = restoreObjectContinuations(x, z);
if (!continuations.isEmpty()) {
ctx.setCaveTerrain(null);
}
for (EngineStage i : stages) { for (EngineStage i : stages) {
i.generate(x, z, blocks, biomes, multicore, ctx); i.generate(x, z, blocks, biomes, multicore, ctx);
} }
@@ -110,6 +110,7 @@ public final class IrisDimensionStackActuator extends EngineAssignedActuator<Nat
if (!context.hasDimensionStack() || !context.getComplex().allowsMantleChunkWrite(x >> 4, z >> 4)) { if (!context.hasDimensionStack() || !context.getComplex().allowsMantleChunkWrite(x >> 4, z >> 4)) {
return; return;
} }
context.setCaveTerrain(null);
MantleChunk<Matter> chunk = engine.getMantle().getMantle().getChunk(x >> 4, z >> 4).use(); MantleChunk<Matter> chunk = engine.getMantle().getMantle().getChunk(x >> 4, z >> 4).use();
try { try {
synchronized (chunk) { synchronized (chunk) {
@@ -5,6 +5,7 @@ import art.arcane.iris.generation.decoration.IrisCeilingDecorator;
import art.arcane.iris.generation.decoration.IrisSurfaceDecorator; import art.arcane.iris.generation.decoration.IrisSurfaceDecorator;
import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.runtime.Engine;
import art.arcane.iris.generation.runtime.SeedManager; import art.arcane.iris.generation.runtime.SeedManager;
import art.arcane.iris.generation.context.ChunkContext;
import art.arcane.iris.generation.mantle.CaveTerrainSnapshot; import art.arcane.iris.generation.mantle.CaveTerrainSnapshot;
import art.arcane.iris.generation.mantle.EngineMantle; import art.arcane.iris.generation.mantle.EngineMantle;
import art.arcane.iris.generation.biome.IrisBiome; import art.arcane.iris.generation.biome.IrisBiome;
@@ -33,6 +34,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame; import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@@ -42,10 +44,12 @@ import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.CALLS_REAL_METHODS; import static org.mockito.Mockito.CALLS_REAL_METHODS;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
public class IrisNaturalCaveScanTest { public class IrisNaturalCaveScanTest {
@@ -72,6 +76,26 @@ public class IrisNaturalCaveScanTest {
verifyScan(16, 16); verifyScan(16, 16);
} }
@Test
public void decorationReusesOnlyTheCarveSnapshotOfItsOwnChunk() throws Exception {
Fixture fixture = new Fixture();
ChunkContext context = mock(ChunkContext.class);
doReturn(CaveTerrainSnapshot.capture(fixture.chunk, -2, 3)).when(context).getCaveTerrain();
clearInvocations(fixture.chunk);
fixture.modifier.decorateNaturalCaves(-32, 48, fixture.output(3, 2), context);
verify(fixture.chunk, never()).use();
verify(context).setCaveTerrain(null);
assertFalse(fixture.calls.isEmpty());
Fixture other = new Fixture();
ChunkContext stale = mock(ChunkContext.class);
doReturn(CaveTerrainSnapshot.capture(other.chunk, 5, 5)).when(stale).getCaveTerrain();
clearInvocations(other.chunk);
other.modifier.decorateNaturalCaves(-32, 48, other.output(3, 2), stale);
verify(other.chunk).use();
assertEquals(fixture.calls, other.calls);
}
@Test @Test
public void listenerFailureReleasesMantleChunk() throws Exception { public void listenerFailureReleasesMantleChunk() throws Exception {
Fixture fixture = new Fixture(); Fixture fixture = new Fixture();