Skip to content

feat(core): transparent ARGB compositing with retained raster layer passes - #248

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

feat(core): transparent ARGB compositing with retained raster layer passes#248
odex21 wants to merge 2 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.

Layer constraints

A node becomes a retained layer only when all of these hold:

  • rasterCache = Retained (opt-in hint)
  • World transform is translation-only (scale/rotation/perspective fall back)
  • overflow: hidden (visible would clip out-of-bounds children)
  • No shadow (shadow draws outside node bounds and would be clipped)

Nodes that do not meet these constraints paint into the surrounding regular Draw pass, preserving flat rendering equivalence.

Verification

  • cargo test: 115/115 (each commit individually green)
  • contracts drift guard: all green (spec.ts <-> generated spec.rs)
  • flat Ui::draw() output unchanged (existing layout/golden tests)
  • regression tests cover overflow:visible and shadow fallback paths

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

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 marked this pull request as ready for review August 11, 2026 10:40
@odex21
odex21 force-pushed the codex/upstream-core-candidates branch 2 times, most recently from 726fe24 to 1acb1f4 Compare August 11, 2026 10:44
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.

Layers require overflow:hidden and no shadow to avoid clipping out-of-bounds children or shadow pixels; both fall back to regular Draw passes.
@odex21
odex21 force-pushed the codex/upstream-core-candidates branch from 1acb1f4 to c533b3c Compare August 11, 2026 10:45
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