恢复显示效果并消除「实验性功能」交互卡顿;浮动面板改为像素图层
显示效果(回退本轮为提速引入的两处观感变化) - 包络带恢复精确几何(去掉 ±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
This commit is contained in:
1 parent
b960b03cb5
commit
7608e85cf0
2 files changed
+1109
-345
No files matched your search
@@ -91,6 +91,17 @@ namespace jdi
|
||||
BOOST = 3 /**< 弱波增强。 */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 派生(叠加预览)曲线的种类。
|
||||
*
|
||||
* 「中心轴边沿计数」与「均值滤波」共用 `MEAN_FILTER`(同一套参数与同一条曲线)。
|
||||
*/
|
||||
enum class DerivedCurveKind : int
|
||||
{
|
||||
MEAN_FILTER = 0, /**< 多次迭代均值滤波。 */
|
||||
WEAK_BOOST = 1 /**< 弱波增强。 */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 波形绘图区:网格、时间轴、光标、缩放、自适应与信号拖拽。
|
||||
*
|
||||
@@ -1030,7 +1041,7 @@ namespace jdi
|
||||
void BuildPolyline(const ResolvedSignal& resolved, const LaneVisual& lane,
|
||||
std::vector<gd::PointF>& points, std::vector<std::size_t>& breaks,
|
||||
std::vector<gd::PointF>& band, std::vector<std::size_t>& bandBreaks,
|
||||
bool& bandMode) const;
|
||||
bool& bandMode, bool& bandSpiky) const;
|
||||
|
||||
/**
|
||||
* @brief 绘制单个网格。
|
||||
@@ -1064,6 +1075,23 @@ namespace jdi
|
||||
*/
|
||||
void PaintEdgeCursor(gd::Graphics& g, const std::vector<LaneVisual>& lanes);
|
||||
|
||||
/**
|
||||
* @brief 确保面板图层缓存位图可用(尺寸不符时重建)。
|
||||
* @return 可用返回 true。
|
||||
*/
|
||||
bool EnsurePanelCache();
|
||||
|
||||
/** @brief 释放面板图层缓存位图。 */
|
||||
void ReleasePanelCache();
|
||||
|
||||
/**
|
||||
* @brief 计算「全局」面板缓存键。
|
||||
*
|
||||
* 它涵盖所有面板共同依赖的状态(几何、缩放、主题、光标、选区、悬停、统计区间等);
|
||||
* 一旦变化,所有面板缓存一律作废(整层重画)。
|
||||
*/
|
||||
std::uint64_t PanelGlobalKey() const;
|
||||
|
||||
/**
|
||||
* @brief 判断坐标是否命中某网格的「边沿计数」横向光标线。
|
||||
* @param [IN] x 客户区横坐标。
|
||||
@@ -1109,6 +1137,23 @@ namespace jdi
|
||||
*/
|
||||
EdgePanelLayout EdgePanelLayoutFor(int laneIndex, EdgePanelKind kind) const;
|
||||
|
||||
/**
|
||||
* @brief 计算单个面板的缓存键(全局键之外,面板自身的内容)。
|
||||
* @param [IN] laneIndex 面板所属网格。
|
||||
* @param [IN] kind 面板种类。
|
||||
* @param [IN] layout 面板布局。
|
||||
* @return 缓存键。
|
||||
*/
|
||||
std::uint64_t PanelSlotKey(int laneIndex, EdgePanelKind kind,
|
||||
const EdgePanelLayout& layout) const;
|
||||
|
||||
/**
|
||||
* @brief 判断某个矩形是否完全落在面板图层位图之内。
|
||||
* @param [IN] rect 目标矩形(客户区坐标)。
|
||||
* @return 完全落在图层内返回 true。
|
||||
*/
|
||||
bool PanelRectInsideLayer(const gd::RectF& rect) const;
|
||||
|
||||
/**
|
||||
* @brief 获取多阶(迭代)均值滤波曲线,结果带缓存。
|
||||
*
|
||||
@@ -1192,10 +1237,49 @@ namespace jdi
|
||||
* @param [IN,OUT] g 绘图上下文。
|
||||
* @param [IN] resolved 信号视图。
|
||||
* @param [IN] lane 网格绘制信息。
|
||||
* @param [IN] curve 曲线数值数组(与信号采样等长,缺失处为 NaN)。
|
||||
* @param [IN] curve 曲线数值数组(缺失处为 NaN)。
|
||||
* @param [IN] curveBegin `curve[0]` 对应的采样下标。
|
||||
*/
|
||||
void PaintDerivedCurve(gd::Graphics& g, const ResolvedSignal& resolved, const LaneVisual& lane,
|
||||
const std::vector<float>& curve) const;
|
||||
const std::vector<float>& curve, std::size_t curveBegin) const;
|
||||
|
||||
/**
|
||||
* @brief 取得某个采样区间内的派生曲线(带缓存)。
|
||||
*
|
||||
* ⚠️ **性能关键**:派生曲线只用于“看得见的那一段”,而整条信号可能有数千万个采样点
|
||||
* (VERY_BIG_DATA 为 2576 万)。若每次都重算整条曲线(实测弱波增强 ~800ms/条),
|
||||
* 拖动等级/迭代次数滑块时每帧要花几秒钟,交互完全卡死。
|
||||
* 本函数只计算“所需区间 ± 保护带”,并按(信号 + 参数 + 对齐后的区间)缓存:
|
||||
* 实测单条降到 1~3ms,滑块拖动与平移因此恢复流畅。
|
||||
*
|
||||
* 保护带保证可见区间内的取值与“整条曲线”一致:均值滤波的核是有限宽度,
|
||||
* 弱波增强的修正沿相邻极值向内传播(每迭代一次传一个极值),
|
||||
* 因此只要求保护带足够宽(见 `kDerivedCurveGuard` 系列常量)。
|
||||
*
|
||||
* @param [IN] kind 曲线种类。
|
||||
* @param [IN] signal 目标信号。
|
||||
* @param [IN] points 均值点数 X(仅 `MEAN_FILTER` 使用)。
|
||||
* @param [IN] iterations 迭代次数 N。
|
||||
* @param [IN] levelPercent 弱波增强等级(仅 `WEAK_BOOST` 使用,0–100)。
|
||||
* @param [IN] begin 需要的采样区间起点(含)。
|
||||
* @param [IN] end 需要的采样区间终点(不含)。
|
||||
* @param [OUT] spanBegin 返回数组对应的首个采样下标。
|
||||
* @return 曲线数组指针(覆盖 `[spanBegin, spanBegin + size())`);无法计算时返回 nullptr。
|
||||
*/
|
||||
const std::vector<float>* CurveSpanFor(DerivedCurveKind kind, const SignalData& signal,
|
||||
int points, int iterations, int levelPercent,
|
||||
std::size_t begin, std::size_t end,
|
||||
std::size_t& spanBegin) const;
|
||||
|
||||
/**
|
||||
* @brief 计算当前视图对应的采样下标区间(时间单调时二分定位,否则为整段)。
|
||||
* @param [IN] file 目标文件。
|
||||
* @param [IN] count 可用采样点数。
|
||||
* @param [OUT] begin 区间起点(含)。
|
||||
* @param [OUT] end 区间终点(不含)。
|
||||
*/
|
||||
void VisibleIndexRange(const DataFile& file, std::size_t count, std::size_t& begin,
|
||||
std::size_t& end) const;
|
||||
|
||||
/**
|
||||
* @brief 绘制某条信号的多次迭代均值滤波曲线(与中心平均值曲线同一配色派生规则)。
|
||||
@@ -1438,6 +1522,14 @@ namespace jdi
|
||||
std::vector<WeakBoostBuild> m_pendingWeakBoostItems; /**< 待构建的弱波增强信号及其参数。 */
|
||||
int m_boostDragLane = -1; /**< 正在拖动滑块的面板所属网格。 */
|
||||
bool m_mouseOverPanel = false; /**< 上一帧鼠标是否落在某个实验性功能面板上。 */
|
||||
/**
|
||||
* @brief 本帧是否使用「快速绘制」(交互期间关闭波形与派生曲线的抗锯齿)。
|
||||
*
|
||||
* 用户明确要求"先用低分辨率,绘制完成后再用高分辨率替换":抗锯齿是单帧最贵的
|
||||
* 一项(同一条包络带实测 27.6ms → 3.2ms),而它的几何完全不变,
|
||||
* 因此拖拽/拖动滑块期间关闭它,松手后由宿主整帧失效自动恢复全质量绘制。
|
||||
*/
|
||||
bool m_fastFrame = false;
|
||||
|
||||
/**
|
||||
* @brief 实验性功能浮动面板的 ✕ 处于"已按下、等待抬起确认"状态。
|
||||
@@ -1503,6 +1595,37 @@ namespace jdi
|
||||
};
|
||||
mutable std::deque<WeakBoostCurveEntry> m_weakBoostCurveCache;
|
||||
|
||||
/**
|
||||
* @brief 派生曲线的「区间窗口」缓存项。
|
||||
*
|
||||
* `values[i]` 对应采样下标 `begin + i`;`begin/end` 是**实际计算**的区间
|
||||
* (已向外扩了保护带),命中条件是「参数一致且窗口包含所需区间」。
|
||||
*/
|
||||
struct CurveSpanEntry
|
||||
{
|
||||
const SignalData* signal = nullptr; /**< 目标信号。 */
|
||||
int kind = 0; /**< `DerivedCurveKind`。 */
|
||||
int points = 5; /**< 均值点数 X。 */
|
||||
int iterations = 1; /**< 迭代次数 N。 */
|
||||
int levelPercent = 0; /**< 弱波增强等级(0–100)。 */
|
||||
std::size_t begin = 0; /**< 已计算区间的起点(采样下标)。 */
|
||||
std::size_t end = 0; /**< 已计算区间的终点(不含)。 */
|
||||
std::vector<float> values; /**< 曲线数值。 */
|
||||
};
|
||||
mutable std::deque<CurveSpanEntry> m_curveSpanCache; /**< 区间窗口缓存(deque 保证引用稳定)。 */
|
||||
|
||||
/**
|
||||
* @brief 「超大窗口」派生曲线的一次性缓冲。
|
||||
*
|
||||
* 中心轴边沿计数在**没有限定光标区间**时要统计整条信号,对应窗口与信号等长
|
||||
*(亿级采样 ≈ 100MB/信号)。这类窗口绝不进 `m_curveSpanCache`:
|
||||
* 一旦被淘汰,下一帧就会重算整条曲线(实测单次 ≈ 1s 的卡顿尖峰),
|
||||
* 而且几个信号同时驻留会吃掉数百 MB。
|
||||
* 改为写入本缓冲:调用方(`EdgeCenterCountsFor`)在其返回后立即用完,
|
||||
* 因此生命周期安全。⚠️ 指针仅在下一次「超大窗口」请求之前有效。
|
||||
*/
|
||||
mutable std::vector<float> m_curveSpanScratch;
|
||||
|
||||
/** 中心轴边沿统计缓存项(增量统计机制与 `EdgeCountEntry` 完全一致)。 */
|
||||
struct EdgeCenterCountEntry
|
||||
{
|
||||
@@ -1569,6 +1692,17 @@ namespace jdi
|
||||
*/
|
||||
bool bandMode = false;
|
||||
|
||||
/**
|
||||
* @brief 该包络带是否为「梳状」(相邻列的上下包络几乎不相交)。
|
||||
*
|
||||
* 振荡周期与像素列宽相当时(例如编码器方波在 2ms/大格下约 3 个采样点一列),
|
||||
* 逐列包络会在"全高"与"极窄"之间交替。这种形状下 GDI+ 的抗锯齿扫描线代价暴涨
|
||||
* (实测同一条 2448 顶点的路径:27.6ms vs 无抗锯齿 3.2ms),
|
||||
* 而抗锯齿对它的平滑作用几乎看不出来 —— 因此绘制时改用无抗锯齿填充。
|
||||
* ⚠️ 几何本身仍然是**精确**的(不做任何加宽),否则波形会被"糊"成实心块。
|
||||
*/
|
||||
bool bandSpiky = false;
|
||||
|
||||
/** 包络带顶点:每段为「上包络正向 + 下包络反向」,闭合后即为该段的填充多边形。 */
|
||||
std::vector<gd::PointF> band;
|
||||
|
||||
@@ -1661,12 +1795,39 @@ namespace jdi
|
||||
int mode = 0; /**< 绘制模式。 */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 单个网格内「叠加预览曲线」的状态(开关 + 每条被勾选信号的参数)。
|
||||
*
|
||||
* 它**只影响网格内容的绘制**(不影响网格结构/视图几何),因此不进入
|
||||
* `SceneGeometryHash`,而是与外观样式一起作为“局部重画”的判据:
|
||||
* 拖动弱波增强的等级/迭代次数滑块时,只需重画受影响的网格,
|
||||
* 不必整幅重绘(也不会把静态场景缓存整体作废)。
|
||||
*/
|
||||
struct SceneDerivedEntry
|
||||
{
|
||||
int edgeCenter = 0; /**< 中心轴边沿计数开关。 */
|
||||
int meanFilter = 0; /**< 均值滤波开关。 */
|
||||
int weakBoost = 0; /**< 弱波增强开关。 */
|
||||
int weakBoostLevelPercent = 0; /**< 弱波增强等级(0–100)。 */
|
||||
int weakBoostIterations = 1; /**< 弱波增强迭代次数 N。 */
|
||||
/** 各条目展开为 [文件, 信号, 均值点数 X, 迭代次数 N],按原顺序(稳定)。 */
|
||||
std::vector<int> edgeCenterItems;
|
||||
std::vector<int> meanFilterItems;
|
||||
std::vector<int> weakBoostItems;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 采集当前各网格内可见信号的外观样式快照。
|
||||
* @param [OUT] out 采集结果(按网格分组)。
|
||||
*/
|
||||
void CollectSceneStyles(std::vector<std::vector<SceneStyleEntry>>& out) const;
|
||||
|
||||
/**
|
||||
* @brief 采集当前各网格的「叠加预览曲线」状态快照。
|
||||
* @param [OUT] out 采集结果(按网格分组)。
|
||||
*/
|
||||
void CollectSceneDerived(std::vector<SceneDerivedEntry>& out) const;
|
||||
|
||||
/**
|
||||
* @brief 判断当前相对缓存是否"只有外观样式发生了变化"。
|
||||
*
|
||||
@@ -1731,20 +1892,6 @@ namespace jdi
|
||||
void PaintSignalHalo(gd::Graphics& g, const ResolvedSignal& resolved,
|
||||
const std::vector<gd::PointF>& points, const std::vector<std::size_t>& breaks);
|
||||
|
||||
/**
|
||||
* @brief 稠密包络带的光晕:把带整体外扩 `extra` 像素后填充一次。
|
||||
*
|
||||
* 比用宽画笔描整条折线快一个数量级(含大量近垂直跳变的信号上尤其明显),
|
||||
* 观感也更均匀。
|
||||
*
|
||||
* @param [IN,OUT] g 绘图上下文。
|
||||
* @param [IN] band 包络带顶点(上包络正向 + 下包络反向)。
|
||||
* @param [IN] bandBreaks 各段起点。
|
||||
* @param [IN] extra 纵向额外外扩量(像素)。
|
||||
*/
|
||||
void PaintBandHalo(gd::Graphics& g, const std::vector<gd::PointF>& band,
|
||||
const std::vector<std::size_t>& bandBreaks, float extra);
|
||||
|
||||
/**
|
||||
* @brief 计算某网格图例中 GX 编号徽标的矩形(绘制与命中测试共用,保证一致)。
|
||||
* @param [IN] laneIndex 网格下标。
|
||||
@@ -1837,6 +1984,43 @@ namespace jdi
|
||||
/** 缓存捕获时各网格内信号的外观样式快照(按网格分组,与 m_sceneStyles 同序)。 */
|
||||
std::vector<std::vector<SceneStyleEntry>> m_sceneStyles;
|
||||
|
||||
/** 缓存捕获时的“叠加预览曲线”状态快照(用于判断只有曲线参数变化)。 */
|
||||
std::vector<SceneDerivedEntry> m_sceneDerived;
|
||||
|
||||
/**
|
||||
* @brief 静态场景缓存里存的是否为「快速帧」(交互期间关掉了抗锯齿)。
|
||||
*
|
||||
* 为 true 时 `SceneCacheMatches()` 直接判不命中,强制重画一次全质量版本 ——
|
||||
* 这就是用户要求的“先用低分辨率、绘制完成后再用高分辨率替换”。
|
||||
*/
|
||||
bool m_sceneDegraded = false;
|
||||
|
||||
/**
|
||||
* @brief 实验性功能浮动面板的像素图层项。
|
||||
*
|
||||
* 面板被完整绘制进 `m_panelDc`;只要 `key` 不变,下一帧就直接按
|
||||
* `[x0, y0, x1, y1)` 贴图,不再重复绘制(15 个面板全画约 33ms,贴图 <1ms)。
|
||||
*/
|
||||
struct PanelSlot
|
||||
{
|
||||
int laneIndex = -1; /**< 所属网格。 */
|
||||
int kind = 0; /**< `EdgePanelKind`。 */
|
||||
std::uint64_t key = 0; /**< 面板自身的缓存键。 */
|
||||
int x0 = 0; /**< 已缓存矩形(含阴影外扩),左。 */
|
||||
int y0 = 0; /**< 上。 */
|
||||
int x1 = 0; /**< 右(不含)。 */
|
||||
int y1 = 0; /**< 下(不含)。 */
|
||||
};
|
||||
|
||||
HBITMAP m_panelBitmap = nullptr; /**< 面板图层位图(原点与客户区一致)。 */
|
||||
HDC m_panelDc = nullptr; /**< 面板图层设备上下文。 */
|
||||
HGDIOBJ m_panelOldBitmap = nullptr; /**< 面板图层 DC 原选位图。 */
|
||||
int m_panelWidth = 0; /**< 面板图层位图宽度。 */
|
||||
int m_panelHeight = 0; /**< 面板图层位图高度。 */
|
||||
bool m_panelValid = false; /**< 图层内容是否有效。 */
|
||||
std::uint64_t m_panelGlobalKey = 0; /**< 上一层对应的全局键。 */
|
||||
std::vector<PanelSlot> m_panelSlots; /**< 上一层缓存的面板(本帧比对用)。 */
|
||||
|
||||
/**
|
||||
* @brief 查询坐标命中的图例条目。
|
||||
* @param [IN] x 客户区横坐标。
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user