How the GfxResourceSystem Manages Rendering Resources in EquilibriumEngine
The GfxResourceSystem tracks, updates, and safely disposes of all bgfx rendering resources by attaching a GfxResource component to entities and using flecs observers to automatically call the appropriate bgfx_destroy_ functions when components are removed.*
The gfx_resource_system serves as the central authority for GPU resource lifecycle management in the clibequilibrium/equilibriumengine. Built on top of the flecs entity component system (ECS) and the bgfx rendering library, this system ensures that textures, buffers, shaders, and framebuffers are automatically cleaned up without manual intervention, preventing memory leaks and handle exhaustion.
Core Architecture of the GfxResourceSystem
Resource Tracking with GfxResource Components
At the heart of the system lies the GfxResource component, a lightweight struct that pairs a bgfx handle with its type classification. According to the source code in equilibrium/systems/rendering/gfx_resource_system.h (lines 8–22), this component stores:
typedef struct {
ResourceType type;
uint16_t handle;
} GfxResource;
Any entity that owns a bgfx-created object—whether a texture, vertex buffer, or shader program—must have this component attached. The handle field stores the bgfx index value, while the type field determines which destruction function the system will invoke during cleanup.
The ResourceType Enum
The ResourceType enum (defined in gfx_resource_system.h) enumerates all supported bgfx resource categories:
RESOURCE_TYPE_TEXTURERESOURCE_TYPE_VERTEX_BUFFERRESOURCE_TYPE_DYNAMIC_VERTEX_BUFFERRESOURCE_TYPE_INDEX_BUFFERRESOURCE_TYPE_PROGRAMRESOURCE_TYPE_FRAME_BUFFERRESOURCE_TYPE_UNIFORM
This classification enables the DestroyGfxResources observer to dispatch the correct bgfx_destroy_* call without requiring manual type checking by the user.
Automatic Resource Lifecycle Management
The DestroyGfxResources Observer
The system registers a flecs observer that triggers on the EcsUnSet event for the GfxResource component. As implemented in equilibrium/systems/rendering/gfx_resource_system.c (lines 45–84), this observer iterates through all components slated for removal and executes the corresponding bgfx destruction logic:
switch (resources[i].type) {
case RESOURCE_TYPE_TEXTURE:
bgfx_destroy_texture(bgfx_texture_handle_t{resources[i].handle});
break;
case RESOURCE_TYPE_VERTEX_BUFFER:
bgfx_destroy_vertex_buffer(bgfx_vertex_buffer_handle_t{resources[i].handle});
break;
case RESOURCE_TYPE_DYNAMIC_VERTEX_BUFFER:
bgfx_destroy_dynamic_vertex_buffer(bgfx_dynamic_vertex_buffer_handle_t{resources[i].handle});
break;
case RESOURCE_TYPE_INDEX_BUFFER:
bgfx_destroy_index_buffer(bgfx_index_buffer_handle_t{resources[i].handle});
break;
case RESOURCE_TYPE_PROGRAM:
bgfx_destroy_program(bgfx_program_handle_t{resources[i].handle});
break;
case RESOURCE_TYPE_FRAME_BUFFER:
bgfx_destroy_frame_buffer(bgfx_frame_buffer_handle_t{resources[i].handle});
break;
case RESOURCE_TYPE_UNIFORM:
bgfx_destroy_uniform(bgfx_uniform_handle_t{resources[i].handle});
break;
default:
ecs_err("Unsupported resource type");
}
Because this observer runs automatically when a GfxResource component is unset—either through entity deletion or explicit component removal—the engine guarantees that no bgfx handles leak, even during complex scene transitions or error conditions.
Shader Hot-Reloading Mechanism
The gfx_resource_system extends beyond simple cleanup to support iterative shader development through its hot-reload pipeline. This feature uses three additional components: HotReloadableShader, FileWatcher, and ReloadShader.
HotReloadableShader Component Structure
The HotReloadableShader component stores metadata required to recreate shader programs at runtime. It tracks:
- The target entity owning the shader
- The component ID and field offset where the program handle resides
- File paths for vertex and fragment shader sources
This design allows the system to update the program handle in place within user-defined components without requiring the user to manually manage the shader entity.
FileWatcher Integration and Reload Triggers
The hot-reload workflow operates through a coordinated observer chain:
-
InitHotReloadShaders: When both
FileWatcherandHotReloadableShaderexist on an entity, this observer registers the shader files with the underlyingxWatchersystem (a cross-platform file-watcher). -
UpdateFileWatcher: Runs every frame via the
OnInputtag to poll for file modifications. -
HotReloadShaders: Triggers on
EcsOnSetwhenReloadShaderis present. This observer:- Loads new vertex/fragment shaders via
shader_load() - Destroys the old program using
bgfx_destroy_program() - Creates a new program with
bgfx_create_program() - Writes the new handle back to the component at the stored
filed_offset
- Loads new vertex/fragment shaders via
According to the source in gfx_resource_system.c (lines 41–42), when the file-watcher detects a change, it tags the entity with a ReloadShader component, queuing the reload for the next frame.
System Initialization and Registration
During engine startup, GfxResourceSystemImport() registers all components and observers with the flecs world. The initialization sequence in equilibrium/systems/rendering/gfx_resource_system.c (lines 80–99) defines the component types:
void GfxResourceSystemImport(ecs_world_t *world) {
ECS_COMPONENT_DEFINE(world, GfxResource);
ECS_COMPONENT_DEFINE(world, HotReloadableShader);
ECS_COMPONENT_DEFINE(world, FileWatcher);
ECS_COMPONENT_DEFINE(world, ReloadShader);
// Observer registrations follow...
}
This import function establishes the contract that any system creating bgfx resources must attach a GfxResource component to participate in automatic cleanup.
Practical Usage Examples
Creating and Attaching a Texture
To create a texture that participates in automatic resource management:
// Create the bgfx texture handle
bgfx_texture_handle_t tex = bgfx_create_texture_2d(
512, 512, false, 1,
BGFX_TEXTURE_FORMAT_RGBA8,
BGFX_TEXTURE_NONE, nullptr);
// Wrap it in a GfxResource component
GfxResource res = {
.type = RESOURCE_TYPE_TEXTURE,
.handle = tex.idx
};
// Attach to entity
ecs_set(world, entity, GfxResource, {res});
When entity is destroyed or the component is removed via ecs_remove(world, entity, GfxResource), the DestroyGfxResources observer automatically invokes bgfx_destroy_texture().
Setting Up Hot-Reloadable Shaders
To enable live shader editing:
// Define a component to hold the program handle
typedef struct {
bgfx_program_handle_t prog;
} MyMaterial;
// Create shader entity
ecs_entity_t shader_ent = ecs_new_id(world);
ecs_set(world, shader_ent, MyMaterial, {BGFX_INVALID_HANDLE});
// Configure hot-reload metadata
HotReloadableShader hot = {
.shader_entity = shader_ent,
.component_id = ecs_id(MyMaterial),
.component_size = sizeof(MyMaterial),
.filed_offset = offsetof(MyMaterial, prog),
.vertex_shader_name = strdup("shaders/vs_myshader.sc"),
.fragment_shader_name = strdup("shaders/fs_myshader.sc")
};
ecs_set(world, shader_ent, HotReloadableShader, {hot});
The gfx_resource_system will now monitor the shader files and automatically recompile the program when changes are detected, updating MyMaterial.prog without requiring engine restart.
Explicit Resource Cleanup
For immediate resource destruction without deleting the entity:
// This triggers the DestroyGfxResources observer immediately
ecs_remove(world, entity, GfxResource);
This pattern is useful when swapping textures or rebuilding buffers where the entity itself must persist but the underlying GPU resource must change.
Summary
- The GfxResourceSystem integrates with flecs ECS to provide automatic lifecycle management for all bgfx resources.
- The GfxResource component pairs a
uint16_thandle with a ResourceType enum to enable type-safe destruction. - The DestroyGfxResources observer runs on
EcsUnSet, ensuringbgfx_destroy_*functions are called for textures, buffers, programs, and uniforms. - HotReloadableShader components enable runtime shader recompilation through integration with the
xWatcherfile-monitoring system. - All resource management logic is centralized in
equilibrium/systems/rendering/gfx_resource_system.candgfx_resource_system.h.
Frequently Asked Questions
How does the GfxResourceSystem prevent memory leaks when entities are destroyed?
The system registers a flecs observer that triggers on the EcsUnSet event for GfxResource components. When an entity is deleted or the component is removed, the observer automatically executes the appropriate bgfx_destroy_texture, bgfx_destroy_program, or other destruction function based on the ResourceType field, ensuring GPU handles are always released.
What types of rendering resources does the GfxResourceSystem support?
The system supports all major bgfx resource types through the ResourceType enum: textures, static and dynamic vertex buffers, index buffers, shader programs, frame buffers, and uniforms. Each type maps to a specific bgfx destruction function in the DestroyGfxResources observer switch statement.
Can the GfxResourceSystem handle hot-reloading of shaders during development?
Yes. By attaching a HotReloadableShader component alongside FileWatcher, the system monitors shader source files on disk. When modifications are detected, the HotReloadShaders observer automatically reloads the vertex and fragment shaders, destroys the old program, creates a new bgfx program, and updates the handle in the target component.
Where is the GfxResourceSystem initialized in the EquilibriumEngine?
The system is initialized via GfxResourceSystemImport(), defined in equilibrium/systems/rendering/gfx_resource_system.c (lines 80–99). This function registers the GfxResource, HotReloadableShader, FileWatcher, and ReloadShader components with the flecs world and attaches all lifecycle observers during engine startup.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →