From 6096c391923c55297e1ec23f169117b96b0e4a13 Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Wed, 15 Apr 2026 15:13:35 -0400 Subject: [PATCH] OptiPass3 --- .../iris/engine/framework/EngineMode.java | 18 +- .../mantle/components/IrisCaveCarver3D.java | 261 +++++++++++++++++- .../components/MantleCarvingComponent.java | 114 ++++++-- .../stream/utility/CachedDoubleStream2D.java | 9 +- .../utility/ChunkFillableDoubleStream2D.java | 5 + .../framework/EngineModeMaintenanceTest.java | 24 ++ 6 files changed, 400 insertions(+), 31 deletions(-) create mode 100644 core/src/main/java/art/arcane/iris/util/project/stream/utility/ChunkFillableDoubleStream2D.java create mode 100644 core/src/test/java/art/arcane/iris/engine/framework/EngineModeMaintenanceTest.java diff --git a/core/src/main/java/art/arcane/iris/engine/framework/EngineMode.java b/core/src/main/java/art/arcane/iris/engine/framework/EngineMode.java index 3319e262c..c82705632 100644 --- a/core/src/main/java/art/arcane/iris/engine/framework/EngineMode.java +++ b/core/src/main/java/art/arcane/iris/engine/framework/EngineMode.java @@ -19,6 +19,7 @@ package art.arcane.iris.engine.framework; import art.arcane.iris.core.tools.IrisToolbelt; +import art.arcane.iris.core.gui.PregeneratorJob; import art.arcane.iris.engine.IrisComplex; import art.arcane.iris.engine.mantle.EngineMantle; import art.arcane.iris.util.project.context.ChunkContext; @@ -75,7 +76,7 @@ public interface EngineMode extends Staged { boolean cacheContext = true; if (J.isFolia()) { org.bukkit.World world = getEngine().getWorld().realWorld(); - if (world != null && IrisToolbelt.isWorldMaintenanceActive(world)) { + if (world != null && shouldDisableContextCacheForMaintenance(world)) { cacheContext = false; } } @@ -88,4 +89,19 @@ public interface EngineMode extends Staged { i.generate(x, z, blocks, biomes, multicore, ctx); } } + + static boolean shouldDisableContextCacheForMaintenance(boolean maintenanceActive, boolean pregeneratorTargetsWorld) { + return maintenanceActive && !pregeneratorTargetsWorld; + } + + private boolean shouldDisableContextCacheForMaintenance(org.bukkit.World world) { + boolean maintenanceActive = IrisToolbelt.isWorldMaintenanceActive(world); + if (!maintenanceActive) { + return false; + } + + PregeneratorJob pregeneratorJob = PregeneratorJob.getInstance(); + boolean pregeneratorTargetsWorld = pregeneratorJob != null && pregeneratorJob.targetsWorld(world); + return shouldDisableContextCacheForMaintenance(maintenanceActive, pregeneratorTargetsWorld); + } } diff --git a/core/src/main/java/art/arcane/iris/engine/mantle/components/IrisCaveCarver3D.java b/core/src/main/java/art/arcane/iris/engine/mantle/components/IrisCaveCarver3D.java index eb7f41a4d..e55c670ad 100644 --- a/core/src/main/java/art/arcane/iris/engine/mantle/components/IrisCaveCarver3D.java +++ b/core/src/main/java/art/arcane/iris/engine/mantle/components/IrisCaveCarver3D.java @@ -1049,11 +1049,13 @@ public class IrisCaveCarver3D { double[] adaptivePlaneDensity = scratch.adaptivePlaneDensity; int axisCells = (16 + adaptiveSampleStep - 1) / adaptiveSampleStep; int axisSamples = axisCells + 1; - for (int sampleXIndex = 0; sampleXIndex < axisSamples; sampleXIndex++) { + int[] adaptivePlaneSampleBounds = scratch.adaptivePlaneSampleBounds; + prepareAdaptivePlaneSampleBounds(planeColumnIndices, planeCount, adaptiveSampleStep, adaptivePlaneSampleBounds, axisCells); + for (int sampleXIndex = adaptivePlaneSampleBounds[0]; sampleXIndex <= adaptivePlaneSampleBounds[1]; sampleXIndex++) { int sampleLocalX = Math.min(sampleXIndex * adaptiveSampleStep, 16); int x = x0 + sampleLocalX; int rowOffset = sampleXIndex * axisSamples; - for (int sampleZIndex = 0; sampleZIndex < axisSamples; sampleZIndex++) { + for (int sampleZIndex = adaptivePlaneSampleBounds[2]; sampleZIndex <= adaptivePlaneSampleBounds[3]; sampleZIndex++) { int sampleLocalZ = Math.min(sampleZIndex * adaptiveSampleStep, 16); adaptivePlaneDensity[rowOffset + sampleZIndex] = sampleDensityNoWarpNoModules(x, y, z0 + sampleLocalZ); } @@ -1099,11 +1101,13 @@ public class IrisCaveCarver3D { double[] adaptivePlaneDensity = scratch.adaptivePlaneDensity; int axisCells = (16 + adaptiveSampleStep - 1) / adaptiveSampleStep; int axisSamples = axisCells + 1; - for (int sampleXIndex = 0; sampleXIndex < axisSamples; sampleXIndex++) { + int[] adaptivePlaneSampleBounds = scratch.adaptivePlaneSampleBounds; + prepareAdaptivePlaneSampleBounds(planeColumnIndices, planeCount, adaptiveSampleStep, adaptivePlaneSampleBounds, axisCells); + for (int sampleXIndex = adaptivePlaneSampleBounds[0]; sampleXIndex <= adaptivePlaneSampleBounds[1]; sampleXIndex++) { int sampleLocalX = Math.min(sampleXIndex * adaptiveSampleStep, 16); int x = x0 + sampleLocalX; int rowOffset = sampleXIndex * axisSamples; - for (int sampleZIndex = 0; sampleZIndex < axisSamples; sampleZIndex++) { + for (int sampleZIndex = adaptivePlaneSampleBounds[2]; sampleZIndex <= adaptivePlaneSampleBounds[3]; sampleZIndex++) { int sampleLocalZ = Math.min(sampleZIndex * adaptiveSampleStep, 16); adaptivePlaneDensity[rowOffset + sampleZIndex] = sampleDensityNoWarpNoModules(x, y, z0 + sampleLocalZ); } @@ -1144,11 +1148,13 @@ public class IrisCaveCarver3D { double[] adaptivePlaneDensity = scratch.adaptivePlaneDensity; int axisCells = (16 + adaptiveSampleStep - 1) / adaptiveSampleStep; int axisSamples = axisCells + 1; - for (int sampleXIndex = 0; sampleXIndex < axisSamples; sampleXIndex++) { + int[] adaptivePlaneSampleBounds = scratch.adaptivePlaneSampleBounds; + prepareAdaptivePlaneSampleBounds(planeColumnIndices, planeCount, adaptiveSampleStep, adaptivePlaneSampleBounds, axisCells); + for (int sampleXIndex = adaptivePlaneSampleBounds[0]; sampleXIndex <= adaptivePlaneSampleBounds[1]; sampleXIndex++) { int sampleLocalX = Math.min(sampleXIndex * adaptiveSampleStep, 16); int x = x0 + sampleLocalX; int rowOffset = sampleXIndex * axisSamples; - for (int sampleZIndex = 0; sampleZIndex < axisSamples; sampleZIndex++) { + for (int sampleZIndex = adaptivePlaneSampleBounds[2]; sampleZIndex <= adaptivePlaneSampleBounds[3]; sampleZIndex++) { int sampleLocalZ = Math.min(sampleZIndex * adaptiveSampleStep, 16); adaptivePlaneDensity[rowOffset + sampleZIndex] = sampleDensityWarpOnly(x, y, z0 + sampleLocalZ); } @@ -1194,17 +1200,25 @@ public class IrisCaveCarver3D { double[] adaptivePlaneDensity = scratch.adaptivePlaneDensity; int axisCells = (16 + adaptiveSampleStep - 1) / adaptiveSampleStep; int axisSamples = axisCells + 1; - for (int sampleXIndex = 0; sampleXIndex < axisSamples; sampleXIndex++) { + int[] adaptivePlaneSampleBounds = scratch.adaptivePlaneSampleBounds; + prepareAdaptivePlaneSampleBounds(planeColumnIndices, planeCount, adaptiveSampleStep, adaptivePlaneSampleBounds, axisCells); + for (int sampleXIndex = adaptivePlaneSampleBounds[0]; sampleXIndex <= adaptivePlaneSampleBounds[1]; sampleXIndex++) { int sampleLocalX = Math.min(sampleXIndex * adaptiveSampleStep, 16); int x = x0 + sampleLocalX; int rowOffset = sampleXIndex * axisSamples; - for (int sampleZIndex = 0; sampleZIndex < axisSamples; sampleZIndex++) { + for (int sampleZIndex = adaptivePlaneSampleBounds[2]; sampleZIndex <= adaptivePlaneSampleBounds[3]; sampleZIndex++) { int sampleLocalZ = Math.min(sampleZIndex * adaptiveSampleStep, 16); - adaptivePlaneDensity[rowOffset + sampleZIndex] = sampleDensityWarpOnly(x, y, z0 + sampleLocalZ); + adaptivePlaneDensity[rowOffset + sampleZIndex] = sampleDensityWarpModules( + x, + y, + z0 + sampleLocalZ, + localModules, + activeModuleCount + ); } } - classifyAdaptivePlaneColumnsWarpModules( + classifyAdaptivePlaneColumnsWarpModulesSampled( x0, z0, y, @@ -1259,6 +1273,10 @@ public class IrisCaveCarver3D { double threshold = planeThresholdLimit[planeIndex] * inverseNormalization; double predictedDensity = adaptivePlanePrediction[planeIndex]; double ambiguityMargin = adaptivePlaneAmbiguity[planeIndex]; + if (isAdaptivePlaneSampleAligned(localX, localZ, adaptiveSampleStep)) { + planeCarve[planeIndex] = predictedDensity <= threshold; + continue; + } if (predictedDensity <= threshold - ambiguityMargin) { planeCarve[planeIndex] = true; continue; @@ -1313,6 +1331,20 @@ public class IrisCaveCarver3D { double threshold = planeThresholdLimit[planeIndex] * inverseNormalization; double predictedDensity = adaptivePlanePrediction[planeIndex]; double ambiguityMargin = adaptivePlaneAmbiguity[planeIndex]; + if (isAdaptivePlaneSampleAligned(localX, localZ, adaptiveSampleStep)) { + planeCarve[planeIndex] = classifyDensityPointNoWarpModulesFromExactDensity( + x0 + localX, + y, + z0 + localZ, + threshold, + predictedDensity, + localModules, + activeModuleCount, + remainingMin, + remainingMax + ); + continue; + } if ((predictedDensity + maxRemaining) <= threshold - ambiguityMargin) { planeCarve[planeIndex] = true; continue; @@ -1370,6 +1402,10 @@ public class IrisCaveCarver3D { double threshold = planeThresholdLimit[planeIndex] * inverseNormalization; double predictedDensity = adaptivePlanePrediction[planeIndex]; double ambiguityMargin = adaptivePlaneAmbiguity[planeIndex]; + if (isAdaptivePlaneSampleAligned(localX, localZ, adaptiveSampleStep)) { + planeCarve[planeIndex] = predictedDensity <= threshold; + continue; + } if (predictedDensity <= threshold - ambiguityMargin) { planeCarve[planeIndex] = true; continue; @@ -1424,6 +1460,20 @@ public class IrisCaveCarver3D { double threshold = planeThresholdLimit[planeIndex] * inverseNormalization; double predictedDensity = adaptivePlanePrediction[planeIndex]; double ambiguityMargin = adaptivePlaneAmbiguity[planeIndex]; + if (isAdaptivePlaneSampleAligned(localX, localZ, adaptiveSampleStep)) { + planeCarve[planeIndex] = classifyDensityPointWarpModulesFromExactDensity( + x0 + localX, + y, + z0 + localZ, + threshold, + predictedDensity, + localModules, + activeModuleCount, + remainingMin, + remainingMax + ); + continue; + } if ((predictedDensity + maxRemaining) <= threshold - ambiguityMargin) { planeCarve[planeIndex] = true; continue; @@ -1446,6 +1496,71 @@ public class IrisCaveCarver3D { } } + private void classifyAdaptivePlaneColumnsWarpModulesSampled( + int x0, + int z0, + int y, + int[] planeColumnIndices, + double[] planeThresholdLimit, + int planeCount, + boolean[] planeCarve, + int adaptiveSampleStep, + double adaptiveThresholdMargin, + double[] adaptivePlaneDensity, + int axisCells, + int axisSamples, + ModuleState[] localModules, + int activeModuleCount, + double[] remainingMin, + double[] remainingMax + ) { + Scratch scratch = SCRATCH.get(); + double[] adaptivePlanePrediction = scratch.adaptivePlanePrediction; + double[] adaptivePlaneAmbiguity = scratch.adaptivePlaneAmbiguity; + prepareAdaptivePlaneColumns( + planeColumnIndices, + planeCount, + adaptiveSampleStep, + adaptiveThresholdMargin, + adaptivePlaneDensity, + axisCells, + axisSamples, + adaptivePlanePrediction, + adaptivePlaneAmbiguity + ); + for (int planeIndex = 0; planeIndex < planeCount; planeIndex++) { + int columnIndex = planeColumnIndices[planeIndex]; + int localX = PowerOfTwoCoordinates.unpackLocal16X(columnIndex); + int localZ = columnIndex & 15; + double threshold = planeThresholdLimit[planeIndex] * inverseNormalization; + double predictedDensity = adaptivePlanePrediction[planeIndex]; + double ambiguityMargin = adaptivePlaneAmbiguity[planeIndex]; + if (isAdaptivePlaneSampleAligned(localX, localZ, adaptiveSampleStep)) { + planeCarve[planeIndex] = predictedDensity <= threshold; + continue; + } + if (predictedDensity <= threshold - ambiguityMargin) { + planeCarve[planeIndex] = true; + continue; + } + if (predictedDensity > threshold + ambiguityMargin) { + planeCarve[planeIndex] = false; + continue; + } + + planeCarve[planeIndex] = classifyDensityPointWarpModules( + x0 + localX, + y, + z0 + localZ, + planeThresholdLimit[planeIndex], + localModules, + activeModuleCount, + remainingMin, + remainingMax + ); + } + } + private boolean classifyDensityPointNoWarpNoModules(int x, int y, int z, double thresholdLimit) { double density = baseDensity.noiseFastSigned3D(x, y, z) * baseWeight; if ((density + detailMinContribution) > thresholdLimit) { @@ -1568,6 +1683,127 @@ public class IrisCaveCarver3D { return density <= thresholdLimit; } + private boolean classifyDensityPointNoWarpModulesFromExactDensity( + int x, + int y, + int z, + double threshold, + double density, + ModuleState[] localModules, + int activeModuleCount, + double[] remainingMin, + double[] remainingMax + ) { + if (activeModuleCount == 0) { + return density <= threshold; + } + + double minRemaining = remainingMin[0] * inverseNormalization; + double maxRemaining = remainingMax[0] * inverseNormalization; + if ((density + minRemaining) > threshold) { + return false; + } + if ((density + maxRemaining) <= threshold) { + return true; + } + + for (int moduleIndex = 0; moduleIndex < activeModuleCount; moduleIndex++) { + density += localModules[moduleIndex].sample(x, y, z) * inverseNormalization; + if ((density + (remainingMin[moduleIndex + 1] * inverseNormalization)) > threshold) { + return false; + } + if ((density + (remainingMax[moduleIndex + 1] * inverseNormalization)) <= threshold) { + return true; + } + } + + return density <= threshold; + } + + private boolean classifyDensityPointWarpModulesFromExactDensity( + int x, + int y, + int z, + double threshold, + double density, + ModuleState[] localModules, + int activeModuleCount, + double[] remainingMin, + double[] remainingMax + ) { + if (activeModuleCount == 0) { + return density <= threshold; + } + + double minRemaining = remainingMin[0] * inverseNormalization; + double maxRemaining = remainingMax[0] * inverseNormalization; + if ((density + minRemaining) > threshold) { + return false; + } + if ((density + maxRemaining) <= threshold) { + return true; + } + + double warpA = warpDensity.noiseFastSigned3D(x, y, z); + double warpB = warpDensity.noiseFastSigned3D(x + 31.37D, y - 17.21D, z + 23.91D); + double warpedX = x + (warpA * warpStrength); + double warpedY = y + (warpB * warpStrength); + double warpedZ = z + ((warpA - warpB) * 0.5D * warpStrength); + for (int moduleIndex = 0; moduleIndex < activeModuleCount; moduleIndex++) { + density += localModules[moduleIndex].sample(warpedX, warpedY, warpedZ) * inverseNormalization; + if ((density + (remainingMin[moduleIndex + 1] * inverseNormalization)) > threshold) { + return false; + } + if ((density + (remainingMax[moduleIndex + 1] * inverseNormalization)) <= threshold) { + return true; + } + } + + return density <= threshold; + } + + private boolean isAdaptivePlaneSampleAligned(int localX, int localZ, int adaptiveSampleStep) { + return localX % adaptiveSampleStep == 0 && localZ % adaptiveSampleStep == 0; + } + + private void prepareAdaptivePlaneSampleBounds( + int[] planeColumnIndices, + int planeCount, + int adaptiveSampleStep, + int[] adaptivePlaneSampleBounds, + int axisCells + ) { + int minSampleX = axisCells; + int maxSampleX = 0; + int minSampleZ = axisCells; + int maxSampleZ = 0; + + for (int planeIndex = 0; planeIndex < planeCount; planeIndex++) { + int columnIndex = planeColumnIndices[planeIndex]; + int localX = PowerOfTwoCoordinates.unpackLocal16X(columnIndex); + int localZ = columnIndex & 15; + int sampleX = Math.min(localX / adaptiveSampleStep, axisCells - 1); + int sampleZ = Math.min(localZ / adaptiveSampleStep, axisCells - 1); + if (sampleX < minSampleX) { + minSampleX = sampleX; + } + if (sampleX + 1 > maxSampleX) { + maxSampleX = sampleX + 1; + } + if (sampleZ < minSampleZ) { + minSampleZ = sampleZ; + } + if (sampleZ + 1 > maxSampleZ) { + maxSampleZ = sampleZ + 1; + } + } + + adaptivePlaneSampleBounds[0] = minSampleX; + adaptivePlaneSampleBounds[1] = maxSampleX; + adaptivePlaneSampleBounds[2] = minSampleZ; + adaptivePlaneSampleBounds[3] = maxSampleZ; + } + private void prepareAdaptivePlaneColumns( int[] planeColumnIndices, int planeCount, @@ -1654,6 +1890,10 @@ public class IrisCaveCarver3D { } ModuleState[] localModules = scratch.activeModules; + return sampleDensityWarpModules(x, y, z, localModules, activeModuleCount); + } + + private double sampleDensityWarpModules(int x, int y, int z, ModuleState[] localModules, int activeModuleCount) { double warpA = warpDensity.noiseFastSigned3D(x, y, z); double warpB = warpDensity.noiseFastSigned3D(x + 31.37D, y - 17.21D, z + 23.91D); double warpedX = x + (warpA * warpStrength); @@ -1882,6 +2122,7 @@ public class IrisCaveCarver3D { private final double[] adaptivePlaneDensity = new double[81]; private final double[] adaptivePlanePrediction = new double[256]; private final double[] adaptivePlaneAmbiguity = new double[256]; + private final int[] adaptivePlaneSampleBounds = new int[4]; private final int[] tileIndices = new int[4]; private final int[] tileLocalX = new int[4]; private final int[] tileLocalZ = new int[4]; diff --git a/core/src/main/java/art/arcane/iris/engine/mantle/components/MantleCarvingComponent.java b/core/src/main/java/art/arcane/iris/engine/mantle/components/MantleCarvingComponent.java index 99a87b1e8..c3a968491 100644 --- a/core/src/main/java/art/arcane/iris/engine/mantle/components/MantleCarvingComponent.java +++ b/core/src/main/java/art/arcane/iris/engine/mantle/components/MantleCarvingComponent.java @@ -31,11 +31,12 @@ import art.arcane.iris.engine.object.IrisDimensionCarvingResolver; import art.arcane.iris.engine.object.IrisRegion; import art.arcane.iris.engine.object.IrisRange; import art.arcane.iris.util.project.context.ChunkContext; +import art.arcane.iris.util.project.stream.ProceduralStream; +import art.arcane.iris.util.project.stream.utility.ChunkFillableDoubleStream2D; import art.arcane.volmlib.util.documentation.ChunkCoordinates; import art.arcane.volmlib.util.mantle.flag.ReservedFlag; import art.arcane.volmlib.util.math.PowerOfTwoCoordinates; import art.arcane.volmlib.util.scheduling.PrecisionStopwatch; -import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import java.util.ArrayList; import java.util.Arrays; @@ -82,11 +83,10 @@ public class MantleCarvingComponent extends IrisMantleComponent { public void generateLayer(MantleWriter writer, int x, int z, ChunkContext context) { IrisComplex complex = context.getComplex(); IrisDimensionCarvingResolver.State resolverState = new IrisDimensionCarvingResolver.State(); - Long2ObjectOpenHashMap caveBiomeCache = new Long2ObjectOpenHashMap<>(FIELD_SIZE * FIELD_SIZE); BlendScratch blendScratch = BLEND_SCRATCH.get(); int[] chunkSurfaceHeights = prepareChunkSurfaceHeights(x, z, context, blendScratch.chunkSurfaceHeights); PrecisionStopwatch resolveStopwatch = PrecisionStopwatch.start(); - List weightedProfiles = resolveWeightedProfiles(x, z, complex, resolverState, caveBiomeCache); + List weightedProfiles = resolveWeightedProfiles(x, z, complex, resolverState); getEngineMantle().getEngine().getMetrics().getCarveResolve().put(resolveStopwatch.getMilliseconds()); for (WeightedProfile weightedProfile : weightedProfiles) { carveProfile(weightedProfile, writer, x, z, chunkSurfaceHeights); @@ -99,7 +99,7 @@ public class MantleCarvingComponent extends IrisMantleComponent { carver.carve(writer, cx, cz, weightedProfile.columnWeights, MIN_WEIGHT, THRESHOLD_PENALTY, weightedProfile.worldYRange, chunkSurfaceHeights); } - private List resolveWeightedProfiles(int chunkX, int chunkZ, IrisComplex complex, IrisDimensionCarvingResolver.State resolverState, Long2ObjectOpenHashMap caveBiomeCache) { + private List resolveWeightedProfiles(int chunkX, int chunkZ, IrisComplex complex, IrisDimensionCarvingResolver.State resolverState) { BlendScratch blendScratch = BLEND_SCRATCH.get(); IrisCaveProfile[] profileField = blendScratch.profileField; Map columnProfileWeights = blendScratch.columnProfileWeights; @@ -107,7 +107,7 @@ public class MantleCarvingComponent extends IrisMantleComponent { IrisCaveProfile[] kernelProfiles = blendScratch.kernelProfiles; double[] kernelProfileWeights = blendScratch.kernelProfileWeights; activeProfiles.clear(); - fillProfileField(profileField, chunkX, chunkZ, complex, resolverState, caveBiomeCache); + fillProfileField(profileField, chunkX, chunkZ, complex, resolverState, blendScratch); for (int localX = 0; localX < CHUNK_SIZE; localX++) { for (int localZ = 0; localZ < CHUNK_SIZE; localZ++) { @@ -268,15 +268,25 @@ public class MantleCarvingComponent extends IrisMantleComponent { } } - private void fillProfileField(IrisCaveProfile[] profileField, int chunkX, int chunkZ, IrisComplex complex, IrisDimensionCarvingResolver.State resolverState, Long2ObjectOpenHashMap caveBiomeCache) { + private void fillProfileField(IrisCaveProfile[] profileField, int chunkX, int chunkZ, IrisComplex complex, IrisDimensionCarvingResolver.State resolverState, BlendScratch blendScratch) { int startX = PowerOfTwoCoordinates.chunkToBlock(chunkX) - BLEND_RADIUS; int startZ = PowerOfTwoCoordinates.chunkToBlock(chunkZ) - BLEND_RADIUS; + prefillProfileFieldSamples(startX, startZ, complex, blendScratch); for (int fieldX = 0; fieldX < FIELD_SIZE; fieldX++) { int worldX = startX + fieldX; for (int fieldZ = 0; fieldZ < FIELD_SIZE; fieldZ++) { int worldZ = startZ + fieldZ; - profileField[(fieldX * FIELD_SIZE) + fieldZ] = resolveColumnProfile(worldX, worldZ, complex, resolverState, caveBiomeCache); + int fieldIndex = (fieldX * FIELD_SIZE) + fieldZ; + profileField[fieldIndex] = resolveColumnProfile( + worldX, + worldZ, + blendScratch.fieldSurfaceHeights[fieldIndex], + blendScratch.fieldRegions[fieldIndex], + blendScratch.fieldSurfaceBiomes[fieldIndex], + blendScratch.fieldCaveBiomes[fieldIndex], + resolverState + ); } } } @@ -291,14 +301,21 @@ public class MantleCarvingComponent extends IrisMantleComponent { return -1; } - private IrisCaveProfile resolveColumnProfile(int worldX, int worldZ, IrisComplex complex, IrisDimensionCarvingResolver.State resolverState, Long2ObjectOpenHashMap caveBiomeCache) { + private IrisCaveProfile resolveColumnProfile( + int worldX, + int worldZ, + double surfaceHeight, + IrisRegion region, + IrisBiome surfaceBiome, + IrisBiome caveBiome, + IrisDimensionCarvingResolver.State resolverState + ) { IrisCaveProfile resolved = null; IrisCaveProfile dimensionProfile = getDimension().getCaveProfile(); if (isProfileEnabled(dimensionProfile)) { resolved = dimensionProfile; } - IrisRegion region = complex.getRegionStream().get(worldX, worldZ); if (region != null) { IrisCaveProfile regionProfile = region.getCaveProfile(); if (isProfileEnabled(regionProfile)) { @@ -306,7 +323,6 @@ public class MantleCarvingComponent extends IrisMantleComponent { } } - IrisBiome surfaceBiome = complex.getTrueBiomeStream().get(worldX, worldZ); if (surfaceBiome != null) { IrisCaveProfile surfaceProfile = surfaceBiome.getCaveProfile(); if (isProfileEnabled(surfaceProfile)) { @@ -314,18 +330,36 @@ public class MantleCarvingComponent extends IrisMantleComponent { } } - int surfaceY = getEngineMantle().getEngine().getHeight(worldX, worldZ, true); - int sampleY = Math.max(1, surfaceY - 56); - long cacheKey = Cache.key(worldX, worldZ); - IrisBiome caveBiome = caveBiomeCache.get(cacheKey); - if (caveBiome == null) { - caveBiome = getEngineMantle().getEngine().getCaveBiome(worldX, sampleY, worldZ, resolverState); - if (caveBiome != null) { - caveBiomeCache.put(cacheKey, caveBiome); + int roundedSurfaceY = (int) Math.round(surfaceHeight); + int sampleY = Math.max(1, roundedSurfaceY - 56); + int worldY = sampleY + getEngineMantle().getEngine().getWorld().minHeight(); + IrisDimensionCarvingEntry rootCarvingEntry = IrisDimensionCarvingResolver.resolveRootEntry(getEngineMantle().getEngine(), worldY, resolverState); + IrisBiome resolvedCarvingBiome = null; + if (rootCarvingEntry != null) { + IrisDimensionCarvingEntry resolvedCarvingEntry = IrisDimensionCarvingResolver.resolveFromRoot(getEngineMantle().getEngine(), rootCarvingEntry, worldX, worldZ, resolverState); + resolvedCarvingBiome = IrisDimensionCarvingResolver.resolveEntryBiome(getEngineMantle().getEngine(), resolvedCarvingEntry, resolverState); + if (resolvedCarvingBiome != null) { + caveBiome = resolvedCarvingBiome; } } if (caveBiome != null) { - IrisCaveProfile caveProfile = caveBiome.getCaveProfile(); + IrisBiome effectiveCaveBiome = caveBiome; + if (resolvedCarvingBiome == null && surfaceBiome != null) { + if (surfaceBiome != null) { + int truncatedSurfaceY = (int) surfaceHeight; + int depthBelowSurface = truncatedSurfaceY - sampleY; + if (depthBelowSurface <= 0) { + effectiveCaveBiome = surfaceBiome; + } else { + int minDepth = Math.max(0, caveBiome.getCaveMinDepthBelowSurface()); + if (depthBelowSurface < minDepth) { + effectiveCaveBiome = surfaceBiome; + } + } + } + } + + IrisCaveProfile caveProfile = effectiveCaveBiome.getCaveProfile(); if (isProfileEnabled(caveProfile)) { resolved = caveProfile; } @@ -334,6 +368,31 @@ public class MantleCarvingComponent extends IrisMantleComponent { return resolved; } + private void prefillProfileFieldSamples(int startX, int startZ, IrisComplex complex, BlendScratch blendScratch) { + fillFieldHeights(complex.getHeightStream(), startX, startZ, blendScratch.fieldSurfaceHeights); + fillFieldObjects(complex.getRegionStream(), startX, startZ, blendScratch.fieldRegions); + fillFieldObjects(complex.getTrueBiomeStream(), startX, startZ, blendScratch.fieldSurfaceBiomes); + fillFieldObjects(complex.getCaveBiomeStream(), startX, startZ, blendScratch.fieldCaveBiomes); + } + + private void fillFieldObjects(ProceduralStream stream, int startX, int startZ, T[] target) { + for (int fieldX = 0; fieldX < FIELD_SIZE; fieldX++) { + int worldX = startX + fieldX; + for (int fieldZ = 0; fieldZ < FIELD_SIZE; fieldZ++) { + target[(fieldX * FIELD_SIZE) + fieldZ] = stream.get(worldX, startZ + fieldZ); + } + } + } + + private void fillFieldHeights(ProceduralStream stream, int startX, int startZ, double[] target) { + for (int fieldX = 0; fieldX < FIELD_SIZE; fieldX++) { + int worldX = startX + fieldX; + for (int fieldZ = 0; fieldZ < FIELD_SIZE; fieldZ++) { + target[(fieldX * FIELD_SIZE) + fieldZ] = stream.getDouble(worldX, startZ + fieldZ); + } + } + } + private IrisCaveCarver3D getCarver(IrisCaveProfile profile) { synchronized (profileCarvers) { IrisCaveCarver3D carver = profileCarvers.get(profile); @@ -363,6 +422,14 @@ public class MantleCarvingComponent extends IrisMantleComponent { && context.getHeight() != null && context.getX() == baseX && context.getZ() == baseZ; + double[] cachedChunkHeights = null; + if (!useContextHeight && context != null) { + ProceduralStream heightStream = context.getComplex().getHeightStream(); + if (heightStream instanceof ChunkFillableDoubleStream2D cachedHeightStream) { + cachedChunkHeights = BLEND_SCRATCH.get().chunkSurfaceHeightSamples; + cachedHeightStream.fillChunkDoubles(baseX, baseZ, cachedChunkHeights); + } + } for (int localX = 0; localX < CHUNK_SIZE; localX++) { int worldX = baseX + localX; for (int localZ = 0; localZ < CHUNK_SIZE; localZ++) { @@ -375,6 +442,10 @@ public class MantleCarvingComponent extends IrisMantleComponent { continue; } } + if (cachedChunkHeights != null) { + surfaceHeights[columnIndex] = (int) Math.round(cachedChunkHeights[columnIndex]); + continue; + } surfaceHeights[columnIndex] = getEngineMantle().getEngine().getHeight(worldX, worldZ); } } @@ -510,6 +581,11 @@ public class MantleCarvingComponent extends IrisMantleComponent { private final IdentityHashMap columnProfileWeights = new IdentityHashMap<>(); private final IdentityHashMap dimensionColumnPlans = new IdentityHashMap<>(); private final IdentityHashMap activeProfiles = new IdentityHashMap<>(); + private final double[] fieldSurfaceHeights = new double[FIELD_SIZE * FIELD_SIZE]; + private final IrisRegion[] fieldRegions = new IrisRegion[FIELD_SIZE * FIELD_SIZE]; + private final IrisBiome[] fieldSurfaceBiomes = new IrisBiome[FIELD_SIZE * FIELD_SIZE]; + private final IrisBiome[] fieldCaveBiomes = new IrisBiome[FIELD_SIZE * FIELD_SIZE]; private final int[] chunkSurfaceHeights = new int[CHUNK_AREA]; + private final double[] chunkSurfaceHeightSamples = new double[CHUNK_AREA]; } } diff --git a/core/src/main/java/art/arcane/iris/util/project/stream/utility/CachedDoubleStream2D.java b/core/src/main/java/art/arcane/iris/util/project/stream/utility/CachedDoubleStream2D.java index 574f824cc..c99d9bb6c 100644 --- a/core/src/main/java/art/arcane/iris/util/project/stream/utility/CachedDoubleStream2D.java +++ b/core/src/main/java/art/arcane/iris/util/project/stream/utility/CachedDoubleStream2D.java @@ -9,7 +9,7 @@ import art.arcane.iris.util.project.stream.ProceduralStream; import art.arcane.volmlib.util.cache.WorldCache2DDouble; import art.arcane.volmlib.util.data.KCache; -public class CachedDoubleStream2D extends BasicStream implements ProceduralStream, MeteredCache, ChunkFillableStream2D { +public class CachedDoubleStream2D extends BasicStream implements ProceduralStream, MeteredCache, ChunkFillableStream2D, ChunkFillableDoubleStream2D { private final ProceduralStream stream; private final WorldCache2DDouble cache; private final Engine engine; @@ -73,4 +73,11 @@ public class CachedDoubleStream2D extends BasicStream implements Procedu int chunkZ = worldZ >> 4; cache.fillChunk(chunkX, chunkZ, target); } + + @Override + public void fillChunkDoubles(int worldX, int worldZ, double[] target) { + int chunkX = worldX >> 4; + int chunkZ = worldZ >> 4; + cache.fillChunk(chunkX, chunkZ, target); + } } diff --git a/core/src/main/java/art/arcane/iris/util/project/stream/utility/ChunkFillableDoubleStream2D.java b/core/src/main/java/art/arcane/iris/util/project/stream/utility/ChunkFillableDoubleStream2D.java new file mode 100644 index 000000000..ed88a1f62 --- /dev/null +++ b/core/src/main/java/art/arcane/iris/util/project/stream/utility/ChunkFillableDoubleStream2D.java @@ -0,0 +1,5 @@ +package art.arcane.iris.util.project.stream.utility; + +public interface ChunkFillableDoubleStream2D { + void fillChunkDoubles(int worldX, int worldZ, double[] target); +} diff --git a/core/src/test/java/art/arcane/iris/engine/framework/EngineModeMaintenanceTest.java b/core/src/test/java/art/arcane/iris/engine/framework/EngineModeMaintenanceTest.java new file mode 100644 index 000000000..fe1fa49ca --- /dev/null +++ b/core/src/test/java/art/arcane/iris/engine/framework/EngineModeMaintenanceTest.java @@ -0,0 +1,24 @@ +package art.arcane.iris.engine.framework; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class EngineModeMaintenanceTest { + @Test + public void maintenanceDisablesContextCacheWithoutActivePregen() { + assertTrue(EngineMode.shouldDisableContextCacheForMaintenance(true, false)); + } + + @Test + public void maintenanceKeepsContextCacheForActivePregenWorld() { + assertFalse(EngineMode.shouldDisableContextCacheForMaintenance(true, true)); + } + + @Test + public void noMaintenanceKeepsContextCacheEnabled() { + assertFalse(EngineMode.shouldDisableContextCacheForMaintenance(false, false)); + assertFalse(EngineMode.shouldDisableContextCacheForMaintenance(false, true)); + } +}