2D canvas contexts started publishing partial frames after canvas
rasterization moved into the Compositor. WebContent still splits large
recorded command lists after 64 commands, but every split batch was sent
through the same compositor path as the end-of-frame flush. The
compositor replayed each batch into the DrawCanvas source surface,
so a pending present could sample a canvas after clear and before the
rest of the next frame had been drawn. Canvas-heavy pages such as
slither.com then flickered between partial and complete frames.
Carry an explicit commit bit with 2D canvas command updates. Non-commit
batches now update a hidden working canvas in the Compositor, while the
display-list-visible surface keeps the last committed canvas contents.
The end-of-frame canvas preparation sends the commit boundary, including
the empty-commit case needed when the auto-flush consumed all recorded
commands before prepare_for_compositing() runs.
Display lists kept canvas and nested navigable content alive through
ExternalContentSource objects. That made the resource graph depend on
process-local object identity instead of a stable surface handle, which
blocks compositor process isolation and made teardown-sensitive embedded
content harder to reason about.
Allocate CompositorSurfaceId values for canvases and child navigables,
publish their backing stores to the owning compositor, and paint them
with DrawCompositorSurface. Child navigables now publish to parent
compositors by CompositorContextId instead of raw object pointers, so
the in-process path uses the same stable addressing model required by a
remote compositor.
Clear and skip stale child surfaces during teardown, preserve Skia
canvas state while drawing compositor surfaces, and add display-list
coverage for canvas and iframe compositor surfaces. The nested navigable
async-scrolling baseline now expects DrawCompositorSurface.
ImmutableBitmap still owned the helper that read pixels from a
PaintingSurface and wrapped the result as an ImmutableBitmap. That kept
a surface readback operation attached to the type we are trying to
remove, even though the snapshot is really a property of the painting
surface.
Add PaintingSurface::snapshot_bitmap() as the explicit readback path.
The remaining callers now wrap that bitmap in ImmutableBitmap only at
the places that still need the old abstraction. Canvas serialization
also uses the same helper, so the BGRA8888 premultiplied snapshot
policy has a single owner.
The rendering thread now uses its own SkiaBackendContext, and the main
thread no longer reaches into GPU-backed Skia objects directly. No Skia
context is shared between threads anymore, so the mutex on
SkiaBackendContext and the lock_context()/unlock_context() pair that
wrapped every PaintingSurface and ImmutableBitmap operation are all dead
weight and can go.
create_with_size() always pulls the process-wide SkiaBackendContext
singleton. Add an optional context parameter so the rendering thread can
build surfaces against its own independent context instead.
Generalize the backing store sharing abstraction into SharedImage, which
represents shared GPU memory independently of Skia and can be used to
share memory between different processes or different GPU contexts.
If we have a Skia backend context available, use it to make image
snapshots. Drops blitting CPU usage from 55% to 0.6% for slither.io on
my machine.
Adds a fallback to ::bitmap() to store a bitmap blitted from an SkImage
if that's available.
GPU backends have maximum texture dimension limits (e.g. 16384 on
macOS Metal). When a surface exceeds this limit, Skia's RenderTarget
returns null and we would VERIFY crash.
Instead, fall back to a CPU-backed surface via SkSurfaces::WrapPixels.
This produces a correct rendering, just without GPU acceleration.
Skia deprecated some non-span versions of their API, but they provided
SK_SUPPORT_UNSPANNED_APIS to expose the legacy versions.
SkFontMgr_New_FontConfig now requires a font scanner to be passed in.
There were a few screenshot tests that visibily looked the same but skia
must've changed some rendering infrastructure as the PNGs were not
matching anymore so I rebaselined those and adjusted the fuzzy matching
config to allow them to pass on both macOS and Linux.
The empty-radial-gradient-crash Ref test started to fail as we were
setting the horizontal scale factor to inf in when the height = 0. It
looks like something changed to make doing that not valid anymore.
The overlay port is removed as the issues, mainly skcms symbol import
and export were resolved upstream in skia and utilized in the new port
version.
Skia deprecated some non-span versions of their API, but they provided
SK_SUPPORT_UNSPANNED_APIS to expose the legacy versions.
SkFontMgr_New_FontConfig now requires a font scanner to be passed in.
There were a few screenshot tests that visibily looked the same but skia
must've changed some rendering infrastructure as the PNGs were not
matching anymore so I rebaselined those and adjusted the fuzzy matching
config to allow them to pass on both macOS and Linux.
The empty-radial-gradient-crash Ref test started to fail as we were
setting the horizontal scale factor to inf in when the height = 0. It
looks like something changed to make doing that not valid anymore.
The overlay port is removed as the issues, mainly skcms symbol import
and export were resolved upstream in skia and utilized in the new port
version.
Shareable Vulkan image allocation on Linux relies on the dma-buf
interface, which is a Linux-specific thing. Therefore, we should only be
compiling it (and any code that uses it) on Linux. This change adds
preprocessor guards to do that. Enabling similar functionality on other
operating systems will need to leverage analogous interfaces on those
platforms, e.g. win32 handles on Windows.
All Vulkan image code will now be guarded by the USE_VULKAN_IMAGES
preprocessor definition, currently enabled on Linux if Vulkan is
available. Additionally, we shuffle around some code in
OpenGLContext.cpp to simplify the preprocessor conditionals.
This adds a new PaintingSurface creation function, create_from_vkimage,
which returns a PaintingSurface backed by a vulkan image. It's analogous
to the existing create_from_iosurface function. In both cases the
backing object will be imported into Skia as a render target and then an
SkSurface will be wrapped around that.
In order to ensure that the image will not be freed while still in use
by Skia, we will manually bump the refcount of the VulkanImage object
before passing it to Skia and then use the releaseCallback parameter of
WrapBackendRenderTarget to register a callback that drops this
reference.
Now, when Skia backend context is available by the time backing stores
are allocated, there is no need to have a separate BackingStore class.
This allows us to get rid of BackingStore -> PaintingSurface cache.
This is implemented by these related changes:
* The Skia alpha type 'Opaque' is selected for surfaces that were
created with the intention of not having an alpha channel.
Previously we were simply creating one with alpha.
* Clearing now happens through Skia's `clear()` which always uses the
source color's value for the result, instead of setting all values
to 0.
* CanvasRenderingContext2D selects a different clearing color based on
the `alpha` context attribute's value.
With this change we ensure that Skia surfaces are allowed to be created
or destroyed only by one thread at a time. Not enforcing this before
resulted in "Single owner failure." assertion crashes in debug mode on
pages with canvas elements.
The Skia Ganesh backend we currently use doesn't support painting from
multiple threads, which could happen before this change when the main
thread used Skia to paint on the HTML canvas while the rendering thread
was working on display list rasterization.
Fixes https://github.com/LadybirdBrowser/ladybird/issues/4172
By doing that we eliminate the need for the vertical flip flag.
As a side effect it fixes the bug when doing:
`canvasContext2d.drawImage(canvasWithWebGLContext, 0, 0);`
produced a flipped image because we didn't account for different origin
while serializing PaintingSurface into Gfx::Bitmap.
Visual progress on https://ciechanow.ski/curves-and-surfaces/
If not set, when copying pixels into the SkImage, skia assumes that the
color space is the same as the input, so no transformation is done. We
are currently setting the color space to sRGB, this is fine for now as
it allows us to start making some transformations, but down the road we
will want to set that to the actual output's display color space.
It makes it a little bit easier to distinguish which one of
read_into_bitmap and write_from_bitmap actually modify the Bitmap that
was passed to the method. NFC.
Previously, constructing a PaintingSurface from an IOSurface required
wrapping IOSurface into a Metal texture before passing it to the
PaintingSurface constructor. This process was cumbersome, as the caller
needed access to a MetalContext to perform the wrapping.
With this change SkiaBackendContext maintains a reference to the
MetalContext which makes it possible to do:
IOSurface -> MetalTexture -> SkSurface within a PaintingSurface
constructor.