How CNA is structured
CNA is organised into four clear, strictly separated layers - from high-level game code down to hardware-specific rendering backends.
Layer overview
┌─────────────────────────────────────────────────────────────────┐
│ Game / Application Code │
│ Uses: Microsoft::Xna::Framework API │
│ Example: MyGame extends Game, calls SpriteBatch::Draw() │
└────────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ XNA-Compatible Public API Layer │
│ include/Microsoft/Xna/Framework/... │
│ Game · GraphicsDevice · SpriteBatch · Texture2D · Color │
│ Vector2/3/4 · Matrix · Rectangle · GameTime · Input │
└────────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ CNA Internal Abstraction Layer (sharp-runtime support) │
│ include/CNA/Internal/Backends/... │
│ IGraphicsBackend · ISpriteBatchBackend · ITextureBackend │
│ Factory: CreateGraphicsBackend(backend_type) │
└──────────┬───────────────┬──────────────┬───────────────────────┘
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌────────────────────────────┐
│ SDL_Renderer │ │ EasyGL │ │ bgfx | Vulkan (Adv.) │
│ Backend │ │ (OpenGL) │ │ Backend │
│ │ │ Backend │ │ │
└──────┬───────┘ └──────┬───────┘ └──────────────┬─────────────┘
│ │ │
└─────────────────┴──────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ SDL3 Platform Layer │
│ Windowing · Input · Audio (SDL3_mixer) · Image (SDL3_image) │
│ OpenGL context · Vulkan surface · bgfx platform init │
└─────────────────────────────────────────────────────────────────┘
Detailed architecture diagram
Layer descriptions
Game / Application Code
Your game logic. Subclasses Game, overrides LoadContent(), Update(), Draw(). Calls XNA-style APIs exclusively. Has zero knowledge of which backend is in use - this is the contract CNA enforces. Games written against this layer are portable across backends.
XNA-Compatible API Layer
Lives under include/Microsoft/Xna/Framework/. This is the surface that game code touches. Class names, namespaces, and method signatures mirror the original XNA 4.0 API as faithfully as practical in C++. No backend-specific code leaks into this layer. Includes: Game, GraphicsDevice, GraphicsDeviceManager, SpriteBatch, Texture2D, Color, all math types, input surfaces, GameTime, GameComponent, etc.
CNA Internal Layer & sharp-runtime Support
Lives under include/CNA/Internal/Backends/ and src/CNA/Internal/Backends/. Defines pure virtual backend interfaces: IGraphicsBackend, ISpriteBatchBackend, ITextureBackend. The factory function CreateGraphicsBackend() selects the correct implementation at compile time based on the CNA_GRAPHICS_BACKEND CMake variable. Also integrates the sharp-runtime utility layer for runtime support primitives.
Backend Implementations & SDL3
Concrete backend implementations live under src/CNA/Internal/Backends/{SdlRenderer,EasyGL,Vulkan,Bgfx}/. Each backend implements the interfaces from Layer 3 using the relevant low-level API. SDL3 (vendored at third_party/SDL) provides the platform abstraction: window management, input events, audio (SDL3_mixer), and image loading (SDL3_image).
SDL3 — important notes
CNA uses SDL3 (not SDL2). The SDL3 API is substantially different from SDL2 — migration between them is not backwards compatible. Key SDL3 specifics used in CNA:
- SDL3_mixer uses the
MIX_Mixermodel with track-based, per-mixer channels — unlike SDL2_mixer's global audio system. - SDL3_image is used for texture/image loading.
- All three libraries (
SDL,SDL_image,SDL_mixer) are vendored as Git submodules underthird_party/. No system SDL install is required by default; pass-DCNA_USE_SYSTEM_SDL=ONto use system packages instead.
SDL3 serves as the foundation for:
- Windowing —
SDL_Windowcreation and management - Event loop — cross-platform event pumping
- Input —
SDL_Keyboard,SDL_Mouse,SDL_Gamepad,SDL_Touch - Audio initialisation — SDL3_mixer setup
- OpenGL context creation — used by the EasyGL backend
- Vulkan surface creation — used by the VULKAN backend
- Android JNI Activity layer — Android platform integration
- Emscripten main loop — WebAssembly/browser event scheduling
Required sibling repositories
CNA depends on two sibling C++ libraries that must be cloned adjacent to the CNA repository. They are not vendored inside CNA — they live at predictable relative paths and are discovered by CMake automatically.
../sharp-runtime/sharp-runtime
sharp-runtime is a C++ library that provides .NET-style type primitives for C++23. It allows CNA code to closely mirror XNA C# code structure without a managed runtime. Clone it at ../sharp-runtime/ relative to the CNA repo root.
Integer aliases:
bytecs— alias foruint8_tshortcs— alias forint16_tintcs— alias forint32_tlongcs— alias forint64_tushortcs,uintcs,ulongcs— unsigned variants
Other primitives:
Single— alias forfloatString—std::stringwrapperIDisposable— virtualDispose()interfaceIEquatable<T>—Equalsmethod interfaceIComparable<T>— comparison interfaceEventHandler<TArgs>— multicast delegate-style callbacksList<T>,Dictionary<K,V>— collection wrappers- I/O primitives
../easy-gl/ (EASYGL backend only)easy-gl
easy-gl wraps OpenGL ES 3.0/3.2 for CNA's EASYGL rendering backend. Clone it at ../easy-gl/ relative to the CNA repo root. It is required only for the EASYGL backend — not needed for SDL_RENDERER, VULKAN, or BGFX.
- Abstracts GLSL shader compilation, linking, and uniform binding
- Manages VAOs, VBOs, FBOs, textures, and renderbuffers
- Provides the OpenGL context for both Linux desktop (OpenGL 3.0+ with ES emulation) and WebGL 2 (via Emscripten)
Which backend should I choose?
Select your rendering backend at configure time with -DCNA_GRAPHICS_BACKEND=<BACKEND>. Use the table below to pick the best fit for your target platform and requirements.
| Backend | Best for | 2D | 3D | WASM | Android | Status |
|---|---|---|---|---|---|---|
SDL_RENDERER |
Portability, simple 2D | ✓ | – | ✓ | ✓ | Stable |
EASYGL |
Linux, Web, full 3D | ✓ | ✓ | ✓ | – | Stable |
VULKAN |
High-performance 3D, Linux | ✓ | ✓ | – | – | Advanced |
BGFX |
Multi-API (Metal, DX) | ✓ | ✓ | – | – | Advanced |
Key architectural decisions
Interface/Implementation separation
Public API headers (include/Microsoft/...) never include backend headers. Backend code never bleeds into game-facing APIs. This boundary is enforced by directory and include structure.
Build-time backend selection
Only one backend is compiled per build. No runtime dispatch overhead. Backend is selected with -DCNA_GRAPHICS_BACKEND=<BACKEND>. This keeps the hot rendering path backend-specific and optimised.
Vendored dependencies
SDL3, SDL3_image, and SDL3_mixer are vendored as Git submodules. No system SDL packages needed. bgfx is fetched via CMake FetchContent. This ensures reproducible builds across environments.
XNA namespace mirroring
C++ namespaces mirror XNA's hierarchy: Microsoft::Xna::Framework, Microsoft::Xna::Framework::Graphics, Microsoft::Xna::Framework::Input, etc. Reduces conceptual migration cost from XNA/MonoGame experience.
sharp-runtime support layer
The sharp-runtime library provides utility and runtime support primitives that CNA's internals depend on. It builds cleanly on all supported platforms with no external dependencies of its own.
Key C++23 features used in CNA
CNA targets C++23 and uses several modern standard library features to mirror XNA's C# semantics cleanly without a managed runtime.
std::optional<T>— replaces XNA nullable types (Nullable<T>) for missing or unset values.std::span<T>— provides buffer views inGetData()/SetData()without copying data.std::string_view— zero-copy string parameters wherever a string is read but not owned.std::unique_ptr<T>/std::shared_ptr<T>— RAII resource management, replacing C#usingblocks andIDisposablepatterns.- Ranges and concepts (where available) — type-safe template constraints replacing unconstrained templates.
[[nodiscard]]attributes — applied toLoad<T>(),Begin(), and similar factory/guard calls to catch accidentally discarded return values.- Structured bindings — used for returning multiple values from internal helpers without heavyweight output-parameter patterns.
if constexpr— compile-time template branching inPackedVectorimplementations and format-dispatch helpers.- Inline variables — for static constants defined directly in header-only types without requiring a separate
.cppdefinition.
Directory structure
cna/
├── include/
│ ├── Microsoft/Xna/Framework/ ← Public XNA-compatible API
│ │ ├── Game.hpp
│ │ ├── Color.hpp
│ │ ├── GameTime.hpp
│ │ ├── Graphics/
│ │ │ ├── GraphicsDevice.hpp
│ │ │ ├── SpriteBatch.hpp
│ │ │ └── Texture2D.hpp
│ │ └── ...
│ └── CNA/Internal/Backends/ ← Backend interfaces
│ ├── IGraphicsBackend.hpp
│ ├── ISpriteBatchBackend.hpp
│ └── ITextureBackend.hpp
│
├── src/
│ ├── Microsoft/Xna/Framework/ ← Public API implementations
│ └── CNA/Internal/Backends/
│ ├── SdlRenderer/ ← SDL_Renderer backend
│ ├── EasyGL/ ← OpenGL backend
│ ├── Bgfx/ ← bgfx backend
│ └── Vulkan/ ← Vulkan backend (2D + 3D, second-most mature; BlendState and OcclusionQuery are the remaining named gaps)
│
├── third_party/
│ ├── SDL/ ← SDL3 submodule
│ ├── SDL_image/ ← SDL3_image submodule
│ └── SDL_mixer/ ← SDL3_mixer submodule
│
├── cmake/ ← CMake helpers
├── examples/ ← Demo/example programs
├── tests/ ← GoogleTest suite
└── CMakeLists.txt