replace dumb caches with guava caches

This commit is contained in:
dfsek 2021-01-14 00:34:23 -07:00
parent 8fd3530653
commit cbc2885c16
7 changed files with 81 additions and 113 deletions

View File

@ -32,21 +32,21 @@ import com.dfsek.terra.debug.Debug;
import com.dfsek.terra.generation.math.SamplerCache; import com.dfsek.terra.generation.math.SamplerCache;
import com.dfsek.terra.registry.LootRegistry; import com.dfsek.terra.registry.LootRegistry;
import com.dfsek.terra.registry.ScriptRegistry; import com.dfsek.terra.registry.ScriptRegistry;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import net.jafama.FastMath; import net.jafama.FastMath;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Random; import java.util.Random;
import java.util.concurrent.ExecutionException;
public class StructureScript { public class StructureScript {
private final Block block; private final Block block;
private final String id; private final String id;
String tempID; String tempID;
private final Map<Location, StructureBuffer> cache; private final Cache<Location, StructureBuffer> cache;
private final TerraPlugin main; private final TerraPlugin main;
public StructureScript(InputStream inputStream, TerraPlugin main, ScriptRegistry registry, LootRegistry lootRegistry, SamplerCache cache) throws ParseException { public StructureScript(InputStream inputStream, TerraPlugin main, ScriptRegistry registry, LootRegistry lootRegistry, SamplerCache cache) throws ParseException {
@ -88,12 +88,7 @@ public class StructureScript {
this.id = parser.getID(); this.id = parser.getID();
tempID = id; tempID = id;
this.main = main; this.main = main;
this.cache = Collections.synchronizedMap(new LinkedHashMap<Location, StructureBuffer>() { this.cache = CacheBuilder.newBuilder().maximumSize(main.getTerraConfig().getStructureCache()).build();
@Override
protected boolean removeEldestEntry(Map.Entry<Location, StructureBuffer> eldest) {
return this.size() > main.getTerraConfig().getStructureCache();
}
});
} }
/** /**
@ -122,12 +117,14 @@ public class StructureScript {
} }
private StructureBuffer computeBuffer(Location location, Random random, Rotation rotation) { private StructureBuffer computeBuffer(Location location, Random random, Rotation rotation) {
synchronized(cache) { try {
return cache.computeIfAbsent(location, loc -> { return cache.get(location, () -> {
StructureBuffer buf = new StructureBuffer(loc); StructureBuffer buf = new StructureBuffer(location);
buf.setSucceeded(applyBlock(new TerraImplementationArguments(buf, rotation, random, 0))); buf.setSucceeded(applyBlock(new TerraImplementationArguments(buf, rotation, random, 0)));
return buf; return buf;
}); });
} catch(ExecutionException e) {
throw new RuntimeException(e);
} }
} }

View File

@ -1,25 +0,0 @@
package com.dfsek.terra.biome;
import com.dfsek.terra.api.math.vector.Vector2;
import com.dfsek.terra.biome.pipeline.BiomeHolder;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.jetbrains.annotations.NotNull;
public class BiomeCache {
LoadingCache<Vector2, BiomeHolder> cache;
public BiomeCache() {
cache = CacheBuilder.newBuilder()
.maximumSize(1024)
.build(
new CacheLoader<Vector2, BiomeHolder>() {
@Override
public BiomeHolder load(@NotNull Vector2 key) throws Exception {
return null;
}
}
);
}
}

View File

@ -50,11 +50,7 @@ public class StandardBiomeProvider implements BiomeProvider {
@Override @Override
public TerraBiome getBiome(int x, int z) { public TerraBiome getBiome(int x, int z) {
try { return biomeCache.getUnchecked(new Vector2(x, z));
return biomeCache.get(new Vector2(x, z));
} catch(ExecutionException e) {
throw new RuntimeException(e);
}
} }
public int getResolution() { public int getResolution() {

View File

@ -10,52 +10,50 @@ import com.dfsek.terra.api.world.biome.TerraBiome;
import com.dfsek.terra.api.world.carving.Worm; import com.dfsek.terra.api.world.carving.Worm;
import com.dfsek.terra.biome.BiomeProvider; import com.dfsek.terra.biome.BiomeProvider;
import com.dfsek.terra.biome.UserDefinedBiome; import com.dfsek.terra.biome.UserDefinedBiome;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Random; import java.util.Random;
public class CarverCache { public class CarverCache {
private final World w; private final LoadingCache<Long, List<Worm.WormPoint>> cache;
private final Map<Long, List<Worm.WormPoint>> carvers; private final UserDefinedCarver carver;
private final TerraPlugin main;
public CarverCache(World w, TerraPlugin main) { public CarverCache(World w, TerraPlugin main, UserDefinedCarver carver) {
this.w = w; this.carver = carver;
this.main = main; cache = CacheBuilder.newBuilder().maximumSize(main.getTerraConfig().getCarverCacheSize())
carvers = Collections.synchronizedMap(new LinkedHashMap<Long, List<Worm.WormPoint>>() { .build(new CacheLoader<Long, List<Worm.WormPoint>>() {
@Override @Override
protected boolean removeEldestEntry(Map.Entry eldest) { public List<Worm.WormPoint> load(@NotNull Long key) {
return this.size() > main.getTerraConfig().getCarverCacheSize(); int chunkX = (int) (key >> 32);
} int chunkZ = (int) key.longValue();
}); BiomeProvider provider = main.getWorld(w).getBiomeProvider();
if(CarverCache.this.carver.isChunkCarved(w, chunkX, chunkZ, new FastRandom(MathUtil.getCarverChunkSeed(chunkX, chunkZ, w.getSeed() + CarverCache.this.carver.hashCode())))) {
long seed = MathUtil.getCarverChunkSeed(chunkX, chunkZ, w.getSeed());
CarverCache.this.carver.getSeedVar().setValue(seed);
Random r = new FastRandom(seed);
Worm carving = CarverCache.this.carver.getWorm(seed, new Vector3((chunkX << 4) + r.nextInt(16), CarverCache.this.carver.getConfig().getHeight().get(r), (chunkZ << 4) + r.nextInt(16)));
List<Worm.WormPoint> points = new GlueList<>();
for(int i = 0; i < carving.getLength(); i++) {
carving.step();
TerraBiome biome = provider.getBiome(carving.getRunning().toLocation(w));
if(!((UserDefinedBiome) biome).getConfig().getCarvers().containsKey(CarverCache.this.carver)) { // Stop if we enter a biome this carver is not present in
return new GlueList<>();
}
points.add(carving.getPoint());
}
return points;
}
return new GlueList<>();
}
});
} }
public List<Worm.WormPoint> getPoints(int chunkX, int chunkZ, UserDefinedCarver carver) { public List<Worm.WormPoint> getPoints(int chunkX, int chunkZ) {
synchronized(carvers) { return cache.getUnchecked(MathUtil.squash(chunkX, chunkZ));
return carvers.computeIfAbsent(MathUtil.squash(chunkX, chunkZ), key -> {
BiomeProvider provider = main.getWorld(w).getBiomeProvider();
if(carver.isChunkCarved(w, chunkX, chunkZ, new FastRandom(MathUtil.getCarverChunkSeed(chunkX, chunkZ, w.getSeed() + carver.hashCode())))) {
long seed = MathUtil.getCarverChunkSeed(chunkX, chunkZ, w.getSeed());
carver.getSeedVar().setValue(seed);
Random r = new FastRandom(seed);
Worm carving = carver.getWorm(seed, new Vector3((chunkX << 4) + r.nextInt(16), carver.getConfig().getHeight().get(r), (chunkZ << 4) + r.nextInt(16)));
List<Worm.WormPoint> points = new GlueList<>();
for(int i = 0; i < carving.getLength(); i++) {
carving.step();
TerraBiome biome = provider.getBiome(carving.getRunning().toLocation(w));
if(!((UserDefinedBiome) biome).getConfig().getCarvers().containsKey(carver)) { // Stop if we enter a biome this carver is not present in
return new GlueList<>();
}
points.add(carving.getPoint());
}
return points;
}
return new GlueList<>();
});
}
} }
} }

View File

@ -11,6 +11,7 @@ import com.dfsek.terra.api.world.carving.Worm;
import com.dfsek.terra.biome.UserDefinedBiome; import com.dfsek.terra.biome.UserDefinedBiome;
import com.dfsek.terra.config.templates.BiomeTemplate; import com.dfsek.terra.config.templates.BiomeTemplate;
import com.dfsek.terra.config.templates.CarverTemplate; import com.dfsek.terra.config.templates.CarverTemplate;
import com.dfsek.terra.debug.Debug;
import net.jafama.FastMath; import net.jafama.FastMath;
import parsii.eval.Expression; import parsii.eval.Expression;
import parsii.eval.Parser; import parsii.eval.Parser;
@ -100,11 +101,12 @@ public class UserDefinedCarver extends Carver {
@Override @Override
public void carve(int chunkX, int chunkZ, World w, BiConsumer<Vector3, CarvingType> consumer) { public void carve(int chunkX, int chunkZ, World w, BiConsumer<Vector3, CarvingType> consumer) {
CarverCache cache = cacheMap.computeIfAbsent(w, world -> new CarverCache(world, main)); CarverCache cache = cacheMap.computeIfAbsent(w, world -> new CarverCache(world, main, this));
if(cacheMap.size() > 1) Debug.info("Map size: " + cacheMap.size());
int carvingRadius = getCarvingRadius(); int carvingRadius = getCarvingRadius();
for(int x = chunkX - carvingRadius; x <= chunkX + carvingRadius; x++) { for(int x = chunkX - carvingRadius; x <= chunkX + carvingRadius; x++) {
for(int z = chunkZ - carvingRadius; z <= chunkZ + carvingRadius; z++) { for(int z = chunkZ - carvingRadius; z <= chunkZ + carvingRadius; z++) {
cache.getPoints(x, z, this).forEach(point -> { cache.getPoints(x, z).forEach(point -> {
Vector3 origin = point.getOrigin(); Vector3 origin = point.getOrigin();
if(FastMath.floorDiv(origin.getBlockX(), 16) != chunkX && FastMath.floorDiv(origin.getBlockZ(), 16) != chunkZ) // We only want to carve this chunk. if(FastMath.floorDiv(origin.getBlockX(), 16) != chunkX && FastMath.floorDiv(origin.getBlockZ(), 16) != chunkZ) // We only want to carve this chunk.
return; return;

View File

@ -4,11 +4,13 @@ import com.dfsek.terra.TerraWorld;
import com.dfsek.terra.api.math.MathUtil; import com.dfsek.terra.api.math.MathUtil;
import com.dfsek.terra.api.platform.TerraPlugin; import com.dfsek.terra.api.platform.TerraPlugin;
import com.dfsek.terra.api.platform.world.World; import com.dfsek.terra.api.platform.world.World;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import net.jafama.FastMath; import net.jafama.FastMath;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
public class SamplerCache { public class SamplerCache {
@ -35,14 +37,18 @@ public class SamplerCache {
private class Container { private class Container {
private final World world; private final World world;
private final TerraWorld terraWorld; private final TerraWorld terraWorld;
private final Map<Long, Sampler> cache = Collections.synchronizedMap(new LinkedHashMap<Long, Sampler>() { private final LoadingCache<Long, Sampler> cache;
@Override
protected boolean removeEldestEntry(Map.Entry<Long, Sampler> eldest) {
return this.size() > main.getTerraConfig().getSamplerCache();
}
});
private Container(World world) { private Container(World world) {
cache = CacheBuilder.newBuilder().maximumSize(main.getTerraConfig().getSamplerCache())
.build(new CacheLoader<Long, Sampler>() {
@Override
public Sampler load(@NotNull Long key) {
int cx = (int) (key >> 32);
int cz = (int) key.longValue();
return new Sampler(cx, cz, terraWorld.getBiomeProvider(), world, terraWorld.getConfig().getTemplate().getBaseBlend(), terraWorld.getConfig().getTemplate().getElevationBlend());
}
});
this.world = world; this.world = world;
terraWorld = main.getWorld(world); terraWorld = main.getWorld(world);
} }
@ -55,9 +61,7 @@ public class SamplerCache {
public Sampler getChunk(int cx, int cz) { public Sampler getChunk(int cx, int cz) {
long key = MathUtil.squash(cx, cz); long key = MathUtil.squash(cx, cz);
synchronized(cache) { return cache.getUnchecked(key);
return cache.computeIfAbsent(key, k -> new Sampler(cx, cz, terraWorld.getBiomeProvider(), world, terraWorld.getConfig().getTemplate().getBaseBlend(), terraWorld.getConfig().getTemplate().getElevationBlend()));
}
} }
} }
} }

View File

@ -61,25 +61,21 @@ public class BiomeTest {
BiomeSource source = new RandomSource(climate, sourceSampler); BiomeSource source = new RandomSource(climate, sourceSampler);
BiomeProvider provider = new StandardBiomeProvider.StandardBiomeProviderBuilder((seed) -> { BiomeProvider provider = new StandardBiomeProvider.StandardBiomeProviderBuilder((seed) -> new BiomePipeline.BiomePipelineBuilder(2)
BiomePipeline pipeline = new BiomePipeline.BiomePipelineBuilder(2) .addStage(new ExpanderStage(new FractalExpander(whiteNoise(1))))
.addStage(new ExpanderStage(new FractalExpander(whiteNoise(1)))) .addStage(new MutatorStage(new ReplaceMutator("OCEAN_TEMP", oceanBiomes, whiteNoise(243))))
.addStage(new MutatorStage(new ReplaceMutator("OCEAN_TEMP", oceanBiomes, whiteNoise(243)))) .addStage(new MutatorStage(new ReplaceMutator("LAND_TEMP", landBiomes, whiteNoise(243))))
.addStage(new MutatorStage(new ReplaceMutator("LAND_TEMP", landBiomes, whiteNoise(243)))) .addStage(new ExpanderStage(new FractalExpander(whiteNoise(2))))
.addStage(new ExpanderStage(new FractalExpander(whiteNoise(2)))) .addStage(new ExpanderStage(new FractalExpander(whiteNoise(2))))
.addStage(new ExpanderStage(new FractalExpander(whiteNoise(2)))) .addStage(new MutatorStage(new SmoothMutator(whiteNoise(3))))
.addStage(new MutatorStage(new SmoothMutator(whiteNoise(3)))) .addStage(new ExpanderStage(new FractalExpander(whiteNoise(4))))
.addStage(new ExpanderStage(new FractalExpander(whiteNoise(4)))) .addStage(new ExpanderStage(new FractalExpander(whiteNoise(4))))
.addStage(new ExpanderStage(new FractalExpander(whiteNoise(4)))) .addStage(new ExpanderStage(new FractalExpander(whiteNoise(4))))
.addStage(new ExpanderStage(new FractalExpander(whiteNoise(4)))) .addStage(new ExpanderStage(new FractalExpander(whiteNoise(4))))
.addStage(new ExpanderStage(new FractalExpander(whiteNoise(4)))) .addStage(new MutatorStage(new BorderMutator("OCEAN", "LAND", whiteNoise(1234), beachBiomes)))
.addStage(new MutatorStage(new BorderMutator("OCEAN", "LAND", whiteNoise(1234), beachBiomes))) .addStage(new MutatorStage(new SmoothMutator(whiteNoise(6))))
.addStage(new MutatorStage(new SmoothMutator(whiteNoise(6)))) .addStage(new MutatorStage(new SmoothMutator(whiteNoise(6))))
.addStage(new MutatorStage(new SmoothMutator(whiteNoise(6)))) .build(source), null).build(0);
.build(source);
System.out.println("Size: " + pipeline.getSize());
return pipeline;
}, null).build(0);
int size = 1000; int size = 1000;