Skip to content

Commit

Permalink
Fix loading scenes with one render thread, throttle progress updates …
Browse files Browse the repository at this point in the history
…during chunk loading (#831)

* Ratelimit for javafx reload when loading chunks.

* Fixed bug where you couldn't load scenes with 1 render Thread. Adjusted rate limited repaint to schedule a thread to run later.
  • Loading branch information
ThatRedox committed Feb 25, 2021
1 parent bcef6eb commit d473618
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 15 deletions.
2 changes: 1 addition & 1 deletion chunky/src/java/se/llbit/chunky/main/Chunky.java
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ public void update() {
}

/**
* Get the common thread pool.
* Get the common thread pool. This should only be used for parallelized processing, not for wait tasks.
*/
public static ForkJoinPool getCommonThreads() {
if (commonThreads == null) {
Expand Down
56 changes: 42 additions & 14 deletions chunky/src/java/se/llbit/chunky/ui/ChunkMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,19 @@

import java.io.File;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
* UI component for the 2D world map.
*
* @author Jesper Öqvist <jesper@llbit.se>
*/
public class ChunkMap implements ChunkUpdateListener, ChunkViewListener, CameraViewListener {
/** Minimum time between JavaFX draws due to chunk updates. */
private final long MAX_CHUNK_UPDATE_RATE = 1000/3;

/** Controls the selection area when selecting visible chunks. */
private static final double CHUNK_SELECT_RADIUS = -8 * 1.4142;
protected final WorldMapLoader mapLoader;
Expand Down Expand Up @@ -101,10 +106,11 @@ public class ChunkMap implements ChunkUpdateListener, ChunkViewListener, CameraV
private final Canvas canvas;
private final Canvas mapOverlay;

volatile boolean repaintQueued = false;
private volatile boolean repaintQueued = false;
private volatile boolean scheduledUpdate = false;
private volatile long lastRedraw = 0;
private Runnable onViewDragged = () -> {};

private AtomicBoolean scheduledUpdate = new AtomicBoolean(false);
private ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();

public ChunkMap(WorldMapLoader loader, ChunkyFxController controller,
MapView mapView, ChunkSelectionTracker chunkSelection,
Expand Down Expand Up @@ -164,10 +170,10 @@ public ChunkMap(WorldMapLoader loader, ChunkyFxController controller,
@Override public void chunkUpdated(ChunkPosition chunk) {
if (view.chunkScale >= 16) {
mapBuffer.drawTile(mapLoader, chunk, chunkSelection);
repaintRatelimited();
} else {
regionUpdated(chunk.getRegionPosition());
}
repaintDeferred();
}

protected final void repaintDirect() {
Expand All @@ -186,6 +192,28 @@ protected final void repaintDeferred() {
}
}

protected final void repaintRatelimited() {
if (lastRedraw == -1) {
return;
}

long delay = (lastRedraw + MAX_CHUNK_UPDATE_RATE) - System.currentTimeMillis();
if (delay > 0) {

// Prevent redraw from occurring until this is done.
lastRedraw = -1;

executor.schedule(() -> {
lastRedraw = System.currentTimeMillis();
repaintDeferred();
}, delay, TimeUnit.MILLISECONDS);
} else {
// No need to be ratelimited, redraw now
lastRedraw = System.currentTimeMillis();
repaintDeferred();
}
}

/**
* Draws a visualization of the 3D camera view on the 2D map.
*/
Expand All @@ -196,14 +224,14 @@ protected void drawViewBounds(Canvas canvas) {
// `withSceneProtected` will block for a long time when a new scene is loaded. This bocks in the JavaFX thread and
// freezes the user interface. Here we check if there has already been an update scheduled, and if not will schedule
// one. Draw view bounds must be run on the JavaFX thread.
if (!scheduledUpdate.get()) {
scheduledUpdate.set(true);
Chunky.getCommonThreads().submit(() -> controller.getChunky().getRenderController().getSceneProvider().withSceneProtected(
scene -> Platform.runLater(() -> {
gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
ChunkMap.drawViewBounds(gc, mapView, scene);
scheduledUpdate.set(false);
}
if (!scheduledUpdate) {
scheduledUpdate = true;
executor.submit(() -> controller.getChunky().getRenderController().getSceneProvider().withSceneProtected(
scene -> Platform.runLater(() -> {
gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
ChunkMap.drawViewBounds(gc, mapView, scene);
scheduledUpdate = false;
}
)));
}
}
Expand Down Expand Up @@ -297,7 +325,7 @@ public void renderView(File targetFile, ProgressTracker progress) {
if (view.scale < 16) {
mapBuffer.drawTile(mapLoader, region, chunkSelection);
mapLoader.regionUpdated(region);
repaintDeferred();
repaintRatelimited();
}
}

Expand Down

0 comments on commit d473618

Please sign in to comment.