Document per-axis retained layout
This commit is contained in:
1 parent
974d6c7a52
commit
ecce4448f0
2 files changed
+43
-14
No files matched your search
@@ -9,7 +9,7 @@ Canonical `main` is **`ca2b4b2`** (#17, the headless rig). Sixteen slices are
|
||||
in.
|
||||
|
||||
**#18 `split/18-position-chain`** is open and finished apart from one decision:
|
||||
worktree `/home/bob/repos/iris-pr18`, head `82fa6c1`, seventeen commits,
|
||||
worktree `/home/bob/repos/iris-pr18`, head `b1b3eca`, eighteen commits,
|
||||
workspace tests passing, fmt and clippy clean. It is LAYOUT.md §2's position chain, generalised
|
||||
to boxes. `/home/bob/repos/ai-app-2` is on `rustify`, worktree clean.
|
||||
|
||||
@@ -217,9 +217,24 @@ hottest texts draw 32 times. Instrumented layout is about 5.5 ms instead of
|
||||
60.79B instructions total (60.8M/frame), and 23.85B cycles. This is about 58%
|
||||
fewer instructions than the roughly 138M/frame #18 path before coalescing,
|
||||
but still about 1.8x the old 3.14 ms resize median. The remaining multiplier
|
||||
is within one constraint traversal, so per-axis retained size validity is now
|
||||
the useful next question. Re-sending the existing output size also no longer
|
||||
starts a resize.
|
||||
is within one constraint traversal. Re-sending the existing output size also
|
||||
no longer starts a resize.
|
||||
|
||||
`b1b3eca` records pixel reads and retained size inputs per axis. `Text` says it
|
||||
reads only its offered width, while `Scroll` says it reads only its scrolling
|
||||
axis. `Span`, `Aligned`, and `Scroll` ask for an exact hint or a retained child
|
||||
length valid under the box they are about to offer, and draw to measure only
|
||||
when neither exists. On the same depth-8 load, resize now averages 449 widget
|
||||
draws, 296 placement calls, 485 drawn-size reads, 12,164 primitive writes, and
|
||||
107 text shapes. Cached uninstrumented 1,000-frame runs measured 4.95–5.24 ms
|
||||
medians, 55.4B instructions total (55.4M/frame), and 21.5–21.6B cycles: about
|
||||
9% fewer instructions than `82fa6c1`, but still about 1.6x the old 3.14 ms
|
||||
median.
|
||||
|
||||
Pixel comparisons now use a 0.05 physical-pixel tolerance. The comparison is
|
||||
against the last actual layout, not the preceding resize event or retained
|
||||
move, so repeated subpixel changes accumulate and eventually redraw. The
|
||||
generated cold-layout oracle uses the same visual tolerance per coordinate.
|
||||
|
||||
The generated tree now includes `Aligned` with every meaningful per-axis
|
||||
alignment. That exposed two retained-layout ordering bugs which `84f589e`
|
||||
@@ -227,7 +242,9 @@ fixes. The regular cold-layout equivalence suite and the ignored 100-seed
|
||||
sweep pass. The latter previously stopped at seed 60 on `140.0` versus
|
||||
`139.99996948242188`: exactly two `f32` ULPs from an equivalent composition
|
||||
order, not a visible layout difference. The oracle now keeps draw presence
|
||||
exact and allows at most four ULPs per pixel coordinate.
|
||||
exact and allows at most 0.05 physical pixels per coordinate. Seed 98 is also
|
||||
part of the ordinary suite because it catches stale retained size beneath an
|
||||
aligned size-changing chain.
|
||||
|
||||
The branches are invariants rather than widget exceptions:
|
||||
|
||||
@@ -255,6 +272,14 @@ stale-size failure. Re-measuring the scroll subtree costs 11 draws rather than
|
||||
1, but remains two orders of magnitude below the old 1,313 and follows the
|
||||
actual dependency invariant.
|
||||
|
||||
A frame-local cache of several size answers under different constraint boxes
|
||||
was also discarded. It reached about 3.5 ms in the instrumented resize rig,
|
||||
but seed 98 showed that a valid answer is not enough: the child's one active
|
||||
drawing may still realize another constraint, and a parent can then lay out
|
||||
from an answer its final child placement does not realize. A correct version
|
||||
needs an answer-plus-realization or verification protocol; adding dirtiness
|
||||
conditions to the cache does not fix that invariant.
|
||||
|
||||
Do not restore the archive's old `DrawMode::Measure`. It skipped primitive and
|
||||
retained-state writes while still walking widgets and shaping text, and once
|
||||
improved a streamed-frame benchmark from 1.39 to 1.22 ms p50. Retained
|
||||
|
||||
+13
-9
@@ -106,8 +106,11 @@ stands rather than new machinery:
|
||||
and the primitive's stored `rel`/`abs` pair every frame, already, on the GPU.
|
||||
A widget laid out purely in `rel`/`abs` terms is therefore already correct
|
||||
after a resize with zero CPU work. Calls to `Painter::px_size` and
|
||||
`Painter::output_size` mark the active widget as reading concrete pixels;
|
||||
only those widgets become dirty when the output actually changes.
|
||||
`Painter::output_size` mark both concrete-pixel axes; `px_len(axis)` and
|
||||
`output_len(axis)` mark only the axis actually read. Only widgets whose read
|
||||
axes changed by more than 0.05 physical pixels become dirty. The comparison
|
||||
is against each widget's last actual draw, so smaller changes accumulate
|
||||
rather than disappearing event by event.
|
||||
|
||||
All pixel-dependent leaves are marked before layout begins, along with every
|
||||
chain of parents that read their sizes. Resize then settles the shallowest
|
||||
@@ -145,18 +148,19 @@ An exact `size_hint` stops propagation when both axes still equal the retained
|
||||
size. Otherwise propagation is deliberately conservative: the child may have
|
||||
changed size, and only its dependent ancestors can assign the final boxes.
|
||||
Unchanged descendants still take `draw_inner`'s retained skip-or-move path.
|
||||
For a stacking container, retained child lengths are cached per axis: a child's
|
||||
width remains reusable while the parent changes width, and its height remains
|
||||
reusable while the parent changes height. A change on the orthogonal axis does
|
||||
invalidate it in both directions. This is a generic constraint rule, not a
|
||||
text exception; wrapped text is merely the common example of height depending
|
||||
on width.
|
||||
An active widget also retains which offered-box and output axes flowed into
|
||||
the size it reported, directly or through a child size it read. A container
|
||||
may use that answer for the same prospective box when every observed input is
|
||||
still within 0.05 physical pixels; content dirtiness anywhere in its size
|
||||
dependency subtree rejects the answer. This is a generic constraint rule, not
|
||||
a text exception. Wrapped text is merely the common example: it reads width,
|
||||
so changing only height leaves its answer valid.
|
||||
|
||||
### 4. Wrapped text, and "needs child height before choosing width"
|
||||
|
||||
**Wrapped text is not a special case any more; it already reads as one
|
||||
draw.** `TextView::render` (`iris/src/widget/text/mod.rs:57-76`) already
|
||||
does exactly what single-draw asks for: it reads `ctx.px_size().x` as the
|
||||
does exactly what single-draw asks for: it reads `ctx.px_len(Axis::X)` as the
|
||||
wrap width, shapes once, and memoizes the shaped layout keyed on that width
|
||||
plus a changed-flag on the buffer and attrs (`:63-69`) — a second call with
|
||||
the same width is a hash-map-style cache hit, not a re-shape. Under the new
|
||||
|
||||
Reference in new issue
Block a user