Skip to content

Commit

Permalink
Merge pull request #247 from JD557/wait-loop
Browse files Browse the repository at this point in the history
Add Never LoopFrequency
  • Loading branch information
JD557 authored Aug 15, 2022
2 parents a1f6d20 + 4a2b43d commit 7704478
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 98 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit 7704478

Please sign in to comment.