Hello Guest

[solved] Best way to do time?

  • 21 Replies
  • 25628 Views
[solved] Best way to do time?
« on: December 02, 2010, 08:17:45 »
I have some game programming books written back in 2002.  Based on their explanation of timing in games, this is what I have come up with:

(Why does the text inside code blocks always look so small?  Could someone please change this?)
Code: [Select]
 private static final double FREQ = 1000000000.0;

  private int shipX = 0;
  private double shipXSpeed = 0.01;
  private int shipY = 0;
  private double shipYSpeed = 0.01;
  private long time;

  private MyClass() {
    time = System.nanoTime();
  }

  private void run() {
    while(!Display.isCloseRequested() && !Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)) {
      double delta = 1.0 / ((System.nanoTime() - time) / FREQ);
      time = System.nanoTime();

      if(Display.isVisible()) {
        processKeyboard();
        processMouse();
        update(delta);
        render();
      }
      else {
        if(Display.isDirty()) {
          render();
        }
        try {
          Thread.sleep(100);
        }
        catch(InterruptedException ex) {
        }
      }
      Display.update();
    }
  }

  private void update(double delta) {
    shipX += (int)(shipXSpeed * delta);
    shipY += (int)(shipYSpeed * delta);
  }

The idea is of course that people with high-end computers will be able to go to 100 FPS, yet the speed will be consistent with all computers.  I'm sure that I'm not telling you guys anything you don't already know.  As you can see, I have to specify a very small speed (0.01).  Also, I'm worried if this will even work.  Should I just use Display.sync(30) instead?  Or sleep?  What do you guys use/suggest?  Thanks.
« Last Edit: December 03, 2010, 20:58:17 by jediTofu »
cool story, bro

*

Offline ryanm

  • *
  • 44
Re: Best way to do time?
« Reply #1 on: December 02, 2010, 10:31:33 »
One thing you might want to do is to always advance your game state with a fixed timestep - you'll get more consistent and repeatable results which should help enormously when bughunting

*

Offline bobjob

  • ****
  • 394
  • LWJGL: WOW SO GOOD
Re: Best way to do time?
« Reply #2 on: December 02, 2010, 11:06:16 »
Recently after looking into a few different ways of dealing with time I decided to use the following approach:


Code: [Select]
int updates, framesPerSecond = 75, logicPerSecond = 60;

private synchronized void refineUpdate(int minus) {
updates -= minus;
}

main loop (GL Thread)
Code: [Select]
while (running) { //main application loop
int perform = updates;
if (perform > 1) perform = perform/2;
for (int i = 0; i < perform; i++) {
updateLogic();
}
refineUpdate(perform);


//sync if desired
//sync(framesPerSecond)

render();
}

loop to check required logic updates (Different Thread)
Code: [Select]

while (running) {
sync(LogicPerSecond);
refineUpdate(-1);
}

The reason I use this method it avoid alot of annoying concerns.
* if VSync is on and computer uses flat screen with 30hz refreshrate
* Can be used to test FPS without capping render calls.
* Keeps logic mostly independant of rendering.
* If rendering takes longer than expected, multiple logic calls to keep game in at expected rate/sync (good for multiplayer).
* keeps logic on the GL thread, in case GL/AL really needs to be called inside the logic method

« Last Edit: December 02, 2010, 13:57:41 by bobjob »

Re: Best way to do time?
« Reply #3 on: December 02, 2010, 11:40:23 »
bobjob: looks like you use an integer field from 2 different threads - this causes race conditions and can result in missed updates. You should use synchronization or better an AtomicInteger.

*

Offline bobjob

  • ****
  • 394
  • LWJGL: WOW SO GOOD
Re: Best way to do time?
« Reply #4 on: December 02, 2010, 11:47:25 »
bobjob: looks like you use an integer field from 2 different threads - this causes race conditions and can result in missed updates. You should use synchronization or better an AtomicInteger.
Really? 1 thread only uses addition, and the other thread only uses subtraction, I cant see any conflicts.

Re: Best way to do time?
« Reply #5 on: December 02, 2010, 12:33:50 »
These operations are not atomic. The "updates++" executes like this:
Code: [Select]
int tmp = this.updates;    // memory read
tmp = tmp + 1;
this.updates = tmp;        // memory write

And your "updates -= performs" executes like this:
Code: [Select]
int tmp = this.updates;    // memory read
tmp = tmp - performs;
this.updates = tmp;        // memory write

The threads can be suspended at any of these steps, and on a multi core system they may even execute at the same time. So the write to this.updates may overwrite the changes done by the other thread.

*

Offline bobjob

  • ****
  • 394
  • LWJGL: WOW SO GOOD
Re: Best way to do time?
« Reply #6 on: December 02, 2010, 13:03:08 »
Thanks, thats good to know. Ill change it then.

*synchronized the update value change
« Last Edit: December 02, 2010, 13:58:32 by bobjob »

Re: Best way to do time?
« Reply #7 on: December 02, 2010, 20:30:29 »
Thanks guys for all the replies!

One thing you might want to do is to always advance your game state with a fixed timestep - you'll get more consistent and repeatable results which should help enormously when bughunting

I'm a bit confused by this.  Can I have an example please?

Code: [Select]
while (running) {
sync(LogicPerSecond);
refineUpdate(-1);
}

Is this Display.sync(...)?  I read in another thread that it wasn't very consistent because it used Thread.sleep(...).


It appears that there are a lot of different ways people are doing time in their games, especially in LWJGL.  Does anyone use LWJGL's Sys.getTime() or Timer?  But I've also heard people complaining about these here.  I wish I could just nail the coffin shut on this, like LWJGL would could come down from the heavens and say, "This is how you should do timing in games for all LWJGL projects."
cool story, bro

Re: Best way to do time?
« Reply #8 on: December 02, 2010, 23:40:49 »
In my original code, adding Thread.yield() or Display.sync(100) smooths it out better after Display.update().  I'm still worried about using Display.sync though...  Also in original code, shipX & shipY should be doubles with no int conversion, just wasn't thinking when I typed it up here.

I tried Sys.getTime(), but it was choppier.  I looked at the resolution, and it's 1000, so it uses milliseconds and is less accurate than nanoTime, which I blame as the culprit.
« Last Edit: December 02, 2010, 23:43:02 by jediTofu »
cool story, bro

*

Offline kappa

  • *****
  • 1319
Re: Best way to do time?
« Reply #9 on: December 03, 2010, 00:31:07 »
nice solutions, personally just use Display.sync(fps) and find that it works fine for my use case.

Then again the master of smooth java games explains his secret recipe here.

*

Offline bobjob

  • ****
  • 394
  • LWJGL: WOW SO GOOD
Re: Best way to do time?
« Reply #10 on: December 03, 2010, 10:56:34 »
Is this Display.sync(...)?  I read in another thread that it wasn't very consistent because it used Thread.sleep(...).

It appears that there are a lot of different ways people are doing time in their games, especially in LWJGL.  Does anyone use LWJGL's Sys.getTime() or Timer?  But I've also heard people complaining about these here.  I wish I could just nail the coffin shut on this, like LWJGL would could come down from the heavens and say, "This is how you should do timing in games for all LWJGL projects."
I was using Nano time in the non-gl thread. It uses Thread.sleep() as I really dont like the idea of a game taking up 100% CPU usage.

I attached the timer class, that I use as a sync timer.

Re: Best way to do time?
« Reply #11 on: December 03, 2010, 20:57:57 »
Thanks for the code bobjob.

Surprisingly, using a custom sync method with nanoTime is worst than Display.sync...not sure why this is.  I even tried copying the Display.sync code verbatim and replacing it with nanoTiime, and it was still choppier.

I am getting the best results with nanoTime for the delta and Display.sync(1000).  The frame rate is very smooth compared to everything else that I have tried, and the CPU usage hovers around <10%, so this is what I'll use as the solution.

Thread.yield is the smoothest (only by a small fraction), but I won't use that after considering CPU usage and after reading that its functionality is NOT standard across all platforms and highly suggested not to be used inside a loop.

Thanx for all replies and code.
cool story, bro

*

Offline bobjob

  • ****
  • 394
  • LWJGL: WOW SO GOOD
Re: [solved] Best way to do time?
« Reply #12 on: December 04, 2010, 01:08:18 »
jediTofu

could you try my benchmark test and tell me which Timer works the smoothest on your system (Timer mode can be set in the Menu).


What is your system btw? OS, Java version aswell as 32 or 64 bit.





Re: [solved] Best way to do time?
« Reply #13 on: December 04, 2010, 02:42:28 »
LWJGL Timer was just a tiny bit smoother and was at 140 FPS avg.
Java Timer was at 133 FPS avg.

I only noticed the smoothness difference with the Water Shader test, but Java Timer was consistently a few (~5) FPS less.

Windows 7 AMD 64-bit, JRE 1.6.0_21-b07
cool story, bro

*

Offline bobjob

  • ****
  • 394
  • LWJGL: WOW SO GOOD
Re: [solved] Best way to do time?
« Reply #14 on: December 04, 2010, 20:40:57 »
LWJGL Timer was just a tiny bit smoother and was at 140 FPS avg.
Java Timer was at 133 FPS avg.
thanx, good to know. I guess for Windows7 64bit LWJGL is the way to go.