From 3ffa44fba919732fc417d35d22db210f0a0a2295 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Thu, 20 Apr 2023 11:50:55 +0200 Subject: [PATCH] Window: Fix live-lock when `animationFrame` is slow If the window is sufficiently complex, it's possible for the average `animationFrame` to take so long we'll start falling behind with no way to ever catch up. And the amount of frames we're behind will quickly grow to the point where we'll be spending five seconds in `animationFrame` before we can get a real frame on the screen. Or even worse with the old while-loop, we'll be stuck indefinitely because the five seconds check only happens outside the loop and we never get to exit the loop if we never catch up, effectively locking up the game. To prevent that, we limit the `animationFrame` calls we make per real frame such that we'll still be able to render approximately 30 real frames per second at the cost of animations slowing down. GitHub: #104 --- .../kotlin/gg/essential/elementa/components/Window.kt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/gg/essential/elementa/components/Window.kt b/src/main/kotlin/gg/essential/elementa/components/Window.kt index dfa0deb0..5b8b468f 100644 --- a/src/main/kotlin/gg/essential/elementa/components/Window.kt +++ b/src/main/kotlin/gg/essential/elementa/components/Window.kt @@ -81,7 +81,15 @@ class Window @JvmOverloads constructor( if (System.currentTimeMillis() - this.systemTime > TimeUnit.SECONDS.toMillis(5)) this.systemTime = System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(5) - while (this.systemTime < System.currentTimeMillis() + 1000 / animationFPS) { + val target = System.currentTimeMillis() + 1000 / animationFPS + val animationFrames = (target - this.systemTime).toInt() / animationFPS + // If the window is sufficiently complex, it's possible for the average `animationFrame` to take so long + // we'll start falling behind with no way to ever catch up. And the amount of frames we're behind will + // quickly grow to the point where we'll be spending five seconds in `animationFrame` before we can get a + // real frame on the screen. + // To prevent that, we limit the `animationFrame` calls we make per real frame such that we'll still be able + // to render approximately 30 real frames per second at the cost of animations slowing down. + repeat(animationFrames.coerceAtMost((animationFPS / 30).coerceAtLeast(1))) { animationFrame() this.systemTime += 1000 / animationFPS }