perf(display): multi-region dirty blit + queued interrupt-DMA staging
Structural color-TFT render win from the graphics research (chunk R). Three
independently-correct parts in the shadow-framebuffer blit; the
readRect/writePixels/markFrameDirty contract is unchanged (no visual
regression), and the e-paper/mono backends are untouched.
1. Multi-region dirty tracking (research v1 §2.3). Replace the single growing
dirty bounding box with DirtyRegionSet<8>: a fixed array of disjoint dirty
rects with an LVGL-style per-axis area-join (overlap/abut/within-gap merge,
cascading). commitFrame() blits each disjoint region instead of the union
span, so a status-bar + far list-row change streams only the two rects. On
slot overflow it degrades to today's single union bbox — correctness never
depends on the slot count. Factored into the header-only, framework-agnostic
dirty_region_set.h and covered by a native host test (area-join + a
brute-force pixel-coverage invariant).
2. Async/queued interrupt-DMA blit + double-buffered staging (research v2
NEW-3). New platform SPI facade calls queueTransmit()/awaitQueued() wrap
spi_device_queue_trans/get_trans_result. blitShadowRect() now ping-pongs two
256-byte staging buffers: queue chunk N's DMA, pack chunk N+1 while N is in
flight, reclaim N in FIFO order. Single-chunk rects keep the polling path
(lower one-shot latency). The display device queue depth is bumped to 2.
3. Buffer-age damage accumulation (research v2 NEW-7): N/A for the single shadow
FB (no aged back buffer); documented in dirty_region_set.h as the natural
home for it once a back buffer / e-paper A/B waveform lands.
4. Render instrumentation gated behind KLEIDOS_RENDER_STATS (debug builds with
the live framebuffer): per-commit blit µs, regions, bytes, frame µs, overflow
and async counters, plus totals. Read/zeroed over serial via the new
`RENDERSTATS [RESET]` debug command (display::formatRenderStats).
Classic-ESP32 feasibility: the queued interrupt-DMA path uses only generic
spi_master queue APIs (no S3-only peripheral), so it works on classic ESP32 as
well as the S3 — no gating needed for the drop-or-gate decision.
sticks3 flash +2356 B (1,611,699 -> 1,614,055). Native suite 955/955 green.
fix(test): configure clangd and clang-tidy for native test tree (#3)
* fix(test): configure clangd and clang-tidy for native test tree
- Generate compile_commands.json from pio run -e native -t compiledb
so clangd resolves src/ headers in test files without errors.
- Fix .clangd PathMatch regex (relative path, not absolute) and add
-I.pio/build/native/unity_config so unity_config.h resolves.
- Remove overly broad Remove: -I* that was stripping all added paths.
- Add -Wno-format-security and UnusedIncludes: None to test block.
- Add test/.clang-tidy that inherits the root config and:
- Allows test_* functions to use snake_case (FunctionIgnoredRegexp)
- Disables cert-err33-c, cert-dcl50-cpp,
cppcoreguidelines-pro-type-cstyle-cast,
bugprone-argument-comment, bugprone-misplaced-widening-cast,
bugprone-implicit-widening-of-multiplication-result,
misc-unused-using-decls — all false positives in test context.
- Add /* intentionally empty */ comment to all empty setUp()/tearDown()
bodies (28 files) to suppress SonarLint c:S1186.
- Fix test_vault_task: change relative include ../../src/vault/vault_task.h
to vault/vault_task.h (consistent with all other test files; -Isrc
already in compile flags).
- Reorganise test/ into a module-mirroring subtree layout:
test/ble/, test/crypto/, test/hal/, test/ota/, test/platform/,
test/states/, test/totp/, test/ui/, test/vault/, test/web/
(old flat directories removed).
- Add sonar-project.properties exclusion e4: suppress c:S1186 on
test/**/*.cpp at SonarCloud level.
All 468 native unit tests pass (pio test -e native).
* fix(clangd): add -DESP_PLATFORM to global CompileFlags
All Kleidos targets are ESP32/ESP32-S3 (framework = espidf), so
ESP_PLATFORM is always defined at compile time. Without this flag in
the clangd config, the language server was activating #else branches
inside #ifdef ESP_PLATFORM blocks, hiding device code and showing
false diagnostics for src/ files.
The test/** fragment already has Remove: -DESP_PLATFORM, so native
unit-test files continue to compile without the flag.
* revert(clangd): remove incorrect -DESP_PLATFORM from global CompileFlags
Adding -DESP_PLATFORM globally caused 'too many errors' in src/ files
because compile_commands.json was generated from the native env and
lacked all ESP-IDF/Xtensa include paths.
The correct fix is to generate compile_commands.json from a device env:
./scripts/generate_compiledb.sh sticks3
This populates the DB with proper -DESP_PLATFORM and all IDF includes
for every src/ translation unit. The symlink at the project root then
points clangd to the right compilation context.
Add ESP32 internal-RTC rtc:: backend for the no-I2C-RTC boards
Replace the no-op rtc:: fallback on the four boards without an I2C calendar
RTC (sticks3/cardputer/tdeck on ESP32-S3, m5stack_gray on classic ESP32) with
a real backend over the SoC internal RTC.
- Time base is gettimeofday/settimeofday (RTC-timer-backed: survives deep sleep
and non-power-on resets), not a monotonic esp_timer count.
- clockTrust() reports Volatile once the clock is set and no cold-boot/
backward-jump is detected, else None (the internal equivalent of the BM8563
voltage-low flag): power-on reset (wakeCause Undefined), a backward time jump
beyond a drift margin, or a never-set clock all fail closed to None.
- The NVS seed (namespace "kleidos", key "rtc_epoch", written by the admin
portal) is read as the floor on boot and written on every set.
- setCountdownTimer/armAlarmWake compute delta = target - now, clamp to the
LP-timer range, and arm sleep::enableTimerWakeup (never 0/underflow); the
internal wake presents as WakeCause::Timer. Functions with no internal
equivalent (setAlarm/readAlarm/alarmTriggered/CLKOUT/...) return false.
Pure conversion/trust/wake logic lives in a host-testable header
(rtc_internal_clock.h) with a native-only injectable epoch seam mirroring
platform/Clock.h; the backend TU wires it to gettimeofday/settimeofday,
NvsStore and the sleep facade. rtc_hal.cpp is converted in place so exactly one
!HAS_BM8563_RTC definition of the rtc:: symbols exists (no ODR clash with the
BM8563 backend).
Claude-Session: https://claude.ai/code/session_01U878bNJtg9bCvAMM8CFKqS
fix(test): configure clangd and clang-tidy for native test tree (#3)
* fix(test): configure clangd and clang-tidy for native test tree
- Generate compile_commands.json from pio run -e native -t compiledb
so clangd resolves src/ headers in test files without errors.
- Fix .clangd PathMatch regex (relative path, not absolute) and add
-I.pio/build/native/unity_config so unity_config.h resolves.
- Remove overly broad Remove: -I* that was stripping all added paths.
- Add -Wno-format-security and UnusedIncludes: None to test block.
- Add test/.clang-tidy that inherits the root config and:
- Allows test_* functions to use snake_case (FunctionIgnoredRegexp)
- Disables cert-err33-c, cert-dcl50-cpp,
cppcoreguidelines-pro-type-cstyle-cast,
bugprone-argument-comment, bugprone-misplaced-widening-cast,
bugprone-implicit-widening-of-multiplication-result,
misc-unused-using-decls — all false positives in test context.
- Add /* intentionally empty */ comment to all empty setUp()/tearDown()
bodies (28 files) to suppress SonarLint c:S1186.
- Fix test_vault_task: change relative include ../../src/vault/vault_task.h
to vault/vault_task.h (consistent with all other test files; -Isrc
already in compile flags).
- Reorganise test/ into a module-mirroring subtree layout:
test/ble/, test/crypto/, test/hal/, test/ota/, test/platform/,
test/states/, test/totp/, test/ui/, test/vault/, test/web/
(old flat directories removed).
- Add sonar-project.properties exclusion e4: suppress c:S1186 on
test/**/*.cpp at SonarCloud level.
All 468 native unit tests pass (pio test -e native).
* fix(clangd): add -DESP_PLATFORM to global CompileFlags
All Kleidos targets are ESP32/ESP32-S3 (framework = espidf), so
ESP_PLATFORM is always defined at compile time. Without this flag in
the clangd config, the language server was activating #else branches
inside #ifdef ESP_PLATFORM blocks, hiding device code and showing
false diagnostics for src/ files.
The test/** fragment already has Remove: -DESP_PLATFORM, so native
unit-test files continue to compile without the flag.
* revert(clangd): remove incorrect -DESP_PLATFORM from global CompileFlags
Adding -DESP_PLATFORM globally caused 'too many errors' in src/ files
because compile_commands.json was generated from the native env and
lacked all ESP-IDF/Xtensa include paths.
The correct fix is to generate compile_commands.json from a device env:
./scripts/generate_compiledb.sh sticks3
This populates the DB with proper -DESP_PLATFORM and all IDF includes
for every src/ translation unit. The symlink at the project root then
points clangd to the right compilation context.