diff --git a/src/components/compositing/compositor.rs b/src/components/compositing/compositor.rs index 5762277b4d1..c9aa14cee14 100644 --- a/src/components/compositing/compositor.rs +++ b/src/components/compositing/compositor.rs @@ -26,6 +26,7 @@ use geom::point::{Point2D, TypedPoint2D}; use geom::rect::Rect; use geom::size::TypedSize2D; use geom::scale_factor::ScaleFactor; +use gfx::render_task::ReRenderMsg; use layers::layers::LayerBufferSet; use layers::platform::surface::NativeCompositingGraphicsContext; use layers::rendergl; @@ -45,6 +46,7 @@ use servo_util::opts::Opts; use servo_util::time::{profile, TimeProfilerChan}; use servo_util::{memory, time, url}; use std::io::timer::sleep; +use std::collections::hashmap::HashMap; use std::path::Path; use std::rc::Rc; use time::precise_time_s; @@ -301,9 +303,10 @@ impl IOCompositor { self.set_layer_clip_rect(pipeline_id, layer_id, new_rect); } - (Ok(Paint(pipeline_id, layer_id, new_layer_buffer_set, epoch)), - NotShuttingDown) => { - self.paint(pipeline_id, layer_id, new_layer_buffer_set, epoch); + (Ok(Paint(pipeline_id, epoch, replies)), NotShuttingDown) => { + for (layer_id, new_layer_buffer_set) in replies.move_iter() { + self.paint(pipeline_id, layer_id, new_layer_buffer_set, epoch); + } } (Ok(ScrollFragmentPoint(pipeline_id, layer_id, point)), NotShuttingDown) => { @@ -743,11 +746,16 @@ impl IOCompositor { match self.scene.root { Some(ref mut layer) => { let rect = Rect(Point2D(0f32, 0f32), page_window.to_untyped()); + let mut request_map = HashMap::new(); let recomposite = - CompositorData::send_buffer_requests_recursively(layer.clone(), - &self.graphics_context, - rect, - scale.get()); + CompositorData::get_buffer_requests_recursively(&mut request_map, + layer.clone(), + &self.graphics_context, + rect, + scale.get()); + for (_pipeline_id, (chan, requests)) in request_map.move_iter() { + let _ = chan.send_opt(ReRenderMsg(requests)); + } self.recomposite = self.recomposite || recomposite; } None => { } diff --git a/src/components/compositing/compositor_data.rs b/src/components/compositing/compositor_data.rs index 5eca6289b4c..c68a2709d1b 100644 --- a/src/components/compositing/compositor_data.rs +++ b/src/components/compositing/compositor_data.rs @@ -11,7 +11,7 @@ use geom::matrix::identity; use geom::point::TypedPoint2D; use geom::rect::Rect; use geom::size::{Size2D, TypedSize2D}; -use gfx::render_task::{ReRenderMsg, UnusedBufferMsg}; +use gfx::render_task::{ReRenderRequest, RenderChan, UnusedBufferMsg}; use layers::layers::{Layer, Flip, LayerBuffer, LayerBufferSet, NoFlip, TextureLayer}; use layers::quadtree::Tile; use layers::platform::surface::{NativeCompositingGraphicsContext, NativeSurfaceMethods}; @@ -20,6 +20,7 @@ use servo_msg::compositor_msg::{Epoch, FixedPosition, LayerId}; use servo_msg::compositor_msg::ScrollPolicy; use servo_msg::constellation_msg::PipelineId; use servo_util::geometry::PagePx; +use std::collections::hashmap::HashMap; use std::rc::Rc; #[cfg(target_os="macos")] @@ -120,11 +121,14 @@ impl CompositorData { // Given the current window size, determine which tiles need to be (re-)rendered and sends them // off the the appropriate renderer. Returns true if and only if the scene should be repainted. - pub fn send_buffer_requests_recursively(layer: Rc>, - graphics_context: &NativeCompositingGraphicsContext, - window_rect: Rect, - scale: f32) - -> bool { + pub fn get_buffer_requests_recursively(requests: &mut HashMap)>, + layer: Rc>, + graphics_context: &NativeCompositingGraphicsContext, + window_rect: Rect, + scale: f32) + -> bool { let (request, unused) = Layer::get_tile_rects_page(layer.clone(), window_rect, scale); let redisplay = !unused.is_empty(); if redisplay { @@ -134,21 +138,24 @@ impl CompositorData { } if !request.is_empty() { // Ask for tiles. - // - // FIXME(#2003, pcwalton): We may want to batch these up in the case in which - // one page has multiple layers, to avoid the user seeing inconsistent states. - let msg = ReRenderMsg(request, - scale, - layer.extra_data.borrow().id, - layer.extra_data.borrow().epoch); - let _ = layer.extra_data.borrow().pipeline.render_chan.send_opt(msg); + let pipeline_id = layer.extra_data.borrow().pipeline.id; + let msg = ReRenderRequest { + buffer_requests: request, + scale: scale, + layer_id: layer.extra_data.borrow().id, + epoch: layer.extra_data.borrow().epoch, + }; + let &(_, ref mut vec) = requests.find_or_insert_with(pipeline_id, |_| { + (layer.extra_data.borrow().pipeline.render_chan.clone(), Vec::new()) + }); + vec.push(msg); } if redisplay { CompositorData::build_layer_tree(layer.clone(), graphics_context); } - let send_child_buffer_request = |kid: &Rc>| -> bool { + let get_child_buffer_request = |kid: &Rc>| -> bool { let mut new_rect = window_rect; let offset = kid.extra_data.borrow().scroll_offset.to_untyped(); new_rect.origin.x = new_rect.origin.x - offset.x; @@ -160,10 +167,11 @@ impl CompositorData { // to make the child_rect appear in coordinates local to it. let child_rect = Rect(new_rect.origin.sub(&kid.bounds.borrow().origin), new_rect.size); - CompositorData::send_buffer_requests_recursively(kid.clone(), - graphics_context, - child_rect, - scale) + CompositorData::get_buffer_requests_recursively(requests, + kid.clone(), + graphics_context, + child_rect, + scale) } None => { false // Layer is offscreen @@ -171,7 +179,7 @@ impl CompositorData { } }; - layer.children().iter().map(send_child_buffer_request).any(|b| b) || redisplay + layer.children().iter().map(get_child_buffer_request).any(|b| b) || redisplay } // Move the sublayer to an absolute position in page coordinates relative to its parent, diff --git a/src/components/compositing/compositor_task.rs b/src/components/compositing/compositor_task.rs index 7534131a935..b4ffaa6ba81 100644 --- a/src/components/compositing/compositor_task.rs +++ b/src/components/compositing/compositor_task.rs @@ -96,10 +96,9 @@ impl RenderListener for CompositorChan { fn paint(&self, pipeline_id: PipelineId, - layer_id: LayerId, - layer_buffer_set: Box, - epoch: Epoch) { - self.chan.send(Paint(pipeline_id, layer_id, layer_buffer_set, epoch)) + epoch: Epoch, + replies: Vec<(LayerId, Box)>) { + self.chan.send(Paint(pipeline_id, epoch, replies)); } fn initialize_layers_for_pipeline(&self, @@ -180,7 +179,7 @@ pub enum Msg { /// Scroll a page in a window ScrollFragmentPoint(PipelineId, LayerId, Point2D), /// Requests that the compositor paint the given layer buffer set for the given page size. - Paint(PipelineId, LayerId, Box, Epoch), + Paint(PipelineId, Epoch, Vec<(LayerId, Box)>), /// Alerts the compositor to the current status of page loading. ChangeReadyState(ReadyState), /// Alerts the compositor to the current status of rendering. diff --git a/src/components/gfx/render_task.rs b/src/components/gfx/render_task.rs index d6c31bce048..5677ecb629e 100644 --- a/src/components/gfx/render_task.rs +++ b/src/components/gfx/render_task.rs @@ -50,9 +50,16 @@ pub struct RenderLayer { pub scroll_policy: ScrollPolicy, } +pub struct ReRenderRequest { + pub buffer_requests: Vec, + pub scale: f32, + pub layer_id: LayerId, + pub epoch: Epoch, +} + pub enum Msg { RenderMsg(SmallVec1), - ReRenderMsg(Vec, f32, LayerId, Epoch), + ReRenderMsg(Vec), UnusedBufferMsg(Vec>), PaintPermissionGranted, PaintPermissionRevoked, @@ -230,12 +237,26 @@ impl RenderTask { self.epoch, self.render_layers.as_slice()); } - ReRenderMsg(tiles, scale, layer_id, epoch) => { - if self.epoch == epoch { - self.render(tiles, scale, layer_id); - } else { - debug!("renderer epoch mismatch: {:?} != {:?}", self.epoch, epoch); + ReRenderMsg(requests) => { + if !self.paint_permission { + debug!("render_task: render ready msg"); + let ConstellationChan(ref mut c) = self.constellation_chan; + c.send(RendererReadyMsg(self.id)); + continue; } + + let mut replies = Vec::new(); + for ReRenderRequest { buffer_requests, scale, layer_id, epoch } + in requests.move_iter() { + if self.epoch == epoch { + self.render(&mut replies, buffer_requests, scale, layer_id); + } else { + debug!("renderer epoch mismatch: {:?} != {:?}", self.epoch, epoch); + } + } + + debug!("render_task: returning surfaces"); + self.compositor.paint(self.id, self.epoch, replies); } UnusedBufferMsg(unused_buffers) => { for buffer in unused_buffers.move_iter().rev() { @@ -269,10 +290,11 @@ impl RenderTask { } /// Renders one layer and sends the tiles back to the layer. - /// - /// FIXME(pcwalton): We will probably want to eventually send all layers belonging to a page in - /// one transaction, to avoid the user seeing inconsistent states. - fn render(&mut self, tiles: Vec, scale: f32, layer_id: LayerId) { + fn render(&mut self, + replies: &mut Vec<(LayerId, Box)>, + tiles: Vec, + scale: f32, + layer_id: LayerId) { time::profile(time::RenderingCategory, self.time_profiler_chan.clone(), || { // FIXME: Try not to create a new array here. let mut new_buffers = vec!(); @@ -417,14 +439,7 @@ impl RenderTask { buffers: new_buffers, }; - debug!("render_task: returning surface"); - if self.paint_permission { - self.compositor.paint(self.id, render_layer.id, layer_buffer_set, self.epoch); - } else { - debug!("render_task: RendererReadyMsg send"); - let ConstellationChan(ref mut c) = self.constellation_chan; - c.send(RendererReadyMsg(self.id)); - } + replies.push((render_layer.id, layer_buffer_set)); self.compositor.set_render_state(IdleRenderState); }) } diff --git a/src/components/msg/compositor_msg.rs b/src/components/msg/compositor_msg.rs index b1e4ae210a0..48d7600a0ae 100644 --- a/src/components/msg/compositor_msg.rs +++ b/src/components/msg/compositor_msg.rs @@ -102,9 +102,8 @@ pub trait RenderListener { /// Sends new tiles for the given layer to the compositor. fn paint(&self, pipeline_id: PipelineId, - layer_id: LayerId, - layer_buffer_set: Box, - epoch: Epoch); + epoch: Epoch, + replies: Vec<(LayerId, Box)>); fn set_render_state(&self, render_state: RenderState); }