Call pre_present_notify, so a settled frame actually reaches the screen
About one start in five, the window kept its 800x600 startup layout on a 1920x1200 surface for good. It was not the layout: tracing iris's own decisions into memory -- eprintln in the draw path makes the fault vanish, which is why it kept getting lost -- gives byte-identical traces for a good and a bad run. Both do redraw_all at (1920, 1200) and draw into a 1920x1200 texture with suboptimal=false. The right frame was drawn every time and the compositor kept showing the first one, and forcing a full repaint did not shift it. winit's Window::pre_present_notify, called immediately before present, is what ties the commit to the surface's frame callback on Wayland. Without it a frame with nothing following it can sit unpresented with nothing left to flush it -- which is precisely a window that has just settled after its opening resize. 0 bad in 40 with the fix, against 4 in 20 without. The stronger number is 0 in 20 in the instrumented configuration that had been 15 in 20, since that is the arrangement the fault liked most. Runtime resizing still round-trips to a byte-identical layout. Ruled out and not worth re-trying: the present mode (the fault survived AutoNoVsync -> AutoVsync at the same rate) and the size cache (redraw_all clears it). desired_maximum_frame_latency = 1 moved the rate without fixing it and was reverted. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3fc224b584
commit
9b331a5e93
2 files changed
+34
-21
No files matched your search
@@ -612,27 +612,33 @@ step measured.
|
|||||||
rather than an answer about Masonry, whose graph adds Vello, Parley,
|
rather than an answer about Masonry, whose graph adds Vello, Parley,
|
||||||
Fontique and Skrifa. Runtime cost of the knob was not measured here.
|
Fontique and Skrifa. Runtime cost of the knob was not measured here.
|
||||||
|
|
||||||
**Open defect found while doing this: iris sometimes never adopts
|
**Fixed: iris never called `pre_present_notify`.** The symptom was
|
||||||
the window's real size.** Measured on the headless rig, ~3 starts in
|
that about one start in five kept the window's 800x600 startup layout
|
||||||
15: the `tabs` example settles showing its 800×600 startup layout in
|
on a 1920x1200 surface for good. What settled it was tracing iris's
|
||||||
the top-left of a 1920×1200 surface, black around it, and stays that
|
own decisions into memory and dumping them from another thread —
|
||||||
way indefinitely — it is not a screenshot taken too early, since the
|
`eprintln!` in the draw path makes the defect vanish, which is why
|
||||||
picture is byte-identical for the next four seconds. What is *not*
|
earlier attempts kept losing it. The traces from a good and a bad run
|
||||||
the cause, each checked: the winit event order is identical in good
|
are **byte-identical**: both lay out and draw `redraw_all at
|
||||||
and bad runs (`Resized(800×600)`, two redraws, `Resized(1920×1200)`,
|
(1920, 1200)` into a 1920x1200 texture with `suboptimal=false`. iris
|
||||||
one redraw), the swapchain reports `1920×1200` and
|
was drawing the right frame every time; the compositor was still
|
||||||
`suboptimal=false` on that last draw, and `output_size` is
|
showing the first one, and forcing a full repaint did not shift it.
|
||||||
`(1920, 1200)` going into it. It is timing-sensitive in the way that
|
What was missing is winit's `Window::pre_present_notify`, called
|
||||||
makes it expensive: adding a single `eprintln!` anywhere in the draw
|
immediately before `present`, which on Wayland is what ties the
|
||||||
or event path hides it completely (0 in 16), which is why the
|
commit to the surface's frame callback. Without it a frame drawn with
|
||||||
instrumentation above could not catch it in the act. A pointer move
|
nothing following it can sit unpresented with nothing left to flush
|
||||||
does not repair it, because iris only redraws when something
|
it — which is exactly a window that has just settled after its
|
||||||
changed; an output mode change does, because that is another resize.
|
opening resize. Measured: **0 bad in 40** with the fix, against 4 in
|
||||||
Left open rather than guessed at. It matters most for **I2**, where
|
20 before it, and — the stronger evidence — 0 in 20 in the
|
||||||
every rotation and every keyboard open is a resize, so a stale frame
|
instrumented configuration that had been 15 in 20. Runtime resizing
|
||||||
would be the normal case rather than a rare one; a Wayland-level
|
still round-trips to a byte-identical layout.
|
||||||
trace of the xdg-surface configure/ack/commit sequence is the next
|
|
||||||
step, not more `eprintln`.
|
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`
|
One thing was fixed on the way, and it is not that bug: `update`
|
||||||
redrew everything when `resized` was set, but `needs_redraw` — which
|
redrew everything when `resized` was set, but `needs_redraw` — which
|
||||||
|
|||||||
@@ -45,6 +45,13 @@ impl UiRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.queue.submit(std::iter::once(encoder.finish()));
|
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();
|
output.present();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user