LWJGL Forum

Programming => General Java Game Development => Topic started by: jediTofu on December 02, 2010, 08:17:45

Title: [solved] Best way to do time?
Post by: jediTofu 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.
Title: Re: Best way to do time?
Post by: ryanm 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
Title: Re: Best way to do time?
Post by: bobjob 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

Title: Re: Best way to do time?
Post by: Matthias 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.
Title: Re: Best way to do time?
Post by: bobjob 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.
Title: Re: Best way to do time?
Post by: Matthias 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.
Title: Re: Best way to do time?
Post by: bobjob on December 02, 2010, 13:03:08
Thanks, thats good to know. Ill change it then.

*synchronized the update value change
Title: Re: Best way to do time?
Post by: jediTofu 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."
Title: Re: Best way to do time?
Post by: jediTofu 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.
Title: Re: Best way to do time?
Post by: kappa 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 (http://lwjgl.org/forum/index.php/topic,3472.msg19279.html#msg19279).
Title: Re: Best way to do time?
Post by: bobjob 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.
Title: Re: Best way to do time?
Post by: jediTofu 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.
Title: Re: [solved] Best way to do time?
Post by: bobjob on December 04, 2010, 01:08:18
jediTofu

could you try my benchmark test (http://have2chat.net/benchmark) 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.




Title: Re: [solved] Best way to do time?
Post by: jediTofu 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
Title: Re: [solved] Best way to do time?
Post by: bobjob 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.
Title: Re: [solved] Best way to do time?
Post by: bobjob on December 04, 2010, 21:51:05
Also just so you know, it makes little sense to use a syncMethod Like:
Display.sync(1000).
as this will vary between different Video cards, weather or not a single frame can even be rendered in 1000 fps.
The human eye cant possibly see more than 250, from what I know.

try (for testing purposes) just using Display.sync(60) don't manage any of the delta values yourself. dont make logic changes based on time, but rather use a set value for an expected gamecycle length.
just so you know, Doom3 uses a capped frame rate of 60

so with the example method:
Code: [Select]
  private void update(double delta) {
    shipX += (int)(shipXSpeed * delta);
    shipY += (int)(shipYSpeed * delta);
  }
change it something like:
Code: [Select]
  private void update() {
    shipX += shipXSpee;
    shipY += shipYSpeed;
  }
only adding "Display.sync(60);" into your game loop

and if your ship speeds are not fast enough/slow enough then change them till they are, and keep it at that specific value for each game cycle.

you will probably need to make sure either v_sync is off, also your screen refresh rate needs to be higher than 59hz to be accurate (My first method removes this issue though).
Title: Re: [solved] Best way to do time?
Post by: jediTofu on December 04, 2010, 22:36:48
Personally, I'd rather use delta; I want computers that can hit 100FPS to be able to use the available computing power, and I think that it's consistently smoother across all systems.  Without the delta, it's choppier in my tests.

I use Display.sync(1000) because without any Display.sync that was what my FPS was hitting.  It doesn't really matter anyway because with Display.sync the highest my FPS can go up to is 120 FPS, even if you were to put >120.  Any arbitrary high value of 120 or higher is fine.  60 seemed to be too choppy, so I'll just use 100.  The problem is that Thread.sleep is chopping more time than is expected of 1ms in Display.sync and the code itself chops time, so 60 is actually 50 or less, etc.  "timeLate" in Display.sync should probably be a higher amount.
Title: Re: [solved] Best way to do time?
Post by: bobjob on December 05, 2010, 22:36:23
Something else to think about, if your ever plan on making a multiplayer game.
If using a delta it might be hard to sync up gameplay. You would also have to make sure that the delta is calculated using strictfp, so it performs the same over all systems.
Title: Re: [solved] Best way to do time?
Post by: Evil-Devil on December 17, 2010, 10:16:30
I tried all three timers in the benchmark and their overall average is pretty close to each other. So i wonder which one to use when it comes to multiplayer.

And the ship demo had some fps bump that applied to all three timers ;)
Title: Re: [solved] Best way to do time?
Post by: bobjob on December 17, 2010, 10:45:25
I tried all three timers in the benchmark and their overall average is pretty close to each other. So i wonder which one to use when it comes to multiplayer.

And the ship demo had some fps bump that applied to all three timers ;)

to get real time multiplayer games working in sync, with a decent timer.

I find the a sync timer, with "lock step" style networking to be an easy and effective solution.
Title: Re: [solved] Best way to do time?
Post by: basil on December 20, 2010, 21:47:35
wicked benchmarker btw !  :D
Title: Re: [solved] Best way to do time?
Post by: bobjob on December 21, 2010, 01:29:27
wicked benchmarker btw !  :D
Thanx basil. Even though it still has a while to go, Im glad its starting to shape up.