viewer
rust2genshin_viewer 是一个 GPUI 写的节点图浏览器。作用
用法
.rlib 或中间产物 .ogia,在左栏双击一个节点图打开它,右栏就是那张图的调用约定,以及各种优化操作。- 打开文件:把
.rlib/.ogia拖进窗口,或在命令行给路径 - 打开一张图:在左栏双击它(单击只是选中)
- 看调用约定:右栏上半是这张函数图对外的接口
- 做优化:右栏下半有「自动整理」、「一键优化」等按钮,以及每条消除规则
.ogia 产出端就是 build-demo:cargo run -p build-demo --bin beyond-servertarget/genshin-unknown-beyond-server/release/librust2genshin_demo.rlib 里带一个 rust2genshin_demo.ogia entry(ar t 可以列出来)。把这个 rlib 拖进 viewer 即可。.ogia 只以 entry 形式存在于 rlib 里。命令行
cargo run -p rust2genshin-viewer -- <路径>cargo run,不能直接双击 target/debug/rust2genshin_viewer.exe —— 它依赖 rustc_driver-*.dll,那个 DLL 在 $(rustc --print sysroot)/lib/rustlib/<host>/lib/ 下,cargo 会把这个目录加进 DLL 搜索路径,直接跑不会。- 不带参数 —— 开一个空窗,等拖放
<路径>—— 启动时直接加载这个.ogia/.rlib--check <路径>—— 不开窗,把解析结果打到 stdout(每张图的节点数、边数、入口/返回下标)--emit-fixture <路径>—— 写一个合成的.ogia夹具后退出(给测试用的确定性输入)
--check。 它能区分「文件里没有图」和「图里没有节点」这两种看起来一样的现象。后者是正常的 —— librust2genshin_lib-*.rlib 里就有一张节点数为 0 的图(rust_begin_unwind),viewer 不该把它当错误。--check 确认文件本身对不对,再看窗口。优化
| 按钮 | 做什么 |
| 自动整理 | 按 core 的布局算法重排坐标。拖动结果会丢,可以 Ctrl+Z 撤回。 |
| 一键优化 | 只跑消除规则到不动点,不含展开 Fn 节点。 |
| 链接全部 | 把所有 IrNodeId::Fn 节点展开成内部图(右键「link」的批量版)。 |
| 循环改写 | 跑 compile::loops::lower_loops —— 把控制流里的环换成 native 的「有限循环」节点。 |
| lower | core 的完整降级流程(Optimizer::lower_ir):展开 Fn → 优化 → 改写环 → 展开列表 → 类型降到 wire 层。 |
eliminate_ 前缀),点一次 = 这条规则在整张图上过一遍。想逐步观察是哪一步改坏的,就一条条点;想一步到位点「一键优化」。Ctrl+Z 回到点击前。IrNodeId::Native,但反过来不成立 —— 点「一键优化」也可能把一张小图消到只剩一两个原生节点(真实产物里 add_n、borrow_read、pair_sum 就是这样),那种图并没有 lower 过,按钮必须还能点。标记跟着存档走,所以 Ctrl+Z 退回去之后按钮会自动恢复。调用约定
- 外层 —— 加载
.ogia那一刻的接口,冻结,永不改变。它代表调用方看到的那个复合节点:左边是参数、右边是返回值、控制流引脚在两侧最上面。参数名和返回值名标在盒子的外侧,并且只写名字 —— 类型要悬停在引脚或连线上才显示(悬停连线时给出两端各自的类型)。 - 内层 —— 当前状态,画成一个普通节点(和画布上那些一样),可以在外层里拖动,拖不出外层。点「一键优化」或「lower」之后它立刻跟着更新。
- 连到内层的出引脚 —— 这个返回值是函数体算出来的;
- 连到外层的入引脚 —— 函数原样把某个参数还回去(没做任何计算);
- 写成一个常数,贴在外层那个返回引脚的左边(盒子内侧,右缘贴引脚、文字往左展开)—— 返回值就是个字面量。常数是那个引脚的值,所以位置只跟外层引脚走,和内层有没有这个引脚无关。
lower 里的 compact_value_exports(core/src/compile/optimize.rs:617)会把没人读的值出口整根摘掉 —— 一个「直接返回常数」的函数正是这样,出口没有节点读,值只留在 decl.proxies_out 里。于是内层连那个出引脚都不剩,常数一度跟着整个消失。现在 FnInterface::orphan_rets(viewer/src/doc.rs)把那张表读出来,这种常数照样画,只是 conv::Constant::inner 为 None(只影响有没有连线,不影响位置)。交互
Ctrl+Z 该是文本编辑的撤销,不该去撤节点图。实现上是一套 gpui key context —— 画布和右栏各挂一次,任一持有焦点即满足;打开页签时会把焦点交给画布,所以打开后立刻能按快捷键。快捷键
| 快捷键 | 作用 | 生效范围 |
Ctrl+Z / Ctrl+Y | 撤销 / 重做 | 只在标签页区域(画布或右栏)有焦点时 |
Ctrl+W | 关闭当前页签 | 同上 |
Alt+← / Alt+→ | 后退 / 前进 | 全局 |
Ctrl+V / Esc | 粘贴到搜索框 / 清空搜索框 | 左栏搜索框有焦点时 |
Shift+左键 | 加选、取消选 | 画布 |
Ctrl+左键 | 跳转到定义 | 画布 |
前进 / 后退
Alt+←/Alt+→- 鼠标的前进 / 后退消息。在 gpui 里它们是
MouseButton::Navigate(Back|Forward),由 Windows 后端的WM_XBUTTONDOWN(XBUTTON1/2)映射而来(gpui/src/platform/windows/events.rs:513-514)。这条链接在窗口根节点上,整个窗口范围内生效,不依赖焦点。
布局
node::layout —— 和 .gia 编码前跑的是同一套,所以 viewer 看到的排列就是产物里的排列,两边不会各排各的。Doc 里每个节点的 position)。拖动直接改它,渲染和命中测试都从它读 —— 没有“渲染用这份、连线用那份”的平行副本,也就没有两边对不上的可能。健壮性
.ogia / .rlib,所以渲染和交互全程不 panic:所有下标访问都走 get,取不到就按原点 / 空值兜底。unwrap(撞上畸形节点就会炸),整段包在 catch_unwind 里 —— 失败时用点击前的字节把图整体回滚,状态栏说明原因,这一跳不进撤销栈。link / lower 会改动 Linker 的持久状态(已展开函数的缓存 / 资产),panic 时图能回滚,但那部分收不回来。这条路径就是 core 自己在跑的展开逻辑,panic 意味着 core 出错。构建
default-members 里(原因见设计):cargo build -p rust2genshin-viewer
cargo test -p rust2genshin-viewer