feat(sifli): SF32LB5x host with EPIC/VG Lite executors and a one-pass planner - #338
Draft
HalfSweet wants to merge 23 commits into
Draft
feat(sifli): SF32LB5x host with EPIC/VG Lite executors and a one-pass planner#338HalfSweet wants to merge 23 commits into
HalfSweet wants to merge 23 commits into
Conversation
TEXT_RUN and SURFACE_QUAD are known members of the closed DrawList op set that a fixed-function RGB565 target never renders. The walker used to fail the whole frame on them, which made the host repaint everything through the software rasterizer; now it advances past them exactly like the core rasterizer.
Core commit 'perf(core): retain damage across structural changes' resynchronizes inserted or removed operations at exact anchors instead of forcing a full redraw. Update the structural-change test and the README sentence that described the old behaviour.
Move the damage tracker's private DrawList decoder into pocketjs_core::drawlist so hardware backends can decode a DrawList once per frame with the same op lengths, scissor tracking, and conservative bounds. Classify every uploaded texture as coverage-only (white RGB or nearest-transparent texels) at upload and after in-place T8 updates, and mark baked corner discs directly, so backends no longer scan texture bytes per frame.
Route the rounded-corner and alpha-only texture decision through Ui::texture_coverage_only instead of scanning texture bytes and caching the result per handle. In-place T8 updates are reclassified by the core, so the renderer no longer tracks raster revisions; invalidate_resources stays as a no-op for host compatibility.
Replace the per-op EpicOps trait and the hand-written walker with a one-pass planner over the core's public DrawList decoder, a typed command set, and a Submit/Frame executor contract driven by static Capabilities. Executors bind the target once per frame and own the A8 planes; consecutive CPU fallbacks share one rasterizer dispatch behind a single fence. The recording MockGpu executes every command with the core's pixel formulas, so blits and quads now have pixel-parity tests.
CPU fallback no longer needs a CPU-writable framebuffer: when the executor forbids direct writes, each batch is copied into an executor-owned tile, rendered there with the core's window rasterizer, and copied back, split into bands that fit the tile. A8 runs are split into plane-sized bands and a plane is rewritten only after a fence retired the blend reading it. DeferredMockGpu keeps commands in flight until fences and proves the ordering for every capability preset; strip rendering is checked against full renders at scale 1 and 2.
Start hosts/sifli with the two C contracts the SiFli host is built on: pocket_core.h, the core entry points the firmware calls (now with an explicit framebuffer count, a software render for device self-checks, and a DrawList hash), and pocketjs_gpu.h, the command-queue ABI the renderer submits through. The pocketjs-sifli staticlib crate implements the entry points over the SiFli backend and forwards commands to the C queue; contracts/spec/gen-c.ts now writes pocket_spec.h for this host as well.
The planner now asks the executor for a natively registered copy of a texture before falling back to portable bytes, gated by blit_native/blit_quad_native, and keeps tinted blits on the CPU unless the executor reports blit_modulate. Fills and A8 bands are split so no command exceeds the executor's coordinate registers; gradients, blits, and quads that would are rendered by the CPU instead.
Add the C executor behind hosts/sifli/include/pocketjs_gpu.h as an SDK component, the project entry points, and a firmware example that links the whole stack. components/pocketjs_gpu: - pocketjs_gpu.c owns the queue: capability report from the chip table, target binding, command dispatch with validation before any hardware write (a refused command returns -(index+1) and has no side effects), fences, two A8 planes and two RGB565 tiles in L1 SRAM (64-byte aligned, non-retained), the native texture registry keyed by core handle and revision, and DWT-based profiling counters. - pocketjs_gpu_epic.c runs one interrupt-driven HAL transaction at a time through public HAL entry points only: fills (with alpha), 2x2 corner gradients, two-layer A8 blends reading a plane window, native RGB565/ARGB8888/L8 blits scaled by the video layer with horizontal mirroring, and tile copies as one-layer blends. Cache cleans are no-ops for the SRAM planes and cover cached PSRAM sources. - pocketjs_gpu_chip.h derives the constants from the SDK feature gates: EPIC_COORDINATES_MAX, A8 and L8 support, vertical mirroring, VG Lite. - Kconfig: POCKETJS_GPU, plane sizes, registry capacity, the transaction threshold, and direct CPU framebuffer writes (off). hosts/sifli/SConscript builds the pocketjs-sifli staticlib with cargo (project working directory so a project's vendoring config applies), appends it to LINKFLAGS_POST, and includes the components; hosts/sifli/Kconfig is sourced from a project's Kconfig.proj through POCKETJS_ROOT. examples/hero-smoke resolves POCKETJS_ROOT by position inside the repository, provides the allocator and panic hooks, opens the queue, creates a core, renders one frame into a PSRAM2 framebuffer, and prints the render statistics and the draw hash. Verified: scons --board=sf32lb58-lcd_n16r32n1_a1_dpi links main.elf with every pocketjs_gpu_*, pocket_core_*, and HAL_EPIC_* symbol resolved, the planes in SRAM and the framebuffer in PSRAM2, no drv_epic symbols, and no undefined references.
Move everything the two SF32LB58 firmware projects duplicated into a reusable host component, add the tool that generates what a project embeds, and make hero-smoke a real guest. components/pocketjs_host (Kconfig POCKETJS_HOST): - host_heap.c: one rt_memheap in cached PSRAM shared by QuickJS and the Rust core behind an alignment-preserving wrapper; the pocket_heap_* and pocket_rust_panic hooks the staticlib links against. - host_mpu.c: the SF32LB58 MPU override (PSRAM1 cached, PSRAM2 non-cacheable) as a strong mpu_config, under POCKETJS_MPU_OVERRIDE. - host_lcd.c: the RGB565 framebuffer ring in PSRAM2 and the RT-Thread LCD device with one draw_rect_async in flight. - host_input.c: KEY1/KEY2 as LEFT/RIGHT levels, or with POCKETJS_KEY_LONG_PRESS_MS the release-pulse plus long-press contract (CIRCLE on the launcher, return to the launcher in a guest); GT911 touch packed into the wide-coordinate wire form. - host_guest.c: pak loading, native texture registration under the handle's content revision (new pocket_core_texture_revision export), the QuickJS realm with the ui bindings and appLaunch, frame and render. - host_main.c: pocketjs_host_run(catalog) with guest switching at frame boundaries and the serial profiler (perf/render/work lines now report GPU submit and wait time, transactions, refusals, tiles, fences, and A8 bands). - pocketjs_catalog.h: the embedded guest table a project generates. tools/sifli.ts: `assets` builds every guest of a project's pocket-sifli.json, bakes optional native paks, writes pocket_assets.S, pocket_catalog.generated.c, and a SHA-256 manifest; `bake`, `vendor` (offline third-party crates plus .cargo/config.toml), `build`, `audit` (link checks), and `verify`. tools/sifli-bake.ts is the .epic baker as an importable module. tests/sifli-sim.test.ts boots the hero demo at 512x300, density 2, scale 2 and asserts a non-flat, deterministic 1024x600 frame; a project directory in POCKETJS_SIFLI_PROJECT adds its guests and launcher flow. Registered as the "sifli sim" stage and the `bun sifli` script. examples/hero-smoke embeds the repository's hero demo through the catalog, ships the 9 MiB hcpu_flash_code partition table the density-2 pak needs, and relaxes GCC 14 pointer diagnostics for the SDK's QuickJS the same way the projects do. hosts/sifli/README.md and docs/PORTING.md document the integration, the memory map, the executor recipes, the input contract, the profiler fields, and the verification boundary. Verified: the sim smoke passes, the bake is byte-deterministic, the contract test is green, the staticlib builds for thumbv8m, and scons links hero-smoke (main.bin 4,848,128 bytes) with the audit clean: no undefined symbols, HAL EPIC entry points and the strong mpu_config and EPIC_IRQHandler from the components, no drv_epic.
…lits SF32LB58 EPIC has no color matrix and no 3x3 transform, so the renderer's capability-gated routing sent projective quads, RGB-modulated blits, and textures without a native copy to the CPU tile path. Add the second executor the capabilities already describe. components/pocketjs_gpu: - pocketjs_gpu_vglite.c (POCKETJS_GPU_VGLITE, needs the SDK's USING_VGLITE): hands the library a contiguous SRAM pool, binds the framebuffer as VG_LITE_BGR565, and runs BLIT through vg_lite_blit_rect with a translate/scale matrix (negative scale for mirrors), BLIT_QUAD through vg_lite_get_transform_matrix from the source rectangle's TL/BL/BR/TR onto the command's quad, tints through VG_LITE_MULTIPLY_IMAGE_MODE, global alpha through vg_lite_source_global_alpha, clips through the scissor, and indexed textures through a CLUT rebuilt per palette. Solid quads sample a 4x4 color texture in SRAM. A vg_lite_finish failure disables the engine for the session. - The registry accepts portable IMG entries (PSM_5650/4444/8888/T8) next to native blobs; VG Lite reads those formats directly. Sources must be 64-byte aligned and cache-clean, which registration enforces, so the queue advertises no inline portable formats and reports BLIT_QUAD_NATIVE | BLIT_MODULATE when VG Lite is up. - The queue tracks the engine in use and drains the other one before a command switches engines; fences and end drain both. The profile counts engine_switches and the host prints it as `switches=`. components/pocketjs_host registers every image without a native copy as a staged portable copy when VG Lite is enabled, so a guest needs no .epic pak to blit through hardware. Documented in hosts/sifli/README.md and docs/PORTING.md, including the conventions still to confirm on a board (tint channel order, SRC_OVER against non-premultiplied sources, projective sampling phase). Verified: hero-smoke links with USING_VGLITE and POCKETJS_GPU_VGLITE (vg_lite_blit_rect, vg_lite_get_transform_matrix, V2D_GPU_IRQHandler resolved, pool in SRAM), the audit is clean, and the renderer crate is unchanged (its routing was already capability-driven).
Two validation modes for the board, and the simulator side that reads them back. components/pocketjs_host: - POCKETJS_SELF_CHECK renders every POCKETJS_SELF_CHECK_INTERVAL-th frame a second time with pocket_core_render_rgb565_software into a scratch framebuffer in PSRAM2 and prints the mismatch ratio, PSNR over all channels, the largest 8-bit channel delta, both CRC32s, the hardware work of the frame, the VG Lite command count, and up to 16 differing pixels. - POCKETJS_FRAME_CRC prints an IEEE CRC-32 of every presented frame with the DrawList hash. - POCKETJS_FORCE_SOFTWARE renders every frame through the software rasterizer to validate the heap, MPU, LCD ring, and input chain before trusting the hardware path. - The GPU profile counts VG Lite commands and can be read without a reset. engine/wasm exports ui_render_rgb565_scaled: the core's RGB565 rasterizer at an integer scale, byte for byte the device's software path. hosts/web/wasm-ops.js exposes it as renderRgb565Scaled and the simulator world as renderRgb565. tools/sifli.ts: `crc <output> --frames N --assert <log>` renders the guest in the simulator at 512x300, density 2, scale 2 and compares the CRC sequence with a board log; `selfcheck <log>` applies the acceptance thresholds (exact for fills, A8 blends, and 1:1 copies; PSNR >= 45 dB, maxd <= 8, mismatch <= 0.5 % for EPIC gradients and scaled blits; PSNR >= 38 dB, mismatch <= 3 % for VG Lite; below 35 dB fails). tests/sifli-sim.test.ts covers the CRC check vector, the deterministic RGB565 frame sequence and its log parser, and the thresholds. Verified: the sifli sim tests pass, the renderer and cafe determinism suites still pass, and hero-smoke links with the self-check enabled (scratch framebuffer at 0x62384000, right after the ring).
Describe the SiFli host as it now exists: the once-per-frame planner and typed command stream in engine/backends/sifli-epic, the EPIC and VG Lite executors in hosts/sifli, SRAM tiles and planes instead of CPU framebuffer writes, the SDK-derived coordinate limits, and the self-check boundary. Add the host to the README host table, the portable-backend host list, the hosts tree, and the non-workspace crate list.
…tries A project entry built with --no-config loses its app-local Pocket config (animation themes, tick rate); use the pocket.config.ts next to the entry when it exists, or the manifest's `config` path, and --no-config only when neither applies. Repository demo outputs keep --no-config.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A SiFli SF32LB5x host for PocketJS:
hosts/sifliplus a restructuredengine/backends/sifli-epic. Firmware projects keep their app, board files, and assets; every hardware call lives in this repository.engine/backends/sifli-epicdecodes the DrawList once per frame (pocketjs_core::drawlist, now public), plans each damage region against a staticCapabilitiestable, and emits typed commands (Fill,FillAlpha,BlendA8,Gradient,Blit,BlitQuad,TileOut/TileIn,Fence) to an executor behind theSubmit/Frametraits. The CPU never writes the framebuffer: software fallback renders into executor-owned SRAM tiles, A8 coverage is built in SRAM planes split into bands, and fences appear only where a plane or tile is reused. Coordinate limits (EPIC_COORDINATES_MAX: 1010 on 55x/56x/58x, 505 on 52x/57x) split fills and bands in the planner.MockGpu/DeferredMockGpurun every command with the core's formulas; 43 tests compare against the core RGB565 rasterizer, including deferred/immediate parity, strips, tiles, bands, and native textures.hosts/sifli/components/pocketjs_gpuis the C executor behindpocketjs_gpu.h: EPIC through publicHAL_EPIC_*entry points (one interrupt-driven transaction in flight; fills, 2×2 gradients, two-layer A8 blends, video-layer scaled native blits, tile copies) and, on SF32LB58, VG Lite for projective quads, RGB-modulated blits, and portable-format textures EPIC cannot read (the SDK defines EPIC's color and 3×3 matrices for 57x only). Engines drain each other on a switch; the profile counts switches. Capabilities come from the SDK feature gates inpocketjs_gpu_chip.h.hosts/sifli/components/pocketjs_host: shared PSRAM heap, SF32LB58 MPU override (PSRAM1 cached, PSRAM2 non-cacheable), RGB565 framebuffer ring with onedraw_rect_asyncin flight, keys (level or pulse + long-press semantics) and touch, the QuickJS realm with theuibindings, an embedded guest catalog with frame-boundary switching, the serial profiler, and validation modes (POCKETJS_SELF_CHECK,POCKETJS_FRAME_CRC,POCKETJS_FORCE_SOFTWARE).hosts/sifli/rust: thepocketjs-siflistaticlib (standalone crate) exporting thepocket_core_*C ABI.bun tools/sifli.ts assets|bake|vendor|build|audit|verify|crc|selfcheck,tests/sifli-sim.test.ts(newsifli simstage), a.epicnative pak baker,pocket_spec.has a second output ofcontracts/spec/gen-c.ts, and the wasm exportui_render_rgb565_scaledfor CRC parity.engine/core/src/drawlist.rs(public decoder shared withdamage.rs),Texture::coverage_onlyclassified at upload.esp32p4-ppais unchanged except its structural-damage test, updated for the damage semantics94cd4c8introduced.The two SF32LB58 firmware projects (Motion Lab, Cover Flow launcher) were switched to this layout with local commits; their regenerated
.pak/.epicassets are byte-identical to the previous toolchain's.Verification
cargo test— core 128, sifli-epic 43, esp32p4-ppa 20;bun tests/contract.tsgreen.bun tools/sifli.ts verifyandPOCKETJS_SIFLI_PROJECT=<launcher project> bun test --conditions=browser tests/sifli-sim.test.ts(five guests +RIGHT → CIRCLE → cafe-main).hosts/sifli/rustbuilds forthumbv8m.main-none-eabihf;scons --board=sf32lb58-lcd_n16r32n1_a1_dpilinksexamples/hero-smoke(VG Lite and self-check on), the Motion Lab project (main.bin3,002,016 B), and the launcher project on N16 (8,479,024 B) and A128 NAND (8,463,296 B);bun tools/sifli.ts auditclean on each (no undefined symbols, component-ownedEPIC_IRQHandlerandmpu_config, nodrv_epic).Still on the board
vg_lite_color_tchannel order for tints,VG_LITE_BLEND_SRC_OVERagainst non-premultiplied sources, projective sampling phase.op_histdebug ring are used as the SDK ships them (out of scope by design).