Resource and Asset Management
A big part of an engine's responsibilities are concerned with the management of resources, typically ones related to graphics. Trial divides this into two parts, resources, and assets.
A resource represents some object that requires manual allocation management. Often this is something like a texture, buffer, shader, etc.
An asset on the other hand generates one or more resources, either dynamically, or from a file on disk.
Resources
Trial in its core offers the following resource
s:
bindable-buffer
buffer-object
compute-shader
framebuffer
memory
shader
shader-program
shader-storage-buffer
struct-buffer
texture
uniform-buffer
vertex-array
vertex-buffer
vertex-struct-buffer
Core extensions, such as trial-harmony, will offer further resources to encompass other state they might need to manage.
The resources protocol is rather slim:
generator
If the resource was produced by an asset or similar, this will return that generator.name
The name of the resource, if any. Resources may also be nameless.dependencies
The list of resources this depends on before it can be allocated.allocate
Allocate the resource. Note that this will fail if thedependencies
aren't already allocated, or the resource has already been allocated.deallocate
Deallocate the resource. Note that this will fail if the resource isn't already allocated.allocated-p
Returns true if the resource is currently allocated.check-allocated
Signals a correctable error if the resource is not allocated.ensure-allocated
Allocates the resource and all of its dependencies if it isn't already.
The individual classes may have additional functions to dynamically resize, update, or otherwise change their contents without explicit reallocation.
All of them will also have different initargs on construction. However, you will typically not need to manually create resources, and should instead rely on assets.
Assets
asset
s on the other hand are what you'll typically use directly. Trial defines the following core assets:
audio
environment-map
image
mesh
model
shader-image
sprite-data
static
tile-data
uniform-block
video
Again, extensions to Trial are likely to define their own asset types.
The API for assets is slightly richer than that for resources. You have the analogous load
, unload
, and loaded-p
. In addition however, there is the explicit reload
, and functions to access the resources that the asset generates:
The //
function is the primary way in which you will access resources from an asset. It takes care to "inline" the reference to the resource, meaning you pay zero access costs for the resource at runtime if you use this function.
This is achieved with a trick: when you use //
to refer to a resource that hasn't been loaded yet, it will return a placeholder-resource
instance. When the referenced resource is ultimately loaded, the placeholder instance is ``change-class`ed into the proper resource type.
Manually referencing a resource from an asset can be done with resource
and. list-resources
.
Sometimes assets also contain extraneous information not contained in resources, such as number of frames, or similar. However, they cannot know this information until they are actually loaded. In such a case, you will want to register-generation-observer
s to get a callback whenever the asset is actually ready.
Pools
Assets reside in pool
s. A pool is a logical grouping of assets, and usually also a physical one, occupying a directory on disk. You typically define at least one pool for your project, and possibly more to avoid name collisions. A pool is defined with define-pool
:
(define-pool my-pool)
The define-pool
should be in a file in the same directory as the data
directory in which the pool's files reside. If you have a different structure, you must pass the :base
initarg explicitly. For example, given the following directory structure:
.
├── data
│ ├── sprite.json
│ └── sprite.png
├── game.asd
└── src
└── game.lisp
You would adjust the pool's path like so:
(define-pool my-pool :base #p"../../data/")
Retrieving assets within a pool happens with asset
or list-assets
. You can also retrieve a subpath in the pool via pool-path
.
Within a pool, then, you define assets via define-asset
. You must set the pool it should be in, the name of the asset, its type, and the "input". Depending on the asset the input should be a pathname or some other value from which the asset's resources and other data are derived. Many assets will also take several options to configure the generated resources' behaviour.
Defining individual assets like this can become way too verbose, however. To cut down on the repetition, we can instead define assets based on the files contained in a directory, via define-assets-from-path
. The pathname
argument should be wild, and is merged with the pool's base directory. The body of the macro should be options for specific assets:
(define-assets-from-path (my-pool image "*.png")
(T :min-filter :nearest :mag-filter :nearest)
(logo :min-filter :linear :mag-filter :linear))
Where the T
option applies by default to all assets defined like this, and ones for specific assets override. The asset name is derived from the input file via pathname-asset-name
, which tries its best to turn the filename into a somewhat sensible symbol name.
Compiled Assets
Some assets require source files that are derived from others. In that case, Trial may automate the compiling and optimizing of these source files. Assets that can be compiled like this will derive from compiled-generator
and you may call compile-resources
on them. You can also call compile-resources
on a pool, which will invoke it for every asset that can.
Trial also presents a standardised way to transcode different formats via transcode
. By default only methods to convert audio files are provided, by relying on the external ffmpeg
application on your system. Some image format systems will also provide ways to transcode image files.an