交互流畅性三项修复:光标不再跟随视界 / 缩放首帧不再卡顿 / 超大信号拖预览不再拖慢前台
一、光标不再跟随视界(用户要求)
- 删除 ClampCursorsAfterPan() 及其在平移路径的三处调用(中键/左键拖拽平移、
PanTimeByFraction 方向键、OnMouseWheel(shift=true))。ClampCursorToView() 保留,
只在真正拖拽光标时使用。
- 现行为:平移/缩放视界时光标的时间位置完全不变(可滑出可视区,读数贴在网格边缘显示)。
- 验证:平移 20 帧把视界移到 [257.6, 386.4]s(光标时间 38.643036 远在视口外)后
CursorTime 完全不变;Shift+滚轮同样不变。
二、缩放首帧不再卡顿(证实"必要像素量级飙升导致首帧卡住"的猜测)
- 量化:每次缩放变化后首帧 70~80ms(宽跨度)/ 26~40ms(第二帧),第三帧起 0.5ms。
- 根因:跨度变化时 Paint 里 if (!LayersStructurallyReady()) RebuildWideBuffer();
会同步重建全部 5 个网格图层(70~80ms),随后一帧再做一次整幅"结算"(40ms),
单次缩放合计约 110ms 卡顿。
- 修法(用户方案:先画了再说,不卡前台):
1) BlitWideBuffer 对"跨度过期"的图层不再跳过(跳过会让网格在重画完成前空白),
改为把旧图层覆盖的时间段重新映射到当前视界,用一次 StretchBlt(HALFTONE)
拉出低分辨率但立即可用的占位画面;
2) Paint 不再同步整批重建:只有"图层数量与网格数量不符"(首次绘制/增删网格,
新增 LayersAllocated())才 RebuildWideBuffer();跨度/内容过期一律入队,由
RenderPendingLaneRebuilds 逐帧补齐(原本遇到跨度不符会跳过该网格,现改为直接
按新跨度重建);
3) 只在视界稳定后(viewStopped)才花预算做精细重绘:视界仍在变化的每一帧只贴拉伸
占位图,避免缩放序列每帧夹带一个网格的整块重建(宽跨度约 20ms)。
- 实测:缩放后首帧 70~80ms → ~10ms;精细重绘分散到后续帧(宽跨度 ~20ms/帧、
深跨度 ~13ms/帧),收敛后由"结算"整幅帧保证像素精确。
- 保留"结算"帧:实测宽基底路径与从零整幅重绘并非像素同源(图层 2448px 按 1px 列聚合
vs 整幅 1224px 列,列相位差对稠密脉冲信号放大成 17 万+ 像素差异),必须保留这次
全质量整幅重绘来收敛。
- 正确性 A/B:缩放收敛后的画面 vs 从零整幅渲染,改动前后同为 3774 像素差异
(预存在的列相位/装饰残差),非本次引入。
三、超大信号拖预览滑块不再拖慢前台(部分改善)
- 量化:无预览基线 0.38ms/帧;预览 + 参数不变 3.4ms/帧;预览 + 参数每帧变化
11.8~15ms/帧;而直接调用 PaintLaneDerivedOverlays 仅 0.375ms —— 开销不在绘制,
而在"参数每帧变化"触发的整条信号(2576 万采样)预览曲线重算调度(预览曲线按整条
信号计算以保证与「构建为新信号」逐位一致)。
- 缓解:新增 kHugeSignalPreviewSamples = 4000000 —— 信号超过该规模且正在拖拽时,
不再每帧排队整条信号的重算,沿用旧曲线、松手后再算一次最新的。实测 15.0→13.7ms。
- 未完全解决:参数变化带来的 ~8ms/帧开销尚未完全归因(已排除绘制;"暂停后台线程"的
对照无效,因为 EnsureCurveWorker() 会在暂停后被重新拉起)。用户提议的"网格内按信号
并行绘制 + 后台缓冲合成"仍需先把渲染器改成无共享可变状态,留待后续评估。
验证:pancheck(平移/纵向 7 场景)全部 dy=0;zoomcheck mode 0/13/14;
make clean && make all 零警告。文档同步更新 §9.17。
This commit is contained in:
1 parent
de27d9b94f
commit
065b948297
3 files changed
+192
-58
No files matched your search
@@ -541,6 +541,70 @@ Documents/ 项目文档(含本文件)
|
||||
「仅水平平移」「仅纵向平移」行为与像素结果不变(已由对照场景 B/C 覆盖)。
|
||||
- `make clean && make all` 零警告。
|
||||
|
||||
### 9.17 交互流畅性三项修复:光标不再跟随视界 / 缩放首帧不再卡顿 / 超大信号拖预览不拖慢前台(本轮)
|
||||
|
||||
#### 一、光标不再跟随视界(用户要求)
|
||||
|
||||
用户报告:"启用光标时,移动视界导致光标超出视界范围,光标会被强制移动到视界边界;
|
||||
我需要光标留在原位置而不是跟随视界移动。"
|
||||
|
||||
- 移除 `ClampCursorsAfterPan()`(含声明)及其在**平移路径**的三处调用:中键/左键拖拽平移、
|
||||
`PanTimeByFraction`(方向键)、`OnMouseWheel(shift=true)`。
|
||||
- `ClampCursorToView()` 保留,**只在真正拖拽光标时**使用(其内注释与头文件说明同步更新)。
|
||||
- 现行为:平移/缩放视界时光标的**时间位置完全不变**,允许滑出可视区(读数贴在网格边缘显示)。
|
||||
- 验证(`.preflight/perf/zoomcheck` mode 14):平移 20 帧(视界移到 `[257.6, 386.4]s`,光标时间
|
||||
`38.643036` 已远在视口外)后 `CursorTime` **完全不变**(`unchanged=1`, `offViewport=1`);
|
||||
Shift+滚轮平移同样不变。
|
||||
|
||||
#### 二、缩放首帧不再卡顿(用户猜测"必要像素量级飙升导致首帧卡住"——**证实**)
|
||||
|
||||
- 量化(`.preflight/perf/zoomcheck` mode 0,`VERY_BIG_DATA` 5 网格逐步放大,逐帧计时):
|
||||
每次缩放变化后 **f1=70~80ms(宽跨度)**、f2=26~40ms,f3 起 0.5ms。
|
||||
- 根因:跨度变化时 `Paint` 里的 `if (!LayersStructurallyReady()) RebuildWideBuffer();` 会
|
||||
**同步重建全部 5 个网格图层**(70~80ms);随后一帧又做一次整幅"结算"(40ms)→ 单次缩放
|
||||
合计约 110ms 的卡顿。
|
||||
- 修法(用户方案:先用旧图拉伸占位、不卡前台):
|
||||
1. **旧跨度像素拉伸占位**:`BlitWideBuffer` 遇到 `layer.span != 当前 span` **不再跳过**
|
||||
(跳过会让网格在重画完成前空白),而是把旧图层覆盖的时间段 `[layer.wideMin, +layer.span]`
|
||||
重新映射到当前视界,用一次 `StretchBlt`(`HALFTONE`)拉出**低分辨率但立即可用**的画面。
|
||||
2. `Paint` **不再同步整批重建**:只有"图层数量与网格数量不符"(首次绘制 / 增删网格,
|
||||
新增 `LayersAllocated()`)才 `RebuildWideBuffer()`;跨度 / 内容过期一律入队,由
|
||||
`RenderPendingLaneRebuilds` 逐帧补齐(原逻辑遇到跨度不符会跳过该网格,现改为直接按**新跨度**重建)。
|
||||
3. **只在视界稳定后**(`viewStopped`)才花预算做精细重绘:视界仍在变化的每一帧只贴拉伸占位图,
|
||||
否则缩放序列的每一帧都会夹带一个网格的整块重建(宽跨度约 20ms)→ 持续掉帧。
|
||||
- 实测:缩放后**首帧 70~80ms → ~10ms**;精细重绘分散到后续帧(宽跨度 ~20ms/帧,深跨度 ~13ms/帧)。
|
||||
收敛后仍由"结算"整幅帧保证像素精确。
|
||||
- ⚠️ **保留"结算"帧**(不可省):实测宽基底路径与从零整幅重绘**并非像素同源**
|
||||
(图层 2448px 按 1px 列聚合 vs 整幅 1224px 列,列相位差对稠密脉冲信号放大成 17 万+ 像素差异),
|
||||
因此必须有这一次全质量整幅重绘来收敛。
|
||||
- 正确性 A/B(mode 13):缩放并收敛后的画面 vs 从零整幅渲染,改动前后**同为 3774 像素差异**
|
||||
(预存在的列相位/装饰残差),**非本次引入**。
|
||||
|
||||
#### 三、超大信号拖预览滑块不再拖慢前台(部分改善,根因未完全定位)
|
||||
|
||||
- 量化(mode 7/10/12,深放大,单网格预览,模拟面板滑块拖拽 `m_dragMode=PEAK_LEVEL`):
|
||||
- 无预览基线(mode 5):0.38ms/帧。
|
||||
- 预览 + **参数不变**(mode 12):3.4ms/帧。
|
||||
- 预览 + **参数每帧变化**(mode 10):11.8ms/帧;弱峰 10 迭代(mode 7):15ms/帧。
|
||||
- **直接调用 `PaintLaneDerivedOverlays`(mode 9):0.375ms** —— 说明开销**不在曲线绘制**。
|
||||
- 结论:开销来自"参数每帧变化"触发的**整条信号**(2576 万采样)预览曲线重算调度
|
||||
(预览曲线按整条信号计算,以保证与「构建为新信号」逐位一致)。
|
||||
- 缓解:新增 `kHugeSignalPreviewSamples = 4000000` —— 信号超过该规模且**正在拖拽**(`m_dragMode != NONE`)时,
|
||||
不再每帧排队"整条信号"的重算,改为沿用旧曲线、松手后再算一次最新的(整条信号重算本来也追不上
|
||||
滑块速度,"冻结"与"每帧重排"观感相同,后者却持续占用后台线程与内存带宽)。
|
||||
实测 15.0 → 13.7ms(改善有限)。
|
||||
- ⚠️ **未完全解决**:参数变化带来的 ~8ms/帧开销**尚未完全归因**(已排除绘制;"暂停后台线程"的
|
||||
对照无效,因为 `EnsureCurveWorker()` 会在暂停后被重新拉起)。用户提议的"网格内按信号并行绘制 +
|
||||
后台缓冲合成"仍是候选方案,但需先把渲染器改造成**无共享可变状态**(与 §11.2 第 5 条硬约束冲突),
|
||||
留待后续评估。
|
||||
|
||||
#### 四、验证
|
||||
|
||||
- `.preflight/perf/pancheck`(平移 / 纵向,7 个场景):全部 `dy=0`(原"中间网格 +1px"也归零)。
|
||||
- `.preflight/perf/zoomcheck`:mode 0(逐帧首帧耗时)、mode 13(缩放收敛,A/B 无回归)、
|
||||
mode 14(光标不跟随视界)。
|
||||
- `make clean && make all` 零警告。
|
||||
|
||||
---
|
||||
|
||||
## 10. 变更日志(本文件维护记录)
|
||||
@@ -572,6 +636,7 @@ Documents/ 项目文档(含本文件)
|
||||
| 2026-10-12 | DeepSeek-V4.1-Flash(**起源设备**) | **「弱峰调整」升级为三组识别规则 + 滑动条 ±400% 梯度驱动**:第一类(两侧均值都要满足,原规则)在滑动条 0–200% 内走完自身 0–200%;第二类("或":任一侧均值满足即可)从 100% 起参与,own 幅度 = clamp(|a|-100%,0,200%);第三类(只与左右同类极值比较,最宽松)从 200% 起参与,own 幅度 = clamp(|a|-200%,0,200%);负值完全对称。每组自身的 ±200% 映射统一为:+100% → 左右同类极值中较温和的一侧、**+200% → 左右同类极值的均值**(不再取较大的那个);-100% → 相邻异类极值中较近的一个、**-200% → 相邻异类极值的均值**(不再取最小的那个)。同时满足多组时取**幅度绝对值最大**的组。验证:`uitest` **463/463**(第一类点 0/50/100/150/200% → 520/570/620/**625/630**;只满足第二类的峰 130 → 100% 不动、150%/200%/250%/300%/400% = 140/150/162.5/175/175;只满足第三类的峰 195 → 200% 不动、250%/300%/400% = 197.5/200/200;±400% 夹取;滑块中间 = 0%、两端 = ±400%)、`panperf`/`layercheck`/`cursorcheck`/`dragcheck` 全过、零警告。 |
|
||||
| 2026-10-12 | DeepSeek-V4.1-Flash(**起源设备**) | **修复"按住中键把视界纵向移动好几个网格高(或横向拖很远)后,出现大量绘制错误与持续抽搐"**:根因是 `RollWideBuffer` 对"位移超出余量"只做**排队等后续帧重画**(`QueueLaneRebuild` + `continue`)—— 该帧 `BlitWideBuffer` 取不到源行 → 网格里出现**空白条带**;且每帧都重新入队而 `RenderPendingLaneRebuilds(1)` 一帧只补 1 个网格 → **补不完 = 持续抽搐**;纵向余量只有 ±半个网格高,正常速度的纵向拖拽每帧就超过它,故几乎必现。修法:**只有"内容真的变了"才排队**,距离类问题一律本帧就地解决 —— 位移小于画布则滚动(O(1) BitBlt + 只补暴露细条),超出画布则**立刻整块重画并重新居中**(0.4ms/网格)。验证:新增探针 `.preflight/dragcheck` 按用户要求**全程按住中键不放**地做检查(图层条带逐像素 / 画布覆盖率 / 画面空白行数与墨迹占比),并把纵向滚动临时关掉做 A/B:立刻报 `layer band diff 76221 / drawing errors 11`,恢复后 **0 错误**;`uitest` **463/463**、`panperf`/`layercheck`/`cursorcheck` 全过、`make clean && make all` 零警告。 |
|
||||
| 2026-10-12 | DeepSeek-V4.1-Flash(**另一台设备**) | **修复「垂直中键拖拽后再平移,网格内容纵向跳变 / 绘制错误」**(详见 §9.16):根因是 `RenderLaneLayerRegion` 结束时**无条件**把 `layer.anchorYMax` 重置为"当前 `yMax`"—— 对整块重画 / 纵向补条成立,但**横向补细条(只改一条竖带、其余像素仍在原锚点)**不成立。纵向拖动以 `kLayerRollQuantum=16px` 量化,停下时图层会残留 `<16px` 的纵向偏移且 `anchorYMax != 当前 yMax`;第一次水平微移就把该偏移**静默清零**(`vOffset→0`)而像素未动 → 整个网格波形纵向错位(最多 15px)。修法:`RenderLaneLayerRegion` 新增形参 `anchorYMax`,内部以它作幅值映射基准(`yMin = anchorYMax - laneSpan`)渲染并写回 `layer.anchorYMax`;横向补条传 `layer.anchorYMax`(保持不变),整块重画/纵向补条传当前(或滚动后)`yMax`。`vOffset==0` 时新旧逻辑完全等价。验证:新增离屏探针 `.preflight/perf/pancheck`(窗口 `SW_HIDE` + 注入消息 + 读图层内部 `anchorYMax`/`vOffset` + 持按钮帧 vs 释放后整幅帧的纵向互相关):修复前 A 场景 `dy=+15px`(lane0 差异 39245),修复后 **`dy=0`**(差异 1220,仅已知列相位残差);对照/中间网格/长距水平/斜向/松开重按共 7 个场景在缩放 0/8/26 步下全部 `dy=0`(中间网格 `+1px` 为整像素量化残差);`make clean && make all` 零警告。 |
|
||||
| 2026-10-13 | DeepSeek-V4.1-Flash(**另一台设备**) | **交互流畅性三项修复**(详见 §9.17):① **光标不再跟随视界**——删除 `ClampCursorsAfterPan()` 及其在平移路径(中键/左键拖拽、Shift+滚轮、方向键)的三处调用,视图变化时光标时间位置**完全不变**(可滑出视口),`ClampCursorToView()` 只在拖拽光标时使用(mode 14:平移 20 帧后 `CursorTime` 不变且 `offViewport=1`)。② **缩放首帧不再卡顿**——根因是跨度变化时 `Paint` 会**同步重建全部 5 个网格图层**(实测 70~80ms)+ 随后整幅结算(40ms)=单次缩放 ~110ms 卡顿;修法:`BlitWideBuffer` 对跨度过期的图层改用**旧像素 `StretchBlt` 拉伸占位**(不再跳过导致空白)、`Paint` 只在"图层数量不符"时同步重建(新增 `LayersAllocated()`)、`RenderPendingLaneRebuilds` 允许按新跨度重建、且**只在视界稳定后**才花预算精细重绘(`viewStopped` 门控);实测首帧 **70~80ms → ~10ms**。保留"结算"帧(实测宽基底路径与整幅重绘有 17 万+ 像素列相位差,非同源)。③ **超大信号拖预览滑块**——量化发现开销**不在绘制**(`PaintLaneDerivedOverlays` 仅 0.375ms),而在"参数每帧变化"触发的**整条信号**(2576 万采样)预览重算调度;新增 `kHugeSignalPreviewSamples=4000000`,超大信号拖拽期间不再每帧排队重算(15.0→13.7ms,**剩余 ~8ms 根因未完全定位**)。验证:`pancheck` 7 场景全 `dy=0`、`zoomcheck` mode 0/13/14、`make clean && make all` 零警告。 |
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in new issue
Block a user