Chapter 43 glTF Conformance and Oracles
CNA’s alpha.1 glTF story is an evidence system as much as an importer: a pinned specification, a deterministic committed corpus, numerical observations from container through bound effect state, renderer-owned pixel campaigns, and an append-only defect ledger. The resulting claim is substantial but deliberately qualified. CNA declares its import/runtime model correct for the scoped core campaign; it does not declare every optional extension, renderer shader, or hostile input complete.
43.1 Pinned authority and deterministic corpus
docs/gltf-conformance.md pins the Khronos glTF 2.0 specification at commit 2b29723d025a995971726f2989697cdc49b1222a; the 149,830-byte AsciiDoc source has SHA-256 55986799907693d3f51b0a474497852c0d6318b85084811fdc05ff0db4b27967. That document is research/test authority, not a build-time dependency.
The standard-library-only generator under tools/gltf_fixtures emits each authored fixture, its GLB twin, its independently stated expectations, and any byte goldens. Generated outputs are committed and guarded by a manifest; check-only regeneration must be byte-identical. At the tag, tests/assets/gltf contains 148 .gltf assets, 148 .glb twins, and 148 .expected.json records in a 744-file tree. The paired container audit therefore examines 296 asset containers, while CNA’s own validation report separately classifies the valid/expected-invalid cases it feeds to the external validator. These denominators answer different questions and must not be merged into one “number of glTF tests.”
The external Khronos validator is pinned and used by the recorded corpus audit, but is not needed to build or run CNA. Likewise, Khronos Sample Assets and Sample Renderer are fetched for a bounded comparison campaign rather than redistributed as CNA runtime dependencies.
43.2 The ladder, including its L0 gate
The semantic ladder has seven observation layers. A separate L0 gate first proves the corpus and oracle machinery are intact; a higher layer is meaningful only when the lower ones agree.
- L1: container structure
-
parser counts, selected scene, relationships, and declared extensions;
- L2: decoded accessors
-
typed arrays after offsets, strides, normalization, and sparse overrides;
- L3: semantic import
-
mesh streams, materials, topology, grouping, skins, animation, and reportable loss before GPU packing;
- L4: composed geometry
-
scene/world positions after node and skin coordinate-space rules;
- L5: GPU bytes
-
vertex and index bytes, compared with field-aware diagnostics;
- L6: bound effect state
-
matrices, material factors, maps, samplers, alpha state, lights, and palettes reaching GpuDrawParams; and
- L7: rendered pixels
-
per-renderer PNGs and explicit safe-rejection dispositions.
L1–L6 run as separately named CTest entries. The L5 corpus has 140 byte-golden dispositions; eight fixtures have no golden because seven are deliberate import rejections and one positive generated-tangent case refuses to duplicate the production tangent algorithm as its oracle. L6 calls the same virtual effect-parameter route used by a draw, but intentionally stops before renderer rasterization.
43.3 Pixel evidence is renderer-specific
The tag carries four complete L7 campaign reports: OPENGLES3/EasyGL, Vulkan, SOFTWARE, and DIRECTX11 under Wine/DXVK. Each report gives all 148 canonical assets an explicit disposition: 140 deterministic captured PNGs and eight safe rejections. Each renderer owns its goldens and requires two independent viewer processes to agree; no renderer silently borrows another’s image or tolerance.
That is Historically recorded evidence in this edition: the reports, image hashes, commands, and guards are present at the immutable tag, but the Bible update did not reproduce all four platform campaigns. Same-renderer repeatability also is not an independent implementation oracle. The tag’s supplementary Sample Renderer retake supplies that second perspective for a curated real-world matrix: its report records 15 captures across 14 gate rows against a pinned Khronos renderer and pinned Sample Assets. Its comparison includes explicit silhouette, coverage, and colour-error thresholds; it is broader than a smoke test and narrower than pixel-identical proof of all 148 fixtures.
43.4 The append-only defect ledger
Normative expectations and measured current behaviour remain separate fields. A known defect must both differ from the normative value and equal its recorded actual value; a fix moves the old observation to history rather than deleting it. At alpha.1 all eight original forensic defects are fixed:
| Defect | Original failure | Tag state |
|---|---|---|
| D1–D3 | node TRS, hierarchy, and matrix transforms | fixed |
| D4 | sparse index accessors decoded as zero | fixed |
| D5 | non-triangle topology silently reinterpreted/rejected | fixed |
| D6 | rigid node animation dropped | fixed for the supported carrier model |
| D7 | factor-only PBR material lost | fixed |
| D8 | ancestry above a skin’s joint set lost | fixed |
D5 now carries point/line topology or converts loops, strips, and fans explicitly. D6 stores rigid scene-node clips on unskinned models and reports the remaining mixed-carrier loss rather than hiding it. D7 reaches the L6 material contract and the recorded renderer campaigns. D8 keeps skeleton-root ancestry and mesh-node cancellation in a separate prefix so animation cannot overwrite them.
43.5 Current capability and loss reporting
GltfImportReportEXT is part of the public CNAEXT result surface. Direct glTF and converter-produced CNJ models expose scene, mesh, skin, camera, light, animation, primitive, and clip counts plus ordered diagnostic codes. AnythingLost() and the aggregate dropped/ approximated counts make a successful load distinguishable from a lossless one. Applications should branch on diagnostic codes, not human-readable messages.
The runtime model also exposes all imported skin mappings, cameras, material-variant names and selection, whole-model bounds, and per-part topology. These additions close several old “parsed but unreachable” gaps. They do not erase the current limits:
-
•
at most two distinct texture-coordinate sets are packed for a PBR primitive;
-
•
only the first four joint influences reach XNA-shaped vertex streams, and stock skinned effects retain their 72-matrix palette ceiling;
-
•
point and spot lights are approximated through XNA’s three directional-light slots; range and cone information is reported as lost;
-
•
EXT_mesh_gpu_instancing does not carry its per-instance transforms into the model, and EXT_meshopt_compression is refused without a decoder;
-
•
KTX2/BasisU and WebP sources require a usable PNG/JPEG fallback; and
-
•
mirror winding is reported per placement but remains an application/draw-state decision.
43.6 What the milestone name means
The tag’s current declaration is GLTF CORE 2.0 IMPORT/RUNTIME MODEL CORRECT. It covers the scoped importer, CNJ route, model graph, effects/state transport, generated corpus, and recorded renderer evidence. The qualification matters: the earlier unqualified wording was superseded because a correct import model cannot by itself prove every renderer’s final pixels.
The broader GLTF ROBUST gate is 9 of 12 rows. Three rows remain open at the tag:
-
1.
complete KHR_materials_specular texture sampling in the remaining Metal and Wicked PBR implementations;
-
2.
native point/spot light terms with falloff and cones in the cross-renderer shader ABI; and
-
3.
a fourth renderer passing the bounded Khronos real-world retake matrix (three qualify, including the Direct3D path).
Those open rows are not contradictions to the qualified core declaration; they define why the stronger milestone remains open. The practical reading rule is therefore precise: a green import plus an empty loss report establishes the represented model for the exercised path, L6 establishes what reaches an effect, and only renderer-engaged/pixel evidence establishes the final image.