Add NodeStatus enum, infrastructure for compositor-side invalidation

This commit is contained in:
eschweic 2013-08-16 16:05:32 -07:00
parent 2359587cbb
commit f9df74595c
4 changed files with 111 additions and 16 deletions

View file

@ -12,7 +12,7 @@ use servo_msg::constellation_msg::PipelineId;
use script::dom::event::{ClickEvent, MouseDownEvent, MouseUpEvent}; use script::dom::event::{ClickEvent, MouseDownEvent, MouseUpEvent};
use script::script_task::SendEventMsg; use script::script_task::SendEventMsg;
use windowing::{MouseWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent, MouseWindowMouseUpEvent}; use windowing::{MouseWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent, MouseWindowMouseUpEvent};
use compositing::quadtree::Quadtree; use compositing::quadtree::{Quadtree, Invalid};
use layers::layers::{ContainerLayerKind, ContainerLayer, TextureLayerKind, TextureLayer, TextureManager}; use layers::layers::{ContainerLayerKind, ContainerLayer, TextureLayerKind, TextureLayer, TextureManager};
use pipeline::Pipeline; use pipeline::Pipeline;
@ -367,6 +367,19 @@ impl CompositorLayer {
} }
} }
pub fn invalidate_rect(&mut self, pipeline_id: PipelineId, rect: Rect<f32>) -> 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. // Adds a child.
pub fn add_child(&mut self, pipeline: Pipeline, page_size: Option<Size2D<f32>>, tile_size: uint, pub fn add_child(&mut self, pipeline: Pipeline, page_size: Option<Size2D<f32>>, tile_size: uint,
max_mem: Option<uint>, clipping_rect: Rect<f32>) { max_mem: Option<uint>, clipping_rect: Rect<f32>) {

View file

@ -66,6 +66,10 @@ impl ScriptListener for CompositorChan {
self.chan.send(msg); self.chan.send(msg);
} }
fn invalidate_rect(&self, id: PipelineId, rect: Rect<uint>) {
self.chan.send(InvalidateRect(id, rect));
}
} }
/// Implementation of the abstract `RenderListener` interface. /// Implementation of the abstract `RenderListener` interface.
@ -131,6 +135,8 @@ pub enum Msg {
ResizeLayer(PipelineId, Size2D<uint>), ResizeLayer(PipelineId, Size2D<uint>),
/// Alerts the compositor that the specified layer has been deleted. /// Alerts the compositor that the specified layer has been deleted.
DeleteLayer(PipelineId), DeleteLayer(PipelineId),
/// Invalidate a rect for a given layer
InvalidateRect(PipelineId, Rect<uint>),
/// Requests that the compositor paint the given layer buffer set for the given page size. /// Requests that the compositor paint the given layer buffer set for the given page size.
Paint(PipelineId, arc::Arc<LayerBufferSet>), Paint(PipelineId, arc::Arc<LayerBufferSet>),
@ -312,6 +318,19 @@ impl CompositorTask {
// TODO: Recycle the old buffers; send them back to the renderer to reuse if // TODO: Recycle the old buffers; send them back to the renderer to reuse if
// it wishes. // 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
}
}
} }
} }
}; };

View file

@ -41,10 +41,24 @@ struct QuadtreeNode<T> {
size: f32, size: f32,
/// The node's children. /// The node's children.
quadrants: [Option<~QuadtreeNode<T>>, ..4], quadrants: [Option<~QuadtreeNode<T>>, ..4],
/// If this node is marked for rendering
render_flag: bool,
/// Combined size of self.tile and tiles of all descendants /// Combined size of self.tile and tiles of all descendants
tile_mem: uint, 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 { enum Quadrant {
@ -72,8 +86,8 @@ impl<T: Tile> Quadtree<T> {
origin: Point2D(0f32, 0f32), origin: Point2D(0f32, 0f32),
size: size as f32, size: size as f32,
quadrants: [None, None, None, None], quadrants: [None, None, None, None],
render_flag: false,
tile_mem: 0, tile_mem: 0,
status: Normal,
}, },
clip_size: Size2D(width, height), clip_size: Size2D(width, height),
max_tile_size: tile_size, max_tile_size: tile_size,
@ -184,7 +198,7 @@ impl<T: Tile> Quadtree<T> {
Rect(Point2D(window.origin.x as f32 / scale, window.origin.y as f32 / scale), 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(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), 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) (ret, redisplay)
} }
@ -193,7 +207,7 @@ impl<T: Tile> Quadtree<T> {
let (ret, redisplay, _) = self.root.get_tile_rects( let (ret, redisplay, _) = self.root.get_tile_rects(
window, window,
Size2D(self.clip_size.width as f32, self.clip_size.height as f32), 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) (ret, redisplay)
} }
@ -218,8 +232,8 @@ impl<T: Tile> Quadtree<T> {
origin: Point2D(0f32, 0f32), origin: Point2D(0f32, 0f32),
size: new_size as f32 / ((difference - i - 1) as f32).exp2(), size: new_size as f32 / ((difference - i - 1) as f32).exp2(),
quadrants: [None, None, None, None], quadrants: [None, None, None, None],
render_flag: false,
tile_mem: self.root.tile_mem, tile_mem: self.root.tile_mem,
status: Normal,
}; };
self.root.quadrants[TL as int] = Some(replace(&mut self.root, new_root)); self.root.quadrants[TL as int] = Some(replace(&mut self.root, new_root));
} }
@ -235,8 +249,8 @@ impl<T: Tile> Quadtree<T> {
origin: Point2D(0f32, 0f32), origin: Point2D(0f32, 0f32),
size: new_size as f32, size: new_size as f32,
quadrants: [None, None, None, None], quadrants: [None, None, None, None],
render_flag: false,
tile_mem: 0, tile_mem: 0,
status: Normal,
}; };
break; break;
} }
@ -245,6 +259,13 @@ impl<T: Tile> Quadtree<T> {
} }
} }
/// 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);
}
/// Generate html to visualize the tree. For debugging purposes only. /// Generate html to visualize the tree. For debugging purposes only.
pub fn get_html(&self) -> ~str { pub fn get_html(&self) -> ~str {
static HEADER: &'static str = "<!DOCTYPE html><html>"; static HEADER: &'static str = "<!DOCTYPE html><html>";
@ -261,8 +282,8 @@ impl<T: Tile> QuadtreeNode<T> {
origin: Point2D(x, y), origin: Point2D(x, y),
size: size, size: size,
quadrants: [None, None, None, None], quadrants: [None, None, None, None],
render_flag: false,
tile_mem: 0, tile_mem: 0,
status: Normal,
} }
} }
@ -335,7 +356,7 @@ impl<T: Tile> QuadtreeNode<T> {
for quad in quads.iter() { for quad in quads.iter() {
self.quadrants[*quad as int] = None; self.quadrants[*quad as int] = None;
} }
self.render_flag = false; self.status = Normal;
self.tile_mem as int - old_size as int self.tile_mem as int - old_size as int
} else { // Send tile to children } else { // Send tile to children
let quad = self.get_quadrant(x, y); let quad = self.get_quadrant(x, y);
@ -380,7 +401,7 @@ impl<T: Tile> QuadtreeNode<T> {
let page_height = (clip_y - self.origin.y).min(&self.size); let page_height = (clip_y - self.origin.y).min(&self.size);
let pix_width = (page_width * scale).ceil() as uint; let pix_width = (page_width * scale).ceil() as uint;
let pix_height = (page_height * 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)), 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))); Rect(Point2D(self.origin.x, self.origin.y), Size2D(page_width, page_height)));
} }
@ -477,9 +498,11 @@ impl<T: Tile> QuadtreeNode<T> {
/// Given a window rect in page coordinates, returns a BufferRequest array, /// 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. /// 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. /// NOTE: this method will sometimes modify the tree by deleting tiles.
/// See the QuadTree function description for more details. /// 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) -> fn get_tile_rects(&mut self, window: Rect<f32>, clip: Size2D<f32>, scale: f32, tile_size: f32, override: bool) ->
(~[BufferRequest], bool, int) { (~[BufferRequest], bool, int) {
let w_x = window.origin.x; let w_x = window.origin.x;
@ -503,8 +526,9 @@ impl<T: Tile> QuadtreeNode<T> {
if s_size <= tile_size { // We are the child if s_size <= tile_size { // We are the child
return match self.tile { return match self.tile {
_ if self.render_flag => (~[], false, 0), _ if self.status == Rendering || self.status == Hidden => (~[], false, 0),
Some(ref tile) if tile.is_valid(scale) => { Some(ref tile) if tile.is_valid(scale) && !override
&& self.status != Invalid => {
let redisplay = match self.quadrants { let redisplay = match self.quadrants {
[None, None, None, None] => false, [None, None, None, None] => false,
_ => true, _ => true,
@ -582,8 +606,10 @@ impl<T: Tile> QuadtreeNode<T> {
}; };
let override = override || self.status == Invalid;
self.status = Normal;
let (c_ret, c_redisplay, c_delta) = match self.quadrants[*quad as int] { 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 => { None => {
// Create new child // Create new child
let new_size = self.size / 2.0; let new_size = self.size / 2.0;
@ -596,7 +622,7 @@ impl<T: Tile> QuadtreeNode<T> {
BL | BR => self.origin.y + new_size, BL | BR => self.origin.y + new_size,
}; };
let mut child = ~QuadtreeNode::new_child(new_x, new_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); self.quadrants[*quad as int] = Some(child);
ret ret
} }
@ -610,6 +636,42 @@ impl<T: Tile> QuadtreeNode<T> {
(ret, redisplay, delta) (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<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);
}
}
}
}
}
}
/// Generate html to visualize the tree. /// Generate html to visualize the tree.
/// This is really inefficient, but it's for testing only. /// This is really inefficient, but it's for testing only.
fn get_html(&self) -> ~str { fn get_html(&self) -> ~str {

View file

@ -69,6 +69,7 @@ pub trait RenderListener {
/// which is used in displaying the appropriate message in the window's title. /// which is used in displaying the appropriate message in the window's title.
pub trait ScriptListener : Clone { pub trait ScriptListener : Clone {
fn set_ready_state(&self, ReadyState); fn set_ready_state(&self, ReadyState);
fn invalidate_rect(&self, PipelineId, Rect<uint>);
} }
/// The interface used by the quadtree to get info about LayerBuffers /// The interface used by the quadtree to get info about LayerBuffers