mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-26 22:10:18 +00:00
Restore evicted owners from plans
- A demanded owner missing from the owner cache is restored from its planned tile (resident or prepared-plan store) instead of drafted again - Same restore path as persisted tiles; plans identical
This commit is contained in:
1 parent
0b2a606a25
commit
b4ee500c14
4 files changed
+83
No files matched your search
@@ -28,6 +28,7 @@ import java.util.concurrent.CompletableFuture;
|
|||||||
import java.util.concurrent.CompletionException;
|
import java.util.concurrent.CompletionException;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ForkJoinWorkerThread;
|
import java.util.concurrent.ForkJoinWorkerThread;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
public final class HydrologyPlanner {
|
public final class HydrologyPlanner {
|
||||||
@@ -66,6 +67,7 @@ public final class HydrologyPlanner {
|
|||||||
final HydrologyFeatureSitePlanner featureSites;
|
final HydrologyFeatureSitePlanner featureSites;
|
||||||
final HydrologyRegionalPlanner regional;
|
final HydrologyRegionalPlanner regional;
|
||||||
private volatile Predicate<HydrologyTileKey> earlyOwnerScope;
|
private volatile Predicate<HydrologyTileKey> earlyOwnerScope;
|
||||||
|
private volatile Function<HydrologyTileKey, HydrologyTile> plannedTiles;
|
||||||
|
|
||||||
public HydrologyPlanner(long worldSeed, HydrologyPlannerSettings settings, HydrologyTerrainSampler sampler) {
|
public HydrologyPlanner(long worldSeed, HydrologyPlannerSettings settings, HydrologyTerrainSampler sampler) {
|
||||||
this(
|
this(
|
||||||
@@ -255,6 +257,14 @@ public final class HydrologyPlanner {
|
|||||||
earlyOwnerScope = scope;
|
earlyOwnerScope = scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Where the plans of tiles already planned are found. An owner draft that left the owner cache is
|
||||||
|
* restored from its tile's plan when a neighbour demands it again, instead of being drafted twice.
|
||||||
|
*/
|
||||||
|
void usePlannedTiles(Function<HydrologyTileKey, HydrologyTile> source) {
|
||||||
|
plannedTiles = source;
|
||||||
|
}
|
||||||
|
|
||||||
void clearOwnerDrafts() {
|
void clearOwnerDrafts() {
|
||||||
resolvedOwners.invalidateAll();
|
resolvedOwners.invalidateAll();
|
||||||
regional.clear();
|
regional.clear();
|
||||||
@@ -300,6 +310,9 @@ public final class HydrologyPlanner {
|
|||||||
return local;
|
return local;
|
||||||
}
|
}
|
||||||
CrossTileResolvedOwner cached = resolvedOwners.getIfPresent(key);
|
CrossTileResolvedOwner cached = resolvedOwners.getIfPresent(key);
|
||||||
|
if (cached == null) {
|
||||||
|
cached = plannedOwner(key);
|
||||||
|
}
|
||||||
if (cached != null) {
|
if (cached != null) {
|
||||||
context.remember(key, cached);
|
context.remember(key, cached);
|
||||||
return cached;
|
return cached;
|
||||||
@@ -346,6 +359,17 @@ public final class HydrologyPlanner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CrossTileResolvedOwner plannedOwner(HydrologyTileKey key) {
|
||||||
|
Function<HydrologyTileKey, HydrologyTile> source = plannedTiles;
|
||||||
|
HydrologyTile tile = source == null ? null : source.apply(key);
|
||||||
|
if (tile == null || tile.resolvedOwner() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
reuseResolvedTile(tile);
|
||||||
|
CrossTileResolvedOwner restored = resolvedOwners.getIfPresent(key);
|
||||||
|
return restored == null ? tile.resolvedOwner() : restored;
|
||||||
|
}
|
||||||
|
|
||||||
List<CrossTileResolvedOwner> resolveLowerRankOwners(
|
List<CrossTileResolvedOwner> resolveLowerRankOwners(
|
||||||
List<HydrologyTileKey> candidateKeys,
|
List<HydrologyTileKey> candidateKeys,
|
||||||
CrossTileResolutionContext context,
|
CrossTileResolutionContext context,
|
||||||
|
|||||||
@@ -161,6 +161,17 @@ public final class HydrologyTileCache implements AutoCloseable {
|
|||||||
this.closed = new AtomicBoolean();
|
this.closed = new AtomicBoolean();
|
||||||
this.localChunks = ThreadLocal.withInitial(LocalChunks::new);
|
this.localChunks = ThreadLocal.withInitial(LocalChunks::new);
|
||||||
this.neighbourPrefetchEnabled = true;
|
this.neighbourPrefetchEnabled = true;
|
||||||
|
planner.usePlannedTiles(this::plannedTile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A tile already planned, resident or in the prepared-plan store, loaded without planning it. */
|
||||||
|
private HydrologyTile plannedTile(HydrologyTileKey key) {
|
||||||
|
HydrologyTile resident = tiles.getIfPresent(key);
|
||||||
|
if (resident != null) {
|
||||||
|
return resident;
|
||||||
|
}
|
||||||
|
PreparedHydrologyTileStore store = persistentStore;
|
||||||
|
return store == null ? null : store.load(key).orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -133,6 +133,33 @@ public class HydrologyPlannerTest {
|
|||||||
assertTileContentsEqual(tile, planner.plan(EARLY_OWNER_TILE));
|
assertTileContentsEqual(tile, planner.plan(EARLY_OWNER_TILE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void evictedOwnerIsRestoredFromItsPlannedTileWithoutDrafting() {
|
||||||
|
AtomicInteger samples = new AtomicInteger();
|
||||||
|
HydrologyTerrainSampler counted = (int x, int z) -> {
|
||||||
|
samples.incrementAndGet();
|
||||||
|
return EARLY_OWNER_TERRAIN.sample(x, z);
|
||||||
|
};
|
||||||
|
HydrologyPlanner planner = new HydrologyPlanner(77L, EARLY_OWNER_SETTINGS, counted);
|
||||||
|
HydrologyTile tile = planner.plan(EARLY_OWNER_TILE);
|
||||||
|
CrossTileResolvedOwner original = tile.resolvedOwner();
|
||||||
|
planner.clearOwnerDrafts();
|
||||||
|
AtomicInteger lookups = new AtomicInteger();
|
||||||
|
planner.usePlannedTiles(key -> {
|
||||||
|
lookups.incrementAndGet();
|
||||||
|
return key.equals(EARLY_OWNER_TILE) ? tile : null;
|
||||||
|
});
|
||||||
|
samples.set(0);
|
||||||
|
|
||||||
|
CrossTileResolvedOwner restored = planner.resolveIndependentOwner(EARLY_OWNER_TILE);
|
||||||
|
|
||||||
|
assertEquals(original, restored);
|
||||||
|
assertEquals(0, samples.get());
|
||||||
|
assertEquals(1, lookups.get());
|
||||||
|
assertSame(restored, planner.resolveIndependentOwner(EARLY_OWNER_TILE));
|
||||||
|
assertEquals(1, lookups.get());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void restoringPreparedTileKeepsExistingCanonicalOwner() {
|
public void restoringPreparedTileKeepsExistingCanonicalOwner() {
|
||||||
HydrologyPlanner planner = new HydrologyPlanner(77L, EARLY_OWNER_SETTINGS, EARLY_OWNER_TERRAIN);
|
HydrologyPlanner planner = new HydrologyPlanner(77L, EARLY_OWNER_SETTINGS, EARLY_OWNER_TERRAIN);
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import java.util.concurrent.TimeoutException;
|
|||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
|
|
||||||
@@ -45,6 +46,7 @@ import static org.junit.Assert.assertEquals;
|
|||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertSame;
|
import static org.junit.Assert.assertSame;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertThrows;
|
import static org.junit.Assert.assertThrows;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
@@ -615,6 +617,25 @@ public class HydrologyTileCacheTest {
|
|||||||
verify(planner).limitEarlyOwners(null);
|
verify(planner).limitEarlyOwners(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void plannerFindsTilesThisCachePlanned() {
|
||||||
|
HydrologyPlanner planner = mock(HydrologyPlanner.class);
|
||||||
|
HydrologyTile tile = mock(HydrologyTile.class);
|
||||||
|
HydrologyPlannerSettings settings = stalePrefetchSettings();
|
||||||
|
when(planner.settings()).thenReturn(settings);
|
||||||
|
when(planner.plan(any(HydrologyTileKey.class))).thenReturn(tile);
|
||||||
|
HydrologyTileCache cache = new HydrologyTileCache(planner, 128);
|
||||||
|
ArgumentCaptor<Function<HydrologyTileKey, HydrologyTile>> planned = ArgumentCaptor.forClass(Function.class);
|
||||||
|
verify(planner).usePlannedTiles(planned.capture());
|
||||||
|
HydrologyTileKey key = new HydrologyTileKey(3, -2);
|
||||||
|
|
||||||
|
assertNull(planned.getValue().apply(key));
|
||||||
|
assertSame(tile, cache.get(key));
|
||||||
|
assertSame(tile, planned.getValue().apply(key));
|
||||||
|
verify(planner, times(1)).plan(key);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void invalidPregenBoundsLeaveQueuedPrefetchIntact() {
|
public void invalidPregenBoundsLeaveQueuedPrefetchIntact() {
|
||||||
HydrologyPlanner planner = mock(HydrologyPlanner.class);
|
HydrologyPlanner planner = mock(HydrologyPlanner.class);
|
||||||
|
|||||||
Reference in new issue
Block a user