fix(plotview): 边沿计数表格归位、统计面板悬停时持续更新、加载不再被统计阻塞、统计提速
1) 表格改造归位(上一轮误做在中心轴上) - 「边沿计数」改为表格:表头(信号名/上升沿计数/下降沿计数)、每信号一行、 行底色取信号配色、三列对齐、内容可滚动(Lane::edgeCountScroll)。 - 「中心轴边沿计数」恢复为可滚动分组列表(条目栏 + 复选框 + 展开后统计行 + 两行滑块), Lane::EnsureEdgeCenterItem 恢复默认展开。 - EdgePanelWidth / EdgePanelLayoutFor / PaintEdgeCursor 统一 listStyle / countTable 判定; EdgeCenterTableHeader 更名为 EdgeCountTableHeader。 2) 鼠标持续移动时统计面板不更新 - 根因:交互态(拖拽或 140ms 内移动)把统计数字整段从面板缓存键中跳过, 只要鼠标不停移动就永远命中旧缓存。 - 修法:扫描推进/完成时自增 m_edgeScanRevision,Paint 开头转为 m_panelStatsVersion (非交互帧立即同步,交互帧按 kEdgeScanStatsRefreshFrames=4 节流), PanelSlotKey 对 COUNT/CENTER 仅混入该版本号(O(1),不再逐信号查询)。 3) 统计进行中无法加载文件 - 新增 PlotView::SetLoadingBusy:加载期间统计时间预算归零、跳过浮动面板绘制、 不再自请求扫描帧;Application::LoadFileAsync 进出各设置一次。实测加载 22~31ms。 4) 统计提速 - 扫描预算由固定采样对改为 QPC 时间片(非交互 6ms / 交互 1.5ms,每块检查超时), 采样对配额改为按未完成状态数公平分摊并把上限提到 8M(交互 2M)。 - 真实 MF4-Record.MF4(283 信号)全部统计状态在 1 帧内完成;悬停+移动帧中位 2~3ms。 验证:离屏探针 p20/p20c/p20d/p21/p22/p23/p25 全绿(新增 p25 12 项); make release / make debug 零 warning;Release 808,960 B。共享规则已同步到 Documents/agent_mem.md。
This commit is contained in:
1 parent
8de8a8fb85
commit
ea704b03ea
9 files changed
+424
-553
No files matched your search
@@ -1280,6 +1280,17 @@ namespace jdi
|
||||
*/
|
||||
bool PanelRefreshPending() const { return m_panelKeysFrozen; }
|
||||
|
||||
/**
|
||||
* @brief 告知绘图区「宿主正在加载文件」。
|
||||
*
|
||||
* 加载期间主线程会不断 `UpdateWindow` 触发整帧绘制,而面板绘制含统计推进与上百行
|
||||
* GDI+ 原语 ⇒ 加载进度被拖得很慢(用户报告"统计没算完就无法开始加载")。
|
||||
* 加载遮罩会盖住整个界面,因此置真时直接跳过面板绘制与统计推进。
|
||||
*
|
||||
* @param [IN] busy true = 正在加载。
|
||||
*/
|
||||
void SetLoadingBusy(bool busy) { m_loadingBusy = busy; }
|
||||
|
||||
|
||||
/**
|
||||
* @brief 取消网格滚动动画(用户自己滚动网格时调用,避免动画与手动滚动抢滚动位置)。
|
||||
@@ -3138,6 +3149,40 @@ namespace jdi
|
||||
/** 诊断计数:中心轴本地曲线计算失败次数(内存不足等)。0 = 一切正常。 */
|
||||
mutable std::size_t m_edgeScanLocalCurveFails = 0;
|
||||
|
||||
/**
|
||||
* @brief **统计数据版本号**:任何累积状态推进(`scannedPair` 前进)或扫完都会自增。
|
||||
*
|
||||
* 用途:面板像素缓存键不能再逐个信号查询统计数字(288 个信号 ⇒ 单帧 288 次查询,
|
||||
* 且中间数字每帧都变 ⇒ 缓存永不命中 ⇒ 每帧整块重画上百行)。改为混入"把
|
||||
* `m_edgeScanRevision` 按节流规则转成的 `m_panelStatsVersion`"(O(1)、零查询):
|
||||
* 统计推进 ⇒ 版本号变化 ⇒ 面板重绘;没推进 ⇒ 键稳定 ⇒ 贴回缓存。
|
||||
*/
|
||||
mutable std::uint64_t m_edgeScanRevision = 0;
|
||||
/** @brief 已反映到面板缓存键里的统计版本号(见 `m_edgeScanRevision`)。 */
|
||||
mutable std::uint64_t m_panelStatsVersion = 0;
|
||||
/** @brief 上一次同步时看到的 `m_edgeScanRevision`(用于判断"有新进展")。 */
|
||||
mutable std::uint64_t m_panelStatsSeen = 0;
|
||||
|
||||
/**
|
||||
* @brief 本帧统计扫描的**时间预算截止点**(QPC 计数)。
|
||||
*
|
||||
* 旧实现用"每帧采样对配额"限制开销,但同样的配额在稀疏/稠密、普通/中心轴之间
|
||||
* 的实际耗时能差 10 倍:配额小 ⇒ 大文件统计极慢(用户 2026-11-02 报告
|
||||
* "VERY_BIG_DATA 统计太慢");配额大 ⇒ 中心轴(每个采样对还要算窗口均值)单帧卡住。
|
||||
* 改为**按时间切**:扫描循环每处理一个块检查一次是否超时,既不会卡帧又能把
|
||||
* "便宜"的普通计数一次性推进得很快。
|
||||
*/
|
||||
mutable std::int64_t m_edgeScanDeadline = 0;
|
||||
|
||||
/**
|
||||
* @brief 宿主正在加载文件(模态遮罩期间):暂停统计推进与面板绘制。
|
||||
*
|
||||
* ⚠️ 加载线程的主循环会不断 `UpdateWindow` 触发 `Render()`,而面板绘制里含统计推进
|
||||
* 与上百行 GDI+ 原语 ⇒ 加载进度被拖慢(用户报告"统计没算完就无法加载文件")。
|
||||
* 加载期间画布被遮罩覆盖,面板本来也看不见,因此直接跳过。
|
||||
*/
|
||||
bool m_loadingBusy = false;
|
||||
|
||||
/**
|
||||
* @brief 累积状态上限(随可见信号总数缩放)。
|
||||
*
|
||||
|
||||
@@ -632,19 +632,36 @@ namespace jdi
|
||||
* 1200000 对 × 11 次浮点运算 ≈ 3 位千万级运算/帧。
|
||||
* 现在交互期间改用 `kEdgeScanPairsPerFrameInteractive`(见 `Paint` 开头)。
|
||||
*/
|
||||
inline const std::size_t kEdgeScanPairsPerFrame = 400000;
|
||||
inline const std::size_t kEdgeScanPairsPerFrame = 8000000;
|
||||
/**
|
||||
* @brief **交互帧**(正在拖动 / 鼠标刚移动过)允许的边沿统计预算。
|
||||
* @brief **交互帧**(正在拖拽 / 鼠标刚移动过)允许的边沿统计**采样对**配额。
|
||||
*
|
||||
* 交互时首要目标是跟手:把预算压到非交互帧的 1/5(实测单帧统计开销 ≈6ms 以内),
|
||||
* 鼠标停下 `kEdgeScanInteractiveIdleMs` 之后自动恢复高预算、快速把统计补齐。
|
||||
* ⚠️ 不能降到 0 —— 那样“按住横向光标不动”时统计会停在“统计中…”。
|
||||
* 真正限制开销的是下面的「时间预算」(`m_edgeScanDeadline`):同样的采样对数在
|
||||
* 普通计数与中心轴(每个对还要算窗口均值)之间能差一个量级,因此**按时间切**:
|
||||
* 采样对配额只负责“公平分摊”(不让第一个状态吃光全帧)。
|
||||
*/
|
||||
inline const std::size_t kEdgeScanPairsPerFrameInteractive = 80000;
|
||||
inline const std::size_t kEdgeScanPairsPerFrameInteractive = 2000000;
|
||||
/**
|
||||
* @brief 非交互帧的统计扫描**时间预算**(毫秒)。
|
||||
*
|
||||
* ⚠️ 用户 2026-11-02 报告“统计太慢(VERY_BIG_DATA)”:旧的固定采样对配额在
|
||||
* 普通计数(1~2ns/对)下把一帧的工作量限得太死,而中心轴(≈7 倍成本)又嫌太大。
|
||||
* 改成按时间切之后,便宜的计数能一次性推进数倍,贵的也绝不会卡住一帧。
|
||||
*/
|
||||
inline const float kEdgeScanTimeBudgetMs = 6.0f;
|
||||
/** @brief 交互帧的统计扫描时间预算(毫秒):优先保证界面跟手。 */
|
||||
inline const float kEdgeScanTimeBudgetInteractiveMs = 1.5f;
|
||||
/**
|
||||
* @brief 鼠标停止移动多久之后恢复高统计预算(毫秒)。
|
||||
*/
|
||||
inline const std::uint64_t kEdgeScanInteractiveIdleMs = 140;
|
||||
/**
|
||||
* @brief 统计数据版本号在**交互期间**的刷新间隔(帧数)。
|
||||
*
|
||||
* 交互帧里统计会继续推进,但每推进一次就重画上百行的面板太贵;节流后
|
||||
* “鼠标一直动、面板一直停在旧数字”的缺陷也修掉了(最慢 ~4 帧 ≈ 60ms 内刷新一次)。
|
||||
*/
|
||||
inline const std::uint64_t kEdgeScanStatsRefreshFrames = 4;
|
||||
/**
|
||||
* @brief 每个累积状态每帧的扫描配额下限(防止多个状态时前面的把预算吃光)。
|
||||
*
|
||||
@@ -701,10 +718,10 @@ namespace jdi
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 表格单元格里的**纯数字**(「中心轴边沿计数」表格用)。
|
||||
* @brief 表格单元格里的**纯数字**(「边沿计数」表格用)。
|
||||
*
|
||||
* 表头已经写了「上升沿计数 / 下降沿计数」,单元格里再写「上升沿 N」就重复了;
|
||||
* 未算完时在数字后追加 `…`(与旧的两列排版同一个语义)。
|
||||
* 未算完时在数字后追加 `…`(与列表式排版的语义一致)。
|
||||
*/
|
||||
inline std::wstring EdgeCountCellText(std::size_t value, bool complete)
|
||||
{
|
||||
@@ -716,8 +733,8 @@ namespace jdi
|
||||
return text;
|
||||
}
|
||||
|
||||
/** @brief 「中心轴边沿计数」表格的表头文本(信号名 / 上升沿计数 / 下降沿计数)。 */
|
||||
inline const wchar_t* EdgeCenterTableHeader(int column)
|
||||
/** @brief 「边沿计数」表格的表头文本(信号名 / 上升沿计数 / 下降沿计数)。 */
|
||||
inline const wchar_t* EdgeCountTableHeader(int column)
|
||||
{
|
||||
switch (column)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user