mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-26 22:10:18 +00:00
Fixed distant terrain bounds
- Shared terrain bounds retain full coordinates and interpolator identities - Cache regressions cover distant corners and concurrent replacement
This commit is contained in:
1 parent
d34dd48f74
commit
750ee87c6b
4 files changed
+135
-67
No files matched your search
+17
@@ -88,6 +88,23 @@ public class IrisComplexGridBoundsCacheTest {
|
||||
assertEquals(1, interpolator.getInvocations());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void distantCornersKeepTheirOwnBounds() throws Exception {
|
||||
IrisComplex complex = createComplex();
|
||||
Method cornerBounds = cornerBoundsMethod();
|
||||
CoordinateInterpolator interpolator = new CoordinateInterpolator();
|
||||
int[][] coordinates = {{64, -32}, {64 + (1 << 21), -32}, {64 - (1 << 21), -32},
|
||||
{64, -32 + (1 << 21)}, {64, -32 - (1 << 21)}};
|
||||
|
||||
for (int[] coordinate : coordinates) {
|
||||
long packed = invokeCornerBounds(complex, cornerBounds, interpolator,
|
||||
coordinate[0], coordinate[1]);
|
||||
assertBoundsBitsEqual(packedCoordinateBounds(coordinate[0], coordinate[1]),
|
||||
new NoiseBounds(unpackLow(packed), unpackHigh(packed)));
|
||||
}
|
||||
assertEquals(coordinates.length, interpolator.getInvocations());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonFiniteCornerBoundsAreNeverCached() throws Exception {
|
||||
IrisComplex complex = createComplex();
|
||||
|
||||
@@ -4,7 +4,9 @@ import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
@@ -15,65 +17,121 @@ public class SharedCornerBoundsTest {
|
||||
@Test
|
||||
public void storedCornersComeBackAndUnknownCornersMiss() {
|
||||
SharedCornerBounds cache = new SharedCornerBounds(1 << 10);
|
||||
long key = SharedCornerBounds.key(12, -40, 1);
|
||||
long key = SharedCornerBounds.key(12, -40);
|
||||
|
||||
assertFalse(cache.contains(key));
|
||||
cache.put(key, 0x1234_5678_9ABC_DEF0L);
|
||||
assertEquals(Long.MIN_VALUE, cache.get(key, 1));
|
||||
cache.put(key, 1, 0x1234_5678_9ABC_DEF0L);
|
||||
|
||||
assertTrue(cache.contains(key));
|
||||
assertEquals(0x1234_5678_9ABC_DEF0L, cache.get(key));
|
||||
assertFalse(cache.contains(SharedCornerBounds.key(12, -40, 0)));
|
||||
assertEquals(0x1234_5678_9ABC_DEF0L, cache.get(key, 1));
|
||||
assertEquals(Long.MIN_VALUE, cache.get(key, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void keysSeparateCoordinatesAndInterpolators() {
|
||||
assertNotEquals(SharedCornerBounds.key(1, 2, 0), SharedCornerBounds.key(2, 1, 0));
|
||||
assertNotEquals(SharedCornerBounds.key(1, 2, 0), SharedCornerBounds.key(1, 2, 1));
|
||||
assertNotEquals(SharedCornerBounds.key(-1, 2, 0), SharedCornerBounds.key(1, 2, 0));
|
||||
assertNotEquals(0L, SharedCornerBounds.key(0, 0, 0));
|
||||
public void zeroKeyMissesUntilTheOriginIsStored() {
|
||||
SharedCornerBounds cache = new SharedCornerBounds(2);
|
||||
long key = SharedCornerBounds.key(0, 0);
|
||||
|
||||
assertEquals(0L, key);
|
||||
assertEquals(Long.MIN_VALUE, cache.get(key, 0));
|
||||
cache.put(key, 0, 73L);
|
||||
assertEquals(73L, cache.get(key, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fullCoordinateDomainKeepsDistinctCorners() {
|
||||
int[] coordinates = {0, 1, -1, 1 << 21, -(1 << 21), 30_000_000, -30_000_000,
|
||||
Integer.MIN_VALUE, Integer.MAX_VALUE};
|
||||
for (int x : coordinates) {
|
||||
for (int z : coordinates) {
|
||||
SharedCornerBounds cache = new SharedCornerBounds(2);
|
||||
long key = SharedCornerBounds.key(x, z);
|
||||
cache.put(key, 0, 73L);
|
||||
for (int otherX : coordinates) {
|
||||
for (int otherZ : coordinates) {
|
||||
long otherKey = SharedCornerBounds.key(otherX, otherZ);
|
||||
if (x == otherX && z == otherZ) {
|
||||
assertEquals(73L, cache.get(otherKey, 0));
|
||||
} else {
|
||||
assertNotEquals(key, otherKey);
|
||||
assertEquals(Long.MIN_VALUE, cache.get(otherKey, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void interpolatorsKeepTheirFullIdentity() {
|
||||
int[] interpolators = {0, 1, -1, 1 << 21, -(1 << 21), Integer.MIN_VALUE, Integer.MAX_VALUE};
|
||||
long key = SharedCornerBounds.key(0, 0);
|
||||
for (int interpolator : interpolators) {
|
||||
SharedCornerBounds cache = new SharedCornerBounds(2);
|
||||
cache.put(key, interpolator, 73L);
|
||||
for (int other : interpolators) {
|
||||
assertEquals(interpolator == other ? 73L : Long.MIN_VALUE, cache.get(key, other));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aCollidingCornerReplacesTheOldOne() {
|
||||
SharedCornerBounds cache = new SharedCornerBounds(2);
|
||||
long first = SharedCornerBounds.key(0, 0, 0);
|
||||
long first = SharedCornerBounds.key(0, 0);
|
||||
long colliding = first;
|
||||
for (int gx = 1; gx < 100_000; gx++) {
|
||||
long candidate = SharedCornerBounds.key(gx, 0, 0);
|
||||
if (cache.slot(candidate) == cache.slot(first)) {
|
||||
long candidate = SharedCornerBounds.key(gx, 0);
|
||||
if (cache.slot(candidate, 0) == cache.slot(first, 0)) {
|
||||
colliding = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assertNotEquals(first, colliding);
|
||||
|
||||
cache.put(first, 1L);
|
||||
cache.put(colliding, 2L);
|
||||
cache.put(first, 0, 1L);
|
||||
cache.put(colliding, 0, 2L);
|
||||
|
||||
assertFalse(cache.contains(first));
|
||||
assertEquals(2L, cache.get(colliding));
|
||||
assertEquals(Long.MIN_VALUE, cache.get(first, 0));
|
||||
assertEquals(2L, cache.get(colliding, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void concurrentReadersNeverSeeAnotherCornersValue() throws InterruptedException {
|
||||
SharedCornerBounds cache = new SharedCornerBounds(1 << 8);
|
||||
SharedCornerBounds cache = new SharedCornerBounds(8);
|
||||
AtomicBoolean torn = new AtomicBoolean();
|
||||
AtomicInteger hits = new AtomicInteger();
|
||||
CountDownLatch start = new CountDownLatch(1);
|
||||
List<Thread> threads = new ArrayList<>();
|
||||
for (int worker = 0; worker < 8; worker++) {
|
||||
int[] coordinates = {0, 1 << 21, -(1 << 21), 30_000_000, -30_000_000,
|
||||
Integer.MIN_VALUE, Integer.MAX_VALUE};
|
||||
int[] interpolators = {0, 1, -1, 1 << 21, Integer.MIN_VALUE, Integer.MAX_VALUE};
|
||||
for (int worker = 0; worker < 4; worker++) {
|
||||
int seed = worker;
|
||||
threads.add(new Thread(() -> {
|
||||
try {
|
||||
start.await();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
torn.set(true);
|
||||
return;
|
||||
}
|
||||
long state = 0x9E3779B97F4A7C15L * (seed + 1);
|
||||
for (int step = 0; step < 400_000; step++) {
|
||||
for (int step = 0; step < 200_000; step++) {
|
||||
state ^= state << 13;
|
||||
state ^= state >>> 7;
|
||||
state ^= state << 17;
|
||||
int gx = (int) (state & 1023);
|
||||
int gz = (int) ((state >>> 10) & 1023);
|
||||
long key = SharedCornerBounds.key(gx, gz, (int) (state & 1));
|
||||
if ((state & 2) == 0) {
|
||||
cache.put(key, key * 31L);
|
||||
} else if (cache.contains(key) && cache.get(key) != key * 31L) {
|
||||
torn.set(true);
|
||||
int gx = coordinates[(int) ((state & Long.MAX_VALUE) % coordinates.length)];
|
||||
int gz = coordinates[(int) ((state >>> 10) % coordinates.length)];
|
||||
int interpolator = interpolators[(int) ((state >>> 20) % interpolators.length)];
|
||||
long key = SharedCornerBounds.key(gx, gz);
|
||||
long expected = expectedValue(key, interpolator);
|
||||
cache.put(key, interpolator, expected);
|
||||
long actual = cache.get(key, interpolator);
|
||||
if (actual != Long.MIN_VALUE) {
|
||||
hits.incrementAndGet();
|
||||
if (actual != expected) {
|
||||
torn.set(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
@@ -81,10 +139,16 @@ public class SharedCornerBoundsTest {
|
||||
for (Thread thread : threads) {
|
||||
thread.start();
|
||||
}
|
||||
start.countDown();
|
||||
for (Thread thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
assertTrue(hits.get() > 0);
|
||||
assertFalse(torn.get());
|
||||
}
|
||||
|
||||
private static long expectedValue(long key, int interpolator) {
|
||||
long value = Long.rotateLeft(key, 17) ^ (0x9E3779B97F4A7C15L * interpolator);
|
||||
return value == Long.MIN_VALUE ? 0L : value;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user