Getting Started with CNA
CNA implements 227 of the 245 public types in the XNA 4.0 API, measured against FNA. Every type in Graphics, Audio, Input, Media, Storage and the math namespace is present. Two things are deliberately absent and will shape any port: the .xnb content pipeline (raw assets plus JSON descriptors are used instead) and compiled .fx shader bytecode (custom shaders must be hand-written GLSL/SPIR-V). Verification is real: 4,373 unit tests and 490 GPU pixel-readback tests across four backends, plus differential testing against a running FNA build. 63 of the 86 official XNA samples build on it. APIs are still evolving — good for research, demos and porting experiments, not yet for shipping a commercial game.
What you are getting
CNA is a C++ framework that mirrors the XNA 4.0 programming model. You write game code against the Microsoft::Xna::Framework C++ API. CNA handles windowing, input, audio, and rendering through SDL3 and a pluggable backend system.
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
- C++23-capable compiler: GCC 12+ or Clang 15+
../sharp-runtimedirectory (sibling to the CNA repo) - no external dependencies../easy-gldirectory - only needed for theEASYGLbackend- SDL3, SDL3_image, and SDL3_mixer are built from vendored submodules - no system packages required
Windows
- CMake 3.20 or newer
- One of: MSVC 2022 (v17.8+), clang-cl, or MinGW-w64
..\sharp-runtimedirectory (sibling to the CNA repo)- SDL3 built from vendored submodules - no pre-built binaries or
CMAKE_PREFIX_PATHneeded
System requirements
| Component | Requirement |
|---|---|
| Compiler | GCC 12+, Clang 15+, or MSVC 2022 v17.8+ (C++23 required) |
| Build system | CMake 3.20+ |
| Git | Any recent version (for submodules) |
| OpenGL (EASYGL backend) | OpenGL ES 3.0 or OpenGL 3.0+ desktop; GPU driver installed |
| Vulkan (VULKAN backend) | Vulkan-capable GPU; vulkan-headers / libvulkan-dev |
| Disk space | ~500 MB (source + build artifacts) |
| RAM | 4 GB minimum for compilation |
| Sibling repos | sharp-runtime and (for EASYGL) easy-gl cloned alongside cna/ |
Clone & initialise
git clone https://github.com/openeggbert/cna.git
cd cna
git submodule update --init --recursive
This populates third_party/SDL, third_party/SDL_image, and third_party/SDL_mixer. After this step no system SDL packages are required.
Quick start build (Linux - EasyGL backend)
git submodule update --init --recursive
cmake -S . -B build -DCNA_GRAPHICS_BACKEND=EASYGL
cmake --build build --target CNA CnaTests
ctest --test-dir build --output-on-failure
Quick start build (Linux - SDL_Renderer backend)
cmake -S . -B build-sdlrenderer -DCNA_GRAPHICS_BACKEND=SDL_RENDERER
cmake --build build-sdlrenderer --target CNA CnaTests
Verify the build
# Run the test suite
ctest --test-dir build --output-on-failure
# Run the hello-triangle verification demo
cmake --build build --target hello-triangle-sdl
Minimal game skeleton
Here is the smallest possible CNA game - a window that clears to cornflower blue, the traditional XNA default clear colour.
#include <memory>
#include "Microsoft/Xna/Framework/Game.hpp"
#include "Microsoft/Xna/Framework/Color.hpp"
#include "Microsoft/Xna/Framework/Graphics/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 {
(void)gameTime;
// Update game state here.
}
void Draw(const GameTime& gameTime) override {
(void)gameTime;
auto& device = getGraphicsDeviceProperty();
device.Clear(CornflowerBlue);
spriteBatch_->Begin();
spriteBatch_->Draw(*logo_, 100.0f, 80.0f);
spriteBatch_->End();
device.Present();
}
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 includes a GoogleTest-based test suite with 4,373 tests covering math types, geometry, curves, game loop semantics, and PackedVector precision.
# Run all tests
ctest --test-dir build --output-on-failure
# Or build and run directly
cmake --build build --target CnaTests
./build/CnaTests
All 4,373 unit tests should run, with 4,371 passing and 2 skipped (those two need a real accelerometer/gyroscope). The unit suite has no known failures. The separate GPU pixel-test suite (ctest) does have 5 known, tracked failures โ see Verification & Known Issues.
3D rendering example
The following example draws a coloured triangle using the EasyGL or Vulkan backend. It demonstrates VertexBuffer, BasicEffect, and the DrawPrimitives call.
#include "Microsoft/Xna/Framework/Game.hpp"
#include "Microsoft/Xna/Framework/Graphics/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_.setPreferredBackBufferWidth(800);
graphics_.setPreferredBackBufferHeight(600);
}
protected:
void LoadContent() override {
effect_ = std::make_unique<BasicEffect>(getGraphicsDeviceProperty());
effect_->setVertexColorEnabled(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::VertexDeclaration,
3, BufferUsage::None);
vb_->SetData(vertices, 3);
}
void Update(const GameTime&) override {}
void Draw(const GameTime&) override {
auto& gd = getGraphicsDeviceProperty();
gd.Clear(Color::CornflowerBlue);
effect_->setWorld(Matrix::Identity);
effect_->setView(Matrix::CreateLookAt(
Vector3(0, 0, 2), Vector3::Zero, Vector3::Up));
effect_->setProjection(Matrix::CreatePerspectiveFieldOfView(
MathHelper::PiOver4, 800.0f / 600.0f, 0.1f, 100.0f));
gd.setVertexBuffer(*vb_);
for (auto& pass : effect_->getCurrentTechnique().Passes) {
pass.Apply();
gd.DrawPrimitives(PrimitiveType::TriangleList, 0, 1);
}
gd.Present();
}
private:
GraphicsDeviceManager graphics_;
std::unique_ptr<BasicEffect> effect_;
std::unique_ptr<VertexBuffer> vb_;
};
int main() { TriangleGame game; game.Run(); }
Build with the EASYGL or VULKAN backend โ 3D rendering is not available on SDL_RENDERER. See the 3D Rendering guide for the full API.
Next steps
- Full build instructions - all platforms and backends
- Rendering backends - which backend to choose and why
- Platform support - Linux, Windows, Android, web
- XNA compatibility - what is implemented
- FAQ - common questions
External references
CNA targets the XNA 4.0 API surface. These references document the target API: