三维与渲染

Windows 桌面端开启 Flutter GPU 与 Impeller OpenGLES 投影阴影卡死定位

记录为 flutter_scene 配置 Windows 原生桌面 Runner(开启 Flutter GPU、规避 MSVC C4819),并通过最小特性探测页定位 Impeller OpenGLES 下 PBR 叠加投影光源首帧卡死的过程。

为了对比三维场景在浏览器与原生桌面端的运行表现,尝试基于 Flutter 3.47.9 为原本只有 Web 端的 flutter_scene(0.24.3)工程补上了 Windows 桌面 Runner。记录跑通构建时的两处配置细节,以及随后定位到的桌面端渲染卡死边界。

1. Windows Runner 开启 Flutter GPU 与编码注意

默认生成的 windows/runner/main.cpp 直接运行 flutter_scene 会报错提示 Flutter GPU 未启用。需要在创建 flutter::DartProject 之后显式打开开关(需 Flutter 3.47.1+):

flutter::DartProject project(L"data");
// Enable Flutter GPU (Impeller) on desktop runner.
project.set_enable_flutter_gpu(true);

顺带需要注意 MSVC 编译器的默认编码检查:Windows Runner 模板默认开启了 /WX(将警告视为错误)且未默认指定 /utf-8,在中文 Windows(代码页 936)下往 main.cpp 写入包含特定字符的 UTF-8 中文注释会触发 warning C4819 并直接中断构建,Runner 侧注释保持纯英文最稳妥。此外桌面 GUI 程序没有浏览器地址栏查询串,Uri.base 返回的是工作目录路径,调试参数可以统一包一层读取层,在桌面端改为从环境变量(如 SCENE_ARGS)解析同名键值对。

2. 首帧停滞现象与最小特性探测矩阵

构建出 Windows Release 可执行文件启动后,引擎日志显示当前 Windows 端 Impeller 走的是 OpenGL ES 后端(Using the Impeller rendering backend (OpenGLESSDF))。场景资源加载与静态网格合批日志均正常输出,但随后界面一直卡在加载遮罩上不再出帧,连 3 秒一次的 Dart 定时器也不再触发;切换到只用 UnlitMaterial 的简单几何测试页,又能以约 1.4 ms/帧 的光栅耗时流畅旋转。

为了排除复杂模型或贴图本身的影响,单独写了一个只放一枚立方体与一片地面的最小探测页(GpuProbePage),通过参数逐项组合开启材质、光源、阴影与后期特性,逐组运行对比结果如下:

特性参数组合(features=...) Windows Release(Impeller OpenGLES)表现
none / pbr / pbr,sun(不投射阴影) 正常连续出帧
pbr,ao / pbr,bloom / pbr,godrays,sun / pbr,area / pbr,fog 正常连续出帧
pbr,fxaa / pbr,taa / pbr,msaa 正常连续出帧
sun,shadow(UnlitMaterial + 平行光阴影) 正常连续出帧
pbr,sun,shadow / pbr,spot,shadow 首帧卡死
pbr,sun,shadow,s512 / pbr,sun,shadow,pcf 首帧卡死(与分辨率及过滤算法无关)
pbr,sun,shadow,fixed / pbr,sun,shadow,nocache 仅出一两帧后停滞

3. 定位结论与当前处理

通过对照表可以缩小问题范围:在当前版本的 Windows Impeller OpenGLES 路径下,单独使用 PBR 材质、单独为 Unlit 材质开阴影、或者开齐各类屏幕空间后期通道都正常;一旦 PhysicallyBasedMaterial 与任意开启 castsShadow: true 的投影光源(平行光或聚光灯)同时存在,就会在渲染线程触发管线停滞,与场景复杂度、阴影分辨率和静态阴影缓存开关均无关系。

目前如果要在 Windows 桌面端运行同一套场景代码做逻辑调试或基础帧率对照,需先在平台分支里临时将桌面端光源的 castsShadow 置为 false 绕开该路径;features=pbr,sun,shadow 这组最小复现页已保留在工程中,留待后续引擎升级或切向桌面 Vulkan 后端时直接复测。