Resource Loading manager. Onto memory or not?

Since I don’t mind reinventing the wheel for the sake of learning something I’ve been working on a small engine/framework as a foundation to work on some simple games. When I was working on my imageLoading manager (part of the resourceLoading bundle), at the moment I have it set up in a way that once a image is loaded, its temp. held (in memory) by the program as long as its running. If a call asks for an image that hasn’t been loaded yet, it does so, else it returns the copy that was loaded and stored in a hashmap (which essentially is in memory).
I’ve also done pretty much the same for my audioResourceLoading manager.

Now what I’m thinking of implementing down the road is that the imageLoading manager only holds image files that are under a certain size or animations (other things like static backgrounds can be reloaded if needed).
And as for the audio, if its sound effects its pre-loaded onto a clip and therefore its held in memory. But for large audio files like background music, I’m thinking of maybe streaming it and buffering it so that its played as its loaded, with a sourceDataLine.

One thing I’m wondering is, I know when i load an image and an instance is presenting it on the screen (using it as representation), its being held in memory. Now if I’m putting a copy of it in a hashmap will that copy be in memory as well? or is it using the same memory space as the first one that was loaded? Same goes for when I create other instances that use this same image as a representation. Does anyone know?

And yes I know tools like Slick2D handle all this resource loading stuff for you (or so I’ve read), but I’m mostly doing it for the learning experience. For example to get that audioLoader going I learned a great deal of the sound API.

So yeah, just wanna hear some thoughts on this. ;D

If you’re using a desktop or laptop then preload whatever you might use and never load resources you’ll never use. If you can have the user (programmer) inform the engine of what resources they will need later, then prioritize storing those resources - leaving resources that might be used in the next level if you want to save future loading times OR freeing resources to make room for non-resources. For example if you’re loading an ice level in a tile based games you don’t need to keep magma, dessert, grass, sky world, or underwater tilesets unless you want to save loading times later. Besides the obvious benefits, this would also let you use a greater variety of art while still using the same amount of ram. Sound effects, tiles, animations, sprites, textures, and models should be preloaded this way. Background music and images are harder, but I would not stream them. For background music, you can sort of get the best of both worlds by loading an entire song and the first 5 seconds of alternate background music as a clip. Hopefully then you wouldn’t need to pause music while loading.

In terms of total memory usage, I would use excess memory to your advantage if you have it. For all types of programs, you need to worry about crowding out other programs. Ordinary programs shouldn’t be greedy, but players understand that they should close other programs if they want better performance, especially for full screen games. Don’t waste memory but don’t force users to change their hardware, operating system, or habits either. I’m guessing memory management efficiency problems are over hyped and I can’t think of any other reason why using less memory than the total physical memory available would slow down most applications. Transferring data to and from different mediums over and over again seems like a bad idea (but I’m biased because hard disk noises have just started to really annoy me a lot more recently.) Maybe try other methods to save space like compression, procedural art, replacing similar tiles on lower end systems, simplifying models, shrinking textures, loading lower bit rate, lossily compressed audio, etc. I think 3D games use similar methods to save video ram, so you could implement a system that allows for adaptive changes or user supplied quality/performance settings.

For a game engine, it probably would be nice to give the developer more control so it can be reused in games where all resources can fit in memory, games with heavy resource usage that require good rendering performance, and games that don’t require many resources and would prefer using that memory for game specific data structures or artificial intelligences. Since I’m not really sure what’s best in practice or what other games/engines try to do I’d like to see what other people think. I think a lot of my suggestions also apply to non pc platforms, but maybe they’re not worth as much since the memory may be slower and it might be quicker to transfer data from flash memory storage.

Regarding caches, my opinion is that SoftReferences provide the perfect solution. Have a resource manager that loads everything with soft references - never free anything. If the the VM needs more memory, it will clear up any cached objects you aren’t currently using. When you need the resources, check if they’re loaded and reload them if required. Here’s the guts of one I used earlier:


    /** image cache */
    HashMap<String, SoftReference<BufferedImage>> imageCache =
            new HashMap<String, SoftReference<BufferedImage>>();

    public BufferedImage getImage(String imageName) {
        SoftReference<BufferedImage> ref = imageCache.get(imageName);

        if(ref == null){
            // we've never stored this image
            BufferedImage img = null;
            try {
                img = ImageIO.read(new File(imageName));
            } catch (IOException ex) {
                // handle exception
            }
            imageCache.put(imageName, new SoftReference<BufferedImage>(img));
            return img;

        } else {
            // we've stored this image before...
            BufferedImage img = ref.get();
            if(img != null){
                // .... return the previously cached image
                return img;
                
            } else {
                // ...however it's since been garbage collected.  Clear the record from the
                // cache and call this method again. It will attempt to load the image
                // when there is no entry for the image in the imageMap
                imageCache.remove(imageName);
                return getImage(imageName);
            }
        }
   }

Personally, I think SoftReference (and Weak and Phantom references) are brilliant features of the JVM which are generally under-employed.

You also might want to look into Guava, specifically CacheBuilder which can construct weak-valued maps and clean up collected entries for you.

Great, thanks people:

@sproingie and kaffiene, thanks for the insight! I’ll definitely will be looking more into the CacheBuilder and the Reference classes (I’ve mildly seen them in the API, but I’m definitely gonna take a better look at those you mentioned there). :smiley:

@Best Username Ever, thats a lot of great tips, I’ll have to re-read to fully absorb it all, but by any means, thanks for taking the time to write that and give me more to think about/look into/try out. :wink:

Kaffiene, that’s a pretty cool implementation, but does this make it more difficult to deal with sprite sheets? It seems, if you want to deal with an animation with multiple frames, this would have to be modified a bit.

Ok, I took another look at it and I modified the function to handle sprite sheets. It’s not perfect, but it works well enough for me:

public BufferedImage getImage(String imageName, int row, int col, int width, int height){
		SoftReference<BufferedImage> ref = imageCache.get(imageName+row+col);
		
		if(ref == null){
			BufferedImage img = null;
			try{
				img = ImageIO.read(new File("src/images/" + imageName + ".png"))
						.getSubimage(col*width, row*height, width, height);
			} catch (IOException ex){
				//handle exception
				System.out.println("src/images/" + imageName + ".png");
				System.out.println(ex.getMessage());
			}
			
			imageCache.put(imageName+row+col, new SoftReference<BufferedImage>(img));
			return img;
		}else{
			//we've stored this image before
			BufferedImage img = ref.get();
			if(img != null){
				return img;
			}else{
				imageCache.remove(imageName+row+col);
				return getImage(imageName, row, col, width, height);
			}
		}
	}

Here, you have to give it a row, column, width of the image, and height of the image. I went ahead and said “give it a sprite sheet and a location on the sheet, if it can’t find it, it loads the sheet, cuts the image out, and saves it back into the image cache”.

Bad news is that you have to keep track of the row/column of the animation you need. I have an animation class that handles it currently, but as far as the resource handler is concerned, there you go.