glx-frames

Library for making endless game-loops, it`s heart of any game engine.

Base usage

Executor will run your gameLoop and automatic collect and calculate all frame stats, also it will be throttle processing, when CPU is more powerful than we need in targetFPS

After each frame, it call afterFrame function with all frame stats, and most useful is frame stats.DeltaTime

import "github.com/fe3dback/glx-frames/frame"

func main() {
  executor := frame.NewExecutor(WithTargetFPS(60))
  err := executor.Execute(ctx, gameLoop, afterFrame)
  // ..
}

func gameLoop() error {
  // run here your:
  // - world.Update
  // - world.Draw
  // - events handle
  // - etc..
  
  time.Sleep(time.Millisecond * 25) // emulate work..

  return nil
}

func afterFrame(stats frame.Stats) {
  // dt = stats.DeltaTime
  fmt.Printf("frm:%03d, FPS: %02d/%02d\n",
    stats.CurrentFrame,
    stats.CurrentFPS,
    stats.FrameTargetFPS,
  )
}

Tasks

You can provide some minor tasks, that will be executed only when we have free CPU time.

For example at targetFPS=60, our frame capacity is 16.6ms When your gameLoop took 10ms, we have free 6.6ms in current frame

This 6.6ms will be used for tasks processing

GC task

Super useful and required in most cases is garbage collection task, that will process golang GC only when we have free time.

Of course on low-end CPU’s, when our FPS always less that targetFPS, it will be executed anyway, at least once in 10 seconds

executor := NewExecutor(
  WithTargetFPS(60),
  
  // add unlimited number of tasks
  WithTask(
    NewTask(
      func() {
        // try to run every frame
        // but not more often that once in 1s
        // but at least once in 10 second guaranteed
        runtime.GC()
        runtime.Gosched()
      },
      WithRunAtLeastOnceIn(time.Second * 10),
      WithRunAtMostOnceIn(time.Second),
      WithPriority(TaskPriorityLow),
    ),
  ),
)

executor.Execute( .. )

Also it already defined in lib, you can use default task:

frame.NewDefaultTaskGarbageCollect()

Custom tasks

You can add any number of tasks, and choose priority from LOW to HIGH, also Executor will take into account other task properties like LastRunTime, AvgExecutionTime and other in priority calculation.

Available stats

type Timings struct {
  StartAt  time.Time
  Duration time.Duration
}

type Stats struct {
  CurrentFrame uint64   // frameID since game start
  CurrentFPS   int      // real counted FPS
  DeltaTime    float64  // use it for all game calculations

  FrameFreeTime    time.Duration // how much was available
  FrameTargetFPS   int           // real fps ceil
  FramePossibleFPS int           // ~ calculated ceil
  FrameTimeLimit   time.Duration // 1s / targetFPS
  ThrottleTime     time.Duration // sleep time

  Execute Timings // full game time
  Frame   Timings // current frame time
  Process Timings // your `gameLoop` function time
  Tasks   Timings // tasks time
}

Full Example

See code in frame/executor_test

Test settings:

  • TestTime = 3s
  • FPSLimit = 24
  • LogicTime = 25ms (4 frame per 100ms / 40 frames per second)

Tasks:

  • high priority 5ms task (run at least once in second, but try to every 500ms)
  • low priority GC (at least once in second, but try to every 100ms)

Test output:

| -- STATS --              | -- Frame --                               |
| elapsed | frame |  FPS   | capacity |       fn |    tasks | throttle |
|  0042ms |  001  |  24/24 |     41ms |     25ms |     06ms |     10ms |
|  0085ms |  002  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  0128ms |  003  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  0170ms |  004  |  24/24 |     41ms |     25ms |     01ms |     15ms |
|  0212ms |  005  |  24/24 |     41ms |     26ms |     00ms |     15ms |
|  0255ms |  006  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  0297ms |  007  |  24/24 |     41ms |     25ms |     00ms |     15ms |
|  0338ms |  008  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  0381ms |  009  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  0424ms |  010  |  24/24 |     41ms |     25ms |     01ms |     15ms |
|  0466ms |  011  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  0509ms |  012  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  0551ms |  013  |  24/24 |     41ms |     25ms |     05ms |     10ms |
|  0593ms |  014  |  24/24 |     41ms |     25ms |     00ms |     15ms |
|  0635ms |  015  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  0677ms |  016  |  24/24 |     41ms |     25ms |     00ms |     15ms |
|  0719ms |  017  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  0762ms |  018  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  0803ms |  019  |  24/24 |     41ms |     25ms |     00ms |     15ms |
|  0846ms |  020  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  0889ms |  021  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  0931ms |  022  |  24/24 |     41ms |     25ms |     00ms |     15ms |
|  0973ms |  023  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  1016ms |  024  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  1058ms |  025  |  24/24 |     41ms |     25ms |     05ms |     10ms |
|  1100ms |  026  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  1143ms |  027  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  1185ms |  028  |  24/24 |     41ms |     25ms |     00ms |     15ms |
|  1227ms |  029  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  1270ms |  030  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  1312ms |  031  |  24/24 |     41ms |     25ms |     00ms |     15ms |
|  1354ms |  032  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  1396ms |  033  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  1438ms |  034  |  24/24 |     41ms |     25ms |     00ms |     15ms |
|  1480ms |  035  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  1522ms |  036  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  1564ms |  037  |  24/24 |     41ms |     25ms |     06ms |     10ms |
|  1606ms |  038  |  24/24 |     41ms |     25ms |     00ms |     15ms |
|  1649ms |  039  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  1691ms |  040  |  24/24 |     41ms |     25ms |     00ms |     15ms |
|  1733ms |  041  |  24/24 |     41ms |     25ms |     00ms |     15ms |
|  1775ms |  042  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  1817ms |  043  |  24/24 |     41ms |     25ms |     00ms |     15ms |
|  1860ms |  044  |  24/24 |     41ms |     25ms |     00ms |     15ms |
|  1902ms |  045  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  1944ms |  046  |  24/24 |     41ms |     25ms |     00ms |     15ms |
|  1986ms |  047  |  24/24 |     41ms |     25ms |     00ms |     15ms |
|  2029ms |  048  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  2070ms |  049  |  24/24 |     41ms |     25ms |     05ms |     10ms |
|  2113ms |  050  |  24/24 |     41ms |     25ms |     00ms |     15ms |
|  2155ms |  051  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  2197ms |  052  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  2240ms |  053  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  2282ms |  054  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  2325ms |  055  |  24/24 |     41ms |     25ms |     00ms |     15ms |
|  2367ms |  056  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  2410ms |  057  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  2452ms |  058  |  24/24 |     41ms |     25ms |     00ms |     15ms |
|  2494ms |  059  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  2537ms |  060  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  2579ms |  061  |  24/24 |     41ms |     25ms |     05ms |     10ms |
|  2621ms |  062  |  24/24 |     41ms |     25ms |     00ms |     15ms |
|  2663ms |  063  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  2706ms |  064  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  2748ms |  065  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  2791ms |  066  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  2833ms |  067  |  24/24 |     41ms |     25ms |     00ms |     15ms |
|  2874ms |  068  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  2917ms |  069  |  24/24 |     41ms |     25ms |     00ms |     16ms |
|  2959ms |  070  |  24/24 |     41ms |     25ms |     00ms |     15ms |
|  3001ms |  071  |  24/24 |     41ms |     25ms |     00ms |     15ms |
--- PASS: TestExecutor_Execute (3.00s)

GitHub

View Github