penpot/render-wasm/AGENTS.md
2026-05-07 09:45:54 +02:00

3.0 KiB
Raw Permalink Blame History

render-wasm Agent Instructions

This component compiles Rust to WebAssembly using Emscripten + Skia. It is consumed by the frontend as a canvas renderer.

Commands

./build          # Compile Rust → WASM (requires Emscripten environment)
./watch          # Incremental rebuild on file change
./test           # Run Rust unit tests (cargo test)
./lint           # clippy -D warnings
cargo fmt --check

Run a single test:

cargo test my_test_name          # by test function name
cargo test shapes::              # by module prefix

Build output lands in ../frontend/resources/public/js/ (consumed directly by the frontend dev server).

Build Environment

The _build_env script sets required env vars (Emscripten paths, EMCC_CFLAGS). ./build sources it automatically. The WASM heap is configured to 256 MB initial with geometric growth.

Architecture

Global state — a single unsafe static mut State accessed exclusively through the with_state!, with_state_mut!, with_current_shape!, with_current_shape_mut!, and with_state_mut_current_shape! macros (defined at the top of src/main.rs). Never access the global directly. Use with_current_shape_mut! when modifying a shape — it calls state.touch_current() to mark the shape dirty for re-render; read-modify-writes that bypass it will silently skip invalidation.

Tile-based rendering — only 512×512 tiles within the viewport (plus a pre-render buffer) are drawn each frame. Tiles outside the range are skipped.

Two-phase updates — shape data is written via exported setter functions (called from ClojureScript), then a single render_frame() triggers the actual Skia draw calls.

Shape hierarchy — shapes live in a flat pool indexed by UUID; parent/child relationships are tracked separately.

Key Source Modules

Path Role
src/main.rs WASM entry points (init, clean_up, render driver) and the state-access macros
src/state.rs, src/state/ Global State struct, ShapesPool, TextEditorState
src/wasm/ #[no_mangle] extern "C" exports grouped by subsystem (shapes, text, layouts, paths, fills, ...)
src/render.rs, src/render/ Render loop, tile cache, Skia surface management
src/shapes.rs, src/shapes/ Shape types, layouts, modifiers, transforms, per-shape draw inputs
src/mem.rs WASM memory allocation (write_bytes, free_bytes) used by the FFI

Frontend Integration

The WASM module is loaded by app.render-wasm.* namespaces in the frontend. ClojureScript calls exported Rust functions to push shape data, then calls render_frame. Do not change export function signatures without updating the corresponding ClojureScript bridge.

Deeper Context

For longer-form architecture, conventions, V3 text editor internals, and performance design lessons, see the penpot-render-wasm skill at .opencode/skills/penpot-render-wasm/. This AGENTS.md is the short-form module guide; the skill carries the depth.