mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +01:00
Auto merge of #17398 - glennw:opt-composite, r=jdm
Improve decisions in compositor over when to draw a frame. This patch fixes a couple of issues in the compositor: 1) Remove the delayed composition code. Previously, this would schedule a composite for 12ms in the future. This doesn't really make any sense with WR. There's no point in doing a composite unless WR has provided a new frame to be drawn. This fixes issues in several benchmarks where we were doing multiple composite / renders per rAF, which is a waste of CPU time. This *does* make the framerate slower in some cases (such as a slow rAF callback) but it's more correct - otherwise we were just compositing the same frame multiple times for no real benefit. 2) Inform the window of the current animation state of the compositor. Specifically, if an animation (or rAF) is currently active, the window system switches to use event polling, and does not block on the OS-level event loop. In the case of active animation, we just assume that we want to be running as the vsync interval and not blocking. This means the compositor thread only sleeps on vsync during animation, which reduces OS scheduling and results in much smoother animation. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/17398) <!-- Reviewable:end -->
This commit is contained in:
commit
819a40bfb0
7 changed files with 70 additions and 175 deletions
|
@ -1538,28 +1538,29 @@ impl Document {
|
|||
self.animation_frame_ident.set(ident);
|
||||
self.animation_frame_list.borrow_mut().push((ident, Some(callback)));
|
||||
|
||||
// No need to send a `ChangeRunningAnimationsState` if we're running animation callbacks:
|
||||
// we're guaranteed to already be in the "animation callbacks present" state.
|
||||
//
|
||||
// This reduces CPU usage by avoiding needless thread wakeups in the common case of
|
||||
// repeated rAF.
|
||||
//
|
||||
// TODO: Should tick animation only when document is visible
|
||||
if !self.running_animation_callbacks.get() {
|
||||
if !self.is_faking_animation_frames() {
|
||||
let global_scope = self.window.upcast::<GlobalScope>();
|
||||
let event = ConstellationMsg::ChangeRunningAnimationsState(
|
||||
global_scope.pipeline_id(),
|
||||
AnimationState::AnimationCallbacksPresent);
|
||||
global_scope.constellation_chan().send(event).unwrap();
|
||||
} else {
|
||||
let callback = FakeRequestAnimationFrameCallback {
|
||||
document: Trusted::new(self),
|
||||
};
|
||||
self.global()
|
||||
.schedule_callback(OneshotTimerCallback::FakeRequestAnimationFrame(callback),
|
||||
MsDuration::new(FAKE_REQUEST_ANIMATION_FRAME_DELAY));
|
||||
}
|
||||
|
||||
// If we are running 'fake' animation frames, we unconditionally
|
||||
// set up a one-shot timer for script to execute the rAF callbacks.
|
||||
if self.is_faking_animation_frames() {
|
||||
let callback = FakeRequestAnimationFrameCallback {
|
||||
document: Trusted::new(self),
|
||||
};
|
||||
self.global()
|
||||
.schedule_callback(OneshotTimerCallback::FakeRequestAnimationFrame(callback),
|
||||
MsDuration::new(FAKE_REQUEST_ANIMATION_FRAME_DELAY));
|
||||
} else if !self.running_animation_callbacks.get() {
|
||||
// No need to send a `ChangeRunningAnimationsState` if we're running animation callbacks:
|
||||
// we're guaranteed to already be in the "animation callbacks present" state.
|
||||
//
|
||||
// This reduces CPU usage by avoiding needless thread wakeups in the common case of
|
||||
// repeated rAF.
|
||||
|
||||
let global_scope = self.window.upcast::<GlobalScope>();
|
||||
let event = ConstellationMsg::ChangeRunningAnimationsState(
|
||||
global_scope.pipeline_id(),
|
||||
AnimationState::AnimationCallbacksPresent);
|
||||
global_scope.constellation_chan().send(event).unwrap();
|
||||
}
|
||||
|
||||
ident
|
||||
|
@ -1596,6 +1597,22 @@ impl Document {
|
|||
ReflowQueryType::NoQuery,
|
||||
ReflowReason::RequestAnimationFrame);
|
||||
|
||||
if spurious && !was_faking_animation_frames {
|
||||
// If the rAF callbacks did not mutate the DOM, then the
|
||||
// reflow call above means that layout will not be invoked,
|
||||
// and therefore no new frame will be sent to the compositor.
|
||||
// If this happens, the compositor will not tick the animation
|
||||
// and the next rAF will never be called! When this happens
|
||||
// for several frames, then the spurious rAF detection below
|
||||
// will kick in and use a timer to tick the callbacks. However,
|
||||
// for the interim frames where we are deciding whether this rAF
|
||||
// is considered spurious, we need to ensure that the layout
|
||||
// and compositor *do* tick the animation.
|
||||
self.window.force_reflow(ReflowGoal::ForDisplay,
|
||||
ReflowQueryType::NoQuery,
|
||||
ReflowReason::RequestAnimationFrame);
|
||||
}
|
||||
|
||||
// Only send the animation change state message after running any callbacks.
|
||||
// This means that if the animation callback adds a new callback for
|
||||
// the next frame (which is the common case), we won't send a NoAnimationCallbacksPresent
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue