From df3b9a049664a6a3f83e9de2f30e86fd02df2bce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81d=C3=A1m=20B=C3=A1nkeszi?= Date: Fri, 21 Apr 2017 01:53:52 +0200 Subject: [PATCH] Huge performance improvement for the rendering. Instead of reopening and closing files for every single block, there is now a BlockCsvWriterHolder class that holds the PrintWriter instances and servers them to the Boxel as needed. These writers are then closed at once when the rendering has completed the creation of blocks. --- .../rendering/control/WorldBuilder.java | 4 ++ .../rendering/model/primitive/Boxel.java | 11 +--- .../rendering/util/BlockCsvWriterHolder.java | 66 +++++++++++++++++++ 3 files changed, 73 insertions(+), 8 deletions(-) create mode 100644 sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/util/BlockCsvWriterHolder.java diff --git a/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/WorldBuilder.java b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/WorldBuilder.java index c8b174c2..a854d19f 100644 --- a/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/WorldBuilder.java +++ b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/control/WorldBuilder.java @@ -21,6 +21,7 @@ import codemetropolis.toolchain.rendering.exceptions.TooLongRenderDurationException; import codemetropolis.toolchain.rendering.model.building.*; import codemetropolis.toolchain.rendering.model.primitive.Boxel; +import codemetropolis.toolchain.rendering.util.BlockCsvWriterHolder; public class WorldBuilder { @@ -98,6 +99,9 @@ public void createBlocks(File directory, int maxTime) throws TooLongRenderDurati raiseProgressEvent(BuildPhase.GENERATING_BLOCKS, count, total, timeElapsed); } stopWatch.stop(); + + // Close all PrintWriters after every single block has been written + BlockCsvWriterHolder.closeWriters(); } public void build(File sourceDirectory) throws RenderingException { diff --git a/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/model/primitive/Boxel.java b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/model/primitive/Boxel.java index 931fff2c..84cb7acd 100644 --- a/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/model/primitive/Boxel.java +++ b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/model/primitive/Boxel.java @@ -1,15 +1,13 @@ package codemetropolis.toolchain.rendering.model.primitive; -import java.io.BufferedWriter; import java.io.File; import java.io.FileNotFoundException; -import java.io.FileWriter; -import java.io.IOException; import java.io.PrintWriter; import codemetropolis.blockmodifier.World; import codemetropolis.toolchain.commons.cmxml.Point; import codemetropolis.toolchain.rendering.model.BasicBlock; +import codemetropolis.toolchain.rendering.util.BlockCsvWriterHolder; public class Boxel implements Primitive { @@ -71,17 +69,14 @@ public int toCSVFile(File directory) { int x = position.getX() >> 9; int z = position.getZ() >> 9; - directory.mkdirs(); String filename = String.format("blocks.%d.%d.csv", x, z); - File file = new File(directory, filename); - try(PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(file, true)))) { + try { + PrintWriter writer = BlockCsvWriterHolder.requestFile(directory, filename); String csv = toCSV(); if(csv != null) writer.println(csv); } catch (FileNotFoundException e) { e.printStackTrace(); - } catch (IOException e1) { - e1.printStackTrace(); } return 1; } diff --git a/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/util/BlockCsvWriterHolder.java b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/util/BlockCsvWriterHolder.java new file mode 100644 index 00000000..7c2f9c3e --- /dev/null +++ b/sources/codemetropolis-toolchain-rendering/src/main/java/codemetropolis/toolchain/rendering/util/BlockCsvWriterHolder.java @@ -0,0 +1,66 @@ +package codemetropolis.toolchain.rendering.util; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.PrintWriter; +import java.util.HashMap; +import java.util.Map; + +/** + * This class is used to hold PrintWriter instances for the rendering. Previously, these files were (re-)opened and + * closed for every single {@link codemetropolis.toolchain.rendering.model.primitive.Boxel}. Instead of that, it now + * holds all of the references for each file, and serves them to the Boxel for writing. When all Boxels have been + * written, all these files can be flushed and closed at once. + * + * @author Adam Bankeszi {@literal } {@literal } + */ +public class BlockCsvWriterHolder { + + /** Holds the PrintWriter references for the blocks csv files. */ + private static Map writers = new HashMap(); + + /** + * Requests a {@link PrintWriter} for the given filename. On the first request it attempts to create the file and + * stores the associated {@link PrintWriter} reference. When the same file is requested a second time, it simply + * returns the already open {@link PrintWriter}. + * + * @param directory The directory in which these files shall be stored. + * @param filename The requested file's name. + * @return The {@link PrintWriter} for the requested file. + * @throws FileNotFoundException if it fails to create the file (or the parent directory). See + * {@link #createAndStoreWriter(File, String)} for more information. + */ + public static PrintWriter requestFile(File directory, String filename) throws FileNotFoundException { + if (writers.containsKey(filename)) { + return writers.get(filename); + } else { + return createAndStoreWriter(directory, filename); + } + } + + /** + * Closes all {@link PrintWriter} instances and clears the storage. + */ + public static void closeWriters() { + writers.values().forEach(writer -> writer.close()); + writers.clear(); + } + + /** + * Creates a new {@link PrintWriter} instance for the specific filename and adds it to the storage. + * + * @param directory The directory in which the file should be created. + * @param filename The filename that should be used. + * @return The new {@link PrintWriter} instance for the newly created file. + * @throws FileNotFoundException if it fails to create the file (or the parent directory). Its most likely due to + * insufficent user permissions for the given path or insufficent drive space. + */ + private static PrintWriter createAndStoreWriter(File directory, String filename) throws FileNotFoundException { + directory.mkdirs(); + File file = new File(directory, filename); + PrintWriter writer = new PrintWriter(file); + writers.put(filename, writer); + return writer; + } + +}