> ## Content Index
> Fetch the complete content index at: https://www.hubwiz.com/blog/llms.txt
> Use this file to discover other available public pages before exploring further.

# Blender 的 UI 抽成库后：跑同一份 gpui 代码，首帧快 5 倍
- URL: https://www.hubwiz.com/blog/bui-blender-ui-library-vs-gpui/
- Published: 2026-11-10T10:32:38.000Z
- Updated: 2026-11-10T10:32:38.000Z
- Description: 同一份 gpui 源码，编译三次：BUI 首帧 40 ms、gpui 231 ms、gpui-fast 226 ms。
- Author: admin
- Tags: Rust, GUI, Blender, 性能, 开源

上周我们发了一篇对 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，两套后端一致。

## 六、三条必须写出来的限制

1. **数字是"某个时间点"的。** 原文写得很清楚：这些数据在 Ryzen 7 5800U 笔记本（Windows 11、插电）上测得，**而且早于 Dear ImGui、Linux GPU 渲染器、亮色主题、Blender 搜索与表达式这些特性落地**。"它们展示的是事情的形状，不是对你机器的承诺。"
2. **gpui 那边不算完全对等。** gpui 没有表单控件，所以基准里它的控件是**带样式的 div**；而 BUI 用的是真的 widget。"同一份代码"指的是源码形态一致，控件语义并不相同。
3. **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 行时谁先撞墙，得另测。

三条实用判断：

1. **你要的是"不像 Electron 的桌面工具"**，而且能接受 GPL —— BUI 现在是这个需求下最省事的一条路，Python 就能写，启动 112 ms。
2. **你要的是"大量自定义绘制 + 高频刷新"**（示波器、波形、3D 视口叠加 2D 面板）——BUI 的 GPU 渲染器 + 损坏区重绘正好对症，而且它自带 Blender 的控件手感。
3. **你已经在 GPUI 上、且痛点不在绘制而在重渲染** —— 换成 BUI 不会自动解决，得先按上周那篇的判据量一遍：你的列表有多长、一次改动会触发多少 view 重建。

**边界**：基准是作者自己跑的，测试代码与原始数据在 `bindings/rust/bench/` 里公开可复跑；但设备单一（一台 5800U 笔记本）、时间点偏早（部分特性未落地）、gpui 侧控件不等价。**方向可信，绝对值别直接抄进你的选型 PPT。**

---

**来源**

- `ppmpreetham/BUI`（GPL-3.0-or-later，2.4k★）：[https://github.com/ppmpreetham/BUI](https://github.com/ppmpreetham/BUI?ref=hubwiz.com)；文档站 [https://bui.preetham.top](https://bui.preetham.top/?ref=hubwiz.com)
- 基准的测试代码与说明：仓库 `bindings/rust/bench/`（含 `settings.rs` 这份被编译三次的同一份源码）
- 本文按 README 的「What BUI is / Why BUI exists / Performance（§30–32）/ 渲染实现」几章翻译，删去安装、逐语言教程、API 参考等章节；所有数字与限制均取自原文
- 前一篇（对 GPUI 的批评）：[对 GPUI 的批评：它显得快，是因为 Zed 的界面太简单](https://www.hubwiz.com/blog/gpui-notify-is-zonejs-critique/)