This tutorial explains in some detail how the Frontend Resource Management System (RMS) works. It is not necessary to understand this in order to use the RMS, but it can be usfeul to look back at this tutorial at some later point.

Resources

In Frontend, a Resource is defined as any object that needs to be loaded from some source (i.e. a file) and might need to be shared between parts of the program that does not know each other. The template class Frontend::Utils::Resource works as a base class for all resources managed by the Frontend Resource Management System (RMS).  The Frontend RMS provides a large set of Resource Types, the so-called R-types. These are specializations of the Resource template class and they are found in the Frontend::Utils namespace. Some of them are:

  • RBitmap - A 2D bitmap image.
  • RTexture2D - A 2D texture image. Textures are sets of bitmaps managed by a graphics device.
  • RScene - A 3D scene graph. Will be explained in a later part of this tutorial
  • RText - A text file/document
  • RXmlDocument - An Xml document
  • RGeometry - A 3D geometry (model)
  • RPixelShader - A pixel shader
  • And many more

R-types are so called Smart Pointers. They can (should) be initialized on the stack and passed arround as if they were basic value types. When you pass R-objects arround they are automatically reference counted and taken care of by the RMS.

Example of how to construct and work with Resource-types:

RBitmap someBitmap("someBitmap.jpg");
RScene some3DScene("someScene.dae");
RScene theSameScene = some3DScene;

As you can see, this is pretty straight-forward.

ResourceProxy

ResourceProxy is the hidden heart of the Frontend RMS. A single instance can be set as the global ResourceProxy at a time, and you can get it through ResourceProxy::GetGlobal(). If you use SimpleSetup, an instance of this class has already been made and set global for you with all the default loaders. A Loader is an interface that can convert a particular resource type (i.e. a file) into another particular resource type. Frontend Utils ships with a lot of ready-to-use loaders for various formats, for instance:

  • Bitmap loaders: PNG, JPG, TGA
  • 3D Scene loaders: COLLADA, FSGML, FSG
  • Audio loaders: OGG, WAV
  • Other: Text, XML

ResourceProxy uses the loader library to work recursively. This means that if you for instance load a 3D scene that requires some textures, and these textures require some bitmaps (for mipmaps), the 3D Scene loader invokes a Texture loader that invokes a Bitmap loader that invokes a FileLoader and so on. So if you for instance add a new Bitmap loader, all 3D scene loaders can automatically take advantage of it and load bitmaps of this new format. To add support for a new format, you just need to add your loader to ResourceProxy using AddService().

Resource Descriptors

A descriptor is a string that is passed to the constructor of a R-type from which uniquely identifies the recipe to load or construct the resource. The most common descriptor type is a plain file name, but it is important to understand that they are not always just file names. For instance, the default TextureLoader in Frontend expects a semicolon-separated list of file names with one file name for each mipmap. If only one (or too few) mipmaps are specified, the remaining mipmaps are auto generated. How the descriptor string is interpreted is completely up to the loaders and other services that are added to the ResourceProxy. You could for instance create your own service which creates pattern bitmaps based on some keywords an parameters in a string.

Descriptor flags

ResourceProxy supports something called descriptor flags. These are flags added to the descriptor string to give directives to services (loaders) about how the resource should be loaded. Flags are added in a semicolon-separated list inside {}-brackets at the end of the descriptor. This bracket is removed from the descriptor before being passed to a service, but it remains in the unique descriptor string that identifies the resulting resource. A common flag to set is the Path flag. This specifies the a folder that is given priority when searching for files the resource depends on. For instance, when loading a 3D scene, the scene file often depends on external texture and geometry files. The Path flag should therefore be set to the path where these resources can be found. Example: scene.dae is placed in “data/”, while the textures scene.dae uses is in “data/textures”. To load this correctly, you use the Path flag as follows:

RScene scene("data/scene.dae{Path=data/textures}");

Optional Singleton

Note that using the global ResoruceProxy is optional. If you want to, you can have several ResourceProxy objects. All the R-types has a constructor overload which allows you to specify which proxy object to use. Only if none is specified, the global proxy is used. Example:

ResourceProxy* myProxy = new MyResourceProxy();
RBitmap someBitmap("someBitmap.jpg");  // Construct using global proxy
RBitmap myBitmap(myProxy, "someBitmap.jpg"); // Construct using myProxy

What problems does the RMS solve?

When coding games and other resource-heavy applications you run into a lot of common design problems. Some of these are:

How to share resources between independent parts of the program and avoid loading the same resource multiple times?

The Frontend RMS allows you to refer to unique resources with unique descriptors, so all parts of the program can access the resources they want at ay time. R-object constructed from the same descriptor gives you the same actual object every time, making sure there is no excessive multiple loading.

How to determine when resources can be deleted to free up memory?

As long as you pass R-objects arround as if they were basic types, they are reference counted and deleted when appropriate - you don’t need to worry about this. To reduce loading times, resources are not actually deleted when there is no references left. ResourceProxy holds a last reference in case the Resource is needed again shortly by another resource being loaded. This is common for example when loading game levels - textures and models are often re-used even though you are changing level. You can force Proxy to dispose all unused resources at any time by calling ResourceProxy::CleanUp().

How to be able to re-load resources without having to restart the application?

All R-objects can be easily re-loaded at any time by calling Reload() on it. This reloads the actuall instance on which Reload() is called, it does not affect other parts of the program refering to the same resource.

To learn more about the Frontend RMS, we recommend that you study the documentation for Resource and ResourceProxy.




FireStats icon Powered by FireStats