Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
135 commits
Select commit Hold shift + click to select a range
9a94a47
fix: add linux platform fallbacks for playback tooling deps
cursoragent Feb 14, 2026
4e9943c
feat: add playback benchmark runner and stream-first audio playback
cursoragent Feb 14, 2026
848069c
improve: reduce medium seek latency in ffmpeg decoder reset
cursoragent Feb 14, 2026
e6db78a
docs: record ffmpeg seek tuning benchmark improvements
cursoragent Feb 14, 2026
a877a29
improve: log audio callback startup latency for playback
cursoragent Feb 14, 2026
9ef6f68
docs: add audio startup telemetry guidance to playback findings
cursoragent Feb 14, 2026
7343a83
improve: prioritize near-target keyframes for long forward seeks
cursoragent Feb 14, 2026
ead3ec9
docs: capture second pass long-seek benchmark results
cursoragent Feb 14, 2026
3defb4c
chore: remove platform-gated ffmpeg import warning
cursoragent Feb 14, 2026
aefedf6
docs: record pass3 seek experiment and rollback decision
cursoragent Feb 14, 2026
3f07409
improve: align playback startup timing instrumentation
cursoragent Feb 14, 2026
aafc580
feat: add playback startup latency report tooling
cursoragent Feb 14, 2026
08a9b85
feat: export playback startup traces for cross-platform analysis
cursoragent Feb 14, 2026
4309091
test: cover startup report parsing for log and csv traces
cursoragent Feb 14, 2026
0fed777
improve: harden seek benchmark sampling and tail reporting
cursoragent Feb 14, 2026
a7ad707
docs: record rejected ffmpeg seek tuning experiments
cursoragent Feb 14, 2026
7ee45dd
improve: coalesce duplicate ffmpeg frame requests
cursoragent Feb 14, 2026
522ea4b
improve: stabilize duplicate burst decode benchmark metrics
cursoragent Feb 14, 2026
281ef2a
feat: add scrub burst latency benchmark
cursoragent Feb 14, 2026
f0d3883
improve: prioritize newest request in wide scrub bursts
cursoragent Feb 14, 2026
41fa6a7
improve: gate scrub supersession to high-resolution streams
cursoragent Feb 14, 2026
bdb0b60
improve: make scrub supersession thresholds runtime configurable
cursoragent Feb 14, 2026
c365f03
improve: add multi-run aggregation to scrub benchmark
cursoragent Feb 14, 2026
a6de84b
improve: add baseline-candidate startup latency delta reporting
cursoragent Feb 14, 2026
d193fde
improve: tune default scrub supersession span threshold
cursoragent Feb 14, 2026
d1e7488
docs: record supersession min-request threshold sweep
cursoragent Feb 14, 2026
f2f6446
docs: record rejected superseded cache-window experiment
cursoragent Feb 14, 2026
1a76ddc
improve: label startup trace rows with run id
cursoragent Feb 14, 2026
ce00244
improve: export scrub benchmark metrics to csv
cursoragent Feb 14, 2026
57d54bf
improve: add run-id filters to startup report
cursoragent Feb 14, 2026
25b42f5
improve: retune scrub supersession span default
cursoragent Feb 14, 2026
dc26a51
improve: harden startup report run-id workflows
cursoragent Feb 14, 2026
1e4fbe6
improve: retune scrub supersession min-pixels default
cursoragent Feb 14, 2026
3b089bb
improve: add run labels to scrub csv exports
cursoragent Feb 14, 2026
e85d965
improve: retune scrub supersession min-requests default
cursoragent Feb 14, 2026
2daa30e
docs: record rejected span sweep after default retunes
cursoragent Feb 14, 2026
20c02e9
improve: add csv export to startup report
cursoragent Feb 14, 2026
e39da9b
feat: add scrub csv report analysis utility
cursoragent Feb 14, 2026
9609219
improve: group scrub csv report by label and video
cursoragent Feb 14, 2026
b29b83b
improve: derive config labels for unlabeled scrub csv rows
cursoragent Feb 14, 2026
ad8b596
docs: record rejected fine span retune to 22
cursoragent Feb 14, 2026
7568ff2
improve: add per-run startup metric listing
cursoragent Feb 14, 2026
a531b4d
improve: add output csv mode to scrub csv report
cursoragent Feb 14, 2026
192b13b
improve: export startup run-list modes to csv
cursoragent Feb 14, 2026
8ea050b
improve: add csv export to playback benchmark
cursoragent Feb 14, 2026
4154b4b
feat: add playback csv report analysis utility
cursoragent Feb 14, 2026
6463deb
improve: simplify desktop playback websocket to rgba-only
cursoragent Feb 14, 2026
e9c8c8d
improve: drop legacy nv12 paths from frame worker
cursoragent Feb 14, 2026
d00e95b
improve: remove unused nv12 webgpu pipeline setup
cursoragent Feb 14, 2026
da10a40
improve: avoid per-frame socket buffer copies
cursoragent Feb 14, 2026
f7248ea
improve: adapt shared frame buffer size for large rgba frames
cursoragent Feb 14, 2026
62f8ac5
improve: add sab fallback telemetry for playback frames
cursoragent Feb 14, 2026
f32ece3
improve: retry sab writes before worker fallback
cursoragent Feb 14, 2026
0c13a37
docs: record rejected adaptive ffmpeg seek-window experiment
cursoragent Feb 14, 2026
1c9caf3
improve: probe shared frame buffer slots before fallback
cursoragent Feb 14, 2026
f87338d
improve: probe ready shared-buffer slots on consumer reads
cursoragent Feb 14, 2026
d634ac5
test: cover shared frame buffer sparse-slot behavior
cursoragent Feb 14, 2026
f376303
improve: cap sab retry loops before worker fallback
cursoragent Feb 14, 2026
d232a5d
test: add sparse-slot readInto coverage for shared buffer
cursoragent Feb 14, 2026
f3b45e4
improve: reuse ready slots when shared frame ring is full
cursoragent Feb 14, 2026
32a7e1f
improve: avoid duplicate scheduled sab retries
cursoragent Feb 14, 2026
b925e0c
improve: expose sab diagnostics through fps stats
cursoragent Feb 14, 2026
0ec5e7d
test: ensure shared buffer never overwrites reading slots
cursoragent Feb 14, 2026
f7ed0cd
improve: extract and test shared-frame transport sizing policy
cursoragent Feb 14, 2026
6f64ae9
refactor: extract sab retry decision policy
cursoragent Feb 14, 2026
f2aa57a
improve: replace oldest ready frame when sab ring is saturated
cursoragent Feb 14, 2026
daae3a9
improve: make shared-buffer oldest-frame reclaim wrap-safe
cursoragent Feb 14, 2026
7ed9096
refactor: extract wrap-safe frame age helper for sab reclaim
cursoragent Feb 14, 2026
bd0c724
improve: signal shared-buffer consumers via write-index changes
cursoragent Feb 14, 2026
5ce1200
improve: show sab transport diagnostics in performance overlay
cursoragent Feb 14, 2026
62d9321
improve: keep cumulative sab fallback diagnostics across windows
cursoragent Feb 14, 2026
8d836e4
improve: add sab slot count and total memory diagnostics
cursoragent Feb 14, 2026
6fc6162
improve: add lifetime SAB transport diagnostics
cursoragent Feb 14, 2026
8d9be08
improve: keep SAB dispatch non-blocking on successful writes
cursoragent Feb 14, 2026
1247721
improve: track cumulative SAB retries in transport diagnostics
cursoragent Feb 14, 2026
94c40d8
improve: skip redundant metadata parsing on worker transport path
cursoragent Feb 14, 2026
a0d8bdc
improve: expose cumulative worker fallback transfer volume
cursoragent Feb 14, 2026
6e59e40
improve: add transport split percentages to diagnostics
cursoragent Feb 14, 2026
0f20bdc
improve: avoid shared-buffer polling copies in frame worker
cursoragent Feb 14, 2026
7f3e597
improve: suppress shared-buffer queue notifications in worker
cursoragent Feb 14, 2026
ea6b968
improve: drop redundant queued-frame callbacks on main thread
cursoragent Feb 14, 2026
d5d527c
improve: drain shared canvas frames to latest sample
cursoragent Feb 14, 2026
c292b71
improve: coalesce shared-buffer bursts in pending worker mode
cursoragent Feb 14, 2026
a0b615a
improve: bypass worker queue acks on fallback dispatch
cursoragent Feb 14, 2026
b65d65a
refactor: unify fallback frame queueing in worker
cursoragent Feb 14, 2026
1d53a3a
improve: compact worker frame queue to latest sample
cursoragent Feb 14, 2026
7da4aac
improve: simplify worker render loop for single-frame queue
cursoragent Feb 14, 2026
90da105
refactor: remove redundant frame-queued worker message path
cursoragent Feb 14, 2026
09fb09c
improve: remove redundant raw frame cache copies in worker
cursoragent Feb 14, 2026
09aa4d8
improve: overlap readback submission with prior map wait
cursoragent Feb 14, 2026
6ace015
refactor: remove stale decoded worker message branches
cursoragent Feb 14, 2026
ce052d5
refactor: use single-slot queued frame state in worker
cursoragent Feb 14, 2026
0df0487
improve: schedule socket frame dispatch via guarded microtasks
cursoragent Feb 14, 2026
fc6c9f4
improve: cap fallback worker frames in flight
cursoragent Feb 14, 2026
bfbe3d6
improve: avoid reschedule spin when worker in-flight cap hits
cursoragent Feb 14, 2026
1fff0b4
improve: track worker in-flight cap hit telemetry
cursoragent Feb 14, 2026
21792cd
improve: add windowed worker cap-hit transport diagnostics
cursoragent Feb 14, 2026
803b9a3
improve: add worker in-flight peak transport diagnostics
cursoragent Feb 14, 2026
96ddac3
improve: track in-flight cap superseded drop diagnostics
cursoragent Feb 14, 2026
348ef8e
improve: make fallback inflight accounting source-aware
cursoragent Feb 14, 2026
8f24a01
refactor: centralize worker inflight dispatch decisions
cursoragent Feb 14, 2026
ee8b8bf
improve: track render source mix in transport diagnostics
cursoragent Feb 14, 2026
10714a0
improve: drop stale out-of-order frames in worker
cursoragent Feb 14, 2026
4ecaeed
improve: add decode benchmark csv export workflow
cursoragent Feb 14, 2026
1147651
improve: add decode csv summary and delta reporting
cursoragent Feb 14, 2026
3d0aa23
improve: gate stale out-of-order frames on direct render path
cursoragent Feb 14, 2026
3388f05
improve: drop stale out-of-order frames before queueing
cursoragent Feb 14, 2026
fcefca2
refactor: unify transport frame-order decisions
cursoragent Feb 14, 2026
4683066
docs: refresh playback findings key file references
cursoragent Feb 14, 2026
a422a1d
improve: guard stride-correction responses against stale order
cursoragent Feb 14, 2026
dc730fb
test: extend transport frame-order edge-case coverage
cursoragent Feb 14, 2026
27d2270
improve: split direct stale-drop telemetry by source
cursoragent Feb 14, 2026
4f3ce05
improve: prefer streaming audio startup with fallback
cursoragent Feb 14, 2026
3fc6d26
improve: classify audio startup path in startup reports
cursoragent Feb 14, 2026
15872a0
improve: tighten direct stride worker lifecycle
cursoragent Feb 14, 2026
f5303fc
improve: export audio startup path rows in startup csv
cursoragent Feb 14, 2026
9e3d1f4
docs: add prerender override startup capture workflow
cursoragent Feb 14, 2026
1916631
improve: add structured audio path columns to startup csv
cursoragent Feb 14, 2026
8e0a58c
improve: ingest startup path-selection events in reports
cursoragent Feb 14, 2026
9a972e1
improve: report startup path-selection timing metrics
cursoragent Feb 14, 2026
dc1dc1c
improve: add streaming-only audio startup override
cursoragent Feb 14, 2026
8376f07
test: cover startup path selection fallback parsing
cursoragent Feb 14, 2026
19da1b2
improve: fix direct-path drop and render telemetry accounting
cursoragent Feb 14, 2026
a721245
refactor: remove unused socket render counter bookkeeping
cursoragent Feb 14, 2026
1a04309
improve: coalesce stride correction worker backlog
cursoragent Feb 14, 2026
d477871
improve: add stride correction backlog diagnostics
cursoragent Feb 14, 2026
c33dfab
refactor: centralize stride dispatch queue decisions
cursoragent Feb 14, 2026
a4e93ae
improve: surface stride correction errors in transport diagnostics
cursoragent Feb 14, 2026
416eaf6
improve: add effective audio startup callback metric
cursoragent Feb 14, 2026
9b15b2e
improve: add scrub seek-distance latency bucket metrics
cursoragent Feb 14, 2026
99b0577
improve: prioritize latest frame in wide scrub bursts
cursoragent Feb 14, 2026
afac933
improve: add scrub latest-first runtime override
cursoragent Feb 14, 2026
243c265
improve: add latest-first scrub threshold controls
cursoragent Feb 14, 2026
4c43e66
improve: add latest-first min-pixels scrub tuning
cursoragent Feb 14, 2026
f792f1c
improve: correct scrub even-run median aggregation
cursoragent Feb 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
492 changes: 492 additions & 0 deletions apps/desktop/src/routes/editor/PerformanceOverlay.tsx

Large diffs are not rendered by default.

35 changes: 35 additions & 0 deletions apps/desktop/src/utils/frame-order.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { describe, expect, it } from "vitest";
import {
frameNumberForwardDelta,
isFrameNumberNewer,
shouldDropOutOfOrderFrame,
} from "./frame-order";

describe("frame-order utilities", () => {
it("treats positive forward deltas as newer", () => {
expect(frameNumberForwardDelta(41, 40)).toBe(1);
expect(isFrameNumberNewer(41, 40)).toBe(true);
});

it("treats wraparound forward deltas as newer", () => {
expect(frameNumberForwardDelta(2, 0xffffffff)).toBe(3);
expect(isFrameNumberNewer(2, 0xffffffff)).toBe(true);
});

it("drops duplicate frame numbers", () => {
expect(shouldDropOutOfOrderFrame(120, 120)).toBe(true);
});

it("drops slightly older out-of-order frames inside stale window", () => {
expect(shouldDropOutOfOrderFrame(119, 120, 30)).toBe(true);
expect(shouldDropOutOfOrderFrame(90, 120, 30)).toBe(true);
});

it("keeps older frames beyond stale window as seek candidates", () => {
expect(shouldDropOutOfOrderFrame(89, 120, 30)).toBe(false);
});

it("keeps forward frames", () => {
expect(shouldDropOutOfOrderFrame(121, 120, 30)).toBe(false);
});
});
27 changes: 27 additions & 0 deletions apps/desktop/src/utils/frame-order.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
export const FRAME_ORDER_STALE_WINDOW = 30;

export function frameNumberForwardDelta(
candidate: number,
reference: number,
): number {
return (candidate - reference) >>> 0;
}

export function isFrameNumberNewer(
candidate: number,
reference: number,
): boolean {
const delta = frameNumberForwardDelta(candidate, reference);
return delta !== 0 && delta < 0x80000000;
}

export function shouldDropOutOfOrderFrame(
candidate: number,
reference: number,
staleWindow: number = FRAME_ORDER_STALE_WINDOW,
): boolean {
if (candidate === reference) return true;
if (isFrameNumberNewer(candidate, reference)) return false;
const backwardDelta = frameNumberForwardDelta(reference, candidate);
return backwardDelta <= staleWindow;
}
30 changes: 30 additions & 0 deletions apps/desktop/src/utils/frame-transport-config.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { describe, expect, it } from "vitest";
import {
DEFAULT_FRAME_BUFFER_CONFIG,
FRAME_BUFFER_MAX_SLOT_SIZE,
FRAME_BUFFER_MAX_TOTAL_BYTES,
computeSharedBufferConfig,
} from "./frame-transport-config";

describe("frame-transport-config", () => {
it("keeps default config for small frames", () => {
const config = computeSharedBufferConfig(4 * 1024 * 1024);
expect(config.slotSize).toBe(DEFAULT_FRAME_BUFFER_CONFIG.slotSize);
expect(config.slotCount).toBe(DEFAULT_FRAME_BUFFER_CONFIG.slotCount);
});

it("increases slot size with aligned headroom", () => {
const config = computeSharedBufferConfig(22 * 1024 * 1024);
expect(config.slotSize).toBe(28 * 1024 * 1024);
expect(config.slotCount).toBe(4);
});

it("caps slot size and total memory budget", () => {
const config = computeSharedBufferConfig(80 * 1024 * 1024);
expect(config.slotSize).toBe(FRAME_BUFFER_MAX_SLOT_SIZE);
expect(config.slotCount).toBe(2);
expect(config.slotSize * config.slotCount).toBeLessThanOrEqual(
FRAME_BUFFER_MAX_TOTAL_BYTES,
);
});
});
37 changes: 37 additions & 0 deletions apps/desktop/src/utils/frame-transport-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type { SharedFrameBufferConfig } from "./shared-frame-buffer";

export const DEFAULT_FRAME_BUFFER_CONFIG: SharedFrameBufferConfig = {
slotCount: 6,
slotSize: 16 * 1024 * 1024,
};

export const FRAME_BUFFER_RESIZE_ALIGNMENT = 2 * 1024 * 1024;
export const FRAME_BUFFER_MAX_SLOT_SIZE = 64 * 1024 * 1024;
export const FRAME_BUFFER_MAX_TOTAL_BYTES = 128 * 1024 * 1024;
export const FRAME_BUFFER_MIN_SLOT_COUNT = 2;

export function alignUp(value: number, alignment: number): number {
if (alignment <= 0) return value;
return Math.ceil(value / alignment) * alignment;
}

export function computeSharedBufferConfig(
requiredBytes: number,
baseConfig: SharedFrameBufferConfig = DEFAULT_FRAME_BUFFER_CONFIG,
): SharedFrameBufferConfig {
const safeRequired = Math.max(requiredBytes, 0);
const withHeadroom = Math.ceil(safeRequired * 1.25);
const alignedBytes = alignUp(withHeadroom, FRAME_BUFFER_RESIZE_ALIGNMENT);
const slotSize = Math.max(
baseConfig.slotSize,
Math.min(FRAME_BUFFER_MAX_SLOT_SIZE, alignedBytes),
);

const maxSlotsByBudget = Math.max(
FRAME_BUFFER_MIN_SLOT_COUNT,
Math.floor(FRAME_BUFFER_MAX_TOTAL_BYTES / slotSize),
);
const slotCount = Math.min(baseConfig.slotCount, maxSlotsByBudget);

return { slotCount, slotSize };
}
41 changes: 41 additions & 0 deletions apps/desktop/src/utils/frame-transport-inflight.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { describe, expect, it } from "vitest";
import {
decideWorkerInflightDispatch,
updateWorkerInflightPeaks,
} from "./frame-transport-inflight";

describe("frame-transport-inflight", () => {
it("dispatches when worker inflight is below limit", () => {
expect(decideWorkerInflightDispatch(1, 2, false)).toEqual({
action: "dispatch",
nextWorkerFramesInFlight: 2,
backpressureHitsIncrement: 0,
supersededDropsIncrement: 0,
});
});

it("returns backpressure without superseded increment when queue empty", () => {
expect(decideWorkerInflightDispatch(2, 2, false)).toEqual({
action: "backpressure",
nextWorkerFramesInFlight: 2,
backpressureHitsIncrement: 1,
supersededDropsIncrement: 0,
});
});

it("returns backpressure with superseded increment when queue occupied", () => {
expect(decideWorkerInflightDispatch(4, 2, true)).toEqual({
action: "backpressure",
nextWorkerFramesInFlight: 4,
backpressureHitsIncrement: 1,
supersededDropsIncrement: 1,
});
});

it("updates worker inflight peaks", () => {
expect(updateWorkerInflightPeaks(3, 2, 5)).toEqual({
peakWindow: 3,
peakTotal: 5,
});
});
});
39 changes: 39 additions & 0 deletions apps/desktop/src/utils/frame-transport-inflight.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
export type WorkerInflightDispatchDecision = {
action: "dispatch" | "backpressure";
nextWorkerFramesInFlight: number;
backpressureHitsIncrement: number;
supersededDropsIncrement: number;
};

export function decideWorkerInflightDispatch(
workerFramesInFlight: number,
limit: number,
hasQueuedNextFrame: boolean,
): WorkerInflightDispatchDecision {
if (workerFramesInFlight >= limit) {
return {
action: "backpressure",
nextWorkerFramesInFlight: workerFramesInFlight,
backpressureHitsIncrement: 1,
supersededDropsIncrement: hasQueuedNextFrame ? 1 : 0,
};
}

return {
action: "dispatch",
nextWorkerFramesInFlight: workerFramesInFlight + 1,
backpressureHitsIncrement: 0,
supersededDropsIncrement: 0,
};
}

export function updateWorkerInflightPeaks(
workerFramesInFlight: number,
peakWindow: number,
peakTotal: number,
) {
return {
peakWindow: Math.max(peakWindow, workerFramesInFlight),
peakTotal: Math.max(peakTotal, workerFramesInFlight),
};
}
67 changes: 67 additions & 0 deletions apps/desktop/src/utils/frame-transport-order.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { describe, expect, it } from "vitest";
import { decideFrameOrder } from "./frame-transport-order";

describe("decideFrameOrder", () => {
it("accepts frame when candidate is missing", () => {
const decision = decideFrameOrder(null, 120, 30);
expect(decision).toEqual({
action: "accept",
nextLatestFrameNumber: 120,
dropsIncrement: 0,
});
});

it("accepts first frame and seeds latest", () => {
const decision = decideFrameOrder(120, null, 30);
expect(decision).toEqual({
action: "accept",
nextLatestFrameNumber: 120,
dropsIncrement: 0,
});
});

it("drops short backward stale frames", () => {
const decision = decideFrameOrder(119, 120, 30);
expect(decision).toEqual({
action: "drop",
nextLatestFrameNumber: 120,
dropsIncrement: 1,
});
});

it("drops duplicate frame numbers", () => {
const decision = decideFrameOrder(120, 120, 30);
expect(decision).toEqual({
action: "drop",
nextLatestFrameNumber: 120,
dropsIncrement: 1,
});
});

it("accepts large backward jumps for seeks", () => {
const decision = decideFrameOrder(80, 120, 30);
expect(decision).toEqual({
action: "accept",
nextLatestFrameNumber: 80,
dropsIncrement: 0,
});
});

it("accepts forward progression", () => {
const decision = decideFrameOrder(121, 120, 30);
expect(decision).toEqual({
action: "accept",
nextLatestFrameNumber: 121,
dropsIncrement: 0,
});
});

it("accepts wraparound forward progression", () => {
const decision = decideFrameOrder(2, 0xffffffff, 30);
expect(decision).toEqual({
action: "accept",
nextLatestFrameNumber: 2,
dropsIncrement: 0,
});
});
});
49 changes: 49 additions & 0 deletions apps/desktop/src/utils/frame-transport-order.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { shouldDropOutOfOrderFrame } from "./frame-order";

export type FrameOrderDecision = {
action: "accept" | "drop";
nextLatestFrameNumber: number | null;
dropsIncrement: number;
};

export function decideFrameOrder(
candidateFrameNumber: number | null,
latestFrameNumber: number | null,
staleWindow: number,
): FrameOrderDecision {
if (candidateFrameNumber === null) {
return {
action: "accept",
nextLatestFrameNumber: latestFrameNumber,
dropsIncrement: 0,
};
}

if (latestFrameNumber === null) {
return {
action: "accept",
nextLatestFrameNumber: candidateFrameNumber,
dropsIncrement: 0,
};
}

if (
shouldDropOutOfOrderFrame(
candidateFrameNumber,
latestFrameNumber,
staleWindow,
)
) {
return {
action: "drop",
nextLatestFrameNumber: latestFrameNumber,
dropsIncrement: 1,
};
}

return {
action: "accept",
nextLatestFrameNumber: candidateFrameNumber,
dropsIncrement: 0,
};
}
28 changes: 28 additions & 0 deletions apps/desktop/src/utils/frame-transport-retry.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { describe, expect, it } from "vitest";
import { decideSabWriteFailure } from "./frame-transport-retry";

describe("frame-transport-retry", () => {
it("falls back immediately for oversized frames", () => {
const decision = decideSabWriteFailure(true, 0, 2);
expect(decision).toEqual({
action: "fallback_oversize",
nextRetryCount: 0,
});
});

it("retries while below retry limit", () => {
const decision = decideSabWriteFailure(false, 1, 2);
expect(decision).toEqual({
action: "retry",
nextRetryCount: 2,
});
});

it("falls back when retry limit is reached", () => {
const decision = decideSabWriteFailure(false, 2, 2);
expect(decision).toEqual({
action: "fallback_retry_limit",
nextRetryCount: 0,
});
});
});
20 changes: 20 additions & 0 deletions apps/desktop/src/utils/frame-transport-retry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export type SabWriteFailureDecision =
| { action: "retry"; nextRetryCount: number }
| { action: "fallback_oversize"; nextRetryCount: number }
| { action: "fallback_retry_limit"; nextRetryCount: number };

export function decideSabWriteFailure(
isOversized: boolean,
currentRetryCount: number,
retryLimit: number,
): SabWriteFailureDecision {
if (isOversized) {
return { action: "fallback_oversize", nextRetryCount: 0 };
}

if (currentRetryCount >= retryLimit) {
return { action: "fallback_retry_limit", nextRetryCount: 0 };
}

return { action: "retry", nextRetryCount: currentRetryCount + 1 };
}
Loading
Loading