mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-28 23:10:32 +00:00
Port the v3 map as iris std map
- iris std map opens the v3 map window; noise, palettes, objects, procedural and image maps are views in it - Rivers view draws v4 hydrology, with an empty state when the pack has rivers off - 3D views fall back to software when LWJGL isn't on the classpath; modded jars use the game's LWJGL - FlatLaf and LWJGL join core; v4's separate studio windows are removed
This commit is contained in:
1 parent
2d211cf106
commit
29cca99947
214 files changed
+27582
-4368
No files matched your search
+3
-3
@@ -1,4 +1,4 @@
|
||||
package art.arcane.iris.studio.view;
|
||||
package art.arcane.iris.studio.map;
|
||||
|
||||
import art.arcane.iris.generation.runtime.Engine;
|
||||
import org.junit.Test;
|
||||
@@ -7,12 +7,12 @@ import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
public class ImageMapStudioGUITest {
|
||||
public class ImageMapExplorerTest {
|
||||
@Test
|
||||
public void exportRequestsHotloadWithoutMutatingTheActiveGenerationData() {
|
||||
Engine engine = mock(Engine.class);
|
||||
|
||||
ImageMapStudioGUI.reloadActiveEngine(engine);
|
||||
ImageMapExplorer.reloadActiveEngine(engine);
|
||||
|
||||
verify(engine).hotloadSilently();
|
||||
verifyNoMoreInteractions(engine);
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package art.arcane.iris.studio.view;
|
||||
package art.arcane.iris.studio.map;
|
||||
|
||||
import art.arcane.iris.generation.image.IrisImageMap;
|
||||
import art.arcane.iris.generation.image.IrisImageMapOrigin;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package art.arcane.iris.studio.view;
|
||||
package art.arcane.iris.studio.map;
|
||||
|
||||
import art.arcane.iris.generation.image.CompiledIrisImageMap;
|
||||
import art.arcane.iris.generation.image.IrisImageMapRuntime;
|
||||
@@ -0,0 +1,74 @@
|
||||
package art.arcane.iris.studio.map;
|
||||
|
||||
import art.arcane.iris.configuration.IrisSettings;
|
||||
import art.arcane.iris.generation.runtime.MeteredCache;
|
||||
import art.arcane.iris.generation.runtime.PreservationRegistry;
|
||||
import art.arcane.iris.spi.IrisServices;
|
||||
import art.arcane.iris.testsupport.PlatformLeakGuard;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public final class NoiseExplorerGUITest {
|
||||
@ClassRule
|
||||
public static final PlatformLeakGuard PLATFORM_GUARD = PlatformLeakGuard.clean();
|
||||
|
||||
@Test
|
||||
public void allExecutorsRegisterForPreservationAndCloseWithWindow() throws Exception {
|
||||
PreservationRegistry previous = IrisServices.getOrNull(PreservationRegistry.class);
|
||||
IrisSettings previousSettings = IrisSettings.settings;
|
||||
RecordingPreservationRegistry preservation = new RecordingPreservationRegistry();
|
||||
AtomicReference<NoiseExplorerGUI> explorerReference = new AtomicReference<>();
|
||||
IrisServices.register(PreservationRegistry.class, preservation);
|
||||
IrisSettings.settings = new IrisSettings();
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(() -> explorerReference.set(NoiseExplorerGUI.createNoiseExplorer(null)));
|
||||
|
||||
assertFalse(preservation.executors.isEmpty());
|
||||
SwingUtilities.invokeAndWait(explorerReference.get()::close);
|
||||
for (ExecutorService executor : preservation.executors) {
|
||||
assertTrue(executor.isShutdown());
|
||||
}
|
||||
} finally {
|
||||
NoiseExplorerGUI explorer = explorerReference.get();
|
||||
if (explorer != null) {
|
||||
SwingUtilities.invokeAndWait(explorer::close);
|
||||
}
|
||||
if (previous == null) {
|
||||
IrisServices.remove(PreservationRegistry.class);
|
||||
} else {
|
||||
IrisServices.register(PreservationRegistry.class, previous);
|
||||
}
|
||||
IrisSettings.settings = previousSettings;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class RecordingPreservationRegistry implements PreservationRegistry {
|
||||
private final List<ExecutorService> executors = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void register(Thread thread) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(ExecutorService service) {
|
||||
executors.add(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerCache(MeteredCache cache) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dereference() {
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package art.arcane.iris.studio.view;
|
||||
package art.arcane.iris.studio.map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
+2
-63
@@ -1,4 +1,4 @@
|
||||
package art.arcane.iris.studio.view;
|
||||
package art.arcane.iris.studio.map;
|
||||
|
||||
import art.arcane.volmlib.util.function.NoiseProvider;
|
||||
import org.junit.Test;
|
||||
@@ -295,6 +295,7 @@ public final class NoiseRenderCoordinatorTest {
|
||||
coordinator.request(stale);
|
||||
assertTrue(firstSample.await(3L, TimeUnit.SECONDS));
|
||||
coordinator.request(latest);
|
||||
releaseFirst.countDown();
|
||||
|
||||
assertTrue(latestCompleted.await(3L, TimeUnit.SECONDS));
|
||||
assertEquals(null, failure.get());
|
||||
@@ -354,66 +355,4 @@ public final class NoiseRenderCoordinatorTest {
|
||||
coordinator.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void latestMultiBandViewCompletesAfterThreeInterruptIgnoringGenerations() throws Exception {
|
||||
CountDownLatch latestCompleted = new CountDownLatch(1);
|
||||
AtomicReference<Throwable> failure = new AtomicReference<>();
|
||||
AtomicBoolean releaseStale = new AtomicBoolean();
|
||||
NoiseRenderCoordinator coordinator = new NoiseRenderCoordinator(2, new NoiseRenderCoordinator.Listener() {
|
||||
@Override
|
||||
public void onRenderStarted(NoiseRenderCoordinator.Request request) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRenderCompleted(NoiseRenderCoordinator.Result result) {
|
||||
if (result.request().revision() == 4L) {
|
||||
latestCompleted.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRenderFailed(NoiseRenderCoordinator.Request request, Throwable error) {
|
||||
failure.set(error);
|
||||
latestCompleted.countDown();
|
||||
}
|
||||
});
|
||||
try {
|
||||
for (long revision = 1L; revision <= 3L; revision++) {
|
||||
CountDownLatch staleStarted = new CountDownLatch(2);
|
||||
coordinator.request(new NoiseRenderCoordinator.Request(
|
||||
revision,
|
||||
(x, z) -> {
|
||||
staleStarted.countDown();
|
||||
while (!releaseStale.get()) {
|
||||
Thread.onSpinWait();
|
||||
}
|
||||
return 0.25D;
|
||||
},
|
||||
new NoiseViewport(0D, 0D, 1D),
|
||||
NoisePalette.TERRAIN,
|
||||
8,
|
||||
4,
|
||||
1
|
||||
));
|
||||
assertTrue(staleStarted.await(1L, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
coordinator.request(new NoiseRenderCoordinator.Request(
|
||||
4L,
|
||||
(x, z) -> 0.75D,
|
||||
new NoiseViewport(0D, 0D, 1D),
|
||||
NoisePalette.TERRAIN,
|
||||
8,
|
||||
4,
|
||||
1
|
||||
));
|
||||
|
||||
assertTrue(latestCompleted.await(3L, TimeUnit.SECONDS));
|
||||
assertEquals(null, failure.get());
|
||||
} finally {
|
||||
releaseStale.set(true);
|
||||
coordinator.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package art.arcane.iris.studio.view;
|
||||
package art.arcane.iris.studio.map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package art.arcane.iris.studio.view;
|
||||
package art.arcane.iris.studio.map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package art.arcane.iris.studio.view;
|
||||
|
||||
import art.arcane.iris.configuration.IrisSettings;
|
||||
import art.arcane.iris.studio.map.MapWindow;
|
||||
import art.arcane.iris.studio.map.NoiseExplorerGUI;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -38,28 +40,28 @@ public class DesktopGuiLaunchTest {
|
||||
|
||||
@Test
|
||||
public void mapLaunchDoesNotCreateAWindowWithoutDesktop() throws Exception {
|
||||
VisionGUI.launch(null, null);
|
||||
MapWindow.openMap(null);
|
||||
|
||||
assertNoDesktopFailure();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noiseLaunchDoesNotCreateAWindowWithoutDesktop() throws Exception {
|
||||
NoiseExplorerGUI.launch();
|
||||
MapWindow.openNoise(null);
|
||||
|
||||
assertNoDesktopFailure();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generatorLaunchDoesNotCreateAWindowWithoutDesktop() throws Exception {
|
||||
NoiseExplorerGUI.launchGeneratorKey("test", null, 12345L);
|
||||
NoiseExplorerGUI.launchGeneratorKey(null, "test", null, 12345L);
|
||||
|
||||
assertNoDesktopFailure();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void imageMapLaunchDoesNotReadEngineWithoutDesktop() throws Exception {
|
||||
ImageMapStudioGUI.launch(null);
|
||||
MapWindow.openImageMaps(null);
|
||||
|
||||
assertNoDesktopFailure();
|
||||
}
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
package art.arcane.iris.studio.view;
|
||||
|
||||
import art.arcane.iris.generation.runtime.MeteredCache;
|
||||
import art.arcane.iris.generation.runtime.PreservationRegistry;
|
||||
import art.arcane.iris.spi.IrisServices;
|
||||
import art.arcane.iris.testsupport.PlatformLeakGuard;
|
||||
import art.arcane.volmlib.util.function.NoiseProvider;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
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 final class NoiseExplorerGUITest {
|
||||
@ClassRule
|
||||
public static final PlatformLeakGuard PLATFORM_GUARD = PlatformLeakGuard.clean();
|
||||
|
||||
@Test
|
||||
public void openingInitialSourceBuildsSamplerExactlyOnce() throws Exception {
|
||||
AtomicInteger factoryCalls = new AtomicInteger();
|
||||
CountDownLatch firstFactoryCall = new CountDownLatch(1);
|
||||
CountDownLatch releaseFactory = new CountDownLatch(1);
|
||||
CountDownLatch duplicateFactoryCall = new CountDownLatch(1);
|
||||
AtomicReference<NoiseExplorerGUI> explorerReference = new AtomicReference<>();
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
NoiseExplorerGUI explorer = new NoiseExplorerGUI(sourceSeed -> {
|
||||
int invocation = factoryCalls.incrementAndGet();
|
||||
if (invocation == 1) {
|
||||
firstFactoryCall.countDown();
|
||||
try {
|
||||
releaseFactory.await(3L, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException error) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
} else {
|
||||
duplicateFactoryCall.countDown();
|
||||
}
|
||||
return constantSampler();
|
||||
}, "Test source", 12345L);
|
||||
explorer.buildSidebar();
|
||||
explorer.openViewer();
|
||||
explorerReference.set(explorer);
|
||||
});
|
||||
|
||||
assertTrue(firstFactoryCall.await(3L, TimeUnit.SECONDS));
|
||||
releaseFactory.countDown();
|
||||
assertFalse(duplicateFactoryCall.await(300L, TimeUnit.MILLISECONDS));
|
||||
assertEquals(1, factoryCalls.get());
|
||||
} finally {
|
||||
releaseFactory.countDown();
|
||||
NoiseExplorerGUI explorer = explorerReference.get();
|
||||
if (explorer != null) {
|
||||
SwingUtilities.invokeAndWait(explorer::close);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allExecutorsRegisterForPreservationAndCloseWithWindow() throws Exception {
|
||||
PreservationRegistry previous = IrisServices.getOrNull(PreservationRegistry.class);
|
||||
RecordingPreservationRegistry preservation = new RecordingPreservationRegistry();
|
||||
AtomicReference<NoiseExplorerGUI> explorerReference = new AtomicReference<>();
|
||||
IrisServices.register(PreservationRegistry.class, preservation);
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(() -> explorerReference.set(
|
||||
new NoiseExplorerGUI(sourceSeed -> constantSampler(), "Test source", 12345L)
|
||||
));
|
||||
|
||||
assertEquals(2, preservation.executors.size());
|
||||
SwingUtilities.invokeAndWait(explorerReference.get()::close);
|
||||
for (ExecutorService executor : preservation.executors) {
|
||||
assertTrue(executor.isShutdown());
|
||||
}
|
||||
} finally {
|
||||
NoiseExplorerGUI explorer = explorerReference.get();
|
||||
if (explorer != null) {
|
||||
SwingUtilities.invokeAndWait(explorer::close);
|
||||
}
|
||||
if (previous == null) {
|
||||
IrisServices.remove(PreservationRegistry.class);
|
||||
} else {
|
||||
IrisServices.register(PreservationRegistry.class, previous);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static NoiseProvider constantSampler() {
|
||||
return (x, z) -> 0.5D;
|
||||
}
|
||||
|
||||
private static final class RecordingPreservationRegistry implements PreservationRegistry {
|
||||
private final List<ExecutorService> executors = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void register(Thread thread) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(ExecutorService service) {
|
||||
executors.add(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerCache(MeteredCache cache) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dereference() {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,370 +0,0 @@
|
||||
package art.arcane.iris.studio.view;
|
||||
|
||||
import art.arcane.iris.studio.render.IrisRenderer;
|
||||
import art.arcane.iris.studio.render.RenderType;
|
||||
import art.arcane.iris.testsupport.Await;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.time.Duration;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.BooleanSupplier;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyDouble;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class VisionRenderControllerTest {
|
||||
@Test
|
||||
public void visibleTilesAreUniqueAndOrderedCenterFirst() {
|
||||
VisionRenderController.RenderSpec spec = spec(RenderType.BIOME, 7L, 0D, 0D, 4D, 1440, 792);
|
||||
|
||||
List<VisionRenderController.VisibleTile> tiles = VisionRenderController.visibleTiles(spec);
|
||||
|
||||
assertEquals(336, tiles.size());
|
||||
Set<String> coordinates = new HashSet<>();
|
||||
double previousDistance = -1D;
|
||||
for (VisionRenderController.VisibleTile tile : tiles) {
|
||||
assertTrue(coordinates.add(tile.tileX() + ":" + tile.tileZ()));
|
||||
assertTrue(tile.distanceSquared() >= previousDistance);
|
||||
previousDistance = tile.distanceSquared();
|
||||
}
|
||||
assertEquals(1_376_256L, VisionRenderController.sampleCount(tiles.size()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void negativeWorldCoordinatesUseFloorBasedTileOwnership() {
|
||||
VisionRenderController.RenderSpec spec = spec(RenderType.BIOME, 1L, -32D, -32D, 1D, 1, 1);
|
||||
|
||||
List<VisionRenderController.VisibleTile> tiles = VisionRenderController.visibleTiles(spec);
|
||||
|
||||
assertEquals(1, tiles.size());
|
||||
assertEquals(-1L, tiles.get(0).tileX());
|
||||
assertEquals(-1L, tiles.get(0).tileZ());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cacheIdentityContainsOnlyAtlasGenerationModeZoomAndWorldPage() {
|
||||
VisionRenderController.TileKey baseline = new VisionRenderController.TileKey(4L, RenderType.BIOME, 4D, 8L, 9L);
|
||||
|
||||
assertNotEquals(baseline, new VisionRenderController.TileKey(5L, RenderType.BIOME, 4D, 8L, 9L));
|
||||
assertNotEquals(baseline, new VisionRenderController.TileKey(4L, RenderType.RIVER, 4D, 8L, 9L));
|
||||
assertNotEquals(baseline, new VisionRenderController.TileKey(4L, RenderType.BIOME, 4.5D, 8L, 9L));
|
||||
assertNotEquals(baseline, new VisionRenderController.TileKey(4L, RenderType.BIOME, 4D, 7L, 9L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void everyPublishedPageIsFinalResolutionAndIdenticalRequestsAreCacheHits() throws Exception {
|
||||
IrisRenderer renderer = mock(IrisRenderer.class);
|
||||
AtomicInteger calls = new AtomicInteger();
|
||||
when(renderer.renderStudioBase(
|
||||
anyDouble(),
|
||||
anyDouble(),
|
||||
anyDouble(),
|
||||
anyInt(),
|
||||
any(RenderType.class),
|
||||
any(BooleanSupplier.class)
|
||||
)).thenAnswer(invocation -> {
|
||||
assertEquals(VisionRenderController.TILE_PIXELS, (int) invocation.getArgument(3));
|
||||
calls.incrementAndGet();
|
||||
return new BufferedImage(
|
||||
VisionRenderController.TILE_PIXELS,
|
||||
VisionRenderController.TILE_PIXELS,
|
||||
BufferedImage.TYPE_INT_RGB
|
||||
);
|
||||
});
|
||||
passThroughRefinement(renderer);
|
||||
VisionRenderController controller = controller(2);
|
||||
VisionRenderController.RenderSpec spec = spec(renderer, RenderType.BIOME, 1L, 0D, 0D, 4D, 128, 128);
|
||||
try {
|
||||
VisionRenderController.Frame first = controller.request(spec);
|
||||
Await.until("the first render to report every tile ready", Duration.ofSeconds(2L),
|
||||
() -> controller.progress(first).ready() == controller.progress(first).total());
|
||||
int rendered = calls.get();
|
||||
|
||||
VisionRenderController.Frame cached = controller.request(spec);
|
||||
|
||||
assertEquals(rendered, calls.get());
|
||||
assertEquals(controller.progress(cached).total(), controller.progress(cached).ready());
|
||||
for (VisionRenderController.VisibleTile tile : cached.tiles()) {
|
||||
assertEquals(VisionRenderController.TILE_PIXELS, controller.image(cached, tile).getWidth());
|
||||
assertEquals(VisionRenderController.TILE_PIXELS, controller.image(cached, tile).getHeight());
|
||||
}
|
||||
} finally {
|
||||
controller.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void smallPanReusesEveryOverlappingWorldPage() throws Exception {
|
||||
IrisRenderer renderer = exactRenderer();
|
||||
VisionRenderController controller = controller(2);
|
||||
try {
|
||||
VisionRenderController.Frame first = controller.request(spec(renderer, RenderType.BIOME, 1L, 16D, 16D, 4D, 128, 128));
|
||||
Await.until("the first render to report every tile ready", Duration.ofSeconds(2L),
|
||||
() -> controller.progress(first).ready() == controller.progress(first).total());
|
||||
Set<VisionRenderController.TileKey> firstKeys = keys(first);
|
||||
|
||||
VisionRenderController.Frame panned = controller.request(spec(renderer, RenderType.BIOME, 1L, 20D, 20D, 4D, 128, 128));
|
||||
|
||||
assertEquals(firstKeys, keys(panned));
|
||||
assertEquals(panned.tiles().size(), controller.progress(panned).ready());
|
||||
for (VisionRenderController.VisibleTile pannedTile : panned.tiles()) {
|
||||
VisionRenderController.VisibleTile original = tile(first, panned.key(pannedTile));
|
||||
assertSame(controller.image(first, original), controller.image(panned, pannedTile));
|
||||
}
|
||||
} finally {
|
||||
controller.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void newerViewPublishesWithoutWaitingForCancelledQueuedWork() throws Exception {
|
||||
IrisRenderer renderer = mock(IrisRenderer.class);
|
||||
CountDownLatch staleStarted = new CountDownLatch(1);
|
||||
CountDownLatch latestStarted = new CountDownLatch(1);
|
||||
when(renderer.renderStudio(
|
||||
anyDouble(),
|
||||
anyDouble(),
|
||||
anyDouble(),
|
||||
anyInt(),
|
||||
any(RenderType.class),
|
||||
any(BooleanSupplier.class)
|
||||
)).thenAnswer(invocation -> {
|
||||
RenderType type = invocation.getArgument(4);
|
||||
BooleanSupplier cancelled = invocation.getArgument(5);
|
||||
if (type == RenderType.REGION) {
|
||||
staleStarted.countDown();
|
||||
while (!cancelled.getAsBoolean()) {
|
||||
Thread.onSpinWait();
|
||||
}
|
||||
throw new java.util.concurrent.CancellationException();
|
||||
}
|
||||
latestStarted.countDown();
|
||||
return new BufferedImage(64, 64, BufferedImage.TYPE_INT_RGB);
|
||||
});
|
||||
when(renderer.renderStudioBase(anyDouble(), anyDouble(), anyDouble(), anyInt(),
|
||||
any(RenderType.class), any(BooleanSupplier.class))).thenAnswer(invocation -> {
|
||||
latestStarted.countDown();
|
||||
return new BufferedImage(64, 64, BufferedImage.TYPE_INT_RGB);
|
||||
});
|
||||
passThroughRefinement(renderer);
|
||||
VisionRenderController controller = controller(2);
|
||||
try {
|
||||
controller.request(spec(renderer, RenderType.REGION, 1L, 0D, 0D, 4D, 64, 64));
|
||||
assertTrue(staleStarted.await(1L, TimeUnit.SECONDS));
|
||||
|
||||
controller.request(spec(renderer, RenderType.BIOME, 1L, 0D, 0D, 4D, 64, 64));
|
||||
|
||||
assertTrue(latestStarted.await(1L, TimeUnit.SECONDS));
|
||||
} finally {
|
||||
controller.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void completeBaseIsVisibleBeforeBlockedHydrologyRefinementStarts() throws Exception {
|
||||
IrisRenderer renderer = exactRenderer();
|
||||
CountDownLatch overlayStarted = new CountDownLatch(1);
|
||||
CountDownLatch releaseOverlay = new CountDownLatch(1);
|
||||
AtomicInteger baseCalls = new AtomicInteger();
|
||||
AtomicInteger overlays = new AtomicInteger();
|
||||
when(renderer.renderStudioBase(anyDouble(), anyDouble(), anyDouble(), anyInt(),
|
||||
any(RenderType.class), any(BooleanSupplier.class))).thenAnswer(invocation -> {
|
||||
baseCalls.incrementAndGet();
|
||||
return new BufferedImage(64, 64, BufferedImage.TYPE_INT_RGB);
|
||||
});
|
||||
when(renderer.refineStudioBiome(anyDouble(), anyDouble(), anyDouble(), any(BufferedImage.class),
|
||||
any(BooleanSupplier.class))).thenAnswer(invocation -> {
|
||||
overlays.incrementAndGet();
|
||||
overlayStarted.countDown();
|
||||
assertTrue(releaseOverlay.await(2L, TimeUnit.SECONDS));
|
||||
return invocation.getArgument(3);
|
||||
});
|
||||
VisionRenderController controller = controller(3);
|
||||
try {
|
||||
VisionRenderController.RenderSpec spec = spec(renderer, RenderType.BIOME, 1L, 0D, 0D, 4D, 192, 128);
|
||||
VisionRenderController.Frame frame = controller.request(spec);
|
||||
assertTrue(overlayStarted.await(2L, TimeUnit.SECONDS));
|
||||
assertEquals(frame.tiles().size(), baseCalls.get());
|
||||
assertEquals(1, overlays.get());
|
||||
assertEquals(0, controller.progress(frame).ready());
|
||||
for (VisionRenderController.VisibleTile tile : frame.tiles()) {
|
||||
assertEquals(64, controller.image(frame, tile).getWidth());
|
||||
}
|
||||
|
||||
releaseOverlay.countDown();
|
||||
Await.until("every biome overlay to complete", Duration.ofSeconds(2L),
|
||||
() -> controller.progress(frame).ready() == frame.tiles().size());
|
||||
assertEquals(frame.tiles().size(), overlays.get());
|
||||
|
||||
VisionRenderController.Frame cached = controller.request(spec);
|
||||
assertEquals(frame.tiles().size(), controller.progress(cached).ready());
|
||||
assertEquals(frame.tiles().size(), baseCalls.get());
|
||||
assertEquals(frame.tiles().size(), overlays.get());
|
||||
} finally {
|
||||
releaseOverlay.countDown();
|
||||
controller.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cancelledOverlayKeepsCachedBaseWithoutMarkingItComplete() throws Exception {
|
||||
IrisRenderer renderer = exactRenderer();
|
||||
CountDownLatch staleStarted = new CountDownLatch(1);
|
||||
CountDownLatch latestStarted = new CountDownLatch(1);
|
||||
CountDownLatch releaseLatest = new CountDownLatch(1);
|
||||
AtomicInteger baseCalls = new AtomicInteger();
|
||||
AtomicInteger overlayCalls = new AtomicInteger();
|
||||
when(renderer.renderStudioBase(anyDouble(), anyDouble(), anyDouble(), anyInt(),
|
||||
any(RenderType.class), any(BooleanSupplier.class))).thenAnswer(invocation -> {
|
||||
baseCalls.incrementAndGet();
|
||||
return new BufferedImage(64, 64, BufferedImage.TYPE_INT_RGB);
|
||||
});
|
||||
when(renderer.refineStudioBiome(anyDouble(), anyDouble(), anyDouble(), any(BufferedImage.class),
|
||||
any(BooleanSupplier.class))).thenAnswer(invocation -> {
|
||||
if (overlayCalls.getAndIncrement() == 0) {
|
||||
staleStarted.countDown();
|
||||
BooleanSupplier cancelled = invocation.getArgument(4);
|
||||
while (!cancelled.getAsBoolean()) {
|
||||
Thread.onSpinWait();
|
||||
}
|
||||
throw new CancellationException();
|
||||
}
|
||||
latestStarted.countDown();
|
||||
assertTrue(releaseLatest.await(2L, TimeUnit.SECONDS));
|
||||
return invocation.getArgument(3);
|
||||
});
|
||||
VisionRenderController controller = controller(2);
|
||||
try {
|
||||
VisionRenderController.RenderSpec spec = spec(renderer, RenderType.BIOME, 1L, 0D, 0D, 4D, 128, 128);
|
||||
VisionRenderController.Frame stale = controller.request(spec);
|
||||
assertTrue(staleStarted.await(2L, TimeUnit.SECONDS));
|
||||
|
||||
VisionRenderController.Frame latest = controller.request(spec);
|
||||
assertTrue(latestStarted.await(2L, TimeUnit.SECONDS));
|
||||
assertEquals(stale.tiles().size(), baseCalls.get());
|
||||
assertEquals(0, controller.progress(latest).ready());
|
||||
for (VisionRenderController.VisibleTile tile : latest.tiles()) {
|
||||
assertEquals(64, controller.image(latest, tile).getWidth());
|
||||
}
|
||||
|
||||
releaseLatest.countDown();
|
||||
Await.until("the replacement frame to finish its overlays", Duration.ofSeconds(2L),
|
||||
() -> controller.progress(latest).ready() == latest.tiles().size());
|
||||
assertEquals(0, controller.progress(stale).ready());
|
||||
} finally {
|
||||
releaseLatest.countDown();
|
||||
controller.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static VisionRenderController controller(int workers) {
|
||||
return new VisionRenderController(
|
||||
() -> {
|
||||
},
|
||||
new VisionRenderController.RuntimeOptions(workers, 8, false)
|
||||
);
|
||||
}
|
||||
|
||||
private static IrisRenderer exactRenderer() {
|
||||
IrisRenderer renderer = mock(IrisRenderer.class);
|
||||
when(renderer.renderStudio(
|
||||
anyDouble(),
|
||||
anyDouble(),
|
||||
anyDouble(),
|
||||
anyInt(),
|
||||
any(RenderType.class),
|
||||
any(BooleanSupplier.class)
|
||||
)).thenAnswer(invocation -> new BufferedImage(
|
||||
invocation.getArgument(3),
|
||||
invocation.getArgument(3),
|
||||
BufferedImage.TYPE_INT_RGB
|
||||
));
|
||||
when(renderer.renderStudioBase(anyDouble(), anyDouble(), anyDouble(), anyInt(),
|
||||
any(RenderType.class), any(BooleanSupplier.class))).thenAnswer(invocation -> new BufferedImage(
|
||||
invocation.getArgument(3), invocation.getArgument(3), BufferedImage.TYPE_INT_RGB));
|
||||
passThroughRefinement(renderer);
|
||||
return renderer;
|
||||
}
|
||||
|
||||
private static void passThroughRefinement(IrisRenderer renderer) {
|
||||
when(renderer.refineStudioBiome(anyDouble(), anyDouble(), anyDouble(), any(BufferedImage.class),
|
||||
any(BooleanSupplier.class))).thenAnswer(invocation -> invocation.getArgument(3));
|
||||
}
|
||||
|
||||
private static Set<VisionRenderController.TileKey> keys(VisionRenderController.Frame frame) {
|
||||
Set<VisionRenderController.TileKey> keys = new HashSet<>();
|
||||
for (VisionRenderController.VisibleTile tile : frame.tiles()) {
|
||||
keys.add(frame.key(tile));
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
private static VisionRenderController.VisibleTile tile(
|
||||
VisionRenderController.Frame frame,
|
||||
VisionRenderController.TileKey key
|
||||
) {
|
||||
for (VisionRenderController.VisibleTile tile : frame.tiles()) {
|
||||
if (frame.key(tile).equals(key)) {
|
||||
return tile;
|
||||
}
|
||||
}
|
||||
throw new AssertionError("Missing tile " + key);
|
||||
}
|
||||
|
||||
private static VisionRenderController.RenderSpec spec(
|
||||
RenderType type,
|
||||
long revision,
|
||||
double centerX,
|
||||
double centerZ,
|
||||
double blocksPerPixel,
|
||||
int width,
|
||||
int height
|
||||
) {
|
||||
return new VisionRenderController.RenderSpec(
|
||||
mock(IrisRenderer.class),
|
||||
type,
|
||||
revision,
|
||||
centerX,
|
||||
centerZ,
|
||||
blocksPerPixel,
|
||||
width,
|
||||
height
|
||||
);
|
||||
}
|
||||
|
||||
private static VisionRenderController.RenderSpec spec(
|
||||
IrisRenderer renderer,
|
||||
RenderType type,
|
||||
long revision,
|
||||
double centerX,
|
||||
double centerZ,
|
||||
double blocksPerPixel,
|
||||
int width,
|
||||
int height
|
||||
) {
|
||||
return new VisionRenderController.RenderSpec(
|
||||
renderer,
|
||||
type,
|
||||
revision,
|
||||
centerX,
|
||||
centerZ,
|
||||
blocksPerPixel,
|
||||
width,
|
||||
height
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package art.arcane.iris.studio.view;
|
||||
|
||||
import art.arcane.iris.localization.DesktopUiMessages;
|
||||
import art.arcane.iris.studio.render.RenderType;
|
||||
import art.arcane.volmlib.util.localization.MessageKey;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
public class VisionRenderTypeTest {
|
||||
@Test
|
||||
public void everyRenderTypeHasALocalizedVisionLabel() {
|
||||
for (RenderType type : RenderType.values()) {
|
||||
MessageKey key = VisionGUI.modeKey(type);
|
||||
assertNotNull(type.name(), key);
|
||||
assertTrueCatalogMember(key);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void riverRenderTypeUsesTheRiverNetworkLabel() {
|
||||
assertSame(DesktopUiMessages.VISION_MODE_RIVER, VisionGUI.modeKey(RenderType.RIVER));
|
||||
assertEquals("iris.desktop.vision.mode.river", DesktopUiMessages.VISION_MODE_RIVER.id());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void teleportCoordinatesFloorAcrossTheNegativeOrigin() {
|
||||
assertEquals(-1, VisionGUI.floorWorldCoordinate(-0.01D));
|
||||
assertEquals(0, VisionGUI.floorWorldCoordinate(0.99D));
|
||||
assertEquals(1, VisionGUI.floorWorldCoordinate(1D));
|
||||
}
|
||||
|
||||
private static void assertTrueCatalogMember(MessageKey key) {
|
||||
assertEquals(key.id(), 1, DesktopUiMessages.keys().stream()
|
||||
.filter(candidate -> candidate.id().equals(key.id()))
|
||||
.count());
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user