diff --git a/RUST.md b/RUST.md index f5dc4a3..8193a80 100644 --- a/RUST.md +++ b/RUST.md @@ -612,27 +612,33 @@ step measured. rather than an answer about Masonry, whose graph adds Vello, Parley, Fontique and Skrifa. Runtime cost of the knob was not measured here. - **Open defect found while doing this: iris sometimes never adopts - the window's real size.** Measured on the headless rig, ~3 starts in - 15: the `tabs` example settles showing its 800×600 startup layout in - the top-left of a 1920×1200 surface, black around it, and stays that - way indefinitely — it is not a screenshot taken too early, since the - picture is byte-identical for the next four seconds. What is *not* - the cause, each checked: the winit event order is identical in good - and bad runs (`Resized(800×600)`, two redraws, `Resized(1920×1200)`, - one redraw), the swapchain reports `1920×1200` and - `suboptimal=false` on that last draw, and `output_size` is - `(1920, 1200)` going into it. It is timing-sensitive in the way that - makes it expensive: adding a single `eprintln!` anywhere in the draw - or event path hides it completely (0 in 16), which is why the - instrumentation above could not catch it in the act. A pointer move - does not repair it, because iris only redraws when something - changed; an output mode change does, because that is another resize. - Left open rather than guessed at. It matters most for **I2**, where - every rotation and every keyboard open is a resize, so a stale frame - would be the normal case rather than a rare one; a Wayland-level - trace of the xdg-surface configure/ack/commit sequence is the next - step, not more `eprintln`. + **Fixed: iris never called `pre_present_notify`.** The symptom was + that about one start in five kept the window's 800x600 startup layout + on a 1920x1200 surface for good. What settled it was tracing iris's + own decisions into memory and dumping them from another thread — + `eprintln!` in the draw path makes the defect vanish, which is why + earlier attempts kept losing it. The traces from a good and a bad run + are **byte-identical**: both lay out and draw `redraw_all at + (1920, 1200)` into a 1920x1200 texture with `suboptimal=false`. iris + was drawing the right frame every time; the compositor was still + showing the first one, and forcing a full repaint did not shift it. + What was missing is winit's `Window::pre_present_notify`, called + immediately before `present`, which on Wayland is what ties the + commit to the surface's frame callback. Without it a frame drawn with + nothing following it can sit unpresented with nothing left to flush + it — which is exactly a window that has just settled after its + opening resize. Measured: **0 bad in 40** with the fix, against 4 in + 20 before it, and — the stronger evidence — 0 in 20 in the + instrumented configuration that had been 15 in 20. Runtime resizing + still round-trips to a byte-identical layout. + + Two things ruled out on the way, both worth not re-trying: the + present mode (the fault survived the move from `AutoNoVsync` to + `AutoVsync` at the same rate) and the size cache (`redraw_all` clears + it). A `desired_maximum_frame_latency` of 1 moved the rate without + fixing it, and was reverted. Iris's own note that she had never seen + the library fail to resize was the useful steer: it pointed away from + the layout code, where two hours had already gone. One thing was fixed on the way, and it is not that bug: `update` redrew everything when `resized` was set, but `needs_redraw` — which diff --git a/iris/src/default/render.rs b/iris/src/default/render.rs index b317498..0004dd1 100644 --- a/iris/src/default/render.rs +++ b/iris/src/default/render.rs @@ -45,6 +45,13 @@ impl UiRenderer { } self.queue.submit(std::iter::once(encoder.finish())); + // Immediately before presenting, so the windowing system can schedule + // the frame. On Wayland this is what ties the commit to the surface's + // frame callback; without it a frame drawn when nothing else follows + // could sit unpresented, and the window kept the layout it had before + // the compositor's first resize -- intermittently, on about a fifth of + // starts, with nothing left to flush it. + self.window.pre_present_notify(); output.present(); }