Commit graph

24 commits

Author SHA1 Message Date
Andreas Kling
ff5273084d LibWeb: Serialize external content surface clears
Lock the shared Skia backend context before clearing an external
content back store.

Nested navigables render through PublishToExternalContent, and that
clear ran outside the locking used by normal display list playback.
This allowed multiple renderer threads to enter the same Ganesh
context concurrently and trip Skia's SingleOwner checks during
painting.
2026-04-13 12:26:14 +02:00
Aliaksandr Kalenik
d9eec0d204 LibWeb: Replace ScrollStateSnapshotByDisplayList with a plain snapshot
Cleanup following the per-Navigable rasterization split: since each
Navigable now rasterizes its own display list independently, the HashMap
keyed on display list was always populated with exactly one entry. Pass
the ScrollStateSnapshot directly through the display list player and
rendering thread instead.
2026-04-07 15:09:43 +02:00
Aliaksandr Kalenik
ad2ee4fe7a LibWeb: Rasterize each Navigable independently on its own thread
Previously, iframes were rasterized synchronously as nested display
lists inside their parent's display list: the parent's paint walk called
record_display_list() on each hosted iframe document and emitted a
PaintNestedDisplayList command that the player would recurse into. Only
the top-level traversable's RenderingThread was ever active, even though
every Navigable already owned one.

The motivation for splitting this apart:
- Work in the outer document no longer has to be re-recorded when only
  an iframe changes. The parent's cached display list now references the
  iframe's rasterized output live via an ExternalContentSource, so an
  iframe invalidation just needs the parent's display list replayed, not
  re-recorded.
- Each iframe now has a self-contained rasterization pipeline, which is
  prep work for moving iframes into separate sandboxed processes.
2026-04-07 15:09:43 +02:00
Andreas Kling
a25fc5ad8a LibCore+LibWeb: Add ScopedAutoreleasePool and use it on macOS
On macOS, Objective-C methods frequently return autoreleased objects
that accumulate until an autorelease pool is drained. Our event loop
(Core::EventLoop) and rendering thread both lacked autorelease pools,
causing unbounded accumulation of autoreleased objects.

The rendering thread was the worst offender: every Skia flush triggers
Metal resource allocation which sets labels on GPU textures via
-[IOGPUMetalResource setLabel:], creating autoreleased CFData objects.
With ~1M+ such objects at 112 bytes each, this leaked ~121MB. Metal
command buffer objects (_MTLCommandBufferEncoderInfo, etc.) also
accumulated, adding another ~128MB.

Add Core::ScopedAutoreleasePool, a RAII wrapper around the ObjC runtime
autorelease pool (no-op on non-macOS), and drain it:
- Every event loop pump (like NSRunLoop does)
- Every compositor loop iteration on the rendering thread
2026-03-15 11:42:43 +01:00
Zaggy1024
8e0d31ff3e LibWeb: Simplify the lifetime of RenderingThread
This was previously using a Core::Promise to notify the thread of the
event loop being destroyed. Instead, use WeakEventLoopReference to
condition all usages, then simply detach the thread and let it run
until an exit is requested.
2026-03-02 17:06:39 -06:00
Aliaksandr Kalenik
c7dc2ba0d3 LibWeb: Remove DrawPaintingSurface
No callers of draw_painting_surface remain after the previous commits
migrated canvas, video, and SVG to use ExternalContentSource or
ImmutableBitmap snapshots.
2026-02-20 18:41:33 +01:00
Zaggy1024
e2635af2ed Everywhere: Move the thread name parameter for Thread constructors
The name parameter formats very poorly when a lambda is passed to
Thread, so let's instead put it first now that all Threads are named.
2026-01-26 15:51:46 -06:00
Zaggy1024
d2a1d727ac Everywhere: Give unnamed threads names 2026-01-26 15:51:46 -06:00
Aliaksandr Kalenik
a86f318a9b LibWeb: Move rendering backpressure from main thread to RenderingThread
In preparation for handling input events on the rendering thread, move
backpressure management to RenderingThread. The rendering thread needs
to manage this independently without querying the main thread.

Previously, the main thread would block when the UI process hadn't yet
released the backing surface. Now, the main thread can continue
producing display lists while the UI process is busy, allowing more work
to happen in parallel. When rasterization is slow and display lists are
produced faster than they can be consumed, presentation requests are
naturally coalesced using a flag-based approach -
multiple present_frame() calls simply update the pending state,
resulting in a single rasterization with the latest display list.
2026-01-26 19:05:34 +01:00
Aliaksandr Kalenik
b69ec8757f LibWeb: Store presentation callback in RenderingThread
This is preparation for future work where the rendering thread will
initiate rasterization independently and notify the UI process without
requiring coordination with the main thread.
2026-01-26 17:01:48 +01:00
Aliaksandr Kalenik
516fb5f2fe LibWeb: Make RenderingThread own display list and backing stores
This change prepares for a future where the rendering thread handles
input events directly, allowing it to trigger repainting without
waiting for the main thread. To support this, the compositor needs to
own the display list, scroll state, and backing stores rather than
receiving them per-frame from the main thread.
2026-01-26 14:53:08 +01:00
Aliaksandr Kalenik
d7f830cdd1 LibWeb: Execute rasterization callback on the rendering thread
Previously, we enqueued a task on the main thread's event loop to
execute the callback. This meant that even though the rendering thread
had finished producing the next frame, there was still a delay before
the main thread notified the UI process.

This change makes the rendering thread execute the callback directly.
This should be safe, as the only pointer captured by the callback is the
traversable `PageClient`, which is expected to remain alive for as long
as the rendering thread exists. The callback then invokes either
`page_did_paint()` or `page_did_take_screenshot()`, both of which
enqueue an IPC message, which is safe to do since `SendQueue` is
protected by a mutex.
2025-10-10 17:25:55 +02:00
Zaggy1024
2aaf53bd2c Everywhere: Use a forward declaration for pointers to Threading::Thread 2025-09-22 17:28:21 -05:00
Aliaksandr Kalenik
8569124b87 LibWeb: Fix scroll state refresh in cached display list for iframes
6507d23 introduced a bug when snapshot for iframe is saved in
`PaintNestedDisplayList` and, since display lists are immutable, it's
not possible to update before the next repaint.

This change fixes the issue by moving `ScrollStateSnapshot` for
nested display lists from `PaintNestedDisplayList` to
`HashMap<NonnullRefPtr<DisplayList>, ScrollStateSnapshot>` that is
placed into pending rendering task, making it possible to update
snapshots for all display lists before the next repaint.

This change doesn't have a test because it's really hard to make a ref
test that will specifically check scenario when scroll offset of an
iframe is advanced after display list is cached. We already have
`Tests/LibWeb/Ref/input/scroll-iframe.html` but unfortunately it did
not catch this bug.

Fixes https://github.com/LadybirdBrowser/ladybird/issues/5486
2025-07-26 11:53:21 -04:00
Aliaksandr Kalenik
410e82c9fd LibWeb: Rearrange code such that a lot less files include Command.h
With this change number of recompiled files after modification of
`Command.h` goes down from >1000 to <100.
2025-07-11 17:37:27 +02:00
Aliaksandr Kalenik
c18314b942 LibWeb+LibGfx: Replace BackingStore with PaintingSurface
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.
2025-07-04 16:12:47 +02:00
Aliaksandr Kalenik
ed1337add1 LibJS: Don't start rendering thread for a navigable created for SVG
Traversable navigable created for rendering svg element doesn't perform
any actual rendering, so there is no need to spawn a rendering thread.
2025-06-06 03:56:57 +02:00
Andrew Kaster
0f81f021ce LibWeb: Clean up main thread promise when exiting RenderingThreads
Not cleaning these up by rejecting or resolving the promise causes
the main thread to try to reject them at EventLoop::exit() time.

If the RenderThread has already been destroyed by then, we get into
use-after-free territory and segfault.
2025-05-30 16:18:23 +02:00
Andrew Kaster
e5465ff8e5 LibWeb: Crash less when the main thread exits while trying to render
Attach a 'job' to the main thread event loop, trusting that the event
loop implementation will cancel it when asked to quit. This is something
that our Unix implementation does, but isn't strictly part of the
contract of EventLoopImplementation.
2025-04-29 09:51:22 -06:00
Aliaksandr Kalenik
6507d23e29 LibWeb: Save ScrollState snapshot in DisplayList to avoid race condition
With this change we save a copy of of scroll state at the time of
recording a display list, instead of actual ScrollState pointer that
could be modifed by the main thread while display list is beings
rasterized on the rendering thread, which leads to a frame painted with
inconsistent scroll state.

Fixes https://github.com/LadybirdBrowser/ladybird/issues/4288
2025-04-12 02:55:18 +02:00
Aliaksandr Kalenik
12a2aebeb6 LibWeb: Move painting surface allocation into rendering thread
Skia has a check in debug mode to verify that surface is only used
within one thread. Before this change we were violating this by
allocating surfaces on the main thread while using and destructing them
on the rendering thread.
2025-04-03 22:01:43 +02:00
Aliaksandr Kalenik
bc0ec84100 LibWeb: Don't queue rendering task for empty display lists 2025-04-02 23:22:38 +02:00
Aliaksandr Kalenik
24527b6ae3 LibWeb: Pass PaintingSurface into DisplayListPlayer::execute()
Deleteing set_surface() makes DisplayListPlayer API a bit more intuitive
because now caller doesn't have to think whether it's necessary to
restore previous surface after execution, instead DisplayListPlayer
takes care of it by maintaining a stack of surfaces.
2025-04-01 23:39:05 +02:00
Aliaksandr Kalenik
24e2c402f5 LibWeb+WebContent: Move display list rasterization off the main thread
The display list is an immutable data structure, so once it's created,
rasterization can be moved to a separate thread. This allows more room
for performing other tasks between processing HTML rendering tasks.

This change makes PaintingSurface, ImmutableBitmap, and GlyphRun atomic
ref-counted, as they are shared between the main and rendering threads
by being included in the display list.
2025-03-31 15:58:15 +01:00