Send information to script as part of finishing layout.

This avoids the need for multiple layout RPC operations immediately
following return of control to script. This means that layout and
script can continue to operate in parallel at this point, rather
than one potentially waiting on the shared mutex to be unlocked.
This commit is contained in:
Josh Matthews 2017-04-10 17:47:20 +10:00
parent c3b9714ab7
commit 9134918844
5 changed files with 60 additions and 51 deletions

View file

@ -17,7 +17,6 @@ use inline::LAST_FRAGMENT_OF_ELEMENT;
use ipc_channel::ipc::IpcSender; use ipc_channel::ipc::IpcSender;
use msg::constellation_msg::PipelineId; use msg::constellation_msg::PipelineId;
use opaque_node::OpaqueNodeMethods; use opaque_node::OpaqueNodeMethods;
use script_layout_interface::PendingImage;
use script_layout_interface::rpc::{ContentBoxResponse, ContentBoxesResponse}; use script_layout_interface::rpc::{ContentBoxResponse, ContentBoxesResponse};
use script_layout_interface::rpc::{HitTestResponse, LayoutRPC}; use script_layout_interface::rpc::{HitTestResponse, LayoutRPC};
use script_layout_interface::rpc::{MarginStyleResponse, NodeGeometryResponse}; use script_layout_interface::rpc::{MarginStyleResponse, NodeGeometryResponse};
@ -28,7 +27,6 @@ use script_traits::LayoutMsg as ConstellationMsg;
use script_traits::UntrustedNodeAddress; use script_traits::UntrustedNodeAddress;
use sequential; use sequential;
use std::cmp::{min, max}; use std::cmp::{min, max};
use std::mem;
use std::ops::Deref; use std::ops::Deref;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use style::computed_values; use style::computed_values;
@ -89,14 +87,8 @@ pub struct LayoutThreadData {
/// Index in a text fragment. We need this do determine the insertion point. /// Index in a text fragment. We need this do determine the insertion point.
pub text_index_response: TextIndexResponse, pub text_index_response: TextIndexResponse,
/// A list of images requests that need to be initiated.
pub pending_images: Vec<PendingImage>,
/// A queued response for the list of nodes at a given point. /// A queued response for the list of nodes at a given point.
pub nodes_from_point_response: Vec<UntrustedNodeAddress>, pub nodes_from_point_response: Vec<UntrustedNodeAddress>,
/// A list of nodes that have just started a CSS transition.
pub newly_transitioning_nodes: Vec<UntrustedNodeAddress>,
} }
pub struct LayoutRPCImpl(pub Arc<Mutex<LayoutThreadData>>); pub struct LayoutRPCImpl(pub Arc<Mutex<LayoutThreadData>>);
@ -201,18 +193,6 @@ impl LayoutRPC for LayoutRPCImpl {
let rw_data = rw_data.lock().unwrap(); let rw_data = rw_data.lock().unwrap();
rw_data.text_index_response.clone() rw_data.text_index_response.clone()
} }
fn pending_images(&self) -> Vec<PendingImage> {
let &LayoutRPCImpl(ref rw_data) = self;
let mut rw_data = rw_data.lock().unwrap();
mem::replace(&mut rw_data.pending_images, vec![])
}
fn newly_transitioning_nodes(&self) -> Vec<UntrustedNodeAddress> {
let &LayoutRPCImpl(ref rw_data) = self;
let mut rw_data = rw_data.lock().unwrap();
mem::replace(&mut rw_data.newly_transitioning_nodes, vec![])
}
} }
struct UnioningFragmentBorderBoxIterator { struct UnioningFragmentBorderBoxIterator {

View file

@ -81,7 +81,8 @@ use profile_traits::mem::{self, Report, ReportKind, ReportsChan};
use profile_traits::time::{self, TimerMetadata, profile}; use profile_traits::time::{self, TimerMetadata, profile};
use profile_traits::time::{TimerMetadataFrameType, TimerMetadataReflowType}; use profile_traits::time::{TimerMetadataFrameType, TimerMetadataReflowType};
use script::layout_wrapper::{ServoLayoutElement, ServoLayoutDocument, ServoLayoutNode}; use script::layout_wrapper::{ServoLayoutElement, ServoLayoutDocument, ServoLayoutNode};
use script_layout_interface::message::{Msg, NewLayoutThreadInfo, Reflow, ReflowQueryType, ScriptReflow}; use script_layout_interface::message::{Msg, NewLayoutThreadInfo, Reflow, ReflowQueryType};
use script_layout_interface::message::{ScriptReflow, ReflowComplete};
use script_layout_interface::reporter::CSSErrorReporter; use script_layout_interface::reporter::CSSErrorReporter;
use script_layout_interface::rpc::{LayoutRPC, MarginStyleResponse, NodeOverflowResponse, OffsetParentResponse}; use script_layout_interface::rpc::{LayoutRPC, MarginStyleResponse, NodeOverflowResponse, OffsetParentResponse};
use script_layout_interface::rpc::TextIndexResponse; use script_layout_interface::rpc::TextIndexResponse;
@ -292,6 +293,37 @@ impl LayoutThreadFactory for LayoutThread {
} }
} }
struct ScriptReflowResult {
script_reflow: ScriptReflow,
result: RefCell<Option<ReflowComplete>>,
}
impl Deref for ScriptReflowResult {
type Target = ScriptReflow;
fn deref(&self) -> &ScriptReflow {
&self.script_reflow
}
}
impl ScriptReflowResult {
fn new(script_reflow: ScriptReflow) -> ScriptReflowResult {
ScriptReflowResult {
script_reflow: script_reflow,
result: RefCell::new(Some(Default::default())),
}
}
}
impl Drop for ScriptReflowResult {
fn drop(&mut self) {
self.script_reflow.script_join_chan.send(
self.result
.borrow_mut()
.take()
.unwrap()).unwrap();
}
}
/// The `LayoutThread` `rw_data` lock must remain locked until the first reflow, /// The `LayoutThread` `rw_data` lock must remain locked until the first reflow,
/// as RPC calls don't make sense until then. Use this in combination with /// as RPC calls don't make sense until then. Use this in combination with
/// `LayoutThread::lock_rw_data` and `LayoutThread::return_rw_data`. /// `LayoutThread::lock_rw_data` and `LayoutThread::return_rw_data`.
@ -476,9 +508,7 @@ impl LayoutThread {
margin_style_response: MarginStyleResponse::empty(), margin_style_response: MarginStyleResponse::empty(),
stacking_context_scroll_offsets: HashMap::new(), stacking_context_scroll_offsets: HashMap::new(),
text_index_response: TextIndexResponse(None), text_index_response: TextIndexResponse(None),
pending_images: vec![],
nodes_from_point_response: vec![], nodes_from_point_response: vec![],
newly_transitioning_nodes: vec![],
})), })),
error_reporter: CSSErrorReporter { error_reporter: CSSErrorReporter {
pipelineid: id, pipelineid: id,
@ -616,10 +646,11 @@ impl LayoutThread {
Box<LayoutRPC + Send>).unwrap(); Box<LayoutRPC + Send>).unwrap();
}, },
Msg::Reflow(data) => { Msg::Reflow(data) => {
let mut data = ScriptReflowResult::new(data);
profile(time::ProfilerCategory::LayoutPerform, profile(time::ProfilerCategory::LayoutPerform,
self.profiler_metadata(), self.profiler_metadata(),
self.time_profiler_chan.clone(), self.time_profiler_chan.clone(),
|| self.handle_reflow(&data, possibly_locked_rw_data)); || self.handle_reflow(&mut data, possibly_locked_rw_data));
}, },
Msg::TickAnimations => self.tick_all_animations(possibly_locked_rw_data), Msg::TickAnimations => self.tick_all_animations(possibly_locked_rw_data),
Msg::SetStackingContextScrollStates(new_scroll_states) => { Msg::SetStackingContextScrollStates(new_scroll_states) => {
@ -955,7 +986,7 @@ impl LayoutThread {
/// The high-level routine that performs layout threads. /// The high-level routine that performs layout threads.
fn handle_reflow<'a, 'b>(&mut self, fn handle_reflow<'a, 'b>(&mut self,
data: &ScriptReflow, data: &mut ScriptReflowResult,
possibly_locked_rw_data: &mut RwData<'a, 'b>) { possibly_locked_rw_data: &mut RwData<'a, 'b>) {
let document = unsafe { ServoLayoutNode::new(&data.document) }; let document = unsafe { ServoLayoutNode::new(&data.document) };
let document = document.as_document().unwrap(); let document = document.as_document().unwrap();
@ -1240,24 +1271,26 @@ impl LayoutThread {
self.respond_to_query_if_necessary(&data.query_type, self.respond_to_query_if_necessary(&data.query_type,
&mut *rw_data, &mut *rw_data,
&mut layout_context); &mut layout_context,
data.result.borrow_mut().as_mut().unwrap());
} }
fn respond_to_query_if_necessary(&self, fn respond_to_query_if_necessary(&self,
query_type: &ReflowQueryType, query_type: &ReflowQueryType,
rw_data: &mut LayoutThreadData, rw_data: &mut LayoutThreadData,
context: &mut LayoutContext) { context: &mut LayoutContext,
reflow_result: &mut ReflowComplete) {
let pending_images = match context.pending_images { let pending_images = match context.pending_images {
Some(ref pending) => std_mem::replace(&mut *pending.lock().unwrap(), vec![]), Some(ref pending) => std_mem::replace(&mut *pending.lock().unwrap(), vec![]),
None => vec![], None => vec![],
}; };
rw_data.pending_images = pending_images; reflow_result.pending_images = pending_images;
let newly_transitioning_nodes = match context.newly_transitioning_nodes { let newly_transitioning_nodes = match context.newly_transitioning_nodes {
Some(ref nodes) => std_mem::replace(&mut *nodes.lock().unwrap(), vec![]), Some(ref nodes) => std_mem::replace(&mut *nodes.lock().unwrap(), vec![]),
None => vec![], None => vec![],
}; };
rw_data.newly_transitioning_nodes = newly_transitioning_nodes; reflow_result.newly_transitioning_nodes = newly_transitioning_nodes;
let mut root_flow = match self.root_flow.borrow().clone() { let mut root_flow = match self.root_flow.borrow().clone() {
Some(root_flow) => root_flow, Some(root_flow) => root_flow,

View file

@ -1214,16 +1214,16 @@ impl Window {
debug!("script: layout forked"); debug!("script: layout forked");
match join_port.try_recv() { let complete = match join_port.try_recv() {
Err(Empty) => { Err(Empty) => {
info!("script: waiting on layout"); info!("script: waiting on layout");
join_port.recv().unwrap(); join_port.recv().unwrap()
} }
Ok(_) => {} Ok(reflow_complete) => reflow_complete,
Err(Disconnected) => { Err(Disconnected) => {
panic!("Layout thread failed while script was waiting for a result."); panic!("Layout thread failed while script was waiting for a result.");
} }
} };
debug!("script: layout joined"); debug!("script: layout joined");
@ -1237,8 +1237,7 @@ impl Window {
self.emit_timeline_marker(marker.end()); self.emit_timeline_marker(marker.end());
} }
let pending_images = self.layout_rpc.pending_images(); for image in complete.pending_images {
for image in pending_images {
let id = image.id; let id = image.id;
let js_runtime = self.js_runtime.borrow(); let js_runtime = self.js_runtime.borrow();
let js_runtime = js_runtime.as_ref().unwrap(); let js_runtime = js_runtime.as_ref().unwrap();
@ -1262,8 +1261,7 @@ impl Window {
} }
} }
let newly_transitioning_nodes = self.layout_rpc.newly_transitioning_nodes(); ScriptThread::note_newly_transitioning_nodes(complete.newly_transitioning_nodes);
ScriptThread::note_newly_transitioning_nodes(newly_transitioning_nodes);
true true
} }

View file

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use {OpaqueStyleAndLayoutData, TrustedNodeAddress}; use {OpaqueStyleAndLayoutData, TrustedNodeAddress, PendingImage};
use app_units::Au; use app_units::Au;
use euclid::point::Point2D; use euclid::point::Point2D;
use euclid::rect::Rect; use euclid::rect::Rect;
@ -12,7 +12,7 @@ use msg::constellation_msg::PipelineId;
use net_traits::image_cache::ImageCache; use net_traits::image_cache::ImageCache;
use profile_traits::mem::ReportsChan; use profile_traits::mem::ReportsChan;
use rpc::LayoutRPC; use rpc::LayoutRPC;
use script_traits::{ConstellationControlMsg, LayoutControlMsg}; use script_traits::{ConstellationControlMsg, LayoutControlMsg, UntrustedNodeAddress};
use script_traits::{LayoutMsg as ConstellationMsg, StackingContextScrollState, WindowSizeData}; use script_traits::{LayoutMsg as ConstellationMsg, StackingContextScrollState, WindowSizeData};
use servo_url::ServoUrl; use servo_url::ServoUrl;
use std::sync::Arc; use std::sync::Arc;
@ -109,6 +109,15 @@ pub struct Reflow {
pub page_clip_rect: Rect<Au>, pub page_clip_rect: Rect<Au>,
} }
/// Information derived from a layout pass that needs to be returned to the script thread.
#[derive(Default)]
pub struct ReflowComplete {
/// The list of images that were encountered that are in progress.
pub pending_images: Vec<PendingImage>,
/// The list of nodes that initiated a CSS transition.
pub newly_transitioning_nodes: Vec<UntrustedNodeAddress>,
}
/// Information needed for a script-initiated reflow. /// Information needed for a script-initiated reflow.
pub struct ScriptReflow { pub struct ScriptReflow {
/// General reflow data. /// General reflow data.
@ -122,19 +131,13 @@ pub struct ScriptReflow {
/// The current window size. /// The current window size.
pub window_size: WindowSizeData, pub window_size: WindowSizeData,
/// The channel that we send a notification to. /// The channel that we send a notification to.
pub script_join_chan: Sender<()>, pub script_join_chan: Sender<ReflowComplete>,
/// The type of query if any to perform during this reflow. /// The type of query if any to perform during this reflow.
pub query_type: ReflowQueryType, pub query_type: ReflowQueryType,
/// The number of objects in the dom #10110 /// The number of objects in the dom #10110
pub dom_count: u32, pub dom_count: u32,
} }
impl Drop for ScriptReflow {
fn drop(&mut self) {
self.script_join_chan.send(()).unwrap();
}
}
pub struct NewLayoutThreadInfo { pub struct NewLayoutThreadInfo {
pub id: PipelineId, pub id: PipelineId,
pub url: ServoUrl, pub url: ServoUrl,

View file

@ -2,7 +2,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use PendingImage;
use app_units::Au; use app_units::Au;
use euclid::point::Point2D; use euclid::point::Point2D;
use euclid::rect::Rect; use euclid::rect::Rect;
@ -38,12 +37,8 @@ pub trait LayoutRPC {
fn offset_parent(&self) -> OffsetParentResponse; fn offset_parent(&self) -> OffsetParentResponse;
/// Query layout for the resolve values of the margin properties for an element. /// Query layout for the resolve values of the margin properties for an element.
fn margin_style(&self) -> MarginStyleResponse; fn margin_style(&self) -> MarginStyleResponse;
/// Requests the list of not-yet-loaded images that were encountered in the last reflow.
fn pending_images(&self) -> Vec<PendingImage>;
/// Requests the list of nodes from the given point. /// Requests the list of nodes from the given point.
fn nodes_from_point_response(&self) -> Vec<UntrustedNodeAddress>; fn nodes_from_point_response(&self) -> Vec<UntrustedNodeAddress>;
/// Requests the list of nodes that have just started CSS transitions in the last reflow.
fn newly_transitioning_nodes(&self) -> Vec<UntrustedNodeAddress>;
fn text_index(&self) -> TextIndexResponse; fn text_index(&self) -> TextIndexResponse;
} }