CNA and XNA 4.0: what the compatibility promise covers

CNA snapshot 009d40f5  ·  Deep Dives › Foundations & compatibility  ·  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. The FNA statements were checked by reading the local FNA reference tree at commit b3551247; sharp-runtime statements at its commit 41b918c9. The Windows 7 reproducer result is CNA's own dated record and was not repeated.

CNA reimplements the XNA 4.0 programming model as a native C++ library. This page states precisely what that promise covers at snapshot 009d40f5: what CNA translates and what it can never load, which external reference settles a disputed question, what CNA deliberately is not, and which licence and provenance terms follow from its FNA lineage. Read it before porting a game, before quoting a compatibility claim, and before treating FNA as the final word on what XNA does.

A translation target, not a loader

CNA cannot run an existing XNA game. There is no CLR, no IL interpreter and no binary compatibility with XNA or FNA assemblies: a compiled C# .dll or .exe has nowhere to go. Porting means translating the game's C# source into C++ written against the same names, and the translation is mechanical but real:

C# against XNAC++ against CNAWhy it cannot stay the same
Microsoft.Xna.Framework.GraphicsMicrosoft::Xna::Framework::GraphicsNamespace syntax only; the path is identical.
texture.Width, effect.World = mtexture.getWidthProperty(), effect.setWorldProperty(m)C++ has no properties; every C# property becomes an accessor pair (below and translation conventions).
Content.Load<Texture2D>("hero")getContentProperty().Load<Texture2D>("hero")Returns the asset by value; there is no garbage-collected reference to hand out.
using (var t = new RenderTarget2D(...))a value or std::unique_ptr whose destructor runs at scope exitOwnership is RAII, not a collector.
Exiting += handler; / -=Exiting += lambda;, or Add and Remove(token)A std::function has no equality, so removal is by token.

What survives the translation is the shape of the API (type names, member names, overload sets, enum numeric values) and, where CNA implements and verifies it, the observable behaviour. What cannot survive is the source language, reflection, delegate identity and managed ownership. Code that must stay in C# reaches CNA through the experimental native C ABI and the separate language bindings, which are pinned to an older ABI than this snapshot exports; see the C API bindings boundary.

That is also the difference from FNA. FNA answers how do C# XNA games keep running on modern platforms without Microsoft? and is binary-compatible with XNA 4.0 assemblies. CNA answers what if the code does not have to be C#? Its README states the goal as providing "a native C++ path for teams that like the XNA/MonoGame model but need non-managed runtime/toolchain control" (README.md).

Three engineering problems in one sentence

"A C++ reimplementation of XNA 4.0" hides three separate problems, and most of the site is organised around one of them.

Reproduce an API

XNA's public surface (Microsoft::Xna::Framework and its sub-namespaces, plus the Windows Phone Microsoft::Devices sensors) has to exist in C++ with the same class names, signatures, namespaces and, as far as possible, the same observable behaviour, so that code written against real XNA or FNA has somewhere to land. At this snapshot the representation census (tools/audit_xna_runtime_surface.py) finds all 331 documented runtime types and all 3,627 documented members represented. That is a statement about declarations, not behaviour; Compatibility levels and the evidence vector separates the two.

Reproduce a runtime

XNA is a managed API and silently assumes .NET vocabulary: System.Object, events and delegates, a .NET-shaped exception hierarchy, TimeSpan, collections, IDisposable. CNA does not reimplement the CLR. It depends on the sibling library sharp-runtime, which its own README (at 41b918c9, branch next) describes as a C++23 implementation of a practical subset of System.* "without attempting to implement a CLR, JIT, garbage collector, or the complete .NET platform". CNA does not link all of it: cmake/SharpRuntimeConsumption.cmake names the component closure CNA needs (Core.Base, IO, Collections.Core, Collections.ObjectModel, Runtime, Threading, Text, Globalization, ComponentModel, Storage, Security.Cryptography, Xml, Resources, and Xml.Serialization everywhere except Windows targets). The ecosystem page covers the dependency itself.

Reproduce a GPU

XNA only ever had to drive Direct3D 9 (the Xbox 360's API is Direct3D-shaped too). CNA has to make the same GraphicsDevice, SpriteBatch and Effect surface produce pixels through 25 public renderer identities over 21 implementation families (cmake/RendererIdentities.cmake): native GPU APIs, the five EasyGL-backed GL profiles, browser canvas and DOM renderers, CPU rasterizers and no-output harnesses. A build contains one renderer by default; an opt-in multi-renderer build chooses one before its first device is created. Identical API presence does not imply identical behaviour or evidence: seven identities are 2D-only by design and refuse 3D calls, and capabilities vary widely (see the capability matrix). Host integration and audio are separate configuration axes again; SDL3 is the default for both, not a requirement.

Who decides what XNA does

A reimplementation needs an external authority for every kind of question, or its tests only encode its authors' beliefs. CNA's instruction files split the authority by question, and the split matters because the obvious candidate, FNA, is itself a reimplementation.

QuestionAuthority at this snapshotWhere it is recorded
Does a member exist, with which signature?Microsoft's XNA 4.0 reference assemblies: the XML documentation plus the DLL metadata, not FNAthe API-surface section of CHECKLIST.md; the census script
How does it behave, day to day?The local FNA source tree, because it is readable and agrees with XNA almost everywhere"Source Reference" in CLAUDE.md
What if measured XNA and FNA disagree?XNA wins, as read from the shipped assemblies' IL (owner decision of 2026-09-04). The divergence is recorded where it is made: a code comment naming the IL, and a row in plans/plan_bindings_upstream.mdCLAUDE.md; one older line of the checklist still says FNA wins on semantics, and the dated decision supersedes it
What pixels does XNA produce?The genuine XNA 4.0 runtime, executed under Wine with DXVK on Linux, captured into a 39-scene reference corpustools/xna-oracle/; Using the XNA oracle as evidence
One disputed visual behaviour that no scene coversA purpose-built C# reproducer run by the project owner on real XNA 4.0 in a Windows 7 virtual machinedocs/xna_culling_compatibility_audit.md (below)
Values such as enums, state presets and packed vectors, compared against FNAA harness that executes the real FNA.dll and diffs dumped values; manual, not a CTesttools/fna-reference/; Verification: independent FNA evidence

The Windows 7 reproducer is the least visible of these instruments. The culling audit started from a visual discrepancy in a ported sample (a disc-shaped surface visible under a tank turret). Reading FNA's source could not settle whether CNA's RasterizerState.CullMode was wrong, so a minimal XNA 4.0 project that draws two triangles of opposite winding under all four cull-mode combinations was run on real XNA in the VM. The screenshot confirmed CNA's culling as correct; the defect was reversed triangle winding in the converted sample mesh data, which was fixed in the sample, not in CNA. The lesson generalises: a disputed behaviour is settled by running the authority, not by reading a second reimplementation.

Why FNA is not the API authority: the Song case

XNA's Song declares Album, Artist, Genre and ToString(). FNA's src/Media/Song.cs declares none of them (checked in the local FNA tree at b3551247), because FNA drops members that its desktop focus cannot populate rather than stubbing them. CNA's Media namespace had been reviewed against FNA in eight consecutive adversarial rounds, and all eight came back clean: every round inherited FNA's omission. The gap was found only when the audit method changed to a member-level diff against Microsoft's reference XML (recorded in CHECKLIST.md as the 2026-07-18 case, tasks MEDIA-174 to MEDIA-180 in plans/plan_media.md).

The repair in Song.hpp shows how such a gap is closed faithfully rather than conveniently: getAlbumProperty(), getArtistProperty() and getGenreProperty() return non-owning pointers that MediaLibrary's scan fills in, nullptr for a song that belongs to no library; there are no setters, because the XNA properties are get-only; and ToString() is an override. The ownership pattern is the one the surrounding Media graph already used (see the Media internals). The checklist turned the lesson into a rule: "FNA doesn't have it either" is not a justification. Its mechanical check greps the reference XML per type and maps P: entries to getXProperty/setXProperty, M: to methods and F: to fields or enum values, with the C++ idioms (op_Equality as operator==, the indexer Item as operator[], GetEnumerator as begin()/end()) not reported as gaps.

FNA is a moving reference too

The local FNA reference at b3551247 (2026-05-01) selects its SDL3 platform implementation by default; SDL2 is used only when the environment variable FNA_PLATFORM_BACKEND=SDL2 is set (src/FNAPlatform/FNAPlatform.cs in that tree). Describing FNA as an SDL2 runtime is therefore historical shorthand. CNA's own instruction file also notes that FNA carries known divergences from XNA, several marked FIXME in FNA's source, which is why matching FNA is no longer a defence for behaviour XNA does not have.

The six stated goals

CNA's README at this snapshot lists six goals:

  1. recreate the XNA developer experience in native C++;
  2. provide a native C++ path for teams that like the XNA/MonoGame model but need non-managed runtime and toolchain control;
  3. mirror core XNA namespaces and API patterns while implementing them incrementally;
  4. decouple the gameplay-facing API from renderer implementation details;
  5. enable one high-level API surface across different rendering technologies;
  6. keep SDL, OpenGL and Vulkan-level concerns behind framework abstractions.

The first two are the "why native C++ rather than FNA" argument above. The middle and last ones are one idea stated several ways, the renderer abstraction, and it has a concrete form: the IGraphicsRenderer contract in IGraphicsRenderer.hpp, the generated descriptor registry (cmake/RendererRegistry.cmake), and the platform services a renderer receives instead of calling a windowing library itself. The architecture overview explains that machinery.

What CNA is not, at this snapshot

The negative space moves as the project grows, and several older statements about it are no longer true. Each row states the current boundary.

StatementStatus at 009d40f5
Not a gameHolds. The README calls CNA "a framework/runtime and abstraction layer—not a game". What ships is the library, its test inventory (904 C++ test files with 12,610 static GoogleTest-family definitions, not a pass count) and example programs.
Not an editor-centred engineHolds. CNA ships no editor (its tools are command-line converters, importers and audits) and declares no scene-graph type in its public headers; the game owns its loop, its content and its draw calls. The opt-in CNA::Graphics engine layer is a set of renderer-side services over GraphicsDevice (see the engine layer), and the Inspector is a diagnostics tool that is not an editor.
Not an XNA content builderNo longer true. A build-time pipeline, the cna-content tool, writes .xnb and CNA's own .cnb; it lives in a module that a running game never links. The runtime ContentManager still only loads, and a Game registers the 61 built-in XNB readers in its constructor, so the older claim that every program must bootstrap the readers itself is also out of date (a stand-alone ContentManager in a tool or test still needs the call). See Content Pipeline.
Not universally compiled-shader compatibleHolds, with three distinct routes: renderer-owned stock effects; compiled XNA/FNA Direct3D 9 Effect Framework bytecode accepted by Effect(GraphicsDevice&, bytes) and the XNB EffectReader on 14 of the 25 identities (FNA3D always, the rest behind nine default-OFF options); and the CNAEXT ShaderEffect for renderer-specific source or binaries. HLSL .fx source, DXBC and MGFX are not accepted at run time. See Effects: compiled effects.
Not an Xbox LIVE clientHolds. GamerServices persists achievements and leaderboards locally and draws two Guide overlays for real (the message box and the keyboard-input box, as Begin/End pairs), while 15 other Guide overlay entry points, ShowSignIn among them, are documented no-ops (see GamerServices: local, and real). There is no sign-in, matchmaking or cloud sync; PlayerMatch, Ranked and JoinInvited throw GamerServicesNotAvailableException.
Not a drop-in DirectX runtimeHolds. The FREEDIRECT renderer fronts the sibling free-direct library, a game-driven DirectDraw, DirectSound and DirectPlay subset over SDL3 whose DirectPlay talks only to other free-direct processes, never to Microsoft's wire protocol.
Not mechanically XNA-pure everywhereThe CNAEXT marker becomes a compiler error only where CNA_STRICT_XNA_API is defined, and CNA's own strict harness exercises the Microsoft::Devices and sensor surface only. The README's description of that define as "a dedicated CMake build option" is inaccurate: no CMake option declares it (see how the strict check proves itself).
Not stableHolds. The product version is still 0.1.0-alpha.1, the snapshot carries no new tag, and before 1.0 minor releases may break the API. The experimental C ABI (0.29.0) versions independently. See Releases: Semantic Versioning before 1.0.

Four identities, not one number

Four values identify "which CNA" and they answer different questions: the product version (0.1.0-alpha.1, assembled from the root project(CNA VERSION 0.1.0) and CNA_VERSION_PRERELEASE by cmake/Version.cmake into the generated CNA/Version.hpp); the Git tag of the only release (v0.1.0-alpha.1); the source commit (009d40f5 for this site); and the C ABI version (0.29.0). Because the product version did not move after the tag, CNA::getVersionString() cannot tell the tag from this snapshot; record the commit. Engineering work between releases is tracked as numbered tasks in the repository's plan files (indexed by plans/README.md); a task's closing count is a snapshot of one configuration, never a current total. Make a release is the maintainer view.

Licence and provenance

CNA is licensed under the Microsoft Public License (Ms-PL), the licence FNA uses, and the match is deliberate: NOTICE.md states that portions of CNA are derived from or based on FNA. The licence text in LICENSE is short and its practical terms are these:

  • a copyright licence to reproduce, prepare derivative works and distribute, and a patent licence under each contributor's patent claims that read on its contribution;
  • a patent-retaliation clause: bringing a patent claim against a contributor over the software ends that contributor's patent licence to you;
  • distributing any portion requires keeping the copyright, patent, trademark and attribution notices; distributing a portion in source form is allowed only under Ms-PL itself, and in compiled form only under a licence that complies with it;
  • no trademark licence and no warranty.

The licence speaks about distributing the software and its derivative works; it contains no clause that reaches a separate work merely because that work uses the library. (This is a reading of the text, not legal advice.)

Two pieces of Microsoft-originated shader material ride on that lineage, and they are the most visible derived parts. The DIRECTX9 renderer compiles the ten Stock Effects HLSL sources in modules/renderers/directx9/src/shaders/xna/, copied byte for byte from FNA, which in turn vendors them from Microsoft's Stock Effects sample; scripts/verify-d3d9-stock-effects-vendored.sh diffs them against an FNA tree and fails on any delta. The FNA3D renderer carries six compiled Effect Framework binaries of the same effects in modules/renderers/fna3d/effects/, with their licence in LICENSE.StockEffects. THIRD_PARTY_NOTICES.md records both, although its path for the HLSL sources still names the pre-module src/CNA/Internal/Backends/D3D9/ location rather than the module directory above.

The same subject is explained at several altitudes. These are the neighbouring pages at each one.