9

I've read that the speed of game objects should not be hindered by FPS but instead should be based on time. How can I seperate the update/draw code to maximize performance without limiting the drawing rate and provide a constant logic update rate based on time?

My current pseudo code is as follows

loop
{
    draw();
    if (ticksElapsed() > 100)
    {
        update();
        ticks+= ticksElapsed();
    }        
}

The problem is the drawing code hinders the performance of the update() rate. And it consumes 100% cpu because if sleep is thrown in, it throws off both drawing/logic functions.

I am also using SDL and it doesn't seem to have a vsync option. I've also heard of the terms fixed and variable time-stepping however I'm not sure how that can be done with sleep()

Nicolas Kaiser
  • 117
  • 1
  • 2
  • 9
Osky
  • 193
  • 1
  • 5
  • 1
    You don't need to waste 100% CPU power just for waiting, put a sleep(0) at the end of the while loops if ticksElapsed() < 100. The OS will return to the thread immediately if there is no other thread that wants to run. But not wasting 100% CPU power anymore. – Maik Semder Jul 10 '11 at 14:56
  • However, best solution for such a 1 thread setup is to use vsync, if you cant vsync, then call sleep(0) in a loop until you reached the target frame rate, then update and draw – Maik Semder Jul 10 '11 at 15:09

1 Answers1

3

In your code snippet it looks like you're trying to run your game in fixed-time step mode by busy-waiting if your drawing and updating took less then 15ms (60fps). This is possible and you guessed right that this can't be done using a sleep call because you don't exactly know how long you're going to sleep. The busy-waiting-loop is the good solution.

However consider the case where your updating and drawing exceed 15ms, you now have the game drawing and updating to slow. You can now do either two things: detect this state and drop frames (skip drawing and go straight to updating until you are in sync again) however if the computer is just to slow it will never catch up.

An other solution is to make your update logic fixed-time independent. You don't need a separate thread for this, you just have to respecify how fast things should move. Instead of 5pixels per tick, you should use 50pixels per second. You would need a high precision timer to achieve this, and all your update logic should be able to access the timer to see how much time passed since last update.

Basically you go from:

void UpdatePlayer()
 player.x += 10;

To

void UpdatePlayer(float elapsedSeconds) //the total seconds elapsed since last update
 player.x += walkspeed * elapsedSeconds;
Roy T.
  • 10,238
  • 39
  • 58
  • So my engine will always consume 100% and there's nothing I can really do about it? – Osky Jul 09 '11 at 19:48
  • 1
    It will consume as much cycles as the scheduler allows it to get, but that's not really an issue since you can't really do a lot of other things while you're playing a game :). – Roy T. Jul 10 '11 at 07:26
  • @Oskenso However, it is a problem if you use more than 1 thread, then the main thread will not let the others run as much as they could, wasting a lot of computational power in the while loop, you really should consider a sleep – Maik Semder Jul 10 '11 at 14:09
  • @Maik Semder: do you have a solution for sleep(x) not being accurate? After the sleep interval has passed, the thread is ready to run. But a ready thread is not guaranteed to run immediately. That's up to the scheduler. When you're using two threads there are other solutions, for that see this excellent article: http://altdevblogaday.com/2011/07/03/threading-and-your-game-loop/ – Roy T. Jul 10 '11 at 14:16
  • 1
    @Roy sleep(0) is the solution. It returns immediately if there is no other thread that wants to run (Sleep WinAPI) and gives other threads the chance to run. If the other thread will not give the main thread the chance to run in return, then you have a threading problem, but blocking everything else by not calling sleep in the first place makes it even worse and is hardly a solution. The key is to call sleep(0) and test the elapsed time until you hit your target fixed frame rate, so you dont waste 100 % CPU just for waiting. – Maik Semder Jul 10 '11 at 14:46
  • @Maik Semder, but how do you guarantee that you return in 15ms or less. I mean the purpose of sleep here was using it to run in fixed-time step mode :). If you're not running in fixed time step than you don't need sleep (because you can use any of the algorithms described above). I've never seen a game do sleep(..) tbh. I wonder if I can find some resources on how XNA achieves fixed-time step. – Roy T. Jul 10 '11 at 17:32
  • @Roy you can't guarantee that. You can't guarantee that with any solution, as we are a) not talking about real time operation systems and b) you have no control what the operation system chooses to do. The point is, when the system is under that much workload, that it wouldn't return in time, then you have much worse problems. But you can't solve that by adding an even heavier workload to the CPU. It's throwing oil into the fire. – Maik Semder Jul 10 '11 at 18:06
  • @Roy under normal conditions the sleep(0) will return in time. But of course, as you already said in your answer, one has to be prepared and deal with the situation that it doesnt and skip some parts. But this you have to do anyway, that's not an argument for adding unnecessary workload to the CPU. I totally agree with the rest of your answer, that's why I didn't downvote it. I just disagree to the part saying that it's better to have a busy wait blocking all threads of the app and the system, than a using a sleep(0) :) – Maik Semder Jul 10 '11 at 18:06
  • Btw. I saw a lot of games using sleep(0), especially multi-threaded ones – Maik Semder Jul 10 '11 at 18:08
  • 1
    @Maik Hmm, OK that last argument got me! You're right ofc the OS can transfer your process from running to ready anytime, so yes sleep(0) would be better :). So advice for the one who asked the question would be: use sleep(0) not sleep(15ms). – Roy T. Jul 11 '11 at 10:37
  • 1
    @Roy I would say so too :) one should never expect the timing of sleep to be exactly, the number passed as parameter is more like a hint to the scheduler, the only assertation is, that is doesnt come back earlier. So yeah, best use 0 as param, that tells the scheduler, "get back as soon as possible", which is usually less than small fractions of milliseconds – Maik Semder Jul 11 '11 at 11:29