Record exact layout equality and measured reuse improvements

This commit is contained in:
iris-ai committed 2026-09-17 15:11:23 -04:00
1 parent 0080bcbcba
commit 8f1a5e0a46
2 files changed
+64 -44

No files matched your search

+60 -43
View File
@@ -376,19 +376,12 @@ boundaries through `through`, or make the grid floor everywhere.
### Where the residual comes from, and the snap ### Where the residual comes from, and the snap
Both remaining steps are a symbolic region re-expressed by division rather The `e44dea3` baseline's two-step allowance came from inverse remapping and
than recomputed the way a cold draw computes it: `AxisRemap::Scale` divides alignment composed by different routes. The frame/extent continuation below
to find a part's fraction of the old box, and `placed_box` scales a mixed retains local widget frames as well as local primitive coordinates, recomposes
`Len` by the alignment. Neither touches the threaded pixel chain, so every in the same order as a cold draw, and removes inverse remapping. Its oracle
layout decision already agrees warm against cold; what differs is the requires exact pixel-region equality. That experiment is not yet the pinned
composed position the shader and hit testing see, by up to 0.002 px. framework; the baseline's snap decision below still applies there.
Closing `Scale` exactly is possible: keep each primitive's region in its
widget's own coordinates and recompose on a move with `within`, eight
multiplies and no division against the current four divisions and twelve
multiplies, exact by construction, sixteen bytes more per primitive. Closing
the alignment one means resolving alignment in pixels, which costs the
retained resize path. Neither is worth a thousandth of a pixel on its own.
Where it does matter is `snap_floor` in `prelude.wgsl`, which adds half a Where it does matter is `snap_floor` in `prelude.wgsl`, which adds half a
layout step before flooring: that absorbs float error and not a layout layout step before flooring: that absorbs float error and not a layout
@@ -429,8 +422,8 @@ back.
The original proposal was implemented by Claude as `5fcace1` on The original proposal was implemented by Claude as `5fcace1` on
`wip/region-and-placement` in `/home/bob/repos/iris-pr18`. It kept the fraction `wip/region-and-placement` in `/home/bob/repos/iris-pr18`. It kept the fraction
reference stable but failed five layout cases and three draw-count cases. reference stable but failed five layout cases and three draw-count cases.
The correction is saved as `efb416b`, pushed to origin's The first correction is `efb416b`; the exact-recomposition continuation is
`wip/region-and-placement`, in the isolated checkout `2ed5503`, pushed to origin's `wip/region-and-placement`, in the isolated checkout
`/home/bob/repos/iris-layout-experiment`; **it is not on PR #18**. The original `/home/bob/repos/iris-layout-experiment`; **it is not on PR #18**. The original
checkout is unchanged. Keep the experiment's build directory separate: Cargo checkout is unchanged. Keep the experiment's build directory separate: Cargo
can accept a different worktree's artifacts as fresh when a target directory can accept a different worktree's artifacts as fresh when a target directory
@@ -475,34 +468,62 @@ placeholder zero answer became reusable once it was drawn during placement.
and `adding_text_to_a_reverse_row_keeps_its_shared_height` reproduces the and `adding_text_to_a_reverse_row_keeps_its_shared_height` reproduces the
zero-height failure with a small tree. zero-height failure with a small tree.
Ordinary verification currently passes: formatting, workspace clippy with **Exact recomposition and reuse.** The continuation replaces `given_len` with
`layout-diagnostics`, all workspace tests (96 suite tests), and the fast `given_region`: each widget retains its original frame in its parent's
generated cases. New regressions also inspect primitive coordinates for mixed coordinates. Moving a retained subtree replays those same local compositions,
frame/extent drawings, including a region node, and a content change after and stops at region nodes. `RegionRemap`, `AxisRemap`, and inverse division
moving an inherited extent. A `tabs` resize from 1920x1200 to 900x1200 matches are gone. Fixed-pixel frames can now change size without forcing otherwise
a cold 900x1200 screenshot with zero differing pixels. The release oracle valid descendants to draw again. This adds 16 bytes per active widget; local
passes 2000 seeds at depth 4 and 1000 seeds at depth 6 over all fifteen cases. primitive coordinates were already retained by `efb416b`.
The shrinker passes 400 seeds at depth 5, and the debug oracle passes 120
seeds at depth 4 with assertions enabled. The shared oracle now compares pixel regions with ordinary equality; there
is no `AGREE_STEPS` allowance. Text publishes the range of its retained line
breaks rather than narrowing that range to every later
requested width. A layout with only explicit line breaks or no breaks holds
at arbitrarily wider widths. New tests compare actual primitive and mask
geometry and assert reuse for fixed-frame resizing and widening unwrapped
text (including explicit newlines and empty content).
Ordinary verification passes: formatting, workspace clippy with
`layout-diagnostics`, workspace tests, and the fast generated cases. The
suite contains 98 tests. A `tabs` resize from 1920x1200
to 900x1200 matches a cold 900x1200 screenshot with zero differing pixels.
With exact equality, the final release oracle passes 2000 seeds at depth 4
and 1000 seeds at depth 6, the shrinker passes 400 seeds at depth 5, and the
debug oracle passes 120 seeds at depth 4 with assertions enabled. Every scan
runs all fifteen scenarios. The fixed-frame reuse regression was also run
against `efb416b`: it fails there with two child draws instead of one.
**Do not land this as a performance improvement.** The old stretching and **Do not land this as a performance improvement.** The old stretching and
wrapper reuse tests pass, and one numeric-size test improves from three draws wrapper reuse tests pass, and one numeric-size test improves from three draws
to two. Cold work also improves against the broken `5fcace1` prototype: 604 to to two. The continuation reduces widget draws from `efb416b` by 6.9% cold,
535 widget draws and 15,296 to 10,384 primitive writes on seed 1, depth 8. 9.0% with many dirty widgets, 7.6% for a size change, and 8.7% for resize.
But against the last working `e44dea3`, the same rig does substantially more Against the last working `e44dea3`, it still does substantially more nested
nested layout work: layout work (seed 1, depth 8):
| phase | widget draws before | prototype | primitive writes before | prototype | | phase | widget draws before | prototype | primitive writes before | prototype |
| --- | ---: | ---: | ---: | ---: | | --- | ---: | ---: | ---: | ---: |
| cold | 369 | 535 | 9635 | 10384 | | cold | 369 | 498 | 9635 | 9704 |
| repaint | 1 | 1 | 1 | 1 | | repaint | 1 | 1 | 1 | 1 |
| many | 157 | 310 | 4721 | 6970 | | many | 157 | 282 | 4721 | 6398 |
| size | 16 | 275 | 106 | 5980 | | size | 16 | 254 | 106 | 5725 |
| scroll | 2 | 2 | 0 | 0 | | scroll | 2 | 2 | 0 | 0 |
| resize | 13 | 328.5 | 0 | 6721.5 | | resize | 13 | 300 | 0 | 6255 |
These are instrumented work counts, not elapsed-time speedups (50 frames per These are instrumented work counts, not elapsed-time speedups (50 frames per
phase except cold, which is one; 578 generated widgets, 191 finally active). phase except cold, which is one; 578 generated widgets, 191 finally active).
Uninstrumented release binaries, run directly with `perf stat -r 3 -e
instructions:u`, also improve over `efb416b`: 2000 size-change frames fall
from 14.3923B to 13.8914B instructions (3.5%); 2000 resize frames fall from
16.9756B to 16.1331B (5.0%). These whole-process totals include fixture setup;
reported run-to-run variation is at most 0.24%. The many-dirty workload falls
from 16.7962B to 16.0030B instructions (4.7%, 2000 frames). Repaint and scroll
keep their draw counts but incur small CPU increases: 0.8849B to 0.8913B
(+0.7%) and 2.4332B to 2.4479B (+0.6%) over 100,000 frames each. These are
CPU-work measurements, not phone frame times. Keeping measurement validity separate from final drawing validity
and deferring to an already dirty parent were also tried; neither improved
this rig's draw counts, so those changes were removed.
Raw placement reads in containers and repeated offer/placement evaluations Raw placement reads in containers and repeated offer/placement evaluations
remain conservative. Retaining local coordinates also costs memory per remain conservative. Retaining local coordinates also costs memory per
primitive. The experiment demonstrates selective reuse with one `Widget::draw` primitive. The experiment demonstrates selective reuse with one `Widget::draw`
@@ -591,17 +612,13 @@ cold reach every length by the same expression.
### What the fuzzers tolerate ### What the fuzzers tolerate
`AGREE_STEPS` in `tests/scenario/mod.rs` is **2**, and both steps are The frame/extent continuation removes `AGREE_STEPS`: warm and cold pixel
positions. One is a box centred in a fraction of its parent against the regions must compare exactly. This is distinct from the equal-share test:
same box centred in its own pixels, 0.001 px on a handful of seeds. The when a row's grid-step count is not divisible by the number of children,
other is `AxisRemap::Scale` re-expressing a part as a fraction of a box that individual share widths can differ while every rerun of that layout must
changed length; one step fails the 400-seed shrinker on `resize-size`, still agree exactly. The PR #18 baseline still allows two position steps;
seeds 384 and 162, by 0.002 px while passing the 100-seed oracle. Two of the its earlier failure at one step (`resize-size`, seeds 384 and 162 at depth 5)
earlier sources were fixed rather than tolerated (`bdab558`): `Scroll` wrote is a useful regression target for the experimental recomposition.
a box it had been given back out as its own length in pixels, and `Span`
placed each child a step from where the last ended rather than as the fixed
parts before it plus one rounded share. See "Where the residual comes from"
above for what closing the rest would cost.
## Retained-layout invariants ## Retained-layout invariants
+4 -1
View File
@@ -871,7 +871,10 @@ replace that reference. A measurement also records the available extent, so
reusing it checks both inputs. Retained primitive and mask coordinates name reusing it checks both inputs. Retained primitive and mask coordinates name
whether they follow the frame or the extent. Numeric extent reads constrain whether they follow the frame or the extent. Numeric extent reads constrain
layout validity; merely drawing in an extent does not require another widget layout validity; merely drawing in an extent does not require another widget
evaluation when it moves. `Widget::draw` remains the only layout body. The evaluation when it moves. Widgets also retain their original local frames,
so reuse composes positions in the same order as a fresh draw instead of
recovering fractions by division. The experimental oracle requires exact
pixel-region equality. `Widget::draw` remains the only layout body. The
prototype has significant container-update costs and is not yet adopted into prototype has significant container-update costs and is not yet adopted into
the pinned framework. the pinned framework.