Optimize pending scroll update

Signed-off-by: stevennovaryo <steven.novaryo@gmail.com>
This commit is contained in:
stevennovaryo 2025-06-04 22:00:54 +08:00
parent 76405114f6
commit 3bced9535e
11 changed files with 75 additions and 45 deletions

View file

@ -220,6 +220,20 @@ impl WebViewRenderer {
);
}
pub(crate) fn send_scroll_result_to_layout(&self, scroll_result: ScrollResult) {
let scroll_state = ScrollState {
scroll_id: scroll_result.external_scroll_id,
scroll_offset: scroll_result.offset,
};
let _ = self.global.borrow().constellation_sender.send(
EmbedderToConstellationMessage::UpdateScrollState(
scroll_result.pipeline_id,
scroll_state,
),
);
}
pub(crate) fn set_frame_tree_on_pipeline_details(
&mut self,
frame_tree: &SendableFrameTree,
@ -862,10 +876,9 @@ impl WebViewRenderer {
combined_event.scroll_location,
)
});
// MYNOTES: change this to single update
// if let Some(scroll_result) = scroll_result {
// self.send_scroll_positions_to_layout_for_pipeline(scroll_result.pipeline_id);
// }
if let Some(scroll_result) = scroll_result {
self.send_scroll_result_to_layout(scroll_result);
}
let pinch_zoom_result = match self
.set_pinch_zoom_level(self.pinch_zoom_level().get() * combined_magnification)

View file

@ -1479,6 +1479,9 @@ where
EmbedderToConstellationMessage::SetScrollStates(pipeline_id, scroll_states) => {
self.handle_set_scroll_states(pipeline_id, scroll_states)
},
EmbedderToConstellationMessage::UpdateScrollState(pipeline_id, scroll_state) => {
self.handle_update_scroll_state(pipeline_id, scroll_state)
},
EmbedderToConstellationMessage::PaintMetric(pipeline_id, paint_metric_event) => {
self.handle_paint_metric(pipeline_id, paint_metric_event);
},
@ -6040,6 +6043,26 @@ where
}
}
#[cfg_attr(
feature = "tracing",
tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace")
)]
fn handle_update_scroll_state(&self, pipeline_id: PipelineId, scroll_state: ScrollState) {
let Some(pipeline) = self.pipelines.get(&pipeline_id) else {
warn!("Discarding scroll offset update for unknown pipeline");
return;
};
if let Err(error) = pipeline
.event_loop
.send(ScriptThreadMessage::UpdateScrollState(
pipeline_id,
scroll_state,
))
{
warn!("Could not send scroll offsets to pipeline: {pipeline_id:?}: {error:?}");
}
}
#[cfg_attr(
feature = "tracing",
tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace")

View file

@ -76,6 +76,7 @@ mod from_compositor {
Self::MediaSessionAction(_) => target!("MediaSessionAction"),
Self::SetWebViewThrottled(_, _) => target!("SetWebViewThrottled"),
Self::SetScrollStates(..) => target!("SetScrollStates"),
Self::UpdateScrollState(..) => target!("UpdateScrollState"),
Self::PaintMetric(..) => target!("PaintMetric"),
Self::EvaluateJavaScript(..) => target!("EvaluateJavaScript"),
Self::CreateMemoryReport(..) => target!("CreateMemoryReport"),

View file

@ -412,27 +412,6 @@ impl Layout for LayoutThread {
.unwrap_or_default()
}
/// Step 1-4 of <https://drafts.csswg.org/cssom-view/#element-scrolling-members>
/// Additionally, we are updating the scroll states to be processed by
fn process_scroll_an_element_position(
&self,
node: OpaqueNode,
scroll_offset: LayoutVector2D,
) -> LayoutVector2D {
// TODO(stevennovaryo): handle step 1-4 properly here
let scroll_id = ExternalScrollId(
combine_id_with_fragment_type(node.id(), FragmentType::FragmentBody),
self.id.into(),
);
let scroll_state = ScrollState {
scroll_id,
scroll_offset,
};
self.update_scroll_node_state(&scroll_state);
scroll_offset
}
#[cfg_attr(
feature = "tracing",
tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace")
@ -528,6 +507,14 @@ impl Layout for LayoutThread {
}
}
}
//
fn update_scroll_offset(&self, scroll_id: ExternalScrollId, scroll_offset: LayoutVector2D) {
if let Some(mut scroll_tree) = self.cached_scroll_tree_mut() {
scroll_tree
.set_scroll_offsets_for_node_with_external_scroll_id(&scroll_id, scroll_offset);
}
}
}
impl LayoutThread {

View file

@ -2547,13 +2547,12 @@ impl Window {
// webrender has a chance to update the offsets.
let x = x_.to_f32().unwrap_or(0.0f32);
let y = y_.to_f32().unwrap_or(0.0f32);
self.layout()
.process_scroll_an_element_position(node.to_opaque(), Vector2D::new(x, y));
let scroll_id = ExternalScrollId(
combine_id_with_fragment_type(node.to_opaque().id(), FragmentType::FragmentBody),
self.pipeline_id().into(),
);
self.layout()
.update_scroll_offset(scroll_id, Vector2D::new(x, y));
// Step 6
// > Perform a scroll of box to position, element as the associated

View file

@ -91,6 +91,7 @@ impl MixedMessage {
#[cfg(feature = "webgpu")]
ScriptThreadMessage::SetWebGPUPort(..) => None,
ScriptThreadMessage::SetScrollStates(id, ..) => Some(*id),
ScriptThreadMessage::UpdateScrollState(id, ..) => Some(*id),
ScriptThreadMessage::EvaluateJavaScript(id, _, _) => Some(*id),
},
MixedMessage::FromScript(inner_msg) => match inner_msg {

View file

@ -1909,6 +1909,9 @@ impl ScriptThread {
ScriptThreadMessage::SetScrollStates(pipeline_id, scroll_states) => {
self.handle_set_scroll_states(pipeline_id, scroll_states)
},
ScriptThreadMessage::UpdateScrollState(pipeline_id, scroll_state) => {
self.handle_update_scroll_state(pipeline_id, scroll_state)
},
ScriptThreadMessage::EvaluateJavaScript(pipeline_id, evaluation_id, script) => {
self.handle_evaluate_javascript(pipeline_id, evaluation_id, script, can_gc);
},
@ -1940,6 +1943,15 @@ impl ScriptThread {
)
}
fn handle_update_scroll_state(&self, pipeline_id: PipelineId, scroll_state: ScrollState) {
let Some(window) = self.documents.borrow().find_window(pipeline_id) else {
warn!("Received scroll states for closed pipeline {pipeline_id}");
return;
};
window.layout_mut().update_scroll_offset(scroll_state.scroll_id, scroll_state.scroll_offset);
}
#[cfg(feature = "webgpu")]
fn handle_msg_from_webgpu_server(&self, msg: WebGPUMsg, can_gc: CanGc) {
match msg {

View file

@ -157,6 +157,7 @@ impl ScrollTreeNode {
/// Set the offset for this node, returns false if this was a
/// non-scrolling node for which you cannot set the offset.
// MYNOTES: should i make this &mut so it will modify the new_offset
pub fn set_offset(&mut self, new_offset: LayoutVector2D) -> bool {
match self.info {
SpatialTreeNodeInfo::Scroll(ref mut info) => {
@ -359,15 +360,7 @@ impl ScrollTree {
offset: LayoutVector2D,
) -> bool {
if let Some(node) = self.get_node_by_external_scroll_id_mut(external_scroll_id) {
match node.info {
SpatialTreeNodeInfo::Scroll(ref mut scroll_info)
if &scroll_info.external_id == external_scroll_id =>
{
scroll_info.offset = offset;
return true;
},
_ => {},
}
node.set_offset(offset);
}
false
}

View file

@ -90,7 +90,11 @@ pub enum EmbedderToConstellationMessage {
SetWebViewThrottled(WebViewId, bool),
/// The Servo renderer scrolled and is updating the scroll states of the nodes in the
/// given pipeline via the constellation.
// MYNOTES: Do we still need this?
SetScrollStates(PipelineId, Vec<ScrollState>),
/// The Servo renderer scrolled and is updating the scroll states of the nodes in the
/// given pipeline via the constellation.
UpdateScrollState(PipelineId, ScrollState),
/// Notify the constellation that a particular paint metric event has happened for the given pipeline.
PaintMetric(PipelineId, PaintMetricEvent),
/// Evaluate a JavaScript string in the context of a `WebView`. When execution is complete or an

View file

@ -247,6 +247,9 @@ pub enum ScriptThreadMessage {
/// The compositor scrolled and is updating the scroll states of the nodes in the given
/// pipeline via the Constellation.
SetScrollStates(PipelineId, Vec<ScrollState>),
/// The compositor scrolled and is updating the scroll state of a node in the given
/// pipeline via the Constellation.
UpdateScrollState(PipelineId, ScrollState),
/// Evaluate the given JavaScript and return a result via a corresponding message
/// to the Constellation.
EvaluateJavaScript(PipelineId, JavaScriptEvaluationId, String),

View file

@ -48,7 +48,7 @@ use style::properties::PropertyId;
use style::properties::style_structs::Font;
use style::selector_parser::{PseudoElement, RestyleDamage, Snapshot};
use style::stylesheets::Stylesheet;
use webrender_api::ImageKey;
use webrender_api::{ExternalScrollId, ImageKey};
use webrender_api::units::{DeviceIntSize, LayoutVector2D};
pub trait GenericLayoutDataTrait: Any + MallocSizeOfTrait {
@ -250,13 +250,7 @@ pub trait Layout {
/// Query scroll offset for any scroll offset query through the layout.
fn query_scroll_offset(&self, node: OpaqueNode) -> LayoutVector2D;
/// Process the step 1-4 of scroll an element algorithm.
/// <https://drafts.csswg.org/cssom-view/#scroll-an-element>
fn process_scroll_an_element_position(
&self,
node: OpaqueNode,
scroll_offset: LayoutVector2D,
) -> LayoutVector2D;
fn update_scroll_offset(&self, scroll_id: ExternalScrollId, scroll_offset: LayoutVector2D);
fn query_content_box(&self, node: TrustedNodeAddress) -> Option<Rect<Au>>;
fn query_content_boxes(&self, node: TrustedNodeAddress) -> Vec<Rect<Au>>;