Some refactoring

This commit is contained in:
Zoe Gidiere 2024-10-11 19:45:12 -06:00
parent 305255511d
commit 6851999926
9 changed files with 194 additions and 180 deletions

View File

@ -1,8 +1,9 @@
package com.dfsek.terra.addons.noise.samplers;
import com.dfsek.terra.api.noise.DerivativeNoiseSampler;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.util.cache.DoubleSeededVector2Key;
import com.dfsek.terra.api.util.cache.DoubleSeededVector3Key;
import com.dfsek.terra.api.util.generic.pair.Pair;
import com.dfsek.terra.api.util.generic.pair.Pair.Mutable;
@ -11,135 +12,63 @@ import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.github.benmanes.caffeine.cache.Scheduler;
import static com.dfsek.terra.api.util.CacheUtils.CACHE_EXECUTOR;
import static com.dfsek.terra.api.util.cache.CacheUtils.CACHE_EXECUTOR;
public class CacheSampler implements NoiseSampler {
private final NoiseSampler sampler;
private final ThreadLocal<Mutable<DoubleSeededVector2, LoadingCache<DoubleSeededVector2, Double>>> cache2D;
private final ThreadLocal<Mutable<DoubleSeededVector3, LoadingCache<DoubleSeededVector3, Double>>> cache3D;
private final ThreadLocal<Mutable<DoubleSeededVector2Key, LoadingCache<DoubleSeededVector2Key, Double>>> cache2D;
private final ThreadLocal<Mutable<DoubleSeededVector3Key, LoadingCache<DoubleSeededVector3Key, Double>>> cache3D;
public CacheSampler(NoiseSampler sampler, int dimensions) {
this.sampler = sampler;
if (dimensions == 2) {
LoadingCache<DoubleSeededVector2, Double> cache = Caffeine
LoadingCache<DoubleSeededVector2Key, Double> cache = Caffeine
.newBuilder()
.executor(CACHE_EXECUTOR)
.scheduler(Scheduler.systemScheduler())
.initialCapacity(256)
.maximumSize(256)
.build(this::sampleNoise);
this.cache2D = ThreadLocal.withInitial(() -> Pair.of(new DoubleSeededVector2(0, 0, 0), cache).mutable());
this.cache2D = ThreadLocal.withInitial(() -> Pair.of(new DoubleSeededVector2Key(0, 0, 0), cache).mutable());
this.cache3D = null;
} else {
LoadingCache<DoubleSeededVector3, Double> cache = Caffeine
LoadingCache<DoubleSeededVector3Key, Double> cache = Caffeine
.newBuilder()
.executor(CACHE_EXECUTOR)
.scheduler(Scheduler.systemScheduler())
.initialCapacity(981504)
.maximumSize(981504)
.build(this::sampleNoise);
this.cache3D = ThreadLocal.withInitial(() -> Pair.of(new DoubleSeededVector3(0, 0, 0, 0), cache).mutable());
this.cache3D = ThreadLocal.withInitial(() -> Pair.of(new DoubleSeededVector3Key(0, 0, 0, 0), cache).mutable());
this.cache2D = null;
}
}
private Double sampleNoise(DoubleSeededVector2 vec) {
this.cache2D.get().setLeft(new DoubleSeededVector2(0, 0, 0));
private Double sampleNoise(DoubleSeededVector2Key vec) {
this.cache2D.get().setLeft(new DoubleSeededVector2Key(0, 0, 0));
return this.sampler.noise(vec.seed, vec.x, vec.z);
}
private Double sampleNoise(DoubleSeededVector3 vec) {
this.cache3D.get().setLeft(new DoubleSeededVector3(0, 0, 0, 0));
return this.sampler.noise(vec.seed, vec.x, vec.z);
private Double sampleNoise(DoubleSeededVector3Key vec) {
this.cache3D.get().setLeft(new DoubleSeededVector3Key(0, 0, 0, 0));
return this.sampler.noise(vec.seed, vec.x, vec.y, vec.z);
}
@Override
public double noise(long seed, double x, double y) {
Mutable<DoubleSeededVector2, LoadingCache<DoubleSeededVector2, Double>> cachePair = cache2D.get();
DoubleSeededVector2 mutableKey = cachePair.getLeft();
Mutable<DoubleSeededVector2Key, LoadingCache<DoubleSeededVector2Key, Double>> cachePair = cache2D.get();
DoubleSeededVector2Key mutableKey = cachePair.getLeft();
mutableKey.set(x, y, seed);
return cachePair.getRight().get(mutableKey);
}
@Override
public double noise(long seed, double x, double y, double z) {
Mutable<DoubleSeededVector3, LoadingCache<DoubleSeededVector3, Double>> cachePair = cache3D.get();
DoubleSeededVector3 mutableKey = cachePair.getLeft();
Mutable<DoubleSeededVector3Key, LoadingCache<DoubleSeededVector3Key, Double>> cachePair = cache3D.get();
DoubleSeededVector3Key mutableKey = cachePair.getLeft();
mutableKey.set(x, y, z, seed);
return cachePair.getRight().get(mutableKey);
}
private static class DoubleSeededVector3 {
double x;
double y;
double z;
long seed;
public DoubleSeededVector3(double x, double y, double z, long seed) {
this.x = x;
this.y = y;
this.z = z;
this.seed = seed;
}
public void set(double x, double y, double z, long seed) {
this.x = x;
this.y = y;
this.z = z;
this.seed = seed;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof DoubleSeededVector3 that) {
return this.y == that.y && this.z == that.z && this.x == that.x && this.seed == that.seed;
}
return false;
}
@Override
public int hashCode() {
int code = (int) Double.doubleToLongBits(x);
code = 31 * code + (int) Double.doubleToLongBits(y);
code = 31 * code + (int) Double.doubleToLongBits(z);
return 31 * code + (Long.hashCode(seed));
}
}
private static class DoubleSeededVector2 {
double x;
double z;
long seed;
public DoubleSeededVector2(double x, double z, long seed) {
this.x = x;
this.z = z;
this.seed = seed;
}
public void set(double x, double z, long seed) {
this.x = x;
this.z = z;
this.seed = seed;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof DoubleSeededVector2 that) {
return this.z == that.z && this.x == that.x && this.seed == that.seed;
}
return false;
}
@Override
public int hashCode() {
int code = (int) Double.doubleToLongBits(x);
code = 31 * code + (int) Double.doubleToLongBits(z);
return 31 * code + (Long.hashCode(seed));
}
}
}

View File

@ -17,6 +17,10 @@ import com.dfsek.terra.api.config.Loader;
import com.dfsek.terra.api.properties.Properties;
import com.dfsek.terra.api.util.generic.Lazy;
import com.github.benmanes.caffeine.cache.Scheduler;
import static com.dfsek.terra.api.util.cache.CacheUtils.CACHE_EXECUTOR;
/*
* Cache prevents configs from loading the same image multiple times into memory
@ -26,8 +30,9 @@ record ImageCache(LoadingCache<String, Image> cache) implements Properties {
ImageLibraryPackConfigTemplate config = pack.getContext().get(ImageLibraryPackConfigTemplate.class);
ImageCache images;
if(!pack.getContext().has(ImageCache.class)) {
var cacheBuilder = Caffeine.newBuilder();
if(config.unloadOnTimeout()) cacheBuilder.expireAfterAccess(config.getCacheTimeout(), TimeUnit.SECONDS);
var cacheBuilder = Caffeine.newBuilder().executor(CACHE_EXECUTOR).scheduler(Scheduler.systemScheduler());
if(config.unloadOnTimeout()) cacheBuilder.expireAfterAccess(config.getCacheTimeout(), TimeUnit.SECONDS) .executor(CACHE_EXECUTOR)
.scheduler(Scheduler.systemScheduler());
images = new ImageCache(cacheBuilder.build(s -> loadImage(s, files)));
pack.getContext().put(images);
} else images = pack.getContext().get(ImageCache.class);

View File

@ -1,7 +1,6 @@
package com.dfsek.terra.api.util;
package com.dfsek.terra.api.util.cache;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

View File

@ -0,0 +1,35 @@
package com.dfsek.terra.api.util.cache;
public class DoubleSeededVector2Key {
public double x;
public double z;
public long seed;
public DoubleSeededVector2Key(double x, double z, long seed) {
this.x = x;
this.z = z;
this.seed = seed;
}
public void set(double x, double z, long seed) {
this.x = x;
this.z = z;
this.seed = seed;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof DoubleSeededVector2Key that) {
return this.z == that.z && this.x == that.x && this.seed == that.seed;
}
return false;
}
@Override
public int hashCode() {
int code = (int) Double.doubleToLongBits(x);
code = 31 * code + (int) Double.doubleToLongBits(z);
return 31 * code + (Long.hashCode(seed));
}
}

View File

@ -0,0 +1,38 @@
package com.dfsek.terra.api.util.cache;
public class DoubleSeededVector3Key {
public double x;
public double y;
public double z;
public long seed;
public DoubleSeededVector3Key(double x, double y, double z, long seed) {
this.x = x;
this.y = y;
this.z = z;
this.seed = seed;
}
public void set(double x, double y, double z, long seed) {
this.x = x;
this.y = y;
this.z = z;
this.seed = seed;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof DoubleSeededVector3Key that) {
return this.y == that.y && this.z == that.z && this.x == that.x && this.seed == that.seed;
}
return false;
}
@Override
public int hashCode() {
int code = (int) Double.doubleToLongBits(x);
code = 31 * code + (int) Double.doubleToLongBits(y);
code = 31 * code + (int) Double.doubleToLongBits(z);
return 31 * code + (Long.hashCode(seed));
}
}

View File

@ -0,0 +1,37 @@
package com.dfsek.terra.api.util.cache;
public class SeededVector2Key {
public int x;
public int z;
public long seed;
public SeededVector2Key(int x, int z, long seed) {
this.x = x;
this.z = z;
this.seed = seed;
}
public void set(int x, int z, long seed) {
this.x = x;
this.z = z;
this.seed = seed;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof SeededVector2Key that) {
return this.z == that.z && this.x == that.x && this.seed == that.seed;
}
return false;
}
@Override
public int hashCode() {
int code = x;
code = 31 * code + z;
return 31 * code + (Long.hashCode(seed));
}
}

View File

@ -0,0 +1,40 @@
package com.dfsek.terra.api.util.cache;
public class SeededVector3Key {
public int x;
public int y;
public int z;
public long seed;
public SeededVector3Key(int x, int y, int z, long seed) {
this.x = x;
this.y = y;
this.z = z;
this.seed = seed;
}
public void set(int x, int y, int z, long seed) {
this.x = x;
this.y = y;
this.z = z;
this.seed = seed;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof SeededVector3Key that) {
return this.y == that.y && this.z == that.z && this.x == that.x && this.seed == that.seed;
}
return false;
}
@Override
public int hashCode() {
int code = x;
code = 31 * code + y;
code = 31 * code + z;
return 31 * code + (Long.hashCode(seed));
}
}

View File

@ -1,5 +1,7 @@
package com.dfsek.terra.api.world.biome.generation;
import com.dfsek.terra.api.util.cache.SeededVector2Key;
import com.dfsek.terra.api.util.cache.SeededVector3Key;
import com.dfsek.terra.api.util.generic.pair.Pair;
import com.dfsek.terra.api.util.generic.pair.Pair.Mutable;
@ -13,7 +15,7 @@ import java.util.Optional;
import com.dfsek.terra.api.Handle;
import com.dfsek.terra.api.world.biome.Biome;
import static com.dfsek.terra.api.util.CacheUtils.CACHE_EXECUTOR;
import static com.dfsek.terra.api.util.cache.CacheUtils.CACHE_EXECUTOR;
/**
@ -24,42 +26,42 @@ import static com.dfsek.terra.api.util.CacheUtils.CACHE_EXECUTOR;
public class CachingBiomeProvider implements BiomeProvider, Handle {
protected final BiomeProvider delegate;
private final int res;
private final ThreadLocal<Pair.Mutable<SeededVector3, LoadingCache<SeededVector3, Biome>>> cache;
private final ThreadLocal<Pair.Mutable<SeededVector2, LoadingCache<SeededVector2, Optional<Biome>>>> baseCache;
private final ThreadLocal<Pair.Mutable<SeededVector3Key, LoadingCache<SeededVector3Key, Biome>>> cache;
private final ThreadLocal<Pair.Mutable<SeededVector2Key, LoadingCache<SeededVector2Key, Optional<Biome>>>> baseCache;
protected CachingBiomeProvider(BiomeProvider delegate) {
this.delegate = delegate;
this.res = delegate.resolution();
LoadingCache<SeededVector2, Optional<Biome>> cache = Caffeine
LoadingCache<SeededVector2Key, Optional<Biome>> cache = Caffeine
.newBuilder()
.executor(CACHE_EXECUTOR)
.scheduler(Scheduler.systemScheduler())
.initialCapacity(256)
.maximumSize(256)
.build(this::sampleBiome);
this.baseCache = ThreadLocal.withInitial(() -> Pair.of(new SeededVector2(0, 0, 0), cache).mutable());
this.baseCache = ThreadLocal.withInitial(() -> Pair.of(new SeededVector2Key(0, 0, 0), cache).mutable());
LoadingCache<SeededVector3, Biome> cache3D = Caffeine
LoadingCache<SeededVector3Key, Biome> cache3D = Caffeine
.newBuilder()
.executor(CACHE_EXECUTOR)
.scheduler(Scheduler.systemScheduler())
.initialCapacity(981504)
.maximumSize(981504)
.build(this::sampleBiome);
this.cache = ThreadLocal.withInitial(() -> Pair.of(new SeededVector3(0, 0, 0, 0), cache3D).mutable());
this.cache = ThreadLocal.withInitial(() -> Pair.of(new SeededVector3Key(0, 0, 0, 0), cache3D).mutable());
}
private Optional<Biome> sampleBiome(SeededVector2 vec) {
this.baseCache.get().setLeft(new SeededVector2(0, 0, 0));
private Optional<Biome> sampleBiome(SeededVector2Key vec) {
this.baseCache.get().setLeft(new SeededVector2Key(0, 0, 0));
return this.delegate.getBaseBiome(vec.x * res, vec.z * res, vec.seed);
}
private Biome sampleBiome(SeededVector3 vec) {
this.cache.get().setLeft(new SeededVector3(0, 0, 0, 0));
private Biome sampleBiome(SeededVector3Key vec) {
this.cache.get().setLeft(new SeededVector3Key(0, 0, 0, 0));
return this.delegate.getBiome(vec.x * res, vec.y * res, vec.z * res, vec.seed);
}
@ -70,16 +72,16 @@ public class CachingBiomeProvider implements BiomeProvider, Handle {
@Override
public Biome getBiome(int x, int y, int z, long seed) {
Mutable<SeededVector3, LoadingCache<SeededVector3, Biome>> cachePair = cache.get();
SeededVector3 mutableKey = cachePair.getLeft();
Mutable<SeededVector3Key, LoadingCache<SeededVector3Key, Biome>> cachePair = cache.get();
SeededVector3Key mutableKey = cachePair.getLeft();
mutableKey.set(x, y, z, seed);
return cachePair.getRight().get(mutableKey);
}
@Override
public Optional<Biome> getBaseBiome(int x, int z, long seed) {
Mutable<SeededVector2, LoadingCache<SeededVector2, Optional<Biome>>> cachePair = baseCache.get();
SeededVector2 mutableKey = cachePair.getLeft();
Mutable<SeededVector2Key, LoadingCache<SeededVector2Key, Optional<Biome>>> cachePair = baseCache.get();
SeededVector2Key mutableKey = cachePair.getLeft();
mutableKey.set(x, z, seed);
return cachePair.getRight().get(mutableKey);
}
@ -93,75 +95,4 @@ public class CachingBiomeProvider implements BiomeProvider, Handle {
public int resolution() {
return delegate.resolution();
}
private static class SeededVector3 {
int x;
int y;
int z;
long seed;
public SeededVector3(int x, int y, int z, long seed) {
this.x = x;
this.y = y;
this.z = z;
this.seed = seed;
}
public void set(int x, int y, int z, long seed) {
this.x = x;
this.y = y;
this.z = z;
this.seed = seed;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof SeededVector3 that) {
return this.y == that.y && this.z == that.z && this.x == that.x && this.seed == that.seed;
}
return false;
}
@Override
public int hashCode() {
int code = x;
code = 31 * code + y;
code = 31 * code + z;
return 31 * code + (Long.hashCode(seed));
}
}
private static class SeededVector2 {
int x;
int z;
long seed;
public SeededVector2(int x, int z, long seed) {
this.x = x;
this.z = z;
this.seed = seed;
}
public void set(int x, int z, long seed) {
this.x = x;
this.z = z;
this.seed = seed;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof SeededVector2 that) {
return this.z == that.z && this.x == that.x && this.seed == that.seed;
}
return false;
}
@Override
public int hashCode() {
int code = x;
code = 31 * code + z;
return 31 * code + (Long.hashCode(seed));
}
}
}

View File

@ -31,7 +31,7 @@ public final class TerraCLI implements Callable<Integer> {
private long seed = 0;
@Option(names = { "--max-height"}, description = "Maximum height of the world.")
private int maxHeight = 384;
private int maxHeight = 320;
@Option(names = { "--min-height"}, description = "Minimum height of the world.")
private int minHeight = -64;