mirror of
https://github.com/servo/servo.git
synced 2025-10-01 00:59:15 +01:00
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:
parent
935db71183
commit
2fe57cc2a2
15 changed files with 153 additions and 125 deletions
|
@ -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> {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::cell::RefCell;
|
||||
use std::error::Error;
|
||||
use std::rc::Rc;
|
||||
|
||||
use compositing::windowing::{AnimationState, EmbedderMethods, WindowMethods};
|
||||
use compositing::windowing::{EmbedderMethods, WindowMethods};
|
||||
use euclid::{Point2D, Scale, Size2D};
|
||||
use servo::{RenderingContext, Servo, TouchEventType, WebView, WindowRenderingContext};
|
||||
use servo_geometry::DeviceIndependentPixel;
|
||||
|
@ -236,15 +236,11 @@ impl embedder_traits::EventLoopWaker for Waker {
|
|||
|
||||
struct WindowDelegate {
|
||||
window: Window,
|
||||
animation_state: Cell<AnimationState>,
|
||||
}
|
||||
|
||||
impl WindowDelegate {
|
||||
fn new(window: Window) -> Self {
|
||||
Self {
|
||||
window,
|
||||
animation_state: Cell::new(AnimationState::Idle),
|
||||
}
|
||||
Self { window }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -252,10 +248,6 @@ impl WindowMethods for WindowDelegate {
|
|||
fn hidpi_factor(&self) -> Scale<f32, DeviceIndependentPixel, DevicePixel> {
|
||||
Scale::new(self.window.scale_factor() as f32)
|
||||
}
|
||||
|
||||
fn set_animation_state(&self, state: compositing::windowing::AnimationState) {
|
||||
self.animation_state.set(state);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn winit_size_to_euclid_size<T>(size: PhysicalSize<T>) -> Size2D<T, DevicePixel> {
|
||||
|
|
|
@ -210,6 +210,8 @@ pub struct Servo {
|
|||
/// and deinitialization of the JS Engine. Multiprocess Servo instances have their
|
||||
/// own instance that exists in the content process instead.
|
||||
_js_engine_setup: Option<JSEngineSetup>,
|
||||
/// Whether or not any WebView in this instance is animating or WebXR is enabled.
|
||||
animating: Cell<bool>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -507,6 +509,7 @@ impl Servo {
|
|||
webviews: Default::default(),
|
||||
servo_errors: ServoErrorChannel::default(),
|
||||
_js_engine_setup: js_engine_setup,
|
||||
animating: Cell::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -518,6 +521,15 @@ impl Servo {
|
|||
*self.delegate.borrow_mut() = delegate;
|
||||
}
|
||||
|
||||
/// Whether or not any [`WebView`] of this Servo instance has animating content, such as a CSS
|
||||
/// animation or transition or is running `requestAnimationFrame` callbacks. In addition, this
|
||||
/// returns true if WebXR content is running. This indicates that the embedding application
|
||||
/// should be spinning the Servo event loop on regular intervals in order to trigger animation
|
||||
/// updates.
|
||||
pub fn animating(&self) -> bool {
|
||||
self.animating.get()
|
||||
}
|
||||
|
||||
/// **EXPERIMENTAL:** Intialize GL accelerated media playback. This currently only works on a limited number
|
||||
/// of platforms. This should be run *before* calling [`Servo::new`] and creating the first [`WebView`].
|
||||
pub fn initialize_gl_accelerated_media(display: NativeDisplay, api: GlApi, context: GlContext) {
|
||||
|
@ -555,6 +567,7 @@ impl Servo {
|
|||
|
||||
self.compositor.borrow_mut().perform_updates();
|
||||
self.send_new_frame_ready_messages();
|
||||
self.send_animating_changed_messages();
|
||||
self.handle_delegate_errors();
|
||||
self.clean_up_destroyed_webview_handles();
|
||||
|
||||
|
@ -580,6 +593,19 @@ impl Servo {
|
|||
}
|
||||
}
|
||||
|
||||
fn send_animating_changed_messages(&self) {
|
||||
let animating = self.compositor.borrow().webxr_running() ||
|
||||
self.webviews
|
||||
.borrow()
|
||||
.values()
|
||||
.filter_map(WebView::from_weak_handle)
|
||||
.any(|webview| webview.animating());
|
||||
if animating != self.animating.get() {
|
||||
self.animating.set(animating);
|
||||
self.delegate().notify_animating_changed(animating);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_delegate_errors(&self) {
|
||||
while let Some(error) = self.servo_errors.try_recv() {
|
||||
self.delegate().notify_error(self, error);
|
||||
|
|
|
@ -28,6 +28,11 @@ pub trait ServoDelegate {
|
|||
/// Request a DevTools connection from a DevTools client. Typically an embedder application
|
||||
/// will show a permissions prompt when this happens to confirm a connection is allowed.
|
||||
fn request_devtools_connection(&self, _servo: &Servo, _request: AllowOrDenyRequest) {}
|
||||
/// Any [`WebView`] in this Servo instance has either started to animate or WebXR is
|
||||
/// running. When a [`WebView`] is animating, it is up to the embedding application
|
||||
/// ensure that `Servo::spin_event_loop` is called at regular intervals in order to
|
||||
/// update the painted contents of the [`WebView`].
|
||||
fn notify_animating_changed(&self, _animating: bool) {}
|
||||
/// Triggered when Servo will load a web (HTTP/HTTPS) resource. The load may be
|
||||
/// intercepted and alternate contents can be loaded by the client by calling
|
||||
/// [`WebResourceLoad::intercept`]. If not handled, the load will continue as normal.
|
||||
|
|
|
@ -81,6 +81,7 @@ pub(crate) struct WebViewInner {
|
|||
page_title: Option<String>,
|
||||
favicon_url: Option<Url>,
|
||||
focused: bool,
|
||||
animating: bool,
|
||||
cursor: Cursor,
|
||||
}
|
||||
|
||||
|
@ -110,6 +111,7 @@ impl WebView {
|
|||
page_title: None,
|
||||
favicon_url: None,
|
||||
focused: false,
|
||||
animating: false,
|
||||
cursor: Cursor::Pointer,
|
||||
})));
|
||||
|
||||
|
@ -265,6 +267,22 @@ impl WebView {
|
|||
.send(EmbedderToConstellationMessage::BlurWebView);
|
||||
}
|
||||
|
||||
/// Whether or not this [`WebView`] has animating content, such as a CSS animation or
|
||||
/// transition or is running `requestAnimationFrame` callbacks. This indicates that the
|
||||
/// embedding application should be spinning the Servo event loop on regular intervals
|
||||
/// in order to trigger animation updates.
|
||||
pub fn animating(self) -> bool {
|
||||
self.inner().animating
|
||||
}
|
||||
|
||||
pub(crate) fn set_animating(self, new_value: bool) {
|
||||
if self.inner().animating == new_value {
|
||||
return;
|
||||
}
|
||||
self.inner_mut().animating = new_value;
|
||||
self.delegate().notify_animating_changed(self, new_value);
|
||||
}
|
||||
|
||||
pub fn rect(&self) -> DeviceRect {
|
||||
self.inner().rect
|
||||
}
|
||||
|
@ -475,12 +493,18 @@ struct ServoRendererWebView {
|
|||
}
|
||||
|
||||
impl RendererWebView for ServoRendererWebView {
|
||||
fn id(&self) -> WebViewId {
|
||||
self.id
|
||||
}
|
||||
|
||||
fn screen_geometry(&self) -> Option<ScreenGeometry> {
|
||||
let webview = WebView::from_weak_handle(&self.weak_handle)?;
|
||||
webview.delegate().screen_geometry(webview)
|
||||
}
|
||||
|
||||
fn id(&self) -> WebViewId {
|
||||
self.id
|
||||
fn set_animating(&self, new_value: bool) {
|
||||
if let Some(webview) = WebView::from_weak_handle(&self.weak_handle) {
|
||||
webview.set_animating(new_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -375,6 +375,11 @@ pub trait WebViewDelegate {
|
|||
/// This [`WebView`] has either become focused or lost focus. Whether or not the
|
||||
/// [`WebView`] is focused can be accessed via [`WebView::focused`].
|
||||
fn notify_focus_changed(&self, _webview: WebView, _focused: bool) {}
|
||||
/// This [`WebView`] has either started to animate or stopped animating. When a
|
||||
/// [`WebView`] is animating, it is up to the embedding application ensure that
|
||||
/// `Servo::spin_event_loop` is called at regular intervals in order to update the
|
||||
/// painted contents of the [`WebView`].
|
||||
fn notify_animating_changed(&self, _webview: WebView, _animating: bool) {}
|
||||
/// The `LoadStatus` of the currently loading or loaded page in this [`WebView`] has changed. The new
|
||||
/// status can accessed via [`WebView::load_status`].
|
||||
fn notify_load_status_changed(&self, _webview: WebView, _status: LoadStatus) {}
|
||||
|
|
|
@ -590,4 +590,5 @@ impl From<SerializableImageData> for ImageData {
|
|||
pub trait RendererWebView {
|
||||
fn id(&self) -> WebViewId;
|
||||
fn screen_geometry(&self) -> Option<ScreenGeometry>;
|
||||
fn set_animating(&self, new_value: bool);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue