CNA vs Alternatives

Choosing between CNA, FNA, MonoGame, and original XNA 4.0

Current documented release: 0.1.0-alpha.1. CNA covers the public XNA namespace surface except Framework.Design, pins signatures with compile-time freeze tests, and offers CNA_STRICT_XNA_API. The tag contains 568 C++ test sources and 8,263 statically discoverable GoogleTest-family definitions, plus a 39-scene XNA oracle corpus that the DIRECTX9 renderer matches at zero tolerance. XACT and read-side XNB are real. XNA/FNA compiled Effect Framework binaries work only on qualified renderer builds; HLSL .fx source is not compiled.

Maturity, honestly. FNA and MonoGame have shipped commercial games for over a decade. CNA's first tagged release is explicitly an alpha: its APIs and experimental native C ABI may change, renderer capabilities vary widely, and the 21-workflow CI inventory does not prove every renderer, driver and platform combination. If you are shipping a commercial title on a deadline, weigh that maturity difference first.

Overview

The XNA 4.0 API has three meaningful successors: FNA, MonoGame, and now CNA. All three preserve the familiar XNA programming model, but each occupies a different niche. FNA is the most faithful managed reimplementation and the right choice for shipping existing C# XNA games. MonoGame extends XNA toward modern commercial development with broad console support and a large community. CNA is the only C++ reimplementation — it targets developers who want the XNA design without a managed runtime, who need a native foundation for an engine, or who are building Linux-first and WASM-first projects.

The original XNA 4.0 is discontinued (last update 2014) and runs only on Windows with .NET 4.0. It remains relevant only for projects still in maintenance on that exact stack.

Feature comparison

Feature CNA FNA MonoGame XNA 4.0
Language C++23 C# (.NET) C# (.NET) C# (.NET)
Runtime Native (no GC) Mono / .NET Mono / .NET .NET 4.0
API model XNA 4.0 compatible XNA 4.0.4 binary compatible XNA-inspired Original
Renderers 50 public identities across 46 implementation families, spanning native GPU APIs, middleware, browser DOM/canvas, CPU rasterizers and no-output harnesses. They are not equally complete. FNA3D (OpenGL, Vulkan, Metal, DX11) OpenGL, DX11, Metal, Vulkan DirectX
Renderer selection Single-renderer by default; opt-in multi-renderer builds select before the first device and can enable explicit fallback Runtime Runtime Runtime
Platforms Linux, Windows, macOS, Emscripten and Android source/build paths; narrow experimental iOS SDL_RENDERER final-link and simulator-smoke evidence. Headless and terminal host modes are independently selectable. Windows, Linux, macOS, iOS, Android, Switch Windows, Linux, macOS, iOS, Android, Switch, consoles Windows, Xbox 360, Windows Phone
API coverage Public XNA namespace surface except Framework.Design, pinned by signature-freeze tests. Effect bytecode and graphics-format behavior remain renderer-qualified. ~100% (binary compatible) ~95%+ Reference
XACT audio Implemented, with named codec/listener limits Implemented Via XACT or custom Full
XNB content pipeline Read-side loader 50 built-ins with FFmpeg, real LZX, typed external references and compiled effects; explicit startup and custom-reader registration, no reflective discovery Yes Yes (via MGCB) Yes
Compiled .fx bytecode Renderer-qualified XNA/FNA D3D9 Effect Framework binaries on FNA3D and opt-in EasyGL, SDL_GPU or Vulkan builds; not HLSL source, DXBC or MGFX Yes (MojoShader) Yes Yes
Custom shaders Hand-written GLSL or SPIR-V through the CNA-only ShaderEffect, per renderer GLSL / HLSL via FNA3D GLSL / HLSL / Metal / SPIR-V HLSL
Surface formats Renderer-dependent Query and test the active renderer Full set Full set Full set
Test suite 568 C++ test sources; 8,263 statically discoverable GoogleTest-family definitions; configuration-scoped CTest registration; 39-scene XNA oracle corpus Some
Continuous integration Growing 21 workflow files including Linux, Apple, Emscripten, platform, multi-renderer and declared C API gates; some jobs are broken/manual and coverage is not exhaustive Established Established
Memory model RAII, no GC Managed GC Managed GC Managed GC
License Ms-PL Ms-PL Ms-PL Proprietary / discontinued
Status 0.1.0-alpha.1 pre-release Stable / active Stable / active Discontinued 2014

When to choose each

Choose CNA when…

  • Your codebase is in C++ and you want XNA-style structure without switching languages.
  • You need a native binary with no managed runtime, GC pauses, or JIT warm-up.
  • You are doing engine research or building a higher-level engine on a stable XNA-shaped API.
  • Linux-first or Web (WASM) deployment is a primary goal.
  • You are already familiar with XNA and want that API in C++23.
  • You value a large source-auditable test inventory and are prepared to run the exact renderer/platform configuration you ship.
  • Your content and graphics needs fit the capability boundaries of a renderer you have verified.

Do not choose CNA when…

  • You are shipping a commercial title on a schedule — FNA and MonoGame have a decade of production use behind them and CNA does not.
  • Your game ships effects in a format the selected renderer cannot consume and you cannot migrate or rebuild them.
  • You need a platform or graphics capability that alpha.1 does not verify for your chosen configuration.
  • You need stable pre-1.0 API/ABI guarantees or production support today.

Choose FNA when…

  • You have an existing C# game that shipped on XNA and need the closest drop-in replacement.
  • Binary compatibility with XNA 4.0.4 assemblies is required.
  • You want the most complete and battle-tested managed XNA reimplementation available.
  • You need XNB content pipeline compatibility for existing asset workflows.

Choose MonoGame when…

  • You are starting a new commercial C# game and want a large, active community behind you.
  • Broad console support (Switch, PlayStation, Xbox) is on your roadmap.
  • You want to use the MGCB content build pipeline and the MonoGame ecosystem of tools.
  • Long-term community maintenance and third-party tutorials matter to your team.

Stick with original XNA 4.0 when…

  • You are maintaining a very old project that must stay on .NET 4.0 on Windows.
  • Migrating to any other framework is out of scope.
  • Note: no new projects should target original XNA. Microsoft discontinued it in 2014 and it is not available for modern Windows without compatibility shims.

C++ vs C# API differences

CNA maps the XNA 4.0 API as faithfully as C++23 allows. The core patterns are the same, but C++ syntax requires some adjustments.

XNA / C# pattern CNA / C++ equivalent
Property texture.Width Getter method texture.getWidthProperty()
Property setter effect.World = m; Setter method effect->SetWorld(m);
using (var x = new T()) { ... } RAII: { auto x = std::make_unique<T>(...); ... } (destructor called on scope exit)
C# delegates / events C++ virtual method overrides or sharp-runtime event wrappers
Content.Load<T>("name") content.Load<T>("name") — identical signature
Namespace Microsoft.Xna.Framework Namespace Microsoft::Xna::Framework
new Texture2D(device, w, h) std::make_unique<Texture2D>(device, w, h)
Color.CornflowerBlue Color::CornflowerBlue
Vector2(1f, 2f) Vector2(1.0f, 2.0f)

Code comparison: LoadContent and Draw

The following side-by-side example shows the same LoadContent + Draw pattern in XNA C# and in CNA C++23. The structure is deliberately identical; only language-level syntax differs.

XNA / MonoGame / FNA — C# CNA — C++23
// C# — XNA / MonoGame / FNA
Texture2D playerTexture;
SpriteBatch spriteBatch;

protected override void LoadContent()
{
    spriteBatch = new SpriteBatch(GraphicsDevice);
    playerTexture = Content.Load<Texture2D>("player");
}

protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.CornflowerBlue);

    spriteBatch.Begin();
    spriteBatch.Draw(
        playerTexture,
        new Vector2(100, 100),
        Color.White);
    spriteBatch.End();

    base.Draw(gameTime);
}
// C++23 — CNA
std::unique_ptr<Texture2D> playerTexture;
std::unique_ptr<SpriteBatch> spriteBatch;

void LoadContent() override
{
    spriteBatch =
        std::make_unique<SpriteBatch>(graphicsDevice);
    playerTexture =
        content.Load<Texture2D>("player");
}

void Draw(GameTime gameTime) override
{
    graphicsDevice->Clear(Color::CornflowerBlue);

    spriteBatch->Begin();
    spriteBatch->Draw(
        *playerTexture,
        Vector2(100.0f, 100.0f),
        Color::White);
    spriteBatch->End();

    Game::Draw(gameTime);
}

Key differences to note: std::unique_ptr replaces new for owned resources; *playerTexture dereferences the smart pointer when passing by value or reference; Game::Draw(gameTime) is the C++ equivalent of base.Draw(gameTime); and static colour / vector constants use :: instead of ..

Migration note

If you are considering porting an existing MonoGame or FNA project to CNA, see the dedicated Migration from MonoGame / FNA guide. It covers content loading (CNA reads .xnb where a reader is registered, and falls back to loose files otherwise), property-to-method renaming, and smart pointer ownership patterns. For the full per-namespace picture of what will and will not work, start at XNA Compatibility.