What's Your Ios Game Loop?

Started by
11 comments, last by zokeefe 7 years, 8 months ago

Just looking at what other people have done, what's been successful, and what pitfalls to avoid.

Current Prototype:

  • Game update is variable update time
  • Game update and rendering done in CADisplayLink callback running on main thread.
  • Audio update occurs on separate thread controlled by AudioUnit.
  • (Game-specific context): Game update takes ~5ms to complete

Thoughts:

  • I feel like variable-time game update might be OK on mobile given we don't have to worry about being preempted as much.
  • Don't know if this is how CADisplayLink is designed to work - perhaps should run game logic on background thread and only render in CADisplayLink callback? Not sure what synchronization issues arise here. Game logic could be allowed to update as fast as possible - or could cap to screen refresh rate.
  • Similarly, could push rendering into same background thread as game update. Again, not sure what synchronization issues arise here.

Goals (The same as everyone else?):

  1. Minimize input -> display latency
  2. Every screen refresh, the device should have an new/updated frame buffer to display.

(Placed in Graphics Programming and Theory because of CPU/GPU synchronization issues - of which I am most uninformed)

Advertisement
  • I feel like variable-time game update might be OK on mobile given we don't have to worry about being preempted as much.
It is never okay to use a variable-step rate. It is trivial to implement and failure to do so is only out of laziness, as it never enhances your game, only adds to its instability.


  • Don't know if this is how CADisplayLink is designed to work - perhaps should run game logic on background thread and only render in CADisplayLink callback? Not sure what synchronization issues arise here. Game logic could be allowed to update as fast as possible - or could cap to screen refresh rate.
  • Similarly, could push rendering into same background thread as game update. Again, not sure what synchronization issues arise here.

  • Minimize input -> display latency

Keep your game thread in a waiting state and trigger it from the display link. Spend as little time as possible in the display link or you will interfere with input timing and responsiveness.
For simplicity, there is no reason to render on a thread separately from your logic thread—just do the update and then render on the same thread.


(Placed in Graphics Programming and Theory because of CPU/GPU synchronization issues - of which I am most uninformed)

Making a separate render thread is non-trivial and requires some experience. Stick to single threads now and research multi-threaded rendering when you are ready.
It is unrelated to anything specific to iOS, and is its own subject large enough to deserve its own research.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

My loop is a little complicated, i work on PC and it should run on 60 or 30 fps dependent on hardware capibility.

I have multiple threads for each of the subsystems running( currently: network, audio, ki, physics and game logic, rendering)

User Input is collected in main thread at the beginning of the game loop, rendering draws always the scene calculated by the previous run of each subsystems;

Loop:

  1. Collect user input
  2. Trigger all subsystems
  3. Wait for finish
  4. Merge results and prepare next render frame
  5. Continue

This leads to the result that rendering is always on frame behind the user input.

Also various subsystems have their own copy of some parts of the scene to avoid race conditions. Which is a problem on huge scenes and low ram.

@L.Spiro

Thanks for commenting :) I've read your blog - and as such am not surprised by

It is never okay to use a variable-step rate. It is trivial to implement and failure to do so is only out of laziness, as it never enhances your game, only adds to its instability.

I don't know if it's "trivial" (at least for me :P). Variable-step allows you to sim right up to start of frame easily, while to do this with fixed-step requires you to fudge all the renderable object transforms (at least just for rendering) No?

The other drawback of fixed-step is you don't know how many update steps you may take - which scares me since doing 1 game loop vs 2 game loops could blow my CPU budget for a frame out of the water and cause us to miss a rendered frame? (Included just for discussion :P).

Keep your game thread in a waiting state and trigger it from the display link.

OK :)

@mgubisch

Thanks for commenting!

Interesting. Out of curiosity, how does one maintain separate threads for game logic & physics effectively? I would assume both want to be touching the same bits of data - to a large extent.

The other drawback of fixed-step is you don't know how many update steps you may take - which scares me since doing 1 game loop vs 2 game loops could blow my CPU budget for a frame out of the water and cause us to miss a rendered frame?

If you need a cap, limit the number of updates you can do in a frame, but each update still needs to be of a fixed amount of time.
You have to be fine with having your game slow down and drop frames for this to work, which means online real-time multi-player games cannot do this, but generally iOS games can.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

@mgubisch

Thanks for commenting!

Interesting. Out of curiosity, how does one maintain separate threads for game logic & physics effectively? I would assume both want to be touching the same bits of data - to a large extent.

As a wrote up in my first post some parts of the scene are stored in each subsystem which uses it. This makes some unneccesary copies which isn't really beautyful and is on my list to optimize.

Also each entity has a common set of properties which may be influenced by various subsystems(e.g. positon, velocity, aso).

Each subsystem wich wants to modify such a proberty sets a change flag to that property.

The merge result step collects all that change requests and applies it to the properties, during next loop run the renderer works with this properties.

If only one subsystem wants a change this is easy, just take it. If more than one subsystems wants to update the same part there is some kind of hierachy which subsystem wins.

In my desing this don't happen to often so i works fine for me.

At the beginning a had user input also as a subsystem, but this was somehow problematic so a apply user input at first, bevor the subsytems start to work.

I tried to make the tasks as fixed to one subsystem as possible, Movement is for nearly everthing handled by physics. Some exceptions like setting a fixed position of velocity is done by gamelogic. But in that cases gamelogic wins and in the next frame physics works with the new values and everthing runs as expected againg.

The idea to this came from an intel paper describing the demo they wrote many years ago as the introduced TBB. In that time a adopted this idea and modified it over the years. But the basic idea having a one or more separate threads for each subsystem stayed the same.

It is never okay to use a variable-step rate

Just to reduce the hardness of L.S.'s stance here; all the console games that I've ever worked on (up until my indie project) have used a variable time-step. If your simulation can tolerate a wide range of delta-time values, and gameplay isn't affected by the varying quality of integration, then variable time-step result in a far simpler codebase...

I'm currently doing game engine contracting for a console games company, and have tried to convince them that they should really be using fixed time-step plus game-state interpolation... but have been unable to provide an objective argument to sway them so far. Their variable-rate system is working perfectly fine for them, and as we say, if it ain't broke, don't fix it.

Making a separate render thread is non-trivial and requires some experience.

While this is true, switching from variable-time-step to fixed-time-step + interpolation lays a lot of the foundations. The game-state interpolation system basically requires that the renderer's input data is separated from the live game-state data, which is required for a good multi-thread implementation :)

If you need a cap, limit the number of updates you can do in a frame, but each update still needs to be of a fixed amount of time. You have to be fine with having your game slow down and drop frames for this to work

You should generally do this in a variable time-step system too -- cap the amount of time that a single frame is allowed to simulate -- as the quality of the integration will be pretty terrible if you try to simulate too much time in one go. As in the fixed-time-step case, this will cause your game to go into slow motion if the actual framerate is too low.

Out of curiosity, how does one maintain separate threads for game logic & physics effectively? I would assume both want to be touching the same bits of data - to a large extent.

The key to any good multithreading system is ensuring that no two threads ever need to share the same mutable data :)
For update/render, this means forming a pipeline where each frame, the update task generates a fresh blob of immutable data which is consumed by the render task.

The other drawback of fixed-step is you don't know how many update steps you may take

It's the same as in a variable-step architecture. You measure how long the previous frame took, and then advance the simulation by that amount of time next frame.

@mgubisch

Interesting. Do you know what the original paper was called?

@Hodgman

Thanks for the reply!

I'm currently doing game engine contracting for a console games company, and have tried to convince them that they should really be using fixed time-step plus game-state interpolation... but have been unable to provide an objective argument to sway them so far.

What argument are you using? :)

Their variable-rate system is working perfectly fine for them, and as we say, if it ain't broke, don't fix it.

This is where I am at now :) It's a 2D engine, my game update loop is very tight. The physics is entirely continuous. The only vulnerable aspect are objects undergoing acceleration dependent on velocity & position (using Verlet).

I wish I could find that object argument you were looking for - then I could stop worrying about this :)

This topic is closed to new replies.

Advertisement