Runtime Renderer Selection
Single and multi-renderer modes
Single-renderer mode remains CNA's default and recommended smallest build. Set CNA_GRAPHICS_RENDERER and only that implementation family is linked.
cmake -S . -B build-one -G Ninja \
-DCNA_GRAPHICS_RENDERER=VULKAN
To link several compatible identities, add the plural list. The singular identity is still the compile-time default and must be a member; the default is normalized to the first registry entry.
cmake -S . -B build-multi -G Ninja \
-DCNA_GRAPHICS_RENDERER=HEADLESS \
-DCNA_GRAPHICS_RENDERERS="HEADLESS;SOFTWARE;STUB"
An empty CNA_GRAPHICS_RENDERERS value means single-renderer mode. A default outside the plural list is a configure error; CNA does not silently rewrite the request.
Selection API
Selection precedence is explicit API preference, then the CNA_GRAPHICS_RENDERER environment variable, then the compile-time default. The environment variable affects runtime selection; it does not add a family that was not linked.
#include "CNA/GraphicsRendererSelection.hpp"
using CNA::GraphicsRendererSelection;
using CNA::GraphicsRendererType;
for (auto available : GraphicsRendererSelection::GetAvailable()) {
// populate a settings UI before Game/GraphicsDevice exists
}
GraphicsRendererSelection::SetPreferred(GraphicsRendererType::Vulkan);
MyGame game;
game.Run();
An unknown value or known-but-uncompiled identity throws. GetAvailable() and GetSelected() are safe before a device exists.
Failure and fallback
Hard failure is the default. If the selected renderer is unavailable or initialization throws, CNA reports the failure. This prevents a game that requested one capability set from quietly running on another.
constexpr GraphicsRendererType fallbackChain[] = {
GraphicsRendererType::OpenGLES3,
GraphicsRendererType::Software,
GraphicsRendererType::Headless
};
GraphicsRendererSelection::SetFallbackChain(fallbackChain);
// Alternatively opt into CNA's automatic fallback ordering:
GraphicsRendererSelection::EnableAutomaticFallback(true);
Fallback history records availability-probe refusals, initialization failures and skipped candidates. If every candidate fails, CNA throws with the first failure as the primary cause.
OpenGL and Vulkan require incompatible window kinds. CNA may destroy and recreate a window it owns while falling back across that boundary. A caller-supplied DeviceWindowHandle cannot be recreated, so that candidate is skipped with WindowKindConflict.
The policy latches after the first GraphicsDevice is constructed. Later calls that change the preference, chain or automatic-fallback flag throw InvalidOperationException. Recreating the same renderer during device reset remains valid.
Default, selected and active are different answers
| Question | API |
|---|---|
| What is the build default? | CNA::getCurrentGraphicsRendererType() |
| What will CNA attempt? | GraphicsRendererSelection::GetSelected() |
| What actually started? | GraphicsRendererSelection::GetActive() |
| What does this device use? | GraphicsDevice::GetGraphicsRendererType() / GetGraphicsRendererName() |
Only the default renderer's CNA_RENDERER_<IDENTITY> macro is defined project-wide. Multi-renderer builds define CNA_MULTI_RENDERER; renderer-aware test targets also receive CNA_RENDERER_PRESENT_<IDENTITY> for each linked identity.
Combination rules
PORTABLEGL+ a real-OpenGL family is rejected because both define/call the same globalgl*symbols.GDI+SOFTWAREis rejected because GDI recompiles the Software translation units with different definitions.- Windows-only, Emscripten-only and macOS-only identities cannot cross target-toolchain partitions.
GLIDEcannot be combined with another renderer because it pins the build to the 32-bit Glide ABI.
Several EasyGL identities can coexist after the alpha.1 runtime-profile change, subject to the native-versus-Emscripten partition.
Cost and dependencies
Every selected family contributes code, build time and dependencies. Adding a second identity served by an already-linked EasyGL family is cheaper than adding an unrelated renderer, but it is not free of platform constraints. Ship only the set your application can meaningfully select and test.
Continue with Tutorial 126: Build and Select Several Renderers or return to the full renderer inventory.