Commit Graph
4 Commits
Author SHA1 Message Date
iris-ai 71c9c39523 Replace placement calls with region nodes 2026-09-15 18:02:28 -04:00
iris-aiandClaude Opus 5 7c50a3e51b Rename a length's abs component to px
`dp` is coming, and then `abs` says which of the two it is not. The
component has always been a pixel count, so the name that admits it is
the one that leaves room for a second unit beside it.

Mechanical: the field on `Len` and `UiScalar`, their constructors,
`to_abs`/`get_abs`, the matching WGSL struct member and the locals
composing it. Field order and types are unchanged, so the `Pod` layout
the shader reads is the same bytes. `f32::abs` is untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 22:29:20 -04:00
iris-aiandClaude Opus 5 d98969158f Give a slot to the children a container places, and nothing else
A widget's region is now held in the coordinates of the slot it draws in
rather than the window's, and `Painter::place` is how a container asks for a
slot: it draws a child it decides the box of and may decide again. Everything
under that slot is a fraction of its box, so placing the child a second time
is one entry to write whether it moved or changed length. A child drawn any
other way has no slot and shares its nearest ancestor's.

That is what keeps the chain short. `chain_cost` measured depth as the cost
-- free to 8, +42.6% at 16 -- and a slot per widget put a transcript's glyphs
past that for nothing, since almost every slot was zero. `Span`, `Aligned`
and `Scroll` are the containers that re-place a child after drawing it, and
`tests/layout.rs` pins that four widgets between a span and a leaf leave the
leaf's chain one deep.

`UiRegion::stretch`, `UiRegion::stretchable` and `UiScalar::stretch` are
gone. Nothing is inverted any more: a box that changed length is written to
its slot, and the descendants recompose against it in the shader. That also
retires the case the guard existed for, where a fixed length has no fraction
to recover -- `tests/layout.rs` now stretches a 40-tall row on its other
axis, which `stretchable` refused outright.

What still walks the CPU is deciding who must draw again, which no chain can
answer: `mark_resized` descends from the widget whose box changed and marks
anything whose own box changed length and whose drawing reads it. A part of
a box with no relative extent on an axis is a fixed length, and composing
into it leaves none either, so the walk stops where a length did not change
-- an 80-wide child in a widened row is not redrawn though it says `Redraw`.

`Span`, `Pad`, `Stack`, `Offset`, `Aligned`, `SetSize` and `LayerOffset` say
`Scale`: each places in fractions and offsets of its own box and none reads
the box's pixel length. `Scroll` and `MaxSize` do read pixels and stay
`Redraw`.

45 tests pass, five of them new. Render verification comes after the CPU
side, per the owner.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 12:25:37 -04:00
iris-ai d8c497fcd7 Measure what the chain walk costs, and find that depth is the cost
`tests/chain_cost.rs` times the pass on the GPU with timestamp queries,
which this adapter supports, rather than by the clock. 200,000 two-pixel
instances at 1024x1024, so vertex work dominates, best of eight batches:

  depth  1     77.9 us    +0.0%
  depth  2     78.1 us    +0.2%
  depth  4     79.0 us    +1.3%
  depth  8     81.8 us    +5.0%
  depth 16    111.1 us   +42.6%
  depth 32    159.6 us  +104.9%
  depth 64    250.3 us  +221.3%

Free to about depth 8 and then roughly 3 us per level. Each step is a
storage load whose address is the previous load's result, so it is the
chaining that costs rather than the arithmetic at each level -- which
means the number would look the same for a slot carrying a whole region
instead of a delta.

That matters because every active widget owns a slot, so a primitive
resolves through its full depth in the widget tree, and LAYOUT.md notes
real trees have exceeded 16. At the couple of hundred primitives an
example draws it is nothing; a transcript's glyphs are tens of thousands
of primitives, which is the regime measured here.

No behaviour change. Recorded rather than acted on: keeping the chain
shallow means not giving every widget a slot, which is a design decision
of LAYOUT.md §2 and §6 and the owner's to make.
2026-09-14 03:31:33 -04:00