When some element that's being rendered in its own GraphicsLayer and with its own backingStore is scaled, the scaling is done with the GPU, and the scaled content will be blurry and with very low quality. This happens because the backingStore, with its original size, is uploaded to a texture and then scaled to the new size. This produces a bad quality scaled result, which is way worse than if the element was directly painted scaled with cairo (which would happen if the element didn't have it's own GraphicsLayer). There's an example of the different results produced at https://people.igalia.com/magomez/scale/difference.html where the image to the left is scaled with cairo (the element doesn't have a GraphicsLayer) and the image to the right is forced into its own layer (with a translate3d transform) and then scaled as well. This becomes more problematic when we take into account that animations will create a layer for the animated element. So, let's say, we animate a 100x100 element with a scale of 5 until 500x500. What happens first is that the element gets a GraphicsLayer and uses a backingStore of 100x100 to render its contents. Then that backingStore is scaled during the animation to 500x500, producing a blurry result. But at the end of the animation, the GraphicsLayer is removed, so the element is painted again into the parent layer with a builtin scale of 5, producing a sudden quality improvement in the element. You can see this behavior at this simple test https://people.igalia.com/magomez/scale/animation.html. If, for some reason, the element already had a GraphicsLayer before the animation, then it's not destroyed after the animation, and the element is not repainted with the builtin scale, so the blurry content will stay (similar to the first test case I mentioned, despite that one doesn't use an animation). What we need to do here is to increase the scale factor of the backingStore used by the element according to the scale value that we're going to use. So if we're scaling by 5, the backingStore should have a scaleFactor of 5 as well, and when the scaled content gets shown it will have the proper quality. There are actually some layout tests for this in compositing (I can't remember which ones) that started passing when I was testing a patch to fix this problem. So, I've created a patch that initially fixes this, but it's on top of 2.28. I need to adapt it to ToT and give it more testing. I'll attach it here. Also, I though that this could potentially be happening if we also animated changes in the size of the element. For example setting a transition to the width and then changing it. But I've checked that in this case the animation is not done in the GPU. For each step of the animation there's a layerflush that creates a new backingStore with the new size at that point of the animation and that's sent to the composition, so the result doesn't get blurry.
Created attachment 460733 [details] WiP patch
Bug 96940 added test cases for Chromium. compositing/text-on-scaled-layer.html compositing/text-on-scaled-surface.html Can your patch unmark them? There is a bug ticket for Mac port. Bug 27684 – Composited elements appear pixelated when scaled up using transform
Comment on attachment 460733 [details] WiP patch View in context: https://bugs.webkit.org/attachment.cgi?id=460733&action=review > Source/WebCore/platform/graphics/nicosia/NicosiaAnimation.cpp:403 > + double scale = 1; This is WebKit style > Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedGraphicsLayer.cpp:1371 > + float parentScale = 1.0; This is not WebKit style. https://webkit.org/code-style-guidelines/#float-suffixes > Source/WebCore/platform/graphics/texmap/coordinated/CoordinatedGraphicsLayer.h:249 > + float m_animationOrTransformScaleFactor { 1.0 }; Ditto.
As discussed on Matrix, https://bugs.webkit.org/show_bug.cgi?id=242833 contains a general fix, however not taking animations/transitions (max scale can be figured out!) into account. Ideally we'd combine these two patches, and introduce a setting if a port enables this or not (or a CSS prop to toggle the old vs. new behavior). Needs discussion with Apple folks!