Platform Support

Per-platform status, stated at the level it has actually been verified

Four separate axes

In 0.1.0-alpha.1, “backend” is too ambiguous to be useful. CNA makes four different decisions:

AxisQuestion it answersSelection
Target operating systemWhich toolchain and OS ABI is this binary built for?CMake toolchain / target; reported by CNA::TargetPlatform
Platform implementationWho owns windows, events, input, timing and host services?CNA_PLATFORM
Graphics rendererWho turns CNA draw calls into pixels?CNA_GRAPHICS_RENDERER and optional CNA_GRAPHICS_RENDERERS
Audio implementationWho opens playback and recording devices?CNA_AUDIO_PLATFORM

The platform implementation implements CNA::Platform::IPlatform and supplies narrow services to a renderer. It does not decide the renderer, audio implementation or target OS. This is also why the old public CNA::Platform enum became CNA::TargetPlatform: CNA::Platform now names the host-integration namespace.

Platform and audio implementations

OptionImplemented valuesDefaultBoundary
CNA_PLATFORMSDL3, SDL2, HEADLESS, TERMINALSDL3TERMINAL is POSIX-only. SDL12, WIN32 and EMSCRIPTEN are reserved but unimplemented and fail configuration.
CNA_AUDIO_PLATFORMSDL3, SDL2, NULLSDL3All three select low-level device code. Only SDL3 defines SOUND_ENABLED and links the SDL3_mixer-backed XNA playback/decoding engine. SDL2 and Null are not feature-equivalent game-audio backends. OPENAL, WASAPI and ALSA are reserved and rejected.
# A display-free simulation that still uses SDL3 audio
cmake -S . -B build-headless \
  -DCNA_PLATFORM=HEADLESS \
  -DCNA_AUDIO_PLATFORM=SDL3 \
  -DCNA_GRAPHICS_RENDERER=HEADLESS

# Real SDL 2.30 host plus the low-level SDL2 audio-device selection;
# alpha.1 does not enable its high-level XNA mixer/playback facade
cmake -S . -B build-sdl2 \
  -DCNA_PLATFORM=SDL2 \
  -DCNA_AUDIO_PLATFORM=SDL2 \
  -DCNA_GRAPHICS_RENDERER=OPENGLES3

SDL2 is not a compatibility shim. When both platform and audio select SDL2, CNA refuses SDL_RENDERER, SDL_GPU, FNA3D and FREEDIRECT because those renderer families link SDL3 directly. This prevents one process from mixing the two SDL generations.

Audio selection is ahead of audio parity. In modules/CMakeLists.txt, SOUND_ENABLED is defined only for CNA_AUDIO_PLATFORM=SDL3. Non-SDL3 builds omit the SDL3_mixer engine, decoder and mixer-dependent tests; the XNA audio facade remains present but does not gain production playback from the SDL2/Null device classes. Null is therefore useful for deterministic configuration and device-contract work, not proof that a silent mixer consumes normal game audio.

What "supported" means here

"Supported" is doing a lot of work in most framework documentation. This page avoids the word and names the level instead, because the difference between "there is code for it" and "someone ran it" is exactly the difference that costs you a weekend.

LevelWhat it means
Code path existsSources and CMake wiring are present for the target.
ConfiguresCMake accepts the target and the chosen renderer, and produces a build system.
Cross-compilesA full build completes from a different host operating system.
Runs under Wine or an emulatorThe binaries execute and render, but not on the operating system they target.
Runs on real hardwareSomeone ran it on the actual platform.
Covered by CIA workflow builds or tests it automatically on push and pull request — the only level that keeps working without anyone remembering to check.

These levels do not imply each other. The repository contains MinGW/Wine paths, but the tag has no automatic MinGW/Wine workflow; the native-Windows lanes also require manual dispatch. macOS has automatic build coverage. Android has source/NDK wiring and no workflow.

Platform matrix

PlatformRenderersHighest level reachedAutomatic CI
Linux Every renderer that is not Windows-only, macOS-only or Emscripten-only — the large majority of the 50. Default: OPENGLES3. Runs on real hardware Yes — multiple focused workflows on ubuntu-* runners
Windows The 14 Windows-only renderers, plus the portable ones. Default: SDL_RENDERER. Cross-compiles; runs under Wine + DXVK / vkd3d-proton No — the two MSVC workflows are manual dispatch only
Web (Emscripten) WEBGL1, WEBGL2, CANVAS, HTML_DOM, SVG_DOM, PIXIJS. Default: WEBGL2. Runs in a real browser (HTML_DOM) Yes — HTML_DOM runtime tests plus an Emscripten multi-renderer build/link lane
macOS METAL (macOS-only), plus the portable renderers. Built on real macOS in CI Yes — Apple and Metal workflows on macOS runners
Android Portable renderers via the NDK toolchain. Code paths and NDK sensor implementations exist No CI of any kind
iOS / tvOS SDL_RENDERER is the only validated iOS renderer; tvOS has no validated path. iOS device final-link and simulator one-frame smoke Yes for the narrow iOS build/simulator lanes; no physical-device or pixel claim

A single renderer remains the default build. The opt-in CNA_GRAPHICS_RENDERERS mode can link compatible sets, but it does not erase per-renderer capability or platform boundaries.

Linux

Linux is CNA's primary development platform and carries most of its CI. It is where the largest number of renderers can be selected, and where the project is developed on real hardware — the default renderer here is OPENGLES3, one of the five EasyGL profiles.

The Windows-gated renderers are also driven from Linux: they are cross-compiled with MinGW-w64 and executed under Wine, so a Linux workstation is in practice the machine where the Direct3D ladder gets exercised too. See Windows for what that does and does not prove.

Practical requirements: the sharp-runtime sibling checkout for every build, easy-gl plus meta-gl for the default renderer, and the FFmpeg development packages, which are mandatory here. Building has the exact commands.

Windows

Windows has the largest platform-exclusive renderer set in the project: 14 renderers refuse to configure anywhere else — the DIRECTX1 to DIRECTX12 ladder plus DIRECT2D, GLIDE and GDI. GLIDE additionally needs a 32-bit i686 toolchain. The default renderer on Windows is SDL_RENDERER.

The repository provides MinGW cross-build and Wine/DXVK or vkd3d-proton execution paths, but none is an automatic workflow in this tag. Two native MSVC workflows exist (one for DIRECTX11, DIRECTX12 and DIRECT2D, one for GDI), and both are manual-dispatch. Treat runtime claims as results from the exact manual configuration, not as continuous native-Windows coverage.

Two further Windows facts worth knowing before you plan a release:

  • There is no video on Windows. FFmpeg is not used on any Windows target and the video translation units are excluded from the build, so Video and VideoPlayer are missing symbols. Calling code compiles and then fails to link.
  • The MinGW cross-build itself is not covered by CI. No workflow builds a MinGW target or runs anything under Wine; that path is exercised by hand.

Web (Emscripten)

The web target is real and has automatic browser lanes. Six renderers are Emscripten-only — WEBGL1, WEBGL2, CANVAS, HTML_DOM, SVG_DOM and PIXIJS — and WEBGL2 is the default. Multi-renderer Emscripten jobs also configure and link compatible browser sets.

There is no save persistence on the web at all. Under Emscripten, SDL_GetPrefPath resolves into volatile MEMFS; CNA mounts no IDBFS and never calls FS.syncfs. Every save your game writes is silently discarded when the page reloads. Nothing fails, nothing warns — the data is simply gone. Plan around it before you ship a browser build.

Your Game object must be heap-allocated on the web. A stack-allocated Game is silently corrupted under Emscripten's event loop. Allocate it with new (or a smart pointer that outlives the loop) in your web entry point.

Video is also absent here, for the same reason as on Windows: the video translation units are excluded from Emscripten builds, so Video/VideoPlayer compile and then fail to link. Networking discovery does not work on Emscripten either.

Serve the generated .html, .js and .wasm from a local web server; browsers block direct file:// access.

macOS

macOS carries the METAL renderer, which drives Apple Metal directly and uses SDL3 only for the window and CAMetalLayer. It is macOS-only, and it is built on a real macos-14 runner on every push and pull request — the one automatic non-Linux native CI job in the project.

Two caveats: METAL throws on custom ShaderEffect shaders, and the CI job is a build, not the full test suite. FFmpeg is a hard requirement on macOS exactly as it is on Linux, so the four development packages must be installed before configuring.

Android

Android is genuinely wired rather than aspirational: the NDK toolchain path works, the code paths exist, and the NDK sensor implementations are real — Compass and Motion are in fact Android-only and report NotSupported elsewhere. See Sensors.

Android has no CI at all — zero jobs, zero presets. Every Android build is one somebody ran by hand, and nothing catches an Android regression automatically. Video is unavailable here too.

iOS and tvOS

iOS support is experimental and deliberately narrow. The tag ships an iOS toolchain and allows only SDL_RENDERER by default. Apple CI final-links a device application and launches a simulator smoke application for one frame. That establishes configuration, link and simulator startup — not physical-device behavior, input/audio coverage or pixel correctness. CNA_APPLE_ALLOW_UNVALIDATED_RENDERER=ON merely permits experiments outside the allow-list; it is not a support claim. tvOS has no validated path.

What CI actually covers

The tag contains 21 GitHub Actions workflow files. They cover intended general tests, platform conformance, multi-renderer Linux and Emscripten builds, Apple/Metal, declared C API compatibility/release gates, and focused renderer/subsystem checks. Trigger and evidence still matter more than the raw file count. The intended unfiltered general job and two Input matrix rows pass the removed EASYGL value and fail during configuration; the C API final target independently fails its renderer-count assertion.

WorkflowTriggerRunnerWhat it does
General, input, devices and focused renderer checksMostly push / PRLinuxUnit, build, browser and renderer-specific contracts; exact filters vary by workflow.
multi-renderer-ci.yml and emscripten-multi-renderer-ci.ymlPush / PRLinux / EmscriptenRuntime-selection sets, combination rules and Emscripten build/link coverage.
platform-ci.ymlPush / PRLinuxPlatform abstraction and SDL2/SDL3/Headless/Terminal combinations.
apple-ci.yml and metal-macos-ci.ymlPush / PRmacOSmacOS/Metal plus iOS device final-link and simulator smoke lanes.
Five c-api-*.yml workflowsPush / PRLinuxABI baseline, compatibility, coverage, limitations and release-gate checks.
d3d-windows-ci.yml and gdi-windows-ci.ymlManual onlyWindowsNative MSVC D3D11/D3D12/Direct2D and GDI suites.

How to read the matrix

The release has much broader automated coverage than the earlier site snapshot, but a workflow is not universal proof:

  • The Windows D3D and GDI lanes remain manual, so their existence is not an automatic merge gate.
  • The iOS device lane final-links; the simulator lane launches one smoke frame. Neither establishes physical-device or full-game correctness.
  • The Emscripten multi-renderer lane proves configuration and linking, while browser-specific workflows provide the runtime evidence for selected web renderers.
  • Android has source and NDK wiring but no automatic workflow in the tag.

Stated plainly: CI exists and spans Linux, macOS and headless-browser jobs, but alpha.1 does not have an effective full-suite gate: its intended general job uses an invalid renderer identity. The GPU pixel/oracle matrix is also run locally, by hand. Verification & Known Issues covers the wider testing picture, and Graphics Renderers the per-renderer side of it.

Where video is available

Video and VideoPlayer exist on Linux and macOS only, where FFmpeg is a mandatory dependency. On Windows, Emscripten and Android the three video translation units are excluded from the build entirely. The headers still exist, so code that uses them compiles and then fails to link — a link error, not a runtime message. Audio and MediaPlayer song playback go through SDL3_mixer and are unaffected. See Video Playback.