mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-26 01:35:08 +02:00
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.
This commit is contained in:
committed by
Alexander Kalenik
parent
1c3d503146
commit
a86f318a9b
Notes:
github-actions[bot]
2026-02-06 11:04:12 +00:00
Author: https://github.com/kalenikaliaksandr Commit: https://github.com/LadybirdBrowser/ladybird/commit/a86f318a9be Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/7632
@@ -39,16 +39,12 @@ struct UpdateBackingStoresCommand {
|
||||
i32 back_bitmap_id;
|
||||
};
|
||||
|
||||
struct PresentFrameCommand {
|
||||
Gfx::IntRect viewport_rect;
|
||||
};
|
||||
|
||||
struct ScreenshotCommand {
|
||||
NonnullRefPtr<Gfx::PaintingSurface> target_surface;
|
||||
Function<void()> callback;
|
||||
};
|
||||
|
||||
using CompositorCommand = Variant<UpdateDisplayListCommand, UpdateBackingStoresCommand, PresentFrameCommand, ScreenshotCommand>;
|
||||
using CompositorCommand = Variant<UpdateDisplayListCommand, UpdateBackingStoresCommand, ScreenshotCommand>;
|
||||
|
||||
class RenderingThread::ThreadData final : public AtomicRefCounted<ThreadData> {
|
||||
public:
|
||||
@@ -72,6 +68,7 @@ public:
|
||||
Threading::MutexLocker const locker { m_mutex };
|
||||
m_exit = true;
|
||||
m_command_ready.signal();
|
||||
m_ready_to_paint.signal();
|
||||
}
|
||||
|
||||
void enqueue_command(CompositorCommand&& command)
|
||||
@@ -81,60 +78,100 @@ public:
|
||||
m_command_ready.signal();
|
||||
}
|
||||
|
||||
void set_needs_present(Gfx::IntRect viewport_rect)
|
||||
{
|
||||
Threading::MutexLocker const locker { m_mutex };
|
||||
m_needs_present = true;
|
||||
m_pending_viewport_rect = viewport_rect;
|
||||
m_command_ready.signal();
|
||||
}
|
||||
|
||||
void compositor_loop()
|
||||
{
|
||||
while (true) {
|
||||
auto command = [this]() -> Optional<CompositorCommand> {
|
||||
{
|
||||
Threading::MutexLocker const locker { m_mutex };
|
||||
while (m_command_queue.is_empty() && !m_exit) {
|
||||
while (m_command_queue.is_empty() && !m_needs_present && !m_exit) {
|
||||
m_command_ready.wait();
|
||||
}
|
||||
if (m_exit)
|
||||
return {};
|
||||
return m_command_queue.dequeue();
|
||||
}();
|
||||
|
||||
if (!command.has_value()) {
|
||||
VERIFY(m_exit);
|
||||
break;
|
||||
break;
|
||||
}
|
||||
|
||||
command->visit(
|
||||
[this](UpdateDisplayListCommand& cmd) {
|
||||
m_cached_display_list = move(cmd.display_list);
|
||||
m_cached_scroll_state_snapshot = move(cmd.scroll_state_snapshot);
|
||||
},
|
||||
[this](UpdateBackingStoresCommand& cmd) {
|
||||
m_backing_stores.front_store = move(cmd.front_store);
|
||||
m_backing_stores.back_store = move(cmd.back_store);
|
||||
m_backing_stores.front_bitmap_id = cmd.front_bitmap_id;
|
||||
m_backing_stores.back_bitmap_id = cmd.back_bitmap_id;
|
||||
},
|
||||
[this](PresentFrameCommand& cmd) {
|
||||
if (!m_cached_display_list || !m_backing_stores.is_valid())
|
||||
return;
|
||||
while (true) {
|
||||
auto command = [this]() -> Optional<CompositorCommand> {
|
||||
Threading::MutexLocker const locker { m_mutex };
|
||||
if (m_command_queue.is_empty())
|
||||
return {};
|
||||
return m_command_queue.dequeue();
|
||||
}();
|
||||
|
||||
if (!command.has_value())
|
||||
break;
|
||||
|
||||
command->visit(
|
||||
[this](UpdateDisplayListCommand& cmd) {
|
||||
m_cached_display_list = move(cmd.display_list);
|
||||
m_cached_scroll_state_snapshot = move(cmd.scroll_state_snapshot);
|
||||
},
|
||||
[this](UpdateBackingStoresCommand& cmd) {
|
||||
m_backing_stores.front_store = move(cmd.front_store);
|
||||
m_backing_stores.back_store = move(cmd.back_store);
|
||||
m_backing_stores.front_bitmap_id = cmd.front_bitmap_id;
|
||||
m_backing_stores.back_bitmap_id = cmd.back_bitmap_id;
|
||||
},
|
||||
[this](ScreenshotCommand& cmd) {
|
||||
if (!m_cached_display_list)
|
||||
return;
|
||||
m_skia_player->execute(*m_cached_display_list, Painting::ScrollStateSnapshotByDisplayList(m_cached_scroll_state_snapshot), *cmd.target_surface);
|
||||
if (cmd.callback) {
|
||||
invoke_on_main_thread([callback = move(cmd.callback)]() mutable {
|
||||
callback();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (m_exit)
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_exit)
|
||||
break;
|
||||
|
||||
bool should_present = false;
|
||||
Gfx::IntRect viewport_rect;
|
||||
{
|
||||
Threading::MutexLocker const locker { m_mutex };
|
||||
if (m_needs_present) {
|
||||
should_present = true;
|
||||
viewport_rect = m_pending_viewport_rect;
|
||||
m_needs_present = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (should_present) {
|
||||
// Block if we already have a frame queued (back pressure)
|
||||
{
|
||||
Threading::MutexLocker const locker { m_mutex };
|
||||
while (m_queued_rasterization_tasks > 1 && !m_exit) {
|
||||
m_ready_to_paint.wait();
|
||||
}
|
||||
if (m_exit)
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_cached_display_list && m_backing_stores.is_valid()) {
|
||||
m_skia_player->execute(*m_cached_display_list, Painting::ScrollStateSnapshotByDisplayList(m_cached_scroll_state_snapshot), *m_backing_stores.back_store);
|
||||
i32 rendered_bitmap_id = m_backing_stores.back_bitmap_id;
|
||||
m_backing_stores.swap();
|
||||
|
||||
invoke_on_main_thread([this, viewport_rect = cmd.viewport_rect, rendered_bitmap_id]() {
|
||||
m_queued_rasterization_tasks++;
|
||||
|
||||
invoke_on_main_thread([this, viewport_rect, rendered_bitmap_id]() {
|
||||
m_presentation_callback(viewport_rect, rendered_bitmap_id);
|
||||
});
|
||||
},
|
||||
[this](ScreenshotCommand& cmd) {
|
||||
if (!m_cached_display_list)
|
||||
return;
|
||||
m_skia_player->execute(*m_cached_display_list, Painting::ScrollStateSnapshotByDisplayList(m_cached_scroll_state_snapshot), *cmd.target_surface);
|
||||
if (cmd.callback) {
|
||||
invoke_on_main_thread([callback = move(cmd.callback)]() mutable {
|
||||
callback();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (m_exit)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,6 +199,21 @@ private:
|
||||
RefPtr<Painting::DisplayList> m_cached_display_list;
|
||||
Painting::ScrollStateSnapshotByDisplayList m_cached_scroll_state_snapshot;
|
||||
BackingStoreState m_backing_stores;
|
||||
|
||||
Atomic<i32> m_queued_rasterization_tasks { 0 };
|
||||
mutable Threading::ConditionVariable m_ready_to_paint { m_mutex };
|
||||
|
||||
bool m_needs_present { false };
|
||||
Gfx::IntRect m_pending_viewport_rect;
|
||||
|
||||
public:
|
||||
void decrement_queued_tasks()
|
||||
{
|
||||
Threading::MutexLocker const locker { m_mutex };
|
||||
VERIFY(m_queued_rasterization_tasks >= 1 && m_queued_rasterization_tasks <= 2);
|
||||
m_queued_rasterization_tasks--;
|
||||
m_ready_to_paint.signal();
|
||||
}
|
||||
};
|
||||
|
||||
RenderingThread::RenderingThread(PresentationCallback presentation_callback)
|
||||
@@ -211,7 +263,7 @@ void RenderingThread::update_backing_stores(RefPtr<Gfx::PaintingSurface> front,
|
||||
|
||||
void RenderingThread::present_frame(Gfx::IntRect viewport_rect)
|
||||
{
|
||||
m_thread_data->enqueue_command(PresentFrameCommand { viewport_rect });
|
||||
m_thread_data->set_needs_present(viewport_rect);
|
||||
}
|
||||
|
||||
void RenderingThread::request_screenshot(NonnullRefPtr<Gfx::PaintingSurface> target_surface, Function<void()>&& callback)
|
||||
@@ -219,4 +271,9 @@ void RenderingThread::request_screenshot(NonnullRefPtr<Gfx::PaintingSurface> tar
|
||||
m_thread_data->enqueue_command(ScreenshotCommand { move(target_surface), move(callback) });
|
||||
}
|
||||
|
||||
void RenderingThread::ready_to_paint()
|
||||
{
|
||||
m_thread_data->decrement_queued_tasks();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user