DeepSeek V4.1 Flash
|
fba40ee1a2
|
「调整」数值符号规则 + 边沿统计按帧推进(含首帧公平分摊)+ 浮动面板排版与弱峰复位控件
一、「调整」类数值的符号(SignalPanel)
1. FormatSignedNumber:0 不再显示 `+`(旧实现恒带符号 → `+0.00`),Y 轴偏移默认值显示 0.00。
2. FormatGainText:非 0 正值带 `+`、负值带 `-`、0 不带符号;仅用于非编辑态显示,
进入编辑的初始文本仍为无符号纯数值。
3. 新增 FormatOffsetTimeText:`Δt:` 后按正负带符号(Δt:+5s 60ms / Δt:-5s 60ms / Δt:0s),
打开面板 / CMD_OFFSET_TIME 提交 / CMD_OFFSET_RESET 三处统一走它。
二、边沿统计:按帧预算 + 每状态公平配额
(用户报告:按住横向光标不动时统计停在“统计中…”不更新;改中心轴参数回车后不动鼠标时停在部分值)
根因:预算按 GetTickCount64() 分桶,而系统时钟粒度约 15.6ms;RequestEdgeScanFrame 的
PostMessage 自持重绘把帧率拉高 ⇒ 同一 tick 内连画多帧却完全不推进。
1. Paint 开头 ++m_edgeScanFrame; m_edgeScanBudget = kEdgeScanPairsPerFrame;(按帧重置),
删除 m_edgeScanTick。
2. 每个状态各自持有每帧配额(pumpedFrame/pumpedPairs):perState = max(60000, 1.2M/share),
share = max(kEdgeScanFairShare=6, m_edgeScans.size())。
注意:不能直接用 size() 分摊 —— 状态在同一帧内逐个创建,第一个那一刻 size()==1 会独占整额,
同帧后面新建的状态全部被截断为 0 配额(即“只有第一条信号推进”)。
实测(合成 5 信号 × 200 万采样、同一网格):首帧 5 个状态全部推进;9 帧内全部扫完(期间不注入
任何鼠标消息);数值与独立参考实现逐位一致;阈值变化后就地重置、随后“停住不动”仍精确扫完。
三、浮动面板排版
1. 信号名宽度上限 kEdgePanelNameMaxWidth=150 逻辑像素:宽度计算与绘制都用 min(实测, 上限) +
UiEllipsize(300 字符名字只比单字符宽 44px,旧实现要多 5100px)。
2. 统计信息两列等宽(新增 EdgeRisingText/EdgeFallingText/EdgeCountsPending):面板宽度按
2×max(两列文本宽)+kEdgePanelStatColumnGap 预留,绘制时两列各占 (可用宽−间距)/2 ⇒ 多条信号的
“上升沿/下降沿”列起点固定、严格垂直对齐。
3. 未完成时 rising 与 falling 两个数字后面都带 `…`(旧实现只在整行末尾追加一次,
视觉上只跟在“下降沿”的数字后面)。
四、弱峰调整面板
1. 每行滑块右侧新增复位控件(复用还原图标绘制 PaintPanelResetIcon):悬停高亮 + IDC_HAND +
一次性提示;点击恢复幅度 0 / 迭代 1 且 maxIterations 回到初始上限 10。
EdgePanelLayout 新增 rowLevelResets/rowIterResets(并进 PanelSlotKey),两行按钮共用同一 X。
2. 迭代次数上限输入框的空内容提示改为“输入上限(当前N)”。
3. 滑块标签去掉多余的 %/N(kWeakPeakLevelLabel=调整幅度;新增 kWeakPeakIterationsSliderLabel=
迭代次数;MEAN / CENTER 输入框的“迭代次数 N”保持不变)。
五、验证(全部离屏;进程/线程 BELOW_NORMAL、窗口 SW_HIDE、不注入真实鼠标键盘)
- 新增 .preflight/perf/panelcheck_main.cpp:46/46 通过(符号规则走真实菜单路径;按帧推进 /
不饿死 / 精确性 / 自持驱动;两列等宽预留(含未完成时两处 …);名字上限;
弱峰复位控件位置 / 对齐 / 点击恢复默认值与上限)。
- 既有 verify_main.cpp:47/47(另 2 条为本机 .preflight 未随远端同步的旧规则断言)。
- make release 零警告。
规则同步:Documents/agent_mem.md 新增 §9.26,§11.2 新增第 43–48 条硬约束,变更日志补一行。
|
2026-10-15 01:35:47 +08:00 |
|
DeepSeek V4.1 Flash
|
65f7625dce
|
修复「偶发的改一个信号的中心轴参数、其它信号也重新统计」
一、现象(用户报告)
加载 VERY_BIG_DATA.CSV、所有信号显示在同一网格、同时启用普通边沿计数与中心轴边沿计数
并勾选全部信号;改变某一个信号的中心轴参数时,**偶发**地让其它几个信号的统计也重新计算
(不是必现,多测几次才出现)。
二、根因(用间歇性探针实测定位,不是推测)
EdgeScanFor 的中心轴分支原先靠 AcquireCoveringCurve + ScheduleCurveJob 拿**整条**均值滤波曲线:
- 千万级信号(2580 万采样)一条整段曲线 ≈ 103MB;每多试一个参数值就多一条(参数变化产生新键),
绘制侧按可见窗口还会再发布一份;
- 这些条目共享同一个曲线缓存,预算为 kCurveSpanCacheMaxBytes = 768MB;
- 一旦超限,PruneCurveEntriesLocked() 从**队首**淘汰 —— 最早发布的往往正是**其它信号**的整段曲线;
- 于是那些信号被判为「曲线未就绪」→ 界面显示「统计中…」并在后台重新排队计算 →
表现出来就是「其它几个信号重新统计」,而且**偶发**(取决于当时缓存里积了多少条目)。
⇒ 统计数值本身是正确的,错的是「几条信号的统计通过一块共享缓存互相牵连」,
而这块缓存的压力取决于用户试过多少个参数值。这才是要根除的问题。
三、修法
1. EdgeScanState 新增 localCurve / localBegin / localEnd / localValid:
中心轴扫描时**就地计算**自己需要的一小段均值滤波曲线(ensureLocalCurve):
- 半径 = points * iterations(迭代 N 次 X 点均值滤波最多向两侧传播 X*N 个采样),
再夹到 kDerivedCurveGuardMax;
- 窗口至少覆盖 8 个扫描块(kEdgeBlockSamples * 8),把「窗口 × 迭代次数」的重复计算摊薄;
- 窗口只有几十 KB~几百 KB,与曲线缓存、与其它信号**彻底解耦**;
- 计算成本按「采样数 × 迭代次数」计入扫描预算(不会一帧算太多);
- **扫完后立即释放** localCurve(区间查询需要时按需重算)→ 多信号网格下常驻内存很小。
2. 删除原「中心轴曲线未就绪就把整 tick 扫描预算清零」的门控:
它会把**同一帧里排在后面的其它信号的统计一起饿死**(测试里表现为全部卡在「统计中」)。
要「让路」只应跳过自己。
3. 加固状态上限:kEdgeScanStateLimit 16 → 64,且超限时**优先淘汰「已扫完」的最旧状态**,
绝不淘汰正在推进的。上限太小会淘汰其它信号的已扫完状态 → 它们从头重扫(同一类症状);
实测 10 个信号 × (普通 + 中心轴)= 20 个状态,原来的 16 就会踩到。
4. 附带收益:统计不再等后台算整条曲线,超大文件下第一个统计值出现得更快。
四、验证(全部离屏,harness 与探针均为 BELOW_NORMAL 优先级,不占用鼠标/键盘/屏幕)
1. uitest 新增 [AU](间歇性复现探针):5 个信号合并在同一网格、普通 + 中心轴全部启用,
用真实编辑路径**连续改 25 次**某个信号的中心轴参数,每轮 14 帧逐帧检查**未被改动的**统计:
CASE A = 状态被删除;CASE B = 状态还在但其曲线消失(显示 0 / "统计中");
CASE C = 状态被重置或截断。
- 修复前:baseline **0/10 完成**(全部卡在「统计中」,此时 curve entries 0);
- 修复后:baseline **10/10 完成**,25 次编辑 **CASE A/B/C 全 0**,且 curve entries 0
(证明统计已完全不需要后台曲线缓存)。
2. 新增「本地窗口 vs 整条曲线」正确性断言:points ∈ {5,17,64,300} × iterations ∈ {1,2,4}
共 12 组,中心轴结果与「整条信号均值滤波后逐点判定」的参考实现**逐位一致**
(证明 X*N 半径的保护带足够)。
3. 合计 789/789 通过(连跑 3 次:2 次全绿,1 次命中既有的菜单点击类 harness 抖动——
[AP] reset 图标与面板菜单,重跑即好,与本轮改动无关);make release 零警告。
规则同步:Documents/agent_mem.md 新增 §9.25,§11.2 新增第 40–42 条硬约束
(派生曲线不得依赖共享整条曲线缓存 / 预算不得因等待而清零 / 状态上限必须大于 种类×信号数),
变更日志补一行。
|
2026-10-14 17:19:09 +08:00 |
|
DeepSeek V4.1 Flash
|
48bc534bc1
|
边沿统计改为「定向失效」:改一个信号的参数不再重算其它信号的统计
一、问题
加载 VERY_BIG_DATA.CSV 并把所有信号显示在同一个网格,同时启用「边沿计数」与
「中心轴边沿计数」(两个面板都作用于网格内全部信号)。此时改变**一个**信号的
中心轴边沿计数参数,两个浮动面板里的**所有**统计值都从头重新计算。
二、根因
参数提交路径(PlotView::EndEdit)与勾选切换路径里用的是 m_edgeScans.clear():
它把两个面板、所有信号的累积统计状态全部丢掉。于是:
- 其它信号(参数没变、键没变、块和依然有效)被迫从头重扫;
- 连普通「边沿计数」(centre == false)的状态也被一起丢掉 → 另一个面板也全部重算。
三、修法
1. 新增 PlotView::InvalidateEdgeScansFor(const SignalData* signal, bool centreOnly):
按键(文件/信号/种类/样式指纹/参数/区间)只擦除**该信号**的累积状态;
centreOnly = true 时只擦除它的「中心轴」状态。
2. EndEdit 的参数提交分支:
- 中心轴参数(EDGE_CENTER_POINTS / EDGE_CENTER_ITERATIONS)变化 →
InvalidateEdgeScansFor(该信号, true);该信号的普通边沿计数、以及其它信号完全不受影响;
- 均值滤波参数变化 → 与统计无关(中心轴用**自己的**条目列表与自己的曲线键)→ 不作废任何统计。
3. 勾选/取消勾选信号:该信号的统计(与参数相关)依然有效,只是"是否显示"变化 →
删除原来的三个 clear()(m_meanCurveCache/m_edgeScans/m_weakPeakCurveCache),
不再作废任何缓存(改为依赖 m_sceneDerived 快照做局部重画)。
四、验证(全部离屏,不占用鼠标/键盘/屏幕;harness 与探针均降到 BELOW_NORMAL 优先级)
- .preflight/uitest 新增 [AT]:3 个信号合并到同一网格,中心轴统计全部扫完后,通过**真实编辑路径**
(plot.BeginEdit(EDGE_CENTER_POINTS, 0, L"7") + plot.EndEdit(true))修改第 2 个信号的参数,
断言:
* 被改信号的中心轴状态已作废,且其条目参数确实变成 7;
* 另两个信号的 scannedPair / rising **逐位不变**(累计块和没有被丢弃);
* 被改信号的**普通**边沿计数状态 scannedPair / rising **逐位不变**(另一个面板完全未失效);
* 未被改动的信号下一次查询仍然 complete(瞬时返回,不需要重扫)。
- 合计 774/774 通过(连跑 3 次稳定);make release 零警告。
规则同步:Documents/agent_mem.md 新增 §9.24,§11.2 新增第 39 条硬约束(参数变化必须定向失效、
禁止对累积缓存整体 clear()),变更日志补一行。
|
2026-10-14 16:30:12 +08:00 |
|
DeepSeek V4.1 Flash
|
375a15c8c2
|
派生曲线内存治理(弱峰调整等)+ 边沿统计改为统一预算状态机(含中心轴)+ 离屏测试零打扰
一、派生曲线内存治理(按上一轮报告的方案实施;绘制性能优先,同时为小内存设备留出空间)
1. PeakExtremum 字段重排(double value + uint32 index + int8 type):24 → 16 字节。
编码器方波这类极值密集信号省约 1/3 的极值表内存,且**不改变任何数值结果**
(value 保持 double,改成 float 会让结果与历史版本不一致):
实测 3M 采样单次计算的工作缓冲峰值 33.23 → 26.73 MiB。
2. ComputeDerivedWindow 对「整段窗口」不再复制采样(直接引用原向量):
8 B/采样 × 2580 万 ≈ 206 MB/条,而“预览曲线一律按整条信号计算”正是最常见情形。
3. 大窗口任务的并发上限由内存压力决定(CurveJob::hugeLimit:内存充足 2、紧张 1):
单条整段曲线工作缓冲约 620 MB(2580 万采样),原先最多 4 条并发 → 峰值 2.5GB+;
实测 4 并发 3M 采样 412 → 324 MiB,且外推值不再随线程数放大。
4. 内存严重不足时**直接拒绝计算**(DerivedMemoryAllows:可用内存 < 输出曲线本体 + 工作缓冲的
最小需求)并只告警一次(新增 kMessagePlotLowMemory;宿主弹一次明确的提示框)。
界面保持可用,绝不会因内存不足抛 bad_alloc 逸出线程导致进程终止。
5. 低内存模式“少量多次”:throttle = true 时并发降为 1、且每阶之间 Sleep(1),慢慢算完。
内存充足时不做任何节流,尽可能快。
6. 取消标志贯通到算法内部(ComputeWeakPeakAdjust / ComputeMovingAverageIterated 新增
cancel 参数,每阶 / 每 1M 采样检查一次)⇒ **过时任务立即退出并归还工作缓冲**;
PauseDerivedAsync / StopCurveWorker 先 m_curveCancelAll = true 再 join。
7. AvailablePhysicalBytes() 结果按 200ms 缓存(绘制路径上不再每帧多次系统调用)。
8. 极值表按 count/8 预留容量(try/catch(bad_alloc) 保护),避免 push_back 反复扩容的 1.5x 峰值。
9. MeanFilterCurveFor / WeakPeakCurveFor(整条同步计算)现在只有测试使用;
产品代码一律走 CurveSpanFor(窗口化 + 异步)。
二、边沿统计:统一「逐块累积 + 每 tick 预算」状态机(普通边沿计数与中心轴共用)
1. EdgeScanState + EdgeScanFor 成为唯一实现,EdgeCountsFor / EdgeCenterCountsFor 是薄包装;
删除 EdgeCenterCountEntry / m_edgeCenterCountCache。
2. **未完成时主动请求下一帧**(RequestEdgeScanFrame → PostMessage(kMessagePlotDerivedReady)):
修掉“鼠标不动就一直显示统计中,只有把鼠标移到面板上才更新”(根因就是没有重绘 → 预算不推进)。
3. 阈值/参数变化就地把状态重置(不新建条目,避免拖动时状态爆炸);重置当 tick 不展示部分值
(修掉“除了最后一个信号显示了一个错误的且不更新的数字,其它变成统计中”);
稳定后展示递增的部分值 + “…”;扫完展示精确值(统一走 EdgeCountsText)。
4. 中心轴判定所需曲线**只在后台计算**:只在 AcquireCoveringCurve 命中(键完全相同)时扫块,
否则 ScheduleCurveJob 后本帧不推进。原先该调用没传 allowAsyncCompute(默认 false)⇒
首帧 / 参数变化后同步算整条均值滤波曲线 ⇒ 界面停顿数秒(用户报告“一改参数整个程序卡住很久”)。
5. MeanFilterValueAt(光标读数)不再调整条同步的 MeanFilterCurveFor,改为窗口化 + 异步。
6. ScheduleCurveJob 改为幂等(m_curveRunningKeys 集合 + 队列去重):统计侧会每帧请求同一份
整条曲线,若每次都当作新请求,正在算的那份立刻被判过时 → 请求→作废→重请求 死循环。
7. 扫描热路径加“无变换/无镜像/非中心轴”的紧凑循环:单次预算耗时 18ms → 1.3ms。
8. 实测(合成方波):6M 采样首帧 1.4ms(部分值)、单次最坏 2.1ms;
拖动横向光标 10 次查询 1.07ms;**同一帧内 5 条信号合计 1.3ms**(预算跨信号共享);
保持不动无需移动鼠标即收敛到与参考实现逐位一致。
三、离屏测试绝不打扰用户的前台工作
- .preflight/uitest 与 .preflight/probe 的 main 开头设置
SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS) +
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL),
再长的测试也不会与用户的编辑/浏览抢 CPU(此前“打字不顺畅”的根因)。
- 继续保持:窗口 SW_HIDE、只注入合成消息(不动真实鼠标键盘)、每次注入后立即 ReleaseCapture
(SetCapture 是系统级的,不释放会把用户的鼠标输入吸进测试进程)。
四、验证(全部离屏)
- .preflight/uitest 新增/强化:预算推进首帧不显示部分值 → 第二帧显示递增部分值 →
保持不动收敛到精确值;未完成时会 PostMessage 请求下一帧(直接断言);
拖动阈值期间 complete == false、不显示部分数字、总耗时 < 60ms;
5 条信号同帧合计 < 120ms(实测 1.3ms);中心轴与参考实现逐位一致且无需鼠标移动即可完成。
- 合计 751/751 通过(连跑 3 次稳定);make release 零警告。
规则同步:Documents/agent_mem.md 新增 §9.23(含内存治理表与实测数据)、§11.2 新增第 32–38 条硬约束、
变更日志补一行。
|
2026-10-14 15:24:19 +08:00 |
|
DeepSeek V4.1 Flash
|
d6d3fcd6c1
|
新标签页分组默认折起 + GX 徽标悬停不改光标 + 标题栏 RAM 改私有工作集 + 边沿计数改为预算推进
一、新建标签页的信号分组默认折起
- Application::NewTab(bool collapseGroups):启动时的首个标签页传 false(加载文件后可直接勾选
信号),用户新建的标签页(Ctrl+T / 标签栏 ⊕ / 标签栏右键)传 true。
- TabConfig::SetGroupsCollapsedByDefault(bool) + SetAllCollapsed(bool)(新增);
SyncToDataSet 里 m_collapsed.resize(fileCount, m_groupsCollapsedByDefault)(原来固定 false)——
只影响之后新出现的分组,已存在的分组保持用户当前选择;ToggleCollapsed 补建状态时同样用它。
二、网格 GX 徽标:悬停不改变鼠标形状
- PlotView::QueryCursor 里 GX 徽标分支由 IDC_HAND 改为 return nullptr(= 保持箭头),
仍然保持"提前 return"的位置以免被光标线/图例判定抢走优先级;
只有拖拽中(LANE_REORDER)才是 IDC_SIZENS。
三、标题栏 RAM 口径:总工作集 → 私有工作集(任务管理器"内存"列)
- 症状:加载大文件再关闭分组后,标题栏仍显示几十/上百 MB,任务管理器只有几 MB。
- 根因:原实现取 PROCESS_MEMORY_COUNTERS::WorkingSetSize(总工作集),它把映射进来的 DLL /
共享页面以及已释放但尚未被系统回收的驻留页面都算进去;任务管理器"内存"列的口径是私有工作集。
- 修法:ProcessWorkingSetBytes() → ProcessPrivateWorkingSetBytes(),用 PROCESS_MEMORY_COUNTERS_EX2
(MinGW 的 psapi.h 没有该类型 → 在 .cpp 里就地声明,布局与文档一致),失败时依次退回
PrivateUsage(私有提交)→ WorkingSetSize。
- 实测(离屏探针):加载后 total/priv/commit = 117.55/109.96/138.28 MiB;
关闭分组后 = 15.44/7.62/8.27 MiB ⇒ 内存没有泄漏、完整归还,用户的差异来自口径。
四、边沿计数(EdgeCountsFor)改为「逐块累积 + 每 tick 预算推进」
- 量化(探针):改前 6M 采样 = 26.8ms / 次阈值变化 → 25.8M 采样 ≈115ms/条;
横向光标拖动时阈值每帧都变 ⇒ 单条信号每帧 115ms,5 条信号同一网格 = 575ms/帧
(与用户"开启即卡顿、之后光标几乎拖不动"完全吻合)。
- 新模型:
* 采样对 k(采样 k-1 → k)按 kEdgeBlockSamples = 8192 分块,每块各存上升/下降沿数;
scannedPair = 下一个待扫描的采样对([1, scannedPair) 已扫完)。
* 查询区间:内部整块用块和相加,两端边界块就地精确扫描(≤2 块,代价固定)⇒ 拖动时间光标的
单帧代价与区间长度无关(单调时间轴下区间不进键);非单调时间轴无法用下标区间表达 ⇒ 区间进键。
* 每 tick 预算 kEdgeScanPairsPerTick = 1200000(GetTickCount64 分桶,同帧多次调用共享);
预算用完返回当前部分值,complete 出参告诉界面"还没算完"。
* 拖动横向光标(EDGE_MOVE)期间不推进(阈值每帧都变,推进纯属白做功)→ 返回"最新的完整条目"
的结果(数字稳定)+ complete = false。
* 快路径:单调 && 无镜像 && 无调整变换时用紧凑无分支循环直接比较 values[k] 与阈值
(NaN 与任何数比较都是 false ⇒ 天然满足"NaN 不参与且打断连续",无需 isfinite 分支)。
- 界面:新增 EdgeCountsText(rising, falling, complete) 统一生成文本(完全未算出显示"统计中…"、
部分结果追加" …"),EdgePanelWidth 与绘制都改用它,宽度按实际文本测量避免数字被截断。
- 实测(改后):6M 采样首次调用 1.7ms(部分值)、5 帧内扫完且与参考实现逐位一致;
单次调用最坏 1.9ms;拖动中连查 10 个阈值合计 0.11ms;区间查询在扫描完成后一次调用即精确。
- EdgeCenterCountsFor 未改(它的曲线窗口本来就被 CurveSpanFor 限制,不是本次报告的场景)。
五、内存与性能排查报告(第 4 项按用户要求只排查、未改动)
- 泄漏审计:RemoveFile 路径会 PauseDerivedAsync(清 m_curveEntries/m_curveJobs/m_curvePins)
→ InvalidateDerivedCaches(清折线/边沿/均值/弱峰/场景缓存),DataSet::RemoveFile 用
"前移 + pop_back + shrink_to_fit" 立即归还采样缓冲;实测三种内存口径全部回落到十几 MB 以下。
- 弱峰调整内存暴涨(峰值 4.4GB):实测工作缓冲 ≈34 B/采样(主导项 = 极值表 PeakExtremum 24 B/极值,
编码器方波极值密集 ⇒ 接近 24 B/采样),3M 采样单次 ≈103 MiB、4 条工作线程并发 +412 MiB;
按 25.8M 采样外推 ⇒ 单次 ≈0.9GB、峰值 ≈3.5GB,再加数据本体 1.03GB 与曲线缓存预算 ≤768MB
⇒ 4~5GB,与用户观测吻合。
- 现有机制只解决"算完/关掉之后归还"(ReuseBufferScope / TrimDerivedCurveDataIfIdle /
kHugeSignalPreviewSamples / 曲线缓存字节预算),没有任何"峰值上限"与"优雅降级":
全仓没有 try/catch,重分配失败抛 std::bad_alloc 而重计算都在 std::thread 里
(CurveWorkerLoop / RunAsyncLoad),异常逸出线程入口 ⇒ std::terminate() ⇒ 进程终止。
1GB 剩余内存时:先大量换页(系统卡顿),提交失败则直接崩溃。
- 建议(未实施):①超大信号派生计算串行化 ②极值表字段瘦身/分块流式 ③废弃任务尽早退出
④GlobalMemoryStatusEx 自适应降档 ⑤线程入口加 try/catch。
六、验证(全部离屏,不占用鼠标/键盘/屏幕)
- 新增 .preflight/probe(诊断探针):内存口径与泄漏审计、边沿计数扫描耗时、弱峰工作缓冲峰值
(带 1ms 峰值采样线程)。
- .preflight/uitest 新增 [AR](边沿计数预算/精确性/区间查询/拖动期 O(1)、GX 徽标悬停光标)与
[AS](标题栏 RAM 口径 = 私有工作集);并给菜单类用例加了关闭残留菜单 + 允许补点的卫生逻辑。
- 合计 738/738 通过(连跑 5 次约 1 次出现既有 harness 抖动:菜单点击类用例,重跑即好);
make release 零警告(Target.exe 被占用时链接报 Permission denied,用临时 -o 名字验证链接)。
规则同步:Documents/agent_mem.md 新增 §9.22(含内存与性能排查报告),§11.2 新增第 26–31 条硬约束,
变更日志补一行。
|
2026-10-14 14:12:54 +08:00 |
|
DeepSeek V4.1 Flash
|
a2a38236fe
|
边沿计数改用「绘制值」+ 派生信号烘入时间偏移 + 图例托底统一为 GX 徽标款 + 调整二级面板向上展开
一、两个「边沿计数」按「画出来的曲线」判定(原先按原始采样值 → 应用过「调整」的信号恒为 0)
- 用户复述的权威定义:设定一个横向(Y 轴)光标,信号绘制出来的曲线从下往上穿过该幅值 = 上升沿,
从上往下穿过 = 下降沿。
- 症状:VERY_BIG_DATA 的「编码器B相」设系数 50 + 时间轴偏移 -20s,把光标拖到画面中间,
很规整的方波却统计出 0/0。
- 根因:EdgeCountsFor / EdgeCenterCountsFor 只比较 signal.values 的原始采样值,而横向光标读的是
屏幕上的值(已乘系数、已加偏移)→ 原始值域与阈值完全不相交。
- 修法:判定值一律取 ApplyDrawTransform(style, values[SourceIndexFor(style, count, index)]);
* 中心轴两侧必须套用同一套变换(drawn - drawnCenter);
* 水平镜像时曲线数组按源下标存储、而统计区间是显示下标 → 请求曲线窗口前必须把
[needFirst, needEnd) 映射为 [count-needEnd, count-needFirst);
* 缓存必须带样式指纹:EdgeCountEntry / EdgeCenterCountEntry 新增 styleKey
(EdgeStyleKey():gain/offsetY 按 1e6 定点 + absolute/mirrorVertical/mirrorHorizontal 位),
否则改完「调整」仍会命中变换前的旧计数。
二、时间区间之外的采样不得计入(区间边界点)
- 症状:A 在阈值以下、B 超过阈值;双光标下一个光标正好在 B 点,旧实现把 B 算成一个上升沿。
- 根因:EdgeIndexRange 在左端多取了一个采样点(if (begin > 0) --begin;)。
- 修法:删掉该外扩,收紧为「只有两个端点都落在 [begin, end) 内的采样对才计入」
(begin = lower_bound(rangeMin)、end = upper_bound(rangeMax),调用方从 begin+1 枚举到 end-1)。
- 结果:把区间起点从 times[crossing-1] 移到 times[crossing] 恰好少一个上升沿。
三、派生信号把 offsetTime 也烘进采样值(推翻 §9.19 的「唯一例外」)
- 用户要求:新信号默认不启用任何「调整」时就必须与网格里的预览逐点重合。
- 修法(Application::BuildDerivedSignal,在「值变换 + 水平镜像反序」之后追加一步):
新信号第 i 个采样 = 已变换的曲线在 time[i] - offsetTime 处的取值(取最近的采样点);
单调时间轴用只前进的游标(整段 O(n));平移后越界的采样置 NaN(与预览一致);
新信号样式全部保持默认(gain=1 / absolute=false / 两个镜像=false / offsetY=0 / offsetTime=0)。
- 退化路径:时间轴非单调或时间列缺失时无法按下标推进取样,退回把 offsetTime 带在样式上
(两条路径的显示效果相同)。
四、图例托底统一为 GX 编号徽标同款 + 新增更小字体 uiTiny
- 托底高度 = kLaneBadgeHeight、上下边界与 LaneBadgeRect() 完全一致、圆角 = 高度的一半(胶囊形)、
配色与描边与徽标相同(选中态 accentSoft/accent)、水平内边距 = 胶囊半径。
- 文本改用新增字体 UiFonts::uiTiny(10 逻辑 px):原 uiSmall(12px) 几乎顶满 17px 高的胶囊。
- LegendItem 拆出 plateWidth(托底宽 = 内容宽 + 2×内边距),条目步进 = plateWidth + kLegendItemGap;
kLegendPlateRadius 已删除(半径一律由徽标高度推出)。
五、「系数/偏移」「镜像」二级面板改为向上展开
- PopupMenu::Open(..., bool expandUp = false):true 时 (x, y) 是菜单的左下角、菜单向上展开;
BuildLayout 增加同参数并记录 m_buildExpandUp(RebuildLayout 必须复用,否则数值回写触发的重排
会让面板跳回向下展开)。
- SignalPanel::OpenGainMenu / OpenMirrorMenu:锚点 = (button.X, button.Y - kAdjustMenuGap),
即「面板左下角对准按钮左上角、再略微上去一点」(kAdjustMenuGap = 4 逻辑像素)。
六、验证(全部离屏,不占用用户的鼠标 / 键盘 / 屏幕)
- .preflight/uitest 新增 [AR]:
* 边沿计数 plain / gain=50 / offsetY / mirrorVertical / mirrorHorizontal 逐个与「独立参考实现」
逐位一致;offsetY 用例给出 A/B:按原始采样值算会得到 7/6 个边沿(正是用户报告的缺陷);
* 区间边界:起点从 crossing-1 移到 crossing 恰好少一个上升沿;
* 中心轴:absolute + offsetY 的样式下与参考实现逐位一致(32234 个边沿量级);
* 派生信号:无偏移时全部 128 万采样逐点等于 transform(滤波);带 +10 个平均采样周期的偏移时,
抽样点等于 transform(滤波) 在 time[i] - offsetTime(最近采样)处的取值;样式全默认;
* 图例托底:height == 徽标高度、Y == 徽标 Y、radius == height/2、色块与文本都在托底之内;
* 调整面板:菜单左边界 == 按钮左边界、菜单底 == 按钮顶 - 4*scale、且不覆盖按钮(两个按钮各测一次);
* GX 拖拽让位:±0.6H 各让位 1 格(目标 0 / 3),±0.4H 与恰好 ±0.5H 都不让位(严格「超过」)。
- 同步修正 harness 里两处「旧规则」断言(否则必然 FAIL 并误导后来者):
[AP] 「派生信号继承调整变换」→ 「样式默认 + 采样值按 time[i]-offsetTime 烘焙」;
[AN] 「图例带无托底(网格线可透出)」→ 「装饰矩形逐块、无一块跨越整条带、未被托底覆盖的列
仍能看到底层内容」。
- 合计 722/722 通过;make release 零警告(本机 GCC 15.2.0 UCRT;Target.exe 被占用时链接会报
Permission denied,已用临时 -o 名字验证链接正确性)。
规则同步:Documents/agent_mem.md 新增 §9.21,§9.19 的「offsetTime 仍需继承」标注为已作废,
新增 §11.2 第 21–25 条硬约束,并补变更日志一行。
|
2026-10-14 11:40:49 +08:00 |
|
DeepSeek V4.1 Flash
|
1b3ab4ac09
|
装饰层改用带 alpha 的独立图层(消除 GX/图例"直角白边")+ 图例圆角托底 + 预览曲线内存释放
一、GX 编号 / 图例「圆角矩形外多出一圈直角白边」的根因与修复
- 根因:装饰层原本是"从后备缓冲区**抓取一块不透明矩形**"(CaptureDecorCache + BitBlt),
圆角之外(属于波形)的像素被一起烘进缓存;为了让这块矩形像素能跨视界复用,
又在圆角矩形外铺了一层**直角**的不透明底色(OpaqueBaseRect)。两者叠加 ⇒ 圆角之外
显示的是网格底色而不是波形 ⇒ 在密集波形上就是一圈直角白边(Y 轴刻度胶囊同理)。
- 修法:把装饰层改成**带真 alpha 的独立图层**:
* m_decorDc 改为 32bpp CreateDIBSection(自上而下),像素可直接读写 alpha;
* 新增 RenderDecorCache():把装饰**单独绘制**进一张全透明的位图(不再从后备缓冲区抓取),
圆角之外保持全透明;位图覆盖范围为「客户区原点 → 绘图区右下角」,绘制坐标与正常绘制
完全一致(GDI 文本不认 GDI+ 变换,绝不能做坐标平移);
* BlitDecorCache() 改用 AlphaBlend(AC_SRC_ALPHA,GDI+ 写入 DIB 段即预乘 alpha)逐块合成
⇒ 圆角之外透出的是**当前**波形,既没有白边也不会残留旧波形像素;
* AlphaBlend 经 GetProcAddress(gdi32!GdiAlphaBlend)(回退 msimg32!AlphaBlend)解析,
不新增链接依赖(两台协作设备工具链不同,避免改 Makefile);
* alpha 修补:GDI 文本(DrawTextW/ExtTextOutW)会把写入像素的 alpha 清成 0,
若不修补文字处会变成"透明洞",因此在装饰矩形内把「A==0 且有颜色」的像素还原为不透明;
* 删除全部外扩的直角不透明底色:Y 轴胶囊与图例托底都只画圆角形状本身;
* 补齐「+N」(图例空间不足)与「(无可见信号)」提示的装饰矩形(否则画进缓存却不被贴回)。
- 装饰缓存的每帧开销因此更低:BlitDecorCache 约 0.19ms/帧(原 BitBlt 方案约 0.3ms),
RenderDecorCache 仅在装饰内容变化时执行(约 2.4ms / 5 网格)。
二、图例样式:与 GX 编号徽标同款圆角托底 + 悬停/选中配色统一
- 每个图例条目 = 一个圆角矩形托底(kLegendPlateRadius=4,cardBg 底 + border 描边),
内容为色块 + 信号名 —— "近似于一个 GX 标签,只是内容换成了图例信息"。
- 选中态与悬停态同配色(accentSoft 底 + accent 描边 + textStrong 文字),与 GX 徽标悬停一致;
悬停高亮画在贴图之后,并**在不透明托底上原样重绘色块与文字**(否则内容会被盖住),
同时保留对应信号的曲线光晕;LegendHit 新增 radius 字段。
- 选中态与左侧信号列表同源(DataSet::Selected()):面板选中 → 图例高亮;图例点选 → 面板行选中。
三、预览曲线内存:thread_local 工作缓冲封顶 + 关闭功能即释放
- 根因:DataModel 里三处 thread_local 工作缓冲(弱峰调整的 current/next/极值表、
ApplyWeakPeakStage 的位移表、迭代均值滤波的中转表)。整段信号(2580 万采样)计算时
每条后台线程常驻数百 MB,而后台曲线线程池最多 4 条线程 ⇒ 2GB 以上;thread_local
只在线程退出时才析构 ⇒ 关掉预览不释放,只有 PauseDerivedAsync()(关闭文件分组会调用)
join 线程后才归还(用户实测 1.42GB → 4.21GB 且关不掉)。
- 修法:① 保留容量封顶(kReuseBufferMaxElements=4M + TrimReuseBuffer + ReuseBufferScope
作用域守卫,所有退出路径都会裁剪):超大计算改为一次性分配、用完即释放,内存不再随
线程数放大;中/小规模仍保留容量以维持"多阶弱峰调整 991ms → 577ms"的收益。
② 新增 AnyDerivedPreviewEnabled() / TrimDerivedCurveDataIfIdle(),在
SetLaneWeakPeak / SetLaneMeanFilter / SetLaneEdgeCenterCount(..., false) 里调用 ——
关闭最后一个预览功能时立即 PauseDerivedAsync()(join 线程 ⇒ thread_local 析构 ⇒ 归还内存)
并清空均值/弱峰曲线缓存。
- 实测(.preflight/perf/stylecheck mode 2,VERY_BIG_DATA):
baseline 1202MB → 弱峰(10 迭代,1 条) 1399MB → 5 条曲线 1792MB → **关闭面板后 1203MB**。
⚠️ 计算期间的瞬时峰值仍可达约 2.2GB(整段 O(n) 计算的固有代价),但计算结束立刻归还。
四、验证(全部离屏,不占用鼠标/键盘/屏幕)
- .preflight/perf/verify_main.cpp(离屏 Application harness)**49/49 通过**,新增/强化:
装饰位图圆角外 alpha≈0、中心 alpha=255(直接读 m_decorBits,证明没有直角白边);
选中图例=悬停配色(accentSoft 像素 未选中 0 / 选中 1525);平移(宽基底)帧的装饰与整幅重绘
逐像素一致(只比较装饰不透明像素:worst=1 / 29554px);图例溢出的「+N」提示已登记且可见。
- .preflight/perf/stylecheck_main.cpp:mode 0(装饰重建/贴回耗时)、mode 1(密集波形下导出
GX+图例角落 PNG 目视)、mode 2(预览曲线内存增长/释放)、mode 4(装饰位图 alpha 形状检查)。
- zoomcheck mode 13:收敛帧 vs 整幅重绘 **逐像素 0 差异**(此前为 3503)。
- make clean && make all 零警告。
|
2026-10-14 03:25:10 +08:00 |
|
DeepSeek V4.1 Flash
|
7c32eca004
|
修复自适应/首帧卡顿回归 + 派生信号烘值 + 图例交互与托底重做
一、性能(详见 agent_mem §9.19)
- AutoRangeLaneToView:把 DataSet::Style(ref) 从逐采样循环里提出来(远端新增的「调整」
功能让它每个采样点解析两次,实测 147ms 劣化到 1200ms),并在「视界覆盖整条信号且
无任何数值变换」时直接取信号已缓存的 min/max(O(1))。工具栏「Y 轴自适应」在全视图下
1200ms → 0.02ms,结果与逐点扫描逐位一致;有变换时仍逐点套用与绘制相同的变换。
- 新增 WarmLaneLayersStep():加载后宽基底图层从未分配,用户第一次平移/选区放大时
Paint 会在那一帧同步重建全部网格图层(首帧 64.3ms)。现在改为在「命中静态场景缓存」
(视界未变、非拖拽)时用既有预算机制每帧至多补 1 个网格,把整批重建摊到静止帧;
选区放大首帧 64.3ms → 13.0ms。
二、派生信号
- 「构建为新信号」把源信号的「调整」变换(系数 → 绝对值 → 垂直镜像 → Y 偏移,以及
水平镜像=数据反序)烘进派生采样值,新信号样式一律保持默认(不再是"继承的指纹");
仅时间轴偏移 offsetTime 因派生信号与源信号共用同一个文件时间轴仍需继承。
三、交互
- 网格 GX 拖拽让位判据改为「鼠标指针」:指针越过相邻网格的中心水平线即让位并出现吸附
提示(原先按"被拖网格的中心",要求把整个网格拖过去一格才吸附)。
- GX 徽标悬停高亮:徽标本体在装饰缓存里被冻结,高亮改为贴图之后的叠加层,并新增
m_hoverBadge 状态驱动重绘/取消;QueryCursor 在徽标上返回 IDC_HAND —— 与左侧信号列表
里同一个 GX 徽标一致。
- 修复「中键平移过视界之后图例悬停/点击/双击改名全部失效」:整幅重绘路径会清空
m_legendHits,而装饰缓存命中时 PaintLaneLegend 提前 return 导致永不重建。现在把该函数
拆成「几何 → 命中/装饰矩形 → 绘制」三段,任何路径都先重建命中矩形。
四、绘制
- 每个信号图例单独加圆角托底(配色/圆角风格与 GX 徽标一致);装饰矩形改为逐块
(徽标一块 + 每个条目一块 + 每个 Y 轴胶囊一块),不再覆盖整条图例带;每块托底先用
不透明底色铺满「外接整数像素再外扩 1px」的矩形再画圆角 —— 消除「GX/图例周边旧波形
像素残留」(逐像素验证:装饰矩形内 0 差异)。
- 图例悬停高亮改为不透明托底并在其上重绘色块与文字(否则不透明托底会把图例内容盖住),
同时保留对应信号的曲线光晕;Y 轴刻度胶囊补不透明外接底色,消除同类残留。
验证:离屏 Application harness(窗口 SW_HIDE + 直接注入鼠标消息 + 从后备缓冲区截图,
不占用鼠标/键盘/屏幕)40/40 通过;zoomcheck 新增 mode 20-26 量化;make release 零警告。
|
2026-10-14 01:55:34 +08:00 |
|
DeepSeek V4.1 Flash
|
940b8e67e6
|
面板/拾色器布局微调 + 修复“拖拽光标飞到视界边缘”(含根因与防复发)
一、样式区「系数/偏移」二级面板:两个偏移输入框等宽对齐
- 带说明文字的 EDIT 行原来各自按自己的文本长度 + 单位后缀算宽度,于是「时间轴」
(文本更长、还带 `s` 后缀)的输入框比「Y轴」宽一截。现在同一次排布里所有
带说明文字的输入框共用**同一个宽度**(取其中最大自然宽度,再按“给最宽的说明文字
留位 / 不超过内容区 3/4”夹取),因此「Y轴」与「时间轴」严格等宽、右边界对齐。
- 断言:两个输入框 126.0 / 126.0,右边界 277.8 / 277.8。
二、自定义颜色浮层(拾色器)的绘制与排版
- **圆角不再漏像素**:色域方块的位图与色相条的渐变填充都**裁剪进同一个圆角路径**
(新增公开的 `UiBuildRoundRectPath`,与 `UiFillRoundRect` / `UiDrawRoundRect`
逐点同源),色相条的圆角与色域方块完全一致。以前填充是直角矩形,会在圆角描边之外
露出四个尖角(色域左下/右下本身就是接近黑色的极值,看起来就是"角落里多出黑色像素点")。
- 色域位图改用 `WrapModeTileFlipXY` 贴图:插值不再采样到边界之外,边缘也不会被压暗。
- 色相条改用**一条带 7 个色标的线性渐变**画满(颜色与原来 6 段拼色一致),
顺带去掉段与段之间的接缝横线。
- RGB 三行:轨道**两侧间隙相等**(左:字母 ↔ 轨道;右:轨道 ↔ 数值区,共用同一常量),
数值区固定 **3 个字符宽**(与数字本身无关),三行轨道等长、同 x,R/G/B 字母与数值
都**右对齐**,因此三个字母、三列数字纵向对齐。
- 色号:固定 **6+1 个字符宽**,**右边界与三行数值的右边界对齐**(像素级验证:两者
最后一个字符同列);预览色块的长宽不变、左边界与轨道左边界对齐。
- 数值/色号正好 3 / 7 个字符宽,编辑态需要把文本区向外扩出输入框内边距,否则最后
一个字符会被输入框内边距裁掉(新增 `PaddedEditRect`,绘制、命中与插入符定位共用)。
三、修复“用选区放大把视界放大到某个临界点后,拖拽光标就飞到视界边缘”
两个独立原因,都属于“不变量只在某一个调用点维护”:
1. **吸附到采样点**没有边界检查:只要采样点间距 ≥6px 就吸附,而视界一旦比一个采样间隔还窄
(选区放大很容易做到),“最近的采样点”就落在视界之外,于是每一次鼠标移动都把光标
吸到视界外、再被夹回边缘 → 表现为光标“飞到视界边缘后跟不动鼠标”。现在吸附要求
视界内至少放得下 2 个采样点,且只接受**可见区间内**的采样点。
2. **选区放大没有最小跨度下限**:滚轮缩放有 `数据跨度 × 1e-9` 的下限,选区放大没有,
可以一直放大到视界宽度只有几个 double ULP。此时“像素 → 时间”的映射退化
(相邻像素映射到同一个 double),鼠标一动光标就跨几百像素。现在新增
`MinimumTimeSpan`(数据跨度 × 1e-9 与 `ULP(时间) × 画布宽度` 取大者)与
**视界的唯一写入口 `SetTimeView`**(拒绝非有限值、纠正左右颠倒、抬到下限之上、
再按数据范围收敛),滚轮缩放 / 选区放大 / 平移 / 适应视图 / 时间轴适应网格全部走它。
- 另新增**光标位置的唯一写入口 `PlaceCursorTime`**(拖拽光标必须经由此处):
拒绝非有限值 → 吸附(带上限与可见区间检查)→ 夹回可见区间。`XToTime` 也补上了
跨度退化时的兜底。
- ⚠️ 为什么会反复出现:这两个不变量(“视界必须仍然可表示/可用”“光标必须落在可见区间内、
且对应鼠标位置”)过去是**在每个调用点各自维护**的。后来加入的路径(选区放大、采样点吸附、
派生信号/偏移等新算子)各自绕过了其中一个环节,而故障只有在放大到极限时才可见,
于是每加一个新功能就可能复发一次。现在视界与光标各只有一个守门人,新功能必须经过它们。
四、离屏验证(窗口隐藏、不碰屏幕/鼠标/键盘)
- 新增 [AQ]:视界比采样间隔还窄时拖光标必须跟随鼠标(修复前偏差 630px、直接贴边);
视界内采样点仍精确吸附;连续橡胶带放大不会再突破可分辨下限(停在 1e-9 数据跨度),
且在极限缩放下拖拽仍精确跟随(修复前 490px 偏差)。
- 新增 [AR]:色域方块与色相条四角之外不再有填充像素(角像素从填充色变为面板底色)、
两侧间隙相等、数值列 3 字符、色号 7 字符且与数值右边界同列(像素级)、预览色块左边界
与轨道左边界对齐、三条轨道等长同 x。
- [AP] 增加两个偏移输入框等宽/右对齐断言。
- ⚠️ 修正 harness 的一个**假通过**:`SetAllVisible(false)` 会清掉标签页的“视图已初始化”标记,
首次渲染的 `FitAll()` 会把测试设置的深缩放覆盖掉([Z]/[AI]/[AQ] 的“深缩放”其实一直在
整段数据上跑,所以这类回归能溜过去)。现在相关用例显式置位该标记,[Z]/[AI] 真正进入
深缩放;并补上用例间残留弹出菜单的隔离(面板停在窗口下半部会向上翻转盖住按钮,
吞掉下一个用例的首次点击)。
- 合计 669/669 通过(连续 4 次);`make release` 零警告(Target.exe 正在运行,链接待其关闭,
已用临时输出名验证链接自身成功)。
|
2026-10-13 20:20:38 +08:00 |
|
DeepSeek V4.1 Flash
|
fc77b0262f
|
「调整」行:系数/偏移 按钮 + 偏移调整(Y 轴/时间轴)二级面板;结算顺序加入偏移;派生信号继承调整变换
按钮与面板:
- 「系数」按钮改名为「系数/偏移」;点击展开的二级面板在原「系数调整」输入框下方新增「偏移调整」:
与「系数调整」同一套绘制(标题 + 标题栏右侧的还原图标),下面两行分别是「Y轴」「时间轴」,
每行左侧说明文字、右侧内联输入框(整行数值框)。
- 「Y轴」默认 0.00,非编辑态**始终带正负号**(+0.00 / -12.50),小数不足两位时补两位、更高精度保留更多位。
- 「时间轴」默认 0.00,非编辑态显示与双光标差值同一套复合单位文本(`Δt:500ms`、`Δt:1h 1min 2s`);
进入编辑态时显示以**秒**为单位的数值,并在输入框最右侧提示单位 `s`。
- 「偏移调整」的还原图标把两个偏移恢复为默认值 0;「系数调整」的还原图标仍只重置系数。
- 面板/输入框的编辑初值与显示文本分离(`MenuItem::editValue` / `editSuffix`),
非编辑态(`Δt:…`)与编辑态(`3662`)不一致时也能正确回显。
语义与结算顺序:
- 结算顺序变为 **系数 → 绝对值 → 镜像 → 偏移**(前三步不变)。
- Y 偏移:给绘制值加常数(波形上下移动);时间偏移:采样绘制在 `t + offsetTime` 处
(例如 t=1s、偏移 -0.5s → 画在 0.5s)。
- ⚠️ Y 偏移以**未镜像空间**为准:垂直镜像开关变化时同步取反偏移量,
使绘制等价于「先加上偏移再镜像」(开关来回切换可逆)。
- 绘制链路全部跟随偏移:`BuildPolyline`(逐点描边 / 像素列包络 / 跨列边界插值 / 粗粒度分组层,
保序的 Y 偏移直接加在分组极值上)、派生预览曲线、光标取值读数(含 Δt)、
可见区间 Y 轴自适应、整段自适应的区间映射;折线缓存键、场景指纹、样式快照与图层指纹都纳入两个偏移。
派生信号(实验性功能「构建为新信号」):
- 修复「预览是调整后的波形、构建出来却按原始状态绘制」:新信号**继承源信号的调整变换**
(系数/绝对值/两个镜像/两个偏移)——网格预览绘制的是 `变换(滤波结果)`,
而构建出的信号只存了滤波结果,因此必须把变换一并继承;
时间轴偏移**只能**靠继承(派生信号与源信号共用同一文件时间轴,无法把时间平移烘进数据)。
离屏验证(不干扰用户):
- ⚠️ harness 现在在每次注入鼠标消息后**立即释放本窗口的鼠标捕获**
(宿主 WM_LBUTTONDOWN 会 SetCapture,隐藏窗口同样会把用户的真实鼠标输入吸走);
后续消息本来就是直接投递,无需捕获。
- 新增 [AP]:按钮标签不裁切、面板 5 行顺序与「偏移调整」位于系数输入框下方、
Y轴/时间轴行的标签与输入框同行且默认值/单位提示正确、时间轴编辑态显示秒值并在最右侧提示 `s`、
Y 偏移提交/显示正负号/波形整体平移量、时间偏移提交/Δt 复合单位(含 3662 → Δt:1h 1min 2s)/
编辑态为秒、**按"偏移若干个采样点"构造的精确几何验证**(采样被画到移位后的像素位置、且不再画在原处)、
读数跟随、两个还原图标、垂直镜像对 Y 偏移取反(可逆)、
以及派生信号继承变换;[AL] 补充「系数→绝对值→镜像→偏移」的顺序与区间映射(含 Y 偏移)。
- 合计 645/645 通过;make clean && make all 零警告。
|
2026-10-13 17:56:00 +08:00 |
|
DeepSeek V4.1 Flash
|
2b02518f85
|
样式区「调整」行改为 系数/镜像/绝对值:系数二级面板(还原图标 + 整行输入框)、镜像二级面板(水平/垂直复选框);Y 轴自适应按变换后曲线
- 「调整」行三个按钮改为「系数」「镜像」「绝对值」(顺序即结算顺序:系数 → 绝对值 → 镜像)。
「系数」「镜像」点击后在按钮下沿展开二级面板(按下只登记、抬起命中同一按钮才展开,
与自定义颜色入口一致),「绝对值」仍是直接切换的按钮。
打开面板时主动清除样式区悬停态 —— 面板会接管鼠标事件,否则该按钮会一直保持"鼠标按住"般的高亮。
- 数据层:`SignalStyle::inverted` 拆成 `mirrorVertical`(Y=0 上下翻转)与
`mirrorHorizontal`(水平镜像 = 数据反序);新增 `Mirrored()`、
`SourceIndexFor`(水平镜像的遍历下标 → 源下标映射)、`TransformValueRange`(区间映射)。
`ApplyDrawTransform` 顺序固定为 系数 → 绝对值 → 垂直镜像;水平镜像在采样遍历层处理。
- 绘制链路:`BuildPolyline`(逐点描边与像素列包络两条路径、跨列边界插值、粗粒度分组层)、
派生预览曲线、光标取值读数(含 Δt)全部按同一变换取值;水平镜像时按源下标区间反序读取,
包络层仍复用粗粒度分组;折线缓存键、`SceneContentHash`、样式快照都带上两个镜像位。
- 系数二级面板:标题「系数调整」,标题栏最右端是一个可点击的「还原」自绘图标按钮
(悬停有高亮反馈,点击后系数重置为 1,面板保持打开);下面是一整行输入框
(不再显示「绘制系数」说明文字),数值至少显示两位小数、精度更高时按实际精度显示
(1 → 1.00、2.5 → 2.50、1.2345678 原样)。
- 镜像二级面板:两行带复选框的切换项「水平镜像」「垂直镜像」,点击后**面板保持打开**
(`MenuItem::keepOpen`),勾选状态就地回写;任一项勾选时「镜像」按钮显示为激活态。
- Y 轴自适应修复:整段自适应(`TabConfig::ComputeLaneRange`)按区间映射(`TransformValueRange`)
计算,不再直接用原始 min/max;可见区间自适应(`AutoRangeLaneToView`)逐点套用绘制变换
(含水平镜像的源下标映射)。用户报告的"调整系数/镜像后自适应区域错误、仍按原始数据"由此修复。
- `PopupMenu`:新增 `MenuItemKind::HEADER_ACTION`(标题 + 右侧可点击图标,图标有悬停高亮)、
`MenuItem::keepOpen`(切换项点击后不关闭菜单)、`HeaderIconRect()`,
以及无说明文字的整行输入框(占满内容区)。
验证(离屏 harness,SW_HIDE、不动鼠标键盘屏幕):
- [AL] 重写/扩充:变换顺序与区间映射(含水平镜像的源下标反序)、调整行三按钮的顺序/等宽/标签不裁切、
「系数」按住不弹出 / 移开取消 / 单击展开(标题 HEADER_ACTION、无标题输入框占满整行、初始 1.00、
两位小数与更高精度回显、提交后面板保持打开、折线缓存键携带系数)、
「还原」图标位置/悬停像素变化/点击后系数回到 1.00 且面板保持打开、
「镜像」面板两行复选框(初始未勾选、keepOpen、勾选后按钮激活、取消其一保留另一)、
打开面板后按钮不再停留高亮、系数/镜像按钮的激活填充与其它启用态切换按钮逐像素同色;
`BuildPolyline` 顶点范围与变换后数据范围逐项对齐(默认/系数 2/绝对值/垂直镜像/三者叠加),
水平镜像用窄视窗逐顶点核对"取的是对面端点的采样"(8/8)+ 读数跟随;
以及 Y 轴自适应:整段/可见区间自适应在系数、绝对值、垂直镜像下均跟随变换后的曲线。
- 合计 594/594 通过;make clean && make all 零警告。
|
2026-10-13 14:50:38 +08:00 |
|
DeepSeek V4.1 Flash
|
2f243dd557
|
样式区「调整」行:系数调整(二级面板输入)+ 取绝对值 + 中心反转
- 移除上一轮新增的「系数增益」输入行与「取绝对值 / 中心反转」按钮行。
- 在「样式」下方、「线宽」上方新增「调整」行,一行三个等宽按钮:
「系数调整」「取绝对值」「中心反转」(后两者即原按钮迁移至此)。
样式区固定行数回到五行(颜色 / 样式 / 调整 / 线宽 / 透明度),标签列宽回到 44(只需容纳「调整」)。
- 「系数调整」交互与"自定义颜色色块入口"一致:按下只登记,**抬起**且指针仍在同一按钮上才展开
二级面板;面板锚定在按钮下沿,内含一个数值输入框(默认 1,EditFilter::NUMBER)。
提交后面板保持打开(与网格 Y 轴上下限的菜单内联编辑一致),并把夹取后的真实值回写输入框。
- 按钮启用态规则:绘制系数 != 1 时显示为"已启用"(与其它切换按钮启用样式完全一致:
accentSoft 底 + accent 描边),等于默认值 1 时为"未启用"样式。
- 系数为 1 / 非保序变换的回退逻辑、SceneContentHash / 样式快照 / 折线缓存键等绘制链路不变。
验证(离屏 harness,SW_HIDE、不动鼠标键盘屏幕):
- [AL] 重构为:五行高度与行序(调整行在样式行与线宽行之间)、三按钮按序铺满且不重叠、
三个标签都完整放得下(无裁切);
「系数调整」按住不弹出 / 移开再松开取消 / 单击展开(SIGNAL_PANEL 范围、标题「系数调整」、
初始值 1、锚定在按钮下沿)→ 点击输入框进入编辑 → 键入 2.5 回车提交(面板保持打开、
输入框回显 2.5、折线缓存键携带新系数)→ 点别处关闭面板;
像素级核对按钮样式:gain=1 时填充为 cardBg,gain=2.5 时填充与"取绝对值已启用"的按钮**逐像素同色**
(0xFFCFE4F3)且与未启用态不同。
- 其余用例(样式变换顺序与顶点范围、拾色器两列等高、选区放大下 GX/图例命中、标题栏内存读数)
保持不变。合计 549/549 通过;make clean && make all 零警告。
|
2026-10-13 13:51:31 +08:00 |
|
DeepSeek V4.1 Flash
|
c3343907ec
|
标题栏内存占用 / 样式区增益与取绝对值·中心反转 / 拾色器排版 / 图例去托底与命中优先级
标题栏:
- 应用名称后追加「 | RAM : <值>」:竖线两侧各留两个空格(与分组标题「组X | 组名」的间距风格一致)。
- 数值按 KB/MB/GB 自适应、保留两位小数(复用 FormatByteSize);用 K32GetProcessMemoryInfo 取本进程
工作集(与任务管理器「内存」一致,无需新增链接依赖);1 秒定时器刷新,数值未变时不调用 SetWindowTextW。
信号样式自定义栏:
- 线宽与透明度交换上下位置(线宽在上、透明度在下)。
- 新增「系数增益」:输入框(默认 1,只接受数值,范围 ±10000),点击进入编辑、回车/点击别处提交。
- 新增「取绝对值」「中心反转」两个同行的切换按钮。
- 三者作用顺序固定为「系数增益 → 取绝对值 → 中心反转」(中心反转最后结算),实现为
ApplyDrawTransform(style, value),在 BuildPolyline(逐点描边与像素列包络两条路径,含粗粒度分组层)、
派生预览曲线与光标取值读数统一调用;样式属于信号本身(跨视图共享)并进入 SceneContentHash /
SceneStyleEntry / 折线缓存键,改动会立即重绘。
- 非保序变换(取绝对值 / 反转 / 负增益)会回退到逐采样点变换、不再使用粗粒度包络层,保证包络精确。
自定义颜色面板:
- 把底部的「色号 + 预览色块」移到 RGB 滑块上方;右侧一列(预览行 + 三行滑块)与左侧「色域方块 +
色相条」高度严格相等,两列上下边对齐,面板不再为它单独保留一行。
网格内:
- 去掉图例与 GX 编号下方的整块不透明托底(它会把网格线整片擦掉,看起来像一个"可点击控件",
而其右侧空白实际并不响应点击)。
- 图例与 GX 徽标改为优先命中:先于时间光标线 / 光标读数 / 边沿计数横向光标 / 选区放大判定,
因此选区放大模式下也能正常拖拽 GX 标签与点选图例(浮动面板仍然优先,与绘制层序一致)。
验证:离屏 harness(.preflight/uitest,SW_HIDE、不动鼠标键盘屏幕)新增
[AK] 标题栏内存读数(格式与竖线间隙、单位阈值、两位小数、随 256MB 分配增长、定时器刷新);
[AL] 样式区行序与几何、增益输入提交、两个切换按钮的真实点击、ApplyDrawTransform 顺序,以及
BuildPolyline 顶点范围与变换后数据范围逐项对齐(默认 / 增益 2 / 取绝对值 / 中心反转 /
负增益+取绝对值+中心反转叠加);
[AM] 拾色器两列严格等高(140.00 == 140.00);
[AN] 选区放大模式下 GX 拖拽重排 + 图例点选 + 网格线穿过图例带(托底已移除)。
另修正旧用例 [AI](远端已删除"光标跟随视界",改为断言光标时间位置不变、允许滑出视窗)。
合计 530/530 通过;make clean && make all 零警告。
|
2026-10-13 12:06:38 +08:00 |
|
DeepSeek V4.1 Flash
|
20b90848fe
|
深放大中键拖拽卡顿根因修复(GDI+ 抗锯齿"满幅锯齿")+ 横向补条裁剪 + 并行网格图层(B/C)
用户报告:VERY_BIG_DATA.CSV 只显示编码器A相、选区放大到 [10.2719,10.2831]s(≈1.12ms 一大格)
后,中键拖拽视界非常卡顿。要求做 B(拆分宽基底路径定位瓶颈)与 C(解除"不允许多线程"约束
并实现并行网格渲染)。
B —— 宽基底路径耗时拆解(.preflight/perf/dragperf mode 1):
该视界下 RollWideBuffer(补条重绘)13.08ms,BlitWideBuffer 0.50 / PaintTimeAxis 0.78 /
PaintLaneDividers 0.04 / PaintLaneDerivedOverlays 0.00 / CaptureDecorCache 0.03ms
—— 瓶颈全在"补条重绘"。
横向补条裁剪(RollWideBuffer 加速):
补条只覆盖图层位图一条窄竖带,但折线原本按整条宽窗口(2x 视界)构建/描边整段顶点。
新增 m_stripRenderActive/m_stripX0/m_stripX1:横向补条时把折线采样区间裁剪到条带对应 x 范围
(两侧留 max(4, lineWidth+3) 像素余量),并旁路折线缓存(补条只构建片段,写缓存会污染整段复用)。
裁剪只对"逐点描边"路径生效,且必须在"包络/描边"判定之后(否则 visibleCount 变化会改路径判定)。
实测 RollWideBuffer 13.08ms -> 1.73ms;拖拽整帧 13~24ms -> ~3ms;补条 vs 整块逐像素 0 差异。
真正的病态根因:GDI+ 抗锯齿描边"满幅锯齿":
拖拽超出预渲染余量时走整块重画,整宽单网格渲染实测 655ms。逐层定位:BuildPolyline 仅 0.03ms;
同样 4481 点交给 Graphics::DrawLines —— AA round 694ms / 无 AA 29ms(23 倍)。即编码器方波在
1~2 采样/像素时形成每像素列一次整幅跳变的锯齿,GDI+ 抗锯齿逐段扫描近竖直满幅边,代价爆炸
(原实现只在 >2 采样/像素或"梳状"时关 AA,这一区间两个条件都不满足)。
修法(几何完全不变,只改"何时走包络/何时关 AA"):
1) 包络带阈值从"每像素 2 个采样(budget)"降到"每像素 1 个采样"(visibleCount > plotWidth);
2) EmitEnvelopeBand 新增 laneHeight 形参,扩展"梳状(关 AA)"判据:多数列列内跨幅 > 0.5x网格高,
或相邻列上下包络整幅跳变(> 0.25x网格高)-> 本段关闭抗锯齿。
实测:整宽单网格渲染 655ms -> 47.7ms;补条 6.9 -> 2.4ms;中键拖拽 200 帧 avg 4.5ms /
max 5.8ms / 0 帧 >8ms(修复前 13~24ms 且含 700ms 尖峰)。目视核对(mode 9 导出 PNG)正常。
C —— 并行网格图层渲染(用户明确解除多线程限制):
文档中"图层渲染一律在 UI 线程内同步完成 / 绝不要搬到后台线程"的约束已按用户要求解除,但根因
(共享可变渲染状态)依然成立,故并行的唯一安全姿势是"每线程持有隔离渲染上下文"。
实现 PlotView::RebuildLaneLayersParallel:每线程一个隔离 PlotView(m_tab 指向自有 m_workerTab
副本,m_area/m_plotRect/m_lanes 都是它自己的),各自渲染不同 laneIndex 到不同的 m_layerFront 元素;
主线程 join 后按真实网格下标重算 LaneLayerKey。
踩坑:BuildLaneVisuals() 在 m_tab==nullptr 时会清空 m_lanes -> 不能靠置空 m_tab 隔离,必须给它一个
非空的、自有的 TabConfig(m_workerTab)。
正确性:并行 vs 顺序逐网格比较 0 差异(5 网格全 0)。
诚实结论:并行仅 1.03x —— 同机自检证明 4 线程 CPU 真并行(3.98x,hw=20),故瓶颈是 GDI+ 光栅化
本身的串行化,不是调度。因此并行对光栅化无收益,仍保持"视界停后每帧补 1 个网格"(一次补齐只会把
一帧从 ~10ms 抬到 ~31ms,更易掉帧);并行只用于本来就要一次画完的 RebuildWideBuffer()。
验证:dragperf mode 0/1/3/5/6/8/9;pancheck 7 场景全 dy=0;zoomcheck mode 0/13/14 无回归
(f1 8~9ms、缩放收敛 3649 差异=预存在残差);make clean && make all 零警告。
文档同步:§9.18 新增;§11.2 第 5、14 条改为"解禁但必须隔离状态"的新表述。
|
2026-10-13 03:16:47 +08:00 |
|
DeepSeek V4.1 Flash
|
065b948297
|
交互流畅性三项修复:光标不再跟随视界 / 缩放首帧不再卡顿 / 超大信号拖预览不再拖慢前台
一、光标不再跟随视界(用户要求)
- 删除 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。
|
2026-10-13 01:25:39 +08:00 |
|
DeepSeek V4.1 Flash
|
de27d9b94f
|
修复垂直中键拖拽后再平移的网格纵向跳变/绘制错误
症状:对某网格做一次纵向中键拖拽(幅值窗口上下平移)后,不松开中键、只做一点点
水平(或垂直)位移,该网格波形整体纵向错位(最多 15px),看起来像"纵向跳帧/抽搐"。
松开后再按下中键做同样的微小水平移动同样复现,且与缩放等级无关。
根因:RenderLaneLayerRegion 结束时无条件把 layer.anchorYMax 重置为"当前 yMax"。
该赋值只对"整块重画 / 纵向补条(整宽)"成立(那两种情况整块像素都对齐到当前映射),
对"横向补细条(整高)"不成立 —— 横向滚动只改一条竖带,其余像素仍在原锚点上。
纵向拖动以 kLayerRollQuantum = 16px 量化:|vOffset| < 16px 时不滚动、交给贴图偏移,
因此拖动停下时图层会残留一个 <16px 的纵向偏移且 anchorYMax != 当前 yMax。第一次水平
微移(哪怕只有 3px)就把该残余偏移静默清零(vOffset 变成 0),而像素并未上移 →
整个网格的波形相对正确位置纵向错位,错位量恰好等于那个残余量。
修法:RenderLaneLayerRegion 新增形参 double anchorYMax(本次渲染像素所对应的纵向锚点),
内部以它作为幅值映射基准(yMin = anchorYMax - laneSpan)渲染,并写回
layer.anchorYMax = anchorYMax。各调用点:
* 整块重画 / 纵向补条 / 待重画补齐:传当前 yMax(纵向补条传滚动后的 layer.anchorYMax,
与已滚动像素同一映射);
* 横向补细条:传 layer.anchorYMax(保持不变),细条因此与其余像素纵向同源,
残余偏移继续由贴图偏移承担。
vOffset == 0 时新逻辑与旧逻辑完全等价,故既有"仅水平/仅纵向平移"的像素结果不变。
验证:新增离屏探针 .preflight/perf/pancheck(窗口 SW_HIDE + 直接注入消息 + 读图层内部
anchorYMax/当前 yMax/vOffset + 把持按钮帧与释放后的整幅全质量帧做纵向互相关)。
修复前 A 场景 H step 1 起 vOff 从 -15.00 被清零为 +0.00、lane0 best dy=+15px(差异 39245);
修复后全程保持 -15.00、best dy=0(差异 1220,仅已知 <=0.5px 列相位残差)。
对照/中间网格/长距水平/斜向/松开重按共 7 个场景在缩放 0/8/26 步下全部 dy=0
(中间网格 +1px 为整像素量化残差)。make clean && make all 零警告。
|
2026-10-12 22:58:27 +08:00 |
|
DeepSeek V4.1 Flash
|
7344bbe9c4
|
修复长距离中键拖拽的绘制错误与抽搐("距离太远"不再退化成排队重画)
症状(用户报告 + TestData/PROBLEM_IMG):按住中键把视界纵向移动好几个网格高(或横向拖很远),
不松开再挪回来时出现大量绘制错误与持续抽搐。
根因:RollWideBuffer 对"位移超出图层余量"只做"排队等后续帧重画"(QueueLaneRebuild + continue):
* 该帧 BlitWideBuffer 的 srcY = marginY + vOffset 已落在画布之外 → 只能贴出/贴不到像素
→ 网格里出现空白条带(截图里的白色块);
* 下一帧位移依旧超限 → 每帧都重新入队,而 RenderPendingLaneRebuilds(1) 一帧只补 1 个网格
→ 5 个网格永远补不完 = 持续抽搐;
* 纵向余量只有 ±marginY(半个网格高 ≈80px),中键纵向拖拽每帧很容易移动 20~150px
→ 几乎必现;横向同理(余量 ±半屏,快速拖拽会超过)。
修法:只有"内容真的变了"(contentStale:外观/网格几何/幅值跨度/可见性…)才排队(那时画面本身
仍是旧内容、不缺口);距离类问题一律本帧就地解决:
* |shift| < bitmapWidth 且 |vOffset| < 画布高 → 滚动:BitBlt 整块平移 + 只补画暴露的细条
(横向条 / 纵向条),并同步锚点记账(wideMin / anchorYMax);
* 任一超出画布(内容整体滚出)→ 本帧立刻整块重画并重新居中
(RenderLaneLayerRegion(i, layer, 0, bitmapWidth, 0, 0, LayerDefaultWideMin()),实测 0.4ms/网格)。
探针(.preflight/dragcheck,按用户要求"全程按住中键不放"):
* 图层条带 vs 同锚点新渲染:逐像素(滚动是纯整数平移,必须相同);
* 覆盖率:(ViewMin - layer.wideMin) * scale == marginX(±1px)且 marginY + vOffset 起的一整个
网格高都落在画布内 —— 任一条不成立,BlitWideBuffer 就会留下空白条带;
* 画面结构:网格内"无墨迹行数"与"墨迹占比"对比整幅重绘参考帧(空白条带 / 内容错位探测器)。
场景:上拖半个网格高;按住拖左右/上下/斜向并拖出窗口;纵向拖 4 个网格高再拖回来并小幅摆动;
快速大位移对角拖拽(每帧 150×110px)。A/B 验证探针有效性:临时把纵向滚动关掉
(kLayerRollQuantum = 100000)→ 立刻报 worst layer band diff 76221 / drawing errors 11;
恢复后 0 错误。
两点说明(避免误判):
1. dragcheck 的"画面像素 diff"会稳定出现 ~2.5 万像素(一个网格的 ~11%)差异 —— 那是图层与整幅
重绘的列相位差(图层画布 2449px 按 1px 列聚合,整幅重绘 1224px 列,相位差 ≤0.5px),对稠密脉冲
信号(256kHz 编码器)会放大成大片边缘差异;这是长期存在、由"结算帧"收敛的现象,不是本轮缺陷,
因此画面检查用结构化指标而不是像素 diff。
2. 截图里"整条网格被信号色填满"是正确渲染:把幅值窗口平移到与稠密信号相交的区间时,每列的
min/max 包络本来就覆盖整个网格(示波器上同样如此)。
验证(全部离屏,不占用鼠标/键盘/屏幕):dragcheck PASS(0 绘制错误;长拖拽 80 帧 + 快速对角 24 帧);
uitest 463/463;panperf PASS(竖直中位 4.4ms、水平 7-10ms);layercheck clobbers=0/24、
pan frames wrong=0/4;cursorcheck 30/30;make clean && make all 零警告。
|
2026-10-12 20:44:51 +08:00 |
|
DeepSeek V4.1 Flash
|
59a84ba126
|
「弱峰调整」升级:三组识别规则 + 滑动条 ±400% 梯度驱动
一、三组识别规则(以波峰 k 为例;L/l = 左相邻波峰/波谷,r/R = 右相邻波谷/波峰;
leftMean = (L+l)/2,rightMean = (r+R)/2)
- 第一类:v < leftMean 且 v < rightMean(两侧的"相邻峰+相邻谷"均值都要低于)
- 第二类:v < leftMean 或 v < rightMean(放宽为"或")
- 第三类:v < L 且 v < R(只与左右同类极值比较,最宽松)
波谷把"低于"换成"高于"、min/max 互换。三组不是包含关系(第三类不蕴含第二类),
因此确实存在"只满足第三类"的点。
二、梯度驱动(滑动条 |a| ∈ [0,400%];每组自身仍是 ±200%)
0–100% :只有第一类在动(0–100%)
100–200% :第一类 100–200%,第二类开始 0–100%
200–300% :第一类到上限,第二类 100–200%,第三类开始 0–100%
300–400% :前两类到上限,第三类 100–200%
负值完全对称。实现:first = sign*min(|a|,2)、second = sign*clamp(|a|-1,0,2)、
third = sign*clamp(|a|-2,0,2);某点同时满足多组时取幅度绝对值最大的一组(= 最严格的那组)。
三、每组自身的目标值映射(±200%)
0 – +100% :当前值 → 左右同类极值中较温和的一侧(波峰取较低者、波谷取较高者)
+100 – +200%:该侧 → 左右同类极值的均值(不再是"较大的那个")
0 – -100% :当前值 → 相邻异类极值中较近的一个(波峰取较高的波谷、波谷取较低的波峰)
-100 – -200%:该侧 → 相邻异类极值的均值(不再是"最小的那个")
负幅度的"不得反向增强"夹取保持不变;相邻极值之间的 smoothstep 平滑偏移逐字未改。
四、界面
滑动条量程 -400% – +400%(0 在正中间,kWeakPeakSliderScale = 4.0);数值列按 "+400%" 取宽;
右侧数值始终带符号。
五、验证(全部离屏,不占用鼠标/键盘/屏幕)
- uitest 463/463:第一类点(弱峰 520、相邻峰 620/640)0/50/100/150/200% → 520/570/620/625/630;
弱谷 580 → 580/540/500/500/500,负值 590/600/605/610;
只满足第二类的峰 130:100% 不动、150/200/250/300/400% → 140/150/162.5/175/175;
负值 -100% 不动、-150/-200/-250/-300% → 115/100/97.5/95;
只满足第三类的峰 195:200% 不动、250/300/400% → 197.5/200/200;
第一类点 200% 已达上限(400% 不再变化);±400% 夹取;默认 0%;滑块中间 = 0%、
两端 = ±400%、轨道 3/4 处 ≈ +200%。
- [Y] 端到端:合成 ripple 弱峰区间 20 → 70(50% 幅度,健康 120)。
- panperf PASS;layercheck clobbers=0/24、pan frames wrong=0/4;cursorcheck 30/30;
dragcheck 0 绘制错误;make clean && make all 零警告。
|
2026-10-12 19:38:46 +08:00 |
|
DeepSeek V4.1 Flash
|
14b60f5d9e
|
修复上下拖动后的绘制错误(图层条带 GDI 裁剪)+「弱峰调整」定义/默认值/符号显示
一、绘制错误真因:图层条带渲染时 GDI 网格线越界作画并烘进图层
上一轮把图层条带从"只有横向条"扩展到"横向条 + 纵向条"(纵向滚动补画)。但 PaintLane 里 GDI
(网格线/分界线)的裁剪矩形 RectF(m_area.X, clipTop, m_area.Width, clipBottom - clipTop) 只把 x
限制在条带内、y 仍是整个网格可见范围。纵向条带(全宽 × 窄 y)渲染时:
* GDI 网格线被画到条带之外(那儿的像素由滚动而来、本来是正确的);
* 那些行原本是"波形盖在网格线之上",变成"网格线盖在波形之上",与正确渲染不一致;
* 错误像素就在图层位图里,会被后续每次平移带着移动 → "拖到哪儿都有绘制错误"。
修法:把"本次允许改动的区域"算成 laneClip = IntersectRect(m_area, 网格可见矩形),同时用于
Graphics::SetClip 与 GDI 的 gridClip(横向补条本来就没问题,所以此前只有纵向/斜向暴露)。
二、新增探针 .preflight/dragcheck(复现用户手势)
注入真实中键消息:先把最上方网格向上拖半个网格高(触发纵向滚动),再保持按住拖左右/上下/斜向、
最后拖出窗口;每帧把图层画布的有效条带(marginY + vOffset 起、网格高行)与"用同一 wideMin 新渲染
的画布"逐像素比较(滚动是纯整数平移,必须逐像素相同;用 FromHBITMAP + LockBits 直接读像素)。
修复前:被拖网格整行不一致(2410 px ≈ 2 行全宽,错误行固定在 canvas 行 138 = 某条横向网格线,
直接指向 GDI 网格线);修复后:最差 ≤171 px(仅补条边界的抗锯齿边缘),0 处绘制错误。
三、「弱峰调整」定义
"是否偏弱"改为(正/负幅度一致):波峰必须同时低于左右两侧各自的「相邻波峰 + 相邻波谷」均值;
波谷必须同时高于上述两个均值。与旧实现(只与左右两个同类极值比较)不同:整段均匀偏弱的纹波
不再被判为弱峰,因此合成测试波形重设计(分段线性、谷恒 500、弱峰 520、健康峰 620/640)。
四、默认幅度与符号显示
默认幅度改为 0%(面板提出即不改动波形);滑块右侧数值始终显示符号(+150% / -150% / 0%),
数值列按 "+200%" 取宽避免跳动。
五、验证(全部离屏,不占用鼠标/键盘/屏幕)
- uitest 441/441:[X] 正幅度 0/50/100/150/200% → 峰 520/570/620/630/640、弱谷 580/540/500/500/500;
负幅度 -50/-100/-200% → 峰 510/500/500、谷 590/600/620;超范围夹取;"未反向增强"(极值点);
健康段逐位不变;30% 幅度下 N=1/2/3/10 → 550/571/571/571(收敛);默认 0%;滑块中间 = 0%、
两端 = ±200%;数值带符号(+150% 与 -150% 墨迹等宽、都比 0% 宽 ≥8px)。
- [Y] 端到端:合成 ripple(谷恒 500、一个弱峰 520)弱峰区间 20 → 70(健康 120)。
- dragcheck PASS(0 绘制错误);panperf 竖直中位 5.1ms、水平 7.1ms、20 面板竖直 25.0ms / 水平 41.3ms;
layercheck clobbers=0/24、pan frames wrong=0/4;cursorcheck 30/30;make clean && make all 零警告。
|
2026-10-12 18:25:31 +08:00 |
|
DeepSeek V4.1 Flash
|
ff3319b842
|
修复上下拖动时的绘制异常/抽搐(纵向预渲染补齐)+「弱峰增强」升级为「弱峰调整」(-200%–+200%)
一、纵向绘制异常/抽搐的根因
上一轮给图层加了「上下各半个网格高」的纵向预渲染余量,但"偏移超出余量"时仍走**排队整块重画**
(一帧只补一个网格):重画生效前 BlitWideBuffer 只能贴出有像素的那一部分,其余行留在底色上
= 网格内出现空白条带("绘制错误");且重画后的锚点与"贴图偏移"不同源 = 波形"没按预期移动"。
鼠标按在最上方网格一路拖到窗口最下方(离开网格很远)时,每帧的幅值位移都远超余量 → 每帧触发整块
重画 = 抽搐;左右拖动时若鼠标同时有纵向位移,同样触发。
二、修法:与横向完全同构的纵向滚动
- RenderLaneLayerRegion 增加 stripY/stripH(纵向条带),可只补画"新暴露的一整条"。
- RollWideBuffer 新增纵向滚动:BitBlt 整块上/下移 roll 行 → 补画暴露的那一条 →
layer.anchorYMax -= roll / pxPerValue(与横向 wideMin 同一套记账)。
必须在横向补条之前(横向补条会用当前 yMax 重置图层锚点,顺序反了细条会带上错的纵向偏移)。
- 滚动量量化(kLayerRollQuantum = 16 像素):不足量子的偏移不滚,交给贴图的 srcY 偏移处理,
使画布映射在拖动中保持稳定、折线缓存(键含幅值映射)得以命中。
未量化时"每帧滚 1~30 行"会让折线每帧重算(实测滚动 3~4ms + 整帧 10~14ms)。
三、「弱峰调整」(原「弱峰增强」)
- 术语统一:ComputeWeakPeakAdjust、WeakPeakItem.amountPercent(-200–200,默认 150)、面板标题
「弱峰调整」、标签「调整幅度 %」、光标胶囊尾缀、构建信号名「<原信号> (弱峰调整 X% M次迭代)」。
- 滑块量程 -200% – +200%,0 在正中间(ratio = (amount/200 + 1) / 2);数值列加宽以容纳 "-200%"。
- 目标值(识别规则与旧实现完全相同;amount = 幅度/100):
* 0 ≤ a ≤ 1:当前值 → 同类极值中较温和的一侧;
* 1 < a ≤ 2:该侧 → 另一侧(与旧实现逐位一致);
* -1 ≤ a < 0:当前值 → 相邻异类极值中较近的一个(波峰看较高的波谷、波谷看较低的波峰);
* -2 ≤ a < -1:较近的异类极值 → 较远的那个(弱峰被压平甚至抹除,左右平滑过渡)。
- 负幅度不得反向增强:目标夹在"不越过当前值"的一侧。平滑偏移的逐点过渡逻辑未改动。
四、验证(全部离屏,不占用鼠标/键盘/屏幕)
- uitest 443/443:[X] 正幅度 0/50/100/150/200% → 峰 550/555/560/580/600(旧值逐位保持)、
谷 450/445/440/420/400;新增负幅度 -50/-100/-200% → 峰 500/450/400、谷 500/550/560;
超范围夹取、"未反向增强"(极值点)、健康峰不动;滑块中间 = 0%、拖出两端 = ±200%;
构建信号名含「弱峰调整」;[AH] 纯竖直拖拽 16 帧仅 2 次图层重画。
- panperf:拖出网格 900px + 对角平移 31 帧,被拖网格的空白行数与整幅重绘逐帧一致(64 vs 64
→ 无空白条带),帧时 10~13ms,末帧差异与水平基线同量级(已知的 ≤0.5px 整数像素残差)。
- layercheck:clobbers=0/24、pan frames wrong=0/4、居中图层 vs 整幅重绘逐像素 0 差异;
cursorcheck 30/30;make clean && make all 零警告。
|
2026-10-12 17:10:51 +08:00 |
|
DeepSeek V4.1 Flash
|
cb030573fa
|
上下/斜向平移卡顿(图层与面板都缺纵向预渲染)+ 深放大拖拽光标跑飞
用户报告:Y 轴自适应 + 选区放大到约 1~2ms 一大格后,中键拖拽移动视界"左右很流畅,上下或
斜向会卡顿",并猜测"上下移动时没有像左右那样的预渲染缓存";另有"小缩放区间拖拽光标跑飞"。
一、先验证猜测(离屏量化,不凭感觉)
panperf 新增:纯竖直拖拽(只改被拖网格幅值、视界不动)+ 逐帧统计图层重画次数 + 5 网格全开
4 类悬浮面板(20 个)后的水平/竖直对比。结果:
* 无面板:水平 6.29ms/帧,竖直 5.26ms/帧,但竖直**每帧都整块重画那个网格的图层**(40/40)。
* 20 个面板:水平 37.7ms/帧,竖直 **166.6ms/帧** —— 这就是用户说的"上下卡顿"。
结论:猜测部分成立(纵向确实没有预渲染),但主因是另一处:浮动面板像素图层的**全局键**里含
visual.yMin/yMax,平移的纵向分量每帧小改幅值 → 整层 20 个面板每帧全部重画。水平平移不改幅值,
所以只有竖直/斜向才触发 —— 正好解释"左右流畅、上下卡顿"的不对称。
二、图层改为二维预渲染(横向 ±半屏 / 纵向 ±半个网格高)
- LaneLayer 增加 marginY / anchorYMax / pxPerValue;位图高度 = ceil(可见网格高) + 2*marginY。
- 幅值范围按**像素行**精确扩展(上扩 marginY/pxPerValue,下扩 (bitmapHeight - marginY - laneHeight)
/pxPerValue):只有这样"扩展跨度 / 画布行数"才严格等于前台的 px/数值,可见网格在画布里的映射
才与整幅渲染逐像素同源(按固定数值量扩展会留下 <1px 的纵向缩放误差,水平残差会从 77k 涨到 105k)。
- 图层里的幅值刻度改以**前台可见网格**的跨度/高度取 NiceStep(m_wideTickSpan / m_wideTickHeight):
画布更高更宽,若按画布值取步长,网格线密度与前台不一致(贴图后拖动时网格线跳变)。
- 贴图:srcY = round(top - lane.Y) + marginY + round((anchorYMax - 当前 yMax) * pxPerValue);
超出画布上下端时只贴有像素的那一段;偏移超出余量才排队重画(重画即重新居中)。
- LaneLayerHash 只计幅值**跨度**、不计绝对范围(绝对范围是贴图偏移的来源)。
- 网格分界线移出图层(m_renderWide 时跳过,改由 PaintLaneDividers() 在宽基底路径补画一次):
它锚定在网格的屏幕位置,纵向偏移贴图会把它带着一起跑。
三、面板像素图层不再被幅值范围击穿
PanelGlobalKey() 去掉 visual.yMin/yMax(面板内容与幅值区间无关)。代价仅是 95.7% 不透明底背后
那 4.3% 透出的底层像素可能旧一帧(色差有界 ≤ 0.043*255 ≈ 11 级),与既有的"旧一帧"取舍一致。
四、深放大下拖拽光标跑飞
历史:388d3b6/67f659a 的"视图变化后夹光标"被 eaf5b9c 刻意删除(改为光标可滞留在视界外 + 贴边读数
+ 方向三角);但深放大下一屏平移就是几十倍可视跨度,光标瞬间被甩到视界外很远 = 用户看到的"跑飞"。
修法(区分连续手势与离散视图变化):
- 新增 ClampCursorsAfterPan(),在**平移**路径调用:PAN 拖拽(中键/左键)、Shift+滚轮、方向键。
- 离散视图变化(选区放大 / 适应视图 / 按网格时间轴展开)仍然不移动光标(先量好再自由探索,
贴边读数可读、可拖回来)——不要把夹取加回这几处。
- 抓取被钉在边缘的光标读数时不再沿用抓取偏移(CursorOffViewSide → m_cursorGrabOffset = 0),
否则首次移动会把目标送到视界外再被夹回边缘("一拖就跳很远 / 光标钉在边缘不动")。
五、验证(全部离屏,不占用鼠标/键盘/屏幕)
- uitest 432/432:[AH] 改为"幅值平移不再让图层失效(只改贴图偏移)、幅值跨度变化仍失效"并新增
"纯竖直拖拽 8 帧 0 次图层重画";新增 [AI]"贴边读数抓取后光标落在鼠标处(误差 0.0px)、
中键平移 1.5 屏 0 帧出界且跟手"。
- panperf(VERY_BIG_DATA、5 网格、1~2ms 一大格):竖直中位 3.8~5.0ms、40 帧仅 1 次图层重画
(修复前 40/40);水平 5.7~6.5ms(与修复前 6.29ms 持平);20 个面板时竖直 166.6 → 24.8ms/帧,
水平 37.7 → 38.8ms/帧(不对称消失)。整幅质量残差与修复前同量级(水平 77k~81k ↔ 77k~81k)。
- layercheck:clobbers=0/24、pan frames wrong=0/4、居中图层 vs 整幅重绘逐像素 0 差异。
- 新增探针 .preflight/cursorcheck(真文件 + 真缩放)30/30。
- make clean && make all 零警告。
|
2026-10-12 16:09:41 +08:00 |
|
DeepSeek V4.1 Flash
|
0afa6360ae
|
修复斜向自由拖拽视界时的卡顿/质量下降/错误绘制;预览曲线恢复抗锯齿;预览曲线不再闪烁
一、斜向拖拽退化的根因(纯水平拖拽测不出来)
平移的纵向分量 ShiftLaneRange 每帧都会小改被拖网格的 yMin/yMax,而网格幅值范围同时参与
「宽基底可用性」的几何指纹(SceneGeometryHash 原本含 yMin/yMax)与每个网格图层键的基
(LaneLayerKey 原本用全局 SceneContentHash):
* 前者 → 每帧都判定宽基底不可用 → 退化成整幅重绘,且拖拽置 m_fastFrame=true → 关抗锯齿
(绘制质量下降 + 卡顿);
* 后者 → 一个网格的幅值变化让全部 5 个网格的图层作废并全部入队,而
RenderPendingLaneRebuilds(1) 一帧只补 1 个 → 长期贴内容不一致的旧图层 = 错误绘制/断线。
二、修法
- SceneContentHash(includeStyles, includeDerived, includeLaneRange) 新增 includeLaneRange;
SceneGeometryHash() = SceneContentHash(false, true, false) → 幅值范围不再参与
「能不能走宽基底快路径」的判定。
- 新增逐网格 LaneLayerHash(laneIndex)(位图高度 + 该网格 yMin/yMax + 该网格各信号的
visible/color/opacity/lineWidth/mode);LaneLayerKey(laneIndex) 的基由全局内容指纹改为它
→ 幅值变化只让那一个网格失效,其余网格保持 Ready。
- Paint 宽基底分支:RenderPendingLaneRebuilds(1) 从 BlitWideBuffer 之后移到
RollWideBuffer() 之后、贴图之前 → 本帧预算内补好的网格本帧就可见(消除「比鼠标慢半拍」与断线)。
三、预览曲线抗锯齿
- PaintDerivedCurve 不再因 m_fastFrame 关闭抗锯齿(仅保留「陡峭段占比 > 1/8」的逃生口)。
波形基底来自图层、永远是全质量抗锯齿;预览曲线是叠加层,交互期间关 AA 就会「比普通信号粗糙」。
交互期间仍把顶点预算减半(低分辨率 → 松手恢复高分辨率)。
四、预览曲线闪烁
- AcquireNewestCurve(key, begin, end, spanBegin) 改为只按「信号 + 曲线种类」匹配(参数无关),
并优先挑窗口覆盖 [begin,end] 的条目;拖参数滑块时先用上一帧的曲线顶替,后台按新参数算好后无缝替换,
不再出现「消失一会儿又画出来」。
五、验证(全部离屏,不占用鼠标/键盘/屏幕)
- uitest 新增 [AH]:逐网格图层失效 / 斜向帧走宽基底快路径 / 参数连改 10 次 0 空白帧 /
隔离抗锯齿断言(快帧混淆边缘像素 8149 vs 8404,旧实现该值为 2)→ 420/420 通过;
make clean && make all 零警告。
- panperf:水平平移中位 6.17ms;斜向拖拽中位 9.16ms、p90 10.67ms、max 11.80ms,
走宽基底快路径 40/40(修复前 0/40),像素差与水平基线同量级(仅已知的 ≤0.5px 整数像素残差)。
- layercheck:clobbers=0/24、pan frames wrong=0/4、居中图层 vs 整幅重绘逐像素 0 差异。
|
2026-10-12 14:22:14 +08:00 |
|
DeepSeek V4.1 Flash
|
a30a6fcf4d
|
弱峰增强改回逐信号滑块;迭代次数上限可就地编辑
一、参数输入方式(回到滑块形态,但每个信号各有一套)
- 面板布局:每个被勾选的信号 = 名称行(+复选框)+ 两行滑块:
「增强等级 %」(0~200%)与「迭代次数 N」(1 ~ 该信号自己的上限)。
EdgePanelLayout 新增逐信号矩形 rowLevelTracks/rowLevelValues/rowIterTracks/rowIterValues;
PEAK 的 rowPoints/rowIterations 恒为空(MEAN/CENTER 仍用文本输入框)。
- 恢复 kWeakPeakSliderRowHeight/kWeakPeakTrackHeight/kWeakPeakTrackMinWidth 与
WeakPeakSliderHit();恢复 PlotDragMode::PEAK_LEVEL/PEAK_ITERATIONS,
并新增 m_peakDragRef(拖动时必须知道是哪个信号)。
- 列宽算式集中在 WeakPeakLabelColumnWidth()/WeakPeakValueColumnWidth(),
与 EdgePanelWidth 共用,避免布局与面板宽度计算漂移。
二、迭代次数滑块上限可就地编辑(用户要求的行为)
- 点击「迭代次数 N」滑块右侧的数值文本 → 整条滑块行(轨道 + 数值)变成一个输入框;
左侧标签保留可见。
- 输入框以空内容开始;空内容时以灰色提示当前上限值(m_peakEditCurrentMax,EndEdit 清零)。
- 过滤器为 EditFilter::BYTE(纯数字):负号、小数点、字母无法键入,粘贴同样被清洗,
从源头满足"只允许输入正整数"。
- 提交(回车 / 点击别处):仅当解析成功且 1 <= 值 <= 1000 时才作为新上限;
空内容、0、超范围一律保持原上限不变;生效后若当前迭代次数超过新上限则自动替换为新上限。
- 输入框在 paintOverlays() 中与 Y 轴上下限输入框一起绘制(覆盖在面板之上)。
- 新增 PlotEditTarget::PEAK_ITERATIONS_MAX;删除 PEAK_LEVEL_PERCENT/PEAK_ITERATIONS
(等级只能拖滑块,迭代次数只能拖滑块)。
三、数据与缓存
- Lane::WeakPeakItem 新增 maxIterations(默认 10)。
- maxIterations 参与 PanelSlotKey(决定圆钮位置,面板必须重画),
但不参与 SceneContentHash(不影响网格内曲线,不应触发波形重画)。
四、测试
- uitest [X] 改为:滑块存在性/层序/列宽、等级滑块拖动(中间=100%、右端=200%)、
迭代滑块拖动(左端=1、右端=当前上限)、点击数值进入上限编辑(空内容 + 提示值 + 输入框
横跨整行、空内容回车不改、'-'/字母被拒、输入 40 生效且右端变 40、下调到 4 时当前值被夹到 4、
输入 0 被拒、点击别处提交),并保存编辑态截图供目视核对。
- make all 零警告;uitest 409/409 通过;panperf PASS;layercheck 保持像素一致。
|
2026-10-12 13:35:50 +08:00 |
|
DeepSeek V4.1 Flash
|
1ddbc37b78
|
平移不再阻塞前台刷新;弱峰增强逐信号参数;浮动面板加入层序
一、平移:前台刷新线程永不阻塞(解决"按住键拖拽时质量下降/断线/周期性卡顿")
- RollWideBuffer 不再整块重画:逐网格整数像素平移 + 只补细条;内容指纹过期或锚点离视界
过远的网格只登记到 m_layerRebuildPending 并仍按现有像素平移(位置照样跟随视界)。
- 新增 RenderPendingLaneRebuilds(budget):Paint 每帧最多重画 1 个网格,其余留到后续帧;
队列非空时 PostMessage(kMessagePlotDerivedReady) 请宿主再送一帧,直到补齐。
- BlitWideBuffer 改为局部贴图:把 BitBlt 夹到图层可用区间,图层没覆盖到的部分留空由底色
填充(旧实现直接跳过整块,导致网格瞬间变空 = "断线")。
- "结算"(整幅全质量精确帧)触发条件收紧为三者同时成立:内容与视界同上一条快路径帧、
m_dragMode == NONE、全部图层就绪。拖拽期间不再结算,消除"宽基底快路径 / 整幅重绘(关AA)"
每两帧振荡造成的质量下降与周期性卡顿。
- 新增 LaneLayerReady(i) / LanesAllReadyForView();原 LayersReadyForView() 更名
LayersStructurallyReady()(只看结构,用于"跨度/布局变化必须整批重建一次")。
- 实测(.preflight/panperf,VERY_BIG_DATA 5 网格深放大,每帧平移 60px):平移 40 帧
中位 6.2ms / p90 7.2ms / max 7.6ms;按住键且视界停在边界 12 帧中位 0.29ms(无整幅重绘)。
二、弱峰增强:改为逐信号参数(对齐均值滤波)
- Lane::WeakPeakItem 由 using = MeanFilterItem 改为独立结构体
{ ref, levelPercent(0-200, 默认 150), iterations(1-10, 默认 1) };删除网格级
Lane::weakPeakLevel / weakPeakIterations。
- 面板改为与均值滤波完全同构:每个被勾选信号一行"名称 + 复选框",第二排两个输入框
(增强等级 % / 迭代次数 N),底部"构建为新信号";删除网格级两个滑块及其常量、
WeakPeakSliderHit、PlotDragMode::PEAK_LEVEL/PEAK_ITERATIONS、m_peakDragLane、
EdgePanelLayout::slider/sliderIterations。
- 新增 PlotEditTarget::PEAK_LEVEL_PERCENT / PEAK_ITERATIONS,EndEdit 中分别夹到 0-200 / 1-10。
- 指纹/快照/预览曲线/光标胶囊/构建新信号全部改为逐信号参数(同一网格内不同信号可用不同强度,
且"预览"与"构建"逐位一致);WeakPeakItem 不再是 MeanFilterItem,purgeRefs 改为泛型 lambda。
三、浮动面板:层序(z-order)与命中修复
- 新增 m_panelOrder(队尾 = 最上层):SyncPanelOrder 保留已有序、新面板追加到最上层;
TopPanelAtPoint 从最上层往下取第一个命中;BringPanelToFront 在点击面板时把它置顶。
- 绘制改为按 m_panelOrder 自下而上;PanelGlobalKey 混合层序(层序变化即整层重画);
QueryCursor / OnMouseMove 的"是否在面板上"也走 TopPanelAtPoint。
- 修复"上层面板的 ✕ / 标题栏被下层参数输入框抢走,导致上层面板关不掉/拖不动"。
四、测试
- uitest 新增 [AG]:层预算(每帧只重画 1 个网格、剩余排队、最终补齐并全部就绪)、
局部贴图(只覆盖视界一半时左侧被贴、右侧保持哨兵色)、面板层序(上层 ✕ 命中上层、
下层不受影响、点击下层置顶、TopPanelAtPoint 随之改变、新开面板在最上层)。
- [X] 改为逐信号参数(含两个输入框就地编辑:写入该信号自己的条目、越界夹取),
移除已不存在的滑块断言。
- 新增离屏探针 .preflight/panperf(平移帧耗时 + 按住键静止帧耗时)。
- make all 零警告;uitest 388/388 通过;panperf PASS;layercheck 保持像素一致。
|
2026-10-12 12:06:42 +08:00 |
|
DeepSeek V4.1 Flash
|
da83dc2987
|
修复缩放后平移的绘制错误/抖动与前台卡顿;弱波增强改名弱峰增强并支持 0~200%
渲染(根因与修法)
- 根因:后台图层线程渲染时会临时改写共享渲染状态(m_area/m_plotRect/视界/网格矩形),
与 UI 线程无锁的平移(OnMouseMove)和命中测试相冲 → 后台的"保存→改写→恢复"会把
用户刚做的平移弹回、命中测试读到错位网格;同时 RollWideBuffer 用单一全局锚点给所有
图层算同一位移,而各图层可能是在不同视界下渲染的(后台逐网格)→ 网格之间时间错位、
贴回像素互相矛盾;补细条又误用"当前视界"的映射(与旧像素不同源)→ ≤0.5px 接缝与拖动抖动。
- 修法:
* 删除后台图层线程(m_layerWorker 及任务队列、EnsureLayerWorker、StopLayerWorker、
LayerWorkerLoop、ScheduleStaleLayerRenders、m_layerBack、m_layerAnchorMin/Valid、
m_lastPaintTick、m_layerRescheduleStreak)。图层渲染一律在 UI 线程内同步完成。
* 逐网格独立锚点与滚动:以 LaneLayer::wideMin 为准,补细条沿用该图层自己的映射。
* RenderLaneLayerRegion 新增 wideMin 形参;新增 LayerDefaultWideMin()。
* RollWideBuffer 逐网格:内容指纹不一致或锚点离视界太远 → 该网格整块重画;
否则整数像素自贴图平移 + 只补新暴露的细条。
* Paint 宽基底分支:!LayersReadyForView() 时先同步 RebuildWideBuffer() 再滚动。
* BackgroundLayerRenderCount() 保留为"同步图层区域渲染次数"的诊断计数。
- 效果:平移不再出现网格时间错位、视界被弹回或波形抖动;居中图层与整幅重绘逐像素一致。
弱峰增强(原「弱波增强」)
- 功能改名:界面文案与标识符统一为 WeakPeak/weakPeak(ComputeWeakPeakBoost、
SetLaneWeakPeak*、EdgePanelKind::PEAK、PlotDragMode::PEAK_LEVEL/PEAK_ITERATIONS、
PLOT_CMD_LANE_WEAK_PEAK、PeakExtremum 等),导出信号名 "<原信号> (弱峰增强 M% N次迭代)"。
- 等级范围改为 0~200%,目标值分段插值:
* 0~100%:在"目标极值自身的值"与"左右同类极值中较温和的一侧"之间插值
(0% 目标就是当前值 → 完全不动,允许用户做极小幅度调整);
* 100~200%:与原先 0~100% 的插值方式一致(200% 完全拉平到另一侧)。
两段在 100% 处连续。新 1.5 == 旧 0.5,新 2.0 == 旧 1.0。
- Lane::weakPeakLevel 默认 1.5;levelPercent(构建请求/曲线缓存键)范围 0~200、默认 150;
新增 kWeakPeakLevelMax = 2.0 与 kWeakPeakPercentMax = 200;滑块轨道比例 = level / 2。
测试
- 同步更新离屏测试 .preflight/uitest:等级期望改为 0/50/100/150/200 五档;阶数扫描与
"完全恢复"块改用 150%/200% 以保持旧期望;派生信号名校验改用新名;
[J] 改为"补丁帧标记降级 → 渲染到缓存重新有效后用修复帧与整幅重绘比对(0 差异)";
[AF] 面板像素容差 32→96(面板 96% 不透明,底层 ≤0.5px 残留会透出,实测最多 6 像素/72 级);
[X] 预览曲线像素计数的颜色容差 6→40。
- 新增离屏检查 .preflight/layercheck:居中图层与整幅重绘逐像素对比、平移逐网格对齐位移、
视界覆盖检测、跨度抖动下的视界保持检测。
- make release 零警告;uitest 380/380 通过(exit=0)。
|
2026-10-12 10:10:06 +08:00 |
|
DeepSeek V4.1 Flash
|
f97c4cf289
|
落地每网格双缓冲图层 + 后台绘制架构:全局宽缓冲区重构为每网格信号图层(宽x3、只含基底),m_layerFront/m_layerBack 双缓冲 + m_layerWorker 后台线程,前台只做位图坐标偏移贴图不再绘制波形;前台图层就绪即用、内容稍旧也照用并交后台补画后交换;含不收敛保护、前台优先让路、固定加锁顺序、渲染状态完整还原;新增 BackgroundLayerRenderCount 验证后台绘制;深放大区间各交互 0.7~8.7ms/帧
|
2026-10-12 03:17:31 +08:00 |
|
DeepSeek V4.1 Flash
|
e22749b57a
|
修复缩放后平移的拖影/残留/错误元素与深放大区间卡顿:网格纵向滚动量纳入内容指纹、样式补丁增加视界校验、宽基底不再用含装饰的画面初始化、装饰缓存改为每帧重捕获并记录矩形与坐标原点、装饰底板改不透明以支持跨视界复用;交互帧 9~19ms 降至 4.5~6ms,纵向拖动残留 2 万+ 通道降至 72 通道,静止/结算帧像素精确;新增离屏诊断模式 6(逐帧计时 + 逐像素对比)
|
2026-10-12 02:40:56 +08:00 |
|
DeepSeek V4.1 Flash
|
1c9c43233d
|
非拖拽视界变化(键盘/工具栏)也走宽基底快路径:124.6ms→8.6ms;新增 wideKey 结算帧恢复像素精确;分支顺序固定为静态贴图→宽基底→样式补丁;宽基底强制全质量抗锯齿且竖向刻度改用前台跨度;像素对比验证 7 张静态/结算帧与 HEAD 字节一致、底座管线忠实(meanAbs 0.68/255)
|
2026-10-12 00:04:01 +08:00 |
|
DeepSeek V4.1 Flash
|
8b1de5d900
|
消除开始拖拽的首帧尖峰:用静态场景缓存初始化宽基底 + 启用预览功能时预建基底
平移首帧不再从零重画 2×视界:平移只是整体横移,上一帧场景缓存内容依然正确,直接贴进宽基底中心再滚动到当前视界(仅当派生预览曲线参数未变时适用)。启用弱波增强/均值滤波/中心轴时预建基底,使首次拖动其参数滑块无需重建。实测 VERY_BIG_DATA 全信号、视界 [40.1756,40.1885]s、5 网格:中键平移拖动最大 8.5ms、弱波增强滑块最大 10.8ms(原分别 35/37.6ms);全部鼠标交互全程 >=60fps、无尖峰;静止帧与 HEAD 逐像素 0 差异。
|
2026-10-11 23:21:37 +08:00 |
|
DeepSeek V4.1 Flash
|
e988da7963
|
宽缓冲改为滚动式:消除平移过程中的周期性重建尖峰
视界每帧移动 d 像素时,把缓冲内容整体自贴图平移 round(d) 像素并只补画新暴露的 |round(d)| 像素宽细条(细条渲染复用整条宽窗口的映射,故网格/波形 x 坐标与刻度密度均正确)。用锚点把内容相对时间轴的亚像素偏移限制在半个像素内;缓冲只覆盖绘图区,时间轴由前台每帧绘制。实测 VERY_BIG_DATA 全信号、视界 [40.1756,40.1885]s、5 网格:持续平移拖动约 6ms/帧、长距最大 10.5ms(原为每屏一次 33ms 尖峰);弱波增强滑块 6ms、样式 8ms;静止帧与 HEAD 逐像素 0 差异。
|
2026-10-11 23:08:46 +08:00 |
|
DeepSeek V4.1 Flash
|
b04167a9d6
|
把宽缓冲区推广为基底复用:拖弱波增强参数滑块降为 O(1)(55ms 到 5ms)
抽出 PaintLaneDerivedOverlays;宽缓冲只存基底(网格/波形/时间轴,跳过派生曲线与图例/Y轴刻度);基底校验键改为 SceneContentHash(true,false)(含外观样式、排除派生参数),因此拖弱波增强等级/迭代滑块不会让基底失效,可复用基底像素 + 只重画派生曲线叠加层。判定为结构未变且外观样式未变(仅视图或派生参数变化)时启用;外观样式变化仍走样式补丁。
实测 VERY_BIG_DATA 全信号、视界 [40.1756,40.1885]s、5 网格:弱波增强滑块 55ms 到 5.2ms,平移 1.3ms、透明度/粗细/颜色 8ms 不变;静止/结算帧与 HEAD 逐像素 0 差异(LockBits)。
|
2026-10-11 22:55:45 +08:00 |
|
DeepSeek V4.1 Flash
|
fa0d62b016
|
平移改为复用已绘制像素:新增宽缓冲区,平移动画降为 O(1) 贴图
按用户分析落地:平移时画面内容不变、只是横向平移,因此以当前视界为中心向左右各多渲染半个视界宽,存成 2×视界宽的离屏位图(用与正常绘制完全相同的 PaintLane/PaintTimeAxis,仅跳过随视界移动的图例/Y 轴刻度,改由装饰层缓存贴回)。平移时前台只做一次 BitBlt 截取子矩形(O(1)),移出边界才重建一次。
实测 VERY_BIG_DATA 全信号、视界 [40.1756,40.1885]s、5 网格:中键平移拖动 34ms→1.3ms(长距 1.7ms),透明度/粗细/颜色 16ms→8ms;静止/结算帧与修复前逐像素 0 差异(LockBits 验证)。
|
2026-10-11 22:36:42 +08:00 |
|
DeepSeek V4.1 Flash
|
aefaaef6fa
|
修复深放大下样式/参数滑块卡顿(探针定位:样式补丁用抗锯齿 + 后台发布整幅作废)
复现:VERY_BIG_DATA 全信号在视界 [40.1756,40.1885]s(Δt≈12.9ms)下拖透明度/粗细/自定义颜色/拾色/弱波增强滑块极卡,但拖光标流畅(光标命中场景缓存)。用 JDI_PERF_PROBE 门控的临时探针精确拆解:整幅/补丁耗时几乎全在包络带 FillPath(抗锯齿 ≈29ms/网格,关掉 ≈5.4ms)。
两个根因:① 样式补丁(拖透明度/粗细/颜色)不在拖拽态 → m_fastFrame=false → 用抗锯齿重画该网格包络带 ≈72ms;② 拖弱波增强/均值滤波滑块时后台每算完一条就 InvalidateSceneCache → 每帧退化成整幅重绘(150ms+)。
修复:① 样式补丁强制快速帧并置 m_sceneDegraded(补丁 72→14ms,静止后自动补全质量);② 拖拽中后台发布只标记降级、不整幅作废(弱波增强滑块 96–176→26ms);③ 交互期间省略浮动面板投影。
同一负载机器实测:透明度/粗细/颜色 77→16ms,弱波增强滑块 96–176→26ms,平移拖动 34ms(快速帧,空闲机约 8ms)。全质量/结算帧与修复前逐像素 0 差异(LockBits 验证)。
|
2026-10-11 19:07:37 +08:00 |
|
DeepSeek V4.1 Flash
|
389b4ec2cf
|
预览曲线改为按整条信号计算(与构建为新信号逐位一致);新增平移装饰层缓存消除拖拽卡顿
1) 预览曲线(均值滤波/中心轴/弱波增强)不再按可见窗口近似,统一按整条信号计算,与 BuildDerivedSignal 调用同一函数、同一段数据,逐位一致(用户要求预览与构建结果一致);整段窗口超门槛时交后台计算。
2) 新增装饰层(图例 + Y 轴刻度)像素缓存:实测整幅重绘中二者合计过半,却与时间轴视图无关。全质量帧捕获、拖拽快速帧贴回,仅捕获装饰矩形本身(无鬼影);用不含派生参数的 SceneDecorHash 校验。2ms/大格拖拽平移 13ms→约 5ms。
3) 后台任务按 (信号,曲线种类) 去重、以最新参数为准,避免拖滑块每帧入队整段任务淹没线程池。
4) 后台线程优先级低于正常,UI 优先获得 CPU。
|
2026-10-11 15:33:37 +08:00 |
|
DeepSeek V4.1 Flash
|
709cbe4a87
|
后台计算线程优先级降为低于正常,确保拖动时 UI 优先获得 CPU
派生曲线计算是可延迟的后台工作,不能与 UI/绘制线程抢核。后台线程池的每个线程设为 THREAD_PRIORITY_BELOW_NORMAL,UI 线程需要运行时系统优先调度 UI,计算只在 UI 空闲时占用 CPU。
|
2026-10-11 14:23:42 +08:00 |
|
DeepSeek V4.1 Flash
|
226943921b
|
完善异步架构:后台线程池 + 预览曲线上层绘制 + 快速帧只用于改主体的拖拽
1) 后台派生曲线计算改为多线程池(线程数 = clamp(硬件并发-1,1,4)),多条预览曲线并行计算、更快出现(原单线程顺序处理 5 条整段弱波增强约 1.8s,用户长时间看不到曲线)。
2) PaintLane 分两遍:先画全部原信号,再画全部派生预览曲线,保证预览曲线在本网格全部原信号之上、不被覆盖(用户要求)。
3) 「快速帧(关抗锯齿)」只用于会改变画面主体的拖拽(PAN/SCROLL/LANE_REORDER/MOVE_SIGNAL/BOOST_*);拖时间光标、选区框选、拖面板这类只改叠加层的交互改为复用静态场景缓存(贴图约 1ms),实测拖光标从整幅重画降到约 1-4ms。
小跨度渲染与旧实现逐像素 0 差异。
|
2026-10-11 14:15:26 +08:00 |
|
DeepSeek V4.1 Flash
|
99247622c1
|
派生曲线计算与 UI 绘制分离(后台线程 + 双缓冲),彻底消除交互卡顿
根因:重计算发生在绘制线程内(Paint 同步算派生预览曲线),一次整段弱波增强约 368ms 会阻塞消息循环,滑块/光标/视界失去跟手。
方案:新增后台计算线程 + 双缓冲。大窗口预览曲线不在绘制线程计算,本帧用最新旧曲线顶替并排后台任务;小窗口仍同步以保证预览实时;后台算完通过自定义消息通知宿主重绘(双缓冲切换)。条目用 shared_ptr 共享、绘制线程钉住整帧有效,后台只追加不改写,无数据竞争。任何改变数据层结构的操作前先 PauseDerivedAsync(join 后台线程),杜绝读取已释放/已移动采样。
实测 VERY_BIG_DATA(5 网格):弱波增强平移 2247ms→15ms,拖滑块 2303ms→15ms,静止重绘→0.25ms,交互均 ≤约 30ms,无 300ms/2s 级尖峰。小窗口同步路径与旧实现逐像素 0 差异。
|
2026-10-11 12:04:53 +08:00 |
|
DeepSeek V4.1 Flash
|
aca84df0ac
|
深度修复实验性功能预览曲线导致的交互卡顿(平移/拖滑块/缩放/改样式不跟手)
根因:派生预览曲线按可见采样窗口计算,代价正比于窗口采样数(弱波增强整段 2576 万采样单条约 368ms);旧策略对超过 4M 采样的窗口不缓存,导致大跨度下每帧(含悬停重绘)重算整段曲线;拖滑块每帧改参数即每帧重算。实测弱波增强整段拖滑块单帧约 2303ms,2.5s 跨度约 89ms。
修复:1) 大窗口也进缓存并按总字节预算淘汰(静止重绘 2247ms-0.25ms);2) 交互期间对超出预算的大窗口延迟重算、复用旧曲线(拖滑块 2303ms-18ms),小窗口仍实时重算;3) 计算缓冲改 thread_local 复用容量。仅改何时计算与是否缓存,几何与取值算法不变,逐像素比对 0 差异。
缩放等级差异:整段放大时窗口为整条信号(千万级)每帧重算故最卡;极小跨度窗口仅几千采样故不卡;中间跨度几十毫秒亦卡。
|
2026-10-11 11:32:58 +08:00 |
|
DeepSeek V4.1 Flash
|
2f7a6bc2d2
|
修复右键网格菜单后悬停提示残留;修复实验性功能预览曲线导致的平移/拖光标卡顿
1) 右键弹出网格菜单前清空绘图区悬停状态(并让 OnMouseLeave 一并清 m_hoverLegend),菜单打开后信号光晕与「可拖拽」提示气泡不再残留。
2) 时间光标处派生取值胶囊原先把请求窗口取成「可见区间 ∪ 光标下标」的并集:光标在视界外时窗口达上千万采样,超缓存上限后每帧重算整段多次迭代滑动平均(实测单帧 300ms+),并随平移/拖滑块/拖光标/改色一起变卡。改为只请求光标所在采样点的小窗口,取值依赖由 CurveSpanFor 保护带覆盖,取值逐位一致、显示效果零变化。
离屏实测 VERY_BIG_DATA:均值滤波平移 326ms→16~23ms,中心轴边沿计数平移 300ms→20ms。
|
2026-10-11 03:47:43 +08:00 |
|
DeepSeek V4.1 Flash
|
7608e85cf0
|
恢复显示效果并消除「实验性功能」交互卡顿;浮动面板改为像素图层
显示效果(回退本轮为提速引入的两处观感变化)
- 包络带恢复精确几何(去掉 ±1 列极值扩展:它把梳状细节并成实心块,用户反馈"显示效果没之前好")
- 派生曲线光晕恢复折线描边画法(不再用"带外扩填充")
交互流畅度
- 派生曲线只计算「可见区间 ± 保护带」窗口(CurveSpanFor + 区间窗口缓存),
拖动参数滑块不再重算整条曲线;均值滤波的保护带按传播半径 X*N 计算,保证窗口内取值与整条曲线一致
- 超大窗口(中心轴未限定光标区间时的"整条信号"统计,千万级 ≈100MB)改写进一次性缓冲、不进缓存:
消除 1141ms 卡顿尖峰,缓存内存 518MB → 2.6MB
- EdgeCenterCountsFor 先查计数缓存再取曲线,且只请求差集所需下标区间(该函数面板每帧都调用)
- 浮动面板改为像素图层(PanelGlobalKey / PanelSlotKey + BitBlt 贴图):只重画变脏的面板,
其余直接贴图;重叠面板用 dirtyRects 级联保证不会把旧像素盖回去
- 快速帧(交互期间)统一关闭抗锯齿(几何逐点不变),松手后强制补一帧全质量画面
其他
- 弱波增强面板内「迭代次数 N」与「弱波增强等级」两个滑块标签左对齐到同一列
验证
- .preflight/uitest 新增 [AF]:贴图帧与重画帧绘图区逐像素一致(0 差异)、面板像素一致、
连续重画确定性、PaintEdgeCursor 62~70ms → 0.3ms;全套 374 项检查通过
- .preflight/perf4 实测(VERY_BIG_DATA,5 网格全开实验性功能):
弱波增强滑块拖拽中位 58.2 → 23.6ms;平移交互帧 61 → 23ms;最大卡顿尖峰 1141 → 47ms
|
2026-10-10 21:35:31 +08:00 |
|
DeepSeek V4.1 Flash
|
b960b03cb5
|
「阶数 N」改称「迭代次数 N」;修复大文件放大后的平移卡顿与越界三角样式
一、命名(用户要求)
- 实验性功能里的「阶数 N」全部改为「迭代次数 N」(面板滑块标签、均值滤波/中心轴
的数值框标签、构建提示语)。
- 相关标识符与函数参数统一改名:order → iterations(Lane::MeanFilterItem::iterations、
Lane::weakBoostIterations、ComputeWeakWaveBoost/ComputeMovingAverageIterated 的形参、
MeanFilterCurveFor/WeakBoostCurveFor/WeakBoostValueAt/EdgeCenterCountsFor、
各曲线缓存项的字段、EdgePanelLayout::rowIterations/sliderIterations、
PlotEditTarget::MEAN_FILTER_ITERATIONS/EDGE_CENTER_ITERATIONS、
PlotDragMode::BOOST_ITERATIONS、kWeakBoostIterationsMin/Max/Label、
DerivedSignalRequest::iterations 等)。网格拖拽重排里的局部 order 为避免歧义
改名为 laneOrder;`std::memory_order_*`、`rowOrder`→`rowIterations` 一并核对无误。
- 导出信号尾缀统一为「N次迭代」:
`<原信号> (N点均值 M次迭代)`、`<原信号> (弱波增强 X% M次迭代)`;
弱波增强此前缺失该信息,现已补上(两种派生信号都恒定带迭代次数)。
状态提示语同步为「… M 次迭代信号。」。
二、越界三角与光标读数的样式统一(用户要求)
- 新增 `kMarkerTriangleGap`:网格内数据点越界时,方向三角与数值标注之间**留出间隙**
(此前三角与胶囊贴在一起,看起来像胶囊上多出的尖角);中心轴/均值滤波/弱波增强的
派生取值标注同样处理。
- 光标时间读数(以及双光标 Δt 读数)改用与网格内数据点**完全一致**的越界样式:
三角画在胶囊**外侧**、尖端朝外、与胶囊之间留出同样的间隙;为此把读数胶囊在越界一侧
向内让出「三角深度 + 间隙」。旧的"三角压在胶囊上沿"的画法以及为它额外留出的叠放
间距(kCursorTipStackGap)一并删除。
三、大文件放大后"平移 / 改配色非常卡"的根因与修复(用户要求,流畅性优先)
用离屏探针(`.preflight/perf3`)在 VERY_BIG_DATA(1.77GB、2580 万采样 × 5 信号、
1224×796 绘图区)上先量化,实测到的三个根因:
1. **包络带"梳状化"**:当信号的振荡周期与像素列宽度相当时(编码器方波在 2ms/大格下
约 3 个采样点一列),逐列包络会在"全高"与"极窄"之间交替——实测 1224 列里有 988 处
全高跳变。这种多边形让 GDI+ 的扫描线填充耗时暴涨:同一条 2448 顶点的路径
**27.6ms → 1.1ms(±1 列极值扩展后快 25 倍)**。这正是"某些放大等级下平移特别卡"
(实测 2ms/大格平移 87ms,其余等级 17~20ms)的原因。
→ `EmitEnvelopeBand` 在拼装包络带时对列窗口做 ±1 列的极值扩展(纵向最多各多 1 像素,
肉眼不可辨),一次性消除该病理。
2. **派生预览曲线按折线逐段描边**:均值滤波/弱波增强曲线在可见采样数超过像素列数时
仍按"每 2 像素一个顶点"的抗锯齿折线绘制,而这类曲线常叠在方波上(大量近垂直跳变),
实测单条 12~42ms,5 个网格合计上百毫秒。
→ 稠密时改用**逐像素列的包络带**(复用 `EmitEnvelopeBand`)一次 FillPath,实测
0.56ms / 1.53ms(分别快 22 倍 / 28 倍),视觉与细折线一致(列宽就是 1 像素)。
3. **悬停光晕用宽笔描整条折线**:实测悬停一帧多花 35ms。
→ 包络带模式下改为"把带整体外扩后填充一次"(`PaintBandHalo`),悬停帧恢复正常。
另外修掉两个相关的固定开销:
- `UiMeasureTextWidth` 增加 (字体, 文本) 记忆化缓存:它是布局代码的热点(浮动面板几何
按「网格数 × 面板种类」重复计算,每帧重复测量同一批字符串成百上千次),实测单个面板
的绘制因此从 3.2ms 降到 1ms 量级。字体重建(DPI 变化)时由 `UiClearMeasureCache` 作废。
实测(VERY_BIG_DATA 全选 5 信号,绘图区全重绘 = 平移一帧):
- 20ms/大格(用户报告的场景):平移 20.1ms → **11.3ms**;
- 2ms/大格(此前最糟):平移 86.7ms → **12.3ms**;
- 网格 1 开弱波增强:63.9ms → **17.6ms**;5 个网格全开:136.6ms → **31.9ms**;
- 悬停信号:整帧 49.6ms → **15.5ms**(与不悬停持平,光晕几乎免费)。
仍存的已知热点:0.5ms/大格附近(可见约 1000 个采样点、走**逐点描边**路径)平移约
28~39ms —— 该区间采样比像素还稀疏、无法用包络带替代,且用户明确要求保留抗锯齿,
本次未动。
四、验证
- 离屏 harness(窗口 SW_HIDE、不占用鼠标/键盘/屏幕)**355 项检查全部通过**,
新增 `[AD]`:越界读数胶囊向内让位、三角在外侧、三角与胶囊间有真实间隙、
胶囊上方不再有内容;取值标注与三角之间保留 `kMarkerTriangleGap` 的间隙(用
散点模式排除波形干扰后逐像素断言)。`[X]/[Y]/[AA]/[AB]/[Z]` 等既有用例同步通过。
- 性能结论来自 `.preflight/perf3`(离屏探针 + 两套微基准),已随任务清理。
|
2026-10-10 20:13:21 +08:00 |
|
DeepSeek V4.1 Flash
|
8c8508e5f8
|
弱波增强:去掉前置滤波并新增「阶数 N」滑块;修复选区放大越界绘制与浮动面板 ✕ 交互
- 弱波增强不再做任何前置滤波(删除内部的 5 点滑动平均 BoostShapeAt):
极值点直接在输入采样上识别。需要平滑时用户可以先用「均值滤波」构建一条
滤波后的信号,再对它做弱波增强。
- 新增「阶数 N」参数(1~10,滑块输入),语义与均值滤波的阶数 N 一致 ——
把同一处理重复应用 N 次,每阶以上一阶为输入:
一阶时改定量严格等于「等级」对应的目标值;多阶会让相邻弱波互相抬高彼此的
基准,从而把较宽的弱波区逐步恢复(实测三连弱波的中间峰 550→575→598.4)。
某一阶若不再产生改动会提前结束迭代。
`ComputeWeakWaveBoost(values, level, order, out)`;
`Lane::weakBoostOrder`(默认 1)随 LaneCarry / SplitLane / 引用重映射同步,
并进入 SceneContentHash;曲线缓存键变为「信号 + 等级 + 阶数」。
- 浮动面板同步新增一行「阶数 N」滑块(新拖拽模式 BOOST_ORDER、
EdgePanelLayout::sliderOrder);「构建为新信号」命名带上阶数
(形如 `<原信号> (弱波增强 60%×2阶)`)。
- 修复:选区放大时鼠标被捕获到绘图区之外(工具栏 / 信号面板 / 时间轴)会把
选区块画到绘图区之外,而拖拽期间只失效绘图区 → 留下无法擦除的墨迹。
现在拖拽时把选区夹在绘图区内,PaintRubberBand 再对 m_plotRect 求交一次。
- 修复:浮动面板的 ✕ 位于标题栏矩形内部,按下后会继续落到「标题栏 → 拖动面板」
分支,表现为「按住 ✕ 却把面板拖走了」;现在按下 ✕ 直接消费该次点击(抬起时
仍按原语义确认关闭),并把 ✕ 的光标改为手型,与其它关闭按钮一致。
- 修复:✕ / 滑块 / 底部按钮的悬停高亮依赖鼠标坐标,但从同一网格的波形移到面板上
时悬停状态毫无变化 → OnMouseMove 返回 false → 宿主不重绘 → 高亮不出现。
现在「进入 / 离开 / 在面板内移动」都会请求重绘。
- 离屏验证(窗口隐藏,不占用鼠标/键盘/屏幕):345 项检查全部通过,含
阶数扫描(N=1 只改外侧弱峰、N=2 传导到中间弱峰、高 N 逼近健康幅值且不越界、
健康峰在任何阶数下逐位不变)、两个滑块的按压/拖拽/钳制、选区越界零墨迹
(绘图区外像素差 0)、面板 ✕ 悬停高亮与按下不拖动、光标拖拽始终在视界内,
以及真实 20K 电机纹波录波(信号范围保持不变、20%+ 采样被调整)。
|
2026-10-10 19:20:09 +08:00 |
|
DeepSeek V4.1 Flash
|
67f659ad23
|
实验性功能:移除「差异放大」、新增「弱波增强」;修复放大后拖拽光标越出视界
- 移除差异放大:ComputeDiffAmplify、EdgePanelKind::DIFF_AMP 面板、DIFF_AMP_*
编辑目标、菜单项与相关缓存,并删除仅供它使用的 SlidingMax / SlidingExtrema /
float 版滑动平均。
- 新增弱波增强 ComputeWeakWaveBoost(values, level, out)(O(n)):
1) 对轻度平滑(5 点滑动平均)后的信号扫描单调段,方向翻转处即为波峰/波谷;
2) 波峰低于左右两个波峰均值(波谷高于左右两个波谷均值)即判为弱波;
3) 波峰目标 = 较小波峰 + 等级×(较大−较小);
波谷目标 = 较大波谷 − 等级×(较大−较小);
4) 极值点取目标值,两侧数据点在相邻极值点的偏移量之间用平滑步进过渡,
因此健康周期逐位不变、数据端点原样保留。
- 弱波增强面板:逐信号复选框 + 「弱波增强等级」滑块(0–100%,整个网格共用)
+ 「构建为新信号」按钮;构建名形如「<原信号> (弱波增强 M%)」。
等级进 SceneContentHash,改动即触发重绘;Lane 新增
weakBoost/weakBoostLevel/weakBoostItems,并在 LaneCarry、SplitLane、
RemapAfterSignalInsert/Removal 中同步。
- 修复:放大到一定程度后左右拖拽时间光标(尤其抓取较宽的读数标签、抓取偏移较大时)
会把光标时间推到视界之外;现在 CURSOR_MOVE 与 CURSOR_SPAN_MOVE 结束后
按可见区间夹取(视图变化仍不移动光标,保留贴边读数与方向三角)。
- 离屏验证(窗口隐藏,不占用鼠标/键盘/屏幕):295 项检查全部通过,
含等级 0/50/100% 的目标值与健康区逐位不变、滑块拖拽与左右钳制、
合成纹波端到端恢复(弱周期峰峰值 100→160,健康周期 200 不变)、
光标拖拽始终落在可见区间内。
|
2026-10-10 18:46:01 +08:00 |
|
DeepSeek V4.1 Flash
|
d4206a8d67
|
实验性功能新增「差异放大」,并修正「构建为新信号」按钮的间距
1. 新增「实验性功能 → 差异放大」(与其它实验功能一样是带勾选的开关)
- 用途:纹波类信号某个周期(甚至半个周期)的 M 形下凹明显偏弱时,
把这段下凹按比例拉回周边振幅,恢复正常周期的对比度,便于看出波形异常。
- 算法(全 O(n),不额外占用与信号长度成正比以外的内存):
以半径约半个纹波周期的滑动平均作为纹波中心线,用邻域内「交流分量的最大值」
作为局部纹波振幅,取全信号振幅的 75 分位作为目标振幅,
逐点增益 = 目标振幅 / 局部振幅(夹在 1 到放大倍数之间),
输出 = 中心线 + 增益 × 交流分量,再夹在周边包络内。
- 关键性质(均已用合成正弦逐点验证):
* 增益下界恒为 1,因此**只有被判定为"偏弱"的采样点会改变**,
其余采样点输出与原值逐位相同 —— 实测逐周期表格中只有一个周期发生变化;
* 放大结果夹在周边包络内,因此**任何一点都不超出原有的振幅范围**,
整体值域与其它周期的峰峰值都不变;
* 端点自动收敛到不放大,避免记录首尾被整片抬升。
- 参数「窗口点数 X」(窗口长度,建议略大于纹波周期的一半)与
「放大倍数 N」(增益上限)都可在浮动面板内按信号编辑。
2. 浮动面板
- 与「均值滤波」完全同一套排版:自上而下逐信号条目,
第一排信号名与复选框、第二排两个参数输入框;取消勾选即不显示该信号的参数与预览曲线;
启用时默认只勾选网格内第一个可见信号。
- 新增「构建为新信号」按钮,按各信号自己的参数把结果插入到源信号下方,
与「均值滤波」的构建机制完全一致(统一走同一套请求与插入流程)。
3. 光标交互
- 纵向光标额外标注差异放大曲线在该时刻的取值,配色与原始信号一致,
数值尾部括号内容为「差异化」,双光标时同样带增量。
4. 修复「构建为新信号」按钮与面板下边界的间距
- 原按钮行直接贴着面板下边框,与面板左右内边距及内容间距都不协调;
现在按钮上方与下方各保留一个与左右一致的内边距(均值滤波与差异放大同时生效)。
测试:离屏验证套件新增两组检查(合成正弦上算法只改动一个周期、放大不越界、
参数上限生效、面板排版与按钮间距;再写一份合成纹波文件真正加载渲染并核对曲线数值),
合计 289 项检查全部通过;验证过程不显示窗口、不移动鼠标键盘、不抓屏。
|
2026-10-10 17:54:27 +08:00 |
|
DeepSeek V4.1 Flash
|
8801c8f019
|
修复缩放视界下拖拽与改色卡顿、加载字节进度、光标越界三角与中心轴均值滤波统一
1. 修复选区放大后的拖拽/改色卡顿(先实测再修,不猜)
- 用离屏探针量化:缩放视界下一帧要画约 250 条网格线,GDI+ DrawLine 每条 0.1~0.19ms,
整帧 26.4ms;而包络带填充本身只要 0.36ms,真正的瓶颈是逐条直线调用。
- 网格线、时间轴刻度、绘图区边框、网格分隔线全部改为 GDI 批量 1px 直线绘制
(一次 SaveDC/IntersectClipRect/创建画笔,逐段 MoveToEx/LineTo);
纵轴数值胶囊改为把所有圆角矩形合并成一个路径、只调用一次填充。
- 半透明网格线颜色先用底色预混成不透明色,否则 GDI 无法绘制透明度;
GDI 绘制不受 GDI+ 裁剪约束,批量绘制一律显式设置裁剪矩形。
- 实测:缩放视界下场景缓存失效帧 26.4 -> 8.25ms,改色应用 7.1 -> 3.15ms,
缓存命中帧 2.06 -> 0.30ms。
2. 读取文件时显示"已读取 / 总大小 百分比"
- 解析进度回调扩展为携带真实字节数(已读字节、文件总字节);
列重组等没有字节可计的阶段只显示百分比。
- 新增字节数格式化:单位在 GB / MB / KB 之间自适应,只输出数字与单位;
已读大小随进度自动切换单位。
- 进度浮层按"已读取 / 文件总大小 百分比"显示,两个大小与百分比都保留两位小数,
完全取代原来的整数百分比。
3. 网格编号拖拽重排的绘制修复
- GX 编号悬停时保持普通箭头光标(不再变成四向箭头),按下拖拽时才使用上下箭头。
- 拖拽时先把整幅静态场景(含主时间轴)贴回,再把目标槽位高亮、被拖网格的轮廓与
投影裁剪在绘图区内,因此拖到工具栏甚至更上方、或拖到时间轴及下方都不再出现
残留与错乱。
4. 网格 Y 轴数值只在完全移出可视区时才不绘制
- 原来只要碰到边界就整条不画;现在按可见范围裁剪胶囊与数值,
部分可见时绘制未被遮挡的部分。
5. 边沿计数横向光标的读数与越界指示
- 幅值读数右移,让开横线左端的三角把手,不再遮挡。
- 幅值超出视界时(横线贴边显示)在读数上方/下方绘制方向三角,
并且与读数水平居中对齐。
6. 常规光标读数越界与双光标叠放
- 光标滞留在视界之外时,时间读数同样绘制方向三角。
- 双光标读数重叠时按时间值排序:小值在上、大值在下(原来固定把第二个上移,
左右光标交换后会显示成大的在上)。
- 双光标同时越界到同一侧时,两条时间读数与时间差值读数在同一列垂直对齐,
并为差值读数绘制方向三角;叠放间距按三角深度放宽,三角不会再压在相邻读数上。
7. 中心轴边沿计数与均值滤波统一
- 参数由单一的"平滑点数 N"改为按信号的"均值点数 X + 阶数 N",与实验性功能中的
均值滤波完全同构(同样的命名、同样的作用、同样的曲线实现),
两条曲线逐位一致,光标处的取值标注规则也统一。
- 浮动面板改为自上而下的逐信号条目,每个条目三排:
第一排信号名与复选框,第二排均值点数与阶数输入框,第三排该信号的上升沿/下降沿统计;
取消勾选即不显示该信号的参数编辑与统计结果,启用时默认只勾选第一个可见信号。
- 网格重建继承、拆分网格、信号插入/删除的下标修正与场景缓存指纹都同步覆盖新条目。
测试:离屏自动验证套件新增一组检查(字节数格式化、解析进度字节、编号悬停/拖拽光标、
双光标读数排序与对齐、中心轴三排布局与参数、横向光标越界三角、加载浮层渲染等),
合计 241 项检查全部通过;验证过程不显示窗口、不移动鼠标键盘、不抓屏。
|
2026-10-10 16:47:05 +08:00 |
|
DeepSeek V4.1 Flash
|
bc8db72f18
|
修复色域拖拽回弹、网格 GX 拖拽重排、均值滤波多阶与逐信号条目
1. 自定义颜色面板:拖动时鼠标移出控件不再"弹回去"
- 根因是选点位置每帧都从 RGB 反推:色相 1 与 0 是同一个颜色(红),拖到色相条底部
下一帧反推回 0,指示条弹到顶部;拖到色域方块右下角之外时明度为 0(黑色),
黑色的饱和度无定义,反推得 0,选点弹到最左边。
- 现在把"选点位置(饱和度/明度)与色相"作为独立状态保存,只由对应控件与外部改色维护;
越界一律夹取到最近边界,因此鼠标离开控件后选点停在边界上。
- 统一色域方块、色相条、预览色块与色号框的圆角;RGB 滑块适当加粗;
色域选点去掉外圈深色描边(只保留白色描边),色相指示器改为白色描边的圆角长方条、
底色取该位置的颜色;色相条改用分段线性渐变绘制。
2. 网格 GX 编号支持拖拽重排
- 按住网格图例左侧的 GX 徽标即可上下拖动,整块网格(连同波形与标尺)跟随鼠标移动;
途经的网格保持自身大小、整体让出一格,目标槽位有吸附高亮提示。
- 松开后真正重排并从下到上/从上到下重新编号,信号列表徽标、图例徽标、菜单与浮动面板
中所有 GX 编号同步更新;手动排布后切换为自定义划分,后续重建网格不会打乱次序。
- 拖动期间复用静态场景缓存,按网格条带平移贴图,因此大文件下依然流畅。
3. 均值滤波升级为"均值点数 X + 阶数 N"
- 原"均值点数 N"更名"均值点数 X",新增"阶数 N":预览曲线以 X 点均值滤波的结果为蓝本,
再重复同样的滤波共 N 阶(1 阶即与原来的单次滤波逐位一致)。
- 浮动面板改为逐信号条目:第一排是信号名与复选框,第二排是均值点数与阶数输入;
只有勾选的信号才绘制预览曲线,未勾选时不显示第二排;启用功能时默认只勾选第一个信号。
- 参数按信号保存(同一网格内不同信号可用不同参数),「构建为新信号」按各自参数构建。
|
2026-10-10 15:33:45 +08:00 |
|
DeepSeek V4.1 Flash
|
2d029fb09b
|
修复合并网格后的新增信号、适应视图联动、调色面板排版与左右越界三角
1. 合并网格后新增信号不再挤进合并网格
- 分组复选框与「全部显示」按钮此前只更新可见性,没有像单信号勾选那样把划分策略
切到自定义,于是合并网格把新增信号一并吞掉。
- 现在两者在确有信号由不可见变可见时同样切换到自定义划分,
新增信号各自独占一个网格并追加在列表最下方,原合并网格保持不变。
2. 适应视图不再取消「选区放大」模式与「选区 Y 轴自适应」
- 此前适应视图会把缩放模式复位为无,并把全部网格的 autoRange 置真;
而自动适应区间只由 autoRange 决定,置真等于把已经算好的时间段幅值丢掉,
表现为"点一下适应视图,Y 轴自适应就失效了"。
- 现在保留缩放模式;开启了「选区 Y 轴自适应」的网格改为按新的时间视图重新匹配,
其余网格才恢复整段自适应。
3. 「按网格时间轴展开」联动其它网格
- 时间视图是全局的,展开后其余开启了「选区 Y 轴自适应」的网格同样重新匹配 Y 轴,
不再只有被展开的那一个网格量程合适。
4. 自定义颜色面板排版重做
- R/G/B 的标签、滑块与数值改为同属右侧一列并共用同一行,标签紧贴各自的滑块;
预览色块与色号移到左列下方并与色域方块同宽,色号编辑框不再超出面板边界;
面板高度按内容重新计算,底部不再留空。
- 色域方块上的选点去掉外圈深色描边,改为"中心为所选颜色 + 白色描边"。
- 色相指示器由黑色横条改为白色描边的圆角长方条,底色取该位置的颜色;
色相条本身改用分段线性渐变绘制。
5. 修复光标数据点向左/向右越界时的三角
- 三角沿朝向伸出的深度大于贴边时距边界的距离,导致有一大半画在网格绘制区之外,
裁剪后只剩残缺一角、描边也因此不完整。
- 现在左右越界时把锚点向内推一个三角深度,三角完整落在绘制区内;
同时四方向越界都让三角与显示的数值纵向居中对齐。
|
2026-10-10 14:38:54 +08:00 |
|
DeepSeek V4.1 Flash
|
40b12d6e8b
|
调色性能优化 + 新增色域方块拾色 + 取值胶囊高对比配色 + 数据点标记修复
1. 修复自定义颜色滑动卡顿(VERY_BIG_DATA 全信号可见时 36ms/步 → 约 12ms/步)
- 根因:场景缓存的内容指纹把逐信号的颜色/不透明度/线宽/模式都算了进去,
改一次颜色就整幅失效,于是所有网格的波形都要重新光栅化。
- 新增"不含外观样式"的几何指纹与样式快照:只有在网格结构、视图几何、信号集合与
文字都不变、仅外观样式变化时,走"局部重画"快路径 —— 只重画颜色变了的那个网格,
再把结果写回场景缓存(下一帧回到纯贴图)。
- 补丁矩形向内对齐到整数像素,避免把属于相邻网格的一行擦掉而留下 1px 接缝;
底色填充按补丁矩形本身进行(大面积不透明填充会走 GDI 快路径,不受 GDI+ 裁剪约束)。
- 该优化对调色板点击、线宽/不透明度滑块、绘制模式切换同样生效。
2. 自定义颜色新增"方块色域图"拾色
- 新增饱和度-明度方块(横轴饱和度、纵轴明度)与纵向色相条,可直接在色域图上取色。
- 与面板内其它控件完全联动:RGB 滑块、十六进制输入、预览色块同源(RGB 为唯一数据源),
并记忆色相,消色差时仍可用色相条换回颜色。
- 色域方块按当前色相预渲染缓存,色相不变时不重复绘制;色域内用十字光标、色相条用纵向光标。
3. 取值胶囊的对比度描边与文本色改为对信号配色线性响应
- 原实现只有一个亮度阈值,文字/描边只有黑、白两套固定取值。现按底色亮度选定亮/暗一侧,
再与底色的互补色按固定比例线性混合(互补色亮度恰为 1−底色亮度),
因此文字与描边的亮度随信号配色线性变化,且任意底色下都保持高对比。
4. 修复光标数据点越界三角的判定与绘制
- 是否改用三角,以"数据点本身是否超出绘制边界"为准,不再因为数值标注被贴边而改变形状。
- 三角方向修正为尖端朝外(此前上越界画下三角、左越界画右三角,方向完全相反),
内三角改按质心收缩,描边不再是歪的;上下越界时三角与显示的数值纵向居中对齐。
|
2026-10-10 14:02:37 +08:00 |
|
DeepSeek V4.1 Flash
|
d3c83237f1
|
信号 X 按钮移至行尾并统一抬起响应,光标读数补秒单位,图例悬停高亮信号,去除筛选框内圈
1. 信号条目的「移除」X 按钮移到最右端
- 与分组行关闭 X 同尺寸(18)、同右边距(同一列)、同行内垂直居中,描边宽度也统一。
- 网格编号 GX 徽标让位到 X 左侧,二者恒在列表最右侧;名称可用宽度改按实际徽标是否显示计算,
不再白留一段空间。
2. 所有 X 关闭按钮由"按下即响应"改为"左键抬起才响应"
- 与常见的可取消按钮一致:按下只记录目标,按住后移开鼠标再松开视为放弃。
- 覆盖文件分组 X、信号 X、筛选框清空 X、标签页 X 与实验性功能面板 X。
- 筛选框的清空 X 在编辑态下走的是宿主的 EDIT_FIELD 分支(OnEditMouseDown/OnEditMouseUp),
非编辑态才走面板的按下/抬起分支,两条路径都已改造。
- 标签页 X 原先只要求"抬起时仍在同一标签内",现收紧为"仍在 X 上"。
- PlotView::OnLButtonUp 增加横坐标参数以便判定抬起位置。
3. 时间光标读数补上 s 后缀
- 新增 FormatCursorTime,标签宽度测量与文字绘制同时使用,避免文字被裁掉;
内联编辑仍使用不带单位的秒值。
4. 鼠标悬停网格图例时高亮对应信号
- 除图例条目的半高亮外,对命中信号绘制与"鼠标贴近曲线"相同的光晕,
便于确认该条目对应哪条曲线。
5. 筛选信号输入框编辑态不再出现内圈
- 把输入框绘制拆分为"底板+边框"与"仅内容"两部分;筛选框外框已自绘,
编辑态改用仅内容版本,空内容时不再套一层多余的圆角内框;
有输入时的清空 X 不受影响。
|
2026-10-10 11:30:07 +08:00 |
|
DeepSeek V4.1 Flash
|
a1e30985b8
|
修复输入框全选高亮溢出、标签页重命名无法失焦、新增信号行删除按钮
1. 内联输入框全选高亮越界(组名/标签页名/信号名编辑)
UiFillRect 在不透明且面积 >= 20000 时走 GDI FillRect 快路径,而 GDI 填充不受
GDI+ SetClip 约束;长文本全选时高亮宽度可达上千像素,于是画到输入框之外。
UiDrawEditField 现在先把高亮矩形与裁剪区求交再填充。
2. 标签页重命名处于编辑态时,点击输入框之外的任何位置都不退出编辑
Application 只在点击落在标签栏内时才把点击交给 TabBar。现在 WM_LBUTTONDOWN
会先检查 m_tabBar.IsEditing(),是则提交(OnEditKey + OnTabBarResult)并退出编辑态,
覆盖绘图区、信号面板、工具栏、分隔条等位置,且不会丢失新名字。
3. 信号选择区域每个信号新增 ✕ 删除按钮
- 位置与所在组复选框同一列(垂直对齐),悬停显示手型光标并高亮。
- 点击后面板向宿主发送 WM_APP+2;Application::RemoveSignal 会重映射所有标签页的
可见性与网格引用、修正当前选中项,再真正从 DataSet 中删除该信号并释放采样内存;
删除组内最后一个信号时退化为移除整个文件分组。
- DataSet::RemoveSignal 采用移动填充 + shrink_to_fit,保证缓冲区内即刻析构。
|
2026-10-10 10:54:50 +08:00 |
|