Compatibility levels and the evidence vector
Evidence basis: source-verified at the pinned commit; tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit 009d40f5; unless a sentence says otherwise, nothing here was built or executed. Recorded runs quoted here (oracle results, campaign totals) are CNA's own records and were not re-run.
"Compatible with XNA" and "tested" each hide several different claims, and CNA's documentation keeps them apart on purpose. This page defines the levels of compatibility, the evidence vocabulary the site uses, what each class of renderer can and cannot prove, and the questions to answer before relying on a capability. It is the reading key for every verification statement on libcna.com, written for porters deciding what to trust and for contributors deciding how to word a result.
Levels of compatibility
There is no honest single percentage for "how compatible is CNA". Earlier project documents and earlier versions of this site quoted type-presence and functional-coverage percentages; they could not be recomputed from the source tree and were withdrawn. What replaces them is a set of distinct questions, each with its own instrument and its own blind spot.
| Level | Question it answers | CNA's instrument at 009d40f5 | What it does not establish |
|---|---|---|---|
| Representation | Does a public declaration with the documented shape exist? | The generated census report (the runtime member report): 331/331 types, 3,627/3,627 documented members, classified EXACT_EQUIVALENT 1,746, SEMANTIC_EQUIVALENT 1,823 and HOST_LANGUAGE_SUBSTITUTION 58 | Behaviour; the report records every entry as not behaviour-assessed. It is a manual script, not a CI gate. |
| Frozen signatures | Will a public signature silently drift? | static_assert freeze tests in PublicApiInputSignatureFreezeTests.cpp | Anything outside Microsoft::Xna::Framework::Input; no other namespace has such a test. |
| Surface purity | Does this code use only real XNA API? | The CNAEXT marker turned into [[deprecated]] by CNA_STRICT_XNA_API, plus a positive and a negative strict harness | That every non-XNA member is marked; CNA's own harness covers the Microsoft::Devices and sensor surface only (see the strict check). |
| Semantic behaviour | Is the observable result the same, even if the mechanism differs? | Unit tests, the manual FNA value harness, CNA's recorded real-XNA probes | Pixel identity; untested members; every host. |
| Exact behaviour | Is the output byte- or pixel-identical to XNA's? | The 39-scene oracle corpus at tolerance 0: DIRECTX9 recorded 39/39 through Wine and DXVK | Other renderers (measured, not gated), native Windows, scenes outside the corpus, anything that does not reach a pixel. |
| Host substitution | Where XNA used a platform service, what replaces it? | Documented substitutions such as the storage root taken from XDG_DATA_HOME, LOCALAPPDATA or HOME, local GamerServices persistence, the selected CNA_PLATFORM implementation | That the substitute behaves like the original on every host; each host needs its own run. |
| Declared boundary | Where does CNA refuse on purpose? | Named refusals: compiled effects on 11 of the 25 identities, file-backed video without FFmpeg (NotSupportedException), 3D calls on the seven 2D-only renderers | A refusal is correct behaviour, not a missing feature; it only matters whether it is named and deterministic. |
The semantic column of the census is mostly language, not behaviour: 1,039 of the 1,040 documented properties are "semantic" only because C++ spells them getXProperty()/setXProperty() (see what 331/331 and 3,627/3,627 mean). Keeping these rows apart is what lets one sentence be true: every documented member exists, and behaviour is verified only for named subsets.
A claim is a vector, not a score
A source tree can contain a test definition that the selected build never registers; CTest can register a command the host cannot execute; a renderer can return success without consuming effect state; and a pixel can be right on one translated runtime without proving another host. A verification claim is therefore recorded as a vector of coordinates, and a claim that omits one of them is weaker than it sounds.
| Coordinate | Examples at this snapshot |
|---|---|
| Public identity and implementation family | OPENGLES3 (EasyGL family), DIRECTX9 (its own family), FNA3D (whose internal driver is FNA3D's choice, not a CNA identity) |
| Configuration | Single renderer or a compatible CNA_GRAPHICS_RENDERERS set; default-OFF options such as CNA_CNAEXT or CNA_DIRECTX9_COMPILED_EFFECTS; the CNA_PLATFORM and CNA_AUDIO_PLATFORM choices |
| Execution host | Native Linux, a Wine prefix, headless Chromium, a macOS-14 runner, an iOS simulator |
| Driver or translation layer | Mesa llvmpipe, lavapipe, DXVK, vkd3d-proton, RADV on one AMD GPU |
| Oracle and tolerance | Real XNA 4.0 PNGs at tolerance 0; a fixture's own Expect* assertions; a renderer's own committed goldens; a ±3 channel tolerance on FNA-recorded effect renders |
| Branches not reached | Exit code 77 (skipped), GTEST_SKIP, a renderer-present test that only exists in another configuration, a probe that found no adapter |
Four examples show why the coordinates occupy genuinely different tiers:
- The 25-identity, 21-family registry in
cmake/RendererIdentities.cmakeand its generated descriptor table are strong structural evidence (names, fixed C ABI values, one descriptor per family) and no rendering evidence at all. - The
DIRECTX9oracle compares 39 scenes with output of the genuine XNA runtime, but both sides ran through Wine and DXVK on Linux; it says nothing yet about native Windows drivers or period hardware. HTML_DOMhas an executed suite in headless Chromium (htmldom-ci.yml), whereas the Emscripten multi-renderer lane only proves thatWEBGL2,CANVAS,HTML_DOMandSVG_DOMconfigure, build and link into one bundle. The same word, "web", covers both.- A recorded pass fraction, such as a full
ctestof aVULKANconfiguration on 2026-09-06, belongs to that campaign's configured population; it is not a current suite total and cannot be added to another campaign's figure (see what the test counts actually count).
An evidence vocabulary
Every page on this site states its evidence basis, and CNA's own records use the same distinctions. The terms are coordinates, not rungs of one ladder: a compile probe can answer an API question a screenshot cannot (the strict leak check proves that a marked call fails to compile), and a hostile-input test and a pixel oracle exercise different contracts.
| Term | Means | Does not mean |
|---|---|---|
| source-verified | A reachable implementation, fallback, refusal or absence was established by reading the pinned source | That anything was built or run |
| build-verified | Names, signatures, includes, templates and link closure were exercised in a named configuration | That the program behaves correctly |
| test-present | A test exists and is registered in some configuration | That it ran, or passed, in yours |
| runtime-observed | A named executable path ran and produced the recorded value, file, trace or lifecycle event | Agreement with any authority |
| renderer-engaged | The intended family and its native or translation route executed, not a substitute renderer or an early skip | That the pixels are right |
| oracle-compared | The observation was compared with an identified authority under a stated normalisation or tolerance | Anything the oracle cannot discriminate |
| pixel-verified | Selected pixels or a whole frame were compared at a stated readback boundary, renderer, host and tolerance | The same result on another host or driver |
| manually or hardware-observed | A person inspected the result, possibly on real hardware | A retained, automated verdict |
| CNA-recorded | A dated project artifact reports the result | That it was re-run for this site |
| blocked / unsupported / not attempted | Three different outcomes: an external precondition prevented the attempt; the path has no implementation; nobody tried | Interchangeable wording |
The evidence box at the top of each deep dive uses a subset of these (source-verified, test-present, build-verified, runtime-observed, oracle-compared, hardware-observed), and the last four appear only when CNA's own records say so and are attributed to CNA. The site never claims to have run anything.
What each kind of renderer can prove
The 25 identities fall into five classes whose evidence ceilings differ by construction. The build default (WEBGL2 under Emscripten, OPENGLES3 on Linux, SDL_RENDERER elsewhere) is a platform convenience, not a maturity ranking.
| Class | Identities | Contract | Evidence ceiling |
|---|---|---|---|
| Programmable GPU | The five EasyGL profiles, VULKAN, WEBGPU, DIRECTX9, DIRECTX11, DIRECTX12, SDL_GPU, OPENGL4, METAL | 3D, state and effect surface with family-specific format, shader and profile limits (METAL is the most conservative) | Renderer readback suites; DIRECTX9 adds the XNA oracle; four families share the 32 parity fixtures |
| Abstraction library | FNA3D | FNA3D's resources and its compiled stock-effect route; cannot execute ShaderEffect source | The oracle gate checks only that every scene renders |
| Bounded CPU 3D | SOFTWARE, PORTABLEGL | Deterministic CPU pixels from two different rasterizers with different scope; SOFTWARE accepts ShaderEffect source for compatibility but never executes it | Byte-exact comparisons are possible without a GPU; CNA measured SOFTWARE at 18 of 39 oracle scenes byte-exact |
| 2D | SDL_RENDERER, DIRECT2D, CANVAS, HTML_DOM, SVG_DOM, FREEDIRECT, GDI | SpriteBatch, textures and bounded render targets; refusing 3D is part of correctness (Unsupported3DGraphicsCallBehavior::Throw by default; WarnAndStub is honoured by four of them) | Family-specific: an executed DOM suite for HTML_DOM, a presentation oracle for GDI, manual native-Windows lanes |
| Validation and no-op | HEADLESS, STUB | HEADLESS validates arguments and state in its default Validation mode and in Trace (which adds a call log), skips those checks in Fast mode (counters only), and refuses readback rather than fabricating pixels; STUB proves only that the framework survives with every capability false | No pixel claim, by design; a shared pixel fixture may "pass" on them by asserting refusal |
The capability matrix gives the per-identity answer for each of the 19 GraphicsCapability members, derived from each renderer's code, and pixel evidence by family lists what evidence exists for each family.
Before relying on a capability
A capability answer is a hint, not a contract. The base IGraphicsRenderer::SupportsCapability still returns true for the original members unless a renderer narrows them, and DIRECTX9 inherits that default wholesale; the finer RendererCapabilityProfile (32 features, 22 limits, per-format usage) answers more precisely but still reports what the code declares, not what ran. Before depending on an operation, pin down the operation, not the feature name:
- texture dimension and format, mip and sub-rectangle transfers, and whether the shader actually samples it;
- 2D or cube target, depth format, sample count, resolve, mip generation and later sampling (a cube face inside a multiple-render-target set is refused by
DIRECTX9,DIRECTX11,DIRECTX12andSDL_GPUalthough each supports both features separately); - vertex and index formats, stream frequency, base and start offsets, primitive topology;
- blend, depth, stencil, rasterizer and sampler state, including reset behaviour and state caching;
- which stock-effect variant runs, the custom shader dialect or binary form, and parameter lifetime;
- query and readback timing, the logical versus physical presentation rectangle, and device-loss recovery;
- the exact platform and native-handle pair, and the descriptor that was actually selected at run time.
When a result differs between renderers, localise it in the same order. Identify the public selector, the implementation family, the native or translation API underneath, and whether the intended route engaged at all (GraphicsDevice::GetGraphicsRendererType() and GetGraphicsRendererName() report the live one in a multi-renderer build). Then reduce the case to a state transition, a readback or a pixel oracle. A successful process exit alone cannot localise a renderer-specific failure: SOFTWARE accepts shader source it never runs, and a no-pixel renderer can pass a fixture by refusing. Choose the graphics oracle by the failure you could have caused applies the same rule to a maintainer's change.
Counts are snapshots of a method
Static source definitions, CTest registrations, executed cases and oracle scenes are four different populations, and CNA's documents and this site never add them together. The site's test figures (904 files, 12,610 GoogleTest-family definitions) are source counts made with a stated command; ctest -N in a configured tree is the only registration count that applies to that tree; the corpus has 39 scenes in its denominator although 46 scenes exist. A task's closing count in CNA's plan files is a snapshot of one configuration on one date and stays attached to that campaign.
Even a simple count depends on its method. Counting the CNAEXT marker as a code token, with comments stripped, finds 1,843 uses in 330 public headers under modules/*/include; counting every whole-word occurrence, comments included, finds 2,279 in 353 headers. Both are correct and neither counts extension members. Hand-maintained figures also lag generated ones: CNA's top-level README still prints the pre-closure 3,467/3,627 member figure and "0/31" oracle scenes, while the generated census and the corpus directory say 3,627/3,627 and 39.
Rules for stating a claim
- Source-proven code and a declared capability are prerequisites for a claim, not runtime results.
- A renderer-engaged claim names the family and the native or CPU operation that was observed.
- A pixel claim uses an image that could have failed; the oracle's own history records exact matches that proved nothing because the scene did not discriminate (see how a comparison is judged).
- An oracle claim names the authority and the tolerance.
- Unsupported, skipped and environment-blocked stay three different outcomes.
- A recorded result carries its date, host and configuration, and is attributed to whoever recorded it.
A sentence that follows these rules reads: "CNA's records report that DIRECTX9, through Wine and DXVK on an AMD Radeon 780M, matched all 39 scenes at tolerance 0; the last dated report covers 31; not repeated on native Windows; not in CI." A sentence that breaks them reads: "CNA is pixel-perfect." How to word a claim collects more examples.
Scoping universal words, numbers and examples
The rules above govern results. Three smaller habits keep the prose around them from overclaiming. They apply equally to CNA's own Markdown and to this site.
A universal word needs a scope
"All", "every", "always", "never", "complete", "supported" and "works" are often exactly right. Each should still be readable as one of five scopes, and the sentence should make clear which:
- API semantics of a named function at a named revision:
GraphicsDeviceManager::EndDraw()presents once for every frame whoseBeginDraw()succeeded (GraphicsDeviceManager.cpp). - A repository population derived mechanically at a revision: all 25 identities listed in
RendererIdentities.cmakeat 009d40f5. - A measured corpus: all 39 scenes of the oracle corpus, on one named renderer, host and tolerance.
- A dated campaign result, marked historical: a full
ctestof one configuration on one day. - A portable user claim, which is rare and must name its exclusions:
GraphicsCapability::ThreeDis true on every identity except the seven 2D-only renderers andSTUB.
Four kinds of number
Numbers in CNA documentation age in four different ways. Each kind needs its own label:
- Generated facts, which a generator derives every time it runs, such as the runtime-member census written by
audit_xna_runtime_surface.py. Quote the generator and regenerate the figure instead of editing it. - Revision facts, counted from a pinned tree, such as this site's test-source figures. Quote the revision and the counting method.
- Configured or executed results, such as a pass fraction or an oracle difference. Name the configuration, host, date and outcome.
- Historical figures, such as alpha.1's renderer count or a task's closing test count. Label them historical and never compare them silently with current totals.
A number that someone typed by hand next to a generated one goes stale without anyone editing it. Besides the README example in the counts section, the EasyGL L7 records (gltf-l7-corpus-report.json, easygl-policy.json) give a hand-written justification that mentions "all 137 renderable assets". Their machine fields record 148 assets, 140 captured and 8 safely rejected.
Five kinds of code example
A listing is one of the following. Each kind promises something different:
- A compiled example records its build target and configuration.
- An example adapted from a compiled CNA test or example names its source and says what scaffolding it leaves out.
- An illustrative excerpt is intentionally incomplete and is never called compilable.
- A negative example states the compile failure or exception it expects. CNA's
StrictXnaApiSurfaceLeakCheck_MustFailToCompileis one. - A command transcript states its revision and host.
An unlabelled example drifts. At 009d40f5 the "Usage Example" in CNA's README.md includes Microsoft/Xna/Framework/Graphics/GraphicsDeviceManager.hpp. The header's real path is Microsoft/Xna/Framework/GraphicsDeviceManager.hpp. The example also writes an unqualified CornflowerBlue and calls device.Present() inside Draw, although EndDraw() already presents. This was checked by reading only; the example was not compiled. The first-game walkthrough has the corrected program. On this site, each example says whether it was syntax-checked against the headers or only read-checked. Images follow the same rule: a screenshot is evidence only for the revision, renderer and host that produced it.
Related pages
The same subject is explained at several altitudes. These are the neighbouring pages at each one.
- Architecture
- Architecture: renderer registry
- Internals
- Test architecture
- Maintainer workflow
- What to test after changing X · Known uncertainty and history
- Tests and validation
- Using the XNA oracle as evidence
- Reference
- Renderer capability matrix
- Deep dives
- CNA and XNA 4.0 · CNAEXT catalogue