mirror of
https://github.com/webadderallorg/Recordly.git
synced 2026-11-09 23:43:35 +00:00
fix: improve cursor telemetry accuracy under CPU load
- Replace setInterval with drift-compensating recursive setTimeout for cursor sampling. Under CPU load setInterval bunches or skips callbacks, creating irregular gaps in telemetry data. - Add binary search + linear interpolation to export cursor lookup (modernFrameRenderer) matching the playback path. Was using O(n) nearest-neighbor which caused visible cursor jumping in exports. - Apply same interpolation fix to extension API getCursorAt().
This commit is contained in:
1 parent
c4ada3505c
commit
2fc74b200f
3 files changed
+94
-28
No files matched your search
@@ -2336,21 +2336,53 @@ export class FrameRenderer {
|
||||
return null;
|
||||
}
|
||||
|
||||
let closest = telemetry[0];
|
||||
let minDist = Math.abs(telemetry[0].timeMs - timeMs);
|
||||
for (let i = 1; i < telemetry.length; i++) {
|
||||
const dist = Math.abs(telemetry[i].timeMs - timeMs);
|
||||
if (dist < minDist) {
|
||||
minDist = dist;
|
||||
closest = telemetry[i];
|
||||
}
|
||||
if (telemetry[i].timeMs > timeMs) {
|
||||
break;
|
||||
// Clamp to first/last sample when out of range
|
||||
if (timeMs <= telemetry[0].timeMs) {
|
||||
const s = telemetry[0];
|
||||
return mapCursorToCanvasNormalized(
|
||||
{ cx: s.cx, cy: s.cy, interactionType: s.interactionType },
|
||||
{
|
||||
maskRect: this.layoutCache?.maskRect,
|
||||
canvasWidth: this.config.width,
|
||||
canvasHeight: this.config.height,
|
||||
},
|
||||
);
|
||||
}
|
||||
if (timeMs >= telemetry[telemetry.length - 1].timeMs) {
|
||||
const s = telemetry[telemetry.length - 1];
|
||||
return mapCursorToCanvasNormalized(
|
||||
{ cx: s.cx, cy: s.cy, interactionType: s.interactionType },
|
||||
{
|
||||
maskRect: this.layoutCache?.maskRect,
|
||||
canvasWidth: this.config.width,
|
||||
canvasHeight: this.config.height,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Binary search for surrounding samples
|
||||
let lo = 0;
|
||||
let hi = telemetry.length - 1;
|
||||
while (lo < hi - 1) {
|
||||
const mid = (lo + hi) >> 1;
|
||||
if (telemetry[mid].timeMs <= timeMs) {
|
||||
lo = mid;
|
||||
} else {
|
||||
hi = mid;
|
||||
}
|
||||
}
|
||||
|
||||
const a = telemetry[lo];
|
||||
const b = telemetry[hi];
|
||||
const span = b.timeMs - a.timeMs;
|
||||
|
||||
// Linear interpolation between samples
|
||||
const t = span > 0 ? (timeMs - a.timeMs) / span : 0;
|
||||
const cx = a.cx + (b.cx - a.cx) * t;
|
||||
const cy = a.cy + (b.cy - a.cy) * t;
|
||||
|
||||
return mapCursorToCanvasNormalized(
|
||||
{ cx: closest.cx, cy: closest.cy, interactionType: closest.interactionType },
|
||||
{ cx, cy, interactionType: a.interactionType },
|
||||
{
|
||||
maskRect: this.layoutCache?.maskRect,
|
||||
canvasWidth: this.config.width,
|
||||
|
||||
@@ -854,17 +854,31 @@ export class ExtensionHost {
|
||||
const t = host._cursorTelemetry;
|
||||
if (!t || t.length === 0) return null;
|
||||
|
||||
let closest = t[0];
|
||||
let minDist = Math.abs(t[0].timeMs - timeMs);
|
||||
for (let i = 1; i < t.length; i++) {
|
||||
const dist = Math.abs(t[i].timeMs - timeMs);
|
||||
if (dist < minDist) {
|
||||
minDist = dist;
|
||||
closest = t[i];
|
||||
if (timeMs <= t[0].timeMs) return { ...t[0] };
|
||||
if (timeMs >= t[t.length - 1].timeMs) return { ...t[t.length - 1] };
|
||||
|
||||
let lo = 0;
|
||||
let hi = t.length - 1;
|
||||
while (lo < hi - 1) {
|
||||
const mid = (lo + hi) >> 1;
|
||||
if (t[mid].timeMs <= timeMs) {
|
||||
lo = mid;
|
||||
} else {
|
||||
hi = mid;
|
||||
}
|
||||
if (t[i].timeMs > timeMs) break;
|
||||
}
|
||||
return { ...closest };
|
||||
|
||||
const a = t[lo];
|
||||
const b = t[hi];
|
||||
const span = b.timeMs - a.timeMs;
|
||||
const frac = span > 0 ? (timeMs - a.timeMs) / span : 0;
|
||||
|
||||
return {
|
||||
...a,
|
||||
cx: a.cx + (b.cx - a.cx) * frac,
|
||||
cy: a.cy + (b.cy - a.cy) * frac,
|
||||
timeMs,
|
||||
};
|
||||
},
|
||||
|
||||
getSmoothedCursor() {
|
||||
|
||||
Reference in new issue
Block a user