GPU Renderer Overview
Render Views on the GPU by integrating a custom GPUDriver into an engine.
On this page
You can render Views directly on the GPU through an engine's existing graphics API by providing a custom GPUDriver. Each accelerated View renders offscreen to a GPU texture that you draw in your frame loop.
📘 Included with AppCore
Applications using AppCore already provide a GPU driver—
App::Create()sets up Direct3D 11 on Windows, Metal on macOS, and OpenGL on Linux. You only need the setup on this page when managing the renderer directly withRenderer::Create().
Choosing Between CPU and GPU
You choose whether to accelerate each View individually using ViewConfig::is_accelerated— CPU and GPU Views can run alongside each other in the same Renderer.
| Renderer | You provide | You read |
|---|---|---|
| CPU (default) | Nothing | Pixel buffer from View::surface() to upload |
| GPU | GPUDriver and shader programs |
Offscreen texture from View::render_target() |
Choose the GPU renderer for animated or demanding interfaces— you also need it to display live engine textures on a page (see Displaying Custom Textures). Both paths support .dds files, though CPU Views and GPU drivers without format support decompress them to BGRA. To keep texture blocks compressed in video memory, you need an accelerated View and a driver that reports the format (see Compressed Textures).
Implementing the Driver
To render on the GPU, you must supply three components:
GPUDriverimplementation — manages textures, render buffers, geometry, and drawing commands for the engine's graphics API. See Implementing a GPUDriver.- Shader programs — the seven shaders required to render geometry and paths, provided in the SDK for each backend. See GPU Shader Programs.
- Device capabilities (optional) — reports texture format support and rendering limits. See GPU Device Capabilities.
Starting from a Reference Driver
Building a GPU driver from scratch requires significant work. We recommend starting from one of the reference implementations in the SDK's platform folder.
These drivers are licensed under the Zlib license and support Direct3D 11, Direct3D 12, Metal, and OpenGL— the same drivers AppCore uses. You can copy the driver matching the engine's graphics API, swap in the engine's device and context handles, and keep the rest of the rendering logic intact.
Registering the Driver
Register the driver with the platform before creating the renderer:
///
/// The driver must outlive the Renderer.
///
static MyGPUDriver gpu_driver;
void InitRenderer() {
Platform::instance().set_gpu_driver(&gpu_driver);
renderer = Renderer::Create();
}
You own the driver instance and must keep it alive for the lifetime of the Renderer (see Your First Game UI for the remaining platform setup).
To make a View use the GPU, set ViewConfig::is_accelerated to true:
ViewConfig view_config;
view_config.is_accelerated = true;
RefPtr<View> view = renderer->CreateView(1280, 720, view_config, nullptr);
🚧 Set Driver Before Creating Accelerated Views
Creating an accelerated View before setting a GPU driver logs a fatal error and exits the process immediately.
Rendering Each Frame
In each frame of the render loop, update and render the display, execute the driver's command list, and draw the resulting texture:
void RenderOneFrame() {
renderer->Update();
renderer->RefreshDisplay(0);
renderer->Render();
///
/// Execute the commands our driver received during Render().
///
gpu_driver.DrawCommandList();
///
/// Draw the View's texture as a quad, mapped with its UV coordinates.
///
RenderTarget target = view->render_target();
if (!target.is_empty) {
// Pseudo-code, bind target.texture_id and draw a quad with
// target.uv_coords here.
}
}
During Renderer::Render(), the library updates resources through the driver and dispatches a list of drawing commands via GPUDriver::UpdateCommandList(). You must execute this command list before calling Renderer::Render() again. The library never draws to the backbuffer (DrawCommandList() in the snippet is a custom helper on MyGPUDriver rather than part of the library interface; see Updating and Rendering for standard loop calls).
Drawing the Render Target
Query View::render_target() every frame to get the active texture. Do not cache the texture identifier, because the texture ID and its dimensions can change at runtime.
Map the drawn quad with target.uv_coords rather than 0..1 coordinates. The library often pads offscreen render targets to larger dimensions, and the UV rectangle defines the active page area within that texture.
📘 Frames Without Repaints
When no page content changes, the library dispatches no command list during
Renderer::Render(). The texture keeps its previous image, so you should continue drawing the quad every frame.