Solving Stuttering With Fixed Timesteps, Once and For All

Ohhh yes, you’re right. Sorry, I’d forgotten that the stuff I posted on TIGSource is a little older; the build I’m currently testing on my own doesn’t use Type 2 (Paint) anymore. I think Type 1 (BufferStrategy) should still be relevant, though, although I did refactor stuff a bit.

Wait, really? I guess this may be the case on my laptop, but on our desktop there seems to be quite a difference. Note: I still have yet to test on my university’s computers.

Holy crap, this is weird.

Alright, so, I tested the examples on my Desktop PC (win7 64bit, i5, Radeon 6870 )

First of all, choosing the Paint option produces extremely smooth movement in all character ( unlike the laptop ).

Secondly, when I run it with double buffering, I get HORRENDOUS stuttering! However, when I close the application and open it again, I suddenly get smooth movements ( although still sometimes stutterring and overall worse than what I get without double buffering ).

So I decided to open up 5 windows of the game. That is, 5 games with double buffering running at the same time.

I noticed that some of the windows had smooth movement, and others had HORRIBLE and moderate stuttering. They were consistent with their stuttering by the way. The window with horrible stuttering was always stuttering, and the ones with less were moderately so consistently, and those without much stuttering also consistently so. I also opene up a game with no double buffering wich continued to produce extremely smooth movement - unlike the double buffered versions.

What could be the cause for this? I’m assuming that the start time of the applications comes into play. Ie System.CurrentTime() has something to do with this. I’m not sure however.

One thing to note however, when I ran 7+ games at the same time, they ALL had SIMULTANEOUSLY ( even the ones without double buffering ) a “heavy” stutter moment that would last like a ½ second. That is the screens would sort of freeze and appear in their correct positions as if the freeze had never happened.

I think it stutters exactly the same way as Cero’s bouncing box, but it’s almost impossible to see because 1. it doesn’t use just a box and 2. they aren’t moving at 1 pixel per update.

  1. The & is a bitwise and operation. Google it.

  2. Movies look somewhat fluid at 24 FPS because it is recorded. Consider how a camera works. It takes in light during a certain time and saves it to a film or a memory card or whatever, while your game provides a perfectly sharp image which is a snapshot of a single moment in time, like a camera with an infinitely short shutter time that takes images at a certain interval. If you have good motion blur and other effects, people will be more forgiving of low FPS, but it’s still better to have higher FPS for less delay and better responsiveness. BTW, you know those cheap drama series that run at hours when nobody watches television that look “fake”. They are shot with 30 FPS cameras. Newer TVs also do frame interpolation to increase the FPS by generating interpolated frames in between the video frames by predicting movement. The difference is very easy to see for me, even more so for anime where the image is sharp like games.

  3. Your SNES never had this kind of random stuttering because it didn’t have the same graphics pipeline as you have in a computer. And stuttering due to mismatched frame rate surely existed. Rendered at either 25 or 30 FPS.

It’s an artifact of using nearest neighbor sampling for the sprites (= rounding errors like you said). The “problem” is related to sprites having integer positions and no texture interpolation. The game is experiencing the same amount of stuttering as the Cero’s bouncing box demo in Java due to frames never being displayed, but it’s hidden by the inherent choppy movement of the sprites.

Both paint and BufferStrategy use double buffering. BufferStrategy is meant for active rendering (repainting every frame, like in a game) while paint() (and Swing in general) is meant for GUIs that only need triggered repaints (mouse clicked a button, a window was shown, e.t.c). Both use double buffering to avoid flickering. BufferStrategy might use page-flipping though, which does the same as double buffering but avoids a copy from the back buffer to the front buffer.

I’d say the GM one stutters a lot more due to the position rounding. With antialiasing and/or texture interpolation it would stutter exactly as much as non-VSynced Java games. Why? Because your monitor is connected to your graphics card.

I’d blame this on Java2D + garbage collection? I’m not a Java2D pro in any way, but a decent graphics library shouldn’t do that. Try to measure the frame time of each frame (System.out.println the delta of each frame).
Note that stuttering that lasts several frames is NOT caused by the graphics pipeline or your graphics driver if you don’t exhibit this problem in other programs. It’s basically Java’s fault. Just use LWJGL. xD

Ah so that paint() is swing. Is it weird that Swing produces better graphics for games even though it’s only meant for simple GUIs?

I’ve almost explicitly used Swing graphics in all my java apps that requires something to be drawn. That is, using JFrame and JPanel. And with those I’ve never experience stuttering. Albeit I’ve only done simple things. But the first application I did was have ball bounce from the sides of the screen and in that app I used only awt with my own implementation of double buffering and did experience stuttering.

Is there a good reason to use Java2D over Swing in anything?

Swing uses Java2D :stuck_out_tongue:

<.< I meant why not always use Swing instead of the the Java2D methods that don’t use Swing.

[EDIT]: It’s worked out fine for me. And considering what’s been seen on this thread it seems to give minimal stuttering compared to BufferStrategy for instance.

Because Swing is a GUI library, not a graphics library. If you want to use GUI items like buttons, menus, etc… then use Swing. Otherwise, it’s an unnecessary overhead.

Is it? It seems to produce more stutterfree graphics… :o

(Was JGO down for a bit?)

Good point, I might make another example like that.

That’s what Cero’s example uses, right, and it still causes stuttering. Is it better than Java2D? I don’t think we’ve determined that quite yet. Also, would Slick2D be a suitable equivalent option?

I see. I thought I observed more stuttering with Paint than with BufferStrategy, personally.

Yeah :frowning:

Java2D is slow. LWJGL is a native binding to OpenGL, which means you can directly access the graphics card. Slick2D has a Java2D-like API but uses LWJGL underneath, so it’s fast for relatively small games.

OpenGL has several hundred times more performance than un-accelerated Java2D (the OpenGL acceleration for Java2D is unreliable in my experience). It also gives you access and control over the only thing that will solve the stuttering that you experienced with Cero’s example, VSync. You’ll also have to use something more accurate than Display.sync(60) though, but that’s a wheel that has already been invented.

I see. Well, if I can’t use Java2D and can’t use Display.sync(60), what would the game loop look like with a fixed timestep? Would I just use Display.update() in the place of g.dispose() and strategy.show()? What if it still stutters? Any possibility of providing example code for a loop? I’m curious as to how you all go about doing this, because, if I remember correctly, when I tried LWJGL I got more stuttering than with Java2D.

My game loop:


canvas.createBufferStrategy(3);
BufferStrategy strategy = canvas.getBufferStrategy();

int frames = 0;
int currentFPS = 0;
long time = System.nanoTime();
long lastTime = System.nanoTime();

canvas.requestFocus();

while(isActive()) {
	long diffTime = System.nanoTime()-lastTime;
	lastTime += diffTime;
	
	int updateCount = 0;
	
	while(diffTime > 0 && (maxUpdates <= 0 || updateCount < maxUpdates) && !isPaused()) {
		int fps = FPS > 0 ? FPS : 60;
		long deltaTime = Math.min(diffTime,ONE_SECOND/fps);
		
		try{
			update(deltaTime);
		}
		catch(Exception exc) {
			exc.printStackTrace();
		}
		
		diffTime -= deltaTime;
		
		updateCount++;
	}
	
	try{
		do{
			do{
				Graphics2D g = (Graphics2D)strategy.getDrawGraphics();
				g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,quality);
				
				try{
					paint(g);
				}
				catch(Exception exc) {
					exc.printStackTrace();
				}
				
				g.dispose();
			}while(strategy.contentsRestored());
			
			strategy.show();
		}while(strategy.contentsLost());
	}
	catch(Exception exc) {
		exc.printStackTrace();
	}
	
	frames++;
	
	if(System.nanoTime()-time >= ONE_SECOND) {
		time = System.nanoTime();
		currentFPS = frames;
		frames = 0;
	}
	
	try{
		if(FPS > 0) {
			long sleepTime = Math.round((ONE_SECOND/FPS)-(System.nanoTime()-lastTime));
			if(sleepTime <= 0)
				continue;
			
			if(isHighQuality()) {
				long prevTime = System.nanoTime();
				while(System.nanoTime()-prevTime <= sleepTime) {
					if(useYield)
						Thread.yield(); //Smoother game loop, LOTS of CPU
					else
						Thread.sleep(1); //Less smooth, not as much CPU
				}
			}
			else
				Thread.sleep(Math.round(sleepTime/1e6)); //Least smooth, very little CPU
		}
		else
			Thread.yield();
	}
	catch(Exception exc) {
		exc.printStackTrace();
	}
}

Java2D is slow. Display.sync(60) is inaccurate (syncs to 63 FPS due to rounding the sleep time each frame from 16.6666667ms to 17ms). I wouldn’t use them. xd

I assume you’re converting from Java2D to LWJGL. Display.update() is equal to g.dispose() + strategy.show(). Take a look at this game loop article for some basics in implementing a good game loop:
http://www.koonsolo.com/news/dewitters-gameloop/
The last “best” game loop with interpolation is probably waaaaay overkill. The “Constant Game Speed with Maximum FPS” should be good.

You can do syncing with an LWJGL Timer object or System.nanoTime() but you might want to still use the endless sleep thread fix. oth have good accuracy.

To implement sleeping, just measure the time it took to update and render the frame, and sleep for the remaining time of the frame time (frame_time = 1000 / frame_rate milliseconds). Be sure to prevent error build-up in the timing.

If the stutter is still unacceptable because you’re wasting hours sitting with a high-speed camera recording a bouncing box running at 1 pixel per update through a microscope instead of actually making your game, you’ve done what you can on this side of the hardware, and the only thing you can do is enable VSync, which is just Display.setVSyncEnabled(true); =D

@Ra4kings game loop:
Are you sure doing Thread.sleep(1) x times is more accurate than just a single Thread.sleep(x)? I mean, if Thread.sleep() has so bad accuracy, isn’t it worse to try to sleep for even shorter times? And you shouldn’t worry about the performance of doing 20 Thread.sleep()s per frame instead of one. It doesn’t use any measurable amount of CPU time at all. Thread.sleep(1) is going to sleep thousands or even millions of times more cycles than it is spending going into and out of sleep.

Also: Thread.sleep() causes considerable jitter on most systems due to the vagaries of the thread schdulers on each. Thread.yield() gives better results but canes batteries on laptops. Doing a busy loop is the best option but is generally quite unfriendly :slight_smile:

Cas :slight_smile:

I use Thread.sleep(0) instead of 1
I think it works better actually

Thank you, ra4king, for your loop code, and thank you everyone in general for your continued feedback!

Well, I guess I will try implementing LWJGL again, then, but I probably won’t be able to work on that for a while. In the meantime, please feel free to keep the discussion going! Perhaps someone else could try their own hand at producing a smooth “bouncing box” example using all of this new information?

I appreciate the hyperbole, but know that the stuttering seems to make the version of my test program with more than just bouncing sprites actually seem pretty legitimately unplayable on my desktop… (with GM still running just fine)

It should be noted that quite a few AAA titles do a busy loop. They are often forced to add a different mechanism in a patch later for laptops with the complaints they get and at least one game caused some video card to overheat. So even outside the java world it is clearly not that easy to solve.

@Saucerking - and how about you, with my games? Any stuttering (windowed/fullscreen)?

Cas :slight_smile:

As I posted near the beginning of this thread, it is theoretically (and unofficially!) possible to request a VSynced buffer strategy in an AWT window using ExtendedBufferCapabilities. How well and when this works is another matter (and as my whole bloody desktop doesn’t VSync, not easy to play with!)

Also see the code in SwingUtilities3 and BufferStrategyRepaintManager.

I’d think it extremely unlikely that this has been enabled by default, but maybe Swing is VSyncing?!

PS. None of the above should in any way be regarded as advice not to use OpenGL! :wink: