修复悬停高亮变长方形色块/联动失效 + 信号列表动画卡顿真因(行位图缓存)
一、悬停高亮恢复"整条曲线发光"(上一轮裁成鼠标附近一段 ⇒ 满幅方波上就是个长方形色块) 光晕的语义是"整条曲线一起发光",对编码器这种满幅方波,一列包络本来就跨满网格高, 裁成 144px 宽 × 满高的窗口在屏幕上就是一个像选区框的色块(用户 2026-10-18 第二轮报告)。 修法(PlotViewPaint.cpp): - PaintSignalHaloFor 恢复**整条**几何(包络带 FillPath / 折线 DrawLines); - 新增 PaintSignalHaloCached:光晕几何只取决于「信号 + 视界 + 网格几何 + 样式」,**与鼠标无关** ⇒ 渲染一次进离屏位图,之后每帧只做一次 AlphaBlend(源位图预乘 alpha,用 256×256 查表); - 缓存键与 AcquirePolyline 同源,**不含网格屏幕位置**(滚动网格只按新位置贴同一张位图)。 210ms/帧(整条描边)→ 一次性 15ms 渲染 + 每帧 ~2ms 贴图。 halocheck 校验:悬停必命中(60/60 位置)、光晕贴合带宽(outsideBand=0)、不越出网格、 bbox 覆盖整条曲线(不是中段方块)、列表行悬停联动仍生效。 二、信号列表动画卡顿的三个真因(探针 panelstutter:129 个可见网格 + 反复"展开→切组复选框→收起") 1) 面板每帧重画 ~36 行 × ~0.35ms(十来个 GDI+ 原语)⇒ panel.Paint 24~35ms; 2) 动画帧里仍在预热网格图层:每帧重建 1 个网格图层,2500 万采样信号 10~30ms ⇒ 帧被拉长; 3) 勾选分组会把上百个**离屏**网格排进待重画队列,随后每帧补 1 个 ⇒ 数秒持续掉帧。 修法: 1) 行位图缓存(SignalPanel::PaintRowCached):按"行的可见内容"做键,整行渲染进离屏 DIB, 命中时一次 BitBlt。panel.Paint 24~35ms → 5~6ms。 ⚠️ 必须 DIB + 内存 DC(gd::Bitmap 上 GDI 会关闭 ClearType,整列文字退化); ⚠️ 行内几何在 RowRect 里平移(GDI 文本不认 GDI+ 世界变换,否则文字画到小位图之外); ⚠️ 渲染到行位图时不按列表矩形裁剪,裁剪放到贴图时的 DC(GDI 不认 GDI+ 裁剪, 必须自己 SaveDC + IntersectClipRect,折起动画时行不许画到组名上); ⚠️ 缓存上限 192 行(≈10MB)。静态画面逐像素一致(面板区域 54/275200 不同)。 2) 新增 PlotView::SetAnimationBusy:UI 动画期间不预热网格图层(队列保留,动画结束后继续)。 3) 待重画队列只保留可视 + 预取带内的网格;RollWideBuffer 不再登记离屏网格。 实测:展开 p50 35ms → 13ms、收起 33ms → 13ms、动画帧内图层重建 56 次 → 0 次。 三、试过并放弃(避免以后重走) - ExtTextOutW 替代 DrawTextW:快 2~4 倍但光栅化不同(面板 10% 像素差异)⇒ 观感变化,放弃; - 用 gd::Bitmap 做行位图:文字丢失 ClearType,放弃。 四、验证(全部离屏,不占用鼠标/键盘/屏幕) - 新增 .preflight/perf/panelstutter_main.cpp(8/8)、halocheck_main.cpp(16/16); - 回归 verify 49/49、panelanim 158/158、hoverdrag 20/20、gridanim 93/93、lanescroll 23/23、 datacheck 69/69、edgecheck 46/46、errcheck 19/19、panflash 19/19、stress 6/6; - make release / make debug 零警告(TARGET_FILE_NAME=TargetCheck/TargetCheckD)。 文档:Documents/agent_mem.md 追加本轮小节(含 4 条新规则:高亮语义不得为性能让步、 GDI 文本的三个隐形陷阱、面板卡顿先量"每行成本×可见行数"与"本帧是否顺带做绘图区重活"、 动画帧内绝不做可延迟的整帧级工作)。
This commit is contained in:
1 parent
0efaa8c0ca
commit
24f0bf849d
11 files changed
+853
-197
No files matched your search
@@ -148,23 +148,15 @@ namespace jdi
|
||||
inline const float kMarkerTriangleGap = 4.0f;
|
||||
inline const size_t kMaxScatterPoints = 2500; /**< 单条散点最大绘制点数。 */
|
||||
/**
|
||||
* @brief 悬停光晕"描边窗口"的半宽(逻辑像素,使用时乘缩放)。
|
||||
* @brief 悬停光晕缓存中「关闭抗锯齿」的顶点数阈值。
|
||||
*
|
||||
* ⚠️ 光晕是**用加粗 GDI+ 折线描边**画出来的,而 GDI+ 描边的代价与
|
||||
* 「线段数 × 每段覆盖的行数」成正比:编码器/方波这类**满幅竖向锯齿**信号
|
||||
* 1 条 2503 顶点的折线实测要 **210ms/帧**(用户 2026-10-18 报告:
|
||||
* 鼠标划过巨型信号的曲线时整帧卡死、浮动提示不再跟随鼠标)。
|
||||
* 因此只描边鼠标左右各 `kHaloWindowHalfWidth` 像素内的顶点:
|
||||
* 顶点数降到几十个 ⇒ 单帧 <1ms,而"鼠标附近那条曲线发光"正是悬停提示该有的语义。
|
||||
* 光晕是**沿整条曲线**画出来的(不能只画鼠标附近的一段:在满幅方波上那看起来
|
||||
* 就是一个长方形色块,像选区框 —— 用户 2026-10-18 第二轮报告)。它的代价只
|
||||
* 在「光晕缓存重建」时付一次,因此通常可直接用抗锯齿;但顶点很多的区间
|
||||
* (深放大后的方波:每段都跨满网格高)GDI+ 抗锯齿的扫描线代价 ≈ 线段数 × 覆盖行数,
|
||||
* 实测可差 20 倍以上。这类密集情形关闭抗锯齿(几何完全不变,半透明光晕上看不出差别)。
|
||||
*/
|
||||
inline const float kHaloWindowHalfWidth = 72.0f;
|
||||
/**
|
||||
* @brief 顶点数达到该值才启用描边窗口裁剪。
|
||||
*
|
||||
* 顶点少的信号(一屏内只有几百个采样)整条描边的代价本来就极小,
|
||||
* 整条发光观感更好,因此不做裁剪。
|
||||
*/
|
||||
inline const size_t kHaloWindowMinPoints = 360;
|
||||
inline const size_t kHaloNoAaMinPoints = 360;
|
||||
inline const size_t kPolylineCacheLimit = 64; /**< 折线顶点缓存条目上限。 */
|
||||
inline const float kMinScatterRadius = 1.6f; /**< 散点最小半径(逻辑像素)。 */
|
||||
inline const float kMaxScatterRadius = 12.0f; /**< 散点最大半径(逻辑像素)。 */
|
||||
|
||||
Reference in new issue
Block a user