From 315230af2756813b04d40d20474963a70a88db43 Mon Sep 17 00:00:00 2001 From: dfsek Date: Sun, 3 Jan 2021 13:24:17 -0700 Subject: [PATCH] implement SamplerCache for drastically increased structure perf --- .../structures/script/StructureScript.java | 4 ++-- .../script/builders/CheckFunctionBuilder.java | 6 ++--- .../script/functions/CheckFunction.java | 6 ++--- .../dfsek/terra/config/base/ConfigPack.java | 14 ++++++------ .../generation/MasterChunkGenerator.java | 8 +++++-- .../math/SamplerCache.java} | 22 ++++++++++++++----- .../dfsek/terra/bukkit/TerraBukkitPlugin.java | 5 +++-- .../command/structure/SpawnCommand.java | 2 +- .../bukkit/src/main/resources/config.yml | 2 +- .../FabricChunkGeneratorWrapper.java | 2 +- 10 files changed, 44 insertions(+), 27 deletions(-) rename common/src/main/java/com/dfsek/terra/{api/structures/world/CheckCache.java => generation/math/SamplerCache.java} (64%) diff --git a/common/src/main/java/com/dfsek/terra/api/structures/script/StructureScript.java b/common/src/main/java/com/dfsek/terra/api/structures/script/StructureScript.java index ad9979c86..b8eb03a89 100644 --- a/common/src/main/java/com/dfsek/terra/api/structures/script/StructureScript.java +++ b/common/src/main/java/com/dfsek/terra/api/structures/script/StructureScript.java @@ -21,7 +21,7 @@ import com.dfsek.terra.api.structures.script.builders.UnaryNumberFunctionBuilder import com.dfsek.terra.api.structures.structure.Rotation; import com.dfsek.terra.api.structures.structure.buffer.Buffer; import com.dfsek.terra.api.structures.structure.buffer.StructureBuffer; -import com.dfsek.terra.api.structures.world.CheckCache; +import com.dfsek.terra.generation.math.SamplerCache; import com.dfsek.terra.registry.LootRegistry; import com.dfsek.terra.registry.ScriptRegistry; import net.jafama.FastMath; @@ -38,7 +38,7 @@ public class StructureScript { private final String id; private final LinkedHashMap cache; - public StructureScript(InputStream inputStream, TerraPlugin main, ScriptRegistry registry, LootRegistry lootRegistry, CheckCache cache) throws ParseException { + public StructureScript(InputStream inputStream, TerraPlugin main, ScriptRegistry registry, LootRegistry lootRegistry, SamplerCache cache) throws ParseException { Parser parser; try { parser = new Parser(IOUtils.toString(inputStream)); diff --git a/common/src/main/java/com/dfsek/terra/api/structures/script/builders/CheckFunctionBuilder.java b/common/src/main/java/com/dfsek/terra/api/structures/script/builders/CheckFunctionBuilder.java index bac7b422f..81399b183 100644 --- a/common/src/main/java/com/dfsek/terra/api/structures/script/builders/CheckFunctionBuilder.java +++ b/common/src/main/java/com/dfsek/terra/api/structures/script/builders/CheckFunctionBuilder.java @@ -6,15 +6,15 @@ import com.dfsek.terra.api.structures.parser.lang.Returnable; import com.dfsek.terra.api.structures.parser.lang.functions.FunctionBuilder; import com.dfsek.terra.api.structures.script.functions.CheckFunction; import com.dfsek.terra.api.structures.tokenizer.Position; -import com.dfsek.terra.api.structures.world.CheckCache; +import com.dfsek.terra.generation.math.SamplerCache; import java.util.List; public class CheckFunctionBuilder implements FunctionBuilder { private final TerraPlugin main; - private final CheckCache cache; + private final SamplerCache cache; - public CheckFunctionBuilder(TerraPlugin main, CheckCache cache) { + public CheckFunctionBuilder(TerraPlugin main, SamplerCache cache) { this.main = main; this.cache = cache; } diff --git a/common/src/main/java/com/dfsek/terra/api/structures/script/functions/CheckFunction.java b/common/src/main/java/com/dfsek/terra/api/structures/script/functions/CheckFunction.java index 13d8efdf3..05de2816b 100644 --- a/common/src/main/java/com/dfsek/terra/api/structures/script/functions/CheckFunction.java +++ b/common/src/main/java/com/dfsek/terra/api/structures/script/functions/CheckFunction.java @@ -12,20 +12,20 @@ import com.dfsek.terra.api.structures.parser.lang.functions.Function; import com.dfsek.terra.api.structures.script.TerraImplementationArguments; import com.dfsek.terra.api.structures.structure.RotationUtil; import com.dfsek.terra.api.structures.tokenizer.Position; -import com.dfsek.terra.api.structures.world.CheckCache; import com.dfsek.terra.api.world.generation.GenerationPhase; import com.dfsek.terra.biome.UserDefinedBiome; import com.dfsek.terra.biome.grid.master.TerraBiomeGrid; import com.dfsek.terra.config.templates.BiomeTemplate; +import com.dfsek.terra.generation.math.SamplerCache; import net.jafama.FastMath; public class CheckFunction implements Function { private final TerraPlugin main; private final Returnable x, y, z; private final Position position; - private final CheckCache cache; + private final SamplerCache cache; - public CheckFunction(TerraPlugin main, Returnable x, Returnable y, Returnable z, CheckCache cache, Position position) { + public CheckFunction(TerraPlugin main, Returnable x, Returnable y, Returnable z, SamplerCache cache, Position position) { this.main = main; this.x = x; this.y = y; diff --git a/common/src/main/java/com/dfsek/terra/config/base/ConfigPack.java b/common/src/main/java/com/dfsek/terra/config/base/ConfigPack.java index 89cd9d54b..8bb85e12f 100644 --- a/common/src/main/java/com/dfsek/terra/config/base/ConfigPack.java +++ b/common/src/main/java/com/dfsek/terra/config/base/ConfigPack.java @@ -9,7 +9,6 @@ import com.dfsek.terra.api.LoaderRegistrar; import com.dfsek.terra.api.platform.TerraPlugin; import com.dfsek.terra.api.structures.loot.LootTable; import com.dfsek.terra.api.structures.script.StructureScript; -import com.dfsek.terra.api.structures.world.CheckCache; import com.dfsek.terra.api.world.biome.Biome; import com.dfsek.terra.api.world.flora.Flora; import com.dfsek.terra.api.world.palette.Palette; @@ -43,6 +42,7 @@ import com.dfsek.terra.config.templates.StructureTemplate; import com.dfsek.terra.config.templates.TreeTemplate; import com.dfsek.terra.generation.items.TerraStructure; import com.dfsek.terra.generation.items.ores.Ore; +import com.dfsek.terra.generation.math.SamplerCache; import com.dfsek.terra.registry.BiomeGridRegistry; import com.dfsek.terra.registry.BiomeRegistry; import com.dfsek.terra.registry.CarverRegistry; @@ -95,12 +95,12 @@ public class ConfigPack implements LoaderRegistrar { private final ConfigLoader selfLoader = new ConfigLoader(); private final Scope varScope = new Scope(); - private final CheckCache checkCache; + private final SamplerCache samplerCache; public ConfigPack(File folder, TerraPlugin main) throws ConfigException { long l = System.nanoTime(); - this.checkCache = new CheckCache(main); + this.samplerCache = new SamplerCache(main); floraRegistry = new FloraRegistry(main); paletteRegistry = new PaletteRegistry(main); treeRegistry = new TreeRegistry(main); @@ -122,7 +122,7 @@ public class ConfigPack implements LoaderRegistrar { public ConfigPack(ZipFile file, TerraPlugin main) throws ConfigException { long l = System.nanoTime(); - this.checkCache = new CheckCache(main); + this.samplerCache = new SamplerCache(main); floraRegistry = new FloraRegistry(main); paletteRegistry = new PaletteRegistry(main); treeRegistry = new TreeRegistry(main); @@ -157,7 +157,7 @@ public class ConfigPack implements LoaderRegistrar { loader.open("structures/data", ".tesf").thenEntries(entries -> { for(Map.Entry entry : entries) { try { - StructureScript structureScript = new StructureScript(entry.getValue(), main, scriptRegistry, lootRegistry, checkCache); + StructureScript structureScript = new StructureScript(entry.getValue(), main, scriptRegistry, lootRegistry, samplerCache); scriptRegistry.add(structureScript.getId(), structureScript); } catch(com.dfsek.terra.api.structures.parser.exceptions.ParseException e) { throw new LoadException("Unable to load script \"" + entry.getKey() + "\"", e); @@ -278,7 +278,7 @@ public class ConfigPack implements LoaderRegistrar { return biomeRegistry; } - public CheckCache getCheckCache() { - return checkCache; + public SamplerCache getSamplerCache() { + return samplerCache; } } diff --git a/common/src/main/java/com/dfsek/terra/generation/MasterChunkGenerator.java b/common/src/main/java/com/dfsek/terra/generation/MasterChunkGenerator.java index 3f5224bd5..e67d013d6 100644 --- a/common/src/main/java/com/dfsek/terra/generation/MasterChunkGenerator.java +++ b/common/src/main/java/com/dfsek/terra/generation/MasterChunkGenerator.java @@ -23,6 +23,7 @@ import com.dfsek.terra.biome.palette.SinglePalette; import com.dfsek.terra.config.base.ConfigPack; import com.dfsek.terra.config.templates.BiomeTemplate; import com.dfsek.terra.generation.math.Sampler; +import com.dfsek.terra.generation.math.SamplerCache; import com.dfsek.terra.util.PaletteUtil; import org.jetbrains.annotations.NotNull; @@ -37,12 +38,15 @@ public class MasterChunkGenerator implements TerraChunkGenerator { private final MaterialData water; private final SinglePalette blank; + private final SamplerCache cache; - public MasterChunkGenerator(ConfigPack c, TerraPlugin main) { + + public MasterChunkGenerator(ConfigPack c, TerraPlugin main, SamplerCache cache) { this.configPack = c; this.main = main; water = main.getWorldHandle().createMaterialData("minecraft:water"); blank = new SinglePalette<>(main.getWorldHandle().createBlockData("minecraft:air")); + this.cache = cache; } @Override @@ -91,7 +95,7 @@ public class MasterChunkGenerator implements TerraChunkGenerator { int xOrig = (chunkX << 4); int zOrig = (chunkZ << 4); - Sampler sampler = new Sampler(chunkX, chunkZ, tw.getGrid(), world, configPack.getTemplate().getElevationBlend(), configPack.getTemplate().getBaseBlend()); + Sampler sampler = cache.getChunk(world, chunkX, chunkZ); for(byte x = 0; x < 16; x++) { for(byte z = 0; z < 16; z++) { diff --git a/common/src/main/java/com/dfsek/terra/api/structures/world/CheckCache.java b/common/src/main/java/com/dfsek/terra/generation/math/SamplerCache.java similarity index 64% rename from common/src/main/java/com/dfsek/terra/api/structures/world/CheckCache.java rename to common/src/main/java/com/dfsek/terra/generation/math/SamplerCache.java index da51a69c0..6c915fd2e 100644 --- a/common/src/main/java/com/dfsek/terra/api/structures/world/CheckCache.java +++ b/common/src/main/java/com/dfsek/terra/generation/math/SamplerCache.java @@ -1,20 +1,19 @@ -package com.dfsek.terra.api.structures.world; +package com.dfsek.terra.generation.math; import com.dfsek.terra.TerraWorld; import com.dfsek.terra.api.platform.TerraPlugin; import com.dfsek.terra.api.platform.world.World; -import com.dfsek.terra.generation.math.Sampler; import net.jafama.FastMath; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; -public class CheckCache { +public class SamplerCache { private final Map cache; private final TerraPlugin main; - public CheckCache(TerraPlugin main) { + public SamplerCache(TerraPlugin main) { cache = new HashMap<>(); this.main = main; } @@ -28,6 +27,15 @@ public class CheckCache { })).get(x, z); } + public Sampler getChunk(World world, int chunkX, int chunkZ) { + return cache.computeIfAbsent(world.getSeed(), seed -> new Container(world, new LinkedHashMap() { + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > main.getTerraConfig().getCheckCache(); + } + })).getChunk(chunkX, chunkZ); + } + private class Container { private final World world; @@ -41,9 +49,13 @@ public class CheckCache { public Sampler get(int x, int z) { int cx = FastMath.floorDiv(x, 16); int cz = FastMath.floorDiv(z, 16); + return getChunk(cx, cz); + } + + public Sampler getChunk(int cx, int cz) { long key = (((long) cx) << 32) | (cz & 0xffffffffL); TerraWorld tw = main.getWorld(world); - return cache.computeIfAbsent(key, k -> new Sampler(cx, cz, tw.getGrid(), world, 4, 8)); + return cache.computeIfAbsent(key, k -> new Sampler(cx, cz, tw.getGrid(), world, tw.getConfig().getTemplate().getBaseBlend(), tw.getConfig().getTemplate().getElevationBlend())); } } } diff --git a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/TerraBukkitPlugin.java b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/TerraBukkitPlugin.java index fa3f8fb32..e18276438 100644 --- a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/TerraBukkitPlugin.java +++ b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/TerraBukkitPlugin.java @@ -145,8 +145,9 @@ public class TerraBukkitPlugin extends JavaPlugin implements TerraPlugin { public @Nullable ChunkGenerator getDefaultWorldGenerator(@NotNull String worldName, @Nullable String id) { return new BukkitChunkGeneratorWrapper(generatorMap.computeIfAbsent(worldName, name -> { if(!registry.contains(id)) throw new IllegalArgumentException("No such config pack \"" + id + "\""); - worlds.put(worldName, registry.get(id)); - return new MasterChunkGenerator(registry.get(id), this); + ConfigPack pack = registry.get(id); + worlds.put(worldName, pack); + return new MasterChunkGenerator(registry.get(id), this, pack.getSamplerCache()); })); } diff --git a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/command/command/structure/SpawnCommand.java b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/command/command/structure/SpawnCommand.java index ef3104cce..fb65819de 100644 --- a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/command/command/structure/SpawnCommand.java +++ b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/command/command/structure/SpawnCommand.java @@ -33,7 +33,7 @@ public class SpawnCommand extends WorldCommand implements DebugCommand { int z = p.getBlockZ(); Position dummy = new Position(0, 0); com.dfsek.terra.api.platform.world.World w = BukkitAdapter.adapt(world); - String check = new CheckFunction(getMain(), new NumericConstant(0, dummy), new NumericConstant(0, dummy), new NumericConstant(0, dummy), getMain().getWorld(w).getConfig().getCheckCache(), dummy).apply(new TerraImplementationArguments(new StructureBuffer( + String check = new CheckFunction(getMain(), new NumericConstant(0, dummy), new NumericConstant(0, dummy), new NumericConstant(0, dummy), getMain().getWorld(w).getConfig().getSamplerCache(), dummy).apply(new TerraImplementationArguments(new StructureBuffer( new com.dfsek.terra.api.math.vector.Location(w, x, y, z) ), Rotation.NONE, new FastRandom(), 0)); diff --git a/platforms/bukkit/src/main/resources/config.yml b/platforms/bukkit/src/main/resources/config.yml index c262958ae..503a395cb 100644 --- a/platforms/bukkit/src/main/resources/config.yml +++ b/platforms/bukkit/src/main/resources/config.yml @@ -5,6 +5,6 @@ dump-default: true biome-search-resolution: 4 cache: carver: 512 - structure: 128 + structure: 1024 master-disable: caves: false \ No newline at end of file diff --git a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/generator/FabricChunkGeneratorWrapper.java b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/generator/FabricChunkGeneratorWrapper.java index c94f7afa2..24c830b78 100644 --- a/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/generator/FabricChunkGeneratorWrapper.java +++ b/platforms/fabric/src/main/java/com/dfsek/terra/fabric/world/generator/FabricChunkGeneratorWrapper.java @@ -76,7 +76,7 @@ public class FabricChunkGeneratorWrapper extends ChunkGenerator implements Gener super(biomeSource, new StructuresConfig(false)); this.pack = configPack; - this.delegate = new MasterChunkGenerator(configPack, TerraFabricPlugin.getInstance()); + this.delegate = new MasterChunkGenerator(configPack, TerraFabricPlugin.getInstance(), pack.getSamplerCache()); delegate.getMain().getLogger().info("Loading world..."); this.biomeSource = biomeSource;