CNA vs Alternatives

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

Current status: CNA implements 227 of the 245 public XNA 4.0 types (measured against FNA), verified by 4,373 unit tests and 490 GPU pixel tests. XACT audio is genuinely implemented, not stubbed. The .xnb content pipeline and compiled .fx shader bytecode remain the two intentional exclusions, and they are what most porting effort will run into. The project is under active development.

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
Rendering backends SDL_RENDERER, EasyGL, Vulkan, bgfx FNA3D (OpenGL, Vulkan, Metal, DX11) OpenGL, DX11, Metal, Vulkan DirectX
Platforms Linux, Android, Web (WASM), Windows (SDL_RENDERER, cross-compiled/Wine-verified) Windows, Linux, macOS, iOS, Android, Switch Windows, Linux, macOS, iOS, Android, Switch, consoles Windows, Xbox 360, Windows Phone
API coverage 227 / 245 types; no .xnb, no .fx ~100% (binary compatible) ~95%+ Reference
XACT audio Real (~97%) Implemented Via XACT or custom Full
XNB content pipeline No (.json descriptors) Yes Yes (via MGCB) Yes
Custom shaders GLSL (EasyGL), SPIR-V (Vulkan) GLSL / HLSL via FNA3D GLSL / HLSL / Metal / SPIR-V HLSL
Test suite 4,373 tests (GoogleTest) Some
Memory model RAII, no GC Managed GC Managed GC Managed GC
License Ms-PL Ms-PL Ms-PL Proprietary / discontinued
Status Active development 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 want a library with a growing automated test suite (currently 4,373 GoogleTest cases).

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.getWidth()
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 asset descriptor conversion (XNB to JSON), property-to-method renaming, and smart pointer ownership patterns.