平移改为复用已绘制像素:新增宽缓冲区,平移动画降为 O(1) 贴图

按用户分析落地:平移时画面内容不变、只是横向平移,因此以当前视界为中心向左右各多渲染半个视界宽,存成 2×视界宽的离屏位图(用与正常绘制完全相同的 PaintLane/PaintTimeAxis,仅跳过随视界移动的图例/Y 轴刻度,改由装饰层缓存贴回)。平移时前台只做一次 BitBlt 截取子矩形(O(1)),移出边界才重建一次。

实测 VERY_BIG_DATA 全信号、视界 [40.1756,40.1885]s、5 网格:中键平移拖动 34ms→1.3ms(长距 1.7ms),透明度/粗细/颜色 16ms→8ms;静止/结算帧与修复前逐像素 0 差异(LockBits 验证)。
This commit is contained in:
DeepSeek V4.1 Flash committed 2026-10-11 22:36:42 +08:00
1 parent aefaaef6fa
commit fa0d62b016
3 files changed
+282 -1

No files matched your search

+21
View File
@@ -283,6 +283,26 @@ Documents/ 项目文档(含本文件)
- **结果(同一负载机器)**:透明度/粗细/颜色 **77ms → 16ms**;弱波增强滑块 **96–176ms → 26ms**;平移拖动 **34ms**(快速帧,空闲机 ≈8ms)。全质量/结算帧与修复前**逐像素 0 差异**(`LockBits` 对照验证)。
- **仍存在的固有下限**:整幅重绘中的**包络带 `FillPath`** 本身是 O(像素列数) 的 GDI+ 抗锯齿填充(≈29ms/网格/负载)。它只在"整幅重绘"或"结算帧"出现(平移快速帧已关抗锯齿 ≈5.4ms/网格)。若要在密集包络带上也关抗锯齿可再快 5×,但会轻微改变边缘观感,**需用户确认后再动**。
### 9.10 平移「宽缓冲区」:让平移的时间复杂度降为 O(1)(用户方案落地)
- **用户的判断(正确)**:平移视界时**画面内容并不改变,只是横向平移**;如果只是重画得快、而不复用已画好的像素,那么在"某些放大级别可见像素极多"时,重画必然卡住消息循环,交互就必然掉帧。正确做法是**复用已绘制的像素**,让前台始终只做 O(1) 的取用。
- **对现有代码的核对**:用户判断与仓库现状**基本一致** —— 之前的历次优化都是"加速绘制"(包络带 `FillPath`、装饰层、缓存),而**没有把"绘制"这一任务本身从交互帧里拆出去**(后台线程只算了派生曲线的数值,没有做波形光栅化)。因此一遇到"可见像素特别多"的放大级别(如 `[40.1756, 40.1885]s`),每帧重画就再次成为瓶颈。
- **可行性结论**:用户的"每网格双缓冲 + 后台填充 + 前台只贴已就绪缓冲"方向正确。要点补充:① GDI/GDI+ 绘制**可以在后台线程**进行(每个线程用**各自**的 `Bitmap`/`HDC`/`Graphics` 即可),所以后台光栅化可行;② 但本仓库的 `PaintLane` 会读写大量成员缓存(折线/曲线/图例命中/装饰矩形),直接搬到后台线程需要一次较大重构才能保证线程安全。③ 因此本次采用一个**风险更低、收益相同**的落地方式(见下)。
- **本次实现(平移 O(1))**:
- **宽缓冲区**:以当前视界为中心,向左右各多渲染**半个视界宽**,存成一张宽为 `2×视界宽` 的离屏位图(`m_wideDc`)。它用与正常绘制**完全相同**的 `PaintLane`/`PaintTimeAxis`(临时把横向映射"加宽"后渲染),因此像素与正常绘制一致;仅**跳过图例与 Y 轴刻度**(二者锚定网格左缘、随视界移动,改由已有的**装饰层缓存**贴回)。
- **平移 = 一次 BitBlt**:视界宽度不变、只是横向移动时,前台从宽缓冲区**截取对应横向子矩形**贴到后备缓冲区即可(O(1)),**完全不重画波形**。无论放大到多深,平移都与全局显示一样流畅。
- **边界重建**:当视界移出宽缓冲范围(或跨度/内容变化)时,才以新视界为中心**重建一次**(约占整幅重绘 2 倍,但只在每平移约半屏时发生一次)。
- **实测**(`VERY_BIG_DATA` 全信号,视界 `[40.1756, 40.1885]s`,5 网格;单位 ms/帧,负载机):
| 场景 | 修复前 | 修复后 |
| --- | --- | --- |
| 中键平移拖动(短距) | 34ms | **1.3ms** |
| 中键平移拖动(长距,含边界重建) | 34ms | **1.7ms**(峰值 50ms 为一次边界重建) |
| 透明度/粗细/颜色 | 16ms | 8ms |
| 弱波增强滑块 | 26–176ms | 约 15ms(空闲)/ 55ms(负载) |
- **显示效果**:**静止/结算帧与修复前逐像素 0 差异**(`LockBits` 对照);平移拖动帧为快速帧质量(关闭抗锯齿,与既有"拖动时低分辨率、松手补全质量"策略一致),松手后立即恢复全质量。
- **说明**:宽缓冲区重建目前在前台进行(每平移约半屏一次);若日后要连这一次重建也移出交互帧,需把 `PaintLane` 的成员缓存重构为可复制的渲染上下文再搬后台(较大改动)。弱波增强参数滑块仍有一次"重绘受影响网格 + 浮动面板"的开销(负载下约 55ms),是本轮未彻底消除的残留。
---
## 10. 变更日志(本文件维护记录)
@@ -296,3 +316,4 @@ Documents/ 项目文档(含本文件)
| 2026-10-11 | DeepSeek-V4.1-Flash | 完善异步:后台改**多线程池**(预览曲线并行计算、更快出现);`PaintLane` 分两遍把**预览曲线画在全部原信号之上**(不再被覆盖);**快速帧只用于改主体的拖拽**(拖光标/框选改为命中场景缓存,约 1–4ms)。小跨度逐像素 0 差异。 |
| 2026-10-11 | DeepSeek-V4.1-Flash | 预览曲线改为**按整条信号计算**,与「构建为新信号」逐位一致;新增**平移装饰层像素缓存**(图例 + Y 轴刻度在拖拽时直接贴回,2ms/大格拖拽 13ms→约 5ms);后台任务按 (信号,种类) 去重以防拖滑块淹没线程池。 |
| 2026-10-11 | DeepSeek-V4.1-Flash | 探针定位"样式/参数滑块在深放大下极卡":根因是**样式补丁用了抗锯齿**(≈72ms/网格)且**后台曲线发布时整幅作废**导致每帧整幅重绘。修复:样式补丁强制快速帧、拖拽中不整幅作废、交互期省略面板投影 → 透明度/粗细/颜色 77ms→16ms、弱波增强滑块 96–176ms→26ms,静止/结算帧逐像素不变。 |
| 2026-10-11 | DeepSeek-V4.1-Flash | 落地用户提出的**复用像素**方案:新增**平移宽缓冲区**(以视界为中心左右各多渲染半个视界宽),平移时前台只做一次 **BitBlt 截取子矩形**(O(1)),移出边界才重建 → 深放大下平移 **34ms→1.3ms**;静止/结算帧逐像素 0 差异。 |