You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
VideoFrame.copyTo is manipulating JS objects in a background queue #955
As per https://w3c.github.io/webcodecs/#dom-videoframe-copyto, destination which is a JS visible object is written from a background queue task, which is an anti-pattern.
This creates problems (JS observability, destination mutating during the copyTo tasks).
There are various ways to fix this, for instance:
make copyTo algorithm synchronous
Expose a new asynchronous API that would transfer the destination buffer and provide the returned value as part of the promise value.
Extend copyTo API surface to implement 2 within it.
Change the spec to write destination asynchronously but in an event loop task with additional checks, even if UA may do it in a smarter way.
As a side note, copyTo algorithm is also synchronously converting to RGB.
It would be convenient to do this asynchronously, which is feasible by cloning the VideoFrame synchronously and using the cloned VideoFrame in the remainder of the algorithm.
Are any of those actually problems? On the Chrome side we did change to using atomic writes for the copy, so we're not exposing undefined behavior. All of the solutions proposed would hurt WASM interop.
From js observability point of view, It's no different from another worker writing to the same shared buffer. copyTo to non-shared buffers shouldn't have this effect (i.e. it should be synchronous).
A naive implementation of the spec would lead to obvious memory issues.
Also, the current spec could quickly lead to interop issues (web page relying on copyTo being sync and UAs having different implementations).
All of the solutions proposed would hurt WASM interop.
How about a Promise-based prefetch() method that would ensure that copy is guaranteed to be synchronous?
copyTo to non-shared buffers shouldn't have this effect (i.e. it should be synchronous).
Then, the spec should describe this algorithm.
FWIW, it is surprising to me that a web page gets widely different (and JS observable) behaviour depending on the input buffer flavour.
A naive implementation of the spec would lead to obvious memory issues.
Can you elaborate on the obvious issues? Do you just mean a client who reads w/o waiting for the promise may get partial/corrupted data?
How about a Promise-based prefetch() method that would ensure that copy is guaranteed to be synchronous?
This would result in two copies IIUC?
Then, the spec should describe this algorithm.
+1, I didn't realize the spec text didn't already have this clarification. It should be clarified to MUST be synchronous for non-shared type buffers and MAY be asynchronous for SAB.
For instance, UA is writing on a buffer in a background queue while web page is resizing it/transferring it on another.
How about a Promise-based prefetch() method that would ensure that copy is guaranteed to be synchronous?
This would result in two copies IIUC?
At least in WebKit, I am not sure how we can avoid two copies for GPU-backed video frames.
As part of the copyTo current approach, we do something like:
Copy from GPU backed video frames to memory that is shareable with the content process
Copy from shareable memory to the JS memory provided by the web page. prefetch would trigger the first copy, copyTo the second.
Can Chromium avoid these two copies?
Then, the spec should describe this algorithm.
+1, I didn't realize the spec text didn't already have this clarification. It should be clarified to MUST be synchronous for non-shared type buffers and MAY be asynchronous for SAB.
If we do this, I would make the algorithm first check whether the buffer is SAB or not.
Background queue would only be used for SAB.
I am also not exactly sure how we are supposed to specify the SAB algorithm.
I would tend to synchronously do a structure clone of the SAB and use the clone in the background queue.
But maybe we can hook to some SAB internals instead.
For instance, UA is writing on a buffer in a background queue while web page is resizing it/transferring it on another.
Ah you mean for the non-SAB cases? Yes, those do have obvious issues. SAB can't be shrunk and can't be transferred.
At least in WebKit, I am not sure how we can avoid two copies for GPU-backed video frames. As part of the copyTo current approach, we do something like:
Copy from GPU backed video frames to memory that is shareable with the content process
Copy from shareable memory to the JS memory provided by the web page. prefetch would trigger the first copy, copyTo the second.
Can Chromium avoid these two copies?
Does WebKit not pass the IOSurface to the content process? We don't always in Chromium for legacy, non-security reasons. In Chromium some frames are mappable so have 1-copy perf and some require read-back which has 2-copy perf as you note.
If we do this, I would make the algorithm first check whether the buffer is SAB or not. Background queue would only be used for SAB.
I am also not exactly sure how we are supposed to specify the SAB algorithm. I would tend to synchronously do a structure clone of the SAB and use the clone in the background queue. But maybe we can hook to some SAB internals instead.
This seems reasonable; I haven't looked at how other specs handle SAB copies though. In Chromium we just ref the underlying buffer.
In Chromium some frames are mappable so have 1-copy perf and some require read-back which has 2-copy perf as you note.
I see. AIUI, prefetch would not trigger any additional cost to either strategies.
And it would allow applications to call copyTo with a consistent behaviour (no blocking IPC call for some frames, no SAB async behaviour).
Can you explain prefetch more precisely? I feel like I'm missing something:
Anything that comes back to the main thread adds the cost of doing the memcpy on the main thread even if the readback is async.
Assuming some prefetch promise, why not just handle that internally? I.e., there's no reason the internal API has to have a blocking IPC. It can just post a main thread task to finish the copy.
SAB behavior is always "async" no matter what we do since it's world read/write.
In any case, copyTo is already shipped by Firefox and Chrome, so at most we want to clarify the spec or make behind the scenes adjustments for better interop. Without a strong reason, adding new methods, attributes, etc seems out of scope.
@Djuffin can you take a stab at a PR which clarifies the algorithms here?
Activity
dalecurtis commentedon Oct 1, 2026
Are any of those actually problems? On the Chrome side we did change to using atomic writes for the copy, so we're not exposing undefined behavior. All of the solutions proposed would hurt WASM interop.
Djuffin commentedon Oct 1, 2026
From js observability point of view, It's no different from another worker writing to the same shared buffer. copyTo to non-shared buffers shouldn't have this effect (i.e. it should be synchronous).
youennf commentedon Oct 2, 2026
A naive implementation of the spec would lead to obvious memory issues.
Also, the current spec could quickly lead to interop issues (web page relying on copyTo being sync and UAs having different implementations).
How about a Promise-based
prefetch()method that would ensure that copy is guaranteed to be synchronous?Then, the spec should describe this algorithm.
FWIW, it is surprising to me that a web page gets widely different (and JS observable) behaviour depending on the input buffer flavour.
dalecurtis commentedon Oct 5, 2026
Can you elaborate on the obvious issues? Do you just mean a client who reads w/o waiting for the promise may get partial/corrupted data?
This would result in two copies IIUC?
+1, I didn't realize the spec text didn't already have this clarification. It should be clarified to MUST be synchronous for non-shared type buffers and MAY be asynchronous for SAB.
youennf commentedon Oct 6, 2026
For instance, UA is writing on a buffer in a background queue while web page is resizing it/transferring it on another.
At least in WebKit, I am not sure how we can avoid two copies for GPU-backed video frames.
As part of the copyTo current approach, we do something like:
prefetchwould trigger the first copy,copyTothe second.Can Chromium avoid these two copies?
If we do this, I would make the algorithm first check whether the buffer is SAB or not.
Background queue would only be used for SAB.
I am also not exactly sure how we are supposed to specify the SAB algorithm.
I would tend to synchronously do a structure clone of the SAB and use the clone in the background queue.
But maybe we can hook to some SAB internals instead.
dalecurtis commentedon Oct 6, 2026
Ah you mean for the non-SAB cases? Yes, those do have obvious issues. SAB can't be shrunk and can't be transferred.
Does WebKit not pass the IOSurface to the content process? We don't always in Chromium for legacy, non-security reasons. In Chromium some frames are mappable so have 1-copy perf and some require read-back which has 2-copy perf as you note.
This seems reasonable; I haven't looked at how other specs handle SAB copies though. In Chromium we just ref the underlying buffer.
youennf commentedon Oct 6, 2026
I see. AIUI,
prefetchwould not trigger any additional cost to either strategies.And it would allow applications to call copyTo with a consistent behaviour (no blocking IPC call for some frames, no SAB async behaviour).
dalecurtis commentedon Oct 6, 2026
Can you explain
prefetchmore precisely? I feel like I'm missing something:memcpyon the main thread even if the readback is async.In any case,
copyTois already shipped by Firefox and Chrome, so at most we want to clarify the spec or make behind the scenes adjustments for better interop. Without a strong reason, adding new methods, attributes, etc seems out of scope.@Djuffin can you take a stab at a PR which clarifies the algorithms here?
Clarify VideoFrame.copyTo() parallel steps and buffer handling