fix(desktop): apply tiles in arrival order so drags never jump back #61

Merged
Claude-Run merged 1 commit from fix/VAPP-48-ordered-tile-blit into main 2026-06-29 15:20:42 +02:00
Member

Inbound cmd 3 tiles decoded via createImageBitmap and each was drawn the moment its own async decode resolved, so tiles painted in decode-completion order rather than arrival (capture) order. Under load a later frame's tile could decode before an earlier frame's overlapping tile, painting old pixels over new ones, so a dragged window visibly jumped back.

Introduce a per-session ordered tile-application queue. Each tile gets a monotonic arrival sequence; decodes still run concurrently (decode latency unchanged), but drawImage is applied strictly in arrival order: a decoded tile waits in a reorder buffer until every earlier-arriving tile has been drawn. Arrival order over the relay is capture order, so the canvas only ever advances forward and an earlier frame can never overwrite a later one. A tile only waits on tiles that arrived before it, so added latency is bounded by the in-flight decodes (sub-frame). A failed decode still advances the cursor so one bad tile cannot stall the queue.

The ordering core is factored into a generic ReorderBuffer at module scope (off-wasm) and covered by host unit tests for out-of-order completion, in-order completion, and the failed-tile-does-not-stall case.

Agent-side per-frame sequencing / coalescing (proposed step 3) is deliberately left out: the issue specifies the browser-side ordering fix lands and is measured against a drag before deciding whether agent-side sequencing is needed, so it is not required for this change.

#VAPP-48

Inbound cmd 3 tiles decoded via createImageBitmap and each was drawn the moment its own async decode resolved, so tiles painted in decode-completion order rather than arrival (capture) order. Under load a later frame's tile could decode before an earlier frame's overlapping tile, painting old pixels over new ones, so a dragged window visibly jumped back. Introduce a per-session ordered tile-application queue. Each tile gets a monotonic arrival sequence; decodes still run concurrently (decode latency unchanged), but drawImage is applied strictly in arrival order: a decoded tile waits in a reorder buffer until every earlier-arriving tile has been drawn. Arrival order over the relay is capture order, so the canvas only ever advances forward and an earlier frame can never overwrite a later one. A tile only waits on tiles that arrived before it, so added latency is bounded by the in-flight decodes (sub-frame). A failed decode still advances the cursor so one bad tile cannot stall the queue. The ordering core is factored into a generic ReorderBuffer<T> at module scope (off-wasm) and covered by host unit tests for out-of-order completion, in-order completion, and the failed-tile-does-not-stall case. Agent-side per-frame sequencing / coalescing (proposed step 3) is deliberately left out: the issue specifies the browser-side ordering fix lands and is measured against a drag before deciding whether agent-side sequencing is needed, so it is not required for this change. #VAPP-48
fix(desktop): apply tiles in arrival order so drags never jump back
All checks were successful
Check / fmt + clippy + build + tests (pull_request) Successful in 59s
Create release / Create release from merged PR (pull_request) Has been skipped
7de3227b64
Inbound cmd 3 tiles decoded via createImageBitmap and each was drawn the moment its own async decode resolved, so tiles painted in decode-completion order rather than arrival (capture) order. Under load a later frame's tile could decode before an earlier frame's overlapping tile, painting old pixels over new ones, so a dragged window visibly jumped back.

Introduce a per-session ordered tile-application queue. Each tile gets a monotonic arrival sequence; decodes still run concurrently (decode latency unchanged), but drawImage is applied strictly in arrival order: a decoded tile waits in a reorder buffer until every earlier-arriving tile has been drawn. Arrival order over the relay is capture order, so the canvas only ever advances forward and an earlier frame can never overwrite a later one. A tile only waits on tiles that arrived before it, so added latency is bounded by the in-flight decodes (sub-frame). A failed decode still advances the cursor so one bad tile cannot stall the queue.

The ordering core is factored into a generic ReorderBuffer<T> at module scope (off-wasm) and covered by host unit tests for out-of-order completion, in-order completion, and the failed-tile-does-not-stall case.

Agent-side per-frame sequencing / coalescing (proposed step 3) is deliberately left out: the issue specifies the browser-side ordering fix lands and is measured against a drag before deciding whether agent-side sequencing is needed, so it is not required for this change.

#VAPP-48
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/vervain-apps!61
No description provided.