This commit is contained in:
Brian Neumann-Fopiano
2026-04-15 15:52:18 -04:00
parent 6096c39192
commit 6a5616abae
14 changed files with 228 additions and 46 deletions
+1 -1
View File
@@ -1 +1 @@
-1511497018 1982643195
@@ -201,9 +201,9 @@ public class IrisComplex implements DataProvider {
IrisBiome b = focusBiome != null ? focusBiome : baseBiomeStream.get(x, z); IrisBiome b = focusBiome != null ? focusBiome : baseBiomeStream.get(x, z);
return getHeight(engine, b, x, z, engine.getSeedManager().getHeight()); return getHeight(engine, b, x, z, engine.getSeedManager().getHeight());
}, Interpolated.DOUBLE).cache2DDouble("heightStream", engine, cacheSize).waste("Height Stream"); }, 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"); .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"); .slope(3).cache2DDouble("slopeStream", engine, cacheSize).waste("Slope Stream");
trueBiomeStream = focusBiome != null ? ProceduralStream.of((x, y) -> focusBiome, Interpolated.of(a -> 0D, trueBiomeStream = focusBiome != null ? ProceduralStream.of((x, y) -> focusBiome, Interpolated.of(a -> 0D,
b -> focusBiome)) b -> focusBiome))
@@ -214,7 +214,7 @@ public class IrisComplex implements DataProvider {
.cache2D("trueBiomeStream", engine, cacheSize).waste("True Biome Stream"); .cache2D("trueBiomeStream", engine, cacheSize).waste("True Biome Stream");
trueBiomeDerivativeStream = trueBiomeStream.contextInjecting((c, x, z) -> IrisContext.getOr(engine).getChunkContext().getBiome().get(x, z)) 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"); .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"); .max(fluidHeight).cache2DDouble("heightFluidStream", engine, cacheSize).waste("Height Fluid Stream");
maxHeightStream = ProceduralStream.ofDouble((x, z) -> height).waste("Max Height 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)) terrainSurfaceDecoration = trueBiomeStream.contextInjecting((c, x, z) -> IrisContext.getOr(engine).getChunkContext().getBiome().get(x, z))
@@ -79,7 +79,7 @@ public class IrisDecorantActuator extends EngineAssignedActuator<BlockData> {
int emptyFor = 0; int emptyFor = 0;
int lastSolid = 0; int lastSolid = 0;
realZ = Math.round(z + j); 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); biome = context.getBiome().get(i, j);
cave = shouldRay ? context.getCave().get(i, j) : null; cave = shouldRay ? context.getCave().get(i, j) : null;
@@ -25,6 +25,7 @@ import art.arcane.iris.engine.object.IrisRegion;
import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.collection.KList;
import art.arcane.iris.util.project.context.ChunkedDataCache; import art.arcane.iris.util.project.context.ChunkedDataCache;
import art.arcane.iris.util.project.context.ChunkContext; 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.volmlib.util.documentation.BlockCoordinates;
import art.arcane.iris.util.project.hunk.Hunk; import art.arcane.iris.util.project.hunk.Hunk;
import art.arcane.volmlib.util.math.RNG; import art.arcane.volmlib.util.math.RNG;
@@ -89,7 +90,7 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<BlockData>
realZ = zf + z; realZ = zf + z;
biome = context.getBiome().get(xf, zf); biome = context.getBiome().get(xf, zf);
region = context.getRegion().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)); hf = Math.round(Math.max(Math.min(h.getHeight(), getDimension().getFluidHeight()), he));
if (hf < 0) { if (hf < 0) {
@@ -174,7 +175,7 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<BlockData>
boolean bedrockEnabled = getDimension().isBedrock(); boolean bedrockEnabled = getDimension().isBedrock();
ChunkedDataCache<IrisBiome> biomeCache = context.getBiome(); ChunkedDataCache<IrisBiome> biomeCache = context.getBiome();
ChunkedDataCache<IrisRegion> regionCache = context.getRegion(); ChunkedDataCache<IrisRegion> regionCache = context.getRegion();
ChunkedDataCache<Double> heightCache = context.getHeight(); ChunkedDoubleDataCache heightCache = context.getHeight();
ChunkedDataCache<BlockData> fluidCache = context.getFluid(); ChunkedDataCache<BlockData> fluidCache = context.getFluid();
ChunkedDataCache<BlockData> rockCache = context.getRock(); ChunkedDataCache<BlockData> rockCache = context.getRock();
int realX = xf + x; int realX = xf + x;
@@ -183,7 +184,7 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<BlockData>
int realZ = zf + z; int realZ = zf + z;
IrisBiome biome = biomeCache.get(xf, zf); IrisBiome biome = biomeCache.get(xf, zf);
IrisRegion region = regionCache.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)); int hf = Math.round(Math.max(Math.min(chunkHeight, fluidHeight), he));
if (hf < 0) { if (hf < 0) {
continue; continue;
@@ -59,6 +59,7 @@ public interface MatterGenerator {
boolean optimizedRegen = forceRegen && !IrisSettings.get().getGeneral().isDebug() && regenPassKey != null; boolean optimizedRegen = forceRegen && !IrisSettings.get().getGeneral().isDebug() && regenPassKey != null;
int writeRadius = optimizedRegen ? Math.min(getRadius(), getRealRadius()) : getRadius(); int writeRadius = optimizedRegen ? Math.min(getRadius(), getRealRadius()) : getRadius();
Set<Long> clearedChunks = optimizedRegen ? getRegenPassSet(REGEN_CLEARED_CHUNKS_BY_PASS, regenPassKey) : new HashSet<>(); Set<Long> clearedChunks = optimizedRegen ? getRegenPassSet(REGEN_CLEARED_CHUNKS_BY_PASS, regenPassKey) : new HashSet<>();
Set<Long> partialChunks = forceRegen ? null : new HashSet<>();
Set<Long> plannedChunks = optimizedRegen ? getRegenPassSet(REGEN_PLANNED_CHUNKS_BY_PASS, regenPassKey) : null; Set<Long> plannedChunks = optimizedRegen ? getRegenPassSet(REGEN_PLANNED_CHUNKS_BY_PASS, regenPassKey) : null;
if (optimizedRegen) { if (optimizedRegen) {
@@ -131,10 +132,22 @@ public interface MatterGenerator {
} }
for (MantleComponent component : pair.getA()) { 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++; componentSkipped++;
continue; 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())) { if (forceRegen && chunk.isFlagged(component.getFlag())) {
chunk.flag(component.getFlag(), false); chunk.flag(component.getFlag(), false);
componentForcedReset++; componentForcedReset++;
@@ -186,6 +199,9 @@ public interface MatterGenerator {
if (plannedChunks != null && !plannedChunks.add(realKey)) { if (plannedChunks != null && !plannedChunks.add(realKey)) {
continue; continue;
} }
if (partialChunks != null && partialChunks.contains(realKey)) {
continue;
}
writer.acquireChunk(realX, realZ).flag(MantleFlag.PLANNED, true); writer.acquireChunk(realX, realZ).flag(MantleFlag.PLANNED, true);
} }
} }
@@ -436,11 +436,8 @@ public class MantleCarvingComponent extends IrisMantleComponent {
int worldZ = baseZ + localZ; int worldZ = baseZ + localZ;
int columnIndex = PowerOfTwoCoordinates.packLocal16(localX, localZ); int columnIndex = PowerOfTwoCoordinates.packLocal16(localX, localZ);
if (useContextHeight) { if (useContextHeight) {
Double cachedHeight = context.getHeight().get(localX, localZ); surfaceHeights[columnIndex] = context.getRoundedHeight(localX, localZ);
if (cachedHeight != null) { continue;
surfaceHeights[columnIndex] = (int) Math.round(cachedHeight);
continue;
}
} }
if (cachedChunkHeights != null) { if (cachedChunkHeights != null) {
surfaceHeights[columnIndex] = (int) Math.round(cachedChunkHeights[columnIndex]); surfaceHeights[columnIndex] = (int) Math.round(cachedChunkHeights[columnIndex]);
@@ -75,7 +75,13 @@ public class IrisCarveModifier extends EngineAssignedModifier<BlockData> {
scratch.reset(); scratch.reset();
PackedWallBuffer walls = scratch.walls; PackedWallBuffer walls = scratch.walls;
ColumnMask[] columnMasks = scratch.columnMasks; ColumnMask[] columnMasks = scratch.columnMasks;
int[] surfaceHeights = scratch.surfaceHeights;
Map<String, IrisBiome> customBiomeCache = scratch.customBiomeCache; Map<String, IrisBiome> 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 { try {
PrecisionStopwatch resolveStopwatch = PrecisionStopwatch.start(); PrecisionStopwatch resolveStopwatch = PrecisionStopwatch.start();
@@ -146,8 +152,9 @@ public class IrisCarveModifier extends EngineAssignedModifier<BlockData> {
if (biome != null) { if (biome != null) {
biome.setInferredType(InferredType.CAVE); biome.setInferredType(InferredType.CAVE);
BlockData data = biome.getWall().get(rng, worldX, yy, worldZ, getData()); 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); output.set(rx, yy, rz, data);
} }
} }
@@ -471,6 +478,7 @@ public class IrisCarveModifier extends EngineAssignedModifier<BlockData> {
private static final class CarveScratch { private static final class CarveScratch {
private final ColumnMask[] columnMasks = new ColumnMask[256]; private final ColumnMask[] columnMasks = new ColumnMask[256];
private final int[] surfaceHeights = new int[256];
private final PackedWallBuffer walls = new PackedWallBuffer(512); private final PackedWallBuffer walls = new PackedWallBuffer(512);
private final Map<String, IrisBiome> customBiomeCache = new HashMap<>(); private final Map<String, IrisBiome> customBiomeCache = new HashMap<>();
@@ -100,9 +100,7 @@ public class IrisDepositModifier extends EngineAssignedModifier<BlockData> {
int x = rng.i(min, max + 1); int x = rng.i(min, max + 1);
int z = 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( int height = (he != null ? he.getHeight((cx << 4) + x, (cz << 4) + z) : context.getRoundedHeight(x, z)) - 7;
context.getHeight().get(x, z)
))) - 7;
if (height <= 0) if (height <= 0)
continue; continue;
@@ -74,6 +74,8 @@ public class IrisBiome extends IrisRegistrant implements IRare {
private final transient AtomicCache<KList<IrisBiome>> realChildren = new AtomicCache<>(); private final transient AtomicCache<KList<IrisBiome>> realChildren = new AtomicCache<>();
private final transient AtomicCache<KList<CNG>> layerHeightGenerators = new AtomicCache<>(); private final transient AtomicCache<KList<CNG>> layerHeightGenerators = new AtomicCache<>();
private final transient AtomicCache<KList<CNG>> layerSeaHeightGenerators = new AtomicCache<>(); private final transient AtomicCache<KList<CNG>> layerSeaHeightGenerators = new AtomicCache<>();
private final transient AtomicCache<KList<IrisOreGenerator>> surfaceOreCache = new AtomicCache<>();
private final transient AtomicCache<KList<IrisOreGenerator>> undergroundOreCache = new AtomicCache<>();
@MinNumber(2) @MinNumber(2)
@Required @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.") @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<IrisOreGenerator> ores = new KList<>(); private KList<IrisOreGenerator> ores = new KList<>();
public BlockData generateOres(int x, int y, int z, RNG rng, IrisData data, boolean surface) { public BlockData generateOres(int x, int y, int z, RNG rng, IrisData data, boolean surface) {
if (ores.isEmpty()) { KList<IrisOreGenerator> localOres = getOres(surface);
if (localOres.isEmpty()) {
return null; return null;
} }
KList<IrisOreGenerator> localOres = ores;
int oreCount = localOres.size(); int oreCount = localOres.size();
for (int oreIndex = 0; oreIndex < oreCount; oreIndex++) { for (int oreIndex = 0; oreIndex < oreCount; oreIndex++) {
IrisOreGenerator oreGenerator = localOres.get(oreIndex); IrisOreGenerator oreGenerator = localOres.get(oreIndex);
if (oreGenerator.isGenerateSurface() != surface) {
continue;
}
BlockData ore = oreGenerator.generate(x, y, z, rng, data); BlockData ore = oreGenerator.generate(x, y, z, rng, data);
if (ore != null) { if (ore != null) {
return ore; return ore;
@@ -195,6 +194,29 @@ public class IrisBiome extends IrisRegistrant implements IRare {
return null; return null;
} }
public void setOres(KList<IrisOreGenerator> ores) {
this.ores = ores == null ? new KList<>() : ores;
surfaceOreCache.reset();
undergroundOreCache.reset();
}
private KList<IrisOreGenerator> getOres(boolean surface) {
AtomicCache<KList<IrisOreGenerator>> oreCache = surface ? surfaceOreCache : undergroundOreCache;
return oreCache.aquire(() -> {
KList<IrisOreGenerator> filtered = new KList<>();
KList<IrisOreGenerator> 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() { public Biome getVanillaDerivative() {
return vanillaDerivative == null ? derivative : vanillaDerivative; return vanillaDerivative == null ? derivative : vanillaDerivative;
} }
@@ -79,6 +79,8 @@ public class IrisDimension extends IrisRegistrant {
private final transient AtomicCache<Double> rad = new AtomicCache<>(); private final transient AtomicCache<Double> rad = new AtomicCache<>();
private final transient AtomicCache<Boolean> featuresUsed = new AtomicCache<>(); private final transient AtomicCache<Boolean> featuresUsed = new AtomicCache<>();
private final transient AtomicCache<Map<String, IrisDimensionCarvingEntry>> carvingEntryIndex = new AtomicCache<>(); private final transient AtomicCache<Map<String, IrisDimensionCarvingEntry>> carvingEntryIndex = new AtomicCache<>();
private final transient AtomicCache<KList<IrisOreGenerator>> surfaceOreCache = new AtomicCache<>();
private final transient AtomicCache<KList<IrisOreGenerator>> undergroundOreCache = new AtomicCache<>();
@MinNumber(2) @MinNumber(2)
@Required @Required
@Desc("The human readable name of this dimension") @Desc("The human readable name of this dimension")
@@ -291,17 +293,14 @@ public class IrisDimension extends IrisRegistrant {
} }
public BlockData generateOres(int x, int y, int z, RNG rng, IrisData data, boolean surface) { public BlockData generateOres(int x, int y, int z, RNG rng, IrisData data, boolean surface) {
if (ores.isEmpty()) { KList<IrisOreGenerator> localOres = getOres(surface);
if (localOres.isEmpty()) {
return null; return null;
} }
KList<IrisOreGenerator> localOres = ores;
int oreCount = localOres.size(); int oreCount = localOres.size();
for (int oreIndex = 0; oreIndex < oreCount; oreIndex++) { for (int oreIndex = 0; oreIndex < oreCount; oreIndex++) {
IrisOreGenerator oreGenerator = localOres.get(oreIndex); IrisOreGenerator oreGenerator = localOres.get(oreIndex);
if (oreGenerator.isGenerateSurface() != surface) {
continue;
}
BlockData ore = oreGenerator.generate(x, y, z, rng, data); BlockData ore = oreGenerator.generate(x, y, z, rng, data);
if (ore != null) { if (ore != null) {
return ore; return ore;
@@ -310,6 +309,29 @@ public class IrisDimension extends IrisRegistrant {
return null; return null;
} }
public void setOres(KList<IrisOreGenerator> ores) {
this.ores = ores == null ? new KList<>() : ores;
surfaceOreCache.reset();
undergroundOreCache.reset();
}
private KList<IrisOreGenerator> getOres(boolean surface) {
AtomicCache<KList<IrisOreGenerator>> oreCache = surface ? surfaceOreCache : undergroundOreCache;
return oreCache.aquire(() -> {
KList<IrisOreGenerator> filtered = new KList<>();
KList<IrisOreGenerator> 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() { public int getFluidHeight() {
return fluidHeight - (int) dimensionHeight.getMin(); return fluidHeight - (int) dimensionHeight.getMin();
} }
@@ -67,6 +67,8 @@ public class IrisRegion extends IrisRegistrant implements IRare {
private final transient AtomicCache<CNG> riverGen = new AtomicCache<>(); private final transient AtomicCache<CNG> riverGen = new AtomicCache<>();
private final transient AtomicCache<CNG> riverChanceGen = new AtomicCache<>(); private final transient AtomicCache<CNG> riverChanceGen = new AtomicCache<>();
private final transient AtomicCache<Color> cacheColor = new AtomicCache<>(); private final transient AtomicCache<Color> cacheColor = new AtomicCache<>();
private final transient AtomicCache<KList<IrisOreGenerator>> surfaceOreCache = new AtomicCache<>();
private final transient AtomicCache<KList<IrisOreGenerator>> undergroundOreCache = new AtomicCache<>();
@MinNumber(2) @MinNumber(2)
@Required @Required
@Desc("The name of the region") @Desc("The name of the region")
@@ -152,17 +154,14 @@ public class IrisRegion extends IrisRegistrant implements IRare {
private KList<IrisOreGenerator> ores = new KList<>(); private KList<IrisOreGenerator> ores = new KList<>();
public BlockData generateOres(int x, int y, int z, RNG rng, IrisData data, boolean surface) { public BlockData generateOres(int x, int y, int z, RNG rng, IrisData data, boolean surface) {
if (ores.isEmpty()) { KList<IrisOreGenerator> localOres = getOres(surface);
if (localOres.isEmpty()) {
return null; return null;
} }
KList<IrisOreGenerator> localOres = ores;
int oreCount = localOres.size(); int oreCount = localOres.size();
for (int oreIndex = 0; oreIndex < oreCount; oreIndex++) { for (int oreIndex = 0; oreIndex < oreCount; oreIndex++) {
IrisOreGenerator oreGenerator = localOres.get(oreIndex); IrisOreGenerator oreGenerator = localOres.get(oreIndex);
if (oreGenerator.isGenerateSurface() != surface) {
continue;
}
BlockData ore = oreGenerator.generate(x, y, z, rng, data); BlockData ore = oreGenerator.generate(x, y, z, rng, data);
if (ore != null) { if (ore != null) {
return ore; return ore;
@@ -171,6 +170,29 @@ public class IrisRegion extends IrisRegistrant implements IRare {
return null; return null;
} }
public void setOres(KList<IrisOreGenerator> ores) {
this.ores = ores == null ? new KList<>() : ores;
surfaceOreCache.reset();
undergroundOreCache.reset();
}
private KList<IrisOreGenerator> getOres(boolean surface) {
AtomicCache<KList<IrisOreGenerator>> oreCache = surface ? surfaceOreCache : undergroundOreCache;
return oreCache.aquire(() -> {
KList<IrisOreGenerator> filtered = new KList<>();
KList<IrisOreGenerator> 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() { public String getName() {
return name; return name;
} }
@@ -17,7 +17,8 @@ public class ChunkContext {
private final int z; private final int z;
private final IrisComplex complex; private final IrisComplex complex;
private final long generationSessionId; private final long generationSessionId;
private final ChunkedDataCache<Double> height; private final ChunkedDoubleDataCache height;
private final int[] roundedHeight;
private final ChunkedDataCache<IrisBiome> biome; private final ChunkedDataCache<IrisBiome> biome;
private final ChunkedDataCache<IrisBiome> cave; private final ChunkedDataCache<IrisBiome> cave;
private final ChunkedDataCache<BlockData> rock; private final ChunkedDataCache<BlockData> rock;
@@ -45,7 +46,8 @@ public class ChunkContext {
this.z = z; this.z = z;
this.complex = complex; this.complex = complex;
this.generationSessionId = generationSessionId; 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.biome = new ChunkedDataCache<>(complex.getTrueBiomeStream(), x, z, cache);
this.cave = new ChunkedDataCache<>(complex.getCaveBiomeStream(), x, z, cache); this.cave = new ChunkedDataCache<>(complex.getCaveBiomeStream(), x, z, cache);
this.rock = new ChunkedDataCache<>(complex.getRockStream(), 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; PrefillPlan resolvedPlan = prefillPlan == null ? PrefillPlan.NO_CAVE : prefillPlan;
boolean capturePrefillMetric = metrics != null; boolean capturePrefillMetric = metrics != null;
long totalStartNanos = capturePrefillMetric ? System.nanoTime() : 0L; long totalStartNanos = capturePrefillMetric ? System.nanoTime() : 0L;
List<PrefillFillTask> fillTasks = new ArrayList<>(6); List<Runnable> fillTasks = new ArrayList<>(6);
if (resolvedPlan.height) { if (resolvedPlan.height) {
fillTasks.add(new PrefillFillTask(height)); fillTasks.add(height::fill);
} }
if (resolvedPlan.biome) { if (resolvedPlan.biome) {
fillTasks.add(new PrefillFillTask(biome)); fillTasks.add(new PrefillFillTask(biome));
@@ -77,12 +79,12 @@ public class ChunkContext {
} }
if (!shouldPrefillAsync(fillTasks.size())) { if (!shouldPrefillAsync(fillTasks.size())) {
for (PrefillFillTask fillTask : fillTasks) { for (Runnable fillTask : fillTasks) {
fillTask.run(); fillTask.run();
} }
} else { } else {
List<CompletableFuture<Void>> futures = new ArrayList<>(fillTasks.size()); List<CompletableFuture<Void>> futures = new ArrayList<>(fillTasks.size());
for (PrefillFillTask fillTask : fillTasks) { for (Runnable fillTask : fillTasks) {
futures.add(CompletableFuture.runAsync(fillTask, MultiBurst.burst)); futures.add(CompletableFuture.runAsync(fillTask, MultiBurst.burst));
} }
for (CompletableFuture<Void> future : futures) { for (CompletableFuture<Void> future : futures) {
@@ -93,6 +95,10 @@ public class ChunkContext {
if (capturePrefillMetric) { if (capturePrefillMetric) {
metrics.getContextPrefill().put((System.nanoTime() - totalStartNanos) / 1_000_000D); metrics.getContextPrefill().put((System.nanoTime() - totalStartNanos) / 1_000_000D);
} }
if (resolvedPlan.height) {
fillRoundedHeight();
}
} }
} }
@@ -117,10 +123,18 @@ public class ChunkContext {
return complex; return complex;
} }
public ChunkedDataCache<Double> getHeight() { public ChunkedDoubleDataCache getHeight() {
return height; 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<IrisBiome> getBiome() { public ChunkedDataCache<IrisBiome> getBiome() {
return biome; return biome;
} }
@@ -175,4 +189,13 @@ public class ChunkContext {
dataCache.fill(); 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));
}
}
}
} }
@@ -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<Double> stream;
private final boolean cache;
private final double[] data;
@BlockCoordinates
public ChunkedDoubleDataCache(ProceduralStream<Double> stream, int x, int z) {
this(stream, x, z, true);
}
@BlockCoordinates
public ChunkedDoubleDataCache(ProceduralStream<Double> 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;
}
}
@@ -49,7 +49,7 @@ public class ChunkContextPrefillPlanTest {
assertEquals(256, regionCalls.get()); assertEquals(256, regionCalls.get());
assertEquals(0, caveCalls.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);
context.getCave().get(2, 3); context.getCave().get(2, 3);
assertEquals(1, caveCalls.get()); assertEquals(1, caveCalls.get());
@@ -112,7 +112,7 @@ public class ChunkContextPrefillPlanTest {
double worldX = invocation.getArgument(0); double worldX = invocation.getArgument(0);
double worldZ = invocation.getArgument(1); double worldZ = invocation.getArgument(1);
return (worldX * 1000D) + worldZ; return (worldX * 1000D) + worldZ;
}).when(heightStream).get(anyDouble(), anyDouble()); }).when(heightStream).getDouble(anyDouble(), anyDouble());
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
ProceduralStream<IrisBiome> biomeStream = mock(ProceduralStream.class); ProceduralStream<IrisBiome> biomeStream = mock(ProceduralStream.class);