mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-22 16:17:44 +00:00
iggy
This commit is contained in:
1 parent
75e8c22268
commit
a8e46e3590
104 files changed
+6002
-2692
No files matched your search
Vendored
+70
@@ -0,0 +1,70 @@
|
||||
package art.arcane.iris.core.pregenerator.cache;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class PregenCacheImplPersistenceTest {
|
||||
private File directory;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
directory = Files.createTempDirectory("iris-pregen-cache-persist").toFile();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void partialRegionChunkPersistsAcrossReload() {
|
||||
PregenCache cache = PregenCache.create(directory);
|
||||
cache.cacheChunk(5, 7);
|
||||
cache.write();
|
||||
|
||||
PregenCache reloaded = PregenCache.create(directory);
|
||||
assertTrue(reloaded.isChunkCached(5, 7));
|
||||
assertFalse(reloaded.isChunkCached(5, 8));
|
||||
assertFalse(reloaded.isRegionCached(0, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void partialRegionNegativeCoordinatesPersistAcrossReload() {
|
||||
PregenCache cache = PregenCache.create(directory);
|
||||
cache.cacheChunk(-3, -1);
|
||||
cache.write();
|
||||
|
||||
PregenCache reloaded = PregenCache.create(directory);
|
||||
assertTrue(reloaded.isChunkCached(-3, -1));
|
||||
assertFalse(reloaded.isChunkCached(-3, -2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fullRegionPersistsAcrossReload() {
|
||||
PregenCache cache = PregenCache.create(directory);
|
||||
cache.cacheRegion(2, 3);
|
||||
cache.write();
|
||||
|
||||
PregenCache reloaded = PregenCache.create(directory);
|
||||
assertTrue(reloaded.isRegionCached(2, 3));
|
||||
assertTrue(reloaded.isChunkCached((2 << 5) + 11, (3 << 5) + 19));
|
||||
assertFalse(reloaded.isRegionCached(2, 4));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void regionCompletedChunkByChunkPersistsAsRegion() {
|
||||
PregenCache cache = PregenCache.create(directory);
|
||||
for (int x = 0; x < 32; x++) {
|
||||
for (int z = 0; z < 32; z++) {
|
||||
cache.cacheChunk(x, z);
|
||||
}
|
||||
}
|
||||
cache.write();
|
||||
|
||||
PregenCache reloaded = PregenCache.create(directory);
|
||||
assertTrue(reloaded.isRegionCached(0, 0));
|
||||
assertTrue(reloaded.isChunkCached(31, 31));
|
||||
assertFalse(reloaded.isChunkCached(32, 0));
|
||||
}
|
||||
}
|
||||
+213
@@ -0,0 +1,213 @@
|
||||
package art.arcane.iris.core.pregenerator.methods;
|
||||
|
||||
import art.arcane.iris.core.pregenerator.PregenListener;
|
||||
import art.arcane.iris.core.pregenerator.PregenTask;
|
||||
import art.arcane.iris.core.pregenerator.PregeneratorMethod;
|
||||
import art.arcane.iris.core.pregenerator.cache.PregenCache;
|
||||
import art.arcane.volmlib.util.mantle.runtime.Mantle;
|
||||
import art.arcane.volmlib.util.math.Position2;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class CachedPregenMethodCompletionTest {
|
||||
private File directory;
|
||||
private PregenCache cache;
|
||||
private CapturingMethod underlying;
|
||||
private PregenTask task;
|
||||
private CachedPregenMethod method;
|
||||
private RecordingListener listener;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
directory = Files.createTempDirectory("iris-pregen-cache-test").toFile();
|
||||
cache = PregenCache.create(directory);
|
||||
underlying = new CapturingMethod();
|
||||
task = PregenTask.builder()
|
||||
.center(new Position2(0, 0))
|
||||
.radiusX(256)
|
||||
.radiusZ(256)
|
||||
.build();
|
||||
method = new CachedPregenMethod(underlying, cache, task);
|
||||
listener = new RecordingListener();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generateChunkDoesNotCacheOnSubmitOnlyOnCompletion() {
|
||||
method.generateChunk(3, 4, listener);
|
||||
|
||||
assertEquals(1, underlying.generateChunkCalls.get());
|
||||
assertFalse(cache.isChunkCached(3, 4));
|
||||
|
||||
underlying.capturedListener.get().onChunkGenerated(3, 4, false);
|
||||
|
||||
assertTrue(cache.isChunkCached(3, 4));
|
||||
assertEquals(1, listener.generated.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generateChunkFailureIsNotCached() {
|
||||
method.generateChunk(6, 9, listener);
|
||||
underlying.capturedListener.get().onChunkFailed(6, 9);
|
||||
|
||||
assertFalse(cache.isChunkCached(6, 9));
|
||||
assertEquals(0, listener.generated.get());
|
||||
assertEquals(1, listener.failed.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generateChunkSkipsUnderlyingMethodWhenCached() {
|
||||
method.generateChunk(1, 2, listener);
|
||||
underlying.capturedListener.get().onChunkGenerated(1, 2, false);
|
||||
assertEquals(1, underlying.generateChunkCalls.get());
|
||||
|
||||
method.generateChunk(1, 2, listener);
|
||||
|
||||
assertEquals(1, underlying.generateChunkCalls.get());
|
||||
assertEquals(2, listener.generated.get());
|
||||
assertEquals(1, listener.generatedCached.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generateRegionReplayRespectsTaskBounds() {
|
||||
cache.cacheRegion(0, 0);
|
||||
List<long[]> expected = new ArrayList<>();
|
||||
task.iterateChunks(0, 0, (x, z) -> expected.add(new long[]{x, z}));
|
||||
assertTrue(expected.size() < 1024);
|
||||
|
||||
method.generateRegion(0, 0, listener);
|
||||
|
||||
assertEquals(0, underlying.generateRegionCalls.get());
|
||||
assertEquals(expected.size(), listener.generated.get());
|
||||
assertEquals(expected.size(), listener.generatedCached.get());
|
||||
}
|
||||
|
||||
private static final class CapturingMethod implements PregeneratorMethod {
|
||||
private final AtomicInteger generateChunkCalls = new AtomicInteger();
|
||||
private final AtomicInteger generateRegionCalls = new AtomicInteger();
|
||||
private final AtomicReference<PregenListener> capturedListener = new AtomicReference<>();
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsRegions(int x, int z, PregenListener listener) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMethod(int x, int z) {
|
||||
return "capture";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateRegion(int x, int z, PregenListener listener) {
|
||||
generateRegionCalls.incrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateChunk(int x, int z, PregenListener listener) {
|
||||
generateChunkCalls.incrementAndGet();
|
||||
capturedListener.set(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mantle getMantle() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class RecordingListener implements PregenListener {
|
||||
private final AtomicInteger generated = new AtomicInteger();
|
||||
private final AtomicInteger generatedCached = new AtomicInteger();
|
||||
private final AtomicInteger failed = new AtomicInteger();
|
||||
|
||||
@Override
|
||||
public void onTick(double chunksPerSecond, double chunksPerMinute, double regionsPerMinute, double percent, long generated, long totalChunks, long chunksRemaining, long eta, long elapsed, String method, boolean cached) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChunkGenerating(int x, int z) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChunkGenerated(int x, int z, boolean cached) {
|
||||
generated.incrementAndGet();
|
||||
if (cached) {
|
||||
generatedCached.incrementAndGet();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChunkFailed(int x, int z) {
|
||||
failed.incrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRegionGenerated(int x, int z) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRegionGenerating(int x, int z) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChunkCleaned(int x, int z) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRegionSkipped(int x, int z) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNetworkStarted(int x, int z) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNetworkFailed(int x, int z) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNetworkReclaim(int revert) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNetworkGeneratedChunk(int x, int z) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNetworkDownloaded(int x, int z) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaving() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChunkExistsInRegionGen(int x, int z) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
package art.arcane.iris.core.runtime;
|
||||
|
||||
import art.arcane.iris.core.IrisSettings;
|
||||
import art.arcane.iris.engine.framework.Engine;
|
||||
import art.arcane.iris.engine.object.IrisDimension;
|
||||
import art.arcane.iris.spi.PlatformBiome;
|
||||
import art.arcane.iris.spi.PlatformBlockState;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HexFormat;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class GoldenHashEngineTest {
|
||||
private static final int MIN_Y = 0;
|
||||
private static final int MAX_Y = 8;
|
||||
|
||||
private IrisSettings previousSettings;
|
||||
private File goldenDir;
|
||||
private Engine engine;
|
||||
private PlatformBlockState stone;
|
||||
private PlatformBlockState dirt;
|
||||
private PlatformBiome plains;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
previousSettings = IrisSettings.settings;
|
||||
IrisSettings.settings = new IrisSettings();
|
||||
goldenDir = Files.createTempDirectory("iris-goldenhash-test").toFile();
|
||||
engine = mock(Engine.class);
|
||||
IrisDimension dimension = mock(IrisDimension.class);
|
||||
when(dimension.getLoadKey()).thenReturn("testdim");
|
||||
when(engine.getDimension()).thenReturn(dimension);
|
||||
stone = mock(PlatformBlockState.class);
|
||||
when(stone.key()).thenReturn("minecraft:stone");
|
||||
dirt = mock(PlatformBlockState.class);
|
||||
when(dirt.key()).thenReturn("minecraft:dirt");
|
||||
plains = mock(PlatformBiome.class);
|
||||
when(plains.key()).thenReturn("minecraft:plains");
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
IrisSettings.settings = previousSettings;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void capturePersistsGoldenFileWithStableFormat() throws Exception {
|
||||
RecordingFeedback feedback = new RecordingFeedback();
|
||||
GoldenHashEngine hashEngine = new GoldenHashEngine(engine, request(GoldenHashEngine.Mode.AUTO), goldenDir, source(dirt, 3, 2, 1), feedback, progress());
|
||||
|
||||
assertTrue(hashEngine.run());
|
||||
File goldenFile = new File(goldenDir, "testdim-s1234-c0x0-r0.hashes");
|
||||
assertTrue(goldenFile.exists());
|
||||
|
||||
List<String> lines = Files.readAllLines(goldenFile.toPath(), StandardCharsets.UTF_8);
|
||||
assertEquals("#iris-goldenhash v1", lines.get(0));
|
||||
assertEquals("#world=testworld", lines.get(1));
|
||||
assertEquals("#dim=testdim", lines.get(2));
|
||||
assertEquals("#seed=1234", lines.get(3));
|
||||
assertEquals("#mc=test-1.0", lines.get(4));
|
||||
assertEquals("#minY=0 maxY=8", lines.get(5));
|
||||
assertEquals("#center=0,0", lines.get(6));
|
||||
assertEquals("#radius=0", lines.get(7));
|
||||
|
||||
String expectedBody = referenceLine(0, 0, 3, 2, 1);
|
||||
assertEquals(expectedBody, lines.get(8));
|
||||
assertEquals("#combined=" + referenceCombined(expectedBody), lines.get(9));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyMatchesGoldenCapture() {
|
||||
RecordingFeedback captureFeedback = new RecordingFeedback();
|
||||
GoldenHashEngine capture = new GoldenHashEngine(engine, request(GoldenHashEngine.Mode.AUTO), goldenDir, source(dirt, 3, 2, 1), captureFeedback, progress());
|
||||
assertTrue(capture.run());
|
||||
|
||||
RecordingFeedback verifyFeedback = new RecordingFeedback();
|
||||
GoldenHashEngine verify = new GoldenHashEngine(engine, request(GoldenHashEngine.Mode.AUTO), goldenDir, source(dirt, 3, 2, 1), verifyFeedback, progress());
|
||||
assertTrue(verify.run());
|
||||
assertTrue(verifyFeedback.ok.stream().anyMatch((String line) -> line.startsWith("GOLDEN MATCH: 1/1")));
|
||||
assertTrue(verifyFeedback.fail.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyFlagsMismatchAndWritesDiagnostics() {
|
||||
RecordingFeedback captureFeedback = new RecordingFeedback();
|
||||
GoldenHashEngine capture = new GoldenHashEngine(engine, request(GoldenHashEngine.Mode.AUTO), goldenDir, source(dirt, 3, 2, 1), captureFeedback, progress());
|
||||
assertTrue(capture.run());
|
||||
|
||||
RecordingFeedback verifyFeedback = new RecordingFeedback();
|
||||
GoldenHashEngine verify = new GoldenHashEngine(engine, request(GoldenHashEngine.Mode.VERIFY), goldenDir, source(dirt, 7, 4, 9), verifyFeedback, progress());
|
||||
assertFalse(verify.run());
|
||||
assertTrue(verifyFeedback.fail.stream().anyMatch((String line) -> line.startsWith("GOLDEN MISMATCH: 1/1")));
|
||||
|
||||
File goldenFile = new File(goldenDir, "testdim-s1234-c0x0-r0.hashes");
|
||||
assertTrue(new File(goldenDir, goldenFile.getName() + ".new").exists());
|
||||
assertTrue(new File(goldenDir, goldenFile.getName() + ".diag-c0x0.txt").exists());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyModeWithoutCaptureFails() {
|
||||
RecordingFeedback feedback = new RecordingFeedback();
|
||||
GoldenHashEngine verify = new GoldenHashEngine(engine, request(GoldenHashEngine.Mode.VERIFY), goldenDir, source(dirt, 3, 2, 1), feedback, progress());
|
||||
assertFalse(verify.run());
|
||||
assertTrue(feedback.fail.stream().anyMatch((String line) -> line.startsWith("No golden capture at ")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void abortsWithoutWritingWhenChunkGenerationFails() {
|
||||
RecordingFeedback feedback = new RecordingFeedback();
|
||||
GoldenHashEngine.ChunkSource broken = (int chunkX, int chunkZ) -> {
|
||||
throw new IllegalStateException("boom");
|
||||
};
|
||||
GoldenHashEngine hashEngine = new GoldenHashEngine(engine, request(GoldenHashEngine.Mode.AUTO), goldenDir, broken, feedback, progress());
|
||||
assertFalse(hashEngine.run());
|
||||
assertFalse(new File(goldenDir, "testdim-s1234-c0x0-r0.hashes").exists());
|
||||
assertTrue(feedback.fail.stream().anyMatch((String line) -> line.startsWith("GoldenHash aborted: 1 chunk(s)")));
|
||||
}
|
||||
|
||||
private GoldenHashEngine.Request request(GoldenHashEngine.Mode mode) {
|
||||
return new GoldenHashEngine.Request("testworld", 1234L, "test-1.0", MIN_Y, MAX_Y, 0, 0, 0, 1, mode, false, false, "null");
|
||||
}
|
||||
|
||||
private GoldenHashEngine.Progress progress() {
|
||||
return new GoldenHashEngine.Progress() {
|
||||
};
|
||||
}
|
||||
|
||||
private GoldenHashEngine.ChunkSource source(PlatformBlockState special, int sx, int sy, int sz) {
|
||||
return (int chunkX, int chunkZ) -> new GoldenHashEngine.ChunkSnapshot() {
|
||||
@Override
|
||||
public int minY() {
|
||||
return MIN_Y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int maxY() {
|
||||
return MAX_Y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlatformBlockState block(int x, int y, int z) {
|
||||
return x == sx && y == sy && z == sz ? special : stone;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlatformBiome biome(int x, int y, int z) {
|
||||
return plains;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private String referenceLine(int chunkX, int chunkZ, int sx, int sy, int sz) throws Exception {
|
||||
MessageDigest blockDigest = MessageDigest.getInstance("SHA-256");
|
||||
MessageDigest biomeDigest = MessageDigest.getInstance("SHA-256");
|
||||
for (int x = 0; x < 16; x++) {
|
||||
for (int z = 0; z < 16; z++) {
|
||||
for (int y = MIN_Y; y < MAX_Y; y++) {
|
||||
String key = x == sx && y == sy && z == sz ? "minecraft:dirt" : "minecraft:stone";
|
||||
blockDigest.update((key + "\n").getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int x = 0; x < 16; x += 4) {
|
||||
for (int z = 0; z < 16; z += 4) {
|
||||
for (int y = MIN_Y; y < MAX_Y; y += 4) {
|
||||
biomeDigest.update("minecraft:plains\n".getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
}
|
||||
return chunkX + " " + chunkZ + " "
|
||||
+ HexFormat.of().formatHex(blockDigest.digest()) + " "
|
||||
+ HexFormat.of().formatHex(biomeDigest.digest());
|
||||
}
|
||||
|
||||
private String referenceCombined(String bodyLine) throws Exception {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
digest.update((bodyLine + "\n").getBytes(StandardCharsets.UTF_8));
|
||||
return HexFormat.of().formatHex(digest.digest());
|
||||
}
|
||||
|
||||
private static final class RecordingFeedback implements GoldenHashEngine.Feedback {
|
||||
private final List<String> ok = new ArrayList<>();
|
||||
private final List<String> warn = new ArrayList<>();
|
||||
private final List<String> fail = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void ok(String message) {
|
||||
ok.add(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String message) {
|
||||
warn.add(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fail(String message) {
|
||||
fail.add(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package art.arcane.iris.util.common.math;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class ChunkSpiralTest {
|
||||
@Test
|
||||
public void centerOutMatchesLegacyOrdering() {
|
||||
int centerX = 5;
|
||||
int centerZ = -3;
|
||||
int radius = 3;
|
||||
List<int[]> expected = legacyOrderedTargets(centerX, centerZ, radius);
|
||||
List<int[]> actual = ChunkSpiral.centerOut(centerX, centerZ, radius);
|
||||
assertEquals(expected.size(), actual.size());
|
||||
for (int i = 0; i < expected.size(); i++) {
|
||||
assertArrayEquals(expected.get(i), actual.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void centerOutStartsAtCenterAndCoversSquare() {
|
||||
List<int[]> targets = ChunkSpiral.centerOut(10, 20, 2);
|
||||
assertEquals(25, targets.size());
|
||||
assertArrayEquals(new int[]{10, 20}, targets.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void centerOutRadiusZeroIsSingleChunk() {
|
||||
List<int[]> targets = ChunkSpiral.centerOut(-7, 9, 0);
|
||||
assertEquals(1, targets.size());
|
||||
assertArrayEquals(new int[]{-7, 9}, targets.get(0));
|
||||
}
|
||||
|
||||
private static List<int[]> legacyOrderedTargets(int centerChunkX, int centerChunkZ, int radius) {
|
||||
List<int[]> targets = new ArrayList<>();
|
||||
for (int dx = -radius; dx <= radius; dx++) {
|
||||
for (int dz = -radius; dz <= radius; dz++) {
|
||||
targets.add(new int[]{centerChunkX + dx, centerChunkZ + dz});
|
||||
}
|
||||
}
|
||||
|
||||
targets.sort(Comparator.comparingInt((int[] t) -> {
|
||||
int ox = t[0] - centerChunkX;
|
||||
int oz = t[1] - centerChunkZ;
|
||||
return ox * ox + oz * oz;
|
||||
}));
|
||||
return targets;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package art.arcane.iris.util.simd;
|
||||
|
||||
import art.arcane.iris.util.project.noise.FastNoiseDouble;
|
||||
import art.arcane.iris.util.project.noise.FastNoiseDouble.FractalType;
|
||||
import art.arcane.iris.util.project.noise.FastNoiseDouble.NoiseType;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class NoiseKernels2DParityTest {
|
||||
private static FastNoiseDouble oracle(long seed, int octaves) {
|
||||
FastNoiseDouble n = new FastNoiseDouble(seed);
|
||||
n.setNoiseType(NoiseType.SimplexFractal);
|
||||
n.setFractalType(FractalType.FBM);
|
||||
n.setFractalOctaves(octaves);
|
||||
return n;
|
||||
}
|
||||
|
||||
private static void assertKernelMatchesOracle(NoiseKernels2D kernel) {
|
||||
RNG rng = new RNG(99L);
|
||||
for (int octaves = 1; octaves <= 8; octaves++) {
|
||||
FastNoiseDouble n = oracle(1337L + octaves, octaves);
|
||||
int length = 256;
|
||||
double[] xs = new double[length];
|
||||
double[] zs = new double[length];
|
||||
double[] expected = new double[length];
|
||||
double[] out = new double[length];
|
||||
for (int k = 0; k < length; k++) {
|
||||
double x = (rng.nextDouble() - 0.5D) * 1_000_000D;
|
||||
double z = (rng.nextDouble() - 0.5D) * 1_000_000D;
|
||||
xs[k] = x;
|
||||
zs[k] = z;
|
||||
expected[k] = n.GetSimplexFractal(x, z);
|
||||
}
|
||||
kernel.simplexFractalFBM(1337L + octaves, octaves, n.getFrequency(), 2.0D, 0.5D, n.getFractalBounding(), xs, zs, out, length);
|
||||
for (int k = 0; k < length; k++) {
|
||||
assertEquals("octaves=" + octaves + " idx=" + k, expected[k], out[k], 0D);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void scalarKernelIsBitExactToOracle() {
|
||||
assertKernelMatchesOracle(new ScalarNoiseKernels2D());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vectorKernelSingleOctaveIsBitExactToOracle() {
|
||||
org.junit.Assume.assumeTrue(VectorNoiseKernels2D.lanesAligned());
|
||||
FastNoiseDouble n = oracle(202020L, 1);
|
||||
RNG rng = new RNG(7L);
|
||||
int length = 333;
|
||||
double[] xs = new double[length];
|
||||
double[] zs = new double[length];
|
||||
double[] expected = new double[length];
|
||||
double[] out = new double[length];
|
||||
for (int k = 0; k < length; k++) {
|
||||
double x = (rng.nextDouble() - 0.5D) * 800_000D;
|
||||
double z = (rng.nextDouble() - 0.5D) * 800_000D;
|
||||
xs[k] = x;
|
||||
zs[k] = z;
|
||||
expected[k] = n.GetSimplexFractal(x, z);
|
||||
}
|
||||
new VectorNoiseKernels2D().simplexFractalFBM(202020L, 1, n.getFrequency(), 2.0D, 0.5D, n.getFractalBounding(), xs, zs, out, length);
|
||||
for (int k = 0; k < length; k++) {
|
||||
assertEquals("idx=" + k, expected[k], out[k], 0D);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vectorKernelAllOctavesAreBitExactToOracle() {
|
||||
org.junit.Assume.assumeTrue(VectorNoiseKernels2D.lanesAligned());
|
||||
assertKernelMatchesOracle(new VectorNoiseKernels2D());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simdSupportReturnsBitExactNoiseKernel() {
|
||||
NoiseKernels2D kernel = SimdSupport.noiseKernels2D();
|
||||
org.junit.Assert.assertNotNull(kernel);
|
||||
assertKernelMatchesOracle(kernel);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simdSupportNeverSelectsVectorBelowProfitableLanes() {
|
||||
if (VectorNoiseKernels2D.profitable()) {
|
||||
org.junit.Assert.assertTrue(SimdSupport.createVectorNoiseKernels2D() instanceof VectorNoiseKernels2D);
|
||||
} else {
|
||||
org.junit.Assert.assertTrue(SimdSupport.noiseKernels2D() instanceof ScalarNoiseKernels2D);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vectorMatchesScalarAcrossEdgeCases() {
|
||||
org.junit.Assume.assumeTrue(VectorNoiseKernels2D.lanesAligned());
|
||||
NoiseKernels2D scalar = new ScalarNoiseKernels2D();
|
||||
NoiseKernels2D vector = new VectorNoiseKernels2D();
|
||||
RNG rng = new RNG(31337L);
|
||||
int[] lengths = {1, 3, 7, 8, 9, 255, 256, 257};
|
||||
double[] edgeCoords = {-4.0D, -3.0D, -1.0D, -0.0D, 0.0D, 1.0D, 4.0D, 1_000_000.0D, -1_000_000.0D};
|
||||
for (int octaves = 1; octaves <= 8; octaves++) {
|
||||
for (int length : lengths) {
|
||||
double[] xs = new double[length];
|
||||
double[] zs = new double[length];
|
||||
double[] a = new double[length];
|
||||
double[] b = new double[length];
|
||||
for (int k = 0; k < length; k++) {
|
||||
boolean edge = (k % 3) == 0;
|
||||
xs[k] = edge ? edgeCoords[k % edgeCoords.length] : (rng.nextDouble() - 0.5D) * 2_000_000D;
|
||||
zs[k] = edge ? edgeCoords[(k + 1) % edgeCoords.length] : (rng.nextDouble() - 0.5D) * 2_000_000D;
|
||||
}
|
||||
scalar.simplexFractalFBM(77L, octaves, 0.01D, 2.0D, 0.5D, 0.6667D, xs, zs, a, length);
|
||||
vector.simplexFractalFBM(77L, octaves, 0.01D, 2.0D, 0.5D, 0.6667D, xs, zs, b, length);
|
||||
for (int k = 0; k < length; k++) {
|
||||
assertEquals("oct=" + octaves + " len=" + length + " idx=" + k, a[k], b[k], 0D);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user