What a human maintainer inherits

CNA snapshot 009d40f5  ·  Development › Human Takeover  ·  source links pinned to 009d40f5

✓

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.

FamilyDirectories at this snapshotWhat it ownsTypical change boundary
Foundationcore, math, platform; also diagnosticsShared 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 compatibilityruntime, graphics, input, audio, media, content, storage, devices, phone, plus the optional gamer-services, net and video-ffmpegXNA-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 modulesgraphics-ext, devices-ext, inspector, designCNAEXT 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 renderersmodules/renderers: 21 family directories with a src/, plus the shared helpers common/d3d and common/mojoshaderBackend-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 toolingcontent-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.
Interopc-api in tree; C#, Java, TypeScript, Python, Rust, Swift, Go and Ruby bindings in their own repositoriesThe 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).
Validationmodule 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:

SelectionAnswersValues at this snapshotDecided in
CNA_PLATFORMWho owns windows, events, input, timing and host services?7: SDL3 (default), SDL2, X11, WAYLAND, WIN32, HEADLESS, TERMINALcmake/PlatformSelection.cmake
CNA_AUDIO_PLATFORMWho opens playback and recording devices, and (for two values) mixes?4: SDL3 (default), SDL2, NULL, ALSA; SOUND_ENABLED only for SDL3 and ALSAcmake/AudioPlatformSelection.cmake
CNA_GRAPHICS_RENDERER / CNA_GRAPHICS_RENDERERSWhich renderer is the default, and which others are compiled into the same binary for runtime selection?25 public identities over 21 familiescmake/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:

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:

  • Game installs its owned platform from the initialiser of its first member, platform_ (InstallPlatform in Game.cpp), so every later member constructor that reaches the ambient platform finds the game's instance; a PlatformInstallation guard undoes the installation if construction throws.
  • GraphicsDevice_ is a value member of Game, and the GraphicsDevice constructor runs resolveRenderer(), so the renderer and its window exist before your derived constructor body — long before Initialize.
  • 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 before destroyNativeResources() 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.cmake exist precisely to destroy a mixer while a voice is active.
  • C API handles outlive a single call; HandleRegistry checks 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>/include is a module's public or cross-module header boundary; cross-module internal contracts live under some module's include/CNA/Internal/. src/ holds the implementation and module-private headers, and no module adds another module's src/ as an include root. Modules may also own tests/, examples/ and benchmarks/.
  • 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/ and scripts/ 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/ and vendor/ are dependency boundaries (submodules and vendored code); treat them as external.
  • Historical material — plans/, plan/, spikes/, audit/, remediation/, modularization/, integration/, docs/ and the root-level NEXT*.md, TODO.md and 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.

The same subsystem is explained at four altitudes. These are the neighbouring pages at each one.

Maintainer workflow
Repository map · Source ownership