From 6f3fe7a39853555e11880bc811de35461d2520fc Mon Sep 17 00:00:00 2001 From: Maik Marschner Date: Fri, 27 Sep 2024 21:19:32 +0200 Subject: [PATCH] Show time elapsed for all tasks and use Duration for the duration and eta. (#1771) * Show time elapsed for all tasks and use Duration for the duration and eta. * Fix tests. * Rename duration and eta to elapsedTime and remainingTime and fix ETA in the console progress listener. --- .../renderer/ConsoleProgressListener.java | 8 +- .../chunky/renderer/DefaultRenderManager.java | 15 +-- .../chunky/renderer/PreviewRenderer.java | 2 +- .../ui/controller/ChunkyFxController.java | 11 ++- .../java/se/llbit/util/ProgressListener.java | 10 +- .../src/java/se/llbit/util/TaskTracker.java | 93 ++++++++++++------- .../renderer/renderdump/RenderDumpTest.java | 3 +- 7 files changed, 85 insertions(+), 57 deletions(-) diff --git a/chunky/src/java/se/llbit/chunky/renderer/ConsoleProgressListener.java b/chunky/src/java/se/llbit/chunky/renderer/ConsoleProgressListener.java index 561cfbec85..6c0a80c63d 100644 --- a/chunky/src/java/se/llbit/chunky/renderer/ConsoleProgressListener.java +++ b/chunky/src/java/se/llbit/chunky/renderer/ConsoleProgressListener.java @@ -19,6 +19,7 @@ import se.llbit.util.ProgressListener; import java.text.DecimalFormat; +import java.time.Duration; /** * Prints progress to standard out. @@ -35,16 +36,17 @@ public ConsoleProgressListener() { decimalFormat.setGroupingUsed(true); } - @Override public void setProgress(String task, int done, int start, int target) { + @Override public void setProgress(String task, int done, int start, int target, Duration elapsedTime) { String line = String.format("%s: %.1f%% (%s of %s)", task, 100 * done / (float) target, decimalFormat.format(done), decimalFormat.format(target)); output(line, done == target); } - @Override public void setProgress(String task, int done, int start, int target, String eta) { + @Override public void setProgress(String task, int done, int start, int target, Duration elapsedTime, Duration remainingTime) { output(String.format("%s: %.1f%% (%s of %s) [ETA=%s]", task, 100 * done / (float) target, - decimalFormat.format(done), decimalFormat.format(target), eta), + decimalFormat.format(done), decimalFormat.format(target), + String.format("%d:%02d:%02d", remainingTime.toHours(), remainingTime.toMinutesPart(), remainingTime.toSecondsPart())), done == target); } diff --git a/chunky/src/java/se/llbit/chunky/renderer/DefaultRenderManager.java b/chunky/src/java/se/llbit/chunky/renderer/DefaultRenderManager.java index b46fa19208..a6d6711dfa 100644 --- a/chunky/src/java/se/llbit/chunky/renderer/DefaultRenderManager.java +++ b/chunky/src/java/se/llbit/chunky/renderer/DefaultRenderManager.java @@ -30,6 +30,7 @@ import se.llbit.math.ColorUtil; import se.llbit.util.TaskTracker; +import java.time.Duration; import java.util.*; import java.util.function.BiConsumer; import java.util.function.BooleanSupplier; @@ -385,19 +386,13 @@ private int samplesPerSecond() { } private void updateRenderProgress() { - double renderTime = bufferedScene.renderTime / 1000.0; - // Notify progress listener. int target = bufferedScene.getTargetSpp(); - long etaSeconds = (long) (((target - bufferedScene.spp) * renderTime) / bufferedScene.spp); - if (etaSeconds > 0) { - int seconds = (int) ((etaSeconds) % 60); - int minutes = (int) ((etaSeconds / 60) % 60); - int hours = (int) (etaSeconds / 3600); - String eta = String.format("%d:%02d:%02d", hours, minutes, seconds); - renderTask.update("Rendering", target, bufferedScene.spp, eta); + long etaMillis = (long) (((target - bufferedScene.spp) * bufferedScene.renderTime) / bufferedScene.spp); + if (etaMillis > 0) { + renderTask.update("Rendering", target, bufferedScene.spp, Duration.ofMillis(etaMillis)); } else { - renderTask.update("Rendering", target, bufferedScene.spp, ""); + renderTask.update("Rendering", target, bufferedScene.spp); } synchronized (this) { diff --git a/chunky/src/java/se/llbit/chunky/renderer/PreviewRenderer.java b/chunky/src/java/se/llbit/chunky/renderer/PreviewRenderer.java index 5c8eb6d4e1..94355daa95 100644 --- a/chunky/src/java/se/llbit/chunky/renderer/PreviewRenderer.java +++ b/chunky/src/java/se/llbit/chunky/renderer/PreviewRenderer.java @@ -54,7 +54,7 @@ public String getDescription() { @Override public void render(DefaultRenderManager manager) throws InterruptedException { TaskTracker.Task task = manager.getRenderTask(); - task.update("Preparing preview", 2, 0, ""); + task.update("Preparing preview", 2, 0); Scene scene = manager.bufferedScene; diff --git a/chunky/src/java/se/llbit/chunky/ui/controller/ChunkyFxController.java b/chunky/src/java/se/llbit/chunky/ui/controller/ChunkyFxController.java index 5e1506390a..7fd253d564 100644 --- a/chunky/src/java/se/llbit/chunky/ui/controller/ChunkyFxController.java +++ b/chunky/src/java/se/llbit/chunky/ui/controller/ChunkyFxController.java @@ -22,6 +22,7 @@ import java.net.URL; import java.nio.file.Path; import java.text.DecimalFormat; +import java.time.Duration; import java.util.ArrayList; import java.util.Collection; import java.util.IdentityHashMap; @@ -63,7 +64,6 @@ import se.llbit.chunky.map.WorldMapLoader; import se.llbit.chunky.renderer.*; import se.llbit.chunky.renderer.export.PictureExportFormats; -import se.llbit.chunky.renderer.RenderManager; import se.llbit.chunky.renderer.export.PictureExportFormat; import se.llbit.chunky.ui.ChunkMap; import se.llbit.chunky.ui.dialogs.*; @@ -73,7 +73,6 @@ import se.llbit.chunky.ui.ProgressTracker; import se.llbit.chunky.ui.RenderCanvasFx; import se.llbit.chunky.ui.UILogReceiver; -import se.llbit.chunky.ui.dialogs.WorldChooser; import se.llbit.chunky.renderer.scene.*; import se.llbit.chunky.world.ChunkSelectionTracker; import se.llbit.chunky.world.ChunkView; @@ -161,7 +160,7 @@ public class ChunkyFxController } private final ProgressListener progressListener = new ProgressListener() { - @Override public void setProgress(String task, int done, int start, int target) { + @Override public void setProgress(String task, int done, int start, int target, Duration elapsedTime) { Platform.runLater(() -> { progressBar.setProgress((double) done / (target - start)); if (target - start > 0) { @@ -172,10 +171,11 @@ public class ChunkyFxController progressLbl.setTooltip(null); } etaLbl.setText("ETA: N/A"); + renderTimeLbl.setText(String.format("Time: %d:%02d:%02d", elapsedTime.toHours(), elapsedTime.toMinutesPart(), elapsedTime.toSecondsPart())); }); } - @Override public void setProgress(String task, int done, int start, int target, String eta) { + @Override public void setProgress(String task, int done, int start, int target, Duration elapsedTime, Duration remainingTime) { Platform.runLater(() -> { progressBar.setProgress((double) done / (target - start)); if (target - start > 0) { @@ -185,7 +185,8 @@ public class ChunkyFxController progressLbl.setText(String.format("%s", task)); progressLbl.setTooltip(null); } - etaLbl.setText("ETA: " + eta); + etaLbl.setText(String.format("ETA: %d:%02d:%02d", remainingTime.toHours(), remainingTime.toMinutesPart(), remainingTime.toSecondsPart())); + renderTimeLbl.setText(String.format("Time: %d:%02d:%02d", elapsedTime.toHours(), elapsedTime.toMinutesPart(), elapsedTime.toSecondsPart())); }); } }; diff --git a/chunky/src/java/se/llbit/util/ProgressListener.java b/chunky/src/java/se/llbit/util/ProgressListener.java index 34eb40120f..9d487c9614 100644 --- a/chunky/src/java/se/llbit/util/ProgressListener.java +++ b/chunky/src/java/se/llbit/util/ProgressListener.java @@ -17,23 +17,25 @@ */ package se.llbit.util; +import java.time.Duration; + /** * Progress listener. * * @author Jesper Öqvist */ public interface ProgressListener { - ProgressListener NONE = (task, done, start, target) -> {}; + ProgressListener NONE = (task, done, start, target, duration) -> {}; /** * Update progress without ETA. */ - void setProgress(String task, int done, int start, int target); + void setProgress(String task, int done, int start, int target, Duration elapsedTime); /** * Update progress with ETA. */ - default void setProgress(String task, int done, int start, int target, String eta) { - setProgress(task, done, start, target); + default void setProgress(String task, int done, int start, int target, Duration elapsedTime, Duration remainingTime) { + setProgress(task, done, start, target, elapsedTime); } } diff --git a/chunky/src/java/se/llbit/util/TaskTracker.java b/chunky/src/java/se/llbit/util/TaskTracker.java index bc1b408833..4498647433 100644 --- a/chunky/src/java/se/llbit/util/TaskTracker.java +++ b/chunky/src/java/se/llbit/util/TaskTracker.java @@ -20,6 +20,9 @@ import se.llbit.log.Log; +import java.time.Duration; +import java.util.Optional; + /** * A task tracker is used to update a progress listener with current task progress. * The task tracker has a stack of tasks. When a new task is created the previous @@ -57,7 +60,7 @@ public TaskTracker(ProgressListener progress, TaskBuilder taskBuilder) { } public TaskTracker(ProgressListener progress, TaskBuilder taskBuilder, - TaskBuilder backgroundTaskBuilder) { + TaskBuilder backgroundTaskBuilder) { this.progress = progress; this.taskBuilder = taskBuilder; this.backgroundTask = backgroundTaskBuilder.newTask(this, null, "N/A", 1); @@ -66,9 +69,17 @@ public TaskTracker(ProgressListener progress, TaskBuilder taskBuilder, public static class Task implements AutoCloseable { public static final Task NONE = new Task(null, null, "None", 1) { - @Override protected void update() { } - @Override protected void updateEta() { } - @Override public void close() { } + @Override + protected void update() { + } + + @Override + protected void updateEta() { + } + + @Override + public void close() { + } }; private String taskName; @@ -76,7 +87,7 @@ public static class Task implements AutoCloseable { private int done; protected final TaskTracker tracker; protected final Task previous; - private String eta = ""; + private Optional eta = Optional.empty(); protected long startTime; public Task(TaskTracker tracker, Task previous, String taskName, int size) { @@ -88,7 +99,8 @@ public Task(TaskTracker tracker, Task previous, String taskName, int size) { this.startTime = System.currentTimeMillis(); } - @Override public void close() { + @Override + public void close() { tracker.currentTask = previous; previous.update(); Log.infof("Task %s: %d in %.3f seconds", taskName, done, @@ -96,77 +108,92 @@ public Task(TaskTracker tracker, Task previous, String taskName, int size) { } protected void update() { - tracker.updateProgress(taskName, target, done, eta); + eta.ifPresentOrElse( + eta -> tracker.updateProgress(taskName, target, done, Duration.ofMillis(System.currentTimeMillis() - startTime), eta), + () -> tracker.updateProgress(taskName, target, done, Duration.ofMillis(System.currentTimeMillis() - startTime))); } - /** Change the task name. */ + /** + * Change the task name. + */ public void update(String task) { this.taskName = task; update(); } - /** Set the current progress. */ + /** + * Set the current progress. + */ public void update(int done) { this.done = done; update(); } - /** Set the current progress. */ + /** + * Set the current progress. + */ public void update(int target, int done) { this.done = done; this.target = target; update(); } - /** Changes the task name and state. */ + /** + * Changes the task name and state. + */ public void update(String task, int target, int done) { - update(task, target, done, ""); + update(task, target, done, null); } - /** Changes the task name and state. */ - public void update(String task, int target, int done, String eta) { + /** + * Changes the task name and state. + */ + public void update(String task, int target, int done, Duration eta) { this.taskName = task; this.done = done; this.target = target; - this.eta = eta; + this.eta = Optional.ofNullable(eta); update(); } protected void updateEta() { long etaSeconds = 0; - if (done > 0) { + if (done > 0 && done <= target) { etaSeconds = ((target - done) * (System.currentTimeMillis() - startTime) / 1000) / done; - } - if (etaSeconds > 0) { - int seconds = (int) ((etaSeconds) % 60); - int minutes = (int) ((etaSeconds / 60) % 60); - int hours = (int) (etaSeconds / 3600); - eta = String.format("%d:%02d:%02d", hours, minutes, seconds); + eta = Optional.of(Duration.ofSeconds(etaSeconds)); } else { - eta = "N/A"; + eta = Optional.empty(); } update(); } - /** Set the current progress and calculate an ETA. */ + /** + * Set the current progress and calculate an ETA. + */ public void updateEta(int done) { this.done = done; updateEta(); } - /** Set the current progress and calculate an ETA. */ + /** + * Set the current progress and calculate an ETA. + */ public void updateEta(int target, int done) { this.done = done; this.target = target; updateEta(); } - /** Reset the ETA start time. */ + /** + * Reset the ETA start time. + */ public void updateStartTime() { this.startTime = System.currentTimeMillis(); } - /** Ratelimited update. Only update when the new progress is greater than the old progress + {@code interval} */ + /** + * Ratelimited update. Only update when the new progress is greater than the old progress + {@code interval} + */ public void updateInterval(int target, int done, int interval) { if (target != this.target || done > this.done + interval) { this.updateEta(target, done); @@ -180,12 +207,12 @@ public void updateInterval(int done, int interval) { } } - private void updateProgress(String taskName, int target, int done, String eta) { - if (!eta.isEmpty()) { - progress.setProgress(taskName, done, 0, target, eta); - } else { - progress.setProgress(taskName, done, 0, target); - } + private void updateProgress(String taskName, int target, int done, Duration duration, Duration eta) { + progress.setProgress(taskName, done, 0, target, duration, eta); + } + + private void updateProgress(String taskName, int target, int done, Duration duration) { + progress.setProgress(taskName, done, 0, target, duration); } public final Task task(String taskName) { diff --git a/chunky/src/test/se/llbit/chunky/renderer/renderdump/RenderDumpTest.java b/chunky/src/test/se/llbit/chunky/renderer/renderdump/RenderDumpTest.java index 3c6bd0cd32..97204b80a5 100644 --- a/chunky/src/test/se/llbit/chunky/renderer/renderdump/RenderDumpTest.java +++ b/chunky/src/test/se/llbit/chunky/renderer/renderdump/RenderDumpTest.java @@ -26,6 +26,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.time.Duration; import java.util.Arrays; import java.util.Base64; import java.util.HashMap; @@ -70,7 +71,7 @@ public void init() { final Map previousProgress = new HashMap<>(); @Override - public void setProgress(String task, int done, int start, int target) { + public void setProgress(String task, int done, int start, int target, Duration elapsedTime) { int previous = previousProgress.getOrDefault(task, Integer.MIN_VALUE); // check that progress is monotonically increasing assertTrue(done >= previous, "progress (" + done + ") should be greater or equal to previous progress (" + previous + ")");