Skip to main content

Custom Backends

Filament’s backend abstraction layer provides a clean separation between the rendering engine and platform-specific graphics APIs. This architecture enables Filament to support multiple graphics backends while maintaining a unified API.

Backend Architecture

Filament currently supports the following backends:
  • OpenGL 4.1+ (Linux, macOS, Windows)
  • OpenGL ES 3.0+ (Android, iOS)
  • Metal (macOS, iOS)
  • Vulkan 1.0 (Android, Linux, macOS, Windows)
  • WebGPU (Android, Linux, macOS, Windows)
  • WebGL 2.0 (Browser)
The backend system is defined in filament/backend/include/backend/:
enum class Backend : uint8_t {
    DEFAULT = 0,  // Automatically selects appropriate driver
    OPENGL = 1,   // OpenGL/ES driver (default on Android)
    VULKAN = 2,   // Vulkan driver (default on Linux/Windows)
    METAL = 3,    // Metal driver (default on macOS/iOS)
    WEBGPU = 4,   // WebGPU driver
    NOOP = 5,     // No-op driver for testing
};

Platform Abstraction Layer

The Platform class abstracts backend creation and platform-specific operations:
class UTILS_PUBLIC Platform {
public:
    struct SwapChain {};
    struct Fence {};
    struct Stream {};
    struct Sync {};
    
    // Platform-specific implementations
};

Creating a Custom Platform

When implementing a custom platform, you can provide your own Platform subclass:
Engine* engine = Engine::Builder()
    .backend(Backend::VULKAN)
    .platform(myCustomPlatform)
    .build();
The Platform object must outlive the Engine instance.

Driver Interface

The Driver class defines the backend interface that all graphics backends must implement:
class Driver {
public:
    virtual ~Driver() noexcept;
    
    // Execute user callbacks
    virtual void purge() noexcept = 0;
    
    // Get shader model information
    virtual ShaderModel getShaderModel() const noexcept = 0;
    
    // Get supported shader languages
    virtual utils::FixedCapacityVector<ShaderLanguage> 
        getShaderLanguages(ShaderLanguage preferredLanguage) const noexcept = 0;
        
    // All driver API methods defined via DriverAPI.inc
};

Command Stream Architecture

Filament uses a command stream pattern to batch rendering commands and execute them asynchronously on the render thread:

How Commands Work

  1. Command Recording - The main thread records commands into a circular buffer
  2. Command Submission - Commands are submitted to the backend driver
  3. Command Execution - The render thread executes commands on the GPU
From filament/backend/include/private/backend/CommandStream.h:
class CommandBase {
protected:
    using Execute = Dispatcher::Execute;
    
public:
    // Executes this command and returns the next one
    CommandBase* execute(Driver& driver) {
        intptr_t next;
        mExecute(driver, this, &next);
        return reinterpret_cast<CommandBase*>(
            reinterpret_cast<intptr_t>(this) + next);
    }
};

Backend Selection

Filament automatically selects the best backend for each platform:
  • macOS/iOS: Metal (default)
  • Windows/Linux: Vulkan (default)
  • Android: Vulkan or OpenGL ES
  • Web: WebGL 2.0
You can override the default selection:
Engine* engine = Engine::Builder()
    .backend(Backend::OPENGL)
    .build();

Feature Levels

Backends support different feature levels based on platform capabilities:
enum class FeatureLevel : uint8_t {
    FEATURE_LEVEL_0 = 0,  // OpenGL ES 2.0 features
    FEATURE_LEVEL_1,      // OpenGL ES 3.0 features (default)
    FEATURE_LEVEL_2,      // OpenGL ES 3.1 + 16 texture units + cubemap arrays
    FEATURE_LEVEL_3       // OpenGL ES 3.1 + 31 texture units + cubemap arrays
};
Query and set feature levels:
// Get supported feature level
FeatureLevel supported = engine->getSupportedFeatureLevel();

// Activate a feature level
engine->setActiveFeatureLevel(FeatureLevel::FEATURE_LEVEL_2);

// Get current active level
FeatureLevel active = engine->getActiveFeatureLevel();

Shader Language Support

Different backends use different shader languages:
BackendShader Languages
OpenGLGLSL/ESSL
VulkanSPIR-V
MetalMSL, Metal Library
WebGPUWGSL
The material compiler (matc) generates shader code for all supported backends from a single material definition.

Backend-Specific Configuration

Metal Configuration

Engine::Config config;
config.metalUploadBufferSizeBytes = 512 * 1024;
config.preferredShaderLanguage = 
    Engine::Config::ShaderLanguage::METAL_LIBRARY;

Engine* engine = Engine::Builder()
    .config(&config)
    .build();

OpenGL Configuration

Engine::Config config;
config.forceGLES2Context = false;  // Use GLES 3.0+

Engine* engine = Engine::Builder()
    .config(&config)
    .build();

Shared Contexts

You can create an engine with a shared context for resource sharing:
Engine* engine = Engine::Builder()
    .backend(Backend::OPENGL)
    .sharedContext(existingGLContext)
    .build();

Platform-Specific Implementations

Filament provides platform-specific implementations:
  • OpenGLPlatform - Base class for OpenGL platforms
  • VulkanPlatform - Vulkan platform abstraction
  • MetalPlatform - Metal platform abstraction
Access the platform object:
Platform* platform = engine->getPlatform();

// Cast to specific platform (requires RTTI)
auto* glPlatform = dynamic_cast<OpenGLPlatform*>(platform);
if (glPlatform) {
    // Use OpenGL-specific features
}

Best Practices

  1. Use the default backend unless you have specific requirements
  2. Match feature levels to your target hardware capabilities
  3. Test on all target platforms as backends behave differently
  4. Handle backend creation failure gracefully
  5. Use shader variants to support multiple backends efficiently

Example: Backend Detection

#include <filament/Engine.h>

using namespace filament;

Engine* createEngine() {
    Engine* engine = Engine::Builder().build();
    
    if (!engine) {
        // Backend initialization failed
        return nullptr;
    }
    
    Backend backend = engine->getBackend();
    printf("Using backend: %s\n", to_string(backend).data());
    
    return engine;
}

See Also

Build docs developers (and LLMs) love