mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +01:00
compositor: Tick animations for an entire WebView at once (#36662)
Previously, when processing animations, the compositor would sent a tick message to each pipeline. This is an issue because now the `ScriptThread` always processes rendering updates for all `Document`s in order to ensure properly ordering. This change makes it so that tick messages are sent for an entire WebView. This means that each `ScriptThread` will always receive a single tick for every time that animations are processed, no matter how many frames are animating. This is the first step toward a refresh driver. In addition, we discard the idea of ticking animation only for animations and or only for request animation frame callbacks. The `ScriptThread` can no longer make this distinction due to the specification and the compositor shouldn't either. This should not really change observable behavior, but should make Servo more efficient when more than a single frame in a `ScriptThread` is animting at once. Testing: This is covered by existing WPT tests as it mainly just improve animation efficiency in a particular case. Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
parent
3793936f05
commit
cbc363bedd
9 changed files with 134 additions and 172 deletions
|
@ -112,13 +112,12 @@ use compositing_traits::{
|
|||
CompositorMsg, CompositorProxy, SendableFrameTree, WebrenderExternalImageRegistry,
|
||||
};
|
||||
use constellation_traits::{
|
||||
AnimationTickType, AuxiliaryWebViewCreationRequest, AuxiliaryWebViewCreationResponse,
|
||||
BroadcastMsg, DocumentState, EmbedderToConstellationMessage, IFrameLoadInfo,
|
||||
IFrameLoadInfoWithData, IFrameSandboxState, IFrameSizeMsg, Job, LoadData, LoadOrigin, LogEntry,
|
||||
MessagePortMsg, NavigationHistoryBehavior, PaintMetricEvent, PortMessageTask, SWManagerMsg,
|
||||
SWManagerSenders, ScriptToConstellationChan, ScriptToConstellationMessage, ScrollState,
|
||||
ServiceWorkerManagerFactory, ServiceWorkerMsg, StructuredSerializedData, TraversalDirection,
|
||||
WindowSizeType,
|
||||
AuxiliaryWebViewCreationRequest, AuxiliaryWebViewCreationResponse, BroadcastMsg, DocumentState,
|
||||
EmbedderToConstellationMessage, IFrameLoadInfo, IFrameLoadInfoWithData, IFrameSandboxState,
|
||||
IFrameSizeMsg, Job, LoadData, LoadOrigin, LogEntry, MessagePortMsg, NavigationHistoryBehavior,
|
||||
PaintMetricEvent, PortMessageTask, SWManagerMsg, SWManagerSenders, ScriptToConstellationChan,
|
||||
ScriptToConstellationMessage, ScrollState, ServiceWorkerManagerFactory, ServiceWorkerMsg,
|
||||
StructuredSerializedData, TraversalDirection, WindowSizeType,
|
||||
};
|
||||
use crossbeam_channel::{Receiver, Select, Sender, unbounded};
|
||||
use devtools_traits::{
|
||||
|
@ -1398,8 +1397,8 @@ where
|
|||
EmbedderToConstellationMessage::ThemeChange(theme) => {
|
||||
self.handle_theme_change(theme);
|
||||
},
|
||||
EmbedderToConstellationMessage::TickAnimation(pipeline_id, tick_type) => {
|
||||
self.handle_tick_animation(pipeline_id, tick_type)
|
||||
EmbedderToConstellationMessage::TickAnimation(webview_ids) => {
|
||||
self.handle_tick_animation(webview_ids)
|
||||
},
|
||||
EmbedderToConstellationMessage::WebDriverCommand(command) => {
|
||||
self.handle_webdriver_msg(command);
|
||||
|
@ -3528,15 +3527,24 @@ where
|
|||
feature = "tracing",
|
||||
tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace")
|
||||
)]
|
||||
fn handle_tick_animation(&mut self, pipeline_id: PipelineId, tick_type: AnimationTickType) {
|
||||
let pipeline = match self.pipelines.get(&pipeline_id) {
|
||||
Some(pipeline) => pipeline,
|
||||
None => return warn!("{}: Got script tick after closure", pipeline_id),
|
||||
};
|
||||
fn handle_tick_animation(&mut self, webview_ids: Vec<WebViewId>) {
|
||||
let mut animating_event_loops = HashSet::new();
|
||||
|
||||
let message = ScriptThreadMessage::TickAllAnimations(pipeline_id, tick_type);
|
||||
if let Err(e) = pipeline.event_loop.send(message) {
|
||||
self.handle_send_error(pipeline_id, e);
|
||||
for webview_id in webview_ids.iter() {
|
||||
for browsing_context in self.fully_active_browsing_contexts_iter(*webview_id) {
|
||||
let Some(pipeline) = self.pipelines.get(&browsing_context.pipeline_id) else {
|
||||
continue;
|
||||
};
|
||||
animating_event_loops.insert(pipeline.event_loop.clone());
|
||||
}
|
||||
}
|
||||
|
||||
for event_loop in animating_event_loops {
|
||||
// No error handling here. It's unclear what to do when this fails as the error isn't associated
|
||||
// with a particular pipeline. In addition, the danger of not progressing animations is pretty
|
||||
// low, so it's probably safe to ignore this error and handle the crashed ScriptThread on
|
||||
// some other message.
|
||||
let _ = event_loop.send(ScriptThreadMessage::TickAllAnimations(webview_ids.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,17 +6,36 @@
|
|||
//! view of a script thread. When an `EventLoop` is dropped, an `ExitScriptThread`
|
||||
//! message is sent to the script thread, asking it to shut down.
|
||||
|
||||
use std::hash::Hash;
|
||||
use std::marker::PhantomData;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use ipc_channel::Error;
|
||||
use ipc_channel::ipc::IpcSender;
|
||||
use script_traits::ScriptThreadMessage;
|
||||
|
||||
static CURRENT_EVENT_LOOP_ID: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#event-loop>
|
||||
pub struct EventLoop {
|
||||
script_chan: IpcSender<ScriptThreadMessage>,
|
||||
dont_send_or_sync: PhantomData<Rc<()>>,
|
||||
id: usize,
|
||||
}
|
||||
|
||||
impl PartialEq for EventLoop {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.id == other.id
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for EventLoop {}
|
||||
|
||||
impl Hash for EventLoop {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.id.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for EventLoop {
|
||||
|
@ -28,9 +47,11 @@ impl Drop for EventLoop {
|
|||
impl EventLoop {
|
||||
/// Create a new event loop from the channel to its script thread.
|
||||
pub fn new(script_chan: IpcSender<ScriptThreadMessage>) -> Rc<EventLoop> {
|
||||
let id = CURRENT_EVENT_LOOP_ID.fetch_add(1, Ordering::Relaxed);
|
||||
Rc::new(EventLoop {
|
||||
script_chan,
|
||||
dont_send_or_sync: PhantomData,
|
||||
id,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue