Source ownership: which part of CNA owns this behaviour?
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 009d40f5; unless a sentence says otherwise, nothing here was built or executed. Paths and symbols were read at 009d40f5; the tests named exist in the tree and are registered by CMake in the configurations that include them, but nothing was configured, built or executed. Statements marked as searched come from reading CMake files, workflows and scripts for a reference, not from running anything.
CNA records ownership in the file tree first and in class names second: a behaviour belongs to the module directory that holds its declaration, to the renderer family or platform backend directory that implements it in the selected build, and to the tests beside them. This page turns that convention into a maintainer's lookup. It shows how ownership is expressed, which files are generated rather than handwritten, where the third-party and sibling-repository boundaries run, and it answers 63 common “who owns this?” questions with owner, header, implementation, tests and the Development page that explains the internals. Every path was checked against the snapshot tree, and the symbols in the table and the prose were located in source at 009d40f5; nothing was built or run.
The table names a starting owner, not a verdict. Where a question has several owners (a public contract, an implementation, a registration) the row shows several. The exact inventories are the generated module index, public header index, test target index and selection axes index; the layout itself is described on the repository map and the link edges on the module dependency map. A row says where to read, not that the behaviour is correct on any host.
How ownership is expressed in the tree
14 signals carry ownership, and most questions need two or three of them. Read the header for the contract, then find what selects an implementation, then find the tests that would fail.
| Signal | Where | What it tells you |
|---|---|---|
| Module directory | modules/: modules/<name>/{CMakeLists.txt, include, src, tests, examples}; 22 framework modules plus renderers/ | The directory is the CMake owner. modules/CMakeLists.txt fails the configure for any .cpp or .mm outside a declared module's src/, tests/, examples/ or benchmarks/ (_cna_framework_modules, _cna_renderer_modules), and for a root-level src/ or include/. Moving a file is an ownership change. |
include/Microsoft/… | GraphicsDevice.hpp | XNA 4.0 shape: Microsoft::Xna::Framework, Microsoft::Devices, Microsoft::Phone. Behaviour here is measured against XNA, and a signature change is a compatibility change that can reach the C ABI. |
include/CNA/… (not Internal) | GraphicsRendererSelection.hpp | CNA-owned surface. Every declaration that is not XNA carries CNAEXT from CNAHelper.hpp, which expands to [[deprecated]] when CNA_STRICT_XNA_API is defined. |
include/CNA/Internal/… | IGraphicsRenderer.hpp | A contract between modules, not for games. Every module publishes its whole include/ PUBLIC, so this is a naming and review convention, not a barrier. |
src/ area folders | Xna/, Internal/, CnaExt/ (the convention in the root CLAUDE.md); single-area modules stay flat | Implementation and module-private headers. Two documented exceptions reach across: every renderer target adds modules/platform/src PRIVATE (renderers/CMakeLists.txt), and the unit-test build adds modules/audio/src (UnitTests.cmake). |
| Renderer family | modules/renderers/<family> | cna_add_renderer() globs the family's src/*.cpp (non-recursive) into cna_renderer_<family>. One family can serve several public identities: EasyGL serves five, while OPENGL4 is a family of its own. The identity-to-family map is in RendererRegistry.cmake, and the index of families is Selection axes index. |
| Platform backend | modules/platform/src/<Backend> | One directory per backend beside Common/. platform/CMakeLists.txt globs only the selected backend, Common/, Headless/ (always) and Terminal/ (every non-Windows target); PlatformFactory.cpp picks the default by CNA_PLATFORM_<NAME>. |
| Audio device and mixer | modules/audio/src | Platform/<Impl> is the device transport (Sdl3, Sdl2, Null, Alsa); Backend/<Mixer> is the mixer (Sdl3Mixer or CnaMixer). The two axes are chosen separately. |
Module tests/ | modules/<name>/tests/ and modules/renderers/<family>/tests/ | GoogleTest sources mirroring the namespace path. UnitTests.cmake globs them into a per-module object group that feeds the aggregate CnaTests and a focused executable (CnaStorageTests, CnaMathTests, …). The C API's tests are the exception: own executables, filtered out of CnaTests. |
Root tests/ | tests/ | Fixtures, recorded reference data, two integration sources and 15 minimal-link probes (ModuleProbes.cmake). Not the unit tests of any one module. |
examples/ | modules/<name>/examples/, plus the loose xvfb_screenshot_demo.cpp | Demos and standalone pixel or contract programs (879 *_test.cpp files), registered by the owning module's own CMake, often only when a particular renderer or platform is selected. Some are ad hoc targets rather than registered tests: the loose demo is built by the EasyGL and SDL_RENDERER example files and named a test by neither. The 32 renderer-neutral parity/ programs are registered per renderer through ParityFixtures.cmake. |
| Oracle programs | tools/xna-oracle | The real-XNA scene renderer CnaOracleRender.cpp is compiled by renderer families' example files (EasyGL, DirectX9), so the program lives in tools/ while its build and its test registration live in the family. Data: tools/xna-oracle/scenes (39 scenes) and reference (39 PNGs). See Using the XNA oracle as evidence. |
tools/, scripts/ | tools/, scripts/ | Generators, audits, harness sources and repository gates. Only some are CMake targets (cna-content, the CNB and glTF converters, the XNB fixture tool); the rest are run by CTest scripts, workflows or by hand. |
cmake/ and per-module CMake | cmake/ and each module's CMakeLists.txt | Selection (PlatformSelection.cmake, AudioPlatformSelection.cmake, RendererSelection.cmake), generation, third-party setup and test registration. A module's own file says what it links and whether it is entered conditionally. |
- Declaration. Search the
include/directories for the public symbol; the module you land in owns the contract, for examplerg -n "class SoundEffect\b" modules/*/include. - Selection. Ask what makes one implementation the live one: a CMake selection file,
PlatformFactory, the generated renderer registry, orCreateSelectedAudioDevice. A directory that exists is not necessarily compiled. - Evidence. Look for tests in the module's
tests/, then the family'sexamples/, then the root probes, and confirm registration in the configuration you care about withctest -N. Absence of a test is a finding.
Generated versus handwritten
Two kinds of file are called generated. Build-tree files are produced by configure or build and are never committed. Committed files are checked in but must stay derivable from a source; some have a gate that fails when they drift, some do not. Before editing either kind, find the generator, change its input, and regenerate. Editing an output by hand is the mistake every header comment warns about.
| Artifact | Where it lives | Generator | Regenerate | Guard at this snapshot |
|---|---|---|---|---|
<build>/generated/CnaRendererRegistry.generated.cpp | build tree | RendererRegistry.cmake (cna_generate_renderer_registry) filling the template | Every configure; the first identity listed is the build default. Compiled into cna_graphics_core. | RendererIdentityRegistry; the descriptor gate cna_renderer_descriptor_gate in RendererDescriptorGate.cmake |
CNA/Version.hpp in <build>/generated/include | build tree | Version.cmake from Version.hpp.in | Edit project(CNA VERSION …) and CNA_VERSION_PRERELEASE in the root CMakeLists.txt. Two copies stay hand-maintained per releasing.md: CHANGELOG.md and PROJECT_NUMBER in the root Doxyfile. | VersionTests.cpp |
| Shared-library stub, Wayland protocol bindings, wasm export list | build tree | SharedRuntimeLibrary.cmake; wayland-scanner in PlatformWayland.cmake; generate_wasm_exports.py called from c-api/CMakeLists.txt | Automatic, and only for CNA_SHARED_LIBRARY, CNA_PLATFORM=WAYLAND and Emscripten builds of the C API respectively. | the build itself |
Fna3dStockEffectBlobs.hpp | build tree | embed_effects.py over the vendored .fxb effect binaries | Automatic: always for FNA3D; for EasyGL, OpenGL4, Software, DirectX9, DirectX11 and DirectX12 only when the family's *_COMPILED_EFFECTS option is on. | family compiled-effect tests |
.sdl-prebuilt-<key>/ | git-ignored, in the source tree | ThirdPartySDL.cmake with SdlPrebuiltFingerprint.cmake | Rebuilt when its fingerprint (source content, patches, configure arguments) changes; delete the reported directory to force it. | CnaSdlPrebuiltFingerprint |
| Engine-layer Doxygen HTML | git-ignored build/doxygen-cnaext | Doxyfile.cnaext | doxygen docs/Doxyfile.cnaext. The root Doxyfile leaves INPUT empty. | CNAEXT_DoxygenGroup (exit 77 without doxygen) |
spirv_shaders.hpp (Vulkan, SDL_gpu) | committed | Vulkan compile_shaders.py, SDL_gpu compile_shaders.py (libshaderc through ctypes) | Run the script in its own directory, or pass --output, and commit the header with the GLSL edit. | SpirvPayloadValidation checks that the payloads are valid SPIR-V (registered only when VULKAN is compiled in; exit 77 without spirv-val). No CMake file, workflow or script recompiles the GLSL and compares (searched; not executed). |
DXBC and SM2 headers (hlsl_shaders.hpp, d3d9_*_shaders.hpp) | committed | compile_shaders_hlsl.py, compile_shaders_sm2.py, compile_pbr_shaders.py: a MinGW-built D3DCompile helper run under Wine | By hand, after a shader or compiler-flag change. | verify_d3d_shaders_reproducible.sh is a manual reproducibility check; no CMake file or workflow calls it (searched). |
*ShaderPackage.generated.hpp (20 headers) | committed | generate_shader_package.py over each package.json manifest and its sources | python3 tools/shader_package/generate_shader_package.py <package.json> --output <header>; --check writes nothing and exits 77 without shaderc. | 18 *ShaderPackageReproducibility tests in ModuleProbes.cmake; the ShadowCaster and ModernResourceInterop packages have no registration there. |
C ABI baseline, compatibility, coverage and limitation records, BoolContractSmoke.c | committed | tools/c-api: generate_abi_baseline.py, generate_compatibility_matrix.py, generate_coverage_inventory.py, generate_limitations.py, generate_bool_contract_test.py | Each takes --write or --check. The per-symbol coverage matrix is an ignored build artifact. | CApiAbiBaseline, CApiCompatibilityMatrix, CApiCoverageMatrix, CApiLimitations, CApiBoolContractCurrent; five c-api-* workflows |
XNB interop corpus tests/assets/xnb/cna/windows/{uncompressed,lzx} | committed | generate_interop_fixtures.cpp built as cna_tool_xnb_interop_fixtures (ToolXnbInteropFixtures.cmake) | Build the tool and run it with the two corpus directories as arguments (its usage line: <output-directory> [<lzx-output-directory>]). | XnbInteropCorpusTest.TheCommittedCorpusIsExactlyWhatTheGeneratorProducesToday |
| CNB golden byte vectors | embedded in test sources | gen_golden_vectors.py, written from the format specification and deliberately independent of CnbWriter | Run the script and update the vectors in the test. | CnbGoldenVectorTests.cpp |
glTF fixture corpus tests/assets/gltf | committed | tools/gltf_fixtures through regenerate-gltf-goldens.sh | scripts/regenerate-gltf-goldens.sh, with --check or --determinism. | the glTF sanitizer workflow runs both flags |
glTF rendered goldens tests/gltf-l7/<renderer> | committed | gltf-l7-corpus.py | --update-goldens, which also requires --report-out. | the same script without the update flag compares |
| OpenGL4's EasyGL parity list | committed | generate_easygl_parity_corpus.py writing EasyGLParityCorpus.cmake | Run it, or --check (exit 1 when stale). | no CMake or workflow registration found (searched) |
| XNA Content Pipeline parity report and component reference | committed | tools/xna-pipeline-oracle: parity_report.py, component_reference.py | Regenerate, then the gates compare bytes. | the XnaPipeline* tests in XnaPipelineParityGates.cmake |
Two drift risks found by searching, not by running. Nothing in CMake, the workflows or scripts/ recompiles the Vulkan or SDL_gpu GLSL and compares it with the committed spirv_shaders.hpp; SpirvPayloadValidation only validates the SPIR-V. And 18 of the 20 shader-package headers have a --check registration; the ShadowCaster and ModernResourceInterop packages do not. An edit to those inputs is protected by review only.
Handwritten tables that gates hold in step
- The renderer identity table exists three times:
RendererIdentities.cmake,CNA::GraphicsRendererTypeinGraphicsRendererType.hpp, and theCNA_GRAPHICS_RENDERER_*values ingraphics.h.check_renderer_identities.pykeeps them equal. - The module lists
_cna_framework_modulesand_cna_renderer_modulesinmodules/CMakeLists.txtare handwritten; the source-partition validator fails the configure when a file falls outside them. - The parity fixture list
CNA_PARITY_FIXTURESis handwritten; every renderer that calls the registration entry point inParityFixtures.cmakepicks a new entry up. - Files that mention “generated” are not always artifacts:
SpirvToWgsl.cppis handwritten code that emits text at run time.
Third-party and sibling boundaries
The rule is one line: third_party/ and vendor/ are external. BuildPerformance.cmake attaches CNA's warnings, sanitizer and linker policy only to targets whose source directory is inside the repository and outside those two trees (cna_is_owned_target), so a target from a sibling checkout is excluded as well. Licence notices are collected in THIRD_PARTY_NOTICES.md, which at this snapshot does not list every vendored copy: cgltf, ENet, dr_libs, stb_image and stb_image_write are absent from it (cgltf and ENet ship a LICENSE file in their own directories).
Pinned in the repository
| Path | Kind | Pin | Consumed by |
|---|---|---|---|
SDL | submodule | cbe3fbe9 | ThirdPartySDL.cmake: built at configure time into .sdl-prebuilt-<key>/; two upstream Vulkan-barrier patches from cmake/patches are applied to a staged copy, never to the submodule |
SDL_image, SDL_mixer | submodules | fcb9d0b1, 3075d3ed | the same file; SDL_mixer backs the SDL3 audio mixer |
draco | submodule | 1.5.7, 87867400 | modules/CMakeLists.txt: add_subdirectory unless CNA_ENABLE_DRACO=OFF or CNA_USE_SYSTEM_DRACO |
googletest | submodule | 7e2c425d | UnitTests.cmake: added while CNA_BUILD_TESTS is on |
third_party/enet | in-tree copy | not a gitlink | ThirdPartyENet.cmake: only while CNA_ENABLE_NET is on (the default). Its missing-source message still tells you to run git submodule update --init third_party/enet, which does nothing for an in-tree directory |
stb, dr_libs, cgltf | in-tree single-header libraries | not gitlinks | module CMake files reach them through CNA_SOURCE_DIR, for example graphics/CMakeLists.txt |
Fetched at configure time
| Dependency | Configured in | Pin | Used when |
|---|---|---|---|
| FNA3D with MojoShader | ThirdPartyFNA3D.cmake | tag 3240147 | FNA3D renderer and any family with compiled effects; 117 MojoShader patches in cmake/patches applied idempotently; override with FETCHCONTENT_SOURCE_DIR_FNA3D |
| PortableGL | ThirdPartyPortableGL.cmake | 63a55db7 | PORTABLEGL only |
| SDL_shadercross and SPIRV-Cross | ThirdPartySDLShaderCross.cmake | 1ff05bec; vulkan-sdk-1.4.350.0 | SDL_gpu when CNA_SDL_GPU_SHADERCROSS is on (default on for Windows and Apple targets, off elsewhere); one small patch |
| SDL2 | ThirdPartySDL2.cmake | fa24d868 (2.30.11) | CNA_PLATFORM=SDL2; CNA_SDL2_ROOT overrides |
| wgpu-native | ThirdPartyWebGPU.cmake | v29.0.1.1 prebuilt release | WEBGPU; SHA-256 checked by WebGPUChecksum.cmake before extraction; CNA_WEBGPU_ROOT overrides |
Sibling repositories
Sibling checkouts are not submodules and CMake will not fetch them; a missing one stops the configure with a named message. The build guide says which branches to clone (Building CNA: sibling checkouts).
| Sibling | Needed by | How CMake consumes it |
|---|---|---|
../sharp-runtime (its next branch) | every build | CMakeLists.txt calls add_subdirectory on CNA_SHARP_RUNTIME_ROOT; modules take components through cna_link_sharp_runtime in SharpRuntimeConsumption.cmake |
../easy-gl and ../meta-gl | the five GL identities | RendererSelection.cmake adds ../easy-gl once; easy-gl in turn expects its own sibling ../meta-gl |
../free-direct and ../free-api | FREEDIRECT | the same file adds ../free-direct, whose own CMake reaches ../free-api |
Lookup table: behaviour, owner, header, implementation, tests
63 rows in five groups. Paths link to the snapshot source; symbols are plain text. Tests are files that exist in the tree and are registered by the module's own CMake or by UnitTests.cmake in the configurations that include them; naming one is not a claim that it passed, and a renderer-family or platform test exists only in a build that selects that family or platform.
Host, runtime and input
Graphics and math
Content
Storage, audio, media, devices and services
C ABI, build, tests and CI
Where ownership is split or surprising
These are the cases where a directory name would send a reader to the wrong place. Each was read in source.
| Case | What is actually true |
|---|---|
| Two clipboard classes | CNA::Input::Clipboard in input (with primary selection) and CNA::Devices::Clipboard in devices-ext (compiled in only with CNA_DEVICES) both end at IPlatformClipboard, which has no header of its own: it is declared in IPlatformSystemServices.hpp. The C ABI reaches both: CnaCApiInputDevices.cpp uses the input class, and the clipboard-acceptance route in CnaCApiDevices.cpp uses the devices-ext one. |
| The input bridge has an SDL name | SdlInputBridge.cpp defines PlatformInputBridge, declared in PlatformInputBridge.hpp. The file name is historical; no input file includes an SDL header. |
FrameworkDispatcher | Declared and defined in the audio module (FrameworkDispatcher.cpp) and called from Game.cpp at the end of the constructor and after the component updates in Game::Update. |
| Renderer identity is not renderer family | Five identities share the EasyGL family. The GDI family compiles eight software-module 2D translation units (CNA_GDI_SOFTWARE_SOURCES, gdi/CMakeLists.txt), the one documented exception to "directory is owner". |
A Model has three front ends | The type lives in graphics, but the runtime glTF reader, the .cnj reader and the .cnb decoder are all in content (ContentManager.cpp). |
| The content compiler | The canonical engine is CNA/Content/Pipeline in content; the XNA facade is content-pipeline; cna-content is a target of ToolContentPipeline.cmake over content.cpp and content_main.cpp, and neither the module nor FreeType is in a game's link closure. |
| Path containment | PathContainment.hpp is in core, but storage, content, content-pipeline and media (playlists, library index) all include it: a change is a destructive-boundary change wherever it is edited. |
| Three unrelated version numbers | The product version (CMake), CNA_ABI_VERSION in abi.h (0.29.0, experimental) and the fixed XNA 4.0 level. Version.hpp.in says so itself. |
| Always-compiled platforms | Headless is compiled into every build and Terminal into every non-Windows build whatever CNA_PLATFORM says, so PlatformConformanceTests.cpp can compare several implementations in one process. |
Using this page
Start from a symptom, pick the closest row, and follow its Read next page for the trace. Before editing, decide whether the row you found is the contract (a public header), the implementation (a family or backend), or the registration (CMake), and estimate the reach with Blast radius and readiness. To become the owner of one row rather than look one up, see Choose one subsystem and own it; the lifetime side of the same objects is Ownership and lifetime master map, and what to run after a change is What to test after changing X.
Deep dives on this topic
Long-form pages that explain the exact semantics, invariants and evidence behind this subject.
- Dependency acquisition: submodules, siblings, fetched pins and host packages — The five ways outside code enters a CNA build, how they compose, what the configure-time SDL build implies, how host packages change what compiles, the sibling contract and how CNA itself is consumed.
- easy-gl and meta-gl: the two-library GL stack beneath the EasyGL family — How meta-gl and easy-gl split loading, typed calls, ownership and failure beneath CNA's five GL identities: revisions CNA needs, feature gating, per-thread state, context loss, tests and build inheritance.
- free-direct and free-api beneath the FREEDIRECT renderer — How CNA's 2D-only FREEDIRECT renderer sits on free-direct and free-api: the dependency chain, what each library implements, the fopen macro hazard and its containment, inherited limits and evidence rules.
- The CNA ecosystem: siblings, references and consumers — Which repositories CNA builds against, audits against and is consumed by, when CMake needs each sibling, where each sibling draws its boundary, and which revision each statement refers to.
- The physical module architecture: ownership, targets and composition — How CNA's 22 framework modules and 21 renderer families own their targets, include roots, tests and examples, which target names cannot be guessed, and how to read a source path at snapshot 009d40f5.
Known issues in this area
Current defects, gaps and limitations at this snapshot that touch this subject.
- CNA-BUG-045: THIRD_PARTY_NOTICES.md still omits the vendored cgltf and stb image headers (and ENet and dr_libs) — Draco and stb_vorbis now have notices, but cgltf, stb_image/stb_image_write, ENet and dr_flac/dr_mp3, all vendored under third_party/ and compiled into CNA libraries, are not listed.
Related pages
The same subsystem is explained at four altitudes. These are the neighbouring pages at each one.
- User guide
- Architecture: module layout · Architecture: directory structure · Building CNA: sibling checkouts
- Architecture
- Physical module dependency map · Architecture overview
- Internals
- Internals · CMake architecture
- Maintainer workflow
- Repository map · Choose one subsystem and own it · Blast radius and readiness
- Tests and validation
- Test architecture and change recipes · What to test after changing X