diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/components/compositing/compositing.rs | 3 | ||||
-rw-r--r-- | src/components/compositing/compositor.rs | 179 | ||||
-rw-r--r-- | src/components/compositing/compositor_data.rs | 776 | ||||
-rw-r--r-- | src/components/compositing/compositor_layer.rs | 963 | ||||
-rw-r--r-- | src/components/compositing/compositor_task.rs | 3 | ||||
-rw-r--r-- | src/components/compositing/quadtree.rs | 734 | ||||
-rw-r--r-- | src/components/gfx/buffer_map.rs | 2 | ||||
-rw-r--r-- | src/components/gfx/render_task.rs | 20 | ||||
-rw-r--r-- | src/components/msg/compositor_msg.rs | 76 | ||||
m--------- | src/support/layers/rust-layers | 0 |
10 files changed, 867 insertions, 1889 deletions
diff --git a/src/components/compositing/compositing.rs b/src/components/compositing/compositing.rs index b807559aa30..737acce3b84 100644 --- a/src/components/compositing/compositing.rs +++ b/src/components/compositing/compositing.rs @@ -49,8 +49,7 @@ pub use constellation::Constellation; mod compositor_task; -mod quadtree; -mod compositor_layer; +mod compositor_data; mod compositor; mod headless; diff --git a/src/components/compositing/compositor.rs b/src/components/compositing/compositor.rs index e1a9bbc0c76..5c47064279d 100644 --- a/src/components/compositing/compositor.rs +++ b/src/components/compositing/compositor.rs @@ -2,13 +2,13 @@ * 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/. */ +use compositor_data::CompositorData; use compositor_task::{Msg, CompositorTask, Exit, ChangeReadyState, SetUnRenderedColor}; use compositor_task::{SetIds, GetGraphicsMetadata, CreateRootCompositorLayerIfNecessary}; use compositor_task::{CreateDescendantCompositorLayerIfNecessary, SetLayerPageSize}; use compositor_task::{SetLayerClipRect, Paint, ScrollFragmentPoint, LoadComplete}; use compositor_task::{ShutdownComplete, ChangeRenderState}; use constellation::SendableFrameTree; -use compositor_layer::CompositorLayer; use pipeline::CompositionPipeline; use platform::{Application, Window}; use windowing; @@ -26,14 +26,15 @@ use geom::point::{Point2D, TypedPoint2D}; use geom::rect::Rect; use geom::size::{Size2D, TypedSize2D}; use geom::scale_factor::ScaleFactor; -use layers::layers::{ContainerLayer, ContainerLayerKind}; +use layers::layers::LayerBufferSet; use layers::platform::surface::NativeCompositingGraphicsContext; use layers::rendergl; use layers::rendergl::RenderContext; use layers::scene::Scene; +use layers::layers::ContainerLayer; use opengles::gl2; use png; -use servo_msg::compositor_msg::{Blank, Epoch, FinishedLoading, IdleRenderState, LayerBufferSet}; +use servo_msg::compositor_msg::{Blank, Epoch, FinishedLoading, IdleRenderState}; use servo_msg::compositor_msg::{LayerId, ReadyState, RenderState, ScrollPolicy, Scrollable}; use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, LoadUrlMsg, NavigateMsg}; use servo_msg::constellation_msg::{PipelineId, ResizedWindowMsg, WindowSizeData}; @@ -59,14 +60,11 @@ pub struct IOCompositor { /// The render context. context: RenderContext, - /// The root ContainerLayer. - root_layer: Rc<ContainerLayer>, - /// The root pipeline. root_pipeline: Option<CompositionPipeline>, /// The canvas to paint a page. - scene: Scene, + scene: Scene<CompositorData>, /// The application window size. window_size: TypedSize2D<DevicePixel, uint>, @@ -113,9 +111,6 @@ pub struct IOCompositor { /// The command line option flags. opts: Opts, - /// The root CompositorLayer - compositor_layer: Option<CompositorLayer>, - /// The channel on which messages can be sent to the constellation. constellation_chan: ConstellationChan, @@ -142,7 +137,6 @@ impl IOCompositor { // // TODO: There should be no initial layer tree until the renderer creates one from the display // list. This is only here because we don't have that logic in the renderer yet. - let root_layer = Rc::new(ContainerLayer()); let window_size = window.framebuffer_size(); let hidpi_factor = window.hidpi_factor(); @@ -151,9 +145,8 @@ impl IOCompositor { port: port, opts: opts, context: rendergl::init_render_context(), - root_layer: root_layer.clone(), root_pipeline: None, - scene: Scene(ContainerLayerKind(root_layer), window_size.as_f32().to_untyped(), identity()), + scene: Scene(window_size.as_f32().to_untyped(), identity()), window_size: window_size, hidpi_factor: hidpi_factor, graphics_context: CompositorTask::create_graphics_context(), @@ -167,7 +160,6 @@ impl IOCompositor { zoom_time: 0f64, ready_state: Blank, load_complete: false, - compositor_layer: None, constellation_chan: constellation_chan, time_profiler_chan: time_profiler_chan, memory_profiler_chan: memory_profiler_chan, @@ -230,9 +222,9 @@ impl IOCompositor { } // Clear out the compositor layers so that painting tasks can destroy the buffers. - match self.compositor_layer { + match self.scene.root { None => {} - Some(ref mut layer) => layer.forget_all_tiles(), + Some(ref layer) => CompositorData::forget_all_tiles(layer.clone()), } // Drain compositor port, sometimes messages contain channels that are blocking @@ -343,8 +335,8 @@ impl IOCompositor { } fn set_unrendered_color(&mut self, pipeline_id: PipelineId, layer_id: LayerId, color: Color) { - match self.compositor_layer { - Some(ref mut layer) => layer.set_unrendered_color(pipeline_id, layer_id, color), + match self.scene.root { + Some(ref layer) => CompositorData::set_unrendered_color(layer.clone(), pipeline_id, layer_id, color), None => false, }; } @@ -367,9 +359,9 @@ impl IOCompositor { layer_id: LayerId, size: Size2D<f32>, unrendered_color: Color) { - let (root_pipeline, root_layer_id) = match self.compositor_layer { - Some(ref compositor_layer) if compositor_layer.pipeline.id == id => { - (compositor_layer.pipeline.clone(), compositor_layer.id_of_first_child()) + let (root_pipeline, root_layer_id) = match self.scene.root { + Some(ref root_layer) if root_layer.extra_data.borrow().pipeline.id == id => { + (root_layer.extra_data.borrow().pipeline.clone(), CompositorData::id_of_first_child(root_layer.clone())) } _ => { match self.root_pipeline { @@ -383,31 +375,25 @@ impl IOCompositor { if layer_id != root_layer_id { let root_pipeline_id = root_pipeline.id; - let mut new_layer = CompositorLayer::new_root(root_pipeline, - size, - self.opts.tile_size, - self.opts.cpu_painting); - new_layer.unrendered_color = unrendered_color; - - self.root_layer.remove_all_children(); - - let new_layer_id = new_layer.id; - assert!(new_layer.add_child_if_necessary(self.root_layer.clone(), - root_pipeline_id, - new_layer_id, - layer_id, - Rect(Point2D(0f32, 0f32), size), - size, - Scrollable)); - - ContainerLayer::add_child_start(self.root_layer.clone(), - ContainerLayerKind(new_layer.root_layer.clone())); + let new_root = Rc::new(ContainerLayer::new(Some(size), self.opts.tile_size, + CompositorData::new_root(root_pipeline, + size, self.opts.cpu_painting))); + new_root.extra_data.borrow_mut().unrendered_color = unrendered_color; + + let parent_layer_id = new_root.extra_data.borrow().id; + assert!(CompositorData::add_child_if_necessary(new_root.clone(), + root_pipeline_id, + parent_layer_id, + layer_id, + Rect(Point2D(0f32, 0f32), size), + size, + Scrollable)); // Release all tiles from the layer before dropping it. - for layer in self.compositor_layer.mut_iter() { - layer.clear_all_tiles(); + for layer in self.scene.root.mut_iter() { + CompositorData::clear_all_tiles(layer.clone()); } - self.compositor_layer = Some(new_layer); + self.scene.root = Some(new_root); } self.ask_for_tiles(); @@ -418,17 +404,17 @@ impl IOCompositor { layer_id: LayerId, rect: Rect<f32>, scroll_policy: ScrollPolicy) { - match self.compositor_layer { - Some(ref mut compositor_layer) => { - let compositor_layer_id = compositor_layer.id; - let page_size = compositor_layer.page_size.unwrap(); - assert!(compositor_layer.add_child_if_necessary(self.root_layer.clone(), - pipeline_id, - compositor_layer_id, - layer_id, - rect, - page_size, - scroll_policy)) + match self.scene.root { + Some(ref root) => { + let parent_layer_id = root.extra_data.borrow().id; + let page_size = root.extra_data.borrow().page_size.unwrap(); + assert!(CompositorData::add_child_if_necessary(root.clone(), + pipeline_id, + parent_layer_id, + layer_id, + rect, + page_size, + scroll_policy)) } None => fail!("Compositor: Received new layer without initialized pipeline"), }; @@ -460,11 +446,11 @@ impl IOCompositor { new_size: Size2D<f32>, epoch: Epoch) { let page_window = self.page_window(); - let (ask, move): (bool, bool) = match self.compositor_layer { - Some(ref mut layer) => { - layer.resize(pipeline_id, layer_id, new_size, page_window, epoch); + let (ask, move): (bool, bool) = match self.scene.root { + Some(ref layer) => { + CompositorData::resize(layer.clone(), pipeline_id, layer_id, new_size, page_window, epoch); let move = self.fragment_point.take().map_or(false, |point| { - layer.move(pipeline_id, layer_id, point, page_window) + CompositorData::move(layer.clone(), pipeline_id, layer_id, point, page_window) }); (true, move) @@ -482,9 +468,9 @@ impl IOCompositor { pipeline_id: PipelineId, layer_id: LayerId, new_rect: Rect<f32>) { - let ask: bool = match self.compositor_layer { - Some(ref mut layer) => { - assert!(layer.set_clipping_rect(pipeline_id, layer_id, new_rect)); + let ask: bool = match self.scene.root { + Some(ref layer) => { + assert!(CompositorData::set_clipping_rect(layer.clone(), pipeline_id, layer_id, new_rect)); true } None => { @@ -508,17 +494,18 @@ impl IOCompositor { let mut new_layer_buffer_set = new_layer_buffer_set; new_layer_buffer_set.mark_will_leak(); - match self.compositor_layer { - Some(ref mut layer) => { - assert!(layer.add_buffers(&self.graphics_context, - pipeline_id, - layer_id, - new_layer_buffer_set, - epoch).is_none()); + match self.scene.root { + Some(ref layer) => { + assert!(CompositorData::add_buffers(layer.clone(), + &self.graphics_context, + pipeline_id, + layer_id, + new_layer_buffer_set, + epoch).is_none()); self.recomposite = true; } None => { - fail!("compositor given paint command with no CompositorLayer initialized"); + fail!("compositor given paint command with no root layer initialized"); } } @@ -531,9 +518,9 @@ impl IOCompositor { layer_id: LayerId, point: Point2D<f32>) { let page_window = self.page_window(); - let (ask, move): (bool, bool) = match self.compositor_layer { - Some(ref mut layer) if layer.pipeline.id == pipeline_id && !layer.hidden => { - (true, layer.move(pipeline_id, layer_id, point, page_window)) + let (ask, move): (bool, bool) = match self.scene.root { + Some(ref layer) if layer.extra_data.borrow().pipeline.id == pipeline_id && !layer.extra_data.borrow().hidden => { + (true, CompositorData::move(layer.clone(), pipeline_id, layer_id, point, page_window)) } Some(_) | None => { self.fragment_point = Some(point); @@ -626,8 +613,8 @@ impl IOCompositor { fn on_load_url_window_event(&mut self, url_string: String) { debug!("osmain: loading URL `{:s}`", url_string); self.load_complete = false; - let root_pipeline_id = match self.compositor_layer { - Some(ref layer) => layer.pipeline.id.clone(), + let root_pipeline_id = match self.scene.root { + Some(ref layer) => layer.extra_data.borrow().pipeline.id.clone(), None => fail!("Compositor: Received LoadUrlWindowEvent without initialized compositor layers"), }; @@ -643,15 +630,15 @@ impl IOCompositor { MouseWindowMouseDownEvent(_, p) => p / scale, MouseWindowMouseUpEvent(_, p) => p / scale, }; - for layer in self.compositor_layer.iter() { - layer.send_mouse_event(mouse_window_event, point); + for layer in self.scene.root.iter() { + CompositorData::send_mouse_event(layer.clone(), mouse_window_event, point); } } fn on_mouse_window_move_event_class(&self, cursor: TypedPoint2D<DevicePixel, f32>) { let scale = self.device_pixels_per_page_px(); - for layer in self.compositor_layer.iter() { - layer.send_mouse_move_event(cursor / scale); + for layer in self.scene.root.iter() { + CompositorData::send_mouse_move_event(layer.clone(), cursor / scale); } } @@ -664,8 +651,8 @@ impl IOCompositor { let page_cursor = cursor.as_f32() / scale; let page_window = self.page_window(); let mut scroll = false; - for layer in self.compositor_layer.mut_iter() { - scroll = layer.handle_scroll_event(page_delta, page_cursor, page_window) || scroll; + for layer in self.scene.root.mut_iter() { + scroll = CompositorData::handle_scroll_event(layer.clone(), page_delta, page_cursor, page_window) || scroll; } self.recomposite_if(scroll); self.ask_for_tiles(); @@ -687,7 +674,7 @@ impl IOCompositor { fn update_zoom_transform(&mut self) { let scale = self.device_pixels_per_page_px(); - self.root_layer.common.borrow_mut().set_transform(identity().scale(scale.get(), scale.get(), 1f32)); + self.scene.set_transform(identity().scale(scale.get(), scale.get(), 1f32)); } fn on_zoom_window_event(&mut self, magnification: f32) { @@ -715,8 +702,8 @@ impl IOCompositor { let page_cursor = TypedPoint2D(-1f32, -1f32); // Make sure this hits the base layer let page_window = self.page_window(); - for layer in self.compositor_layer.mut_iter() { - layer.handle_scroll_event(page_delta, page_cursor, page_window); + for layer in self.scene.root.mut_iter() { + CompositorData::handle_scroll_event(layer.clone(), page_delta, page_cursor, page_window); } self.recomposite = true; @@ -735,13 +722,13 @@ impl IOCompositor { fn ask_for_tiles(&mut self) { let scale = self.device_pixels_per_page_px(); let page_window = self.page_window(); - for layer in self.compositor_layer.mut_iter() { - if !layer.hidden { + for layer in self.scene.root.mut_iter() { + if !layer.extra_data.borrow().hidden { let rect = Rect(Point2D(0f32, 0f32), page_window.to_untyped()); - let recomposite = layer.get_buffer_request(&self.graphics_context, - rect, - scale.get()) || - self.recomposite; + let recomposite = CompositorData::get_buffer_request(layer.clone(), + &self.graphics_context, + rect, + scale.get()) || self.recomposite; self.recomposite = recomposite; } else { debug!("Compositor: root layer is hidden!"); @@ -755,16 +742,16 @@ impl IOCompositor { // Adjust the layer dimensions as necessary to correspond to the size of the window. self.scene.size = self.window_size.as_f32().to_untyped(); // Render the scene. - match self.compositor_layer { - Some(ref mut layer) => { - self.scene.background_color.r = layer.unrendered_color.r; - self.scene.background_color.g = layer.unrendered_color.g; - self.scene.background_color.b = layer.unrendered_color.b; - self.scene.background_color.a = layer.unrendered_color.a; + match self.scene.root { + Some(ref layer) => { + self.scene.background_color.r = layer.extra_data.borrow().unrendered_color.r; + self.scene.background_color.g = layer.extra_data.borrow().unrendered_color.g; + self.scene.background_color.b = layer.extra_data.borrow().unrendered_color.b; + self.scene.background_color.a = layer.extra_data.borrow().unrendered_color.a; + rendergl::render_scene(layer.clone(), self.context, &self.scene); } None => {} } - rendergl::render_scene(self.context, &self.scene); }); // Render to PNG. We must read from the back buffer (ie, before diff --git a/src/components/compositing/compositor_data.rs b/src/components/compositing/compositor_data.rs new file mode 100644 index 00000000000..19cbfe20b15 --- /dev/null +++ b/src/components/compositing/compositor_data.rs @@ -0,0 +1,776 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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/. */ + +use pipeline::CompositionPipeline; +use windowing::{MouseWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent}; +use windowing::{MouseWindowMouseUpEvent}; + +use azure::azure_hl::Color; +use geom::length::Length; +use geom::matrix::identity; +use geom::point::{Point2D, TypedPoint2D}; +use geom::rect::{Rect, TypedRect}; +use geom::size::{Size2D, TypedSize2D}; +use gfx::render_task::{ReRenderMsg, UnusedBufferMsg}; +use gfx; +use layers::layers::{ContainerLayer, Flip, LayerBuffer, LayerBufferSet, NoFlip, TextureLayer}; +use layers::quadtree::{Tile, Normal, Hidden}; +use layers::platform::surface::{NativeCompositingGraphicsContext, NativeSurfaceMethods}; +use layers::texturegl::{Texture, TextureTarget}; +use script::dom::event::{ClickEvent, MouseDownEvent, MouseMoveEvent, MouseUpEvent}; +use script::script_task::{ScriptChan, SendEventMsg}; +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::rc::Rc; + +#[cfg(target_os="macos")] +#[cfg(target_os="android")] +use layers::layers::VerticalFlip; +#[cfg(not(target_os="macos"))] +use layers::texturegl::TextureTarget2D; +#[cfg(target_os="macos")] +use layers::texturegl::TextureTargetRectangle; + +pub struct CompositorData { + /// This layer's pipeline. BufferRequests and mouse events will be sent through this. + pub pipeline: CompositionPipeline, + + /// The ID of this layer within the pipeline. + pub id: LayerId, + + /// The offset of the page due to scrolling. (0,0) is when the window sees the + /// top left corner of the page. + pub scroll_offset: TypedPoint2D<PagePx, f32>, + + /// The bounds of this layer in terms of its parent (a.k.a. the scissor box). + pub bounds: Rect<f32>, + + /// The size of the underlying page in page coordinates. This is an option + /// because we may not know the size of the page until layout is finished completely. + /// if we have no size yet, the layer is hidden until a size message is recieved. + pub page_size: Option<Size2D<f32>>, + + /// When set to true, this layer is ignored by its parents. This is useful for + /// soft deletion or when waiting on a page size. + pub hidden: bool, + + /// The behavior of this layer when a scroll message is received. + pub wants_scroll_events: WantsScrollEventsFlag, + + /// Whether an ancestor layer that receives scroll events moves this layer. + pub scroll_policy: ScrollPolicy, + + /// True if CPU rendering is enabled, false if we're using GPU rendering. + pub cpu_painting: bool, + + /// The color to use for the unrendered-content void + pub unrendered_color: Color, + + pub scissor: Option<Rect<f32>>, + + /// A monotonically increasing counter that keeps track of the current epoch. + /// add_buffer() calls that don't match the current epoch will be ignored. + pub epoch: Epoch, +} + +#[deriving(PartialEq, Clone)] +pub enum WantsScrollEventsFlag { + WantsScrollEvents, + DoesntWantScrollEvents, +} + +trait Clampable { + fn clamp(&self, mn: &Self, mx: &Self) -> Self; +} + +impl Clampable for f32 { + /// Returns the number constrained within the range `mn <= self <= mx`. + /// If any of the numbers are `NAN` then `NAN` is returned. + #[inline] + fn clamp(&self, mn: &f32, mx: &f32) -> f32 { + match () { + _ if self.is_nan() => *self, + _ if !(*self <= *mx) => *mx, + _ if !(*self >= *mn) => *mn, + _ => *self, + } + } +} + +impl CompositorData { + pub fn new(pipeline: CompositionPipeline, + layer_id: LayerId, + bounds: Rect<f32>, + page_size: Option<Size2D<f32>>, + cpu_painting: bool, + wants_scroll_events: WantsScrollEventsFlag, + scroll_policy: ScrollPolicy, + hidden: bool) -> CompositorData { + CompositorData { + pipeline: pipeline, + id: layer_id, + scroll_offset: TypedPoint2D(0f32, 0f32), + bounds: bounds, + page_size: page_size, + hidden: hidden, + wants_scroll_events: wants_scroll_events, + scroll_policy: scroll_policy, + cpu_painting: cpu_painting, + unrendered_color: gfx::color::rgba(0.0, 0.0, 0.0, 0.0), + scissor: None, + epoch: Epoch(0), + } + } + + pub fn new_root(pipeline: CompositionPipeline, + page_size: Size2D<f32>, + cpu_painting: bool) -> CompositorData { + CompositorData::new(pipeline, + LayerId::null(), + Rect(Point2D(0f32, 0f32), page_size), + Some(page_size), + cpu_painting, + WantsScrollEvents, + FixedPosition, + false) + } + + + pub fn id_of_first_child(layer: Rc<ContainerLayer<CompositorData>>) -> LayerId { + layer.children().next().expect("no first child!").extra_data.borrow().id + } + + /// Adds a child layer to the layer with the given ID and the given pipeline, if it doesn't + /// exist yet. The child layer will have the same pipeline, tile size, memory limit, and CPU + /// painting status as its parent. + /// + /// Returns: + /// * True if the layer was added; + /// * True if the layer was not added because it already existed; + /// * False if the layer could not be added because no suitable parent layer with the given + /// ID and pipeline could be found. + pub fn add_child_if_necessary(layer: Rc<ContainerLayer<CompositorData>>, + pipeline_id: PipelineId, + parent_layer_id: LayerId, + child_layer_id: LayerId, + rect: Rect<f32>, + page_size: Size2D<f32>, + scroll_policy: ScrollPolicy) -> bool { + if layer.extra_data.borrow().pipeline.id != pipeline_id || layer.extra_data.borrow().id != parent_layer_id { + return layer.children().any(|kid| { + CompositorData::add_child_if_necessary(kid, + pipeline_id, + parent_layer_id, + child_layer_id, + rect, + page_size, + scroll_policy) + }) + } + + // See if we've already made this child layer. + if layer.children().any(|kid| { + kid.extra_data.borrow().pipeline.id == pipeline_id && + kid.extra_data.borrow().id == child_layer_id + }) { + return true + } + + let new_kid = Rc::new(ContainerLayer::new(Some(page_size), + ContainerLayer::tile_size(layer.clone()), + CompositorData::new(layer.extra_data.borrow().pipeline.clone(), + child_layer_id, + rect, + Some(page_size), + layer.extra_data.borrow().cpu_painting, + DoesntWantScrollEvents, + scroll_policy, + false))); + + new_kid.extra_data.borrow_mut().scissor = Some(rect); + new_kid.common.borrow_mut().origin = rect.origin; + + // Place the kid's layer in the container passed in. + ContainerLayer::add_child_end(layer.clone(), new_kid.clone()); + + true + } + + /// Move the layer's descendants that don't want scroll events and scroll by a relative + /// specified amount in page coordinates. This also takes in a cursor position to see if the + /// mouse is over child layers first. If a layer successfully scrolled, returns true; otherwise + /// returns false, so a parent layer can scroll instead. + pub fn handle_scroll_event(layer: Rc<ContainerLayer<CompositorData>>, + delta: TypedPoint2D<PagePx, f32>, + cursor: TypedPoint2D<PagePx, f32>, + window_size: TypedSize2D<PagePx, f32>) + -> bool { + // If this layer is hidden, neither it nor its children will scroll. + if layer.extra_data.borrow().hidden { + return false + } + + // If this layer doesn't want scroll events, neither it nor its children can handle scroll + // events. + if layer.extra_data.borrow().wants_scroll_events != WantsScrollEvents { + return false + } + + // Allow children to scroll. + let cursor = cursor - layer.extra_data.borrow().scroll_offset; + for child in layer.children() { + match child.extra_data.borrow().scissor { + None => { + error!("CompositorData: unable to perform cursor hit test for layer"); + } + Some(rect) => { + let rect: TypedRect<PagePx, f32> = Rect::from_untyped(&rect); + if cursor.x >= rect.origin.x && cursor.x < rect.origin.x + rect.size.width + && cursor.y >= rect.origin.y && cursor.y < rect.origin.y + rect.size.height + && CompositorData::handle_scroll_event(child.clone(), + delta, + cursor - rect.origin, + rect.size) { + return true + } + } + } + } + + // This scroll event is mine! + // Scroll this layer! + let old_origin = layer.extra_data.borrow().scroll_offset.clone(); + layer.extra_data.borrow_mut().scroll_offset = old_origin + delta; + + // bounds checking + let page_size = match layer.extra_data.borrow().page_size { + Some(size) => size, + None => fail!("CompositorData: tried to scroll with no page size set"), + }; + + let window_size = window_size.to_untyped(); + let scroll_offset = layer.extra_data.borrow().scroll_offset.to_untyped(); + + let min_x = (window_size.width - page_size.width).min(0.0); + layer.extra_data.borrow_mut().scroll_offset.x = Length(scroll_offset.x.clamp(&min_x, &0.0)); + + let min_y = (window_size.height - page_size.height).min(0.0); + layer.extra_data.borrow_mut().scroll_offset.y = Length(scroll_offset.y.clamp(&min_y, &0.0)); + + if old_origin - layer.extra_data.borrow().scroll_offset == TypedPoint2D(0f32, 0f32) { + return false + } + + let offset = layer.extra_data.borrow().scroll_offset.clone(); + CompositorData::scroll(layer.clone(), offset) + } + + /// Actually scrolls the descendants of a layer that scroll. This is called by + /// `handle_scroll_event` above when it determines that a layer wants to scroll. + fn scroll(layer: Rc<ContainerLayer<CompositorData>>, scroll_offset: TypedPoint2D<PagePx, f32>) -> bool { + let mut result = false; + + // Only scroll this layer if it's not fixed-positioned. + if layer.extra_data.borrow().scroll_policy != FixedPosition { + // Scroll this layer! + layer.extra_data.borrow_mut().scroll_offset = scroll_offset; + + let scroll_offset = layer.extra_data.borrow().scroll_offset.clone(); + layer.common.borrow_mut().set_transform( + identity().translate(scroll_offset.x.get(), scroll_offset.y.get(), 0.0)); + + result = true + } + + for child in layer.children() { + result = CompositorData::scroll(child.clone(), scroll_offset) || result; + } + + result + } + + // Takes in a MouseWindowEvent, determines if it should be passed to children, and + // sends the event off to the appropriate pipeline. NB: the cursor position is in + // page coordinates. + pub fn send_mouse_event(layer: Rc<ContainerLayer<CompositorData>>, event: MouseWindowEvent, cursor: TypedPoint2D<PagePx, f32>) { + let cursor = cursor - layer.extra_data.borrow().scroll_offset; + for child in layer.children() { + if child.extra_data.borrow().hidden { + continue; + } + + match child.extra_data.borrow().scissor { + None => { + error!("CompositorData: unable to perform cursor hit test for layer"); + } + Some(rect) => { + let rect: TypedRect<PagePx, f32> = Rect::from_untyped(&rect); + if cursor.x >= rect.origin.x && cursor.x < rect.origin.x + rect.size.width + && cursor.y >= rect.origin.y && cursor.y < rect.origin.y + rect.size.height { + CompositorData::send_mouse_event(child.clone(), event, cursor - rect.origin); + return; + } + } + } + } + + // This mouse event is mine! + let message = match event { + MouseWindowClickEvent(button, _) => ClickEvent(button, cursor.to_untyped()), + MouseWindowMouseDownEvent(button, _) => MouseDownEvent(button, cursor.to_untyped()), + MouseWindowMouseUpEvent(button, _) => MouseUpEvent(button, cursor.to_untyped()), + }; + let ScriptChan(ref chan) = layer.extra_data.borrow().pipeline.script_chan; + let _ = chan.send_opt(SendEventMsg(layer.extra_data.borrow().pipeline.id.clone(), message)); + } + + pub fn send_mouse_move_event(layer: Rc<ContainerLayer<CompositorData>>, cursor: TypedPoint2D<PagePx, f32>) { + let message = MouseMoveEvent(cursor.to_untyped()); + let ScriptChan(ref chan) = layer.extra_data.borrow().pipeline.script_chan; + let _ = chan.send_opt(SendEventMsg(layer.extra_data.borrow().pipeline.id.clone(), message)); + } + + // 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 get_buffer_request(layer: Rc<ContainerLayer<CompositorData>>, + graphics_context: &NativeCompositingGraphicsContext, + window_rect: Rect<f32>, + scale: f32) + -> bool { + let (request, unused) = ContainerLayer::get_tile_rects_page(layer.clone(), window_rect, scale); + let redisplay = !unused.is_empty(); + if redisplay { + // Send back unused tiles. + let _ = layer.extra_data.borrow().pipeline.render_chan.send_opt(UnusedBufferMsg(unused)); + } + 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); + } + + if redisplay { + CompositorData::build_layer_tree(layer.clone(), graphics_context); + } + + let transform = |kid: Rc<ContainerLayer<CompositorData>>| -> bool { + match kid.extra_data.borrow().scissor { + Some(scissor) => { + 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; + new_rect.origin.y = new_rect.origin.y - offset.y; + match new_rect.intersection(&scissor) { + Some(new_rect) => { + // Child layers act as if they are rendered at (0,0), so we + // subtract the layer's (x,y) coords in its containing page + // to make the child_rect appear in coordinates local to it. + let child_rect = Rect(new_rect.origin.sub(&scissor.origin), + new_rect.size); + CompositorData::get_buffer_request(kid.clone(), graphics_context, child_rect, scale) + } + None => { + false // Layer is offscreen + } + } + } + None => fail!("child layer not clipped!"), + } + }; + + layer.children().filter(|x| !x.extra_data.borrow().hidden) + .map(transform) + .fold(false, |a, b| a || b) || redisplay + + } + + // Move the sublayer to an absolute position in page coordinates relative to its parent, + // and clip the layer to the specified size in page coordinates. + // If the layer is hidden and has a defined page size, unhide it. + // This method returns false if the specified layer is not found. + pub fn set_clipping_rect(layer: Rc<ContainerLayer<CompositorData>>, + pipeline_id: PipelineId, + layer_id: LayerId, + new_rect: Rect<f32>) + -> bool { + debug!("compositor_data: starting set_clipping_rect()"); + match CompositorData::find_child_with_layer_and_pipeline_id(layer.clone(), pipeline_id, layer_id) { + Some(child_node) => { + debug!("compositor_data: node found for set_clipping_rect()"); + child_node.common.borrow_mut().origin = new_rect.origin; + let old_rect = child_node.extra_data.borrow().scissor.clone(); + child_node.extra_data.borrow_mut().scissor = Some(new_rect); + match old_rect { + Some(old_rect) => { + ContainerLayer::set_status_page(layer.clone(), old_rect, Normal, false); // Rect is unhidden + } + None => {} // Nothing to do + } + ContainerLayer::set_status_page(layer.clone(), new_rect, Hidden, false); // Hide the new rect + + // If possible, unhide child + let mut child_data = child_node.extra_data.borrow_mut(); + if child_data.hidden && child_data.page_size.is_some() { + child_data.hidden = false; + } + true + } + None => { + layer.children() + .any(|kid| CompositorData::set_clipping_rect(kid.clone(), pipeline_id, layer_id, new_rect)) + + } + } + } + + // Set the layer's page size. This signals that the renderer is ready for BufferRequests. + // If the layer is hidden and has a defined clipping rect, unhide it. + // This method returns false if the specified layer is not found. + pub fn resize(layer: Rc<ContainerLayer<CompositorData>>, + pipeline_id: PipelineId, + layer_id: LayerId, + new_size: Size2D<f32>, + window_size: TypedSize2D<PagePx, f32>, + epoch: Epoch) + -> bool { + debug!("compositor_data: starting resize()"); + if layer.extra_data.borrow().pipeline.id != pipeline_id || layer.extra_data.borrow().id != layer_id { + return CompositorData::resize_helper(layer.clone(), pipeline_id, layer_id, new_size, epoch) + } + + debug!("compositor_data: layer found for resize()"); + layer.extra_data.borrow_mut().epoch = epoch; + layer.extra_data.borrow_mut().page_size = Some(new_size); + + let unused_buffers = ContainerLayer::resize(layer.clone(), new_size); + if !unused_buffers.is_empty() { + let _ = layer.extra_data.borrow().pipeline + .render_chan + .send_opt(UnusedBufferMsg(unused_buffers)); + } + + // Call scroll for bounds checking if the page shrunk. Use (-1, -1) as the cursor position + // to make sure the scroll isn't propagated downwards. + CompositorData::handle_scroll_event(layer.clone(), TypedPoint2D(0f32, 0f32), TypedPoint2D(-1f32, -1f32), window_size); + layer.extra_data.borrow_mut().hidden = false; + CompositorData::set_occlusions(layer.clone()); + true + } + + pub fn move(layer: Rc<ContainerLayer<CompositorData>>, + pipeline_id: PipelineId, + layer_id: LayerId, + origin: Point2D<f32>, + window_size: TypedSize2D<PagePx, f32>) + -> bool { + // Search children for the right layer to move. + if layer.extra_data.borrow().pipeline.id != pipeline_id || layer.extra_data.borrow().id != layer_id { + return layer.children().any(|kid| { + CompositorData::move(kid.clone(), pipeline_id, layer_id, origin, window_size) + }); + } + + if layer.extra_data.borrow().wants_scroll_events != WantsScrollEvents { + return false + } + + // Scroll this layer! + let old_origin = layer.extra_data.borrow().scroll_offset; + layer.extra_data.borrow_mut().scroll_offset = Point2D::from_untyped(&(origin * -1.0)); + + // bounds checking + let page_size = match layer.extra_data.borrow().page_size { + Some(size) => size, + None => fail!("CompositorData: tried to scroll with no page size set"), + }; + let window_size = window_size.to_untyped(); + let scroll_offset = layer.extra_data.borrow().scroll_offset.to_untyped(); + + let min_x = (window_size.width - page_size.width).min(0.0); + layer.extra_data.borrow_mut().scroll_offset.x = Length(scroll_offset.x.clamp(&min_x, &0.0)); + let min_y = (window_size.height - page_size.height).min(0.0); + layer.extra_data.borrow_mut().scroll_offset.y = Length(scroll_offset.y.clamp(&min_y, &0.0)); + + // check to see if we scrolled + if old_origin - layer.extra_data.borrow().scroll_offset == TypedPoint2D(0f32, 0f32) { + return false; + } + + let offset = layer.extra_data.borrow().scroll_offset.clone(); + CompositorData::scroll(layer.clone(), offset) + } + + // Returns whether the layer should be vertically flipped. + #[cfg(target_os="macos")] + fn texture_flip_and_target(cpu_painting: bool, size: Size2D<uint>) -> (Flip, TextureTarget) { + let flip = if cpu_painting { + NoFlip + } else { + VerticalFlip + }; + + (flip, TextureTargetRectangle(size)) + } + + #[cfg(target_os="android")] + fn texture_flip_and_target(cpu_painting: bool, size: Size2D<uint>) -> (Flip, TextureTarget) { + let flip = if cpu_painting { + NoFlip + } else { + VerticalFlip + }; + + (flip, TextureTarget2D) + } + + #[cfg(target_os="linux")] + fn texture_flip_and_target(_: bool, _: Size2D<uint>) -> (Flip, TextureTarget) { + (NoFlip, TextureTarget2D) + } + + + + fn find_child_with_layer_and_pipeline_id(layer: Rc<ContainerLayer<CompositorData>>, + pipeline_id: PipelineId, + layer_id: LayerId) + -> Option<Rc<ContainerLayer<CompositorData>>> { + for kid in layer.children() { + if pipeline_id == kid.extra_data.borrow().pipeline.id && layer_id == kid.extra_data.borrow().id { + return Some(kid); + } + } + return None + } + + // A helper method to resize sublayers. + fn resize_helper(layer: Rc<ContainerLayer<CompositorData>>, + pipeline_id: PipelineId, + layer_id: LayerId, + new_size: Size2D<f32>, + epoch: Epoch) + -> bool { + debug!("compositor_data: starting resize_helper()"); + + let found = match CompositorData::find_child_with_layer_and_pipeline_id(layer.clone(), pipeline_id, layer_id) { + Some(child) => { + debug!("compositor_data: layer found for resize_helper()"); + child.extra_data.borrow_mut().epoch = epoch; + child.extra_data.borrow_mut().page_size = Some(new_size); + + let unused_buffers = ContainerLayer::resize(child.clone(), new_size); + if !unused_buffers.is_empty() { + let _ = child.extra_data.borrow().pipeline.render_chan.send_opt(UnusedBufferMsg(unused_buffers)); + } + + let scissor_clone = child.extra_data.borrow().scissor.clone(); + match scissor_clone { + Some(scissor) => { + // Call scroll for bounds checking if the page shrunk. Use (-1, -1) as the + // cursor position to make sure the scroll isn't propagated downwards. + let size: TypedSize2D<PagePx, f32> = Size2D::from_untyped(&scissor.size); + CompositorData::handle_scroll_event(child.clone(), + TypedPoint2D(0f32, 0f32), + TypedPoint2D(-1f32, -1f32), + size); + child.extra_data.borrow_mut().hidden = false; + } + None => {} // Nothing to do + } + true + } + None => false, + }; + + if found { // Boolean flag to get around double borrow of self + CompositorData::set_occlusions(layer.clone()); + return true + } + + // If we got here, the layer's ID does not match ours, so recurse on descendents (including + // hidden children). + layer.children().any(|kid| { + CompositorData::resize_helper(kid.clone(), pipeline_id, layer_id, new_size, epoch) + }) + } + + // Collect buffers from the quadtree. This method IS NOT recursive, so child layers + // are not rebuilt directly from this method. + pub fn build_layer_tree(layer: Rc<ContainerLayer<CompositorData>>, graphics_context: &NativeCompositingGraphicsContext) { + // Clear all old textures. + layer.tiles.borrow_mut().clear(); + + // Add new tiles. + ContainerLayer::do_for_all_tiles(layer.clone(), |buffer: &Box<LayerBuffer>| { + debug!("osmain: compositing buffer rect {}", buffer.rect); + + let size = Size2D(buffer.screen_pos.size.width as int, + buffer.screen_pos.size.height as int); + + debug!("osmain: adding new texture layer"); + + // Determine, in a platform-specific way, whether we should flip the texture + // and which target to use. + let (flip, target) = + CompositorData::texture_flip_and_target(layer.extra_data.borrow().cpu_painting, + buffer.screen_pos.size); + + // Make a new texture and bind the layer buffer's surface to it. + let texture = Texture::new(target); + debug!("COMPOSITOR binding to native surface {:d}", + buffer.native_surface.get_id() as int); + buffer.native_surface.bind_to_texture(graphics_context, &texture, size); + + // Set the layer's transform. + let rect = buffer.rect; + let transform = identity().translate(rect.origin.x, rect.origin.y, 0.0); + let transform = transform.scale(rect.size.width, rect.size.height, 1.0); + + // Make a texture layer and add it. + let texture_layer = Rc::new(TextureLayer::new(texture, buffer.screen_pos.size, + flip, transform)); + layer.tiles.borrow_mut().push(texture_layer); + }); + } + + // Add LayerBuffers to the specified layer. Returns the layer buffer set back if the layer that + // matches the given pipeline ID was not found; otherwise returns None and consumes the layer + // buffer set. + // + // 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. + pub fn add_buffers(layer: Rc<ContainerLayer<CompositorData>>, + graphics_context: &NativeCompositingGraphicsContext, + pipeline_id: PipelineId, + layer_id: LayerId, + mut new_buffers: Box<LayerBufferSet>, + epoch: Epoch) + -> Option<Box<LayerBufferSet>> { + debug!("compositor_data: starting add_buffers()"); + if layer.extra_data.borrow().pipeline.id != pipeline_id || layer.extra_data.borrow().id != layer_id { + // ID does not match ours, so recurse on descendents (including hidden children). + for child_layer in layer.children() { + match CompositorData::add_buffers(child_layer.clone(), + graphics_context, + pipeline_id, + layer_id, + new_buffers, + epoch) { + None => return None, + Some(buffers) => new_buffers = buffers, + } + } + + // Not found. Give the caller the buffers back. + return Some(new_buffers) + } + + debug!("compositor_data: layers found for add_buffers()"); + + if layer.extra_data.borrow().epoch != epoch { + debug!("add_buffers: compositor epoch mismatch: {:?} != {:?}, id: {:?}", + layer.extra_data.borrow().epoch, + epoch, + layer.extra_data.borrow().pipeline.id); + let _ = layer.extra_data.borrow().pipeline.render_chan.send_opt(UnusedBufferMsg(new_buffers.buffers)); + return None + } + + { + let mut unused_tiles = vec!(); + for buffer in new_buffers.buffers.move_iter().rev() { + unused_tiles.push_all_move(ContainerLayer::add_tile_pixel(layer.clone(), buffer)); + } + if !unused_tiles.is_empty() { // send back unused buffers + let _ = layer.extra_data.borrow().pipeline.render_chan.send_opt(UnusedBufferMsg(unused_tiles)); + } + } + + CompositorData::build_layer_tree(layer.clone(), graphics_context); + None + } + + // Recursively sets occluded portions of quadtrees to Hidden, so that they do not ask for + // tile requests. If layers are moved, resized, or deleted, these portions may be updated. + fn set_occlusions(layer: Rc<ContainerLayer<CompositorData>>) { + for kid in layer.children() { + if !kid.extra_data.borrow().hidden { + match kid.extra_data.borrow().scissor { + None => {} // Nothing to do + Some(rect) => { + ContainerLayer::set_status_page(layer.clone(), rect, Hidden, false); + } + } + } + } + + for kid in layer.children() { + if !kid.extra_data.borrow().hidden { + CompositorData::set_occlusions(kid.clone()); + } + } + } + + /// Destroys all quadtree tiles, sending the buffers back to the renderer to be destroyed or + /// reused. + fn clear(layer: Rc<ContainerLayer<CompositorData>>) { + let mut tiles = ContainerLayer::collect_tiles(layer.clone()); + + if !tiles.is_empty() { + // We have no way of knowing without a race whether the render task is even up and + // running, but mark the tiles as not leaking. If the render task died, then the + // tiles are going to be cleaned up. + for tile in tiles.mut_iter() { + tile.mark_wont_leak() + } + + let _ = layer.extra_data.borrow().pipeline.render_chan.send_opt(UnusedBufferMsg(tiles)); + } + } + + /// Destroys tiles for this layer and all descendent layers, sending the buffers back to the + /// renderer to be destroyed or reused. + pub fn clear_all_tiles(layer: Rc<ContainerLayer<CompositorData>>) { + CompositorData::clear(layer.clone()); + for kid in layer.children() { + CompositorData::clear_all_tiles(kid.clone()); + } + } + + /// Destroys all tiles of all layers, including children, *without* sending them back to the + /// renderer. You must call this only when the render task is destined to be going down; + /// otherwise, you will leak tiles. + /// + /// This is used during shutdown, when we know the render task is going away. + pub fn forget_all_tiles(layer: Rc<ContainerLayer<CompositorData>>) { + let tiles = ContainerLayer::collect_tiles(layer.clone()); + for tile in tiles.move_iter() { + let mut tile = tile; + tile.mark_wont_leak() + } + + for kid in layer.children() { + CompositorData::forget_all_tiles(kid.clone()); + } + } + + pub fn set_unrendered_color(layer: Rc<ContainerLayer<CompositorData>>, pipeline_id: PipelineId, layer_id: LayerId, color: Color) -> bool { + if layer.extra_data.borrow().pipeline.id != pipeline_id || layer.extra_data.borrow().id != layer_id { + for child_layer in layer.children() { + if CompositorData::set_unrendered_color(child_layer.clone(), pipeline_id, layer_id, color) { + return true; + } + } + return false; + } + + layer.extra_data.borrow_mut().unrendered_color = color; + return true; + } +} + diff --git a/src/components/compositing/compositor_layer.rs b/src/components/compositing/compositor_layer.rs deleted file mode 100644 index eb3c56fd188..00000000000 --- a/src/components/compositing/compositor_layer.rs +++ /dev/null @@ -1,963 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -use quadtree::{Quadtree, Normal, Hidden}; -use pipeline::CompositionPipeline; -use windowing::{MouseWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent}; -use windowing::{MouseWindowMouseUpEvent}; - -use azure::azure_hl::Color; -use geom::length::Length; -use geom::matrix::identity; -use geom::point::{Point2D, TypedPoint2D}; -use geom::rect::{Rect, TypedRect}; -use geom::size::{Size2D, TypedSize2D}; -use gfx::render_task::{ReRenderMsg, UnusedBufferMsg}; -use gfx; -use layers::layers::{ContainerLayerKind, ContainerLayer, Flip, NoFlip, TextureLayer}; -use layers::layers::TextureLayerKind; -use layers::platform::surface::{NativeCompositingGraphicsContext, NativeSurfaceMethods}; -use layers::texturegl::{Texture, TextureTarget}; -use script::dom::event::{ClickEvent, MouseDownEvent, MouseMoveEvent, MouseUpEvent}; -use script::script_task::{ScriptChan, SendEventMsg}; -use servo_msg::compositor_msg::{Epoch, FixedPosition, LayerBuffer, LayerBufferSet, LayerId}; -use servo_msg::compositor_msg::{ScrollPolicy, Tile}; -use servo_msg::constellation_msg::PipelineId; -use servo_util::geometry::PagePx; -use std::rc::Rc; - -#[cfg(target_os="macos")] -#[cfg(target_os="android")] -use layers::layers::VerticalFlip; -#[cfg(not(target_os="macos"))] -use layers::texturegl::TextureTarget2D; -#[cfg(target_os="macos")] -use layers::texturegl::TextureTargetRectangle; - -/// The amount of memory usage allowed per layer. -static MAX_TILE_MEMORY_PER_LAYER: uint = 10000000; - -/// The CompositorLayer represents an element on a page that has a unique scroll -/// or animation behavior. This can include absolute positioned elements, iframes, etc. -/// Each layer can also have child layers. -/// -/// FIXME(#2003, pcwalton): This should be merged with the concept of a layer in `rust-layers` and -/// ultimately removed, except as a set of helper methods on `rust-layers` layers. -pub struct CompositorLayer { - /// This layer's pipeline. BufferRequests and mouse events will be sent through this. - pub pipeline: CompositionPipeline, - - /// The ID of this layer within the pipeline. - pub id: LayerId, - - /// The bounds of this layer in terms of its parent (a.k.a. the scissor box). - pub bounds: Rect<f32>, - - /// The size of the underlying page in page coordinates. This is an option - /// because we may not know the size of the page until layout is finished completely. - /// if we have no size yet, the layer is hidden until a size message is recieved. - pub page_size: Option<Size2D<f32>>, - - /// The offset of the page due to scrolling. (0,0) is when the window sees the - /// top left corner of the page. - pub scroll_offset: TypedPoint2D<PagePx, f32>, - - /// This layer's children. These could be iframes or any element which - /// differs in scroll behavior from its parent. Each is associated with a - /// ContainerLayer which determines its position relative to its parent and - /// clipping rect. Children are stored in the order in which they are drawn. - pub children: Vec<CompositorLayerChild>, - - /// This layer's quadtree. This is where all buffers are stored for this layer. - pub quadtree: MaybeQuadtree, - - /// The root layer of this CompositorLayer's layer tree. Buffers are collected - /// from the quadtree and inserted here when the layer is painted to the screen. - pub root_layer: Rc<ContainerLayer>, - - /// When set to true, this layer is ignored by its parents. This is useful for - /// soft deletion or when waiting on a page size. - pub hidden: bool, - - /// A monotonically increasing counter that keeps track of the current epoch. - /// add_buffer() calls that don't match the current epoch will be ignored. - pub epoch: Epoch, - - /// The behavior of this layer when a scroll message is received. - pub wants_scroll_events: WantsScrollEventsFlag, - - /// Whether an ancestor layer that receives scroll events moves this layer. - pub scroll_policy: ScrollPolicy, - - /// True if CPU rendering is enabled, false if we're using GPU rendering. - pub cpu_painting: bool, - - /// The color to use for the unrendered-content void - pub unrendered_color: Color, -} - -/// Helper struct for keeping CompositorLayer children organized. -pub struct CompositorLayerChild { - /// The child itself. - pub child: Box<CompositorLayer>, - /// A ContainerLayer managed by the parent node. This deals with clipping and - /// positioning, and is added above the child's layer tree. - pub container: Rc<ContainerLayer>, -} - -/// Helper enum for storing quadtrees. Either contains a quadtree, or contains -/// information from which a quadtree can be built. -enum MaybeQuadtree { - Tree(Quadtree<Box<LayerBuffer>>), - NoTree(uint, Option<uint>), -} - -impl MaybeQuadtree { - fn tile_size(&self) -> uint { - match *self { - Tree(ref quadtree) => quadtree.max_tile_size, - NoTree(tile_size, _) => tile_size, - } - } -} - -#[deriving(PartialEq, Clone)] -pub enum WantsScrollEventsFlag { - WantsScrollEvents, - DoesntWantScrollEvents, -} - -fn create_container_layer_from_rect(rect: Rect<f32>) -> Rc<ContainerLayer> { - let container = Rc::new(ContainerLayer()); - *container.scissor.borrow_mut() = Some(rect); - container.common.borrow_mut().transform = - identity().translate(rect.origin.x, rect.origin.y, 0f32); - container -} - -trait Clampable { - fn clamp(&self, mn: &Self, mx: &Self) -> Self; -} - -impl Clampable for f32 { - /// Returns the number constrained within the range `mn <= self <= mx`. - /// If any of the numbers are `NAN` then `NAN` is returned. - #[inline] - fn clamp(&self, mn: &f32, mx: &f32) -> f32 { - match () { - _ if self.is_nan() => *self, - _ if !(*self <= *mx) => *mx, - _ if !(*self >= *mn) => *mn, - _ => *self, - } - } -} - - -impl CompositorLayer { - /// Creates a new `CompositorLayer`. - fn new(pipeline: CompositionPipeline, - layer_id: LayerId, - bounds: Rect<f32>, - page_size: Option<Size2D<f32>>, - tile_size: uint, - cpu_painting: bool, - wants_scroll_events: WantsScrollEventsFlag, - scroll_policy: ScrollPolicy) - -> CompositorLayer { - CompositorLayer { - pipeline: pipeline, - id: layer_id, - bounds: bounds, - page_size: page_size, - scroll_offset: TypedPoint2D(0f32, 0f32), - children: vec!(), - quadtree: match page_size { - None => NoTree(tile_size, Some(MAX_TILE_MEMORY_PER_LAYER)), - Some(page_size) => { - Tree(Quadtree::new(Size2D(page_size.width as uint, page_size.height as uint), - tile_size, - Some(MAX_TILE_MEMORY_PER_LAYER))) - } - }, - root_layer: Rc::new(ContainerLayer()), - hidden: true, - epoch: Epoch(0), - wants_scroll_events: wants_scroll_events, - scroll_policy: scroll_policy, - cpu_painting: cpu_painting, - unrendered_color: gfx::color::rgba(0.0, 0.0, 0.0, 0.0), - } - } - - /// Creates a new root `CompositorLayer` bound to a composition pipeline with an optional page - /// size. If no page size is given, the layer is initially hidden and initialized without a - /// quadtree. - pub fn new_root(pipeline: CompositionPipeline, - page_size: Size2D<f32>, - tile_size: uint, - cpu_painting: bool) - -> CompositorLayer { - CompositorLayer { - pipeline: pipeline, - id: LayerId::null(), - bounds: Rect(Point2D(0f32, 0f32), page_size), - page_size: Some(page_size), - scroll_offset: TypedPoint2D(0f32, 0f32), - children: vec!(), - quadtree: NoTree(tile_size, Some(MAX_TILE_MEMORY_PER_LAYER)), - root_layer: Rc::new(ContainerLayer()), - hidden: false, - epoch: Epoch(0), - wants_scroll_events: WantsScrollEvents, - scroll_policy: FixedPosition, - cpu_painting: cpu_painting, - unrendered_color: gfx::color::rgba(0.0, 0.0, 0.0, 0.0), - } - } - - /// Adds a child layer to the layer with the given ID and the given pipeline, if it doesn't - /// exist yet. The child layer will have the same pipeline, tile size, memory limit, and CPU - /// painting status as its parent. - /// - /// Returns: - /// * True if the layer was added; - /// * True if the layer was not added because it already existed; - /// * False if the layer could not be added because no suitable parent layer with the given - /// ID and pipeline could be found. - pub fn add_child_if_necessary(&mut self, - container_layer: Rc<ContainerLayer>, - pipeline_id: PipelineId, - parent_layer_id: LayerId, - child_layer_id: LayerId, - rect: Rect<f32>, - page_size: Size2D<f32>, - scroll_policy: ScrollPolicy) - -> bool { - if self.pipeline.id != pipeline_id || self.id != parent_layer_id { - return self.children.mut_iter().any(|kid_holder| { - kid_holder.child.add_child_if_necessary(kid_holder.container.clone(), - pipeline_id, - parent_layer_id, - child_layer_id, - rect, - page_size, - scroll_policy) - }) - } - - // See if we've already made this child layer. - if self.children.iter().any(|kid_holder| { - kid_holder.child.pipeline.id == pipeline_id && - kid_holder.child.id == child_layer_id - }) { - return true - } - - let mut kid = box CompositorLayer::new(self.pipeline.clone(), - child_layer_id, - rect, - Some(page_size), - self.quadtree.tile_size(), - self.cpu_painting, - DoesntWantScrollEvents, - scroll_policy); - - kid.hidden = false; - - // Place the kid's layer in a container... - let kid_container = create_container_layer_from_rect(rect); - ContainerLayer::add_child_start(kid_container.clone(), - ContainerLayerKind(kid.root_layer.clone())); - - // ...and add *that* container as a child of the container passed in. - ContainerLayer::add_child_end(container_layer, - ContainerLayerKind(kid_container.clone())); - - self.children.push(CompositorLayerChild { - child: kid, - container: kid_container, - }); - true - } - - /// Move the layer's descendants that don't want scroll events and scroll by a relative - /// specified amount in page coordinates. This also takes in a cursor position to see if the - /// mouse is over child layers first. If a layer successfully scrolled, returns true; otherwise - /// returns false, so a parent layer can scroll instead. - pub fn handle_scroll_event(&mut self, - delta: TypedPoint2D<PagePx, f32>, - cursor: TypedPoint2D<PagePx, f32>, - window_size: TypedSize2D<PagePx, f32>) - -> bool { - // If this layer is hidden, neither it nor its children will scroll. - if self.hidden { - return false - } - - // If this layer doesn't want scroll events, neither it nor its children can handle scroll - // events. - if self.wants_scroll_events != WantsScrollEvents { - return false - } - - // Allow children to scroll. - let cursor = cursor - self.scroll_offset; - for child in self.children.mut_iter() { - match *child.container.scissor.borrow() { - None => { - error!("CompositorLayer: unable to perform cursor hit test for layer"); - } - Some(rect) => { - let rect: TypedRect<PagePx, f32> = Rect::from_untyped(&rect); - if cursor.x >= rect.origin.x && cursor.x < rect.origin.x + rect.size.width - && cursor.y >= rect.origin.y && cursor.y < rect.origin.y + rect.size.height - && child.child.handle_scroll_event(delta, - cursor - rect.origin, - rect.size) { - return true - } - } - } - } - - // This scroll event is mine! - // Scroll this layer! - let old_origin = self.scroll_offset; - self.scroll_offset = self.scroll_offset + delta; - - // bounds checking - let page_size = match self.page_size { - Some(size) => size, - None => fail!("CompositorLayer: tried to scroll with no page size set"), - }; - - let window_size = window_size.to_untyped(); - let scroll_offset = self.scroll_offset.to_untyped(); - - let min_x = (window_size.width - page_size.width).min(0.0); - self.scroll_offset.x = Length(scroll_offset.x.clamp(&min_x, &0.0)); - - let min_y = (window_size.height - page_size.height).min(0.0); - self.scroll_offset.y = Length(scroll_offset.y.clamp(&min_y, &0.0)); - - if old_origin - self.scroll_offset == TypedPoint2D(0f32, 0f32) { - return false - } - let offset = self.scroll_offset; - self.scroll(offset) - } - - #[allow(dead_code)] - fn dump_layer_tree(&self, layer: Rc<ContainerLayer>, indent: String) { - println!("{}scissor {:?}", indent, layer.scissor.borrow()); - for kid in layer.children() { - match kid { - ContainerLayerKind(ref container_layer) => { - self.dump_layer_tree((*container_layer).clone(), format!("{} ", indent)); - } - TextureLayerKind(_) => { - println!("{} (texture layer)", indent); - } - } - } - } - - /// Actually scrolls the descendants of a layer that scroll. This is called by - /// `handle_scroll_event` above when it determines that a layer wants to scroll. - fn scroll(&mut self, scroll_offset: TypedPoint2D<PagePx, f32>) -> bool { - let mut result = false; - - // Only scroll this layer if it's not fixed-positioned. - if self.scroll_policy != FixedPosition { - // Scroll this layer! - self.scroll_offset = scroll_offset; - - self.root_layer.common.borrow_mut().set_transform( - identity().translate(self.scroll_offset.x.get(), self.scroll_offset.y.get(), 0.0)); - - result = true - } - - for kid_holder in self.children.mut_iter() { - result = kid_holder.child.scroll(scroll_offset) || result; - } - - result - } - - // Takes in a MouseWindowEvent, determines if it should be passed to children, and - // sends the event off to the appropriate pipeline. NB: the cursor position is in - // page coordinates. - pub fn send_mouse_event(&self, event: MouseWindowEvent, cursor: TypedPoint2D<PagePx, f32>) { - let cursor = cursor - self.scroll_offset; - for child in self.children.iter().filter(|&x| !x.child.hidden) { - match *child.container.scissor.borrow() { - None => { - error!("CompositorLayer: unable to perform cursor hit test for layer"); - } - Some(rect) => { - let rect: TypedRect<PagePx, f32> = Rect::from_untyped(&rect); - if cursor.x >= rect.origin.x && cursor.x < rect.origin.x + rect.size.width - && cursor.y >= rect.origin.y && cursor.y < rect.origin.y + rect.size.height { - child.child.send_mouse_event(event, cursor - rect.origin); - return; - } - } - } - } - - // This mouse event is mine! - let message = match event { - MouseWindowClickEvent(button, _) => ClickEvent(button, cursor.to_untyped()), - MouseWindowMouseDownEvent(button, _) => MouseDownEvent(button, cursor.to_untyped()), - MouseWindowMouseUpEvent(button, _) => MouseUpEvent(button, cursor.to_untyped()), - }; - let ScriptChan(ref chan) = self.pipeline.script_chan; - let _ = chan.send_opt(SendEventMsg(self.pipeline.id.clone(), message)); - } - - pub fn send_mouse_move_event(&self, cursor: TypedPoint2D<PagePx, f32>) { - let message = MouseMoveEvent(cursor.to_untyped()); - let ScriptChan(ref chan) = self.pipeline.script_chan; - let _ = chan.send_opt(SendEventMsg(self.pipeline.id.clone(), message)); - } - - // 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 get_buffer_request(&mut self, - graphics_context: &NativeCompositingGraphicsContext, - window_rect: Rect<f32>, - scale: f32) - -> bool { - let mut redisplay = false; - match self.quadtree { - NoTree(..) => {} - Tree(ref mut quadtree) => { - let (request, unused) = quadtree.get_tile_rects_page(window_rect, scale); - - // Workaround to make redisplay visible outside block. - redisplay = !unused.is_empty(); - if redisplay { - // Send back unused tiles. - let _ = self.pipeline.render_chan.send_opt(UnusedBufferMsg(unused)); - } - 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, self.id, self.epoch); - let _ = self.pipeline.render_chan.send_opt(msg); - } - } - }; - - if redisplay { - self.build_layer_tree(graphics_context); - } - - let transform = |x: &mut CompositorLayerChild| -> bool { - match *x.container.scissor.borrow() { - Some(scissor) => { - let mut new_rect = window_rect; - let offset = x.child.scroll_offset.to_untyped(); - new_rect.origin.x = new_rect.origin.x - offset.x; - new_rect.origin.y = new_rect.origin.y - offset.y; - match new_rect.intersection(&scissor) { - Some(new_rect) => { - // Child layers act as if they are rendered at (0,0), so we - // subtract the layer's (x,y) coords in its containing page - // to make the child_rect appear in coordinates local to it. - let child_rect = Rect(new_rect.origin.sub(&scissor.origin), - new_rect.size); - x.child.get_buffer_request(graphics_context, child_rect, scale) - } - None => { - false // Layer is offscreen - } - } - } - None => fail!("child layer not clipped!"), - } - }; - self.children.mut_iter().filter(|x| !x.child.hidden) - .map(transform) - .fold(false, |a, b| a || b) || redisplay - } - - - // Move the sublayer to an absolute position in page coordinates relative to its parent, - // and clip the layer to the specified size in page coordinates. - // If the layer is hidden and has a defined page size, unhide it. - // This method returns false if the specified layer is not found. - pub fn set_clipping_rect(&mut self, - pipeline_id: PipelineId, - layer_id: LayerId, - new_rect: Rect<f32>) - -> bool { - debug!("compositor_layer: starting set_clipping_rect()"); - match self.children.iter().position(|kid_holder| { - pipeline_id == kid_holder.child.pipeline.id && - layer_id == kid_holder.child.id - }) { - Some(i) => { - debug!("compositor_layer: node found for set_clipping_rect()"); - let child_node = self.children.get_mut(i); - child_node.container.common.borrow_mut().set_transform( - identity().translate(new_rect.origin.x, new_rect.origin.y, 0.0)); - let old_rect = child_node.container.scissor.borrow().clone(); - *child_node.container.scissor.borrow_mut() = Some(new_rect); - match self.quadtree { - NoTree(..) => {} // Nothing to do - Tree(ref mut quadtree) => { - match old_rect { - Some(old_rect) => { - quadtree.set_status_page(old_rect, Normal, false); // Rect is unhidden - } - None => {} // Nothing to do - } - quadtree.set_status_page(new_rect, Hidden, false); // Hide the new rect - } - } - // If possible, unhide child - if child_node.child.hidden && child_node.child.page_size.is_some() { - child_node.child.hidden = false; - } - true - } - None => { - // ID does not match any of our immediate children, so recurse on - // descendents (including hidden children) - self.children - .mut_iter() - .map(|kid_holder| &mut kid_holder.child) - .any(|kid| kid.set_clipping_rect(pipeline_id, layer_id, new_rect)) - } - } - } - - // Set the layer's page size. This signals that the renderer is ready for BufferRequests. - // If the layer is hidden and has a defined clipping rect, unhide it. - // This method returns false if the specified layer is not found. - pub fn resize(&mut self, - pipeline_id: PipelineId, - layer_id: LayerId, - new_size: Size2D<f32>, - window_size: TypedSize2D<PagePx, f32>, - epoch: Epoch) - -> bool { - debug!("compositor_layer: starting resize()"); - if self.pipeline.id != pipeline_id || self.id != layer_id { - return self.resize_helper(pipeline_id, layer_id, new_size, epoch) - } - - debug!("compositor_layer: layer found for resize()"); - self.epoch = epoch; - self.page_size = Some(new_size); - match self.quadtree { - Tree(ref mut quadtree) => { - let _ = self.pipeline - .render_chan - .send_opt(UnusedBufferMsg(quadtree.resize(new_size.width as uint, - new_size.height as uint))); - } - NoTree(tile_size, max_mem) => { - self.quadtree = Tree(Quadtree::new(Size2D(new_size.width as uint, - new_size.height as uint), - tile_size, - max_mem)) - } - } - // Call scroll for bounds checking if the page shrunk. Use (-1, -1) as the cursor position - // to make sure the scroll isn't propagated downwards. - self.handle_scroll_event(TypedPoint2D(0f32, 0f32), TypedPoint2D(-1f32, -1f32), window_size); - self.hidden = false; - self.set_occlusions(); - true - } - - pub fn move(&mut self, - pipeline_id: PipelineId, - layer_id: LayerId, - origin: Point2D<f32>, - window_size: TypedSize2D<PagePx, f32>) - -> bool { - // Search children for the right layer to move. - if self.pipeline.id != pipeline_id || self.id != layer_id { - return self.children.mut_iter().any(|kid_holder| { - kid_holder.child.move(pipeline_id, layer_id, origin, window_size) - }) - } - - if self.wants_scroll_events != WantsScrollEvents { - return false - } - - // Scroll this layer! - let old_origin = self.scroll_offset; - self.scroll_offset = Point2D::from_untyped(&(origin * -1.0)); - - // bounds checking - let page_size = match self.page_size { - Some(size) => size, - None => fail!("CompositorLayer: tried to scroll with no page size set"), - }; - let window_size = window_size.to_untyped(); - let scroll_offset = self.scroll_offset.to_untyped(); - - let min_x = (window_size.width - page_size.width).min(0.0); - self.scroll_offset.x = Length(scroll_offset.x.clamp(&min_x, &0.0)); - let min_y = (window_size.height - page_size.height).min(0.0); - self.scroll_offset.y = Length(scroll_offset.y.clamp(&min_y, &0.0)); - - // check to see if we scrolled - if old_origin - self.scroll_offset == TypedPoint2D(0f32, 0f32) { - return false; - } - let offset = self.scroll_offset; - self.scroll(offset) - } - - // Returns whether the layer should be vertically flipped. - #[cfg(target_os="macos")] - fn texture_flip_and_target(cpu_painting: bool, size: Size2D<uint>) -> (Flip, TextureTarget) { - let flip = if cpu_painting { - NoFlip - } else { - VerticalFlip - }; - - (flip, TextureTargetRectangle(size)) - } - - #[cfg(target_os="android")] - fn texture_flip_and_target(cpu_painting: bool, size: Size2D<uint>) -> (Flip, TextureTarget) { - let flip = if cpu_painting { - NoFlip - } else { - VerticalFlip - }; - - (flip, TextureTarget2D) - } - - #[cfg(target_os="linux")] - fn texture_flip_and_target(_: bool, _: Size2D<uint>) -> (Flip, TextureTarget) { - (NoFlip, TextureTarget2D) - } - - // A helper method to resize sublayers. - fn resize_helper(&mut self, - pipeline_id: PipelineId, - layer_id: LayerId, - new_size: Size2D<f32>, - epoch: Epoch) - -> bool { - debug!("compositor_layer: starting resize_helper()"); - let found = match self.children.iter().position(|kid_holder| { - pipeline_id == kid_holder.child.pipeline.id && - layer_id == kid_holder.child.id - }) { - Some(i) => { - debug!("compositor_layer: layer found for resize_helper()"); - let child_node = self.children.get_mut(i); - let child = &mut child_node.child; - child.epoch = epoch; - child.page_size = Some(new_size); - match child.quadtree { - Tree(ref mut quadtree) => { - let _ = child.pipeline.render_chan.send_opt(UnusedBufferMsg(quadtree.resize(new_size.width as uint, - new_size.height as uint))); - } - NoTree(tile_size, max_mem) => { - child.quadtree = Tree(Quadtree::new(Size2D(new_size.width as uint, - new_size.height as uint), - tile_size, - max_mem)) - } - } - match *child_node.container.scissor.borrow() { - Some(scissor) => { - // Call scroll for bounds checking if the page shrunk. Use (-1, -1) as the - // cursor position to make sure the scroll isn't propagated downwards. - let size: TypedSize2D<PagePx, f32> = Size2D::from_untyped(&scissor.size); - child.handle_scroll_event(TypedPoint2D(0f32, 0f32), - TypedPoint2D(-1f32, -1f32), - size); - child.hidden = false; - } - None => {} // Nothing to do - } - true - } - None => false, - }; - - if found { // Boolean flag to get around double borrow of self - self.set_occlusions(); - return true - } - - // If we got here, the layer's ID does not match ours, so recurse on descendents (including - // hidden children). - self.children - .mut_iter() - .map(|kid_holder| &mut kid_holder.child) - .any(|kid_holder| kid_holder.resize_helper(pipeline_id, layer_id, new_size, epoch)) - } - - // Collect buffers from the quadtree. This method IS NOT recursive, so child CompositorLayers - // are not rebuilt directly from this method. - pub fn build_layer_tree(&mut self, graphics_context: &NativeCompositingGraphicsContext) { - // Iterate over the children of the container layer. - let mut current_layer_child = self.root_layer.first_child.borrow().clone(); - - // Delete old layer. - while current_layer_child.is_some() { - let trash = current_layer_child.clone().unwrap(); - current_layer_child.clone().unwrap().with_common(|common| { - current_layer_child = common.next_sibling.clone(); - }); - ContainerLayer::remove_child(self.root_layer.clone(), trash); - } - - // Add new tiles. - let quadtree = match self.quadtree { - NoTree(..) => fail!("CompositorLayer: cannot build layer tree for {:?}, - no quadtree initialized", self.pipeline.id), - Tree(ref mut quadtree) => quadtree, - }; - - let all_tiles = quadtree.get_all_tiles(); - for buffer in all_tiles.iter() { - debug!("osmain: compositing buffer rect {}", buffer.rect); - - let size = Size2D(buffer.screen_pos.size.width as int, - buffer.screen_pos.size.height as int); - - // Find or create a texture layer. - let texture_layer; - current_layer_child = match current_layer_child.clone() { - None => { - debug!("osmain: adding new texture layer"); - - // Determine, in a platform-specific way, whether we should flip the texture - // and which target to use. - let (flip, target) = - CompositorLayer::texture_flip_and_target(self.cpu_painting, - buffer.screen_pos.size); - - // Make a new texture and bind the layer buffer's surface to it. - let texture = Texture::new(target); - debug!("COMPOSITOR binding to native surface {:d}", - buffer.native_surface.get_id() as int); - buffer.native_surface.bind_to_texture(graphics_context, &texture, size); - - // Make a texture layer and add it. - texture_layer = Rc::new(TextureLayer::new(texture, - buffer.screen_pos.size, - flip)); - ContainerLayer::add_child_end(self.root_layer.clone(), - TextureLayerKind(texture_layer.clone())); - None - } - Some(TextureLayerKind(existing_texture_layer)) => { - texture_layer = existing_texture_layer.clone(); - - let texture = &existing_texture_layer.texture; - buffer.native_surface.bind_to_texture(graphics_context, texture, size); - - // Move on to the next sibling. - current_layer_child.unwrap().with_common(|common| { - common.next_sibling.clone() - }) - } - Some(_) => fail!("found unexpected layer kind"), - }; - - // Set the layer's transform. - let rect = buffer.rect; - let transform = identity().translate(rect.origin.x, rect.origin.y, 0.0); - let transform = transform.scale(rect.size.width, rect.size.height, 1.0); - texture_layer.common.borrow_mut().set_transform(transform); - } - - // Add child layers. - for child in self.children.mut_iter().filter(|x| !x.child.hidden) { - current_layer_child = match current_layer_child { - None => { - { - let mut common = child.container.common.borrow_mut(); - (*common).parent = None; - common.prev_sibling = None; - common.next_sibling = None; - } - ContainerLayer::add_child_end(self.root_layer.clone(), - ContainerLayerKind(child.container.clone())); - None - } - Some(_) => { - fail!("CompositorLayer: Layer tree failed to delete"); - } - }; - } - } - - // Add LayerBuffers to the specified layer. Returns the layer buffer set back if the layer that - // matches the given pipeline ID was not found; otherwise returns None and consumes the layer - // buffer set. - // - // 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. - pub fn add_buffers(&mut self, - graphics_context: &NativeCompositingGraphicsContext, - pipeline_id: PipelineId, - layer_id: LayerId, - mut new_buffers: Box<LayerBufferSet>, - epoch: Epoch) - -> Option<Box<LayerBufferSet>> { - debug!("compositor_layer: starting add_buffers()"); - if self.pipeline.id != pipeline_id || self.id != layer_id { - // ID does not match ours, so recurse on descendents (including hidden children). - for child_layer in self.children.mut_iter() { - match child_layer.child.add_buffers(graphics_context, - pipeline_id, - layer_id, - new_buffers, - epoch) { - None => return None, - Some(buffers) => new_buffers = buffers, - } - } - - // Not found. Give the caller the buffers back. - return Some(new_buffers) - } - - debug!("compositor_layer: layers found for add_buffers()"); - - if self.epoch != epoch { - debug!("add_buffers: compositor epoch mismatch: {:?} != {:?}, id: {:?}", - self.epoch, - epoch, - self.pipeline.id); - let _ = self.pipeline.render_chan.send_opt(UnusedBufferMsg(new_buffers.buffers)); - return None - } - - { - let quadtree = match self.quadtree { - NoTree(..) => { - fail!("CompositorLayer: cannot add buffers, no quadtree initialized") - } - Tree(ref mut quadtree) => quadtree, - }; - - let mut unused_tiles = vec!(); - for buffer in new_buffers.buffers.move_iter().rev() { - unused_tiles.push_all_move(quadtree.add_tile_pixel(buffer.screen_pos.origin.x, - buffer.screen_pos.origin.y, - buffer.resolution, - buffer)); - } - if !unused_tiles.is_empty() { // send back unused buffers - let _ = self.pipeline.render_chan.send_opt(UnusedBufferMsg(unused_tiles)); - } - } - - self.build_layer_tree(graphics_context); - return None - } - - // Recursively sets occluded portions of quadtrees to Hidden, so that they do not ask for - // tile requests. If layers are moved, resized, or deleted, these portions may be updated. - fn set_occlusions(&mut self) { - let quadtree = match self.quadtree { - NoTree(..) => return, // Cannot calculate occlusions - Tree(ref mut quadtree) => quadtree, - }; - for child in self.children.iter().filter(|x| !x.child.hidden) { - match *child.container.scissor.borrow() { - None => {} // Nothing to do - Some(rect) => { - quadtree.set_status_page(rect, Hidden, false); - } - } - } - for child in self.children.mut_iter().filter(|x| !x.child.hidden) { - child.child.set_occlusions(); - } - } - - /// Destroys all quadtree tiles, sending the buffers back to the renderer to be destroyed or - /// reused. - fn clear(&mut self) { - match self.quadtree { - NoTree(..) => {} - Tree(ref mut quadtree) => { - let mut tiles = quadtree.collect_tiles(); - - // We have no way of knowing without a race whether the render task is even up and - // running, but mark the tiles as not leaking. If the render task died, then the - // tiles are going to be cleaned up. - for tile in tiles.mut_iter() { - tile.mark_wont_leak() - } - - let _ = self.pipeline.render_chan.send_opt(UnusedBufferMsg(tiles)); - } - } - } - - /// Destroys tiles for this layer and all descendent layers, sending the buffers back to the - /// renderer to be destroyed or reused. - pub fn clear_all_tiles(&mut self) { - self.clear(); - for kid in self.children.mut_iter() { - kid.child.clear_all_tiles(); - } - } - - /// Destroys all tiles of all layers, including children, *without* sending them back to the - /// renderer. You must call this only when the render task is destined to be going down; - /// otherwise, you will leak tiles. - /// - /// This is used during shutdown, when we know the render task is going away. - pub fn forget_all_tiles(&mut self) { - match self.quadtree { - NoTree(..) => {} - Tree(ref mut quadtree) => { - let tiles = quadtree.collect_tiles(); - for tile in tiles.move_iter() { - let mut tile = tile; - tile.mark_wont_leak() - } - } - } - - for kid in self.children.mut_iter() { - kid.child.forget_all_tiles(); - } - } - - pub fn id_of_first_child(&self) -> LayerId { - self.children.iter().next().expect("no first child!").child.id - } - - pub fn set_unrendered_color(&mut self, pipeline_id: PipelineId, layer_id: LayerId, color: Color) -> bool { - if self.pipeline.id != pipeline_id || self.id != layer_id { - for child_layer in self.children.mut_iter() { - if child_layer.child.set_unrendered_color(pipeline_id, layer_id, color) { - return true; - } - } - return false; - } - - self.unrendered_color = color; - return true; - } -} - diff --git a/src/components/compositing/compositor_task.rs b/src/components/compositing/compositor_task.rs index e4e1654f200..faf34915a84 100644 --- a/src/components/compositing/compositor_task.rs +++ b/src/components/compositing/compositor_task.rs @@ -15,7 +15,8 @@ use geom::point::Point2D; use geom::rect::Rect; use geom::size::Size2D; use layers::platform::surface::{NativeCompositingGraphicsContext, NativeGraphicsMetadata}; -use servo_msg::compositor_msg::{Epoch, LayerBufferSet, LayerId, LayerMetadata, ReadyState}; +use layers::layers::LayerBufferSet; +use servo_msg::compositor_msg::{Epoch, LayerId, LayerMetadata, ReadyState}; use servo_msg::compositor_msg::{RenderListener, RenderState, ScriptListener, ScrollPolicy}; use servo_msg::constellation_msg::{ConstellationChan, PipelineId}; use servo_util::memory::MemoryProfilerChan; diff --git a/src/components/compositing/quadtree.rs b/src/components/compositing/quadtree.rs deleted file mode 100644 index f3b4e44e301..00000000000 --- a/src/components/compositing/quadtree.rs +++ /dev/null @@ -1,734 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -// Implements a Quadtree data structure to keep track of which tiles have -// been rasterized and which have not. - -use geom::point::Point2D; -use geom::size::Size2D; -use geom::rect::Rect; -use gfx::render_task::BufferRequest; -use std::cmp; -use std::mem::replace; -use std::num::next_power_of_two; -use servo_msg::compositor_msg::Tile; - -#[cfg(test)] -use layers::platform::surface::NativePaintingGraphicsContext; - -/// Parent to all quadtree nodes. Stores variables needed at all levels. All method calls -/// at this level are in pixel coordinates. -pub struct Quadtree<T> { - // The root node of the quadtree - pub root: Box<QuadtreeNode<T>>, - // The size of the layer in pixels. Tiles will be clipped to this size. - // Note that the underlying quadtree has a potentailly larger size, since it is rounded - // to the next highest power of two. - pub clip_size: Size2D<uint>, - // The maximum size of the tiles requested in pixels. Tiles requested will be - // of a size anywhere between half this value and this value. - pub max_tile_size: uint, - // The maximum allowed total memory of tiles in the tree. If this limit is reached, tiles - // will be removed from the tree. Set this to None to prevent this behavior. - pub max_mem: Option<uint>, -} - -/// A node in the tree. All method calls at this level are in page coordinates. -struct QuadtreeNode<T> { - /// The tile belonging to this node. Note that parent nodes can have tiles. - pub tile: Option<T>, - /// The position of the node in page coordinates. - pub origin: Point2D<f32>, - /// The width and height of the node in page coordinates. - pub size: f32, - /// The node's children. - pub quadrants: [Option<Box<QuadtreeNode<T>>>, ..4], - /// Combined size of self.tile and tiles of all descendants - pub tile_mem: uint, - /// The current status of this node. See below for details. - pub status: NodeStatus, -} - -/// The status of a QuadtreeNode. This determines the behavior of the node -/// when querying for tile requests. -#[deriving(PartialEq)] -pub enum NodeStatus { - /// If we have no valid tile, request one; otherwise, don't send a request. - Normal, - /// Render request has been sent; ignore this node until tile is inserted. - Rendering, - /// Do not send tile requests. Overrides Invalid. - Hidden, - /// Send tile requests, even if the node has (or child nodes have) a valid tile. - Invalid, -} - -enum Quadrant { - TL = 0, - TR = 1, - BL = 2, - BR = 3, -} - -fn div_ceil(x: uint, y: uint) -> uint { - let div = x / y; - if x % y == 0u { div } - else { div + 1u } -} - -impl<T: Tile> Quadtree<T> { - /// Public method to create a new Quadtree - /// Takes in the initial width and height of the space, a maximum tile size, and - /// a maximum amount of memory. Tiles will be deleted if this memory is exceeded. - /// Set max_mem to None to turn off automatic tile removal. - pub fn new(clip_size: Size2D<uint>, tile_size: uint, max_mem: Option<uint>) -> Quadtree<T> { - // Spaces must be squares and powers of 2, so expand the space until it is - let longer = cmp::max(clip_size.width, clip_size.height); - let num_tiles = div_ceil(longer, tile_size); - let power_of_two = next_power_of_two(num_tiles); - let size = power_of_two * tile_size; - - Quadtree { - root: box QuadtreeNode { - tile: None, - origin: Point2D(0f32, 0f32), - size: size as f32, - quadrants: [None, None, None, None], - tile_mem: 0, - status: Normal, - }, - clip_size: clip_size, - max_tile_size: tile_size, - max_mem: max_mem, - } - } - - /// Add a tile associated with a given pixel position and scale. - /// If the tile pushes the total memory over its maximum, tiles will be removed - /// until total memory is below the maximum again. These tiles are returned. - pub fn add_tile_pixel(&mut self, x: uint, y: uint, scale: f32, tile: T) -> Vec<T> { - let (_, tiles) = self.root.add_tile(x as f32 / scale, y as f32 / scale, tile, - self.max_tile_size as f32 / scale); - let mut tiles = tiles; - match self.max_mem { - Some(max) => { - while self.root.tile_mem > max { - let r = self.root.remove_tile(x as f32 / scale, y as f32 / scale); - match r { - (Some(tile), _, _) => tiles.push(tile), - _ => fail!("Quadtree: No valid tiles to remove"), - } - } - } - None => {} // Nothing to do - } - tiles - } - - /// Get all the tiles in the tree. - pub fn get_all_tiles<'r>(&'r self) -> Vec<&'r T> { - self.root.get_all_tiles() - } - - /// Given a window rect in pixel coordinates, this function returns a list of BufferRequests for tiles that - /// need to be rendered. It also returns a vector of tiles if the window needs to be redisplayed, i.e. if - /// no tiles need to be rendered, but the display tree needs to be rebuilt. This can occur when the - /// user zooms out and cached tiles need to be displayed on top of higher resolution tiles. - /// When this happens, higher resolution tiles will be removed from the quadtree. - #[cfg(test)] - pub fn get_tile_rects_pixel(&mut self, window: Rect<int>, scale: f32) -> (Vec<BufferRequest>, Vec<T>) { - let (ret, unused, _) = self.root.get_tile_rects( - Rect(Point2D(window.origin.x as f32 / scale, window.origin.y as f32 / scale), - Size2D(window.size.width as f32 / scale, window.size.height as f32 / scale)), - Size2D(self.clip_size.width as f32, self.clip_size.height as f32), - scale, self.max_tile_size as f32 / scale, false); - (ret, unused) - } - - /// Same function as above, using page coordinates for the window. - pub fn get_tile_rects_page(&mut self, window: Rect<f32>, scale: f32) -> (Vec<BufferRequest>, Vec<T>) { - let (ret, unused, _) = self.root.get_tile_rects( - window, - Size2D(self.clip_size.width as f32, self.clip_size.height as f32), - scale, self.max_tile_size as f32 / scale, false); - (ret, unused) - } - - /// Creates a new quadtree at the specified size. This should be called when the window changes size. - pub fn resize(&mut self, width: uint, height: uint) -> Vec<T> { - // Spaces must be squares and powers of 2, so expand the space until it is - let longer = cmp::max(width, height); - let num_tiles = div_ceil(longer, self.max_tile_size); - let power_of_two = next_power_of_two(num_tiles); - let size = power_of_two * self.max_tile_size; - let ret = self.root.collect_tiles(); - self.root = box QuadtreeNode { - tile: None, - origin: Point2D(0f32, 0f32), - size: size as f32, - quadrants: [None, None, None, None], - status: Normal, - tile_mem: 0, - }; - self.clip_size = Size2D(width, height); - ret - } - - /// Resize the underlying quadtree without removing tiles already in place. - /// Might be useful later on, but resize() should be used for now. - /// TODO: return tiles after shrinking - #[cfg(test)] - pub fn bad_resize(&mut self, width: uint, height: uint) { - self.clip_size = Size2D(width, height); - let longer = cmp::max(width, height); - let new_num_tiles = div_ceil(longer, self.max_tile_size); - let new_size = next_power_of_two(new_num_tiles); - // difference here indicates the number of times the underlying size of the quadtree needs - // to be doubled or halved. It will recursively add a new root if it is positive, or - // recursivly make a child the new root if it is negative. - let difference = (new_size as f32 / self.root.size as f32).log2() as int; - if difference > 0 { // doubling - let difference = difference as uint; - for i in range(0, difference) { - let new_root = box QuadtreeNode { - tile: None, - origin: Point2D(0f32, 0f32), - size: new_size as f32 / ((difference - i - 1) as f32).exp2(), - quadrants: [None, None, None, None], - tile_mem: self.root.tile_mem, - status: Normal, - }; - self.root.quadrants[TL as uint] = Some(replace(&mut self.root, new_root)); - } - } else if difference < 0 { // halving - let difference = difference.abs() as uint; - for _ in range(0, difference) { - let remove = replace(&mut self.root.quadrants[TL as uint], None); - match remove { - Some(child) => self.root = child, - None => { - self.root = box QuadtreeNode { - tile: None, - origin: Point2D(0f32, 0f32), - size: new_size as f32, - quadrants: [None, None, None, None], - tile_mem: 0, - status: Normal, - }; - break; - } - } - } - } - } - - /// Set the status of all quadtree nodes within the given rect in page coordinates. If - /// include_border is true, then nodes on the edge of the rect will be included; otherwise, - /// only nodes completely occluded by the rect will be changed. - pub fn set_status_page(&mut self, rect: Rect<f32>, status: NodeStatus, include_border: bool) { - self.root.set_status(rect, status, include_border); - } - - /// Remove and return all tiles in the tree. Use this before deleting the quadtree to prevent - /// a GC pause. - pub fn collect_tiles(&mut self) -> Vec<T> { - self.root.collect_tiles() - } -} - -impl<T: Tile> QuadtreeNode<T> { - /// Private method to create new children - fn new_child(x: f32, y: f32, size: f32) -> QuadtreeNode<T> { - QuadtreeNode { - tile: None, - origin: Point2D(x, y), - size: size, - quadrants: [None, None, None, None], - tile_mem: 0, - status: Normal, - } - } - - /// Determine which child contains a given point in page coords. - fn get_quadrant(&self, x: f32, y: f32) -> Quadrant { - if x < self.origin.x + self.size / 2.0 { - if y < self.origin.y + self.size / 2.0 { - TL - } else { - BL - } - } else if y < self.origin.y + self.size / 2.0 { - TR - } else { - BR - } - } - - /// Get all tiles in the tree, parents first. - fn get_all_tiles<'r>(&'r self) -> Vec<&'r T> { - let mut ret = vec!(); - - match self.tile { - Some(ref tile) => ret.push(tile), - None => {} - } - - for quad in self.quadrants.iter() { - match *quad { - Some(ref child) => ret.push_all_move(child.get_all_tiles()), - None => {} - } - } - - return ret; - } - - /// Add a tile associated with a given position in page coords. If the tile size exceeds the maximum, - /// the node will be split and the method will recurse until the tile size is within limits. - /// Returns an the difference in tile memory between the new quadtree node and the old quadtree node, - /// along with any deleted tiles. - fn add_tile(&mut self, x: f32, y: f32, tile: T, tile_size: f32) -> (int, Vec<T>) { - debug!("Quadtree: Adding: ({}, {}) size:{}px", self.origin.x, self.origin.y, self.size); - - if x >= self.origin.x + self.size || x < self.origin.x - || y >= self.origin.y + self.size || y < self.origin.y { - fail!("Quadtree: Tried to add tile to invalid region"); - } - - if self.size <= tile_size { // We are the child - let old_size = self.tile_mem; - self.tile_mem = tile.get_mem(); - let mut unused_tiles = match replace(&mut self.tile, Some(tile)) { - Some(old_tile) => vec!(old_tile), - None => vec!(), - }; - for child in self.quadrants.mut_iter() { - match *child { - Some(ref mut node) => { - unused_tiles.push_all_move(node.collect_tiles()); - } - None => {} // Nothing to do - } - *child = None; - } - self.status = Normal; - (self.tile_mem as int - old_size as int, unused_tiles) - } else { // Send tile to children - let quad = self.get_quadrant(x, y); - match self.quadrants[quad as uint] { - Some(ref mut child) => { - let (delta, unused) = child.add_tile(x, y, tile, tile_size); - self.tile_mem = (self.tile_mem as int + delta) as uint; - (delta, unused) - } - None => { // Make new child - let new_size = self.size / 2.0; - let new_x = match quad { - TL | BL => self.origin.x, - TR | BR => self.origin.x + new_size, - }; - let new_y = match quad { - TL | TR => self.origin.y, - BL | BR => self.origin.y + new_size, - }; - let mut c = box QuadtreeNode::new_child(new_x, new_y, new_size); - let (delta, unused) = c.add_tile(x, y, tile, tile_size); - self.tile_mem = (self.tile_mem as int + delta) as uint; - self.quadrants[quad as uint] = Some(c); - (delta, unused) - } - } - } - } - - /// Get a tile rect in screen and page coords for a given position in page coords - fn get_tile_rect(&mut self, x: f32, y: f32, clip_x: f32, clip_y: f32, scale: f32, - tile_size: f32) -> BufferRequest { - if x >= self.origin.x + self.size || x < self.origin.x - || y >= self.origin.y + self.size || y < self.origin.y { - fail!("Quadtree: Tried to query a tile rect outside of range"); - } - - if self.size <= tile_size { - let pix_x = (self.origin.x * scale).ceil() as uint; - let pix_y = (self.origin.y * scale).ceil() as uint; - let page_width = self.size.min(clip_x - self.origin.x); - let page_height = self.size.min(clip_y - self.origin.y); - let pix_width = (page_width * scale).ceil() as uint; - let pix_height = (page_height * scale).ceil() as uint; - self.status = Rendering; - return BufferRequest(Rect(Point2D(pix_x, pix_y), Size2D(pix_width, pix_height)), - Rect(Point2D(self.origin.x, self.origin.y), Size2D(page_width, page_height))); - } - - let quad = self.get_quadrant(x,y); - match self.quadrants[quad as uint] { - None => { - let new_size = self.size / 2.0; - let new_x = match quad { - TL | BL => self.origin.x, - TR | BR => self.origin.x + new_size, - }; - let new_y = match quad { - TL | TR => self.origin.y, - BL | BR => self.origin.y + new_size, - }; - let mut c = box QuadtreeNode::new_child(new_x, new_y, new_size); - let result = c.get_tile_rect(x, y, clip_x, clip_y, scale, tile_size); - self.quadrants[quad as uint] = Some(c); - result - } - Some(ref mut child) => child.get_tile_rect(x, y, clip_x, clip_y, scale, tile_size), - } - } - - /// Removes a tile that is far from the given input point in page coords. Returns the tile removed, - /// a bool that is true if the child has no tiles and needs to be deleted, and an integer showing the - /// amount of memory changed by the operation. Unfortunately, the tile has to be an option, because - /// there are occasionally leaves without tiles. However, the option will always be Some as long as - /// this quadtree node or at least one of its descendants is not empty. - fn remove_tile(&mut self, x: f32, y: f32) -> (Option<T>, bool, int) { - if self.tile.is_some() { - let ret = replace(&mut(self.tile), None); - return match (ret, &self.quadrants) { - (Some(tile), &[None, None, None, None]) => { - let size = -(tile.get_mem() as int); - (Some(tile), true, size) - } - (Some(tile), _) => { - let size = -(tile.get_mem() as int); - (Some(tile), false, size) - } - _ => fail!("Quadtree: tile query failure in remove_tile"), - } - } - - // This is a hacky heuristic to find a tile that is "far away". There are better methods. - let quad = self.get_quadrant(x, y); - let queue = match quad { - TL => [BR, BL, TR, TL], - TR => [BL, BR, TL, TR], - BL => [TR, TL, BR, BL], - BR => [TL, TR, BL, BR], - }; - - let mut del_quad: Option<Quadrant> = None; - let mut ret = (None, false, 0); - - for quad in queue.iter() { - match self.quadrants[*quad as uint] { - Some(ref mut child) => { - let (tile, flag, delta) = child.remove_tile(x, y); - match tile { - Some(_) => { - self.tile_mem = (self.tile_mem as int + delta) as uint; - if flag { - del_quad = Some(*quad); - } else { - return (tile, flag, delta); - } - - ret = (tile, flag, delta); - break; - } - None => {}, - } - } - None => {}, - } - } - - match del_quad { - Some(quad) => { - self.quadrants[quad as uint] = None; - let (tile, _, delta) = ret; - match (&self.tile, &self.quadrants) { - (&None, &[None, None, None, None]) => (tile, true, delta), - _ => (tile, false, delta) - } - } - None => ret, - } - } - - /// Given a window rect in page coordinates, returns a BufferRequest array, - /// an unused tile array, and the difference in tile memory between the new and old quadtree nodes. - /// The override bool will be true if a parent node was marked as invalid; child nodes will be - /// treated as invalid as well. - /// NOTE: this method will sometimes modify the tree by deleting tiles. - /// See the QuadTree function description for more details. - fn get_tile_rects(&mut self, - window: Rect<f32>, - clip: Size2D<f32>, - scale: f32, - tile_size: f32, - override: bool) - -> (Vec<BufferRequest>, Vec<T>, int) { - let w_x = window.origin.x; - let w_y = window.origin.y; - let w_width = window.size.width; - let w_height = window.size.height; - let s_x = self.origin.x; - let s_y = self.origin.y; - let s_size = self.size; - - // if window is outside of visible region, nothing to do - if w_x + w_width < s_x || w_x > s_x + s_size - || w_y + w_height < s_y || w_y > s_y + s_size - || w_x >= clip.width || w_y >= clip.height { - return (vec!(), vec!(), 0); - } - - // clip window to visible region - let w_width = w_width.min(clip.width - w_x); - let w_height = w_height.min(clip.height - w_y); - - if s_size <= tile_size { // We are the child - return match self.tile { - _ if self.status == Rendering || self.status == Hidden => (vec!(), vec!(), 0), - Some(ref tile) if tile.is_valid(scale) && !override - && self.status != Invalid => { - let redisplay = match self.quadrants { - [None, None, None, None] => false, - _ => true, - }; - let mut delta = 0; - let mut unused_tiles = vec!(); - if redisplay { - let old_mem = self.tile_mem; - for child in self.quadrants.mut_iter() { - match *child { - Some(ref mut node) => { - unused_tiles.push_all_move(node.collect_tiles()); - } - None => {} // Nothing to do - } - *child = None; - } - self.tile_mem = tile.get_mem(); - delta = self.tile_mem as int - old_mem as int; - - } - (vec!(), unused_tiles, delta) - } - _ => (vec!(self.get_tile_rect(s_x, s_y, clip.width, clip.height, scale, tile_size)), vec!(), 0), - } - } - - // Otherwise, we either have children or will have children - let w_tl_quad = self.get_quadrant(w_x, w_y); - let w_br_quad = self.get_quadrant(w_x + w_width, w_y + w_height); - - // Figure out which quadrants the window is in - let mut quads_to_check = Vec::with_capacity(4); - match (w_tl_quad, w_br_quad) { - (tl, br) if tl as int == br as int => { - quads_to_check.push(tl); - } - (TL, br) => { - quads_to_check.push(TL); - quads_to_check.push(br); - match br { - BR => { - quads_to_check.push(TR); - quads_to_check.push(BL); - } - _ => {} - } - } - (tl, br) => { - quads_to_check.push(tl); - quads_to_check.push(br); - } - } - - let mut request = vec!(); - let mut unused = vec!(); - let mut delta = 0; - - for quad in quads_to_check.iter() { - // Recurse into child - let new_window = match *quad { - TL => Rect(window.origin, - Size2D(w_width.min(s_x + s_size / 2.0 - w_x), - w_height.min(s_y + s_size / 2.0 - w_y))), - TR => Rect(Point2D(w_x.max(s_x + s_size / 2.0), - w_y), - Size2D(w_width.min(w_x + w_width - (s_x + s_size / 2.0)), - w_height.min(s_y + s_size / 2.0 - w_y))), - BL => Rect(Point2D(w_x, - w_y.max(s_y + s_size / 2.0)), - Size2D(w_width.min(s_x + s_size / 2.0 - w_x), - w_height.min(w_y + w_height - (s_y + s_size / 2.0)))), - BR => Rect(Point2D(w_x.max(s_x + s_size / 2.0), - w_y.max(s_y + s_size / 2.0)), - Size2D(w_width.min(w_x + w_width - (s_x + s_size / 2.0)), - w_height.min(w_y + w_height - (s_y + s_size / 2.0)))), - - }; - - let override = override || self.status == Invalid; - self.status = Normal; - let (c_request, c_unused, c_delta) = match self.quadrants[*quad as uint] { - Some(ref mut child) => child.get_tile_rects(new_window, clip, scale, tile_size, override), - None => { - // Create new child - let new_size = self.size / 2.0; - let new_x = match *quad { - TL | BL => self.origin.x, - TR | BR => self.origin.x + new_size, - }; - let new_y = match *quad { - TL | TR => self.origin.y, - BL | BR => self.origin.y + new_size, - }; - let mut child = box QuadtreeNode::new_child(new_x, new_y, new_size); - let ret = child.get_tile_rects(new_window, clip, scale, tile_size, override); - self.quadrants[*quad as uint] = Some(child); - ret - } - }; - - delta = delta + c_delta; - request.push_all(c_request.as_slice()); - unused.push_all_move(c_unused); - } - self.tile_mem = (self.tile_mem as int + delta) as uint; - (request, unused, delta) - } - - /// Remove all tiles from the tree. Use this to collect all tiles before deleting a branch. - fn collect_tiles(&mut self) -> Vec<T> { - let mut ret = match replace(&mut self.tile, None) { - Some(tile) => vec!(tile), - None => vec!(), - }; - for child in self.quadrants.mut_iter() { - match *child { - Some(ref mut node) => { - ret.push_all_move(node.collect_tiles()); - } - None => {} // Nothing to do - } - } - ret - } - - /// Set the status of nodes contained within the rect. See the quadtree method for - /// more info. - fn set_status(&mut self, rect: Rect<f32>, status: NodeStatus, borders: bool) { - let self_rect = Rect(self.origin, Size2D(self.size, self.size)); - let intersect = rect.intersection(&self_rect); - let intersect = match intersect { - None => return, // We do not intersect the rect, nothing to do - Some(rect) => rect, - }; - - if self_rect == intersect { // We are completely contained in the rect - if !(self.status == Hidden && status == Invalid) { // Hidden trumps Invalid - self.status = status; - } - return; // No need to recurse - } - - match self.quadrants { - [None, None, None, None] => { // We are a leaf - if borders && !(self.status == Hidden && status == Invalid) { - self.status = status; - } - } - _ => { // We are internal - for quad in self.quadrants.mut_iter() { - match *quad { - None => {} // Nothing to do - Some(ref mut child) => { - child.set_status(intersect, status, borders); - } - } - } - } - } - } -} - -#[test] -pub fn test_resize() { - struct T { - a: int, - } - - impl Tile for T { - fn get_mem(&self) -> uint { - 1 - } - - fn is_valid(&self, _: f32) -> bool { - true - } - fn get_size_2d(&self) -> Size2D<uint> { - Size2D(0u, 0u) - } - fn mark_wont_leak(&mut self) {} - fn destroy(self, _: &NativePaintingGraphicsContext) {} - } - - let mut q = Quadtree::new(Size2D(6u, 6), 1, None); - q.add_tile_pixel(0, 0, 1f32, T{a: 0}); - q.add_tile_pixel(5, 5, 1f32, T{a: 1}); - q.bad_resize(8, 1); - assert!(q.root.size == 8.0); - q.bad_resize(18, 1); - assert!(q.root.size == 32.0); - q.bad_resize(8, 1); - assert!(q.root.size == 8.0); - q.bad_resize(3, 1); - assert!(q.root.size == 4.0); - assert!(q.get_all_tiles().len() == 1); -} - -#[test] -pub fn test() { - struct T { - a: int, - } - - impl Tile for T { - fn get_mem(&self) -> uint { - 1 - } - - fn is_valid(&self, _: f32) -> bool { - true - } - fn get_size_2d(&self) -> Size2D<uint> { - Size2D(0u, 0u) - } - fn mark_wont_leak(&mut self) {} - fn destroy(self, _: &NativePaintingGraphicsContext) {} - } - - let mut q = Quadtree::new(Size2D(8u, 8), 2, Some(4)); - q.add_tile_pixel(0, 0, 1f32, T{a: 0}); - q.add_tile_pixel(0, 0, 2f32, T{a: 1}); - q.add_tile_pixel(0, 0, 2f32, T{a: 2}); - q.add_tile_pixel(2, 0, 2f32, T{a: 3}); - assert!(q.root.tile_mem == 3); - assert!(q.get_all_tiles().len() == 3); - q.add_tile_pixel(0, 2, 2f32, T{a: 4}); - q.add_tile_pixel(2, 2, 2f32, T{a: 5}); - assert!(q.root.tile_mem == 4); - - let (request, _) = q.get_tile_rects_pixel(Rect(Point2D(0, 0), Size2D(2, 2)), 2f32); - assert!(request.is_empty()); - let (request, _) = q.get_tile_rects_pixel(Rect(Point2D(0, 0), Size2D(2, 2)), 1.9); - assert!(request.is_empty()); - let (request, _) = q.get_tile_rects_pixel(Rect(Point2D(0, 0), Size2D(2, 2)), 1f32); - assert!(request.len() == 4); - - q.add_tile_pixel(0, 0, 0.5, T{a: 6}); - q.add_tile_pixel(0, 0, 1f32, T{a: 7}); - let (_, unused) = q.get_tile_rects_pixel(Rect(Point2D(0, 0), Size2D(2, 2)), 0.5); - assert!(!unused.is_empty()); - assert!(q.root.tile_mem == 1); -} diff --git a/src/components/gfx/buffer_map.rs b/src/components/gfx/buffer_map.rs index 2d052b09553..5bdb490c092 100644 --- a/src/components/gfx/buffer_map.rs +++ b/src/components/gfx/buffer_map.rs @@ -5,7 +5,7 @@ use std::collections::hashmap::HashMap; use geom::size::Size2D; use layers::platform::surface::NativePaintingGraphicsContext; -use servo_msg::compositor_msg::Tile; +use layers::quadtree::Tile; use std::hash::Hash; use std::hash::sip::SipState; use std::mem; diff --git a/src/components/gfx/render_task.rs b/src/components/gfx/render_task.rs index 17c5e23f657..8048db3b70d 100644 --- a/src/components/gfx/render_task.rs +++ b/src/components/gfx/render_task.rs @@ -17,11 +17,12 @@ use geom::rect::Rect; use geom::size::Size2D; use layers::platform::surface::{NativePaintingGraphicsContext, NativeSurface}; use layers::platform::surface::{NativeSurfaceMethods}; +use layers::layers::{BufferRequest, LayerBuffer, LayerBufferSet}; use layers; use native; use rustrt::task; use rustrt::task::TaskOpts; -use servo_msg::compositor_msg::{Epoch, IdleRenderState, LayerBuffer, LayerBufferSet, LayerId}; +use servo_msg::compositor_msg::{Epoch, IdleRenderState, LayerId}; use servo_msg::compositor_msg::{LayerMetadata, RenderListener, RenderingRenderState, ScrollPolicy}; use servo_msg::constellation_msg::{ConstellationChan, Failure, FailureMsg, PipelineId}; use servo_msg::constellation_msg::{RendererReadyMsg}; @@ -57,23 +58,6 @@ pub enum Msg { ExitMsg(Option<Sender<()>>), } -/// A request from the compositor to the renderer for tiles that need to be (re)displayed. -#[deriving(Clone)] -pub struct BufferRequest { - // The rect in pixels that will be drawn to the screen - screen_rect: Rect<uint>, - - // The rect in page coordinates that this tile represents - page_rect: Rect<f32>, -} - -pub fn BufferRequest(screen_rect: Rect<uint>, page_rect: Rect<f32>) -> BufferRequest { - BufferRequest { - screen_rect: screen_rect, - page_rect: page_rect, - } -} - #[deriving(Clone)] pub struct RenderChan(Sender<Msg>); diff --git a/src/components/msg/compositor_msg.rs b/src/components/msg/compositor_msg.rs index 8955e746efa..b1e4ae210a0 100644 --- a/src/components/msg/compositor_msg.rs +++ b/src/components/msg/compositor_msg.rs @@ -5,48 +5,14 @@ use azure::azure_hl::Color; use geom::point::Point2D; use geom::rect::Rect; -use geom::size::Size2D; -use layers::platform::surface::{NativeGraphicsMetadata, NativePaintingGraphicsContext}; -use layers::platform::surface::{NativeSurface, NativeSurfaceMethods}; +use layers::platform::surface::NativeGraphicsMetadata; +use layers::layers::LayerBufferSet; use serialize::{Encoder, Encodable}; use std::fmt::{Formatter, Show}; use std::fmt; use constellation_msg::PipelineId; -pub struct LayerBuffer { - /// The native surface which can be shared between threads or processes. On Mac this is an - /// `IOSurface`; on Linux this is an X Pixmap; on Android this is an `EGLImageKHR`. - pub native_surface: NativeSurface, - - /// The rect in the containing RenderLayer that this represents. - pub rect: Rect<f32>, - - /// The rect in pixels that will be drawn to the screen. - pub screen_pos: Rect<uint>, - - /// The scale at which this tile is rendered - pub resolution: f32, - - /// NB: stride is in pixels, like OpenGL GL_UNPACK_ROW_LENGTH. - pub stride: uint, -} - -/// A set of layer buffers. This is an atomic unit used to switch between the front and back -/// buffers. -pub struct LayerBufferSet { - pub buffers: Vec<Box<LayerBuffer>> -} - -impl LayerBufferSet { - /// Notes all buffer surfaces will leak if not destroyed via a call to `destroy`. - pub fn mark_will_leak(&mut self) { - for buffer in self.buffers.mut_iter() { - buffer.native_surface.mark_will_leak() - } - } -} - /// The status of the renderer. #[deriving(PartialEq, Clone)] pub enum RenderState { @@ -160,41 +126,3 @@ impl<E, S: Encoder<E>> Encodable<S, E> for Box<ScriptListener> { Ok(()) } } - -/// The interface used by the quadtree and buffer map to get info about layer buffers. -pub trait Tile { - /// Returns the amount of memory used by the tile - fn get_mem(&self) -> uint; - /// Returns true if the tile is displayable at the given scale - fn is_valid(&self, f32) -> bool; - /// Returns the Size2D of the tile - fn get_size_2d(&self) -> Size2D<uint>; - - /// Marks the layer buffer as not leaking. See comments on - /// `NativeSurfaceMethods::mark_wont_leak` for how this is used. - fn mark_wont_leak(&mut self); - - /// Destroys the layer buffer. Painting task only. - fn destroy(self, graphics_context: &NativePaintingGraphicsContext); -} - -impl Tile for Box<LayerBuffer> { - fn get_mem(&self) -> uint { - // This works for now, but in the future we may want a better heuristic - self.screen_pos.size.width * self.screen_pos.size.height - } - fn is_valid(&self, scale: f32) -> bool { - (self.resolution - scale).abs() < 1.0e-6 - } - fn get_size_2d(&self) -> Size2D<uint> { - self.screen_pos.size - } - fn mark_wont_leak(&mut self) { - self.native_surface.mark_wont_leak() - } - fn destroy(self, graphics_context: &NativePaintingGraphicsContext) { - let mut this = self; - this.native_surface.destroy(graphics_context) - } -} - diff --git a/src/support/layers/rust-layers b/src/support/layers/rust-layers -Subproject 2b208270fac74af639228bc7d065436a73e67b2 +Subproject 5ff99f3ac797bd1bed205549d705264fc85d309 |