• 环境:银河麒麟国防版 / X11 会话 / NVIDIA RTX 3060 Ti (8G) / 驱动 595.91.07 / TESP Linux 打包版(Vulkan RHI)

  • 现象:主界面(登录页)帧率仅 7.74 FPS(129 ms/帧),全系统性卡顿

  • 结论X Server 没有加载 NVIDIA 的 X 驱动,OpenGL 栈回退到 llvmpipe 软件光栅器。Vulkan 渲染虽然在独显上,但每帧画面要回拷到 CPU 软件显示栈才能上屏,成为瓶颈。

  • 状态:已修复,主界面卡顿消失


一、现象与初始数据

stat unit 输出:

指标
FPS 7.74
Frame 129.03 ms
Game 2.72 ms
Draw 3.65 ms
RHIT 3.31 ms
GPU Time (空白)
DynRes Unsupported
Draws / Prims 38 / 836

关键矛盾:CPU 三条线程(Game / Draw / RHIT)加起来不到 10 ms,但整帧 129 ms。约 120 ms 不在任何一条被统计的线程里。


二、排查过程

1. 排除 GPU 侧(全部否定)

nvidia-smi --query-gpu=utilization.gpu,memory.used,memory.total,pstate,clocks.sm,clocks.max.sm,power.draw,power.limit,temperature.gpu,pcie.link.gen.current,pcie.link.width.current --format=csv -l 1

结果:22%, 1484MiB/8192MiB, P8, 210MHz/2100MHz, 15W/200W, 44℃, gen1, x8

sudo nvidia-smi -q -d PERFORMANCE | grep -A15 "Clocks Event Reasons"

结果:Idle : Active,其余(SW Power Cap / HW Slowdown / Thermal / Sync Boost / Display Clock)全部 Not Active

即:驱动自己认定 GPU 是空闲的,没有任何限频原因。

强制锁频验证:

sudo nvidia-smi -pm 1
sudo nvidia-smi -lgc 1500,2100

重启程序后帧率毫无变化,仍是 7 FPS → 频率变量彻底排除。

由此排除:算力不足、频率未拉升、功耗墙、温度墙、显存不足、PCIe 带宽(Gen1 是 P8 待机态的正常表现,x8 对游戏影响 <2%)。

2. 定位到"呈现阻塞"

关键机制:UE 在统计 Draw / RHIT 时会主动扣除等待时间(RenderThreadIdle / RHIThreadIdle)。 因此渲染线程卡在 vkAcquireNextImageKHR / vkQueuePresentKHR 上的 100 ms 会被从 Draw 中减掉,只计入 Frame

"CPU 三线程闲 + GPU 报 Idle + Frame 129 ms" —— 这是 Present 阻塞的典型特征。

量级换算:4K 一帧 3840×2160×4B ≈ 33 MB,129 ms → 约 257 MB/s。这不是显卡在工作,是 CPU 在搬内存。

3. 参照程序对比(关键分叉)

glxgears -info          # 全屏约 410 FPS(后续证实是 llvmpipe 在跑)
vkcube                  # 小窗流畅,拉大窗口明显卡顿

vkcube 的开销随窗口像素面积增长 —— 说明每帧存在按像素收费的全屏拷贝,而非零拷贝翻页(page flip)。

⚠️ 踩坑:vkcube 的帧数参数是 --c(双横杠),写成 -c 会直接打印 Usage 后退出(real 0m0.001s)。该版本也没有 --width / --height 选项,窗口大小只能手动拖。

4. 定位根因

glxinfo -B | grep -E "OpenGL renderer|OpenGL vendor"
OpenGL vendor   string: VMware, Inc.
OpenGL renderer string: llvmpipe (LLVM 11.0.0, 128 bits)
xrandr --listproviders
Providers: number : 1
Provider 0: id: 0x47 cap: 0x0 crtcs: 4 outputs: 4 associated providers: 0 name: modesetting

llvmpipe = Mesa 纯 CPU 软件光栅器VMware, Inc. 是 Mesa 软件驱动的厂商字符串,与虚拟机无关)。 Provider 只有一个 modesetting(通用 KMS 驱动),不是 NVIDIA-0,且 cap: 0x0 连 PRIME 卸载能力都没有。

排除项(均已确认正常,非本次原因):

cat /sys/module/nvidia_drm/parameters/modeset   # Y
echo $XDG_SESSION_TYPE                          # x11(非 Wayland/XWayland)

三、根因与完整因果链

X Server 用通用 modesetting 驱动绑定了显示设备,而不是 nvidia_drv.so

  1. nvidia-drm.modeset=1 使 NVIDIA 注册了一个 DRM 设备(4 个输出)

  2. X 用 modesetting 驱动绑上它,而非 NVIDIA 的 X 驱动

  3. Mesa 在该设备上找不到可用 GL 驱动 → 回退 llvmpipe 软件渲染

  4. TESP 是 Vulkan,Vulkan ICD 绕过 X 直接找到独显 → 所以 nvidia-smi 里能看到 TESP 进程(C+G),造成"显卡在用"的假象

  5. 但每帧画完后要从显存回拷到 CPU,再交给软件显示栈上屏 → 约 257 MB/s、100 ms 的固定开销

  6. GPU 只干了约 3 ms 的活就闲置 → 驱动报 Idle: Active锁频无效

  7. UE 把 Present 等待从 Draw/RHIT 中扣除 → 三线程全"闲",Frame 却 129 ms

  8. 拷贝按像素收费 → vkcube 小窗流畅、大窗卡顿


四、修复方法

1. 确认 X 的 NVIDIA 驱动模块存在

ls -l /usr/lib/xorg/modules/drivers/ | grep -i nvidia
ls -l /usr/lib/x86_64-linux-gnu/libGLX_*.so.0

若缺少 nvidia_drv.so,说明驱动是按"仅计算"方式安装的(如 .run 包带 --no-opengl-files),需重装完整显示驱动。

2. 查看 X 当初为何未选中 NVIDIA

grep -iE "nvidia|modesetting|\(EE\)|\(WW\).*GLX" /var/log/Xorg.0.log | head -40

3. 生成 NVIDIA 的 X 配置

优先使用 prime 切换器(若存在,比手改配置安全):

prime-select query
sudo prime-select nvidia

否则:

sudo nvidia-xconfig

或手写 /etc/X11/xorg.conf.d/10-nvidia.conf(BusID 取自 nvidia-smi00000000:04:00.0):

Section "Device"
    Identifier "nvidia"
    Driver     "nvidia"
    BusID      "PCI:4:0:0"
EndSection

Section "ServerLayout"
    Identifier "layout"
    Screen 0 "nvidia"
EndSection

Section "Screen"
    Identifier "nvidia"
    Device     "nvidia"
    Option     "AllowEmptyInitialConfiguration"
EndSection

4. 重启后验证

glxinfo -B | grep -E "OpenGL renderer|OpenGL vendor"

必须变为:

OpenGL vendor   string: NVIDIA Corporation
OpenGL renderer string: NVIDIA GeForce RTX 3060 Ti/PCIe/SSE2
xrandr --listproviders

Provider name 应变为 NVIDIA-0

5. 操作风险与回退

改 X 配置有黑屏风险。动手前备份已有配置:

sudo cp /etc/X11/xorg.conf /etc/X11/xorg.conf.bak

黑屏时按 Ctrl+Alt+F2 切 TTY,删除新增配置后重启:

sudo rm /etc/X11/xorg.conf.d/10-nvidia.conf && sudo reboot

五、经验沉淀

1. Linux 部署交付的开机自检清单

新机器部署 TESP 后,先跑这两条再谈性能

glxinfo -B | grep -E "OpenGL renderer|OpenGL vendor"
xrandr --listproviders
  • renderer 必须是 NVIDIA GeForce ...,出现 llvmpipe / softpipe / swrast / VMware, Inc. 一律判为显示栈未接独显

  • Provider name 必须是 NVIDIA-0,出现 modesetting 同样判不合格

2. nvidia-smi 里看到进程 ≠ 走的是硬件路径

Vulkan ICD 独立于 X 加载驱动。渲染在独显上,但呈现路径可能仍绕回 CPU。 判断硬件加速是否真正生效,要看 glxinfo 的 renderer,不是看 nvidia-smi 的进程列表。

3. stat unit 的读法

  • Frame 远大于 Game/Draw/RHIT 之和 → 帧时间花在"等待"上,不在计算或绘制上。 UE 会把 idle/wait 从 Draw、RHIT 中扣除,Present 阻塞只体现在 Frame。

  • Draws 少不代表 GPU 负载低:UE5 的 Nanite、Lumen、Virtual Shadow Map 是 Compute 驱动的,不计入 Draws 计数

  • DynRes: Unsupported 在 PC 上是正常显示,不构成任何证据。

  • GPU Time 空白在 Shipping 打包版里是正常的(STATS=0 编译掉了),不代表驱动缺 timestamp 支持。要精确定位到具体渲染 Pass,需要 Development 打包版才能用 stat gpu / ProfileGPU

4. Clocks Event Reasons 的价值

sudo nvidia-smi -q -d PERFORMANCE | grep -A15 "Clocks Event Reasons"

唯一 Idle : Active、其余全 Not Active = 驱动明确声明 GPU 没活干,可以一次性排除算力/功耗/温度/限频四大类原因,避免在 GPU 侧继续浪费时间。

5. 一个高性价比的二分实验

vkcube(Vulkan,与 UE 同路径)和 glxgears(OpenGL)做参照,并观察帧率是否随窗口面积恶化

  • 参照程序同样慢 → 系统级问题,与业务程序无关

  • 开销随像素面积线性增长 → 存在逐帧全屏拷贝,指向呈现/显示路径

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐