Tutorial 72: Choosing the Right Rendering Backend

CNA Tutorials  ·  Architecture

CNA supports four rendering backends. The backend is selected at compile time via a CMake option and compiled into the binary. Runtime switching is not supported.

Backend comparison

Backend API coverage Rendering Platforms Best for
SDL_RENDERER 2D-only by design 2D only Linux, Windows, Android, Web, macOS Pure 2D games, CI/CD, maximum compatibility
EASYGL Most mature 2D + 3D Linux, Windows, Android (GLES 3.0), Web (WebGL 2) Most games, broadest GPU support, easiest debug
VULKAN Second-most mature 2D + 3D Linux, Windows, Android Performance-critical games, modern GPUs
BGFX Full parity (Phase 72) 2D + 3D Linux, Windows, macOS, Android, Web Multi-backend portability via a single abstraction

SDL_RENDERER — 2D only, best compatibility

The SDL_RENDERER backend maps CNA's API onto SDL3's built-in 2D accelerated renderer. It requires no OpenGL or Vulkan driver. SDL3's renderer targets can be hardware-accelerated via the OS compositor (Metal on macOS, D3D11 on Windows, OpenGL on Linux) without the application needing to use those APIs directly.

Limitations: no VertexBuffer, no IndexBuffer, no custom Effect shaders, no BasicEffect 3D rendering, no RenderTarget2D with depth/stencil, no MRT. Any call that requires 3D will throw a NotSupportedException.

Use this backend when: your game is purely 2D, you need to run in a CI environment without a GPU driver, or you are targeting a platform with no OpenGL/Vulkan support.

EASYGL — most mature backend, OpenGL ES 3.0

The EASYGL backend uses the easy-gl library (a sibling repo) to provide an OpenGL ES 3.0 / OpenGL 3.3 core profile implementation. It is CNA's primary, most mature backend, and covers the vast majority of the XNA 4.0 API surface including:

  • Full 3D rendering with VertexBuffer, IndexBuffer, BasicEffect, custom Effect
  • RenderTarget2D with depth/stencil and MRT (up to 4 targets)
  • All BlendState, DepthStencilState, RasterizerState presets
  • GLSL shader support via the Effect system
  • Texture formats: Color, HalfVector4, Vector4, Rgba1010102, HdrBlendable

EASYGL is the recommended backend for development and for shipping on Linux, Windows, and Android. RenderDoc attaches cleanly for GPU debugging.

VULKAN — second-most mature, best performance

The VULKAN backend targets Vulkan 1.2 and is CNA's second-most mature backend, with all five stock effects implemented and pixel-tested. It offers lower CPU overhead and better multi-threading opportunities (though CNA does not currently use Vulkan secondary command buffers from worker threads).

The setup cost is higher: you need vulkan-headers and libvulkan-dev, and Vulkan validation layers are recommended during development (see Tutorial 73). Shader code compiles to SPIR-V at build time via glslangValidator or shaderc.

Use VULKAN when: you are targeting modern desktop GPUs, you need the best frame time performance, or you want to future-proof your pipeline — outside of workloads that need custom BlendState modes or working OcclusionQuery.

BGFX — full 2D+3D parity, multi-backend

The BGFX backend wraps the bgfx library, which in turn can target OpenGL, Metal, Direct3D 11/12, or Vulkan depending on the platform. It reached full 2D+3D pixel-verified parity with EasyGL and Vulkan as of this project's Phase 72 — all five stock effects, render targets (MSAA/mip/depth), and all four state classes are real and pixel-tested. Two named gaps remain: custom ShaderEffect source compilation is unsupported (bgfx wants precompiled binary shaders), and OcclusionQuery correctness can't be pixel-verified in this project's CI sandbox. It provides the widest platform reach including macOS (Metal) from a single CMake build.

Decision table

SituationChoose
Pure 2D game, need web & mobileSDL_RENDERER or BGFX
3D game, Linux/Windows primaryEASYGL
3D game, best performance, modern hardwareVULKAN
Need macOS Metal supportBGFX
Development & debuggingEASYGL (best RenderDoc / apitrace support)
CI without GPUSDL_RENDERER (works with software renderer)

How to set the backend in CMake

Pass -DCNA_GRAPHICS_BACKEND=<NAME> to the CMake configure step. The value is case-sensitive.

// CMakeLists.txt excerpt — backend selection

set(CNA_GRAPHICS_BACKEND "EASYGL" CACHE STRING
    "CNA rendering backend: SDL_RENDERER | EASYGL | VULKAN | BGFX")
set_property(CACHE CNA_GRAPHICS_BACKEND PROPERTY STRINGS
    SDL_RENDERER EASYGL VULKAN BGFX)

# Validate the choice
if(NOT CNA_GRAPHICS_BACKEND MATCHES "^(SDL_RENDERER|EASYGL|VULKAN|BGFX)$")
    message(FATAL_ERROR
        "Invalid CNA_GRAPHICS_BACKEND: '${CNA_GRAPHICS_BACKEND}'. "
        "Valid values: SDL_RENDERER, EASYGL, VULKAN, BGFX")
endif()

# Each backend adds its own CMake subdirectory
add_subdirectory(src/backends/${CNA_GRAPHICS_BACKEND})

# Expose the selection as a preprocessor define
target_compile_definitions(CNA PUBLIC
    CNA_BACKEND_${CNA_GRAPHICS_BACKEND}=1)

To configure and build with the Vulkan backend:

cmake -S . -B build-vulkan -DCNA_GRAPHICS_BACKEND=VULKAN
cmake --build build-vulkan --target CNA CnaTests

Preprocessor defines in game code

You can use the preprocessor defines to include backend-specific code paths in your game:

#if defined(CNA_BACKEND_VULKAN)
    // Vulkan-specific: enable Vulkan validation layers at startup
    SDL_SetHint(SDL_HINT_VULKAN_DISPLAY, "1");
#elif defined(CNA_BACKEND_EASYGL)
    // EasyGL-specific: request a debug context for RenderDoc
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS,
                        SDL_GL_CONTEXT_DEBUG_FLAG);
#endif

Runtime switching (not supported)

CNA does not support switching the rendering backend at runtime. The backend is compiled into the binary and initialised once during Game::Run(). If you need to support multiple backends from a single executable, build the project twice with different CNA_GRAPHICS_BACKEND settings and ship both binaries (e.g. auto-select based on SDL_GetPlatform() in a launcher shell script).

Alternatively, the BGFX backend selects its underlying API at runtime from the set of backends bgfx was compiled with, which gives some degree of runtime flexibility on platforms where BGFX supports multiple APIs.