- 删除已完成使命的 UI_Refactor_Plan/Progress/Report(已在 dev 提交 764e5be)。 - 新增 Documents/Code_Review_Plan.md:结构 + 性能 + 体积的节点化计划, 含已确认的问题事实(dynamic_cast 在最小复现里 +75,264 B、 cp-demangle.o 56,320 B、每个 EdgePanelKind 出现 21-26 次等)。 - 新增 Documents/Code_Review_Progress.md:基线数字与节点记录。
11 KiB
11 KiB
Code Review 计划(分支 Code_Review)
目标:在不引入性能回归、不改变用户可见行为的前提下,对全项目做一次结构 + 性能 + 体积 的查漏补缺,并把「改一个功能要动 40 处」的屎山点收敛成表驱动/单一实现。
基线(本计划开始时):
- 分支
Code_Review,起点764e5be(dev 已删除过时的UI_Refactor_*三份文档)。make release产物 890,880 B(.text753,136 B、.rdata36,936 B、.rsrc20,656 B、.pdata30,420 B、.xdata29,496 B、.idata12,856 B)。- 源码约 5.6 万行;最大单文件
PlotViewPaint.cpp6,561 行、PlotView.hpp4,654 行。约束:
- 每完成一个节点编译验证(release + debug 零警告)并提交,同时更新
Documents/Code_Review_Progress.md。- 性能相关节点必须给出同一次运行内的相对量(历史教训:绝对毫秒阈值不可靠)。
- 不占用用户的鼠标/键盘/屏幕(只用纯控制台探针或源码级核对)。
一、已确认的问题(本计划的事实基础)
A. 体积
| 项 | 实测 | 说明 |
|---|---|---|
dynamic_cast(Toolbar.cpp:370) |
+75,264 B | 最小复现程序:39,936 B → 115,200 B。它会链入 RTTI 元数据 + cp-demangle.o(map 里 56,320 B)。而该处 Button::widget 本来就是 UiButtonWidget* ⇒ 转换完全多余。 |
-fno-rtti |
未启用 | 移除上一条后即可开启,进一步去掉 type_info 表。 |
.pdata + .xdata |
60 KB | -fno-exceptions 已开;SEH 展开表仍存在。可尝试 -fno-asynchronous-unwind-tables(历史上链接失败,需复核)。 |
libmingwex 的 strtodg/gdtoa/mingw_wpformat |
27.8 KB | 来自 wcstod / wprintf 类调用;可评估用自写解析替代 wcstod 的收益(公式/输入框解析)。 |
.rsrc |
20.7 KB | 图标(PNG 帧)+ manifest + 版本信息,已优化过。 |
| 重复常量/重复实现 | 多处 | kFormulaEnvelopeBuckets 已清理;仍有 ClampDouble 两份实现等。 |
B. 性能(结构性热点)
- 面板开关/参数处理是 6 个
EdgePanelKind× ~20–26 处 switch 分支(实测每个 kind 出现 21–26 次,全部由手写if/switch维护)。这是新增功能必然漏改 2–3 处的根本原因 (历史已发生 4 次:MARK漏拖动、RESAMPLE漏拖动+关闭、CLIP漏滚轮+缓存键)。 SceneContentHash里 6 段几乎相同的逐面板混入循环(PlotViewCacheKey.cpp)。EdgePanelLayoutFor是巨型分支,EdgePanelLayout结构体带 20+ 个按面板类型区分的 矩形数组;高度算式与几何算式分开写(历史上多减一个 pad 造成空白空白 BUG)。PlotView有 30+ 个缓存,每个都手写「键 + DC + 位图 + 贴图 + 失效」五件套; 失效路径散落在 8 个文件里,m_sceneValid=false出现 6 处。ApplicationCommands.cpp2,165 行混杂:标签页、数据派生、菜单、工具栏、键盘、鼠标。PlotView.hpp4,654 行(声明 + 内联实现),单文件认知负担极大。
C. 过时/错误注释与命名(抽样已确认)
agent_mem.md§3「目录结构」仍写着已不存在的Utilities/CsvTable/、Utilities/DataModel/等目录;§2 写-Os而实际是-Oz。- 源码中留有历史轮次的「本轮」「上一轮」叙述(对当前读者无意义),以及被推翻方案的注释。
PlotViewInternal.hpp里kFormulaEnvelope*注释已随实现迁移,需同步。
二、节点计划
优先级:S(安全、收益明确、风险低)→ A(结构调整,中风险,需逐步验证)→ B(深度重构,必须有可验证的前后一致性)。
阶段 0:建立基线(节点 P0)
- P0.1 记录基线:release/debug 体积、
.text/.rdata等节表、关键路径的可复跑测量程序 (纯控制台,链接Build/Release/*.o,不入库)。已完成基线的部分见下表。 - P0.2 建立
Documents/Code_Review_Progress.md,每节点完成后追加一行结论 + 数字。
阶段 1:体积(节点 P1–P5,安全优先)
- P1 移除
dynamic_cast+ 开启-fno-rtti- 把
Toolbar::BuildBoxes的dynamic_cast<const UiButtonWidget*>改为直接用Button::widget(已是该类型)或给UiWidget加虚函数ContentBounds()。 - Makefile
CXXFLAGS += -fno-rtti;全仓确认无typeid/dynamic_cast/type_info。 - 预期 −70~80 KB。验收:产物下降 ≥60 KB;功能逐项核对(工具栏按钮、布局边界调试)。
- 把
- P2 体积审计第二轮
- 复核
-fno-asynchronous-unwind-tables(历史上链接失败,试-fno-unwind-tables组合)。 - 评估
wcstod替身(自写定点/浮点解析,仅在公式与输入框使用)去掉strtodg/gdtoa/gethex/hexnan(约 20 KB)。 - 评估
--gc-sections在 LTO 下是否已生效(读 map 确认无死节)。 - 验收:逐项给出「测得的字节数 + 是否采纳」的结论,不接受「感觉变小」。
- 复核
- P3 重复实现归并
ClampDouble两份(PlotViewInternal.hpp/UiLayout.cpp)→ 统一到Shared。- 扫描全部
static/匿名命名空间重复工具函数,合并为单一实现。 - 重复字符串/常量字面量、重复的三元式开关表。
- P4 死代码与未使用成员清理
- 用非 LTO 对象 +
nm反查「定义但从未被引用」的函数;删除确认无引用的实现。 - 清理
PlotView.hpp中已无人读取的成员/字段。
- 用非 LTO 对象 +
- P5 资源与启动数据
.rsrc复核(图标帧数、manifest、版本字符串);.idata导入表复核。
阶段 2:性能(节点 P6–P10)
- P6 建立可复跑的渲染性能探针(纯控制台)
- 目标:能在不弹窗口的情况下测量
BuildLaneVisuals/SceneContentHash/EdgePanelLayoutFor/ 面板绘制 / 信号面板绘制的耗时。 - 方式:链接
Build/Release/*.o+ 手工构造DataSet/TabConfig,只调用纯函数与PlotView的非窗口方法(避免Application::Initialize触发模态框,见历史教训)。 - 所有断言用同一进程内的相对量。
- 目标:能在不弹窗口的情况下测量
- P7 面板元数据表驱动(核心)
- 新增
EdgePanelSpec(每 kind 一行:命令 id、默认尺寸、是否列表型、footer 类型、 滚动字段偏移、面板状态字段偏移、绘制函数指针、命中函数指针)。 - 把 6×~20 处
switch (kind)收敛为「查表 + 3 个通用实现(列表型 / 表格型 / 固定行型)」。 - 验收:逐像素对比改造前后面板(离屏渲染哈希);新增一个假 kind 只需加一行表项即可 被绘制/命中/拖动/关闭/滚动全覆盖(用编译期断言或探针验证)。
- 新增
- P8
SceneContentHash混入收敛- 把 6 段重复循环改为「按面板表遍历 + 每面板一个
MixContent()」。 - 验收:改造前后哈希值逐位相同(同一状态)。
- 把 6 段重复循环改为「按面板表遍历 + 每面板一个
- P9
EdgePanelLayoutFor表驱动 + 高度/几何同源- 高度算式与内容几何共用同一份
PanelMetrics计算(消除重复减 pad 类 BUG)。 - 验收:探针打印「内容区下边界 vs 按钮上边界」的像素差,与改造前一致。
- 高度算式与内容几何共用同一份
- P10 缓存五件套抽象(低风险版)
- 抽出
OffscreenCache(DC/位图/键/贴图/失效),只替换三处结构完全相同的 (derived / decor / panel),保留 scene 与 lane layer 不动(热路径最复杂)。 - 验收:逐像素一致 + 帧时间不回退。
- 抽出
阶段 3:架构(节点 P11–P14)
- P11
ApplicationCommands.cpp拆分- →
ApplicationTabs.cpp(标签页)/ApplicationDerive.cpp(派生/重采样/截取/克隆)/ApplicationCommands.cpp(命令分发)/ApplicationMenus.cpp(上下文菜单与设置菜单)。 - 纯搬移,零行为变更;Makefile 无需改动(按目录通配收集)。
- →
- P12
PlotView.hpp拆分- 把
EdgePanelLayout、派生曲线缓存条目、图层结构体等纯数据结构搬到PlotViewTypes.hpp;把PlotView的内联小函数按关注点分块放到PlotViewInline.hpp。 - 目标:
PlotView.hpp≤ 2,500 行。
- 把
- P13 UI 基础层职责核对
- 核对
UiWidget/UiComponent/UiLayout/UiBox/UiSurface/UiButtonWidget/UiControls的边界是否重叠;合并确认冗余的(只保留一套矩形/命中/布局原语)。 - 明确依赖方向:
Components/*→Components/Shared→Utilities(校验无反向)。
- 核对
- P14 绘制/功能组件边界
PlotViewPaint/Raster/Panels/Features/Interaction的职责再核对; 把「面板几何」与「面板绘制」的交叉依赖理顺(几何 → 共享PanelMetrics)。- 把散落在
PlotView里的「颜色/线宽/胶囊样式」常量收敛到Theme/PaintUtil。
阶段 4:注释与命名清理(节点 P15)
- P15 注释/命名审计
- 删除「本轮/上一轮」叙述、被推翻方案的描述、与实现不符的注释。
- 统一命名:
*Dc/*DC、Lane*vsGrid*、Edge*vsPanel*等不一致处。 - 确保所有注释与当前实现一致(重点:
PlotViewInternal.hpp、PlotViewCacheKey.cpp)。 - 同步更新
agent_mem.md的 §2/§3(已经不准确的目录结构与-Os)。
阶段 5:验证与报告(节点 P16)
- P16 最终验证与报告
- release + debug 零警告;产物体积对比;关键路径探针全部通过。
- 生成
Documents/Code_Review_Report.md:逐节点写「改了什么 / 为什么 / 效果 / 未做的事与原因」,并给出体积与性能的前后对照表。
三、验收标准(硬性)
make release与make debug零 error、零 warning。- 产物体积必须下降(目标:从 890,880 B 降到 ≤ 780 KB;若某项无法达成,在报告里 说明实测字节数与原因)。
- 不改变任何用户可见行为(面板几何、绘制像素、交互语义一致)。
- 每条性能结论都必须有同进程相对量或逐像素/逐位一致性证据。
- 每个节点单独提交,提交信息用中文、说明改动与验证方式。
四、风险与对策
| 风险 | 对策 |
|---|---|
| 面板表驱动改造引入像素差异 | 改造前后用离屏渲染哈希对比;先改 CLIP/MARK 两个最简单面板做样板,再推广 |
-fno-rtti 破坏第三方/库代码 |
全仓 grep 确认;先单独提交,失败可独立回退 |
| 缓存抽象拖慢热路径 | 只抽象非热路径(derived/decor/panel),scene/lane 不动;每次改完测帧时间 |
| 大文件拆分时误改逻辑 | 纯搬移(不重写函数体),逐文件 diff 核对;先拆 ApplicationCommands |
| 探针触发模态「内存不足」框 | 绝不在探针里调 Application::Initialize()(历史教训);只构造数据层/纯函数 |
五、进度索引
进度与每节点的实测数字见 Documents/Code_Review_Progress.md。
最终结论见 Documents/Code_Review_Report.md。