Document deferred layout requests and verification on Iris PR 19
This commit is contained in:
1 parent
ab8b05b762
commit
4c7072e62f
3 files changed
+104
-16
No files matched your search
+38
-14
@@ -4,10 +4,33 @@ Where the work in flight stands. The settled layout design, the vocabulary
|
||||
and the measurement method are in `docs/LAYOUT.md`; what the review of #19
|
||||
found is in `docs/LAYOUT_LOG.md`.
|
||||
|
||||
## Deferred comparisons are submitted on PR #19
|
||||
|
||||
The `layout/one-ask` tip is `8780b40`, in `/home/bob/repos/iris-deferred`.
|
||||
`/home/bob/repos/iris` remains on `layout/bounds`; that branch's `de1eb7e` was
|
||||
fast-forwarded into #19 before this work. The app's Iris pin is untouched.
|
||||
|
||||
The new request path composes min/max/clamp before assigning leftover slots,
|
||||
while ordinary widgets keep returning `Size`. Known requests skip provisional
|
||||
painting; measured leaves can complete a nested expression. The implementation
|
||||
uses a reusable expression arena and retained draw buffers. The allocation rig
|
||||
checks zero allocations after warm-up for unchanged plain and clamped trees.
|
||||
Generalized cross-axis maxima are deferred because hidden main-axis shares
|
||||
must not contribute to them. The current design is in `docs/LAYOUT.md`.
|
||||
|
||||
Format, workspace Clippy with and without layout diagnostics, and all 197
|
||||
workspace tests pass. The broad scans passed: 400 depth-5 trees through the
|
||||
shrinker, 1,000 depth-6 and 2,000 depth-4 generated trees, plus 400 depth-5
|
||||
expression trees and 400 relative-bound trees. All 34,986 unbounded cold boxes
|
||||
match `de1eb7e`. The forced-redraw allocation fixtures report zero allocations
|
||||
across 100 resize frames after warm-up. The text resize benchmark uses 5.89%
|
||||
more retired instructions than `de1eb7e`; this is not a universal speedup.
|
||||
See `docs/LAYOUT.md` for the measurements and remaining measured paths.
|
||||
|
||||
## The Iris layout repair is submitted
|
||||
|
||||
**Iris PR #19** (`layout/one-ask`) replaces closed #18. The tip is `2dba90b`,
|
||||
and past the reviewed `cadfba0` it is twelve rounds, each described in
|
||||
**Iris PR #19** (`layout/one-ask`) replaces closed #18. Before the deferred
|
||||
comparison work above, its review rounds past `cadfba0` are described in
|
||||
`docs/LAYOUT_LOG.md`:
|
||||
|
||||
- **The repair**, `add6774` and `84dad21` -- collapsed-share placement,
|
||||
@@ -68,9 +91,8 @@ over 2026-09-17 to 19; it is current, not frozen.
|
||||
|
||||
The core design remains sound. Round-to-nearest is still unchanged.
|
||||
|
||||
Two checkouts share one Git storage: `/home/bob/repos/iris` is the active
|
||||
`layout/one-ask` worktree, and `ai-app-2/iris` stays on `app-pin` at the
|
||||
app's `32f6ad8` pin until the integration below is ready.
|
||||
The Iris worktrees share Git storage. `ai-app-2/iris` stays on `app-pin` at
|
||||
the app's `32f6ad8` pin until the integration below is ready.
|
||||
|
||||
### The branch layout, and the trap that used to be here
|
||||
|
||||
@@ -198,17 +220,19 @@ glyphs do not follow a shortened entry.
|
||||
are caller bugs under `debug_assert`, but the fallbacks differ.
|
||||
- `docs/LAYOUT.md` §4, §5 and the density section name `Painter::place`,
|
||||
`Painter::region()`, `SetSize`, `desired_width`, `apply_rest`, `Len::dp`,
|
||||
`Aligned` and `MaxSize`, none of which exist. Do not restore
|
||||
`OnResize::Translate` or `OrthoSize`.
|
||||
- **`layout/bounds` (`de1eb7e`) is green and ready to review**, kept off #19
|
||||
so that branch stays one subject. A rule holds what a widget answers
|
||||
`Aligned`, which no longer exist; `MaxSize` now exists with the bounds API.
|
||||
Do not restore `OnResize::Translate` or `OrthoSize`.
|
||||
- **`layout/bounds` (`de1eb7e`) is included in #19** for the deferred
|
||||
comparison implementation. A rule holds what a widget answers
|
||||
(`SizeRule::{Min, Max, Clamp}`); a widget holds the box (`MaxSize`, which
|
||||
`.max_width`/`.max_height` build). Bryan settled the split on 2026-09-20
|
||||
after four readings were measured; the reasoning and the one remaining hole
|
||||
-- a retained answer re-placed under a different rel base, which is older
|
||||
than bounds -- are in `docs/LAYOUT_LOG.md`. A bound may not contain
|
||||
`leftover`, and the generated trees grow bounds in pixels until that hole is
|
||||
closed.
|
||||
after four readings were measured; the reasoning is in `docs/LAYOUT_LOG.md`.
|
||||
The deferred-request work fixes stale relative-bound reuse by tracking the
|
||||
input base and comparing resolved bounds, with a reduced natural-size-hint
|
||||
regression and 400 depth-5 relative-bound trees. Intrinsic bounds remain
|
||||
`Len`s; comparisons involving leftover use `SizeRequest`. Ordinary generated
|
||||
trees retain pixel bounds; `deferred_generated` supplies relative bounds and
|
||||
expressions in separate corpora.
|
||||
- `LazySpan`.
|
||||
- `Scroll` taking a direction rather than one axis.
|
||||
|
||||
|
||||
@@ -12,6 +12,67 @@ in flight stands. The sections from "Three names, and the one argument that
|
||||
says them" onwards are the settled design, the findings that outlived the
|
||||
working log, and the measurement method.
|
||||
|
||||
## Deferred size requests on PR #19
|
||||
|
||||
`Widget::draw` still returns the small, copyable `Size` of two `LayoutLen`s.
|
||||
An optional `size_request` method describes an axis without drawing it. Its
|
||||
context can query children and compose sums, minima and maxima; `None` means
|
||||
that drawing in a concrete offer is necessary. Existing leaf widgets need no
|
||||
new method: their `size_hint` supplies a plain request when available.
|
||||
|
||||
Declarations can use expressions, for example
|
||||
`rect(color).width(leftover(1).clamp(40, 120))`. In a 300-pixel row beside an
|
||||
uncapped equal share, that child takes 120 and its sibling takes 180. At 100
|
||||
pixels they take 50 each. Comparisons can have shares on both sides, such as
|
||||
`(px(30) + leftover(1)).min(leftover(2))`. Weights must be nonnegative.
|
||||
|
||||
A span discovers requests, measures any unknown content, solves the shared
|
||||
allocation, then places children. Known requests avoid provisional painting.
|
||||
After measuring a container, discovery can use its children's measured answers
|
||||
to finish an expression that was previously incomplete. Thus a measured leaf
|
||||
inside a nested capped row does not force the row's request to become a pixel
|
||||
constant. A custom container must implement request composition to propagate
|
||||
such expressions through itself.
|
||||
|
||||
Plain sums retain the existing symbolic allocation path. Comparisons with a
|
||||
known ordering (pixels with equal fractional/share coefficients, or shares
|
||||
with equal fixed terms) fold to ordinary lengths. Remaining expressions are
|
||||
monotone piecewise linear functions of one share unit. The allocator advances
|
||||
through exact rational crossings until the sum fills the offered room. Caps
|
||||
return room to other shares; floors may overflow; all caps being reached may
|
||||
leave unused room. Prefix rounding gives adjacent slots the same edge.
|
||||
|
||||
Persistent declarations share immutable expression nodes. Temporary composition
|
||||
uses a reusable arena, and each retained drawing keeps its vector capacities.
|
||||
Temporary request handles are valid only within that layout pass. An explicit
|
||||
allocated placement distinguishes a solved slot from a box that merely fills
|
||||
its parent's region, so declarations are not evaluated a second time against
|
||||
the slot they already chose. Request dependencies invalidate the allocator when
|
||||
a descendant's rules or contents change. A solved declaration retains its
|
||||
pixel length when moved, including by scrolling; it must not become a fresh
|
||||
fraction of the destination box. Relative intrinsic bounds record their input
|
||||
base separately from the widget's resulting box, and retained answers compare
|
||||
resolved bounds before reuse.
|
||||
|
||||
Two measured cases remain intentional. Intrinsic fixed content is drawn in the
|
||||
remaining offer and then moved; drawing it in its reported size would change
|
||||
wrapping and overflow. When no leftover room exists, discovered intrinsic
|
||||
shares use that same measured path. Cross-axis span maxima also remain measured:
|
||||
a main-axis share can be hidden, so the maximum of every child's request is
|
||||
not necessarily the maximum of the children that actually draw. Generalizing
|
||||
that would require carrying visibility through the expression system too.
|
||||
|
||||
The allocation rig forces every widget to redraw across 100 resize frames:
|
||||
both the plain and clamped eight-row fixtures allocate zero times after warm-up.
|
||||
This is a measured property of those stable visible trees, not a guarantee for
|
||||
arbitrary widgets or visibility changes. An eight-level known nested span paints
|
||||
its leaf once. The 40-row, 1,000-frame text resize rig costs 9,891,085,554 retired
|
||||
instructions versus 9,341,015,256 at `de1eb7e` (5.89% more), with the same printed
|
||||
geometry. Request dependency tracking has a cost even when text still needs
|
||||
measurement. The 400-tree unbounded cold dump preserves all 34,986 boxes.
|
||||
|
||||
The app's submodule pin is unchanged. This design lives on Iris PR #19.
|
||||
|
||||
## Design
|
||||
|
||||
### UI ownership and frame access
|
||||
|
||||
+5
-2
@@ -870,8 +870,11 @@ span rather than silently discarding its `rest` component.
|
||||
the box a parent asks a widget in is its *region*, where the drawing ends up
|
||||
is its *placement*, and the length a fraction resolves against is its *rel
|
||||
base*. A container says all three in one `PlaceDesc` argument. `Widget::draw`
|
||||
remains the only layout body. The invariants, the measured costs, the fuzzing
|
||||
method and the failed hypotheses are all in `docs/LAYOUT.md`; the app's pin at
|
||||
remains the only layout body. PR #19 also has optional size-request discovery
|
||||
for deferred min/max/clamp expressions (2026-09-20); ordinary widgets still
|
||||
return `Size`, and plain requests retain symbolic allocation. The invariants,
|
||||
the measured costs, the fuzzing method and the failed hypotheses are all in
|
||||
`docs/LAYOUT.md`; the app's pin at
|
||||
`32f6ad8` predates every part of it, so nothing here depends on it until the
|
||||
integration described in `docs/HANDOFF.md` lands.
|
||||
|
||||
|
||||
Reference in new issue
Block a user