-
Notifications
You must be signed in to change notification settings - Fork 508
Fix Chromium performance hit in calls due to blur filter #4678
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix Chromium performance hit in calls due to blur filter #4678
Conversation
|
/backport to stable20 |
| this.blur = this.$store.getters.getBlurRadius(width, height) | ||
| }, | ||
| generateBlurredBackgroundImage() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or just a plain background?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that if we can use a blurred background we should use it, but of course if the performance hit is still bad even with an OffscreenCanvas then the plain background could be the only solution.
Signed-off-by: Daniel Calviño Sánchez <[email protected]>
Signed-off-by: Daniel Calviño Sánchez <[email protected]>
The CSS blur filter in the video background causes Chromium's UI to be very slow. The performance hit is larger when the element to blur is larger, and also when the blur radius is larger, so a promoted speaker with a blurred image seems to be the worst case (even worse than a grid with several rows and columns). However, even if the CSS blur filter is slow, applying the blur filter on a canvas is much more performant (maybe the blur filter itself has the same performance in both cases, but the UI uses it several times in a row when rendering the element, but that is just a guess). Due to this now a blurred background image is generated from the original background image whenever the blur size changes, and that blurred image is the one used in the image element without any CSS filter. Signed-off-by: Daniel Calviño Sánchez <[email protected]>
Until now a new source image was loaded every time that the blurred image had to be generated. Now the source image is loaded only when the URL of the background image changes and that "cached" image is reused to generate any blurred image. The source image is loaded using a watcher instead of a computed property because the "complete" read-only property of HTMLImageElement is not reactive. Due to this it is not possible to set "blurredBackgroundImageSource" as a computed property and then check its "complete" attribute in "generatedBackgroundBlur" to know if the image is loaded or not. Signed-off-by: Daniel Calviño Sánchez <[email protected]>
Signed-off-by: Daniel Calviño Sánchez <[email protected]>
The cache id needs to be generated from the background image URL rather than the image itself (as checksuming the image would require drawing it to a canvas, which although still faster than drawing a blurred image would defeat the use of a cache). For simplicity the images are cached locally in the VideoBackground element rather than in a central store. Due to this the images will be regenerated again if the component is created again (for example, when changing between speaker mode or grid view), but minimizes the posibility of using a wrong cached blurred image if the image returned by the URL changed. Signed-off-by: Daniel Calviño Sánchez <[email protected]>
As the "blur" operation returns a promise it needs to be ensured that the elements will not be used after the component was destroyed. Similarly the background should be restored after getting the "raw" size of the image element to prevent an empty background to be shown while the new background is being generated (although in practice this will not happen due to "blur" still generating the image in the UI thread, but it will be needed once moved to a worker) Signed-off-by: Daniel Calviño Sánchez <[email protected]>
If several blurred backgrounds are requested while a previous one is being generated only the last requested background will be generated once the previous one finished, as the intermediate ones would be no longer needed. Signed-off-by: Daniel Calviño Sánchez <[email protected]>
Until now the generation of the blurred backgrounds was done in a normal canvas, so it happened in the main thread and made the UI unresponsive while the image was being generated. Now, to solve that, the blurred background is generated in an OffscreenCanvas in a worker (if supported by the browser). Signed-off-by: Daniel Calviño Sánchez <[email protected]>
0714337 to
9708a54
Compare
|
I would still need to check that no memory leaks were introduced and that older browsers still work as expected (they should, but better ensure), but other than that I think that this is ready for review. Also feel free to merge too if you do not see any potential memory leak and older browsers work :-) |
| width = height * sourceAspectRatio | ||
| } | ||
| const cacheId = this.backgroundImageUrl + '-' + width + '-' + height + '-' + this.backgroundBlur |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it is likely that someone can overflow the cache by resizing the window gradually
maybe need to use stepping for different width/height (like using divisions of 1024 or something)
in general we probably only ever need to catch 2 or 3 dimensions: the one in grid view, the one in stripe view and the one in speaker view
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You would really need to resize the window very slowly and for a while to overflow the cache. But it would be indeed possible.
Therefore I will limit the cache of each VideoBackground to the last ten calculated dimensions or so (as whether the sidebars are open or not also affect the size of the background and, thus, the blur).
|
Feels like this brings us further away from "keep it simple" for "just the blurred background", not sure if it's a good idea to go with such complex solution, at least one that we need to maintain ourselves. Is there no library out there that we could use that already solved this ? |
|
Should the cache be separate from the VideoBackground view ? This back then I chose to cache avatars outside the component, see #4284 |
as much as I like the blur, I'm totally in on that. All the troubles and CPU problems sound like not worth it and as stated before, I tend to say let's just use a plain background then. |
|
/backport to stable20.1 |
To be honest I did not check, I just implemented it. But if there is a suitable library let's use it, sure :-) Suggestions welcome :-)
I thought about it, but I kept it in the VideoBackground so it was reloaded whenever the avatar might be reloaded (for example, when changing between grid view and speaker mode). This was to prevent a mismatch between the avatar and its background. However... a user changing her avatar during a call should be quite a corner case, so it might be better to cache the backgrounds globally so they are reused when switching views.
I would keep the blur system. It is of course more complex than just using |
marcoambrosini
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested and works, though sometimes when switching to the grid view and trying to select videos, nothing happens. Not sure if this is related to this pr. No errors in the console, the selected video peer id is logged correctly
The CSS blur filter used in the VideoBackground component causes a severe performance hit in Chromium (the performance hit is higher the larger the image, and the larger the radius (which happens with larger images, the perfect storm :-P )).
To solve that, and given that Chromium has support for OffscreenCanvas, now the blurred background is generated instead using an OffscreenCanvas in a worker. Generating the blurred image in a normal canvas already improves the performance a lot, but it is still somewhat slow, so it needs to be done in a worker to not block the UI thread during the generation. Due to the slowness a cache for blurred backgrounds was added too.
Pending:
Known issues:
The blurred background is cached locally in the component instance, and preventing the generation of other blurred backgrounds while a previous one is still being generated is also done per component instance. Due to this switching quickly and several times between the speaker mode and the grid view causes the generation of the last backgrounds to be delayed until the previous ones are ready. However that should not be something common to do, it is an annoyance rather than a big issue and the UI is still responsive while doing that, so I would not invest time on fixing that (or at least not at this moment).
Grid blur is not set for LocalVideo component, so it always uses its own self-calculated blur radius. It seems that there is something else needed besides propagating the value from LocalVideo to VideoBackground and setting
:video-background-blur="videoBackgroundBlur"in the LocalVideo components of Grid.vue (probably just guarding against calculating the blur when there are no rows or columns, asvideoWidthandvideoHeightmay be NaN or Infinity).Future?:
How to test
Result with this pull request
The sidebar closes and opens immediately
Result without this pull request
The UI is unresponsive (even if audio and video work as expected) for several seconds while the sidebar is closing and opening