3. Resources

Resources are things like /foo/bar/MyThing, that are named using a path from the base game directory. They may or may not actually exist as files on disk. Typically resources either represent assets defined in Lua files, such as classes, objects, materials, etc. (covered in the various other chapters of this book). Or, they represent assets on disk, such as sound files, textures, meshes, etc. The latter, disk resources, are the subject of this chapter.

3.1. Disk Resource Management

Disk resources can either be loaded or unloaded, which means whether or not they are occupying RAM (this can be CPU RAM or GPU RAM). Loading usually takes a noticeable amount of time, so it's a bad idea to do it during gameplay. When a body is created that depends on a resource, e.g. a GfxBody depending on mesh and texture resources, those resources will be automatically loaded (if needed). However, loading the resource immediately before creating the body will introduce a noticeable delay.

In a streaming game, the objects declare their required resources (usually in the class) and these resources are loaded in a background thread. The object is not activated until the resources are available. In this situation you rarely have to worry about loading / unloading resources. However in a non-streaming game it can be simpler to load everything before gameplay begins.

There are a number of API calls that can be used to manually control the loading and unloading of resources. In the following calls, name is an absolute path. To give a relative path, use backticks (§8.1.1).

disk_resource_load(name)  -- Note: Raises an error if it's already loaded
disk_resource_ensure_loaded(name)  -- Load, or no-op if already loaded
disk_resource_loaded(name)  -- Test if loaded
disk_resource_reload(name)  -- Useful during development to see new assets

Using the above calls, it is possible to load resources way ahead of time. This is useful in a game that is not large enough to need streaming, as you can load everything before the actual gameplay begins, or switch out resources between levels. However there is a catch; Grit manages the available memory and GPU memory. If, upon load, there is not enough CPU or GPU RAM, Grit will unload the least recently used resource(s) to make space for the one being loaded. A resource is used if it has at least one user:

disk_resource_users(name)  -- How many users does a resource have?  

If you use a resource in some explicit way (e.g. create a GfxBody that uses a mesh), then Grit will automatically ensure it is not unloaded by increasing the user counter for that resource. However, if you want to prevent unloading during a time when the resource is not actually used, you need to tell Grit that you're really still using it. To tell Grit that you're using a resource, you create and keep a hold on it:

myhold = disk_resource_hold_make(name)
-- Resource will not be unloaded by Grit but may not be loaded yet
disk_resource_ensure_loaded(myhold.name)  -- Load only if unloaded
myhold:destroy()
-- Now Grit may unload it if there is a RAM shortage
-- Or you can force the unload now:
disk_resource_unload(name)  -- Works only if users == 0

Note that creating a hold on an object does not load it, but it will stop it being unloaded by the system until the hold is released. Loading a resource when you don't have a hold on it can be dangerous because Grit may unload it again, causing a stall when you next use it. The idiom is therefore always to create the hold, then load the resource, keeping the hold until you don't need to use the resource anymore.

3.2. Disk Resource System

The following calls allow you to query the state of the system itself:

disk_resource_num()  // Count all disk resources.
disk_resource_num_loaded()  // Count all loaded disk resources.
disk_resource_all()  // List all disk resources.
disk_resource_all_loaded()  // List all loaded disk resources.
host_ram_available()  // CPU RAM cap (configured by user).
host_ram_used()
gfx_gpu_ram_available()  // GPU RAM cap (configured by user.
gfx_gpu_ram_used()

The following flag helps debug stalls during rendering. It enables a warning if resources are loaded in the foreground thread. In non- streaming games, you can turn this on after loading your level to ensure that you loaded everything ahead of time. In between levels, you can disable it, do your unloading/loading work and then re-enable it. In streaming games, you can leave it on while the game is streaming to ensure you haven't forgotten to declare resources used by your objects.

option("FOREGROUND_WARNINGS", true)  -- Enable warnings if resources are loaded too late