https://bugs.kde.org/show_bug.cgi?id=523813
Bug ID: 523813
Summary: Wayland: synchronous shm buffer teardown in
ShmPool::unref() causes multi-frame hitches when
opening/closing windows
Classification: Plasma
Product: kwin
Version First 6.7.3
Reported In:
Platform: Other
OS: Linux
Status: REPORTED
Severity: normal
Priority: NOR
Component: wayland-generic
Assignee: [email protected]
Reporter: [email protected]
Target Milestone: ---
Opening or closing windows makes the compositor miss several consecutive
frames. On this machine (2880x1800 @ 120 Hz, 8.33 ms
frame budget, VRR) closing a window produces frame gaps of up to ~75 ms, and up
to ~112 ms when the CPU is in a power-saving
EPP state (details under Additional Information). The hitch is fully
reproducible and clearly visible in any concurrently
running animation.
Profiling shows the stall is KWin performing synchronous teardown of the closed
window's wl_shm buffers inside the render
loop. At 2880x1800 a single ARGB buffer is ~20 MB; munmap()ing it zaps
thousands of PTEs in one call, and this happens
mid-frame.
Evidence (perf + ftrace, with samples restricted to the missed-frame windows
only):
- Presented frames were timestamped via the drm:drm_vblank_event_delivered
tracepoint (trace_clock=mono, deduped by sequence
number), with a client animating continuously so every gap is a real miss
rather than an idle desktop. KWin's own ftrace
paint markers (org.kde.kwin.FTrace) gave per-frame paint durations. perf
record --clockid=monotonic shares that timebase,
so samples could be bucketed into the exact gap windows. This matters: in an
aggregate profile the teardown cost is
invisible; it only stands out when profiling inside the gaps.
- Inside window-close frame gaps, ~50% of stalled time is in close() and
munmap() syscalls.
- KWin::ShmClientBuffer::~ShmClientBuffer appears in 89 of 92 close() samples
and 36 of 64 munmap() samples.
- The kernel side of those munmaps is dominated by zap_present_ptes and
filemap_remove_folio, consistent with unmapping
~20 MB of shm pages in one call.
- The remainder is decoration teardown on the same path:
KDecoration3::DecorationShadow::~DecorationShadow and QImage
destruction.
- Scheduling is not the cause: kwin_wayland runs SCHED_RR and its wakeup-to-run
latency was 1-30 us in every observed gap.
The main thread is on-CPU doing destruction work instead of painting.
Implicated code, src/wayland/shmclientbuffer.cpp (identical in current git
master, so this is not fixed upstream):
void ShmPool::unref()
{
--refCount;
if (refCount == 0) {
delete this;
}
}
When the last buffer of a closed window drops its reference, the pool is
deleted inline, cascading into the multi-megabyte
munmap and fd close at whatever point in the frame the refcount happens to hit
zero in practice, on the compositor thread
mid-frame.
STEPS TO REPRODUCE
1. Wayland session on a high-resolution display (larger surfaces mean larger
shm buffers and a worse stall); a power-saving CPU governor amplifies it. Run
something that animates continuously (e.g. glxgears) so frame misses are
observable.
2. Open and close a handful of windows (e.g. konsole instances).
3. Watch the animation; optionally record frame timing via KWin's ftrace
markers or the drm vblank tracepoint.
OBSERVED RESULT
A multi-frame hitch on every window close (smaller ones on open): frame gaps up
to ~75 ms against the 8.33 ms budget,
~112 ms with EPP=power. With ftrace markers enabled, the long paints/gaps line
up exactly with the ShmClientBuffer
destructor.
EXPECTED RESULT
Closing a window should not cost the compositor multiple frames. Client
buffer/pool destruction (the ~20 MB munmap, fd close, decoration shadow
teardown) should be deferred out of the render loop e.g. queued until after the
current frame is submitted, or released incrementally or on another thread.
SOFTWARE/OS VERSIONS
Operating System: Fedora Linux 44 (KDE Plasma Desktop Edition), kernel
7.1.5-201.fc44.x86_64
KDE Plasma Version: 6.7.3
KDE Frameworks Version: 6.28.0
Qt Version: 6.11.1
ADDITIONAL INFORMATION
Hardware: Intel Core Ultra 7 258V (Lunar Lake), Arc 140V iGPU, xe driver, Mesa
26.1.5. Internal OLED panel eDP-1,
2880x1800 @ 120 Hz, VRR enabled.
CPU-frequency dependence (why laptops on battery are hit hardest): the teardown
is CPU-bound, so the stall scales with clock
speed. Worst-case paint time for a window open/close storm by intel_pstate
energy_performance_preference:
power: 28.8 ms
balance_performance: 15.1 ms
performance: 14.8 ms
On battery (EPP=power, CPU around 1.6 GHz) worst-case frame gaps reached ~112
ms; forcing EPP=balance_performance halves
this to ~48 ms but cannot eliminate it, because the root cause is when the work
runs, not how fast.
A related but separate issue was diagnosed with the same instrumentation: the
Overview effect (QuickSceneEffect) rebuilds
its entire QML scene and creates a fresh GL context on every activation (~44 ms
of paint, gaps to 126 ms, 88.6% userspace:
QQmlComponent::create, QQuickRenderControl::initialize -> eglCreateContext, NIR
shader compilation, KSvg, freetype).
I can file that separately if useful. Possibly relevant to it:
QuickSceneEffect::setViewCachingEnabled() exists but has no callers in the
tree; experimentally enabling it for Overview produced rendering corruption
(stale regions, clients stuck on frame callbacks), so the dormant caching path
does not look usable as-is.
Happy to provide the raw perf/ftrace captures or re-run measurements against a
patch.
--
You are receiving this mail because:
You are watching all bug changes.