Implements LAYOUT.md end to end: one fn draw(&mut self, &mut Painter) -> Size replaces draw + desired_width/desired_height on every widget in iris/src/widget/, SizeCtx and Cache are deleted, and a moved widget (Scroll, Offset) costs one move_offsets write resolved by a shared resolve_move WGSL function in both shader stages -- O(1) regardless of how many primitives are in its subtree, measured at 500 in the new iris/src/layout_tests.rs (a plain unit test: UiRenderState touches no GPU or window). Five real bugs surfaced only by diffing iris/run-headless.sh screenshots against the pre-change tree and are written up in LAYOUT.md's "Deviations found during implementation": Aligned's provisional draw composing painter.region() a second time through widget_within; Sized/ MaxSize reporting a capped size while still painting their child unconstrained (fine under the old two-pass model, wrong once a parent like Aligned draws before knowing the final size); a widget's move_offsets parent link being unreadable from self.active while its own ActiveData is still mid-construction; Painter::reposition needing the child's *painted* footprint (its reported size, top-left anchored) rather than its offered region; and a widget's move slot needing to be reused in place across redraws, with its delta reset, rather than reallocated. All four iris/examples render pixel-identical to the pre-change tree. cargo fmt/clippy/test clean across the workspace (18 tests: 14 pre-existing plus 4 new). Co-Authored-By: Claude Sonnet <noreply@anthropic.com>
80 lines
4.3 KiB
Markdown
80 lines
4.3 KiB
Markdown
# iris: notable public API changes
|
|
|
|
For Iris to read on her own time. Each entry is a change to iris's public
|
|
surface that a widget author or app author would notice: a trait method
|
|
added, removed or re-shaped; a type that callers construct differently; a
|
|
capability that moved. Small and trivial changes do not go here.
|
|
|
|
An entry gives the date, what changed, why, and a short before/after where
|
|
it helps judge the change without the session that made it. Newest first.
|
|
|
|
## 2026-09-04: `Widget::draw` reports the size it used; `desired_width`/`desired_height` are gone
|
|
|
|
A widget used to implement three methods (`draw`, `desired_width`,
|
|
`desired_height`); it now implements one, `fn draw(&mut self, painter: &mut
|
|
Painter) -> Size`, which draws into `painter.region()` and returns how much
|
|
of it was used. Why: the two extra methods routinely re-simulated what
|
|
`draw` was about to do anyway (`Span::desired_ortho` copied its own draw
|
|
loop to get cross-axis sizing right) — one visit per widget per frame
|
|
instead of up to three. A container that needs a child's size before
|
|
placing it (alignment, centering) draws the child once at a provisional
|
|
region, reads the returned `Size`, and calls the new `Painter::reposition`
|
|
to move it into its final spot — an O(1) offset write, not a second draw. A
|
|
widget whose drawn output never depends on the size it's given (a
|
|
fixed-size `Rect`, a decoded `Image`) overrides the new `fn
|
|
is_size_independent(&self) -> bool { false }` to `true`, which skips
|
|
redrawing it when only its offered region changes shape.
|
|
|
|
```rust
|
|
// before
|
|
fn draw(&mut self, painter: &mut Painter) { /* ... */ }
|
|
fn desired_width(&mut self, ctx: &mut SizeCtx) -> Len { /* ... */ }
|
|
fn desired_height(&mut self, ctx: &mut SizeCtx) -> Len { /* ... */ }
|
|
|
|
// after
|
|
fn draw(&mut self, painter: &mut Painter) -> Size { /* ... */ }
|
|
```
|
|
|
|
`SizeCtx` and `Cache` are gone with it — see `LAYOUT.md` for the full
|
|
design, the move-offset mechanism this shipped alongside, and the file
|
|
list.
|
|
|
|
## 2026-09-04: texture pipeline rebuilt off the binding array
|
|
|
|
`Textures`/`TextureHandle`, `GlyphPrimitive`, and `UiRenderNode::new` all
|
|
changed shape. Why: the old pipeline bound every texture ever drawn in one
|
|
`binding_array<texture_2d<f32>>` and asked every device, unconditionally,
|
|
for `VK_EXT_descriptor_indexing` — a real share of Android GPUs lack it,
|
|
and it failed outright on the Android emulator's software Vulkan. See
|
|
TEXTURES.md's "Recommended shape" and "Implemented, 2026-09-04".
|
|
|
|
- **`UiRenderNode::new` drops its `limits: UiLimits` parameter, and
|
|
`UiLimits` is gone.** Before: `UiRenderNode::new(&device, &queue,
|
|
&config, UiLimits::default())`. After: `UiRenderNode::new(&device,
|
|
&queue, &config)`. Nothing replaces it — there are no more
|
|
binding-array limits to size.
|
|
- **`src/default/render.rs`'s device request asks for no features and no
|
|
binding-array limits.** Before: `required_features:
|
|
Features::TEXTURE_BINDING_ARRAY | Features::PARTIALLY_BOUND_BINDING_ARRAY
|
|
| Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING`
|
|
plus two `max_binding_array_*` limits. After: `Features::empty()` (the
|
|
`DeviceDescriptor` default) and only `max_buffer_size` set, which was
|
|
never about the binding array.
|
|
- **`TextureHandle` has no `primitive()` method any more**; a caller
|
|
outside `iris` shouldn't have been calling it (it fed the old renderer's
|
|
internals), but if something did: use `image_index()` for a standalone
|
|
image's bind-group index. There is no equivalent for a page — a page has
|
|
no bind group of its own now, see below.
|
|
- **`GlyphPrimitive` has no public constructor from a struct literal.**
|
|
Before: `GlyphPrimitive { uv_min, uv_max, view_idx, sampler_idx, color,
|
|
flags }`. After: `GlyphPrimitive::new(uv_min, uv_max, layer, color,
|
|
flags)` — one `layer` (the shared atlas array's layer) instead of a
|
|
`view_idx`/`sampler_idx` pair, since a page is now a layer of one array
|
|
texture rather than its own bound texture.
|
|
- **A widget author drawing images is unaffected**: `Painter::texture`/
|
|
`texture_at`/`texture_within` and `Textures::add` keep their signatures.
|
|
What changed underneath is that each standalone image now gets its own
|
|
`wgpu::BindGroup` and draw call instead of a slot in the shared array —
|
|
invisible from the widget API, visible only in `UiRenderNode`'s internals
|
|
and in `iris`'s device requirements.
|