悬停光晕 210ms/帧 → 4ms(包络带填充 + 光标窗口裁剪)+ 平移丢波形判据修正 + 随机组合压力测试
一、悬停巨型信号时整帧卡死(用户报:VERY_BIG_DATA 的「编码器A相」,浮动提示不再跟随鼠标)
探针复现:hoverMean = 210.31ms / baseline = 0.90ms。
根因:PaintSignalHalo 用「加粗 GDI+ 折线」描边整条曲线,而包络带信号的顶点是
「每像素列上/下包络交替」的满幅竖锯齿 —— GDI+ 描边代价 ≈ 线段数 × 覆盖行数
(2503 顶点 × 1081 行)。同一几何改用 FillPath 只要 ~1ms。
修法(PlotViewPaint.cpp / PlotViewInternal.hpp):
1) 包络带信号(cached.bandMode)的光晕改为**填充与曲线本身同一几何**的包络带
(bandBreaks 段 → 上包络正向 + 下包络反向,alpha 0x55);
2) 只画鼠标附近一段(kHaloWindowHalfWidth=72 逻辑像素、kHaloWindowMinPoints=360):
· 包络带:窗口换算成列区间再填充(下包络反向存储 ⇒ 列 c 在 start+half+(half-1-c),
列区间恰对应一段正向可读的连续区间);
· 折线描边:O(n) 扫一遍取窗口内的连续顶点段,段首尾各多含一个顶点(跨窗口的线段完整画出);
· 散点高亮同样按窗口过滤(步长仍与 PaintLane 同源)。
3) 窗口中心 = 鼠标 x;鼠标不在绘图区时取绘图区中心(列表/样式区悬停代价同样恒定)。
实测 210ms → 4.0ms(提示恢复跟手)。几何正确性用「透明位图上两次绘制」探针确认:
窗口内与整条光晕逐像素相同(mismatched=0)、窗口外 0 像素(far=0)、全部落在包络带内(outsideBand=0)。
二、组折叠/展开动画卡顿甚至中断(用户用 MF4→BIG→MF4 + 只勾「编码器A相」稳定复现)
这是上面那条的连带症状:列表悬停时 Application::Render 把 panel.HoveredSignal() 推给
SetExternalHoverSignal ⇒ 鼠标每动一次就重画一次光晕;悬停在 2576 万采样的编码器行上
= 210ms/帧 ⇒ 动画定时器被饿死,看起来「卡顿甚至中断」。
同一修复后列表悬停帧 0.72ms(≈ 基线 0.81ms)。探针 [3b] 固化该判据。
三、快速中键拖拽「所有网格信号一起消失」——主修复已正确,之前的失败是度量错了
上一轮把「位移超出 kLayerRollMaxShiftPixels 就作废图层」改成
「driftLimit + 每帧最多滚 kLayerRollMaxStepPixels=240px」,剩余位移由 BlitWideBuffer 的
srcX 承担 ⇒ |shift| 可长期停在 ±(margin-24) 附近而**不会空白**。
之前探针拿「适应视图时的墨迹」当基准,而平移本身会移出数据范围(时间轴外没有采样),
墨迹自然下降 ⇒ 误报。正确判据(已固化进 panflash / stress):
同一次拖拽中把当前帧与**同一视界的整幅强制重绘**(m_sceneValid=false + 清空 m_layerFront)比墨迹,
比值 < 0.9 才算丢波形。三条拖拽模式(120px/帧、90×7 斜向、260px/帧)全部 badFrames=0,
worstRatio 0.966~0.999;MF4 8 个单点信号网格 framesWithMissingPoints=0。
四、新增随机组合压力测试(用户第 4 项要求:给出评估方法与压力验证)
.preflight/perf/stress_main.cpp:1 个 VERY_BIG_DATA(5×2576 万采样)+ 6 个 MF4 分组
(~1500 信号、41 个可见网格),固定种子的随机序列覆盖:中键拖拽(抽样做空白判据)、
滚轮缩放/分布缩放、悬停扫掠、组折叠展开(**每帧都把鼠标扫过正在出现/消失的行**,并校验
m_contentHeight 单调)、适应视图、面板滚动条拖拽、勾选/取消信号、标签页新建/切换/关闭。
判据全部为**同一次运行内的相对量**(基线帧 vs 交互帧),避免绝对 ms 阈值的漂移。
实测:ops=26 frames=209 mean=8.53ms worst=44.87ms(toggleSignal) overBudget=0;
pan blank check 90 个车道样本 0 个低于整幅重绘的 80%;group animation heightRegressions=0。
最慢帧是勾选信号(41 个网格全部重画曲线像素),属于用户明确排除的「需要重绘信号曲线」场景。
五、验证(全部离屏,不占用鼠标/键盘/屏幕)
- 新增 .preflight/perf/panflash_main.cpp(panflash.exe)19/19:三条拖拽模式的丢波形判据、
MF4 单点网格、悬停耗时(4.0ms)、列表悬停联动(0.72ms)、光晕窗口化几何精确性;
- 新增 stress 6/6;既有回归 verify 49/49、panelanim 158/158、hoverdrag 20/20、
gridanim 93/93、lanescroll 23/23、datacheck 69/69;
- make release TARGET_FILE_NAME=TargetCheck 通过(Target.exe 被运行中的实例占用)。
文档:Documents/agent_mem.md 新增本轮小节(含 4 条新规则:GDI+ 加粗描边不得用于包络带几何、
随鼠标变化的绘制必须 O(光标附近)、判据必须是同一次运行内的相对量、平移正确性只能与整幅重绘对比)。
This commit is contained in:
1 parent
fd606a6f80
commit
0efaa8c0ca
4 files changed
+288
-50
No files matched your search
@@ -78,15 +78,13 @@ namespace jdi
|
||||
inline const int kLayerRollQuantumX = 32;
|
||||
|
||||
/**
|
||||
* @brief 图层横向位移超过该值(位图像素)时就**作废图层**(而不是同步整块重画)。
|
||||
* @brief 单次「横向重新居中」的**最大**滚动量(位图像素)。
|
||||
*
|
||||
* ⚠️⚠️ 在前台同步重画一条**巨型信号**的图层实测 **20~130ms**;
|
||||
* 拖拽时只要有一个“离屏累积了大位移、刚进入预取带”的网格就会卡一下。
|
||||
* 作废后本帧由 `BlitWideBuffer` 的**网格底衬**兜底(廉价),波形等视界停下后
|
||||
* 由 `RenderPendingLaneRebuilds` 按预算逐个补 —— 这正是用户要求的
|
||||
* “没来得及画信号时就按只有网格叠底的状态继续滚动”。
|
||||
* ⚠️ 滚动本身是廉价的(BitBlt + 补一条 `|roll|` 宽的竖带),但补条要重建那条带的
|
||||
* 折线/包络 ⇒ 代价与 `|roll|` 成正比。限制单次步长可以把“快速拖动”的补条代价
|
||||
* 摊到多帧;余额由 `BlitWideBuffer` 的源偏移承担(**不会空白**,也不会画错)。
|
||||
*/
|
||||
inline const int kLayerRollMaxShiftPixels = 160;
|
||||
inline const int kLayerRollMaxStepPixels = 240;
|
||||
|
||||
/**
|
||||
* @brief 每帧最多为多少个网格做**横向重新居中**(补细条)。
|
||||
@@ -149,6 +147,24 @@ namespace jdi
|
||||
*/
|
||||
inline const float kMarkerTriangleGap = 4.0f;
|
||||
inline const size_t kMaxScatterPoints = 2500; /**< 单条散点最大绘制点数。 */
|
||||
/**
|
||||
* @brief 悬停光晕"描边窗口"的半宽(逻辑像素,使用时乘缩放)。
|
||||
*
|
||||
* ⚠️ 光晕是**用加粗 GDI+ 折线描边**画出来的,而 GDI+ 描边的代价与
|
||||
* 「线段数 × 每段覆盖的行数」成正比:编码器/方波这类**满幅竖向锯齿**信号
|
||||
* 1 条 2503 顶点的折线实测要 **210ms/帧**(用户 2026-10-18 报告:
|
||||
* 鼠标划过巨型信号的曲线时整帧卡死、浮动提示不再跟随鼠标)。
|
||||
* 因此只描边鼠标左右各 `kHaloWindowHalfWidth` 像素内的顶点:
|
||||
* 顶点数降到几十个 ⇒ 单帧 <1ms,而"鼠标附近那条曲线发光"正是悬停提示该有的语义。
|
||||
*/
|
||||
inline const float kHaloWindowHalfWidth = 72.0f;
|
||||
/**
|
||||
* @brief 顶点数达到该值才启用描边窗口裁剪。
|
||||
*
|
||||
* 顶点少的信号(一屏内只有几百个采样)整条描边的代价本来就极小,
|
||||
* 整条发光观感更好,因此不做裁剪。
|
||||
*/
|
||||
inline const size_t kHaloWindowMinPoints = 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