From 2fefc83e3f6dc049faecc48ce67505a9c1a629d5 Mon Sep 17 00:00:00 2001 From: sudacode Date: Thu, 6 Aug 2026 23:52:35 -0700 Subject: [PATCH] fix(playback): stop forcing legacy OpenGL renderer on X11 mpv backend (#188) --- changes/x11-backend-keeps-mpv-renderer.md | 4 ++++ docs-site/troubleshooting.md | 4 ++-- launcher/mpv.test.ts | 12 +++++------- src/shared/mpv-x11-backend.test.ts | 9 +++++---- src/shared/mpv-x11-backend.ts | 18 ++++++++++++------ 5 files changed, 28 insertions(+), 19 deletions(-) create mode 100644 changes/x11-backend-keeps-mpv-renderer.md diff --git a/changes/x11-backend-keeps-mpv-renderer.md b/changes/x11-backend-keeps-mpv-renderer.md new file mode 100644 index 00000000..2e303c12 --- /dev/null +++ b/changes/x11-backend-keeps-mpv-renderer.md @@ -0,0 +1,4 @@ +type: fixed +area: playback + +- XWayland/X11 mode (`--backend=x11`, or the automatic fallback on non-Hyprland/Sway Wayland sessions) no longer forces mpv onto `--vo=gpu --gpu-api=opengl`. It now only pins the window context (`--gpu-context=x11vk,x11egl,x11`), so a `vo=gpu-next` config keeps its renderer, API, and user shaders. Forcing the legacy OpenGL renderer crashed mpv on the first fullscreen toggle for anyone using a gpu-next user shader that emits a 4-component LUMA hook (ArtCNN and friends), which asserts in mpv's old renderer (`copy_image: *offset + count < sizeof(dst)`) as soon as the shader's upscale-only condition turns on. diff --git a/docs-site/troubleshooting.md b/docs-site/troubleshooting.md index f684fd48..d4c4c47b 100644 --- a/docs-site/troubleshooting.md +++ b/docs-site/troubleshooting.md @@ -406,7 +406,7 @@ On any Wayland session that is not Hyprland or Sway (KDE Plasma, GNOME, and othe SubMiner handles this automatically: - It launches its own window under XWayland (it sets `--ozone-platform-hint=x11`). -- Every mpv it launches (via the `subminer` launcher, Jellyfin, or YouTube) is pinned to XWayland too - Wayland environment hints are stripped and an X11 GPU context (`--gpu-context=x11egl,x11`) is applied. +- Every mpv it launches (via the `subminer` launcher, Jellyfin, or YouTube) is pinned to XWayland too - Wayland environment hints are stripped and an X11 GPU context (`--gpu-context=x11vk,x11egl,x11`) is applied. Only the window context is overridden; your `vo`/`gpu-api` and user shaders are left alone. - While mpv is windowed, the overlay is a managed X11 window owned by the tracked mpv window (`WM_TRANSIENT_FOR`), so it stays above mpv while other foreground X11/Xwayland apps can still cover both windows. - While tracked mpv is fullscreen, SubMiner swaps the visible overlay to a focusable-false X11 override-redirect window. That path can stay above the active fullscreen mpv window without requiring a KDE/KWin-specific rule, and SubMiner hides/releases it when mpv is no longer the active X11/Xwayland window. - The visible overlay is shown inactive on Linux, so normal hover should not steal keyboard focus from mpv. @@ -420,7 +420,7 @@ Requirements: `xdotool`, `xprop`, and `xwininfo` must be installed. SubMiner use This almost always means mpv came up as a **native Wayland** window that the XWayland overlay cannot cover. It happens when mpv is launched **manually** (your own command), because SubMiner can only force XWayland on the mpv processes it launches itself. Fix it one of these ways: - Launch playback through SubMiner (the `subminer` launcher or the tray), which forces XWayland for you, or -- Force XWayland in your own mpv invocation, e.g. `mpv --gpu-context=x11egl …`, or launch with `WAYLAND_DISPLAY= mpv …`, or set `gpu-context=x11egl` in your `mpv.conf`. +- Force XWayland in your own mpv invocation, e.g. `mpv --gpu-context=x11vk,x11egl,x11 …`, or launch with `WAYLAND_DISPLAY= mpv …`, or set `gpu-context=x11vk` (Vulkan) / `gpu-context=x11egl` (OpenGL) in your `mpv.conf`. To confirm mpv is on XWayland, `xdotool search --class mpv` should return a window id (a native Wayland mpv returns nothing). diff --git a/launcher/mpv.test.ts b/launcher/mpv.test.ts index dbf6512d..57c3a3e9 100644 --- a/launcher/mpv.test.ts +++ b/launcher/mpv.test.ts @@ -222,7 +222,7 @@ test('buildMpvEnv preserves native Wayland env for supported Hyprland and Sway a }); }); -test('buildMpvBackendArgs forces an explicit X11 renderer stack when backend resolves to x11', () => { +test('buildMpvBackendArgs pins the X11 window context when backend resolves to x11', () => { withPlatform('linux', () => { assert.deepEqual( buildMpvBackendArgs(makeArgs({ backend: 'x11' }), { @@ -230,12 +230,12 @@ test('buildMpvBackendArgs forces an explicit X11 renderer stack when backend res WAYLAND_DISPLAY: 'wayland-0', XDG_SESSION_TYPE: 'wayland', }), - ['--vo=gpu', '--gpu-api=opengl', '--gpu-context=x11egl,x11'], + ['--gpu-context=x11vk,x11egl,x11'], ); }); }); -test('buildMpvBackendArgs forces the same X11 renderer stack for unsupported Wayland auto fallback', () => { +test('buildMpvBackendArgs pins the same X11 window context for unsupported Wayland auto fallback', () => { withPlatform('linux', () => { assert.deepEqual( buildMpvBackendArgs(makeArgs({ backend: 'auto' }), { @@ -245,7 +245,7 @@ test('buildMpvBackendArgs forces the same X11 renderer stack for unsupported Way XDG_CURRENT_DESKTOP: 'KDE', XDG_SESSION_DESKTOP: 'plasma', }), - ['--vo=gpu', '--gpu-api=opengl', '--gpu-context=x11egl,x11'], + ['--gpu-context=x11vk,x11egl,x11'], ); }); }); @@ -292,9 +292,7 @@ test('buildConfiguredMpvDefaultArgs appends maximized launch mode to configured '--secondary-sub-visibility=no', '--alang=ja,jp,jpn,japanese,en,eng,english,enus,en-us', '--slang=ja,jp,jpn,japanese,en,eng,english,enus,en-us', - '--vo=gpu', - '--gpu-api=opengl', - '--gpu-context=x11egl,x11', + '--gpu-context=x11vk,x11egl,x11', '--window-maximized=yes', ], ); diff --git a/src/shared/mpv-x11-backend.test.ts b/src/shared/mpv-x11-backend.test.ts index 44b9d987..d770ae81 100644 --- a/src/shared/mpv-x11-backend.test.ts +++ b/src/shared/mpv-x11-backend.test.ts @@ -99,9 +99,10 @@ test('applyX11EnvOverrides strips Wayland hints and pins session type to x11', ( assert.equal(result.XDG_SESSION_TYPE, 'x11'); }); -test('MPV_X11_BACKEND_ARGS pins the GPU stack to X11', () => { - assert.deepEqual( - [...MPV_X11_BACKEND_ARGS], - ['--vo=gpu', '--gpu-api=opengl', '--gpu-context=x11egl,x11'], +test('MPV_X11_BACKEND_ARGS pins the window context to X11 without overriding the renderer', () => { + assert.deepEqual([...MPV_X11_BACKEND_ARGS], ['--gpu-context=x11vk,x11egl,x11']); + assert.equal( + MPV_X11_BACKEND_ARGS.some((arg) => arg.startsWith('--vo=') || arg.startsWith('--gpu-api=')), + false, ); }); diff --git a/src/shared/mpv-x11-backend.ts b/src/shared/mpv-x11-backend.ts index 16435dcd..8ab88f43 100644 --- a/src/shared/mpv-x11-backend.ts +++ b/src/shared/mpv-x11-backend.ts @@ -12,12 +12,18 @@ so the gate and the mpv backend args stay in one place. */ -/** mpv args that pin the GPU/windowing stack to X11/XWayland (libGL via EGL on X11). */ -export const MPV_X11_BACKEND_ARGS = [ - '--vo=gpu', - '--gpu-api=opengl', - '--gpu-context=x11egl,x11', -] as const; +/** + * mpv args that pin the *windowing* stack to X11/XWayland, in Vulkan-then-OpenGL order. + * mpv walks the list and skips contexts that do not match the configured `--gpu-api`, + * so this works for both a Vulkan and an OpenGL config. + * + * Deliberately does NOT set `--vo`/`--gpu-api`: forcing `--vo=gpu --gpu-api=opengl` here + * used to drop configs off `vo=gpu-next` onto the legacy renderer, where user shaders + * written for gpu-next (e.g. ArtCNN, `//!COMPONENTS 4` LUMA hooks) abort mpv with + * `copy_image: Assertion '*offset + count < sizeof(dst)' failed` as soon as their + * upscale-only `//!WHEN` condition turns on, i.e. on the first fullscreen toggle. + */ +export const MPV_X11_BACKEND_ARGS = ['--gpu-context=x11vk,x11egl,x11'] as const; export type LinuxDesktopEnv = { xdgCurrentDesktop: string;