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_TEXTURE
  • RESOURCE_TYPE_VERTEX_BUFFER
  • RESOURCE_TYPE_DYNAMIC_VERTEX_BUFFER
  • RESOURCE_TYPE_INDEX_BUFFER
  • RESOURCE_TYPE_PROGRAM
  • RESOURCE_TYPE_FRAME_BUFFER
  • RESOURCE_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:

  1. InitHotReloadShaders: When both FileWatcher and HotReloadableShader exist on an entity, this observer registers the shader files with the underlying xWatcher system (a cross-platform file-watcher).

  2. UpdateFileWatcher: Runs every frame via the OnInput tag to poll for file modifications.

  3. HotReloadShaders: Triggers on EcsOnSet when ReloadShader is 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

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_t handle with a ResourceType enum to enable type-safe destruction.
  • The DestroyGfxResources observer runs on EcsUnSet, ensuring bgfx_destroy_* functions are called for textures, buffers, programs, and uniforms.
  • HotReloadableShader components enable runtime shader recompilation through integration with the xWatcher file-monitoring system.
  • All resource management logic is centralized in equilibrium/systems/rendering/gfx_resource_system.c and gfx_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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →