From 6a5616abae857ba86a0ad4a0bb06aadb3e4a5156 Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Wed, 15 Apr 2026 15:52:18 -0400 Subject: [PATCH] d --- core/plugins/Iris/cache/instance | 2 +- .../art/arcane/iris/engine/IrisComplex.java | 6 +- .../engine/actuator/IrisDecorantActuator.java | 2 +- .../actuator/IrisTerrainNormalActuator.java | 7 +- .../iris/engine/mantle/MatterGenerator.java | 18 ++++- .../components/MantleCarvingComponent.java | 7 +- .../engine/modifier/IrisCarveModifier.java | 10 ++- .../engine/modifier/IrisDepositModifier.java | 4 +- .../arcane/iris/engine/object/IrisBiome.java | 34 +++++++-- .../iris/engine/object/IrisDimension.java | 36 +++++++-- .../arcane/iris/engine/object/IrisRegion.java | 34 +++++++-- .../util/project/context/ChunkContext.java | 37 ++++++++-- .../context/ChunkedDoubleDataCache.java | 73 +++++++++++++++++++ .../context/ChunkContextPrefillPlanTest.java | 4 +- 14 files changed, 228 insertions(+), 46 deletions(-) create mode 100644 core/src/main/java/art/arcane/iris/util/project/context/ChunkedDoubleDataCache.java diff --git a/core/plugins/Iris/cache/instance b/core/plugins/Iris/cache/instance index c2362f085..1a937a41b 100644 --- a/core/plugins/Iris/cache/instance +++ b/core/plugins/Iris/cache/instance @@ -1 +1 @@ --1511497018 \ No newline at end of file +1982643195 \ No newline at end of file diff --git a/core/src/main/java/art/arcane/iris/engine/IrisComplex.java b/core/src/main/java/art/arcane/iris/engine/IrisComplex.java index a599781a8..29ef5902e 100644 --- a/core/src/main/java/art/arcane/iris/engine/IrisComplex.java +++ b/core/src/main/java/art/arcane/iris/engine/IrisComplex.java @@ -201,9 +201,9 @@ public class IrisComplex implements DataProvider { IrisBiome b = focusBiome != null ? focusBiome : baseBiomeStream.get(x, z); return getHeight(engine, b, x, z, engine.getSeedManager().getHeight()); }, Interpolated.DOUBLE).cache2DDouble("heightStream", engine, cacheSize).waste("Height Stream"); - roundedHeighteightStream = heightStream.contextInjecting((c, x, z) -> IrisContext.getOr(engine).getChunkContext().getHeight().get(x, z)) + roundedHeighteightStream = heightStream.contextInjecting((c, x, z) -> IrisContext.getOr(engine).getChunkContext().getHeight().getDouble(x, z)) .round().waste("Rounded Height Stream"); - slopeStream = heightStream.contextInjecting((c, x, z) -> IrisContext.getOr(engine).getChunkContext().getHeight().get(x, z)) + slopeStream = heightStream.contextInjecting((c, x, z) -> IrisContext.getOr(engine).getChunkContext().getHeight().getDouble(x, z)) .slope(3).cache2DDouble("slopeStream", engine, cacheSize).waste("Slope Stream"); trueBiomeStream = focusBiome != null ? ProceduralStream.of((x, y) -> focusBiome, Interpolated.of(a -> 0D, b -> focusBiome)) @@ -214,7 +214,7 @@ public class IrisComplex implements DataProvider { .cache2D("trueBiomeStream", engine, cacheSize).waste("True Biome Stream"); trueBiomeDerivativeStream = trueBiomeStream.contextInjecting((c, x, z) -> IrisContext.getOr(engine).getChunkContext().getBiome().get(x, z)) .convert(IrisBiome::getDerivative).cache2D("trueBiomeDerivativeStream", engine, cacheSize).waste("True Biome Derivative Stream"); - heightFluidStream = heightStream.contextInjecting((c, x, z) -> IrisContext.getOr(engine).getChunkContext().getHeight().get(x, z)) + heightFluidStream = heightStream.contextInjecting((c, x, z) -> IrisContext.getOr(engine).getChunkContext().getHeight().getDouble(x, z)) .max(fluidHeight).cache2DDouble("heightFluidStream", engine, cacheSize).waste("Height Fluid Stream"); maxHeightStream = ProceduralStream.ofDouble((x, z) -> height).waste("Max Height Stream"); terrainSurfaceDecoration = trueBiomeStream.contextInjecting((c, x, z) -> IrisContext.getOr(engine).getChunkContext().getBiome().get(x, z)) diff --git a/core/src/main/java/art/arcane/iris/engine/actuator/IrisDecorantActuator.java b/core/src/main/java/art/arcane/iris/engine/actuator/IrisDecorantActuator.java index ee510fe56..9c09687e1 100644 --- a/core/src/main/java/art/arcane/iris/engine/actuator/IrisDecorantActuator.java +++ b/core/src/main/java/art/arcane/iris/engine/actuator/IrisDecorantActuator.java @@ -79,7 +79,7 @@ public class IrisDecorantActuator extends EngineAssignedActuator { int emptyFor = 0; int lastSolid = 0; realZ = Math.round(z + j); - height = (int) Math.round(context.getHeight().get(i, j)); + height = context.getRoundedHeight(i, j); biome = context.getBiome().get(i, j); cave = shouldRay ? context.getCave().get(i, j) : null; diff --git a/core/src/main/java/art/arcane/iris/engine/actuator/IrisTerrainNormalActuator.java b/core/src/main/java/art/arcane/iris/engine/actuator/IrisTerrainNormalActuator.java index c6c186e7a..d204b4a8d 100644 --- a/core/src/main/java/art/arcane/iris/engine/actuator/IrisTerrainNormalActuator.java +++ b/core/src/main/java/art/arcane/iris/engine/actuator/IrisTerrainNormalActuator.java @@ -25,6 +25,7 @@ import art.arcane.iris.engine.object.IrisRegion; import art.arcane.volmlib.util.collection.KList; import art.arcane.iris.util.project.context.ChunkedDataCache; import art.arcane.iris.util.project.context.ChunkContext; +import art.arcane.iris.util.project.context.ChunkedDoubleDataCache; import art.arcane.volmlib.util.documentation.BlockCoordinates; import art.arcane.iris.util.project.hunk.Hunk; import art.arcane.volmlib.util.math.RNG; @@ -89,7 +90,7 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator realZ = zf + z; biome = context.getBiome().get(xf, zf); region = context.getRegion().get(xf, zf); - he = (int) Math.round(Math.min(h.getHeight(), context.getHeight().get(xf, zf))); + he = Math.min(h.getHeight(), context.getRoundedHeight(xf, zf)); hf = Math.round(Math.max(Math.min(h.getHeight(), getDimension().getFluidHeight()), he)); if (hf < 0) { @@ -174,7 +175,7 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator boolean bedrockEnabled = getDimension().isBedrock(); ChunkedDataCache biomeCache = context.getBiome(); ChunkedDataCache regionCache = context.getRegion(); - ChunkedDataCache heightCache = context.getHeight(); + ChunkedDoubleDataCache heightCache = context.getHeight(); ChunkedDataCache fluidCache = context.getFluid(); ChunkedDataCache rockCache = context.getRock(); int realX = xf + x; @@ -183,7 +184,7 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator int realZ = zf + z; IrisBiome biome = biomeCache.get(xf, zf); IrisRegion region = regionCache.get(xf, zf); - int he = (int) Math.round(Math.min(chunkHeight, heightCache.get(xf, zf))); + int he = Math.min(chunkHeight, context.getRoundedHeight(xf, zf)); int hf = Math.round(Math.max(Math.min(chunkHeight, fluidHeight), he)); if (hf < 0) { continue; diff --git a/core/src/main/java/art/arcane/iris/engine/mantle/MatterGenerator.java b/core/src/main/java/art/arcane/iris/engine/mantle/MatterGenerator.java index 323dd159e..e19c752c4 100644 --- a/core/src/main/java/art/arcane/iris/engine/mantle/MatterGenerator.java +++ b/core/src/main/java/art/arcane/iris/engine/mantle/MatterGenerator.java @@ -59,6 +59,7 @@ public interface MatterGenerator { boolean optimizedRegen = forceRegen && !IrisSettings.get().getGeneral().isDebug() && regenPassKey != null; int writeRadius = optimizedRegen ? Math.min(getRadius(), getRealRadius()) : getRadius(); Set clearedChunks = optimizedRegen ? getRegenPassSet(REGEN_CLEARED_CHUNKS_BY_PASS, regenPassKey) : new HashSet<>(); + Set partialChunks = forceRegen ? null : new HashSet<>(); Set plannedChunks = optimizedRegen ? getRegenPassSet(REGEN_PLANNED_CHUNKS_BY_PASS, regenPassKey) : null; if (optimizedRegen) { @@ -131,10 +132,22 @@ public interface MatterGenerator { } for (MantleComponent component : pair.getA()) { - if (!forceRegen && chunk.isFlagged(component.getFlag())) { + if (!component.isEnabled()) { + continue; + } + + boolean componentAlreadyGenerated = !forceRegen && chunk.isFlagged(component.getFlag()); + if (componentAlreadyGenerated) { componentSkipped++; continue; } + + int componentPassRadius = Math.ceilDiv(component.getRadius(), 16); + if (Math.abs(i) > componentPassRadius || Math.abs(j) > componentPassRadius) { + partialChunks.add(passKey); + continue; + } + if (forceRegen && chunk.isFlagged(component.getFlag())) { chunk.flag(component.getFlag(), false); componentForcedReset++; @@ -186,6 +199,9 @@ public interface MatterGenerator { if (plannedChunks != null && !plannedChunks.add(realKey)) { continue; } + if (partialChunks != null && partialChunks.contains(realKey)) { + continue; + } writer.acquireChunk(realX, realZ).flag(MantleFlag.PLANNED, true); } } 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 c3a968491..c1c700206 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 @@ -436,11 +436,8 @@ public class MantleCarvingComponent extends IrisMantleComponent { int worldZ = baseZ + localZ; int columnIndex = PowerOfTwoCoordinates.packLocal16(localX, localZ); if (useContextHeight) { - Double cachedHeight = context.getHeight().get(localX, localZ); - if (cachedHeight != null) { - surfaceHeights[columnIndex] = (int) Math.round(cachedHeight); - continue; - } + surfaceHeights[columnIndex] = context.getRoundedHeight(localX, localZ); + continue; } if (cachedChunkHeights != null) { surfaceHeights[columnIndex] = (int) Math.round(cachedChunkHeights[columnIndex]); diff --git a/core/src/main/java/art/arcane/iris/engine/modifier/IrisCarveModifier.java b/core/src/main/java/art/arcane/iris/engine/modifier/IrisCarveModifier.java index 14b4700aa..40f00be82 100644 --- a/core/src/main/java/art/arcane/iris/engine/modifier/IrisCarveModifier.java +++ b/core/src/main/java/art/arcane/iris/engine/modifier/IrisCarveModifier.java @@ -75,7 +75,13 @@ public class IrisCarveModifier extends EngineAssignedModifier { scratch.reset(); PackedWallBuffer walls = scratch.walls; ColumnMask[] columnMasks = scratch.columnMasks; + int[] surfaceHeights = scratch.surfaceHeights; Map customBiomeCache = scratch.customBiomeCache; + for (int columnIndex = 0; columnIndex < 256; columnIndex++) { + int localX = PowerOfTwoCoordinates.unpackLocal16X(columnIndex); + int localZ = columnIndex & 15; + surfaceHeights[columnIndex] = context.getRoundedHeight(localX, localZ); + } try { PrecisionStopwatch resolveStopwatch = PrecisionStopwatch.start(); @@ -146,8 +152,9 @@ public class IrisCarveModifier extends EngineAssignedModifier { if (biome != null) { biome.setInferredType(InferredType.CAVE); BlockData data = biome.getWall().get(rng, worldX, yy, worldZ, getData()); + int columnIndex = PowerOfTwoCoordinates.packLocal16(rx, rz); - if (data != null && B.isSolid(output.get(rx, yy, rz)) && yy <= context.getHeight().get(rx, rz)) { + if (data != null && B.isSolid(output.get(rx, yy, rz)) && yy <= surfaceHeights[columnIndex]) { output.set(rx, yy, rz, data); } } @@ -471,6 +478,7 @@ public class IrisCarveModifier extends EngineAssignedModifier { private static final class CarveScratch { private final ColumnMask[] columnMasks = new ColumnMask[256]; + private final int[] surfaceHeights = new int[256]; private final PackedWallBuffer walls = new PackedWallBuffer(512); private final Map customBiomeCache = new HashMap<>(); diff --git a/core/src/main/java/art/arcane/iris/engine/modifier/IrisDepositModifier.java b/core/src/main/java/art/arcane/iris/engine/modifier/IrisDepositModifier.java index 5af3f6bf5..cea8f2c88 100644 --- a/core/src/main/java/art/arcane/iris/engine/modifier/IrisDepositModifier.java +++ b/core/src/main/java/art/arcane/iris/engine/modifier/IrisDepositModifier.java @@ -100,9 +100,7 @@ public class IrisDepositModifier extends EngineAssignedModifier { int x = rng.i(min, max + 1); int z = rng.i(min, max + 1); - int height = (he != null ? he.getHeight((cx << 4) + x, (cz << 4) + z) : (int) (Math.round( - context.getHeight().get(x, z) - ))) - 7; + int height = (he != null ? he.getHeight((cx << 4) + x, (cz << 4) + z) : context.getRoundedHeight(x, z)) - 7; if (height <= 0) continue; diff --git a/core/src/main/java/art/arcane/iris/engine/object/IrisBiome.java b/core/src/main/java/art/arcane/iris/engine/object/IrisBiome.java index c416dee93..bf2463735 100644 --- a/core/src/main/java/art/arcane/iris/engine/object/IrisBiome.java +++ b/core/src/main/java/art/arcane/iris/engine/object/IrisBiome.java @@ -74,6 +74,8 @@ public class IrisBiome extends IrisRegistrant implements IRare { private final transient AtomicCache> realChildren = new AtomicCache<>(); private final transient AtomicCache> layerHeightGenerators = new AtomicCache<>(); private final transient AtomicCache> layerSeaHeightGenerators = new AtomicCache<>(); + private final transient AtomicCache> surfaceOreCache = new AtomicCache<>(); + private final transient AtomicCache> undergroundOreCache = new AtomicCache<>(); @MinNumber(2) @Required @Desc("This is the human readable name for this biome. This can and should be different than the file name. This is not used for loading biomes in other objects.") @@ -176,17 +178,14 @@ public class IrisBiome extends IrisRegistrant implements IRare { private KList ores = new KList<>(); public BlockData generateOres(int x, int y, int z, RNG rng, IrisData data, boolean surface) { - if (ores.isEmpty()) { + KList localOres = getOres(surface); + if (localOres.isEmpty()) { return null; } - KList localOres = ores; + int oreCount = localOres.size(); for (int oreIndex = 0; oreIndex < oreCount; oreIndex++) { IrisOreGenerator oreGenerator = localOres.get(oreIndex); - if (oreGenerator.isGenerateSurface() != surface) { - continue; - } - BlockData ore = oreGenerator.generate(x, y, z, rng, data); if (ore != null) { return ore; @@ -195,6 +194,29 @@ public class IrisBiome extends IrisRegistrant implements IRare { return null; } + public void setOres(KList ores) { + this.ores = ores == null ? new KList<>() : ores; + surfaceOreCache.reset(); + undergroundOreCache.reset(); + } + + private KList getOres(boolean surface) { + AtomicCache> oreCache = surface ? surfaceOreCache : undergroundOreCache; + return oreCache.aquire(() -> { + KList filtered = new KList<>(); + KList localOres = ores; + int oreCount = localOres.size(); + for (int oreIndex = 0; oreIndex < oreCount; oreIndex++) { + IrisOreGenerator oreGenerator = localOres.get(oreIndex); + if (oreGenerator.isGenerateSurface() == surface) { + filtered.add(oreGenerator); + } + } + + return filtered; + }); + } + public Biome getVanillaDerivative() { return vanillaDerivative == null ? derivative : vanillaDerivative; } diff --git a/core/src/main/java/art/arcane/iris/engine/object/IrisDimension.java b/core/src/main/java/art/arcane/iris/engine/object/IrisDimension.java index 7da5b9e41..0d553ef5f 100644 --- a/core/src/main/java/art/arcane/iris/engine/object/IrisDimension.java +++ b/core/src/main/java/art/arcane/iris/engine/object/IrisDimension.java @@ -79,6 +79,8 @@ public class IrisDimension extends IrisRegistrant { private final transient AtomicCache rad = new AtomicCache<>(); private final transient AtomicCache featuresUsed = new AtomicCache<>(); private final transient AtomicCache> carvingEntryIndex = new AtomicCache<>(); + private final transient AtomicCache> surfaceOreCache = new AtomicCache<>(); + private final transient AtomicCache> undergroundOreCache = new AtomicCache<>(); @MinNumber(2) @Required @Desc("The human readable name of this dimension") @@ -242,7 +244,7 @@ public class IrisDimension extends IrisRegistrant { private boolean disableExplorerMaps = false; @Desc("Collection of ores to be generated") @ArrayType(type = IrisOreGenerator.class, min = 1) - private KList ores = new KList<>(); + private KList ores = new KList<>(); @MinNumber(0) @MaxNumber(318) @Desc("The Subterrain Fluid Layer Height") @@ -291,17 +293,14 @@ public class IrisDimension extends IrisRegistrant { } public BlockData generateOres(int x, int y, int z, RNG rng, IrisData data, boolean surface) { - if (ores.isEmpty()) { + KList localOres = getOres(surface); + if (localOres.isEmpty()) { return null; } - KList localOres = ores; + int oreCount = localOres.size(); for (int oreIndex = 0; oreIndex < oreCount; oreIndex++) { IrisOreGenerator oreGenerator = localOres.get(oreIndex); - if (oreGenerator.isGenerateSurface() != surface) { - continue; - } - BlockData ore = oreGenerator.generate(x, y, z, rng, data); if (ore != null) { return ore; @@ -309,6 +308,29 @@ public class IrisDimension extends IrisRegistrant { } return null; } + + public void setOres(KList ores) { + this.ores = ores == null ? new KList<>() : ores; + surfaceOreCache.reset(); + undergroundOreCache.reset(); + } + + private KList getOres(boolean surface) { + AtomicCache> oreCache = surface ? surfaceOreCache : undergroundOreCache; + return oreCache.aquire(() -> { + KList filtered = new KList<>(); + KList localOres = ores; + int oreCount = localOres.size(); + for (int oreIndex = 0; oreIndex < oreCount; oreIndex++) { + IrisOreGenerator oreGenerator = localOres.get(oreIndex); + if (oreGenerator.isGenerateSurface() == surface) { + filtered.add(oreGenerator); + } + } + + return filtered; + }); + } public int getFluidHeight() { return fluidHeight - (int) dimensionHeight.getMin(); diff --git a/core/src/main/java/art/arcane/iris/engine/object/IrisRegion.java b/core/src/main/java/art/arcane/iris/engine/object/IrisRegion.java index c3baf230a..b3ca1a91e 100644 --- a/core/src/main/java/art/arcane/iris/engine/object/IrisRegion.java +++ b/core/src/main/java/art/arcane/iris/engine/object/IrisRegion.java @@ -67,6 +67,8 @@ public class IrisRegion extends IrisRegistrant implements IRare { private final transient AtomicCache riverGen = new AtomicCache<>(); private final transient AtomicCache riverChanceGen = new AtomicCache<>(); private final transient AtomicCache cacheColor = new AtomicCache<>(); + private final transient AtomicCache> surfaceOreCache = new AtomicCache<>(); + private final transient AtomicCache> undergroundOreCache = new AtomicCache<>(); @MinNumber(2) @Required @Desc("The name of the region") @@ -152,17 +154,14 @@ public class IrisRegion extends IrisRegistrant implements IRare { private KList ores = new KList<>(); public BlockData generateOres(int x, int y, int z, RNG rng, IrisData data, boolean surface) { - if (ores.isEmpty()) { + KList localOres = getOres(surface); + if (localOres.isEmpty()) { return null; } - KList localOres = ores; + int oreCount = localOres.size(); for (int oreIndex = 0; oreIndex < oreCount; oreIndex++) { IrisOreGenerator oreGenerator = localOres.get(oreIndex); - if (oreGenerator.isGenerateSurface() != surface) { - continue; - } - BlockData ore = oreGenerator.generate(x, y, z, rng, data); if (ore != null) { return ore; @@ -171,6 +170,29 @@ public class IrisRegion extends IrisRegistrant implements IRare { return null; } + public void setOres(KList ores) { + this.ores = ores == null ? new KList<>() : ores; + surfaceOreCache.reset(); + undergroundOreCache.reset(); + } + + private KList getOres(boolean surface) { + AtomicCache> oreCache = surface ? surfaceOreCache : undergroundOreCache; + return oreCache.aquire(() -> { + KList filtered = new KList<>(); + KList localOres = ores; + int oreCount = localOres.size(); + for (int oreIndex = 0; oreIndex < oreCount; oreIndex++) { + IrisOreGenerator oreGenerator = localOres.get(oreIndex); + if (oreGenerator.isGenerateSurface() == surface) { + filtered.add(oreGenerator); + } + } + + return filtered; + }); + } + public String getName() { return name; } diff --git a/core/src/main/java/art/arcane/iris/util/project/context/ChunkContext.java b/core/src/main/java/art/arcane/iris/util/project/context/ChunkContext.java index 9a9ace524..6f5768033 100644 --- a/core/src/main/java/art/arcane/iris/util/project/context/ChunkContext.java +++ b/core/src/main/java/art/arcane/iris/util/project/context/ChunkContext.java @@ -17,7 +17,8 @@ public class ChunkContext { private final int z; private final IrisComplex complex; private final long generationSessionId; - private final ChunkedDataCache height; + private final ChunkedDoubleDataCache height; + private final int[] roundedHeight; private final ChunkedDataCache biome; private final ChunkedDataCache cave; private final ChunkedDataCache rock; @@ -45,7 +46,8 @@ public class ChunkContext { this.z = z; this.complex = complex; this.generationSessionId = generationSessionId; - this.height = new ChunkedDataCache<>(complex.getHeightStream(), x, z, cache); + this.height = new ChunkedDoubleDataCache(complex.getHeightStream(), x, z, cache); + this.roundedHeight = new int[cache ? 256 : 0]; this.biome = new ChunkedDataCache<>(complex.getTrueBiomeStream(), x, z, cache); this.cave = new ChunkedDataCache<>(complex.getCaveBiomeStream(), x, z, cache); this.rock = new ChunkedDataCache<>(complex.getRockStream(), x, z, cache); @@ -56,9 +58,9 @@ public class ChunkContext { PrefillPlan resolvedPlan = prefillPlan == null ? PrefillPlan.NO_CAVE : prefillPlan; boolean capturePrefillMetric = metrics != null; long totalStartNanos = capturePrefillMetric ? System.nanoTime() : 0L; - List fillTasks = new ArrayList<>(6); + List fillTasks = new ArrayList<>(6); if (resolvedPlan.height) { - fillTasks.add(new PrefillFillTask(height)); + fillTasks.add(height::fill); } if (resolvedPlan.biome) { fillTasks.add(new PrefillFillTask(biome)); @@ -77,12 +79,12 @@ public class ChunkContext { } if (!shouldPrefillAsync(fillTasks.size())) { - for (PrefillFillTask fillTask : fillTasks) { + for (Runnable fillTask : fillTasks) { fillTask.run(); } } else { List> futures = new ArrayList<>(fillTasks.size()); - for (PrefillFillTask fillTask : fillTasks) { + for (Runnable fillTask : fillTasks) { futures.add(CompletableFuture.runAsync(fillTask, MultiBurst.burst)); } for (CompletableFuture future : futures) { @@ -93,6 +95,10 @@ public class ChunkContext { if (capturePrefillMetric) { metrics.getContextPrefill().put((System.nanoTime() - totalStartNanos) / 1_000_000D); } + + if (resolvedPlan.height) { + fillRoundedHeight(); + } } } @@ -117,10 +123,18 @@ public class ChunkContext { return complex; } - public ChunkedDataCache getHeight() { + public ChunkedDoubleDataCache getHeight() { return height; } + public int getRoundedHeight(int x, int z) { + if (roundedHeight.length == 0) { + return (int) Math.round(height.getDouble(x, z)); + } + + return roundedHeight[(z << 4) + x]; + } + public ChunkedDataCache getBiome() { return biome; } @@ -175,4 +189,13 @@ public class ChunkContext { dataCache.fill(); } } + + private void fillRoundedHeight() { + for (int z = 0; z < 16; z++) { + int rowOffset = z << 4; + for (int x = 0; x < 16; x++) { + roundedHeight[rowOffset + x] = (int) Math.round(height.getDouble(x, z)); + } + } + } } diff --git a/core/src/main/java/art/arcane/iris/util/project/context/ChunkedDoubleDataCache.java b/core/src/main/java/art/arcane/iris/util/project/context/ChunkedDoubleDataCache.java new file mode 100644 index 000000000..ea3b7f494 --- /dev/null +++ b/core/src/main/java/art/arcane/iris/util/project/context/ChunkedDoubleDataCache.java @@ -0,0 +1,73 @@ +package art.arcane.iris.util.project.context; + +import art.arcane.iris.util.project.stream.ProceduralStream; +import art.arcane.iris.util.project.stream.utility.ChunkFillableDoubleStream2D; +import art.arcane.volmlib.util.documentation.BlockCoordinates; + +import java.util.Arrays; +import java.util.concurrent.Executor; + +public class ChunkedDoubleDataCache { + private final int x; + private final int z; + private final ProceduralStream stream; + private final boolean cache; + private final double[] data; + + @BlockCoordinates + public ChunkedDoubleDataCache(ProceduralStream stream, int x, int z) { + this(stream, x, z, true); + } + + @BlockCoordinates + public ChunkedDoubleDataCache(ProceduralStream stream, int x, int z, boolean cache) { + this.x = x; + this.z = z; + this.stream = stream; + this.cache = cache; + this.data = new double[cache ? 256 : 0]; + if (cache) { + Arrays.fill(this.data, Double.NaN); + } + } + + public void fill() { + fill(null); + } + + public void fill(Executor executor) { + if (!cache) { + return; + } + + if (stream instanceof ChunkFillableDoubleStream2D cachedStream) { + cachedStream.fillChunkDoubles(x, z, data); + return; + } + + for (int row = 0; row < 16; row++) { + int rowOffset = row << 4; + int worldZ = z + row; + for (int column = 0; column < 16; column++) { + data[rowOffset + column] = stream.getDouble(x + column, worldZ); + } + } + } + + @BlockCoordinates + public double getDouble(int x, int z) { + if (!cache) { + return stream.getDouble(this.x + x, this.z + z); + } + + int index = (z << 4) + x; + double value = data[index]; + if (!Double.isNaN(value)) { + return value; + } + + double sampled = stream.getDouble(this.x + x, this.z + z); + data[index] = sampled; + return sampled; + } +} diff --git a/core/src/test/java/art/arcane/iris/util/project/context/ChunkContextPrefillPlanTest.java b/core/src/test/java/art/arcane/iris/util/project/context/ChunkContextPrefillPlanTest.java index cc040cdba..74f39f629 100644 --- a/core/src/test/java/art/arcane/iris/util/project/context/ChunkContextPrefillPlanTest.java +++ b/core/src/test/java/art/arcane/iris/util/project/context/ChunkContextPrefillPlanTest.java @@ -49,7 +49,7 @@ public class ChunkContextPrefillPlanTest { assertEquals(256, regionCalls.get()); assertEquals(0, caveCalls.get()); - assertEquals(34051D, context.getHeight().get(2, 3), 0D); + assertEquals(34051D, context.getHeight().getDouble(2, 3), 0D); context.getCave().get(2, 3); context.getCave().get(2, 3); assertEquals(1, caveCalls.get()); @@ -112,7 +112,7 @@ public class ChunkContextPrefillPlanTest { double worldX = invocation.getArgument(0); double worldZ = invocation.getArgument(1); return (worldX * 1000D) + worldZ; - }).when(heightStream).get(anyDouble(), anyDouble()); + }).when(heightStream).getDouble(anyDouble(), anyDouble()); @SuppressWarnings("unchecked") ProceduralStream biomeStream = mock(ProceduralStream.class);