mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
compositor: Add an initial RefreshDriver (#37169)
This adds a *very* basic implementation of the `RefreshDriver` concept to the Servo renderer. The initial idea is that controls the frequency of display during animations. It eliminates the "slowdown" workaround for WPT tests and now Servo animations don't move faster than 120 FPS (observed to be slower in practice). This establishes a base change which will be used to implement non-display-list-producing layouts in a followup change. Fixes #3406. (though much more work remains) Testing: Covered by existing WPT tests. Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
parent
801ac9e22a
commit
9dc1391bef
6 changed files with 292 additions and 69 deletions
|
@ -9,7 +9,7 @@ use std::fs::create_dir_all;
|
|||
use std::iter::once;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use base::cross_process_instant::CrossProcessInstant;
|
||||
use base::id::{PipelineId, WebViewId};
|
||||
|
@ -53,6 +53,7 @@ use webrender_api::{
|
|||
};
|
||||
|
||||
use crate::InitialCompositorState;
|
||||
use crate::refresh_driver::RefreshDriver;
|
||||
use crate::webview_manager::WebViewManager;
|
||||
use crate::webview_renderer::{PinchZoomResult, UnknownWebView, WebViewRenderer};
|
||||
|
||||
|
@ -86,6 +87,9 @@ pub enum WebRenderDebugOption {
|
|||
}
|
||||
/// Data that is shared by all WebView renderers.
|
||||
pub struct ServoRenderer {
|
||||
/// The [`RefreshDriver`] which manages the rythym of painting.
|
||||
refresh_driver: RefreshDriver,
|
||||
|
||||
/// This is a temporary map between [`PipelineId`]s and their associated [`WebViewId`]. Once
|
||||
/// all renderer operations become per-`WebView` this map can be removed, but we still sometimes
|
||||
/// need to work backwards to figure out what `WebView` is associated with a `Pipeline`.
|
||||
|
@ -151,18 +155,14 @@ pub struct IOCompositor {
|
|||
/// The number of frames pending to receive from WebRender.
|
||||
pending_frames: usize,
|
||||
|
||||
/// The [`Instant`] of the last animation tick, used to avoid flooding the Constellation and
|
||||
/// ScriptThread with a deluge of animation ticks.
|
||||
last_animation_tick: Instant,
|
||||
|
||||
/// A handle to the memory profiler which will automatically unregister
|
||||
/// when it's dropped.
|
||||
_mem_profiler_registration: ProfilerRegistration,
|
||||
}
|
||||
|
||||
/// Why we need to be repainted. This is used for debugging.
|
||||
#[derive(Clone, Copy, Default)]
|
||||
struct RepaintReason(u8);
|
||||
#[derive(Clone, Copy, Default, PartialEq)]
|
||||
pub(crate) struct RepaintReason(u8);
|
||||
|
||||
bitflags! {
|
||||
impl RepaintReason: u8 {
|
||||
|
@ -386,6 +386,10 @@ impl IOCompositor {
|
|||
);
|
||||
let compositor = IOCompositor {
|
||||
global: Rc::new(RefCell::new(ServoRenderer {
|
||||
refresh_driver: RefreshDriver::new(
|
||||
state.constellation_chan.clone(),
|
||||
state.event_loop_waker,
|
||||
),
|
||||
shutdown_state: state.shutdown_state,
|
||||
pipeline_to_webview_map: Default::default(),
|
||||
compositor_receiver: state.receiver,
|
||||
|
@ -406,7 +410,6 @@ impl IOCompositor {
|
|||
webrender: Some(state.webrender),
|
||||
rendering_context: state.rendering_context,
|
||||
pending_frames: 0,
|
||||
last_animation_tick: Instant::now(),
|
||||
_mem_profiler_registration: registration,
|
||||
};
|
||||
|
||||
|
@ -450,7 +453,16 @@ impl IOCompositor {
|
|||
}
|
||||
|
||||
pub fn needs_repaint(&self) -> bool {
|
||||
!self.needs_repaint.get().is_empty()
|
||||
let repaint_reason = self.needs_repaint.get();
|
||||
if repaint_reason.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
!self
|
||||
.global
|
||||
.borrow()
|
||||
.refresh_driver
|
||||
.wait_to_paint(repaint_reason)
|
||||
}
|
||||
|
||||
pub fn finish_shutting_down(&mut self) {
|
||||
|
@ -519,15 +531,17 @@ impl IOCompositor {
|
|||
pipeline_id,
|
||||
animation_state,
|
||||
) => {
|
||||
if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) {
|
||||
if webview_renderer
|
||||
.change_pipeline_running_animations_state(pipeline_id, animation_state) &&
|
||||
webview_renderer.animating()
|
||||
{
|
||||
// These operations should eventually happen per-WebView, but they are
|
||||
// global now as rendering is still global to all WebViews.
|
||||
self.process_animations(true);
|
||||
}
|
||||
let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
|
||||
return;
|
||||
};
|
||||
|
||||
if webview_renderer
|
||||
.change_pipeline_running_animations_state(pipeline_id, animation_state)
|
||||
{
|
||||
self.global
|
||||
.borrow()
|
||||
.refresh_driver
|
||||
.notify_animation_state_changed(webview_renderer);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -572,14 +586,15 @@ impl IOCompositor {
|
|||
},
|
||||
|
||||
CompositorMsg::SetThrottled(webview_id, pipeline_id, throttled) => {
|
||||
if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) {
|
||||
if webview_renderer.set_throttled(pipeline_id, throttled) &&
|
||||
webview_renderer.animating()
|
||||
{
|
||||
// These operations should eventually happen per-WebView, but they are
|
||||
// global now as rendering is still global to all WebViews.
|
||||
self.process_animations(true);
|
||||
}
|
||||
let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
|
||||
return;
|
||||
};
|
||||
|
||||
if webview_renderer.set_throttled(pipeline_id, throttled) {
|
||||
self.global
|
||||
.borrow()
|
||||
.refresh_driver
|
||||
.notify_animation_state_changed(webview_renderer);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1271,39 +1286,6 @@ impl IOCompositor {
|
|||
self.set_needs_repaint(RepaintReason::Resize);
|
||||
}
|
||||
|
||||
/// If there are any animations running, dispatches appropriate messages to the constellation.
|
||||
fn process_animations(&mut self, force: bool) {
|
||||
// When running animations in order to dump a screenshot (not after a full composite), don't send
|
||||
// animation ticks faster than about 60Hz.
|
||||
//
|
||||
// TODO: This should be based on the refresh rate of the screen and also apply to all
|
||||
// animation ticks, not just ones sent while waiting to dump screenshots. This requires
|
||||
// something like a refresh driver concept though.
|
||||
if !force && (Instant::now() - self.last_animation_tick) < Duration::from_millis(16) {
|
||||
return;
|
||||
}
|
||||
self.last_animation_tick = Instant::now();
|
||||
|
||||
let animating_webviews: Vec<_> = self
|
||||
.webview_renderers
|
||||
.iter()
|
||||
.filter_map(|webview_renderer| {
|
||||
if webview_renderer.animating() {
|
||||
Some(webview_renderer.id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
if !animating_webviews.is_empty() {
|
||||
if let Err(error) = self.global.borrow().constellation_sender.send(
|
||||
EmbedderToConstellationMessage::TickAnimation(animating_webviews),
|
||||
) {
|
||||
warn!("Sending tick to constellation failed ({error:?}).");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_zoom_reset_window_event(&mut self, webview_id: WebViewId) {
|
||||
if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown {
|
||||
return;
|
||||
|
@ -1410,6 +1392,11 @@ impl IOCompositor {
|
|||
/// Render the WebRender scene to the active `RenderingContext`. If successful, trigger
|
||||
/// the next round of animations.
|
||||
pub fn render(&mut self) -> bool {
|
||||
self.global
|
||||
.borrow()
|
||||
.refresh_driver
|
||||
.notify_will_paint(self.webview_renderers.iter());
|
||||
|
||||
if let Err(error) = self.render_inner() {
|
||||
warn!("Unable to render: {error:?}");
|
||||
return false;
|
||||
|
@ -1419,9 +1406,6 @@ impl IOCompositor {
|
|||
// the scene no longer needs to be repainted.
|
||||
self.needs_repaint.set(RepaintReason::empty());
|
||||
|
||||
// Queue up any subsequent paints for animations.
|
||||
self.process_animations(true);
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
|
@ -1492,10 +1476,8 @@ impl IOCompositor {
|
|||
|
||||
if opts::get().wait_for_stable_image {
|
||||
// The current image may be ready to output. However, if there are animations active,
|
||||
// tick those instead and continue waiting for the image output to be stable AND
|
||||
// all active animations to complete.
|
||||
// continue waiting for the image output to be stable AND all active animations to complete.
|
||||
if self.animations_or_animation_callbacks_running() {
|
||||
self.process_animations(false);
|
||||
return Err(UnableToComposite::NotReadyToPaintImage(
|
||||
NotReadyToPaint::AnimationsActive,
|
||||
));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue