技术博客

Compatibility 渲染器:GLES3 的 fallback 路径

第 53 / 149 篇 · 2026-06-28 · godot · 渲染管线渲染器源码分析

打开 Godot 4 的渲染器选择下拉框,前两个选项(Forward+、Mobile)是 RD 家族的,第三个 Compatibility 是个异类:它完全不经过 RenderingDevice。display/rendering/drive 选 opengl3 后,整条渲染栈走的是 drivers/gles3/ 下一套自 3.x 延续下来的光栅器——RasterizerStorageGLES3、RasterizerCanvasGLES3、RasterizerSceneGLES3。要理解这个渲染器,得先回答一个问题:都 4.x 了,为什么还养一套不走红 abstraction 的老代码?

为什么 Compatibility 不走 RD

RD 的设计底线是 Vulkan 1.0 级别的能力:显式屏障、subpass、compute、storage buffer。GLES3 有其中一部分(3.1 有 compute),但 WebGL2——Compatibility 必须覆盖的目标——没有 storage image、没有 SSBO 的原生等价物(WebGL2 只有 UBO),更没有显式 barrier 概念。也就是说RD 的抽象面在 GLES3/WebGL2 上无法无损映射,强套抽象的结果是每个调用点塞 if-else 降级,抽象层退化成装饰。

Godot 的选择是诚实的:WebGL2 设备就不配走现代抽象,给它们一条直通驱动的老路。代价是要维护两套渲染器代码(RD 系 + GLES3 系),servers/rendering/renderer_rd/ 和 drivers/gles3/ 各自演进;收益是 Godot 4 保住了 Godot 3 的全部设备面——尤其是浏览器。对引擎团队这是理性的账:Web 游戏的性能上限本来就低,渲染器新旧不重要,能跑才重要。

代码血统与结构

drivers/gles3/rasterizer_gles3.cpp 是入口,初始化顺序和 RD 系对称:创建上下文、初始化 storage/canvas/scene 三个子光栅器、注册到 RenderingServer。功能对照:

RD 系 GLES3 系
servers/rendering/renderer_rd/storage_rd/ drivers/gles3/storage/(mesh/material/texture)
servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp drivers/gles3/rasterizer_canvas_gles3.cpp
forward_clustered/render_forward_clustered.cpp drivers/gles3/rasterizer_scene_gles3.cpp

scene 那栏能看出量级差异:Forward+ 的主渲染器是一万多行的文件加十几个 effect 模块,GLES3 的 scene 光栅器把所有前向光照塞在一个大文件里——不是 4.x 写得偷懒,是 GLES3 路径本来就没有那么多 pass 可拆。

静默降级清单

Compatibility 的文档(doc/classes/RenderingServer.xml 和各版本迁移指南)列了明确的降级项,实战中容易"静默"踩到的有这些:

  • 全局光照归零:SDFGI、VoxelGI 运行时全部不可用,烘焙的 LightmapGI 是唯一的 GI 来源,GIProbe 老工程迁移过来直接没效果。
  • 光源数硬上限:GLES3 路径用 uniform 数组传灯参数,WebGL2 的 UBO 上限(普遍 16KB~64KB)卡死了灯数,超出部分不是丢灯是不渲染。
  • shader 语言能力收缩:hint_screen_texture 类屏幕采样在 WebGL2 上有副本限制;部分内建函数(如某些 textureLod 组合)在 ANGLE 转译层上行为不一致。
  • MSAA 受限:WebGL2 的 multisample renderbuffer 不普遍支持解析到纹理,引擎按能力检测降级。
  • compute 不存在:粒子 GPU 模拟、cluster 构建全部回退 CPU 版(粒子回 CPU 模拟是 Compatibility 掉帧的头号原因)。

"静默"的意思是:项目设置里这些选项照样能勾,勾了不报错,只是没有效果——编辑器预览用 Compatibility 跑的时候一切正常,导出到 Web 才发现 GI 没了。防御办法只有一个:开发期就用目标渲染器跑,项目设置里和 RD 系绑定的选项(SDFGI 相关、volumetric fog 相关)在 Compatibility 下视为无效。

ANGLE 这个中间层

Windows 和 Web 上的 Compatibility 实际不直接对 GLES3 驱动,而是经过 ANGLE(Chrome 的同源项目):Windows 上 ANGLE 把 GLES3 翻译成 D3D11,macOS 上翻译成 Metal,浏览器里各家 WebGL 实现自己就是翻译层。这意味着 Compatibility 的 bug 面是两层翻译的复合——同样一个 shader,在原生 GLES3 驱动上正常,过 ANGLE 转 D3D 可能因为 varying 打包规则差异挂掉。Godot 的 CI 对 Web 导出跑的是 Chromium headless,恰好也是 ANGLE 路径,所以这类问题大多在合入前被拦住,但冷门组合(Firefox + 老核显)仍偶有漏网。

定位一图流

Compatibility 的定位:能力地板之下的保底路径 RD 系(Forward+/Mobile) Vulkan 1.0 能力以上 Compatibility(GLES3) WebGL2 / 老驱动保底 Godot 3.x 设备面 GLES2/3 时代的存量 不走红抽象的原因:RD 的抽象面在 WebGL2 上无法无损映射 维护成本:两套渲染器并行演进;收益:浏览器与核显全覆盖 降级静默发生:SDFGI 等 RD 系选项可勾无效,开发期就要用目标渲染器验证 ANGLE 复合翻译层:Windows→D3D11、macOS→Metal、浏览器→各家实现

实操结论

Compatibility 的正确用法是作为目标而不是过渡:如果你的发行目标里有 Web 或十年前的核显,第一天就切 Compatibility 开发,shader、光照、GI 全部按它的能力面设计,把"降级"从风险清单里划掉。反过来,如果你的目标只有桌面和当代手机,别让兼容性焦虑把你按在 GLES3 上——RD 系的两个渲染器才是 Godot 4 的主航道,Compatibility 三年内的演进明显慢于 RD 系,这是观察资源分配就能看出来的事实。

本文基于 Godot 4.x 源码:drivers/gles3/rasterizer_gles3.cpp 及同目录光栅器家族、servers/rendering/renderer_rd/ 对照;能力降级以 doc/classes/RenderingServer.xml 与官方 Web 导出文档为准。