Tutorial 01: Introduction to CNA
What you’ll learn
- What CNA is, and how it differs from XNA 4.0, MonoGame and FNA.
- How C++ changes the API shape you knew from C#: property getters/setters, namespaces, manual memory.
- Why
sharp-runtimetypes turn up in almost every CNA signature. - What the
CNAEXTmarker means when you meet it in a header.
Before you start — None — this tutorial stands alone. It is the entry point to the series; read it before anything else.
What is CNA?
CNA is a C++23 reimplementation of the Microsoft XNA 4.0 game framework. It exposes the same Microsoft::Xna::Framework API that XNA developers know, but compiles to native code using modern C++23 and runs on Linux, Windows, macOS, Android and the web (iOS is experimental). SDL3 is CNA’s windowing platform layer and reaches Windows, X11, Wayland, macOS, Android and the browser through its own video drivers; headless and terminal platforms exist for windowless and text-mode runs.
Which CNA these tutorials describe. They track CNA snapshot c1c316b9 (branch apple/m4-stabilization, 9 October 2026), a development snapshot 3,687 commits after the v0.1.0-alpha.1 tag. CNA’s own version string still reads 0.1.0-alpha.1. A plain git clone https://github.com/libcna/cna.git gives you the alpha.1 default branch, so Tutorial 02 clones -b apple/m4-stabilization.
The name "CNA" is a deliberate reversal of "XNA" — it signals that you are writing C++ instead of C#, but the programming model is the same. If you have ever written an XNA or MonoGame game you will find CNA immediately familiar.
CNA covers the public Microsoft::Xna::Framework namespaces, including the Framework.Design type converters, which live in the opt-in CNA::Design module. That includes real 2D and 3D rendering, audio, input, math, and content loading. CNA’s own audit tool reports all 331 public XNA runtime types and 3,627 documented runtime members as represented; that is a statement about API shape, not about behavior. Rather than publish a blended "percent complete" figure, CNA pins its public API with compile-time signature-freeze tests and a CNA_STRICT_XNA_API purity mode. In this snapshot the test tree contains 813 C++ test source files and 11,380 statically discoverable GoogleTest-family definitions (alpha.1: 568 and 8,263); the executable and CTest totals remain configuration-dependent.
Reading .xnb content files is supported: CNA implements the XNB container, LZX decompression, and 61 built-in type readers (60 where sharp-runtime has no native int128). A Game subclass registers them for you; only a standalone ContentManager (a tool, a test) must call CNA::Internal::Xnb::RegisterAllBuiltInXnbReaders() once. Content is loaded through a ladder: .xnb, then .cnb (CNA’s own compiled container), then loose files. CNA is now both an XNB loader and a build-time content pipeline: the cna-content tool (CMake target cna_content_tool) turns source files into .cnb or .xnb. Its EffectReader can load XNA/FNA D3D9 Effect Framework bytecode only on a renderer build advertising CompiledEffects; HLSL .fx source is never compiled at run time (the build-time tool can call an external fxc-compatible compiler), and DXBC and MGFX are unsupported inputs.
CNA is research and demo quality, not yet recommended for shipping commercial games. It is ideal for learning game development, porting XNA/MonoGame projects to C++, building demos, and experimenting with game engine design.
Renderers
CNA exposes 14 renderer identities across 12 implementation families. A normal build compiles one renderer; an opt-in CNA_GRAPHICS_RENDERERS build can include several and choose one before the first graphics device is created. The identities are not equally complete: some are full 2D+3D paths, one (SDL_RENDERER) is deliberately 2D-only, and others are no-output or experimental targets. The Graphics Renderers page has the per-renderer detail; the ones you are most likely to pick are:
| Renderer | CMake flag | Use case |
|---|---|---|
SDL_RENDERER | -DCNA_GRAPHICS_RENDERER=SDL_RENDERER | Portable 2D only, no shaders. The default when nothing more specific applies |
OPENGLES3 | -DCNA_GRAPHICS_RENDERER=OPENGLES3 | OpenGL ES 3.0 / OpenGL 3.0+, full 2D and 3D. The Linux default |
VULKAN | -DCNA_GRAPHICS_RENDERER=VULKAN | Vulkan, low-level control, 2D and 3D |
SDL_GPU | -DCNA_GRAPHICS_RENDERER=SDL_GPU | SDL3's GPU API, 2D and 3D |
WEBGL2 | -DCNA_GRAPHICS_RENDERER=WEBGL2 | WebGL 2 in the browser. The Emscripten default; Emscripten builds only |
SOFTWARE | -DCNA_GRAPHICS_RENDERER=SOFTWARE | CPU rasterizer, deterministic pixel tests, no GPU needed. Off-screen on windowing platforms — read pixels back with GetBackBufferData(); only the TERMINAL platform displays its frames |
HEADLESS | -DCNA_GRAPHICS_RENDERER=HEADLESS | CI logic testing, no GPU/window, no pixel output at all |
DIRECTX9 | -DCNA_GRAPHICS_RENDERER=DIRECTX9 | Windows only. 2D and 3D, and the renderer with the exact-match test against the 39-scene real-XNA oracle corpus (recorded 0-diff at tolerance 0 under Wine + DXVK; not a CI gate), since real XNA ran on Direct3D 9 |
DIRECTX11 | -DCNA_GRAPHICS_RENDERER=DIRECTX11 | Windows only. Native Direct3D 11 |
METAL | -DCNA_GRAPHICS_RENDERER=METAL | Native Metal on macOS and iOS, 2D and 3D |
The default is chosen for you if you do not pass the flag: WEBGL2 under Emscripten, OPENGLES3 on Linux, and SDL_RENDERER otherwise. Two equivalent single-renderer forms exist and must not be mixed: -DCNA_GRAPHICS_RENDERER=<NAME>, or -DCNA_RENDERER_<NAME>=ON with exactly one turned on. Passing a name that is not one of the 14 to -DCNA_GRAPHICS_RENDERER is a hard CMake FATAL_ERROR that names the value, not a silent fallback. The switch form is checked less strictly: -DCNA_RENDERER_<NAME>=ON selects a public identity, but a switch for an unrecognised name (a typo, or an old spelling such as D3D9) is not read at all and the default renderer is configured, so read the CNA: Using <X> graphics renderer line of the configure log.
Two more things worth knowing early. The three GL-profile identities — OPENGLES3, OPENGL33 and WEBGL2 — share one internal implementation called EasyGL, which lives in the ../easy-gl sibling repository. Two renderers (DIRECTX9 and DIRECTX11) are Windows-only, WEBGL2 is Emscripten-only, and METAL runs on macOS and iOS. The repository has manual Wine paths for the Windows-only renderers; no automatic workflow runs them.
For these tutorials we will use OPENGLES3 as the primary renderer because it supports both 2D and 3D rendering and runs on any machine with a modern GPU driver. See Tutorial 72 for a fuller comparison.
sharp-runtime
CNA depends on sharp-runtime, a companion C++ library that provides C#-compatible primitive types. This gives you types like intcs (equivalent to C# int), bytecs, Single, floatcs, boolcs, and C# string/array semantics. These types make the CNA source code mirror the original XNA C# code as closely as possible and help when porting existing XNA games.
You will see sharp-runtime types in CNA API signatures. In practice you can often pass plain C++ values (int, float, bool) directly because the types are implicitly convertible. The sharp-runtime repository must be cloned as a sibling directory to the cna directory, and for this snapshot it must be its apple/m4-stabilization branch.
The CNAEXT marker
Some CNA APIs that have no XNA equivalent are annotated with a CNAEXT marker in the source. This helps you distinguish CNA extensions from XNA-faithful APIs when porting code.
CNA vs XNA vs MonoGame
| Property | XNA 4.0 | MonoGame | CNA |
|---|---|---|---|
| Language | C# | C# | C++23 |
| Runtime | .NET Framework | .NET / Mono | Native (no runtime) |
| Status | Discontinued 2013 | Active | Active (research) |
| Platforms | Windows / Xbox 360 | Many | Linux, Windows, macOS, Android, Web (iOS experimental) |
| Rendering | DirectX | DirectX / OpenGL / Metal / Vulkan | One of 14 identities by default, or a compatible runtime-selectable set; host platform is independent |
| API compatibility | Reference | Broad | XNA-facing signatures pinned by compile-time freeze tests |
| GC overhead | Yes (.NET GC) | Yes (.NET GC) | None — RAII/smart pointers |
If you already know XNA or MonoGame, porting to CNA is mostly a matter of translating C# idioms to C++ idioms. The class names, method names, and overall structure stay the same.
Key Differences: C++ vs C#
The biggest adjustment when coming from XNA/MonoGame is moving from C# to C++. Here are the most common differences you will encounter throughout these tutorials:
Memory management
C# uses garbage collection — you can allocate objects freely and forget about them. C++ requires explicit memory management. CNA games use smart pointers to handle this safely:
// C# (XNA/MonoGame)
SpriteBatch spriteBatch;
spriteBatch = new SpriteBatch(GraphicsDevice);
// C++ (CNA) — use unique_ptr for exclusive ownership
std::unique_ptr<SpriteBatch> spriteBatch_;
spriteBatch_ = std::make_unique<SpriteBatch>(getGraphicsDeviceProperty());
Properties become getter/setter methods
C# properties like GraphicsDevice.Viewport become getViewportProperty() / setViewportProperty() in CNA's C++ API:
// C# (XNA)
var vp = GraphicsDevice.Viewport;
// C++ (CNA)
auto vp = getGraphicsDeviceProperty().getViewportProperty();
Namespaces and includes
Every CNA class lives in the Microsoft::Xna::Framework namespace hierarchy and requires a corresponding include:
#include "Microsoft/Xna/Framework/Game.hpp"
#include "Microsoft/Xna/Framework/Color.hpp"
#include "Microsoft/Xna/Framework/Graphics/SpriteBatch.hpp"
using namespace Microsoft::Xna::Framework;
using namespace Microsoft::Xna::Framework::Graphics;
sharp-runtime types
CNA function signatures use sharp-runtime types. In practice you can usually pass standard C++ types and they will implicitly convert:
// These are equivalent — intcs is implicitly constructible from int
intcs width = 800;
int height = 600;
graphics_.setPreferredBackBufferWidthProperty(width);
graphics_.setPreferredBackBufferHeightProperty(height);
No LINQ or delegates
Where XNA C# code uses delegates or LINQ, CNA uses C++ lambdas and standard algorithms. You will see this most in audio and effects APIs.
What You Can Build
CNA has enough of the XNA API implemented to build a wide variety of game types today:
- 2D games — platformers, shoot-em-ups, puzzle games, visual novels. SpriteBatch, Texture2D, SpriteFont, and full input handling are all working.
- 3D games — BasicEffect, VertexBuffer, IndexBuffer, Model loading, and draw primitives work on the OPENGLES3 and VULKAN renderers. glTF 2.0 files load at runtime through
getContentProperty().Load<Model>("…")with no tooling step. - Tech demos — The CNA House 3D demo runs in the browser via WebAssembly.
- Ports of XNA/MonoGame games — If you have an existing XNA or MonoGame project, CNA provides a migration path to C++ without rewriting your entire game.
Audio (through SDL3_mixer, or through CNA’s own mixer with the native ALSA backend, including a real XACT parser and player) and touch input (all 10 XNA gesture types detected) are largely functional. XNA/FNA D3D9 Effect Framework binaries load on FNA3D and, where the matching *_COMPILED_EFFECTS option is switched on, on EasyGL-family, Vulkan, WebGPU, Software, DirectX 9, DirectX 11, Metal and SDL_GPU builds; HLSL .fx source is not compiled at run time, DXBC and MGFX remain unsupported inputs, and custom renderer-native shaders use ShaderEffect. Two platform caveats are worth flagging up front: on the web, saves persist only through the browser’s IndexedDB storage (a threaded build needs -DCNA_EMSCRIPTEN_USE_WASMFS=OFF for that), and video playback needs the optional FFmpeg backend, which is never built for Windows, Emscripten, Android or iOS — the Video types still link there but throw NotSupportedException. The roadmap tracks current status.
How These Tutorials Are Structured
This tutorial series builds a complete knowledge base in a linear sequence. Each tutorial adds one concept and provides working, compilable code examples.
- Tutorials 01-02 — Introduction and environment setup. No code yet.
- Tutorials 03-05 — Getting a window open, understanding the game class and game loop.
- Tutorials 06-09 — Rendering: shapes, colors, textures, and text.
- Tutorials 10-11 — Input: keyboard and mouse.
- Tutorials 12-13 — Movement and animation.
Each tutorial assumes you have completed the previous ones. If you have XNA experience you may skim the early tutorials quickly — the C++ patterns will be the main new thing.
Code examples use the following conventions:
- Member variables end with
_(e.g.,spriteBatch_) using namespace Microsoft::Xna::Framework;is assumed in all code examples- Headers are abbreviated — full include lists are shown only in tutorial 03
Prerequisites
These tutorials assume:
- C++ basics — you are comfortable with classes, inheritance, pointers, and the standard library. You do not need to be a C++ expert, but you should know what
std::unique_ptris. - Game development concepts — you understand what a game loop is at a high level (update state, render frame, repeat).
- Command line — you can run CMake and build commands in a terminal.
- XNA/MonoGame familiarity is helpful but not required — the tutorials explain every concept from first principles.
You do not need to know C# or have used XNA before. Tutorial 02 walks you through installing every dependency from scratch.
Ready to get started? Head to Tutorial 02: Setting Up Your Dev Environment.