mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-21 15:57:38 +00:00
Redesigned pregeneration map
- Desktop pregeneration uses a proportional map with labeled controls and metrics - Pixel updates remain bounded across large targets and minimized windows - Preview lifecycle keeps pause controls attached to their owning job - Bundled language catalogs include the revised desktop labels
This commit is contained in:
commit
28c715ff51
26 files changed
+1406
-375
No files matched your search
@@ -0,0 +1,109 @@
|
||||
package art.arcane.iris.core.gui;
|
||||
|
||||
import art.arcane.iris.core.pregenerator.PregenTask;
|
||||
import art.arcane.volmlib.util.math.Position2;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
import java.awt.Color;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class PregenMapPanelTest {
|
||||
@Test
|
||||
public void singleChunkAndInclusiveFinalEdgesReceivePixels() throws Exception {
|
||||
PregenMapPanel single = panel(new PregenRenderSnapshot.Bounds(5, -9, 5, -9));
|
||||
single.submit(5, -9, Color.GREEN);
|
||||
flush(single);
|
||||
assertEquals(Color.GREEN.getRGB(), image(single).getRGB(0, 0));
|
||||
|
||||
PregenMapPanel rectangle = panel(new PregenRenderSnapshot.Bounds(-2, -3, 1, -1));
|
||||
rectangle.submit(1, -1, Color.RED);
|
||||
rectangle.submit(-2, -3, Color.BLUE);
|
||||
flush(rectangle);
|
||||
assertEquals(Color.RED.getRGB(), image(rectangle).getRGB(3, 2));
|
||||
assertEquals(Color.BLUE.getRGB(), image(rectangle).getRGB(0, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void largeMapsStayBoundedAndEverySubmittedChunkCanReachAPixel() throws Exception {
|
||||
PregenMapPanel panel = panel(new PregenRenderSnapshot.Bounds(Integer.MIN_VALUE, -10, Integer.MAX_VALUE, 10));
|
||||
panel.submit(Integer.MAX_VALUE, 10, Color.GREEN);
|
||||
panel.submit(0, 0, Color.RED);
|
||||
flush(panel);
|
||||
|
||||
assertEquals(1024, image(panel).getWidth());
|
||||
assertEquals(21, image(panel).getHeight());
|
||||
assertEquals(Color.GREEN.getRGB(), image(panel).getRGB(1023, 20));
|
||||
assertEquals(Color.RED.getRGB(), image(panel).getRGB(512, 10));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void duplicateAndOutOfBoundsSubmissionsDoNotRequestAnotherFrame() throws Exception {
|
||||
PregenMapPanel panel = panel(new PregenRenderSnapshot.Bounds(0, 0, 1, 1));
|
||||
panel.submit(2, 0, Color.GREEN);
|
||||
SwingUtilities.invokeAndWait(() -> assertFalse(panel.flush()));
|
||||
panel.submit(0, 0, Color.GREEN);
|
||||
SwingUtilities.invokeAndWait(() -> assertTrue(panel.flush()));
|
||||
panel.submit(0, 0, Color.GREEN);
|
||||
SwingUtilities.invokeAndWait(() -> assertFalse(panel.flush()));
|
||||
SwingUtilities.invokeAndWait(panel::disposeMap);
|
||||
panel.submit(0, 0, Color.RED);
|
||||
SwingUtilities.invokeAndWait(() -> assertFalse(panel.flush()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pixelUpdatesRetainTheirLatestValueUntilTheViewFlushes() throws Exception {
|
||||
PregenMapPanel panel = panel(new PregenRenderSnapshot.Bounds(0, 0, 10_000, 0));
|
||||
for (int index = 0; index < 100_000; index++) {
|
||||
panel.submit(index % 10_001, 0, Color.RED);
|
||||
}
|
||||
panel.submit(10_000, 0, Color.GREEN);
|
||||
flush(panel);
|
||||
assertEquals(Color.GREEN.getRGB(), image(panel).getRGB(1023, 0));
|
||||
assertEquals(1024, image(panel).getWidth());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapFitPreservesRectangularChunkAspect() {
|
||||
Rectangle wide = PregenMapPanel.fit(new PregenRenderSnapshot.Bounds(0, 0, 3, 1), 802, 602);
|
||||
assertEquals(new Rectangle(1, 101, 800, 400), wide);
|
||||
Rectangle tall = PregenMapPanel.fit(new PregenRenderSnapshot.Bounds(0, 0, 1, 3), 802, 602);
|
||||
assertEquals(new Rectangle(251, 1, 300, 600), tall);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void arithmeticTaskBoundsMatchActualRectangularTraversal() {
|
||||
PregenTask task = PregenTask.builder().center(new Position2(-17, 35)).radiusX(9).radiusZ(37).build();
|
||||
int[] actual = {Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE};
|
||||
task.iterateAllChunks((x, z) -> {
|
||||
actual[0] = Math.min(actual[0], x);
|
||||
actual[1] = Math.min(actual[1], z);
|
||||
actual[2] = Math.max(actual[2], x);
|
||||
actual[3] = Math.max(actual[3], z);
|
||||
});
|
||||
assertArrayEquals(actual, task.chunkBounds());
|
||||
}
|
||||
|
||||
private static PregenMapPanel panel(PregenRenderSnapshot.Bounds bounds) throws Exception {
|
||||
PregenMapPanel[] result = new PregenMapPanel[1];
|
||||
SwingUtilities.invokeAndWait(() -> result[0] = new PregenMapPanel(bounds));
|
||||
return result[0];
|
||||
}
|
||||
|
||||
private static void flush(PregenMapPanel panel) throws Exception {
|
||||
SwingUtilities.invokeAndWait(panel::flush);
|
||||
}
|
||||
|
||||
private static BufferedImage image(PregenMapPanel panel) throws Exception {
|
||||
Field field = PregenMapPanel.class.getDeclaredField("image");
|
||||
field.setAccessible(true);
|
||||
return (BufferedImage) field.get(panel);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package art.arcane.iris.core.gui;
|
||||
|
||||
import art.arcane.iris.core.pregenerator.IrisPregenerator;
|
||||
import art.arcane.iris.core.pregenerator.PregenPhaseTracker;
|
||||
import art.arcane.iris.core.pregenerator.PregenRates;
|
||||
import art.arcane.volmlib.util.format.MemoryMonitor;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.CALLS_REAL_METHODS;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
|
||||
public class PregeneratorJobRenderSnapshotTest {
|
||||
@Test
|
||||
public void viewSnapshotPublishesAllRatesMemoryAndFailedCountsTogether() throws Exception {
|
||||
Fixture fixture = new Fixture();
|
||||
when(fixture.pregenerator.getFailedChunks()).thenReturn(2L);
|
||||
|
||||
fixture.tick();
|
||||
|
||||
PregenRenderSnapshot view = fixture.job.renderSnapshot();
|
||||
assertEquals(PregenRenderSnapshot.Phase.GENERATING, view.phase());
|
||||
assertEquals(12D, view.progress().chunksPerSecond(), 0D);
|
||||
assertEquals(7D, view.progress().overallChunksPerSecond(), 0D);
|
||||
assertEquals(18D, view.progress().thirtySecondChunksPerSecond(), 0D);
|
||||
assertEquals(24D, view.progress().sixtySecondChunksPerSecond(), 0D);
|
||||
assertEquals(2L, view.progress().failed());
|
||||
assertEquals(1234L, view.usedMemoryBytes());
|
||||
assertEquals(0.25D, view.memoryUsage(), 0D);
|
||||
assertEquals(256L, view.allocationBytesPerSecond());
|
||||
assertTrue(view.cached());
|
||||
assertSame(view, fixture.job.renderSnapshot());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stoppingAndFinishedPhasesCannotBeOverwrittenByTickOrSave() throws Exception {
|
||||
Fixture fixture = new Fixture();
|
||||
fixture.stopping.set(true);
|
||||
fixture.tick();
|
||||
assertEquals(PregenRenderSnapshot.Phase.STOPPING, fixture.job.renderSnapshot().phase());
|
||||
fixture.job.onSaving();
|
||||
assertEquals(PregenRenderSnapshot.Phase.STOPPING, fixture.job.renderSnapshot().phase());
|
||||
fixture.stopping.set(false);
|
||||
fixture.set("finished", true);
|
||||
fixture.tick();
|
||||
assertEquals(PregenRenderSnapshot.Phase.STOPPING, fixture.job.renderSnapshot().phase());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pausedAndErrorStateKeepTheirPrecedence() throws Exception {
|
||||
Fixture fixture = new Fixture();
|
||||
when(fixture.pregenerator.paused()).thenReturn(true);
|
||||
fixture.job.onSaving();
|
||||
assertEquals(PregenRenderSnapshot.Phase.PAUSED, fixture.job.renderSnapshot().phase());
|
||||
assertTrue(fixture.job.renderSnapshot().progress().paused());
|
||||
fixture.set("failure", "Preparation failed");
|
||||
fixture.tick();
|
||||
assertEquals(PregenRenderSnapshot.Phase.ERROR, fixture.job.renderSnapshot().phase());
|
||||
assertEquals("Preparation failed", fixture.job.renderSnapshot().failure());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void finishedWindowCannotPauseItsReplacementJob() throws Exception {
|
||||
Fixture previous = new Fixture();
|
||||
Fixture replacement = new Fixture();
|
||||
Runnable queuedPause = previous.job::togglePause;
|
||||
previous.set("finished", true);
|
||||
Field field = PregeneratorJob.class.getDeclaredField("instance");
|
||||
field.setAccessible(true);
|
||||
@SuppressWarnings("unchecked")
|
||||
AtomicReference<PregeneratorJob> instance = (AtomicReference<PregeneratorJob>) field.get(null);
|
||||
PregeneratorJob original = instance.getAndSet(replacement.job);
|
||||
try {
|
||||
queuedPause.run();
|
||||
verifyNoInteractions(previous.pregenerator, replacement.pregenerator);
|
||||
} finally {
|
||||
instance.set(original);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Fixture {
|
||||
private final PregeneratorJob job = mock(PregeneratorJob.class, CALLS_REAL_METHODS);
|
||||
private final IrisPregenerator pregenerator = mock(IrisPregenerator.class);
|
||||
private final AtomicBoolean stopping = new AtomicBoolean();
|
||||
|
||||
private Fixture() throws Exception {
|
||||
MemoryMonitor monitor = mock(MemoryMonitor.class);
|
||||
when(monitor.getUsedBytes()).thenReturn(1234L);
|
||||
when(monitor.getUsagePercent()).thenReturn(0.25D);
|
||||
when(monitor.getPressure()).thenReturn(256L);
|
||||
when(pregenerator.getRates()).thenReturn(new PregenRates(7D, 12D, 18D, 24D));
|
||||
set("bounds", new PregenRenderSnapshot.Bounds(-5, 2, 8, 12));
|
||||
set("pregenerator", pregenerator);
|
||||
set("monitor", monitor);
|
||||
set("stopRequested", stopping);
|
||||
set("saving", new AtomicBoolean());
|
||||
set("onProgress", List.of());
|
||||
set("apiPhases", new PregenPhaseTracker());
|
||||
}
|
||||
|
||||
private void tick() {
|
||||
job.onTick(12D, 720D, 1D, 0.5D, 5L, 10L, 3L, 1000L, 2500L, "Async", true);
|
||||
}
|
||||
|
||||
private void set(String name, Object value) throws Exception {
|
||||
Field field = PregeneratorJob.class.getDeclaredField(name);
|
||||
field.setAccessible(true);
|
||||
field.set(job, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user