protonscr

Remote Play host on NVIDIA: are there plans to consume dmabuf in the NVENC path? (the driver supports it now)

steamopen Feature RequestNVIDIA driversStreaming
ValveSoftware/steam-for-linux#13460 · opened 2026-07-28 by lixsldotnet · updated 2026-08-05 · 1 comments · github
Llixsldotnet 2026-07-28 github

Disclaimer: I am a C# developer, graphics programming is not my area.
Sorry if I use some terms wrong.

My setup, in short: Steam runs in a gamescope session inside a Proxmox
LXC with a NVIDIA GPU, and I stream via Remote Play to my Mac.

What I noticed

While streaming, the F6 overlay shows different capture paths depending
on the GPU:

  • Steam Machine (AMD): Desktop PipeWire NV12 DMABUF + VAAPI HEVC
  • my NVIDIA host: Desktop PipeWire NV12 + NVENC HEVC (no DMABUF)

So on AMD the encoder reads the frames directly (zero-copy), on NVIDIA
Steam copies them through shared memory instead.

For a long time this was a driver limitation: the NVIDIA driver did not
export DRM format modifiers for NV12, so dmabuf could not work. But the
new R610 beta driver (610.43.03) added exactly that, and I could verify
that gamescope now really offers the capture as dmabuf on this driver.
Steam still picks the shared memory path anyway.

Why I care

The shared memory path is expensive on NVIDIA. In my case games dropped
to a third of their framerate as soon as a stream was connected (31 fps
instead of 78 at 3440x1440 on a RTX 4080 SUPER). I found a local
gamescope workaround that gets the framerate back, so I am fine for now,
but from what I understand a dmabuf path for NVENC would remove the
problem properly, like it already works on AMD.

The question

Are there plans to support dmabuf for the NVENC encoder path, now that
the driver side works? Also a "not planned" would help me, then I stop
checking the changelogs.

(Related: capture on NVIDIA also needs the still open gamescope PR #2271,
which Bazzite already ships.)

Setup

  • Host: Arch container on Proxmox, gamescope 3.16.25 (DRM backend, dummy
    plug), Steam stable with -pipewire -pipewire-dmabuf, RTX 4080 SUPER,
    NVIDIA 610.43.03 (same behaviour on 595.84)
  • Client: macOS Steam Link, HEVC, wired
TThomasMeisters 2026-08-05 github

Same behaviour here on KDE Plasma Wayland (KWin 6.7.3) rather than gamescope, so
it isn't gamescope-specific. Since the open question is whether a client can
actually consume the dmabuf the driver now exports, I tested that directly — it
can.

Host: CachyOS, RTX 4060 Ti, NVIDIA 610.57.04, PipeWire 1.6.8, Steam stable
1785799196 launched with -pipewire -pipewire-dmabuf. Client: Mac mini, Metal
HEVC decode, direct connection.

Same signature, with no error logged and only 0.7 ms between the two events:

CDesktopCapturePipeWire: PipeWire stream format changed (... stride 15360, size 33177600, dmabuf: 1)
CDesktopCapturePipeWire: PipeWire stream format changed (... stride 15360, size 33177600, dmabuf: 0)
>>> Capture method set to Desktop PipeWire NV12 + NVENC HEVC

At 3840×2160 that costs AvgCaptureMS 42.8, SlowCapturePercent 93–98,
AvgFPS 23, logged once a second as:

Slow framerate: game 0.00, capture 42.93, convert 0.06, encode 7.42,
                network 11.20, decode 6.90, display 0.89 (capture)

Everything else is healthy — encode 7 ms, network 11 ms, decode 7 ms. Capture
alone exceeds the whole 16.6 ms budget. At 1920×1080 it drops to 17 ms and holds
58 FPS, consistent with a resolution-proportional CPU copy.

Your vendor observation matches the shipped strings. ubuntu12_32/steamui.so
contains Desktop PipeWire NV12, Desktop PipeWire NV12 DMABUF and
Desktop PipeWire RGB DMABUF, but no DMABUF variant has ever appeared in my logs
on NVIDIA — only Desktop PipeWire NV12 + NVENC HEVC. So the path is in the
client and just isn't selected here.

The Steam client is 32-bit, so "the driver supports it now" has to hold in 32-bit
too. I wrote a minimal 32-bit ScreenCast client (portal → PipeWire → EGL) using
BGRx/XR24, the same format Steam requests, and it imports KWin's real screencast
buffer without trouble (source attached):

=== portal-dmabuf-client (32-BIT) ===
  EGL 1.5, vendor NVIDIA, gbm backend nvidia
  dma_buf_import: yes, import_modifiers: yes
  KWin offers 3840x2160 BGRx with 15 modifier(s): 0x300000000606010 0x300000000606011 ...
  fixating on modifier 0x300000000606010
  stream state: streaming
  first frame: n_datas=1 type=DmaBuf
    plane 0: fd=29 offset=0 stride=15360 size=33177600
IMPORT OK

Single plane, stride 15360 — the same buffer geometry Steam logs and then
declines. I also checked 32-bit against 64-bit and they're identical:
EGL_EXT_image_dma_buf_import and ..._modifiers both present, 54 dmabuf
formats, 13 modifiers for XR24 including LINEAR, and successful imports with
block-linear, linear and implicit modifiers.

A few things ruled out: stable 1785799196 and publicbeta 1785347151 behave
identically; -pipewire-dmabuf changes nothing; and /proc/<pid>/maps confirms
Steam loads the host libpipewire-0.3.so.0.1608.0 rather than a Steam Runtime
copy.

One packaging note that may matter for other Arch-family users: there's no
lib32-egl-gbm, so /usr/lib32/libnvidia-egl-gbm.so.1 is missing and EGL fails
outright with Couldn't initialize EGL: 0x3001. I built it from NVIDIA/egl-gbm
for 32-bit, which cleared that error and got dmabuf: 1 to appear in negotiation
at all. The fallback to shared memory persists regardless — but anyone on Arch
hits the EGL failure first and probably never reaches this issue.

Also worth flagging for anyone reproducing: Steam needs -pipewire or it never
starts a capture session and sits on Desktop Black Frame. AvgCaptureMS and
SlowCapturePercent look excellent when it's capturing nothing, so cross-check
AvgServerBitrate (Kbit) before believing any improvement.

So a "not planned" would be useful here too — but the driver-side blocker does
now seem to be gone, in 32-bit as well as 64-bit.

portal-dmabuf-client.c

Nothing extracted yet.