Getting Started with CNA
CNA 0.1.0-alpha.1 is still the version string CNA reports, but this guide documents a newer commit than the v0.1.0-alpha.1 tag: snapshot c1c316b9 (9 October 2026) on the apple/m4-stabilization branch. That matters for the very first command: git clone https://github.com/libcna/cna.git gives you the default branch (develop, the alpha.1 commit), so clone with -b apple/m4-stabilization, and use the apple/m4-stabilization branch of sharp-runtime too (below). Start with a single renderer and the default SDL3 platform/audio implementations; multi-renderer selection, the headless and terminal platforms, SDL-free (windowless) builds, non-default audio-device selection and compiled Effect Framework bytecode are opt-in topics covered by their dedicated guides. Only CNA_AUDIO_PLATFORM=SDL3 and ALSA enable the real XNA mixer/playback path. The experimental C API source layer is not a getting-started path. APIs may change before 1.0, so pin a commit when following these commands.
Writing in C#? This guide builds CNA’s C++ framework. For C#, pick the route that matches where you start: a new C# game starts from the installable dotnet new cna-game template (Tutorial 171); an existing XNA, FNA or MonoGame game keeps its source and gets a thin SDK project over CNA.XnaCompat (Migrating C# games to CNA.NET); and cna-dotnet-samples holds the original Microsoft samples as compatibility references. All three go through CNA.NET.
What you are getting
CNA is a C++ framework that mirrors the XNA 4.0 programming model. You write game code against Microsoft::Xna::Framework. The default build uses SDL3 for host and audio services, but CNA_PLATFORM, CNA_AUDIO_PLATFORM and graphics-renderer selection are independent, and a windowless build can contain no SDL at all (Building without SDL).
If you have XNA or MonoGame experience, the patterns will feel familiar. The main difference is C++ instead of C#.
Prerequisites
Linux
- CMake 3.20 or newer
- A C++23-capable compiler. CMake does not check the version, but the code uses
<format>, and CI builds with GCC 14 (g++-14on Ubuntu 24.04); do not assume GCC 12 ../sharp-runtimedirectory (sibling to the CNA repo), on itsapple/m4-stabilizationbranch - no external dependencies../easy-gland../meta-gldirectories - needed for all three GL-profile renderers (OPENGLES3,OPENGL33,WEBGL2), including the Linux defaultOPENGLES3;easy-gl(default branchdevelop) needsmeta-gl(apple/m4-stabilizationbranch) next to it- SDL3, SDL3_image, and SDL3_mixer are built from vendored submodules - no system SDL packages required. The X11/GL/Vulkan/ALSA/D-Bus development packages should be installed before the first configure so the vendored SDL gets real window and audio drivers (see Building for the list)
- FFmpeg development packages are optional: without them
Video/VideoPlayerexist but throwNotSupportedException
Windows
- CMake 3.20 or newer
- MSVC (the manual CI workflows use it on
windows-latest) or MinGW-w64 (cross-compiling from Linux); clang-cl is not exercised by any workflow ..\sharp-runtimedirectory (sibling to the CNA repo,apple/m4-stabilizationbranch)- SDL3 built from vendored submodules - no pre-built binaries or
CMAKE_PREFIX_PATHneeded - Windows-only renderers (2):
DIRECTX9,DIRECTX11. FFmpeg video is never built for Windows
macOS
- CMake 3.20 or newer and a current Xcode / AppleClang (CI uses
macos-26); deployment floor macOS 13.3 ../sharp-runtimesibling (apple/m4-stabilizationbranch); SDL3 from the vendored submodules;SDL_RENDERERis the default renderer (METALis the native Apple renderer, on macOS and iOS)- FFmpeg (for example from Homebrew) is optional
System requirements
| Component | Requirement |
|---|---|
| Compiler | A C++23 compiler. CI's workflows use GCC 14, AppleClang (macos-26), MSVC (windows-latest), mingw-w64 GCC and Emscripten 6.0.3; the CMake files enforce no compiler version |
| Build system | CMake 3.20+ (3.27+ for the default libcna.so layout on native Linux) |
| Git | Any recent version (for the five submodules) |
| OpenGL (OPENGLES3 renderer) | OpenGL ES 3.0 or OpenGL 3.0+ desktop; GPU driver installed (or Mesa's software driver) |
| Vulkan (VULKAN renderer) | Vulkan-capable GPU or driver; vulkan-headers / libvulkan-dev |
| Disk space | Several GB for a focused build (cna_demo_2d or one focused test target) including the configure-time SDL build. A default build of everything (tests and examples) produces hundreds of executables; CNA's own note measured about 104 MB per statically linked Debug executable before the shared-library layout became the Linux default |
| RAM | No requirement is published or measured; if a compile is killed for lack of memory, rebuild with fewer parallel jobs (--parallel 2) |
| Sibling repos | sharp-runtime (apple/m4-stabilization branch) and (for the GL renderers) easy-gl (develop) plus meta-gl (apple/m4-stabilization) cloned alongside cna/ |
Clone & initialise
mkdir cna-workspace && cd cna-workspace
git clone -b apple/m4-stabilization https://github.com/libcna/cna.git
git clone -b apple/m4-stabilization https://github.com/libcna/sharp-runtime.git
git clone https://github.com/libcna/easy-gl.git
git clone -b apple/m4-stabilization https://github.com/libcna/meta-gl.git
cd cna
git submodule update --init
This populates third_party/SDL, third_party/SDL_image, third_party/SDL_mixer, third_party/draco and vendor/googletest. After this step no system SDL packages are required. Leave off --recursive: CNA's CMake says the non-recursive form is the right one (recursion only pulls unused codec submodules). To pin the exact snapshot this guide documents: git checkout c1c316b9c7a846ce8002809c151fcd1af14942c9.
Quick start build (Linux - EasyGL renderer)
cmake -S . -B build -DCNA_GRAPHICS_RENDERER=OPENGLES3
cmake --build build --target cna_demo_2d --parallel
(cd build && ./cna_demo_2d)
The first configure builds SDL3, SDL3_image and SDL3_mixer from source into a persistent .sdl-prebuilt-* directory, so it takes a while (no timing was measured); later configures reuse it. Run the demo from the build directory, where the post-build step copied its Content/ folder. cmake --build build with no target builds every test and example and is much larger.
Quick start build (Linux - SDL_Renderer renderer)
cmake -S . -B build-sdlrenderer -DCNA_GRAPHICS_RENDERER=SDL_RENDERER
cmake --build build-sdlrenderer --target cna_demo_2d --parallel
This needs only sharp-runtime and the vendored SDL3 - no easy-gl/meta-gl checkouts. SDL_RENDERER is 2D only.
Verify the build
# Pure-CPU unit tests of one module - no display needed
cmake --build build --target CnaMathTests --parallel
./build/CnaMathTests
# Run the demo for six frames and exit (needs a display; on a headless box install
# xvfb and let xvfb-run provide one)
(cd build && xvfb-run -a ./cna_demo_2d --smoke 6)
CNA defines no hello-triangle-sdl target of its own (older text mentions one; the name is easy-gl's example, see Building); the demos are cna_demo_2d and, when the default renderer is one of the three EasyGL profiles, VULKAN, WEBGPU or FNA3D, cna_house3d_demo.
Minimal game skeleton
Here is the smallest possible CNA game - a window that clears to cornflower blue, the traditional XNA default clear colour, and draws one texture. (Replace assets/logo.png with a file that exists relative to where you run the program.)
#include <memory>
#include "Microsoft/Xna/Framework/Game.hpp"
#include "Microsoft/Xna/Framework/Color.hpp"
#include "Microsoft/Xna/Framework/GraphicsDeviceManager.hpp"
#include "Microsoft/Xna/Framework/Graphics/SpriteBatch.hpp"
#include "Microsoft/Xna/Framework/Graphics/Texture2D.hpp"
using namespace Microsoft::Xna::Framework;
using namespace Microsoft::Xna::Framework::Graphics;
class MyGame final : public Game {
public:
MyGame() : graphics_(this) {}
protected:
void LoadContent() override {
spriteBatch_ = std::make_unique<SpriteBatch>(getGraphicsDeviceProperty());
logo_ = std::make_unique<Texture2D>("assets/logo.png", getGraphicsDeviceProperty());
}
void Update(GameTime& gameTime) override {
// Update game state here.
Game::Update(gameTime); // updates registered game components
}
void Draw(const GameTime& gameTime) override {
auto& device = getGraphicsDeviceProperty();
device.Clear(Color::CornflowerBlue);
spriteBatch_->Begin();
spriteBatch_->Draw(*logo_, 100.0f, 80.0f);
spriteBatch_->End();
Game::Draw(gameTime); // draws components; Game presents the frame after Draw returns
}
private:
GraphicsDeviceManager graphics_;
std::unique_ptr<SpriteBatch> spriteBatch_;
std::unique_ptr<Texture2D> logo_;
};
int main() {
MyGame game;
game.Run();
return 0;
}
Testing your build
CNA's snapshot contains 813 C++ test source files and 11,380 static GoogleTest-family definitions covering the framework and configuration-specific modules (alpha.1: 568 and 8,263). There are also standalone pixel programs under examples/ that are not in those figures.
# Build and run one module's tests directly
cmake --build build --target CnaCoreTests --parallel
./build/CnaCoreTests
# Or the aggregate binary (all modules; a large build)
cmake --build build --target CnaTests --parallel
./build/CnaTests
# CTest: build everything first (a full `cmake --build build`), then filter
xvfb-run -a ctest --test-dir build -L input --output-on-failure
This runs what the configured build registered. Parameterized tests, platform/renderer selection, optional layers and dependencies change the executable and CTest inventory; run ctest --test-dir build -N to inspect it. Many CTest entries are separate executables that --target CnaTests does not build, and window-creating tests need a DISPLAY, so an unfiltered ctest after building only CnaTests is misleading. There are 22 focused test targets (CnaCoreTests, CnaMathTests, CnaAudioTests, ...), listed on Building. See Verification & Known Issues.
3D rendering example
The following example draws a coloured triangle using the EasyGL or Vulkan renderer. It demonstrates VertexBuffer, BasicEffect, and the DrawPrimitives call.
#include <memory>
#include "Microsoft/Xna/Framework/Game.hpp"
#include "Microsoft/Xna/Framework/GraphicsDeviceManager.hpp"
#include "Microsoft/Xna/Framework/Graphics/BasicEffect.hpp"
#include "Microsoft/Xna/Framework/Graphics/VertexBuffer.hpp"
#include "Microsoft/Xna/Framework/Graphics/VertexPositionColor.hpp"
using namespace Microsoft::Xna::Framework;
using namespace Microsoft::Xna::Framework::Graphics;
class TriangleGame final : public Game {
public:
TriangleGame() : graphics_(this) {
graphics_.setPreferredBackBufferWidthProperty(800);
graphics_.setPreferredBackBufferHeightProperty(600);
}
protected:
void LoadContent() override {
effect_ = std::make_unique<BasicEffect>(getGraphicsDeviceProperty());
effect_->VertexColorEnabled = true;
VertexPositionColor vertices[] = {
{ Vector3( 0.0f, 0.5f, 0.0f), Color::Red },
{ Vector3( 0.5f, -0.5f, 0.0f), Color::Green },
{ Vector3(-0.5f, -0.5f, 0.0f), Color::Blue },
};
vb_ = std::make_unique<VertexBuffer>(
getGraphicsDeviceProperty(),
VertexPositionColor::getVertexDeclarationStatic(),
3, BufferUsage::None);
vb_->SetData(vertices, 3);
}
void Update(GameTime& gameTime) override { Game::Update(gameTime); }
void Draw(const GameTime& gameTime) override {
auto& gd = getGraphicsDeviceProperty();
gd.Clear(Color::CornflowerBlue);
effect_->World = Matrix::getIdentityProperty();
effect_->View = Matrix::CreateLookAt(
Vector3(0, 0, 2), Vector3::Zero, Vector3::Up);
effect_->Projection = Matrix::CreatePerspectiveFieldOfView(
MathHelper::PiOver4, 800.0f / 600.0f, 0.1f, 100.0f);
gd.SetVertexBuffer(vb_.get());
for (auto& pass : effect_->getCurrentTechniqueProperty()->getPassesProperty()) {
pass.Apply();
gd.DrawPrimitives(PrimitiveType::TriangleList, 0, 1);
}
Game::Draw(gameTime); // Game presents the frame after Draw returns
}
private:
GraphicsDeviceManager graphics_;
std::unique_ptr<BasicEffect> effect_;
std::unique_ptr<VertexBuffer> vb_;
};
int main() { TriangleGame game; game.Run(); }
Build with the OPENGLES3, OPENGL33 or VULKAN renderer — 3D rendering is not available on SDL_RENDERER. See the 3D Rendering guide for the full API.
Beyond the default: other platforms at a glance
The commands above use the default SDL3 platform. A few other one-line starting points (each is explained on the linked page):
- X11 or Wayland (Linux): the default SDL3 platform picks the session’s video driver; force one with
SDL_VIDEODRIVER=x11orSDL_VIDEODRIVER=wayland— Tutorial 135 and Tutorial 136. - No SDL at all (windowless):
cmake -S . -B build -DCNA_ENABLE_SDL=OFF -DCNA_PLATFORM=HEADLESS -DCNA_AUDIO_PLATFORM=NULL -DCNA_GRAPHICS_RENDERER=HEADLESS— Tutorial 138. - Web:
emcmake cmake --preset web— Building and Platforms. - Windows from Linux: the
cmake/toolchains/mingw-w64.cmaketoolchain file — Building.
Next steps
- Full build instructions - all platforms and renderers
- Renderers - which renderer to choose and why
- Platform support - Linux, Windows, macOS, Android, iOS, web; the three platform implementations and three audio implementations
- Windows, X11 and Wayland - how SDL3 reaches each window system, and building without SDL
- XNA compatibility - what is implemented
- FAQ - common questions
External references
CNA targets the XNA 4.0 API surface. These references document the target API:
Deep dives on this topic
Long-form pages that explain the exact semantics, invariants and evidence behind this subject.
- A first CNA game, read line by line — A minimal CNA game and its extension to movement, edge clamping, sound and rectangle collision, explaining each framework contract and where CNA conveniences differ from portable XNA code.
Known issues in this area
Current defects, gaps and limitations at this snapshot that touch this subject.
- CNA-BUG-244: README.md's Usage Example does not compile (nonexistent Graphics/GraphicsDeviceManager.hpp include, unqualified CornflowerBlue) and presents every frame twice — The README's game skeleton includes a nonexistent header path, writes CornflowerBlue without Color::, and calls device.Present() in Draw although EndDraw() already presents, so the copied program fails to compile and, on