Controlling FPS in J2ME applications

2007-02-28

Scope of Article

This article begins by giving a general introduction to the concept of FPS and illustrates its everyday use before diving into the specifics of how it is employed in J2ME applications. Lastly, the FPS code is discussed and offered for inclusion in your MIDlet.

Terminology

FPS = Frames Per Second
fps = the measuring unit for FPS
Hz = Hertz (cycle frequency used in electronic devices)

What is FPS?

FPS stands for “Frames Per Second” and is a measure of full-screen updates that occur within a single second. FPS is used everywhere screens/monitors are used. Your TV uses it to show you exactly 25 or 30fps (actually 29.97fps) depending where in the world you live. Movie theatres use it to show films at 24 fps.

Why is FPS used?

Without FPS a screen would playback video at unstable speeds.

If you’ve watched a silent motion picture where actors walk fast one second then slow the next you have a good idea of the effects of no-FPS video playback. FPS keeps the screen synchronized with the video’s original recording speed.

How does FPS differ from Hz?

If you’ve ever tuned your PC monitor’s settings you’ve likely come into contact with the Hz (short for Hertz) setting. Hz is the measurement of screen refreshes to the monitor. It usually coincides with the Hz of the electricity flow, for instance 60Hz in North America/Japan and 50Hz in most of Europe.

FPS and Hz are similar but not identical. North American TVs for instance refresh
the screen 60 times per second (60Hz) but have a 30fps.

What is happening is that only half of the screen is being updated in a 1Hz cycle and the other half in the next 1Hz cycle. This is referred to as interlacing. First the odd lines are rendered to screen then the even lines. This way it takes 2Hz to update the entire screen and at 60Hz it can render 30fps.

Remember FPS counts full-screen updates whereas Hz counts screen refreshes, which are not required to be full refreshes.

Still images and the illusion of motion.

The video seen on your TV is really comprised of still images that, displayed fast enough, provide the illusion of motion. But you already knew that. What you might not know is that your eyes have their own FPS! At 18 fps or higher your eyes are incapable of distinguishing the still images that make up a segment of video. This is why TVs and movie theatres display video at rates higher than 18.


FPS in J2ME

Now that you understand FPS and why it is needed let’s look at FPS’ role in J2ME applications.

Much like video, games render scenes to the screen. If these scenes are rendered too slow the player loses interest. If rendered too fast keeping up with the action might prove difficult. In this sense FPS is more important in games than it is in TV because the player is interacting with and being immersed in the game.

With regards to J2ME, FPS is paramount to providing a consistent experience across a wide range of devices. Each device vendor is free to use any CPU and component configuration and part of the power of J2ME is maintaining functional operation while lacking a reference hardware configuration. If the device were a reference hardware configuration like that of the GameBoy Advance the maximum FPS would be a known number.

Unfortunately, performance may vary wildly from device to device. While applying an FPS setting cannot make a game render scenes faster it can ensure that the game will not render more frames than are specified within one second.

So what is the ideal FPS setting?

There is no one correct answer for this. If the goal is to provide a consistent experience across many devices, testing will be required to gauge the result and to find that sweet spot that balances performance with consistency.

The resulting number may even be lower than the eye’s FPS of 18fps. This is acceptable to some degree by most players depending on the gameplay. It is advised that any game should be tested with FPS settings as low as 5fps if only to define the absolute minimum acceptable FPS rating.

A note about sample video distribution

If you are planning on providing a sample video capture of your game in action to others you may wish to use 15fps as it can be played back smoothly on TVs supporting 30fps and computer monitors supporting 60Hz or 75Hz as 30, 60 and 75 are multiples of 15.

The FPS class

For my own purposes I have created a FPS controller class that integrates well within any framework. The code is as follows…

/*
	Copyright 2007 Jason Fuerstenberg

	Licensed under the Apache License, Version 2.0 (the "License");
	you may not use this file except in compliance with the License.
	You may obtain a copy of the License at

		http://www.apache.org/licenses/LICENSE-2.0

	Unless required by applicable law or agreed to in writing, software
	distributed under the License is distributed on an "AS IS" BASIS,
	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	See the License for the specific language governing permissions and
	limitations under the License.
 */
import java.lang.Math;

/**
 * Utility class for exposing FPS related functionality to a J2ME application.
 *
 * @author Jason Fuerstenberg
 */
public class FPS {

	/*
	 * The millisecond timestamp of the current render frame if used.
	 */
	private static long frameTime;

	private FPS() {
		// singleton implementation
	}

	/**
	 * Returns the best delay given the specified FPS.
	 * The delay is used in application loops and is typically provided to the
	 * Thread.sleep() method.
	 *
	 *
	 * The basic formula for this calcuation is as below:
	 *
	 * delay = 1000 / fps
	 *
	 * The delay in millseconds, is the product of 1000 milliseconds divided
	 * by the desired FPS.
	 *
	 * Note: To be compatible with the CLDC 1.0 specification this method
	 * makes no use of floating point math and therefore the returned delay
	 * is always an integral number and may not be the precise result.  The
	 * results are precise to within 0.5ms.
	 *
	 * For example an FPS of 3 should produce a delay of 333.33333ms
	 * however this method will return a delay of 333ms.
	 *
	 * Alternately an FPS of 6 would produce a 166.66666ms delay.  The actual
	 * returned delay is 167ms.
	 *
	 * @param fps The desired frames per second.
	 * @return A delay (in milliseconds) within 0.5ms accuracy.
	 * @throws IllegalArgumentException if fps does not fall within 1-60.
	 */
	public static int getDelayFromFPS(int fps) {

		if (fps < 1 || fps > 60) {
			// invalid fps
			throw new IllegalArgumentException("fps must range from 1 to 60.");
		}

		// determine the delay
		int delay = 1000 / fps;

		// determine the duration of a cycle if the above delay were applied
		int cycleDuration = delay * fps;

		if (cycleDuration == 1000) {
			/*
			 * Found the perfect delay!
			 * Return it now.
			 */
			return delay;
		}

		/*
		 * The calculated delay, if applied, will not result in the desired FPS.
		 * Find the delay which will produce the closest FPS cycle from the
		 * following choices:
		 *
		 * 1. the calculated delay
		 * 2. the calculated delay - 1ms
		 * 3. the calculated delay + 1ms
		 */
		int delayMinusOne = delay - 1;
		int cycleDuration2 = delayMinusOne * fps;

		int delayPlusOne = delay + 1;
		int cycleDuration3 = delayPlusOne * fps;

		int diff1, diff2, diff3;

		diff1 = Math.max(cycleDuration, 1000) - Math.min(cycleDuration, 1000);
		diff2 = Math.max(cycleDuration2, 1000) - Math.min(cycleDuration2, 1000);
		diff3 = Math.max(cycleDuration3, 1000) - Math.min(cycleDuration3, 1000);

		if (diff1 < diff2) {
			return (diff1 < diff3 ? delay : delayPlusOne);
		}

		return (diff2 < diff3 ? delayMinusOne : delayPlusOne);
	}

	/**
	 * Enforces a cycle delay on behalf of the application while using
	 * an internally managed frame time.
	 *
	 * @param delay The delay to apply to this cycle.
	 */
	public static void enforceCycleDelay(int delay) {
		enforceCycleDelay(frameTime, delay);
	}

	/**
	 * Enforces a cycle delay on behalf of the application.
	 *
	 * @param frameTime The millisecond timestamp of the current frame's
	 * 			start time.
	 * @param delay The delay to apply to this cycle.
	 */
	public static void enforceCycleDelay(long frameTime, int delay) {

		long sleepTime = delay - (System.currentTimeMillis() - frameTime);

		if (sleepTime > 0) {

			try {
				Thread.sleep(sleepTime);
			} catch (InterruptedException ie) {
				/*
				 * There is no appropriate course of action to recover from
				 * an InterruptedException and therefore only the fact that it
				 * occurs is output to the stack trace.
				 */
				ie.printStackTrace();
			}
		}

		// Set the current frame-time.
		FPS.frameTime = System.currentTimeMillis();
	}

	/**
	 * Returns the interally managed frame-time.
	 * The value may or may not be set depending on the previous usage of
	 * this class.
	 *
	 * @return The internal frame-time.
	 */
	public static long getFrameTime() {
		return frameTime;
	}
}

How does the class work?

The class has 2 purposes:

  • Determine the most appropriate delay for a given FPS setting. Remember, Java doesn’t understand FPS. The Thread.sleep() method expects a millisecond delay setting. The getDelayFromFPS() method will determine the best delay to within 0.5 ms accuracy given a FPS setting.
  • To provide an easy-to-use fps controller which integrates into any MIDlet and is callable from multiple locations.
  •  

    Where does the invocation to this class belong?

    The game loop!

    Since each game loop iteration can be thought of as a cycle a game can use a call to the enforceCycleDelay() method to signal the end time of the previous frame and the start time of the next frame. To achieve the desired the FPS it calculates the remaining time it should attempt to sleep, if any.

    Managing the frame’s start time.

    The FPS class has an internal frame start time but will also accept an external start time when provided one. In addition, the internal frame start time can be retrieved via the getFrameTime() method for general consumption by the caller.

    A word about Java’s thread scheduling

    Java does not provide its own threading functionality but merely abstracts the OS’ thread scheduler with a consistent API. As such, different devices will offer different responsiveness. As the FPS class is dependent upon this functionality there is a chance that the requested FPS setting may not be honored at all times.

    Multithreaded considerations

    Each thread that executes on a mobile device can consume significant amounts of CPU time. In fact many embedded OSes do not support the concept of pre-emptive multithreading where each thread is forced to play fair and only uses a small slice of the CPU’s time. In this case each thread might be allowed to “hog” the CPU. The FPS class can still adjust since it is aware of the accurate passage of time but it cannot prevent a game loop iteration from taking longer than the FPS setting to complete.

    Please read my article on Multithreading in J2ME for more in depth information.

    Entry Filed under: graphics & animation. .

    1 Comment

    Trackback this post


    Recent Posts

    a

    Feeds

    Blogroll