OpenGL Resource management

Met.3D uses a centralized OpenGL resource management system called MGLResourcesManager. It is responsible for GPU resource lifetime management, resource sharing across OpenGL contexts, actor registration, and OpenGL capability initialization.

OpenGL resources are typically managed manually through opaque resource handles. Without a centralized management system, this can easily lead to:

  • GPU memory leaks

  • Duplicated resources

  • Inconsistent ownership semantics

  • Unnecessary shader recompilation

For example, if multiple actors require the same shader or GPU buffer, each actor could otherwise create and manage its own copy independently.

MGLResourcesManager addresses these issues by storing graphics resources centrally and sharing them between consumers. Managed resources are reference-counted and are only deleted once no component references them anymore and space is needed.

The manager additionally tracks the GPU memory usage of registered resources and uses this information to limit the total amount of cached GPU data.

Beyond GPU resource management, MGLResourcesManager also acts as the central entry point for OpenGL initialization in Met.3D. It validates OpenGL version compatibility, queries supported features and extensions, and exposes capability information required by rendering components.

The manager furthermore maintains the registration of actor factories and actor groups used throughout the system.

Design goals

The OpenGL resource management system was designed with the following goals:

  • Centralized GPU resource ownership

  • Resource sharing across rendering systems

  • Automatic lifetime management

  • Prevention of GPU memory leaks

  • Reduced shader recompilation overhead

  • GPU memory usage tracking

  • Resource caching and deferred eviction

  • Centralized OpenGL capability initialization

  • Centralized actor factory registration

OpenGL context management

MGLResourcesManager owns a dedicated OpenGL context that is used for resource management operations when no other OpenGL context is currently active.

This context is shared with all other OpenGL contexts used by Met.3D. As all rendering contexts in Met.3D participate in the same OpenGL sharing group, resources created through the resource manager are accessible from all shared contexts.

The dedicated resource manager context is primarily used for:

  • Resource creation outside active rendering passes

  • Background GPU resource initialization

  • Resource cleanup and deletion

  • Capability and extension queries during initialization

This design ensures that resource management operations can still be performed safely even if no rendering view currently owns the active OpenGL context.

Important

OpenGL resource creation and deletion always require a valid active OpenGL context.

The dedicated context owned by MGLResourcesManager guarantees that the resource manager can perform these operations independently of the currently active rendering view.

OpenGL initialization and capability management

MGLResourcesManager is responsible for initializing the OpenGL runtime environment used by Met.3D. This is done in MGLResourcesManager::initializeGL()

During initialization, the manager:

  • Verifies that the current OpenGL version is supported

  • Queries available OpenGL extensions

  • Detects supported rendering features

  • Checks implementation-specific limitations

  • Exposes capability information to rendering components

The queried capabilities are:

  • OpenGL version support

  • Available OpenGL extensions

  • Line width support

This functionality provides a centralized abstraction layer for OpenGL feature detection and prevents individual rendering components from having to perform redundant capability checks independently.

Actor factory and group management

MGLResourcesManager additionally maintains the registration of actor factories and actor groups.

The registration of all available actors is performed in MApplicationConfigurationManager::registerActorFactories(). It registers the actor factories in the resource manager.

Actor factories are used to instantiate actors, while actor groups provide logical grouping and organization of available actor types inside the application. This is exposed to the user on actor creation, where the groups represent different categories of actors.

Centralizing actor registration inside the resource manager ensures that:

  • Actor factories are globally accessible

  • Actor categories remain consistent across the application

Text renderer management

MGLResourcesManager owns and initializes the global text manager used throughout Met.3D. It is accessed through:

MGLResourcesManager::getTextManager()

The text manager uses the dedicated OpenGL context owned by MGLResourcesManager for all OpenGL resource creation and management.

As the resource manager context participates in the global OpenGL sharing group, text rendering resources created by the text manager are accessible from all rendering contexts.

The text manager is initialized during the creation of the resource manager.

Managed resource types

All managed resources must derive from GL::MAbstractGPUDataItem. The base class provides getGPUMemorySize_KiB() to get the size of the resource in memory, and getRequestKey(), which identifies the item in the system.

Currently, the following OpenGL resources can be managed:

  • Textures (GL::MTexture)

  • Index Buffers (GL::MIndexBuffer)

  • Vertex Buffers (GL::MVertexBuffer or GL::MTypedVertexBuffer)

  • Shader-storage-buffer objects (GL::MShaderStorageBufferObject)

  • Uniform block buffers (GL::MUniformBufferObject)

Resource lifecycle

Resource creation and registration

You create GPU resources with a normal heap allocation. Newly created GPU resources must be registered with the resource manager using:

MGLResourcesManager::tryStoreGPUItem()

If successful, ownership of the resource is transferred to the resource manager.

The operation may fail if a resource with the same key is already registered in the system, or insufficient GPU memory is available. The caller is responsible for deleting the resource they tried to store.

To check whether a resource already exists, use:

MGLResourcesManager::contains()

Acquiring resource references

Once you successfully stored a resource, consumers must acquire a reference using:

MGLResourcesManager::getGPUItem()

This increments the internal reference counter of the resource.

Important

tryStoreGPUItem() does not automatically create an active reference for the caller. Failing to call getGPUItem() after insertion leaves the resource with a reference count of zero.

Note

For textures specifically, MGLResourcesManager::createTexture() wraps texture creation, registration, and reference acquisition into a single call.

This helper function exists primarily to reduce boilerplate code.

Releasing resources

Once a consumer no longer requires a resource, it must release its reference using:

MGLResourcesManager::releaseGPUItem()

To release all currently held references at once, use:

MGLResourcesManager::releaseAllGPUItemReferences()

Resource caching and eviction

Resources whose reference count reaches zero are not immediately deleted. Instead, they remain cached inside the resource manager to allow future reuse without re-uploading data to the GPU.

Unused resources are only deleted when GPU memory pressure requires additional space.

The eviction order is determined by the order in which resources were released.

Shader management

Shader programs are managed separately from other GPU resources.

Instead of directly managing compiled shader programs, the resource manager stores shared instances of GL::MShaderEffect, which encapsulate shader loading and compilation.

Shader effects are created or retrieved using:

MGLResourcesManager::generateEffectProgram()

If the function creates a new shader effect instance, it returns true. Existing shader effects are reused whenever possible.

Shader compilation itself is performed through the corresponding MShaderEffect::compile*() methods.

Shader effects are managed through shared ownership semantics and are automatically released once no component references them anymore.

Usage Guidelines

Always acquire references explicitly

After storing a resource successfully, always retrieve it again via getGPUItem() to establish ownership correctly.

Release resources when no longer needed

Failing to release resource references prevents the manager from reclaiming GPU memory.

Prefer shared resources

Whenever possible, reuse existing GPU resources instead of creating duplicate buffers, textures, or shader programs.