implement caching biome provider

This commit is contained in:
dfsek 2021-12-30 16:01:19 -07:00
parent 74237e7568
commit d36fc7dec1
10 changed files with 74 additions and 50 deletions

View File

@ -22,7 +22,6 @@ import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.profiler.ProfileFrame;
import com.dfsek.terra.api.world.WritableWorld;
import com.dfsek.terra.api.world.biome.Biome;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
import com.dfsek.terra.api.world.chunk.generation.ChunkGenerator;
@ -48,24 +47,23 @@ public class NoiseChunkGenerator3D implements ChunkGenerator {
this.air = platform.getWorldHandle().air();
this.carverHorizontalResolution = carverHorizontalResolution;
this.carverVerticalResolution = carverVerticalResolution;
this.samplerCache = new SamplerProvider(platform, c.getBiomeProvider(), elevationBlend);
this.samplerCache = new SamplerProvider(platform, elevationBlend);
}
@Override
@SuppressWarnings("try")
public void generateChunkData(@NotNull ProtoChunk chunk, @NotNull WorldProperties world,
@NotNull BiomeProvider biomeProvider,
int chunkX, int chunkZ) {
try(ProfileFrame ignore = platform.getProfiler().profile("chunk_base_3d")) {
BiomeProvider grid = configPack.getBiomeProvider();
int xOrig = (chunkX << 4);
int zOrig = (chunkZ << 4);
Sampler3D sampler = samplerCache.getChunk(chunkX, chunkZ, world);
Sampler3D sampler = samplerCache.getChunk(chunkX, chunkZ, world, biomeProvider);
long seed = world.getSeed();
LazilyEvaluatedInterpolator carver = new LazilyEvaluatedInterpolator(configPack.getBiomeProvider(),
LazilyEvaluatedInterpolator carver = new LazilyEvaluatedInterpolator(biomeProvider,
chunkX,
chunkZ,
world.getMaxHeight(),
@ -80,7 +78,7 @@ public class NoiseChunkGenerator3D implements ChunkGenerator {
int cx = xOrig + x;
int cz = zOrig + z;
Biome biome = grid.getBiome(cx, cz, seed);
Biome biome = biomeProvider.getBiome(cx, cz, seed);
PaletteInfo paletteInfo = biome.getContext().get(PaletteInfo.class);
@ -113,7 +111,7 @@ public class NoiseChunkGenerator3D implements ChunkGenerator {
public BlockState getBlock(WorldProperties world, int x, int y, int z) {
BiomeProvider provider = configPack.getBiomeProvider();
Biome biome = provider.getBiome(x, z, world.getSeed());
Sampler3D sampler = samplerCache.get(x, z, world);
Sampler3D sampler = samplerCache.get(x, z, world, configPack.getBiomeProvider());
PaletteInfo paletteInfo = biome.getContext().get(PaletteInfo.class);

View File

@ -56,12 +56,9 @@ public class ChunkInterpolator {
double[][][] noiseStorage = new double[5][5][size + 1];
BiomeCache cache = new BiomeCache(provider);
for(int x = 0; x < 5; x++) {
for(int z = 0; z < 5; z++) {
BiomeNoiseProperties generationSettings = cache.get(xOrigin + (x << 2), zOrigin + (z << 2), seed)
BiomeNoiseProperties generationSettings = provider.getBiome(xOrigin + (x << 2), zOrigin + (z << 2), seed)
.getContext()
.get(BiomeNoiseProperties.class);
Map<BiomeNoiseProperties, MutableInteger> genMap = new HashMap<>();
@ -72,7 +69,7 @@ public class ChunkInterpolator {
for(int xi = -blend; xi <= blend; xi++) {
for(int zi = -blend; zi <= blend; zi++) {
genMap.computeIfAbsent(
cache.get(xOrigin + (x << 2) + (xi * step), zOrigin + (z << 2) + (zi * step), seed)
provider.getBiome(xOrigin + (x << 2) + (xi * step), zOrigin + (z << 2) + (zi * step), seed)
.getContext()
.get(BiomeNoiseProperties.class),
g -> new MutableInteger(0)).increment(); // Increment by 1
@ -102,20 +99,6 @@ public class ChunkInterpolator {
}
}
private static final class BiomeCache {
private final BiomeProvider provider;
private final Map<Vector2Int, Biome> cache = new HashMap<>();
private BiomeCache(BiomeProvider provider) {
this.provider = provider;
}
public Biome get(int x, int z, long seed) {
return cache.computeIfAbsent(Vector2Int.of(x, z), vec -> provider.getBiome(x, z, seed));
}
}
private static int reRange(int value, int high) {
return FastMath.max(FastMath.min(value, high), 0);
}

View File

@ -19,6 +19,7 @@ package com.dfsek.terra.addons.chunkgenerator.generation.math.samplers;
import com.dfsek.terra.api.world.info.WorldProperties;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
@ -31,30 +32,32 @@ import com.dfsek.terra.api.util.generic.pair.Pair;
import com.dfsek.terra.api.world.World;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
import java.util.concurrent.ExecutionException;
public class SamplerProvider {
private final LoadingCache<WorldContext, Sampler3D> cache;
private final Cache<WorldContext, Sampler3D> cache;
private final int elevationSmooth;
public SamplerProvider(Platform platform, BiomeProvider provider, int elevationSmooth) {
cache = CacheBuilder.newBuilder().maximumSize(platform.getTerraConfig().getSamplerCache())
.build(new CacheLoader<>() {
@Override
public Sampler3D load(@NotNull WorldContext context) {
return new Sampler3D(context.cx, context.cz, context.seed, context.minHeight, context.maxHeight, provider,
elevationSmooth);
}
});
public SamplerProvider(Platform platform, int elevationSmooth) {
this.elevationSmooth = elevationSmooth;
cache = CacheBuilder.newBuilder().maximumSize(platform.getTerraConfig().getSamplerCache()).build();
}
public Sampler3D get(int x, int z, WorldProperties world) {
public Sampler3D get(int x, int z, WorldProperties world, BiomeProvider provider) {
int cx = FastMath.floorDiv(x, 16);
int cz = FastMath.floorDiv(z, 16);
return getChunk(cx, cz, world);
return getChunk(cx, cz, world, provider);
}
public Sampler3D getChunk(int cx, int cz, WorldProperties world) {
return cache.getUnchecked(new WorldContext(cx, cz, world.getSeed(), world.getMinHeight(), world.getMaxHeight()));
public Sampler3D getChunk(int cx, int cz, WorldProperties world, BiomeProvider provider) {
WorldContext context = new WorldContext(cx, cz, world.getSeed(), world.getMinHeight(), world.getMaxHeight());
try {
return cache.get(context, () -> new Sampler3D(context.cx, context.cz, context.seed, context.minHeight, context.maxHeight, provider,
elevationSmooth));
} catch(ExecutionException e) {
throw new RuntimeException(e);
}
}
private record WorldContext(int cx, int cz, long seed, int minHeight, int maxHeight) {

View File

@ -86,6 +86,6 @@ public class CheckFunction implements Function<String> {
private double sample(double x, double y, double z, SamplerProvider cache, World world) {
int cx = FastMath.floorDiv((int) x, 16);
int cz = FastMath.floorDiv((int) z, 16);
return cache.getChunk(cx, cz, world).sample(x - (cx << 4), y, z - (cz << 4));
return cache.getChunk(cx, cz, world, world.getBiomeProvider()).sample(x - (cx << 4), y, z - (cz << 4));
}
}

View File

@ -59,4 +59,8 @@ public interface BiomeProvider {
* @return {@link Iterable} of all biomes this provider can generate.
*/
Iterable<Biome> getBiomes();
default BiomeProvider caching() {
return new CachingBiomeProvider(this);
}
}

View File

@ -0,0 +1,38 @@
package com.dfsek.terra.api.world.biome.generation;
import com.dfsek.terra.api.Handle;
import com.dfsek.terra.api.util.MathUtil;
import com.dfsek.terra.api.world.biome.Biome;
import java.util.HashMap;
import java.util.Map;
/**
* A biome provider implementation that lazily evaluates biomes, and caches them.
*
* This is for use in chunk generators, it makes the assumption that <b>the seed remains the same for the duration of its use!</b>
*/
public class CachingBiomeProvider implements BiomeProvider, Handle {
private final BiomeProvider delegate;
private final Map<Long, Biome> cache = new HashMap<>();
protected CachingBiomeProvider(BiomeProvider delegate) {
this.delegate = delegate;
}
@Override
public BiomeProvider getHandle() {
return delegate;
}
@Override
public Biome getBiome(int x, int z, long seed) {
return cache.computeIfAbsent(MathUtil.squash(x, z), key -> delegate.getBiome(x, z, seed));
}
@Override
public Iterable<Biome> getBiomes() {
return delegate.getBiomes();
}
}

View File

@ -7,6 +7,7 @@
package com.dfsek.terra.api.world.chunk.generation;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
import com.dfsek.terra.api.world.info.WorldProperties;
import org.jetbrains.annotations.NotNull;
@ -18,7 +19,7 @@ import com.dfsek.terra.api.world.WritableWorld;
public interface ChunkGenerator {
void generateChunkData(@NotNull ProtoChunk chunk, @NotNull WorldProperties world,
void generateChunkData(@NotNull ProtoChunk chunk, @NotNull WorldProperties world, @NotNull BiomeProvider biomeProvider,
int chunkX, int chunkZ);
BlockState getBlock(WorldProperties world, int x, int y, int z);

View File

@ -21,7 +21,6 @@ import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.bukkit.world.BukkitWorldProperties;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.BlockPopulator;
@ -35,11 +34,9 @@ import java.util.Random;
import java.util.stream.Collectors;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.world.ServerWorld;
import com.dfsek.terra.api.world.chunk.generation.ChunkGenerator;
import com.dfsek.terra.api.world.chunk.generation.util.GeneratorWrapper;
import com.dfsek.terra.bukkit.world.BukkitProtoWorld;
import com.dfsek.terra.bukkit.world.BukkitServerWorld;
public class BukkitChunkGeneratorWrapper extends org.bukkit.generator.ChunkGenerator implements GeneratorWrapper {
@ -60,7 +57,7 @@ public class BukkitChunkGeneratorWrapper extends org.bukkit.generator.ChunkGener
@Override
public void generateNoise(@NotNull WorldInfo worldInfo, @NotNull Random random, int x, int z, @NotNull ChunkData chunkData) {
delegate.generateChunkData(new BukkitProtoChunk(chunkData), new BukkitWorldProperties(worldInfo), x, z);
delegate.generateChunkData(new BukkitProtoChunk(chunkData), new BukkitWorldProperties(worldInfo), pack.getBiomeProvider().caching(), x, z);
}
@Override

View File

@ -88,7 +88,7 @@ public class CLIWorld implements ServerWorld, NBTSerializable<Stream<Pair<Vector
try {
int num = amount.getAndIncrement();
CLIChunk chunk = getChunkAt(finalX, finalZ);
chunkGenerator.generateChunkData(chunk, this, finalX, finalZ);
chunkGenerator.generateChunkData(chunk, this, pack.getBiomeProvider().caching(), finalX, finalZ);
CLIProtoWorld protoWorld = new CLIProtoWorld(this, finalX, finalZ);
pack.getStages().forEach(stage -> stage.populate(protoWorld));
if(num % 240 == 239) {

View File

@ -155,7 +155,7 @@ public class FabricChunkGeneratorWrapper extends net.minecraft.world.gen.chunk.C
public CompletableFuture<Chunk> populateNoise(Executor executor, Blender arg, StructureAccessor structureAccessor, Chunk chunk) {
return CompletableFuture.supplyAsync(() -> {
ProtoWorld world = (ProtoWorld) ((StructureAccessorAccessor) structureAccessor).getWorld();
delegate.generateChunkData((ProtoChunk) chunk, world, chunk.getPos().x, chunk.getPos().z);
delegate.generateChunkData((ProtoChunk) chunk, world, pack.getBiomeProvider().caching(), chunk.getPos().x, chunk.getPos().z);
pack.getStages().forEach(populator -> {
if(populator instanceof Chunkified) {
populator.populate(world);