What CNA offers
CNA provides a structured, incremental C++ implementation of the XNA 4.0 programming model. Below is a detailed overview of what the current development snapshot provides, where it is still partial, and what remains planned.
This page describes CNA c1c316b9 (post 0.1.0-alpha.1). That is a development snapshot: branch apple/m4-stabilization as of 9 October 2026, 3,687 commits after the tag v0.1.0-alpha.1; CNA's product version string is still 0.1.0-alpha.1 and no newer tag exists. Compared with alpha.1 it has a curated set of 14 renderer identities in 12 implementation families, SDL3 as the one windowing platform (Windows, X11 and Wayland through SDL’s video drivers), an SDL-optional windowless build, ALSA audio with CNA's own mixer, a build-time content pipeline with the CNB format and an XNB writer, Framework.Design converters, Diagnostics and Inspector modules, a renderer capability-profile API, service-backed Gamer Services with an optional server, and an experimental C ABI at version 0.46.0 that the C# binding CNA.NET builds on. Presence is not the same as behavior: use the per-renderer capability and verification pages, and expect API changes before 1.0. Build it from commit c1c316b9 on branch apple/m4-stabilization (sharp-runtime and meta-gl on their branches of the same name); the repository's default branch still points at alpha.1. See the release notes.
XNA 4.0 API Implementation
CNA mirrors the Microsoft::Xna::Framework namespace hierarchy, translating XNA's C# API into idiomatic C++23.
Game Loop
The Game base class provides the full Initialize(), LoadContent(), Update(), Draw(), UnloadContent() lifecycle. GameTime, GameComponent, DrawableGameComponent, GameComponentCollection, GameWindow, and FrameworkDispatcher are all implemented.
XNA clock semantics: with a fixed time step the first Update runs with ElapsedGameTime equal to zero (a variable time step reports the measured elapsed time) and TotalGameTime advances after Update returns, as measured on the real XNA runtime (alpha.1 followed FNA here); the default presentation mode of GraphicsDeviceManager is Letterbox. The rule applies to the native Tick() path; the Emscripten main-loop callback keeps its own fixed-step accumulator. The Game constructor now registers the built-in XNB readers, and on Emscripten Game::Run() no longer unwinds the caller, so a stack-allocated Game is legal (application executables opt in to Asyncify).
SpriteBatch
Full 2D rendering abstraction. Supports all SpriteSortMode values, transform matrix, custom Effect, blend and sampler states. DrawString with SpriteFont included. Renderer-agnostic across all 12 identities that produce pixels; HEADLESS validates and traces the calls and STUB discards them.
XNA behavior kept: Begin has eight overloads (including const BlendState* variants), destinations stay sub-pixel, DrawString follows XNA's first-glyph rule, and SpriteBatch never restores blend, sampler, depth or rasterizer state after End. See the SpriteBatch guide.
Texture2D
Full texture lifecycle: load from disk, GetData/SetData for all mip levels, SaveAsPng/SaveAsJpeg, pixel cache. RenderTarget2D is a proper subclass with FBO + depth renderbuffer (EasyGL), off-screen Vulkan images, or the equivalent native target on the other 3D renderers; its IsContentLost flag and ContentLost event are real.
Known gap: public format support is renderer-qualified. SurfaceFormat has 27 members (20 XNA plus 7 CNAEXT); eight renderer families (SDL_GPU, VULKAN, WEBGPU, DIRECTX11, METAL, FNA3D, the EasyGL family and SOFTWARE) classify Texture2D formats themselves, and all of them but FNA3D classify render-target formats too, while the rest defer to the framework's SurfaceFormat::Color-only rule, under which any other format throws. The default GraphicsProfile is Reach, so HiDef-only formats also throw unless HiDef is requested. An internal native-format table does not by itself make that format constructible through the public API.
GraphicsDevice
Full render state API: BlendState, DepthStencilState, RasterizerState, SamplerState, scissor rect, viewport, render targets. DrawPrimitives, DrawIndexedPrimitives, DrawUserIndexedPrimitives (including the XNA generic-array overloads, spelled with std::vector), DrawInstancedPrimitives (hardware-accelerated on the renderers that support instancing), GetBackBufferData<Color> all implemented. GraphicsDevice exposes all 6 XNA events and all 5 Draw* families. State objects follow XNA's binding rule: mutating one after it was bound to a device throws InvalidOperationException.
Cross-renderer caveat: GraphicsCapability now has 19 members. The base SupportsCapability() still answers true for most of the original members, but only DIRECTX9 relies on that default wholesale; every other renderer overrides it, and GraphicsDevice itself derives six answers (compiled effects, float and half-float render targets, half-float filtering, compute, indirect draw) from hooks that default to false and ANDs multiple render targets with the profile limit. Treat a true from DIRECTX9 as "not contradicted", and use the capability-profile API below when you need detail.
Math Types
Complete math library: Vector2/3/4, Matrix, Quaternion, Color, Rectangle, Point, BoundingBox, BoundingSphere, BoundingFrustum, Plane, Ray, MathHelper, Curve, CurveKey. All operators, static factories, and interpolation methods. Matrix has all 30 statics including Decompose, billboards, shadow and reflection; Curve::Evaluate handles all 5 loop types; Color carries all 141 named constants. Vector3::Transform, matrix products and inversion, and BoundingSphere::CreateFromPoints reproduce XNA's 32-bit x87 accumulation width, so results can differ in the last bits from alpha.1; Vector3::Length and Distance still sum in float and can differ in the last bit from XNA. A known gap in the core Framework namespace is BoundingFrustum::Intersects(Ray): unlike real XNA 4.0, which computes the entry distance, CNA reports no hit for every ray whose origin is outside the frustum, 0 for an origin inside, and throws NotImplementedException only for an origin exactly on a frustum plane. It is not the only one: BoundingSphere::Contains(BoundingFrustum) and BoundingBox::Contains(BoundingFrustum) never answer Disjoint.
PackedVector Types
All 17 XNA 4.0 PackedVector types implemented in the Microsoft::Xna::Framework::Graphics::PackedVector namespace: Byte4, HalfSingle, HalfVector2/4, NormalizedByte2/4, NormalizedShort2/4, Rg32, Rgba1010102, Rgba64, Short2/4, Vector2/4. A correct IEEE 754 half-float codec is implemented from scratch, handling subnormals, infinities and NaN in both directions. Float-taking constructors now saturate, round ties-to-even and pack NaN as zero, checked against 68 measurements from the genuine XNA 4.0 runtime. The alpha.1 gap is closed: all 17 types now have ToString() (the packed value in hex), GetHashCode() and Equals.
Input
The Input namespace has zero stubs. Keyboard exposes all 160 Keys members; Mouse covers position, buttons and wheel; on the SDL3 platform — the platform of every windowed build — GamePad is a real SDL3 bridge with rumble, trigger rumble, LED control, hot-plug and per-device capability probing. TouchPanel genuinely detects all 10 XNA gesture types via a 474-line state machine with exponentially-smoothed flick velocity; optional mouse-to-touch emulation is off by default. A MIME-typed CNA::Input::Clipboard API is new.
Known bug: TouchCollection reports IsReadOnly == true, but its mutators mutate instead of throwing.
Devices & Sensors
The Microsoft::Devices namespace talks to real hardware. Accelerometer and Gyroscope use the SDL sensor path on Android, iOS and desktop with m/s²-to-g conversion and concurrency handling. VibrateController uses haptics and deliberately excludes gamepads. Compass and Motion are Android-only and report NotSupported elsewhere. Microsoft::Devices::Environment reports Device on mobile targets and Emulator elsewhere.
Build and evidence: the CNA_DEVICES CMake option defaults to OFF and gates only the CNA-specific CNA::Devices extensions (the Microsoft::Devices sensors are always built). The path-filtered devices-tests.yml workflow turns it on and runs the Microsoft::Devices and CNA::Devices suites named in its two test filters with UBSan on desktop Linux (the filters miss seven Microsoft::Devices suites and, by naming renamed dialog suites, four CNA::Devices ones); hardware-dependent checks self-skip when no physical sensor is present.
Audio
Implemented (SDL3_mixer, or CNA's own mixer on ALSA): SoundEffect, SoundEffectInstance (volume, pitch, pan, looping), DynamicSoundEffectInstance, MediaPlayer/Song. Multi-listener Apply3D accepts any positive listener count (the nearest listener decides); Microphone capture is real on SDL3 and ALSA.
XACT is a real parser and player, not a facade: a binary parser for XGS/XWB/XSB behind AudioEngine, SoundBank, WaveBank and Cue, using FACT volume and RPC behavior. The default output path is SDL3_mixer; audio-platform selection is independent (see Platform and audio implementations).
Known gaps: only the first PlayWave per track is honoured; XMA and WMA wave-bank entries are not decoded — they log a diagnostic and yield a null sound rather than throwing, so the effect is silently missing; reverb send is a deliberate no-op; .m4a/.aac are unplayable; and neither mixer decodes Opus (the vendored SDL3_mixer is built with Opus off, and CNA's own ALSA mixer refuses it).
Content System & .xnb Pipeline
Extensible ContentManager with the ContentTypeReader<T> pattern. Load<T>() returns the asset by value and resolves a name through a ladder: .xnb, then .cnb (CNA's own binary container), then loose files — Texture2D (PNG/BMP/etc.), SoundEffect (WAV), Song, Video, plus SpriteFont, Model and Effect from CNA's .cnj JSON descriptor format.
A real .xnb loader exists. It offers 61 built-in readers (60 without native 128-bit integer support), including typed external references and a renderer-qualified compiled-effect reader. LZX decompression, shared resources and malformed-input checks are implemented. The Game constructor registers the built-ins; a stand-alone ContentManager (tools, tests) still starts with an empty registry and needs CNA::Internal::Xnb::RegisterAllBuiltInXnbReaders().
CNA now also writes content. The build-time tool cna-content compiles source assets to .cnb or .xnb (see Content Pipeline & Formats below); it is not linked into a shipped game. ResourceContentManager is implemented as well (XNA's constructor and its protected OpenStream).
Known boundaries: CNA cannot discover arbitrary game types through .NET reflection, but a game can register an explicit ContentTypeReader<T> factory or declare a field list once with ReflectiveTypeReaderBuilder<T>. The real EffectReader requires an active renderer with CompiledEffects, and closed generic readers beyond the pre-registered ones still need explicit registration. Public Load<T> still uses the file ladder, so only derived classes reach the resource-stream path.
Networking
The Net namespace provides ENet networking and SystemLink/LAN discovery. CNA's own service supports PlayerMatch and Ranked discovery, invitations, authenticated WSS relay, restart recovery and host migration. Online sessions use relay from the outset; there is no direct Internet peer negotiation or STUN/ICE hole punching. Optional Opus voice has capture, encode, transport, decode and playback implementations, with one local talker per machine. Loopback and synthetic voice paths are tested; real WAN/NAT, microphone/speaker and cross-platform qualification remain separate.
GamerServices & Avatar
CNA implements XNA-style GamerServices APIs backed by local profiles and a real CNA HTTPS/SQLite service: account sign-in, profiles, friends, presence, messages, achievements and leaderboards, player reviews, parties and per-account avatars. Local progress uses checked, locked JSON replacement and reloads after application restart; it does not promise power-loss durability. Achievements and eligible scores are authenticated title-managed claims, with server-derived timestamps, aggregation and ranking; ranked agreement does not prove gameplay or implement TrueSkill.
Guide and avatars: Guide provides custom in-game sign-in, keyboard/message boxes, social panes, invitations, avatar editing and notifications. Standard AvatarRenderer::Draw() loads CNA models and issues SkinnedEffect draws using a 71-bone skeleton. Avatar descriptions, catalogs, assets and animations are CNA-owned formats and art; matching XNA names or the 1021-byte buffer does not make them compatible with Xbox avatar data.
Boundaries: this service does not interoperate with Xbox Live/Microsoft identity, partner tokens, marketplace commerce or Xbox avatar assets. Guide is custom game UI, and marketplace screens do not provide real payment. Browser online transport, service-computed skill, online guests and online pre-join QoS remain partial or unsupported. Software tests establish working paths without certifying every original Xbox behavior or physical device.
Framework.Design converters
Microsoft::Xna::Framework::Design is implemented as the opt-in CNA::Design module: MathTypeConverter (derived from System::ComponentModel::ExpandableObjectConverter) plus twelve concrete converters for Point, Rectangle, Vector2/3/4, Quaternion, Matrix, Color, BoundingBox, BoundingSphere, Plane and Ray. Property lists follow XNA's order, string parsing is culture-aware for the types that accept strings, and malformed text throws ArgumentException. Registered with EnsureFrameworkDesignConvertersRegistered(); 25 test definitions.
Boundary: it needs the sharp-runtime ComponentModel implementation and is deliberately outside the CNA umbrella target — link CNA::Design explicitly. The Windows Forms property-grid side has no C++ counterpart. Alpha.1 documented this namespace as the one XNA area CNA did not cover. See Framework.Design converters.
Windows Phone shell & push (Microsoft::Phone)
The new phone module provides Microsoft::Phone::Shell (PhoneApplicationService with Launching, Activated, Deactivated and Closing events driven from Game's platform lifecycle after an AttachEXT call) and Microsoft::Phone::Notification (HttpNotificationChannel, which is its own local HTTP listener thread, RawPushNotificationMessage and ToastPushNotificationMessage). It is the Windows Phone 7 lifecycle and push API rather than XNA 4.0, is linked only when a game names cna_phone, and is outside the C API scope. 18 test definitions.
XNA API coverage census
All 331 documented XNA 4.0 runtime types and 3,627 documented members (253 constructors, 1,518 methods, 1,040 properties, 753 fields, 63 events) have a matching C++ declaration in this snapshot, measured by tools/audit_xna_runtime_surface.py, a Clang-based census against Microsoft's own XML documentation and DLL metadata. The XNA Content Pipeline assembly is a separate census (128 of 128 types, 705 of 705 members, 10 importers, 12 processors).
Read it correctly: the report itself says it measures representation of the documented surface only. Behavior, exception detail, renderer output and online-service availability are separate measures, verified only for the subsets listed on Verification & Known Issues. The census is a manual script, not a CTest or CI gate; only the Input namespace has a compile-time signature freeze. See XNA compatibility.
3D Rendering Pipeline
Twelve of the 14 renderer identities report a 3D pipeline (HEADLESS only validates the calls). SDL_RENDERER is deliberately 2D-only and refuses 3D calls deterministically rather than silently doing nothing, and STUB is a no-op that reports no 3D either. The desktop and ES GL profiles, Vulkan and the Direct3D 9 and 11 renderers are declared Production by CNA, Metal and SDL_GPU Supported; SDL_GPU (declared Supported) and the CPU Software rasterizer (Experimental) are the fast-moving 3D paths.
3D Rendering
Vertex layouts, depth test/write, blend, cull mode and matrix upload across every renderer that reports ThreeD; the stock effects carry per-renderer shader sets for EasyGL, Vulkan, SDL_GPU, WebGPU and the Direct3D renderers. Tested with the House 3D demo — procedural walls, roof, windows, door, floor with player movement and gravity. Also runs via WebGL 2 in the browser. See the 3D rendering guide.
Effects System
All five XNA stock effects — BasicEffect, AlphaTestEffect, DualTextureEffect, EnvironmentMapEffect, SkinnedEffect — and a public SpriteEffect (XNA keeps its sprite effect internal), implemented natively in C++ rather than translated from bytecode. All IEffectMatrices, IEffectFog and IEffectLights interfaces, and EffectParameter with all Get/Set overloads.
Compiled effects: already-compiled XNA/FNA Direct3D 9 Effect Framework binaries (.fxb, or an XNB Effect through EffectReader) run on 11 of the 14 identities in 9 families: FNA3D always, and 10 identities behind default-OFF build options (one option enables all three EasyGL identities; Vulkan, WebGPU, Software, Direct3D 9, Direct3D 11, SDL_GPU and Metal each have their own). A default configure therefore reports CompiledEffects true on FNA3D only. Effect(GraphicsDevice&, bytes) is a real constructor, not a throwing stub; MGFX and DXBC are refused. CNA does not compile HLSL .fx at run time. At build time cna-content compiles .fx through an external legacy fxc (not yet checked against a genuine Microsoft fxc). CNAEXT ShaderEffect remains the renderer-oriented source path (GLSL, SPIR-V, WGSL or HLSL, depending on the renderer). See Effects and Tutorial 128.
PBR & Skeletal Animation
Beyond the XNA stock set, CNA ships non-XNA effects and types: PbrEffect and SkinnedPbrEffect for physically based rendering, and a full skeletal-animation stack (SkinningData on Model::Tag with AnimationPlayer, AnimationClipEXT, BoneTrackEXT and KeyframeEXT, alongside the separate Avatar-oriented SkinnedModelEXT). MorphTargetEXT adds blend shapes, alongside tangent and skinned vertex types and sRGB/BC7 surface formats. Shadow-receiver and image-based-light state (IShadowReceiverEXT, ImageBasedLightEXT, punctual and area lights) sit on the effects.
Where it draws: full metallic-roughness PBR with specular-texture sampling on seven families (9 identities: DIRECTX9/11, the three EasyGL identities, SDL_GPU, VULKAN, WEBGPU, METAL); a reduced CPU cross-check on SOFTWARE; FNA3D refuses PBR draws by name. Direct glTF loads assign PbrEffect/SkinnedPbrEffect per mesh part. Shadow sampling and image-based lighting are honoured only by the EasyGL identities, VULKAN, SDL_GPU and WEBGPU; a renderer without them accepts the state and ignores it. The shadow map and environment textures themselves are rendered by the application.
Build boundary: these XNA-shaped extensions are always compiled. The CNA_CNAEXT CMake option defaults to OFF and gates only the separate CNA::Graphics module (next card). No CI workflow enables CNA_CNAEXT; verify the exact extension and renderer configuration you ship.
CNAEXT extensions (CNA::Graphics)
With -DCNA_CNAEXT=ON CNA adds a small module of graphics helpers that XNA never had: three retro post-processing effects — CRTEffect (scanlines, curvature, vignette, display masks), DepthEffect (colour-depth and palette reduction with optional ordered dithering) and AsciiPostProcessEffect (an ASCII-art rendering of any finished frame) — plus DebugDraw for debug lines, boxes, spheres and frusta, and portable shader packages (ShaderCodeEXT, ShaderPackageEXT) for ShaderEffect.
Requirements: the CRT and colour-depth effects need a renderer that executes custom ShaderEffect source from their package (the EasyGL family, VULKAN, WEBGPU; HLSL variants exist but are unproven on Direct3D); the ASCII effect and DebugDraw run on any renderer with readback or BasicEffect. No CI workflow builds the module. See CNAEXT extensions and Tutorial 116.
Vertex & Index Buffers
VertexBuffer, DynamicVertexBuffer, IndexBuffer (16/32-bit), DynamicIndexBuffer. All four VertexPosition* types: VertexPositionColor, VertexPositionTexture, VertexPositionColorTexture, VertexPositionNormalTexture. Under the default Reach profile 32-bit indices throw unless HiDef is requested. A buffer that is currently bound cannot be rewritten (SetData throws) unless you use SetDataOptions::Discard or NoOverwrite on a dynamic buffer.
Model System
Model::Draw with bone transforms, IEffectMatrices binding, and a full mesh/part rendering pipeline. Models load from .cnj descriptors, from .gltf/.glb directly, or from .xnb and .cnb via ContentManager. Content routes are now plural: direct runtime glTF, .cnb/.xnb compiled by cna-content, and gltf_to_cnj, the offline glTF 2.0 → .cnj Model/AnimationClip converter that is built unconditionally. Runtime glTF imports every mesh group into one Model, keeps skins and rigid node animation, and enforces extensionsRequired. See Model loading.
SpriteFont
Full glyph data model, MeasureString, and SpriteBatch::DrawString with three overloads. Fonts load from .cnj descriptors naming a pre-rendered glyph atlas, from .xnb/.cnb, or are built from a .spritefont description by cna-content (FreeType, CNA_ENABLE_FONT_PIPELINE, default AUTO).
Media & Video Playback
Real: MediaPlayer genuinely plays songs through the selected mixer (SDL3_mixer, or CNA's own mixer on ALSA) and exposes a real FFT spectrum and waveform via GetVisualizationData, captured from the live post-mix callback. VideoPlayer genuinely decodes video through FFmpeg (real libavcodec/libavformat/libswresample with PTS-driven pacing) when the backend is built, plays the video's own audio track, and supports multi-track selection. The MediaLibrary catalogue is also real: it resolves your actual Music and Pictures folders, scans them recursively, parses ID3v2, Vorbis-comment, Opus and FLAC tags, probes durations, discovers album art, reads playlists and saves pictures to disk.
Known gaps: FFmpeg is now optional: CNA_ENABLE_VIDEO is AUTO (default, enabled only when libavcodec, libavformat, libavutil and libswresample are found), ON (requires them) or OFF. The Video and VideoPlayer types and the XNB VideoReader exist in every build; without the backend a file-backed Video or VideoPlayer::Play throws NotSupportedException at run time instead of failing to link. FFmpeg is never built on Windows, Emscripten, Android or iOS, so real video playback is native Linux/macOS only, and the optional split was not run on macOS by its author. Unrecognised video pixel formats render magenta rather than raising an error. See Video playback.
Storage
All 3 Storage types use real std::filesystem IO, a hand-written glob matcher, and <root>/<displayName>/Player{N} namespacing. The async Begin*/End* pairs execute synchronously. New: container names and every container-relative path are contained — absolute paths, .. escapes and symlink escapes throw std::invalid_argument. The root comes from XDG_DATA_HOME, %LOCALAPPDATA% or the platform equivalent (not SDL_GetPrefPath) under an app name that is the literal game unless the program calls StorageDevice::SetAppNameEXT.
Caveats: the Storage module has one test source with 14 static definitions (ten on path containment and container deletion, four on an exception round trip) and no read/write round-trip test, so applications should verify their target filesystem behavior. On the web CNA mounts no persistent file system: anything written is lost on page reload unless you persist through your own browser storage.
Multisample Anti-Aliasing
Enabled via GraphicsDeviceManager: set PreferMultiSampling = true and MultiSampleCount on PresentationParameters before device creation. Support is decided per device: MSAA is reported (probed where the API requires it) on the EasyGL identities, Vulkan, WebGPU, SDL_GPU, Direct3D 11, Metal (from the device’s sample counts) and FNA3D; Software implements 4x MSAA on the CPU. It is not available on the 2D-only SDL_RENDERER.
OcclusionQuery
Hardware occlusion queries are reported by the EasyGL identities, Vulkan, WebGPU (counts reported as imprecise on a Metal adapter), Direct3D 9/11 and Metal, by FNA3D where its driver has them, by SOFTWARE as a CPU implementation, and by HEADLESS without rasterizing. They are not available on SDL_GPU (SDL’s GPU API has no occlusion query) or the 2D-only SDL_RENDERER. Under the default Reach profile an OcclusionQuery throws unless HiDef is requested.
Context-Loss Recovery
GraphicsDevice exposes DeviceLost, DeviceResetting and DeviceReset events following the XNA pattern. EasyGL implements real GL context-loss recovery (relevant on Android and some desktop compositors), and DIRECTX9 implements full device-lost and D3DPOOL_DEFAULT recovery. Reading the sources, DIRECTX11 and WEBGPU also raise the reset events from their recovery paths, while VULKAN reports a lost device (DeviceLost, status Lost) but deliberately does not attempt a reset. Recovery was not exercised on real hardware for this page; DeviceLostException, DeviceNotResetException and NoSuitableGraphicsDeviceException now derive from System::Exception rather than std::runtime_error.
Renderer capability profiles
GraphicsCapability grew from 14 to 19 members (adding FloatRenderTargets, HalfFloatRenderTargets, HalfFloatTextureLinearFiltering, ComputeShaders and IndirectDraw). Beyond that, GraphicsDevice::GetRendererCapabilityProfileEXT() returns a RendererCapabilityProfile: 32 RendererFeature ids with a four-state answer (Unknown, Unsupported, Supported, Restricted), 22 RendererLimit values (texture size, vertex streams, compute work-group limits, buffer sizes, attachments, alignments, timestamp period), per-format usage for all 27 SurfaceFormat members, and a readable English report from GetRendererCapabilityReportEXT(). It is built lazily, cached, and exposed through five C ABI functions. Branch on capabilities, never on renderer names. See Tutorial 133 and Tutorial 101.
Modern graphics features
Modern renderers report modern capabilities: base-instance drawing, float and half-float render targets, and shadow-map and image-based-lighting sampling on VULKAN, SDL_GPU, WEBGPU and the EasyGL family (some device-conditional). Compute and indirect-draw support is reported by VULKAN, SDL_GPU, WEBGPU, DIRECTX11 and the EasyGL family on ES 3.1 / GL 4.3 or newer contexts (never on WebGL). These are capability answers from the renderers’ internal implementations; there is no public compute-shader or storage-buffer class for game code.
GraphicsProfile: Reach by default
The default GraphicsProfile is Reach, and this snapshot enforces its ceilings on every renderer (alpha.1 enforced them on DIRECTX9 only). Under Reach, multiple render targets, OcclusionQuery, 32-bit indices, float and HDR targets, packed formats and large cubes throw or degrade unless you request HiDef through GraphicsDeviceManager (or CNA::SetProjectGraphicsProfileEXT). Many alpha.1-era tutorials omit this, so a snippet that renders MRT or 32-bit-indexed terrain may throw as written.
Content Pipeline & Formats
CNA still loads content at run time through ContentManager; what is new in this snapshot is a build-time pipeline that produces it. The pipeline lives outside the runtime link closure — only the cna-content compiler links it — so FreeType and FFmpeg never enter a shipped game. Alpha.1 could only read .xnb; this snapshot also writes .xnb and a new native format, .cnb.
The cna-content build tool
cna-content build <source | directory | .contentproj> -o <out> [--format cnb|xnb] runs Importer → Processor → Writer over a file or a whole tree, preserving relative logical names. Options include --workers 1..64, --config for a strict .cna-content.json, --explain and --xna-compatible (three refusals become XNA-style warnings). Builds are incremental through a manifest and a lock file, and CMake gets cna_add_content(TARGET ... SOURCE_DIR ... OUTPUT_DIR ... FORMAT ...). The tool is built by default; only its optional inputs are switchable: FreeType (CNA_ENABLE_FONT_PIPELINE), FFmpeg (CNA_ENABLE_MEDIA_PIPELINE), zstd (CNA_CNB_ZSTD) and an external fxc. See Content Pipeline and Command-line tools.
CNB — CNA's compiled container
A deterministic, little-endian, one-asset-per-file container that shares no code with XNB and has no reader tables or platform byte: a 64-byte header, 48-byte table-of-contents entries and a CRC-32C on the header, the table and every chunk (hardware CRC where available). Schemas exist for Texture2D/3D/Cube, SpriteFont, Model, AnimationClip, Curve, SoundEffect, Song and Video; game-defined types register through RegisterCnbLoaderEXT<T>. Zstandard chunk compression exists in the library but no shipped tool enables it, and memory mapping was measured and rejected. cna_tool_cnb_info validates and prints a file. See CNB format.
XNB writing
cna-content --format xnb writes XNB (version 4 or 5, Reach or HiDef profile, no compression, LZX or LZ4, XNA 4.0 or portable reader names, several target platforms) with a real LZX encoder. Writers exist for Texture2D/3D/Cube, SpriteFont, SoundEffect, Song, Video, vertex and index buffers, the stock effects, compiled Effect, EffectMaterial, Model and common collections. CNA reports golden-file byte identity with XNA 4.0 for simple types and that its LZX output loads in a genuine XNA runtime under Wine; treat that as developer evidence, not a CI result. A glTF → XNB Model drops skeleton, animation clips, morph targets and lights with warnings and downgrades PBR to stock effects.
XNA Content Pipeline API
Microsoft::Xna::Framework::Content::Pipeline is represented in 107 headers: 10 importers (Texture, Wav, Mp3, Wma, Wmv, Effect, FontDescription, X, Fbx, Xml), 12 processors, IntermediateSerializer, ContentCompiler/ContentWriter, and the BuildContent and ContentProject tasks (.contentproj is read without MSBuild). It is a view over CNA's canonical pipeline engine, not a second engine: 128 of 128 types and 705 of 705 members are represented. The custom-component API is explicitly experimental, the .wma/.wmv importers could not be measured against the genuine runtime, and there is no Content Pipeline C ABI.
Effects at build time
A compiled .fxb needs no compiler and becomes an XNB Effect. HLSL .fx source is compiled by an external, legacy fxc at profile fx_2_0 (chosen with --fx-compiler, the CNA_FXC environment variable, a CMake-baked path or fxc on PATH, optionally under wine) and only with --format xnb: CNB has no Effect schema. CNA embeds no HLSL compiler, MGFX and DXBC remain unsupported, and the .fx route has not been checked against a genuine Microsoft fxc.
Converters & inspection tools
Alongside cna-content the tree builds cna_tool_gltf_to_cnj (offline glTF 2.0 → .cnj), cna_tool_gltf_to_cnb, cna_tool_cnj_to_cnb, cna_tool_source_to_cnb (image, sound, song, video and cube-map sources), cna_tool_cnb_info and an XNB interop-fixture tool. Alpha.1 shipped only gltf_to_cnj. See Command-line tools.
| Source | Output as .cnb | Output as .xnb |
|---|---|---|
.png .jpg .bmp .tga .gif .psd .hdr and similar | Texture2D (Rgba8 only; a DXT request keeps Rgba8 and warns) | Texture2D |
.wav (and .mp3/.wma decoded, with the media pipeline) | SoundEffect | SoundEffect |
.mp3 .ogg .flac .opus .aac .wma as a Song | Song: metadata plus a streaming reference; the media file is deployed beside it | Song |
.mp4 .ogv .webm .mkv .avi .mov | Video: metadata plus a reference | Video |
.gltf .glb | Model (one primary; generateChildAssets for multi-group files) | Model, with skeleton, animation, morphs and lights dropped and warned |
.cnj | Texture, SpriteFont, SoundEffect, Curve, AnimationClip, Model | The same, where the XNB writer supports the type |
.spritefont (FreeType) | SpriteFont | SpriteFont |
.fxb / .fx | Not supported (no Effect schema) | Effect (.fx only with an external fxc) |
.x .fbx | Model through the XNA ModelProcessor | Model |
.xml (XNA intermediate) | Refused | Any type registered with the XNA ContentCompiler |
.xnb | Transcoded to native CNB | Source only, not rewritten |
Platform and audio implementations
SDL3 is the windowing platform, while this snapshot independently selects host integration and an audio device, and can be configured without SDL for windowless builds. The three audio values are not feature-equivalent: high-level XNA playback exists for SDL3 (SDL3_mixer) and ALSA (CNA's own mixer) only.
Windowing & Events
The default SDL3 platform manages windows and events on every operating system. The IPlatform layer has three implementations selected with CNA_PLATFORM: SDL3 (default, the only one with windows), HEADLESS and POSIX TERMINAL; any other name is refused at configure time. A 32-flag PlatformCapabilities contract states exactly what each implementation can do, and a missing capability refuses deterministically instead of half-working. See Platform Support.
Selectable audio integration
CNA_AUDIO_PLATFORM independently selects SDL3, NULL or ALSA (OPENAL and WASAPI are reserved and refused). SDL3 and ALSA define SOUND_ENABLED and provide a mixer (SDL3_mixer or CNA's own) behind SoundEffect, MediaPlayer, XACT and decoding. Null compiles the low-level device contract without providing XNA playback, and only Linux has an SDL-free audio path with playback (ALSA), so SDL-free builds elsewhere use Null audio.
SDL3_image Loading
SDL3_image is used for loading PNG, JPG, and other texture formats into Texture2D. Vendored alongside SDL3.
Windows, X11 and Wayland through SDL3
SDL3 is CNA’s one windowing implementation. Windows, X11 and Wayland are reached through SDL’s windows, x11 and wayland video drivers (SDL_VIDEODRIVER picks one), and the native handles renderers need — an HWND, an X11 Display* and window, a Wayland wl_display* and wl_surface* — reach them through NativeWindowHandle. CI runs the SDL3 window suite on SDL’s x11 driver under Xvfb and its wayland driver under a headless Weston, asserting the handle each hands a renderer; a native-MSVC Windows job runs on manual dispatch. See Windows, X11 and Wayland.
SDL is optional (CNA_ENABLE_SDL)
CNA_ENABLE_SDL=AUTO|ON|OFF decides whether the vendored SDL3 sub-build runs at all. With OFF, any selection that needs SDL is refused with a named reason: the SDL3 platform, SDL3 audio, and the SDL_RENDERER, SDL_GPU and FNA3D renderers. Because SDL3 is the only windowing implementation, an SDL-free build is windowless — HEADLESS or TERMINAL with a CPU or no-pixel renderer — for servers, test runners and terminal games; CI builds one with ALSA audio and checks that nothing links SDL.
ALSA audio & CNA's own mixer
CNA_AUDIO_PLATFORM=ALSA loads libasound.so.2 at run time (never linked); the device comes from CNA_AUDIO_DEVICE (null is silent) and capture from CNA_AUDIO_RECORDING_DEVICE. CNA's own mixer provides a linear-interpolation resampler, per-track and post-mix callbacks, loops and streams, and decodes WAV (PCM, float, MS-ADPCM, IMA-ADPCM), Ogg Vorbis, MP3 and FLAC; it does not decode Opus, WMA/xWMA or XMA. CI runs the mixer, ALSA device, capture and XACT-category suites on ALSA's null device. PipeWire and PulseAudio desktops are reached through ALSA's default device plug-ins.
Terminal & Headless platforms
The POSIX TERMINAL platform (termios/poll, Kitty keyboard-protocol probe, one window) accepts only the CPU renderers SOFTWARE, HEADLESS and STUB, and only SOFTWARE actually displays: it hands each finished frame to the terminal's surface presenter. HEADLESS is always compiled: one window object, no services, every capability false. CI covers both, including a pseudo-TTY test of the 2D demo.
| Platform | Uses SDL? | GL / Vulkan services | Notable host services | Automatic CI evidence |
|---|---|---|---|---|
SDL3 (default) | SDL3 | GL context; Vulkan surface where SDL reports support | Clipboard, drag-and-drop, gamepad, sensors, dialogs, tray, camera (conditional); the only windowing implementation, on every operating system (Windows, X11 and Wayland through SDL’s video drivers) | Many workflows: Linux (Xvfb and headless Weston), macOS, iOS, Emscripten; Windows on manual dispatch |
HEADLESS | No | None | One window object, all capabilities false; always compiled | Yes |
TERMINAL | No | None (CPU frames only) | POSIX only; surface presenter, Kitty keyboard probe | Unit tests plus a pseudo-TTY demo test |
| Audio value | OS | SOUND_ENABLED / mixer | Capture | XNA playback |
|---|---|---|---|---|
SDL3 (default) | Every target CNA supports | Yes / SDL3_mixer | Yes | SoundEffect, instances, DynamicSoundEffectInstance, MediaPlayer, XACT, 3D audio |
NULL | All | No / none | No | Deterministic silent transport; classes compile without playback |
ALSA | Linux only | Yes / CNA's own mixer | Yes | The same XNA surface, decoding WAV, Vorbis, MP3 and FLAC |
Pluggable Renderers
CNA separates game-facing APIs from 14 public renderer identities implemented by 12 families (the three GL profiles share one EasyGL implementation). CNA_GRAPHICS_RENDERER selects the compact single-renderer default; CNA_GRAPHICS_RENDERERS can link a compatible set and GraphicsRendererSelection resolves one before the first device. A name outside the 14 is a configure-time error, never a silent fallback. Game code continues to use the same XNA-shaped interfaces. See the renderer reference.
14 renderer identities is not 14 equally complete renderers. They range from paths with real-XNA pixel evidence through deliberately 2D-only families, to renderers that never present to a window at all (HEADLESS, STUB) and experimental implementations. The Maturity column below is CNA's own declaration in GraphicsBackendMaturity (Production, Supported or Experimental), not a measurement, and capability reporting still varies, so a true is not universal proof. Single-renderer builds remain the smallest verification slices; a compatible multi-renderer build can exercise several linked families at runtime, but dependencies, platform gates and test registration still vary.
| Renderer | Maturity | Scope | Notes |
|---|---|---|---|
OPENGLES3 | Production (declared) | 2D + 3D | Linux-default EasyGL profile: MRT, occlusion, Texture3D and instancing; MSAA and anisotropy by runtime probe. |
OPENGL33 | Production (declared) | 2D + 3D | Desktop OpenGL 3.3 core through EasyGL, with native wireframe; on macOS the core-profile context, where the Apple campaign ran EasyGL’s tests. |
WEBGL2 | Supported (declared) | 2D + 3D | Emscripten-only default of the web; in the WEBGL2 + WEBGPU bundle that CI builds and link-checks. |
VULKAN | Production (declared) | 2D + 3D | Most complete modern surface: SPIR-V pipeline, MSAA resolve, stencil, compute, indirect draw, GPU timers, float targets; compiled effects are opt-in. |
DIRECTX9 | Production (declared) | 2D + 3D | Windows-only; target of the real-XNA oracle corpus (recorded 0-diff at tolerance 0 under Wine + DXVK; not in CI). Still the one renderer without a SupportsCapability override. |
DIRECTX11 | Production (declared) | 2D + 3D | Windows-only; state-object caches, MRT, float targets and runtime shader compilation; renderer-internal compute and indirect draw; Windows lanes are manual (a recorded native-MSVC run passed its 300 CTest entries). |
SDL_GPU | Supported (declared) | 2D + 3D | SPIR-V on SDL3's GPU API with compute, indirect draw and float, wide and packed texture formats; SDL’s Direct3D 12 and Metal drivers through SDL_shadercross where enabled. |
METAL | Supported (declared) | 2D + 3D | macOS and iOS; MSAA, MRT, occlusion queries, instancing and multi-stream input, float, packed and DXT formats, SpriteBatch MSL custom effects and opt-in compiled XNA effects; no compute. Qualified on a physical Mac mini M4 in CNA’s Apple campaign (see macOS); CI runs it on GitHub’s virtual Macs. |
FNA3D | Experimental (declared) | 2D + 3D | Adapter over FNA3D; compiled effects always on, no ShaderEffect source; refuses PBR draws; its CTest gates only that scenes render. |
WEBGPU | Experimental (declared) | 2D + 3D | Native wgpu-native and a browser route; MRT, occlusion, instancing, compute and half-float linear filtering reported; CI builds it only inside the Emscripten bundle; the Apple campaign ran its GPU tests on macOS. |
SOFTWARE | Experimental (declared) | 2D + 3D | CPU rasterizer, no GPU needed; MSAA, up to 4 MRTs, occlusion, instancing; ignores custom shader source; off-screen except on the Terminal platform. |
HEADLESS | Supported (diagnostic) | none | Test harness: validates arguments and traces calls, deliberately renders nothing; useful for game-logic CI. |
STUB | Supported (diagnostic) | none | Smallest complete renderer: renders nothing, all 19 capabilities false; default of the dev/unit presets. |
SDL_RENDERER | Production (declared), within 2D scope | 2D only | Portable SDL path with deliberate 3D rejection; the default on Windows, macOS, iOS and Android, and one of the two renderers the iOS build admits. |
| Renderer | 3D | DS | AA | RT | An | Wf | Oq | Cx | T3 | MS | In | St | Ad | CE | F32 | F16 | HF | Cs | Id |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
SDL_RENDERER | – | – | – | – | – | – | – | – | – | – | – | – | Y | – | – | – | – | – | – |
OPENGLES3 | Y | Y | c | Y | c | c | Y | Y | Y | Y | Y | Y | Y | c | c | c | Y | c | c |
OPENGL33 | Y | Y | c | Y | c | Y | Y | Y | Y | Y | Y | Y | Y | c | c | c | Y | c | c |
WEBGL2 | Y | Y | c | Y | c | c | Y | Y | Y | Y | Y | Y | Y | c | c | c | Y | – | – |
VULKAN | Y | Y | c | c | c | c | Y | Y | Y | Y | Y | c | Y | c | c | c | c | c | c |
WEBGPU | Y | Y | c | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | c | c | c | Y | Y | c |
HEADLESS | Y | Y | Y | Y | Y | Y | Y | Y | – | – | Y | Y | – | – | – | – | – | – | – |
SOFTWARE | Y | Y | Y | Y | Y | Y | Y | – | Y | Y | Y | Y | Y | c | Y | Y | Y | – | – |
STUB | – | – | – | – | – | – | – | – | – | – | – | – | – | – | – | – | – | – | – |
DIRECTX11 | Y | Y | c | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | c | c | c | c | Y | Y |
DIRECTX9 | Y | Y | Y | Y* | Y | Y | Y | Y | Y | – | Y | Y | Y | c | – | – | – | – | – |
SDL_GPU | Y | c | c | Y | Y | Y | – | c | Y | Y | Y | c | Y | c | c | c | c | Y | Y |
METAL | Y | Y | c | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | c | Y | Y | Y | – | – |
FNA3D | Y | Y | c | Y | Y | Y | c | – | Y | Y | c | c | Y | Y | – | – | – | – | – |
Reading the matrix: the cells are code-derived from each renderer's SupportsCapability arms at this snapshot, not measured results. The public answer is GraphicsDevice::SupportsCapability(), which derives CE, F32, F16, HF, Cs and Id from separate hooks that default to false. DIRECTX9 inherits the permissive base default; Y* means its MRT count is additionally limited by profile (Reach 1, HiDef 4). For SDL_GPU the renderer-level switch answers a few cells more conservatively than the device level does. Conditions behind c: GL MSAA needs GL_MAX_SAMPLES above 1; anisotropy needs the anisotropic-filter extension; compute needs ES 3.1 or GL 4.3 (never WebGL); compiled effects need the build option below; float and half-float targets come from a runtime probe or DXGI format queries.
| Family (identities) | Compiled effects | Gate |
|---|---|---|
FNA3D (FNA3D) | Always on | None: FNA3D pulls MojoShader unconditionally |
EasyGL (OPENGLES3, OPENGL33, WEBGL2) | With the option | CNA_EASYGL_COMPILED_EFFECTS (one option for all three) |
Vulkan (VULKAN) | With the option | CNA_VULKAN_COMPILED_EFFECTS |
WebGPU (WEBGPU) | With the option | CNA_WEBGPU_COMPILED_EFFECTS; the browser build translates SPIR-V to WGSL |
SDL_GPU (SDL_GPU) | With the option | CNA_SDL_GPU_COMPILED_EFFECTS |
Software (SOFTWARE) | With the option | CNA_SOFTWARE_COMPILED_EFFECTS |
Metal (METAL) | With the option | CNA_METAL_COMPILED_EFFECTS; MojoShader SPIR-V translated to MSL by SPIRV-Cross |
Direct3D 9, 11 (DIRECTX9, DIRECTX11) | With the option | CNA_DIRECTX9_COMPILED_EFFECTS, CNA_DIRECTX11_COMPILED_EFFECTS |
HEADLESS, STUB, SDL_RENDERER | Never | No implementation |
| Family (identities) | Platform gate | Needs SDL3? | Dependency |
|---|---|---|---|
EasyGL: OPENGLES3, OPENGL33 | Native hosts (not Emscripten); CMake warns OPENGLES3 is primarily tested on Linux elsewhere | No; needs a GL context from the SDL3 platform | Siblings ../easy-gl and ../meta-gl |
EasyGL: WEBGL2 | Emscripten only | No | Same siblings; the link contract pins WebGL 2 |
VULKAN | Any host with a Vulkan loader and a Vulkan surface from the SDL3 platform | No | Vulkan SDK/loader |
WEBGPU | Native (Linux, macOS, Windows) and browser | No | Pinned wgpu-native v29.0.1.1 (SHA-256 verified) or the Emscripten emdawnwebgpu port |
SDL_GPU | Any host SDL3's GPU API supports | Yes | SDL3; SDL_shadercross + SPIRV-Cross (default ON on Windows and Apple); optional libshaderc |
FNA3D | Any host FNA3D and SDL3 support | Yes | Fetched FNA3D and MojoShader |
DIRECTX9, DIRECTX11 | Windows only (native or mingw-w64 cross-build) | No; takes the HWND of the SDL3 window | Windows SDK libraries (d3d9, d3d11, dxgi, d3dcompiler) |
METAL | macOS and iOS (tvOS refuses it) | SDL3 is the Apple platform | Objective-C++; AppKit or UIKit, Metal, QuartzCore, Foundation |
SDL_RENDERER | Any SDL3 platform | Yes | Vendored SDL3 |
SOFTWARE | Any host; off-screen except on the Terminal platform | No | None (MojoShader only with its compiled-effects option) |
HEADLESS, STUB | Any host | No | None |
Choosing and switching renderers. Build time: exactly one -DCNA_GRAPHICS_RENDERER=<NAME> (or one CNA_RENDERER_<NAME>=ON); omit it for the per-host default (Emscripten WEBGL2, Linux OPENGLES3, everything else SDL_RENDERER). Names are exact-case in CMake. One combination rule is enforced: Windows-only, Emscripten-only and Apple-only identities cannot mix. Run time: an explicit GraphicsRendererSelection::SetPreferred() beats the CNA_GRAPHICS_RENDERER environment variable (or the Emscripten Module.cnaPreferredRenderer property), which beats the build default. A renderer that is not compiled in throws, fallback is off unless you enable a chain or automatic fallback, and the choice latches when the first GraphicsDevice is created successfully. GraphicsAdapter::UseNullDevice and UseReferenceDevice map to HEADLESS and SOFTWARE. See Runtime renderer selection.
SDL_Renderer Renderer
The most portable 2D renderer, built on SDL3's own hardware-accelerated renderer. It is the default on Windows, macOS, iOS, Android and other non-Linux hosts and one of the two renderers the iOS build admits. 3D calls throw by design rather than quietly drawing nothing, and its capability reporting is written to match what it can actually do (additive blending is the one capability it reports). Requires the SDL3 platform. Selected with -DCNA_GRAPHICS_RENDERER=SDL_RENDERER.
EasyGL (OpenGL) Renderer
The shared family behind three public profiles: OPENGLES3 (the Linux default), OPENGL33 and WEBGL2 (the Emscripten default). It provides shader-driven 2D/3D rendering with MRT, occlusion queries, Texture3D, multi-stream input and instancing on every profile, and MSAA, anisotropy and (outside WebGL) compute where the context provides them; on macOS it runs on Apple’s core-profile OpenGL. Needs the sibling easy-gl and meta-gl repositories, and compiled effects are opt-in with one option for all three. Selected with -DCNA_GRAPHICS_RENDERER=OPENGLES3.
Vulkan Renderer
A native Vulkan family with SPIR-V ShaderEffect, render-target and readback paths, and the most complete modern surface of any renderer: compute, indirect draw, base-instance drawing, GPU timers, float and half-float render targets, shadow sampling and image-based lighting (some device-conditional). XNA/FNA compiled Effect Framework bytecode is separately opt-in with CNA_VULKAN_COMPILED_EFFECTS. Selected with -DCNA_GRAPHICS_RENDERER=VULKAN.
Known gaps: it reports a lost device but deliberately does not attempt a reset, and compiled effects refuse vertex-stage texture sampling; see the Roadmap.
Direct3D 11 Renderer
Windows-only, hard-gated at CMake configure time, with state-object caches, MRT, instancing, float and half-float render targets and runtime HLSL compilation for ShaderEffect. Compiled effects are opt-in with CNA_DIRECTX11_COMPILED_EFFECTS. Native MSVC coverage is manual-dispatch. Selected with -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/mingw-w64.cmake -DCNA_GRAPHICS_RENDERER=DIRECTX11.
Verify capability boundaries: the renderer now overrides SupportsCapability() (compute and indirect draw report false), but do not infer correct draw offsets or device support from the family's presence alone.
WebGPU Renderer
An experimental renderer using native wgpu-native (pinned v29.0.1.1) and, new since alpha.1, a browser route through the Emscripten emdawnwebgpu port. It has a working 2D baseline and a real 3D path: MRT (two to four targets), occlusion queries, wireframe by edge expansion, multi-stream input, instancing, probed MSAA and float render targets, compute, and shadow and IBL sampling. Compiled effects are opt-in and translate SPIR-V to WGSL in the browser build. Selected with -DCNA_GRAPHICS_RENDERER=WEBGPU.
Known gaps: CNA still declares it Experimental; half-float linear filtering is false, source ShaderEffect runs as WGSL only, and no CI workflow names it. The alpha.1 statements that it had no render targets, MSAA, instancing or working stencil no longer hold.
Headless Renderer
Renders nothing by design: no window, GPU or graphics API calls. It provides argument validation, resource-lifecycle tracking and draw counters for game-logic CI, in three modes (Fast, Validation by default, Trace). It is also what GraphicsAdapter::UseNullDevice selects. Selected with -DCNA_GRAPHICS_RENDERER=HEADLESS.
Be aware: GetBackBufferData now throws NotSupportedException instead of returning the last clear colour: a renderer with no honest pixel result refuses rather than inventing one. It is not a pixel oracle.
Software Renderer
A genuine CPU rasterizer with edge functions, a Z-buffer and perspective-correct interpolation into a CPU-owned framebuffer. Useful for GPU-free pixel tests, and it is what GraphicsAdapter::UseReferenceDevice selects. Selected with -DCNA_GRAPHICS_RENDERER=SOFTWARE.
Scope and gaps: it now handles triangle lists and strips, lines and points, indexed and multi-stream draws, up to four render targets, 4x MSAA, occlusion queries, instancing and float render targets, with opt-in compiled effects. It still ignores custom ShaderEffect source, has no shadow sampling or IBL, and is not a real-time renderer by design. It is off-screen on every windowing platform; only the Terminal platform displays its frames. On the real-XNA oracle corpus it was measured at 18 of 39 scenes byte-exact (30 within one channel value), and only two line scenes have a CTest, which fails on a render failure rather than on a pixel difference.
Direct3D 9 Renderer
Windows-only and the renderer used for the 39-scene XNA oracle comparison at zero tolerance. It includes shader, device-lost recovery and MRT paths. The oracle result is specific to DIRECTX9 and was recorded through Wine and DXVK on Linux, the same host that produced the genuine-XNA reference images; it is not repeated on native Windows and does not run in CI. Compiled effects are opt-in with CNA_DIRECTX9_COMPILED_EFFECTS. Selected with -DCNA_GRAPHICS_RENDERER=DIRECTX9.
Note: SupportsCapability() is still not overridden, so this is the one renderer that relies wholesale on the permissive default.
SDL_GPU Renderer
Built on SDL3's GPU API with deferred/retained rendering, stencil and MSAA paths, compute, indirect draw and float render targets. Compiled XNA/FNA effects are separately opt-in with CNA_SDL_GPU_COMPILED_EFFECTS. Selected with -DCNA_GRAPHICS_RENDERER=SDL_GPU.
Known gaps: it is no longer Vulkan-only: with CNA_SDL_GPU_SHADERCROSS (default ON on Windows and Apple) CNA's SPIR-V shaders are translated for SDL’s Direct3D 12 and Metal drivers. Occlusion queries are not available (SDL’s GPU API has none), and source ShaderEffect needs libshaderc, so it is unavailable on Windows, Apple and Emscripten builds. It requires the SDL3 platform.
ASCII post-process effect
Quantizes any Texture2D or RenderTarget2D into a glyph and colour grid. It is a CNAEXT post-process effect, CNA::Graphics::AsciiPostProcessEffect: because it works through the public Texture2D::GetData() and SpriteBatch API rather than a renderer-internal type, it runs on any renderer, including 3D-rendered sources.
Note: it is deliberately not an XNA Effect subclass — it performs its own multi-step CPU readback and re-upload, so it costs a GPU→CPU readback per draw. Requires -DCNA_CNAEXT=ON. It is not a terminal renderer; the POSIX Terminal platform instead displays frames from the SOFTWARE renderer.
Metal Renderer
A native Apple Metal renderer for macOS and iOS; SDL3 supplies only the window and the view that holds the CAMetalLayer. It draws 2D and 3D with MSAA, multiple render targets, occlusion queries, instancing and multi-stream input, stores XNA’s float, packed and DXT formats natively in 2D, cube and volume textures, runs SpriteBatch custom effects written in MSL, and runs compiled XNA effects when built with CNA_METAL_COMPILED_EFFECTS. It has no compute or indirect draw. It was qualified on a physical Mac mini M4 in CNA’s Apple campaign. Selected with -DCNA_GRAPHICS_RENDERER=METAL.
Evidence boundary: on a physical Mac mini M4 the full test tree passed (11,015 tests, none failed), the 261 Metal-labelled tests passed under the Metal validation layers in Debug and Release, and all 91 ported XNA samples passed the renderer matrix; the console was locked, so on-screen presentation was not observed. CNA’s verdict is Supported, not primary-production (compiled effects are opt-in, custom effects are SpriteBatch-only, PresentInterval.Two presents as One, no soak run). On iOS it ran in the iOS Simulator with an exact pixel probe; no physical iPhone or iPad ran it. CI builds it on GitHub’s macos-26 runners.
FNA3D Renderer
An adapter over the FNA-XNA FNA3D C library (pinned), which chooses SDL_GPU, Direct3D 11 or OpenGL at run time (override with FNA3D_FORCE_DRIVER). It runs any compiled Effect Framework binary — compiled effects are always on, with no build option — but not ShaderEffect source. It reports 3D, MRT, multi-stream input and probed MSAA and instancing; no float render targets or compute, and it refuses PBR draws by name. Requires the SDL3 platform. Selected with -DCNA_GRAPHICS_RENDERER=FNA3D.
Evidence boundary: its oracle CTest gates only that all 39 scenes render, not pixel equality.
Stub Renderer
The smallest possible complete renderer: it renders nothing and keeps no bookkeeping, all 19 capabilities are false and it reports no 3D. It is the default of the dev, unit and release-modules presets and is always ranked last by automatic fallback. Selected with -DCNA_GRAPHICS_RENDERER=STUB.
Cross-Platform Architecture
CNA separates target OS from host integration, audio I/O and graphics rendering; support claims below are scoped to the evidence in this snapshot.
Linux (x86_64)
The primary development platform, with OPENGLES3 as the default renderer and the broadest CI coverage: SDL3 with OPENGLES3, VULKAN, SOFTWARE and SDL_RENDERER, the SDL3 window suite on SDL’s x11 driver (Xvfb) and wayland driver (headless Weston), and an SDL-free Headless lane with ALSA audio. SDL3’s Wayland driver is built only if its development packages were present when SDL was first built. FFmpeg video decoding is optional (CNA_ENABLE_VIDEO) and built on Linux and macOS only when the FFmpeg libraries are found. See Windows, X11 and Wayland.
Windows (x86_64)
Reached through a MinGW-w64 cross-toolchain as well as native MSVC, with windows from SDL3’s windows video driver. Two renderers are Windows-only (DIRECTX9, DIRECTX11). The Direct3D paths have focused evidence — Direct3D 11 validated on one physical Windows 11 machine, and a recorded native-MSVC run on GitHub’s Windows image passing all 300 of its CTest entries — but the native Windows workflows remain manual-dispatch rather than automatic merge gates, and much Windows evidence is cross-compiled and run under Wine. FFmpeg video is never built for Windows.
Android
Genuinely wired through CMake and the Android NDK — sensors link against android, and Compass/Motion use a real NDK fusion of 5 sensors. The default renderer is SDL_RENDERER and the platform is SDL3. Caveats: there is no Android CI and no CMake preset, the only Android build project is the Devices demo (minSdk 24, arm64-v8a), and FFmpeg video is unavailable on this target.
Emscripten / Web
A real platform with WEBGL2, the Emscripten-only default, plus WEBGPU through its browser route. CI builds and links one bundle with WEBGL2;WEBGPU and asserts both renderers and the JavaScript selection surface are in it; it does not run them in a browser. Saves persist: the storage module mounts the browser’s IndexedDB for StorageDevice and isolated storage and restores it before main(). Games that start threads build with Emscripten threads. FFmpeg video remains excluded. The strongest evidence is the 90 C++ ports of XNA samples that play at samples.libcna.com, each run in Chrome when it was completed.
macOS / iOS
macOS was tested on a physical Mac mini M4 (macOS 27.0.1, Xcode 27): seven renderer trees — METAL, OPENGL33, WEBGPU, SDL_GPU, FNA3D, SOFTWARE and SDL_RENDERER — passed their full CTest suites with no failure, and CI builds on GitHub’s macos-26 runners (deployment floor macOS 13.3; no Intel Mac was run). iOS is experimental: SDL_RENDERER and METAL are admitted (floor iOS 16.3, networking off); the iOS Simulator (iPhone 17 profile) runs a smoke app and an exact METAL pixel probe, and device apps final-link but no physical iPhone or iPad ran them. tvOS is unsupported.
Terminal & headless hosts
The TERMINAL platform runs CNA in a POSIX terminal with the SOFTWARE renderer displaying frames, and HEADLESS runs any game logic without a window. CI covers both, including a pseudo-TTY test that runs the 2D demo through the terminal presenter. There is no Windows console backend.
Diagnostics, Inspector & C API
Three new opt-in pieces sit beside the runtime: an in-process diagnostics layer, an out-of-process inspector built on it, and an experimental C ABI. The Inspector and the C API stay outside a default game's link closure, Diagnostics compiles to nothing unless enabled, and none of the three is a release.
Diagnostics & Profiler
CNA::Diagnostics is a renderer-independent, in-process observation layer: counters, gauges, per-frame counters, a 240-frame history, resource metadata and, in FULL mode, CPU zones, markers and an event history with bounded recording and Chrome-trace export. It uses only the C++ standard library: no thread, no socket, no renderer dependency. Enable it with -DCNA_DIAGNOSTICS=OFF|STATS|FULL (default OFF); in lower modes the instrumentation macros compile to nothing and never evaluate their arguments.
Built in: game tick, update and draw zones, draw-call, primitive, texture-binding, effect and render-target counters, sprite submissions, resource metadata for textures and buffers, and audio voice gauges. Not published: GPU timings, input metrics and process memory; no C ABI routes exist for it, and no CI workflow enables it. See Diagnostics.
Inspector
An optional, view-only development tool with two parts: an in-process agent (CNA::Inspector::Agent, one background thread, authenticated TCP, a compact binary protocol) and a separate cna-inspector bridge that serves an offline browser UI at http://127.0.0.1:<port>/. The UI has Session, Performance, CPU profiler, Graphics, Resources, Audio, Input and Events views. It cannot edit state, run scripts or inject input.
Security and limits: loopback by default (a remote bind needs allowRemote), a 256-bit token from the OS random source, and nothing runs until the application calls Agent::Start() — there is no static initializer or environment hook. Build it with -DCNA_BUILD_INSPECTOR=ON (refused on Emscripten, Android and iOS; not in the CNA umbrella target). Resource previews are unavailable in real sessions today because no renderer installs a preview provider, and no CI workflow covers it. See Inspector.
C API (C ABI 0.46.0)
A C99-consumable ABI over the C++ framework: 60 public headers and 3,210 exported routes, opaque handles, a per-thread last-error record, count-then-copy strings, one active runtime per process, a host-driven frame loop and an opt-in queue for calls from other threads. The version is 0.46.0 (alpha.1: 0.7.0) and remains an experimental 0.x ABI; renderer identity values are stable and unused ones stay reserved, and 0.46.0 adds a UIKit native-window kind for iOS. A recorded baseline (197 struct layouts, 3,210 exports) is enforced by header-only gates, and a find_package(CNA CONFIG) package with CNA::CApi and a hello_cna example is exercised by a local consumer test on Linux and, on a physical Mac mini M4, as a Mach-O libcna_c_api.dylib exporting exactly the 3,210 routes, installed with an @loader_path rpath, plus a static archive (119/119 C API tests in the SOFTWARE and SDL_RENDERER trees).
Status: CNA's semantic inventory lists 8,142 public C++ symbols — 7,053 implemented, 15 partial, 631 planned and 443 not applicable — and its own release gate reads “Not ready” because of those 631. No CNA CI job builds the C library on any platform; the CNA.NET project builds and uses it on Linux and, locally, on a physical Mac mini M4. The C# binding CNA.NET is the maintained consumer and admits exactly this ABI version after a compatibility review; the Common Lisp, Go, Java, Python, Ruby, Rust, Swift and TypeScript bindings are archived and not maintained. See the C API page.
Modern C++23 Codebase
C++23 Standard
CNA targets C++23 throughout (extensions off). CMake does not check a compiler version; CI builds with GCC 14, current AppleClang, MSVC on windows-latest, mingw-w64 and Emscripten 6.0.3, and the code base uses <format> unconditionally, which needs libstdc++ 13, a current libc++ or MSVC 2022. The alpha.1 statement “GCC 12+, Clang 15+, or MSVC 2022 v17.8+” is neither enforced by the build nor proven by CI.
CMake Build System
CMake 3.20+ with vendored SDL dependencies (git submodules) by default. Configure the target OS, platform (CNA_PLATFORM), renderer set, audio (CNA_AUDIO_PLATFORM) and SDL availability (CNA_ENABLE_SDL) as independent axes; single-renderer SDL3 remains the simplest default. 17 visible configure presets ship, and on native ELF GNU/Clang builds with CMake 3.27+ CNA_SHARED_LIBRARY defaults ON so executables link one libcna.so. Optional inputs are three-state switches (CNA_ENABLE_VIDEO, CNA_ENABLE_FONT_PIPELINE, CNA_ENABLE_MEDIA_PIPELINE, CNA_CNB_ZSTD). Siblings: sharp-runtime always, easy-gl (with meta-gl) for the GL identities. For this snapshot clone CNA and sharp-runtime on branch apple/m4-stabilization.
Test Suite
This snapshot contains 813 C++ test source files (781 with a counted test macro) and 11,380 static GoogleTest-family definitions, counted with the site's published method (alpha.1: 568 and 8,263), plus 781 standalone examples/**/*_test.cpp pixel programs outside those figures. Instantiated GoogleTest cases and CTest registrations vary with renderer, platform, feature options and dependencies, so every result is reported for a configured build and no CTest total is published. There are 22 focused per-module test targets besides the aggregate CnaTests. Renderer pixel programs, the XNA oracle and FNA differential checks cover separate compatibility questions. See Verification & Known Issues.
Coverage is uneven: Storage has one source with 14 statically discoverable definitions and Media 304; each still needs configuration-specific execution evidence.
Continuous Integration
The snapshot has 18 GitHub Actions workflow files (24 jobs), 16 of which trigger automatically on push and pull request. They span intended general and focused Linux tests, runtime multi-renderer sets, platform abstraction (SDL3 on X11 and Wayland, an SDL-free Headless job, and a manual native-MSVC SDL3 job), Emscripten and browser lanes, Apple/Metal and five build-free C API gates. The alpha.1 defect (a stale EASYGL value in the general job and two Input rows) is fixed. Native Windows workflows remain manual, and there is no Android, C-library-build or oracle-corpus workflow. Workflow files are configuration, not results: whether each lane currently passes was not evaluated for this page, and build/link/simulator evidence must not be upgraded into a physical-device or universal runtime claim.
Reference-checked behavior
CNA keeps 39 renderer-neutral test scenes (256×256, HiDef) whose reference images come from running the genuine Microsoft XNA 4.0 runtime under Wine with DXVK on Linux, plus 7 unbound-texture scenes and a 17-format channel-expansion table measured the same way. The repository records all 39 scenes as pixel-exact on the DIRECTX9 oracle path (CTest D3D9_XNA_Diff, the same stack, zero tolerance); the last consolidated dated report covers 31 of them and later per-scene notes record the rest. No 39-scene run was executed for this documentation, it has not been repeated on native Windows, and it does not run in CI. No other renderer is held to that bar: EasyGL and Software have a CTest on two line scenes (the EasyGL test fails on any pixel difference; the Software test only if a scene does not render), and FNA3D gates only that scenes render.
Also present: 32 cross-renderer parity fixtures registered for EasyGL, WebGPU, SDL_GPU and Direct3D 11 (their oracle is the fixtures' own assertions, not real XNA); a glTF conformance corpus of 148 assets (140 captured and 8 safely rejected) with renderer-owned goldens, plus a Khronos comparison subset of 13; and a manual harness that diffs enums, presets, packed vectors and viewport against a running FNA build.
sharp-runtime
A required sibling repo and a substantial project in its own right: a C++23 reimplementation of a .NET BCL subset, described by its own README as a set of 41 independently selectable components (a moving companion-project figure, not a CNA metric; its test counts are deliberately not restated here). CNA now requests the Resources and Xml.Serialization components, which exist only on sharp-runtime's apple/m4-stabilization branch, so this snapshot needs sharp-runtime next, not its default main. It implements System::* broadly — primitives, Collections (Generic, Immutable, Concurrent, Frozen, Specialized, ObjectModel), IO (plus Compression, Hashing, IsolatedStorage), Text (including a full System.Text.Json and Regex), Net (Sockets, Http, WebSockets), Threading (plus Tasks and Channels), Xml (plus Linq and XPath), Globalization (10 calendars), Numerics (including BigInteger), Diagnostics and Buffers. Security.Cryptography covers hashing rather than asymmetric crypto, TLS or X.509.