Record what the last of the fixed-point imprecision is
Traced rather than guessed: the same box reached two ways, each rounding where the other does not. Two of the three are fixed, the third is priced -- it costs the retained resize path, at a thousandth of a pixel. `OrthoSize` is deleted rather than paused; the decision it was waiting on was that a span asks whether a rule already answers for it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ddf746d8a4
commit
617331f913
1 file changed
+35
-19
+35
-19
@@ -8,7 +8,7 @@ log.
|
||||
|
||||
Canonical Iris `main` is **`ca2b4b2`** (#17, the headless rig). **#18
|
||||
`split/18-position-chain`** is open in `/home/bob/repos/iris-pr18`; its local
|
||||
head is **`cb955f1`**, fifty-five commits. Built-in alignment is complete there;
|
||||
head is **`bdab558`**, fifty-seven commits. Built-in alignment is complete there;
|
||||
see "Built-in alignment" below for the retained-layout details. No PR reviews
|
||||
were present when checked on 2026-09-15.
|
||||
|
||||
@@ -72,13 +72,14 @@ rewrite:
|
||||
`core/src/ui/holds.rs`, the retained tests, and the generated cold-layout oracle
|
||||
pin those rules. Seeds 10 and 86 are now in the ordinary generated set.
|
||||
|
||||
## Verification at `cb955f1`
|
||||
## Verification at `bdab558`
|
||||
|
||||
- `cargo fmt --all --check`
|
||||
- `cargo build --workspace --all-features`
|
||||
- `cargo clippy --workspace --all-targets --all-features -- -D warnings`
|
||||
- `cargo test --workspace --all-features`: 102 passed, 10 ignored
|
||||
- The release generated cold-layout oracle passed 100 seeds in 75 s.
|
||||
- `cargo test --workspace --all-features`: 103 passed, 10 ignored
|
||||
- The release generated cold-layout oracle passed 100 seeds in 9.4 s, and a
|
||||
shrinker case at 300 seeds in 3.5 s: both run a thread per core but one.
|
||||
- The release shrinker passed **all five** cases -- `resize`, `repaint`,
|
||||
`resize-repaint`, `reorder`, `size-change` -- at 300 seeds of depth 5:
|
||||
26,001 widgets per case, largest tree 331. It passed 1000 seeds of depth 6
|
||||
@@ -137,6 +138,13 @@ tighter figures.
|
||||
|
||||
## Retained-layout invariants
|
||||
|
||||
- A span is as long across itself as its longest child, unless a rule beside
|
||||
it says how long it is -- and then it does not read its children there at
|
||||
all, since the answer is not wanted and reading one is what makes its size
|
||||
depend on it. `OrthoSize` was that second case written twice and is gone
|
||||
(`9d8415d`); `Painter::ruled` is how a container asks which it is in, and
|
||||
the only thing a widget may learn about a rule over it.
|
||||
|
||||
- A region node holds a whole `UiRegion` in its parent node's coordinates.
|
||||
`UiRegion::FULL` is the identity. Widgets opt in with `.region_node()` or
|
||||
`Widgets::set_region_node`; ordinary widgets share the nearest ancestor
|
||||
@@ -315,12 +323,24 @@ pixels and `Holds`. Decided with Bryan on 2026-09-15.
|
||||
rounding, so it scales with the step: at `1/64` that was 0.016 px, enough
|
||||
to move a box, and at `1/1024` it is a thousandth of a pixel. Range is
|
||||
+/-2.1M px and conversion to `f32` is exact to 16,384 px.
|
||||
- **What the fuzzers ask for is a step per operation.** The shrinker's five
|
||||
cases agree within one (`resize` exactly), the oracle's two-operation cases
|
||||
within two. Closing that needs one way of asking how long a box is: today a
|
||||
chain composed down through `within` and a length measured against the
|
||||
window are two, and they round separately. That is the next real step in
|
||||
precision and it is bigger than any of these four commits.
|
||||
- **What the fuzzers ask for is a step per level of nesting**, which is two
|
||||
for these trees. Traced on 2026-09-16 to the same box reached two ways,
|
||||
each rounding where the other does not -- not accumulation, and not one
|
||||
place. Two of them are fixed in `bdab558`:
|
||||
- `Scroll` wrote a box it had been given back out as its own length in
|
||||
pixels. Centring a part in `rel 1` lands a step from centring it in
|
||||
`px 900`, because `a(x - y)` and `ax - ay` do not round alike. Content
|
||||
that fills the viewport unscrolled is handed back as it came, and the
|
||||
`repaint` and `resize-repaint` cases became exact.
|
||||
- `Span` placed each child a step from where the last ended, carrying every
|
||||
share's rounding along the row. A position is now the fixed parts before
|
||||
it, exact, plus one rounded share. Two hundred equal shares of a 1000 px
|
||||
row ended at 999.999 and now end at 1000.
|
||||
What is left is a box centred in a fraction of its parent against the same
|
||||
box centred in its own pixels, one step per level between them. Closing it
|
||||
means alignment resolved in pixels everywhere -- which costs the retained
|
||||
resize path, since it is the fractional form that re-centres a subtree
|
||||
without redrawing it. Not worth it at a thousandth of a pixel.
|
||||
- `Holds::through` inverts `px + rel * box`, which rounds, so the answer is
|
||||
an interval even for a single length. It maps the half step either side,
|
||||
plus one more for the two ways above; inverting the length alone gives a
|
||||
@@ -376,6 +396,11 @@ targets went from 14.3 s to 7.7 s and `target/` from 45 GB to 13 GB. Pick a
|
||||
module out with `cargo test --test suite layout::`. The fuzzers and the
|
||||
`*_cost` measurements are still their own targets.
|
||||
|
||||
The fuzzers take a thread per core but one (`9d8415d`), since a seed grows,
|
||||
lays out and drops its tree alone: the oracle's hundred seeds went from 68 s
|
||||
to 9.4 s and a shrinker case at 300 seeds from 18 s to 3.5 s. A failing seed
|
||||
still shrinks and panics on its own thread.
|
||||
|
||||
Run the long generated oracle only after ordinary tests pass:
|
||||
|
||||
```sh
|
||||
@@ -433,15 +458,6 @@ the same `Holds` contract and box chain.
|
||||
|
||||
Queued from this work, in order:
|
||||
|
||||
- Delete `OrthoSize`. It is redundant now that size is built in: a span should
|
||||
read its own rule on the orthogonal axis and, where that is fixed, skip
|
||||
reading its children's orthogonal sizes entirely, which is what `Full` does
|
||||
today. That also removes the span's own instance of the compounding above.
|
||||
**Paused 2026-09-16 for a decision**: a widget deliberately cannot see its
|
||||
own rule ("the widget under a rule never learns of it"), so either that
|
||||
changes or the span reports something else on the orthogonal axis -- which
|
||||
is what every span reports, and a column sized to its widest item is what
|
||||
`Children` is for.
|
||||
- `Scroll` should take a direction rather than one axis: vertical, horizontal,
|
||||
or both. Reporting `LEFTOVER` on both axes is already the right shape for it.
|
||||
- Restore `max_width`/`max_height` as `SizeRule::{Min, Max, Clamp}`. `8220a78`
|
||||
|
||||
Reference in new issue
Block a user