diff --git a/backend/js/src/main/scala/eu/joaocosta/minart/backend/JsLoopRunner.scala b/backend/js/src/main/scala/eu/joaocosta/minart/backend/JsLoopRunner.scala index 761ce0ed..d6ab5724 100644 --- a/backend/js/src/main/scala/eu/joaocosta/minart/backend/JsLoopRunner.scala +++ b/backend/js/src/main/scala/eu/joaocosta/minart/backend/JsLoopRunner.scala @@ -17,28 +17,40 @@ object JsLoopRunner extends LoopRunner { frequency: LoopFrequency, cleanup: () => Unit ): Loop[S] = { - val iterationMillis = frequency match { - case LoopFrequency.Uncapped => 0 - case LoopFrequency.LoopDuration(ms) => ms - } - new Loop[S] { - def run(initialState: S) = { - def finiteLoopAux(state: S): Unit = { - val startTime = System.currentTimeMillis() - val newState = operation(state) - if (!terminateWhen(newState)) { - val endTime = System.currentTimeMillis() - val waitTime = iterationMillis - (endTime - startTime) - if (waitTime > 0 || !hasWindow) timers.setTimeout(waitTime.toDouble)(finiteLoopAux(newState)) - else dom.window.requestAnimationFrame((_: Double) => finiteLoopAux(newState)) - } else { cleanup() } + frequency match { + case LoopFrequency.Never => + new Loop[S] { + def run(initialState: S) = operation(initialState) + } + case LoopFrequency.Uncapped => + new Loop[S] { + def finiteLoopAux(state: S): Unit = { + val newState = operation(state) + if (!terminateWhen(newState)) { + if (!hasWindow) timers.setTimeout(0)(finiteLoopAux(newState)) + else dom.window.requestAnimationFrame((_: Double) => finiteLoopAux(newState)) + } else { cleanup() } + } + def run(initialState: S) = { + finiteLoopAux(initialState) + } + } + case LoopFrequency.LoopDuration(iterationMillis) => + new Loop[S] { + def finiteLoopAux(state: S): Unit = { + val startTime = System.currentTimeMillis() + val newState = operation(state) + if (!terminateWhen(newState)) { + val endTime = System.currentTimeMillis() + val waitTime = iterationMillis - (endTime - startTime) + if (waitTime > 0 || !hasWindow) timers.setTimeout(waitTime.toDouble)(finiteLoopAux(newState)) + else dom.window.requestAnimationFrame((_: Double) => finiteLoopAux(newState)) + } else { cleanup() } + } + def run(initialState: S) = { + finiteLoopAux(initialState) + } } - finiteLoopAux(initialState) - } } } - - def singleRun(operation: () => Unit, cleanup: () => Unit): Loop[Unit] = new Loop[Unit] { - def run(initialState: Unit) = operation() - } } diff --git a/backend/jvm/src/main/scala/eu/joaocosta/minart/backend/JavaLoopRunner.scala b/backend/jvm/src/main/scala/eu/joaocosta/minart/backend/JavaLoopRunner.scala index f2f48156..e6da238b 100644 --- a/backend/jvm/src/main/scala/eu/joaocosta/minart/backend/JavaLoopRunner.scala +++ b/backend/jvm/src/main/scala/eu/joaocosta/minart/backend/JavaLoopRunner.scala @@ -13,30 +13,42 @@ object JavaLoopRunner extends LoopRunner { frequency: LoopFrequency, cleanup: () => Unit ): Loop[S] = { - val iterationMillis = frequency match { - case LoopFrequency.Uncapped => 0 - case LoopFrequency.LoopDuration(ms) => ms + frequency match { + case LoopFrequency.Never => + new Loop[S] { + def run(initialState: S) = operation(initialState) + } + case LoopFrequency.Uncapped => + @tailrec + def finiteLoopAux(state: S): Unit = { + val newState = operation(state) + if (!terminateWhen(newState)) finiteLoopAux(newState) + else () + } + new Loop[S] { + def run(initialState: S) = { + finiteLoopAux(initialState) + cleanup() + } + } + case LoopFrequency.LoopDuration(iterationMillis) => + new Loop[S] { + @tailrec + def finiteLoopAux(state: S): Unit = { + val startTime = System.currentTimeMillis() + val newState = operation(state) + if (!terminateWhen(newState)) { + val endTime = System.currentTimeMillis() + val waitTime = iterationMillis - (endTime - startTime) + if (waitTime > 0) Thread.sleep(waitTime) + finiteLoopAux(newState) + } else () + } + def run(initialState: S) = { + finiteLoopAux(initialState) + cleanup() + } + } } - @tailrec - def finiteLoopAux(state: S): Unit = { - val startTime = System.currentTimeMillis() - val newState = operation(state) - if (!terminateWhen(newState)) { - val endTime = System.currentTimeMillis() - val waitTime = iterationMillis - (endTime - startTime) - if (waitTime > 0) Thread.sleep(waitTime) - finiteLoopAux(newState) - } else () - } - new Loop[S] { - def run(initialState: S) = { - finiteLoopAux(initialState) - cleanup() - } - } - } - - def singleRun(operation: () => Unit, cleanup: () => Unit): Loop[Unit] = new Loop[Unit] { - def run(initialState: Unit) = operation() } } diff --git a/backend/native/src/main/scala/eu/joaocosta/minart/backend/SdlLoopRunner.scala b/backend/native/src/main/scala/eu/joaocosta/minart/backend/SdlLoopRunner.scala index 1c08f284..0ee187d6 100644 --- a/backend/native/src/main/scala/eu/joaocosta/minart/backend/SdlLoopRunner.scala +++ b/backend/native/src/main/scala/eu/joaocosta/minart/backend/SdlLoopRunner.scala @@ -18,38 +18,51 @@ object SdlLoopRunner extends LoopRunner { frequency: LoopFrequency, cleanup: () => Unit ): Loop[S] = { - val iterationMillis = frequency match { - case LoopFrequency.Uncapped => 0 - case LoopFrequency.LoopDuration(ms) => ms - } - @tailrec - def finiteLoopAux(state: S): Unit = { - val startTime = SDL_GetTicks() - val newState = operation(state) - if (!terminateWhen(newState)) { - val endTime = SDL_GetTicks() - val waitTime = iterationMillis - (endTime - startTime).toInt - if (waitTime > 0) SDL_Delay(waitTime.toUInt) - finiteLoopAux(newState) - } else () - } - new Loop[S] { - def run(initialState: S) = { - finiteLoopAux(initialState) - cleanup() - } - } - } - - def singleRun(operation: () => Unit, cleanup: () => Unit): Loop[Unit] = new Loop[Unit] { - def run(initialState: Unit) = { - operation() - var quit = false - val event = stackalloc[SDL_Event]() - while (!quit) { - if (SDL_WaitEvent(event) == 1 && event.type_ == SDL_QUIT) { quit = true } - } - cleanup() + frequency match { + case LoopFrequency.Never => + new Loop[S] { + def run(initialState: S) = { + operation(initialState) + var quit = false + val event = stackalloc[SDL_Event]() + while (!quit) { + if (SDL_WaitEvent(event) == 1 && event.type_ == SDL_QUIT) { quit = true } + } + cleanup() + } + } + case LoopFrequency.Uncapped => + new Loop[S] { + @tailrec + def finiteLoopAux(state: S): Unit = { + val newState = operation(state) + if (!terminateWhen(newState)) { + finiteLoopAux(newState) + } else () + } + def run(initialState: S) = { + finiteLoopAux(initialState) + cleanup() + } + } + case LoopFrequency.LoopDuration(iterationMillis) => + new Loop[S] { + @tailrec + def finiteLoopAux(state: S): Unit = { + val startTime = SDL_GetTicks() + val newState = operation(state) + if (!terminateWhen(newState)) { + val endTime = SDL_GetTicks() + val waitTime = iterationMillis - (endTime - startTime).toInt + if (waitTime > 0) SDL_Delay(waitTime.toUInt) + finiteLoopAux(newState) + } else () + } + def run(initialState: S) = { + finiteLoopAux(initialState) + cleanup() + } + } } } } diff --git a/core/shared/src/main/scala/eu/joaocosta/minart/graphics/ImpureRenderLoop.scala b/core/shared/src/main/scala/eu/joaocosta/minart/graphics/ImpureRenderLoop.scala index 89b1dbc3..7f9148c4 100644 --- a/core/shared/src/main/scala/eu/joaocosta/minart/graphics/ImpureRenderLoop.scala +++ b/core/shared/src/main/scala/eu/joaocosta/minart/graphics/ImpureRenderLoop.scala @@ -30,15 +30,6 @@ object ImpureRenderLoop extends RenderLoop.Builder[Function1, Function2] { ): RenderLoop[Unit] = statefulRenderLoop[Unit]((c: Canvas, _: Unit) => renderFrame(c), frameRate) - def singleFrame(renderFrame: Canvas => Unit): RenderLoop[Unit] = new RenderLoop[Unit] { - def run( - runner: LoopRunner, - canvasManager: CanvasManager, - canvasSettings: Canvas.Settings, - initialState: Unit - ): Unit = { - val canvas = canvasManager.init(canvasSettings) - runner.singleRun(() => renderFrame(canvas), () => if (canvas.isCreated()) canvas.close()).run() - } - } + def singleFrame(renderFrame: Canvas => Unit): RenderLoop[Unit] = + statelessRenderLoop(renderFrame, LoopFrequency.Never) } diff --git a/core/shared/src/main/scala/eu/joaocosta/minart/runtime/LoopFrequency.scala b/core/shared/src/main/scala/eu/joaocosta/minart/runtime/LoopFrequency.scala index 7230c73c..8493e29d 100644 --- a/core/shared/src/main/scala/eu/joaocosta/minart/runtime/LoopFrequency.scala +++ b/core/shared/src/main/scala/eu/joaocosta/minart/runtime/LoopFrequency.scala @@ -16,6 +16,9 @@ object LoopFrequency { /** Uncapped loop frequency. */ case object Uncapped extends LoopFrequency + /** Run a single iteration */ + case object Never extends LoopFrequency + /** 60 Hz. */ final val hz60 = fromHz(60) @@ -35,14 +38,16 @@ object LoopFrequency { * * @param duration minimum loop iteration duration */ - def fromDuration(duration: FiniteDuration): LoopFrequency = - if (duration.toMillis <= 0) Uncapped + def fromDuration(duration: Duration): LoopFrequency = + if (!duration.isFinite) Never + else if (duration.toMillis <= 0) Uncapped else LoopDuration(duration.toMillis) /** Builds a [[LoopFrequency]] in Hz. */ def fromHz(hz: Int): LoopFrequency = { - if (hz <= 0 || 1000 / hz == 0) Uncapped + if (hz <= 0) Never + else if (1000 / hz == 0) Uncapped else LoopDuration(1000 / hz) } } diff --git a/core/shared/src/main/scala/eu/joaocosta/minart/runtime/LoopRunner.scala b/core/shared/src/main/scala/eu/joaocosta/minart/runtime/LoopRunner.scala index e5a0dba5..0ef80e22 100644 --- a/core/shared/src/main/scala/eu/joaocosta/minart/runtime/LoopRunner.scala +++ b/core/shared/src/main/scala/eu/joaocosta/minart/runtime/LoopRunner.scala @@ -23,13 +23,6 @@ trait LoopRunner { frequency: LoopFrequency, cleanup: () => Unit ): Loop[S] - - /** Runs a single operation. - * - * @param operation operation to perform - * @param cleanup cleanup procedure to run when the operation is finished - */ - def singleRun(operation: () => Unit, cleanup: () => Unit): Loop[Unit] } object LoopRunner {