
本文是《穿透虚拟化图形栈virtio-gpu blob 机制全景解析》专栏的开篇。整个专栏将从 guest 使用 BO 的视角出发系统梳理现代 virtio-gpu 性能的基石——blob 协议的机制与实现。仅从省去拷贝这一点来看blob 的重要性无论怎样强调都不为过。让我们先回答一个根本的问题在没有 blob 之前虚拟化图形栈是怎么利用一块显存的它为什么不够用了1、故事的起点conventional resourcevirtio-gpu 最初的资源模型我们今天称之为conventional resource传统资源。它的命令是VIRTIO_GPU_CMD_RESOURCE_CREATE_2D和_CREATE_3D。它的世界观非常“图形化”可以浓缩成三条资源必须是有类型的typed。创建一个资源时你必须告诉设备它的宽、高、像素格式VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM之类。资源天生就是一张“纹理”或一个“渲染目标”。guest 存储与 host 存储是两份靠命令显式搬运。guest 侧有一份内存用来 CPU 访问host 侧 GPU 有另一份。两者之间的数据同步靠RESOURCE_ATTACH_BACKING把 guest 页挂到资源上TRANSFER_TO_HOST_2D/TRANSFER_FROM_HOST_3D显式地把字节从一侧拷到另一侧。一切以“提交—拷贝—呈现”为节奏。guest 改了内存就要TRANSFER_TO_HOSThost 渲染完了要给 guest 看就要TRANSFER_FROM_HOST。在 OpenGL 固定管线、2D 桌面合成的年代这套模型工作得很好。它简单、自洽、语义清晰。我们用一张图看清它的“拷贝节奏”Host GPUGuest 内核Guest 应用Host GPUGuest 内核Guest 应用写入纹理数据 (guest 内存)RESOURCE_ATTACH_BACKING (挂 guest 页)TRANSFER_TO_HOST_2D (拷贝!)GPU 渲染TRANSFER_FROM_HOST_3D (拷回!)读取结果注意那两个拷贝!。它们是这套模型的心跳也是它的原罪。2、困局现代 API 让这套模型处处漏风时代变了。当我们要在虚拟机里跑Vulkan、OpenCL、compute、乃至 GPU 通用计算如 ROCm时conventional resource 的三条世界观逐条崩塌。2.1 现代内存根本“没有类型”Vulkan 的VkDeviceMemory、OpenCL 的 buffer、compute 的 SSBO本质上都是一段无结构的字节untyped memory。它们没有宽高格式可能这一刻当顶点缓冲下一刻当 storage buffer。强行套CREATE_3D的“宽×高×格式”模型等于要求你给一段裸内存编造一个假的图像格式。这既别扭又会丢失真实的分配语义比如对齐、内存类型索引。规范的回应需要一种“只有大小、没有类型”的资源。2.2 拷贝正在谋杀性能TRANSFER_TO/FROM_HOST的每一次调用都是一次真实的内存拷贝外加命令往返。对一张 4K 纹理是几十 MB对一块 compute 数据集可能是几个 GB。现代 GPU 工作负载的特征是高频、大块、低延迟。在每帧、甚至每次 dispatch 都插入一次全量拷贝是不可接受的。我们真正想要的是guest CPU 写进去的地址就是host GPU 读出来的地址——零拷贝。2.3 host-visible coherent 内存无处安放Vulkan 有一类关键内存HOST_VISIBLE | HOST_COHERENT。应用vkMapMemory拿到一个指针直接写GPU 立即可见不需要任何显式 flush 或 transfer。这在物理机上靠的是 CPU 和 GPU 共享同一段物理内存 缓存一致性协议。可在虚拟机里guest 应用 map 到的是 guest 地址host GPU 用的是 host 地址——conventional 模型下这两者被拷贝隔开“coherent”这个语义根本无法实现。我们需要让 host 分配的内存直接出现在 guest 的地址空间里。而这正是后面要讲的 QEMUMemoryRegion KVM 二级页表要解决的事。2.4 跨设备 / 跨进程共享无从谈起现代合成器要把 GPU 渲染的 buffer 交给 display 控制器、视频编码器甚至另一个进程/容器。物理世界靠dma-buf这个内核对象在设备与进程间共享同一块内存。conventional resource 是 virtio-gpu 设备的私有概念没有一个可以拿出去、被别的子系统认识的“内核句柄”。跨设备cross-device、跨进程共享这条路根本不存在。3、blob一次“把资源和类型解耦”的范式转移面对这四条困局virtio-gpu 规范引入了blob resource命令VIRTIO_GPU_CMD_RESOURCE_CREATE_BLOB。它的核心思想只有一句话但分量极重资源不再是“一张有类型的图像”而是“一段字节存储”。存储从哪来、能不能映射、能不能共享由几个正交的维度独立描述。对照四条困局blob 逐一作答conventional 的困局blob 的回应内存必须有类型blob 只有sizeuntyped天然匹配 Vulkan/compute拷贝谋杀性能存储可以是 host 分配后直接映射进 guest零拷贝host-visible coherent 无处安放MAPPABLE host-visible 内存区让 host 内存出现在 guest BAR跨设备/进程共享CROSS_DEVICE 导出为dma-buf内核对象而“存储从哪来、可不可映射、可不可共享”这几个正交维度正是 blob 三个关键输入参数——blob_mem、blob_flags、blob_id——所要表达的。它们构成了理解整个 blob 机制的语义骨架。我们先给一个“剧透版”的对照让你对后面的旅程有个预期blob 的三维输入 · 规范层blob_mem存储在哪:GUEST/HOST3D/bothblob_flags可见性:MAPPABLE/SHAREABLE/CROSS_DEVICEblob_id引用哪块已有 host 内存后端据此选择存储形态dma-buf fdopaque fd UUIDGEM handle虚拟地址 / SVM看到右边那四种“存储形态”了吗它们最终会被后端填进一个叫virgl_context_blob的结构体里——那是本专栏的主角之一。但现在请先把它放一放。4、本篇小结与下一站这一篇我们回答了“为什么”conventional resource 是一套“有类型 显式拷贝”的模型在 2D/固定管线时代够用现代 APIVulkan、compute、ROCm带来了untyped 内存、零拷贝、host-visible coherent、跨设备共享四条硬需求逐条击穿了旧模型blob resource 的范式转移是把“资源”和“类型”解耦用blob_mem × blob_flags × blob_id三个正交维度重新描述存储这套解耦的“账单”最终要由QEMU 的地址空间注入和KVM 的二级页表来结算——这是旧模型完全不需要触碰的深水区。作为引子先把这张贯穿整个专栏的地图放在这里。你不需要现在就看懂它——后面每一篇我都会告诉你“你现在在这里”Guest 虚拟机virtqueue 命令virgl_renderer APIget_blobKVM_SET_USER_MEMORY_REGION应用 / Mesa 驱动virgl · venusGuest 内核drivers/gpu/drm/virtioVMM · QEMU / crosvmvirglrenderer 核心后端vrend · venus · drm · rocmHypervisor · KVMEPT / NPT / stage-2blob 机制的“全部意义”就是让最上面那块 guest 里的内存诉求能够以零拷贝、可映射、可共享的方式一路打通到最下面 hypervisor 的页表。而在 blob 出现之前这条路是断的——中间必须靠“拷贝”来接力。导航下一篇第 2 篇 整栈全景图一次 blob 分配要穿过多少层