diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index f9cd4f1007e..a2d86d9d5a5 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -19,7 +19,7 @@ use geom::rect::{Rect, TypedRect}; use geom::size::TypedSize2D; use geom::scale_factor::ScaleFactor; use gfx::paint_task::Msg as PaintMsg; -use gfx::paint_task::{PaintChan, PaintRequest}; +use gfx::paint_task::PaintRequest; use layers::geometry::{DevicePixel, LayerPixel}; use layers::layers::{BufferRequest, Layer, LayerBufferSet}; use layers::rendergl; @@ -64,6 +64,9 @@ pub struct IOCompositor { /// The root pipeline. root_pipeline: Option, + /// Tracks details about each active pipeline that the compositor knows about. + pipeline_details: HashMap, + /// The canvas to paint a page. scene: Scene, @@ -102,12 +105,6 @@ pub struct IOCompositor { /// The time of the last zoom action has started. zoom_time: f64, - /// Current display/reflow status of each pipeline. - ready_states: HashMap, - - /// Current paint status of each pipeline. - paint_states: HashMap, - /// Whether the page being rendered has loaded completely. /// Differs from ReadyState because we can finish loading (ready) /// many times for a single page. @@ -130,9 +127,6 @@ pub struct IOCompositor { /// Pending scroll events. pending_scroll_events: Vec, - - /// PaintTask channels that we know about, used to return unused buffers. - paint_channels: HashMap, } pub struct ScrollEvent { @@ -159,6 +153,27 @@ struct HitTestResult { point: TypedPoint2D, } +struct PipelineDetails { + /// The pipeline associated with this PipelineDetails object. + pipeline: Option, + + /// The status of this pipeline's ScriptTask. + ready_state: ReadyState, + + /// The status of this pipeline's PaintTask. + paint_state: PaintState, +} + +impl PipelineDetails { + fn new() -> PipelineDetails { + PipelineDetails { + pipeline: None, + ready_state: ReadyState::Blank, + paint_state: PaintState::Painting, + } + } +} + impl IOCompositor { fn new(window: Rc, sender: Box, @@ -178,6 +193,7 @@ impl IOCompositor { port: receiver, context: None, root_pipeline: None, + pipeline_details: HashMap::new(), scene: Scene::new(Rect { origin: Point2D::zero(), size: window_size.as_f32(), @@ -192,8 +208,6 @@ impl IOCompositor { viewport_zoom: ScaleFactor(1.0), zoom_action: false, zoom_time: 0f64, - ready_states: HashMap::new(), - paint_states: HashMap::new(), got_load_complete_message: false, got_set_frame_tree_message: false, constellation_chan: constellation_chan, @@ -202,7 +216,6 @@ impl IOCompositor { fragment_point: None, outstanding_paint_msgs: 0, last_composite_time: 0, - paint_channels: HashMap::new(), } } @@ -347,7 +360,7 @@ impl IOCompositor { } (Msg::PaintTaskExited(pipeline_id), ShutdownState::NotShuttingDown) => { - if self.paint_channels.remove(&pipeline_id).is_none() { + if self.pipeline_details.remove(&pipeline_id).is_none() { panic!("Saw PaintTaskExited message from an unknown pipeline!"); } } @@ -362,14 +375,7 @@ impl IOCompositor { } fn change_ready_state(&mut self, pipeline_id: PipelineId, ready_state: ReadyState) { - match self.ready_states.entry(pipeline_id) { - Occupied(entry) => { - *entry.into_mut() = ready_state; - } - Vacant(entry) => { - entry.set(ready_state); - } - } + self.get_or_create_pipeline_details(pipeline_id).ready_state = ready_state; self.window.set_ready_state(self.get_earliest_pipeline_ready_state()); // If we're painting in headless mode, schedule a recomposite. @@ -379,27 +385,43 @@ impl IOCompositor { } fn get_earliest_pipeline_ready_state(&self) -> ReadyState { - if self.ready_states.len() == 0 { + if self.pipeline_details.len() == 0 { return ReadyState::Blank; } - return self.ready_states.values().fold(ReadyState::FinishedLoading, - |a, &b| cmp::min(a, b)); - + return self.pipeline_details.values().fold(ReadyState::FinishedLoading, + |v, ref details| { + cmp::min(v, details.ready_state) + }); } fn change_paint_state(&mut self, pipeline_id: PipelineId, paint_state: PaintState) { - match self.paint_states.entry(pipeline_id) { - Occupied(entry) => { - *entry.into_mut() = paint_state; - } - Vacant(entry) => { - entry.set(paint_state); - } - } - + self.get_or_create_pipeline_details(pipeline_id).paint_state = paint_state; self.window.set_paint_state(paint_state); } + pub fn get_or_create_pipeline_details<'a>(&'a mut self, + pipeline_id: PipelineId) + -> &'a mut PipelineDetails { + if !self.pipeline_details.contains_key(&pipeline_id) { + self.pipeline_details.insert(pipeline_id, PipelineDetails::new()); + } + return self.pipeline_details.get_mut(&pipeline_id).unwrap(); + } + + pub fn get_pipeline<'a>(&'a self, pipeline_id: PipelineId) -> &'a CompositionPipeline { + match self.pipeline_details.get(&pipeline_id) { + Some(ref details) => { + match details.pipeline { + Some(ref pipeline) => pipeline, + None => panic!("Compositor layer has an unitialized pipeline ({}).", + pipeline_id), + + } + } + None => panic!("Compositor layer has an unknown pipeline ({}).", pipeline_id), + } + } + fn change_page_title(&mut self, _: PipelineId, title: Option) { self.window.set_page_title(title); } @@ -409,10 +431,12 @@ impl IOCompositor { } fn all_pipelines_in_idle_paint_state(&self) -> bool { - if self.ready_states.len() == 0 { + if self.pipeline_details.len() == 0 { return false; } - return self.paint_states.values().all(|&value| value == PaintState::Idle); + return self.pipeline_details.values().all(|ref details| { + details.paint_state == PaintState::Idle + }); } fn has_paint_msg_tracking(&self) -> bool { @@ -454,7 +478,7 @@ impl IOCompositor { // If we have an old root layer, release all old tiles before replacing it. match self.scene.root { - Some(ref mut layer) => layer.clear_all_tiles(), + Some(ref layer) => layer.clear_all_tiles(self), None => { } } self.scene.root = Some(self.create_frame_tree_root_layers(frame_tree, None)); @@ -481,13 +505,11 @@ impl IOCompositor { scroll_policy: ScrollPolicy::Scrollable, }; - let root_layer = CompositorData::new_layer(pipeline.clone(), - layer_properties, + let root_layer = CompositorData::new_layer(layer_properties, WantsScrollEventsFlag::WantsScrollEvents, opts::get().tile_size); - if !self.paint_channels.contains_key(&pipeline.id) { - self.paint_channels.insert(pipeline.id, pipeline.paint_chan.clone()); - } + + self.get_or_create_pipeline_details(pipeline.id).pipeline = Some(pipeline.clone()); // All root layers mask to bounds. *root_layer.masks_to_bounds.borrow_mut() = true; @@ -504,10 +526,6 @@ impl IOCompositor { frame_tree: &SendableFrameTree, frame_rect: Option>) -> Rc> { - // Initialize the ReadyState and PaintState for this pipeline. - self.ready_states.insert(frame_tree.pipeline.id, ReadyState::Blank); - self.paint_states.insert(frame_tree.pipeline.id, PaintState::Painting); - let root_layer = self.create_root_layer_for_pipeline_and_rect(&frame_tree.pipeline, frame_rect); for kid in frame_tree.children.iter() { @@ -517,17 +535,25 @@ impl IOCompositor { } fn update_frame_tree(&mut self, frame_tree_diff: &FrameTreeDiff) { - let parent_layer = self.find_pipeline_root_layer(frame_tree_diff.parent_pipeline.id); + let pipeline_id = frame_tree_diff.parent_pipeline.id; + let parent_layer = match self.find_pipeline_root_layer(pipeline_id) { + Some(root_layer) => root_layer, + None => { + debug!("Ignoring FrameTreeUpdate message for pipeline ({}) shutting down.", + pipeline_id); + return; + } + }; parent_layer.add_child( self.create_root_layer_for_pipeline_and_rect(&frame_tree_diff.pipeline, frame_tree_diff.rect)); } - fn find_pipeline_root_layer(&self, pipeline_id: PipelineId) -> Rc> { - match self.find_layer_with_pipeline_and_layer_id(pipeline_id, LayerId::null()) { - Some(ref layer) => layer.clone(), - None => panic!("Tried to create or update layer for unknown pipeline"), + fn find_pipeline_root_layer(&self, pipeline_id: PipelineId) -> Option>> { + if !self.pipeline_details.contains_key(&pipeline_id) { + panic!("Tried to create or update layer for unknown pipeline") } + self.find_layer_with_pipeline_and_layer_id(pipeline_id, LayerId::null()) } fn update_layer_if_exists(&mut self, properties: LayerProperties) -> bool { @@ -541,14 +567,21 @@ impl IOCompositor { } fn create_or_update_base_layer(&mut self, layer_properties: LayerProperties) { + let pipeline_id = layer_properties.pipeline_id; + let root_layer = match self.find_pipeline_root_layer(pipeline_id) { + Some(root_layer) => root_layer, + None => { + debug!("Ignoring CreateOrUpdateBaseLayer message for pipeline ({}) shutting down.", + pipeline_id); + return; + } + }; + let need_new_base_layer = !self.update_layer_if_exists(layer_properties); if need_new_base_layer { - let root_layer = self.find_pipeline_root_layer(layer_properties.pipeline_id); root_layer.update_layer_except_bounds(layer_properties); - let root_layer_pipeline = root_layer.extra_data.borrow().pipeline.clone(); let base_layer = CompositorData::new_layer( - root_layer_pipeline.clone(), layer_properties, WantsScrollEventsFlag::DoesntWantScrollEvents, opts::get().tile_size); @@ -575,10 +608,12 @@ impl IOCompositor { } fn create_descendant_layer(&self, layer_properties: LayerProperties) { - let root_layer = self.find_pipeline_root_layer(layer_properties.pipeline_id); - let root_layer_pipeline = root_layer.extra_data.borrow().pipeline.clone(); - let new_layer = CompositorData::new_layer(root_layer_pipeline, - layer_properties, + let root_layer = match self.find_pipeline_root_layer(layer_properties.pipeline_id) { + Some(root_layer) => root_layer, + None => return, // This pipeline is in the process of shutting down. + }; + + let new_layer = CompositorData::new_layer(layer_properties, WantsScrollEventsFlag::DoesntWantScrollEvents, root_layer.tile_size); root_layer.add_child(new_layer); @@ -660,17 +695,16 @@ impl IOCompositor { new_layer_buffer_set: Box, epoch: Epoch) { match self.find_layer_with_pipeline_and_layer_id(pipeline_id, layer_id) { - Some(layer) => self.assign_painted_buffers_to_layer(layer, new_layer_buffer_set, epoch), - None => { - match self.paint_channels.entry(pipeline_id) { - Occupied(entry) => { - let message = PaintMsg::UnusedBuffer(new_layer_buffer_set.buffers); - let _ = entry.get().send_opt(message); - }, - Vacant(_) => panic!("Received a buffer from an unknown pipeline!"), - } + Some(layer) => { + self.assign_painted_buffers_to_layer(layer, new_layer_buffer_set, epoch); + return; } + None => {} } + + let pipeline = self.get_pipeline(pipeline_id); + let message = PaintMsg::UnusedBuffer(new_layer_buffer_set.buffers); + let _ = pipeline.paint_chan.send_opt(message); } fn assign_painted_buffers_to_layer(&mut self, @@ -687,7 +721,7 @@ impl IOCompositor { // FIXME(pcwalton): This is going to cause problems with inconsistent frames since // we only composite one layer at a time. - assert!(layer.add_buffers(new_layer_buffer_set, epoch)); + assert!(layer.add_buffers(self, new_layer_buffer_set, epoch)); self.composite_if_necessary(); } @@ -785,7 +819,7 @@ impl IOCompositor { debug!("osmain: loading URL `{}`", url_string); self.got_load_complete_message = false; let root_pipeline_id = match self.scene.root { - Some(ref layer) => layer.extra_data.borrow().pipeline.id.clone(), + Some(ref layer) => layer.get_pipeline_id(), None => panic!("Compositor: Received WindowEvent::LoadUrl without initialized compositor \ layers"), }; @@ -803,14 +837,14 @@ impl IOCompositor { MouseWindowEvent::MouseUp(_, p) => p, }; match self.find_topmost_layer_at_point(point / self.scene.scale) { - Some(result) => result.layer.send_mouse_event(mouse_window_event, result.point), + Some(result) => result.layer.send_mouse_event(self, mouse_window_event, result.point), None => {}, } } fn on_mouse_window_move_event_class(&self, cursor: TypedPoint2D) { match self.find_topmost_layer_at_point(cursor / self.scene.scale) { - Some(result) => result.layer.send_mouse_move_event(result.point), + Some(result) => result.layer.send_mouse_move_event(self, result.point), None => {}, } } @@ -922,25 +956,21 @@ impl IOCompositor { fn convert_buffer_requests_to_pipeline_requests_map(&self, requests: Vec<(Rc>, - Vec)>) -> - HashMap)> { + Vec)>) + -> HashMap> { let scale = self.device_pixels_per_page_px(); - let mut results: - HashMap)> = HashMap::new(); + let mut results: HashMap> = HashMap::new(); for (layer, mut layer_requests) in requests.into_iter() { - let &(_, ref mut vec) = - match results.entry(layer.extra_data.borrow().pipeline.id) { - Occupied(mut entry) => { - *entry.get_mut() = - (layer.extra_data.borrow().pipeline.paint_chan.clone(), vec!()); - entry.into_mut() - } - Vacant(entry) => { - entry.set((layer.extra_data.borrow().pipeline.paint_chan.clone(), vec!())) - } - }; + let vec = match results.entry(layer.get_pipeline_id()) { + Occupied(mut entry) => { + *entry.get_mut() = Vec::new(); + entry.into_mut() + } + Vacant(entry) => { + entry.set(Vec::new()) + } + }; // All the BufferRequests are in layer/device coordinates, but the paint task // wants to know the page coordinates. We scale them before sending them. @@ -976,7 +1006,7 @@ impl IOCompositor { if layer.extra_data.borrow().id == LayerId::null() { let layer_rect = Rect(-layer.extra_data.borrow().scroll_offset.to_untyped(), layer.bounds.borrow().size.to_untyped()); - let pipeline = &layer.extra_data.borrow().pipeline; + let pipeline = self.get_pipeline(layer.get_pipeline_id()); let ScriptControlChan(ref chan) = pipeline.script_chan; chan.send(ConstellationControlMsg::Viewport(pipeline.id.clone(), layer_rect)); } @@ -1012,9 +1042,9 @@ impl IOCompositor { self.convert_buffer_requests_to_pipeline_requests_map(layers_and_requests); let mut num_paint_msgs_sent = 0; - for (_pipeline_id, (chan, requests)) in pipeline_requests.into_iter() { + for (pipeline_id, requests) in pipeline_requests.into_iter() { num_paint_msgs_sent += 1; - let _ = chan.send_opt(PaintMsg::Paint(requests)); + let _ = self.get_pipeline(pipeline_id).paint_chan.send_opt(PaintMsg::Paint(requests)); } self.add_outstanding_paint_msg(num_paint_msgs_sent); @@ -1232,7 +1262,7 @@ fn find_layer_with_pipeline_and_layer_id_for_layer(layer: Rc Option>> { - if layer.extra_data.borrow().pipeline.id == pipeline_id && + if layer.extra_data.borrow().pipeline_id == pipeline_id && layer.extra_data.borrow().id == layer_id { return Some(layer); } diff --git a/components/compositing/compositor_layer.rs b/components/compositing/compositor_layer.rs index ba6d969c0e1..9afb50cb005 100644 --- a/components/compositing/compositor_layer.rs +++ b/components/compositing/compositor_layer.rs @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use compositor_task::LayerProperties; -use pipeline::CompositionPipeline; +use compositor::IOCompositor; use windowing::{MouseWindowEvent, WindowMethods}; use azure::azure_hl; @@ -20,13 +20,15 @@ use layers::platform::surface::NativeSurfaceMethods; use script_traits::CompositorEvent::{ClickEvent, MouseDownEvent, MouseMoveEvent, MouseUpEvent}; use script_traits::{ScriptControlChan, ConstellationControlMsg}; use servo_msg::compositor_msg::{Epoch, LayerId, ScrollPolicy}; +use servo_msg::constellation_msg::PipelineId; use std::num::Float; use std::num::FloatMath; use std::rc::Rc; pub struct CompositorData { - /// This layer's pipeline. BufferRequests and mouse events will be sent through this. - pub pipeline: CompositionPipeline, + /// This layer's pipeline id. The compositor can associate this id with an + /// actual CompositionPipeline. + pub pipeline_id: PipelineId, /// The ID of this layer within the pipeline. pub id: LayerId, @@ -47,13 +49,12 @@ pub struct CompositorData { } impl CompositorData { - pub fn new_layer(pipeline: CompositionPipeline, - layer_properties: LayerProperties, + pub fn new_layer(layer_properties: LayerProperties, wants_scroll_events: WantsScrollEventsFlag, tile_size: uint) -> Rc> { let new_compositor_data = CompositorData { - pipeline: pipeline, + pipeline_id: layer_properties.pipeline_id, id: layer_properties.id, wants_scroll_events: wants_scroll_events, scroll_policy: layer_properties.scroll_policy, @@ -68,20 +69,24 @@ impl CompositorData { } } -pub trait CompositorLayer { +pub trait CompositorLayer { fn update_layer_except_bounds(&self, layer_properties: LayerProperties); fn update_layer(&self, layer_properties: LayerProperties); - fn add_buffers(&self, new_buffers: Box, epoch: Epoch) -> bool; + fn add_buffers(&self, + compositor: &IOCompositor, + new_buffers: Box, + epoch: Epoch) + -> bool; /// Destroys all layer tiles, sending the buffers back to the painter to be destroyed or /// reused. - fn clear(&self); + fn clear(&self, compositor: &IOCompositor); /// Destroys tiles for this layer and all descendent layers, sending the buffers back to the /// painter to be destroyed or reused. - fn clear_all_tiles(&self); + fn clear_all_tiles(&self, compositor: &IOCompositor); /// Destroys all tiles of all layers, including children, *without* sending them back to the /// painter. You must call this only when the paint task is destined to be going down; @@ -104,10 +109,12 @@ pub trait CompositorLayer { // sends the event off to the appropriate pipeline. NB: the cursor position is in // page coordinates. fn send_mouse_event(&self, + compositor: &IOCompositor, event: MouseWindowEvent, cursor: TypedPoint2D); fn send_mouse_move_event(&self, + compositor: &IOCompositor, cursor: TypedPoint2D); fn clamp_scroll_offset_and_scroll_layer(&self, @@ -120,6 +127,9 @@ pub trait CompositorLayer { /// Return a flag describing how this layer deals with scroll events. fn wants_scroll_events(&self) -> WantsScrollEventsFlag; + + /// Return the pipeline id associated with this layer. + fn get_pipeline_id(&self) -> PipelineId; } #[deriving(Copy, PartialEq, Clone)] @@ -165,7 +175,7 @@ pub enum ScrollEventResult { ScrollPositionUnchanged, } -impl CompositorLayer for Layer { +impl CompositorLayer for Layer { fn update_layer_except_bounds(&self, layer_properties: LayerProperties) { self.extra_data.borrow_mut().epoch = layer_properties.epoch; self.extra_data.borrow_mut().scroll_policy = layer_properties.scroll_policy; @@ -190,33 +200,35 @@ impl CompositorLayer for Layer { // // If the epoch of the message does not match the layer's epoch, the message is ignored, the // layer buffer set is consumed, and None is returned. - fn add_buffers(&self, new_buffers: Box, epoch: Epoch) -> bool { + fn add_buffers(&self, + compositor: &IOCompositor, + new_buffers: Box, + epoch: Epoch) + -> bool { if self.extra_data.borrow().epoch != epoch { debug!("add_buffers: compositor epoch mismatch: {} != {}, id: {}", self.extra_data.borrow().epoch, epoch, - self.extra_data.borrow().pipeline.id); - let msg = PaintMsg::UnusedBuffer(new_buffers.buffers); - let _ = self.extra_data.borrow().pipeline.paint_chan.send_opt(msg); + self.get_pipeline_id()); + let pipeline = compositor.get_pipeline(self.get_pipeline_id()); + let _ = pipeline.paint_chan.send_opt(PaintMsg::UnusedBuffer(new_buffers.buffers)); return false; } - { - for buffer in new_buffers.buffers.into_iter().rev() { - self.add_buffer(buffer); - } + for buffer in new_buffers.buffers.into_iter().rev() { + self.add_buffer(buffer); + } - let unused_buffers = self.collect_unused_buffers(); - if !unused_buffers.is_empty() { // send back unused buffers - let msg = PaintMsg::UnusedBuffer(unused_buffers); - let _ = self.extra_data.borrow().pipeline.paint_chan.send_opt(msg); - } + let unused_buffers = self.collect_unused_buffers(); + if !unused_buffers.is_empty() { // send back unused buffers + let pipeline = compositor.get_pipeline(self.get_pipeline_id()); + let _ = pipeline.paint_chan.send_opt(PaintMsg::UnusedBuffer(unused_buffers)); } return true; } - fn clear(&self) { + fn clear(&self, compositor: &IOCompositor) { let mut buffers = self.collect_buffers(); if !buffers.is_empty() { @@ -227,16 +239,17 @@ impl CompositorLayer for Layer { buffer.mark_wont_leak() } - let _ = self.extra_data.borrow().pipeline.paint_chan.send_opt(PaintMsg::UnusedBuffer(buffers)); + let pipeline = compositor.get_pipeline(self.get_pipeline_id()); + let _ = pipeline.paint_chan.send_opt(PaintMsg::UnusedBuffer(buffers)); } } /// Destroys tiles for this layer and all descendent layers, sending the buffers back to the /// painter to be destroyed or reused. - fn clear_all_tiles(&self) { - self.clear(); + fn clear_all_tiles(&self, compositor: &IOCompositor) { + self.clear(compositor); for kid in self.children().iter() { - kid.clear_all_tiles(); + kid.clear_all_tiles(compositor); } } @@ -314,6 +327,7 @@ impl CompositorLayer for Layer { } fn send_mouse_event(&self, + compositor: &IOCompositor, event: MouseWindowEvent, cursor: TypedPoint2D) { let event_point = cursor.to_untyped(); @@ -325,15 +339,17 @@ impl CompositorLayer for Layer { MouseWindowEvent::MouseUp(button, _) => MouseUpEvent(button, event_point), }; - let pipeline = &self.extra_data.borrow().pipeline; + + let pipeline = compositor.get_pipeline(self.get_pipeline_id()); let ScriptControlChan(ref chan) = pipeline.script_chan; let _ = chan.send_opt(ConstellationControlMsg::SendEvent(pipeline.id.clone(), message)); } fn send_mouse_move_event(&self, + compositor: &IOCompositor, cursor: TypedPoint2D) { let message = MouseMoveEvent(cursor.to_untyped()); - let pipeline = &self.extra_data.borrow().pipeline; + let pipeline = compositor.get_pipeline(self.get_pipeline_id()); let ScriptControlChan(ref chan) = pipeline.script_chan; let _ = chan.send_opt(ConstellationControlMsg::SendEvent(pipeline.id.clone(), message)); } @@ -362,4 +378,7 @@ impl CompositorLayer for Layer { self.extra_data.borrow().wants_scroll_events } + fn get_pipeline_id(&self) -> PipelineId { + self.extra_data.borrow().pipeline_id + } }