非拖拽视界变化(键盘/工具栏)也走宽基底快路径:124.6ms→8.6ms;新增 wideKey 结算帧恢复像素精确;分支顺序固定为静态贴图→宽基底→样式补丁;宽基底强制全质量抗锯齿且竖向刻度改用前台跨度;像素对比验证 7 张静态/结算帧与 HEAD 字节一致、底座管线忠实(meanAbs 0.68/255)
This commit is contained in:
1 parent
8b1de5d900
commit
1c9c43233d
3 files changed
+128
-63
No files matched your search
@@ -2258,6 +2258,10 @@ namespace jdi
|
||||
bool m_wideValid = false; /**< 宽缓冲区内容是否有效。 */
|
||||
std::uint64_t m_wideKey = 0; /**< 宽缓冲区对应的内容指纹(外观/派生参数等)。 */
|
||||
bool m_renderWide = false; /**< 正在渲染宽缓冲区(重入 `PaintLane` 时跳过装饰)。 */
|
||||
/** 上一次走"宽缓冲快路径"时的内容指纹(含视界)。当前帧指纹与之相同 → 视界已停止变化,
|
||||
* 此时改走一次整幅重绘("结算"),保证最终呈现的是**像素精确**的全质量帧。 */
|
||||
std::uint64_t m_wideLastKey = 0;
|
||||
bool m_wideLastValid = false;
|
||||
|
||||
/**
|
||||
* @brief 实验性功能浮动面板的像素图层项。
|
||||
|
||||
@@ -5568,58 +5568,6 @@ namespace jdi
|
||||
}
|
||||
};
|
||||
|
||||
// 视界平移 / 派生预览曲线参数变化:画面**基底**(网格/波形)不变,只是横向平移或
|
||||
// 叠加的派生曲线变化。直接复用「宽缓冲区」已渲染的**基底**像素,只把派生曲线作为
|
||||
// 叠加层重画 —— 前台 O(1),不重画波形,因此无论放大到多深都保持跟手。
|
||||
// 判定:网格结构未变(几何指纹一致)且**逐信号外观样式未变** → 属视图/派生变化,可用宽基底;
|
||||
// 外观样式(颜色/粗细/透明度/模式)变化 → 交给下方"样式补丁"分支局部重画。
|
||||
bool wideUsable = false;
|
||||
if (m_fastFrame && m_decorValid && HasDrawableContent() && m_sceneValid &&
|
||||
m_sceneGeometryHash == SceneGeometryHash())
|
||||
{
|
||||
std::vector<std::vector<SceneStyleEntry>> currentStyles;
|
||||
CollectSceneStyles(currentStyles);
|
||||
wideUsable = (currentStyles == m_sceneStyles);
|
||||
}
|
||||
if (wideUsable)
|
||||
{
|
||||
// 已有可用基底 → 滚动(只补画新暴露的细条,恒为 O(细条),无周期性大尖峰);
|
||||
// 否则优先用"上一帧场景缓存"初始化基底(平移首帧:贴图 + 细条,避免从零重画
|
||||
// 2×视界造成的一次尖峰),再不行才整幅重建。
|
||||
bool ready = (m_wideValid && WideBufferMatches()) ? RollWideBuffer() : false;
|
||||
if (!ready && InitWideBufferFromScene())
|
||||
{
|
||||
ready = RollWideBuffer();
|
||||
}
|
||||
if (!ready)
|
||||
{
|
||||
ready = RebuildWideBuffer();
|
||||
}
|
||||
if (ready)
|
||||
{
|
||||
// 绘图区底色(覆盖时间轴与侧边留白区域)。
|
||||
UiFillRect(g, m_area, m_theme->plotBg);
|
||||
// 贴基底(绘图区:网格 + 波形)。
|
||||
BlitWideBuffer(g);
|
||||
// 时间轴:宽缓冲不含它,前台按当前视图绘制。
|
||||
const double viewMin = Tab().ViewMin();
|
||||
const double viewMax = Tab().ViewMax();
|
||||
const double spanNow = viewMax - viewMin;
|
||||
const double timeStep = NiceStep((spanNow > 0.0) ? spanNow : 1.0, kTimeTickTarget);
|
||||
std::vector<double> timeTicks;
|
||||
BuildTicks(viewMin, viewMax, timeStep, timeTicks);
|
||||
PaintTimeAxis(g, timeTicks, timeStep);
|
||||
// 派生预览曲线作为叠加层重画(很轻);随后装饰层与其它叠加层。
|
||||
for (std::size_t i = 0; i < m_lanes.size(); ++i)
|
||||
{
|
||||
PaintLaneDerivedOverlays(g, static_cast<int>(i), m_lanes[i]);
|
||||
}
|
||||
BlitDecorCache(g);
|
||||
paintOverlays();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 网格次序拖拽:静态场景缓存仍然有效,但各网格要按位移重新"摆放"。
|
||||
// 这里逐网格把缓存中的横向条带平移贴回(几十微秒),
|
||||
// 因此拖动百万级采样的网格时依然流畅,且被拖网格的波形随鼠标自由移动。
|
||||
@@ -5640,6 +5588,82 @@ namespace jdi
|
||||
paintOverlays();
|
||||
return;
|
||||
}
|
||||
|
||||
// 视界平移 / 派生预览曲线参数变化:画面**基底**(网格/波形)不变,只是横向平移或
|
||||
// 叠加的派生曲线变化。直接复用「宽缓冲区」已渲染的**基底**像素,只把派生曲线作为
|
||||
// 叠加层重画 —— 前台 O(1),不重画波形,因此无论放大到多深都保持跟手。
|
||||
// 判定:网格结构未变(几何指纹一致)且**逐信号外观样式未变** → 属视图/派生变化,可用宽基底;
|
||||
// 外观样式(颜色/粗细/透明度/模式)变化 → 交给下方"样式补丁"分支局部重画。
|
||||
// ⚠️ 位置必须在"静态场景贴图"**之后**:视界与样式都没变时(典型:鼠标悬停移动)
|
||||
// 应由静态场景贴图命中,绝不能落到这里 —— 否则每两帧就会触发一次"结算整幅重绘",
|
||||
// 悬停反而变卡。而它又必须在"样式补丁"**之前**:派生曲线参数变化(拖弱波增强 /
|
||||
// 均值滤波滑块)同样只需叠加重画派生曲线,走本分支比"逐网格补丁"更快且质量更高。
|
||||
// ⚠️ **不要求"正在拖拽"**:非拖拽的视界变化(键盘方向键 / 工具栏)同样走此快路径,
|
||||
// 避免该放大区间"整幅重绘约 124ms"的卡顿。
|
||||
bool wideUsable = false;
|
||||
if (m_decorValid && HasDrawableContent() && m_sceneValid &&
|
||||
m_sceneGeometryHash == SceneGeometryHash())
|
||||
{
|
||||
std::vector<std::vector<SceneStyleEntry>> currentStyles;
|
||||
CollectSceneStyles(currentStyles);
|
||||
wideUsable = (currentStyles == m_sceneStyles);
|
||||
}
|
||||
if (wideUsable)
|
||||
{
|
||||
// 内容(含视界)与本帧上一条快路径帧相同 → 视界已停止变化 → 走一次整幅"结算"
|
||||
// 全质量帧(保证最终呈现**像素精确**;此后命中静态场景缓存,不再回到本分支)。
|
||||
std::uint64_t wideKey = SceneContentHash(true);
|
||||
wideKey ^= static_cast<std::uint64_t>(std::llround(Tab().ViewMin() * 1.0e6));
|
||||
wideKey *= 1099511628211ull;
|
||||
wideKey ^= static_cast<std::uint64_t>(std::llround(Tab().ViewMax() * 1.0e6));
|
||||
|
||||
if (m_wideLastValid && m_wideLastKey == wideKey)
|
||||
{
|
||||
m_wideLastValid = false; // 结算:落到下方整幅重绘
|
||||
}
|
||||
else
|
||||
{
|
||||
// 已有可用基底 → 滚动(只补画新暴露的细条,恒为 O(细条),无周期性大尖峰);
|
||||
// 否则优先用"上一帧场景缓存"初始化基底(平移首帧:贴图 + 细条,避免从零重画
|
||||
// 2×视界造成的一次尖峰),再不行才整幅重建。
|
||||
bool ready = (m_wideValid && WideBufferMatches()) ? RollWideBuffer() : false;
|
||||
if (!ready && InitWideBufferFromScene())
|
||||
{
|
||||
ready = RollWideBuffer();
|
||||
}
|
||||
if (!ready)
|
||||
{
|
||||
ready = RebuildWideBuffer();
|
||||
}
|
||||
if (ready)
|
||||
{
|
||||
m_wideLastKey = wideKey;
|
||||
m_wideLastValid = true;
|
||||
// 绘图区底色(覆盖时间轴与侧边留白区域)。
|
||||
UiFillRect(g, m_area, m_theme->plotBg);
|
||||
// 贴基底(绘图区:网格 + 波形)。
|
||||
BlitWideBuffer(g);
|
||||
// 时间轴:宽缓冲不含它,前台按当前视图绘制。
|
||||
const double wideViewMin = Tab().ViewMin();
|
||||
const double wideViewMax = Tab().ViewMax();
|
||||
const double wideSpanNow = wideViewMax - wideViewMin;
|
||||
const double wideTimeStep =
|
||||
NiceStep((wideSpanNow > 0.0) ? wideSpanNow : 1.0, kTimeTickTarget);
|
||||
std::vector<double> wideTimeTicks;
|
||||
BuildTicks(wideViewMin, wideViewMax, wideTimeStep, wideTimeTicks);
|
||||
PaintTimeAxis(g, wideTimeTicks, wideTimeStep);
|
||||
// 派生预览曲线作为叠加层重画(很轻);随后装饰层与其它叠加层。
|
||||
for (std::size_t i = 0; i < m_lanes.size(); ++i)
|
||||
{
|
||||
PaintLaneDerivedOverlays(g, static_cast<int>(i), m_lanes[i]);
|
||||
}
|
||||
BlitDecorCache(g);
|
||||
paintOverlays();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 只有"外观样式"变化(典型场景:拖动自定义颜色滑块实时调色):网格结构、视图几何、
|
||||
// 波形采样都没变,只是颜色/不透明度/线宽/模式不同 —— 此时只重画受影响的网格,
|
||||
// 再写回缓存。(VERY_BIG_DATA 实测:整幅重绘 30ms+,单网格重画约 4ms。)
|
||||
@@ -5647,9 +5671,9 @@ namespace jdi
|
||||
std::vector<int> patchLanes;
|
||||
if (FindStyleOnlyLanes(patchLanes) && BlitSceneCache(g))
|
||||
{
|
||||
// ⚠️ 样式/参数**正在连续变化**(拖透明度/粗细/拾色/实验性功能滑块):这本身就是
|
||||
// 一种"交互中"。若不切换快速帧,本帧会用**抗锯齿**重画波形包络带 —— 实测
|
||||
// 单条网格的包络带 AA 填充 ≈29ms(本机负载下 72ms),而关掉 AA 只要 ≈5.6ms。
|
||||
// ⚠️ 样式**正在连续变化**(拖透明度 / 粗细 / 拾色滑块):这本身就是一种"交互中"。
|
||||
// 若不切换快速帧,本帧会用**抗锯齿**重画波形包络带 —— 实测单条网格的包络带
|
||||
// AA 填充 ≈29ms(本机负载下 72ms),而关掉 AA 只要 ≈5.6ms。
|
||||
// 因此补丁阶段强制快速帧,并在补丁后把场景缓存标记为"已降级",
|
||||
// 下一帧(参数稳定后)自动补一帧全质量 —— 既保证拖动手感,又不牺牲最终画质。
|
||||
m_fastFrame = true;
|
||||
@@ -6865,6 +6889,11 @@ namespace jdi
|
||||
Tab().ViewMax() = wideMin + static_cast<double>(bitmapWidth) / scale;
|
||||
|
||||
m_renderWide = true;
|
||||
// 宽缓冲必须与"同一视界的整幅渲染"**逐像素同源**,否则平移后网格线数量 / 锯齿会不同:
|
||||
// * 强制全质量抗锯齿(不受"拖拽快速帧"影响,绝不降级);
|
||||
// * 网格竖向刻度沿用**前台视界跨度**(而非 2× 的宽窗口跨度),否则竖网格线会减半。
|
||||
const bool savedFastFrame = m_fastFrame;
|
||||
m_fastFrame = false;
|
||||
BuildLaneVisuals();
|
||||
// 宽缓冲必须沿用**当前视界**的 Y 轴范围,否则幅值映射与前台不一致。
|
||||
for (std::size_t i = 0; i < m_lanes.size() && i < savedLanes.size(); ++i)
|
||||
@@ -6881,17 +6910,16 @@ namespace jdi
|
||||
UiFillRect(g, m_area, m_theme->plotBg);
|
||||
UiFillRect(g, m_plotRect, m_theme->cardBg);
|
||||
|
||||
const double viewMin = Tab().ViewMin();
|
||||
const double viewMax = Tab().ViewMax();
|
||||
const double sp = viewMax - viewMin;
|
||||
const double timeStep = NiceStep((sp > 0.0) ? sp : 1.0, kTimeTickTarget);
|
||||
const double fgSpan = savedViewMax - savedViewMin;
|
||||
const double timeStep = NiceStep((fgSpan > 0.0) ? fgSpan : 1.0, kTimeTickTarget);
|
||||
std::vector<double> timeTicks;
|
||||
BuildTicks(viewMin, viewMax, timeStep, timeTicks);
|
||||
BuildTicks(Tab().ViewMin(), Tab().ViewMax(), timeStep, timeTicks);
|
||||
for (std::size_t i = 0; i < m_lanes.size(); ++i)
|
||||
{
|
||||
PaintLane(g, static_cast<int>(i), m_lanes[i], timeTicks);
|
||||
}
|
||||
}
|
||||
m_fastFrame = savedFastFrame;
|
||||
m_renderWide = false;
|
||||
|
||||
// 恢复布局 / 视图 / 网格。
|
||||
@@ -7066,6 +7094,10 @@ namespace jdi
|
||||
Tab().ViewMax() = newWideMin + static_cast<double>(bitmapWidth) / scale;
|
||||
|
||||
m_renderWide = true;
|
||||
// 与 `RebuildWideBuffer` 保持完全一致:细条补画也必须**全质量抗锯齿**,且网格竖向刻度
|
||||
// 沿用**前台视界跨度**,否则滚动后新露出的细条会与既有基底的网格 / 锯齿不一致。
|
||||
const bool savedFastFrame = m_fastFrame;
|
||||
m_fastFrame = false;
|
||||
BuildLaneVisuals();
|
||||
for (std::size_t i = 0; i < m_lanes.size() && i < savedLanes.size(); ++i)
|
||||
{
|
||||
@@ -7079,8 +7111,8 @@ namespace jdi
|
||||
g.SetTextRenderingHint(gd::TextRenderingHintClearTypeGridFit);
|
||||
UiFillRect(g, m_area, m_theme->plotBg);
|
||||
UiFillRect(g, m_area, m_theme->cardBg);
|
||||
const double sp = Tab().ViewMax() - Tab().ViewMin();
|
||||
const double timeStep = NiceStep((sp > 0.0) ? sp : 1.0, kTimeTickTarget);
|
||||
const double fgSpan = savedViewMax - savedViewMin;
|
||||
const double timeStep = NiceStep((fgSpan > 0.0) ? fgSpan : 1.0, kTimeTickTarget);
|
||||
std::vector<double> timeTicks;
|
||||
BuildTicks(Tab().ViewMin(), Tab().ViewMax(), timeStep, timeTicks);
|
||||
for (std::size_t i = 0; i < m_lanes.size(); ++i)
|
||||
@@ -7088,6 +7120,7 @@ namespace jdi
|
||||
PaintLane(g, static_cast<int>(i), m_lanes[i], timeTicks);
|
||||
}
|
||||
}
|
||||
m_fastFrame = savedFastFrame;
|
||||
m_renderWide = false;
|
||||
|
||||
m_area = savedArea;
|
||||
|
||||
+29
-1
@@ -354,7 +354,34 @@ Documents/ 项目文档(含本文件)
|
||||
| 透明度/粗细/颜色 | 8–16ms | 8–15ms |
|
||||
|
||||
- **结论**:用户报告的全部**鼠标**交互(左/中键拖动平移、样式滑块/拾色、弱波增强参数滑块)在该放大区间**全程 ≥60fps、无尖峰**;静止/结算帧与 HEAD **逐像素 0 差异**。
|
||||
- **仍存在的残留**:**非拖拽的视界变化**(键盘方向键 / 工具栏,非用户报告的鼠标场景)仍走整幅重绘(约 125ms)—— 因其没有"拖拽结束"事件可用来触发一次精确的全质量重绘,若走宽缓冲快路径会留下 ≤0.5px 的亚像素偏差且无法"结算"回精确帧,故保持整幅重绘以保证像素精确。
|
||||
- **当时仍存在的残留**:**非拖拽的视界变化**(键盘方向键 / 工具栏,非用户报告的鼠标场景)仍走整幅重绘(约 125ms)—— 因其没有"拖拽结束"事件可用来触发一次精确的全质量重绘,若走宽缓冲快路径会留下 ≤0.5px 的亚像素偏差且无法"结算"回精确帧,故当时保持整幅重绘以保证像素精确。**该残留已在 9.14 中解决。**
|
||||
|
||||
---
|
||||
|
||||
### 9.14 非拖拽视界变化(键盘 / 工具栏)也走宽基底快路径 + "结算"帧保证像素精确
|
||||
9.13 的残留(非拖拽视界变化 ≈125ms / 8fps)已消除:
|
||||
- **宽基底判定去掉"必须在拖拽中"**:只要"网格几何指纹 + 逐信号外观样式 + 装饰层"未变(派生曲线参数变化也算),任何**视界变化**都可用宽基底,前台只贴图 + 画时间轴/派生叠加/装饰层 —— 键盘方向键、工具栏缩放平移帧降到 **≈8ms**。
|
||||
- **"结算"帧(保证最终像素精确)**:宽基底是整数像素平移,实际视界位置带**≤0.5px** 亚像素偏差。因此在快路径帧里把"内容指纹 ⊕ 视界"揉成一个 `wideKey`;**若本帧的 `wideKey` 与上一条快路径帧相同 → 判定"视界已停止变化" → 放弃快路径,落到整幅重绘**(一次全质量精确帧),并清空 `m_wideLastValid`。该帧同时刷新静态场景缓存,之后所有帧命中缓存(≈1ms),不会反复重绘。
|
||||
- **分支顺序有硬约束**:静态场景贴图 → **宽基底** → 样式补丁 → 整幅重绘。
|
||||
* 宽基底必须在**静态场景贴图之后**:视界与样式都没变时(鼠标悬停移动)必须命中场景贴图,否则每两帧就会触发一次"结算整幅重绘",悬停反而变卡。
|
||||
* 宽基底必须在**样式补丁之前**:派生预览曲线参数变化(拖弱波增强 / 均值滤波滑块)走宽基底比"逐网格补丁"更快且质量更高(补丁会关抗锯齿)。
|
||||
- **宽基底必须与"同一视界的整幅渲染"同源**(否则平移后网格/锯齿与前台不一致):基底渲染时**强制全质量抗锯齿**(不受拖拽快速帧影响),且网格竖向刻度沿用**前台视界跨度**而非 2× 的宽窗口跨度(后者会让竖网格线减半)。
|
||||
- **实测**(`VERY_BIG_DATA`,视界 `[40.1756, 40.1885]s`,ms/帧):
|
||||
|
||||
| 场景 | 9.13 | 本次 |
|
||||
| --- | --- | --- |
|
||||
| **非拖拽视界变化(键盘/工具栏)** | 最大 124.6ms(≈8fps) | **最大 8.6ms** |
|
||||
| 中键平移拖动(短/长距) | 8.5 / 7.4ms | 9.3 / 14.6ms |
|
||||
| 透明度 / 颜色滑块 | 8–16ms | 10.6 / 12.5ms |
|
||||
| 弱波增强滑块 | 最大 10.8ms | **最大 7.5ms** |
|
||||
| 粗细滑块 | 8–15ms | 40 帧中仅**首帧** 127ms(其余 7.7ms) |
|
||||
|
||||
- **粗细滑块那一帧 127ms 是"设计内"的**:样式连续变化期间走"样式补丁 + 已降级标记"(快),一旦样式**停止变化**(含"回到与缓存相同的值")就必须补一帧全质量 —— 该帧必然是整幅重绘。它是**用户松手后的唯一一帧**,不是持续卡顿。
|
||||
- **保真度实测**(新增 `perf.exe … 5` 像素对比模式):快路径与"同一视界的整幅全质量渲染"对比 ——
|
||||
* 视界几乎不动(<0.01px,不滚动)时:`meanAbs = 0.68/255`(逐像素几乎一致,证明"底座 + 贴图"管线本身忠实)。
|
||||
* 平移约 17px 时:`meanAbs ≈ 13.5/255`(差异集中在**密波形**上,是 ≤0.5px 亚像素平移的必然结果,肉眼不可辨);停止平移后的"结算"帧回到**像素精确**。
|
||||
- **质量回退检查**:`mean_0/1/2`、`boost_0/1/2`、`panref` 七张静态/结算帧与 HEAD **字节完全一致**(SHA256 相同)。
|
||||
- **意外收获**:平移**中途**帧比 HEAD 更干净 —— HEAD 把静态场景(含**图例 / Y 轴刻度**)直接贴进宽基底,平移后这些像素随之移动,出现**重复的网格标题与刻度重影**(见对比 PNG);本次改动后同一场景不再出现重影。
|
||||
|
||||
---
|
||||
|
||||
@@ -373,3 +400,4 @@ Documents/ 项目文档(含本文件)
|
||||
| 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 差异。残留:非拖拽视界变化(键盘/工具栏)仍整幅重绘。 |
|
||||
| 2026-10-11 | DeepSeek-V4.1-Flash | 消除 9.13 残留:**非拖拽视界变化(键盘/工具栏)也走宽基底快路径**(124.6ms→**8.6ms**),并用 `wideKey` 相等判定"视界已停止变化"触发一次**结算**整幅全质量帧以恢复像素精确;分支顺序固定为"静态贴图 → 宽基底 → 样式补丁 → 整幅",宽基底强制全质量抗锯齿且竖向刻度用前台跨度。新增像素对比模式验证:底座管线忠实(meanAbs 0.68/255),平移期仅 ≤0.5px 亚像素差;7 张静态/结算帧与 HEAD **字节一致**;平移中途帧不再有图例/刻度重影。 |
|
||||
Reference in new issue
Block a user