Contribute to CNA
CNA is an open-source C++23 reimplementation of the XNA 4.0 API built on SDL3. Contributions of any kind are welcome — new implementations, tests, platform fixes, and documentation improvements all help.
Getting started
Step 1 — Clone CNA and initialise submodules
git clone https://github.com/openeggbert/cna.git
cd cna
git submodule update --init --recursive
This populates third_party/SDL, third_party/SDL_image, and third_party/SDL_mixer. SDL3 is built from source by CMake — no system SDL packages are required.
Step 2 — Clone sibling repositories
CNA always requires sharp-runtime as a sibling directory next to cna/. Two more siblings are needed only by specific renderers: easy-gl for EasyGL and free-direct for FreeDirect.
parent/
├── cna/
├── sharp-runtime/ # always required
├── easy-gl/ # required by the OPENGLES3 renderer
└── free-direct/ # required by the FREEDIRECT renderer
Clone them at the same level:
git clone https://github.com/openeggbert/sharp-runtime.git
git clone https://github.com/openeggbert/easy-gl.git
git clone https://github.com/openeggbert/free-direct.git
Step 3 — Build with EasyGL
cmake -S . -B build -DCNA_GRAPHICS_RENDERER=OPENGLES3
cmake --build build --target CnaTests
CNA is an interface umbrella, not a standalone build target; build CnaTests, an example, or your consumer target. CNA accepts 50 renderer identities. A normal configure selects one through CNA_GRAPHICS_RENDERER; an opt-in CNA_GRAPHICS_RENDERERS list can link several compatible families for pre-device runtime selection. See the Building documentation for the full inventory and platform gates.
Step 4 — Run the tests
ctest --test-dir build --output-on-failure
The alpha.1 source tree has 568 C++ test files and 8,263 statically discoverable GoogleTest-family definitions. What reaches CnaTests and CTest depends on renderer set, platform, audio implementation, host and options. Run the configuration you changed and include its configure command, ctest -N inventory and failing output in a report.
Single-renderer build directories remain the easiest way to isolate a matrix. Multi-renderer builds are valuable for registry, selection and fallback work, but they do not remove renderer-specific dependencies or platform gates. Alpha.1 has 21 workflow files; local coverage of the exact family and driver you changed remains valuable.
Repository structure
| Path | Contents |
|---|---|
modules/*/include/ |
Module-owned public C++ headers, including the XNA 4.0-shaped API and CNA namespaces. Non-XNA declarations in the XNA surface require CNAEXT. |
modules/*/src/ |
Module-owned implementations; renderer families live under modules/renderers/. |
modules/*/tests/ |
Module-owned GoogleTest sources. Alpha.1 has 568 C++ test files and 8,263 statically discoverable definitions; a build compiles only its applicable subset. |
third_party/ |
Vendored submodules: SDL3, SDL3_image, SDL3_mixer. Do not modify these directly. |
docs/ |
CNA-internal documentation: xna-4-api-coverage.md, coverage.md, and related notes on implementation status. |
CHECKLIST.md |
Per-file porting checklist tracking which XNA types have been ported. Also explains the CNAEXT marker convention. |
GRAPHICS_TASKS.md |
Graphics-specific implementation task plan listing remaining work per renderer. |
The CNAEXT marker
Rule: When porting XNA APIs, stay faithful to the public XNA 4.0 surface. Use the CNAEXT marker only for C++ glue code that has no counterpart in the managed XNA/FNA API.
Methods and types annotated with a // CNAEXT comment in the source are not part of the XNA 4.0 public API. They exist purely to support C++ idioms and integration patterns. Examples:
- Iterator support —
begin()/end()on collections to enable range-for loops GetTypeName()— runtime type name helper for debugging- RAII helpers and move constructors that have no equivalent in managed C#
The authoritative reference for the XNA 4.0 public API surface is FNA (C#). When in doubt whether a method belongs to the XNA API, check the corresponding FNA class. If it is present there without modification, it belongs in CNA without the CNAEXT marker. If it is a C++-only addition, mark it CNAEXT.
What needs work
Below are the most impactful contribution opportunities, grouped by area. Any of these would be a meaningful addition to the project.
Broaden compiled-effect portability
Alpha.1 implements XNA/FNA D3D9 Effect Framework bytecode on FNA3D and opted-in EasyGL, SDL_GPU and Vulkan builds. The open work is to extend that qualified path, add real sample runtime evidence, or build an explicit conversion strategy for different inputs such as HLSL .fx source, DXBC and MGFX. Do not treat those formats as interchangeable.
bgfx 32-bit index buffers
A confirmed correctness bug. The bgfx renderer never overrides CreateIndexBuffer32, so 32-bit indices are uploaded into a 16-bit buffer and DrawUserIndexedPrimitives corrupts geometry. Every other 3D renderer implements this. Well-scoped and clearly diagnosed — a good first substantial contribution.
bgfx D3D and Metal shader variants
bgfx ships 108 shader blobs, but only for GL, GLES, Vulkan and WebGPU — there are no D3D or Metal variants. The practical consequence is that 3D silently draws nothing on bgfx's default Windows and macOS renderers. Building out the missing shader variants through bgfx's shader toolchain would make the renderer genuinely cross-platform.
Truthful SupportsCapability()
The base explicitly returns false for multi-stream input and compiled effects and delegates stencil, but returns true for the remaining capabilities. DIRECTX9, DIRECTX11, DIRECTX12 and SDL_GPU never override it at all. Because most entries fail open, several renderers advertise features whose implementation throws or silently does nothing. Renderers that report honestly — deterministically refusing what they cannot do — are the model to follow.
DIRECTX12 scissor, viewport, stencil and blend factor
DIRECTX12 has a strong core (PSO and root-signature caches, a barrier tracker) but four unimplemented pieces of fixed-function state, and its MRT path clears attachments but never draws to them. Windows-only work with a clear specification and existing sibling implementations in DIRECTX9 and DIRECTX11 to follow.
Draw-offset handling
startIndex and baseVertex are ignored on DIRECTX11, and on bgfx outside the wireframe path. Any code that draws a sub-range of a shared buffer therefore renders the wrong geometry on those renderers. Small, well-defined, and pixel-testable.
Broader CI coverage
Alpha.1 has 21 workflow files covering Linux, Apple, Emscripten, platform abstraction, runtime multi-renderer selection and five declared C API gates, while Windows D3D/GDI lanes remain manual. The C API's final target is nevertheless compile-blocked by its incomplete renderer map, and the wider matrix is not exhaustive across 50 identities, drivers and target platforms. Fixing or extending a reproducible gap is useful work.
CnaTests on Windows
Windows-native lanes exist but the D3D/GDI workflows are manual. A useful contribution is to make a specific renderer lane automatic and reproducible, with its exact MSVC configuration and scoped test inventory documented.
macOS support
CNA has automatic macOS/Metal coverage and experimental iOS final-link plus simulator-smoke coverage. Useful contributions include testing other valid macOS renderer configurations and carefully expanding iOS evidence beyond its alpha.1 SDL_RENDERER boundary.
XNB reader gaps
The read-side XNB loader has 50 built-in readers with FFmpeg and 49 without it. VideoReader, typed external references and a real compiled-effect EffectReader are present. Reflection-based discovery is not; custom readers and closed generic types need explicit registration through the available creator/reader APIs. See XNB Content Pipeline.
ContentManager lifecycle
ContentManager::Unload() clears its maps without disposing the assets they held, and ResourceContentManager is the repository's only pure stub. Both are small, self-contained fixes with obvious correctness criteria.
Test coverage
Coverage remains uneven even with 8,263 statically discoverable definitions. Storage has only five GoogleTest-family definitions, while Media has 286 and the Canvas renderer has 17; none of those source counts proves runtime success on a target. New focused tests and configuration-specific execution evidence are welcome.
Code style
- C++23 throughout. CNA requires GCC 12+, Clang 15+, or MSVC 2022 v17.8+. Use modern C++ features freely — structured bindings, ranges, concepts — but keep readability first.
- API shape is fixed. The
Microsoft::Xna::Frameworkpublic API mirrors XNA 4.0 exactly. Do not rename, remove, or add overloads to public methods without a compelling reason backed by FNA precedent. - Properties become getter/setter methods. C#
public int Width { get; set; }becomesintcs getWidthProperty() constandvoid setWidthProperty(intcs value)in C++. - Use
sharp-runtimetypes. Useintcs,bytecs,Single,String, events, and interfaces fromsharp-runtimeto match C# semantics. Avoid rawintorfloatat the public API boundary. - No extra runtime dependencies. CNA's dependency set is deliberately small and mostly vendored or submoduled — SDL3 (plus SDL_image and SDL_mixer),
sharp-runtime, and the per-renderer siblingseasy-glandfree-direct, alongside in-treeenet,cgltf,stbandwgpu-native. Do not introduce new third-party libraries without prior discussion. - Mark non-XNA additions as
CNAEXT. Any method or type added purely for C++ convenience must carry a// CNAEXTcomment so that API coverage tooling stays accurate.
License and attribution
CNA is licensed under the Microsoft Public License (Ms-PL). All contributions must be compatible with the Ms-PL. By submitting a pull request you agree that your contribution will be distributed under the same license.
Deriving from FNA (C#): FNA is the authoritative XNA 4.0 reference and is also licensed under the Ms-PL. If you port logic from FNA's C# source into CNA, you must preserve the Ms-PL attribution. See NOTICE.md and THIRD_PARTY_NOTICES.md for the existing attribution text and the pattern to follow.