RESOLVED FIXED 230766
Canvas drawImage is very slow when used with video as source
https://bugs.webkit.org/show_bug.cgi?id=230766
Summary Canvas drawImage is very slow when used with video as source
Patryk Rogalski
Reported 2021-09-24 12:01:45 PDT
Created attachment 439181 [details] comparison between Chrome and Safari I'm running the latest Safari and Safari Technology Preview and I've run into a performance issue in Safari. I have a video that is encoded in H264 2176x1088. The video itself plays smoothly however I have to cut the content of it into 8 different canvas elements. I have a working demo of it working however the performance I'm getting is abysmal. I've ran the same code in Google Chrome 94 and it's perfect. Here is the comparison: Chrome takes about 0.5-2ms to draw Safari takes 150-330ms to draw Firefox takes 100-150ms And it has to run at 30 fps... which is completely impossible right now. Here is a demo to showcase the issue: https://cdn.zu.casa/dev/apple/index.html If open developer console it'll print out to the console the time it took to render.
Attachments
comparison between Chrome and Safari (41.09 MB, video/quicktime)
2021-09-24 12:01 PDT, Patryk Rogalski
no flags
Patch for EWS (2.21 KB, patch)
2021-10-24 18:49 PDT, Cameron McCormack (:heycam)
no flags
Patch (2.76 KB, patch)
2021-10-27 16:25 PDT, Cameron McCormack (:heycam)
no flags
Patch (4.78 KB, patch)
2021-10-28 23:28 PDT, Cameron McCormack (:heycam)
no flags
Patch for landing (2.72 KB, patch)
2021-11-04 20:35 PDT, Cameron McCormack (:heycam)
no flags
Patryk Rogalski
Comment 1 2021-09-24 12:12:14 PDT
I had an older version of Safari on my iMac and it ran super fast (1.5-4ms) - as fast as in Google Chrome. After updating to the latest version it's terrible (200-350ms).
Sam Sneddon [:gsnedders]
Comment 2 2021-09-27 01:22:01 PDT
Quick look at a sample suggests we're blocking on cross-thread/process communication a bunch. Probably a regression caused by moving more things out-of-process?
Radar WebKit Bug Importer
Comment 3 2021-09-27 09:12:51 PDT
Cameron McCormack (:heycam)
Comment 4 2021-10-24 16:51:44 PDT
Thanks for the bug report. Sam is right: this is because with GPU process canvas rendering enabled, we end up doing some copies of the HTMLVideoElement frame data (which lives in the GPU process) to share it (synchronously) to the Web process, only to ship it back to the GPU process again for the drawImage() call. We should be able to avoid any of this copying.
Cameron McCormack (:heycam)
Comment 5 2021-10-24 18:49:46 PDT
Created attachment 442334 [details] Patch for EWS
Cameron McCormack (:heycam)
Comment 6 2021-10-27 16:25:38 PDT
Cameron McCormack (:heycam)
Comment 7 2021-10-27 17:16:28 PDT
Simon pointed out to me there's another call to nativeImageForCurrentTime(), in createPattern(). But patterns currently can't avoid copying the pattern source image data from the GPU process, so I'll leave that be for now. And filed https://bugs.webkit.org/show_bug.cgi?id=232411 for the more general problem.
Cameron McCormack (:heycam)
Comment 8 2021-10-28 23:28:38 PDT
EWS
Comment 9 2021-10-29 14:45:47 PDT
Committed r285055 (243698@main): <https://commits.webkit.org/243698@main> All reviewed patches have been landed. Closing bug and clearing flags on attachment 442787 [details].
ayumi_kojima
Comment 10 2021-11-01 09:28:08 PDT
Reverted r285055 for reason: Reverting because this commit may have caused webgl/1.0.x/conformance/textures/misc/texture-corner-case-videos.html and webgl/2.0.y/conformance/textures/misc/texture-corner-case-videos.html to time out Committed r285110 (243751@main): <https://commits.webkit.org/243751@main>
Said Abou-Hallawa
Comment 11 2021-11-01 11:13:48 PDT
Comment on attachment 442787 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=442787&action=review > Source/WebCore/html/canvas/CanvasRenderingContext2DBase.cpp:1680 > + if (!canvasBase().buffer()->isRemote()) { We have been trying to avoid adding this condition in WebCore. Can't we do this by checking whether 'c', which is the GraphicsContext of canvasBase().buffer(), is a recording GraphicsContext or not? I think you can replace this condition by if (c->hasPlatformContext()) { ... return { }; } This function has been used for this purpose in WebCore in a few places. Just search for it. > Source/WebCore/platform/graphics/ImageBuffer.h:74 > + virtual bool isRemote() const { return false; } Please avoid adding such check in WebCore. > Source/WebKit/WebProcess/GPU/graphics/RemoteImageBufferProxy.h:98 > + bool isRemote() const final { return true; } This method can be private.
Cameron McCormack (:heycam)
Comment 12 2021-11-01 14:48:10 PDT
(In reply to Said Abou-Hallawa from comment #11) > > Source/WebCore/html/canvas/CanvasRenderingContext2DBase.cpp:1680 > > + if (!canvasBase().buffer()->isRemote()) { > > We have been trying to avoid adding this condition in WebCore. Can't we do > this by checking whether 'c', which is the GraphicsContext of > canvasBase().buffer(), is a recording GraphicsContext or not? > > I think you can replace this condition by > > if (c->hasPlatformContext()) { > ... > return { }; > } I had that in my original patch but Simon was not sure about it, so I changed it to this more direct check on the ImageBuffer. I can change it back.
Wenson Hsieh
Comment 13 2021-11-01 16:16:22 PDT
(In reply to Cameron McCormack (:heycam) from comment #12) > (In reply to Said Abou-Hallawa from comment #11) > > > Source/WebCore/html/canvas/CanvasRenderingContext2DBase.cpp:1680 > > > + if (!canvasBase().buffer()->isRemote()) { > > > > We have been trying to avoid adding this condition in WebCore. Can't we do > > this by checking whether 'c', which is the GraphicsContext of > > canvasBase().buffer(), is a recording GraphicsContext or not? > > > > I think you can replace this condition by > > > > if (c->hasPlatformContext()) { > > ... > > return { }; > > } > > I had that in my original patch but Simon was not sure about it, so I > changed it to this more direct check on the ImageBuffer. I can change it > back. Generally speaking, methods that explicitly reference the WebKit process model (e.g. the notion of "remote" or "proxy" objects) normally exist in the WebKit2 layer, with client layer hooks (on WebChromeClient, for instance) that WebCore code may use in order to delegate decision-making to things that know about the process model. That said, there are some exceptions to this. But in general, ImageBuffer and its subclasses/backends have preserved this separation — it's also how we've (previously) avoided adding a method like `isRemote()` to image buffer.
Cameron McCormack (:heycam)
Comment 14 2021-11-04 20:35:42 PDT
Created attachment 443372 [details] Patch for landing
EWS
Comment 15 2021-11-04 21:06:19 PDT
Committed r285333 (243896@main): <https://commits.webkit.org/243896@main> All reviewed patches have been landed. Closing bug and clearing flags on attachment 443372 [details].
Note You need to log in before you can comment on or make changes to this bug.