Skip to content

[Bug]: Whole-screen recordings freeze/judder on GNOME/Wayland (AMD) — capture forced onto shm, mutter under-delivers vs dmabuf #507

Description

@Beetix

Search existing issues

  • I have searched the existing issues

Describe the bug

On GNOME/Wayland with an AMD GPU, recordings of the whole monitor play back frozen/juddery — the video sticks on a frame for a fraction of a second, then jumps. Recording a single window of the same content is noticeably smoother, and OBS recording the same monitor on the same machine is smooth.

After instrumenting the PipeWire helper and comparing it against OBS, this is not an encoder problem — it is a frame delivery problem. On this compositor the helper is forced onto the shm/memfd capture path, and mutter delivers far fewer frames over shm than over dmabuf (which OBS uses).

Measured on the same machine (full monitor 1920×1080, VAAPI encode). Distinct/non-duplicate frames via ffmpeg -vf mpdecimate:

Capture Distinct fps
OpenScreen — full monitor, editor scrolling 2.1 – 10.9
OpenScreen — window ~9.6
OBS — full monitor, editor scrolling 24.3

The saved files are valid 60fps CFR, but the content only changes a few times per second; the rest are held duplicates (the "stuck" look). The helper's dropped-frame counter is ~0 every run — it is not discarding frames it received; mutter simply never sends them. The encoder is fine and ruled out: backend vaapi, ~3 ms/frame, 0–7 dropped frames per recording.

Expected behavior

A whole-monitor recording should capture motion as smoothly as a window recording (and as OBS does on the same machine) — i.e. deliver frames at up to the negotiated maxFramerate (~60), not collapse to a few distinct frames per second.

To Reproduce

  1. GNOME/Wayland session on an AMD GPU (mutter is the ScreenCast producer).
  2. Record the whole monitor while a mostly-static app updates a small region (e.g. scrolling/typing in a WYSIWYG editor).
  3. Play back the file — motion is frozen/juddery. Recording the same app as a window is smoother.
  4. Confirm objectively: ffmpeg -i <file> -vf mpdecimate -an -f null - reports only a few distinct frames/sec despite a 60fps file.

Screenshots

No response

OS

Linux

OS Version

Arch Linux — GNOME/Wayland (mutter), AMD Radeon (radeonsi/renoir), PipeWire 1.6.8, app 1.10.0

Other OS

No response

Browser

None

Browser Version

No response

Other Browser

No response

Device Type

Desktop

Other Device

No response

Additional context

Root cause. The helper offers the shm format first and dmabuf second, and on GNOME/KDE shm always wins (by design — electron/native/pipewire-capture/csrc/pw_shim.c, ~L1318-1342). The hidden cost is that mutter's shm delivery for a whole monitor is heavily throttled (it must GPU→CPU copy each full-screen frame), so the recorder is starved of distinct frames and pads to 60fps CFR by holding the last picture.

Negotiated PipeWire format (pw-cli enum-params <node> Format) — framerate is identical; the only difference is the buffer type:

Param OBS OpenScreen helper
framerate 0/1 (variable) 0/1 (variable)
maxFramerate ~60 ~60
buffer type dmabuf (modifier present) shm/memfd (no modifier)

The helper cannot use dmabuf on this hardware. Forcing it (OPENSCREEN_PIPEWIRE_FORCE_DMABUF=1) fails outright:

stream-error: PipeWire stream reported an error in state error: no more input formats

osc_build_enum_format_dmabuf only advertises LINEAR/INVALID modifiers because frames are read via a CPU mmap (only linear buffers are mmap-able), but on AMD/mutter the monitor framebuffer is tiled — so mutter can't return a linear dmabuf and negotiation dies. That is exactly why the code defaults to shm. OBS works because it consumes the tiled dmabuf on the GPU, never touching the shm path. A window is less affected because the smaller surface lets mutter's per-frame shm copy keep up.

Suggested fix. Consume tiled dmabuf on the GPU (EGL/Vulkan import → GPU encode, or GPU blit to linear) instead of CPU-mmap-ing linear buffers — i.e. match OBS's capture path. This is the only way off the throttled shm path on AMD/GNOME, and it is a substantial change to the dmabuf read path in pw_shim.c + capture.rs, not a config tweak. Framerate/maxFramerate negotiation is not a lever — the helper already matches OBS there.

Related but distinct: #287 and #92 are dmabuf/negotiation failures on niri/wlroots; this issue is about degraded delivery on GNOME where capture succeeds but is starved over shm.

Metadata

Metadata

Assignees

No one assigned

    Labels

    status: fixed in mainWork is merged into main but may not be in a downloadable release yet.status: pending releaseMerged change is waiting for a packaged desktop release.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions