Repository map
Evidence basis: source-verified at the pinned commit; tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit 3b43bd86; unless a sentence says otherwise, nothing here was built or executed. File counts were computed from the Git tree of the pinned commit and link edges were read from each module's CMakeLists.txt; the tests named exist and are registered in CMake, but nothing was configured, built or executed for this page.
CNA is a module-oriented monorepo. Production code lives under modules/, build policy under cmake/, shared fixtures and cross-module suites under tests/, and generators, audits, launchers and harness sources under tools/ and scripts/. This page is the map of that layout at snapshot 3b43bd86: what each directory holds, how a module directory is shaped, which headers are public, Internal or private, what is generated, where the vendor boundaries run and which tests belong to whom. Read it before opening a source file you did not write; the lookup "which part of CNA owns this behaviour?" is a separate page, Source ownership.
How the three layout pages divide the work. This page is the map: directories, module shape, header classes, generated files, vendor boundaries, test ownership. Source ownership answers "who owns this behaviour" by symbol and subsystem. The physical module dependency map draws the link edges between the module targets. File and header counts below were computed from the Git tree of the pinned commit; nothing was built or executed for this page. The user-level view of the same layout is Architecture: module layout and Architecture: directory structure.
Top-level map
Every row is a directory (or file group) at the repository root, with the number of files Git tracks in it at this snapshot. The right-hand column says when you would edit there. Counts describe tree shape, not importance.
| Path | What it holds at this snapshot | Modify when… |
|---|---|---|
modules/3,902 files | 23 directories: 22 declared framework modules plus renderers/ (8 implementation families and the one shared helper directory common/mojoshader). Public and Internal headers, implementations, module-owned tests, examples and benchmarks, and each module's own CMakeLists.txt. | Changing CNA behaviour or API. This is where nearly every production change lands. |
cmake/133 files | Platform, audio, SDL-availability and renderer selection and the renderer registry; third-party setup (ThirdParty*.cmake) and the sibling version checks (DependencyVersions); build policy (BuildPerformance, SharedRuntimeLibrary, SharedRuntimePolicy, CxxRuntimeLinkage, SharpRuntimeConsumption, ApplePlatform); the installed C++ SDK (InstallCxx, InstallCxxDependencies, CNACppConfig.cmake.in); test registration (UnitTests, TestHelpers, TestDisplayPolicy, Harnesses, XnaPipelineParityGates); script-mode gates under Tests/ (among them the link-footprint gates); toolchain files for mingw-w64 and iOS; two templates; and 7 files under patches/ (SDL’s five platform patches, and SDL_shadercross’s one patch with the script that applies it). | Adding a backend, option or target, or changing how the configuration is composed. See CMake architecture. |
tests/2,067 files | Data and cross-module material, not the unit tests of any one module: assets/ (1,141 files, mostly glTF, XNA 4.0 and XNB fixtures), gltf-l7/ (282: rendered-pixel golden images and policies for OPENGLES3 (EasyGL) and SOFTWARE), reference/xna40/ (570: the recorded Content Pipeline oracle data and the effect-structure defaults), fixtures/ (compiled-effect fixtures that tests replay), interop/, support/ (a header-only compiled-effect conformance suite), modules/ (15 minimal-link module probes and the mixed-runtime fixtures), link_footprint/ (6 link-footprint consumers), apple/ (the Apple smoke application), and six C++ sources at the root: the two harness-policy sources compiled into every test executable and four integration suites (OracleCorpusTests.cpp among them). | A contract spans several modules, a fixture or reference asset changes, or a module's link closure must be pinned. See Where tests live. |
tools/496 files | Sources of CNA's own command-line tools and harnesses (the cna-content front end under tools/content, the glTF and CNB converters), the oracle and reference tooling (xna-pipeline-oracle, xna-oracle, xna-sample-sweep, fna-reference, cna-reference), platform launchers and audits (tools/platform: 30 files including run_gpu_tests_private.sh, x11_test_server.sh, wayland_test_server.sh and the SDL containment audits), the C API generators, among them the route generator (tools/c-api), the link-audit tools (tools/link_audit), the shader-package generator and the build-performance scripts (tools/build). | Automating or auditing a repository invariant; changing a tool's own behaviour. |
scripts/49 files | Repository gates that CTest and CI run (check_renderer_identities.py, check_renderer_combinations.py, check_module_link_closure.py, check_test_display_isolation.py), the CNAEXT lint scripts (present, but registered by no CMake file or workflow at this snapshot), corpus and oracle runners, the Wine, DXVK and vkd3d wrappers, the browser-test drivers and scripts/ci/clone_siblings.sh. | A gate's rule changes, or a new invariant deserves a script. |
docs/192 files | Design notes and contracts: docs/physical-modules.md, per-renderer notes, the platform notes, the C API documents under docs/c-api, the link-size architecture note, recorded reports. | A documented contract changes with the implementation. Treat a document as evidence of intent and check it against the code: for example the physical-modules note says the top-level examples/ holds exactly one thing, but the tree also has a loose examples/xvfb_screenshot_demo.cpp that is not a top-level target: only the EasyGL example CMake file compiles it, as an ad hoc test target. |
.github/workflows/18 files | The CI workflows: 25 jobs over Linux, macOS, iOS, Windows (MSVC) and a WebAssembly build. | Validation coverage or environment changes. See what CI covers. |
third_party/, vendor/ | Dependency boundary: three submodules and four in-tree vendored libraries under third_party/, and the googletest submodule under vendor/. | Only deliberate dependency maintenance. See Third-party and vendor boundaries. |
examples/18 files | The cross-renderer golden-image corpus examples/golden/, loaded at run time through repository-root-relative paths, plus the loose file mentioned above. Module examples live with their module, not here. | A golden image is regenerated. Do not add new examples here: a module-specific example belongs in that module's examples/. |
spikes/78 files | 21 existence-gate probe directories, one per question a new backend or platform had to answer before real code was written (for example the terminal, WebGPU, Direct3D 9, FNA3D and several real-XNA behaviour probes). | Recording a new probe. They are evidence for a decision, not production code. |
| Planning and handoff records | plans/ (66 files; the plan identifiers quoted in CMake comments, such as FX-062 or SDLGPU-92, refer to them, though some cited plans have since been deleted), and root records: handoff.md (the current state and known defects), CHECKLIST.md, CNA_ENGINE_REPAIR_HANDOFF.md and CNA_DEMOS_REPAIR_MATRIX.json. The misc/ notes were retired on 25 October 2026: their lasting content moved into docs/ (for example docs/cnj-format.md, docs/binding.md, docs/consoles.md, docs/location-proposal.md, docs/build-performance.md), and the cna-demos and museum repair handoffs and the inherited review were removed once their findings were closed. The per-file audit, remediation, modularization and integration trees of earlier snapshots are no longer in the tree; Git history keeps them. | Recording intent or history. None of it is a build input; use it to learn why a rule exists, then confirm what executes in the source. |
| Root files | CMakeLists.txt, CMakePresets.json, .gitmodules, Doxyfile, README.md, the contributor rules CLAUDE.md and AGENTS.md, CHANGELOG.md, the licence and third-party notices. | Root policy, presets, version identity. |
There is no top-level include/ or src/. Consumer-visible headers live under each module's own include/, and implementations under its src/. A production translation unit outside every declared module fails the configure, and so does a root-level src/ or include/ directory reappearing ("legacy global tree reappeared"): see modules/CMakeLists.txt. A search that starts by looking for a shared include root will find nothing; start from the owning module.
Physical module shape
modules/<name>/ ├── CMakeLists.txt target and dependency declaration; collects its own sources ├── include/ public and cross-module headers, spelled as consumers include them │ ├── Microsoft/... XNA-shaped API (Microsoft/Xna/Framework/..., Microsoft/Devices, Microsoft/Phone) │ └── CNA/... CNA-owned surface; CNA/Internal/... holds cross-module internal contracts ├── src/ implementation and module-private headers │ ├── Xna/ XNA API implementation │ ├── Internal/ CNA::Internal engine parts │ └── CnaExt/ CNAEXT extension surfaces (single-area modules stay flat) ├── tests/ GoogleTest sources mirroring the namespace path ├── examples/ demos and example programs, registered by the module's own CMake └── benchmarks/ only diagnostics and inspector have one
The include tree mirrors the namespace-facing path, so #include "Microsoft/Xna/Framework/Graphics/Texture2D.hpp" resolves from modules/graphics/include and the spelling is identical wherever a consumer sits. A module's src/ uses the area convention Xna/, Internal/, CnaExt/ (see modules/graphics/CMakeLists.txt for the globs); the platform module instead gives every implementation its own directory (Sdl3/, Headless/, Terminal/) beside Common/ (the portable pieces more than one of them shares), and the audio module separates Platform/ (devices) from Backend/ (mixers). The CNA umbrella composes module targets as an INTERFACE library; it does not compile into one giant library, and cmake --build <dir> --target CNA is not a buildable target (the umbrella and its link closure).
- How a module becomes a target.
modules/CMakeLists.txtdefinescna_add_module(target alias sources...): a STATIC librarycna_<name>with the aliasCNA::<Alias>, linked PUBLIC tocna_build_configand publishing that module's owninclude/as its PUBLIC include root. Sources are collected withfile(GLOB … CONFIGURE_DEPENDS)from the module's ownsrc/(the graphics core globs itsXna,InternalandCnaExtareas, a renderer family globs only its ownsrc/*.cpp, not recursively), so adding a.cppthere needs no CMake edit. The one module that lists its sources by hand isc-api. - Naming exceptions.
gamer-services,netandvideo-ffmpegcalladd_librarydirectly (CNA_GamerServices,CNA_Net,cna_video_ffmpeg);corealso defines the header-onlycna_core_headers(CNA::CoreHeaders) thatmath,storageandphonelink instead of the archive;content-pipelineiscna_content_pipelinewith the aliasCNA::ContentPipelineBuild;c-apiiscna_c_api, a shared library (static under Emscripten); each renderer family iscna_renderer_<family>with-replaced by_. - Location is ownership. Every file's directory is its CMake owner. The shared renderer helpers are deliberate sharing inside their own directories:
common/mojoshader(cna_renderer_mojoshader_effect, the compiled-effect runtime that FNA3D and the compiled-effects options link) and thecna_renderer_d3d9_effectsub-target insidedirectx9. - Renderer families need not have every subdirectory. Six of the 8 families have a
tests/directory (and so does the shared helper);headlessandstubkeep their tests as example programs underexamples/.
The 23 directories and their targets
The direct CNA link edges below were read from each module's CMakeLists.txt (PUBLIC unless marked); sharp-runtime components and third-party libraries are left out. "Umbrella" means the target is one of the parts of the CNA INTERFACE library in modules/CMakeLists.txt. The three intentional static-archive cycles (graphics and input, audio and media, graphics and the selected renderer) are why some edges are PRIVATE.
| Directory | Target (alias) | Umbrella | Owns | Direct CNA link edges |
|---|---|---|---|---|
core | cna_core (CNA::Core), cna_core_headers | yes | Base types, logging and exceptions, TargetPlatform, the renderer identity enum and GraphicsRendererSelection, the CNAEXT marker macro | none |
math | cna_math | yes | Vectors, matrices, quaternions, colours, bounding volumes, curves | cna_core_headers |
diagnostics | cna_diagnostics | yes | Counters, zones and frame history, compiled to nothing when CNA_DIAGNOSTICS=OFF | none |
platform | cna_platform | yes | IPlatform and its implementations; the module that links the native windowing and host libraries (privately on desktop; audio and the SDL-by-identity or SDL-upstream renderer families link SDL for their own backends) | cna_core_headers |
graphics | cna_graphics_core (CNA::GraphicsCore) | yes | XNA graphics API, GraphicsDevice, renderer interfaces, descriptors and the generated registry | cna_math, cna_core, cna_diagnostics, cna_platform; PRIVATE cna_input and every selected renderer |
input | cna_input | yes | Keyboard, mouse, gamepad, touch and gestures | cna_graphics_core, cna_math, cna_core, cna_platform |
audio | cna_audio | yes | XNA audio and XACT, the audio devices and mixers | cna_core, cna_diagnostics, cna_math; PRIVATE cna_platform, cna_media, cna_input |
media | cna_media | yes | MediaPlayer, MediaLibrary, the Video surface (link-complete without FFmpeg) | cna_audio, cna_graphics_core; PRIVATE cna_input, and cna_video_ffmpeg when FFmpeg is enabled |
video-ffmpeg | cna_video_ffmpeg (CNA::VideoFfmpeg) | no | The optional FFmpeg backend; entered only when FFmpeg was found | cna_build_config; linked into cna_media only |
content | cna_content | yes | ContentManager, XNB and CNB readers and writers, the canonical pipeline API under CNA/Content/Pipeline | cna_graphics_core, cna_audio, cna_media, cna_math, cna_core |
content-pipeline | cna_content_pipeline (CNA::ContentPipelineBuild) | no | Build-time-only components: the FreeType .spritefont route, FFmpeg importers, FBX and .x readers, the external effect-compiler service, the XNA Content.Pipeline facade | cna_content; linked by cna_content_compiler and the content-pipeline test targets, never by the umbrella |
storage | cna_storage | yes | StorageDevice, StorageContainer and path containment | cna_core_headers |
runtime | cna_runtime | yes | Game, GameWindow, GraphicsDeviceManager, components, the project graphics profile | cna_graphics_core, cna_input, cna_content, cna_audio, cna_media, cna_core, cna_diagnostics, cna_math, cna_platform |
devices | cna_devices | yes | The XNA-compatible Microsoft::Devices sensors and vibration | cna_runtime, cna_graphics_core, cna_core, cna_math |
devices-ext | cna_devices_ext | yes | CNA device extensions: camera, clipboard, dialogs, tray, power, locale, URL launcher | cna_runtime, cna_graphics_core, cna_core, cna_math, cna_platform; never the XNA device base |
phone | cna_phone | no | The Microsoft::Phone application shell | cna_runtime, cna_core_headers |
graphics-ext | cna_graphics_ext | yes | The CNAEXT graphics extensions: retro post-process effects, DebugDraw and portable shader packages, compiled only with CNA_CNAEXT | cna_graphics_core |
design | cna_design | no (opt-in) | Framework.Design type converters | cna_math |
inspector | cna_inspector and the cna-inspector executable | no | Inspector agent, bridge and browser UI; defined only with CNA_BUILD_INSPECTOR | cna_diagnostics, cna_core_headers |
gamer-services | CNA_GamerServices (CNA::GamerServices) | no | GamerServices, Guide, Avatar, local persistence; entered only with CNA_ENABLE_GAMER_SERVICES (default: the value of CNA_ENABLE_NET, which requires it) | cna_runtime, cna_storage |
net | CNA_Net (CNA::Net) | no | Microsoft::Xna::Framework::Net over ENet; entered only with CNA_ENABLE_NET | CNA_GamerServices, enet |
c-api | cna_c_api (CNA::CApi) | no | The experimental C ABI (0.50.0; public headers held to a C99 consumer floor, CNA's own C sources built as C17); entered only with CNA_BUILD_C_API | PRIVATE CNA with CNA_SHARED_LIBRARY; otherwise PRIVATE cna_core, cna_platform, cna_runtime, cna_graphics_ext, cna_storage, cna_content, cna_media, cna_devices, cna_devices_ext, CNA_Net, CNA_GamerServices |
renderers | cna_renderer_<family> for each of 8 families | the selected family or set | Backend translation of resources, draws, presentation and synchronisation behind IGraphicsRenderer | PRIVATE reverse edges to cna_graphics_core, cna_core, cna_math |
Rows for graphics and renderers hide the interesting part: the graphics core links every selected family privately (the factory edge) and each family links the graphics core back (the reverse edges), which is why static link order is a declared invariant rather than an accident. Both halves are traced in the CMake architecture chapter. The generated module index lists the same directories with header, source and test-file counts and entry-point headers; its "test files" column counts every file under tests/, not the 900 C++ test sources discussed below.
Public, Internal and private headers
"Public" is a tree-shape word at this snapshot, not an export mechanism. Every module publishes its whole include/ directory PUBLIC, the opt-in installed C++ SDK (CNA_INSTALL_CXX, cmake/InstallCxx.cmake) installs those same whole trees, Internal headers included, and nothing stops a target that links a module from including a header under CNA/Internal. The classes below are conventions enforced by review and by the gates in What keeps this map true.
| Class | Where | Meaning and example |
|---|---|---|
| XNA-shaped public API | include/Microsoft/… | The names and shapes of XNA 4.0 and the Windows Phone shell. Example: GraphicsDevice.hpp. |
| CNA-owned public surface | include/CNA/… outside Internal/ | Facilities and CNAEXT extensions that are not XNA: GraphicsRendererSelection.hpp, RendererCapabilityProfile.hpp, and the CNAEXT extensions under graphics-ext. Every declaration that is not part of XNA 4.0 is tagged CNAEXT through CNAHelper.hpp. |
| Cross-module internal contracts | include/CNA/Internal/… | Contracts between modules that are not for consumers: IGraphicsRenderer.hpp and the descriptor and registry headers beside it, and each family's own CNA/Internal/Renderers/<Family>/ root. They live in an include root, so "Internal" is a naming and review convention, not a visibility barrier. |
| Module-private headers | src/… | Headers next to the implementation. No module adds another module's src/ as an include root, with two deliberate exceptions: every renderer target adds modules/platform/src PRIVATE (the SDL-backed families on the platform SDL audit's allow-list reach the one edge Sdl3RendererInterop.hpp), and the test build adds modules/audio/src so an implementation test can see a private audio device. |
| C ABI headers | modules/c-api/include/CNA/C/*.h | 60 plain-C headers with their own version (ABI 0.50.0, experimental) and their own build gates; most routes are generated from modules/c-api/spec/*.toml. See C API and bindings architecture. |
| Generated headers | build tree | CNA/Version.hpp is rendered into <build>/generated/include and added to core's include roots; see Generated and checked-in generated files. |
The generated public header index lists 718 non-Internal headers under include/CNA and include/Microsoft in 21 modules; it says nothing about which of them are supported API.
Subsystem families
Group modules by the question they answer and the boundary a change crosses, not by directory name. Each row is a family of modules that usually change together; the last column states the ownership fact a reviewer should hold in mind. For the per-object lifetime rules behind these statements see Ownership and lifetime master map and What a human maintainer inherits.
| Family | Modules | Calls and ownership |
|---|---|---|
| Orchestration | runtime | Game coordinates the platform, services, components and frame callbacks; it owns the platform it installs and the GraphicsDevice value member (Runtime module internals). |
| Host boundary | platform, input, audio | Platform implementations emit events and provide narrow services; nothing outside platform links a platform library. Input state is process-wide. Audio devices and mixers are selected by their own axis (Platform architecture). |
| Rendering | graphics, graphics-ext, renderers/* | GraphicsDevice owns public resource tracking; the selected renderer translates commands. graphics-ext holds the opt-in CNAEXT extensions (retro effects, DebugDraw) above the same device (Graphics architecture). |
| Assets | content, content-pipeline, media | Runtime load and cache live in content together with the canonical pipeline engine and the XNB and CNB writers; content-pipeline holds only what must never ship in a game. Neither the pipeline module nor FreeType is in a game's link closure (Content architecture). |
| Interop | c-api | The experimental C ABI (public headers held to a C99 consumer floor) converts handles and errors across the C++ boundary; a C++ change can become ABI, lifetime and compatibility work. Opt-in through CNA_BUILD_C_API, which requires CNA_ENABLE_NET=ON. |
| Foundations | core, math, storage, diagnostics | Shared value types, identities and services; keep upward dependencies out. math and storage link only the header-only cna_core_headers. |
| Device and phone compatibility | devices, devices-ext, phone | Sensors, device extensions and the Windows Phone shell sit above runtime; the extension module never depends on the XNA device base. |
| Optional services and tooling | net, gamer-services, video-ffmpeg, design, inspector | Each is entered or linked only when asked for (CNA_ENABLE_NET, CNA_ENABLE_VIDEO, CNA_BUILD_INSPECTOR) or named on a link line. A default build says nothing about them. |
How to find the owner
- Find the public symbol's declaration with a search under the module
include/directories, for examplerg -n "class SoundEffect\b" modules/*/include. The directory you land in is the candidate owner. - Find the out-of-line definition, the constructor and every place the state is mutated in that module's
src/. A static XNA API (Keyboard::GetState,StorageDevice) may reach an ambient platform rather than a context you can see. - Trace factory and registry creation instead of assuming the directory is selected. For a renderer that means the identity map in
cmake/RendererRegistry.cmake, the family's*RendererDescriptor.cppand the generatedCnaRendererRegistry.generated.cpp; for a platform,PlatformFactory; for audio, the compiled-in device and mixer. - Search the focused tests in the owning module's
tests/first, then the conformance and integration tests that name the symbol, then the renderer-family example programs. - Read the module's
CMakeLists.txtbefore crossing a boundary: which targets it links, whether the link is PUBLIC or PRIVATE, and whether the module is entered conditionally. - Confirm the answer against Source ownership, the module dependency map and the generated module index and public header index, which give source-pinned entry points.
Generated and checked-in generated files
Two different things are called "generated". Some files are produced into the build tree during configure or build and never committed; others are committed but must stay derivable from their sources, and a gate fails when they drift. Before committing a change to either kind, find the generating rule and reproduce the output from clean input.
| File | Produced by | Committed? | Guard |
|---|---|---|---|
<build>/generated/CnaRendererRegistry.generated.cpp | cmake/RendererRegistry.cmake from cmake/templates/CnaRendererRegistry.generated.cpp.in, compiled into cna_graphics_core | no | the descriptor gate and RendererIdentityRegistry |
<build>/generated/include/CNA/Version.hpp | cmake/Version.cmake from the version set in the root CMakeLists.txt | no | single source of the version string |
<build>/generated/cna_shared_library.cpp | cmake/SharedRuntimeLibrary.cmake (a one-line stub so libcna.so has a source) | no | only with CNA_SHARED_LIBRARY |
| the compiled-effects stock-effect header for EasyGL | a custom command in modules/renderers/easygl/CMakeLists.txt | no | only with CNA_EASYGL_COMPILED_EFFECTS |
.sdl-prebuilt-<key>/ in the source tree | the configure-time SDL sub-builds (the SDL prebuilt) | no (git-ignored) | a build manifest stamp |
spirv_shaders.hpp for SDL_gpu | compile_shaders.py beside the file (modules/renderers/sdl-gpu/src/shaders/spirv_shaders.hpp) | yes, marked "do not edit by hand" | the SpirvPayloadValidation gate in a configuration that includes SDL_GPU, where spirv-val is installed |
modules/c-api/src/generated/*.inc (the C routes) | tools/c-api/generate_routes.py from the 56 route-family specifications in modules/c-api/spec | yes | the c-api-generated-routes workflow and the CApiGeneratedRoutesCurrent gate (--check) |
*ShaderPackage.generated.hpp (the graphics-ext post-process package and the graphics examples' portable-tint package) | tools/shader_package/generate_shader_package.py | yes | the *ShaderPackageReproducibility tests re-derive a package with --check and skip (77) without shaderc; three are registered in cmake/Tests/ModuleProbes.cmake (PostProcessShaderPackageReproducibility, ShaderPackageReproducibility and ShaderEffect3DShaderPackageReproducibility) |
| the C ABI baseline, compatibility matrix, coverage inventory and limitations | the generators under tools/c-api | yes | six C API workflows and the CApi* CTest gates |
| the content-pipeline parity report | tools/xna-pipeline-oracle/parity_report.py | yes | the XnaPipeline* gates in cmake/XnaPipelineParityGates.cmake |
the glTF conformance corpus (tests/assets/gltf fixtures, manifests and vertex/index goldens) | scripts/regenerate-gltf-goldens.sh | yes | --check and --determinism in the glTF sanitizer workflow |
| rendered glTF goldens | scripts/gltf-l7-corpus.py --update-goldens | yes; the script requires --report-out with it and a second run that agrees byte for byte | run below an X server (normally xvfb-run -a) |
Third-party and vendor boundaries
Four mechanisms bring outside code into a CNA build, and each has a different rule for how you touch it. The general rule: third_party/ and vendor/ are external. CNA's build policy (warnings, sanitizers, debug-information and linker options) is attached only to targets whose source directory lies inside the repository and outside those two trees, so a change there is dependency maintenance, not a CNA edit.
| Kind | What | How it enters the build |
|---|---|---|
| Git submodules (five gitlinks) | third_party/SDL (pinned at 86296ac8), third_party/SDL_mixer (3075d3ed), third_party/draco (1.5.7, 87867400), vendor/googletest (7e2c425d), and the avatar catalogs modules/gamer-services/assets/avatars (cna-avatar-assets, 4617a1e7) | git clone --recurse-submodules, or git submodule update --init in an existing clone and after a pull that moves a submodule (CNA’s own instructions call the non-recursive form enough and much faster). Configure checks third_party/SDL and third_party/SDL_mixer against the recorded revisions (CNA_VENDORED_SOURCE_CHECK), so a stale submodule stops it; an uninitialised avatar submodule stops it too unless CNA_ENABLE_GAMER_SERVICES=OFF, and a source archive without submodules (a GitHub ZIP) cannot build avatars. SDL and SDL_mixer are built at configure time into a persistent prebuilt directory; Draco is a subdirectory unless CNA_ENABLE_DRACO=OFF or a system package is chosen; googletest is added while CNA_BUILD_TESTS=ON. A missing submodule stops the configure with a named message. With CNA_ENABLE_SDL=OFF the SDL submodules are never touched. |
| Vendored in the tree | third_party/enet, third_party/stb, third_party/dr_libs, third_party/cgltf | Compiled or included directly (ENet only with CNA_ENABLE_NET; it is upstream ENet master 5a9c537f, unmodified). The module CMake files reach the vendored single headers through CNA_SOURCE_DIR rather than CMAKE_SOURCE_DIR, so a consumer that adds CNA as a subdirectory still finds them. |
| Sibling repositories | ../sharp-runtime (every build; its next branch for this snapshot), ../easy-gl with ../meta-gl (the three GL identities) | Separate checkouts entered with add_subdirectory(../<repo>); CMake will not fetch them, but configure checks the version each declares against cmake/DependencyVersions.cmake (sharp-runtime 0.1.0, easy-gl 0.1.1, meta-gl 0.4.1; CNA_CHECK_DEPENDENCY_VERSIONS=OFF turns a mismatch into a warning). sharp-runtime's location is the CNA_SHARP_RUNTIME_ROOT cache path and easy-gl's is CNA_EASYGL_ROOT. See the Building CNA sibling checkouts. |
| Fetched at configure time | FNA3D with MojoShader from CNA’s forks libcna/FNA3D (pinned commit 10f650b9d0f6) and libcna/mojoshader (its submodule, 03b686fe1a33), wgpu-native (v29.0.1.1), and SDL_shadercross (1ff05bec) with SPIRV-Cross | Only when the renderer or option that needs them is selected. CNA’s FNA3D and MojoShader fixes are commits on the forks’ cna branches, so nothing is patched at configure time; a FetchContent checkout that drifts from the pin is refused. cmake/patches holds SDL’s five platform patches and SDL_shadercross’s one build-integration patch. Details in CMake architecture: third-party dependencies. |
Where tests live and who owns them
Test ownership follows source ownership. A module's unit tests live in that module's tests/, mirroring the namespace path, and are compiled into a per-module object group; renderer-family behaviour is exercised mostly by example programs in the family's examples/; only genuinely cross-module material sits in the top-level tests/. The counts follow the table; how the sources are assembled into executables is described on Test architecture.
| Location | Owns | Runs as |
|---|---|---|
modules/<name>/tests/ | Focused GoogleTest sources for that module | One object group, linked into the aggregate CnaTests and into that module's focused executable |
modules/renderers/<family>/tests/ and modules/renderers/common/*/tests/ | Renderer-policy suites that need no device (capabilities, descriptors, state mapping) | The renderers group, CnaRendererTests |
modules/<name>/examples/, modules/renderers/<family>/examples/ | Demos and the standalone pixel and smoke programs (599 *_test.cpp programs, outside the 900 and 11,927 figures) | Separate executables, each registered through cna_register_renderer_test or add_test in the module's own CMake |
modules/c-api/tests/ | The C API's consumer tests: mostly C files with their own main(), plus six C++ files | Their own executables and CApi_* CTest entries; deliberately not part of CnaTests |
top-level tests/*.cpp | Four cross-module suites (OracleCorpusTests.cpp among them), plus the two harness-policy sources compiled into every test executable | The integration group, CnaIntegrationTests |
tests/modules/, tests/link_footprint/ | Fifteen minimal-link probes, one per module alias, the mixed-runtime fixtures, and six link-footprint consumers | Standalone executables with a paired link-closure or link-footprint gate |
tests/assets, tests/reference, tests/gltf-l7, tests/fixtures | Fixtures and recorded reference data, addressed by repository-root-relative literal paths | Read by tests; discovered CnaTests cases run with the repository root as their working directory |
The 900 C++ test source files counted by the site method (every .cpp under a tests/ or test/ directory, vendored code excluded) are distributed as follows, recomputed by owning directory from the Git tree at this snapshot. The figure is a source inventory, not an executable case count and not a pass result.
| Owner | Files | Owner | Files |
|---|---|---|---|
| graphics | 198 | renderer families and helpers (6 families and common/mojoshader) | 30 |
| content | 189 | devices | 29 |
| content-pipeline | 55 | core, devices-ext (10 each) | 20 |
| input | 50 | storage | 7 |
| math | 42 | graphics-ext | 6 |
| audio | 41 | c-api (six C++ files) | 6 |
| media | 40 | design, phone (2 each) | 4 |
| gamer-services | 39 | diagnostics, inspector (1 each) | 2 |
| platform | 37 | top-level tests/ (6 sources, 22 probe and mixed-runtime fixtures, 6 link-footprint consumers, 2 Apple smoke-app sources) | 36 |
| net | 35 | runtime | 34 |
What keeps this map true
Most of the layout is enforced mechanically, so a map that disagrees with the tree is the map's fault. These are the gates to run or read when you move a file, add a module, or add a dependency edge.
- Source-partition validator in
modules/CMakeLists.txt: every.cppor.mmundermodules/must sit in a declared module'ssrc/,tests/,examples/orbenchmarks/tree (lists_cna_framework_modulesand_cna_renderer_modules); a legacy rootsrc/orinclude/fails. Moving a file between directories is a build-ownership change, not a rename. - Minimal-link probes in
cmake/Tests/ModuleProbes.cmake: fifteen tiny consumers of one module alias each, paired withModuleLinkClosure_*tests that runscripts/check_module_link_closure.pyover the generated link line and fail when a forbidden archive or library appears. A HEADLESS-only configuration additionally proves that renderer-neutral graphics needs no native graphics SDK, and theLinkFootprint_*gates incmake/Tests/LinkFootprint.cmakecheck that a program links only the subsystems it uses. - Identity and discipline scripts:
scripts/check_renderer_identities.pyholds the CMake identity list, the registry map, the C++ enumeration and the C ABI header to one canonical table;scripts/check_renderer_target_discipline.pyandscripts/check_runtime_renderer_discipline.pykeep families from reading global scalars and keep renderer-specific decisions behind descriptors. - Configure-time SDL audits (the platform ratchet, the non-production SDL audit and the hot-path lint) keep SDL inside the platform module; see CMake architecture: gates that run at configure time.
The physical-modules note docs/physical-modules.md is a useful summary but is not a substitute for the CMake files it summarises: for example its dependency table omits the diagnostics and platform edges of cna_runtime that the module's own CMakeLists.txt declares. When the two disagree, the CMake file is what executes.
Deep dives on this topic
Long-form pages that explain the exact semantics, invariants and evidence behind this subject.
- Project records as evidence: plans, ledgers, versions, handoffs and re-audits — How to use and write CNA's plans, handoffs, audits and gates as evidence: roles, stable task IDs, executable ledgers, version coordinates, defect-ledger rules and documentation re-audits.
- The physical module architecture: ownership, targets and composition — How CNA's 22 framework modules and 8 renderer families own their targets, include roots, tests and examples, which target names cannot be guessed, and how to read a source path.
Known issues in this area
Current defects, gaps and limitations at this snapshot that touch this subject.
- CNA-BUG-055: CHANGELOG.md's v0.1.0-alpha.1 section says 49 renderer identities for a release that had 50 — The C API documents, core_ext.h and ModuleProbes.cmake no longer state obsolete whole-registry renderer counts, but the alpha.1 release notes still say 49 renderer identities where that tag's selection list holds 50; no
- CNA-BUG-232: docs/physical-modules.md has no row for modules/platform, omits the platform and diagnostics edges of runtime, input, audio and devices-ext, and says top-level examples/ holds only examples/golden — CNA's physical-module map, which check_renderer_identities.py keeps under count control, never mentions the platform module that input, runtime, audio and devices-ext link, and misdescribes the top-level examples directo
- CNA-VGAP-019: The HEADLESS native-SDK-free link-closure gates run in no CI job — The link-closure check reads a Ninja tree's link line, so general-tests-ci.yml evaluates its OPENGLES3 tree's gates; the NativeSdkFree_* and GraphicsNativeSdkFree gates exist only in single-renderer HEADLESS trees, which
Related pages
The same subsystem is explained at four altitudes. These are the neighbouring pages at each one.
- Architecture
- Physical module dependency map · Architecture overview
- Internals
- Internals · CMake architecture
- Maintainer workflow
- Source ownership · What a human maintainer inherits
- Tests and validation
- Test architecture and change recipes
- Reference
- Module index · Public header index · Selection axes index