Put positions on the grid, and decode them in the shader
`UiScalar` is `Rel` beside `Px` rather than two floats, so composing a position down a chain of boxes adds exactly and rounds only at the two multiplies `within` makes. `UiSpan`, `UiRegion` and `UiVec2` follow it, the hand-written `Hash` goes away with the bits it hashed, and `impl_op!` grows a `same` form for a type whose fields are not the same kind of number. `Len` is still floats, so the seam converts: `Px::from_f32` where a span adds a child's length to its cursor, and `to_f32` where something outside layout wants pixels. Those go when `Len` follows. The GPU reads what the CPU wrote: the instance attributes are `Sint32x2` and the shader decodes by `1/64` and `1/2^24`, both exact in `f32`, then composes the move chain in floats as before. It has to agree with itself frame to frame rather than with the CPU to the last bit. Two things fell out of making the numbers exact. `floor` at the rasteriser was picking the pixel below wherever a fraction divided a window exactly. A fifth of 1920 is 383.99998 through a rounded `Rel` -- and was 384.0 through an `f32` that happened to round up -- so five tabs each lost their last column. `snap_floor` takes a coordinate within half a step of a boundary to be on it, which is the same rule as everywhere else here: decide where values do not land. A widget measured on one layer and drawn again on another kept the first layer, because `try_reuse` compared everything about a retained drawing except which list it sits in. `Stack` does exactly that for its background, so every panel's text went under its own background. It only worked before because the two asks differed by a rounding and forced a redraw; `tests/retained.rs` pins it now, and `ReuseOutcome` can say `WrongLayer`. Checked: fmt, clippy, 100 tests, 100 generated seeds in 70 s, all five shrinker cases at 300 seeds. `tabs`, `view` and `minimal` render byte-identical at 1920x1200; `random` differs in 36 pixels by one level; `text` differs where glyph origins moved onto the grid -- same positions, same spacing, different subpixel coverage, checked at 6x against the old render. Measured on the way: with the fuzzer comparing for *equality* rather than within 0.05 px, `resize`, `repaint` and `size-change` already pass 100 seeds. `reorder` fails one seed by exactly one step, which is the `Len` seam above. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7548139861
commit
4e28f1047e
19 files changed
+256
-138
No files matched your search
@@ -1,7 +1,7 @@
|
||||
#[cfg(feature = "layout-diagnostics")]
|
||||
use crate::layout_diagnostics::{self as diag, Counter};
|
||||
use crate::{
|
||||
Axis, Holds, Len, RegionAlign, RenderedText, Size, StrongWidget, TextAttrs, TextBuffer,
|
||||
Axis, Holds, Len, Px, RegionAlign, RenderedText, Size, StrongWidget, TextAttrs, TextBuffer,
|
||||
TextData, TextureHandle, UiRegion, UiRenderState, UiRsc, UiScalar, UiVec2, WidgetId, Widgets,
|
||||
render::{
|
||||
GlyphPrimitive, Mask, MaskIdx, MoveIdx, Primitive, PrimitiveHandle, PrimitiveInst,
|
||||
@@ -293,8 +293,8 @@ impl<'a> Painter<'a> {
|
||||
fn px_within_offer(&self, local: UiRegion) -> Vec2 {
|
||||
let size = local.size();
|
||||
Vec2::new(
|
||||
size.x.to_px(self.offered_px.x),
|
||||
size.y.to_px(self.offered_px.y),
|
||||
size.x.to_px(Px::from_f32(self.offered_px.x)).to_f32(),
|
||||
size.y.to_px(Px::from_f32(self.offered_px.y)).to_f32(),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user