libservo: Move animation tracking from WindowMethods to delegates (#36400)

This changes removes animation tracking from the `WindowMethods` trait
and moves it to `ServoDelegate` and `WebViewDelegate`.

- Animation changes per-`WebView` are now triggered in the compositor
  only when the value is updated there, rather than right after ticking
  animations.
- Both `WebView` and `Servo` now expose an `animation()` method, so
  tracking animation state actually becomes unecessary in many cases,
  such as that of desktop servoshell, which can just read the value
  when the event loop spins.

Testing: No tests necessary as the API layer is still untested. Later,
tests will be added for the `WebView` API and this can be tested then.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
Martin Robinson 2025-04-09 21:41:53 +02:00 committed by GitHub
parent 935db71183
commit 2fe57cc2a2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 153 additions and 125 deletions

View file

@ -58,7 +58,7 @@ use webrender_api::{
use crate::InitialCompositorState;
use crate::webview::{UnknownWebView, WebView};
use crate::webview_manager::WebViewManager;
use crate::windowing::{self, WebRenderDebugOption, WindowMethods};
use crate::windowing::{WebRenderDebugOption, WindowMethods};
#[derive(Debug, PartialEq)]
enum UnableToComposite {
@ -258,22 +258,20 @@ impl PipelineDetails {
self.animation_callbacks_running
}
pub(crate) fn tick_animations(&self, compositor: &IOCompositor) -> bool {
let animation_callbacks_running = self.animation_callbacks_running;
let animations_running = self.animations_running;
if !animation_callbacks_running && !animations_running {
return false;
}
pub(crate) fn animating(&self) -> bool {
!self.throttled && (self.animation_callbacks_running || self.animations_running)
}
if self.throttled {
return false;
pub(crate) fn tick_animations(&self, compositor: &IOCompositor) {
if !self.animating() {
return;
}
let mut tick_type = AnimationTickType::empty();
if animations_running {
if self.animations_running {
tick_type.insert(AnimationTickType::CSS_ANIMATIONS_AND_TRANSITIONS);
}
if animation_callbacks_running {
if self.animation_callbacks_running {
tick_type.insert(AnimationTickType::REQUEST_ANIMATION_FRAME);
}
@ -281,7 +279,6 @@ impl PipelineDetails {
if let Err(e) = compositor.global.borrow().constellation_sender.send(msg) {
warn!("Sending tick to constellation failed ({:?}).", e);
}
true
}
}
@ -490,6 +487,17 @@ impl IOCompositor {
}
}
pub fn webxr_running(&self) -> bool {
#[cfg(feature = "webxr")]
{
self.global.borrow().webxr_main_thread.running()
}
#[cfg(not(feature = "webxr"))]
{
false
}
}
fn set_needs_repaint(&self, reason: RepaintReason) {
let mut needs_repaint = self.needs_repaint.get();
needs_repaint.insert(reason);
@ -1313,23 +1321,9 @@ impl IOCompositor {
}
self.last_animation_tick = Instant::now();
#[cfg(feature = "webxr")]
let webxr_running = self.global.borrow().webxr_main_thread.running();
#[cfg(not(feature = "webxr"))]
let webxr_running = false;
let any_webviews_animating = !self
.webviews
.iter()
.all(|webview| !webview.tick_all_animations(self));
let animation_state = if !any_webviews_animating && !webxr_running {
windowing::AnimationState::Idle
} else {
windowing::AnimationState::Animating
};
self.window.set_animation_state(animation_state);
for webview in self.webviews.iter() {
webview.tick_all_animations(self);
}
}
pub(crate) fn device_pixels_per_page_pixel(&self) -> Scale<f32, CSSPixel, DevicePixel> {

View file

@ -217,37 +217,41 @@ impl WebView {
})
}
/// Sets or unsets the animations-running flag for the given pipeline, and schedules a
/// recomposite if necessary. Returns true if the pipeline is throttled.
/// Sets or unsets the animations-running flag for the given pipeline. Returns true if
/// the pipeline is throttled.
pub(crate) fn change_running_animations_state(
&mut self,
pipeline_id: PipelineId,
animation_state: AnimationState,
) -> bool {
let pipeline_details = self.ensure_pipeline_details(pipeline_id);
match animation_state {
AnimationState::AnimationsPresent => {
pipeline_details.animations_running = true;
},
AnimationState::AnimationCallbacksPresent => {
pipeline_details.animation_callbacks_running = true;
},
AnimationState::NoAnimationsPresent => {
pipeline_details.animations_running = false;
},
AnimationState::NoAnimationCallbacksPresent => {
pipeline_details.animation_callbacks_running = false;
},
}
pipeline_details.throttled
let throttled = {
let pipeline_details = self.ensure_pipeline_details(pipeline_id);
match animation_state {
AnimationState::AnimationsPresent => {
pipeline_details.animations_running = true;
},
AnimationState::AnimationCallbacksPresent => {
pipeline_details.animation_callbacks_running = true;
},
AnimationState::NoAnimationsPresent => {
pipeline_details.animations_running = false;
},
AnimationState::NoAnimationCallbacksPresent => {
pipeline_details.animation_callbacks_running = false;
},
}
pipeline_details.throttled
};
let animating = self.pipelines.values().any(PipelineDetails::animating);
self.renderer_webview.set_animating(animating);
throttled
}
pub(crate) fn tick_all_animations(&self, compositor: &IOCompositor) -> bool {
let mut ticked_any = false;
pub(crate) fn tick_all_animations(&self, compositor: &IOCompositor) {
for pipeline_details in self.pipelines.values() {
ticked_any = pipeline_details.tick_animations(compositor) || ticked_any;
pipeline_details.tick_animations(compositor)
}
ticked_any
}
pub(crate) fn tick_animations_for_pipeline(

View file

@ -4,8 +4,6 @@
//! Abstract windowing methods. The concrete implementations of these can be found in `platform/`.
use std::fmt::Debug;
use embedder_traits::{EventLoopWaker, MouseButton};
use euclid::Scale;
use net::protocols::ProtocolRegistry;
@ -27,12 +25,6 @@ pub enum WebRenderDebugOption {
RenderTargetDebug,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum AnimationState {
Idle,
Animating,
}
// TODO: this trait assumes that the window is responsible
// for creating the GL context, making it current, buffer
// swapping, etc. Really that should all be done by surfman.
@ -40,12 +32,6 @@ pub trait WindowMethods {
/// Get the HighDPI factor of the native window, the screen and the framebuffer.
/// TODO(martin): Move this to `RendererWebView` when possible.
fn hidpi_factor(&self) -> Scale<f32, DeviceIndependentPixel, DevicePixel>;
/// Set whether the application is currently animating.
/// Typically, when animations are active, the window
/// will want to avoid blocking on UI events, and just
/// run the event loop at the vsync interval.
/// TODO(martin): Move this to `RendererWebView` when possible.
fn set_animation_state(&self, _state: AnimationState);
}
pub trait EmbedderMethods {