From b7db58461fb4334d07afc91c7f6803528cf461a1 Mon Sep 17 00:00:00 2001 From: "Blue (Lukas Rieger)" Date: Tue, 21 Jan 2020 21:05:41 +0100 Subject: [PATCH] Save state during render with CLI and restart unfinished renders --- .../bluecolored/bluemap/cli/BlueMapCLI.java | 186 +++++++++++------- .../bluemap/common/RenderManager.java | 12 +- 2 files changed, 124 insertions(+), 74 deletions(-) diff --git a/BlueMapCLI/src/main/java/de/bluecolored/bluemap/cli/BlueMapCLI.java b/BlueMapCLI/src/main/java/de/bluecolored/bluemap/cli/BlueMapCLI.java index e3ec4b6a..4f03cfc9 100644 --- a/BlueMapCLI/src/main/java/de/bluecolored/bluemap/cli/BlueMapCLI.java +++ b/BlueMapCLI/src/main/java/de/bluecolored/bluemap/cli/BlueMapCLI.java @@ -24,8 +24,14 @@ */ package de.bluecolored.bluemap.cli; +import java.io.DataInputStream; +import java.io.DataOutputStream; import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; @@ -36,6 +42,8 @@ import java.util.UUID; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.TimeUnit; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; @@ -152,86 +160,118 @@ public void renderMaps() throws IOException { resourcePack.saveTextureFile(textureExportFile); RenderManager renderManager = new RenderManager(config.getRenderThreadCount()); + File rmstate = new File(configFolder, "rmstate"); + + if (rmstate.exists()) { + try ( + InputStream in = new GZIPInputStream(new FileInputStream(rmstate)); + DataInputStream din = new DataInputStream(in); + ){ + renderManager.readState(din, maps.values()); + Logger.global.logInfo("Found unfinished render, continuing ... (If you want to start a new render, delete the this file: " + rmstate.getCanonicalPath()); + } catch (IOException ex) { + Logger.global.logError("Failed to read saved render-state! Remove the file " + rmstate.getCanonicalPath() + " to start a new render.", ex); + } + } else { + for (MapType map : maps.values()) { + Logger.global.logInfo("Creating render-task for map '" + map.getId() + "' ..."); + Logger.global.logInfo("Collecting tiles ..."); + + Collection chunks; + if (!forceRender) { + long lastRender = webSettings.getLong(map.getId(), "last-render"); + chunks = map.getWorld().getChunkList(lastRender); + } else { + chunks = map.getWorld().getChunkList(); + } + + HiresModelManager hiresModelManager = map.getTileRenderer().getHiresModelManager(); + Collection tiles = hiresModelManager.getTilesForChunks(chunks); + Logger.global.logInfo("Found " + tiles.size() + " tiles to render! (" + chunks.size() + " chunks)"); + if (!forceRender && chunks.size() == 0) { + Logger.global.logInfo("(This is normal if nothing has changed in the world since the last render. Use -f on the command-line to force a render of all chunks)"); + } + + if (tiles.isEmpty()) { + continue; + } + + RenderTask task = new RenderTask(map.getName(), map); + task.addTiles(tiles); + task.optimizeQueue(); + + renderManager.addRenderTask(task); + } + } + + Logger.global.logInfo("Starting render ..."); renderManager.start(); - for (MapType map : maps.values()) { - Logger.global.logInfo("Rendering map '" + map.getId() + "' ..."); - Logger.global.logInfo("Collecting tiles to render..."); - - Collection chunks; - if (!forceRender) { - long lastRender = webSettings.getLong(map.getId(), "last-render"); - chunks = map.getWorld().getChunkList(lastRender); - } else { - chunks = map.getWorld().getChunkList(); - } - - HiresModelManager hiresModelManager = map.getTileRenderer().getHiresModelManager(); - Collection tiles = hiresModelManager.getTilesForChunks(chunks); - Logger.global.logInfo("Found " + tiles.size() + " tiles to render! (" + chunks.size() + " chunks)"); - if (!forceRender && chunks.size() == 0) { - Logger.global.logInfo("(This is normal if nothing has changed in the world since the last render. Use -f on the command-line to force a render of all chunks)"); - } - - if (tiles.isEmpty()) { - continue; - } + long startTime = System.currentTimeMillis(); - Logger.global.logInfo("Starting Render..."); - long starttime = System.currentTimeMillis(); - - RenderTask task = new RenderTask("Map-Render: " + map.getName(), map); - task.addTiles(tiles); - task.optimizeQueue(); - - renderManager.addRenderTask(task); - - long lastLogUpdate = System.currentTimeMillis(); - long lastSave = lastLogUpdate; - - while(!task.isFinished()) { - try { - Thread.sleep(200); - } catch (InterruptedException e) {} - - long now = System.currentTimeMillis(); - - if (lastLogUpdate < now - 10000) { // print update all 10 seconds - lastLogUpdate = now; - long time = task.getActiveTime(); - - String durationString = DurationFormatUtils.formatDurationWords(time, true, true); - int tileCount = task.getRemainingTileCount() + task.getRenderedTileCount(); - double pct = (double)task.getRenderedTileCount() / (double) tileCount; - - long ert = (long)((time / pct) * (1d - pct)); - String ertDurationString = DurationFormatUtils.formatDurationWords(ert, true, true); - - double tps = task.getRenderedTileCount() / (time / 1000.0); - - Logger.global.logInfo("Rendered " + task.getRenderedTileCount() + " of " + tileCount + " tiles in " + durationString + " | " + GenericMath.round(tps, 3) + " tiles/s"); - Logger.global.logInfo(GenericMath.round(pct * 100, 3) + "% | Estimated remaining time: " + ertDurationString); - } - - if (lastSave < now - 5 * 60000) { // save every 5 minutes - lastSave = now; - map.getTileRenderer().save(); - } - } - - map.getTileRenderer().save(); - + long lastLogUpdate = startTime; + long lastSave = startTime; + + while(renderManager.getRenderTaskCount() != 0) { try { - webSettings.set(starttime, map.getId(), "last-render"); - webSettings.save(); - } catch (IOException e) { - Logger.global.logError("Failed to update web-settings!", e); + Thread.sleep(200); + } catch (InterruptedException e) {} + + + long now = System.currentTimeMillis(); + + if (lastLogUpdate < now - 10000) { // print update all 10 seconds + RenderTask currentTask = renderManager.getCurrentRenderTask(); + lastLogUpdate = now; + long time = currentTask.getActiveTime(); + + String durationString = DurationFormatUtils.formatDurationWords(time, true, true); + int tileCount = currentTask.getRemainingTileCount() + currentTask.getRenderedTileCount(); + double pct = (double)currentTask.getRenderedTileCount() / (double) tileCount; + + long ert = (long)((time / pct) * (1d - pct)); + String ertDurationString = DurationFormatUtils.formatDurationWords(ert, true, true); + + double tps = currentTask.getRenderedTileCount() / (time / 1000.0); + + Logger.global.logInfo("Rendering map '" + currentTask.getName() + "':"); + Logger.global.logInfo("Rendered " + currentTask.getRenderedTileCount() + " of " + tileCount + " tiles in " + durationString + " | " + GenericMath.round(tps, 3) + " tiles/s"); + Logger.global.logInfo(GenericMath.round(pct * 100, 3) + "% | Estimated remaining time: " + ertDurationString); + } + + if (lastSave < now - 1 * 60000) { // save every minute + RenderTask currentTask = renderManager.getCurrentRenderTask(); + + lastSave = now; + currentTask.getMapType().getTileRenderer().save(); + + try ( + OutputStream os = new GZIPOutputStream(new FileOutputStream(rmstate)); + DataOutputStream dos = new DataOutputStream(os); + ){ + renderManager.writeState(dos); + } catch (IOException ex) { + Logger.global.logError("Failed to save render-state!", ex); + } } } renderManager.stop(); - Logger.global.logInfo("Waiting for all threads to quit..."); + //render finished, so remove render state file + rmstate.delete(); + + for (MapType map : maps.values()) { + webSettings.set(startTime, map.getId(), "last-render"); + } + + try { + webSettings.save(); + } catch (IOException e) { + Logger.global.logError("Failed to update web-settings!", e); + } + + Logger.global.logInfo("Waiting for all threads to quit ..."); if (!ForkJoinPool.commonPool().awaitQuiescence(30, TimeUnit.SECONDS)) { Logger.global.logWarning("Some save-threads are taking very long to exit (>30s), they will be ignored."); } @@ -240,14 +280,14 @@ public void renderMaps() throws IOException { } public void startWebserver() throws IOException { - Logger.global.logInfo("Starting webserver..."); + Logger.global.logInfo("Starting webserver ..."); BlueMapWebServer webserver = new BlueMapWebServer(configManager.getMainConfig()); webserver.start(); } private boolean loadResources() throws IOException, ParseResourceException { - Logger.global.logInfo("Loading resources..."); + Logger.global.logInfo("Loading resources ..."); MainConfig config = configManager.getMainConfig(); diff --git a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/RenderManager.java b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/RenderManager.java index 402fb16d..ac5f12e9 100644 --- a/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/RenderManager.java +++ b/BlueMapCommon/src/main/java/de/bluecolored/bluemap/common/RenderManager.java @@ -157,7 +157,17 @@ public int getQueueSize() { * Returns a copy of the deque with the render tasks in order as array */ public RenderTask[] getRenderTasks(){ - return renderTasks.toArray(new RenderTask[renderTasks.size()]); + synchronized (renderTasks) { + return renderTasks.toArray(new RenderTask[renderTasks.size()]); + } + } + + public int getRenderTaskCount(){ + return renderTasks.size(); + } + + public RenderTask getCurrentRenderTask() { + return renderTasks.peek(); } public boolean isRunning() {