Faster caching

This commit is contained in:
Daniel Mills 2020-09-12 01:44:54 -04:00
parent 9272765f3c
commit 11cab800e3
11 changed files with 121 additions and 292 deletions

View File

@ -198,6 +198,11 @@
<scope>provided</scope>
</dependency>
<!-- Utilities -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>2.8.5</version>
</dependency>
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId>

View File

@ -21,6 +21,10 @@ public class IrisSettings
@Desc("Iris generator threads (must be 2 or higher). Threads in iris are not a perfect scale for performance as a lot of data has to be shared. 16 Threads is a good rule of thumb. Use 8 threads on a quad core processor.")
public int threads = 16;
@DontObfuscate
@Desc("Iris uses a lot of caching to speed up chunk generation. Setting this higher uses more memory, but may improve performance. Anything past 15,000 should be avoided because there is little benefit past this value.")
public int atomicCacheSize = 10000;
@DontObfuscate
@Desc("Compress parallax data in memory to reduce memory usage in exchange for more cpu usage.")
public boolean parallaxCompression = true;
@ -37,6 +41,10 @@ public class IrisSettings
@Desc("If A is a child of B, and B is a child of A, how deep should iris follow the children in biomes. Lower is faster gen.")
public int maxBiomeChildDepth = 5;
@DontObfuscate
@Desc("The size of each tile pregen will generate (in chunks)")
public int pregenTileSize = 32;
@DontObfuscate
@Desc("When enabled, The cache is shared for all chunks and cleared periodically instead of per chunk. This uses more memory but provides a ~15% speedup.")
public boolean sharedCaching = true;

View File

@ -21,7 +21,7 @@ public class CommandIris extends MortarCommand
@Command
private CommandIrisDownload download;
@Command
private CommandIrisWhat what;

View File

@ -85,7 +85,7 @@ public abstract class ContextualTerrainProvider implements TerrainProvider, List
tickLatch = new ChronoLatch(650);
perSecond = new ChronoLatch(1000);
hlast = M.ms();
cache = new AtomicMulticache();
cache = new AtomicMulticache((IrisTerrainProvider) this);
CNG.creates = 0;
generated = 0;
ticks = 0;

View File

@ -249,7 +249,7 @@ public class IrisTerrainProvider extends SkyTerrainProvider implements IrisConte
int iz = (int) z;
double height = getTerrainHeight(ix, iz);
IrisRegion region = sampleRegion(ix, iz);
IrisBiome biome = sampleTrueBiome(ix, iz, height);
IrisBiome biome = sampleTrueBiome(ix, iz);
if(biome.getCachedColor() != null)
{
@ -276,7 +276,7 @@ public class IrisTerrainProvider extends SkyTerrainProvider implements IrisConte
int iz = (int) z;
double height = getTerrainHeight(ix, iz);
IrisRegion region = sampleRegion(ix, iz);
IrisBiome biome = sampleTrueBiome(ix, iz, height);
IrisBiome biome = sampleTrueBiome(ix, iz);
hb = biome;
hr = region;
return biome.getName() + " (" + Form.capitalizeWords(biome.getInferredType().name().toLowerCase().replaceAll("\\Q_\\E", " ") + ") in " + region.getName() + "\nY: " + (int) height);

View File

@ -65,7 +65,6 @@ public abstract class ParallelTerrainProvider extends DimensionalTerrainProvider
protected void onGenerate(RNG random, int x, int z, TerrainChunk terrain)
{
getCache().targetChunk(x, z);
PrecisionStopwatch p = PrecisionStopwatch.start();
AtomicSliverMap map = new AtomicSliverMap();
HeightMap height = new HeightMap();

View File

@ -84,20 +84,10 @@ public abstract class TopographicTerrainProvider extends ParallelTerrainProvider
{
if(ignoreFluid)
{
return getCache().getCarvedHeightIgnoreWater(x, z, () ->
{
int h = (int) Math.round(getTerrainHeight(x, z));
h = getGlCarve().getSurfaceCarve(x, h, z);
return h;
});
return getCache().getCarvedHeightIgnoreWater(x, z);
}
return getCache().getCarvedHeightIgnoreWater(x, z, () ->
{
int h = (int) Math.round(getTerrainWaterHeight(x, z));
h = getGlCarve().getSurfaceCarve(x, h, z);
return h;
});
return getCache().getCarvedHeight(x, z);
}
public int getCarvedHeight(int x, int z)
@ -140,7 +130,7 @@ public abstract class TopographicTerrainProvider extends ParallelTerrainProvider
int height = (int) Math.round(noise);
boolean carvable = getGlCarve().couldCarveBelow(rx, height, rz);
IrisRegion region = sampleRegion(rx, rz);
IrisBiome biome = sampleTrueBiome(rx, rz, noise);
IrisBiome biome = sampleTrueBiome(rx, rz);
IrisBiome carveBiome = null;
Biome onlyBiome = Iris.biome3d ? null : biome.getGroundBiome(getMasterRandom(), rz, getDimension().getFluidHeight(), rx);
@ -582,20 +572,21 @@ public abstract class TopographicTerrainProvider extends ParallelTerrainProvider
}
private double getNoiseHeight(int rx, int rz)
public double getNoiseHeight(int rx, int rz)
{
double h = getBiomeHeight(rx, rz);
return h;
}
public IrisBiome sampleTrueBiomeBase(int x, int z, int height)
public IrisBiome sampleTrueBiomeBase(int x, int z)
{
if(!getDimension().getFocus().equals(""))
{
return focus();
}
int height = (int) Math.round(getTerrainHeight(x, z));
double wx = getModifiedX(x, z);
double wz = getModifiedZ(x, z);
IrisRegion region = sampleRegion(x, z);
@ -671,29 +662,19 @@ public abstract class TopographicTerrainProvider extends ParallelTerrainProvider
return sampleTrueBiome(x, z);
}
public IrisBiome sampleTrueBiome(int x, int z)
{
return sampleTrueBiome(x, z, getTerrainHeight(x, z));
}
public IrisRegion sampleRegion(int x, int z)
{
return getCache().getRegion(x, z, () ->
{
double wx = getModifiedX(x, z);
double wz = getModifiedZ(x, z);
return glBiome.getRegion(wx, wz);
});
return getCache().getRegion(x, z);
}
public IrisBiome sampleTrueBiome(int x, int z, double noise)
public IrisBiome sampleTrueBiome(int x, int z)
{
if(!getDimension().getFocus().equals(""))
{
return focus();
}
return getCache().getBiome(x, z, () -> sampleTrueBiomeBase(x, z, (int) Math.round(noise)));
return getCache().getBiome(x, z);
}
@Override
@ -724,7 +705,7 @@ public abstract class TopographicTerrainProvider extends ParallelTerrainProvider
public double getTerrainHeight(int x, int z)
{
return getCache().getHeight(x, z, () -> getNoiseHeight(x, z) + getFluidHeight());
return getCache().getHeight(x, z);
}
public double getTerrainWaterHeight(int x, int z)
@ -885,47 +866,49 @@ public abstract class TopographicTerrainProvider extends ParallelTerrainProvider
Iris.info("Loaded " + generators.size() + " Generators");
}
public IrisBiome computeRawBiome(int x, int z)
{
if(!getDimension().getFocus().equals(""))
{
IrisBiome biome = loadBiome(getDimension().getFocus());
for(String i : getDimension().getRegions())
{
IrisRegion reg = loadRegion(i);
if(reg.getLandBiomes().contains(biome.getLoadKey()))
{
biome.setInferredType(InferredType.LAND);
break;
}
if(reg.getSeaBiomes().contains(biome.getLoadKey()))
{
biome.setInferredType(InferredType.SEA);
break;
}
if(reg.getShoreBiomes().contains(biome.getLoadKey()))
{
biome.setInferredType(InferredType.SHORE);
break;
}
}
return biome;
}
double wx = getModifiedX(x, z);
double wz = getModifiedZ(x, z);
IrisRegion region = glBiome.getRegion(wx, wz);
IrisBiome res = glBiome.generateRegionData(wx, wz, x, z, region);
return res;
}
public IrisBiome sampleBiome(int x, int z)
{
return getCache().getRawBiome(x, z, () ->
{
if(!getDimension().getFocus().equals(""))
{
IrisBiome biome = loadBiome(getDimension().getFocus());
for(String i : getDimension().getRegions())
{
IrisRegion reg = loadRegion(i);
if(reg.getLandBiomes().contains(biome.getLoadKey()))
{
biome.setInferredType(InferredType.LAND);
break;
}
if(reg.getSeaBiomes().contains(biome.getLoadKey()))
{
biome.setInferredType(InferredType.SEA);
break;
}
if(reg.getShoreBiomes().contains(biome.getLoadKey()))
{
biome.setInferredType(InferredType.SHORE);
break;
}
}
return biome;
}
double wx = getModifiedX(x, z);
double wz = getModifiedZ(x, z);
IrisRegion region = glBiome.getRegion(wx, wz);
IrisBiome res = glBiome.generateRegionData(wx, wz, x, z, region);
return res;
});
return getCache().getRawBiome(x, z);
}
}

View File

@ -1,245 +1,81 @@
package com.volmit.iris.gen.atomics;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import com.volmit.iris.Iris;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.volmit.iris.IrisSettings;
import com.volmit.iris.gen.IrisTerrainProvider;
import com.volmit.iris.object.IrisBiome;
import com.volmit.iris.object.IrisRegion;
import com.volmit.iris.util.KMap;
import com.volmit.iris.util.ChunkPosition;
public class AtomicMulticache
{
public static boolean broken = false;
private final AtomicInteger x;
private final AtomicInteger z;
private int hit = 0;
private int miss = 0;
private final KMap<Long, Double> height;
private final KMap<Long, Integer> carvedHeight;
private final KMap<Long, Integer> carvedHeightIgnoreWater;
private final KMap<Long, IrisBiome> biome;
private final KMap<Long, IrisBiome> rawBiome;
private final KMap<Long, IrisRegion> region;
private final LoadingCache<ChunkPosition, Double> height;
private final LoadingCache<ChunkPosition, Integer> carvedHeight;
private final LoadingCache<ChunkPosition, Integer> carvedHeightIgnoreWater;
private final LoadingCache<ChunkPosition, IrisBiome> biome;
private final LoadingCache<ChunkPosition, IrisBiome> rawBiome;
private final LoadingCache<ChunkPosition, IrisRegion> region;
public AtomicMulticache()
public AtomicMulticache(IrisTerrainProvider gen)
{
x = new AtomicInteger(0);
z = new AtomicInteger(0);
height = new KMap<Long, Double>();
carvedHeight = new KMap<Long, Integer>();
carvedHeightIgnoreWater = new KMap<Long, Integer>();
biome = new KMap<Long, IrisBiome>();
rawBiome = new KMap<Long, IrisBiome>();
region = new KMap<Long, IrisRegion>();
}
public void targetChunk(int x, int z)
{
if(broken)
height = Caffeine.newBuilder().maximumSize(getLimit()).build((c) -> gen.getNoiseHeight(c.getX(), c.getZ()) + gen.getFluidHeight());
carvedHeight = Caffeine.newBuilder().maximumSize(getLimit()).build((c) ->
{
return;
}
this.x.set(x);
this.z.set(z);
if(!IrisSettings.get().sharedCaching || Iris.lowMemoryMode)
int h = (int) Math.round(gen.getTerrainWaterHeight(c.getX(), c.getZ()));
h = gen.getGlCarve().getSurfaceCarve(c.getX(), h, c.getZ());
return h;
});
carvedHeightIgnoreWater = Caffeine.newBuilder().maximumSize(getLimit()).build((c) ->
{
drop();
}
else
int h = (int) Math.round(gen.getTerrainHeight(c.getX(), c.getZ()));
h = gen.getGlCarve().getSurfaceCarve(c.getX(), h, c.getZ());
return h;
});
biome = Caffeine.newBuilder().maximumSize(getLimit()).build((c) -> gen.sampleTrueBiomeBase(c.getX(), c.getZ()));
rawBiome = Caffeine.newBuilder().maximumSize(getLimit()).build((c) -> gen.computeRawBiome(c.getX(), c.getZ()));
region = Caffeine.newBuilder().maximumSize(getLimit()).build((c) ->
{
if(height.size() > getLimit())
{
height.clear();
}
if(carvedHeight.size() > getLimit())
{
carvedHeight.clear();
}
if(carvedHeightIgnoreWater.size() > getLimit())
{
carvedHeightIgnoreWater.clear();
}
if(biome.size() > getLimit())
{
biome.clear();
}
if(rawBiome.size() > getLimit())
{
rawBiome.clear();
}
if(region.size() > getLimit())
{
region.clear();
}
}
double wx = gen.getModifiedX(c.getX(), c.getZ());
double wz = gen.getModifiedZ(c.getX(), c.getZ());
return gen.getGlBiome().getRegion(wx, wz);
});
}
private int getLimit()
{
return 1024;
return IrisSettings.get().getAtomicCacheSize();
}
public double getHeight(int x, int z, Supplier<Double> g)
public double getHeight(int x, int z)
{
if(broken)
{
return -5784;
}
long pos = pos(x, z);
Double r = height.get(pos);
if(r == null)
{
miss++;
r = g.get();
height.put(pos, r);
}
else
{
hit++;
}
return r;
return height.get(new ChunkPosition(x, z));
}
public int getCarvedHeight(int x, int z, Supplier<Integer> g)
public int getCarvedHeight(int x, int z)
{
if(broken)
{
return -57841;
}
long pos = pos(x, z);
Integer r = carvedHeight.get(pos);
if(r == null)
{
miss++;
r = g.get();
carvedHeight.put(pos, r);
}
else
{
hit++;
}
return r;
return carvedHeight.get(new ChunkPosition(x, z));
}
public int getCarvedHeightIgnoreWater(int x, int z, Supplier<Integer> g)
public int getCarvedHeightIgnoreWater(int x, int z)
{
if(broken)
{
return -57841;
}
long pos = pos(x, z);
Integer r = carvedHeightIgnoreWater.get(pos);
if(r == null)
{
miss++;
r = g.get();
carvedHeightIgnoreWater.put(pos, r);
}
else
{
hit++;
}
return r;
return carvedHeightIgnoreWater.get(new ChunkPosition(x, z));
}
public IrisRegion getRegion(int x, int z, Supplier<IrisRegion> g)
public IrisRegion getRegion(int x, int z)
{
long pos = pos(x, z);
IrisRegion r = region.get(pos);
if(r == null)
{
miss++;
r = g.get();
region.put(pos, r);
}
else
{
hit++;
}
return r;
return region.get(new ChunkPosition(x, z));
}
public IrisBiome getBiome(int x, int z, Supplier<IrisBiome> g)
public IrisBiome getBiome(int x, int z)
{
long pos = pos(x, z);
IrisBiome r = biome.get(pos);
if(r == null)
{
miss++;
r = g.get();
biome.put(pos, r);
}
else
{
hit++;
}
return r;
return biome.get(new ChunkPosition(x, z));
}
public IrisBiome getRawBiome(int x, int z, Supplier<IrisBiome> g)
public IrisBiome getRawBiome(int x, int z)
{
if(broken)
{
return null;
}
long pos = pos(x, z);
IrisBiome r = rawBiome.get(pos);
if(r == null)
{
miss++;
r = g.get();
rawBiome.put(pos, r);
}
else
{
hit++;
}
return r;
}
public double getCacheHitRate()
{
return (double) hit / (double) (hit + miss);
}
private long pos(int x, int z)
{
if(broken)
{
return 1;
}
return (((long) x) << 32) | (z & 0xffffffffL);
return rawBiome.get(new ChunkPosition(x, z));
}
public void updateHeight(int x, int z, int h)
@ -248,12 +84,12 @@ public class AtomicMulticache
{
return;
}
height.put(pos(x, z), (double) h);
height.put(new ChunkPosition(x, z), (double) h);
}
public double getSize()
{
return height.size() + region.size() + biome.size() + rawBiome.size();
return height.estimatedSize() + region.estimatedSize() + biome.estimatedSize() + rawBiome.estimatedSize() + carvedHeight.estimatedSize() + carvedHeightIgnoreWater.estimatedSize();
}
public void drop()
@ -263,13 +99,11 @@ public class AtomicMulticache
return;
}
hit = 0;
miss = 0;
height.clear();
region.clear();
biome.clear();
rawBiome.clear();
carvedHeight.clear();
carvedHeightIgnoreWater.clear();
height.invalidateAll();
region.invalidateAll();
biome.invalidateAll();
rawBiome.invalidateAll();
carvedHeight.invalidateAll();
carvedHeightIgnoreWater.invalidateAll();
}
}

View File

@ -124,7 +124,6 @@ public class IrisVision extends JPanel implements MouseWheelListener
public BufferedImage getTile(KSet<BlockPosition> fg, int div, int x, int z, O<Integer> m)
{
Iris.proj.getCurrentProject().getCache().targetChunk(x, z);
BlockPosition key = new BlockPosition((int) mscale, Math.floorDiv(x, div), Math.floorDiv(z, div));
fg.add(key);

View File

@ -81,7 +81,7 @@ public class PregenGui extends JPanel
g.drawString(i, 20, hh += h);
}
J.sleep((long) 1);
J.sleep((long) 1000);
repaint();
}

View File

@ -11,6 +11,7 @@ import org.bukkit.event.Listener;
import org.bukkit.event.world.ChunkUnloadEvent;
import com.volmit.iris.Iris;
import com.volmit.iris.IrisSettings;
import com.volmit.iris.gui.PregenGui;
public class PregenJob implements Listener
@ -37,7 +38,7 @@ public class PregenJob implements Listener
private Spiraler chunkSpiraler;
private boolean first;
private Consumer2<ChunkPosition, Color> consumer;
private int cubeSize = 7;
private int cubeSize = IrisSettings.get().getPregenTileSize();
int xc = 0;
public PregenJob(World world, int size, MortarSender sender, Runnable onDone)