Skip to content
Open
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
6 changes: 6 additions & 0 deletions contracts/spec/spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,10 @@ export const PROP = {
// center = node center, outer radius = min(w,h)/2, color
// = bgColor. Axis-aligned worlds only (rotation belongs
// in arcStart).
rasterCache: 143, // enum RasterCache. Retained asks capable hosts to cache
// this subtree as a transparent raster layer and apply
// translation during composition. Flat draw() output is
// unchanged; hosts opt in through Ui.draw_retained().
} as const;

export type PropName = keyof typeof PROP;
Expand Down Expand Up @@ -485,6 +489,7 @@ export const PROP_VALUE_KIND: Record<PropName, number> = {
rotateX: VALUE_KIND.f32, rotateY: VALUE_KIND.f32,
translateZ: VALUE_KIND.f32, perspective: VALUE_KIND.f32,
arcStart: VALUE_KIND.f32, arcSweep: VALUE_KIND.f32, arcWidth: VALUE_KIND.f32,
rasterCache: VALUE_KIND.int,
};

// ---------------------------------------------------------------------------
Expand All @@ -502,6 +507,7 @@ export const ENUMS = {
TextAlign: { Left: 0, Center: 1, Right: 2 },
/** Gradient direction: `bg-gradient-to-t|b|l|r`. */
GradDir: { ToTop: 0, ToBottom: 1, ToLeft: 2, ToRight: 3 },
RasterCache: { None: 0, Retained: 1 },
/**
* Animation easing. Spring/SpringBouncy ignore durMs (physics decide);
* OutBack overshoots ~10%. All tick at fixed dt = 1/60 s.
Expand Down
3 changes: 2 additions & 1 deletion docs/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ PocketJS/
pak container constants (magic/header/entry/align/fnv1a) [R]
gen-rust.ts codegen → engine/core/src/spec.rs (committed)
engine/core/ Rust lib `pocketjs-core` — #![no_std] + alloc
framework/src/lib.rs pub struct Ui: apply-ops, tick(1/60), draw() → &DrawList
framework/src/lib.rs pub struct Ui: apply-ops, tick(1/60), draw() → &DrawList;
draw_retained() → ordered Draw/Layer passes for capable hosts
framework/src/spec.rs GENERATED — tests/contract.ts re-runs gen-rust.ts and
byte-compares this file (airtight drift guard) [R]
framework/src/tree.rs node arena: Vec<Node> + free list + GENERATION COUNTER
Expand Down
59 changes: 59 additions & 0 deletions docs/RETAINED_LAYERS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Retained raster layers

Status: accepted and implemented.

## Context

`Ui::draw()` deliberately produces one flat DrawList. That contract is simple
and deterministic, but a host cannot reuse subtree pixels when only the
subtree's screen translation changes. Re-rasterizing text during scrolling is
especially expensive on small no-JIT systems.

## Decision

Property id 143 is the append-only `rasterCache` integer property. A value of
`RasterCache.Retained` asks a capable host to cache that subtree as a
transparent raster surface. It is a hint: ordinary `Ui::draw()` stays flat and
pixel-compatible.

Capable hosts opt in with `Ui::draw_retained()`. It returns ordered
`RetainedPass` values:

- `Draw` contains regular content before or after a layer;
- `Layer` contains a layer-local DrawList, logical surface dimensions, screen
translation, and the inherited screen clip.

Hosts must composite every pass in order. A layer's DrawList remains unchanged
when only its translation changes, so the host can keep pixels and update the
composition coordinates. The explicit-size ARGB surface raster function avoids
pretending that the layer is a viewport-sized framebuffer.

The core currently retains translation-only 2D layer roots whose pixels are
bounded by their logical box (`overflow: hidden` with no root shadow). Scale,
rotation, perspective, visible overflow, or a root shadow falls back to the
surrounding regular DrawList. This preserves content until a host can implement
those cases without forcing re-rasterization.

## Consequences

- The flat DrawList ABI and existing hosts do not change behavior.
- A retained host owns surface allocation, cache invalidation, and composition
scheduling.
- Retaining many or large subtrees can consume substantial memory; the hint is
intentionally explicit rather than automatic.

## Rejected alternatives

- Moving a rectangle after rendering the flat frame does not avoid subtree
raster work and cannot recover painter-order ownership.
- Making all transformed nodes implicit layers makes memory and scheduling
costs unpredictable.
- Teaching the application to draw text through a platform-specific side path
duplicates layout and font semantics outside PocketJS.

## Verification

- Core tests compare flat raster output with CPU composition of translated and
clipped retained passes (maximum channel delta: one, from blend rounding).
- Translation-only tests assert that layer-local words remain stable while the
composition coordinates change.
Loading
Loading