完善异步架构:后台线程池 + 预览曲线上层绘制 + 快速帧只用于改主体的拖拽

1) 后台派生曲线计算改为多线程池(线程数 = clamp(硬件并发-1,1,4)),多条预览曲线并行计算、更快出现(原单线程顺序处理 5 条整段弱波增强约 1.8s,用户长时间看不到曲线)。
2) PaintLane 分两遍:先画全部原信号,再画全部派生预览曲线,保证预览曲线在本网格全部原信号之上、不被覆盖(用户要求)。
3) 「快速帧(关抗锯齿)」只用于会改变画面主体的拖拽(PAN/SCROLL/LANE_REORDER/MOVE_SIGNAL/BOOST_*);拖时间光标、选区框选、拖面板这类只改叠加层的交互改为复用静态场景缓存(贴图约 1ms),实测拖光标从整幅重画降到约 1-4ms。
小跨度渲染与旧实现逐像素 0 差异。
This commit is contained in:
DeepSeek V4.1 Flash committed 2026-10-11 14:15:26 +08:00
1 parent 99247622c1
commit 226943921b
3 files changed
+58 -17

No files matched your search

+48 -16
View File
@@ -2353,17 +2353,27 @@ namespace jdi
void PlotView::EnsureCurveWorker() const
{
if (m_curveWorker.joinable())
if (!m_curveWorkers.empty())
{
return;
}
// **多核并行**:后台线程池并发计算多条派生曲线(5 个网格各一条时,
// 串行要 5×368ms,并行只要约 1×368ms),预览曲线因此能很快出现。
// 线程数 = 硬件并发 - 1(给 UI/绘制线程留一个核),夹在 1~4。
unsigned int hardware = std::thread::hardware_concurrency();
int workers = static_cast<int>(hardware == 0 ? 2u : hardware) - 1;
workers = std::max(1, std::min(workers, 4));
m_curveWorkerStop = false;
m_curveWorker = std::thread([this]() { CurveWorkerLoop(); });
m_curveWorkers.reserve(static_cast<std::size_t>(workers));
for (int index = 0; index < workers; ++index)
{
m_curveWorkers.emplace_back([this]() { CurveWorkerLoop(); });
}
}
void PlotView::StopCurveWorker() const
{
if (!m_curveWorker.joinable())
if (m_curveWorkers.empty())
{
return;
}
@@ -2373,7 +2383,14 @@ namespace jdi
m_curveJobs.clear();
}
m_curveCv.notify_all();
m_curveWorker.join();
for (std::thread& worker : m_curveWorkers)
{
if (worker.joinable())
{
worker.join();
}
}
m_curveWorkers.clear();
}
void PlotView::CurveWorkerLoop() const
@@ -5461,11 +5478,18 @@ namespace jdi
return;
}
// 交互期间(拖拽 / 拖动滑块)改用「快速绘制」:关闭波形与派生曲线的抗锯齿。
// 用户明确要求"先用低分辨率,绘制完成后再用高分辨率替换"——
// 抗锯齿是单帧最贵的一项(同一条包络带实测 27.6ms → 3.2ms),几何完全不变;
// 松手时宿主会整帧失效,因此全质量版本随即补上。
m_fastFrame = (m_dragMode != PlotDragMode::NONE);
// 「快速绘制(关闭抗锯齿)」只在**会改变画面主体(波形/网格几何)**的拖拽期间启用:
// 这些拖拽每帧都要重画波形,关掉抗锯齿能显著加速(同一条包络带 27.6ms → 3.2ms)。
// ⚠️ 像「拖时间光标」「选区框选」「拖浮动面板」这类**只改叠加层、画面主体不变**的交互,
// 一旦启用快速帧就会给静态场景缓存打上「已降级」标记 → 缓存失效 → 每帧整幅重画
//(实测拖光标从约 1ms 骤升到十几毫秒)。因此这里**不**把它们算作快速帧,
// 让它们直接复用静态场景缓存(贴图 ~1ms)+ 叠加层,做到真正跟手。
const bool sceneChangingDrag =
(m_dragMode == PlotDragMode::PAN) || (m_dragMode == PlotDragMode::SCROLL_LANES) ||
(m_dragMode == PlotDragMode::LANE_REORDER) || (m_dragMode == PlotDragMode::MOVE_SIGNAL) ||
(m_dragMode == PlotDragMode::BOOST_LEVEL) ||
(m_dragMode == PlotDragMode::BOOST_ITERATIONS);
m_fastFrame = sceneChangingDrag;
// 贴图之后、静态场景之外的全部内容(随鼠标变化或位于最上层)。
// 三个分支(完全命中 / 局部重画 / 整幅重绘)共用同一段,保证观感完全一致。
@@ -6857,10 +6881,20 @@ namespace jdi
g.FillPath(&brush, &scatterPath);
}
}
}
// 中心轴均值滤波曲线(「实验性功能 → 边沿计数(中心轴)」)叠加绘制在原信号之上。
// 它只是叠加显示的派生曲线,**不会**加入左侧信号列表;
// **只有该信号被勾选时**才绘制(参数与「均值滤波」同源,曲线逐位一致)。
// 第二遍:把所有**派生预览曲线**统一绘制在**本网格全部原信号之上**。
// 若在同一个信号循环里就地叠加,后绘制的原信号会把先前信号的预览曲线盖住
//(用户明确要求"预览信号处于原信号上层不要被覆盖")。
for (const SignalRef& ref : laneSignals)
{
const ResolvedSignal resolved = ResolveSignal(ref);
if (resolved.data == nullptr || resolved.style == nullptr || !resolved.visible)
{
continue;
}
// 中心轴均值滤波曲线(「实验性功能 → 边沿计数(中心轴)」):与「均值滤波」同源,
// 只有被勾选的信号才绘制。
if (centerOn)
{
const Lane::EdgeCenterItem* centerItem =
@@ -6870,8 +6904,7 @@ namespace jdi
PaintMeanFilterCurve(g, resolved, lane, centerItem->points, centerItem->iterations);
}
}
// 均值滤波曲线(「实验性功能 → 均值滤波」):同样是仅用于预览的叠加曲线,
// 但**只有该信号被勾选时**才绘制(参数取自它自己的条目)。
// 均值滤波预览曲线:只有被勾选的信号才绘制(参数取自它自己的条目)。
if (meanFilterOn)
{
const Lane::MeanFilterItem* item =
@@ -6881,8 +6914,7 @@ namespace jdi
PaintMeanFilterCurve(g, resolved, lane, item->points, item->iterations);
}
}
// 弱波增强曲线:同样是仅用于预览的叠加曲线,只在被勾选时绘制
//(增强等级取自整个网格)。
// 弱波增强预览曲线:只有被勾选的信号才绘制(增强等级取自整个网格)。
if (weakBoostOn)
{
const Lane::WeakBoostItem* item =