Skip to content

Commit 4974173

Browse files
author
Roy Lin
committed
feat: add native layout scene pipeline
1 parent ed4c471 commit 4974173

14 files changed

Lines changed: 3527 additions & 4 deletions

File tree

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ The crate provides:
3939
announcements, and field-level backend capability reporting
4040
- Tailwind-compatible utility parsing into a platform-neutral native style
4141
model
42+
- versioned 1/64-point layout snapshots, stable keyed hit regions, explicit
43+
projection diagnostics, and Native IR-to-Graphics rectangle lowering
4244
- a built-in `rsx_ui` component registry plus calculator, dogfood, and
4345
component-playground applications
4446

@@ -326,6 +328,11 @@ constraints, and explicit keypad sizing. Their semantics and layout intent are
326328
therefore shared. Exact pixels still follow each native toolkit's control
327329
metrics, font fallback, DPI scaling, and rendering behavior.
328330

331+
The self-drawn rectangle fixture now compiles that same shared calculator tree
332+
into versioned layout records and a Graphics Scene. It pins deterministic
333+
layout/scene fingerprints, retained software output, and a reviewed local DX12
334+
readback threshold; text and real window presentation remain roadmap work.
335+
329336
Direct native examples follow the same naming pattern:
330337

331338
```sh
@@ -413,6 +420,7 @@ libraries.
413420

414421
- [Runtime and protocol architecture](docs/architecture.md)
415422
- [Renderer field inventory](docs/renderer-field-inventory.md)
423+
- [Layout and Graphics scene contract](docs/layout-scene.md)
416424
- [Native app shell](docs/app-shell.md)
417425
- [Native packaging](docs/packaging.md)
418426
- [React Aria native direction](docs/react-aria-native.md)

docs/architecture.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,17 @@ enumerates all current `PortableStyle` fields and exhaustively assigns native
161161
roles and normalized input events to their first complete delivery milestone.
162162
Later-milestone fields remain explicit diagnostics in earlier renderer slices.
163163

164+
The [layout and scene contract](layout-scene.md) now implements the first
165+
generic vertical slice. `NativeElement` keys become collision-safe stable layout
166+
paths; boxes are quantized to 1/64 logical point; paint, hit regions, and
167+
diagnostics share those identities. The semantic-only layout module produces a
168+
versioned `LayoutSnapshot`, while the optional Graphics adapter rejects
169+
error-level M3 omissions and derives stable `DrawId` values from layout paths.
170+
The existing shared calculator tree pins layout and scene fingerprints plus
171+
software and local Direct3D 12 image evidence. This is rectangle-path evidence,
172+
not a claim that text, full flex/grid behavior, presentation, or M4 interaction
173+
has landed.
174+
164175
The remaining dependencies stay one-way:
165176

166177
- `ComponentCx`, RSX parsing, and `rsx_ui` authoring compile outward-facing

docs/layout-scene.md

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# Layout and Scene Contract
2+
3+
Updated: 2026-08-01
4+
5+
The first self-drawn renderer slice consumes the existing `NativeElement`
6+
tree. It does not define a calculator-specific visual tree or copy component
7+
state into a renderer model.
8+
9+
```rust
10+
use a3s_gui::drawing::{scene_from_layout, LayoutSceneOptions};
11+
use a3s_gui::layout::layout_native_tree;
12+
use a3s_gui::Size;
13+
14+
let layout = layout_native_tree(&native, Size::new(410.0, 620.0))?;
15+
layout.require_supported()?;
16+
let scene = scene_from_layout(&layout, LayoutSceneOptions::default())?;
17+
# Ok::<(), a3s_gui::GuiError>(())
18+
```
19+
20+
Layout is available in semantic-only builds. Graphics scene extraction is
21+
available only with the `graphics` feature, while rendering remains behind
22+
`software-reference` or `gpu`.
23+
24+
## Versioned records
25+
26+
`LayoutSnapshot` schema version 1 contains:
27+
28+
- logical surface size and boxes quantized to 1/64 logical point
29+
- flat `LayoutNodeRecord` values with role, parent identity, border/content
30+
boxes, inherited clip, z-index, paint order, hit eligibility, and box paint
31+
- separate `LayoutHitRegion` values keyed to the same semantic elements
32+
- structured warnings and errors with element and field attribution
33+
- deterministic serialization, fingerprinting, and node-level layout diffs
34+
35+
`LayoutElementId` is a path of byte-length-prefixed sibling keys. Reordering a
36+
sibling does not change its identity, and `/` or other punctuation in a key
37+
cannot make two paths collide. The Graphics adapter derives each `DrawId` from
38+
that path plus a stable paint slot.
39+
40+
The snapshot contains no labels, values, passwords, native handles, GPU
41+
objects, or product state.
42+
43+
## Implemented M3 slice
44+
45+
The current deterministic box path implements the subset needed by the shared
46+
calculator:
47+
48+
- vertical block flow and no-wrap horizontal/vertical flex flow
49+
- explicit, percentage, minimum, and maximum size with border/content sizing
50+
- physical and horizontal-writing logical padding, margin, inset, and border
51+
- row/column gap, source order, integer order, main/cross-axis alignment, and
52+
stretch for auto cross sizes
53+
- relative, absolute, and fixed positioning
54+
- overflow rectangle clipping, visibility, opacity, pointer eligibility,
55+
simple sibling z-order, and cumulative descendant opacity
56+
- solid color backgrounds, per-edge solid borders, circular corner radii, and
57+
opaque rounded background/border composition
58+
59+
The portable Tailwind contract historically treats a positive border width as
60+
solid when no explicit border style is present. Explicit `none` and `hidden`
61+
still suppress the border. The layout path preserves that behavior so it
62+
matches the existing native backends without depending on browser Preflight.
63+
64+
Full flex growth and shrinkage, wrapping, grid/table layout, vertical writing,
65+
baseline alignment, elliptical radii, CSS expression evaluation, inherited or
66+
functional colors, and complete stacking contexts are not claimed by this
67+
slice.
68+
69+
## Projection diagnostics
70+
71+
Every effective `PortableStyle` field is checked against the executable
72+
[renderer field inventory](renderer-field-inventory.md).
73+
74+
- A later-milestone field produces a warning. Text and control roles therefore
75+
retain their M3 boxes while their visible content remains an explicit M4
76+
item.
77+
- An M3 field or value that this slice cannot project produces an error.
78+
- A property retained in `PortableStyle::unsupported` produces an error.
79+
- `scene_from_layout` rejects any snapshot containing an error before it emits
80+
a draw command.
81+
82+
This lets inspection tools display partial boxes without allowing a required
83+
calculator style to disappear silently from an accepted scene fixture.
84+
85+
## Retention and paint ordering
86+
87+
Repeated snapshots have identical fingerprints. `LayoutSnapshot::diff`
88+
reports stable-key additions, removals, changed records, dirty bounds, and
89+
surface rebuilds. Graphics performs the subsequent primitive diff and retained
90+
damage calculation.
91+
92+
A parent background is emitted before its descendants. Siblings are placed by
93+
z-index, flex order, and source order, and each subtree remains contiguous.
94+
This is sufficient for the calculator and simple overlays; complete CSS
95+
stacking-context behavior remains M3 work.
96+
97+
Overflow clips apply to descendants and are intersected through the tree.
98+
Fixed-position descendants use the viewport rather than inheriting an ordinary
99+
ancestor clip. Hit regions use the same quantized identity and visible bounds
100+
but remain separate from paint commands.
101+
102+
## Calculator evidence
103+
104+
`tests/calculator_layout_scene.rs` compiles the existing shared calculator RSX,
105+
lowers it through `RsxCompilerBridge`, wraps the real 410 by 620 window native
106+
tree, and then uses this generic path. The fixture pins:
107+
108+
- layout fingerprint `16529597026056060935`
109+
- Graphics scene fingerprint `2100550662756266801`
110+
- exact repeated software output and retained no-damage behavior
111+
- background, transparent exterior, ordinary-key, and equals-key pixels
112+
113+
On the 2026-08-01 local Direct3D 12 run, GPU readback differed from the software
114+
reference at 940 of 254,200 pixels (0.370%), all around rasterized edges, with a
115+
maximum channel delta of 91. The reviewed non-text gate allows at most 0.5% of
116+
pixels and a maximum channel delta of 96 while requiring the listed solid
117+
pixels to match exactly.
118+
119+
An unavailable adapter skips the local GPU test and is not cross-platform
120+
evidence. Metal and Vulkan runs, real window presentation, text, input, IME,
121+
and accessibility remain separate roadmap gates.

docs/roadmap.md

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ Acceptance gates:
219219

220220
### M1 - GUI architecture cleanup and dependency integration
221221

222-
Status: current.
222+
Status: complete.
223223

224224
Landed evidence:
225225

@@ -238,8 +238,8 @@ Landed evidence:
238238
- the versioned renderer inventory accounts for all 504 `PortableStyle`
239239
fields, every `NativeRole`, all normalized input events, and the focus,
240240
overlay, text, and accessibility records required by cutover
241-
242-
Remaining work is the first `NativeElement` layout-to-scene adapter.
241+
- the first generic `NativeElement -> LayoutSnapshot -> Graphics Scene`
242+
adapter landed with stable keyed identity and explicit projection diagnostics
243243

244244
Deliverables:
245245

@@ -285,7 +285,28 @@ Acceptance gates:
285285

286286
### M3 - Generic layout and scene vertical slice
287287

288-
Status: planned after M2.
288+
Status: current.
289+
290+
Landed evidence:
291+
292+
- layout schema version 1 records 1/64-point quantized boxes, stable
293+
length-prefixed element paths, paint, clips, z/order, and separate hit regions
294+
- the generic engine covers the calculator's row/column flow, box model,
295+
explicit/min/max size, alignment, absolute positioning, overflow clipping,
296+
opacity, and solid rectangle paint without a calculator renderer model
297+
- unsupported M3 fields are error diagnostics rejected by scene extraction;
298+
later role/style work remains visible as warnings
299+
- stable layout diffs feed the Graphics scene/damage diff, and stable layout
300+
paths derive retained `DrawId` values
301+
- the existing shared 410 by 620 calculator Native IR pins layout fingerprint
302+
`16529597026056060935` and scene fingerprint `2100550662756266801`
303+
- repeated software output is byte-identical with no retained damage; the local
304+
Direct3D 12 readback passed the reviewed 0.5%/96 non-text threshold with
305+
exact solid-color checkpoints
306+
307+
Remaining work includes full flex growth/shrink/wrap, complete stacking
308+
contexts, redraw scheduling, Metal/Vulkan evidence, and real thin-host window
309+
presentation.
289310

290311
Deliverables:
291312

src/drawing.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ pub use a3s_graphics::{
1010
Primitive, Rect, Scene, SceneBuilder, Size, StrokeRect, SCENE_SCHEMA_VERSION,
1111
};
1212

13+
mod layout_scene;
14+
pub use layout_scene::{scene_from_layout, LayoutSceneOptions};
15+
1316
#[cfg(feature = "gpu")]
1417
pub use a3s_graphics::{
1518
GpuBackend, GpuBackendPreference, GpuCapabilities, GpuDeviceType, GpuFrame, GpuPowerPreference,

0 commit comments

Comments
 (0)