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 STUDIO_NOISE_CACHE_SIZE = 32_768;
private static final int TERRAIN_COLUMN_CACHE_SIZE = 524_288; private static final int TERRAIN_COLUMN_CACHE_SIZE = 524_288;
private static final int BAKE_CHUNKS_PER_NOISE_CHUNK = 8; 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. */ /** 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; 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. */ /** 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) @Getter(AccessLevel.NONE)
@Setter(AccessLevel.NONE) @Setter(AccessLevel.NONE)
private transient boolean hydrologyNoiseCachesClosed; private transient boolean hydrologyNoiseCachesClosed;
@Getter(AccessLevel.NONE)
@Setter(AccessLevel.NONE)
private transient HydrologyNoiseCacheBudget.Reservation hydrologyBakeReservation;
private RNG rng; private RNG rng;
private double fluidHeight; private double fluidHeight;
private IrisData data; private IrisData data;
@@ -626,6 +630,27 @@ public class IrisComplex implements DataProvider {
hydrologyNoiseCaches = List.of(); 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) { void enablePreparedHydrologyCache(String runtimeIdentity, Path persistentRoot) {
if (hydrologyRuntime != null) { if (hydrologyRuntime != null) {
hydrologyRuntime.enableSharedCache(runtimeIdentity, persistentRoot); hydrologyRuntime.enableSharedCache(runtimeIdentity, persistentRoot);
@@ -1085,9 +1110,11 @@ public class IrisComplex implements DataProvider {
private final class HydrologyHeightProvider implements IrisHydrologyNaturalHeightProvider { private final class HydrologyHeightProvider implements IrisHydrologyNaturalHeightProvider {
private final boolean batch = supportsHeightBatch(); private final boolean batch = supportsHeightBatch();
private volatile boolean bakeReserved;
@Override @Override
public double sample(int blockX, int blockZ) { public double sample(int blockX, int blockZ) {
reserveBake();
return naturalHeightStream.getDouble(blockX, blockZ); return naturalHeightStream.getDouble(blockX, blockZ);
} }
@@ -1102,8 +1129,17 @@ public class IrisComplex implements DataProvider {
IrisHydrologyNaturalHeightProvider.super.sampleBatch(coordinates, count, heights); IrisHydrologyNaturalHeightProvider.super.sampleBatch(coordinates, count, heights);
return; return;
} }
reserveBake();
sampleNaturalBatch(coordinates, count, heights); sampleNaturalBatch(coordinates, count, heights);
} }
private void reserveBake() {
if (bakeReserved) {
return;
}
bakeReserved = true;
reserveHydrologyBake(HydrologyNoiseCacheBudget.SHARED);
}
} }
private final class BakeSource implements PlaneSource { private final class BakeSource implements PlaneSource {
@@ -3003,6 +3039,7 @@ public class IrisComplex implements DataProvider {
hydrologyRuntime.close(); hydrologyRuntime.close();
} }
releaseHydrologyNoiseCaches(); releaseHydrologyNoiseCaches();
releaseHydrologyBake();
bakeCache.clear(); bakeCache.clear();
if (transitionDisplacement != null) { if (transitionDisplacement != null) {
transitionDisplacement.clear(); transitionDisplacement.clear();
@@ -228,11 +228,54 @@ public class IrisComplexHydrologyNoiseCacheTest {
HydrologyNoiseCacheBudget budget = new HydrologyNoiseCacheBudget(Long.MAX_VALUE); HydrologyNoiseCacheBudget budget = new HydrologyNoiseCacheBudget(Long.MAX_VALUE);
setField(fixture.complex, "detached", true); setField(fixture.complex, "detached", true);
fixture.complex.expandHydrologyNoiseCaches(budget); fixture.complex.expandHydrologyNoiseCaches(budget);
fixture.complex.reserveHydrologyBake(budget);
fixture.assertCapacities(1_024); fixture.assertCapacities(1_024);
assertEquals(1_024, fixture.bakes.capacity());
setField(fixture.complex, "detached", false); setField(fixture.complex, "detached", false);
fixture.complex.setHydrologyRuntime(null); fixture.complex.setHydrologyRuntime(null);
fixture.complex.expandHydrologyNoiseCaches(budget); fixture.complex.expandHydrologyNoiseCaches(budget);
fixture.complex.reserveHydrologyBake(budget);
fixture.assertCapacities(1_024); 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()); assertEquals(0L, budget.reservedBytes());
} }
} }