mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 23:50:19 +00:00
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:
parent
1b127ac082
commit
5c6b879715
Notes:
github-actions[bot]
2024-10-25 08:22:14 +00:00
Author: https://github.com/awesomekling Commit: https://github.com/LadybirdBrowser/ladybird/commit/5c6b8797159 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/1956 Reviewed-by: https://github.com/gmta ✅
2 changed files with 146 additions and 134 deletions
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Reference in a new issue