Skip to content

feat(linux/wlr): implement SHM capture fallback for headless setups#4946

Open
atassis wants to merge 2 commits intoLizardByte:masterfrom
atassis:feat/shm-capture-fallback
Open

feat(linux/wlr): implement SHM capture fallback for headless setups#4946
atassis wants to merge 2 commits intoLizardByte:masterfrom
atassis:feat/shm-capture-fallback

Conversation

@atassis
Copy link
Copy Markdown

@atassis atassis commented Apr 4, 2026

Description

Implements SHM (shared memory) capture as a fallback when DMA-BUF screencopy fails.

Currently, wlr-screencopy only supports DMA-BUF capture via GBM buffer allocation. On headless NVIDIA setups (no physical display connected), GBM allocation fails because the NVIDIA driver cannot create GBM buffers without an active DRM output. This makes Wayland screencopy unusable on headless NVIDIA — the existing code path logs "SHM capture not implemented" and gives up.

This PR implements the SHM fallback that was stubbed out in buffer_done():

  1. Binds wl_shm interface from the Wayland registry
  2. When DMA-BUF fails (or is unavailable), allocates a memfd-backed wl_shm_pool and requests the compositor to copy frames into it
  3. Converts SHM pixel data (handles both 4bpp XRGB8888 and 3bpp BGR888 formats) and feeds it to the encoder via the existing wlr_ram_t CPU path
  4. Caches GBM failure to avoid retrying DMA-BUF allocation every frame
  5. Makes EGL initialization non-fatal so wlr_ram_t can operate in SHM-only mode

The DMA-BUF path is completely unchanged — SHM only activates when GBM fails or the compositor doesn't advertise zwp_linux_dmabuf_v1.

Screenshot

Issues Fixed or Closed

Roadmap Issues

Type of Change

  • feat: New feature (non-breaking change which adds functionality)
  • fix: Bug fix (non-breaking change which fixes an issue)
  • docs: Documentation only changes
  • style: Changes that do not affect the meaning of the code (white-space, formatting, missing semicolons, etc.)
  • refactor: Code change that neither fixes a bug nor adds a feature
  • perf: Code change that improves performance
  • test: Adding missing tests or correcting existing tests
  • build: Changes that affect the build system or external dependencies
  • ci: Changes to CI configuration files and scripts
  • chore: Other changes that don't modify src or test files
  • revert: Reverts a previous commit
  • BREAKING CHANGE: Introduces a breaking change (can be combined with any type above)

Checklist

  • Code follows the style guidelines of this project
  • Code has been self-reviewed
  • Code has been commented, particularly in hard-to-understand areas
  • Code docstring/documentation-blocks for new or existing methods/components have been added or updated
  • Unit tests have been added or updated for any new or modified functionality

AI Usage

  • None: No AI tools were used in creating this PR
  • Light: AI provided minor assistance (formatting, simple suggestions)
  • Moderate: AI helped with code generation or debugging specific parts
  • Heavy: AI generated most or all of the code changes

When DMA-BUF capture fails (e.g. GBM cannot allocate buffers on
headless NVIDIA without an active DRM output), fall back to SHM
shared memory capture via wl_shm.

The SHM path creates a memfd-backed wl_shm_pool, receives pixel
data from the compositor via wlr-screencopy, and feeds it to the
encoder through the existing wlr_ram_t CPU path.

Supported SHM formats:
- 4 bpp (XRGB8888/ARGB8888): direct memcpy
- 3 bpp (BGR888): pixel conversion to BGRA8888

Key changes:
- Bind wl_shm interface in screencopy path
- Add create_and_copy_shm() with memfd + mmap allocation
- Refactor create_and_copy_dmabuf() to return bool for fallback
- Cache GBM failure to avoid per-frame retry
- Handle SHM frames in wlr_ram_t::snapshot() via memcpy
- Make EGL init non-fatal (SHM path does not require EGL)
- Force wlr_ram_t on reinit when SHM fallback is active

Tested on headless NVIDIA RTX 5060 Ti with labwc compositor,
NVENC HEVC encoding, streaming to Moonlight client.
@atassis atassis force-pushed the feat/shm-capture-fallback branch from 0262c5d to 2bcea4f Compare April 4, 2026 05:03
BGR888 (0x34324742) stores bytes in R,G,B memory order.
Previous code copied them straight into BGRA8888, causing
red/blue inversion (orange appeared bluish in Moonlight).

Swap src positions 0↔2 so B and R land in correct BGRA slots.
Also deduplicate SHM format log to fire only once.
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Apr 4, 2026

Quality Gate Failed Quality Gate failed

Failed conditions
3 New issues
3 New Code Smells (required ≤ 0)
2 Duplicated Blocks on New Code (required ≤ 0)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant