XFCE4 搭配 NVIDIA GPU 绕过 VT Switching 导致渲染管线阻塞问题的方法

现象

全新安装的 Linuxmint 22.3,使用 XFCE4 桌面,在使用 NVIDIA GPU 并安装较高版本(580以上,未精确测试)驱动程序时,首次登录可以正常使用桌面,但是在锁定并解锁、或使用 ctrl alt f1 组合键切换 VT 至 tty 再切回后,出现桌面无渲染但鼠标光标可动的情况。

/var/log/Xorg.0.log 中可观测到如下输出:

[ 60623.296] (WW) NVIDIA(0): Failed to request fliplock.

根因分析

NVIDIA 驱动在处理 VT 切换时存在问题,会导致 VBlank 事件无法正确上报至用户态,使得桌面环境的 Compositor 基于垂直同步信号驱动的渲染管线阻塞。

绕过办法

重启 xfwm4

丢失运行时状态。不推荐。

关闭 Compositing

直接在设置中找到 Window Manager Tweaks 下的 Enable display compositing,并取消勾选该项。

将会面临一些现代桌面渲染功能失效的问题,不推荐。

打补丁

核心思路是在用户态异步检测到 stall condition 时,重启渲染管线,清除异常。

xfwm4 负责窗口管理和 compositing,使用 X11 的 XSyncAwaitFence() 实现垂同。该 API 不提供异步接口,无法简单完成对 xfwm4 的修复。

未探索直接对 X.org 进行补丁的可能性。维护成本不容乐观。

Workaround

由于无法直接重启 xfwm4 ,所以必须将 compositor 替换为其它独立实现,以便在渲染管线 stall 后直接重启 compositor,间接重启渲染管线。

可选的 X11 compositor 有很多,经测试,picomcompiz 都可以支持桌面渲染。前者轻量,后者功能多。compiz 有可能会和 xfwm4 的窗口管理功能冲突。

需要在 VT 切换事件后立即重启 compositor,且需要在桌面登出后清理掉相关进程。以 picom 为例:

创建可执行文件: /usr/bin/picom-restart.sh

#!/usr/bin/env sh

set -e

DBUS_SESSION="$(loginctl session-status)"
DBUS_SESSION="$(printf "%s" "${DBUS_SESSION}" | head -n 1 | cut -d ' ' -f 1)"
DBUS_SESSION_PATH="/org/freedesktop/login1/session/${DBUS_SESSION}"

FIFO="${XDG_RUNTIME_DIR:-/tmp}/dbus-monitor-$$"
rm -f "$FIFO"
mkfifo "$FIFO" || exit 1

cleanup() {
    kill "$DBUS_PID" 2>/dev/null
    rm -f "$FIFO"
}

# On signals: clear EXIT trap first so cleanup doesn't run twice
trap 'trap - EXIT; cleanup; exit 0' TERM HUP INT QUIT
trap cleanup EXIT

dbus-monitor --system --profile \
    "type=signal,interface='org.freedesktop.DBus.Properties',path='${DBUS_SESSION_PATH}'" \
    > "$FIFO" 2>/dev/null &
DBUS_PID=$!

while IFS= read -r line; do
    ACTIVE="$(busctl get-property org.freedesktop.login1 "${DBUS_SESSION_PATH}" org.freedesktop.login1.Session Active | awk '{print $2}')"
    STATE="$(busctl get-property org.freedesktop.login1 "${DBUS_SESSION_PATH}" org.freedesktop.login1.Session State | awk '{gsub(/"/, "", $2); print $2}')"

    # VT activation: Active changed to true
    if ${ACTIVE}; then
        echo "Graphical VT active now!" >&2
        pkill -u "${USER}" picom 2>/dev/null || true
        picom &
    else
        if [ "${STATE}" = "closing" ]; then
            echo "Graphical Session Closed!" >&2
            exit 0
        fi
    fi
done < "$FIFO"

创建 xdg 配置实现自动启动:/etc/xdg/autostart/picom-restart.desktop

[Desktop Entry]
Version=1.0
Type=Application
NoDisplay=false
Name=picom-restart
GenericName=X compositor
Comment=An X compositor watcher
Categories=Utility;
Keywords=compositor;composite manager;window effects;transparency;opacity;
TryExec=picom-restart.sh
Exec=picom-restart.sh
StartupNotify=false
Terminal=false

为 picom 创建配置文件,使用 GLX 后端并启用 vsync:~/.config/picom/picom.conf

backend="glx";

vsync=true;

glx-no-stencil=true;
glx-no-rebind-pixmap=true;

使用其它 compositor 的话按需修改脚本。