Skip to content

feat(core): transparent ARGB compositing and retained raster layer passes - #247

Closed
odex21 wants to merge 4 commits into
pocket-stack:mainfrom
odex21:codex/upstream-core-candidates
Closed

feat(core): transparent ARGB compositing and retained raster layer passes#247
odex21 wants to merge 4 commits into
pocket-stack:mainfrom
odex21:codex/upstream-core-candidates

Conversation

@odex21

@odex21 odex21 commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Two commits on top of upstream main (f658cd0):

  1. feat(core): transparent ARGB compositing with true src-over — ARGB targets clear to fully transparent and blend with true straight-alpha src-over for destinations that already hold content; opaque destinations are unchanged, so RGBA/RGB565 targets and existing hosts keep their previous behavior.
  2. feat(core): add retained raster layer passes — opt-in rasterCache property (id 143) plus Ui::draw_retained(), returning ordered Draw/Layer passes. Translation-only layer roots keep stable layer-local DrawLists, so capable hosts can retain pixels and only move composition coordinates; the flat Ui::draw() ABI stays byte-compatible.

Motivation

The ESP32-P4 product host composites a transparent UI layer over a fluid background and scrolls a retained lyric layer by translation only. The previous PSP-era "blend against opaque black and force alpha 255" flattened semi-transparent destination pixels on layered surfaces; the retained passes let hosts skip re-rasterization when only the layer position changes.

Verification

  • cargo test: commit 1 = 111/111, tip = 114/114 (each commit individually green)
  • contracts drift guard: all green (spec.ts <-> generated spec.rs)
  • flat Ui::draw() output unchanged (existing layout/golden tests)

Performance (measured on the ESP32-P4 product host)

On-device A/B with a minimal scrolling-list app (no audio/WS/cover/fluid), identical code except rasterCache 0 vs 1, sampled via the host serial perf probe (1 s windows, medians):

rasterCache=0 rasterCache=1 (retained)
fps median 45.9 (n=43) 52.8 (n=36)
render 13.1 ms 0.63 ms
js 1.48 ms 0.49 ms
present 6.8 ms 17.5 ms

Retained layers cut per-frame raster cost by ~95% (13.1 ms -> 0.63 ms) and raise fps by ~15%; the remaining wall is the host-side retained composite chain (blend + pipeline wait), not core raster. Numbers are host-specific; the core-level benefit is the stable layer-local DrawList verified by tests.

Notes

odex21 added 4 commits August 7, 2026 17:56
At runtime the PSRAM heap can be too fragmented for a multi-MB lyric-glyph atlas bitmap (observed on esp32p4: a 1.5 MB bake alloc fired the Rust alloc_error handler and rebooted the device mid-playback).

Make Atlas::parse fallible: cmap/bitmap now use try_reserve_exact and return None on allocation failure, so Fonts::load() reports false and the host degrades to tofu glyphs instead of aborting the whole app.
Add slotExtraChars to bakeAtlases and a --slot-extra-chars=<slot>:<file>
CLI flag so large charset sets (e.g. full GB2312 hanzi) can be baked into
a single font slot instead of being multiplied across every used slot.
ARGB targets now clear to fully transparent and write straight color + coverage into transparent destination pixels, so hosts can composite the scene over their own background layers.

When the destination already holds content, blends use true straight-alpha src-over:
  out_a   = a + dst_a * (1 - a)
  out_rgb = (src * a + dst_rgb * dst_a * (1 - a)) / out_a
instead of the previous "blend against opaque black and force alpha 255", which flattened semi-transparent destination pixels (glyph edges over another layer, translucent backgrounds) on layered surfaces.

Opaque destinations reduce to the previous mix (same rgb, alpha 255), so RGBA/RGB565 targets and existing hosts are unchanged. The ARGB memory layout test now seeds a fullscreen opaque rect so both targets carry content in every pixel under the transparent-clear contract; a reference-math test pins the semi-transparent src-over result.
@odex21 odex21 closed this Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant