最近给笔记本接上 4K 显示器以后,在 KDE Plasma Wayland 下移动鼠标总觉得一顿一顿的。手上这只无线鼠标标称 8K 回报率,所以我第一反应是回报率没跑起来,于是先从输入设备和 USB HID 链路开始查。结果绕了一圈才发现,鼠标本身没什么问题,真正的原因藏在显示器的 TypeC 的连接里。

这里把排查过程整理一下,也算给自己留个记录。

环境

项目配置
系统Kalpa Desktop
内核Linux 7.2.6
桌面KDE Plasma Wayland
笔记本ThinkPad X1 Carbon Gen 8
CPU / GPUIntel Core i7-10510U
显卡驱动i915
显示器Philips 27E1N8900,3840x2160
连接方式TypeC 直连显示器

先怀疑鼠标回报率

通过 AI 相互排查,从 /proc/bus/input/devices 可以找到鼠标的主输入节点:

N: Name="Compx Wireless mouse 8k NANO dongle-L"
H: Handlers=mouse4 event10
I: Bus=0003 Vendor=373b Product=11fe Version=0111

USB 拓扑显示接收器工作在 High-Speed USB 链路上:

Bus 001 Device 008: ID 373b:11fe Compx Wireless mouse 8k NANO dongle-L
Driver=usbhid, 480M

再看 USB HID 的端点描述符:

bEndpointAddress     0x81  EP 1 IN
wMaxPacketSize       0x0007  1x 7 bytes
bInterval            1

sysfs 里给出的实际轮询间隔是:

/sys/bus/usb/devices/1-2.3/1-2.3:1.0/ep_81 interval=125us

换算成理论轮询频率:

$$ f = \frac{1}{125\ \mu s} = 8000\ Hz $$

另外顺手确认了这两项:

power/control=on
usbhid mousepoll=0

到这里其实基本可以排除几个常见的怀疑对象:

  • USB 主机端没有把鼠标限制在低回报率;
  • 接收器端点具备 8000 Hz 的理论轮询能力;
  • USB 自动挂起没有让接收器周期性休眠;
  • 内核也没有通过 usbhid.mousepoll 强制覆盖设备回报率。

真正的问题:屏幕只有 30 Hz

既然鼠标这边看不出毛病,就回头看了一眼 KDE 的输出配置:

Output: DP-2
Resolution: 3840x2160
Refresh rate: 29.98 Hz
Scale: 2

刷新率只有 29.98 Hz。大约 30 Hz 下每一帧大约持续:

$$ t = \frac{1}{29.98} \approx 33.4\ ms $$

也就是说,鼠标哪怕按 1000 Hz 甚至 8000 Hz 上报,屏幕每秒也只能画出大约 30 次位置变化,看起来自然是一跳一跳的。
所以我改变了排查的方向,应该是屏幕刷新率的问题了。当时 DRM/KScreen 能提供的高分辨率模式只有这几个:

[email protected]
[email protected]
[email protected]

显示器和显卡其实都支持 4K60

先确认 GPU 和驱动:

Intel Corporation CometLake-U GT2 [UHD Graphics] [8086:9b41]
Kernel driver in use: i915

再解析显示器 EDID 里的首选详细时序:

DTD: 3840x2160
pixel_clock=533.25 MHz
refresh=59.997 Hz

由此可以确认:

  • 显示器原生支持 3840x2160@60 Hz;
  • Intel GPU 和 i915 都支持这个输出;
  • 60 Hz 并不是 KDE 主动隐藏的,而是内核根据当前链路带宽,把传不动的模式过滤掉了。

问题于是变成了:链路带宽为什么不够?

中间的一次误判

机器上当时还接着一个 Belkin USB-C Multimedia Hub(USB ID 050d:092b/092c)。这款产品的规格本来就只支持 4K30,所以我一度以为是内核误判了视频信号走的是这个 Hub。

后来让 AI 跑了下排查,发现 Type-C partner、USB 根设备和 DRM 连接器逐一对应起来,其实内核已经弄清楚了情况:

port0 -> VIA Labs USB 2.0/3.0 Hub -> 显示器直连
port1 -> Belkin USB-C Multimedia Hub
DP-1  -> port0 的显示器视频链路

Belkin Hub 虽然也插在电脑上,但并不在这台显示器的视频路径里。

根因:USB 3.x 占掉了两条 lane

所以问题就又回到了显示器上,这台显示器是通过一根 TypeC 一线通是同时提供三样东西的:

  • DisplayPort Alt Mode 视频;
  • 显示器内置 USB Hub 的数据传输;
  • USB Power Delivery 供电。

排查时,显示器内置的 VIA Labs USB Hub 正以 USB 3.x 速率工作:

2109:0817 USB3.0 Hub
speed=5000

在常见的 USB-C DP Alt Mode 配置下,USB 3.x 会占用两条高速 lane,留给 DisplayPort 的就只剩两条。Comet Lake 平台跑 HBR2 时,两条 lane 的有效带宽是:

$$ DP\ HBR2\ x2 = 5.4 \times 2 \times 0.8 = 8.64\ Gbit/s $$

而这台显示器 4K60 RGB 8-bit 的有效像素数据大约是:

$$ 533.25\ MHz \times 24\ bit \approx 12.80\ Gbit/s $$

两条 HBR2 lane 显然扛不住。如果四条 lane 都给 DisplayPort,有效带宽则是:

$$ DP\ HBR2\ x4 = 5.4 \times 4 \times 0.8 = 17.28\ Gbit/s $$

足够跑 4K60。所以根因很清楚:显示器内置的 USB Hub 跑在 USB 3.x 模式,DisplayPort 只分到两条 lane,链路最多只能撑到 4K30。

解决办法

那么我就尝试在显示器 OSD 里把 USB 数据模式切换到 USB 2.0,或者叫「高分辨率优先」之类的选项,然后重新插拔一次 TypeC 线。这样 USB 2.0 数据会改走独立的低速线对,四条高速 lane 都可以分配给 DisplayPort。代价是显示器内置 USB Hub 的速率从 5 Gbit/s 降到 480 Mbit/s(这个问题对于链接鼠标键盘这类的设备来说,问题不大)。

果然,飞利浦的这款 OLED 显示器有对应的选项,于是我在 OSD 选线中选择了视频分辨率优先,然后重新插拔显示器链接的 TypeC 一线通。

切换后的验证

切换完成后,DRM 状态如下:

DP-1 status=connected
DP-1 enabled=enabled

/sys/class/drm/card1-DP-1/modes 里出现了两个 3840x2160 条目,说明 4K60 模式已经重新回到内核的模式列表里。

KWin 的输出配置记录为:

{
  "width": 3840,
  "height": 2160,
  "refreshRate": 59997,
  "scale": 2,
  "highDynamicRange": false,
  "wideColorGamut": false
}

实际刷新率约为 59.997 Hz。

与此同时,原来的 USB3 节点消失了,只剩下显示器的 USB 2.0 Hub(而我实际上也没有把鼠标和键盘都链接到这个 Hub 上):

idVendor=2109
idProduct=2817
product=USB2.0 Hub
speed=480

配置确实按预期生效了:USB Hub 降到 USB 2.0,DisplayPort 拿回四条高速 lane,4K60 正常工作。之前因为 30 Hz 带来的明显的鼠标卡顿感,也就跟着消失了。

顺手做的核显优化

那么问题解决以后,我又顺便看了一下 Intel 核显跑 4K60 的整体状态。AI Agent 帮我总结了下,目前的环境已经有不少有利条件:

  • 使用 KDE Plasma Wayland,没有额外的 X11 合成路径;
  • 输出为 [email protected] Hz;
  • 2 倍整数缩放,省掉了分数缩放带来的额外开销;
  • HDR 和 Wide Color Gamut 都是关闭的;
  • 已安装 Mesa 和 intel-media-driver,图形和 VA-API 视频硬件加速都有着落;
  • Intel P-state 正常启用;
  • iGPU 可以在 300–1150 MHz 之间动态调频,检查时跑到了 883 MHz;
  • 接着交流电源,CPU energy-performance preference 为 balance_performance。

内核参数方面,在十代 Comet Lake 上强制打开 GuC、PSR 或 DC 并不会提高物理刷新率,反而可能把内核状态搞乱,带来闪屏、冻结或者休眠恢复后黑屏之类的问题。FBC 也还是交给每个平台的默认策略比较稳妥。

倒是考虑为了减少后台内存规整带来的桌面帧延迟尖峰,我在工作站的 sysctl 配置里加了一行:

vm.compaction_proactiveness = 0

这台机器已经把 Transparent Huge Pages 限制为 madvise,主动内存规整对普通桌面程序的收益本来就有限,关掉以后更偏向交互延迟的稳定。

结论

回头来看,这次的问题并不是鼠标 USB 回报率太低,而是显示链路只跑在 30 Hz:

显示器 USB Hub 使用 USB 3.x
    ↓
USB-C 只给 DisplayPort 分配两条 HBR2 lane
    ↓
4K60 带宽不足,内核只提供 4K30
    ↓
屏幕每 33.4 ms 才更新一次,鼠标视觉上明显卡顿

把显示器 USB Hub 切换到 USB 2.0 以后,DisplayPort 拿到四条高速 lane,4K60 模式恢复,问题也就解决了。所以显示器的配置问题还是需要各个系统环境的具体配置,同时我也好奇为啥 Windows 和 macOS 下原有配置还是能跑 60Hz 的视频输出,可能是 Linux 内核对于这块更加严格或者谨慎一些吧(谁知道呢)。