Batch history biome reads

- saved biome capture resolves each quart column through one engine call
- column-invariant height, region, surface and cave fallback resolved once
- cavern reads use one pinned mantle chunk per column
- saved biome cells interned per capture
- engine getters borrow the bound route runtime for other chunks instead of opening a stage
This commit is contained in:
Brian Neumann-Fopiano committed 2026-09-28 14:04:36 -04:00
1 parent c00dfbddce
commit adbcba3efc
8 files changed
+471 -65

No files matched your search

@@ -59,6 +59,7 @@ import art.arcane.volmlib.util.documentation.BlockCoordinates;
import art.arcane.volmlib.util.documentation.ChunkCoordinates;
import art.arcane.volmlib.util.function.Function2;
import art.arcane.volmlib.util.hunk.Hunk;
import art.arcane.volmlib.util.mantle.runtime.Mantle;
import art.arcane.volmlib.util.mantle.runtime.MantleChunk;
import art.arcane.volmlib.util.mantle.flag.MantleFlag;
import art.arcane.volmlib.util.math.M;
@@ -341,25 +342,101 @@ public interface Engine extends DataProvider, Fallible, BlockUpdater, Renderer,
@BlockCoordinates
default IrisBiome getCaveOrMantleBiome(int x, int y, int z) {
IrisBiome mantleBiome = mantleCaveBiome(x, y, z);
return mantleBiome != null ? mantleBiome : getCaveBiome(x, y, z);
}
private IrisBiome mantleCaveBiome(int x, int y, int z) {
IrisBiome flooded = floodedCaveBiome(x, y, z);
return flooded != null ? flooded : customCaveBiome(getMantle().getMantle().get(x, y, z, MatterCavern.class));
}
private IrisBiome floodedCaveBiome(int x, int y, int z) {
HydrologyCaveCell hydrology = HydrologyCaveStorage.getIfPresent(
getMantle().getMantle(), x, y, z);
if (hydrology != null && !hydrology.floodedBiomeKey().isEmpty()) {
IrisBiome biome = getData().getBiomeLoader().load(hydrology.floodedBiomeKey());
if (biome != null) {
return biome;
return getData().getBiomeLoader().load(hydrology.floodedBiomeKey());
}
return null;
}
private IrisBiome customCaveBiome(MatterCavern cavern) {
if (cavern != null && cavern.getCustomBiome() != null && !cavern.getCustomBiome().isEmpty()) {
return getData().getBiomeLoader().load(cavern.getCustomBiome());
}
return null;
}
private MantleChunk<Matter> useCavernChunk(int x, int z) {
Mantle<Matter> mantle = getMantle().getMantle();
int chunkX = x >> 4;
int chunkZ = z >> 4;
return mantle.hasTectonicPlate(chunkX >> 5, chunkZ >> 5) ? mantle.getChunk(chunkX, chunkZ).use() : null;
}
/**
* Fills {@code biomes[i]} with {@link #getBiomeOrMantle(int, int, int)} and {@code regions[i]} with
* {@link #getRegion(int, int, int)} at y = i * step of one column, resolving what does not vary along it once.
*/
@BlockCoordinates
default void getBiomeOrMantleColumn(int x, int z, int step, IrisBiome[] biomes, IrisRegion[] regions) {
if (getDimensionStackContext() != null) {
for (int index = 0; index < biomes.length; index++) {
biomes[index] = getBiomeOrMantle(x, index * step, z);
regions[index] = getRegion(x, index * step, z);
}
return;
}
IrisComplex complex = getComplex();
int caveTop = getHeight(x, z) - 2;
IrisRegion region = getRegion(x, 0, z);
IrisBiome surface = null;
boolean cavernChunkResolved = false;
MantleChunk<Matter> cavernChunk = null;
boolean caveColumnResolved = false;
IrisBiome caveBase = null;
IrisBiome caveSurface = null;
int caveSurfaceY = 0;
try {
for (int index = 0; index < biomes.length; index++) {
int y = index * step;
regions[index] = region;
if (y > caveTop || complex.isTerrain3DSurface(x, y, z)) {
if (surface == null) {
surface = getSurfaceBiome(x, z);
}
biomes[index] = surface;
continue;
}
IrisBiome biome = floodedCaveBiome(x, y, z);
if (biome == null) {
if (!cavernChunkResolved) {
cavernChunk = useCavernChunk(x, z);
cavernChunkResolved = true;
}
biome = cavernChunk == null || y < 0 || y >= getMantle().getMantle().getWorldHeight()
? null : customCaveBiome(cavernChunk.get(x & 15, y, z & 15, MatterCavern.class));
}
if (biome == null) {
biome = resolveConfiguredCaveBiome(x, y, z, null);
}
if (biome == null) {
if (!caveColumnResolved) {
boolean naturalFallback = answersFromNaturalTerrain(x, z);
caveSurface = caveDepthSurfaceBiome(x, z, naturalFallback);
caveSurfaceY = caveDepthSurfaceY(x, z, naturalFallback);
caveBase = getCaveBiome(x, z);
caveColumnResolved = true;
}
biome = depthCaveBiome(caveBase, y, caveSurface, caveSurfaceY);
}
biomes[index] = biome;
}
} finally {
if (cavernChunk != null) {
cavernChunk.release();
}
}
MatterCavern m = getMantle().getMantle().get(x, y, z, MatterCavern.class);
if (m != null && m.getCustomBiome() != null && !m.getCustomBiome().isEmpty()) {
IrisBiome biome = getData().getBiomeLoader().load(m.getCustomBiome());
if (biome != null) {
return biome;
}
}
return getCaveBiome(x, y, z);
}
@ChunkCoordinates
@@ -386,16 +463,22 @@ public interface Engine extends DataProvider, Fallible, BlockUpdater, Renderer,
return configuredBiome;
}
boolean naturalFallback = answersFromNaturalTerrain(x, z);
DimensionStackContext dimensionStackContext = getDimensionStackContext();
IrisBiome surfaceBiome = naturalFallback || dimensionStackContext == null
? naturalFallback
? getComplex().naturalSurfaceBiome(x, z)
: getSurfaceBiome(x, z)
: getComplex().getTrueBiomeStream().get(x, z);
int surfaceY = naturalFallback
IrisBiome surfaceBiome = caveDepthSurfaceBiome(x, z, naturalFallback);
int surfaceY = caveDepthSurfaceY(x, z, naturalFallback);
return resolveDepthCaveBiome(x, y, z, surfaceBiome, surfaceY);
}
private IrisBiome caveDepthSurfaceBiome(int x, int z, boolean naturalFallback) {
if (naturalFallback) {
return getComplex().naturalSurfaceBiome(x, z);
}
return getDimensionStackContext() == null ? getSurfaceBiome(x, z) : getComplex().getTrueBiomeStream().get(x, z);
}
private int caveDepthSurfaceY(int x, int z, boolean naturalFallback) {
return naturalFallback
? getComplex().naturalTrueHeight(x, z)
: getComplex().getHeightStream().get(x, z).intValue();
return resolveDepthCaveBiome(x, y, z, surfaceBiome, surfaceY);
}
@BlockCoordinates
@@ -431,7 +514,10 @@ public interface Engine extends DataProvider, Fallible, BlockUpdater, Renderer,
IrisBiome surfaceBiome,
int surfaceY
) {
IrisBiome caveBiome = getCaveBiome(x, z);
return depthCaveBiome(getCaveBiome(x, z), y, surfaceBiome, surfaceY);
}
private static IrisBiome depthCaveBiome(IrisBiome caveBiome, int y, IrisBiome surfaceBiome, int surfaceY) {
if (caveBiome == null) {
return surfaceBiome;
}
@@ -519,7 +519,7 @@ public class IrisEngine implements Engine {
return drawBiomeEnvironment(x, z, saved.get());
}
try (GenerationHistoryRuntimeRouter.CoordinateScope ignored =
openGenerationHistoryCoordinateScopeUnchecked(x, z, "draw a pregeneration preview")) {
openGenerationHistoryReadScope(x, z, "draw a pregeneration preview")) {
return Engine.super.draw(x, z);
}
});
@@ -535,7 +535,7 @@ public class IrisEngine implements Engine {
return saved.get();
}
try (GenerationHistoryRuntimeRouter.CoordinateScope ignored =
openGenerationHistoryCoordinateScopeUnchecked(x, z, "resolve a biome environment")) {
openGenerationHistoryReadScope(x, z, "resolve a biome environment")) {
return Engine.super.getBiomeEnvironment(x, y, z);
}
}
@@ -547,7 +547,7 @@ public class IrisEngine implements Engine {
return saved.get();
}
try (GenerationHistoryRuntimeRouter.CoordinateScope ignored =
openGenerationHistoryCoordinateScopeUnchecked(x, z, "resolve a mantle biome environment")) {
openGenerationHistoryReadScope(x, z, "resolve a mantle biome environment")) {
return Engine.super.getBiomeOrMantleEnvironment(x, y, z);
}
}
@@ -559,7 +559,7 @@ public class IrisEngine implements Engine {
return saved.get();
}
try (GenerationHistoryRuntimeRouter.CoordinateScope ignored =
openGenerationHistoryCoordinateScopeUnchecked(x, z, "resolve a surface biome environment")) {
openGenerationHistoryReadScope(x, z, "resolve a surface biome environment")) {
return Engine.super.getSurfaceBiomeEnvironment(x, z);
}
}
@@ -595,7 +595,7 @@ public class IrisEngine implements Engine {
return saved.get().biome();
}
try (GenerationHistoryRuntimeRouter.CoordinateScope ignored =
openGenerationHistoryCoordinateScopeUnchecked(x, z, "resolve a biome")) {
openGenerationHistoryReadScope(x, z, "resolve a biome")) {
return Engine.super.getBiome(x, y, z);
}
}
@@ -608,11 +608,27 @@ public class IrisEngine implements Engine {
return saved.get().biome();
}
try (GenerationHistoryRuntimeRouter.CoordinateScope ignored =
openGenerationHistoryCoordinateScopeUnchecked(x, z, "resolve a biome or mantle biome")) {
openGenerationHistoryReadScope(x, z, "resolve a biome or mantle biome")) {
return Engine.super.getBiomeOrMantle(x, y, z);
}
}
@BlockCoordinates
@Override
public void getBiomeOrMantleColumn(int x, int z, int step, IrisBiome[] biomes, IrisRegion[] regions) {
if (usesSavedBiomeEnvironment()) {
for (int index = 0; index < biomes.length; index++) {
biomes[index] = getBiomeOrMantle(x, index * step, z);
regions[index] = getRegion(x, index * step, z);
}
return;
}
try (GenerationHistoryRuntimeRouter.CoordinateScope ignored =
openGenerationHistoryReadScope(x, z, "resolve a biome or mantle biome column")) {
Engine.super.getBiomeOrMantleColumn(x, z, step, biomes, regions);
}
}
@BlockCoordinates
@Override
public IrisRegion getRegion(int x, int z) {
@@ -621,7 +637,7 @@ public class IrisEngine implements Engine {
return saved.get().region();
}
try (GenerationHistoryRuntimeRouter.CoordinateScope ignored =
openGenerationHistoryCoordinateScopeUnchecked(x, z, "resolve a region")) {
openGenerationHistoryReadScope(x, z, "resolve a region")) {
return Engine.super.getRegion(x, z);
}
}
@@ -634,7 +650,7 @@ public class IrisEngine implements Engine {
return saved.get().region();
}
try (GenerationHistoryRuntimeRouter.CoordinateScope ignored =
openGenerationHistoryCoordinateScopeUnchecked(x, z, "resolve a vertical region")) {
openGenerationHistoryReadScope(x, z, "resolve a vertical region")) {
return Engine.super.getRegion(x, y, z);
}
}
@@ -647,7 +663,7 @@ public class IrisEngine implements Engine {
return saved.get().biome();
}
try (GenerationHistoryRuntimeRouter.CoordinateScope ignored =
openGenerationHistoryCoordinateScopeUnchecked(x, z, "resolve a cave or mantle biome")) {
openGenerationHistoryReadScope(x, z, "resolve a cave or mantle biome")) {
return Engine.super.getCaveOrMantleBiome(x, y, z);
}
}
@@ -665,7 +681,7 @@ public class IrisEngine implements Engine {
return Engine.super.getCaveBiome(x, z);
}
try (GenerationHistoryRuntimeRouter.CoordinateScope ignored =
openGenerationHistoryCoordinateScopeUnchecked(x, z, "resolve a cave biome")) {
openGenerationHistoryReadScope(x, z, "resolve a cave biome")) {
return Engine.super.getCaveBiome(x, z);
}
}
@@ -692,7 +708,7 @@ public class IrisEngine implements Engine {
return Engine.super.getCaveBiome(x, y, z, state);
}
try (GenerationHistoryRuntimeRouter.CoordinateScope ignored =
openGenerationHistoryCoordinateScopeUnchecked(x, z, "resolve a vertical cave biome")) {
openGenerationHistoryReadScope(x, z, "resolve a vertical cave biome")) {
return Engine.super.getCaveBiome(x, y, z, state);
}
}
@@ -708,7 +724,7 @@ public class IrisEngine implements Engine {
return Engine.super.getSurfaceBiome(x, z);
}
try (GenerationHistoryRuntimeRouter.CoordinateScope ignored =
openGenerationHistoryCoordinateScopeUnchecked(x, z, "resolve a surface biome")) {
openGenerationHistoryReadScope(x, z, "resolve a surface biome")) {
return Engine.super.getSurfaceBiome(x, z);
}
}
@@ -717,7 +733,7 @@ public class IrisEngine implements Engine {
@Override
public double getSlope(int x, int z) {
try (GenerationHistoryRuntimeRouter.CoordinateScope ignored =
openGenerationHistoryCoordinateScopeUnchecked(x, z, "resolve terrain slope")) {
openGenerationHistoryReadScope(x, z, "resolve terrain slope")) {
return Engine.super.getSlope(x, z);
}
}
@@ -732,7 +748,7 @@ public class IrisEngine implements Engine {
@Override
public int getHeight(int x, int z, boolean ignoreFluid) {
try (GenerationHistoryRuntimeRouter.CoordinateScope ignored =
openGenerationHistoryCoordinateScopeUnchecked(x, z, "resolve terrain height")) {
openGenerationHistoryReadScope(x, z, "resolve terrain height")) {
return Engine.super.getHeight(x, z, ignoreFluid);
}
}
@@ -745,7 +761,7 @@ public class IrisEngine implements Engine {
return recorded.get().objectKeys();
}
try (GenerationHistoryRuntimeRouter.CoordinateScope ignored =
openGenerationHistoryCoordinateScopeUnchecked(x << 4, z << 4, "resolve chunk objects")) {
openGenerationHistoryReadScope(x << 4, z << 4, "resolve chunk objects")) {
return getMantle().getObjectComponent().guess(x, z);
}
}
@@ -758,7 +774,7 @@ public class IrisEngine implements Engine {
return new HashSet<>(recorded.get().pointsOfInterest());
}
try (GenerationHistoryRuntimeRouter.CoordinateScope ignored =
openGenerationHistoryCoordinateScopeUnchecked(
openGenerationHistoryReadScope(
chunkX << 4, chunkZ << 4, "resolve chunk points of interest")) {
Set<ChunkGenerationSemantics.PointOfInterest> pois = new HashSet<>();
getMantle().getMantle().iterateChunk(
@@ -854,7 +870,7 @@ public class IrisEngine implements Engine {
int queryMinimumZ = Math.max(minimumZ, chunkMinimumZ);
int queryMaximumZ = Math.min(maximumZ, chunkMinimumZ + 15);
try (GenerationHistoryRuntimeRouter.CoordinateScope ignored =
openGenerationHistoryCoordinateScopeUnchecked(
openGenerationHistoryReadScope(
chunkMinimumX,
chunkMinimumZ,
"resolve native structure volumes")) {
@@ -981,13 +997,16 @@ public class IrisEngine implements Engine {
return hasGenerationRuntimeScope() && getComplex().isNaturalTerrainContext();
}
private GenerationHistoryRuntimeRouter.CoordinateScope openGenerationHistoryCoordinateScopeUnchecked(
private GenerationHistoryRuntimeRouter.CoordinateScope openGenerationHistoryReadScope(
int blockX,
int blockZ,
String operation
) {
try {
return openGenerationHistoryCoordinateScope(blockX, blockZ);
GenerationHistoryRuntimeRouter router = generationHistoryRuntimeRouter;
return router == null
? openGenerationHistoryCoordinateScope(blockX, blockZ)
: router.openReadScope(blockX, blockZ);
} catch (IOException failure) {
throw new IllegalStateException("Unable to " + operation + " through Iris generation history at "
+ blockX + "," + blockZ + ".", failure);
@@ -391,6 +391,25 @@ public final class GenerationHistoryRuntimeRouter implements AutoCloseable {
return new CoordinateScope(blockX, blockZ, stage.route, stage);
}
/**
* A coordinate scope for engine reads. Inside another chunk's route whose runtime the thread is bound to, it borrows
* that runtime instead of opening a stage: every generation stage binds the active activation, which cannot change
* while the route holds its transition participation.
*/
public CoordinateScope openReadScope(int blockX, int blockZ) throws IOException {
RuntimeRoute current = scopedRoute.get();
if (current != null) {
if (current.chunkX() == Math.floorDiv(blockX, GenerationBoundary.CHUNK_SIZE)
&& current.chunkZ() == Math.floorDiv(blockZ, GenerationBoundary.CHUNK_SIZE)) {
return new CoordinateScope(blockX, blockZ, current, null);
}
if (current.binding == engine.captureGenerationRuntimeBinding()) {
return new CoordinateScope(blockX, blockZ, null, null);
}
}
return openCoordinateScope(blockX, blockZ);
}
public void recordNaturalTerrain(SavedTerrainChunk terrain) {
SavedTerrainChunk captured = Objects.requireNonNull(terrain, "natural terrain");
RuntimeRoute route = scopedRoute.get();
@@ -6,6 +6,7 @@ import art.arcane.iris.generation.terrain.IrisRegion;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -30,6 +31,8 @@ public final class SavedBiomeCapture {
TransitionGenerationPlan transition = engine.getComplex().getTransitionGenerationPlan();
Map<Long, Optional<SavedBiomeChunk>> historical = new HashMap<>();
Map<Integer, List<SavedBiomeChunk.Span>> vertical = new HashMap<>(16);
Cells cells = new Cells(activationId);
Column column = new Column(engine, minimumY, height);
for (int localX = 0; localX < 16; localX++) {
for (int localZ = 0; localZ < 16; localZ++) {
int blockX = startX + localX;
@@ -37,7 +40,7 @@ public final class SavedBiomeCapture {
IrisRegion region = engine.getRegion(blockX, blockZ);
IrisBiome surface = engine.getSurfaceBiome(blockX, blockZ);
int surfaceY = minimumY + engine.getHeight(blockX, blockZ);
SavedBiomeChunk.Cell surfaceCell = cell(activationId, surface, region);
SavedBiomeChunk.Cell surfaceCell = cells.cell(surface, region);
if (floating != null) {
FloatingBiomeOverlay.Identity floatingSurface = floating.surfaceAt(localX, localZ);
if (floatingSurface != null) {
@@ -53,33 +56,33 @@ public final class SavedBiomeCapture {
int quartKey = quartX * 16 + quartZ;
List<SavedBiomeChunk.Span> spans = vertical.get(quartKey);
if (spans == null) {
spans = captureVertical(new Column(engine, activationId, startX + quartX, startZ + quartZ), transition, saved, historical, floating);
spans = captureVertical(column, startX + quartX, startZ + quartZ, cells, transition, saved, historical, floating);
vertical.put(quartKey, spans);
}
SavedBiomeChunk.Cell caveBase = cell(activationId, engine.getCaveBiome(blockX, blockZ), region);
SavedBiomeChunk.Cell caveBase = cells.cell(engine.getCaveBiome(blockX, blockZ), region);
result.column(localX, localZ, new SavedBiomeChunk.Column(surfaceCell, caveBase, spans));
}
}
return result.build();
}
private static List<SavedBiomeChunk.Span> captureVertical(Column column, TransitionGenerationPlan transition,
SavedBiomeRuntime saved, Map<Long, Optional<SavedBiomeChunk>> historical, FloatingBiomeOverlay floating) throws IOException {
Engine engine = column.engine();
int minimumY = engine.getMinHeight();
int maximumY = minimumY + engine.getHeight();
private static List<SavedBiomeChunk.Span> captureVertical(Column column, int blockX, int blockZ, Cells cells,
TransitionGenerationPlan transition, SavedBiomeRuntime saved,
Map<Long, Optional<SavedBiomeChunk>> historical, FloatingBiomeOverlay floating) throws IOException {
column.engine().getBiomeOrMantleColumn(blockX, blockZ, 4, column.biomes(), column.regions());
int minimumY = column.minimumY();
int maximumY = minimumY + column.height();
List<SavedBiomeChunk.Span> spans = new ArrayList<>();
SavedBiomeChunk.Cell previous = null;
int startY = minimumY;
for (int worldY = minimumY; worldY < maximumY; worldY += 4) {
IrisBiome biome = engine.getBiomeOrMantle(column.blockX(), worldY - minimumY, column.blockZ());
IrisRegion region = engine.getRegion(column.blockX(), worldY - minimumY, column.blockZ());
SavedBiomeChunk.Cell current = cell(column.activationId(), biome, region);
for (int index = 0; index < column.biomes().length; index++) {
int worldY = minimumY + index * 4;
SavedBiomeChunk.Cell current = cells.cell(column.biomes()[index], column.regions()[index]);
if (floating != null) {
current = overlay(current, floating.volumeAt(column.blockX() & 15, worldY - minimumY, column.blockZ() & 15));
current = overlay(current, floating.volumeAt(blockX & 15, worldY - minimumY, blockZ & 15));
}
if (transition != null) {
current = historicalCell(new Sample(column.blockX(), worldY, column.blockZ(), false), current, transition, saved, historical);
current = historicalCell(new Sample(blockX, worldY, blockZ, false), current, transition, saved, historical);
}
if (previous != null && !previous.equals(current)) {
spans.add(new SavedBiomeChunk.Span(startY, worldY, previous));
@@ -132,7 +135,40 @@ public final class SavedBiomeCapture {
Objects.requireNonNull(region, "region").getLoadKey());
}
private record Column(Engine engine, long activationId, int blockX, int blockZ) {
private record Column(Engine engine, int minimumY, int height, IrisBiome[] biomes, IrisRegion[] regions) {
private Column(Engine engine, int minimumY, int height) {
this(engine, minimumY, height, new IrisBiome[(height + 3) / 4], new IrisRegion[(height + 3) / 4]);
}
}
private static final class Cells {
private final long activationId;
private IrisBiome[] biomes = new IrisBiome[8];
private IrisRegion[] regions = new IrisRegion[8];
private SavedBiomeChunk.Cell[] cells = new SavedBiomeChunk.Cell[8];
private int size;
private Cells(long activationId) {
this.activationId = activationId;
}
private SavedBiomeChunk.Cell cell(IrisBiome biome, IrisRegion region) {
for (int index = 0; index < size; index++) {
if (biomes[index] == biome && regions[index] == region) {
return cells[index];
}
}
SavedBiomeChunk.Cell created = SavedBiomeCapture.cell(activationId, biome, region);
if (size == cells.length) {
biomes = Arrays.copyOf(biomes, size * 2);
regions = Arrays.copyOf(regions, size * 2);
cells = Arrays.copyOf(cells, size * 2);
}
biomes[size] = biome;
regions[size] = region;
cells[size++] = created;
return created;
}
}
private record Sample(int x, int y, int z, boolean surface) {