Skip to content

Commit

Permalink
LibWeb: Move updating the rendering into HTML task
Browse files Browse the repository at this point in the history
Implements whatwg/html#10007 which basically
moves style, layout and painting from HTML processing task into HTML
task with "rendering" source.

The biggest difference is that now we no longer schedule HTML event loop
processing whenever we might need a repaint, but instead queue a global
rendering task 60 times per second that will check if any documents
need a style/layout/paint update.

That is a great simplification of our repaint scheduling model. Before
we had:
- Optional timer that schedules animation updates 60 hz
- Optional timer that schedules rAF updates
- PaintWhenReady state to schedule a paint if navigable doesn't have a
  rendering opportunity on the last event loop iteration

Now all that is gone and replaced with a single timer that drives
repainting at 60 hz and we don't have to worry about excessive repaints.

In the future, hard-coded 60 hz refresh interval could be replaced with
CADisplayLink on macOS and similar API on linux to drive repainting in
synchronization with display's refresh rate.
  • Loading branch information
kalenikaliaksandr authored and rmg-x committed Oct 4, 2024
1 parent 81be285 commit 55a4a18
Show file tree
Hide file tree
Showing 12 changed files with 199 additions and 262 deletions.
3 changes: 0 additions & 3 deletions Userland/Libraries/LibWeb/Animations/Animation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -559,9 +559,6 @@ WebIDL::ExceptionOr<void> Animation::play()
// https://www.w3.org/TR/web-animations-1/#play-an-animation
WebIDL::ExceptionOr<void> Animation::play_an_animation(AutoRewind auto_rewind)
{
if (auto document = document_for_timing())
document->ensure_animation_timer();

// 1. Let aborted pause be a boolean flag that is true if animation has a pending pause task, and false otherwise.
auto aborted_pause = m_pending_pause_task == TaskState::Scheduled;

Expand Down
25 changes: 0 additions & 25 deletions Userland/Libraries/LibWeb/DOM/Document.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4568,31 +4568,6 @@ void Document::remove_replaced_animations()
}
}

void Document::ensure_animation_timer()
{
constexpr static auto timer_delay_ms = 1000 / 60;
if (!m_animation_driver_timer) {
m_animation_driver_timer = Core::Timer::create_repeating(timer_delay_ms, [this] {
bool has_animations = false;
for (auto& timeline : m_associated_animation_timelines) {
if (!timeline->associated_animations().is_empty()) {
has_animations = true;
break;
}
}
if (!has_animations) {
m_animation_driver_timer->stop();
return;
}
auto* window_or_worker = dynamic_cast<HTML::WindowOrWorkerGlobalScopeMixin*>(&realm().global_object());
VERIFY(window_or_worker);
update_animations_and_send_events(window_or_worker->performance()->now());
});
}

m_animation_driver_timer->start();
}

Vector<JS::NonnullGCPtr<Animations::Animation>> Document::get_animations()
{
Vector<JS::NonnullGCPtr<Animations::Animation>> relevant_animations;
Expand Down
1 change: 0 additions & 1 deletion Userland/Libraries/LibWeb/DOM/Document.h
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,6 @@ class Document
void append_pending_animation_event(PendingAnimationEvent const&);
void update_animations_and_send_events(Optional<double> const& timestamp);
void remove_replaced_animations();
void ensure_animation_timer();

Vector<JS::NonnullGCPtr<Animations::Animation>> get_animations();

Expand Down
10 changes: 0 additions & 10 deletions Userland/Libraries/LibWeb/HTML/AnimationFrameCallbackDriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,10 @@ namespace Web::HTML {
struct AnimationFrameCallbackDriver {
using Callback = Function<void(double)>;

AnimationFrameCallbackDriver()
{
m_timer = Core::Timer::create_single_shot(16, [] {
HTML::main_thread_event_loop().schedule();
});
}

[[nodiscard]] WebIDL::UnsignedLong add(Callback handler)
{
auto id = ++m_animation_frame_callback_identifier;
m_callbacks.set(id, move(handler));
if (!m_timer->is_active())
m_timer->start();
return id;
}

Expand Down Expand Up @@ -60,7 +51,6 @@ struct AnimationFrameCallbackDriver {
WebIDL::UnsignedLong m_animation_frame_callback_identifier { 0 };

OrderedHashMap<WebIDL::UnsignedLong, Callback> m_callbacks;
RefPtr<Core::Timer> m_timer;
};

}
384 changes: 184 additions & 200 deletions Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class EventLoop : public JS::Cell {
void spin_until(JS::SafeFunction<bool()> goal_condition);
void spin_processing_tasks_with_source_until(Task::Source, JS::SafeFunction<bool()> goal_condition);
void process();
void queue_task_to_update_the_rendering();

// https://html.spec.whatwg.org/multipage/browsing-the-web.html#termination-nesting-level
size_t termination_nesting_level() const { return m_termination_nesting_level; }
Expand Down Expand Up @@ -114,7 +115,7 @@ class EventLoop : public JS::Cell {

bool m_skip_event_loop_processing_steps { false };

bool m_is_running_reflow_steps { false };
bool m_is_running_rendering_task { false };
};

EventLoop& main_thread_event_loop();
Expand Down
3 changes: 3 additions & 0 deletions Userland/Libraries/LibWeb/HTML/EventLoop/Task.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ class Task final : public JS::Cell {
// https://html.spec.whatwg.org/multipage/server-sent-events.html#remote-event-task-source
RemoteEvent,

// https://html.spec.whatwg.org/multipage/webappapis.html#rendering-task-source
Rendering,

// !!! IMPORTANT: Keep this field last!
// This serves as the base value of all unique task sources.
// Some elements, such as the HTMLMediaElement, must have a unique task source per instance.
Expand Down
1 change: 0 additions & 1 deletion Userland/Libraries/LibWeb/Page/Page.h
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,6 @@ class PageClient : public JS::Cell {
virtual void inspector_did_execute_console_script([[maybe_unused]] String const& script) { }
virtual void inspector_did_export_inspector_html([[maybe_unused]] String const& html) { }

virtual void schedule_repaint() = 0;
virtual bool is_ready_to_paint() const = 0;

virtual DisplayListPlayerType display_list_player_type() const = 0;
Expand Down
1 change: 0 additions & 1 deletion Userland/Libraries/LibWeb/SVG/SVGDecodedImageData.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ class SVGDecodedImageData::SVGPageClient final : public PageClient {
virtual void paint_next_frame() override { }
virtual void process_screenshot_requests() override { }
virtual void paint(DevicePixelRect const&, Painting::BackingStore&, Web::PaintOptions = {}) override { }
virtual void schedule_repaint() override { }
virtual bool is_ready_to_paint() const override { return true; }

virtual DisplayListPlayerType display_list_player_type() const override { return m_host_page->client().display_list_player_type(); }
Expand Down
25 changes: 8 additions & 17 deletions Userland/Services/WebContent/PageClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,32 +52,25 @@ PageClient::PageClient(PageHost& owner, u64 id)
, m_backing_store_manager(*this)
{
setup_palette();
}

PageClient::~PageClient() = default;
int refresh_interval = 1000 / 60; // FIXME: Account for the actual refresh rate of the display
m_paint_refresh_timer = Core::Timer::create_repeating(refresh_interval, [] {
Web::HTML::main_thread_event_loop().queue_task_to_update_the_rendering();
});

void PageClient::schedule_repaint()
{
if (m_paint_state != PaintState::Ready) {
m_paint_state = PaintState::PaintWhenReady;
return;
}
m_paint_refresh_timer->start();
}

PageClient::~PageClient() = default;

bool PageClient::is_ready_to_paint() const
{
return m_paint_state == PaintState::Ready;
}

void PageClient::ready_to_paint()
{
auto old_paint_state = exchange(m_paint_state, PaintState::Ready);

if (old_paint_state == PaintState::PaintWhenReady) {
// NOTE: Repainting always has to be scheduled from HTML event loop processing steps
// to make sure style and layout are up-to-date.
Web::HTML::main_thread_event_loop().schedule();
}
m_paint_state = PaintState::Ready;
}

void PageClient::visit_edges(JS::Cell::Visitor& visitor)
Expand Down Expand Up @@ -196,8 +189,6 @@ void PageClient::process_screenshot_requests()

void PageClient::paint_next_frame()
{
process_screenshot_requests();

auto back_store = m_backing_store_manager.back_store();
if (!back_store)
return;
Expand Down
4 changes: 2 additions & 2 deletions Userland/Services/WebContent/PageClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ class PageClient final : public Web::PageClient {
};
static void set_use_skia_painter(UseSkiaPainter);

virtual void schedule_repaint() override;
virtual bool is_ready_to_paint() const override;

virtual Web::Page& page() override { return *m_page; }
Expand Down Expand Up @@ -191,7 +190,6 @@ class PageClient final : public Web::PageClient {
enum class PaintState {
Ready,
WaitingForClient,
PaintWhenReady,
};

PaintState m_paint_state { PaintState::Ready };
Expand All @@ -212,6 +210,8 @@ class PageClient final : public Web::PageClient {
WeakPtr<WebContentConsoleClient> m_top_level_document_console_client;

JS::Handle<JS::GlobalObject> m_console_global_object;

RefPtr<Core::Timer> m_paint_refresh_timer;
};

}
1 change: 0 additions & 1 deletion Userland/Services/WebWorker/PageHost.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ class PageHost final : public Web::PageClient {
virtual void process_screenshot_requests() override {};
virtual void paint(Web::DevicePixelRect const&, Web::Painting::BackingStore&, Web::PaintOptions = {}) override;
virtual void request_file(Web::FileRequest) override;
virtual void schedule_repaint() override {};
virtual bool is_ready_to_paint() const override { return true; }
virtual Web::DisplayListPlayerType display_list_player_type() const override { VERIFY_NOT_REACHED(); }

Expand Down

0 comments on commit 55a4a18

Please sign in to comment.