修复缩放后平移的拖影/残留/错误元素与深放大区间卡顿:网格纵向滚动量纳入内容指纹、样式补丁增加视界校验、宽基底不再用含装饰的画面初始化、装饰缓存改为每帧重捕获并记录矩形与坐标原点、装饰底板改不透明以支持跨视界复用;交互帧 9~19ms 降至 4.5~6ms,纵向拖动残留 2 万+ 通道降至 72 通道,静止/结算帧像素精确;新增离屏诊断模式 6(逐帧计时 + 逐像素对比)

This commit is contained in:
DeepSeek V4.1 Flash committed 2026-10-12 02:40:56 +08:00
1 parent 1c9c43233d
commit e22749b57a
3 files changed
+224 -58

No files matched your search

+39 -9
View File
@@ -2154,19 +2154,23 @@ namespace jdi
*/
bool m_sceneDegraded = false;
// ---- 「装饰层」像素缓存(水平平移时不变的内容:Y 轴刻度胶囊 + 图例)----
// 平移会让视图每帧变化 → 静态场景缓存必然失效 → 每帧都要重画波形**以及**网格线、
// Y 轴刻度文字、图例(实测后者占整幅重绘的一半以上)。但这些装饰内容**只取决于网格几何与
// 信号外观,与时间轴视图无关**,平移时不变化。因此在每张「全质量帧」后把装饰区域的像素
// 捕获下来,拖拽(快速帧)时直接贴回,省去每帧重绘文字与圆角底板的开销。
// 仅在完全相同(`m_decorKey` 命中)时启用,避免几何/外观变化时用到过期像素。
// ---- 「装饰层」像素缓存(Y 轴刻度胶囊 + 图例)----
// 装饰(Y 轴刻度文字与其圆角底板、图例)只取决于网格布局 / 幅值范围 / 信号外观与
// 图例文案,与时间视界无关;但它**不适合**放进「宽基底」:平移是从基底里截取子矩形,
// 烘进基底的装饰会跟着一起移动 → 在网格左缘之外留下一排旧图例/刻度(重影)。
// 因此单独缓存一帧**按当前视界绘制**的装饰像素,并在内容指纹完全一致时整块贴回。
//
// ⚠️ 关键不变量:**每一帧“真正绘制了装饰”都要重新捕获**(无论是否快速帧)。
// 只要保证“缓存像素”与“缓存指纹”永远同源,贴回就不可能过期;
// 反之(例如只在全质量帧捕获)会在几何/外观变化后贴回旧像素,
// 表现为残留 / 黑块 / 错位等错误元素。
/**
* @brief 确保装饰缓存位图尺寸正确(不足则重建)。
* @param [IN] width 位图宽度。 @param [IN] height 位图高度。
* @return 可用返回 true。
*/
bool EnsureDecorCache(int width, int height);
/** @brief 释放装饰缓存位图。 */
/** @brief 释放装饰缓存位图并作废缓存。 */
void ReleaseDecorCache();
/**
* @brief 把本帧记录下来的装饰矩形从后备缓冲区捕获进装饰缓存。
@@ -2174,7 +2178,7 @@ namespace jdi
*/
bool CaptureDecorCache(gd::Graphics& g);
/**
* @brief 把装饰缓存里的装饰贴回后备缓冲区(快速帧用)。
* @brief 把装饰缓存里的装饰贴回后备缓冲区(本帧跳过了装饰绘制时使用)。
* @param [IN,OUT] g 绘图上下文。 @return 贴图成功返回 true。
*/
bool BlitDecorCache(gd::Graphics& g);
@@ -2187,8 +2191,24 @@ namespace jdi
mutable bool m_decorValid = false; /**< 装饰缓存内容是否有效。 */
mutable std::uint64_t m_decorKey = 0; /**< 装饰缓存对应的内容指纹。 */
mutable bool m_useDecorCache = false; /**< 本帧是否跳过绘制装饰、改用缓存贴回。 */
/** 本帧需要捕获 / 贴回的装饰矩形(各网格的 Y 轴胶囊与图例带)。 */
/** 本帧需要捕获的装饰矩形(各网格的 Y 轴胶囊与图例带)。 */
mutable std::vector<gd::RectF> m_decorRects;
/**
* @brief 缓存里那批像素对应的装饰矩形(捕获时与像素同时保存)。
*
* ⚠️ 贴回必须用这一份,**不能**用当前的 `m_decorRects`:
* 命中缓存时各网格会跳过绘制、根本不会重建矩形列表(为空),
* 若用当前列表就会贴错位置或一个都不贴(表现为装饰消失 / 黑块 / 错位)。
*/
mutable std::vector<gd::RectF> m_decorCapturedRects;
/**
* @brief 捕获缓存像素时使用的绘图区原点(`m_area` 左上角)。
*
* ⚠️ 贴回时**必须用这一份**推算源偏移:宽缓冲区渲染会临时改写 `m_area`,
* 若捕获与贴回两帧的 `m_area` 不同,直接用当前 `m_area` 作原点会让整块装饰错位。
*/
mutable int m_decorOriginX = 0;
mutable int m_decorOriginY = 0;
// ---- 平移「宽缓冲区」:平移只改变绘制坐标、不改变画面内容 ----
// 视界平移时整个波形并不改变,只是横向平移。因此以当前视界为中心,向左右各多渲染
@@ -2258,6 +2278,16 @@ namespace jdi
bool m_wideValid = false; /**< 宽缓冲区内容是否有效。 */
std::uint64_t m_wideKey = 0; /**< 宽缓冲区对应的内容指纹(外观/派生参数等)。 */
bool m_renderWide = false; /**< 正在渲染宽缓冲区(重入 `PaintLane` 时跳过装饰)。 */
/**
* @brief 本次 `PaintLane` 只绘制「装饰」(Y 轴刻度胶囊 + 图例 + 网格分界线)。
*
* 宽缓冲区只存「基底」(底色 + 网格线 + 波形 + 派生曲线),**不含装饰**:
* 因为装饰锚定在网格左缘、随视界横向移动,若烘进基底,平移截取时会产生重影。
* 因此前台每帧用本标志重画一次装饰(复用与整幅渲染**完全相同**的绘制代码,
* 保证逐像素一致),而不是去贴回任何缓存像素。
*/
bool m_renderDecorOnly = false;
/** 上一次走"宽缓冲快路径"时的内容指纹(含视界)。当前帧指纹与之相同 → 视界已停止变化,
* 此时改走一次整幅重绘("结算"),保证最终呈现的是**像素精确**的全质量帧。 */
std::uint64_t m_wideLastKey = 0;