CNA is a C++ reimplementation of the XNA 4.0 programming model, built on SDL3 and a pluggable graphics renderer layer.
It is a framework/runtime and abstraction layer—not a game—designed to preserve XNA-style APIs (Microsoft::Xna::Framework) while using modern C++ internals.
CNA demonstrates engine-level C++ architecture, graphics abstraction design, and renderer-oriented systems engineering.
git submodule update --init --recursive
cmake -S . -B build -DCNA_GRAPHICS_RENDERER=OPENGLES3
cmake --build build --target CnaTests
ctest --test-dir build --output-on-failureCurrent release: 0.1.0-alpha.1 (pre-release — the public API may still change; see
CHANGELOG.md for what the release contains and
docs/releasing.md for how versions are managed). Compiled code reads its
own version from CNA::getVersionString() in CNA/Version.hpp.
Looking for a specific doc?
docs/has 93 Markdown documents — seedocs/README.mdfor an index of what's current vs. historical.
Microsoft::Xna::Framework::Graphicsmilestone: qualified ~90% XNA/FNA compatibility, test-execution-verified (not estimated) — every one of the ~26 major Graphics classes is present, implemented, and tested. As of 2026-07-11, all 5 confirmed bugs behind the original 2026-07-09 milestone declaration are fixed (VulkanBlendState, EasyGL anisotropic filtering,IndexElementSize's numeric values,Model's root-bone override,SpriteBatch::Draw's optional source rectangle), and VulkanOcclusionQuery(previously architecturally blocked) is fixed too.docs/graphics-compatibility-report.mdis a dated snapshot from that declaration, kept for its methodology, not current status — seeNEXT.md§5 for the actively-maintained bug list. What's left to 100% is a smaller set of individually-tracked issues plus a handful of project-owner architecture decisions (e.g. SDL_RendererTextureAddressMode::Wrap/Mirror,Texture3D/TextureCubesampler-bind architecture) — none silent or undocumented.- Overall XNA 4.0 API surface: 227 of 245 public FNA types are present in CNA (92.7%, computed 2026-07-11 by diffing FNA's public type list against CNA's headers) — 100% for
Graphics/Audio/Input(+Touch)/Storage; the real gap is.Content(4/12 — no.xnbreader, by design) and.Media(25/25 present, but 14 are shells). Seedocs/xna-4-api-coverage.md. Note this is a different metric from the Graphics bullet above — this one counts whether a type/class exists at all across every XNA namespace (a raw presence count), while the Graphics "~90%" figure is a narrower, bug-weighted quality gate scoped to just the ~26 major Graphics classes (it also counts behavioral correctness, not just presence — Graphics itself is 91/91 = 100% present). The two numbers measuring different things is expected, not a typo or a contradiction. - Compiled XNA effects:
Effect(GraphicsDevice&, byte[])and the canonical XNBEffectReaderexecute XNA/FNA Direct3D 9 Effect Framework bytecode on theFNA3Drenderer, including public reflection, parameter mutation, techniques/passes, pass states, cloning, 3D draws, andSpriteBatch. Other renderers currently reportGraphicsCapability::CompiledEffects == falseand reject the constructor explicitly; MGFX and runtime.fxsource compilation remain separate formats/projects. Seedocs/shader-effect-vs-fx-bytecode.md. SDL_RENDERERrenderer: Implemented path focused on practical 2D rendering workflows; 2D-only by design (3D calls throw).OPENGLES2/OPENGLES3/OPENGL33/WEBGL1/WEBGL2renderers: the most mature GL-family public renderers overall — one shared internal implementation (EasyGL, on top ofeasy-gl) driven by a GL profile choice, not five separate implementations.OPENGLES3(desktop/mobile GLES 3.0) andWEBGL2(Emscripten, GLES 3.0 → WebGL 2.0) have full 2D+3D pixel-verified coverage — this is what was previously the singleEASYGLpublic renderer, split into its real public identities.OPENGL33(desktop GL 3.3 core) andWEBGL1(Emscripten, GLES 2.0 → WebGL 1.0) are newer and still landing — seeplan_glbackends.mdfor current per-profile status.OPENGLES2(native GLES 2.0, GLSL ES 1.00, Phase-2 expansion) carries a deliberately narrower ES 2.0 capability boundary — seedocs/opengles2-renderer.md.VULKANrenderer: Real, working 3D rendering (all 5 stock effects, render targets, depth/stencil state,BlendState,OcclusionQuery) — second-most mature renderer; the one remaining named gap is an isolatedRasterizerState.DepthBiassub-case. Seedocs/xna-4-api-coverage.md's per-renderer table for current detail.BGFXrenderer: Broad 2D+3D functionality, largely pixel-verified parity with EasyGL/Vulkan as of this project's Phase 72 — but not unqualified full parity: known real limitations remain, including aDepth24Stencil8-attachedRenderTargetCubeface producing no colour output (Task 952, deferred, root cause not yet found),DrawIndexedPrimitivesExsilently discardingstartIndex/baseVertexon a sub-range indexed draw (Task 954), and occlusion-query pixel-count correctness that can't be verified under this project's own sandbox's software GL driver. SeeNEXT.md§5 for the current, complete list.SOKOLrenderer: Experimental renderer onsokol_gfx, a single-header GPU abstraction (OpenGL 4.1 core today; GLES3/D3D11/Metal/WebGPU are wired but unimplemented). The current baseline covers context/pass lifecycle, the fullClearfamily, back-buffer read-back,Texture2D,TextureCube, vertex/index buffers, a pixel-verifiedSpriteBatchwith realBlendState/SamplerStatesupport, vertex-coloured/textured/lit/dual-textured/skinned/environment-mapped 3D geometry (BasicEffectwith real depth testing, face culling, ambient + up to 3 per-pixel directional lights, specular, emissive, alpha test and fog;DualTextureEffect;SkinnedEffectwith a 72-bone palette;EnvironmentMapEffectwith real cube-map reflection and Fresnel),Viewport.MinDepth/MaxDepth, instanced draws, customShaderEffects (raw-GL bypass of thesg_pipelinepath, bothSpriteBatchand 3D draws), MRT (2-4RenderTarget2Dtargets via a real multi-attachmentsg_pass), andRenderTarget2D/RenderTargetCube(real colour + depth-stencil attachments, MSAA, correct sampling orientation, immediate first-use, directGetDatareadback, mip-mapped rendering). PBR shading,RenderTargetCubeMSAA (a permanent sokol_gfx boundary), andTexture3Dsampling are not implemented and fail loudly. Seedocs/sokol-renderer.mdandplan_sokol.md.WEBGPUrenderer: Experimental renderer using nativewgpu-native. The current baseline covers device/surface setup, clear/present, RGBA8Texture2D, vertex/index uploads and WGSL SpriteBatch rendering. It is not yet a 3D-parity replacement for EasyGL/Vulkan/Bgfx; seedocs/webgpu-renderer.mdandplan_webgpu.md.SKIArenderer: Experimental CPU-raster 2D path using a pinned external Skia build. Clear/presentation/readback,Texture2D, complete sharedSpriteBatch/SpriteFont2D routes, raster targets, bounded 2D SkSL, CPU cube/volume transfer storage, and six-faceRenderTargetCubeemulation are verified. It deliberately does not advertise 3D, depth/stencil, MSAA, MRT, occlusion queries, or cube/volume sampling; direct and emulated alternatives are recorded indocs/skia-renderer.md, the feature matrix, andplan_skia.md.OPENGL4renderer: Real desktop OpenGL 4.x core-profile renderer (4.1 minimum requested,SDL_GL_CONTEXT_PROFILE_CORE), deliberately independent of the EasyGL-implementedOPENGLES3/OPENGL33/WEBGL1/WEBGL2family — those profiles target ES 3.0/WebGL or a GL 3.3 core profile, not 4.x core. Uses its own small hand-rolled GL loader (GL4Loader), zero new third-party dependency beyond the platform's own GL library. All five stock effects plusPbrEffect/SkinnedPbrEffect(GLSL 410 core, stride-dispatched), real FBO render targets (2D + cube + MRT), backbuffer and render-target MSAA, realGL_SAMPLES_PASSEDocclusion queries (exact pixel counts, unlike the EasyGL family's ES any-samples boolean), real wireframe viaglPolygonMode(pixel-oracle-verified),Texture3D/TextureCubewith real readback, 16/32-bit index buffers,baseVertex, custom GLSLShaderEffect(3D + SpriteBatch), and hardware instancing through the unified vertex-stream transport. Multi-stream vertex input is reported unsupported and refused deterministically. 25 dedicated pixel-readback CTest suites, all verified against a real 4.5-core context. Seedocs/opengl4-renderer.mdandplan_opengl4.md.OPENGL1renderer: Historical-class legacy desktop OpenGL 1.x fixed-function renderer — immediate-mode vertex emission,GL_MODELVIEW/GL_PROJECTIONmatrices,glLight*lighting (3 directional lights + specular + emissive),glTexEnv*combiners (DualTextureEffect,EnvironmentMapEffectreflection subset), realGL_FOGdriven by an exact inversion of the FNA fog vector,glAlphaFuncalpha-test approximation. Runtime-discovers 1.2–1.5-era features viaSDL_GL_GetProcAddress(FBO render targets 2D+cube with readback and mip regeneration, backbuffer + RT MSAA,ARB_occlusion_querywith exactGL_SAMPLES_PASSEDcounts, extended blend, anisotropy, cube maps) — no GL loader library, no shaders anywhere, zero new third-party dependency. No custom effects, no MRT, noTexture3D, no instancing/multi-stream — all reported truthfully and refused deterministically. 38 dedicated CTest suites. Seedocs/opengl1-renderer.mdandplan_opengl1.md.OPENGL2renderer: Native desktop OpenGL 2.1 compatibility-profile renderer, GLSL 1.10 throughout (runtime-compiled inline programs, attribute names bound viaglBindAttribLocation), deliberately independent of the EasyGL-implemented GL family and of the other GL renderers. All five stock effects plusPbrEffect/SkinnedPbrEffect, FNA fog-vector fog, real FBO render targets (2D incl. MSAA + cube), real MRT (up to 8 targets with real depth/MSAA resolve), realGL_SAMPLES_PASSEDocclusion queries,Texture3D/TextureCubewith readback, 16/32-bit indices, softwarebaseVertex(pointer re-base -- noglDrawElementsBaseVertexon 2.1), full customVertexDeclarationsupport (name-driven binding reads exactly the declared bytes), custom GLSL 1.10ShaderEffect(3D + SpriteBatch), real Letterbox/Overscan/Stretch presentation modes, context-loss recovery, and hardware instancing when the driver grantsGL_ARB_draw_instanced/GL_ARB_instanced_arrays-- the driver-dependence is why this lane addedGraphicsCapability::Instancing. Multi-stream vertex input reported unsupported and refused deterministically. 48 dedicated CTest suites. Seedocs/opengl2-renderer.mdandplan_opengl2.md.OPENGLES1renderer: Genuine OpenGL ES 1.1 fixed-function renderer ("Common"/CM profile), deliberately independent of the EasyGL-implemented GL family — EasyGL targets shader-based ES 3.0/GL 3.3/WebGL pipelines and cannot create an ES 1.1 context at all, so the two share no code. No shaders anywhere (zero#versiondirectives, zero shader entry points); fixed-function matrices,glLight*lighting,glTexEnv*multitexture combiners,GL_FOG, alpha test, FBO render targets viaGL_OES_framebuffer_object, andWireFrameemulated by re-expanding triangles toGL_LINES. Multiple render targets, occlusion queries, custom effects,Texture3D, multi-stream vertex input and instancing are all reportedfalseand refused deterministically. Requires a real system OpenGL ES 1.1 library (libGLESv1_CMplusGLES/gl.h/GLES/glext.h; Debianlibgles1,libgles-dev), gated at configure time with aFATAL_ERROR— nothing vendored or downloaded. Note that Debian builds Mesa with-Dgles1=disabled, so its stock driver cannot create an ES 1.1 context on any device; the renderer is validated against a locally built ES1-capable Mesa driven byscripts/opengles1-test-env.sh(verified runtime identity:OpenGL ES-CM 1.1 Mesa 25.0.7, softpipe). 7 dedicated pixel-readback CTest suites. Seedocs/opengles1-renderer.mdandplan_opengles1.md.MAGNUMrenderer: Desktop OpenGL 3.3 core through Magnum's typed GL wrappers. It provides native polygon-mode wireframe while the EasyGL profiles provide their own measured line-expansion emulation. Its accepted capability and validation boundary is recorded indocs/magnum-renderer.md,plan_magnum.md, andintegration/lanes/magnum.md.LLGLrenderer: Experimental renderer on LLGL. CNA's accepted support route is LLGL's OpenGL module on Linux/X11/x86_64; it is the only automatic choice. Explicit Vulkan selection is rejected after native validation exposed descriptor, image-layout, and teardown violations, and Null is diagnostics-only. The current accepted boundary is recorded indocs/llgl-renderer.md,plan_llgl.md, andintegration/lanes/llgl.md.DIRECTX9renderer: Windows-only native Direct3D 9 renderer targeting real XNA 4.0 pixel authenticity, not just feature parity — it runs Microsoft's own vendored Stock Effects HLSL bytecode, cross-compiled via MinGW-w64 and verified through Wine+DXVK on a real GPU (14 CTest binaries). A checked-in 31-scene oracle corpus diffs CNA's render against the real XNA 4.0 runtime's own render of the same scene at--tolerance 0: 0/31 scenes currently diverge.GraphicsProfile.Reach/.HiDefenforcement is real (the only CNA renderer where it is). Render targets sampled as textures, non-ColorSurfaceFormat, and real-Windows hardware verification are still open. Seedocs/directx9-renderer.md,docs/d3d9-divergence-report.md, andplan_dx9.md.DIRECTX11renderer: Windows-only native Direct3D 11 renderer, cross-compiled via MinGW-w64 and verified through Wine+DXVK on a real GPU (6 CTest binaries, 96+ checks) — all 10 stock HLSL shader variants (BasicEffect/AlphaTestEffect/DualTextureEffect/EnvironmentMapEffect/SkinnedEffect), textures/render targets (MRT/MSAA/occlusion queries), state objects, SpriteBatch, and a runtime-D3DCompile()customShaderEffectpath are real and pixel-verified. Real-Windows hardware verification (device-lost recovery, WARP fallback, driver-specific parity) is still open. Seedocs/directx11-renderer.mdandplan_dx.md.DIRECT2Drenderer: Windows-only hardware-accelerated 2D-only renderer usingID2D1DeviceContext; D3D11/DXGI only host the Direct2D device and flip-model swap chain. AuthoredTexture2Dmips, non-mipmappedRenderTarget2D, SpriteBatch transforms/tint/flip/addressing, native anisotropic filtering, exact supported Direct2D Porter–Duff modes, logical presentation/readback, resize and 2D resource recovery are implemented. Additive blending, mipmapped render targets, and every 3D path are rejected rather than approximated. Wine covers the compatibility matrix; built-in effects, selected image composites, physical display/DPI output, and debug-layer evidence remain native-Windows gates. Seedocs/direct2d-renderer.mdandplan_direct2d.md.DIRECTX12renderer: Windows-only native Direct3D 12 renderer, cross-compiled via MinGW-w64 and verified through Wine+vkd3d-proton on a real GPU, off-screen only (DirectX12_SmokeCTest, 80/80 checks) — device/queue/heaps/command-lists/fences/barriers/PSOs/root-signatures are real, all 10 stock HLSL shader variants (same DXBC asDIRECTX11) and a realSpriteBatchare pixel-verified off-screen, and device-removed recovery is real and functionally proven. Swap-chain presentation is a known, real, unresolved gap on this dev loop (genuine Wine/vkd3d-protondxgi.dllarchitecture mismatch, not a CNA bug); runtime-settable blend/depth-stencil/rasterizer state, per-slotSamplerState, render targets,Texture3D, occlusion queries, and real-Windows hardware verification are all still open. Seedocs/directx12-renderer.mdandplan_dx.md.CANVASrenderer: Emscripten-only HTML Canvas 2D renderer (SpriteBatch/Texture2D/SpriteFont/RenderTarget2Donly, 2D-only by design likeSDL_RENDERER) —SpriteBatch(incl. rotation/origin/flip/tint/transform), textures/render targets,BlendState/SamplerStatemapping, andSpriteFontare all implemented and structurally reviewed, verified via a realemcmake/emcc6.0.2 configure+build (CnaTestslinks and a renderer-agnostic GTest suite genuinely passes undernode). Not yet pixel-verified in a real browser — this dev loop has no DOM/CanvasRenderingContext2Dat all (nodehas none;SDL_Init(SDL_INIT_VIDEO)itself throws under Emscripten/node). Seedocs/canvas-renderer.md(incl. a manual browser verification checklist) andplan_canvas.md.HTML_DOMrenderer: Emscripten-only DOM/CSS renderer with the same 2D-only scope, but no<canvas>in the sprite path at all — each sprite is a pooled<div>placed by a CSStransform, textured withbackground-image, faded withopacity, and composited by the browser itself. A frame in which nothing moves costs nothing; a frame in which sprites only move costs onetransformwrite each, and a wholeSpriteBatchcrosses the wasm/JS boundary exactly once.RenderTarget2Dis backed by a real off-screen canvas; the DOM backbuffer cannot be read back (no browser API rasterizes a live DOM subtree) and says so. Verified by a realemcmakebuild and a real headless-Chromium run (scripts/run-htmldom-browser-test.sh), which asserts against the produced DOM. Seedocs/html-dom-renderer.mdandplan_html_dom.md.FREEDIRECTrenderer (formerlyDIRECTX3): Cross-platform (genuinely — builds and runs via ordinary/usr/bin/c++, no MinGW/Wine needed) DirectDraw-shaped 2D renderer fronting../free-direct, a sibling project's own DirectDraw reimplementation. 2D-only by design, same spirit asSDL_RENDERER. All 8 plan phases complete: real device/window bring-up with a CPU-owned "shadow backbuffer" (working around a realLock()-on-primary gap infree-directitself), texture/render-target renderers, a CPUSpriteBatchcompositor (BltFastfast path + a from-scratch edge-function rasterizer for everything else), all 4 realBlendStatepresets with genuinely distinct formulas (not one collapsed baseline), bilinear filtering, and realWrap/Mirrortexture addressing — the latter two are a real capability win overSDL_RENDERER, which hasWrap/Mirror⛔ BLOCKED. Seedocs/freedirect-renderer.mdandplan_freedirect.md.DIRECTX1renderer: Windows-only, MinGW-cross-compiled 2D renderer talking to a real Windowsddraw.h— genuine COMIDirectDraw/IDirectDrawSurfacev1 interfaces only (neverIDirectDraw2+), run under Wine, with no../free-directreimplementation involved at all (the opposite delivery route fromFREEDIRECT). DirectX 1 shipped no Direct3D at all, so every 3D call throws by construction, not by policy. PortsFREEDIRECT's already-verified CPUSpriteBatchcompositor and blend-mode math verbatim (IDirectDrawSurface::Blthas never supported rotation in any DirectX version), while sourcing the surface layer from a real device — a real Win32HWND, a realIDirectDrawobject, and a per-frame recomputed letterboxPresent()that (unlikeFREEDIRECT) has no stale-scale bug after a resize. All 8 plan phases complete, 10/10 CTests passing through a real Wineddraw.dllrun. Seedocs/directx1-renderer.md,plan_dx1.md, andplan_dxold.md(the roadmap for the wider DIRECTX1/2/3/5/6/7/8/10 renderer family).DIRECTX2renderer: Same real Windows-only 2D layer asDIRECTX1(DirectDraw v1, ported verbatim), plus a real, working 3D pipeline — the first legacy-DirectX renderer in this family with actual 3D rendering, not a permanent throw. Built onIDirect3D2/IDirect3DDevice2'sDrawPrimitive/DrawIndexedPrimitiveimmediate-mode API, not the literal DirectX-2-SDK execute-buffer surface (IDirect3D/IDirect3DDevice::Execute), which an exhaustive 14-variant existence-gate spike found non-functional in this environment's Wine despite every API call succeeding — an owner-confirmed scope decision to deliver genuine 3D over exact-SDK-version purity.VertexBuffer/IndexBuffer(16- and 32-bit), real CPU transform + near-plane clipping submitted asD3DTLVERTEX, genuine order-independent depth-test occlusion, real one-texture sampling, full per-draw rasterizer/depth/blend/sampler state, and (Phase O9) real CPU-computed ambient + directional-light Lambertian/Blinn-Phong lighting for the two normal-bearing vertex layouts (specular composited by realD3DRENDERSTATE_SPECULARENABLEhardware) are all pixel-verified;WireFramefill mode is spike-confirmed genuinely distinct. Fog/multitexture/environment-mapping/skinning are still accepted but not evaluated, matching theSoftwarerenderer's own identical scope boundary. All 9 plan phases complete, 19/19 CTests passing through a real Wineddraw.dll+d3d.dllrun. Seedocs/directx2-renderer.md,plan_dx2.md, anddx2-spike/README.md(the execute-buffer investigation).DIRECTX3renderer: CNA's real DirectX 3 renderer (originally landed under the temporaryDX30name; renamed toDIRECTX3on 2026-08-04 when thefree-direct-backed renderer becameFREEDIRECT, then toDIRECTX3in the 2026-08 naming normalization — its historicalDX30-*task IDs are unchanged). ArchitecturallyDIRECTX2plus one upgrade: the DirectDraw object isIDirectDraw2(not v1), QueryInterface'd immediately afterDirectDrawCreateand used for every subsequent call — spike-confirmed a fully-functional drop-in for everythingDIRECTX1/DIRECTX2already do, including the entireIDirect3D2/IDirect3DDevice23D chain. Everything else (2D compositor, 3D pipeline, CPU lighting,WireFrame) is a verbatim, mechanically-ported copy ofDIRECTX2's own post-Phase-O9 code. 19/19 CTests pass, all green on the first run. Seedocs/directx3-renderer.md,plan_dx3.md, anddx3-spike/README.md.DIRECTX5renderer: CNA's real DirectX 5 renderer — the first release where execute buffers disappear entirely (IDirect3DDevice3only ever exposesDrawPrimitive/DrawIndexedPrimitive). A further port ofDIRECTX3's own 2D+3D layers: every surface (not just the top object) upgrades to v4 (IDirectDraw4/IDirectDrawSurface4/DDSURFACEDESC2/DDSCAPS2), and the 3D layer upgrades toIDirect3D3/IDirect3DDevice3/IDirect3DViewport3, submitting the sameD3DTLVERTEXstruct via the newD3DFVF_TLVERTEXFVF bitmask instead of the oldD3DVERTEXTYPEenum. Also uses a realIDirect3DViewport3::Clear2call for depth clearing, replacingDIRECTX2/DIRECTX3's manual Z-bufferLock()workaround. 19/19 CTests pass, all green on the first run. Seedocs/directx5-renderer.md,plan_dx5.md, anddx5-spike/README.md.DIRECTX6renderer: CNA's real DirectX 6 renderer — introduces no new COM interface at all (confirmed via header inspection: noIDirect3D4/IDirect3DDevice4exists), reusingIDirect3D3/IDirect3DDevice3/IDirect3DViewport3/IDirectDraw4verbatim fromDIRECTX5. Its real deliverable is genuine stencil buffer operations, resolving a boundaryDIRECTX2/DIRECTX3/DIRECTX5all explicitly documented as unavailable: a combined depth+stencil Z-buffer surface (DDPF_ZBUFFER|DDPF_STENCILBUFFER, 32-bit total, D24S8-equivalent) plus realD3DRENDERSTATE_STENCIL*write/test wiring inApplyDepthStencilState, proven end-to-end throughGraphicsDevice.DepthStencilState(stamp-then-test, order-independent). Multitexturing is deliberately deferred (D3DFVF_TLVERTEXcarries only one UV pair; genuineDualTextureEffectsupport would need a second vertex layout) and DXTn is out of scope (no consumer in CNA's content pipeline) — both documented, not silently dropped. Everything else is an unmodified port ofDIRECTX5's own 2D+3D layers. 20/20 CTests pass (19 ported + the newDirectX6_Stencil), all green on the first run. Seedocs/directx6-renderer.md,plan_dx6.md, anddx6-spike/README.md.DIRECTX7renderer: CNA's real DirectX 7 renderer — a genuine architectural change vsDIRECTX6: newIDirectDraw7/IDirect3D7/IDirect3DDevice7interfaces (created via the newDirectDrawCreateExentry point), the entire viewport object removed (noIDirect3DViewportat all any more —IDirect3DDevice7::SetViewport/Clearare direct device methods), a shorterCreateDevicesignature, and texture binding simplified to a directSetTexture(stage, surface)call (no more texture-handle indirection). Stencil is unchanged fromDIRECTX6, ported verbatim and spike-confirmed to survive all three architectural changes. Hardware T&L is genuinely available in this environment's Wine but deliberately not adopted (this renderer family submits CPU-pre-transformed-and-lit vertices by design); cube environment maps are deferred for the same class of reason asDIRECTX6's multitexture deferral. A real, empirically-found API restriction: the legacyD3DRENDERSTATE_TEXTUREMAPBLENDrender state is rejected outright by DIRECTX7 ("Render state 0x15 is invalid in d3d7"), fixed withSetTextureStageState/D3DTOP_MODULATEinstead. 20/20 CTests pass (19 ported + the renamedDirectX7_Stencil). Seedocs/directx7-renderer.md,plan_dx7.md, anddx7-spike/README.md.DIRECTX8renderer: CNA's real DirectX 8 renderer — architecturally very different fromDIRECTX1..DIRECTX7: DirectDraw and Direct3D merge in DIRECTX8, so this renderer has no DirectDraw at all, a singleIDirect3D8::CreateDevicecall creating both the device and its own real swap chain (the same device-bring-up shape asDirectX9Renderer). Delivered via DXVK 2.6.0's D8VK (Direct3DCreate8, not Wine's built-inddraw.dll), the same "Route B" pattern D3D9/D3D11/D3D12 already use. Scope is fixed-function 3D only (an owner-confirmed decision — real XNA effects needps_2_0+ regardless of Shader Model 1.x support, so a real SM1.x pipeline would not makeCreateEffectRendererusable for actual content).D3DTLVERTEXno longer exists in the real headers (D3D8 introduced the generic FVF model) — a hand-definedDirectX8TLVertexreproduces the same byte layout. No scaled-blit primitive exists at all (CopyRectsis same-size-only, noStretchRect) — solved with an internal logical-resolution render target and a letterbox-scaled full-screen-quadPresent().SpriteBatchis a genuine redesign (real GPU-textured quads through the fixed-function pipeline, not a DirectDrawBltcompositor) and blending is real GPU hardware blending with no preset-detection fallback (unlikeDIRECTX2-DIRECTX7's CPU emulation) — though D3D8 has no configurable blend equation, so factor-only-matching a preset is genuinely indistinguishable from that preset on this hardware.AnisotropicFilteringreportstrue(unlike every prior renderer in this family) since DIRECTX8 runs on a real GPU via DXVK. Two environment-specific Wine/DXVK/AMD-RADV driver bugs were found and worked around (a dedicated Wine prefix withdxgideliberately not DXVK-overridden, and forcing the software Vulkan device to avoid a real RADV bug on the second consecutivePresent()call) — both fully documented, not code defects. 20/20 CTests pass. Seedocs/directx8-renderer.md,plan_dx8.md, anddx8-spike/README.md.DIRECTX10renderer: CNA's real Direct3D 10 renderer — architecturally very different fromDIRECTX1..DIRECTX8: D3D10 (2006) removed the fixed-function pipeline entirely, so every 2D and 3D draw is a real, compiled HLSLvs_4_0/ps_4_0shader pair (D3DCompile, following this project's ownDIRECTX9/DIRECTX11precedent), notDIRECTX1..DIRECTX8's CPU-transform-and-submit model. Delivered via Wine's own builtind3d10.dll/d3d10_1.dll(thin wrappers; DXVK 2.6.0 ships nod3d10.dllat all) forwarding to DXVK's reald3d10core.dll+ DXVK'sdxgi.dll. Real state OBJECTS (ID3D10BlendState/DepthStencilState/RasterizerState/SamplerState, not per-call render states) and real MRT support (MultipleRenderTargetsreportstrue, a genuine difference from everyDIRECTX1..DIRECTX8renderer). Scope is deliberately bounded for this v1 (an owner-confirmed decision, mirroringDIRECTX1's own "baseline first, richness later" precedent):DrawColoredPrimitivesis real (vertex-color only, matchingBasicEffect(VertexColorEnabled=true)), while lighting/texturing viaDrawPrimitivesEx, custom effects, and occlusion query are left atIGraphicsRenderer's own safe defaults.SpriteBatchis real GPU-quad rendering through the same real shader pipeline. Two environment bugs were found and fixed (a stale/brokend3d10.dllsymlink inherited from an older DXVK version, and a real DXVKdxgiPresent()-path bug under Xvfb worked around by testing against the real desktopDISPLAY=:0), plus three real renderer bugs found via CTest (aD3D10_BLEND_DESCAPI-shape difference from D3D11's own per-target blend array, a 180°-rotation winding-order/culling bug, and aSpriteBatch::Begin()ordering bug that discarded a caller's own transform matrix). 10/10 CTests pass. Seedocs/directx10-renderer.md,plan_d3d10.md, anddx10-spike/README.md.GLIDErenderer: 32-bit Windows native Glide 3.x call path for a separately suppliedglide3x.dllruntime, with no rendering fallback.SpriteBatchtextures are native ARGB4444 TMU uploads and compatible quads are submitted throughgrDrawVertexArrayContiguous. The deliberately constrained real 3D path includes clipped color/textured triangle lists/strips, TMU0 mipmapping, native Z/cull/alpha-test state and the fixed-function per-vertex BasicEffect subset; it is not shader emulation or full EasyGL parity. Seedocs/glide-renderer.mdandplan_glide.md.METALrenderer (macOS only, experimental): Direct nativeMTLDevice/CAMetalLayerrendering with runtime-compiled MSL shaders. Its supported and evidence-backed boundary is documented indocs/metal-renderer.mdandplan_metal.md; iOS and tvOS remain unvalidated and are not claimed.- Verification methodology: differential testing against a real, running
FNA.dllreference implementation (tools/fna-reference/), disputed behavior settled against genuine XNA 4.0 on a Windows 7 VM, and a compile-timeCNAEXTpurity check (a dedicated CMake build option that turns every non-XNA-tagged declaration into a[[deprecated]]warning under-Werror) — seeCHECKLIST.md's "CNAEXT markers" section andCMakeLists.txt. - Automatic CI is partial (see
.github/workflows/): Linux workflows run theInputandDevices/Sensorsgtest suites, not the full ~4,370-test unit suite or the complete GPU pixel matrix. A dedicated macOS 14 workflow builds the native Metal renderer and runs only its supported contract tests; a separate manual-dispatch Windows MSVC workflow covers D3D11/D3D12/Direct2D renderer CTests, with the Direct2D leg recording native debug-layer, WARP, and runtime/adapter artifacts. There is no automatic Windows or Android gate, and the macOS gate does not establish support for the Metal paths documented as unsupported.
- Recreate the XNA developer experience in native C++.
- Provide a native C++ path for teams that like the XNA/MonoGame model but need non-managed runtime/toolchain control.
- Mirror core XNA namespaces and API patterns while implementing them incrementally.
- Decouple gameplay-facing API from rendering renderer implementation details.
- Enable one high-level API surface across different rendering technologies.
- Keep SDL/OpenGL/Vulkan-level concerns behind framework abstractions.
- Public API uses XNA-style namespaces, especially under
Microsoft::Xna::Framework. - Core game loop and framework primitives are available (
Game,GameTime, graphics types, input/audio surfaces). - Compatibility is partial and evolving; implementation status is tracked progressively in source.
Keyboard,Mouse(incl.MouseCursor),GamePad(up to 4 players),TouchPanel/TouchCollection, and theGestureDetectorgesture recognizer (Tap/DoubleTap/Hold/Drag/Flick/Pinch) — all underMicrosoft::Xna::Framework::Input, matching FNA/XNA 4.0 behavior member-for-member. Seeplan_input.mdfor the full FNA-parity audit record.- CNAEXT extensions beyond stock XNA:
TextInputEXT(IME composition), rumble/trigger-rumble/light-bar/ gyro/accelerometer onGamePad, rawCNA::Input::Joysticks(distinct fromGamePad's mapped view), device-levelCNA::Input::Sensors/Power, andCNA::Input::Hapticsfor standalone haptic devices. - Single platform-event funnel (
IPlatform::PollEvents→PlatformInputBridge::ProcessEvent), renderer-agnostic and independent of the selected native event source. SDL3 translation stays inside its platform implementation; theCnaTestsinput suite verifies the shared state path.
GraphicsDeviceabstraction with renderer delegation.SpriteBatchAPI withBegin(...)/Draw(...)/End()workflow.Texture2Dabstraction with renderer-owned texture resources.
- glTF 2.0 loads directly:
Content.Load<Model>("character.glb")— no offline step. An offline converter (tools/gltf_to_cnj) produces.cnj+ binary sidecars for the same asset, and the two loaders are held to identical output by a per-fixture parity sweep. - Geometry, PBR materials, skinning, animation (LINEAR/STEP/CUBICSPLINE), morph targets, cameras and punctual lights all import. What that costs is stated rather than implied: XNA's model is four joint influences and three directional lights, two sampled UV channels, and one colour channel — glTF data beyond those is counted and reported, never silently dropped.
- Correctness is held by a generated 145-asset conformance corpus: the exact L0–L6 numerical ladder covers container, accessor, semantic mesh, world geometry, packed GPU bytes and bound effect parameters per commit (including ASan + UBSan), then the production OPENGLES3 viewer supplies the final deterministic L7 image/disposition gate.
- Read
docs/gltf-limitations.mdbefore choosing CNA for a glTF pipeline. It lists every approximation and every unsupported feature next to the report field that names the loss at run time.CNAEXT.md§3.2 carries the same information as a per-capability status table.
- SDL3-based platform foundation for windowing/input/audio integration.
- Renderer abstraction supports targeting multiple rendering paths from one API layer.
- Windows support via the
SDL_RENDERERrenderer (MSVC, clang-cl, or MinGW-w64) — cross-compiled with MinGW-w64 and verified running under Wine. - Linux support via
OPENGLES3/OPENGL33(OpenGL) orSDL_RENDERER. - Web (Emscripten) and Android (NDK) targets are implemented and verified, not just
architecturally planned — see section 7 (Networking, Services & Avatar) below for real
cross-platform
Netverification on both. - macOS has a native CI build/test gate; iOS/iPadOS is experimental platform support.
The Apple workflow final-links an actual
.appfor device and simulator and launches a one-frameGamesmoke application in the simulator. There is still no physical-device, pixel, touch, audio, storage or performance evidence. The boundary is stated per claim indocs/apple-platforms.md.
- Native C++23 codebase and explicit control over memory/lifetime.
- Interface-driven renderer boundaries to keep hot rendering paths renderer-specific.
- Lightweight gameplay-facing API over renderer-specific implementations.
CNA is organized into clear layers with strict responsibility boundaries:
+-----------------------------------------------------------+
| Game / Application Code |
| (uses Microsoft::Xna::Framework API) |
+------------------------------+----------------------------+
|
v
+-----------------------------------------------------------+
| API Layer (XNA-style public surface) |
| include/Microsoft/Xna/Framework/... |
| - Game, GraphicsDevice, SpriteBatch, Texture2D, ... |
+------------------------------+----------------------------+
|
v
+-----------------------------------------------------------+
| CNA Internal Layer (abstractions/factories) |
| include/CNA/Internal/Renderers + src/CNA/Internal/Renderers |
| - IGraphicsRenderer, ISpriteBatchRenderer, ITextureRenderer |
+------------------------------+----------------------------+
|
v
+-----------------------------------------------------------+
| Renderer Implementations |
| src/CNA/Internal/Renderers/{SdlRenderer,EasyGL,Vulkan,Skia} |
+-----------------------------------------------------------+
- Public API lives under
include/Microsoft/...and stays framework-facing. - Renderer contracts live under
CNA::Internal::Renderersinterfaces. - Renderer implementations live under
src/CNA/Internal/Renderers/.... GraphicsDeviceconstructs renderers via factory (CreateGraphicsRenderer(...)) based on build-time renderer selection.
SpriteBatch is the primary 2D rendering abstraction.
- You create it against a
GraphicsDevice. - Call
Begin(...)to start a draw pass. - Issue
Draw(...)calls for textures/sprites. - Call
End()to close the batch.
The API surface is renderer-agnostic, while rendering behavior is executed by renderer-specific ISpriteBatchRenderer implementations.
This keeps game code stable while allowing renderer-specific optimizations in SDL renderer, EasyGL, Vulkan, Skia, and the other selected paths.
CNA exposes 49 public renderer identities through CNA_GRAPHICS_RENDERER (choose one per build
configuration). The canonical registration, implementation-sharing, capability, and platform-gate
inventory is docs/renderer-registry.md.
Renderers are normally chosen at compile time, one per build. CNA can also be built with several renderers and the concrete one chosen at runtime, before the game starts:
#include "CNA/GraphicsRendererSelection.hpp"
CNA::GraphicsRendererSelection::SetPreferred(CNA::GraphicsRendererType::Vulkan);A renderer that is unavailable or fails to start is an error by default — CNA never silently substitutes another. An opt-in fallback chain is available when a game wants one. See docs/runtime-renderer-selection.md.
The former ASCII renderer identity was removed 2026-08 in favor of a renderer-neutral post-process
effect, CNA::Graphics::AsciiPostProcessEffect (modules/graphics-ext/), usable with any renderer's
RenderTarget2D output — see docs/ascii-post-process-effect.md.
SDL_RENDERERSDL_GPUOPENGLES2(native OpenGL ES 2.0, GLSL ES 1.00; internal implementation: EasyGL -- seedocs/opengles2-renderer.md)OPENGLES3(internal implementation: EasyGL)OPENGL33(internal implementation: EasyGL)WEBGL1(Emscripten only; internal implementation: EasyGL)WEBGL2(Emscripten only; internal implementation: EasyGL)BGFXVULKANWEBGPUMAGNUMSKIABLEND2D(CPU 2D vector rasterizer via Blend2D, presented through a streaming SDL_Renderer texture -- seedocs/blend2d-renderer.md)HEADLESSSOFTWARESTUBOPENGLES1(genuine OpenGL ES 1.1 fixed-function, "Common"/CM profile -- independent of theOPENGLES2/OPENGLES3/OPENGL33/WEBGL1/WEBGL2family (internally EasyGL, a shader-based programmable pipeline), which cannot create an ES 1.1 context; needs a real systemlibGLESv1_CM)OPENGL4(real desktop OpenGL 4.x core profile -- deliberately independent of theOPENGLES2/OPENGLES3/OPENGL33/WEBGL1/WEBGL2family (internally EasyGL), which cannot create a desktop 4.x core-profile context)OPENGL1(legacy desktop OpenGL 1.x fixed-function -- Historical class, independent of the EasyGL-implemented GL family and ofOPENGL4; desktop Linux/Windows only)OPENGL2(native desktop OpenGL 2.1 compatibility profile, GLSL 1.10 -- independent of the EasyGL-implemented GL family and of the other GL renderers)DIRECTX9(Windows-only; native Direct3D 9 running Microsoft's own vendored Stock Effects HLSL bytecode)DIRECTX11(Windows-only)DIRECT2D(Windows-only, 2D-only)DIRECTX12(Windows-only)CANVAS(Emscripten only)HTML_DOM(Emscripten only)SVG_DOM(Emscripten only, 2D-only; SpriteBatch output as real<svg>/<image>DOM elements -- seedocs/svg-dom-renderer.md)FREEDIRECT(formerlyDIRECTX3)DIRECTX1(Windows-only)DIRECTX2(Windows-only)DIRECTX3(Windows-only; CNA's real DirectX 3 renderer -- seeplan_dx3.md)DIRECTX5(Windows-only; CNA's real DirectX 5 renderer -- DirectDraw v4 + Direct3D v3 FVFDrawPrimitive)DIRECTX6(Windows-only; CNA's real DirectX 6 renderer -- same interfaces as DIRECTX5, plus real stencil buffer operations)DIRECTX7(Windows-only; CNA's real DirectX 7 renderer -- new IDirectDraw7/IDirect3D7/IDirect3DDevice7, viewport object removed, direct texture binding)DIRECTX8(Windows-only; CNA's real DirectX 8 renderer -- no DirectDraw at all, real IDirect3D8/IDirect3DDevice8 via DXVK's D8VK, fixed-function 3D only)DIRECTX10(Windows-only; CNA's real Direct3D 10 renderer -- no fixed-function pipeline at all, real HLSL vs_4_0/ps_4_0 shaders via Wine's own d3d10.dll + DXVK's d3d10core.dll, real MRT)WICKED(Wicked Engine'swi::graphicsRHI; Linux/Windows, needs a Wicked Engine checkout)SOKOL(sokol_gfx single-header GPU abstraction; dispatches onto desktop OpenGL 4.1 core here)DILIGENT(experimental)GLIDE(32-bit Windows-only; requires externalglide3x.dllat runtime)GDI(Windows-only, 2D-only)LLGL(experimental; accepted support is OpenGL on Linux/X11/x86_64)METAL(macOS only, experimental — seedocs/metal-renderer.md)FNA3D(FNA's own XNA-shaped graphics library; picks SDL_GPU/Direct3D 11/OpenGL at runtime, and executes XNA's actual stock effects — seedocs/fna3d-renderer.md)OPENVG(OpenVG 1.1 2D vector graphics via ShivaVG on a desktop OpenGL context; desktop Linux/Windows/macOS -- seedocs/openvg-renderer.md)PORTABLEGL(CPU software OpenGL 3.x-ish pipeline viarswinkle/PortableGL; no GPU/window required -- seedocs/portablegl-renderer.md)TINYGL(CPU fixed-function OpenGL 1.x subset viaC-Chads/tinygl; 1-bit colour-key transparency, no stencil/scissor/render targets/shaders -- seedocs/tinygl-renderer.md)IGL(facebook/igl "Intermediate Graphics Library"; drives IGL's own OpenGL/GLX or Vulkan backend, selected per process withCNA_IGL_BACKEND-- seedocs/igl-renderer.md)
-
SDL_Renderer renderer
- Simpler integration and broad SDL portability.
- Good for straightforward 2D workflows.
-
EasyGL renderer (OpenGL-based path through
easy-gl)- Custom shader-driven rendering path.
- Better control over rendering behavior and extensibility than fixed SDL renderer usage.
-
Direct2D renderer (Windows-only 2D path)
- Native accelerated
SpriteBatch,Texture2DandRenderTarget2Dthrough Direct2D 1.1. - Explicitly excludes 3D/depth/MRT/custom shaders; use
DIRECTX11for those features.
- Native accelerated
-
BGFX renderer
- Dedicated renderer option with the same public rendering API coverage as other renderers.
- Integrates through CNA renderer abstraction and can be selected via
CNA_GRAPHICS_RENDERER=BGFX. - Uses native
bgfxAPI (window/platform init, texture creation, sprite draws, frame submission), notSDL_Rendererrendering. bgfxis integrated in CMake for this renderer viaFetchContent(bgfx.cmake).
-
Diligent Engine renderer (experimental)
- The only renderer that does not target one native API: DiligentCore is itself an abstraction over Direct3D 11/12, Vulkan, OpenGL and Metal, so the native API is chosen at runtime (
D3D12→Vulkan→D3D11→OpenGL, overridable with theCNA_DILIGENT_DEVICEenvironment variable). - Shaders are authored once in HLSL and cross-compiled by Diligent for whichever device was selected.
- Implements a 2D/3D baseline only — render targets, cube/volume textures, MSAA and most stock effects are not implemented yet and refuse loudly instead of approximating. See
docs/diligent-renderer.mdandplan_diligent.md.
- The only renderer that does not target one native API: DiligentCore is itself an abstraction over Direct3D 11/12, Vulkan, OpenGL and Metal, so the native API is chosen at runtime (
-
Vulkan renderer
- Present as an architecture target/scaffold.
- Current implementation is incomplete and contains TODO/stub areas.
-
Skia renderer
- Select with
CNA_GRAPHICS_RENDERER=SKIA; requires the explicitly pinned external raster artifact rather than downloading a dependency during ordinary CMake configuration. - Provides the verified CPU-raster 2D boundary above. It is not a fallback alias for EasyGL, and SDL is used only to present the completed CPU image.
- See
docs/skia-renderer.mdfor the exact capability policy anddocs/skia-developer-build.mdfor the pinned artifact, fresh-checkout build, Xvfb tests, fallback policy, and diagnostics.
- Select with
-
GDI renderer (Windows-only, 2D-only)
- CPU-rasterizes CNA's 2D SpriteBatch/textures and presents the resulting RGBA8 backbuffer to
the SDL window's native
HWNDusing classic Win32 GDI. - Intended for compatibility, tools, UI, and modest-resolution 2D games; it is not a hardware-
accelerated replacement for the D3D/SDL_GPU renderers and explicitly rejects 3D operations.
See
docs/gdi-renderer.mdfor its exact supported surface.
- CPU-rasterizes CNA's 2D SpriteBatch/textures and presents the resulting RGBA8 backbuffer to
the SDL window's native
Beyond graphics, CNA ports the XNA 4.0 GamerServices and Net namespaces (and, within
GamerServices, the Avatar subsystem), with real cross-platform networking behind them.
- Complete XNA-shaped port of the Xbox LIVE-era gamer services API surface:
Gamer,SignedInGamer,GamerProfile,FriendGamer/FriendCollection, leaderboards (LeaderboardReader/LeaderboardWriter/LeaderboardEntry),Guide, achievements, and more. - Not binary-compatible with real Xbox Live — reimplements the public API shape with local/synthetic semantics, matching how FNA itself already handles this namespace.
- Complete
NetworkSessionAPI surface (5 enums + 18 classes). - Real networking for
NetworkSessionType::SystemLink, backed by ENet (reliable UDP, vendored directly underthird_party/enet) — hosting, joining, LAN discovery,AppDatarelay, disconnect handling, andStartGame/EndGamestate broadcast all run over a genuine transport, not a stub. Every otherNetworkSessionTyperemains a synthetic (non-networked) stub, matching upstream XNA/FNA behavior. - Verified real networking across four platforms:
- Linux — native ENet/UDP, including a genuine two-OS-process loopback test.
- Windows — native ENet/UDP via WinSock2; cross-compiled with MinGW-w64 and verified running under Wine.
- Web (Emscripten) — real ENet traffic carried over actual WebSocket connections. A browser tab can only ever be a network client (browsers cannot open listening sockets at all); real hosting requires a Node.js-run process.
- Android (NDK) — native ENet/UDP via bionic libc's genuine POSIX sockets, verified on a real x86_64 emulator — no platform-specific transport workarounds needed at all, unlike Web.
AvatarAnimation,AvatarDescription,AvatarRenderer, and their supporting enums/types (all withinMicrosoft::Xna::Framework::GamerServices) are ported from a decompiled real Microsoft XNA 4.0 reference assembly — FNA itself never implemented Avatar, since real avatar rendering required Xbox Live's cloud avatar-editor service. The API shape is complete, with the real (occasionally surprising, always-inert) stubbed behavior of the original assembly preserved faithfully rather than "improved."
- Language: C++23
- Core platform/runtime library: SDL3 (vendored via Git submodule at
third_party/SDL) - Media integration:
SDL3_image,SDL3_mixer(vendored via Git submodules) - Graphics dependency:
easy-gl(for theOPENGLES2/OPENGLES3/OPENGL33/WEBGL1/WEBGL2renderers), resolved from the canonical../easy-glsibling; EasyGL in turn resolves../meta-gl - Networking: ENet (vendored directly at
third_party/enet) — reliable-UDP transport backingMicrosoft::Xna::Framework::Net'sSystemLinksessions - Utility/runtime layer:
sharp-runtime - Build system: CMake
- Tests: GoogleTest (
CnaTeststarget)
- CMake 3.20+
- C++23-capable compiler (GCC 12+ or Clang 15+)
- Dependency directories available to CMake:
../sharp-runtime../easy-gland../meta-gl(needed for theOPENGLES2/OPENGLES3/OPENGL33/WEBGL1/WEBGL2renderers)
- SDL3, SDL3_image, and SDL3_mixer are built from vendored submodules by default — no system SDL packages required.
- CMake 3.20+
- One of:
- MSVC 2022 (Visual Studio 2022, v17.8+, with C++20/23 support)
- clang-cl (LLVM for Windows, targeting MSVC ABI)
- MinGW-w64 (either natively on Windows or cross-compiled from Linux)
- Dependency directories:
../sharp-runtime(no external dependencies — builds cleanly on Windows)
- SDL3, SDL3_image, and SDL3_mixer are built from vendored submodules by default — no pre-built SDL binaries or
CMAKE_PREFIX_PATHconfiguration required.
Before the first build, initialise the vendored SDL submodules:
git submodule update --init --recursiveThis populates third_party/SDL, third_party/SDL_image, and third_party/SDL_mixer.
After that, no system SDL packages are required.
Building from a source zip/tarball instead of a Git clone? GitHub's "Download ZIP" and release archives do not include submodule contents, so
third_party/SDLwill be empty and CMake aborts with a clear error (Missing vendored 'SDL' … Run: git submodule update --init --recursive, fromcmake/ThirdPartySDL.cmake). Either clone with Git and run the command above, or set-DCNA_USE_SYSTEM_SDL=ONto use system-installed SDL3 packages.
git submodule update --init --recursive
cmake -S . -B build -DCNA_GRAPHICS_RENDERER=OPENGLES3
cmake --build build --target CnaTestsgit submodule update --init --recursive
cmake -S . -B build-sdlrenderer -DCNA_GRAPHICS_RENDERER=SDL_RENDERER
cmake --build build-sdlrenderer --target CnaTestsOn Windows the SDL_RENDERER renderer is selected automatically when no renderer is
explicitly specified. SDL is built from the vendored submodule — no pre-built SDL
binaries or CMAKE_PREFIX_PATH needed.
git submodule update --init --recursive
cmake -S . -B build-win -DCNA_GRAPHICS_RENDERER=SDL_RENDERER
cmake --build build-win --target CnaTests# Install cross toolchain
sudo apt install mingw-w64
git submodule update --init --recursive
cmake -S . -B build-windows \
-DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/mingw-w64.cmake \
-DCNA_GRAPHICS_RENDERER=SDL_RENDERER
cmake --build build-windows --target CnaTestsbrew install ccache ffmpeg
git submodule update --init
cmake -S . -B cmake-build-macos -DCNA_GRAPHICS_RENDERER=SDL_RENDERER
cmake --build cmake-build-macos --target CnaTests --parallel 4METAL is available here as well (-DCNA_GRAPHICS_RENDERER=METAL); its own supported contract is
narrower than "it builds" — see docs/metal-renderer.md.
Requires a macOS host with Xcode. This produces a final-linked cna_ios_smoke.app for a device
or simulator; the Apple workflow also launches its one-frame Game path in the simulator. This
is not evidence for a physical device or correct pixels/input/audio/storage — see
docs/apple-platforms.md for the exact boundary.
cmake -S . -B cmake-build-ios \
-DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/ios.cmake \
-DCNA_GRAPHICS_RENDERER=SDL_RENDERER \
-DCNA_BUILD_TESTS=OFF -DCNA_BUILD_EXAMPLES=OFF
cmake --build cmake-build-ios --parallel 4
# Simulator: add -DCNA_IOS_SIMULATOR=ON (use a separate build directory).
# Device deployment needs the Xcode generator and a team id:
# -G Xcode -DCNA_APPLE_DEVELOPMENT_TEAM=<TEAMID>If you prefer to link against system-installed SDL3 packages instead of the
vendored submodules, pass -DCNA_USE_SYSTEM_SDL=ON:
cmake -S . -B build -DCNA_USE_SYSTEM_SDL=ON -DCNA_GRAPHICS_RENDERER=SDL_RENDERER
cmake --build build --target CnaTestsThis calls find_package(SDL3 REQUIRED), find_package(SDL3_image REQUIRED),
and find_package(SDL3_mixer REQUIRED) and requires those packages to be present
on the system (e.g. installed via your package manager).
cmake -S . -B build -DCNA_GRAPHICS_RENDERER=BGFX
cmake -S . -B build -DCNA_GRAPHICS_RENDERER=VULKANGLIDE is a real historical Glide 3.x call path, not an SDL rendering fallback. It loads a
separately supplied glide3x.dll at runtime (typically an emulator such as dgVoodoo2). It supports
2D SpriteBatch draws and a constrained fixed-function color/textured 3D path, both submitted as
real Glide triangles. See
docs/glide-renderer.md for runtime setup and limitations.
cmake -S . -B cmake-build-glide -G Ninja \
-DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/mingw-w64-i686.cmake \
-DCNA_GRAPHICS_RENDERER=GLIDE \
-DCNA_BUILD_TESTS=ON
cmake --build cmake-build-glide --target cna_glide_smoke -j2Copy the external emulator's compatible glide3x.dll beside cna_glide_smoke.exe (or set
CNA_GLIDE3X_DLL) before running it under Windows or Wine. CNA does not redistribute that DLL.
A native Direct3D 9 renderer, Windows-only (hard-FATAL_ERROR-gated at configure time, same as
D3D11/D3D12), targeting real XNA 4.0 pixel authenticity rather than just feature parity —
see docs/directx9-renderer.md for what that means and why. Developed and
verified on this repo's own Debian dev machine via the same MinGW-w64 cross toolchain the other
Windows renderers use, tested locally through Wine + DXVK (scripts/run-wine-dxvk9.sh). See
docs/directx9-renderer.md and plan_dx9.md for full detail.
# Install cross toolchain (same package D3D11/D3D12/SDL_RENDERER's own Windows cross-build uses)
sudo apt install mingw-w64
git submodule update --init --recursive
cmake -S . -B cmake-build-d3d9 \
-DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/mingw-w64.cmake \
-DCNA_GRAPHICS_RENDERER=D3D9 \
-DCNA_BUILD_TESTS=ON
cmake --build cmake-build-d3d9 --target CnaTests --parallel 4Running the resulting .exes needs a Wine + DXVK dev-loop, in a prefix separate from D3D11's own
(docs/directx9-renderer.md has full setup steps); CTest wires this in automatically:
ctest --test-dir cmake-build-d3d9 -L D3D9 --output-on-failureA native Direct3D 11 renderer, Windows-only (hard-FATAL_ERROR-gated at configure time on any other
CMAKE_SYSTEM_NAME). Developed and verified on this repo's own Debian dev machine via the same
MinGW-w64 cross toolchain SDL_RENDERER uses, tested locally through Wine + DXVK
(scripts/run-wine-dxvk.sh) before any real-Windows verification pass. See
docs/directx11-renderer.md and plan_dx.md for full detail.
# Install cross toolchain (same package SDL_RENDERER's own Windows cross-build uses)
sudo apt install mingw-w64
git submodule update --init --recursive
cmake -S . -B cmake-build-d3d11 \
-DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/mingw-w64.cmake \
-DCNA_GRAPHICS_RENDERER=D3D11 \
-DCNA_BUILD_TESTS=ON
cmake --build cmake-build-d3d11 --target CnaTests --parallel 4Running the resulting .exes needs a Wine + DXVK dev-loop (docs/directx11-renderer.md has full setup
steps); CTest wires this in automatically:
ctest --test-dir cmake-build-d3d11 -R D3D11 --output-on-failureA native Direct3D 12 renderer, Windows-only (hard-FATAL_ERROR-gated at configure time, same as
D3D11). Also developed and verified on this repo's own Debian dev machine via the same MinGW-w64
cross toolchain, but tested locally through Wine + vkd3d-proton (scripts/run-wine-vkd3d.sh),
not DXVK — D3D12 needs a different Windows-D3D-to-Vulkan translation layer than D3D11, with its own
dedicated Wine prefix. Every check currently runs off-screen only — swap-chain presentation is
a known, real, unresolved gap on this dev loop (see docs/directx12-renderer.md). See
docs/directx12-renderer.md and plan_dx.md for full detail.
# Install cross toolchain (same package D3D11/SDL_RENDERER's own Windows cross-build uses)
sudo apt install mingw-w64
git submodule update --init --recursive
cmake -S . -B cmake-build-d3d12 \
-DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/mingw-w64.cmake \
-DCNA_GRAPHICS_RENDERER=D3D12 \
-DCNA_BUILD_TESTS=ON
cmake --build cmake-build-d3d12 --target CnaTests --parallel 4Running the resulting .exes needs a Wine + vkd3d-proton dev-loop, in a prefix separate from
D3D11's own (docs/directx12-renderer.md has full setup steps); CTest wires this in automatically:
ctest --test-dir cmake-build-d3d12 -R D3D12 --output-on-failureThis repository intentionally prioritizes framework/runtime development over shipping a bundled game demo executable.
Use these commands for quick environment and rendering-path verification:
ctest --test-dir build --output-on-failure
cmake --build build --target hello-triangle-sdl| Platform | Compiler | Renderer | Status |
|---|---|---|---|
| Linux x86_64 | GCC 12+ | OPENGLES3, SDL_RENDERER | ✅ |
| Linux x86_64 | Clang 15+ | OPENGLES3, SDL_RENDERER | ✅ |
| Windows x86_64 | MSVC 2022 | SDL_RENDERER | planned |
| Windows x86_64 (native) | MinGW-w64 | SDL_RENDERER | planned |
| Linux → Windows (cross) | MinGW-w64 | SDL_RENDERER | ✅ verified building + full test suite under Wine |
| Linux → Windows (cross) | MinGW-w64 | D3D9 | ✅ verified building + D3D9 CTest suite (14 tests) under Wine+DXVK on a real GPU — 0/31 oracle scenes diverge from real XNA 4.0 at --tolerance 0; real Windows hardware verification still open, see docs/directx9-renderer.md |
| Linux → Windows (cross) | MinGW-w64 | D3D11 | ✅ verified building + D3D11 CTest suite (6 tests, 96+ checks) under Wine+DXVK on a real GPU — real Windows hardware verification still open, see docs/directx11-renderer.md |
| Linux → Windows (cross) | MinGW-w64 | D3D12 | ✅ verified building + D3D12 CTest suite (1 test, 80/80 checks, off-screen only) under Wine+vkd3d-proton on a real GPU — swap-chain presentation and real Windows hardware verification both still open, see docs/directx12-renderer.md |
| Web (Emscripten) | emcc/Clang (emsdk) | WEBGL2 | ✅ verified building + running under Node.js (as EASYGL, prior to the plan_glbackends.md rename — not yet re-verified under its new WEBGL2 name/build flags) |
| Android (NDK) | Clang (NDK 29/30) | OPENGLES3 | ✅ verified building + running on a real x86_64 emulator (as EASYGL, prior to the plan_glbackends.md rename) |
Minimal XNA-style game skeleton in CNA:
#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;
}- API mirroring strategy: Public classes follow XNA naming and namespace conventions to reduce conceptual migration cost from XNA/MonoGame-style code.
- Abstraction design: Gameplay-facing rendering APIs (
GraphicsDevice,SpriteBatch,Texture2D) delegate to renderer interfaces instead of exposing low-level renderer objects. - Separation of concerns: Public framework API, internal contracts, and renderer implementations are physically separated in directory structure and ownership.
- Renderer-oriented architecture: Renderer can be swapped at build-time with a single CMake option while keeping high-level game code stable.
- Performance-minded C++ implementation: Native code path enables tighter control over memory, lifetime, and rendering behavior than managed runtime abstractions.
- Continue expanding XNA API coverage and behavior parity (incremental, class-by-class).
- Extend compiled XNA Effect Framework bytecode beyond the completed
FNA3Dimplementation. Each additional renderer remains gated off until it passes the shared reflection, state, lifecycle, 3D, and SpriteBatch conformance contract; fixed-function/2D-only renderers stay explicitly unsupported. - Consider a real
.xnbcontent-pipeline reader (currently a deliberate design choice, not a bug — CNA loads raw assets + JSON descriptors instead). - Close the remaining named architecture-decision gaps (SDL_Renderer
TextureAddressMode::Wrap/Mirror, SDL_RendererTexture3D/TextureCubeconstruction, EasyGL non-ColorSurfaceFormatGPU forwarding,Texture3D/TextureCubesampler-bind architecture) — seeNEXT.md§5 anddocs/graphics-renderer-feature-matrix.md. - Strengthen cross-platform execution targets and validation coverage.
CNA is licensed under the Microsoft Public License (Ms-PL). See the LICENSE file for details.
Portions of CNA are derived from or based on FNA, which is also licensed under the Microsoft Public License (Ms-PL).