https://bugs.webkit.org/show_bug.cgi?id=228682
rdar://79699997
Reviewed by Simon Fraser.
Source/WebCore:
In the case where GPU process is enabled for canvas rendering but is not enabled for DOM rendering, canvas
elements with software CSS filters fail to repaint as the canvas' backing image buffer changes. In this
particular scenario, we first paint the canvas' remotely-backed image buffer into the filter's input buffer,
which is backed by an unaccelerated graphics context. The filter is then applied, producing an output image
buffer which we then paint into the page. The bug occurs during the initial step of painting the contents of the
canvas' remote image buffer into the filter's input context, which involves creating a CGImageRef from the
`ImageBufferCGBackend::draw`.
When asking for a CGImageRef from the canvas' image buffer (which is backed by an IOSurface that's drawn into in
the GPU process), QuartzCore caches the resulting CGImageRef corresponding to the IOSurface handle in the web
process. Subsequently, when we try to repaint the canvas, we'll end up using this same initial cached CGImageRef
because the graphics context of the IOSurface handle in the web process hasn't been drawn into (because all the
actual draw calls are made in the GPU process). As such, regardless of the current state of the canvas' image
buffer, the input to the filter will always be the initial native image created from the canvas' image buffer.
We avoid this problem when GPU process is disabled because the calls to update the canvas' 2D context in the
web process will clear out the cached CGImageRef in QuartzCore. Additionally, we avoid this problem when drawing
into accelerated contexts because QuartzCore will draw the cached CGImageRef using a codepath that reads back
the up-to-date contents from the IOSurface. However, in the QuartzCore's unaccelerated (ripc) image drawing
codepath, we copy the contents of the cached image right away, causing us to end up with stale image data that
does not reflect the current state of the IOSurface.
To work around this in Safari 15 (in both Monterey as well as downlevel versions of macOS), we deploy a similar
technique as we'd previously used in r201334 to invalidate QuartzCore's cached image, but only:
1. If the seed value of the backing IOSurface has changed since the last time we've drawn the image buffer, and
2. The destination context we're painting the image buffer into is unaccelerated.
Test: css3/filters/canvas-with-filter-after-repaint.html
* platform/graphics/cg/ImageBufferCGBackend.cpp:
(WebCore::ImageBufferCGBackend::draw):
(WebCore::ImageBufferCGBackend::drawPattern):
Call into `prepareToDrawIntoContext` (see below).
* platform/graphics/cg/ImageBufferCGBackend.cpp:
(WebCore::ImageBufferCGBackend::prepareToDrawIntoContext):
* platform/graphics/cg/ImageBufferCGBackend.h:
* platform/graphics/cg/ImageBufferIOSurfaceBackend.cpp:
(WebCore::ImageBufferIOSurfaceBackend::prepareToDrawIntoContext):
Add a new subclassable method on ImageBufferCGBackend that's invoked before drawing the contents of the image
buffer into a given destination GraphicsContext. We use this opportunity in ImageBufferIOSurfaceBackend to
notice if the IOSurface seed count has updated since we've last drawn the image buffer, and intentionally emit a
no-op drawing command (i.e. filling an empty rect) in the graphics context to invalidate QuartzCore's cached
CGImageRef. Note that we only deploy this workaround when drawing into unaccelerated contexts (see above for
more details).
(WebCore::ImageBufferIOSurfaceBackend::invalidateCachedNativeImage const):
Pull the no-op drawing command out into a separate helper method, with a comment describing why this exists.
(WebCore::ImageBufferIOSurfaceBackend::drawConsuming):
(WebCore::ImageBufferIOSurfaceBackend::copyCGImageForEncoding const):
* platform/graphics/cg/ImageBufferIOSurfaceBackend.h:
* platform/graphics/cocoa/IOSurface.h:
* platform/graphics/cocoa/IOSurface.mm:
(WebCore::IOSurface::seed const):
Add a helper method to grab the seed value of an IOSurface.
LayoutTests:
* css3/filters/canvas-with-filter-after-repaint-expected.html: Added.
* css3/filters/canvas-with-filter-after-repaint.html: Added.
Canonical link: https://commits.webkit.org/240170@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@280543 268f45cc-cd09-0410-ab3c-d52691b4dbfc