Skip to content

Commit

Permalink
Show time elapsed for all tasks and use Duration for the duration and…
Browse files Browse the repository at this point in the history
… 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.
  • Loading branch information
leMaik authored Sep 27, 2024
1 parent cab6b0a commit 6f3fe7a
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import se.llbit.util.ProgressListener;

import java.text.DecimalFormat;
import java.time.Duration;

/**
* Prints progress to standard out.
Expand All @@ -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);
}

Expand Down
15 changes: 5 additions & 10 deletions chunky/src/java/se/llbit/chunky/renderer/DefaultRenderManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.*;
Expand All @@ -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;
Expand Down Expand Up @@ -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) {
Expand All @@ -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) {
Expand All @@ -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()));
});
}
};
Expand Down
10 changes: 6 additions & 4 deletions chunky/src/java/se/llbit/util/ProgressListener.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,25 @@
*/
package se.llbit.util;

import java.time.Duration;

/**
* Progress listener.
*
* @author Jesper Öqvist <[email protected]>
*/
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);
}
}
93 changes: 60 additions & 33 deletions chunky/src/java/se/llbit/util/TaskTracker.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand All @@ -66,17 +69,25 @@ 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;
private int target;
private int done;
protected final TaskTracker tracker;
protected final Task previous;
private String eta = "";
private Optional<Duration> eta = Optional.empty();
protected long startTime;

public Task(TaskTracker tracker, Task previous, String taskName, int size) {
Expand All @@ -88,85 +99,101 @@ 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,
(System.currentTimeMillis() - startTime) / 1000.0);
}

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);
Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -70,7 +71,7 @@ public void init() {
final Map<String, Integer> 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 + ")");
Expand Down

0 comments on commit 6f3fe7a

Please sign in to comment.