Blender 的 UI 抽成库后:跑同一份 gpui 代码,首帧快 5 倍
同一份 gpui 源码,编译三次:BUI 首帧 40 ms、gpui 231 ms、gpui-fast 226 ms。
上周我们发了一篇对 GPUI 的批评,说它的状态模型按树付费。这周有个更好玩的东西出现:有人把 Blender 的界面从源码树里拆出来,做成了一个库,然后拿同一份 gpui 语法的源码编译了三遍。
结果:首帧 40 毫秒对 gpui 的 231 毫秒、gpui-fast 的 226 毫秒;启动消耗 CPU 27 毫秒对 226 / 217;闲置时 gpui 每分钟烧 743 毫秒 CPU,BUI 是 0。
项目叫 BUI(Blender's UI),GPL-3.0,ppmpreetham/BUI,两天内 2.4k star。
一、它是什么
一句话:Blender 的用户界面,作为一个库,可以从 Python、Rust、JavaScript(React、Svelte)、C、C++ 和 Dear ImGui 里用。
README 的说法很直接:人们花了二十年构建并优化 Blender 的 GUI,它省电、省内存、非常快——"我们把它解耦出来,让每个人都有机会用它做自己的产品。"
它同时发布了几个包:pip install blenderui、cargo add blenderui、npm i @blender-ui/react,Windows / Linux / macOS 三平台。渲染器有两套:默认的 CPU 软件渲染,和可选的 GPU 渲染(BUI_GPU=1)。
它给不同语言准备了不同的"语法皮":Python 侧可以写 tkinter 风格、Qt 风格、Blender 组件风格;Rust 侧可以写 rsx!、view!,也可以写 gpui 风格的 builder 链(div().flex().gap_2().child(...)、Render、cx.listener)。
二、为什么这件事值得看
因为它绕开了"重写一个 GUI 框架"这件事。
大多数 Rust GUI 框架是在重新发明一套布局、绘制、字体、输入法、可访问性。Blender 的界面是已经在几十万行 C 代码里跑了二十年的东西:它有自己的一套 widget、自己的一套交互(拖数字、框选、右键菜单)、自己的一套主题系统。
BUI 的做法是把这一层"抠"出来——保留 widget 与交互,把渲染与宿主窗口重新接。于是你可以得到"Blender 的手感",但写的是 Python 或 Rust。
对做工具的人来说,这个诱惑很具体:你要的不是另一个框架,你要的是"看起来不像 Electron"。
三、基准一:启动与闲置
同一份小设置窗口(标签、输入框、复选框、滑块、按钮),五种写法,加上同一个 gpui 语法源文件 settings.rs 分别编译到 BUI、gpui、gpui-fast。10 次启动的中位数,从进程启动到第一帧上屏,Python 3.14 / Node 22:
| 写法 | 渲染器 | 启动 | 启动耗 CPU | 闲置内存 | 闲置每分钟 CPU |
|---|---|---|---|---|---|
| Rust(HTML + CSS) | CPU | 38 ms | 27 ms | 13 MB | 0 ms |
| Rust(HTML + CSS) | GPU | 42 ms | 61 ms | 31 MB | 1.8 ms |
| Blender 组件(Python) | CPU | 112 ms | 86 ms | 23 MB | 0 ms |
| tkinter 语法(Python) | CPU | 139 ms | 112 ms | 25 MB | 0.2 ms |
| 同一份 gpui 代码:BUI | CPU | 40 ms | 27 ms | 15 MB | 0 ms |
| 同一份 gpui 代码:BUI | GPU | 43 ms | 60 ms | 31 MB | 2.1 ms |
| 同一份 gpui 代码:gpui | GPU | 231 ms | 226 ms | 29 MB | 743 ms |
| 同一份 gpui 代码:gpui-fast | GPU | 226 ms | 217 ms | 31 MB | 2.5 ms |
原文的总结是:同一份代码,BUI 的首帧比 gpui 与 gpui-fast 早约 5 倍,到达那里的 CPU 少约 8 倍,在 CPU 渲染器上内存只有一半;闲置时 gpui 持续占用约 1.2% 的一个核,BUI 是 0。
笔记本测不了"每个 app 的能耗",所以它用"app 引发的 CPU 时间"来代理:启动时消耗的 CPU + 窗口闲置 20 秒内消耗的 CPU。BUI 只在有变化时重绘,其余时间睡——所以两套渲染器的闲置占用都接近零。
四、基准二:帧时间
Rust 侧,场景是 3,000 个动画格子,所有 app 在同一会话内跑(GPU 行用 Direct3D 11):
| BUI(GPU) | BUI(CPU) | gpui-fast | gpui | |
|---|---|---|---|---|
| FPS | 59.9 | 57.6 | 31.5 | 19.9 |
| 每帧 CPU | 11.6 ms | 22.0 ms | 31.9 ms + GPU | 50.5 ms + GPU |
| 内存 | 40 MB | 30 MB | 65 MB | 77 MB |
| 首帧 | ~5 ms | ~5 ms | ~200 ms | ~220 ms |
| 闲置 CPU | 0 % | 0–0.9 % | 0–0.4 % | 0.9–1.4 % |
更大的场景里差距更明显:1,000 个动画格子时,BUI 两套渲染器都跑满 60 FPS,gpui-fast 49.2、gpui 36.4;不封顶的话,BUI 在 100 格能到 1,491 FPS、1,000 格 309、3,000 格 94。
Python 与 C 的完整组件 demo(520×900):整帧重绘约 0.7 ms,悬停或拖数字这类"只画损坏区"的帧约 0.05 ms,原生窗口从输入事件到上屏约 0.06 ms。对照:Dear ImGui 的 demo 窗口全部展开(27k 三角形,1280×800)一次软件全重绘约 5–8 ms。
五、它快在哪
作者给了四条实现上的原因,都是常规手段,但组合得干净:
- GPU 渲染器里,每个图元都是同一个 shader 的一个实例,所以一帧只是几次实例化绘制(Windows 上是 Direct3D 11,Linux 上是 OpenGL 3.3)。
- 文字与图片缓存在图集(atlas)里。
- 帧渲染到一个持续存在的目标上,只有损坏区域重绘——这就是上面那个 0.05 ms 的悬停帧。
- Windows 上用 flip-model 交换链 + dirty rect 呈现。
它还做了一个很硬的正确性检查:GPU 着色器"求值软件光栅器的公式",结果在 172,800 个像素里有 10 个差异超过 3/255,两套后端一致。
六、三条必须写出来的限制
- 数字是"某个时间点"的。 原文写得很清楚:这些数据在 Ryzen 7 5800U 笔记本(Windows 11、插电)上测得,而且早于 Dear ImGui、Linux GPU 渲染器、亮色主题、Blender 搜索与表达式这些特性落地。"它们展示的是事情的形状,不是对你机器的承诺。"
- gpui 那边不算完全对等。 gpui 没有表单控件,所以基准里它的控件是带样式的 div;而 BUI 用的是真的 widget。"同一份代码"指的是源码形态一致,控件语义并不相同。
- gpui-fast 的数字在同一台机器上会波动(重场景 30–60 FPS),所以只能在同一个会话内比。
还有一条不在基准里、但更要紧的:GPL-3.0-or-later。 你想把它嵌进闭源商业产品,这就是个真问题——Blender 的 UI 便宜,是因为它一直是 GPL 的。
七、我们的整理
上周那篇批评说的是状态模型:GPUI 的 notify 只精确到实体,成本跟着树走。BUI 证明的是引擎层:把布局、绘制、字体、损坏区重绘做到位,同一份代码的首帧和帧时间能差 5 倍、闲置 CPU 能差到 0。
这两件事不矛盾,也不能互相替代。
- BUI 的快,主要来自"只画变了的东西"和"绘制不经过整棵树的重建"——那是引擎与呈现的功劳。
- 上周那篇说的是"一次状态变更会触发多少应用代码重跑"——那是状态模型的问题,BUI 没有回答它(它给的是一套 widget 与事件接口,不是 signal 图)。
- 所以正确的读法是:同一个状态模型,换一个好引擎,常数能降一个数量级;但渐近行为还是由状态模型决定。 3,000 行时差 5 倍,30,000 行时谁先撞墙,得另测。
三条实用判断:
- 你要的是"不像 Electron 的桌面工具",而且能接受 GPL —— BUI 现在是这个需求下最省事的一条路,Python 就能写,启动 112 ms。
- 你要的是"大量自定义绘制 + 高频刷新"(示波器、波形、3D 视口叠加 2D 面板)——BUI 的 GPU 渲染器 + 损坏区重绘正好对症,而且它自带 Blender 的控件手感。
- 你已经在 GPUI 上、且痛点不在绘制而在重渲染 —— 换成 BUI 不会自动解决,得先按上周那篇的判据量一遍:你的列表有多长、一次改动会触发多少 view 重建。
边界:基准是作者自己跑的,测试代码与原始数据在 bindings/rust/bench/ 里公开可复跑;但设备单一(一台 5800U 笔记本)、时间点偏早(部分特性未落地)、gpui 侧控件不等价。方向可信,绝对值别直接抄进你的选型 PPT。
来源
ppmpreetham/BUI(GPL-3.0-or-later,2.4k★):https://github.com/ppmpreetham/BUI;文档站 https://bui.preetham.top- 基准的测试代码与说明:仓库
bindings/rust/bench/(含settings.rs这份被编译三次的同一份源码) - 本文按 README 的「What BUI is / Why BUI exists / Performance(§30–32)/ 渲染实现」几章翻译,删去安装、逐语言教程、API 参考等章节;所有数字与限制均取自原文
- 前一篇(对 GPUI 的批评):对 GPUI 的批评:它显得快,是因为 Zed 的界面太简单