Solving Stuttering With Fixed Timesteps, Once and For All

Link worked from me in the email sent by JGO on my Android phone :slight_smile: Yeah that’s the loop all my games use now. Uses double floating point precision to calculate how many frames it should have ticked at by a given time, resets every now and again for sanity, and vsync takes care of fullscreen timing nicely but otherwise it seems to render nicely enough in windowed mode. I don’t care much about windowed mode though. Proper games run fullscreen for that immersive experience :slight_smile:

Cas :slight_smile:

Oh yeah and go ahead, all my code is public for a reason.

Cas :slight_smile:

Thank you!!!

Looks like I spoke too soon:

I had already known that, sometimes, our desktop (which usually yields the most visible stuttering) runs the Java programs I’ve been testing pretty well for some reason; but only for a while, and in a currently unpredictable manner. Even the Java2D builds run smoothly in this situation! It seems that this had been the case when I was testing my LWJGL examples earlier, because yesterday they seemed to yield lots of stuttering… Looks like I might try to give Cas’s method a go!

EDIT:

[quote]Admittedly, I don’t know how this setting fares on a computer on which vsync works; it may have to be turned off on such computers. But then again, on such a computer, vsync might yield smooth rendering on its own.
[/quote]
Looks like the program runs just fine under these particular settings, at least as far as I can tell.

By the way: currently, the only way I know how to test if vsync has an effect is to turn off Display.sync (and frequent yielding and sleeping) and see if toggling vsync does anything. On my school’s computers the FPS drops from several hundred (without vsync) to 60 (with vsync), whereas on our desktop and my laptop the FPS seems to stay at several hundred regardless of the state of vsync reported by the program.

Also, I’m still not really sure about the following point:

[quote]I guess it’s also possible that the smooth rendering that seemed to be achieved with vsync on the computers at my school could only have been achieved with the speed of those computers (they seem pretty fast).
[/quote]
In other words, I don’t know if my programs might still yield stuttering on other computers for which vsync has an effect; it could just be that, as usual, the main reason my programs run smoothly on my school’s computers is that they’re fast (or more powerful in some other fashion).

Added three new examples based on code by Cas, Notch and Tommy: box.com download (Note: haven’t tested these much yet)

Still haven’t made it so the programs detect the refresh rate of the display.

A question for Cas, if it’s alright to ask: In your code, how does the following code work? How is it different from calling just Sys.getTime?

Sys.getTime() & 0x7FFFFFFFFFFFFFFFL

Sys.getTime() & 0x7FFFFFFFFFFFFFFFL

The L indicates the value is a long integer (64 bits).
The bit-and operation sets the sign bit to zero, assuming that the long is signed. Thus the getTime value is guaranteed to be positive. Maybe you knew that and the question is why it would be necessary to reset the sign bit?

No, I didn’t know that; thank you for the explanation. Again, I’m kind of a novice… Please bear with me!

So is it basically a faster way of doing abs(x), or something along those lines?

The GM Example is smooth, the rest is not :frowning: It really bugs me. In december I hopefully have the time to work on my perfect smooth loop for java :smiley: Thank you for putting everything together!

The mask causes the time to wrap around and restart at 0 instead of Long.MIN_VALUE. This is important if you want to be able to generate consistent non-negative timestamps at some point after the heat death of the universe.

That would depend on the resolution of Sys.getTime() and what number it starts at of course.

Cas :slight_smile:

There are 292 years in 2^63 nanos. Your point about the start value is correct, but I doubt any CPU ever created has ever had its counter high-bit set.

That’s what I thought until exactly this somehow happened on my Galaxy S 2.

Cas :slight_smile:

O_______O

Wait, I thought the wrapping was achieved later in the code by something like:

if (now < then) { }

…Yes?

More observations (although… note that most if not all of my observations throughout both the TIGForums thread and this thread are basically subjective…):

Recently I seem to have observed that the GM example actually stutters more than I had thought. I think it’s usually better than the Java examples and all around GM’s the most consistent, but currently I suspect that:

-When GM stutters it can be more noticeable than some Java implementations? Especially on (only on?) those computers on which LWJGL’s vsync can take effect.

-GM usually tends to run smoothly for longer periods of time than the Java examples, but not? on those computers for which the Java examples don’t stutter (i.e. my school’s computers). Furthermore, on those computers GM seems to alternate between no stuttering and stuttering more frequently than the Java examples.

Also, another issue: While the simple GM example stutters on those school computers, I had difficulty detecting any stuttering (there was some stuttering, but very little of it) with the demo for a game of mine, Platform Battle 2. In fact I think this might also have been the case during some trials on our desktop (although Java stutters on it). This may have something to do with the implementation:

-With the simple example, nothing is scaled and the box is moved at 2 pixels, whereas, in PB2, although the object I observed (a moving platform in the tutorial) still moves at 2 pixels, the graphics are scaled to two times its “actual” size by a method by ChevyRay.

-PB2 uses a sprite for the platform and has a patterned background. (I do, however, think that I looked pretty closely…)

Install FRAPS demo and report the FPS.

Cas :slight_smile:

Wait, isn’t the FPS reported by the program enough?


I updated the box.com download with a new example. If I understand correctly, it’s a sort of hybrid between Cas’s example and Eli Delventhal’s. I’m not sure if it’s any smoother than any of the other Java examples, but there’s at least one new thing being done here.

I noticed that Cas’s example can loop many times without CPU problems. I think this means that, even if one of these example programs loops many times, the CPU only starts groaning if it also renders very fast (although it seems that the CPU still starts groaning if the looping is too fast). I tried looping like this in the hybrid example because it seems to me that, since the program checks the time more often, the timing would be more precise.

Also, I incorporated Eli Delventhal’s timing method but applied it not only to logic updates but also to the rendering, which amounts, if I understand correctly and if I’m using the right terms, to making logic and rendering almost completely independent of each other in a sense. Put another way, they’re each sort of on their own schedule.

Again, I can’t really tell if all this allows the program to run any better than any of the other examples, but there it is anyway.

Here’s some of the relevant code:

public void start() {
		try {
			Display.setDisplayMode(new DisplayMode(800, 600));
			Display.create();
		} catch (LWJGLException e) {
			e.printStackTrace();
			System.exit(0);
		}
		
		initGL(); // init OpenGL
		
		vsync = false;
		dsync = false;
		wait = true;
		drawInterpolated = true;
		
		timerResolution = 1000000000;
		updateRate = 60;
		renderRate = GraphicsEnvironment.getLocalGraphicsEnvironment()
				.getDefaultScreenDevice()
				.getDisplayMode().getRefreshRate();
		if (renderRate == java.awt.DisplayMode.REFRESH_RATE_UNKNOWN) renderRate = 60;
		
		double updateInterval = timerResolution / (double) updateRate;
		double renderInterval = timerResolution / (double) renderRate;
		double lastFPSTime = System.nanoTime();
		double lastUpdateTime = lastFPSTime;
		double lastRenderTime = lastFPSTime;
		int updateCount = 0;
		int renderCount = 0;
		int loopCount = 0;
		updatesPerSecond = 0;
		rendersPerSecond = 0;
		loopsPerSecond = 0;
		maxUpdates = 5;
		
		Display.setVSyncEnabled(vsync);
		
		while (!Display.isCloseRequested()) {
			double now = System.nanoTime();
			int localUpdateCount = 0;
			while (now - lastUpdateTime > updateInterval && localUpdateCount < maxUpdates) {
				update();
				lastUpdateTime += updateInterval;
				localUpdateCount++;
				updateCount++;
			}
			if (now - lastUpdateTime > updateInterval) {
				lastUpdateTime = now - updateInterval;
			}
			now = System.nanoTime();
			if (now - lastFPSTime > timerResolution) {
				lastFPSTime = now;
				//lastFPSTime += timerResolution;
				updatesPerSecond = updateCount;
				rendersPerSecond = renderCount;
				loopsPerSecond = loopCount;
				updateCount = 0;
				renderCount = 0;
				loopCount = 0;
				Display.setTitle(updatesPerSecond
						+ ", " + rendersPerSecond
						+ ", " + loopsPerSecond
						+ "; VSync: "+ vsync
						+ "; Display.sync(): " + dsync 
						+ "; Interpolated drawing: " + drawInterpolated 
						+ "; Yield and sleep: " + wait);
			}
			interpolation = (float) ((now - lastUpdateTime) / updateInterval);
			now = System.nanoTime();
			if (now - lastRenderTime > renderInterval) {
				renderGL();
				Display.update();
				//lastRenderTime = now;
				lastRenderTime += renderInterval;
				renderCount++;
			}
			while (now - lastRenderTime > renderInterval) {
				lastRenderTime += renderInterval;
			}
			loopCount++;
			if (dsync) {
				Display.sync(60); // cap fps to 60fps
			} else if (wait) {
				Thread.yield();
				try {
					Thread.sleep(1);
				} catch (Exception e) {}
			}
		}
		
		Display.destroy();
	}

FRAPS tells it like it is :slight_smile:

Cas :slight_smile:

Well, I think I’ll have to look into it more to be able to use it?


Two things:

a) You know, I think I’m just going to incorporate the “extra sleeping thread” fix; with it, at least three of the Java examples seem to work just fine, as far as I can tell so far. The Java examples still stutter on my laptop, but so does the Game Maker example. Note: the download has not yet been updated.

I still feel that this solution is a bit inelegant, though. The extra thread forces the program to use a more precise (?) timer, right? Does that mean that functions like System.nanoTime() become more precise, that Thread.sleep becomes more precise, or both? I’d like to think that it’s Thread.sleep; it would be nice to know that the timing, at least, is always reliable.

Does anyone know why the reliability of Thread.sleep would be so conditional (?)? Will they ever make it so Thread.sleep always behaves as it does with this solution?

b) Before incorporating that solution, though, something strange happened on my laptop: it started acting like our desktop for a moment! That is, the Game Maker example seemed to run more smoothly than usual, and some if not most of the Java examples were less smooth than usual and couldn’t maintain 60 FPS. The programs started to behave as normal after I restarted the computer; this sometimes works on my desktop. Thus, it seems to me that there are (at least) two common computer settings, one in which Game Maker and Java behave similarly, and one in which Game Maker gets less stuttering than usual but in which Java gets more. What could be the cause of this?

btw really appreciate your work here Saucer.
My game still jitters when there not much going on.

And the “DelventhalExample” looks the best to me. Gotta try that. Nice package

Thanks for the input, Cero! It’s been a while since I’ve visited this topic, ha ha.

Have you tried the “extra sleeping thread” method? It seems that the fix:

  • has no effect on computers that yield little stuttering anyway,
  • reduces most stuttering on the computer I tested that yielded the most stuttering, and
  • has no effect on my laptop, on which the GM test program and my programs all stutter.

Note that I haven’t updated the downloads with this fix.

By the way, although I had known about that fix from when this thread was started, it seemed too inelegant. Eventually, I decided to incorporate it into my programs.

as yes you mean this


new Thread() { 
{ setDaemon(true); start(); } 
@Override public void run() { while(true) {try {Thread.sleep(Integer.MAX_VALUE);}catch(Throwable t){} } } 
}; 

sure, I use this.

Also I have to add. the stutter, in general, seems to happen more often when there is not a much going on.
For example in m game, when I’m just walking and there is nothing fancy going on, its more likely to happen than when there is more going on.
This would also explain why my simple cube example is so very vulnerable to this, and seems to stutter somewhat in all game loop examples
So my guess is that, why a game would run at a very high FPS normally, like > 600 or something, slowing it down to 60 can cause stutters way more likely than slowing it down form like 200 fps or 100 fps to 60.

so I guess this is the bottom line: The higher your fps WOULD BE, the more the slowing down has to be done, the higher the chance of stuttering is.
This doesn’t seems to apply for fullscreen Vsynced.