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

PathContents
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.

Open

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.

Open

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.

Open

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.

Open

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.

Open

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.

Open

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.

Needs CI

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.

Needs a Windows toolchain fix

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.

Unofficial

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.

Open

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.

Open

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.

Always welcome

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::Framework public 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; } becomes intcs getWidthProperty() const and void setWidthProperty(intcs value) in C++.
  • Use sharp-runtime types. Use intcs, bytecs, Single, String, events, and interfaces from sharp-runtime to match C# semantics. Avoid raw int or float at 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 siblings easy-gl and free-direct, alongside in-tree enet, cgltf, stb and wgpu-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 // CNAEXT comment 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.

View CNA on GitHub