mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-11-01 08:50:06 +00:00
Clipped pregen prefetch
- Limited chunk prefetch rings to the windows a bounded pregeneration still needs - Forwarded pregeneration region bounds from the async, medieval and modded methods - Restored unclipped prefetch when the pregeneration closes - Added a runtime test for clipped and restored prefetch
This commit is contained in:
1 parent
ca33273d17
commit
def73cff96
7 files changed
+116
-1
No files matched your search
+5
@@ -348,6 +348,11 @@ public final class ModdedPregenMethod implements PregeneratorMethod {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRegionBounds(int minRegionX, int minRegionZ, int maxRegionX, int maxRegionZ) {
|
||||||
|
hydrology.bound(minRegionX, minRegionZ, maxRegionX, maxRegionZ);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRegionsAhead(List<long[]> regions) {
|
public void onRegionsAhead(List<long[]> regions) {
|
||||||
if (!closing.get()) {
|
if (!closing.get()) {
|
||||||
|
|||||||
+6
@@ -18,6 +18,12 @@ public final class HydrologyPregeneration implements AutoCloseable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public synchronized void bound(int minRegionX, int minRegionZ, int maxRegionX, int maxRegionZ) {
|
||||||
|
if (!closed) {
|
||||||
|
runtime.boundPregeneration(epoch, minRegionX, minRegionZ, maxRegionX, maxRegionZ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void close() {
|
public synchronized void close() {
|
||||||
if (!closed) {
|
if (!closed) {
|
||||||
|
|||||||
+53
-1
@@ -36,6 +36,7 @@ import java.io.UncheckedIOException;
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -77,6 +78,7 @@ public final class HydrologyRuntime {
|
|||||||
private final RasterFailures regionPreviewFailures;
|
private final RasterFailures regionPreviewFailures;
|
||||||
private final Set<Object> previews;
|
private final Set<Object> previews;
|
||||||
private final LinkedHashMap<WindowKey, WindowPrefetch> prefetches;
|
private final LinkedHashMap<WindowKey, WindowPrefetch> prefetches;
|
||||||
|
private final Map<Object, PregenArea> pregenAreas;
|
||||||
private final HydrologyBinding binding;
|
private final HydrologyBinding binding;
|
||||||
private final Function<PreparedHydrologyCacheIdentity, HydrologyPlanStore> stores;
|
private final Function<PreparedHydrologyCacheIdentity, HydrologyPlanStore> stores;
|
||||||
private final boolean caveFluidPolicies;
|
private final boolean caveFluidPolicies;
|
||||||
@@ -99,6 +101,7 @@ public final class HydrologyRuntime {
|
|||||||
this.regionPreviewFailures = null;
|
this.regionPreviewFailures = null;
|
||||||
this.previews = null;
|
this.previews = null;
|
||||||
this.prefetches = null;
|
this.prefetches = null;
|
||||||
|
this.pregenAreas = null;
|
||||||
this.binding = HydrologyBinding.UNBOUND;
|
this.binding = HydrologyBinding.UNBOUND;
|
||||||
this.stores = identity -> null;
|
this.stores = identity -> null;
|
||||||
this.caveFluidPolicies = false;
|
this.caveFluidPolicies = false;
|
||||||
@@ -125,6 +128,7 @@ public final class HydrologyRuntime {
|
|||||||
this.regionPreviewFailures = new RasterFailures(ticker);
|
this.regionPreviewFailures = new RasterFailures(ticker);
|
||||||
this.previews = new HashSet<>();
|
this.previews = new HashSet<>();
|
||||||
this.prefetches = new LinkedHashMap<>(PREFETCH_CAPACITY * 2, 0.75f, true);
|
this.prefetches = new LinkedHashMap<>(PREFETCH_CAPACITY * 2, 0.75f, true);
|
||||||
|
this.pregenAreas = new HashMap<>();
|
||||||
this.binding = Objects.requireNonNull(binding, "binding");
|
this.binding = Objects.requireNonNull(binding, "binding");
|
||||||
this.stores = Objects.requireNonNull(stores, "stores");
|
this.stores = Objects.requireNonNull(stores, "stores");
|
||||||
this.caveFluidPolicies = caveFluidPolicies;
|
this.caveFluidPolicies = caveFluidPolicies;
|
||||||
@@ -354,8 +358,28 @@ public final class HydrologyRuntime {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void boundPregeneration(Object epoch, int minRegionX, int minRegionZ, int maxRegionX, int maxRegionZ) {
|
||||||
|
Objects.requireNonNull(epoch, "epoch");
|
||||||
|
if (maxRegionX < minRegionX || maxRegionZ < minRegionZ) {
|
||||||
|
throw new IllegalArgumentException("Invalid pregeneration bounds " + minRegionX + "," + minRegionZ + " to " + maxRegionX + ","
|
||||||
|
+ maxRegionZ);
|
||||||
|
}
|
||||||
|
if (cache == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
synchronized (prefetches) {
|
||||||
|
pregenAreas.put(epoch, new PregenArea(minRegionX - 1, minRegionZ - 1, maxRegionX + 1, maxRegionZ + 1));
|
||||||
|
prefetches.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void cancelPregeneration(Object epoch) {
|
void cancelPregeneration(Object epoch) {
|
||||||
if (cache != null) {
|
if (cache != null) {
|
||||||
|
synchronized (prefetches) {
|
||||||
|
if (pregenAreas.remove(epoch) != null) {
|
||||||
|
prefetches.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
cache.cancel(epoch);
|
cache.cancel(epoch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -667,7 +691,7 @@ public final class HydrologyRuntime {
|
|||||||
&& (!known.prepared().isDone() || known.evictions() == evictions)) {
|
&& (!known.prepared().isDone() || known.evictions() == evictions)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
CompletableFuture<Void> prepared = cache.prepareWindowsAround(chunkX, chunkZ, chunkX, chunkZ, 1 + settings.prefetchRings());
|
CompletableFuture<Void> prepared = prepareAround(chunkX, chunkZ, owner, 1 + settings.prefetchRings());
|
||||||
prefetches.put(owner, new WindowPrefetch(prepared, evictions));
|
prefetches.put(owner, new WindowPrefetch(prepared, evictions));
|
||||||
if (prefetches.size() > PREFETCH_CAPACITY) {
|
if (prefetches.size() > PREFETCH_CAPACITY) {
|
||||||
prefetches.pollFirstEntry();
|
prefetches.pollFirstEntry();
|
||||||
@@ -675,6 +699,24 @@ public final class HydrologyRuntime {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CompletableFuture<Void> prepareAround(int chunkX, int chunkZ, WindowKey owner, int rings) {
|
||||||
|
PregenArea covering = null;
|
||||||
|
for (PregenArea area : pregenAreas.values()) {
|
||||||
|
if (area.contains(owner)) {
|
||||||
|
covering = covering == null ? area : covering.union(area);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (covering == null) {
|
||||||
|
return cache.prepareWindowsAround(chunkX, chunkZ, chunkX, chunkZ, rings);
|
||||||
|
}
|
||||||
|
int minX = Math.max(owner.wx() - rings, covering.minX());
|
||||||
|
int minZ = Math.max(owner.wz() - rings, covering.minZ());
|
||||||
|
int maxX = Math.min(owner.wx() + rings, covering.maxX());
|
||||||
|
int maxZ = Math.min(owner.wz() + rings, covering.maxZ());
|
||||||
|
return cache.prepareWindowsAround(minX * CHUNKS_PER_WINDOW, minZ * CHUNKS_PER_WINDOW, (maxX + 1) * CHUNKS_PER_WINDOW - 1,
|
||||||
|
(maxZ + 1) * CHUNKS_PER_WINDOW - 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
private static List<WindowKey> neighbourhood(int chunkX, int chunkZ) {
|
private static List<WindowKey> neighbourhood(int chunkX, int chunkZ) {
|
||||||
WindowKey own = WindowKey.containing(chunkX << 4, chunkZ << 4);
|
WindowKey own = WindowKey.containing(chunkX << 4, chunkZ << 4);
|
||||||
List<WindowKey> keys = new ArrayList<>(9);
|
List<WindowKey> keys = new ArrayList<>(9);
|
||||||
@@ -754,6 +796,16 @@ public final class HydrologyRuntime {
|
|||||||
private record WindowPrefetch(CompletableFuture<Void> prepared, long evictions) {
|
private record WindowPrefetch(CompletableFuture<Void> prepared, long evictions) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private record PregenArea(int minX, int minZ, int maxX, int maxZ) {
|
||||||
|
private boolean contains(WindowKey key) {
|
||||||
|
return key.wx() > minX && key.wx() < maxX && key.wz() > minZ && key.wz() < maxZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PregenArea union(PregenArea other) {
|
||||||
|
return new PregenArea(Math.min(minX, other.minX), Math.min(minZ, other.minZ), Math.max(maxX, other.maxX), Math.max(maxZ, other.maxZ));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static final class LastRaster {
|
private static final class LastRaster {
|
||||||
private long key;
|
private long key;
|
||||||
private ChunkWaterRaster raster;
|
private ChunkWaterRaster raster;
|
||||||
|
|||||||
@@ -268,6 +268,7 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
|||||||
@Override
|
@Override
|
||||||
public void onRegionBounds(int minRegionX, int minRegionZ, int maxRegionX, int maxRegionZ) {
|
public void onRegionBounds(int minRegionX, int minRegionZ, int maxRegionX, int maxRegionZ) {
|
||||||
retention = createRetention(minRegionX, minRegionZ, maxRegionX, maxRegionZ);
|
retention = createRetention(minRegionX, minRegionZ, maxRegionX, maxRegionZ);
|
||||||
|
hydrology.bound(minRegionX, minRegionZ, maxRegionX, maxRegionZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
private PregenChunkRetention createRetention(int minRegionX, int minRegionZ, int maxRegionX, int maxRegionZ) {
|
private PregenChunkRetention createRetention(int minRegionX, int minRegionZ, int maxRegionX, int maxRegionZ) {
|
||||||
|
|||||||
@@ -219,6 +219,11 @@ public class MedievalPregenMethod implements PregeneratorMethod {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRegionBounds(int minRegionX, int minRegionZ, int maxRegionX, int maxRegionZ) {
|
||||||
|
hydrology.bound(minRegionX, minRegionZ, maxRegionX, maxRegionZ);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRegionsAhead(List<long[]> regions) {
|
public void onRegionsAhead(List<long[]> regions) {
|
||||||
if (!closing.get()) {
|
if (!closing.get()) {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ public final class PregenHydrology implements AutoCloseable {
|
|||||||
private final Supplier<Engine> engine;
|
private final Supplier<Engine> engine;
|
||||||
private HydrologyRuntime owner;
|
private HydrologyRuntime owner;
|
||||||
private HydrologyPregeneration epoch;
|
private HydrologyPregeneration epoch;
|
||||||
|
private int[] bounds;
|
||||||
private boolean closed;
|
private boolean closed;
|
||||||
|
|
||||||
public PregenHydrology(Supplier<Engine> engine) {
|
public PregenHydrology(Supplier<Engine> engine) {
|
||||||
@@ -28,12 +29,21 @@ public final class PregenHydrology implements AutoCloseable {
|
|||||||
withdraw();
|
withdraw();
|
||||||
owner = runtime;
|
owner = runtime;
|
||||||
epoch = runtime == null ? null : runtime.openPregeneration();
|
epoch = runtime == null ? null : runtime.openPregeneration();
|
||||||
|
applyBounds();
|
||||||
}
|
}
|
||||||
if (epoch != null) {
|
if (epoch != null) {
|
||||||
epoch.prepare(regions);
|
epoch.prepare(regions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public synchronized void bound(int minRegionX, int minRegionZ, int maxRegionX, int maxRegionZ) {
|
||||||
|
if (closed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bounds = new int[]{minRegionX, minRegionZ, maxRegionX, maxRegionZ};
|
||||||
|
applyBounds();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void close() {
|
public synchronized void close() {
|
||||||
closed = true;
|
closed = true;
|
||||||
@@ -41,6 +51,12 @@ public final class PregenHydrology implements AutoCloseable {
|
|||||||
owner = null;
|
owner = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void applyBounds() {
|
||||||
|
if (epoch != null && bounds != null) {
|
||||||
|
epoch.bound(bounds[0], bounds[1], bounds[2], bounds[3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void withdraw() {
|
private void withdraw() {
|
||||||
if (epoch != null) {
|
if (epoch != null) {
|
||||||
epoch.close();
|
epoch.close();
|
||||||
|
|||||||
+30
@@ -67,6 +67,7 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||||||
import java.util.function.BooleanSupplier;
|
import java.util.function.BooleanSupplier;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
@@ -414,6 +415,35 @@ public class HydrologyRuntimeTest {
|
|||||||
assertEquals(3, prefetches.get());
|
assertEquals(3, prefetches.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(timeout = 180_000L)
|
||||||
|
public void boundedPregenerationPrefetchesOnlyWindowsItsChunksNeed() {
|
||||||
|
FakePlanners planners = new FakePlanners(settings());
|
||||||
|
HydrologyPlanCache cache = spy(new HydrologyPlanCache(planners.settings, planners::region, planners::window,
|
||||||
|
pool(planners, 2), () -> false));
|
||||||
|
List<int[]> prepared = Collections.synchronizedList(new ArrayList<>());
|
||||||
|
doAnswer(invocation -> {
|
||||||
|
prepared.add(new int[]{invocation.getArgument(0), invocation.getArgument(1), invocation.getArgument(2), invocation.getArgument(3),
|
||||||
|
invocation.getArgument(4)});
|
||||||
|
return CompletableFuture.completedFuture(null);
|
||||||
|
}).when(cache).prepareWindowsAround(anyInt(), anyInt(), anyInt(), anyInt(), anyInt());
|
||||||
|
HydrologyRuntime runtime = new HydrologyRuntime(planners.settings, cache, planners::natural, () -> false, planners.ticker::get);
|
||||||
|
runtimes.add(runtime);
|
||||||
|
int chunks = WindowKey.SIZE >> 4;
|
||||||
|
HydrologyPregeneration pregeneration = runtime.openPregeneration();
|
||||||
|
pregeneration.bound(0, 0, 1, 1);
|
||||||
|
|
||||||
|
runtime.raster(0, 0);
|
||||||
|
assertArrayEquals(new int[]{-chunks, -chunks, 3 * chunks - 1, 3 * chunks - 1, 0}, prepared.getLast());
|
||||||
|
runtime.raster(chunks + 5, chunks + 5);
|
||||||
|
assertArrayEquals(new int[]{-chunks, -chunks, 3 * chunks - 1, 3 * chunks - 1, 0}, prepared.getLast());
|
||||||
|
runtime.raster(-3 * chunks, 0);
|
||||||
|
assertArrayEquals(new int[]{-3 * chunks, 0, -3 * chunks, 0, 2}, prepared.getLast());
|
||||||
|
|
||||||
|
pregeneration.close();
|
||||||
|
runtime.raster(1, 1);
|
||||||
|
assertArrayEquals(new int[]{1, 1, 1, 1, 2}, prepared.getLast());
|
||||||
|
}
|
||||||
|
|
||||||
@Test(timeout = 180_000L)
|
@Test(timeout = 180_000L)
|
||||||
public void preparePregenerationPlansRegionsInOrder() throws Exception {
|
public void preparePregenerationPlansRegionsInOrder() throws Exception {
|
||||||
FakePlanners planners = new FakePlanners(settings());
|
FakePlanners planners = new FakePlanners(settings());
|
||||||
|
|||||||
Reference in new issue
Block a user