WebGL 下别从 GPU 读回网格
现象
同一个 Godot 4 项目,桌面浏览器里一切正常,iPhone 的微信里却一直停在"引擎初始化中"。
原因
首页在搭场景时,会多次调用 surface_get_arrays() 和 get_mesh_arrays() 读取网格顶点,用来合并网格、重新上色、生成图标面片。在 Compatibility 渲染器(也就是网页上用的 WebGL 2)下,引擎不在 CPU 上保留网格的副本,每次调用都要通过 glGetBufferSubData 把数据从显存读回来。
iPhone 上的微信默认走自己的一套 GL 路径,这种读回在那条路径上第一次就报错了。就算路径能用,这也是同步等待:首页一次加载要读几十到上百次,进对局还要再来一遍。
做法
- 只留一个入口。写了一个
MeshArrays.of(mesh, surface),返回和读回完全相同的数组,但数据只从 CPU 侧来。 - 自己建的网格:上传时顺手把数组记在网格的 meta 里。
- 内置的基本体(盒子、圆柱、球、胶囊等):照引擎的 C++ 源码在脚本里重新生成一遍,并且在同样的位置把数值舍入成单精度,保证和引擎生成的逐位一致。
- 导入的 glTF 模型:用无头模式离线烘焙一份数组库(无头模式下读回其实是 CPU 拷贝),运行时按"场景路径 + 节点路径"直接取。角色的多个部件也在离线时就合并好,合计 1.5 MB。
- 剩下的情况仍然允许读回,但会记一次错误并计数。测试跑完后只要计数不为零,整轮测试就算失败,以后谁再引入读回都会被立刻发现。
收获
画面逐像素不变,iPhone 上能进首页了,桌面上第一次进对局的卡顿也少了。最大的教训是:在 WebGL 上,从 GPU 读任何东西都要当成"很贵而且可能失败"来对待。需要的数据,从一开始就在 CPU 上留一份。