Remove the 2D cache bypass

Hydrology sampling now goes through the engine 2D caches; the thread-local bypass and its test are gone.
This commit is contained in:
Brian Neumann-Fopiano
2026-09-01 19:22:42 -04:00
parent 5b1b636539
commit b57a2e2fc8
6 changed files with 3 additions and 226 deletions
@@ -95,13 +95,9 @@ public final class IrisHydrologyRuntime implements AutoCloseable {
this.settings = createSettings(context.dimension(), hydrology);
HydrologyGeometrySampler geometrySampler = geometrySampler(context, hydrology);
IrisHydrologyRoutingTerrainSampler.Sources terrainSources = new IrisHydrologyRoutingTerrainSampler.Sources(
(int x, int z, double rawNaturalHeight) -> ProceduralStream.bypass2DCaches(
() -> createTerrainBasis(x, z, rawNaturalHeight)
),
this::createTerrainBasis,
(int x, int z) -> context.naturalHeightProvider().sample(x, z),
(int x, int z) -> ProceduralStream.bypass2DCaches(
() -> context.naturalOceanClassifier().isOcean(x, z)
)
(int x, int z) -> context.naturalOceanClassifier().isOcean(x, z)
);
this.routingTerrainSampler = new IrisHydrologyRoutingTerrainSampler(
terrainSources,
@@ -370,7 +366,7 @@ public final class IrisHydrologyRuntime implements AutoCloseable {
return cached;
}
}
HydrologyTerrainSample sampled = ProceduralStream.bypass2DCaches(() -> createDetailedTerrainSample(x, z));
HydrologyTerrainSample sampled = createDetailedTerrainSample(x, z);
synchronized (terrainSampleLock) {
HydrologyTerrainSample existing = terrainSamples.get(packed);
if (existing != null) {
@@ -72,18 +72,9 @@ import art.arcane.iris.util.project.stream.utility.SynchronizedStream;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
@SuppressWarnings("ALL")
public interface ProceduralStream<T> extends ProceduralLayer, Interpolated<T> {
static <V> V bypass2DCaches(Supplier<V> operation) {
return ProceduralStreamCacheBypass.supply(operation);
}
static boolean is2DCacheBypassActive() {
return ProceduralStreamCacheBypass.isActive();
}
static ProceduralStream<Double> ofDouble(Function2<Double, Double, Double> f) {
try {
return of(f, Interpolated.DOUBLE);
@@ -1,28 +0,0 @@
package art.arcane.iris.util.project.stream;
import java.util.function.Supplier;
final class ProceduralStreamCacheBypass {
private static final ThreadLocal<Integer> DEPTH = new ThreadLocal<>();
private ProceduralStreamCacheBypass() {
}
static boolean isActive() {
return DEPTH.get() != null;
}
static <T> T supply(Supplier<T> operation) {
Integer previousDepth = DEPTH.get();
DEPTH.set(previousDepth == null ? 1 : previousDepth + 1);
try {
return operation.get();
} finally {
if (previousDepth == null) {
DEPTH.remove();
} else {
DEPTH.set(previousDepth);
}
}
}
}
@@ -34,9 +34,6 @@ public class CachedDoubleStream2D extends BasicStream<Double> implements Procedu
@Override
public Double get(double x, double z) {
if (ProceduralStream.is2DCacheBypassActive()) {
return stream.getDouble((int) x, (int) z);
}
return cache.get((int) x, (int) z);
}
@@ -47,9 +44,6 @@ public class CachedDoubleStream2D extends BasicStream<Double> implements Procedu
@Override
public double getDouble(double x, double z) {
if (ProceduralStream.is2DCacheBypassActive()) {
return stream.getDouble((int) x, (int) z);
}
return cache.get((int) x, (int) z);
}
@@ -55,9 +55,6 @@ public class CachedStream2D<T> extends BasicStream<T> implements ProceduralStrea
@Override
public T get(double x, double z) {
if (ProceduralStream.is2DCacheBypassActive()) {
return stream.get((int) x, (int) z);
}
return cache.get((int) x, (int) z);
}
@@ -1,173 +0,0 @@
package art.arcane.iris.util.project.stream;
import art.arcane.iris.engine.framework.Engine;
import art.arcane.iris.engine.framework.MeteredCache;
import art.arcane.iris.engine.framework.PreservationRegistry;
import art.arcane.iris.spi.IrisServices;
import art.arcane.iris.util.project.stream.interpolation.Interpolated;
import art.arcane.iris.util.project.stream.utility.CachedDoubleStream2D;
import art.arcane.iris.util.project.stream.utility.CachedStream2D;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
import static org.mockito.Mockito.mock;
public class ProceduralStreamCacheBypassTest {
private PreservationRegistry previousRegistry;
private Engine engine;
@Before
public void registerPreservationService() {
previousRegistry = IrisServices.getOrNull(PreservationRegistry.class);
IrisServices.register(PreservationRegistry.class, new NoOpPreservationRegistry());
engine = mock(Engine.class);
}
@After
public void restorePreservationService() {
if (previousRegistry == null) {
IrisServices.remove(PreservationRegistry.class);
} else {
IrisServices.register(PreservationRegistry.class, previousRegistry);
}
}
@Test
public void normalAccessCachesIntegerQuantizedValues() {
AtomicInteger genericCalls = new AtomicInteger();
ProceduralStream<Integer> genericSource = ProceduralStream.of(
(x, z) -> genericCalls.incrementAndGet(),
Interpolated.INT
);
CachedStream2D<Integer> genericCache = new CachedStream2D<>("generic", engine, genericSource, 8);
assertEquals(Integer.valueOf(1), genericCache.get(12.9D, -3.9D));
assertEquals(Integer.valueOf(1), genericCache.get(12.1D, -3.1D));
assertEquals(1, genericCalls.get());
assertEquals(256L, genericCache.getSize());
AtomicInteger doubleCalls = new AtomicInteger();
ProceduralStream<Double> doubleSource = ProceduralStream.ofDouble((x, z) -> {
doubleCalls.incrementAndGet();
return x * 31D + z;
});
CachedDoubleStream2D doubleCache = new CachedDoubleStream2D("double", engine, doubleSource, 8);
assertEquals(-1_026D, doubleCache.getDouble(-33.9D, -3.9D), 0D);
assertEquals(-1_026D, doubleCache.getDouble(-33.1D, -3.1D), 0D);
assertEquals(1, doubleCalls.get());
assertEquals(256L, doubleCache.getSize());
}
@Test
public void bypassMatchesNestedTwoDimensionalSemanticsWithoutGrowingCaches() {
DoubleChain baseline = new DoubleChain(engine);
DoubleChain bypassed = new DoubleChain(engine);
for (int index = 0; index < 32; index++) {
double x = index * 64D + 4.875D;
double z = index * -96D + 5.625D;
double expected = baseline.output.get(x, z);
double actual = ProceduralStream.bypass2DCaches(() -> bypassed.output.get(x, z));
assertEquals(Double.doubleToLongBits(expected), Double.doubleToLongBits(actual));
}
assertEquals(2_048L, baseline.inner.getSize());
assertEquals(2_048L, baseline.converted.getSize());
assertEquals(2_048L, baseline.output.getSize());
assertEquals(0L, bypassed.inner.getSize());
assertEquals(0L, bypassed.converted.getSize());
assertEquals(0L, bypassed.output.getSize());
}
@Test
public void nestedScopesRestoreAfterExceptions() {
DoubleChain chain = new DoubleChain(engine);
ProceduralStream.bypass2DCaches(() -> {
assertThrows(IllegalStateException.class, () -> ProceduralStream.bypass2DCaches(() -> {
chain.output.getDouble(3_000.75D, -4_000.25D);
throw new IllegalStateException("expected");
}));
chain.output.getDouble(5_000.75D, -6_000.25D);
assertEquals(0L, chain.inner.getSize());
assertEquals(0L, chain.converted.getSize());
assertEquals(0L, chain.output.getSize());
return null;
});
chain.output.getDouble(7_000.75D, -8_000.25D);
assertEquals(256L, chain.inner.getSize());
assertEquals(256L, chain.converted.getSize());
assertEquals(256L, chain.output.getSize());
}
@Test
public void bypassIsThreadLocal() {
CachedDoubleStream2D cache = new CachedDoubleStream2D(
"thread-local",
engine,
ProceduralStream.ofDouble((x, z) -> x * 17D + z),
8
);
ExecutorService executor = Executors.newSingleThreadExecutor();
try {
ProceduralStream.bypass2DCaches(() -> {
CompletableFuture<Double> otherThread = CompletableFuture.supplyAsync(
() -> cache.getDouble(1_024.75D, 2_048.25D),
executor
);
assertEquals(19_456D, otherThread.join(), 0D);
assertEquals(256L, cache.getSize());
cache.getDouble(4_096.75D, 8_192.25D);
assertEquals(256L, cache.getSize());
return null;
});
cache.getDouble(12_288.75D, 16_384.25D);
assertEquals(512L, cache.getSize());
} finally {
executor.shutdownNow();
}
}
private static final class DoubleChain {
private final CachedDoubleStream2D inner;
private final CachedStream2D<Double> converted;
private final CachedDoubleStream2D output;
private DoubleChain(Engine engine) {
ProceduralStream<Double> source = ProceduralStream.ofDouble(
(x, z) -> x * x * 0.125D + z * z * 0.25D + x * z * 0.0625D
);
inner = new CachedDoubleStream2D("inner", engine, source, 8);
ProceduralStream<Double> conversion = inner.convert(value -> value * 1.25D - 7D);
converted = new CachedStream2D<>("converted", engine, conversion, 8);
output = new CachedDoubleStream2D("output", engine, converted.slope(3), 8);
}
}
private static final class NoOpPreservationRegistry implements PreservationRegistry {
@Override
public void register(Thread thread) {
}
@Override
public void register(ExecutorService service) {
}
@Override
public void registerCache(MeteredCache cache) {
}
@Override
public void dereference() {
}
}
}