Bad rendering loop?

Hey all,

I’m trying to make a frame loop which can be limited. The code I have written is intended to limit it to 60 frames per second. However, I only get 40 frames per second. When I remove my limiter code, I get more than 250 frames per second (and if I remove the random balls on the screen, I get over 800 FPS, without the limiter code). I’m hoping somebody here can shed some light on what I’m doing wrong or what I need to do.

Here’s my code:

import java.awt.*;
import java.awt.image.*;

public class FPS extends Canvas implements Runnable {
	private int FPS = 60;
	private int gameW = 1024;
	private int gameH = 768;

	private Graphics screen;
	private BufferedImage buffer;

	private int fps = 0;
	private	int frames = 0;
	private long lastLoopTime = System.currentTimeMillis();

	public FPS() {
		setSize(gameW, gameH);
	}

	public void addNotify() {
		super.addNotify();

		buffer = new BufferedImage(gameW, gameH, Transparency.OPAQUE);
		screen = buffer.getGraphics();

		(new Thread(this)).start();
	}

	public void run() {
		long next = System.currentTimeMillis()+(1000/FPS);

		while(true) {
			if (System.currentTimeMillis() >= next) {
				long last = (System.currentTimeMillis()-lastLoopTime);
				if (last >= 1000) {
					fps = frames;
					frames = 0;
					lastLoopTime = System.currentTimeMillis();
				}
				frames++;

				screen.setColor(Color.black);
				screen.fillRect(0, 0, gameW, gameH);

				gameLoop();

				next = System.currentTimeMillis()+(1000/FPS);
			}

			Graphics g = getGraphics();
			g.drawImage(buffer, 0, 0, this);
			g.dispose();
		}
	}

	public void gameLoop() {
		screen.setColor(Color.green);
		screen.drawString("FPS = "+fps, 100, 100);

		int w = 20;
		int h = 20;
		screen.setColor(Color.gray);
		for (int i=0; i<100; i++) {
			int x = (int)(Math.random() * (gameW-w));
			int y = (int)(Math.random() * (gameH-h));
			screen.drawOval(x, y, w, h);
		}
	}

	public static void main(String args[]) {
		Frame f = new Frame("FPS");

		f.add(new FPS());
		f.setResizable(false);

		f.pack();
		f.show();
	}
}

System specs are as follows:
CPU: 2GHz AMD Athlon 64-bit 3200+
RAM: 1GB DDR2 SDRAM
Video: 256MB nVidia GeForce 7600GT

Thanks in advanced,
Jamison


		if (System.currentTimeMillis() >= next) {

				// LOTS OF LONG STUFF

				next = System.currentTimeMillis()+(1000/FPS);
		}

The time between the if-check and the ‘next =’, is basicly lost…

Try:


long now = System.currentTimeMillis();
		if (now >= next) {

				// LOTS OF LONG STUFF

				next = now+(1000/FPS);
		}

Or:


		if (System.currentTimeMillis() >= next) {

				// LOTS OF LONG STUFF

				next += (1000/FPS);
		}

Wow, thanks so much. It works perfectly now. Limiting it to 60 FPS exceeds by about 3 (63 FPS) but every other frame rate is exact. Much is appreciated! Oh, by the way, I used the second method explained (next += 1000/FPS).

Limiting it to 60 FPS exceeds by about 3 (63 FPS)[…]

1000/60=16.6…
1000/16=62.5

Thats why the maximum framerate in games with a 1msec res timer, which are sorta kinda capped to 60, jumps between 62 and 63.

I normally fix that using:


long[] intervals = new long[]{16,17,17}; // 16.666666667
int counter=0;

		if (System.currentTimeMillis() >= next) {

				// LOTS OF LONG STUFF

				next += intervals[(counter++) % intervals.length];
		}

I only get around 24fps. Something as simple as that should run a lot faster, something seriously is killing the speed.

Have you applied Riven’s fix? As I stated before, I was only getting 40 FPS before. Here is the complete code, with his fix:

import java.awt.*;
import java.awt.image.*;

public class FPS extends Canvas implements Runnable {
	private int FPS = 60;
	private int gameW = 1024;
	private int gameH = 768;

	private Graphics screen;
	private BufferedImage buffer;

	private int fps = 0;
	private	int frames = 0;
	private long lastLoopTime = System.currentTimeMillis();

	public FPS() {
		setSize(gameW, gameH);
		setIgnoreRepaint(true);
	}

	public void addNotify() {
		super.addNotify();

		buffer = new BufferedImage(gameW, gameH, Transparency.OPAQUE);
		screen = buffer.getGraphics();

		(new Thread(this)).start();
	}

	public void run() {
		long next = System.currentTimeMillis()+(1000/FPS);

		while(true) {
			if (System.currentTimeMillis() >= next) {
				long last = (System.currentTimeMillis()-lastLoopTime);
				if (last >= 1000) {
					fps = frames;
					frames = 0;
					lastLoopTime = System.currentTimeMillis();
				}
				frames++;

				screen.setColor(Color.black);
				screen.fillRect(0, 0, gameW, gameH);

				gameLoop();

				next += 1000/FPS;
			}

			Graphics g = getGraphics();
			g.drawImage(buffer, 0, 0, this);
			g.dispose();
		}
	}

	public void gameLoop() {
		screen.setColor(Color.green);
		screen.drawString("FPS = "+fps, 100, 100);

		int w = 20;
		int h = 20;
		screen.setColor(Color.gray);
		for (int i=0; i<100; i++) {
			int x = (int)(Math.random() * (gameW-w));
			int y = (int)(Math.random() * (gameH-h));
			screen.drawOval(x, y, w, h);
		}
	}

	public static void main(String args[]) {
		Frame f = new Frame("FPS");

		f.add(new FPS());
		f.setResizable(false);

		f.pack();
		f.show();
	}
}

UPDATE: This also has ignore repaint set to true (the orignal did not) because it was causing repaint problems (screen flicker, bleeding) when the OS would attempt to repaint the screen.

Using BufferedImage for a back-buffer is a bad idea. You should use either VolatileImage or BufferStrategy (preferably the
latter). Basically, with your current code you don’t get any hardware acceleration: your rendering
goes to the an image in heap, and it is then copied to the screen.

Search around for examples on BufferStrategy or take a look at javadocs for jdk6
(much improved docs for how to use the BS):
http://java.sun.com/javase/6/docs/api/java/awt/image/BufferStrategy.html

Thanks,
Dmitri
Java2D Team

Tell me this: How is a BufferStrategy better than a BufferedImage when I can get about 500 frames per second with a BufferedImage (when nothing is rendered, just the back ground color and the FPS) and only about 150 frames per second with a BufferStrategy.

(NOTE: I could get much more than 500 frames per second if I did not use Thread.sleep())

When debuging my game I wrote a small test, BufferStrategy vs. BufferedImage, BS was noticably better. Here is a conclusion:

all this in windowed mode, later in thread Commander Keith told me BS should be even faster in full screen because it can use page flipping (pointer shifting).

So I guess your example is broken somewhere in rendering loop when using BS.

In my game, I’m drawing full-alpha PNG images and I changed my buffer to a BufferStrategy and I only got about 120 FPS. Why stick with 120 FPS when I can get over 500 FPS with a BufferedImage? It just makes absolutely no sense at all - it’s a huge speed decrease.

Tell me this: How is a BufferStrategy better than a BufferedImage when I can get about 500 frames per second
with a BufferedImage (when nothing is rendered, just the back ground color and the FPS) and only about 150
frames per second with a BufferStrategy.

Your follow up post answers that question. You are using some rendering pipeline, which does alpha stuff in software. So keeping the buffer over in software land is surely faster than going back and forth a zillion times a frame.

do you just draw into the bufferedimage or do you also once per round draw it to screen?

Obviously, yes, I draw it to the screen. Why wouldn’t I? If I didn’t than nothing would be visible… Look at the code.

Jamison: 1.) I wonder why your tone is so agressive. Theres absolutly no need to do so!

2.) You are rendering stuff which can’t be done in hw with the java2d-backend you are using. This triggers readbacks from vram->ram which is slow. Run you application with -Dsun.java2d.trace=count to see where software rendering happens and then try to alter your code, and/or experiment with other rendering pipelines (OpenGL for example).

lg Clemens

I think what they’re saying is that the BufferStrategy is using “hardware acceleration” by using DirectDraw/Direct3D whereas the BufferImage is not, its all in software (ie no graphics card & VRAM, just the CPU & normal RAM).

In this case it seems like the hardware ‘acceleration’ is actually slower than software mode, thus your slow-down.

To make sure this is the problem, compare your two approaches when both use the noddraw option so neither have hardware acceleration. This VM option is: -Dsun.java2d.noddraw=true

from: http://java.sun.com/j2se/1.5.0/docs/guide/2d/flags.html

Keith

(PS: this is a hairy topic full of wierd performance things so good luck! Not even the J2D team can work out which pipeline is the best for different computers but they’re working on it…)

I’m sorry about the tone. It’s just this is getting me so agrivated as to why I only got so low framerate while almost every other game I’ve ran gets over 1200+ or something.

Also, those -Dsun.java2d.* commands don’t seem to do anything. But am I using them correctly? I put them in a BAT file to run my game:

@ECHO OFF

java FPS -Dsun.java2d.trace=count
pause

Let me also point out that my screen size (not my actual monitor, but the game’s screen, the buffer) is only 320x240. If I change that to 1024x768, I get less than 60 frames per second.

Well, noddraw just disables the use of direct draw, as far as I know this does not mean everything is unaccalerated (however I could be wrong).

@jamison: I don’t know wether its ok to specify the -D arguments after the main class, better try java -Dsun.java2d.trace=count FPS

You’ll see how often stuff was calles, whereas names with java2d.loopsn in it are in general “bad” -> loops mean stuff is done in Software, however this is only a problem if the count is very high. (-> frequently using software rendering)

lg Clemens

But am I using them correctly?

No you hand that parameter over to your program and not over to java.

java [switches for java] YourProgram [switches for your program]

Okay, I got the -Dsun.java2d.* stuff to work. And trace=count just outputs some weird stuff. Here’s the output, I can’t understand it.

3 calls to GDIFillRect
31769 calls to sun.java2d.loops.Blit::Blit(IntRgb, SrcNoEa, IntRgb)
2787 calls to sun.java2d.loops.DrawGlyphList::DrawGlyphList(AnyColor, SrcNoEa, A
nyInt)
26210 calls to sun.java2d.loops.Blit$GeneralMaskBlit::Blit(IntArgb, SrcOverNoEa,
 IntRgb)
1 call to sun.java2d.loops.TransformHelper::TransformHelper(IntArgb, SrcNoEa, In
tArgbPre)
1858 calls to sun.java2d.loops.FillRect::FillRect(AnyColor, SrcNoEa, AnyInt)
3 calls to GDIDrawRect
931 calls to sun.java2d.windows.GDIBlitLoops::Blit(IntRgb, SrcNoEa, "GDI")
26210 calls to sun.java2d.loops.MaskBlit::MaskBlit(IntArgb, SrcOver, IntRgb)
6790 calls to sun.java2d.windows.DDBlitLoops::Blit("Integer RGB DirectDraw", Src
NoEa, "Integer RGB DirectDraw")
96562 total calls to 10 different primitives