Skip to content

加速帧交接 ​

CefTexture 和 CefTexture2D 在 RenderingServer.frame_pre_draw 发布浏览器纹理。 该信号在主线程执行,通过 call_on_render_thread 安排发布工作,本身不提供 GPU 完成保证。 浏览器生命周期、CEF 消息泵、事件和尺寸更新仍由 process 回调推进。

所有权与完成条件 ​

每个浏览器的 view 和 popup 使用独立的流,每条流有三个持久快照槽位。 调整尺寸期间最多保留两代资源;尺寸与 BGRA/RGBA 格式共同确定资源代际。 没有空闲槽位时,在读取 CEF 借用资源之前跳过捕获,并保留最后显示的帧。 跳过的绘制会由主线程重新请求 view/popup 绘制,直到捕获成功,确保静态页面也能在 初始化、尺寸变化或槽位耗尽后恢复。

  1. RD 创建并初始化 staging,将一个像素复制到每槽位独立的 sentinel,然后异步读回。 读回完成后才能使用槽位,此时 Godot 与原生后端都处于已知的复制源状态。 空资源通过一次性全零上传初始化,不要求 sRGB 渲染目标或 UAV 清空能力;浏览器帧始终留在 GPU。
  2. OnAcceleratedPaint 打开当前借用源,复制到空闲槽位,恢复复制源状态,等待所有 对借用源的 GPU 访问结束后才返回。不使用 HANDLE、IOSurface 或 DMA-BUF 标识缓存借用帧。
  3. 绘制前,渲染线程选择最新完成的快照,记录 RD.texture_copy(staging, display), 由 Godot 跟踪显示纹理的复制与采样依赖。
  4. 从 display 复制一个像素到该槽位的 sentinel,再执行 texture_get_data_async(sentinel)。只有匹配资源代际与使用序号的完成回调才能释放槽位。

直接从 staging 复制 sentinel 无法保证发布复制的完成顺序。帧计数、frame_post_draw、 CPU 调用结束和超时都不能作为 GPU 完成证据。无法证明完成的资源会被隔离,直到设备销毁。 新显示纹理绑定后才回收旧资源,尚未完成的回调持有各自资源代际。

原生后端契约 ​

后端捕获与交接
Windows D3D12回调内直接通过 D3D12 打开 CEF 共享句柄,在自有队列捕获,通过 COMMON 状态获取并释放源。CPU 已观察到的槽位完成证明先前的 Godot 读取已经结束;捕获 fence 约束 Godot 队列上的发布顺序,并保证回调返回前结束借用源访问。保留 RD 的复制源状态,增强屏障与旧式屏障通过 COMMON 交接。
Windows Vulkan每次回调重新导入 Win32 外部内存,优先使用与 Godot 同族且已预留的私有队列,无可用预留时沿用 Godot 队列。获取并释放外部图像所有权,恢复 staging 的 TRANSFER_SRC_OPTIMAL 状态,等待原生复制 fence 后才返回。Godot 队列上的信号量等待保证私有队列捕获先于发布。
Linux Vulkan导入当前 DMA-BUF 平面与 DRM modifier,通过 sync-file 信号量等待生产者(旧内核使用原生 DMA-BUF fence 轮询),获取并释放 foreign 队列所有权,在回调返回前完成复制。
macOS Metal打开当前 IOSurface,在 Godot 自身的线程安全 MTLCommandQueue 上复制到启用 hazard tracking 的 staging;同队列资源依赖保证可见性,waitUntilCompleted 保证借用源不再被访问。

Vulkan 钩子串行化实际队列的 CPU 访问,覆盖 Godot 后台传输工作。 初始化会验证设备与队列的来源;仅在渲染线程执行不能保证队列独占访问。 驱动会缓存队列包装函数的地址,因此其模块在进程生命周期内保持加载;重建扩展后需要重启 Godot。

Windows 的设备创建钩子可在物理队列族有剩余容量时追加一条队列,保持 Godot 原有索引和优先级不变。 只有成功创建记录与队列来源均确认后,导入器才使用私有队列;未预留时沿用已有的共享队列路径。 私有捕获发出信号量,Godot 队列通过空提交在 transfer 阶段等待,再执行后续 RD 复制。 独立 fence 负责回收这次等待;正常私有捕获路径不会同步等待这个 Godot 队列 fence。 Linux 继续使用 Godot 实际队列捕获。

Godot 4.6+ 直接返回原生 D3D12 纹理与命令队列。导入器在提交捕获工作前校验队列所属设备。 空闲 D3D12 槽位已有初始化或上次发布的完成证明。若再把新的 Godot 到捕获队列 fence 排在无关绘制工作之后,会使捕获产生多余等待,并可能在 VSync 下拖低浏览器帧率。 捕获队列到 Godot 队列的 fence 仍然保留。

弹出菜单展现 ​

CefTexture 通过子 overlay 显示加速 popup,可延伸到节点边界之外,仍遵守祖先节点的常规裁剪。 CefTexture2D 保持公开 RenderingServer 纹理代理不变:平时指向原生 view, popup 可见时指向合成 view 与 popup 的 GPU canvas viewport。 该画布使用预乘 alpha,并将 popup 裁剪在纹理边界内;打开、隐藏或调整 popup 尺寸不会替换公开纹理 RID。 合成直接使用 GPU 纹理,不进行 CPU 读回,也不切换到软件渲染。

错误处理与验证 ​

浏览器初始化保留已有的软件回退行为:加速不可用或无法创建原生导入器时,回退到软件渲染并记录原因。 设置 enable_accelerated_osr = false 同样使用软件渲染。

加速渲染启动后,捕获或发布失败时保留最后成功发布的帧; 未能确认 GPU 工作完成的资源会被隔离,直到设备销毁。

图形像素与生命周期测试见 tests/rendering。 测试验证请求的渲染器与线程模式。加速测试用例拒绝启动回退,防止软件渲染像素使 GPU 测试误判通过; 引擎错误、过期像素序号、崩溃或超时同样导致失败。 无界面测试只验证运行时推进,不能验证 GPU 共享与实际显示像素。

pacing 场景开启 VSync,预热 3 秒,再测量 20 秒的 CEF rAF 与 Godot 绘制节奏, 测量期间不读回 viewport。它检查持续帧率及最后 10 秒的帧率,随后保留活跃浏览器退出。 popup 场景在跨站重定向后发送原生鼠标与键盘输入,验证焦点保持、菜单打开与选择、 重新打开与关闭、popup 边界变化、纹理标识稳定,以及颜色与 alpha 保持。

最低支持 Godot 4.6。编译通过不能代替各操作系统、GPU 驱动与 Godot 版本上的实机验证。 此项工作继续推进 #227。