核心突破:近乎原生的 GPU 虚拟化

virtio-nvgpu 项目在 KVM 虚拟机内部实现了近乎原生的 NVIDIA GPU 访问能力。实测数据显示,虚拟机内的渲染性能仅比宿主机低不到 2%,且 CPU 资源消耗与宿主机基本持平。

该方案的核心在于驱动 ABI 层面的转发virtio-nvgpu 直接在 Linux 虚拟机和宿主机之间转发 NVIDIA 内核驱动的 ioctl 调用,完全绕过了传统方案中 API 层面的翻译开销。这意味着虚拟机可以直接运行未经修改的 NVIDIA 用户态驱动程序,使用相同的库、Vulkan 接口和 NVENC 编码器,并直接连接同一张物理显卡。

其主要应用场景定位为无头流媒体传输:虚拟机内的合成器在 GPU 上完成渲染、合成及帧编码,最终输出压缩视频流。在此模式下,虚拟机无需连接物理显示器,显卡控制权由宿主机保留。

性能实测:与裸金属环境几乎无异

该项目目前已可用,并经过严格实测验证。在测试中,一个 Wayland 客户端在虚拟机内呈现,捕获层在游戏自有设备上编码,最终输出 H.264 码流。ffmpeg 成功解码了 618 帧且无任何错误。

测试环境基于 RTX 3060(驱动版本 595.99.02),对比对象为同一台宿主机上的裸金属环境,负载均为相同的无头 Vulkan 任务。数据显示:

  • 高负载场景(每帧耗时 > 2ms):虚拟机性能与裸金属环境的差距控制在 2% 以内。例如,当宿主机处理单帧耗时 39ms 时,虚拟机帧时间偏差仅为 -0.4%(处于噪声范围内);耗时 9.9ms 时,偏差为 -0.7%。
  • 极低负载场景(每帧耗时 ≤ 0.5ms):性能损耗主要来自唤醒成本。当帧时长极短(如 0.05ms)时,偏差可达 +40.8%,但这并非转发开销,而是由于等待 GPU 的唤醒成本(约 0.02ms)在极短帧长中占比显著所致。

CPU 开销方面,虚拟机表现优异。在以 ~100 fps 运行 12 秒的无限制测试中,单个虚拟机的 CPU 占用时间为 0.37s,而宿主机裸金属环境为 0.40s。这表明在渲染循环中,因转发产生的额外开销几乎为零。原因在于 NVIDIA 用户态驱动程序通过映射内存直接提交命令,而这些内存即为宿主机内存。在处理的 813,691 帧中,后端仅发送了 13,792 条消息,平均每 59 帧才有一次跨边界交互,且绝大部分属于设备初始化阶段。

多虚拟机支持:单卡均匀负载

在单张 RTX 3060 上同时运行四个虚拟机,每个虚拟机承受相同负载,结果显示负载分配极其均匀:

  • 四个虚拟机的帧率分别为 25.84、26.49、25.57、25.79 fps,总计 103.7 fps,与单虚拟机时的 102.9 fps 基本持平。
  • 中位数帧时间均稳定在 39.16ms 左右,差异仅在小数点后四位。
  • 所有四个虚拟机均能正确渲染,并同时以精确的 60 Hz 速率编码 H.264 视频,未触及 NVENC 会话数量限制。

需注意的是,“四”仅为实际测试的数量,并非该技术的技术上限。

驱动支持与功能现状

驱动版本支持:主要测试基于驱动版本 595.99.02。RTX A2000 在版本 615.71.09 下可渲染但未进行基准测试。项目已发布 ABI 配置文件包括 535.129.03、580.178.04、595.71.05,采用范围匹配机制,早于首个配置文件的版本将被拒绝。

已知支持的功能:

  • 虚拟机枚举显卡:nvidia-smi 报告真实的功耗和显存,deviceUUID 与宿主机一致。
  • Vulkan 渲染:vulkaninfo 正常退出,离屏绘制像素级正确。
  • Wayland 客户端通过虚拟机内的合成器呈现。
  • 通过 Vulkan Video 进行 NVENC 编码,在客户端自己的设备上执行。
  • 导入的缓冲区即为宿主机内存,通过共享窗口映射。

尚未实现的功能:

  • 超过四个虚拟机或更重负载任务的测试(八个虚拟机尚未尝试)。
  • 双卡及双驱动版本支持。
  • CUDA 功能虽已转发但未在枚举之外进行测试;沙箱隔离机制、按版本划分的驱动共享以及多租户信封功能尚未构建。

架构设计:为何优于现有方案

传统方案如 virtio-gpu + Venus 需要在 API 层序列化每个 Vulkan/OpenGL 调用,导致高延迟和高 CPU 开销,且虚拟机端无法直接访问 GPU 缓冲区进行 NVENC 编码。VFIO 直通 虽性能原生,但会将整张 GPU 专供一个 VM 使用,不适合多租户环境。

virtio-nvgpu 的不同之处在于,翻译发生在内核驱动层(针对 /dev/nvidia* 的 ioctl),而非图形 API 层。虚拟机在本地构建 GPU 命令缓冲区,单个绘制调用不会被序列化。每帧的边界跨越次数从 Venus 的数千次降低至 ~5–20 次,CPU 开销接近于零,且虚拟机拥有缓冲区的完全所有权,使得零拷贝 NVENC 编码成为可能。

仓库结构与许可

项目包含四个组件,采用混合许可策略以平衡内核模块要求与生态兼容性:

  • driver/(GPL-2.0):虚拟机内核模块,负责注册设备节点并转发 ioctl/mmap。
  • device/(Apache-2.0):virtio 设备实现,作为 Rust crate,不依赖任何 VMM,通过 trait 抽象 VMM 相关关注点。
  • isolate/(Apache-2.0):设计文档,规划用于保存真实设备文件描述符的每虚拟机沙箱辅助程序(目前尚未代码实现)。
  • protocol/(BSD-3-Clause OR GPL-2.0+):双方共享的线格式和 ABI 定义,采用双重许可。

该结构遵循 chromeos/virtio-media 模式,旨在解决 GPL 驱动与宽松许可设备 crate 在同一仓库并列的问题。