Files
qbz/crates/qbz-qt/qml
vicrodh 5b274cab57 perf(qt): acknowledge a route change on the next presented frame
Measured by driving the real window and diffing the captured frames: a
click on Discover > Home left the window with ZERO pixels changing for
590 ms — the flyout did not even close. The click handler, the flyout
close, the sidebar highlight, the page blank and the whole mount all ran
inside ONE turn of the event loop, so the first frame that could be
presented was the one that already had the finished page in it.

The breakdown matters more than the total, because only one part of it
is what it looks like:

    navigate_to (Rust, GUI thread)      7.6 ms
    bridge queue hop                    1   ms
    the QML mount (`built`)           263   ms
    post-mount work before a frame   ~290   ms   <-- `to-idle`

`built` is under half of it. `to-idle` is the number that matches the
freeze, and any future work here should be judged on that one.

So the route is now committed in two phases: arm on the change, build on
the next PRESENTED frame (frameSwapped). Frame N carries the cheap half
— flyout gone, highlight moved, page blanked — and frame N+1 pays for
the mount. Acknowledgement measured at 73-90 ms across five routes and
repeat runs, against 590 ms before.

Two things this had to get right, both caught by capturing frames:

  - The backstop timer was a RACE at 32 ms. Render-at-next-vsync
    (16.67 ms here, per QSG_INFO=1) plus a QUEUED frameSwapped delivery
    lands in ~16-33 ms, so Home acknowledged at +90 ms while Library got
    nothing at all on the same build. 250 ms puts the guard outside that
    range; it now only covers the hidden-window case it was written for.
  - During the armed frame `viewLoader.item` is still the OUTGOING view,
    so the incoming route's discriminators were applied to it. The
    activeTab Binding wrote "home" onto LibraryView — which has an
    activeTab, so the typeof guard passed — emptying its body and
    restarting the page fade over the blank. Both Bindings now require
    the commit to have happened.

Not asynchronous: incubation is time-sliced and was reverted the day it
was tried; every view is still built synchronously.

cc
2026-08-21 23:59:10 -06:00
..