What a human maintainer inherits
Evidence basis: source-verified at the pinned commit. Claims on this page were checked by reading the CNA source at commit 009d40f5; unless a sentence says otherwise, nothing here was built or executed. Counts are recomputed from the snapshot's CMake and tree (23 module directories, 21 renderer families, 25 identities, 7 platforms, 4 audio implementations); maturity per family is not claimed.
CNA is not one engine library with replaceable window wrappers. At snapshot 009d40f5 the tree has 23 directories under modules/ (22 declared framework modules plus the renderers/ tree), a separately selected platform, audio transport and renderer identity, 21 physical renderer-family directories serving 25 public renderer identities, an experimental C ABI (0.29.0, 61 public headers), eight external language bindings in other repositories, and a large conditional build and test graph (904 C++ test source files, 12,610 statically declared GoogleTest-family cases). Those counts describe the scope a reviewer has to cover, not code quality or feature completeness.
The main engineering families
The physical layout is enforced, not conventional. modules/CMakeLists.txt declares the framework modules (_cna_framework_modules) and renderer modules (_cna_renderer_modules) and fails the configure when a translation unit lies outside every declared module's src/, tests/, examples/ or benchmarks/ tree, so a file's directory is its CMake owner. The one documented exception is the GDI renderer compiling eight software-module 2D translation units; their physical owner stays software. Each module is created by cna_add_module as a static library cna_<name> with an alias CNA::<Alias> and exposes only its own include/ root.
| Family | Directories at this snapshot | What it owns | Typical change boundary |
|---|---|---|---|
| Foundation | core, math, platform; also diagnostics | Shared types and value conventions, the renderer identity enum (CNA::GraphicsRendererType lives in core), host services, and native event / window / surface access behind CNA::Platform::IPlatform. | Header or contract edits propagate to many modules and to all seven platform implementations. |
| Runtime and compatibility | runtime, graphics, input, audio, media, content, storage, devices, phone, plus the optional gamer-services, net and video-ffmpeg | XNA-facing Game, device, input, audio and content behaviour. Some static XNA APIs (for example Keyboard::GetState, StorageDevice) use the ambient platform because they cannot be handed a context. | Object construction, event semantics and resource disposal cross layers. |
| CNA extensions and tooling modules | graphics-ext, devices-ext, inspector, design | CNAEXT seams beyond XNA (the engine layer, device extensions), the inspector transport and the Framework.Design converters. | Extension APIs are public too; they reach the C ABI and bindings like any other surface. |
| Physical renderers | modules/renderers: 21 family directories with a src/, plus the shared helpers common/d3d and common/mojoshader | Backend-specific resource translation, draws, presentation and synchronization behind IGraphicsRenderer. | One family may be local; a change to the renderer interface, the descriptor or shared state is not. |
| Build-time pipeline and tooling | content-pipeline, cmake/, tools/, scripts/ | Asset import, processing and writing (cna-content), generated registries, probes, source audits and gates. | A format change must be followed through the runtime reader and the test assets. The content pipeline is deliberately outside every game's link closure. |
| Interop | c-api in tree; C#, Java, TypeScript, Python, Rust, Swift, Go and Ruby bindings in their own repositories | The opaque-handle and error boundary (CNA_Handle, CNA_Result) and the foreign ownership rules on the other side. | A C++ change can become C ABI, lifetime and release-compatibility work. The C API is opt-in (CNA_BUILD_C_API). |
| Validation | module tests/ and examples/, root tests/, cmake/UnitTests.cmake, cmake/Harnesses.cmake, .github/workflows/ (20 workflow files) | Configured evidence, usually conditional on the selected host and backend. | "All tests" means all tests in one configured graph. |
Modules that look small or optional — design, diagnostics, media, net, phone and others — are not presumed simple. Each has an internals page in the Development area (for example design, phone, net), written from source reading at this snapshot; none of those pages establishes runtime behaviour on every host. The generated module index is a locator, and the repository map and source-ownership guide explain who owns what. The user-level view of the same layout is Architecture: module layout.
Understand the three backend selections before debugging
Three CMake selections decide which code actually runs, and they are composed by the root CMakeLists.txt from separate files:
| Selection | Answers | Values at this snapshot | Decided in |
|---|---|---|---|
CNA_PLATFORM | Who owns windows, events, input, timing and host services? | 7: SDL3 (default), SDL2, X11, WAYLAND, WIN32, HEADLESS, TERMINAL | cmake/PlatformSelection.cmake |
CNA_AUDIO_PLATFORM | Who opens playback and recording devices, and (for two values) mixes? | 4: SDL3 (default), SDL2, NULL, ALSA; SOUND_ENABLED only for SDL3 and ALSA | cmake/AudioPlatformSelection.cmake |
CNA_GRAPHICS_RENDERER / CNA_GRAPHICS_RENDERERS | Which renderer is the default, and which others are compiled into the same binary for runtime selection? | 25 public identities over 21 families | cmake/RendererIdentities.cmake, cmake/RendererSelection.cmake, cmake/RendererRegistry.cmake |
The user guides count four axes — these three plus the target operating system — and one switch, CNA_ENABLE_SDL (see Platform support: four separate axes and Architecture: the four build axes); the selection axes index lists every value. The selections are validated separately and have hard exclusions that fail at configure time with a named reason rather than falling back.
Platform selection is not graphics selection. SDL3 can host Vulkan or SDL_gpu; native X11 and Wayland expose their own GL-context and Vulkan-surface services; TERMINAL has no graphical window, so cmake/RendererSelection.cmake refuses every renderer except the CPU-frame identities SOFTWARE, PORTABLEGL, HEADLESS and STUB before any renderer dependency is probed. A physical family may implement several public names: EasyGL serves five GL profiles (OPENGLES2, OPENGLES3, OPENGL33, WEBGL1, WEBGL2) through separate descriptor accessors on one target, while OPENGL4 is a family of its own. And in a multi-renderer build the default, the selected and the active renderer can differ (runtime renderer selection).
Before attributing a failure to "the renderer", record the exact CMake cache (the three selections, the toolchain, CNA_ENABLE_SDL) and the identity that was actually constructed: CNA::GraphicsRendererSelection::GetActive(), the CNA: graphics renderer: <name> line the device logs on first construction, and GetFallbackHistory() if a fallback chain was enabled.
Where small changes have broad effects
The broadest blast radius belongs to a handful of contracts:
CNA::Platform::IPlatform(IPlatform.hpp) — seven implementations; Headless is compiled into every build and Terminal into every POSIX build, whateverCNA_PLATFORMsays, so thatPlatformConformanceTests.cppcan hold several implementations to one contract in one process;IGraphicsRendererandGraphicsRendererDescriptor(IGraphicsRenderer.hpp,GraphicsRendererDescriptor.hpp) — 21 families;GraphicsDeviceandGraphicsResource(GraphicsDevice.hpp,GraphicsResource.hpp) — every resource type and every renderer;- public headers in general, the compiled renderer selection, and the renderer identity table, which exists three times (the CMake list,
CNA::GraphicsRendererTypeinGraphicsRendererType.hpp, andCNA_GRAPHICS_RENDERER_*inCNA/C/graphics.h) and is held together byscripts/check_renderer_identities.py; - the C ABI's handle and ownership rules.
A one-line virtual signature or disposal-order change may demand edits in all backends, their tests, the fallback logic and the bindings. More local work can live in a specific terminal input decoder, X11 event translator, specialized content reader or one backend's shader translation — provided the common contract and the serialized format stay unchanged. "Local" must be proved by call search and tests, not inferred from a file name.
Lifecycle-sensitive areas deserve extra care; each of these is visible in the source at this snapshot:
Gameinstalls its owned platform from the initialiser of its first member,platform_(InstallPlatforminGame.cpp), so every later member constructor that reaches the ambient platform finds the game's instance; aPlatformInstallationguard undoes the installation if construction throws.GraphicsDevice_is a value member ofGame, and theGraphicsDeviceconstructor runsresolveRenderer(), so the renderer and its window exist before your derived constructor body — long beforeInitialize.- Resources register with the device (
AddResourceReference) as raw pointers and are not C++-owned by that registry; a resource that outlives its device checks a weak lifetime token (resourceDeviceLifetime_) rather than a dangling pointer. GraphicsDevice::Dispose(bool)disposes every registered resource beforedestroyNativeResources()resets the renderer, so the renderer is still alive while resources release native objects.- Audio device callbacks can overlap instance destruction; the SDL3-audio harnesses in
cmake/Harnesses.cmakeexist precisely to destroy a mixer while a voice is active. - C API handles outlive a single call;
HandleRegistrychecks generation, object kind and creating thread on every use.
The ownership and thread and callback maps make these relationships explicit; the blast-radius page turns them into review questions.
What the repository layout signals
modules/<name>/includeis a module's public or cross-module header boundary; cross-module internal contracts live under some module'sinclude/CNA/Internal/.src/holds the implementation and module-private headers, and no module adds another module'ssrc/as an include root. Modules may also owntests/,examples/andbenchmarks/.modules/renderers/contains the physical backends and the shared renderer helpers.cmake/performs selection (PlatformSelection,AudioPlatformSelection,RendererIdentities,RendererSelection,RendererCombinations), registry generation (RendererRegistry), third-party discovery (ThirdParty*.cmake) and test registration (UnitTests,TestHelpers,Harnesses,TestDisplayPolicy).- Root
tests/holds cross-module suites, fixtures, reference data and assets (OracleCorpusTests.cpp,fixtures/,assets/,reference/,gltf-l7/,interop/); module-local tests belong to their physical owners. tools/andscripts/contain generators, probes, harness sources and developer checks, not runtime libraries..github/workflows/shows where a configuration is actually compiled or executed (20 workflow files, 28 jobs; see what CI actually covers).third_party/andvendor/are dependency boundaries (submodules and vendored code); treat them as external.- Historical material —
plans/,plan/,spikes/,audit/,remediation/,modularization/,integration/,docs/and the root-levelNEXT*.md,TODO.mdand handoff notes — can explain a migration, but current implementation and active CMake are authoritative.
Start with CMakeLists.txt and modules/CMakeLists.txt to see composition (option defaults, selection order, the CNA umbrella target, the ownership gate), then use the generated module inventory to find a physical owner and the physical module dependency map for link edges. This page is deliberately about where responsibility lives; it does not claim that each optional family is mature or tested on every host — renderer coverage is configuration-scoped.
Related pages
The same subsystem is explained at four altitudes. These are the neighbouring pages at each one.
- Architecture
- Architecture overview · Physical module dependency map
- Internals
- Internals · Renderer selection internals
- Maintainer workflow
- Repository map · Source ownership
- Tests and validation
- Test architecture · Renderer coverage is configuration-scoped
- Reference
- Module index · Selection axes index