Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 60 additions & 2 deletions .github/workflows/esp32p4.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,36 @@ on:
- ".github/workflows/esp32p4.yml"
- "engine/core/**"
- "engine/backends/esp32p4-ppa/**"
- "engine/crates/pocket-mod/**"
- "engine/crates/pocket-ui-surface/**"
- "contracts/**"
- "framework/**"
- "hosts/esp32p4/**"
- "tools/build.ts"
- "tools/esp32p4*.ts"
- "tests/esp32p4*.test.ts"
- "vapor/boards/**"
- "vapor/compiler/**"
- "package.json"
- "bun.lock"
push:
branches: [main]
paths:
- ".github/workflows/esp32p4.yml"
- "engine/core/**"
- "engine/backends/esp32p4-ppa/**"
- "engine/crates/pocket-mod/**"
- "engine/crates/pocket-ui-surface/**"
- "contracts/**"
- "framework/**"
- "hosts/esp32p4/**"
- "tools/build.ts"
- "tools/esp32p4*.ts"
- "tests/esp32p4*.test.ts"
- "vapor/boards/**"
- "vapor/compiler/**"
- "package.json"
- "bun.lock"
workflow_dispatch: {}

permissions:
Expand All @@ -29,6 +51,11 @@ jobs:

- uses: dtolnay/rust-toolchain@stable

- uses: oven-sh/setup-bun@v2

- name: Install JavaScript dependencies
run: bun install --frozen-lockfile

- name: Test RGB565 core rasterizer
run: cargo test --locked --manifest-path engine/core/Cargo.toml

Expand All @@ -38,16 +65,47 @@ jobs:
- name: Check ESP-IDF Rust adapter
run: cargo check --locked --manifest-path engine/backends/esp32p4-ppa/Cargo.toml --features esp-idf

- name: Test complete QuickJS host runtime
run: cargo test --locked --manifest-path hosts/esp32p4/runtime/Cargo.toml

- name: Test ESP32-P4 build and device tooling
run: bun test tests/esp32p4-profile.test.ts tests/esp32p4-device.test.ts

- name: Generate framework style module
run: bun tools/build.ts hero >/dev/null

- name: Check TypeScript contracts
run: bunx tsc --noEmit

full-host:
name: Full PocketJS host final link
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4

- name: Cross-build QuickJS and link the Waveshare firmware
uses: espressif/esp-idf-ci-action@v1
with:
esp_idf_version: v5.5.4
target: esp32p4
extra_docker_args: --user root
command: bash hosts/esp32p4/waveshare-7b/ci-build.sh

esp-idf:
name: ESP-IDF release/v6.0
name: ESP-IDF ${{ matrix.idf }}
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
idf: [v5.5.4, release-v6.0]
steps:
- uses: actions/checkout@v4

- name: Build ESP32-P4 component smoke app
uses: espressif/esp-idf-ci-action@v1
with:
esp_idf_version: release-v6.0
esp_idf_version: ${{ matrix.idf }}
target: esp32p4
path: hosts/esp32p4/examples/ppa-smoke
13 changes: 10 additions & 3 deletions docs/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,18 @@ artifacts: `$JOB_TMP/map-*.json`).
region and replay the complete DrawList under a root clip; framebuffer
format/scale signatures and `Ui::raster_revision()` prevent stale reuse.
Region limits and full-redraw thresholds remain backend policy.
- **Rounded gradients are small retained layers**: the core bakes the exact
analytic-span pixels into bounded PSM 8888 `[R,G,B,A]` textures and emits
stable `TEX_QUAD` tiles. The cache is capped at 256 KiB, reuses
generation-tagged texture slots under phase churn, and falls back to the same
spans when a layer cannot fit. It never creates a full-frame 32-bit
intermediate.
- **ESP32-P4 stays 16-bit**: `engine/backends/esp32p4-ppa/` consumes the same
DrawList into an opaque RGB565 target. It maps flat fills, A8 coverage
blending, and compatible PSM 5650 texture transforms to the PPA, then
preserves ordering with the core RGB565 rasterizer for unsupported ops.
It never allocates a full-frame RGB888/ARGB8888 intermediate.
blending, straight-alpha PSM 8888 texture blending, and compatible PSM 5650
texture transforms to the PPA, then preserves ordering with the core RGB565
rasterizer for unsupported ops. It never allocates a full-frame
RGB888/ARGB8888 intermediate.
- **Native animation**: tweens/springs tick in Rust per vblank with **fixed
dt = 1/60 s** (frame content is a pure function of frame index — this is
what makes byte-exact goldens possible **[R]**). JS only declares motion.
Expand Down
17 changes: 11 additions & 6 deletions engine/backends/esp32p4-ppa/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@ Accelerated paths:
- antialiased font runs (`A8` coverage composed once, then `BLEND`);
- single-color alpha textures such as PocketJS rounded-corner masks (`A8`
`BLEND`);
- straight-alpha PocketJS PSM 8888 `[R,G,B,A]` texture quads (`RGBA8`
`BLEND`), including bounded retained rounded-gradient layers;
- opaque PSM 5650 texture quads (`SRM`) when scaling semantics are compatible.

Gradients, arbitrary triangles, textured triangles, and unsupported texture
formats fall back to `pocketjs_core::raster::render_scaled_rgb565_over`.
Uncached gradients, arbitrary triangles, textured triangles, and unsupported
texture formats fall back to
`pocketjs_core::raster::render_scaled_rgb565_over`.
No full-frame RGB888 or ARGB8888 surface is allocated.

## Incremental rendering
Expand All @@ -36,10 +39,12 @@ composition without touching unchanged pixels.

Keep one `RenderTargetState` per framebuffer. This is required for
double-buffered hosts because each target contains a different older frame.
The first render and structural DrawList changes use a conservative full
redraw. This backend additionally promotes damage covering at least 75
percent of the viewport; that transaction-cost policy is deliberately kept
outside the common damage planner.
The first render uses a conservative full redraw. Bounded structural edits
are localized by retaining identical DrawList prefixes and suffixes; malformed
lists and changes that cannot be localized fail closed. This backend also
promotes damage covering at least 75 percent of the viewport; that
transaction-cost policy is deliberately kept outside the common damage
planner.

Core-managed texture, font, and style mutations bump `Ui::raster_revision()`,
so every `RenderTargetState` automatically forces a complete repaint and the
Expand Down
53 changes: 53 additions & 0 deletions engine/backends/esp32p4-ppa/src/esp_idf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,25 @@ unsafe extern "C" {
blue: u8,
global_alpha: u8,
) -> i32;
fn pocketjs_ppa_blend_rgba8888_rgb565(
handle: *mut c_void,
destination: *mut u16,
destination_pixels: usize,
width: u32,
height: u32,
source: *const u8,
source_len: usize,
source_width: u32,
source_height: u32,
source_x: u32,
source_y: u32,
source_rect_width: u32,
source_rect_height: u32,
destination_x: u32,
destination_y: u32,
destination_rect_width: u32,
destination_rect_height: u32,
) -> i32;
fn pocketjs_ppa_srm_psm5650_rgb565(
handle: *mut c_void,
destination: *mut u16,
Expand Down Expand Up @@ -155,6 +174,40 @@ impl PpaOps for EspIdfPpaOps {
}
}

fn blend_rgba8888_rgb565(
&mut self,
destination: &mut [u16],
width: u32,
height: u32,
source: &[u8],
source_width: u32,
source_height: u32,
source_rect: Rect,
destination_rect: Rect,
) -> bool {
unsafe {
pocketjs_ppa_blend_rgba8888_rgb565(
self.handle,
destination.as_mut_ptr(),
destination.len(),
width,
height,
source.as_ptr(),
source.len(),
source_width,
source_height,
source_rect.x,
source_rect.y,
source_rect.w,
source_rect.h,
destination_rect.x,
destination_rect.y,
destination_rect.w,
destination_rect.h,
) != 0
}
}

fn srm_psm5650_to_rgb565(
&mut self,
destination: &mut [u16],
Expand Down
Loading