mirror of
https://github.com/PolyhedralDev/Terra.git
synced 2025-07-01 23:47:50 +00:00
WIP Caching Hints and Caching Sampler
This commit is contained in:
parent
370b2e0122
commit
1ae0d1f867
@ -17,6 +17,7 @@ import com.dfsek.terra.addons.manifest.api.AddonInitializer;
|
|||||||
import com.dfsek.terra.addons.noise.config.CubicSplinePointTemplate;
|
import com.dfsek.terra.addons.noise.config.CubicSplinePointTemplate;
|
||||||
import com.dfsek.terra.addons.noise.config.DimensionApplicableNoiseSampler;
|
import com.dfsek.terra.addons.noise.config.DimensionApplicableNoiseSampler;
|
||||||
import com.dfsek.terra.addons.noise.config.templates.BinaryArithmeticTemplate;
|
import com.dfsek.terra.addons.noise.config.templates.BinaryArithmeticTemplate;
|
||||||
|
import com.dfsek.terra.addons.noise.config.templates.CacheSamplerTemplate;
|
||||||
import com.dfsek.terra.addons.noise.config.templates.DerivativeNoiseSamplerTemplate;
|
import com.dfsek.terra.addons.noise.config.templates.DerivativeNoiseSamplerTemplate;
|
||||||
import com.dfsek.terra.addons.noise.config.templates.DomainWarpTemplate;
|
import com.dfsek.terra.addons.noise.config.templates.DomainWarpTemplate;
|
||||||
import com.dfsek.terra.addons.noise.config.templates.FunctionTemplate;
|
import com.dfsek.terra.addons.noise.config.templates.FunctionTemplate;
|
||||||
@ -150,6 +151,8 @@ public class NoiseAddon implements AddonInitializer {
|
|||||||
noiseRegistry.register(addon.key("MAX"), () -> new BinaryArithmeticTemplate<>(MaxSampler::new));
|
noiseRegistry.register(addon.key("MAX"), () -> new BinaryArithmeticTemplate<>(MaxSampler::new));
|
||||||
noiseRegistry.register(addon.key("MIN"), () -> new BinaryArithmeticTemplate<>(MinSampler::new));
|
noiseRegistry.register(addon.key("MIN"), () -> new BinaryArithmeticTemplate<>(MinSampler::new));
|
||||||
|
|
||||||
|
noiseRegistry.register(addon.key("CACHE"), () -> new CacheSamplerTemplate(plugin.getGenerationThreads()));
|
||||||
|
|
||||||
|
|
||||||
Map<String, DimensionApplicableNoiseSampler> packSamplers = new LinkedHashMap<>();
|
Map<String, DimensionApplicableNoiseSampler> packSamplers = new LinkedHashMap<>();
|
||||||
Map<String, FunctionTemplate> packFunctions = new LinkedHashMap<>();
|
Map<String, FunctionTemplate> packFunctions = new LinkedHashMap<>();
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.dfsek.terra.addons.noise.config.templates;
|
||||||
|
|
||||||
|
import com.dfsek.tectonic.api.config.template.annotations.Default;
|
||||||
|
import com.dfsek.tectonic.api.config.template.annotations.Value;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.noise.samplers.CacheSampler;
|
||||||
|
import com.dfsek.terra.addons.noise.samplers.LinearHeightmapSampler;
|
||||||
|
import com.dfsek.terra.api.noise.NoiseSampler;
|
||||||
|
|
||||||
|
|
||||||
|
public class CacheSamplerTemplate extends SamplerTemplate<LinearHeightmapSampler> {
|
||||||
|
@Value("sampler")
|
||||||
|
@Default
|
||||||
|
private NoiseSampler sampler;
|
||||||
|
|
||||||
|
private final int generationThreads;
|
||||||
|
|
||||||
|
public CacheSamplerTemplate(int generationThreads) {
|
||||||
|
this.generationThreads = generationThreads;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NoiseSampler get() {
|
||||||
|
return new CacheSampler(sampler, getDimensions(), generationThreads);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,125 @@
|
|||||||
|
package com.dfsek.terra.addons.noise.samplers;
|
||||||
|
|
||||||
|
import com.dfsek.terra.api.noise.DerivativeNoiseSampler;
|
||||||
|
import com.dfsek.terra.api.noise.NoiseSampler;
|
||||||
|
|
||||||
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
|
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||||
|
|
||||||
|
|
||||||
|
public class CacheSampler implements DerivativeNoiseSampler {
|
||||||
|
|
||||||
|
private final NoiseSampler sampler;
|
||||||
|
private final LoadingCache<DoubleSeededVector2, Double> cache2D;
|
||||||
|
private final LoadingCache<DoubleSeededVector3, Double> cache3D;
|
||||||
|
private final LoadingCache<DoubleSeededVector2, double[]> cache2DDirv;
|
||||||
|
private final LoadingCache<DoubleSeededVector3, double[]> cache3DDirv;
|
||||||
|
|
||||||
|
public CacheSampler(NoiseSampler sampler, int dimensions, int generationThreads) {
|
||||||
|
this.sampler = sampler;
|
||||||
|
if (dimensions == 2) {
|
||||||
|
this.cache2D = Caffeine
|
||||||
|
.newBuilder()
|
||||||
|
.initialCapacity(0)
|
||||||
|
.maximumSize(256L * generationThreads) // 1 full chunk (high res)
|
||||||
|
.build(vec -> sampler.noise(vec.seed, vec.x, vec.z));
|
||||||
|
cache3D = null;
|
||||||
|
cache3DDirv = null;
|
||||||
|
if (DerivativeNoiseSampler.isDifferentiable(sampler)) {
|
||||||
|
this.cache2DDirv = Caffeine
|
||||||
|
.newBuilder()
|
||||||
|
.initialCapacity(0)
|
||||||
|
.maximumSize(256L * generationThreads) // 1 full chunk (high res)
|
||||||
|
.build(vec -> ((DerivativeNoiseSampler) sampler).noised(vec.seed, vec.x, vec.z));
|
||||||
|
} else {
|
||||||
|
cache2DDirv = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.cache3D = Caffeine
|
||||||
|
.newBuilder()
|
||||||
|
.initialCapacity(0)
|
||||||
|
.maximumSize(256L * generationThreads) // 1 full chunk (high res)
|
||||||
|
.build(vec -> sampler.noise(vec.seed, vec.x, vec.y, vec.z));
|
||||||
|
cache2D = null;
|
||||||
|
cache2DDirv = null;
|
||||||
|
if (DerivativeNoiseSampler.isDifferentiable(sampler)) {
|
||||||
|
this.cache3DDirv = Caffeine
|
||||||
|
.newBuilder()
|
||||||
|
.initialCapacity(0)
|
||||||
|
.maximumSize(256L * generationThreads) // 1 full chunk (high res)
|
||||||
|
.build(vec -> ((DerivativeNoiseSampler) sampler).noised(vec.seed, vec.x, vec.y, vec.z));
|
||||||
|
} else {
|
||||||
|
cache3DDirv = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDifferentiable() {
|
||||||
|
return DerivativeNoiseSampler.isDifferentiable(sampler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] noised(long seed, double x, double y) {
|
||||||
|
return cache2DDirv.get(new DoubleSeededVector2(x, y, seed));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double[] noised(long seed, double x, double y, double z) {
|
||||||
|
return cache3DDirv.get(new DoubleSeededVector3(x, y, z, seed));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double noise(long seed, double x, double y) {
|
||||||
|
DoubleSeededVector2 vec = new DoubleSeededVector2(x, y, seed);
|
||||||
|
if (cache2DDirv != null && cache2DDirv.estimatedSize() != 0) {
|
||||||
|
return cache2DDirv.get(vec)[0];
|
||||||
|
}
|
||||||
|
return cache2D.get(vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double noise(long seed, double x, double y, double z) {
|
||||||
|
DoubleSeededVector3 vec = new DoubleSeededVector3(x, y, z, seed);
|
||||||
|
if (cache3DDirv != null && cache3DDirv.estimatedSize() != 0) {
|
||||||
|
return cache3DDirv.get(vec)[0];
|
||||||
|
}
|
||||||
|
return cache3D.get(vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
private record DoubleSeededVector3(double x, double y, double z, long 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 record DoubleSeededVector2(double x, double z, long 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -81,4 +81,7 @@ public interface Platform extends LoaderRegistrar {
|
|||||||
@NotNull
|
@NotNull
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
Profiler getProfiler();
|
Profiler getProfiler();
|
||||||
|
|
||||||
|
@Contract(pure = true)
|
||||||
|
int getGenerationThreads();
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
package com.dfsek.terra.api.world.biome.generation;
|
package com.dfsek.terra.api.world.biome.generation;
|
||||||
|
|
||||||
|
import com.dfsek.terra.api.Platform;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Contract;
|
import org.jetbrains.annotations.Contract;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@ -91,11 +93,11 @@ public interface BiomeProvider {
|
|||||||
return StreamSupport.stream(getBiomes().spliterator(), false);
|
return StreamSupport.stream(getBiomes().spliterator(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
default CachingBiomeProvider caching() {
|
default CachingBiomeProvider caching(Platform platform) {
|
||||||
if(this instanceof CachingBiomeProvider cachingBiomeProvider) {
|
if(this instanceof CachingBiomeProvider cachingBiomeProvider) {
|
||||||
return cachingBiomeProvider;
|
return cachingBiomeProvider;
|
||||||
}
|
}
|
||||||
return new CachingBiomeProvider(this);
|
return new CachingBiomeProvider(this, platform.getGenerationThreads());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,19 +21,20 @@ public class CachingBiomeProvider implements BiomeProvider, Handle {
|
|||||||
private final LoadingCache<SeededVector3, Biome> cache;
|
private final LoadingCache<SeededVector3, Biome> cache;
|
||||||
private final LoadingCache<SeededVector2, Optional<Biome>> baseCache;
|
private final LoadingCache<SeededVector2, Optional<Biome>> baseCache;
|
||||||
|
|
||||||
protected CachingBiomeProvider(BiomeProvider delegate) {
|
protected CachingBiomeProvider(BiomeProvider delegate, int generationThreads) {
|
||||||
this.delegate = delegate;
|
this.delegate = delegate;
|
||||||
this.res = delegate.resolution();
|
this.res = delegate.resolution();
|
||||||
|
int size = generationThreads * 98304;
|
||||||
this.cache = Caffeine
|
this.cache = Caffeine
|
||||||
.newBuilder()
|
.newBuilder()
|
||||||
.scheduler(Scheduler.disabledScheduler())
|
.scheduler(Scheduler.disabledScheduler())
|
||||||
.initialCapacity(98304)
|
.initialCapacity(size)
|
||||||
.maximumSize(98304) // 1 full chunk (high res)
|
.maximumSize(size) // 1 full chunk (high res)
|
||||||
.build(vec -> delegate.getBiome(vec.x * res, vec.y * res, vec.z * res, vec.seed));
|
.build(vec -> delegate.getBiome(vec.x * res, vec.y * res, vec.z * res, vec.seed));
|
||||||
|
|
||||||
this.baseCache = Caffeine
|
this.baseCache = Caffeine
|
||||||
.newBuilder()
|
.newBuilder()
|
||||||
.maximumSize(256) // 1 full chunk (high res)
|
.maximumSize(256L * generationThreads) // 1 full chunk (high res)
|
||||||
.build(vec -> delegate.getBaseBiome(vec.x * res, vec.z * res, vec.seed));
|
.build(vec -> delegate.getBaseBiome(vec.x * res, vec.z * res, vec.seed));
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -77,7 +78,7 @@ public class CachingBiomeProvider implements BiomeProvider, Handle {
|
|||||||
int code = x;
|
int code = x;
|
||||||
code = 31 * code + y;
|
code = 31 * code + y;
|
||||||
code = 31 * code + z;
|
code = 31 * code + z;
|
||||||
return 31 * code + ((int) (seed ^ (seed >>> 32)));
|
return 31 * code + (Long.hashCode(seed));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,7 +96,7 @@ public class CachingBiomeProvider implements BiomeProvider, Handle {
|
|||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int code = x;
|
int code = x;
|
||||||
code = 31 * code + z;
|
code = 31 * code + z;
|
||||||
return 31 * code + ((int) (seed ^ (seed >>> 32)));
|
return 31 * code + (Long.hashCode(seed));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -310,6 +310,24 @@ public abstract class AbstractPlatform implements Platform {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int getGenerationThreadsWithReflection(String className, String fieldName, String project) {
|
||||||
|
try {
|
||||||
|
Class aClass = Class.forName(className);
|
||||||
|
int threads = aClass.getField(fieldName).getInt(null);
|
||||||
|
logger.info("{} found, setting {} generation threads.", project, threads);
|
||||||
|
return threads;
|
||||||
|
} catch(ClassNotFoundException e) {
|
||||||
|
logger.info("{} not found.", project);
|
||||||
|
} catch(NoSuchFieldException e) {
|
||||||
|
logger.warn("{} found, but {} field not found this probably means {0} has changed its code and " +
|
||||||
|
"Terra has not updated to reflect that.", project, fieldName);
|
||||||
|
} catch(IllegalAccessException e) {
|
||||||
|
logger.error("Failed to access {} field in {}, assuming 1 generation thread.", fieldName, project, e);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void register(TypeRegistry registry) {
|
public void register(TypeRegistry registry) {
|
||||||
loaders.register(registry);
|
loaders.register(registry);
|
||||||
@ -339,4 +357,9 @@ public abstract class AbstractPlatform implements Platform {
|
|||||||
public @NotNull Profiler getProfiler() {
|
public @NotNull Profiler getProfiler() {
|
||||||
return profiler;
|
return profiler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getGenerationThreads() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -232,7 +232,7 @@ public class ConfigPackImpl implements ConfigPack {
|
|||||||
ConfigPackPostTemplate packPostTemplate = new ConfigPackPostTemplate();
|
ConfigPackPostTemplate packPostTemplate = new ConfigPackPostTemplate();
|
||||||
selfLoader.load(packPostTemplate, packManifest);
|
selfLoader.load(packPostTemplate, packManifest);
|
||||||
seededBiomeProvider =
|
seededBiomeProvider =
|
||||||
template.getBiomeCache() ? packPostTemplate.getProviderBuilder().caching() : packPostTemplate.getProviderBuilder();
|
template.getBiomeCache() ? packPostTemplate.getProviderBuilder().caching(platform) : packPostTemplate.getProviderBuilder();
|
||||||
checkDeadEntries();
|
checkDeadEntries();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +51,13 @@ public class PlatformImpl extends AbstractPlatform {
|
|||||||
|
|
||||||
private final TerraBukkitPlugin plugin;
|
private final TerraBukkitPlugin plugin;
|
||||||
|
|
||||||
|
private int generationThreads;
|
||||||
|
|
||||||
public PlatformImpl(TerraBukkitPlugin plugin) {
|
public PlatformImpl(TerraBukkitPlugin plugin) {
|
||||||
|
generationThreads = getGenerationThreadsWithReflection("ca.spottedleaf.moonrise.common.util.MoonriseCommon", "WORKER_THREADS", "Moonrise");
|
||||||
|
if (generationThreads == 0) {
|
||||||
|
generationThreads = 1;
|
||||||
|
}
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
load();
|
load();
|
||||||
}
|
}
|
||||||
@ -108,6 +114,11 @@ public class PlatformImpl extends AbstractPlatform {
|
|||||||
return itemHandle;
|
return itemHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getGenerationThreads() {
|
||||||
|
return generationThreads;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void register(TypeRegistry registry) {
|
public void register(TypeRegistry registry) {
|
||||||
super.register(registry);
|
super.register(registry);
|
||||||
|
@ -22,6 +22,8 @@ public class CLIPlatform extends AbstractPlatform {
|
|||||||
private final CLIWorldHandle worldHandle = new CLIWorldHandle();
|
private final CLIWorldHandle worldHandle = new CLIWorldHandle();
|
||||||
private final CLIItemHandle itemHandle = new CLIItemHandle();
|
private final CLIItemHandle itemHandle = new CLIItemHandle();
|
||||||
|
|
||||||
|
private final int generationThreads = Runtime.getRuntime().availableProcessors() - 1;
|
||||||
|
|
||||||
public CLIPlatform() {
|
public CLIPlatform() {
|
||||||
LOGGER.info("Root directory: {}", getDataFolder().getAbsoluteFile());
|
LOGGER.info("Root directory: {}", getDataFolder().getAbsoluteFile());
|
||||||
load();
|
load();
|
||||||
@ -58,4 +60,9 @@ public class CLIPlatform extends AbstractPlatform {
|
|||||||
super.register(registry);
|
super.register(registry);
|
||||||
registry.registerLoader(PlatformBiome.class, (TypeLoader<PlatformBiome>) (annotatedType, o, configLoader, depthTracker) -> () -> o);
|
registry.registerLoader(PlatformBiome.class, (TypeLoader<PlatformBiome>) (annotatedType, o, configLoader, depthTracker) -> () -> o);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getGenerationThreads() {
|
||||||
|
return generationThreads;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,9 @@ package com.dfsek.terra.lifecycle;
|
|||||||
import ca.solostudios.strata.Versions;
|
import ca.solostudios.strata.Versions;
|
||||||
import ca.solostudios.strata.parser.tokenizer.ParseException;
|
import ca.solostudios.strata.parser.tokenizer.ParseException;
|
||||||
import ca.solostudios.strata.version.Version;
|
import ca.solostudios.strata.version.Version;
|
||||||
|
|
||||||
|
import com.dfsek.terra.api.util.reflection.ReflectionUtil;
|
||||||
|
|
||||||
import net.minecraft.MinecraftVersion;
|
import net.minecraft.MinecraftVersion;
|
||||||
import net.minecraft.enchantment.Enchantment;
|
import net.minecraft.enchantment.Enchantment;
|
||||||
import net.minecraft.registry.Registry;
|
import net.minecraft.registry.Registry;
|
||||||
@ -37,8 +40,15 @@ public abstract class LifecyclePlatform extends ModPlatform {
|
|||||||
private static final AtomicReference<Registry<MultiNoiseBiomeSourceParameterList>> NOISE = new AtomicReference<>();
|
private static final AtomicReference<Registry<MultiNoiseBiomeSourceParameterList>> NOISE = new AtomicReference<>();
|
||||||
private static final AtomicReference<Registry<Enchantment>> ENCHANTMENT = new AtomicReference<>();
|
private static final AtomicReference<Registry<Enchantment>> ENCHANTMENT = new AtomicReference<>();
|
||||||
private static MinecraftServer server;
|
private static MinecraftServer server;
|
||||||
|
private int generationThreads;
|
||||||
|
|
||||||
public LifecyclePlatform() {
|
public LifecyclePlatform() {
|
||||||
|
generationThreads = getGenerationThreadsWithReflection("com.ishland.c2me.base.common.GlobalExecutors", "GLOBAL_EXECUTOR_PARALLELISM", "C2ME");
|
||||||
|
if (generationThreads == 0) {
|
||||||
|
generationThreads = getGenerationThreadsWithReflection("ca.spottedleaf.moonrise.common.util.MoonriseCommon", "WORKER_THREADS", "Moonrise");
|
||||||
|
} if (generationThreads == 0) {
|
||||||
|
generationThreads = 1;
|
||||||
|
}
|
||||||
CommonPlatform.initialize(this);
|
CommonPlatform.initialize(this);
|
||||||
load();
|
load();
|
||||||
}
|
}
|
||||||
@ -55,6 +65,8 @@ public abstract class LifecyclePlatform extends ModPlatform {
|
|||||||
ENCHANTMENT.set(enchantmentRegistry);
|
ENCHANTMENT.set(enchantmentRegistry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MinecraftServer getServer() {
|
public MinecraftServer getServer() {
|
||||||
return server;
|
return server;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user