平移不再阻塞前台刷新;弱峰增强逐信号参数;浮动面板加入层序

一、平移:前台刷新线程永不阻塞(解决"按住键拖拽时质量下降/断线/周期性卡顿")
- 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 保持像素一致。
This commit is contained in:
DeepSeek V4.1 Flash committed 2026-10-12 12:06:42 +08:00
1 parent da83dc2987
commit 1ddbc37b78
5 files changed
+706 -544

No files matched your search

+15 -23
View File
@@ -374,13 +374,17 @@ namespace jdi
using EdgeCenterItem = MeanFilterItem;
/**
* @brief 「弱峰增强」中某个信号自己的条目。
* @brief 「弱峰增强」中某个信号自己的参数条目。
*
* 目前只用到 `ref`(增强等级属于整个网格,统一保存在 `weakPeakLevel` 中);
* 复用 `MeanFilterItem` 结构是为了与均值滤波/中心轴共享
* 「条目查找 + 信号插入/删除时的引用重映射」等既有实现。
* 与「均值滤波」完全一致:**每个被勾选的信号各自持有参数**
*(增强等级 0–200% + 迭代次数 N),因此同一网格内不同信号可以使用不同强度。
*/
using WeakPeakItem = MeanFilterItem;
struct WeakPeakItem
{
SignalRef ref; /**< 目标信号。 */
int levelPercent = 150; /**< 增强等级(0–200;100% = 到达较温和的一侧,200% = 完全拉平)。 */
int iterations = 1; /**< 迭代次数 N(1–10)。 */
};
std::wstring name; /**< 网格名称。 */
std::vector<SignalRef> signals; /**< 该网格包含的信号(按加入顺序)。 */
@@ -505,35 +509,23 @@ namespace jdi
*
* 开启后为本网格内**被勾选**的信号额外绘制一条「弱峰增强」曲线:
* 识别纹波的波峰/波谷,把“比左右同类极值更弱”的波峰上拉、波谷下拉,
* 并按「弱峰增强等级」在左右两个同类极值之间取目标值,使其恢复与周边一致的振幅。
* 并按各信号自己的「增强等级」在左右两个同类极值之间取目标值,使其恢复与周边振幅。
* 这条曲线只是叠加显示,不会作为信号加入左侧列表。
*/
bool weakPeak = false;
/**
* @brief 弱峰增强等级(0–2,面板上显示为 0%–200%)。
* @brief 参与弱峰增强预览的信号及其**各自的参数**(只包含被勾选的信号)。
*
* 0 = 目标就是当前值(完全不动);1 = 拉到较温和的一侧(波峰取较低的左/右波峰,
* 波谷取较高的左/右波谷);2 = 拉到另一侧(完全拉平)。默认 1.5,等价于旧版的 50%。
* 参数(等级 0–200% + 迭代次数 N)按信号保存,与「均值滤波」的实现方式一致:
* 同一网格内不同信号可以使用不同的增强强度,也可以分别「构建为新信号」。
*/
double weakPeakLevel = 1.5;
/**
* @brief 弱峰增强的迭代次数 N(1–10)。
*
* 与「均值滤波」的迭代次数 N 一样表示“把同一处理重复应用 N 次”(每一阶以上一阶为输入):
* 一阶时改定量就已严格等于「等级」对应的目标值;多阶会把相邻弱峰互相抬高
* 彼此的基准,从而把较宽的弱峰区逐步恢复。
*/
int weakPeakIterations = 1;
/** 参与弱峰增强预览的信号条目(只包含被勾选的信号)。 */
std::vector<WeakPeakItem> weakPeakItems;
/** 取某个信号的弱峰增强条目(未勾选时返回空指针)。 */
/** 取某个信号的弱峰增强参数(未勾选时返回空指针)。 */
const WeakPeakItem* FindWeakPeakItem(const SignalRef& ref) const;
/** 取得或创建某信号的弱峰增强条目(用于勾选)。 */
/** 取得或创建某信号的弱峰增强参数(用于勾选)。 */
WeakPeakItem& EnsureWeakPeakItem(const SignalRef& ref);
/** 取消某信号的弱峰增强勾选。 */
+7 -12
View File
@@ -136,8 +136,6 @@ namespace jdi
double meanFilterPanelY = -1.0;
bool weakPeak = false;
std::vector<Lane::WeakPeakItem> weakPeakItems;
double weakPeakLevel = 1.5;
int weakPeakIterations = 1;
bool weakPeakPanelOpen = true;
double weakPeakPanelX = -1.0;
double weakPeakPanelY = -1.0;
@@ -168,8 +166,6 @@ namespace jdi
carry.meanFilterPanelY = lane.meanFilterPanelY;
carry.weakPeak = lane.weakPeak;
carry.weakPeakItems = lane.weakPeakItems;
carry.weakPeakLevel = lane.weakPeakLevel;
carry.weakPeakIterations = lane.weakPeakIterations;
carry.weakPeakPanelOpen = lane.weakPeakPanelOpen;
carry.weakPeakPanelX = lane.weakPeakPanelX;
carry.weakPeakPanelY = lane.weakPeakPanelY;
@@ -211,8 +207,6 @@ namespace jdi
lane.meanFilterPanelY = carry.meanFilterPanelY;
lane.weakPeak = carry.weakPeak;
lane.weakPeakItems = carry.weakPeakItems;
lane.weakPeakLevel = carry.weakPeakLevel;
lane.weakPeakIterations = carry.weakPeakIterations;
lane.weakPeakPanelOpen = carry.weakPeakPanelOpen;
lane.weakPeakPanelX = carry.weakPeakPanelX;
lane.weakPeakPanelY = carry.weakPeakPanelY;
@@ -1618,17 +1612,20 @@ namespace jdi
}
}
// 均值滤波 / 中心轴边沿计数的条目同样持有信号引用,必须一起删除/前移。
const auto purgeRefs = [fileIndex, signalIndex](std::vector<Lane::MeanFilterItem>& items)
// 均值滤波 / 中心轴边沿计数 / 弱峰增强的条目同样持有信号引用,必须一起删除/前移。
// 三类条目都是「带 `ref` 的结构体」,因此用泛型 lambda 统一处理
//(弱峰增强的参数与均值滤波不同,不能再共用同一个具体类型)。
const auto purgeRefs = [fileIndex, signalIndex](auto& items)
{
using ItemType = typename std::decay_t<decltype(items)>::value_type;
items.erase(std::remove_if(items.begin(), items.end(),
[fileIndex, signalIndex](const Lane::MeanFilterItem& item)
[fileIndex, signalIndex](const ItemType& item)
{
return item.ref.fileIndex == fileIndex &&
item.ref.signalIndex == signalIndex;
}),
items.end());
for (Lane::MeanFilterItem& item : items)
for (ItemType& item : items)
{
if (item.ref.fileIndex == fileIndex && item.ref.signalIndex > signalIndex)
{
@@ -1931,8 +1928,6 @@ namespace jdi
lane.edgeCenterPanelX = source.edgeCenterPanelX;
lane.edgeCenterPanelY = source.edgeCenterPanelY;
lane.weakPeak = source.weakPeak;
lane.weakPeakLevel = source.weakPeakLevel;
lane.weakPeakIterations = source.weakPeakIterations;
lane.weakPeakPanelOpen = source.weakPeakPanelOpen;
lane.weakPeakPanelX = source.weakPeakPanelX;
lane.weakPeakPanelY = source.weakPeakPanelY;