mirror of
https://github.com/servo/servo.git
synced 2025-08-29 17:18:23 +01:00
script: Generate only a single frame during "update the rendering" (#38858)
Instead of generating a frame for every display list, which might be one rendered frame per `<iframe>`, generate only a single frame per call to "update the rendering." This should make rendering more efficient when there are `<iframe>`s present and also open up optimizations for non-display list frames. Testing: This could potentially reduce flashing of content during rendering updates, but that is very difficult to test. Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
parent
4f68508624
commit
e7a963cca0
6 changed files with 40 additions and 10 deletions
|
@ -671,6 +671,11 @@ impl IOCompositor {
|
||||||
transaction
|
transaction
|
||||||
.set_display_list(display_list_info.epoch, (pipeline_id, built_display_list));
|
.set_display_list(display_list_info.epoch, (pipeline_id, built_display_list));
|
||||||
self.update_transaction_with_all_scroll_offsets(&mut transaction);
|
self.update_transaction_with_all_scroll_offsets(&mut transaction);
|
||||||
|
self.global.borrow_mut().send_transaction(transaction);
|
||||||
|
},
|
||||||
|
|
||||||
|
CompositorMsg::GenerateFrame => {
|
||||||
|
let mut transaction = Transaction::new();
|
||||||
self.generate_frame(&mut transaction, RenderReasons::SCENE);
|
self.generate_frame(&mut transaction, RenderReasons::SCENE);
|
||||||
self.global.borrow_mut().send_transaction(transaction);
|
self.global.borrow_mut().send_transaction(transaction);
|
||||||
},
|
},
|
||||||
|
|
|
@ -42,6 +42,7 @@ mod from_constellation {
|
||||||
Self::SendInitialTransaction(..) => target!("SendInitialTransaction"),
|
Self::SendInitialTransaction(..) => target!("SendInitialTransaction"),
|
||||||
Self::SendScrollNode(..) => target!("SendScrollNode"),
|
Self::SendScrollNode(..) => target!("SendScrollNode"),
|
||||||
Self::SendDisplayList { .. } => target!("SendDisplayList"),
|
Self::SendDisplayList { .. } => target!("SendDisplayList"),
|
||||||
|
Self::GenerateFrame { .. } => target!("GenerateFrame"),
|
||||||
Self::GenerateImageKey(..) => target!("GenerateImageKey"),
|
Self::GenerateImageKey(..) => target!("GenerateImageKey"),
|
||||||
Self::UpdateImages(..) => target!("UpdateImages"),
|
Self::UpdateImages(..) => target!("UpdateImages"),
|
||||||
Self::GenerateFontKeys(..) => target!("GenerateFontKeys"),
|
Self::GenerateFontKeys(..) => target!("GenerateFontKeys"),
|
||||||
|
|
|
@ -2111,6 +2111,9 @@ impl Window {
|
||||||
// properly process ScrollBehavior here.
|
// properly process ScrollBehavior here.
|
||||||
let reflow_phases_run =
|
let reflow_phases_run =
|
||||||
self.reflow(ReflowGoal::UpdateScrollNode(scroll_id, Vector2D::new(x, y)));
|
self.reflow(ReflowGoal::UpdateScrollNode(scroll_id, Vector2D::new(x, y)));
|
||||||
|
if reflow_phases_run.needs_frame() {
|
||||||
|
self.compositor_api().generate_frame();
|
||||||
|
}
|
||||||
|
|
||||||
// > If the scroll position did not change as a result of the user interaction or programmatic
|
// > If the scroll position did not change as a result of the user interaction or programmatic
|
||||||
// > invocation, where no translations were applied as a result, then no scrollend event fires
|
// > invocation, where no translations were applied as a result, then no scrollend event fires
|
||||||
|
@ -2351,7 +2354,9 @@ impl Window {
|
||||||
// iframe size updates.
|
// iframe size updates.
|
||||||
//
|
//
|
||||||
// See <https://github.com/servo/servo/issues/14719>
|
// See <https://github.com/servo/servo/issues/14719>
|
||||||
self.Document().update_the_rendering();
|
if self.Document().update_the_rendering().needs_frame() {
|
||||||
|
self.compositor_api().generate_frame();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn layout_blocked(&self) -> bool {
|
pub(crate) fn layout_blocked(&self) -> bool {
|
||||||
|
|
|
@ -67,9 +67,7 @@ use js::jsapi::{
|
||||||
};
|
};
|
||||||
use js::jsval::UndefinedValue;
|
use js::jsval::UndefinedValue;
|
||||||
use js::rust::ParentRuntime;
|
use js::rust::ParentRuntime;
|
||||||
use layout_api::{
|
use layout_api::{LayoutConfig, LayoutFactory, RestyleReason, ScriptThreadFactory};
|
||||||
LayoutConfig, LayoutFactory, ReflowPhasesRun, RestyleReason, ScriptThreadFactory,
|
|
||||||
};
|
|
||||||
use media::WindowGLContext;
|
use media::WindowGLContext;
|
||||||
use metrics::MAX_TASK_NS;
|
use metrics::MAX_TASK_NS;
|
||||||
use net_traits::image_cache::{ImageCache, ImageCacheResponseMessage};
|
use net_traits::image_cache::{ImageCache, ImageCacheResponseMessage};
|
||||||
|
@ -1118,7 +1116,7 @@ impl ScriptThread {
|
||||||
// steps per doc in docs. Currently `<iframe>` resizing depends on a parent being able to
|
// steps per doc in docs. Currently `<iframe>` resizing depends on a parent being able to
|
||||||
// queue resize events on a child and have those run in the same call to this method, so
|
// queue resize events on a child and have those run in the same call to this method, so
|
||||||
// that needs to be sorted out to fix this.
|
// that needs to be sorted out to fix this.
|
||||||
let mut built_any_display_lists = false;
|
let mut should_generate_frame = false;
|
||||||
for pipeline_id in documents_in_order.iter() {
|
for pipeline_id in documents_in_order.iter() {
|
||||||
let document = self
|
let document = self
|
||||||
.documents
|
.documents
|
||||||
|
@ -1201,19 +1199,21 @@ impl ScriptThread {
|
||||||
|
|
||||||
// > Step 22: For each doc of docs, update the rendering or user interface of
|
// > Step 22: For each doc of docs, update the rendering or user interface of
|
||||||
// > doc and its node navigable to reflect the current state.
|
// > doc and its node navigable to reflect the current state.
|
||||||
built_any_display_lists = document
|
should_generate_frame =
|
||||||
.update_the_rendering()
|
document.update_the_rendering().needs_frame() || should_generate_frame;
|
||||||
.contains(ReflowPhasesRun::BuiltDisplayList) ||
|
|
||||||
built_any_display_lists;
|
|
||||||
|
|
||||||
// TODO: Process top layer removals according to
|
// TODO: Process top layer removals according to
|
||||||
// https://drafts.csswg.org/css-position-4/#process-top-layer-removals.
|
// https://drafts.csswg.org/css-position-4/#process-top-layer-removals.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if should_generate_frame {
|
||||||
|
self.compositor_api.generate_frame();
|
||||||
|
}
|
||||||
|
|
||||||
// Perform a microtask checkpoint as the specifications says that *update the rendering*
|
// Perform a microtask checkpoint as the specifications says that *update the rendering*
|
||||||
// should be run in a task and a microtask checkpoint is always done when running tasks.
|
// should be run in a task and a microtask checkpoint is always done when running tasks.
|
||||||
self.perform_a_microtask_checkpoint(can_gc);
|
self.perform_a_microtask_checkpoint(can_gc);
|
||||||
built_any_display_lists
|
should_generate_frame
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Schedule a rendering update ("update the rendering"), if necessary. This
|
/// Schedule a rendering update ("update the rendering"), if necessary. This
|
||||||
|
|
|
@ -111,6 +111,9 @@ pub enum CompositorMsg {
|
||||||
/// An [ipc::IpcBytesReceiver] used to send the raw data of the display list.
|
/// An [ipc::IpcBytesReceiver] used to send the raw data of the display list.
|
||||||
display_list_receiver: ipc::IpcBytesReceiver,
|
display_list_receiver: ipc::IpcBytesReceiver,
|
||||||
},
|
},
|
||||||
|
/// Ask the renderer to generate a frame for the current set of display lists that
|
||||||
|
/// have been sent to the renderer.
|
||||||
|
GenerateFrame,
|
||||||
/// Create a new image key. The result will be returned via the
|
/// Create a new image key. The result will be returned via the
|
||||||
/// provided channel sender.
|
/// provided channel sender.
|
||||||
GenerateImageKey(IpcSender<ImageKey>),
|
GenerateImageKey(IpcSender<ImageKey>),
|
||||||
|
@ -244,6 +247,13 @@ impl CrossProcessCompositorApi {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ask the Servo renderer to generate a new frame after having new display lists.
|
||||||
|
pub fn generate_frame(&self) {
|
||||||
|
if let Err(error) = self.0.send(CompositorMsg::GenerateFrame) {
|
||||||
|
warn!("Error generating frame: {error}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new image key. Blocks until the key is available.
|
/// Create a new image key. Blocks until the key is available.
|
||||||
pub fn generate_image_key_blocking(&self) -> Option<ImageKey> {
|
pub fn generate_image_key_blocking(&self) -> Option<ImageKey> {
|
||||||
let (sender, receiver) = ipc::channel().unwrap();
|
let (sender, receiver) = ipc::channel().unwrap();
|
||||||
|
|
|
@ -441,6 +441,15 @@ bitflags! {
|
||||||
const BuiltStackingContextTree = 1 << 2;
|
const BuiltStackingContextTree = 1 << 2;
|
||||||
const BuiltDisplayList = 1 << 3;
|
const BuiltDisplayList = 1 << 3;
|
||||||
const UpdatedScrollNodeOffset = 1 << 4;
|
const UpdatedScrollNodeOffset = 1 << 4;
|
||||||
|
const UpdatedCanvasContents = 1 << 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ReflowPhasesRun {
|
||||||
|
pub fn needs_frame(&self) -> bool {
|
||||||
|
self.intersects(
|
||||||
|
Self::BuiltDisplayList | Self::UpdatedScrollNodeOffset | Self::UpdatedCanvasContents,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue