# p05-program-artifacts — results v1 Tree `~/w7/p05-program-artifacts`, branch `p05/program-artifacts`, base `feat/disaggregated @ 6672778b`. Build: `build-linux/` Release, `/usr/sbin/clang++` (clang 22), ccache (`CCACHE_BASEDIR=/home/swung/w7`), libstdc++ (`__GLIBCXX__ 20260625`), `MOBILEGL_BUILD_TEST=ON`, `MOBILEGL_BUILD_INTEGRATION_TEST=ON`. Not pushed. `~/w7/pipe` untouched (only read: `ctest -N`, `nm`, and a direct run of one existing test binary for the A/B below). ## Commits (oldest first) | hash | subject | |---|---| | `a180dc57` | `[Refactor] (Program): extract ProgramArtifacts.h - move TypeFacts, ResourceReflection, XfbVarying, LinkArtifacts and SpirvArtifacts to namespace scope with in-class aliases so every existing spelling compiles unchanged; pure move` | | `3ce1264c` | `[Refactor] (Program): stop ProgramObject.h including SpvcSession.h - it names no spirv-cross or SPIRV-Reflect symbol` | | `77beb7d6` | `[Feat] (Program): add the reflection-archive field tables and sizeof trip wires to ProgramArtifacts.h` | Files touched (vs `6672778b`): `MobileGL/MG_State/GLState/ProgramState/ProgramArtifacts.h` (new, 576 lines), `MobileGL/MG_State/GLState/ProgramState/ProgramObject.h` (-386/+15), `MobileGL/MG_Test/Program/ProgramArtifactsTest.cpp` (new), `MobileGL/MG_Test/Program/CMakeLists.txt` (+21). Nothing else — commit 2 needed no consumer include additions (all 8 includers + `ProgramObject.cpp` built without `SpvcSession.h` forwarded). ## What landed (B.2 checklist) - `ProgramArtifacts.h`: banner copied from `ProgramObject.h`; includes exactly `` and ``; PURITY comment; `namespace MobileGL::MG_State::GLState { inline constexpr Uint kInvalidUniformOffset = ~0u; TypeFacts; ResourceReflection; the 4 `using` reflections; XfbVarying; LinkArtifacts (+ its comment block, with the one added I5 sentence); SpirvArtifacts (+ comment block); VisitFields x5; trip wires }`. Struct bodies moved verbatim (re-indented one level), no field added/removed/reordered/re-typed; `program` and `uniformInitialValues` keep their glslang types (D5). - `ProgramObject.h`: `#include "ProgramArtifacts.h"` after `"ShaderObject.h"`; old `:41-104` replaced by the 9-line alias block (fully qualified RHS); `kInvalidUniformOffset` re-exported as `static constexpr Uint kInvalidUniformOffset = MobileGL::MG_State::GLState::kInvalidUniformOffset;` (comment kept); old `:1144-1171` and `:1173-1449` deleted; commit 2 drops `#include `. `MAX_UNIFORM_LOCATIONS` and the `glslang::` accessors stay. `m_artifacts`/`m_spirv` private, `Artifacts()`/`Spirv()` untouched (I5). - `VisitFields`: free constrained templates (`requires std::same_as, T>`), contract `v(const char*, Field&)`, tables never recurse; counts TypeFacts 20, ResourceReflection 14 (`type` as a value), XfbVarying 11, LinkArtifacts 57 (every member except `program`), SpirvArtifacts 8. - Trip wires: `static_assert(is_trivially_copyable_v && sizeof(TypeFacts) == 44)`; under `#if defined(__GLIBCXX__) && !defined(_GLIBCXX_DEBUG) && (SIZE_MAX == UINT64_MAX)`: `MGL_RESOURCEREFLECTION_SIZE 128`, `MGL_XFBVARYING_SIZE 128`, `MGL_LINKARTIFACTS_SIZE 1056`, `MGL_SPIRVARTIFACTS_SIZE 88` (measured on this build via `== 1` notes); `#elif defined(_LIBCPP_VERSION) && (SIZE_MAX == UINT64_MAX) && defined(MGL_ARTIFACT_SIZES_LIBCXX_PINNED)` left inert for the integrator (C.4); the four `static_assert`s guarded by `#ifdef MGL_LINKARTIFACTS_SIZE` with the "add the field to VisitFields(...) then update this number" messages. - Tests (`ProgramArtifacts.*`, label `unit`, first include is the artifacts header): `AliasesAreTheSameTypes` (9 `is_same_v` static_asserts + `kInvalidUniformOffset`), `TypeFactsIsPodOf44Bytes`, `VisitFieldsCoversEveryMember` (20/14/11/57/8, const == non-const, names distinct), `VisitFieldsPassesConstnessThrough` (extra: const walk yields const refs, mutable walk writes through), `SizesArePinnedOnThisToolchain` (`EXPECT_EQ` under the same `#if`, `RecordProperty("sizeof_*")` x5 always, plus a printf line `sizeof: TypeFacts=44 ResourceReflection=128 XfbVarying=128 LinkArtifacts=1056 SpirvArtifacts=88`). ## Verification — every command and its actual result All run in WSL Arch, cwd `~/w7/p05-program-artifacts`, after commit 3 (`77beb7d6`) unless stated. | # | command | result | |---|---|---| | 1 | `cmake --build build-linux -j 16` (after each commit) | commit 1: OK (186 steps, whole ProgramObject.h fan-out); commit 2: OK (183 steps, zero consumer edits needed); commit 3: OK; post-commit re-run: `ninja: no work to do.` Logs `~/w7/pa-build{1,2,3}.log` | | 2 | `ctest --test-dir build-linux -L unit --no-tests=error -j 8` | `100% tests passed, 0 tests failed out of 1463` (3 `***Skipped`: `LogLevel.DebugBuildKeepsEverything`, `LogLevel.AssertIsLiveInDebugBuilds`, `XfbFrontendOrderInvarianceTest.DumpBSpirvForDisassembly` — skip by design). Log `~/w7/pa-unit3.log`. (After commit 1 alone: 1460/1460.) | | 3 | `ctest --test-dir build-linux -L integration-gpu --no-tests=error -j 8` | `99% tests passed, 5 tests failed out of 868`: the four `*IsActuallyArmedWhenTheEnvironmentPinsItOn*` (DirectGLES UnlocatedIoBlocks, DirectVulkan PrimGenReroute, DirectGLES + DirectVulkan PointSizeDemotion) — the known `-j` flake — and `DirectVulkan.IterationRPProgram203Scenario.FixedCompleteInputProducesFixedCompleteGoldenOutput`. Log `~/w7/pa-igpu3.log` | | 4 | `ctest --test-dir build-linux -L integration-gpu --rerun-failed -j 1` | `80% tests passed, 1 tests failed out of 5`: the four flake lanes pass; `DirectVulkan.IterationRPProgram203Scenario.FixedCompleteInputProducesFixedCompleteGoldenOutput` still fails (`IterationRPProgram203Scenario.cpp:371,377`, golden half-float texel mismatch, `mismatchTexels` 1 vs 0). Log `~/w7/pa-igpu3-rerun.log` | | 5 | A/B of #4's failure on the UNTOUCHED base binaries: `MOBILEGL_BACKEND_TYPE=DirectVulkan __EGL_VENDOR_LIBRARY_FILENAMES=/usr/share/glvnd/egl_vendor.d/50_mesa.json ~/w7/pipe/build-linux/MobileGL/MG_IntegrationTest/MobileGLIntegrationTest --gtest_filter=IterationRPProgram203Scenario.FixedCompleteInputProducesFixedCompleteGoldenOutput --gtest_also_run_disabled_tests` (cwd = that binary's `MG_IntegrationTest` dir; the env is what the ctest ENVIRONMENT property injects) x3, and the same for my binary x3 | base (built 22:06 at `6672778b`): FAILED 3/3; mine: FAILED 3/3. **Pre-existing on this WSL/lavapipe machine, independent of this package** (the package touches no code path — only type scope — and the library `.text` is byte-identical, see #13). Its DirectGLES twin is `***Skipped` on both. | | 6 | `ctest -N` name diff: `ctest --test-dir build-linux -N \| grep -E '^\s+Test\s+#' \| sed 's/^ *Test *#[0-9]*: //' \| sort` vs the same on `~/w7/pipe/build-linux`; `comm -23 before after` | before 2326, after 2331; `comm -23` **empty**; `comm -13` = exactly the 5 new `ProgramArtifacts.*` names. NOTE: the brief's pattern `'^\s+Test #'` (single space) only matches test numbers >= 1000 (ctest right-aligns: `Test #984:` vs `Test #1234:`); with it the diff is garbage (two spurious `QueryTest.*` "additions", my tests invisible). Use `'^\s+Test\s+#'`. | | 7 | `ctest --test-dir build-linux -L unit -N \| grep -c ProgramArtifacts` | `5` (the label reaches `ctest -L unit`) | | 8 | ODR: `grep -rn "^\s*struct \(TypeFacts\|ResourceReflection\|XfbVarying\|LinkArtifacts\|SpirvArtifacts\)\b" MobileGL` | exactly 5 hits, all in `ProgramArtifacts.h` (lines 31, 63, 95, 160, 359) | | 9 | `grep -n "#include" MobileGL/MG_State/GLState/ProgramState/ProgramArtifacts.h` | 2 lines: `` (line 10), `` (line 15) | | 10 | `grep -c "glslang::" MobileGL/MG_State/GLState/ProgramState/ProgramArtifacts.h` | `2` (lines 165 `SharedPtr program;` and 232 `Vector uniformInitialValues;`) | | 11 | Closure probe: `printf '#include \n' > /tmp/probe_pa.cpp; clang++ -std=gnu++23 -Iinclude -IMobileGL -IMobileGL/MG_Pipe -I3rdparty/xxHash -I3rdparty/Vulkan-Headers/include -H -E -o /dev/null /tmp/probe_pa.cpp 2>&1 \| grep -E '^\.+ ' \| grep -E 'ShaderObject.h\|SpvcSession.h\|MG_Util/ShaderTranspiler/\|MobileGL/Config.h\|MobileGL/MG_Backend/\|BufferState/'` | **empty** (0 forbidden of 653 closure lines). The only `MobileGL/` headers in the closure: `Defines.h`, `Includes.h`, `ProgramArtifacts.h`, `MG_Util/Debug/Log.h`, `MG_Util/GLExtensions.h`, `MG_Util/PlatformStubs.h`, `MG_Util/Types.h`. | | 12 | `clang++ ... -fsyntax-only /tmp/probe_pa.cpp` | OK (header self-contained; `ProgramArtifactsTest.cpp` includes it first as the in-tree proof) | | 13 | `git diff --stat 6672778b -- MobileGL/MG_Backend MobileGL/MG_Impl` | **empty** (also after commit 2 — no include-line additions were needed) | | 14 | Symbol attribution (no `scripts/symbol_report.py` in this tree — done by hand with `nm --defined-only -S` + `c++filt` + `size --format=sysv`, script `scratchpad/wf3/p05-program-artifacts/symcmp.py`; normalisation folds `GLState::ProgramObject::{TypeFacts,ResourceReflection,XfbVarying,LinkArtifacts,SpirvArtifacts}` -> `GLState::X` on both sides, multiset compare on (name, type, size)); before = `~/w7/pipe/build-linux/libMobileGL.so` (6672778b, 22:06), after = `build-linux/libMobileGL.so` | `symbol-report: .text 10792579 -> 10792579` (byte-identical); `30511 -> 30511 defined symbols: 0 added, 0 removed, 1 resized, 43 renamed`; identical after commit 1 alone (`~/w7/pa-sym1.txt`) and after commit 3 (`~/w7/pa-sym3.txt`). The 1 "resized" is `r [122] -> [107] typeinfo name for std::_Sp_counted_ptr_inplace<...LinkArtifacts, allocator, _Lock_policy 2>` — the RTTI **name string** (rodata, not code), 15 bytes shorter because `13ProgramObject` left the mangled name. Fully attributed; commits 2 and 3 add no symbol (VisitFields is uninstantiated outside the test binary). | | 15 | Red-for-its-reason (scratch copy of the header under `/tmp/pa-neg`, tree untouched): add `Int addedWithoutATableEntry = 0;` to `LinkArtifacts`, `clang++ -I/tmp/pa-neg ... -fsyntax-only /tmp/probe_pa.cpp` | exit 1: `error: static assertion failed ... 'sizeof(LinkArtifacts) == 1056': LinkArtifacts changed size: add the field to VisitFields(LinkArtifacts) ..., then update this number` / `note: expression evaluates to '1064 == 1056'` | | 16 | `git status --short` (excluding `build-linux/`) after each commit | clean | | 17 | Line endings: `grep -c $'\r'` on the two headers and the test | 0 / 0 / 0 | ## Deviations from the brief (with reasons) 1. **Comment respellings in the moved text (commit 1).** Five comment lines that moved verbatim spell `glslang::TType`, `glslang::TLayoutMatrix`, `glslang::TBasicType`, `glslang::TObjectReflection`, `glslang::TProgram`; with them the header had 8 `glslang::` lines, and the gate/D5 pins the count at exactly 2. Respelled comment-only as `glslang TType` etc. (no code token changed). The brief's "verbatim" and "exactly 2" cannot both hold literally; the gate wins (`ROADMAP.md:7`). 2. **`LinkArtifacts` field count is 57 of 58, not 52 of 53.** Counted mechanically from the moved source (`program` + 57 others; a lines-ending-in-`;` count under-reads because several members carry trailing comments). `VisitFieldsCoversEveryMember` expects 57. The brief's 53 is a stale scout number. 3. **`ctest -N` grep pattern** corrected to `'^\s+Test\s+#'` (see #6) — the brief's pattern silently drops tests numbered < 1000. 4. **One extra test** `VisitFieldsPassesConstnessThrough` (adds only; brief allows additions) — pins that the single table really serves both directions. 5. **`MobileGL/MG_Test/Program/CMakeLists.txt` stanza** appended at the end of the file (after the existing `gtest_discover_tests` block) rather than "copied next to" `AsyncCompileTest`; content is the `AsyncCompileTest` shape with `DISCOVERY_TIMEOUT 30 PROPERTIES LABELS unit` as specified. 6. **`scripts/symbol_report.py` absent** in this tree (owned by `p05-include-gate`); the by-hand equivalent above records the numbers. The brief's `--strip-scope 'MobileGL::MG_State::GLState::ProgramObject::'` as a plain textual strip is NOT the right normalisation (it leaves a bare `LinkArtifacts` on the before side versus `MobileGL::MG_State::GLState::LinkArtifacts` on the after side and reports 43 added / 43 removed); the integrator's run of the real script should fold `GLState::ProgramObject::X` -> `GLState::X` (or strip only `ProgramObject::`). Flagging so C.4 does not read a false 43/43. ## Notes for the integrator - libc++ branch of the trip wires is inert until `MGL_ARTIFACT_SIZES_LIBCXX_PINNED` is defined with the NDK numbers (C.4); `ProgramArtifacts.SizesArePinnedOnThisToolchain` prints and `RecordProperty`s all five sizes on every platform for that purpose. - The glibc++ guard is exactly the brief's (`__GLIBCXX__ && !_GLIBCXX_DEBUG && SIZE_MAX == UINT64_MAX`); it does not exclude `_GLIBCXX_USE_CXX11_ABI=0` (COW `std::string`, 8 bytes) — no MobileGL toolchain uses that, mentioned only for completeness. - `DirectVulkan.IterationRPProgram203Scenario.FixedCompleteInputProducesFixedCompleteGoldenOutput` is red on this WSL machine at the base commit (#5) — expect it in the integrator's baseline too. ## Unfinished Nothing in scope. Not done by design/ownership: libc++ size pin (integrator, C.4); `scripts/check_include_closure.py --probe artifacts-header` run (script lives in `p05/include-gate`; the manual `-H` probe #11 is its equivalent); narrowing `ProgramTranslationCache.h`/`ProgramInterface.cpp` to the new header and the `UniformInitializer` re-typing (P7 follow-ups per the brief).