From 8facfc6683429672d6ef96c14ce61e694c5f1657 Mon Sep 17 00:00:00 2001 From: nib9888 Date: Sun, 23 Oct 2022 16:44:55 +0100 Subject: [PATCH 1/6] Add a moving average for SPS Add a linked list to ChunkyFxController and update setSamplesPerSecond to use this to set the SPS value based on a moving average --- .../ui/controller/ChunkyFxController.java | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) 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 65f248c607..7712d850c0 100644 --- a/chunky/src/java/se/llbit/chunky/ui/controller/ChunkyFxController.java +++ b/chunky/src/java/se/llbit/chunky/ui/controller/ChunkyFxController.java @@ -193,7 +193,9 @@ public void showRenderPreview() { static class GUIRenderListener implements RenderStatusListener { private final ChunkyFxController gui; private int spp; - private int sps; + private LinkedList sps_queue = new LinkedList(); + private static final int AVERAGE_PERIOD = 150; + private int sps_average; public GUIRenderListener(ChunkyFxController renderControls) { this.gui = renderControls; @@ -210,7 +212,24 @@ public GUIRenderListener(ChunkyFxController renderControls) { } @Override public void setSamplesPerSecond(int sps) { - this.sps = sps; + // Rolling average of SPS + int avg; + if (sps_queue.size() >= AVERAGE_PERIOD) { + avg = sps_average; + // Probably not needed, but just in case sps_queue is too long + for (int i = sps_queue.size(); i > AVERAGE_PERIOD; i--) { + sps_queue.removeLast(); + } + avg -= sps_queue.removeLast() / AVERAGE_PERIOD; + sps_queue.addFirst(sps); + avg += sps / AVERAGE_PERIOD; + } else { + avg = sps_average * sps_queue.size(); + avg += sps; + sps_queue.addFirst(sps); + avg /= sps_queue.size(); + } + sps_average = avg; updateSppStats(); } @@ -222,7 +241,7 @@ public GUIRenderListener(ChunkyFxController renderControls) { private void updateSppStats() { Platform.runLater(() -> gui.sppLbl.setText(String .format("%s SPP, %s SPS", gui.decimalFormat.format(spp), - gui.decimalFormat.format(sps)))); + gui.decimalFormat.format(sps_average)))); } @Override public void renderStateChanged(RenderMode state) { From 4e59cc186d0add1d6232e9d8e2d1bbff94a52976 Mon Sep 17 00:00:00 2001 From: nib9888 Date: Sun, 23 Oct 2022 19:11:34 +0100 Subject: [PATCH 2/6] Revert "Add a moving average for SPS" This reverts commit 8facfc6683429672d6ef96c14ce61e694c5f1657. --- .../ui/controller/ChunkyFxController.java | 25 +++---------------- 1 file changed, 3 insertions(+), 22 deletions(-) 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 7712d850c0..65f248c607 100644 --- a/chunky/src/java/se/llbit/chunky/ui/controller/ChunkyFxController.java +++ b/chunky/src/java/se/llbit/chunky/ui/controller/ChunkyFxController.java @@ -193,9 +193,7 @@ public void showRenderPreview() { static class GUIRenderListener implements RenderStatusListener { private final ChunkyFxController gui; private int spp; - private LinkedList sps_queue = new LinkedList(); - private static final int AVERAGE_PERIOD = 150; - private int sps_average; + private int sps; public GUIRenderListener(ChunkyFxController renderControls) { this.gui = renderControls; @@ -212,24 +210,7 @@ public GUIRenderListener(ChunkyFxController renderControls) { } @Override public void setSamplesPerSecond(int sps) { - // Rolling average of SPS - int avg; - if (sps_queue.size() >= AVERAGE_PERIOD) { - avg = sps_average; - // Probably not needed, but just in case sps_queue is too long - for (int i = sps_queue.size(); i > AVERAGE_PERIOD; i--) { - sps_queue.removeLast(); - } - avg -= sps_queue.removeLast() / AVERAGE_PERIOD; - sps_queue.addFirst(sps); - avg += sps / AVERAGE_PERIOD; - } else { - avg = sps_average * sps_queue.size(); - avg += sps; - sps_queue.addFirst(sps); - avg /= sps_queue.size(); - } - sps_average = avg; + this.sps = sps; updateSppStats(); } @@ -241,7 +222,7 @@ public GUIRenderListener(ChunkyFxController renderControls) { private void updateSppStats() { Platform.runLater(() -> gui.sppLbl.setText(String .format("%s SPP, %s SPS", gui.decimalFormat.format(spp), - gui.decimalFormat.format(sps_average)))); + gui.decimalFormat.format(sps)))); } @Override public void renderStateChanged(RenderMode state) { From d09db867a134e4b604212c58d8d727a7b42e6961 Mon Sep 17 00:00:00 2001 From: nib9888 Date: Sun, 23 Oct 2022 19:12:53 +0100 Subject: [PATCH 3/6] Add a moving average for SPS based on time Adds two linked lists that keep track of SPP for the past 3 minutes (configurable), and then use this to compute a moving average --- .../chunky/renderer/DefaultRenderManager.java | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/chunky/src/java/se/llbit/chunky/renderer/DefaultRenderManager.java b/chunky/src/java/se/llbit/chunky/renderer/DefaultRenderManager.java index 94ed65e9d7..7cf3737e95 100644 --- a/chunky/src/java/se/llbit/chunky/renderer/DefaultRenderManager.java +++ b/chunky/src/java/se/llbit/chunky/renderer/DefaultRenderManager.java @@ -288,6 +288,10 @@ public void run() { getRenderer().sceneReset(this, reason, resetCount); getPreviewRenderer().sceneReset(this, reason, resetCount); + // Reset sps rolling average + spp_history.clear(); + spp_history_times.clear(); + // Select the correct renderer Renderer render = mode == RenderMode.PREVIEW ? getPreviewRenderer() : getRenderer(); @@ -370,6 +374,10 @@ private void updateRenderState(Scene scene) { } } + private static final int SPS_AVERAGE_TIME = 180; + private LinkedList spp_history = new LinkedList(); + private LinkedList spp_history_times = new LinkedList(); + /** * @return the current rendering speed in samples per second (SPS) */ @@ -378,7 +386,11 @@ private int samplesPerSecond() { int canvasHeight = bufferedScene.canvasHeight(); long pixelsPerFrame = (long) canvasWidth * canvasHeight; double renderTime = bufferedScene.renderTime / 1000.0; - return (int) ((bufferedScene.spp * pixelsPerFrame) / renderTime); + if (renderTime < SPS_AVERAGE_TIME) { + return (int) ((bufferedScene.spp * pixelsPerFrame) / renderTime); + } else { + return (int) ((bufferedScene.spp - spp_history.get(0)) * pixelsPerFrame / SPS_AVERAGE_TIME); + } } private void updateRenderProgress() { @@ -398,6 +410,13 @@ private void updateRenderProgress() { } synchronized (this) { + // Update list of spp values + spp_history_times.addLast(renderTime); + spp_history.addLast(bufferedScene.spp); + while (spp_history_times.get(0) + SPS_AVERAGE_TIME < renderTime) { + spp_history_times.removeFirst(); + spp_history.removeFirst(); + } // Update render status display. renderStatusListeners.forEach(listener -> { listener.setRenderTime(bufferedScene.renderTime); From 989515570558374658e51cdadbd1ba1b4efad813 Mon Sep 17 00:00:00 2001 From: nib9888 Date: Sun, 23 Oct 2022 20:12:55 +0100 Subject: [PATCH 4/6] Fix SPS rolling average for long render passes Add a hacky fix for when render passes are significantly longer than SPS_AVERAGE_TIME by calculating the average time for a render pass --- .../java/se/llbit/chunky/renderer/DefaultRenderManager.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/chunky/src/java/se/llbit/chunky/renderer/DefaultRenderManager.java b/chunky/src/java/se/llbit/chunky/renderer/DefaultRenderManager.java index 7cf3737e95..279ff27b7e 100644 --- a/chunky/src/java/se/llbit/chunky/renderer/DefaultRenderManager.java +++ b/chunky/src/java/se/llbit/chunky/renderer/DefaultRenderManager.java @@ -386,6 +386,12 @@ private int samplesPerSecond() { int canvasHeight = bufferedScene.canvasHeight(); long pixelsPerFrame = (long) canvasWidth * canvasHeight; double renderTime = bufferedScene.renderTime / 1000.0; + + // Avoid incorrect output if the average time per render pass were to exceed SPS_AVERAGE_TIME + // Somewhat hacky, as it will break if there is an outlier pass that's >SPS_AVERAGE_TIME, and 0 will be returned + if (renderTime / bufferedScene.spp >= SPS_AVERAGE_TIME) + return (int) ((bufferedScene.spp * pixelsPerFrame) / renderTime); + if (renderTime < SPS_AVERAGE_TIME) { return (int) ((bufferedScene.spp * pixelsPerFrame) / renderTime); } else { From 3f53593996ac659ccd5da58a1982711ee5d17048 Mon Sep 17 00:00:00 2001 From: nib9888 Date: Mon, 24 Oct 2022 21:36:11 +0100 Subject: [PATCH 5/6] Remove hacky fix in favour of slight refactor, fixing the issue properly --- .../chunky/renderer/DefaultRenderManager.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/chunky/src/java/se/llbit/chunky/renderer/DefaultRenderManager.java b/chunky/src/java/se/llbit/chunky/renderer/DefaultRenderManager.java index 279ff27b7e..b5da116785 100644 --- a/chunky/src/java/se/llbit/chunky/renderer/DefaultRenderManager.java +++ b/chunky/src/java/se/llbit/chunky/renderer/DefaultRenderManager.java @@ -387,16 +387,14 @@ private int samplesPerSecond() { long pixelsPerFrame = (long) canvasWidth * canvasHeight; double renderTime = bufferedScene.renderTime / 1000.0; - // Avoid incorrect output if the average time per render pass were to exceed SPS_AVERAGE_TIME - // Somewhat hacky, as it will break if there is an outlier pass that's >SPS_AVERAGE_TIME, and 0 will be returned - if (renderTime / bufferedScene.spp >= SPS_AVERAGE_TIME) + // Handle the first render pass + if (spp_history_times.size() == 1) { return (int) ((bufferedScene.spp * pixelsPerFrame) / renderTime); - - if (renderTime < SPS_AVERAGE_TIME) { - return (int) ((bufferedScene.spp * pixelsPerFrame) / renderTime); - } else { - return (int) ((bufferedScene.spp - spp_history.get(0)) * pixelsPerFrame / SPS_AVERAGE_TIME); } + + double timeDiff = renderTime - spp_history_times.getFirst(); + int sppDiff = bufferedScene.spp - spp_history.getFirst(); + return (int) (sppDiff * pixelsPerFrame / timeDiff); } private void updateRenderProgress() { @@ -419,10 +417,12 @@ private void updateRenderProgress() { // Update list of spp values spp_history_times.addLast(renderTime); spp_history.addLast(bufferedScene.spp); - while (spp_history_times.get(0) + SPS_AVERAGE_TIME < renderTime) { + // These lists shouldn't contain information older than SPS_AVERAGE_TIME, but must contain at least one old value + while ((spp_history_times.getFirst() + SPS_AVERAGE_TIME < renderTime) && (spp_history_times.size() > 2)) { spp_history_times.removeFirst(); spp_history.removeFirst(); } + // Update render status display. renderStatusListeners.forEach(listener -> { listener.setRenderTime(bufferedScene.renderTime); From 5f3cbec71cc6d71cf296dad0440630f507373efb Mon Sep 17 00:00:00 2001 From: nib9888 Date: Wed, 26 Oct 2022 21:26:43 +0100 Subject: [PATCH 6/6] Cleanly handle first render pass --- .../se/llbit/chunky/renderer/DefaultRenderManager.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/chunky/src/java/se/llbit/chunky/renderer/DefaultRenderManager.java b/chunky/src/java/se/llbit/chunky/renderer/DefaultRenderManager.java index b5da116785..a143f384c9 100644 --- a/chunky/src/java/se/llbit/chunky/renderer/DefaultRenderManager.java +++ b/chunky/src/java/se/llbit/chunky/renderer/DefaultRenderManager.java @@ -291,6 +291,8 @@ public void run() { // Reset sps rolling average spp_history.clear(); spp_history_times.clear(); + spp_history.addLast(0); + spp_history_times.addLast(0.0); // Select the correct renderer Renderer render = mode == RenderMode.PREVIEW ? getPreviewRenderer() : getRenderer(); @@ -374,7 +376,7 @@ private void updateRenderState(Scene scene) { } } - private static final int SPS_AVERAGE_TIME = 180; + private static final int SPS_AVERAGE_TIME = 30; private LinkedList spp_history = new LinkedList(); private LinkedList spp_history_times = new LinkedList(); @@ -387,11 +389,6 @@ private int samplesPerSecond() { long pixelsPerFrame = (long) canvasWidth * canvasHeight; double renderTime = bufferedScene.renderTime / 1000.0; - // Handle the first render pass - if (spp_history_times.size() == 1) { - return (int) ((bufferedScene.spp * pixelsPerFrame) / renderTime); - } - double timeDiff = renderTime - spp_history_times.getFirst(); int sppDiff = bufferedScene.spp - spp_history.getFirst(); return (int) (sppDiff * pixelsPerFrame / timeDiff);