Planner bake reservation

- Hydrology planner height reads reserve bake capacity up to the hydrology cache target from the shared budget
- Planner reservation is released when the complex closes
This commit is contained in:
Brian Neumann-Fopiano committed 2026-10-22 01:21:41 -04:00
1 parent 81f2d2332f
commit d7a2a52c72
2 files changed
+80

No files matched your search

@@ -141,6 +141,7 @@ public class IrisComplex implements DataProvider {
private static final int STUDIO_NOISE_CACHE_SIZE = 32_768;
private static final int TERRAIN_COLUMN_CACHE_SIZE = 524_288;
private static final int BAKE_CHUNKS_PER_NOISE_CHUNK = 8;
private static final int HYDROLOGY_BAKE_BUDGET_SHARES = 2;
/** One million corners: about 20 MB, roughly a 4000 by 4000 block area at the 4-block grid. */
private static final int SHARED_CORNER_BOUNDS_CAPACITY = 1 << 20;
/** The slope streams measure the rise across a run of this many blocks, so gradient is slope over run. */
@@ -217,6 +218,9 @@ public class IrisComplex implements DataProvider {
@Getter(AccessLevel.NONE)
@Setter(AccessLevel.NONE)
private transient boolean hydrologyNoiseCachesClosed;
@Getter(AccessLevel.NONE)
@Setter(AccessLevel.NONE)
private transient HydrologyNoiseCacheBudget.Reservation hydrologyBakeReservation;
private RNG rng;
private double fluidHeight;
private IrisData data;
@@ -626,6 +630,27 @@ public class IrisComplex implements DataProvider {
hydrologyNoiseCaches = List.of();
}
synchronized void reserveHydrologyBake(HydrologyNoiseCacheBudget budget) {
if (detached || hydrologyRuntime == null || hydrologyNoiseCachesClosed || hydrologyBakeReservation != null) {
return;
}
int capacity = bakeCache.capacity();
int requested = HydrologyNoiseCacheBudget.MAXIMUM_CHUNKS - capacity;
if (requested <= 0) {
return;
}
hydrologyBakeReservation = budget.reserve(HYDROLOGY_BAKE_BUDGET_SHARES, requested);
bakeCache.ensureCapacity(capacity + hydrologyBakeReservation.additionalChunks());
}
private synchronized void releaseHydrologyBake() {
if (hydrologyBakeReservation == null) {
return;
}
hydrologyBakeReservation.close();
hydrologyBakeReservation = null;
}
void enablePreparedHydrologyCache(String runtimeIdentity, Path persistentRoot) {
if (hydrologyRuntime != null) {
hydrologyRuntime.enableSharedCache(runtimeIdentity, persistentRoot);
@@ -1085,9 +1110,11 @@ public class IrisComplex implements DataProvider {
private final class HydrologyHeightProvider implements IrisHydrologyNaturalHeightProvider {
private final boolean batch = supportsHeightBatch();
private volatile boolean bakeReserved;
@Override
public double sample(int blockX, int blockZ) {
reserveBake();
return naturalHeightStream.getDouble(blockX, blockZ);
}
@@ -1102,8 +1129,17 @@ public class IrisComplex implements DataProvider {
IrisHydrologyNaturalHeightProvider.super.sampleBatch(coordinates, count, heights);
return;
}
reserveBake();
sampleNaturalBatch(coordinates, count, heights);
}
private void reserveBake() {
if (bakeReserved) {
return;
}
bakeReserved = true;
reserveHydrologyBake(HydrologyNoiseCacheBudget.SHARED);
}
}
private final class BakeSource implements PlaneSource {
@@ -3003,6 +3039,7 @@ public class IrisComplex implements DataProvider {
hydrologyRuntime.close();
}
releaseHydrologyNoiseCaches();
releaseHydrologyBake();
bakeCache.clear();
if (transitionDisplacement != null) {
transitionDisplacement.clear();
@@ -228,11 +228,54 @@ public class IrisComplexHydrologyNoiseCacheTest {
HydrologyNoiseCacheBudget budget = new HydrologyNoiseCacheBudget(Long.MAX_VALUE);
setField(fixture.complex, "detached", true);
fixture.complex.expandHydrologyNoiseCaches(budget);
fixture.complex.reserveHydrologyBake(budget);
fixture.assertCapacities(1_024);
assertEquals(1_024, fixture.bakes.capacity());
setField(fixture.complex, "detached", false);
fixture.complex.setHydrologyRuntime(null);
fixture.complex.expandHydrologyNoiseCaches(budget);
fixture.complex.reserveHydrologyBake(budget);
fixture.assertCapacities(1_024);
assertEquals(1_024, fixture.bakes.capacity());
assertEquals(0L, budget.reservedBytes());
}
}
@Test
public void plannerReservationGrowsTheBakeToTheHydrologyTarget() throws Exception {
try (Fixture fixture = new Fixture(1_024)) {
HydrologyNoiseCacheBudget budget = new HydrologyNoiseCacheBudget(Long.MAX_VALUE);
fixture.complex.reserveHydrologyBake(budget);
fixture.complex.reserveHydrologyBake(budget);
assertEquals(32_768, fixture.bakes.capacity());
assertEquals(2L * (32_768L - 1_024L) * 4_096L, budget.reservedBytes());
fixture.complex.ensureTerrainNoiseCacheSize(4_096);
assertEquals(32_768, fixture.bakes.capacity());
fixture.complex.close();
assertEquals(0L, budget.reservedBytes());
fixture.complex.reserveHydrologyBake(budget);
assertEquals(0L, budget.reservedBytes());
}
}
@Test
public void plannerReservationTakesOnlyTheGrantedShareOfTheBudget() throws Exception {
try (Fixture fixture = new Fixture(1_024)) {
HydrologyNoiseCacheBudget budget = new HydrologyNoiseCacheBudget(2L * 80L * 4_096L + 4_095L);
fixture.complex.reserveHydrologyBake(budget);
assertEquals(1_104, fixture.bakes.capacity());
assertEquals(2L * 80L * 4_096L, budget.reservedBytes());
fixture.complex.close();
assertEquals(0L, budget.reservedBytes());
}
}
@Test
public void plannerReservationLeavesLargerBakesUnchanged() throws Exception {
try (Fixture fixture = new Fixture(65_536)) {
HydrologyNoiseCacheBudget budget = new HydrologyNoiseCacheBudget(Long.MAX_VALUE);
fixture.complex.reserveHydrologyBake(budget);
assertEquals(65_536, fixture.bakes.capacity());
assertEquals(0L, budget.reservedBytes());
}
}