[solved] Best way to do time?

Started by jediTofu, December 02, 2010, 08:17:45

Previous topic - Next topic

jediTofu

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?)
 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.
cool story, bro

ryanm

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

bobjob

Recently after looking into a few different ways of dealing with time I decided to use the following approach:


int updates, framesPerSecond = 75, logicPerSecond = 60;

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


main loop (GL Thread)
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)
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


Matthias

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.

bobjob

Quote from: 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.
Really? 1 thread only uses addition, and the other thread only uses subtraction, I cant see any conflicts.

Matthias

These operations are not atomic. The "updates++" executes like this:
int tmp = this.updates;    // memory read
tmp = tmp + 1;
this.updates = tmp;        // memory write


And your "updates -= performs" executes like this:
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.

bobjob

Thanks, thats good to know. Ill change it then.

*synchronized the update value change

jediTofu

Thanks guys for all the replies!

Quote from: 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

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

Quote from: bobjob on December 02, 2010, 11:06:16
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

jediTofu

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.
cool story, bro

kappa

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.

bobjob

Quote from: jediTofu on December 02, 2010, 20:30:29
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.

jediTofu

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

bobjob

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.





jediTofu

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

bobjob

Quote from: 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.
thanx, good to know. I guess for Windows7 64bit LWJGL is the way to go.