LibWeb: Don't allocate a new HeapFunction 60 times per second

We can reuse the same HeapFunction when queueing up a rendering task
on the HTML event loop. No need to create extra work for the garbage
collector like this.
This commit is contained in:
Andreas Kling 2024-10-25 09:56:48 +02:00 committed by Andreas Kling
parent 1b127ac082
commit 5c6b879715
Notes: github-actions[bot] 2024-10-25 08:22:14 +00:00
2 changed files with 146 additions and 134 deletions

View file

@ -32,6 +32,10 @@ EventLoop::EventLoop(Type type)
{ {
m_task_queue = heap().allocate_without_realm<TaskQueue>(*this); m_task_queue = heap().allocate_without_realm<TaskQueue>(*this);
m_microtask_queue = heap().allocate_without_realm<TaskQueue>(*this); m_microtask_queue = heap().allocate_without_realm<TaskQueue>(*this);
m_rendering_task_function = JS::create_heap_function(heap(), [this] {
update_the_rendering();
});
} }
EventLoop::~EventLoop() = default; EventLoop::~EventLoop() = default;
@ -43,6 +47,7 @@ void EventLoop::visit_edges(Visitor& visitor)
visitor.visit(m_microtask_queue); visitor.visit(m_microtask_queue);
visitor.visit(m_currently_running_task); visitor.visit(m_currently_running_task);
visitor.visit(m_backup_incumbent_settings_object_stack); visitor.visit(m_backup_incumbent_settings_object_stack);
visitor.visit(m_rendering_task_function);
} }
void EventLoop::schedule() void EventLoop::schedule()
@ -239,158 +244,161 @@ void EventLoop::queue_task_to_update_the_rendering()
if (document->is_decoded_svg()) if (document->is_decoded_svg())
continue; continue;
queue_global_task(Task::Source::Rendering, *navigable->active_window(), JS::create_heap_function(navigable->heap(), [this] mutable { queue_global_task(Task::Source::Rendering, *navigable->active_window(), *m_rendering_task_function);
VERIFY(!m_is_running_rendering_task); }
m_is_running_rendering_task = true; }
ScopeGuard const guard = [this] {
m_is_running_rendering_task = false;
};
// FIXME: 1. Let frameTimestamp be eventLoop's last render opportunity time. void EventLoop::update_the_rendering()
{
VERIFY(!m_is_running_rendering_task);
m_is_running_rendering_task = true;
ScopeGuard const guard = [this] {
m_is_running_rendering_task = false;
};
// FIXME: 2. Let docs be all fully active Document objects whose relevant agent's event loop is eventLoop, sorted arbitrarily except that the following conditions must be met: // FIXME: 1. Let frameTimestamp be eventLoop's last render opportunity time.
auto docs = documents_in_this_event_loop();
docs.remove_all_matching([&](auto& document) {
return !document->is_fully_active();
});
// 3. Filter non-renderable documents: Remove from docs any Document object doc for which any of the following are true: // FIXME: 2. Let docs be all fully active Document objects whose relevant agent's event loop is eventLoop, sorted arbitrarily except that the following conditions must be met:
docs.remove_all_matching([&](auto const& document) { auto docs = documents_in_this_event_loop();
auto navigable = document->navigable(); docs.remove_all_matching([&](auto& document) {
if (!navigable) return !document->is_fully_active();
return true; });
// FIXME: doc is render-blocked; // 3. Filter non-renderable documents: Remove from docs any Document object doc for which any of the following are true:
docs.remove_all_matching([&](auto const& document) {
auto navigable = document->navigable();
if (!navigable)
return true;
// doc's visibility state is "hidden"; // FIXME: doc is render-blocked;
if (document->visibility_state() == "hidden"sv)
return true;
// FIXME: doc's rendering is suppressed for view transitions; or // doc's visibility state is "hidden";
if (document->visibility_state() == "hidden"sv)
return true;
// doc's node navigable doesn't currently have a rendering opportunity. // FIXME: doc's rendering is suppressed for view transitions; or
if (!navigable->has_a_rendering_opportunity())
return true;
return false; // doc's node navigable doesn't currently have a rendering opportunity.
}); if (!navigable->has_a_rendering_opportunity())
return true;
// FIXME: 4. Unnecessary rendering: Remove from docs any Document object doc for which all of the following are true: return false;
});
// FIXME: 5. Remove from docs all Document objects for which the user agent believes that it's preferable to skip updating the rendering for other reasons. // FIXME: 4. Unnecessary rendering: Remove from docs any Document object doc for which all of the following are true:
// FIXME: 6. For each doc of docs, reveal doc. // FIXME: 5. Remove from docs all Document objects for which the user agent believes that it's preferable to skip updating the rendering for other reasons.
// FIXME: 7. For each doc of docs, flush autofocus candidates for doc if its node navigable is a top-level traversable. // FIXME: 6. For each doc of docs, reveal doc.
// 8. For each doc of docs, run the resize steps for doc. [CSSOMVIEW] // FIXME: 7. For each doc of docs, flush autofocus candidates for doc if its node navigable is a top-level traversable.
for (auto& document : docs) {
document->run_the_resize_steps(); // 8. For each doc of docs, run the resize steps for doc. [CSSOMVIEW]
for (auto& document : docs) {
document->run_the_resize_steps();
}
// 9. For each doc of docs, run the scroll steps for doc. [CSSOMVIEW]
for (auto& document : docs) {
document->run_the_scroll_steps();
}
// 10. For each doc of docs, evaluate media queries and report changes for doc. [CSSOMVIEW]
for (auto& document : docs) {
document->evaluate_media_queries_and_report_changes();
}
// 11. For each doc of docs, update animations and send events for doc, passing in relative high resolution time given frameTimestamp and doc's relevant global object as the timestamp [WEBANIMATIONS]
for (auto& document : docs) {
document->update_animations_and_send_events(document->window()->performance()->now());
};
// FIXME: 12. For each doc of docs, run the fullscreen steps for doc. [FULLSCREEN]
// FIXME: 13. For each doc of docs, if the user agent detects that the backing storage associated with a CanvasRenderingContext2D or an OffscreenCanvasRenderingContext2D, context, has been lost, then it must run the context lost steps for each such context:
// 14. For each doc of docs, run the animation frame callbacks for doc, passing in the relative high resolution time given frameTimestamp and doc's relevant global object as the timestamp.
auto now = HighResolutionTime::unsafe_shared_current_time();
for (auto& document : docs) {
run_animation_frame_callbacks(*document, now);
}
// FIXME: 15. Let unsafeStyleAndLayoutStartTime be the unsafe shared current time.
// 16. For each doc of docs:
for (auto& document : docs) {
// 1. Let resizeObserverDepth be 0.
size_t resize_observer_depth = 0;
// 2. While true:
while (true) {
// 1. Recalculate styles and update layout for doc.
// NOTE: Recalculation of styles is handled by update_layout()
document->update_layout();
// FIXME: 2. Let hadInitialVisibleContentVisibilityDetermination be false.
// FIXME: 3. For each element element with 'auto' used value of 'content-visibility':
// FIXME: 4. If hadInitialVisibleContentVisibilityDetermination is true, then continue.
// 5. Gather active resize observations at depth resizeObserverDepth for doc.
document->gather_active_observations_at_depth(resize_observer_depth);
// 6. If doc has active resize observations:
if (document->has_active_resize_observations()) {
// 1. Set resizeObserverDepth to the result of broadcasting active resize observations given doc.
resize_observer_depth = document->broadcast_active_resize_observations();
// 2. Continue.
continue;
} }
// 9. For each doc of docs, run the scroll steps for doc. [CSSOMVIEW] // 7. Otherwise, break.
for (auto& document : docs) { break;
document->run_the_scroll_steps(); }
// 3. If doc has skipped resize observations, then deliver resize loop error given doc.
if (document->has_skipped_resize_observations()) {
// FIXME: Deliver resize loop error.
}
}
// FIXME: 17. For each doc of docs, if the focused area of doc is not a focusable area, then run the focusing steps for doc's viewport, and set doc's relevant global object's navigation API's focus changed during ongoing navigation to false.
// FIXME: 18. For each doc of docs, perform pending transition operations for doc. [CSSVIEWTRANSITIONS]
// 19. For each doc of docs, run the update intersection observations steps for doc, passing in the relative high resolution time given now and doc's relevant global object as the timestamp. [INTERSECTIONOBSERVER]
for (auto& document : docs) {
document->run_the_update_intersection_observations_steps(now);
}
// FIXME: 20. For each doc of docs, record rendering time for doc given unsafeStyleAndLayoutStartTime.
// FIXME: 21. For each doc of docs, mark paint timing for doc.
// 22. For each doc of docs, update the rendering or user interface of doc and its node navigable to reflect the current state.
for (auto& document : docs) {
document->page().client().process_screenshot_requests();
auto navigable = document->navigable();
if (navigable && document->needs_repaint()) {
auto* browsing_context = document->browsing_context();
auto& page = browsing_context->page();
if (navigable->is_traversable()) {
VERIFY(page.client().is_ready_to_paint());
page.client().paint_next_frame();
} }
}
}
// 10. For each doc of docs, evaluate media queries and report changes for doc. [CSSOMVIEW] // 23. For each doc of docs, process top layer removals given doc.
for (auto& document : docs) { for (auto& document : docs) {
document->evaluate_media_queries_and_report_changes(); document->process_top_layer_removals();
} }
// 11. For each doc of docs, update animations and send events for doc, passing in relative high resolution time given frameTimestamp and doc's relevant global object as the timestamp [WEBANIMATIONS] for (auto& document : docs) {
for (auto& document : docs) { if (document->readiness() == HTML::DocumentReadyState::Complete && document->style_computer().number_of_css_font_faces_with_loading_in_progress() == 0) {
document->update_animations_and_send_events(document->window()->performance()->now()); HTML::TemporaryExecutionContext context(HTML::relevant_settings_object(*document), HTML::TemporaryExecutionContext::CallbacksEnabled::Yes);
}; document->fonts()->resolve_ready_promise();
}
// FIXME: 12. For each doc of docs, run the fullscreen steps for doc. [FULLSCREEN]
// FIXME: 13. For each doc of docs, if the user agent detects that the backing storage associated with a CanvasRenderingContext2D or an OffscreenCanvasRenderingContext2D, context, has been lost, then it must run the context lost steps for each such context:
// 14. For each doc of docs, run the animation frame callbacks for doc, passing in the relative high resolution time given frameTimestamp and doc's relevant global object as the timestamp.
auto now = HighResolutionTime::unsafe_shared_current_time();
for (auto& document : docs) {
run_animation_frame_callbacks(*document, now);
}
// FIXME: 15. Let unsafeStyleAndLayoutStartTime be the unsafe shared current time.
// 16. For each doc of docs:
for (auto& document : docs) {
// 1. Let resizeObserverDepth be 0.
size_t resize_observer_depth = 0;
// 2. While true:
while (true) {
// 1. Recalculate styles and update layout for doc.
// NOTE: Recalculation of styles is handled by update_layout()
document->update_layout();
// FIXME: 2. Let hadInitialVisibleContentVisibilityDetermination be false.
// FIXME: 3. For each element element with 'auto' used value of 'content-visibility':
// FIXME: 4. If hadInitialVisibleContentVisibilityDetermination is true, then continue.
// 5. Gather active resize observations at depth resizeObserverDepth for doc.
document->gather_active_observations_at_depth(resize_observer_depth);
// 6. If doc has active resize observations:
if (document->has_active_resize_observations()) {
// 1. Set resizeObserverDepth to the result of broadcasting active resize observations given doc.
resize_observer_depth = document->broadcast_active_resize_observations();
// 2. Continue.
continue;
}
// 7. Otherwise, break.
break;
}
// 3. If doc has skipped resize observations, then deliver resize loop error given doc.
if (document->has_skipped_resize_observations()) {
// FIXME: Deliver resize loop error.
}
}
// FIXME: 17. For each doc of docs, if the focused area of doc is not a focusable area, then run the focusing steps for doc's viewport, and set doc's relevant global object's navigation API's focus changed during ongoing navigation to false.
// FIXME: 18. For each doc of docs, perform pending transition operations for doc. [CSSVIEWTRANSITIONS]
// 19. For each doc of docs, run the update intersection observations steps for doc, passing in the relative high resolution time given now and doc's relevant global object as the timestamp. [INTERSECTIONOBSERVER]
for (auto& document : docs) {
document->run_the_update_intersection_observations_steps(now);
}
// FIXME: 20. For each doc of docs, record rendering time for doc given unsafeStyleAndLayoutStartTime.
// FIXME: 21. For each doc of docs, mark paint timing for doc.
// 22. For each doc of docs, update the rendering or user interface of doc and its node navigable to reflect the current state.
for (auto& document : docs) {
document->page().client().process_screenshot_requests();
auto navigable = document->navigable();
if (navigable && document->needs_repaint()) {
auto* browsing_context = document->browsing_context();
auto& page = browsing_context->page();
if (navigable->is_traversable()) {
VERIFY(page.client().is_ready_to_paint());
page.client().paint_next_frame();
}
}
}
// 23. For each doc of docs, process top layer removals given doc.
for (auto& document : docs) {
document->process_top_layer_removals();
}
for (auto& document : docs) {
if (document->readiness() == HTML::DocumentReadyState::Complete && document->style_computer().number_of_css_font_faces_with_loading_in_progress() == 0) {
HTML::TemporaryExecutionContext context(HTML::relevant_settings_object(*document), HTML::TemporaryExecutionContext::CallbacksEnabled::Yes);
document->fonts()->resolve_ready_promise();
}
}
}));
} }
} }

View file

@ -81,6 +81,8 @@ private:
virtual void visit_edges(Visitor&) override; virtual void visit_edges(Visitor&) override;
void update_the_rendering();
Type m_type { Type::Window }; Type m_type { Type::Window };
JS::GCPtr<TaskQueue> m_task_queue; JS::GCPtr<TaskQueue> m_task_queue;
@ -116,6 +118,8 @@ private:
bool m_skip_event_loop_processing_steps { false }; bool m_skip_event_loop_processing_steps { false };
bool m_is_running_rendering_task { false }; bool m_is_running_rendering_task { false };
JS::GCPtr<JS::HeapFunction<void()>> m_rendering_task_function;
}; };
EventLoop& main_thread_event_loop(); EventLoop& main_thread_event_loop();