ImagePool Singleton Class

While working on GunCore, Matt and I have been wearing many hats; The designer, programmer, marketing strategist, artist, sound designer, webmaster, and blogger. Because of this rush, I’ve been neglecting some of my code where I’m just programming and not software engineering and I regret it every time I have to modify them.

I’ve been applying to new graduate software eng jobs lately and it really was the excuse I needed to spend more time with the software eng textbooks I refused to sell.  I have been wanting to iterate through some of GunCores code anyway and now I can practice what I’m reading and make GunCore more efficient at the same time.

To start off I’d like to share my ImagePool Singleton Class, its implementation, problems I faced and am still facing and most importantly, the things I think I’m doing correctly but might not be.

Previously in my game each instance of a game object had all of the images associated with it. When an enemy class was created, its image would be loaded and stored in memory. This way, 10 monsters with 20 images for their animation each, will be 200 images in memory; all of which are copies of just 20 unique images.

At first I was just going to declare all of the game’s images at the initialization of the application and make every game object just point to the image they want to display. Now the same image won’t be repeated in memory but as levels and especially chapters change, lots of images will never be used again or will never be used at all.  This looks like a job for a Object Pool.

I wanted to be able to:

  • Store all images in one place
  • Have the ability to dynamically add images
  • Check for and eliminate copies
  • Remove images that haven’t been used recently
  • Provide global access to the entire application

An Object Pool design pattern allows me to encapsulate all the images, check for expiry and copies and a Singleton design pattern enforces the one and only instance of the pool that I will need, as well as global access through out the application.

Here is the implementation that I’m working on. I’m at the point where it’s working, I don’t have any crashes, but I do have some concerns.

//Singleton Object Pool for storing images.
public class ImagePool {

	private static ImagePool instance = null;
	private static HashMap<String, ImageHolder> imageMap = new HashMap<String, ImageHolder>();
	private static int mapSize = 0;
	private static final int expiryCutOff = 1000;
	private static int expireTimer = 0;
	private static int checkTimer = 0;
	private static int checkRate = (int) expiryCutOff / 10;

	private ImagePool() {

	}

	private static class ImagePoolHolder {
		public static final ImagePool instance = new ImagePool();
	}

	public static ImagePool getInstance() {
		return ImagePoolHolder.instance;
	}

	// Returns the image to the string imageName is referencing
	// with rotation and brightness attributes
	public static Image getImage(String imageName, int rot, float darkness) {
		String image = imageName;
		// Force the rotation angle to 0-360
		rot = rot % 360;
		if (rot < 0) {
			rot = 360 + rot % 360;
		}
		// Create unique key for each unique image
		String imageID = imageName.concat("" + rot + "" + darkness);
		// Add new images or update old ones.
		if (!(imageMap.containsKey(imageID))) {
			ImageHolder imageHolder = new ImageHolder(initImage(
					ImagePool.class.getResource(image).getPath(), darkness,
					rot, Color.RED), expireTimer);
			imageMap.put(imageID, imageHolder);
			mapSize++;
			System.out.println("ADDING   imageCount: " + mapSize
					+ " imageID: " + imageID);
		} else {
			imageMap.get(imageID).updateFrames(expireTimer);
		}

		return imageMap.get(imageID).getImage();
	}

	// Called externally every frame. In case of slow computers,
	// using real time will expire objects early so using frames instead
	public static void checkExpiry() {
		expireTimer++;
		checkTimer++;
		// Because this method will be called every frame, the checkRate allows
		// control of the checkExpiry frequency.
		// Currently set to 1/10 of the expire time.
		if (checkTimer > checkRate) {
			checkTimer = 0;
			Set<String> set = imageMap.keySet();
			Iterator<String> iter = set.iterator();
			ArrayList<String> keyList = new ArrayList<String>();
			// add all expired keys into the keyList.
			for (int i = 0; i < set.size(); i++) {
				String s = iter.next().toString();
				if (imageMap.containsKey(s)) {
					if (expireTimer - expiryCutOff > imageMap.get(s)
							.getFrames()) {
						keyList.add(s);
					}
				}
			}
			// Remove all images that have expired keys that are in
			// the keyList
			for (int i = 0; i < keyList.size(); i++) {
				if (imageMap.containsKey(keyList.get(i))) {
					imageMap.remove(keyList.get(i));
					mapSize--;
					System.out.println("REMOVING imageCount: " + mapSize
							+ " imageID: " + keyList.get(i));
				}
			}
		}
	}
}

^ ImagePool Class Source. Edit: I changed the Singleton structure to be more thread safe using the Bill Pugh design.

Let me break it down.

I start with a HashMap called imageMap. I needed a data structure with unique keys and because of the high amount of insertions and deletions, as images expire and are remade, a hashMap seems slightly better then its ordered brethren, the linkedHashMap.

Each unique image needs a unique key and hashMaps always have unique keys so I can check for similar images by checking if the key exists or not.  For a key I decided to use a String that holds the image’s path + rotation degree + brightness which are all the variables to defining a unique image.  Any other image modifiers will have to be done in the art phase and become a separate PNG or I’ll just have to concatenate another variable onto the String. For GunCore, I don’t expect to.

The values for the HashMap imageMap are images but I had to wrap them in a class with an expiry date to track images which have not been requested recently for removal. The ImageHolder class wraps the images.

//ImageHolder Class is created in line 37 of the ImagePool
//Wraps an image with an expiry date so that old images can be removed
public class ImageHolder {

	private Image image;
	private int expireFrames;

	public ImageHolder(Image i, int e) {
		image = i;
		expireFrames = e;
	}

	public Image getImage() {
		return image;
	}

	public int getFrames() {
		return expireFrames;
	}

	public void updateFrames(int f) {
		expireFrames = f;
	}

}

^ ImageHolder Class Source

Every object can be drawn by calling ImagePool like this:

//Global classes can call for an image like this
g2d.drawImage(
	ImagePool.getInstance().getImage(f.getImageName(), f.getRotation(), F.getBrightness()),
(int) f.getX(), (int) f.getY(), this);

^ Game Class Draw Method

Every time getImage is called with a path, rotation and brightness the HashMap checks the key and then either finds it, updates the expiry date and returns the image associated with it’s key, or doesn’t find it and creates it, stores it and returns it.  The goal is to minimise the creating and storing parts.

	public static Image getImage(String imageName, int rot, float darkness) {
		String image = imageName;
		// Force the rotation angle to 0-360
		rot = rot % 360;
		if (rot < 0) {
			rot = 360 + rot % 360;
		}
		// Create unique key for each unique image
		String imageID = imageName.concat("" + rot + "" + darkness);
		// Add new images or update old ones.
		if (!(imageMap.containsKey(imageID))) {
			ImageHolder imageHolder = new ImageHolder(initImage(
					ImagePool.class.getResource(image).getPath(), darkness,
					rot, Color.RED), expireTimer);
			imageMap.put(imageID, imageHolder);
			mapSize++;
			System.out.println("ADDING   imageCount: " + mapSize
					+ " imageID: " + imageID);
		} else {
			imageMap.get(imageID).updateFrames(expireTimer);
		}

		return imageMap.get(imageID).getImage();
	}

^ ImagePool Class Source

Now that I have the expiry date updating I need a method to check them all and remove the expired ones. The expiry date timer does not use real time to expire images.  For fun I tried to run this game on my netbook which looks like it’s running at about 10 frames a second. If I used real time, that slow computer would be even slower as recently used but infrequently used images are expired and replaced over and over again.  If instead I use a frame counter, slower computers will hold the images for the length of gameplay I intended.

I decided to encapsulate this expiry frame counter in the imagePool class, calling checkExpiry every frame and making it only really check the HashMap for expired images a fraction of those times. Alternately, I could just not call checkExpiry every frame and decide when to in the already crowded main game loop’s class. The first way allows me to stay on one page of code and not bounce around so I choose it.

Currently the rate of checking for expired images is 1/10 of the time allowed before an image expires. This seems to be a fair assumption for now. The real rate would be the optimization of performance and memory management. A challenge for another day.

	public static void checkExpiry() {
		expireTimer++;
		checkTimer++;
		// Because this method will be called every frame, the checkRate allows
		// control of the checkExpiry frequency.
		// Currently set to 1/10 of the expire time.
		if (checkTimer > checkRate) {
			checkTimer = 0;

^ ImagePool Class Source

The checkExpiry method iterates through the whole hashMap and stores all expired keys into an arrayList and then deletes all the keys in the arrayList out of the hashMap.  When I tried to delete the keys from the hashMap on the fly I was having null pointer errors as I assume were caused by removing images while iterating through.

</span>
<pre>// add all expired keys into the keyList.
			for (int i = 0; i < set.size(); i++) {
				String s = iter.next().toString();
				if (imageMap.containsKey(s)) {
					if (expireTimer - expiryCutOff > imageMap.get(s)
							.getFrames()) {
						keyList.add(s);
					}
				}
			}
			// Remove all images that have expired keys that are in
			// the keyList
			for (int i = 0; i < keyList.size(); i++) {
				if (imageMap.containsKey(keyList.get(i))) {
					imageMap.remove(keyList.get(i));
					mapSize--;
					System.out.println("REMOVING imageCount: " + mapSize
							+ " imageID: " + keyList.get(i));
				}
			}

^ ImagePool Class Source

I’ve noticed that if I load a level and immediately load a different one, both level’s images will fill up the hashMap and zero frames of expiry time will pass.  After about 10 levels I can fill up the hashMap and cause a full heap error.

To combat this I clear the hashMap of all images before every level load but a level with to many different images being requested at the same time could still crash the game.  Maybe I’ll add a max size to the hashMap to catch overflow.

As the game runs I get a list of added and removed images as they are requested (and are not available) and expire.

Printout of ImagePool actions

That’s my first attempt at an ImagePool. The game is currently using it without any noticeable problems and levels with large amounts of the same wall run smoother. I’m new to posting code publicly and have to admit it’s a little humbling but there is always more to learn, so any feedback at all is appreciated.

About these ads

0 Responses to “ImagePool Singleton Class”



  1. Leave a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s




Follow the dev on Twitter.

About Me

Orun Irunmale
I'm Orun, an indie game developer. This is my development blog for all my software escapades.

RSS oruncode RSS

  • New Site & New Blog January 25, 2013
    All my blogging and game projects will be on our new site.  Come check it out. This is my last post here at oruncode.  Lazorun.com  Lazorun.com/blog
    oruncode
  • My Ludum Dare #23 Game April 25, 2012
    Made in just 48 hours, The Last Ride Home is a driving game that will test your reaction timing and your persistance as you try to drive through as many levels as possible. Here is a gameplay video. The game can be played in the Chrome Browser. You can play it now here is a link […]
    oruncode
  • March is my Month of JavaScript March 21, 2012
    March is turning out to be the month of JavaScript for me. Ever since I was asked to copy this game for a job interview a few weeks ago, I’ve been enjoying making anything I can think of with JS and HTML5 canvas. This was my first JS project. It’s an image matching game. I jacked […]
    oruncode

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: