From f9df74595c8c0d01d3db87ed8b84db3d51def039 Mon Sep 17 00:00:00 2001 From: eschweic Date: Fri, 16 Aug 2013 16:05:32 -0700 Subject: [PATCH] Add NodeStatus enum, infrastructure for compositor-side invalidation --- .../main/compositing/compositor_layer.rs | 15 ++- src/components/main/compositing/mod.rs | 19 ++++ src/components/main/compositing/quadtree.rs | 92 ++++++++++++++++--- src/components/msg/compositor_msg.rs | 1 + 4 files changed, 111 insertions(+), 16 deletions(-) diff --git a/src/components/main/compositing/compositor_layer.rs b/src/components/main/compositing/compositor_layer.rs index c846e58a175..fce05548531 100644 --- a/src/components/main/compositing/compositor_layer.rs +++ b/src/components/main/compositing/compositor_layer.rs @@ -12,7 +12,7 @@ use servo_msg::constellation_msg::PipelineId; use script::dom::event::{ClickEvent, MouseDownEvent, MouseUpEvent}; use script::script_task::SendEventMsg; use windowing::{MouseWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent, MouseWindowMouseUpEvent}; -use compositing::quadtree::Quadtree; +use compositing::quadtree::{Quadtree, Invalid}; use layers::layers::{ContainerLayerKind, ContainerLayer, TextureLayerKind, TextureLayer, TextureManager}; use pipeline::Pipeline; @@ -366,6 +366,19 @@ impl CompositorLayer { } } } + + pub fn invalidate_rect(&mut self, pipeline_id: PipelineId, rect: Rect) -> bool { + if self.pipeline.id == pipeline_id { + let quadtree = match self.quadtree { + NoTree(_, _) => return true, // Nothing to do + Tree(ref mut quadtree) => quadtree, + }; + quadtree.set_status_page(rect, Invalid, true); + return true; + } + // ID does not match ours, so recurse on descendents (including hidden children). + self.children.mut_iter().map(|x| &mut x.child).any(|x| x.invalidate_rect(pipeline_id, rect)) + } // Adds a child. pub fn add_child(&mut self, pipeline: Pipeline, page_size: Option>, tile_size: uint, diff --git a/src/components/main/compositing/mod.rs b/src/components/main/compositing/mod.rs index 6417e8128f4..1931037b793 100644 --- a/src/components/main/compositing/mod.rs +++ b/src/components/main/compositing/mod.rs @@ -66,6 +66,10 @@ impl ScriptListener for CompositorChan { self.chan.send(msg); } + fn invalidate_rect(&self, id: PipelineId, rect: Rect) { + self.chan.send(InvalidateRect(id, rect)); + } + } /// Implementation of the abstract `RenderListener` interface. @@ -131,6 +135,8 @@ pub enum Msg { ResizeLayer(PipelineId, Size2D), /// Alerts the compositor that the specified layer has been deleted. DeleteLayer(PipelineId), + /// Invalidate a rect for a given layer + InvalidateRect(PipelineId, Rect), /// Requests that the compositor paint the given layer buffer set for the given page size. Paint(PipelineId, arc::Arc), @@ -312,6 +318,19 @@ impl CompositorTask { // TODO: Recycle the old buffers; send them back to the renderer to reuse if // it wishes. } + + InvalidateRect(id, rect) => { + match compositor_layer { + Some(ref mut layer) => { + layer.invalidate_rect(id, Rect(Point2D(rect.origin.x as f32, + rect.origin.y as f32), + Size2D(rect.size.width as f32, + rect.size.height as f32))); + ask_for_tiles(); + } + None => {} // Nothing to do + } + } } } }; diff --git a/src/components/main/compositing/quadtree.rs b/src/components/main/compositing/quadtree.rs index d58f568694d..0ffc9fa31e7 100644 --- a/src/components/main/compositing/quadtree.rs +++ b/src/components/main/compositing/quadtree.rs @@ -41,10 +41,24 @@ struct QuadtreeNode { size: f32, /// The node's children. quadrants: [Option<~QuadtreeNode>, ..4], - /// If this node is marked for rendering - render_flag: bool, /// Combined size of self.tile and tiles of all descendants tile_mem: uint, + /// The current status of this node. See below for details. + status: NodeStatus, +} + +/// The status of a QuadtreeNode. This determines the behavior of the node +/// when querying for tile requests. +#[deriving(Eq)] +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 { @@ -72,8 +86,8 @@ impl Quadtree { origin: Point2D(0f32, 0f32), size: size as f32, quadrants: [None, None, None, None], - render_flag: false, tile_mem: 0, + status: Normal, }, clip_size: Size2D(width, height), max_tile_size: tile_size, @@ -184,7 +198,7 @@ impl Quadtree { 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); + scale, self.max_tile_size as f32 / scale, false); (ret, redisplay) } @@ -193,7 +207,7 @@ impl Quadtree { let (ret, redisplay, _) = 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); + scale, self.max_tile_size as f32 / scale, false); (ret, redisplay) } @@ -218,8 +232,8 @@ impl Quadtree { origin: Point2D(0f32, 0f32), size: new_size as f32 / ((difference - i - 1) as f32).exp2(), quadrants: [None, None, None, None], - render_flag: false, tile_mem: self.root.tile_mem, + status: Normal, }; self.root.quadrants[TL as int] = Some(replace(&mut self.root, new_root)); } @@ -235,8 +249,8 @@ impl Quadtree { origin: Point2D(0f32, 0f32), size: new_size as f32, quadrants: [None, None, None, None], - render_flag: false, tile_mem: 0, + status: Normal, }; break; } @@ -245,6 +259,13 @@ impl Quadtree { } } + /// 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, status: NodeStatus, include_border: bool) { + self.root.set_status(rect, status, include_border); + } + /// Generate html to visualize the tree. For debugging purposes only. pub fn get_html(&self) -> ~str { static HEADER: &'static str = ""; @@ -261,8 +282,8 @@ impl QuadtreeNode { origin: Point2D(x, y), size: size, quadrants: [None, None, None, None], - render_flag: false, tile_mem: 0, + status: Normal, } } @@ -335,7 +356,7 @@ impl QuadtreeNode { for quad in quads.iter() { self.quadrants[*quad as int] = None; } - self.render_flag = false; + self.status = Normal; self.tile_mem as int - old_size as int } else { // Send tile to children let quad = self.get_quadrant(x, y); @@ -380,7 +401,7 @@ impl QuadtreeNode { let page_height = (clip_y - self.origin.y).min(&self.size); let pix_width = (page_width * scale).ceil() as uint; let pix_height = (page_height * scale).ceil() as uint; - self.render_flag = true; + 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))); } @@ -477,9 +498,11 @@ impl QuadtreeNode { /// Given a window rect in page coordinates, returns a BufferRequest array, /// a redisplay boolean, 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, clip: Size2D, scale: f32, tile_size: f32) -> + fn get_tile_rects(&mut self, window: Rect, clip: Size2D, scale: f32, tile_size: f32, override: bool) -> (~[BufferRequest], bool, int) { let w_x = window.origin.x; @@ -503,8 +526,9 @@ impl QuadtreeNode { if s_size <= tile_size { // We are the child return match self.tile { - _ if self.render_flag => (~[], false, 0), - Some(ref tile) if tile.is_valid(scale) => { + _ if self.status == Rendering || self.status == Hidden => (~[], false, 0), + Some(ref tile) if tile.is_valid(scale) && !override + && self.status != Invalid => { let redisplay = match self.quadrants { [None, None, None, None] => false, _ => true, @@ -582,8 +606,10 @@ impl QuadtreeNode { }; + let override = override || self.status == Invalid; + self.status = Normal; let (c_ret, c_redisplay, c_delta) = match self.quadrants[*quad as int] { - Some(ref mut child) => child.get_tile_rects(new_window, clip, scale, tile_size), + 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; @@ -596,7 +622,7 @@ impl QuadtreeNode { BL | BR => self.origin.y + new_size, }; let mut child = ~QuadtreeNode::new_child(new_x, new_y, new_size); - let ret = child.get_tile_rects(new_window, clip, scale, tile_size); + let ret = child.get_tile_rects(new_window, clip, scale, tile_size, override); self.quadrants[*quad as int] = Some(child); ret } @@ -610,6 +636,42 @@ impl QuadtreeNode { (ret, redisplay, delta) } + /// Set the status of nodes contained within the rect. See the quadtree method for + /// more info. + fn set_status(&mut self, rect: Rect, 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); + } + } + } + } + } + } + /// Generate html to visualize the tree. /// This is really inefficient, but it's for testing only. fn get_html(&self) -> ~str { diff --git a/src/components/msg/compositor_msg.rs b/src/components/msg/compositor_msg.rs index 410da56f6e0..f1337c94301 100644 --- a/src/components/msg/compositor_msg.rs +++ b/src/components/msg/compositor_msg.rs @@ -69,6 +69,7 @@ pub trait RenderListener { /// which is used in displaying the appropriate message in the window's title. pub trait ScriptListener : Clone { fn set_ready_state(&self, ReadyState); + fn invalidate_rect(&self, PipelineId, Rect); } /// The interface used by the quadtree to get info about LayerBuffers