技术博客

RenderingDeviceDriver 的诞生:4.3 拆出驱动层解决了什么问题

第 41 / 149 篇 · 2025-08-11 · godot · 渲染Vulkan源码分析

4.2 之前想给 Godot 加第二个图形后端,得重写整个 RenderingDevice——那个类五千多行,Vulkan 对象生命周期、命令提交、资源状态跟踪全缠在一起。4.3 的解法是把类沿一条缝切开:所有"和具体 API 打交道"的代码下沉为新类 RenderingDeviceDriver(RDD),RenderingDevice(RD)只留跨后端的簿记。这是 4.x 渲染抽象层最大的一次内部重构。

切口在哪

切分依据是"这个决定需不需要懂 Vulkan"。servers/rendering/rendering_device.cpp 里原来混着两类代码:

  • 资源簿记:RID 表里这个纹理的格式、尺寸、当前布局,uniform set 的哈希缓存,barrier 需要的访问状态历史——这些是 API 无关的,换 D3D12 语义完全一样。
  • API 调用:创建 VkImage、记 command buffer、提交 queue——每个 API 的写法都不同。

4.3 后第二类全部进 drivers/vulkan/rendering_device_driver_vulkan.cpp,RDD 的接口按操作族划分:buffer、texture、sampler、descriptor set、pipeline、command queue、framebuffer……RD 手里只剩一张 RenderingDeviceDriver *driver,所有落 API 的动作都过它。上层(场景渲染器、渲染器前端)完全无感——RD 的公开 API 一个签名都没改。

// 拆分后的调用关系(示意,非真实代码)
RenderingDevice::texture_create(format, view) {
    // RD 侧:登记 RID、查重、簿记状态
    // RDD 侧:VkImage + VkImageView 真正落地
    RID rid = driver->texture_create(format, view);
    texture_owner.initialize_rid(rid, ...);
    return rid;
}

解决的真问题:barrier 的统一推导

拆分前,RD 里最让人头疼的是 barrier:rendering_device_vulkan.cpp 里每个操作后手写 VkPipelineStage/VkAccessFlag 转换,加新特性要在十几处补 barrier 矩阵。下沉之后,RD 不再直接面对 VkImageLayout——它向 RDD 描述意图("这个纹理接下来要被采样"),RDD 的 Vulkan 实现自己推导出布局转换和 barrier。意图描述是 API 无关的,D3D12 实现把同样的意图翻译成 resource barrier,Metal 翻译成 pass 依赖。

这让"自动 barrier"从 Vulkan 后端的实现技巧升级成 RD 层的正式契约:上层再也不需要(也不允许)猜底层什么时候插同步。三-7 篇会展开这个推导的机制。

给 D3D12/Metal 铺路的实际进度

拆分的公开动机是官方 issue 里挂了几年的多后端路线图。RDD 落地后,新后端的工作量从"重写 RenderingDevice"缩到"实现一份 RDD 接口":D3D12 版 RDD 在 4.4/4.5 周期进入实验性,Metal 版在社区分支里也有原型。对比 3.x 的 GLES3/Vulkan 双后端——那时每个后端都是一份完整渲染器——4.x 的架构让 $N$ 个后端只需要 $N$ 份 RDD 加一份 RD。

也要注意这笔账的另一面:RDD 是窄接口高速路的反面教材——为了让 RD 完全后端无关,RDD 的参数类型被压成最简(大量 const void * 风格的描述结构),类型安全靠约定。读源码时能感觉到这是刻意选择:宁可 RDD 层啰嗦,也要保住 RD 层的干净。三-10 篇会量化这层间接调用的实际开销,结论是基本没有。

RenderingDevice RID 簿记 / 状态跟踪 RenderingDeviceDriver API 调用 / 意图翻译 RDD Vulkan 布局转换 + barrier 推导 RDD D3D12 / Metal 同一接口 翻译各自语义 上层无感: RD 公开 API 签名不变 拆分前 barrier 散落各处, 拆分后由驱动按意图统一推导

本文所述文件位于 Godot 4.3-stable 的 servers/rendering/rendering_device.cpp、drivers/vulkan/rendering_device_driver_vulkan.cpp,代码为示意。