上下/斜向平移卡顿(图层与面板都缺纵向预渲染)+ 深放大拖拽光标跑飞
用户报告:Y 轴自适应 + 选区放大到约 1~2ms 一大格后,中键拖拽移动视界"左右很流畅,上下或
斜向会卡顿",并猜测"上下移动时没有像左右那样的预渲染缓存";另有"小缩放区间拖拽光标跑飞"。
一、先验证猜测(离屏量化,不凭感觉)
panperf 新增:纯竖直拖拽(只改被拖网格幅值、视界不动)+ 逐帧统计图层重画次数 + 5 网格全开
4 类悬浮面板(20 个)后的水平/竖直对比。结果:
* 无面板:水平 6.29ms/帧,竖直 5.26ms/帧,但竖直**每帧都整块重画那个网格的图层**(40/40)。
* 20 个面板:水平 37.7ms/帧,竖直 **166.6ms/帧** —— 这就是用户说的"上下卡顿"。
结论:猜测部分成立(纵向确实没有预渲染),但主因是另一处:浮动面板像素图层的**全局键**里含
visual.yMin/yMax,平移的纵向分量每帧小改幅值 → 整层 20 个面板每帧全部重画。水平平移不改幅值,
所以只有竖直/斜向才触发 —— 正好解释"左右流畅、上下卡顿"的不对称。
二、图层改为二维预渲染(横向 ±半屏 / 纵向 ±半个网格高)
- LaneLayer 增加 marginY / anchorYMax / pxPerValue;位图高度 = ceil(可见网格高) + 2*marginY。
- 幅值范围按**像素行**精确扩展(上扩 marginY/pxPerValue,下扩 (bitmapHeight - marginY - laneHeight)
/pxPerValue):只有这样"扩展跨度 / 画布行数"才严格等于前台的 px/数值,可见网格在画布里的映射
才与整幅渲染逐像素同源(按固定数值量扩展会留下 <1px 的纵向缩放误差,水平残差会从 77k 涨到 105k)。
- 图层里的幅值刻度改以**前台可见网格**的跨度/高度取 NiceStep(m_wideTickSpan / m_wideTickHeight):
画布更高更宽,若按画布值取步长,网格线密度与前台不一致(贴图后拖动时网格线跳变)。
- 贴图:srcY = round(top - lane.Y) + marginY + round((anchorYMax - 当前 yMax) * pxPerValue);
超出画布上下端时只贴有像素的那一段;偏移超出余量才排队重画(重画即重新居中)。
- LaneLayerHash 只计幅值**跨度**、不计绝对范围(绝对范围是贴图偏移的来源)。
- 网格分界线移出图层(m_renderWide 时跳过,改由 PaintLaneDividers() 在宽基底路径补画一次):
它锚定在网格的屏幕位置,纵向偏移贴图会把它带着一起跑。
三、面板像素图层不再被幅值范围击穿
PanelGlobalKey() 去掉 visual.yMin/yMax(面板内容与幅值区间无关)。代价仅是 95.7% 不透明底背后
那 4.3% 透出的底层像素可能旧一帧(色差有界 ≤ 0.043*255 ≈ 11 级),与既有的"旧一帧"取舍一致。
四、深放大下拖拽光标跑飞
历史:388d3b6/67f659a 的"视图变化后夹光标"被 eaf5b9c 刻意删除(改为光标可滞留在视界外 + 贴边读数
+ 方向三角);但深放大下一屏平移就是几十倍可视跨度,光标瞬间被甩到视界外很远 = 用户看到的"跑飞"。
修法(区分连续手势与离散视图变化):
- 新增 ClampCursorsAfterPan(),在**平移**路径调用:PAN 拖拽(中键/左键)、Shift+滚轮、方向键。
- 离散视图变化(选区放大 / 适应视图 / 按网格时间轴展开)仍然不移动光标(先量好再自由探索,
贴边读数可读、可拖回来)——不要把夹取加回这几处。
- 抓取被钉在边缘的光标读数时不再沿用抓取偏移(CursorOffViewSide → m_cursorGrabOffset = 0),
否则首次移动会把目标送到视界外再被夹回边缘("一拖就跳很远 / 光标钉在边缘不动")。
五、验证(全部离屏,不占用鼠标/键盘/屏幕)
- uitest 432/432:[AH] 改为"幅值平移不再让图层失效(只改贴图偏移)、幅值跨度变化仍失效"并新增
"纯竖直拖拽 8 帧 0 次图层重画";新增 [AI]"贴边读数抓取后光标落在鼠标处(误差 0.0px)、
中键平移 1.5 屏 0 帧出界且跟手"。
- panperf(VERY_BIG_DATA、5 网格、1~2ms 一大格):竖直中位 3.8~5.0ms、40 帧仅 1 次图层重画
(修复前 40/40);水平 5.7~6.5ms(与修复前 6.29ms 持平);20 个面板时竖直 166.6 → 24.8ms/帧,
水平 37.7 → 38.8ms/帧(不对称消失)。整幅质量残差与修复前同量级(水平 77k~81k ↔ 77k~81k)。
- layercheck:clobbers=0/24、pan frames wrong=0/4、居中图层 vs 整幅重绘逐像素 0 差异。
- 新增探针 .preflight/cursorcheck(真文件 + 真缩放)30/30。
- make clean && make all 零警告。
This commit is contained in:
1 parent
0afa6360ae
commit
cb030573fa
3 files changed
+308
-35
No files matched your search
@@ -917,13 +917,25 @@ namespace jdi
|
||||
/**
|
||||
* @brief 将两条光标的时间夹取到当前可见时间范围内。
|
||||
*
|
||||
* ⚠️ 现在**只在拖拽光标时**使用。视图变化(平移/放大/自适应)必须**不**移动光标:
|
||||
* ⚠️ 拖拽光标时使用;**平移**视界后也要用(见 `ClampCursorsAfterPan`)。
|
||||
* 而**离散**的视图变化(选区放大 / 适应视图 / 按网格时间轴展开)必须**不**移动光标:
|
||||
* 光标允许滞留在网格可视区域之外,此时仅把它的取值/时间读数贴在网格边缘显示。
|
||||
*
|
||||
* @param [IN] which 要夹取的光标序号(1 或 2)。
|
||||
*/
|
||||
void ClampCursorToView(int which);
|
||||
|
||||
/**
|
||||
* @brief **平移视界**之后把时间光标夹回可见区间(单/双光标都处理)。
|
||||
*
|
||||
* ⚠️ 与「离散视图变化不移动光标」并不矛盾:选区放大 / 适应视图 / 按网格时间轴展开
|
||||
* 仍然不动光标(用户可以先量好再自由探索,光标读数贴在边缘可读、可拖回来);
|
||||
* 但**平移**是连续手势 —— 尤其在深放大区间,光标一旦被落在屏外就会立刻"跑飞"
|
||||
* 很远(一屏的平移就是几十甚至上百倍可视跨度),用户会以为光标丢失。
|
||||
* 因此在平移(中键/左键拖拽、Shift+滚轮、方向键)之后夹一次。
|
||||
*/
|
||||
void ClampCursorsAfterPan();
|
||||
|
||||
/**
|
||||
* @brief 计算双光标之间的时间差(绝对值)。
|
||||
* @return 时间差;非双光标模式返回 0。
|
||||
@@ -2274,9 +2286,18 @@ namespace jdi
|
||||
HBITMAP bitmap = nullptr; /**< 离屏位图。 */
|
||||
HGDIOBJ oldBitmap = nullptr; /**< DC 原选位图。 */
|
||||
int width = 0; /**< 位图宽度(含左右各半屏余量)。 */
|
||||
int height = 0; /**< 位图高度(= 网格高)。 */
|
||||
int height = 0; /**< 位图高度(含上下各半个网格高的余量)。 */
|
||||
int marginY = 0; /**< 位图在**可见网格上方**预留的像素行数(纵向预渲染余量)。 */
|
||||
double wideMin = 0.0; /**< 位图 x=0 对应的时间。 */
|
||||
double span = 0.0; /**< 渲染时的视界跨度(决定像素/时间)。 */
|
||||
/**
|
||||
* @brief 渲染时的**纵向锚点**:该网格的 `yMax`(可见窗口顶行对应的数值)。
|
||||
*
|
||||
* 与 `wideMin` 同理,平移(纵向是幅值平移)时只移动**贴图偏移**、不重画像素:
|
||||
* 需要贴到屏幕顶行的位图行 = `marginY + round((anchorYMax - 当前 yMax) * pxPerValue)`。
|
||||
*/
|
||||
double anchorYMax = 0.0;
|
||||
double pxPerValue = 0.0; /**< 位图纵向像素 / 数值单位(= 可见网格高 / 幅值跨度)。 */
|
||||
std::uint64_t key = 0; /**< 内容指纹(含跨度、网格几何与外观)。 */
|
||||
bool valid = false; /**< 是否有可显示的像素。 */
|
||||
};
|
||||
@@ -2300,8 +2321,27 @@ namespace jdi
|
||||
*/
|
||||
bool RenderLaneLayerRegion(int laneIndex, LaneLayer& layer, int stripX, int stripW,
|
||||
double wideMin);
|
||||
/**
|
||||
* @brief 前台补画各网格的**分界线**(网格顶部那条较重的横线)。
|
||||
*
|
||||
* ⚠️ 分界线锚定在网格的**屏幕位置**(`lane.rect.Y`),幅值平移时它**不跟着移动**,
|
||||
* 因此不能烘进"纵向可平移"的图层里(否则纵向拖动时它会跟着波形跑)。
|
||||
* 整幅重绘由 `PaintLane` 直接绘制;宽基底快路径(贴图层像素)必须在这里补画一次。
|
||||
*/
|
||||
void PaintLaneDividers(gd::Graphics& g) const;
|
||||
/** @brief 「以当前视界居中」的默认图层映射(位图 `x=0` 对应的时间)。 */
|
||||
double LayerDefaultWideMin() const;
|
||||
/** @brief 图层位图的横向余量(左右各半屏 → 可横向平移 ±半屏)。 */
|
||||
int LayerMarginX() const;
|
||||
/**
|
||||
* @brief 图层位图的纵向余量(上下各半个网格高 → 可纵向平移 ±半个网格高)。
|
||||
*
|
||||
* ⚠️ 纵向余量让「拖拽平移的纵向分量」(幅值平移)与横向平移一样**只改贴图偏移**,
|
||||
* 不必每帧重画整块图层 —— 这正是「左右平移流畅、上下平移卡顿」的根因所在。
|
||||
* 余量越大可连续拖拽的范围越大,但每个横向补条 / 整块重画的代价也按余量线性增长
|
||||
*(因此取「半个网格高」:一整屏的额外内容已足够,且不拖慢已经流畅的横向平移)。
|
||||
*/
|
||||
int LayerMarginY(float laneHeight) const;
|
||||
|
||||
/**
|
||||
* @brief 以当前视界为中心重建全部网格图层(同步;跨度变化 / 首帧 / 图层缺失时使用)。
|
||||
@@ -2371,6 +2411,17 @@ namespace jdi
|
||||
|
||||
bool m_renderWide = false; /**< 正在渲染信号图层(重入 `PaintLane` 时跳过装饰)。 */
|
||||
|
||||
/**
|
||||
* @brief 渲染信号图层时,横向网格线(幅值刻度)的「参考跨度 / 参考高度」。
|
||||
*
|
||||
* ⚠️ **必须是前台可见网格的值**,而不是图层画布的(含纵向余量的)值:
|
||||
* 图层的幅值刻度必须以该跨度为基准取「整齐步长」,否则画布里的网格线密度与
|
||||
* 整幅渲染不一致 —— 贴图后拖动时网格线会跳变。步长一致时刻度落在同一个
|
||||
* 绝对值格点上,于是纵向平移就是纯粹的像素平移(逐像素同源)。
|
||||
*/
|
||||
double m_wideTickSpan = 0.0;
|
||||
float m_wideTickHeight = 0.0f;
|
||||
|
||||
/**
|
||||
* @brief 本次 `PaintLane` 只绘制「装饰」(Y 轴刻度胶囊 + 图例 + 网格分界线)。
|
||||
*
|
||||
|
||||
Reference in new issue
Block a user