qt/wayland: subsurface in sync mode for atomic-with-parent resize
The previous desync mode meant our wl_subsurface commits applied
immediately, independent of Qt's parent surface commits. That's
lower-latency in the steady state but during resize it left a
one-frame window where the parent had already grown to the new
size but our child subsurface was still showing its old-size
buffer — the parent's translucent QWidget background showed
through the gap. The original pre-subsurface QPainter blit didn't
have this issue because everything was on one surface and resize
was inherently atomic.
Sync mode (the wl_subsurface default) restores that atomicity:
our wl_surface.commit on the child caches state until the parent
commits, then both apply in the same compositor frame. Resize
becomes lockstep.
The cost is that frames now need a parent commit to become
visible. drainVulkan now calls update() after each presentDmabuf
to schedule the paintEvent that triggers Qt's backing-store
flush (= parent wl_surface.commit). Latency penalty vs desync:
one event-loop turn (sub-millisecond at idle).
Pairs with b8d2f25cf (synchronous draw in resizeEvent) — that
fix attached the new-size buffer to the subsurface inside
resizeEvent; this fix ensures the attach gets applied atomically
with the parent's next commit.
Co-Authored-By: claude-flow <ruv@ruv.net>
pull/12846/head
parent
b8d2f25cf5
commit
94c51e227f
|
|
@ -1551,6 +1551,12 @@ void GhosttySurface::drainVulkan() {
|
|||
m_subsurfacePresenter->presentDmabuf(
|
||||
frame.fd, frame.drm_format, frame.drm_modifier, frame.width,
|
||||
frame.height, frame.stride, width(), height());
|
||||
// The subsurface is in wl_subsurface sync mode, so the buffer
|
||||
// we just attached only becomes visible when Qt's parent surface
|
||||
// commits. update() schedules a paintEvent which triggers
|
||||
// Qt's backing-store flush (= parent wl_surface.commit), at
|
||||
// which point our cached subsurface state applies atomically.
|
||||
update();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -254,11 +254,20 @@ SubsurfacePresenter::tryCreate(QWindow *parent) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
// Independent frame pacing: the renderer's present cadence is
|
||||
// driven by libghostty's render thread, not the GUI thread's paint
|
||||
// cycle, so we don't want our wl_subsurface state changes to wait
|
||||
// for the parent's next commit. `set_desync` is what allows that.
|
||||
wl_subsurface_set_desync(sub);
|
||||
// Sync mode (the wl_subsurface default — we don't call set_desync).
|
||||
// In sync mode our wl_surface.commit caches state until the parent
|
||||
// surface commits, at which point both apply atomically. That's
|
||||
// what gives resize its lockstep behavior — parent grows to the
|
||||
// new size and our subsurface's matching new-size buffer apply in
|
||||
// the same compositor frame, so there's no transient gap where the
|
||||
// parent's translucent background shows through.
|
||||
//
|
||||
// The cost: our frames need a parent commit to become visible. The
|
||||
// GhosttySurface caller compensates by calling update() after each
|
||||
// presentDmabuf — that schedules a paintEvent, which triggers Qt
|
||||
// to flush the parent surface's backing store (= a wl_surface.commit
|
||||
// on the parent). Total latency penalty vs desync: one event-loop
|
||||
// turn, sub-millisecond at idle.
|
||||
|
||||
// Subsurface covers the parent at the origin. Phase 4 will keep
|
||||
// this in sync on splits/tabs/etc.; for now the GhosttySurface
|
||||
|
|
|
|||
Loading…
Reference in New Issue