Verification & Known Issues

How CNA's XNA compatibility is actually checked — and everything currently known to be broken

All figures on this page were taken from the CNA source tree and its test runs, not from a changelog. If a number here disagrees with a blog post or a README, trust this page — and if it disagrees with what ctest prints on your machine, trust ctest.

How CNA is verified

An XNA reimplementation can pass its own test suite perfectly and still be wrong, because the tests encode the author's belief about what XNA does. CNA works around that with five layers of checking, three of which compare against something outside the project.

Layer What it checks Scale
Unit tests (GoogleTest) Math, geometry, curves, packed vectors, game-loop semantics, state objects, input, audio, networking. Runs headless. 4,373 tests
GPU pixel-readback tests (ctest) Renders a scene on a real GPU context, reads the framebuffer back, and asserts on actual pixel values. This is what catches "the shader compiles but draws the wrong thing." 490 tests across 4 backends
Differential testing against FNA A C# harness links the real, running FNA.dll and dumps reference values to JSON; a matching C++ tool dumps CNA's; a script diffs them key by key. Ground truth comes from executing FNA, not from reading its source. tools/fna-reference/, tools/cna-reference/
Validation against real XNA 4.0 Where FNA itself might be wrong, behaviour has been checked against genuine XNA 4.0 running on a Windows 7 VM. The default CullMode audit was settled this way. Ad hoc, for disputed behaviour
API-purity compile check Every CNA method that is not part of XNA 4.0 is tagged NOXNA. A dedicated build target turns that tag into [[deprecated]] and compiles with -Werror, so the build breaks if XNA-surface code ever leans on a CNA extension. 957 tagged declarations
💡

The NOXNA tag is worth understanding if you care about portability. It means you can always tell, at compile time, whether you are writing code that would also compile against real XNA — build with -DCNA_STRICT_XNA_API and any use of a CNA-only extension becomes a hard error.

Current test results

Backend selection is a compile-time choice, so each backend gets its own full test run.

Backend Unit tests GPU pixel tests Status
EasyGL (OpenGL ES 3.x) 4,371 / 4,373 188 / 190 Primary backend
Vulkan 4,371 / 4,373 125 / 126 Full 2D + 3D
bgfx 4,375 / 4,377 102 / 104 Full 2D + 3D
SDL_Renderer 4,371 / 4,373 67 / 67 2D only, by design

In every run, the 2 non-passing unit tests are skips, not failures: they exercise the Accelerometer and Gyroscope classes and need physical sensor hardware that a build machine does not have. The unit suite has no known failures.

The GPU suite is where the 5 real open failures live. They are enumerated below — none of them is unexplained.

What CI covers (and what it doesn't)

Be clear-eyed about this: GitHub Actions runs only two workflows, both on Linux, and between them they cover the Input and Devices/Sensors suites — not the full 4,373-test suite and not the 490-test GPU suite. Everything else is verified by running ctest locally. There is no Windows, macOS, Android, or WebAssembly CI job at all.

Verified by What
Automated CI Linux x86_64. Input suite on all four backends; Devices/Sensors suite under UndefinedBehaviorSanitizer; an AddressSanitizer + UBSan build.
Local runs only The full unit suite, the entire 490-test GPU pixel suite, all Graphics/Audio/Net verification, the Emscripten web build, and the MinGW-w64 Windows cross-build.
Not currently building Android (NDK). Blocked on build regressions in the sibling sharp-runtime repository, not in CNA itself.

Known open issues

This is the complete list as of the current source tree. Each item is tracked in the project's own task log.

Rendering

Issue Backend Status
A RenderTargetCube face with a Depth24Stencil8 attachment produces no colour output at all. Investigated three times with apitrace and RenderDoc; root cause not found. bgfx Deferred
Two-attachment MRT: the second attachment stays black. EasyGL Open
RasterizerState.DepthBias does not take effect. Vulkan Open
MSAA render-target resolve fails. This is an environment limitation — the test machine's bgfx path negotiates a legacy GL 2.1 software context. bgfx Environmental
GraphicsDevice.ReferenceStencil's independent-override semantics are not wired to the backend. EasyGL, bgfx Open (fixed on Vulkan)
DrawIndexedPrimitives silently ignores startIndex/baseVertex, always drawing from 0. Not hit by any current sample, since every ModelMeshPart owns its own buffer — but it will bite any genuine sub-range draw. bgfx Open
A full-backbuffer SpriteBatch draw issued before any 3D draw in the same frame breaks that frame's 3D rendering. EasyGL Open

Effects

  • EnvironmentMapEffect and SkinnedEffect do not forward DirectionalLight1 / DirectionalLight2 — only light 0 reaches the GPU.
  • SkinnedEffect has no specular implementation, and WeightsPerVertex is a no-op on the GPU side.

API gaps that throw at runtime

Member Behaviour
Effect(GraphicsDevice&, byte[]) Throws NotImplementedException. This is the compiled .fx bytecode constructor — see Deliberate deviations.
BoundingFrustum::Intersects(Ray) Throws NotImplementedException. Every other BoundingFrustum overload works.
NetworkMachine::RemoveFromSession() Throws NotImplementedException — matching FNA, which also stubs this.
ResourceContentManager::OpenStream() Throws. The class is an API placeholder only.
MediaLibrary and the whole media-catalog tree (Album, Artist, Genre, Picture, Playlist and their collections) All 14 types are API-shaped shells — every non-trivial member throws. There is no OS media-library integration behind them. Song, MediaPlayer, Video and VideoPlayer, by contrast, are fully real.
LeaderboardReader / LeaderboardWriter, Gamer::GetFromGamertag, Guide::ShowMessageBox Throw or no-op. These need Xbox LIVE, which no longer exists for third-party titles — FNA stubs them out for the same reason.

Needs an architecture decision

  • Texture3D and TextureCube derive from GraphicsResource rather than Texture, so neither can be bound as a shader sampler through the generic EffectParameter route.
  • GraphicsDevice holds BlendState / DepthStencilState / RasterizerState by value. XNA and FNA use reference semantics, so mutating a state object after assigning it has no effect in CNA where it would in XNA.

Deliberate deviations from XNA

These are not bugs and are not on a roadmap to be "fixed" — they are design decisions with real consequences for porting, so plan around them.

XNA does CNA does Why it matters
Loads assets from .xnb files produced by the Content Pipeline Loads raw source assets by extension (.png, .wav, .ogg, .dds, .mp4…) plus JSON descriptors for fonts, models and shaders A compiled .xnb file cannot be loaded by CNA at all. Porting a game means re-authoring its content, not copying its Content folder.
Ships custom shaders as compiled .fx bytecode Requires hand-written GLSL/SPIR-V via the CNA-only ShaderEffect class The five stock effects (BasicEffect and friends) work everywhere. Any custom HLSL shader must be rewritten by hand, per backend.
Selects a graphics device at runtime Selects the backend at compile time via CNA_GRAPHICS_BACKEND One binary targets exactly one backend. There is no runtime fallback path.
Supports Xbox LIVE matchmaking (PlayerMatch, Ranked) Implements only SystemLink for real — genuine UDP over ENet with LAN broadcast discovery. Other session types are synthetic. LAN multiplayer works across real machines. Internet matchmaking does not exist.

Reproducing these numbers yourself

Nothing here requires special access. Clone the repositories side by side and run the suite:

git clone https://github.com/openeggbert/cna.git
git clone https://github.com/openeggbert/sharp-runtime.git
git clone https://github.com/openeggbert/easy-gl.git
cd cna
git submodule update --init --recursive

cmake -S . -B build -DCNA_GRAPHICS_BACKEND=EASYGL
cmake --build build --target CNA CnaTests

# unit suite
./build/CnaTests

# GPU pixel suite (needs a GL context; use xvfb-run on a headless box)
ctest --test-dir build --output-on-failure

Swap EASYGL for VULKAN, BGFX or SDL_RENDERER to reproduce the other rows of the results table. The GPU tests are built from the programs in examples/ — each one renders a scene, reads the framebuffer back and asserts on pixels, so you can open any of them to see exactly what is being claimed.

Found a discrepancy between this page and what your build reports? That is worth an issue — the whole point of publishing these numbers is that they are checkable.