Am I calculating the FPS right?

This is my main loop:

	public void loop() {
		while (gameRunning) {
			fpsCounter++;
			
			if (isFocusOwner()) {
				long now = System.currentTimeMillis();
// --------------- I calculate the FPS here -----------------------
				if (now - oneSecond >= 1000) {
					oneSecond = now;
					fps = fpsCounter;
					fpsCounter = 0;
				}
// -------------- I sync the game speed here -------------------------
				if (now - Globals.timer >= Globals.frameDiff) {
					Globals.timer = now;
					doAction();
				}

				doDraw();
			} else
				try { Thread.sleep(100); } catch (Exception ex) {}
		}
	}

OK, so what I do here is this:

  • during each loop I increase fpsCounter
  • I check if 1 second has passed since I last updated oneSecond
  • if one second has passed, I pass the value of fpsCounter to fps (to be printed out on the screen) and I reset fpsCounter to zero; also, I update oneSecond to the current time

With this I get that my game is running at 13 FPS! That si incredibly slow, I was expecting something around 70, not less than 60 in any case. Am I calculating the FPS right?

If not, then is 13 FPS normal for Java games? I remember a year ago when I worked with C++ it was always around 70 FPS… ???

Yea the fps counter is alright.

If not, then is 13 FPS normal for Java games?

No.

You kick your main thread into zzz land for ~100msec each frame. Remove that line and the fps will go up.

See here how to do throttling with a crap timer (such as currentTimeMillis):
http://www.java-gaming.org/forums/index.php?topic=11640.msg93602#msg93602

edit: on a second thought… that sleep stuff is in the else branch… no idea what you’re doing thats so slow. Fullalpha with java2d perhaps?

Yeah I send it to sleep when the window is not focused, so it would not use 100% of the CPU.
I am using Java2D and I use translucent images. I load each image, create a translucent counterpart and make all the pink pixels transparent.

Here’s some of the stuff I do, if anyone notices what I should change just let me know.

JFrame initialization:

	public Game() {
		super(Settings.getProperty("game.title"));
		setResizable(false);
		setBounds(0,0,Globals.RESOLUTION_X,Globals.RESOLUTION_Y);
		setSize(Globals.RESOLUTION_X, Globals.RESOLUTION_Y);
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		setIgnoreRepaint(true);
		gc.setLayout(null);
		setUndecorated(true);
		gc.setBackground(Color.BLACK);
}

Graphics mode initialization. First I check whether it should be fullscreen or not, then I set the display mode to 800x600x32

	/** Initialize buffers and fullscreen */
	private void initGraphics() {
		if (Settings.getPropertyBool("display.fullscreen")) {
			if (Globals.GRAPHICS_DEVICE.isFullScreenSupported())
				Globals.GRAPHICS_DEVICE.setFullScreenWindow(this);
			DisplayMode[] displayModes = Globals.GRAPHICS_DEVICE.getDisplayModes();
			int selected = 0;
			for (int i=0; i<displayModes.length; i++) {
				if (displayModes[i].getWidth() == Globals.RESOLUTION_X &&
						displayModes[i].getHeight() == Globals.RESOLUTION_Y &&
						displayModes[i].getBitDepth() == Globals.BPS)
					selected = i;
			}
			Globals.GRAPHICS_DEVICE.setDisplayMode(displayModes[selected]);
		}
		
		createBufferStrategy(2);
	}

I load images and make them transparent like this:

	public static Color TRANSPARENT_COLOR = new Color(255,0,255);
	private static HashMap pictures = new HashMap();
	
	public static Image createTransparentImage(String path) throws IOException {
		// no need to load the image twice
		if (pictures.containsKey(path))
			return (Image)pictures.get(path);
		
		File file = new File(path);
		if (!file.exists())
			throw new IOException("File not found: " + path);
		
		BufferedImage image = ImageIO.read(file);
		
		BufferedImage result = Globals.GRAPHICS_CONFIG.createCompatibleImage(image.getWidth(),image.getHeight(),Transparency.TRANSLUCENT);
		Graphics g2 = result.getGraphics();
		// draw image on the transparent image
		g2.drawImage(image,0,0,null);
		g2.dispose();
		// make the pixels transparent
		DataBuffer dbuffer = result.getRaster().getDataBuffer();
		// get array of pixels
		int[] imageData = ((DataBufferInt)dbuffer).getData();
				
		int r,g,b;
		int red = TRANSPARENT_COLOR.getRed();
		int blue = TRANSPARENT_COLOR.getBlue();
		int green = TRANSPARENT_COLOR.getGreen();
		for (int i=0; i<imageData.length; i++) {
			r = getR(imageData[i]);
			g = getG(imageData[i]);
			b = getB(imageData[i]);

			if ( (r == red) && (b == blue) && (g == green) ) {
				imageData[i] = 0;  // set pixel to 0 (also sets its alpha to 0)				
			}
		}		
		
		pictures.put(path, result);
		
		return result;
	}

I tried changing the Transparency.TRANSLUCENT to Transparency.BITMASK but this only made the game improve to 16 fps

Oh yeah, and all the images are 64x64 pixels. Now I remember there was a limit in Java that if an image is too large it won’t be accelerated. What was this limit?

I’m drawing out a map, so far only the walls and corridors. On top of the map I draw a cursor. I’ve just checked, and if I comment out the line which draws the map, the FPS goes up to 370. So obviously it is the map images that cause the problem… :-\

If I get the image with createCompatibleVolatileImage() then the FPS goes up to 70-90, but then it isn’t transparent. Even if I save a GIF image with a transparent background, it will make the background white.

I also tried resizing the images to 32x32 but the FPS remained the same…

I also tried using the -Dsun.java2d.ddforcevram=true arguement, but it didn’t do a thing…

With rendering hints set for speed I can get no more than 16 FPS…

Graphics2D g = (Graphics2D)Globals.CANVAS; g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); g.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED); g.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED); g.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE); g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_OFF); g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED); g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);

Obviously I need to do something with those images…

OK now I’ve tried loading the image not as a BufferedImage but as a simple Image:

Image img = Toolkit.getDefaultToolkit().getImage(path);

Interesting that this way I get 70 FPS. Still, this way I have to make the images transparent in a drawing program, and can’t do it myself inside the application… but if I can’t find any other way then this will be good as well…

Dude, use edit on your post to add new comments… this is ugly and unreadable what you do.

In windows under DirectX (default) alpha images aren’t accelarated. Try to use OpenGL pipeline and see how it works, search forums to see how to enable it, it’s a command at startup or a line of code of system proparty. Buffered images try their best to be accelerated, I think you should use them. If you load the image and then work anything on it, it loses accelerration. When you’re done with your work, create a new BufferedImage with that data.

That’s it, that actually made it work!!! ;D

I create a compatible BufferedImage, pass through its data array and turn all the pink pixels into transparent. Then finally I create a new compatible image, and paint the modified BufferedImage on it.
Looks like the problem all along was that I modified the compatible image’s data array, so it stopped being accelerated.

Thanks for the help, sorry for spamming ;D

Extremely minor nit-pick, but you’re leaking a little bit of time once every second
If you change “oneSecond = now;” into “oneSecond += 1000;”, you stop the leaking. It probably won’t affect the reported fps, though. :wink:

I made the changes and it dropped from 70 to 69 FPS!
http://www.fenomas.com/tomatobb/images/smiles/nnf_yikes.gif
;D

Just change it back for better performance :wink: ;D

As far as I know, currentTimeMillis() is inaccurate, at least with windows. Try nanoTime() instead and remember to divide by 1.000.000 to convert nano to milli.

I cant exactly tell what you do with your transpant image, but there might be better ways. CreateCompatibleImage has the transparency options opaque, bitmask and translucent. If you just want masked images (pixel is visible or not, but no alpha), use bitmask transparency.
You can read transparent GIFs (use bitmask transparency) and PNGs (support full alpha information) and draw them on a BufferedImage. So you dont need all this per pixel computation.

-JAW

You can actually use Thread.sleep(long milis, int nanos), which means you can do this; Thread.sleep(nanoTime/1000000,(int)nanoTime%1000000); also note that the sleep is also inaccurate in itself, there is a couple ms delay in the call and the shorter you get it to sleep the more it will be inaccurate. You basicly have to compensate that aswell if you really wanna get accurate;

This is an example of a piece of code out of the book “Killer Game Programming in Java” which covers for it;


// Number of frames with a delay of 0 ms before the animation thread yields to other running threads.
private static final int NO_DELAYS_PER_YIELD = 16;
private long period = 10000000; // 10000000ns (10ms) giving 100 FPS 
....
// Time handling code in mainloop;
beforeTime = System.nanoTime();
...
doGameLogic(); // movement, collision, drawing etc.
...
afterTime = System.nanoTime();
timeDiff = afterTime - beforeTime;
sleepTime = ( period - timeDiff ) - overSleepTime;   // time left in this loop

if ( sleepTime > 0 ) {
       	try {
     		/* Sleeptime is in nanosecs, and there is currently no Thread.sleep(nanosecs)
    	         * only Thread.sleep(long ms, int nanosecs)
    	         * since theres 1000000ns to 1ms divide by 1000000 and ns is mod 1000000;
       		 */
       		Thread.sleep ( sleepTime / 1000000, (int) sleepTime % 1000000 ); 
	        		
       	} catch ( Exception e ) {}
       	// Since there is a slight inaccuracy in the sleep, we need to cover for it 
       	overSleepTime = ( System.nanoTime() - afterTime ) - sleepTime;
} else { 
       	overSleepTime = 0;

        if ( ++noDelays >= NO_DELAYS_PER_YIELD ) {
                Thread.yield( );   // give another thread a chance to run
	        noDelays = 0;
	}
}

If the sleep( ) call sleeps for 12 ms instead of the desired 10 ms, then overSleepTime will be assigned 2 ms. On the next iteration of the loop, this value will be deducted from the sleep time, reducing it by 2 ms. In this way, sleep inaccuracies are corrected.