Skip to content

feat(render): Add backend-independent damage rendering#173

Merged
doodlewind merged 4 commits into
pocket-stack:mainfrom
HalfSweet:feat/esp32p4-dirty-regions
Jul 24, 2026
Merged

feat(render): Add backend-independent damage rendering#173
doodlewind merged 4 commits into
pocket-stack:mainfrom
HalfSweet:feat/esp32p4-dirty-regions

Conversation

@HalfSweet

@HalfSweet HalfSweet commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Base

Summary

  • add a backend-independent DamageTracker that compares retained DrawLists
    and produces conservative logical damage regions for persistent targets
  • add incremental RGBA8, PPA-memory ARGB8, and RGB565 software raster entry
    points while preserving the existing full-frame APIs
  • integrate the ESP32-P4 PPA renderer with one damage snapshot per persistent
    framebuffer and retain DrawList painter order across PPA and software
    fallback operations
  • expose opt-in incremental WASM exports and switch the browser and playground
    hosts to them
  • default to at most 8 disjoint regions, merging excess regions
    conservatively, and promote damage covering at least 75% of the target to a
    full redraw

Design

The tracker uses a two-phase prepare/commit lifecycle so failed renders do not
advance framebuffer history. Each target snapshot includes the DrawList,
viewport/format/scale signature, and the Core raster-resource revision.
Initial frames, incompatible targets, changed resources, structural DrawList
changes, or explicitly invalidated targets therefore repaint conservatively.

Incremental rasterization clears each selected region and replays the complete
current DrawList under a root clip. This preserves blending, occlusion, and
mixed hardware/software painter order without requiring individual operations
to know what was previously behind them.

The original full-frame rendering ABI remains available for callers without a
persistent framebuffer and for deterministic golden capture.

Commits

  • ec0e103 feat(core): add incremental damage rendering
  • 88961a0 feat(esp32p4): render PPA frames incrementally
  • 5e56599 feat(web): enable incremental wasm rendering
  • c4d55cd docs: describe incremental rendering architecture

Validation

Re-run after rebasing onto main:

  • Core Rust tests: 98 passed
  • ESP32-P4 PPA Rust tests: 17 passed
  • PPA esp-idf feature check passed
  • WASM Rust test and wasm32-unknown-unknown release build passed
  • generated browser WASM incremental ABI smoke passed
  • browser test group: 165 passed
  • git diff --check passed

Additional validation performed before the rebase:

  • Vue SFC, Cafe, DeepZoom, IM, and Launcher simulation groups passed
  • 9 disjoint changes are merged into the default 8 regions, with incremental
    RGB565 and PPA output byte-exact against full-frame references
  • golden run: 42 passed and 7 existing mismatches; rerunning the untouched
    base commit produced byte-identical actual PNGs for all 7 mismatches

The repository-wide Bun command also encountered the existing PSP toolchain
doctor test hanging while validating an invalid explicit LLVM override. The
remaining scripted test groups were run separately and passed.

@HalfSweet
HalfSweet marked this pull request as ready for review July 24, 2026 06:55
@HalfSweet
HalfSweet force-pushed the feat/esp32p4-dirty-regions branch from 04827f1 to c4d55cd Compare July 24, 2026 09:22

@doodlewind doodlewind left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the full diff and ran the complete local gate (the repo has no CI for bun tests/goldens). This is clean work — the damage planner lands at exactly the right altitude and the correctness story is proven, not asserted.

Validation (local merge with current main)

  • engine/core 98/98, esp32p4-ppa 17/17, engine/wasm 1/1, --features esp-idf check clean; PR CI green.
  • Goldens with the rebuilt wasm: 49/49 via the full-render path.
  • Extra end-to-end check I ran: temporarily switched tests/golden.ts capture to renderIncremental()49/49 goldens still pass byte-exactly across all 11 apps, with sparse frame captures exercising multi-frame damage accumulation between renders. The web-host switch cannot drift from the pinned pixels.
  • Full bun run test chain green.

What I particularly like

  • damage.rs is genuinely backend-neutral: mechanism (diff → disjoint conservative regions) in core, policy (region cap, 75% promotion) at the caller. No platform vocabulary anywhere in core/wasm/web code.
  • The prepare/commit transaction plus invalidate() on failed renders, and the compatibility full-render path invalidating the wasm tracker — the two ABIs can interleave safely.
  • Ui::raster_revision also retroactively hardens #160's alpha_texture_cache against same-handle content mutation. Nice catch.
  • Scissor-moved ops are covered by treating the clip rects themselves as damage bounds — subtle and correct.
  • wasm-ops.js feature-detects the new exports and falls back for older wasm builds, honoring the established tolerance convention.

Non-blocking observations

  1. update_texture_t8 bumps the revision every video frame, so incremental hosts do full redraws for the whole playback — correct and no worse than today; a future targeted path could damage only the video quad's bounds.
  2. Fresh texture uploads also bump the revision, forcing one full redraw even for not-yet-referenced textures — over-conservative but cheap and rare.
  3. docs/DESIGN.md calls the byte order "PPA-memory ARGB8" — core code correctly says LE-ARGB8888/BGRA-memory; suggest the doc adopt the neutral name too.
  4. Per-region replay re-decodes the full DrawList up to 8×; pixel work dominates and the promotion policy caps pathology, so fine as shipped — worth remembering if region counts ever grow.

Merge-ready from my side (squash subject should lowercase "add" per the Conventional Commits house style).

@doodlewind
doodlewind merged commit 00149da into pocket-stack:main Jul 24, 2026
2 checks passed
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.

2 participants