消除开始拖拽的首帧尖峰:用静态场景缓存初始化宽基底 + 启用预览功能时预建基底
平移首帧不再从零重画 2×视界:平移只是整体横移,上一帧场景缓存内容依然正确,直接贴进宽基底中心再滚动到当前视界(仅当派生预览曲线参数未变时适用)。启用弱波增强/均值滤波/中心轴时预建基底,使首次拖动其参数滑块无需重建。实测 VERY_BIG_DATA 全信号、视界 [40.1756,40.1885]s、5 网格:中键平移拖动最大 8.5ms、弱波增强滑块最大 10.8ms(原分别 35/37.6ms);全部鼠标交互全程 >=60fps、无尖峰;静止帧与 HEAD 逐像素 0 差异。
This commit is contained in:
1 parent
e988da7963
commit
8b1de5d900
3 files changed
+101
-1
No files matched your search
@@ -2232,6 +2232,16 @@ namespace jdi
|
||||
bool RollWideBuffer();
|
||||
/** @brief 渲染宽缓冲区中因滚动而新暴露的一条细条(宽 |shiftPixels|)。 */
|
||||
void RenderWideStrip(int shiftPixels);
|
||||
/**
|
||||
* @brief 用「上一帧的静态场景缓存」初始化宽缓冲区(平移首帧用,避免从零重画)。
|
||||
*
|
||||
* 平移时画面只是整体横向移动,场景缓存里的内容(含派生曲线/图例)**依然正确**;
|
||||
* 因此把场景缓存的绘图区直接贴进宽缓冲中心即可,随后滚动到当前视界。
|
||||
* 仅当"视界相对场景缓存发生了平移"(跨度一致)时可用。
|
||||
*
|
||||
* @return 初始化成功返回 true。
|
||||
*/
|
||||
bool InitWideBufferFromScene();
|
||||
/** @brief 宽缓冲区内容是否仍与当前视图/内容一致。 */
|
||||
bool WideBufferMatches() const;
|
||||
/** @brief 使宽缓冲区失效。 */
|
||||
|
||||
@@ -1845,6 +1845,8 @@ namespace jdi
|
||||
{
|
||||
lane.edgeCenterPanelOpen = false;
|
||||
}
|
||||
// 预建「宽缓冲基底」,避免首次拖动参数滑块时的一次性重建尖峰。
|
||||
RebuildWideBuffer();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1988,6 +1990,8 @@ namespace jdi
|
||||
{
|
||||
lane.weakBoostPanelOpen = false;
|
||||
}
|
||||
// 预建「宽缓冲基底」(不含派生曲线),使用户首次拖动该滑块时无需从零重建(避免一次性尖峰)。
|
||||
RebuildWideBuffer();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2069,6 +2073,8 @@ namespace jdi
|
||||
{
|
||||
lane.meanFilterPanelOpen = false;
|
||||
}
|
||||
// 预建「宽缓冲基底」,避免首次拖动参数滑块时的一次性重建尖峰。
|
||||
RebuildWideBuffer();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -5578,8 +5584,13 @@ namespace jdi
|
||||
if (wideUsable)
|
||||
{
|
||||
// 已有可用基底 → 滚动(只补画新暴露的细条,恒为 O(细条),无周期性大尖峰);
|
||||
// 否则(首帧 / 跨度或内容变化)整幅重建一次基底。
|
||||
// 否则优先用"上一帧场景缓存"初始化基底(平移首帧:贴图 + 细条,避免从零重画
|
||||
// 2×视界造成的一次尖峰),再不行才整幅重建。
|
||||
bool ready = (m_wideValid && WideBufferMatches()) ? RollWideBuffer() : false;
|
||||
if (!ready && InitWideBufferFromScene())
|
||||
{
|
||||
ready = RollWideBuffer();
|
||||
}
|
||||
if (!ready)
|
||||
{
|
||||
ready = RebuildWideBuffer();
|
||||
@@ -6963,6 +6974,68 @@ namespace jdi
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PlotView::InitWideBufferFromScene()
|
||||
{
|
||||
if (!m_sceneValid || m_sceneDc == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const double span = Tab().ViewMax() - Tab().ViewMin();
|
||||
const double sceneSpan = m_sceneKey.viewMax - m_sceneKey.viewMin;
|
||||
if (!(span > 0.0) || std::fabs(sceneSpan - span) > 1.0e-9)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// 仅当"派生预览曲线参数未变"时可用:此时场景缓存内容(含派生曲线/图例)依然正确。
|
||||
// (拖弱波增强/均值滤波参数滑块时派生曲线会变,必须从零重建基底。)
|
||||
std::vector<SceneDerivedEntry> currentDerived;
|
||||
CollectSceneDerived(currentDerived);
|
||||
if (currentDerived.size() != m_sceneDerived.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (std::size_t i = 0; i < currentDerived.size(); ++i)
|
||||
{
|
||||
const SceneDerivedEntry& a = currentDerived[i];
|
||||
const SceneDerivedEntry& b = m_sceneDerived[i];
|
||||
if (a.edgeCenter != b.edgeCenter || a.meanFilter != b.meanFilter ||
|
||||
a.weakBoost != b.weakBoost || a.weakBoostLevelPercent != b.weakBoostLevelPercent ||
|
||||
a.weakBoostIterations != b.weakBoostIterations ||
|
||||
a.edgeCenterItems != b.edgeCenterItems || a.meanFilterItems != b.meanFilterItems ||
|
||||
a.weakBoostItems != b.weakBoostItems)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const int width = static_cast<int>(std::lround(m_plotRect.Width));
|
||||
const int margin = std::max(1, width / 2);
|
||||
const int bitmapWidth = width + 2 * margin;
|
||||
const float plotHeight = m_plotRect.Height;
|
||||
const int bitmapHeight = static_cast<int>(std::ceil(plotHeight));
|
||||
if (!EnsureWideBuffer(bitmapWidth, bitmapHeight))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 场景缓存的绘图区相对其原点(m_sceneX/m_sceneY)的位置。
|
||||
const int srcX = static_cast<int>(std::floor(m_plotRect.X)) - m_sceneX;
|
||||
const int srcY = static_cast<int>(std::floor(m_plotRect.Y)) - m_sceneY;
|
||||
if (BitBlt(m_wideDc, margin, 0, width, bitmapHeight, m_sceneDc, srcX, srcY, SRCCOPY) == FALSE)
|
||||
{
|
||||
m_wideValid = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
const double scale = static_cast<double>(width) / span;
|
||||
m_wideMin = m_sceneKey.viewMin - static_cast<double>(margin) / scale;
|
||||
m_wideAnchorMin = m_sceneKey.viewMin;
|
||||
m_wideSpan = span;
|
||||
m_wideKey = SceneContentHash(true, false);
|
||||
m_wideValid = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void PlotView::RenderWideStrip(int shiftPixels)
|
||||
{
|
||||
const int width = static_cast<int>(std::lround(m_plotRect.Width));
|
||||
|
||||
@@ -340,6 +340,22 @@ Documents/ 项目文档(含本文件)
|
||||
- **显示效果**:滚动平移帧截图核对无缝隙/无残字;**静止/结算帧与 HEAD 逐像素 0 差异**(`LockBits` 验证)。
|
||||
- **剩余残留**:**每次"开始一次拖拽"的第一帧**需新建缓冲(2×视界基底,负载下约 38ms 的一次尖峰);持续拖动过程中无尖峰。要消除这一次性启动尖峰,需把基底渲染做成可后台的独立渲染器(同上,较大重构)。非拖拽视界变化仍整幅重绘。
|
||||
|
||||
### 9.13 消除"开始拖拽"的首帧尖峰:用场景缓存初始化基底 + 启用预览功能时预建基底
|
||||
9.12 的残留(首帧需从零新建 2×视界基底,负载下约 38ms)已消除:
|
||||
- **平移首帧用「上一帧静态场景缓存」初始化宽基底**(`InitWideBufferFromScene`):平移只是整体横向移动,场景缓存里的内容(含派生曲线/图例)**依然正确**,因此把它直接贴进宽基底中心即可,再滚动到当前视界 —— 首帧只需一次 BitBlt + 一条细条,**不再从零重画 2×视界**。仅当"派生预览曲线参数未变"时适用(拖这些参数时派生曲线会变,须从零重建基底)。
|
||||
- **启用带预览的实验性功能时预建基底**:在 `SetLaneWeakBoost`/`SetLaneMeanFilter`/`SetLaneEdgeCenterCount(…, true)` 里预建宽基底,使**首次拖动这些参数滑块**时无需从零重建(避免一次性尖峰)。
|
||||
- **实测**(`VERY_BIG_DATA` 全信号,视界 `[40.1756, 40.1885]s`,5 网格,负载机 ms/帧):
|
||||
|
||||
| 场景 | 9.12 | 本次 |
|
||||
| --- | --- | --- |
|
||||
| 中键平移拖动(短距) | 最大 35ms(首帧重建) | **最大 8.5ms** |
|
||||
| 中键平移拖动(长距) | 最大 10.5ms | **最大 7.4ms** |
|
||||
| 弱波增强滑块 | 最大 37.6ms(首帧重建) | **最大 10.8ms** |
|
||||
| 透明度/粗细/颜色 | 8–16ms | 8–15ms |
|
||||
|
||||
- **结论**:用户报告的全部**鼠标**交互(左/中键拖动平移、样式滑块/拾色、弱波增强参数滑块)在该放大区间**全程 ≥60fps、无尖峰**;静止/结算帧与 HEAD **逐像素 0 差异**。
|
||||
- **仍存在的残留**:**非拖拽的视界变化**(键盘方向键 / 工具栏,非用户报告的鼠标场景)仍走整幅重绘(约 125ms)—— 因其没有"拖拽结束"事件可用来触发一次精确的全质量重绘,若走宽缓冲快路径会留下 ≤0.5px 的亚像素偏差且无法"结算"回精确帧,故保持整幅重绘以保证像素精确。
|
||||
|
||||
---
|
||||
|
||||
## 10. 变更日志(本文件维护记录)
|
||||
@@ -356,3 +372,4 @@ Documents/ 项目文档(含本文件)
|
||||
| 2026-10-11 | DeepSeek-V4.1-Flash | 落地用户提出的**复用像素**方案:新增**平移宽缓冲区**(以视界为中心左右各多渲染半个视界宽),平移时前台只做一次 **BitBlt 截取子矩形**(O(1)),移出边界才重建 → 深放大下平移 **34ms→1.3ms**;静止/结算帧逐像素 0 差异。 |
|
||||
| 2026-10-11 | DeepSeek-V4.1-Flash | 把宽缓冲推广为**基底复用**:抽出 `PaintLaneDerivedOverlays`、宽缓冲只存基底(不含派生曲线),基底键排除派生参数 → 拖弱波增强滑块 **55ms→5.2ms**;平移 1.3ms、样式 8ms 不变;静止帧逐像素 0 差异。残留:宽缓冲重建在前台(约每屏一次)、非拖拽视界变化仍整幅重绘。 |
|
||||
| 2026-10-11 | DeepSeek-V4.1-Flash | 宽缓冲改为**滚动式**(自贴图平移 + 只补画新暴露细条),消除"移出边界整幅重建"的周期性尖峰;缓冲只覆盖绘图区、时间轴前台绘制、亚像素误差用锚点限制在 ≤0.5px → 持续拖动平移约 6ms/帧、无尖峰(长距最大 10.5ms);静止帧逐像素 0 差异。残留:每次开始拖拽的首帧需新建缓冲(一次启动尖峰)。 |
|
||||
| 2026-10-11 | DeepSeek-V4.1-Flash | 消除"开始拖拽"的首帧尖峰:平移首帧用**静态场景缓存初始化宽基底**(`InitWideBufferFromScene`,贴图 + 细条,不再从零重画 2×视界);启用带预览的实验性功能时**预建基底**。实测:平移拖动最大 8.5ms、弱波增强滑块最大 10.8ms(原分别 35/37.6ms),用户全部鼠标交互在该放大区间**全程 ≥60fps、无尖峰**;静止帧逐像素 0 差异。残留:非拖拽视界变化(键盘/工具栏)仍整幅重绘。 |
|
||||
Reference in new issue
Block a user