From 479e269c8bc705f28677088da946f4a747e6d311 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Tue, 14 Mar 2023 13:26:06 +0100 Subject: [PATCH] WebContent: Give paint requests a chance to happen between input events Before this patch, we had an issue where the WebContent process could get backed up with tons of pending input events (especially mouse moves) and have to work through all of those before responding to a paint request from the UI process. This could lead to a situation where we went for a very long time without seeing any visual updates. The approach I've taken here is pretty simple, we basically make a queue of all incoming input events on the WebContent process side, and then process that queue one event at a time, using a zero timer. This is basic, but it allows paint requests to come in between the input events and we do now get more frequent visual updates even during heavy pressure from input events. --- .../WebContent/ConnectionFromClient.cpp | 121 +++++++++++++++++- .../WebContent/ConnectionFromClient.h | 38 +++++- 2 files changed, 151 insertions(+), 8 deletions(-) diff --git a/Userland/Services/WebContent/ConnectionFromClient.cpp b/Userland/Services/WebContent/ConnectionFromClient.cpp index e473217a9f..d3f9ce3195 100644 --- a/Userland/Services/WebContent/ConnectionFromClient.cpp +++ b/Userland/Services/WebContent/ConnectionFromClient.cpp @@ -43,6 +43,7 @@ ConnectionFromClient::ConnectionFromClient(NonnullOwnPtr sock , m_page_host(PageHost::create(*this)) { m_paint_flush_timer = Web::Platform::Timer::create_single_shot(0, [this] { flush_pending_paint_requests(); }); + m_input_event_queue_timer = Web::Platform::Timer::create_single_shot(0, [this] { process_next_input_event(); }); } void ConnectionFromClient::die() @@ -155,39 +156,145 @@ void ConnectionFromClient::flush_pending_paint_requests() m_pending_paint_requests.clear(); } +void ConnectionFromClient::process_next_input_event() +{ + if (m_input_event_queue.is_empty()) + return; + + auto event = m_input_event_queue.dequeue(); + event.visit( + [&](QueuedMouseEvent const& event) { + switch (event.type) { + case QueuedMouseEvent::Type::MouseDown: + report_finished_handling_input_event(page().handle_mousedown( + event.position.to_type(), + event.button, event.buttons, event.modifiers)); + break; + case QueuedMouseEvent::Type::MouseUp: + report_finished_handling_input_event(page().handle_mouseup( + event.position.to_type(), + event.button, event.buttons, event.modifiers)); + break; + case QueuedMouseEvent::Type::MouseMove: + report_finished_handling_input_event(page().handle_mousemove( + event.position.to_type(), + event.buttons, event.modifiers)); + break; + case QueuedMouseEvent::Type::DoubleClick: + report_finished_handling_input_event(page().handle_doubleclick( + event.position.to_type(), + event.button, event.buttons, event.modifiers)); + break; + case QueuedMouseEvent::Type::MouseWheel: + report_finished_handling_input_event(page().handle_mousewheel( + event.position.to_type(), + event.button, event.buttons, event.modifiers, event.wheel_delta_x, event.wheel_delta_y)); + break; + } + }, + [&](QueuedKeyboardEvent const& event) { + switch (event.type) { + case QueuedKeyboardEvent::Type::KeyDown: + report_finished_handling_input_event(page().handle_keydown((KeyCode)event.key, event.modifiers, event.code_point)); + break; + case QueuedKeyboardEvent::Type::KeyUp: + report_finished_handling_input_event(page().handle_keyup((KeyCode)event.key, event.modifiers, event.code_point)); + break; + } + }); + + if (!m_input_event_queue.is_empty()) + m_input_event_queue_timer->start(); +} + void ConnectionFromClient::mouse_down(Gfx::IntPoint position, unsigned int button, unsigned int buttons, unsigned int modifiers) { - report_finished_handling_input_event(page().handle_mousedown(position.to_type(), button, buttons, modifiers)); + enqueue_input_event( + QueuedMouseEvent { + .type = QueuedMouseEvent::Type::MouseDown, + .position = position, + .button = button, + .buttons = buttons, + .modifiers = modifiers, + }); } void ConnectionFromClient::mouse_move(Gfx::IntPoint position, [[maybe_unused]] unsigned int button, unsigned int buttons, unsigned int modifiers) { - report_finished_handling_input_event(page().handle_mousemove(position.to_type(), buttons, modifiers)); + enqueue_input_event( + QueuedMouseEvent { + .type = QueuedMouseEvent::Type::MouseMove, + .position = position, + .button = button, + .buttons = buttons, + .modifiers = modifiers, + }); } void ConnectionFromClient::mouse_up(Gfx::IntPoint position, unsigned int button, unsigned int buttons, unsigned int modifiers) { - report_finished_handling_input_event(page().handle_mouseup(position.to_type(), button, buttons, modifiers)); + enqueue_input_event( + QueuedMouseEvent { + .type = QueuedMouseEvent::Type::MouseUp, + .position = position, + .button = button, + .buttons = buttons, + .modifiers = modifiers, + }); } void ConnectionFromClient::mouse_wheel(Gfx::IntPoint position, unsigned int button, unsigned int buttons, unsigned int modifiers, i32 wheel_delta_x, i32 wheel_delta_y) { - report_finished_handling_input_event(page().handle_mousewheel(position.to_type(), button, buttons, modifiers, wheel_delta_x, wheel_delta_y)); + enqueue_input_event( + QueuedMouseEvent { + .type = QueuedMouseEvent::Type::MouseWheel, + .position = position, + .button = button, + .buttons = buttons, + .modifiers = modifiers, + .wheel_delta_x = wheel_delta_x, + .wheel_delta_y = wheel_delta_y, + }); } void ConnectionFromClient::doubleclick(Gfx::IntPoint position, unsigned int button, unsigned int buttons, unsigned int modifiers) { - report_finished_handling_input_event(page().handle_doubleclick(position.to_type(), button, buttons, modifiers)); + enqueue_input_event( + QueuedMouseEvent { + .type = QueuedMouseEvent::Type::DoubleClick, + .position = position, + .button = button, + .buttons = buttons, + .modifiers = modifiers, + }); } void ConnectionFromClient::key_down(i32 key, unsigned int modifiers, u32 code_point) { - report_finished_handling_input_event(page().handle_keydown((KeyCode)key, modifiers, code_point)); + enqueue_input_event( + QueuedKeyboardEvent { + .type = QueuedKeyboardEvent::Type::KeyDown, + .key = key, + .modifiers = modifiers, + .code_point = code_point, + }); } void ConnectionFromClient::key_up(i32 key, unsigned int modifiers, u32 code_point) { - report_finished_handling_input_event(page().handle_keyup((KeyCode)key, modifiers, code_point)); + enqueue_input_event( + QueuedKeyboardEvent { + .type = QueuedKeyboardEvent::Type::KeyUp, + .key = key, + .modifiers = modifiers, + .code_point = code_point, + }); +} + +void ConnectionFromClient::enqueue_input_event(Variant event) +{ + m_input_event_queue.enqueue(move(event)); + m_input_event_queue_timer->start(); } void ConnectionFromClient::report_finished_handling_input_event(bool event_was_handled) diff --git a/Userland/Services/WebContent/ConnectionFromClient.h b/Userland/Services/WebContent/ConnectionFromClient.h index ffc116ee8a..4d0fe3d134 100644 --- a/Userland/Services/WebContent/ConnectionFromClient.h +++ b/Userland/Services/WebContent/ConnectionFromClient.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2021, Andreas Kling + * Copyright (c) 2018-2023, Andreas Kling * Copyright (c) 2021-2023, Linus Groh * Copyright (c) 2022, Tim Flynn * @@ -9,6 +9,7 @@ #pragma once #include +#include #include #include #include @@ -119,6 +120,41 @@ private: HashMap m_requested_files {}; int last_id { 0 }; + + struct QueuedMouseEvent { + enum class Type { + MouseMove, + MouseDown, + MouseUp, + MouseWheel, + DoubleClick, + }; + Type type {}; + Gfx::IntPoint position {}; + unsigned button {}; + unsigned buttons {}; + unsigned modifiers {}; + int wheel_delta_x {}; + int wheel_delta_y {}; + }; + + struct QueuedKeyboardEvent { + enum class Type { + KeyDown, + KeyUp, + }; + Type type {}; + i32 key {}; + unsigned int modifiers {}; + u32 code_point {}; + }; + + void enqueue_input_event(Variant); + void process_next_input_event(); + + Queue> m_input_event_queue; + + RefPtr m_input_event_queue_timer; }; }