网格底衬永不空白 + 动画流畅性根治 + 光标随首个信号重现 + 「放大自动点线」新判据
一、网格底衬消失(半屏/局部/全屏,且要等一会才刷新好)—— 两个机制叠加 1) BlitWideBuffer 对「跨度已过期」的图层原用 StretchBlt 放大占位:深度放大时把一小段像素放大 几十倍 ⇒ 整块网格变成一片无网格的模糊色块(用户看到的就是「网格叠底消失、没有任何内容」)。 修法:删除占位拉伸;对拿不到(完整)图层像素的网格【就地补画网格底衬】 (新增 PaintLaneGridBacking + m_renderGridOnly,复用 PaintLane 的网格部分,跳过波形/散点/ 派生曲线/Y 轴刻度/图例)。底衬代价极低,可每帧执行 ⇒ 网格永不空白/闪烁。 ⚠️ 顺序:先画底衬(GDI+)再 g.GetHDC() 做 BitBlt;只有全部网格都贴不出像素时才交给整幅重绘。 2) 待重画队列按下标顺序消费(一次 1 个)⇒ 上百个网格时可视网格要等上百帧。 修法:RenderPendingLaneRebuilds 里做稳定划分,【可视网格优先】(LaneVisuallyRelevant); 「结算」判据由 LanesAllReadyForView() 改为 LanesVisiblyReadyForView()。 二、过渡动画卡顿/不平顺/直接闪到终态 —— 三处根因 1) 「m_layerRebuildPending 非空就每帧请求再送一帧」:上百个网格时 700ms 内挤出 124 次重绘, 而 WM_TIMER 优先级低于投递消息 ⇒ 动画定时器被饿死(实测 frames=124 / timerTicks=1)。 修法:新增 AnyVisibleLanePending(),只用「还有没有可视网格待补」决定是否请求再送一帧; 离屏网格改为机会式预热(实测 frames=25 / timerTicks=16)。 2) SetTimer 的间隔会被量化到系统 tick(默认 15.6ms):请求 16ms 实际等到下一个 tick ⇒ 实测帧距 ~26ms(只有约 38fps)。修法:kScrollTimerPeriodMs 16 → 10(实测 ~15.5ms ≈ 64fps; 真实消息循环帧距 ~25ms → ~16ms)。实测映射:8→15.8 / 10→15.5 / 12→16.5 / 16→26.4 / 31→37.9。 ⚠️ timeBeginPeriod(1) 在本机无效(已 A/B 验证)。 3) 网格滚动联动 / 「回到最上方」每帧都改 LaneScroll(),而它原本参与「宽基底可用性」判据 (SceneGeometryHash)⇒ 每一帧都退化成整幅重绘。修法:SceneContentHash 新增 includeLaneScroll 形参, 宽基底可用性判据不计网格滚动量/各网格屏幕纵向位置(它们只是图层的贴图落点); 静态场景缓存的指纹仍然计入。实测网格滚动动画单帧 18.36ms → 1.19ms。 三、光标工具激活且没有任何信号绘制时,首个信号出现后按规则重新出现(用户要求) 新增 PlotView::ResetCursorPlacementOnFreshContent()(规则与 CycleCursorMode() 的「首次出现」同源: 单光标置于视图中心;双光标第二条紧邻第一条且落在可见范围内),由 Application::SynchronizeView 的「首次出现可见信号」分支在 FitAll() 之后调用。 实测:单光标 stale=4.60 → 新的视图中心 20.92;双光标 stale=(2.89,7.18) → (20.92,26.07)。 四、「放大自动点线」规则改为:任一小格内数据点少于 5 个即自动切换(用户重新定义) 新增 PlotView::AutoPointLineNeeded()(推翻此前「一屏内数据点 < N×4」):按 GridMinorCellCount() 切成横向小格,只要存在任何一个小格内有效数据点 < kAutoPointLineMinPointsPerCell(=5) 就切「点线」。 实现:先「总有效点 < N×5」鸽巢 O(1) 充分判定;单调时间轴逐小格两次二分取区间(单格累计到 5 个即早退, 另给 kAutoPointLineCellScanCap=512 封顶);非单调时间轴一次有界扫描分桶。保留「视界内至少一个可见点」保护。 ⚠️ 光标吸附仍用旧的整屏判据 SignalSnapEnabled(用户明确要求「其它规则不变」)。 验证(全部离屏,不占用鼠标/键盘/屏幕) - 新增 .preflight/perf/gridanim_main.cpp(gridanim.exe)55/55: [A] 缩放/适应视图/「预热未完成再滚动」等每个用例逐帧断言「每个可视网格都有网格底衬 (有竖直锐边、无模糊占位、无未绘制带)」;[B] 直接驱动 + 真实消息循环两条路径统计每帧耗时、 帧距均值/最大与进度单调性;[C] 光标重现;[D] 自动点线新判据(稠密不切、稀疏切换、恢复原样式)。 - 既有回归:verify 49/49、panelanim 156/156、datacheck 69/69、edgecheck 46/46、errcheck 19/19。 - make release 零错误零警告(使用 TARGET_FILE_NAME=TargetCheck 以避免与用户正在运行的实例冲突)。 文档:Documents/agent_mem.md 新增 §9.42、§11.2 第 117–124 条与变更日志一行。
This commit is contained in:
1 parent
f845616938
commit
34375481de
7 files changed
+702
-168
No files matched your search
@@ -2531,6 +2531,102 @@ else if (panelOnly) { /* 只回送“本帧重绘过的区域并集” */ }
|
||||
|
||||
---
|
||||
|
||||
### 9.42 网格底衬永不空白 + 动画流畅性根治 + 光标随首个信号重现 + 「放大自动点线」新判据(本轮)
|
||||
|
||||
> 用户 2026-10-17 第六轮(本机)。四项需求:
|
||||
> ① 选区放大 / 适应视图后网格底衬**消失(半屏或局部)**、要等一会才刷新好;
|
||||
> ② 所有过渡动画**卡顿 / 不平顺 / 直接闪到终态**(折起展开、网格滚动、图例联动、回到最上方按钮);
|
||||
> ③ 光标工具激活且**没有任何信号绘制**时,首个信号出现后光标要**按规则重新出现**(不再停在旧残留位置);
|
||||
> ④ 「放大自动点线」规则改为:**存在任何一个小格内数据点少于 5 个**即自动切换。
|
||||
> 离屏验证:新增 `.preflight/perf/gridanim_main.cpp`(`gridanim.exe`,**55/55**,含**真实消息循环**计时);
|
||||
> 既有 `verify` **49/49**、`panelanim` **156/156**、`datacheck` **69/69**、`edgecheck` **46/46**、
|
||||
> `errcheck` **19/19**;`make release` 零警告。
|
||||
|
||||
#### 一、网格底衬消失:**旧跨度图层的 `StretchBlt` 占位拉伸** + 预热按下标顺序推进
|
||||
|
||||
- **两个机制叠加**(都能单独造成用户看到的现象):
|
||||
1. `BlitWideBuffer` 对「跨度已过期」的图层(缩放瞬间)原用 `StretchBlt` 把旧图层**放大**占位 ——
|
||||
深度放大时那是把一小段像素放大几十倍 ⇒ 整块网格变成**一片无网格的模糊色块**,
|
||||
用户眼里就是「网格叠底消失、没有任何内容」。局部/半屏/全屏取决于有多少个网格处于过期状态。
|
||||
2. 待重画队列(`m_layerRebuildPending`)**按下标顺序**消费,一次只补 1 个网格;
|
||||
上百个网格时,用户正在看的那些(往往在下半部)可能要等**上百帧**才轮到
|
||||
⇒「需要等一会网格底衬和信号才正确加载」。
|
||||
- **修法**:
|
||||
* **删除占位拉伸**;`BlitWideBuffer` 对「拿不到(完整)图层像素」的网格**就地补画网格底衬**
|
||||
(`PaintLaneGridBacking` → 复用 `PaintLane` 的网格部分,新增 `m_renderGridOnly` 跳过
|
||||
波形/散点/派生曲线/Y 轴刻度/图例)。底衬代价极低,可**每帧**执行 ⇒ 网格永不空白/闪烁。
|
||||
⚠️ 顺序必须**先画底衬(GDI+)再取 HDC 做 BitBlt**(`g.GetHDC()` 之后不能再调 GDI+)。
|
||||
⚠️ 只有**所有**网格都贴不出像素时才 `return false`(交给整幅重绘),否则会停在「只有底衬、没有波形」。
|
||||
* 队列消费改为**可视网格优先**(`std::stable_partition` + `LaneVisuallyRelevant`)⇒ 可视区域 ~10 帧内收敛。
|
||||
* 「结算」(整幅精确帧)判据由 `LanesAllReadyForView()` 改为 **`LanesVisiblyReadyForView()`**:
|
||||
上百个网格时不必等全部**离屏**图层就绪(否则结算被拖到上百帧之后)。
|
||||
|
||||
#### 二、动画卡顿根因之一:**「队列非空就请求再送一帧」造成的高频重绘饿死动画定时器**
|
||||
|
||||
- **现象**:用户循环「勾选分组复选框(全部信号→124 个网格)→ 折起/展开分组」时动画卡住/闪烁。
|
||||
- **根因**:设备队列里长期残留大量**离屏**网格 ⇒ `WarmLaneLayersStep` / 宽基底分支按
|
||||
「`m_layerRebuildPending` 非空」**每帧**都 `PostMessage(kMessagePlotDerivedReady)` ⇒ 700ms 内挤出
|
||||
**124 次重绘**;而 `WM_TIMER` 的优先级**低于投递消息** ⇒ 动画定时器被饿死。
|
||||
**实测(探针)**:修复前该场景 `frames=124 / timerTicks=1`(动画几乎没动);修复后 `frames=25 / timerTicks=16`。
|
||||
- **修法**:新增 `AnyVisibleLanePending()`,只用「**还有可视网格待补**」决定是否请求再送一帧;
|
||||
离屏网格改为**机会式**预热(任何一帧顺手补一个)。
|
||||
|
||||
#### 三、动画卡顿根因之二:**16ms 定时器被系统 tick 量化成 ~26ms**(只有约 38fps)
|
||||
|
||||
- **现象**:所有过渡动画「不平顺、像掉帧」。用户要求「找出一个能精准评估动画流畅性的方式」。
|
||||
- **方法**:探针**子类化宿主窗口**(`SetWindowLongPtrW(GWLP_WNDPROC)`)记录每个 `WM_PAINT` / `WM_TIMER(id=3)`
|
||||
的时刻,跑**真实消息循环**(真定时器、真 `WM_PAINT`)并统计帧距均值/最大。窗口需
|
||||
「离屏但可见」(`-30000,-30000` + `WS_EX_TOOLWINDOW` + `SWP_SHOWWINDOW|SWP_NOACTIVATE`),
|
||||
否则隐藏窗口没有 `WM_PAINT`。
|
||||
- **实测(关键数据)**:请求周期 → 实际帧距:**8ms→15.8 / 10ms→15.5 / 12ms→16.5 / 16ms→26.4 / 31ms→37.9**。
|
||||
⇒ `SetTimer(...,16,...)` 会被量化到**下一个**系统 tick(默认 15.6ms),实际 ~26ms(≈38fps)。
|
||||
- **修法**:`kScrollTimerPeriodMs` **16 → 10**(量化结果正好一个 tick,实测 ~15.5ms ≈ 64fps);
|
||||
实测真实循环帧距 ~25ms → **~16ms**,动画恢复平滑。`timeBeginPeriod(1)` 在本机**无效果**(已验证)。
|
||||
- 另一处:**网格滚动联动 / 回到最上方**每帧都在改 `LaneScroll()`,而它原本参与
|
||||
「宽基底可用性」判据(`SceneGeometryHash`)⇒ 每一帧都退化成**整幅重绘**。
|
||||
修法:`SceneContentHash` 新增 `includeLaneScroll` 形参;**静态场景缓存仍计入**它(缓存位图内容取决于它),
|
||||
但**宽基底可用性判据不再计入**(网格滚动只改各网格的**贴图落点**,图层内容完全不变,
|
||||
`BlitWideBuffer` 本来就按 `lane.rect` 逐网格贴图)。
|
||||
**实测**:网格滚动动画单帧 **18.36ms → 1.19ms(均值)/ 23.02ms → 6.83ms(最坏)**;可跑满 60fps。
|
||||
|
||||
#### 四、光标:没有任何信号绘制时,首个信号出现后**按规则重新出现**(用户要求)
|
||||
|
||||
- `PlotView::ResetCursorPlacementOnFreshContent()`:规则与 `CycleCursorMode()` 的「首次出现」**完全同源** ——
|
||||
单光标置于视图中心;双光标第一条置于视图中心、第二条紧邻第一条且保证落在可见范围内。
|
||||
- 接入点:`Application::SynchronizeView` 的 `else if (!tab.ViewInitialized())` 分支
|
||||
(= 「刚才没有任何可见信号、现在首次出现」)在 `FitAll()` **之后**调用它。
|
||||
- 实测:单光标 stale=4.60 → 新位置 = 视图中心 20.92;双光标 stale=(2.89, 7.18) → (20.92, 26.07)(均与中心/邻位一致)。
|
||||
|
||||
#### 五、「放大自动点线」新判据:**任一小格内数据点 < 5**(用户 2026-10-17 重新定义)
|
||||
|
||||
- **推翻**此前的「一屏内数据点 < N×4」(`SignalSnapEnabled`)。新判据 `PlotView::AutoPointLineNeeded`:
|
||||
按 `GridMinorCellCount()` 把视界切成 N 个**横向小格**,**只要存在任何一个小格内的有效数据点 < 5**
|
||||
就自动切「点线」(更贴近用户意图:**局部**稀疏也提示)。
|
||||
- 实现要点:① 先用「有效点总数 < N×5」做 **O(1) 鸽巢充分判定**;
|
||||
② 时间轴单调时每个小格用**两次二分**取样本区间(代价与采样数无关,单格扫到 5 个有效点即早退,
|
||||
再给 `kAutoPointLineCellScanCap=512` 封顶);③ 非单调时一次有界扫描按小格分桶。
|
||||
④ 保留「视界内至少有一个可见数据点」的保护(否则整屏无点时会被判成稀疏而白切一次)。
|
||||
- ⚠️ **光标吸附仍用旧的整屏判据**(`SignalSnapEnabled`):用户明确要求「其它规则不变」——
|
||||
§9.30 记的「自动点线 / 吸附同源」**自本轮起不再成立**。
|
||||
- 实测(探针写入 20001 点 / [0,1000] 的单信号):全视图(span=1000,50 小格,每格 400 点)⇒ 不切换;
|
||||
放大到 span=2.5(每格 1 点)⇒ 切换并置 `autoScatterActive`;`FitAll()` 回到稠密 ⇒ 恢复原样式。
|
||||
|
||||
#### 六、⚠️ 本轮方法论教训
|
||||
|
||||
1. **`theme.cardBg == theme.plotBg`(都是 kBase3)**⇒ 任何「按颜色区分『空白/已绘制』」的探针判据
|
||||
都是**色盲**的(实测假绿)。判断「网格是否画出来」只能用**结构判据**(竖直锐边)。
|
||||
2. **竖直锐边判据必须要求「同一列多行都有锐变」**:网格**分界线**是一条横线,会让每一列都"看起来有锐边"
|
||||
(第一版 `SharpColumns` 因此假绿)。改为「同一 x 至少 3 行有 |Δ|≥阈值」后才有判别力。
|
||||
3. **后备缓冲区是持久的**:宽基底路径只把绘图区填成 `plotBg`(== `cardBg`)⇒ 本帧没被覆盖的网格
|
||||
**保留上一帧像素**(陈旧但"看起来有内容")⇒ 探针里"没有空白"**不能**证明没问题。
|
||||
要证伪必须**注入一个刺眼占位色**(本轮试过品红)或直接对比两张图。
|
||||
4. ⚠️ **探针里动画计时必须在启动动画后先画一帧**:`Render` 才负责 `SetTimer` 武装动画定时器;
|
||||
否则消息循环无消息可等 ⇒ `frames=0`(本轮假失败两次)。
|
||||
5. ⚠️ 构建 25 个源文件的探针约 **43 秒**;`build.ps1` 若失败只在 `$LASTEXITCODE` 上体现,
|
||||
直接跑旧 exe 会拿到**旧结果**(本轮因此误判过一次)—— 必须检查构建输出。
|
||||
|
||||
---
|
||||
|
||||
## 10. 变更日志(本文件维护记录)
|
||||
|
||||
| 日期 | 设备/模型 | 摘要 |
|
||||
@@ -2589,6 +2685,8 @@ else if (panelOnly) { /* 只回送“本帧重绘过的区域并集” */ }
|
||||
|
||||
| 2026-10-16 | DeepSeek-V4.1-Flash(**本机**) | **回退 5b52eef 并修复“屏幕不刷新”根因(拖动滚动条卡顿 / 悬停高亮卡住 / 信号拖拽无效果,详见 §9.41;§9.40 随之作废)**。根因(代码级):`Render()` 的回送被写成 `if (fullFrame) … else if (panelOnly) …` 而**没有兜底 else**,而“鼠标只在绘图区内移动”的帧恰好两个条件都不满足 ⇒ 后备缓冲区画对了、窗口 DC 一个像素都不回送(悬停光晕 / 滑块 / 拖拽结果全部停在旧像素)。修法:`git reset --hard` 撤销该提交(留 `backup-bad-commit` 标签),只挑回与更新区域无关的功能改动(联动前展开折起组 / 适应视图不动滚动条 / 「回到最上方」按钮 / 网格滚动平滑动画),彻底删除 `m_paintPlot`/`m_panelDirty`/`panelOnly`/并集回送;宿主只保留 3 处最小改动且 `Render` 的绘制与 BitBlt 与 `82b5ebe` 逐字节相同。验证:`.preflight/diag` **19/19**(新增离屏可见窗口探针:滚动条拖动 / 悬停光晕出现与消退 / 信号拖拽换网格)、`.preflight/check` **137/137**、源码护栏 `.preflight/diag/verify_blit.ps1` **10/10**(A/B:换回坏版本 ⇒ 4 项如实失败)、`make release` 零警告。教训:① 离屏探针**测不到窗口回送**(屏幕外 DWM 不合成、窗口 DC 恒空白)⇒ 回送类约束必须有源码护栏;② 离屏窗口 + `TrackMouseEvent` 会**立刻**收到 `WM_MOUSELEAVE`,探针须丢弃该消息;③ 网格回收会让下标左移,断言要用锚信号。 |
|
||||
|
||||
| 2026-10-17 | DeepSeek-V4.1-Flash(**本机**) | **网格底衬永不空白 + 动画流畅性根治 + 光标随首个信号重现 + 「放大自动点线」新判据(详见 §9.42)**。① **网格底衬消失**:旧跨度图层的 `StretchBlt` 占位拉伸在深度放大时把一小段像素放大几十倍 ⇒ 整块网格变成**无网格的模糊色块**;叠加待重画队列**按下标顺序**消费(可视网格要等上百帧)⇒「消失 + 要等一会才刷新好」。修法:**删除占位拉伸**,改为对拿不到像素的网格**就地补画网格底衬**(`PaintLaneGridBacking` + `m_renderGridOnly`,代价极低可每帧执行),队列改**可视网格优先**,结算判据改 `LanesVisiblyReadyForView()`。② **动画卡顿(两处根因)**:**(a)**「`m_layerRebuildPending` 非空就每帧请求再送一帧」在上百个网格时挤出 100+ 次重绘、把优先级更低的 `WM_TIMER` **饿死**(实测 `frames=124/timerTicks=1`)⇒ 改用 `AnyVisibleLanePending()`(`frames=25/timerTicks=16`);**(b)** `SetTimer(...,16,...)` 被系统 tick **量化成 ~26ms(≈38fps)** ⇒ 周期改 **10ms**(实测 ~15.5ms ≈ 64fps,真实消息循环帧距 ~25ms → ~16ms);**(c)** 网格滚动量原本参与「宽基底可用性」判据 ⇒ 滚动动画每帧整幅重绘 ⇒ 新增 `SceneContentHash(..., includeLaneScroll)`(判据不计、场景缓存仍计),单帧 **18.36ms → 1.19ms**。③ **光标随首个信号重现**:新增 `ResetCursorPlacementOnFreshContent()`(与 `CycleCursorMode()` 首次启用同源),接入 `SynchronizeView` 的「首次出现可见信号」分支。④ **「放大自动点线」新判据**:任一小格内数据点 < 5 即切换(`AutoPointLineNeeded`;二分/鸽巢封顶);⚠️ 光标吸附仍用旧整屏判据(用户要求"其它规则不变")。验证:新增 `.preflight/perf/gridanim_main.cpp` **55/55**(含真实消息循环帧距统计、光标重现、点线规则);`verify` 49/49、`panelanim` 156/156、`datacheck` 69/69、`edgecheck` 46/46、`errcheck` 19/19;`make release` 零警告。⚠️ 教训:`cardBg == plotBg` ⇒ 颜色判据色盲;后备缓冲区持久 ⇒ 「看不到空白」不能证明没问题;网格分界线会让「有锐边」判据假绿(须要求同列多行)。§11.2 新增第 117–124 条。 |
|
||||
|
||||
## 11. 跨设备协作须知(起源设备视角)
|
||||
|
||||
> **本文件是唯一权威记忆**。所有设备 / Agent 共用它,**不要再新建 `agent_mem_origin.md` 等旁支记忆文件**。
|
||||
@@ -3152,6 +3250,51 @@ else if (panelOnly) { /* 只回送“本帧重绘过的区域并集” */ }
|
||||
116. ⚠️ **对“看起来坏了”的现象,先怀疑探针自己的取点与判据**:本轮先后误判“悬停不生效”
|
||||
“信号没换网格”,实际都是探针取点踩到了「GX 重排徽标 / 图例 / 时间光标线」这些
|
||||
**优先级更高的热区**,或用了错误的比较基准(拿滚动后的 rect 当比较区域)。
|
||||
117. ⚠️⚠️ **宽基底快路径里拿不到图层像素的网格必须【就地补画网格底衬】,绝不允许留白**:
|
||||
旧实现 `continue`(留白)或用旧跨度像素 `StretchBlt` 放大占位(深度放大 ⇒ 一片无网格的模糊色块)
|
||||
—— 两者在用户眼里都是「网格叠底消失,没有任何内容」(用户 2026-10-17 报告)。
|
||||
✓ `PaintLaneGridBacking`(经 `m_renderGridOnly` 复用 `PaintLane` 的网格部分:底色 + 网格线 + 分界线,
|
||||
跳过波形/散点/派生曲线/Y 轴刻度/图例)。**底衬代价极低 ⇒ 每帧都可以画**。
|
||||
⚠️ 顺序:**先画底衬(GDI+)再 `g.GetHDC()` 做 BitBlt**(取到 HDC 后不能再调 GDI+ 绘制)。
|
||||
⚠️ 只有**所有**网格都贴不出像素时才返回 false(交给整幅重绘),否则会停在「只有底衬、没有波形」。
|
||||
⚠️ 绝不要恢复 `StretchBlt` 占位拉伸。
|
||||
118. ⚠️ **待重画队列必须【可视网格优先】**(`RenderPendingLaneRebuilds` 里 `std::stable_partition` +
|
||||
`LaneVisuallyRelevant`):队列是按下标顺序登记的,上百个网格时用户正在看的那些要等上百帧
|
||||
⇒ 表现是「需要等一会才正确加载」(用户 2026-10-17 报告)。同理「结算」判据用
|
||||
**`LanesVisiblyReadyForView()`**(不要 `LanesAllReadyForView()`:离屏图层与画面无关,会让结算被拖后上百帧)。
|
||||
119. ⚠️⚠️ **“是否请求宿主再送一帧”只能看【还有没有可视网格待补】**(`AnyVisibleLanePending()`),
|
||||
**绝不能**用「`m_layerRebuildPending` 非空」:上百个网格时队列长期残留大量**离屏**网格 ⇒ 每帧都请求重绘
|
||||
⇒ 700ms 内挤出 100+ 次重绘,而 `WM_TIMER` 优先级**低于投递消息** ⇒ **动画定时器被饿死**
|
||||
(实测 `frames=124 / timerTicks=1`;改为可视判定后 `frames=25 / timerTicks=16`)。离屏网格改为**机会式**预热。
|
||||
120. ⚠️⚠️ **`SetTimer` 的间隔会被量化到系统 tick(默认 15.6ms)**:请求 16ms 实际等到**下一个** tick
|
||||
⇒ 实测帧距 **≈26ms(只有约 38fps)**,所有过渡动画都"不平顺"(用户 2026-10-17 报告)。
|
||||
✓ 动画定时器周期必须 **≤ 一个 tick**(当前 `kScrollTimerPeriodMs = 10` ⇒ 实测 ~15.5ms ≈ 64fps)。
|
||||
实测映射:8ms→15.8 / 10ms→15.5 / 12ms→16.5 / **16ms→26.4** / 31ms→37.9。
|
||||
⚠️ `timeBeginPeriod(1)` 在本机**无效**(已 A/B 验证),不要指望它。
|
||||
121. ⚠️ **「宽基底可用性」判据(`SceneGeometryHash`)绝不能计入网格纵向滚动量 / 各网格屏幕纵向位置**
|
||||
(`SceneContentHash(..., includeLaneScroll=false)`):它们只是图层的**贴图落点**,
|
||||
图层内容完全不变(`BlitWideBuffer` 按 `lane.rect` 逐网格贴图)。
|
||||
若计入 ⇒「网格滚动到可视区 / 回到最上方」**每一帧都退化成整幅重绘**
|
||||
(实测单帧 18.36ms → 1.19ms)。⚠️ **静态场景缓存**的指纹**仍然必须**计入 `LaneScroll()`(缓存内容取决于它)。
|
||||
122. ⚠️ **「放大自动点线」判据(用户 2026-10-17 重新定义,推翻「一屏内数据点 < N×4」)**:
|
||||
按 `GridMinorCellCount()` 切成横向小格,**只要存在任何一个小格内有效数据点 < 5** 就切「点线」
|
||||
(`PlotView::AutoPointLineNeeded`)。实现:先「总有效点 < N×5」O(1) 充分判定;
|
||||
单调时间轴逐小格**两次二分**(单格累计到 5 个即早退,另给 `kAutoPointLineCellScanCap=512` 封顶);
|
||||
非单调时一次有界扫描分桶。保留「视界内至少一个可见点」的保护。
|
||||
⚠️ **光标吸附仍用旧的整屏判据**(`SignalSnapEnabled`)—— 用户明确要求“其它规则不变”,
|
||||
§9.30 的「自动点线 / 吸附同源」自本轮起**不再成立**。
|
||||
123. ⚠️ **光标工具激活且【没有任何信号绘制】时,首个信号出现后光标要按规则重新出现**:
|
||||
`PlotView::ResetCursorPlacementOnFreshContent()`(规则与 `CycleCursorMode()` 首次启用**同源**),
|
||||
由 `Application::SynchronizeView` 的 `else if (!tab.ViewInitialized())`(= 首次出现可见信号)分支在
|
||||
`FitAll()` **之后**调用(用户 2026-10-17 要求:不要再停在旧的残留位置)。
|
||||
124. ⚠️ **探针里的动画计时**:启动动画后必须**先画一帧**(`Render` 才负责 `SetTimer` 武装动画定时器),
|
||||
否则真实消息循环无消息可等 ⇒ `frames=0` 假失败。真实循环计时还必须把窗口置为
|
||||
**「离屏但可见」**(`-30000,-30000` + `WS_EX_TOOLWINDOW` + `SWP_SHOWWINDOW|SWP_NOACTIVATE`),
|
||||
隐藏窗口**没有** `WM_PAINT`。
|
||||
⚠️ **`theme.cardBg == theme.plotBg`(都是 kBase3)**⇒ 用颜色区分「空白/已绘制」的探针判据是**色盲**的;
|
||||
判断网格是否画出必须用结构判据(竖直锐边),且必须要求「同一列多行都有锐变」(否则网格**分界线**会让
|
||||
每列都像有锐边)。⚠️ **后备缓冲区是持久的**:宽基底路径只把绘图区填成 plotBg(==cardBg),
|
||||
本帧没被覆盖的网格**保留上一帧像素** ⇒ 探针「看不到空白」不能证明没问题。
|
||||
|
||||
### 11.3 离屏验证方法学(本机偏好,强烈建议沿用)
|
||||
|
||||
|
||||
Reference in new issue
Block a user