Repaint CPU stages
This diagnostic separates the card compositor’s already charged repaint CPU into preparation, output-state construction, and output commit. It leaves the existing budget producer and acceptance limits unchanged. Extra clock reads and a log row have overhead; this is not a speedup claim.
Preparation covers Sway output scene configuration and card preparation.
Construction covers wlr_scene_output_build_state, including painting, and
any subsequent tearing test. It is not pure Pixman time. Commit covers output
commit CPU, not time until the panel emits light. Uncommitted attempts include
no-frame early returns as well as build/commit failures. Their costs carry
forward into the next successful frame exactly as in the existing producer.
The exporter accepts only the eight named unsigned numeric diagnostic fields.
analyze.py first requires the existing input/submit/frame-cost correlation,
then requires a repaint row for every submitted frame, matching render CPU,
an exact sum of stages, and one successful attempt after any unsuccessful
attempts. Missing, duplicate, malformed or inconsistent rows fail analysis.
Per-stage percentile values are not additive.
Host proof
build.json identifies the actual cross-built package, source hashes and
private raw-log hash. headless.json records 17 native input-routing checks
and eight source-timing checks using the RISC-V compositor under QEMU user
emulation. The native host client supplied the application surfaces. This was
not a Linux guest, physical touch, or a panel/performance acceptance run.
The normalized headless-telemetry.log reconstructs headless-profile.json:
104 submitted frames have complete matching stage accounting. Host CPU shares
must not be interpreted as the board’s bottleneck.
python3 -m unittest tests.test_card_shell_telemetry tests.test_card_shell_board_tools
TMPDIR=/mnt/MediaVolume/home/jadams nix build .#card-shell \
--max-jobs 1 --cores 8 --no-link --print-out-paths
python3 tests/card_shell_runtime.py --native-touch --benchmark --delayed-touch \
--sway /nix/store/374p33ril3p92i8d805fvzmsjqxhaxsv-sway-unwrapped-riscv64-unknown-linux-gnu-1.12/bin/sway \
--client /nix/store/6nskh3ldk3ya7mwxbb258qszrxhw2i61-card-composition-probe-client-0.1/bin/card-composition-probe-client \
--output NEW_HEADLESS_DIRECTORY
python3 docs/evidence/card-shell/repaint-stages/analyze.py \
--input docs/evidence/card-shell/repaint-stages/headless-telemetry.log \
--output NEW_PROFILE.json
The independent source review found no correctness issue and ran all 26 narrow producer/exporter tests successfully, including corrupt diagnostic controls. The unchanged budget parser’s 18 self-tests also passed during development.
Physical gate
run-board.py is a bounded one-run derivative of the committed throw-sampling
runner. It uses the same acceptance workload, normal kernel, Pixman policy,
configuration and RISC-V clients, changing only the instrumented compositor
and exporter. Stage it beside the three published session/acceptance/budget
tools and verify their hashes before launch. On the reserved board, run it
inside a root transient service with an outer 660-second deadline:
python3 /var/lib/k230/card-repaint-tools-REVISION/run-board.py \
--board --revision FULL_PUBLISHED_REVISION \
--output /var/lib/k230/card-repaint-stages-REVISION
The runner retains its independent restoration watchdog and 540-second session limit. A completed collection does not mean performance acceptance. Restore the normal shell and verify system identity, storage, Wi-Fi and HTTPS afterward. Real-finger feel and default-image acceptance remain open.
Physical profile on the normal kernel
The bounded run used published revision
58461f518821cb2b7f1b9d2d80223677d24b3602, the package in build.json, and the
unchanged acceptance workload. board/result.json records the actual running
Sway executable, tool hashes, UTC start/end times, boot ID, and restoration.
board/transfer.json verifies the nine exported public text files. Raw process
logs and pictures were excluded; no new visual or real-finger proof is claimed.
board/profile.json was reconstructed from
board/run-1/public/telemetry.log. All 268 submitted frames have complete
matching stage accounting, including one uncommitted attempt. Construction and
painting dominate repaint CPU; this does not isolate individual Pixman calls.
| Cards | Frames | Prepare share | Build share | Commit share | Build CPU p95 |
|---|---|---|---|---|---|
| One | 130 | 1.06% | 97.46% | 1.48% | 14.00 ms |
| Two | 138 | 0.86% | 97.77% | 1.37% | 19.79 ms |
Both workload budgets still FAIL. Total charged frame CPU p95/max was 16.55/48.98 ms with one card and 23.85/25.20 ms with two. Tracking interval p95 was 57.48 ms for both, against 33.334 ms. Other declared input, release, and incremental-memory metrics pass. The one-card 48.98 ms frame includes 33.45 ms of input handling and 15.41 ms of render CPU: repaint optimization alone cannot be assumed to resolve that outlier.
The acceptance process exited 1: 12 of 13 injected checks were observed,
but upward-throw-close-request failed again. Subsequent refusal, timeout,
accepted-close and normal-control routes were observed. The previous three
clean repeats remain valid historical observations, not proof of universal
throw reliability. The new failure remains in the reports. Its cause cannot
be assigned to the timing hooks merely because this run added instrumentation;
the acceptance setup and state-transition trace require separate analysis.
board/verification.json records exact reconstruction of the unchanged budget
report except its generated created_at timestamp. This is one diagnostic
run, not a paired performance comparison or an accepted optimized path.
normal-restoration.json and its serial log independently verify the normal
system, shell/seatd, Wi-Fi association and HTTPS, credential permissions, root
sentinel, storage boundaries, and protected boot/firmware hashes. Its boot ID
matches the workload: this session neither rebooted nor flashed the board.
The check’s repeat mode compares the earlier storage baseline; it does not
claim a new reboot in this trial.
The next bounded rendering experiment is reusing bilinearly scaled pixels only while source content and geometry are unchanged, while preserving live surface callbacks, subsurfaces, privacy invalidation and the existing fallback. Source-buffer identity alone cannot prove freshness. Measure actual cache reuse and same-package enabled/disabled board results before claiming a gain. Tasks 4.2, 5.1 and 5.3 remain open.