Using Texture Caches

Textures are an important part of any 3D environment. They give realism to any surface. However, any reasonable application typically will have many, many textures to load and use - most of them repeatedly. Java3D also gains a performance boost if they are used intelligently. This cache is designed to manage the instances held in memory, but not the way your application manages them with Java3D.


The texture cache system supplied here is designed to cache objects at a much higher level. One of the fundamental assumptions that Java3D makes is based on object instance references. If the internals can determine that you've used the same reference more than once, it can apply a lot of optimisations. In the case of textures, you gain great benefits because Java3D only needs to store one image on the video card, rather than multiple copies for every time you have used the image. That is, Java3D has no magic ball to work out that you have used a file more than once if you have created separate references to it. The idea of this cache system is to keep around the higher-level Java3D objects representing textures so that you can gain the maximum benefits from Java3D's performance.

Of course, not everyone has the same needs from their cache. We have supplied three standard caches that will probably suit the majority of situations. However, we do allow custom cache implementations to be also registered with the factory so that you may cache according to your specific needs.

Default Cache Implementations

As mentioned previously, the idea of the cache is to not be a One-Size-Fits-All approach. Instead we provide a number of different cache implementations for you to choose from - or you could write your own. This explains the basics of each type.

There are three implementations provided by default: Fixed, LRU and WeakRef.

Fixed cache never throws anything away unless you explicitly tell it to. Useful if you have a lot of small textures or a small amount of large ones that you use frequently.

WeakRef cache uses the Java weak-reference system to maintain textures. All textures are maintained with a weak reference. The normal rules apply. If the reference is garbage collected, we fetch all the object back again and put it back into the cache. Useful when you have a relatively small memory footprint or if you are using several really large images that impact memory usage elsewhere and you don't care if they disappear or not (that is, you access them infrequently and the performance penalty of having to reload them from disk is not an issue).

LRU cache uses the standard Least Recently Used algorithm to maintain the items in the cache. The most recently accessed objects are maintained and when too many are loaded the cache starts tossing away the ones that have not been accessed recently. The implementation here uses a fixed size cache (20 by default, but can be changed). Any more textures stored than the given number and the oldest items are turfed.


Setting up

From the outside, the texture cache system looks fairly trivial. There are only two classes, an interface and a single exception defined in the package (org.j3d.texture). Underneath there lies a great complexity - luckily you'll never had to see that!

Basic class layout

The two most interesting classes for you are the factory class TextureCacheFactory and the abstract representation of a particular cache implementation (TextureCache). The factory is responsible for managing requests and the instances of the individual cache types, while the abstract interface provides the access to the caching mechanisms.

Requesting a Cache

To make use of a cache, you start with the factory. The factory uses all static methods, so you don't need to create or fetch an instance of the factory. You can fetch the cache through the getCache() method. There are two forms of this - one with no arguments and one which takes an integer value. We'll look at the int-arg version first.

As you are aware, different cache implementations are available. In this form of getCache() you tell the factory which of those implementations you would like. The integer argument represents the type of cache. If you are using one of the standard types, then there are a collection of constants provided at the top of the class factory. These values are FIXED_CACHE,LRU_CACHE,WEAKREF_CACHE. To select a cache, just pass one of these values. For example, to ask for the LRU cache, you would use the following code:

    TextureCache cache =
If you have previously registered a custom cache implementation, you can fetch that using this method too. Simply supply the int value that you registered the cache under as the value of the argument. In this form, you can fetch the default cache using:
    TextureCache cache = TextureCacheFactory.getCache();

The alternate no-arg form of getCache() is used to fetch the pre-configured default cache implementation, for those that don't need to know or care about fetching particular types.

Using the cache

Once you have an instance of TextureCache, you can now start using it to store and retrieve texture information. The cache allows you to cache texture information at two levels - individual ImageComponent instances and Texture instances. This allows you control over which objects you want to cache or create as new. One feature we use here is that if you fetch a file as a Texture, the API also says that it should cache the ImageComponent instance too.
    At this stage the APIs only deal with 2D textures. A future expansion will include the option for 3D textures.

To load a texture, you can supply either a filename or a URL object. The filename can be fully qualified or relative. In order of evaluation, we try to load a name as a Java File first, which means it will find relative files relative to your CWD. If that does not specify a valid file, the next step is to use the name as a relative name to your classpath. This should allow you to leave textures in JAR files and the like.The call is then as simple as:

    try {
        Texture texture = cache.fetchTexture(somefilename);
    } catch(IOException ioe) {
To explicitly remove a texture from the cache, call the removeTexture() method with the filename or URL that you used to fetch the texture with in the first place. If you want to clear the entire contents of the cache call clearAll().

System Properties

There are two system properties that can be used with the texture caching. Both properties must be set before the texture caching system is started. If you set them after the first reference to the texture cache then the values will be ignored.
The default cache type is a string with one of the values fixed, lru or weakref (case-sensitive strings). This sets the type of cache to be returned if there is a request for the default cache.

The LRUSize is used to control the maximum number of items that can be stored in that cache implementation before items start getting removed. The value is an integer and the default value is 20.


Creating Custom Cache Implementations



Texture caching is quite easy to add to any application and will add significant performance increases when used correctly. The texture cache provides a collection of the most used types and allows you to create your own custom types if needed.