Update submodule pointers; address nits; add optional page_size

This commit is contained in:
eschweic 2013-08-05 18:43:29 -07:00
parent 974ed79144
commit 4b2d7f0901
6 changed files with 132 additions and 81 deletions

View file

@ -18,26 +18,30 @@ use pipeline::Pipeline;
/// The CompositorLayer represents an element on a page that has a unique scroll /// The CompositorLayer represents an element on a page that has a unique scroll
/// or animation behavior. This can include absolute positioned elements, iframes, etc. /// or animation behavior. This can include absolute positioned elements, iframes, etc.
/// Each layer can also have child elements. /// Each layer can also have child layers.
pub struct CompositorLayer { pub struct CompositorLayer {
/// This layer's pipeline. BufferRequests will be sent through this. /// This layer's pipeline. BufferRequests and mouse events will be sent through this.
pipeline: Pipeline, pipeline: Pipeline,
/// The rect that this layer occupies in page coordinates. The offset /// The size of the underlying page in page coordinates. This is an option
/// is blind to any parents this layer may have; it only refers to the /// because we may not know the size of the page until layout is finished completely.
/// scroll position of the page. /// if we have no size yet, the layer is hidden until a size message is recieved.
page_rect: Rect<f32>, 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.
scroll_offset: Point2D<f32>,
/// This layer's children. These could be iframes or any element which /// This layer's children. These could be iframes or any element which
/// differs in scroll behavior from its parent. Each is associated with a /// differs in scroll behavior from its parent. Each is associated with a
/// ContainerLayer which determines its position relative to its parent and /// ContainerLayer which determines its position relative to its parent and
/// clipping rect. Children are stored in the order in which they are drawn. /// clipping rect. Children are stored in the order in which they are drawn.
children: ~[CompositorLayerChild], children: ~[CompositorLayerChild],
/// This layer's quadtree. This is where all buffers are stored for this layer. /// This layer's quadtree. This is where all buffers are stored for this layer.
quadtree: Quadtree<~LayerBuffer>, quadtree: MaybeQuadtree,
/// The root layer of this CompositorLayer's layer tree. Buffers are collected /// 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. /// from the quadtree and inserted here when the layer is painted to the screen.
root_layer: @mut ContainerLayer, root_layer: @mut ContainerLayer,
/// When set to true, this layer is ignored by its parents. This is useful for
// TODO: Eventually, it may be useful to have an ID associated with each layer. /// soft deletion or when waiting on a page size.
hidden: bool,
} }
/// Helper struct for keeping CompositorLayer children organized. /// Helper struct for keeping CompositorLayer children organized.
@ -49,27 +53,41 @@ struct CompositorLayerChild {
container: @mut ContainerLayer, container: @mut ContainerLayer,
} }
pub fn CompositorLayer(pipeline: Pipeline, page_size: Size2D<f32>, tile_size: uint, /// Helper enum for storing quadtrees. Either contains a quadtree, or contains
max_mem: Option<uint>) -> CompositorLayer { /// information from which a quadtree can be built.
CompositorLayer { enum MaybeQuadtree {
pipeline: pipeline, Tree(Quadtree<~LayerBuffer>),
page_rect: Rect(Point2D(0f32, 0f32), page_size), NoTree(uint, Option<uint>),
children: ~[],
quadtree: Quadtree::new(page_size.width as uint, page_size.height as uint,
tile_size,
max_mem),
root_layer: @mut ContainerLayer(),
}
} }
impl CompositorLayer { impl CompositorLayer {
/// Creates a new CompositorLayer without a page size that is initially hidden.
pub fn new(pipeline: Pipeline, page_size: Option<Size2D<f32>>, tile_size: uint, max_mem: Option<uint>)
-> CompositorLayer {
CompositorLayer {
pipeline: pipeline,
page_size: page_size,
scroll_offset: Point2D(0f32, 0f32),
children: ~[],
quadtree: match page_size {
None => NoTree(tile_size, max_mem),
Some(page_size) => Tree(Quadtree::new(page_size.width as uint,
page_size.height as uint,
tile_size,
max_mem)),
},
root_layer: @mut ContainerLayer(),
hidden: true,
}
}
// Move the layer by as relative specified amount in page coordinates. Does not change // Move the layer by as relative specified amount in page coordinates. Does not change
// the position of the layer relative to its parent. This also takes in a cursor position // the position of the layer relative to its parent. This also takes in a cursor position
// to see if the mouse is over child layers first. If a layer successfully scrolled, returns // 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. // true; otherwise returns false, so a parent layer can scroll instead.
pub fn scroll(&mut self, delta: Point2D<f32>, cursor: Point2D<f32>, window_size: Size2D<f32>) -> bool { pub fn scroll(&mut self, delta: Point2D<f32>, cursor: Point2D<f32>, window_size: Size2D<f32>) -> bool {
let cursor = cursor - self.page_rect.origin; let cursor = cursor - self.scroll_offset;
for self.children.mut_iter().advance |child| { for self.children.mut_iter().filter(|x| !x.child.hidden).advance |child| {
match child.container.scissor { match child.container.scissor {
None => { None => {
error!("CompositorLayer: unable to perform cursor hit test for layer"); error!("CompositorLayer: unable to perform cursor hit test for layer");
@ -84,22 +102,26 @@ impl CompositorLayer {
} }
} }
let old_origin = self.page_rect.origin; let old_origin = self.scroll_offset;
self.page_rect.origin = self.page_rect.origin + delta; self.scroll_offset = self.scroll_offset + delta;
// bounds checking // bounds checking
let min_x = (window_size.width - self.page_rect.size.width).min(&0.0); let page_size = match self.page_size {
self.page_rect.origin.x = self.page_rect.origin.x.clamp(&min_x, &0.0); Some(size) => size,
let min_y = (window_size.height - self.page_rect.size.height).min(&0.0); None => fail!("CompositorLayer: tried to scroll with no page size set"),
self.page_rect.origin.y = self.page_rect.origin.y.clamp(&min_y, &0.0); };
let min_x = (window_size.width - page_size.width).min(&0.0);
self.scroll_offset.x = self.scroll_offset.x.clamp(&min_x, &0.0);
let min_y = (window_size.height - page_size.height).min(&0.0);
self.scroll_offset.y = self.scroll_offset.y.clamp(&min_y, &0.0);
// check to see if we scrolled // check to see if we scrolled
if old_origin - self.page_rect.origin == Point2D(0f32, 0f32) { if old_origin - self.scroll_offset == Point2D(0f32, 0f32) {
return false; return false;
} }
self.root_layer.common.set_transform(identity().translate(self.page_rect.origin.x, self.root_layer.common.set_transform(identity().translate(self.scroll_offset.x,
self.page_rect.origin.y, self.scroll_offset.y,
0.0)); 0.0));
true true
} }
@ -108,9 +130,8 @@ impl CompositorLayer {
// sends the event off to the appropriate pipeline. NB: the cursor position is in // sends the event off to the appropriate pipeline. NB: the cursor position is in
// page coordinates. // page coordinates.
pub fn send_mouse_event(&self, event: MouseWindowEvent, cursor: Point2D<f32>) { pub fn send_mouse_event(&self, event: MouseWindowEvent, cursor: Point2D<f32>) {
let cursor = cursor - self.page_rect.origin; let cursor = cursor - self.scroll_offset;
// FIXME: maybe we want rev_iter() instead? Depends on draw order for self.children.iter().filter(|&x| !x.child.hidden).advance |child| {
for self.children.iter().advance |child| {
match child.container.scissor { match child.container.scissor {
None => { None => {
error!("CompositorLayer: unable to perform cursor hit test for layer"); error!("CompositorLayer: unable to perform cursor hit test for layer");
@ -135,16 +156,24 @@ impl CompositorLayer {
self.pipeline.script_chan.send(SendEventMsg(self.pipeline.id.clone(), message)); self.pipeline.script_chan.send(SendEventMsg(self.pipeline.id.clone(), message));
} }
// Given the current window size, determine which tiles need to be redisplayed // Given the current window size, determine which tiles need to be (re)rendered
// and sends them off the the appropriate renderer. // and sends them off the the appropriate renderer.
// Returns a bool that is true if the scene should be repainted. // Returns a bool that is true if the scene should be repainted.
pub fn get_buffer_request(&mut self, window_rect: Rect<f32>, scale: f32) -> bool { pub fn get_buffer_request(&mut self, window_rect: Rect<f32>, scale: f32) -> bool {
let rect = Rect(Point2D(-self.page_rect.origin.x + window_rect.origin.x, let rect = Rect(Point2D(-self.scroll_offset.x + window_rect.origin.x,
-self.page_rect.origin.y + window_rect.origin.y), -self.scroll_offset.y + window_rect.origin.y),
window_rect.size); window_rect.size);
let (request, redisplay) = self.quadtree.get_tile_rects_page(rect, scale); let mut redisplay: bool;
if !request.is_empty() { { // block here to prevent double mutable borrow of self
self.pipeline.render_chan.send(ReRenderMsg(request, scale, self.pipeline.id.clone())); let quadtree = match self.quadtree {
NoTree(_, _) => fail!("CompositorLayer: cannot get buffer request, no quadtree initialized"),
Tree(ref mut quadtree) => quadtree,
};
let (request, r) = quadtree.get_tile_rects_page(rect, scale);
redisplay = r; // workaround to make redisplay visible outside block
if !request.is_empty() {
self.pipeline.render_chan.send(ReRenderMsg(request, scale, self.pipeline.id.clone()));
}
} }
if redisplay { if redisplay {
self.build_layer_tree(); self.build_layer_tree();
@ -167,7 +196,7 @@ impl CompositorLayer {
} }
} }
}; };
self.children.mut_iter() self.children.mut_iter().filter(|x| !x.child.hidden)
.transform(transform) .transform(transform)
.fold(false, |a, b| a || b) || redisplay .fold(false, |a, b| a || b) || redisplay
} }
@ -189,7 +218,7 @@ impl CompositorLayer {
return true; return true;
} }
// ID does not match any of our immediate children, so recurse on descendents. // ID does not match any of our immediate children, so recurse on descendents (including hidden children)
self.children.mut_iter().transform(|x| &mut x.child).any(|x| x.set_clipping_rect(pipeline_id, new_rect)) self.children.mut_iter().transform(|x| &mut x.child).any(|x| x.set_clipping_rect(pipeline_id, new_rect))
} }
@ -198,15 +227,22 @@ impl CompositorLayer {
// This method returns false if the specified layer is not found. // This method returns false if the specified layer is not found.
pub fn resize(&mut self, pipeline_id: PipelineId, new_size: Size2D<f32>, window_size: Size2D<f32>) -> bool { pub fn resize(&mut self, pipeline_id: PipelineId, new_size: Size2D<f32>, window_size: Size2D<f32>) -> bool {
if self.pipeline.id == pipeline_id { if self.pipeline.id == pipeline_id {
self.page_rect.size = new_size; self.page_size = Some(new_size);
// TODO: might get buffers back here // TODO: might get buffers back here
self.quadtree.resize(new_size.width as uint, new_size.height as uint); match self.quadtree {
// Call scroll for bounds checking of the page shrunk. Tree(ref mut quadtree) => quadtree.resize(new_size.width as uint, new_size.height as uint),
NoTree(tile_size, max_mem) => self.quadtree = Tree(Quadtree::new(new_size.width as uint,
new_size.height as uint,
tile_size,
max_mem)),
}
// Call scroll for bounds checking of the page shrunk. Use (-1, -1) as the cursor position
// to make sure the scroll isn't propagated downwards.
self.scroll(Point2D(0f32, 0f32), Point2D(-1f32, -1f32), window_size); self.scroll(Point2D(0f32, 0f32), Point2D(-1f32, -1f32), window_size);
return true; return true;
} }
// ID does not match ours, so recurse on descendents. // ID does not match ours, so recurse on descendents (including hidden children)
let transform = |x: &mut CompositorLayerChild| -> bool { let transform = |x: &mut CompositorLayerChild| -> bool {
match x.container.scissor { match x.container.scissor {
Some(scissor) => { Some(scissor) => {
@ -237,7 +273,7 @@ impl CompositorLayer {
} }
// Add child layers. // Add child layers.
for self.children.mut_iter().advance |child| { for self.children.mut_iter().filter(|x| !x.child.hidden).advance |child| {
current_layer_child = match current_layer_child { current_layer_child = match current_layer_child {
None => { None => {
child.container.common.parent = None; child.container.common.parent = None;
@ -253,7 +289,12 @@ impl CompositorLayer {
} }
// Add new tiles. // Add new tiles.
let all_tiles = self.quadtree.get_all_tiles(); let quadtree = match self.quadtree {
NoTree(_, _) => fail!("CompositorLayer: cannot get buffer request, no quadtree initialized"),
Tree(ref mut quadtree) => quadtree,
};
let all_tiles = quadtree.get_all_tiles();
for all_tiles.iter().advance |buffer| { for all_tiles.iter().advance |buffer| {
debug!("osmain: compositing buffer rect %?", &buffer.rect); debug!("osmain: compositing buffer rect %?", &buffer.rect);
@ -292,19 +333,26 @@ impl CompositorLayer {
// Add LayerBuffers to the specified layer. Returns false if the layer is not found. // Add LayerBuffers to the specified layer. Returns false if the layer is not found.
pub fn add_buffers(&mut self, pipeline_id: PipelineId, new_buffers: &LayerBufferSet) -> bool { pub fn add_buffers(&mut self, pipeline_id: PipelineId, new_buffers: &LayerBufferSet) -> bool {
if self.pipeline.id == pipeline_id { if self.pipeline.id == pipeline_id {
for new_buffers.buffers.iter().advance |buffer| { { // block here to prevent double mutable borrow of self
// TODO: This may return old buffers, which should be sent back to the renderer. let quadtree = match self.quadtree {
self.quadtree.add_tile_pixel(buffer.screen_pos.origin.x, buffer.screen_pos.origin.y, NoTree(_, _) => fail!("CompositorLayer: cannot get buffer request, no quadtree initialized"),
buffer.resolution, ~buffer.clone()); Tree(ref mut quadtree) => quadtree,
};
for new_buffers.buffers.iter().advance |buffer| {
// TODO: This may return old buffers, which should be sent back to the renderer.
quadtree.add_tile_pixel(buffer.screen_pos.origin.x, buffer.screen_pos.origin.y,
buffer.resolution, ~buffer.clone());
}
} }
self.build_layer_tree(); self.build_layer_tree();
return true; return true;
} }
// ID does not match ours, so recurse on descendents. // ID does not match ours, so recurse on descendents (including hidden children).
self.children.mut_iter().transform(|x| &mut x.child).any(|x| x.add_buffers(pipeline_id, new_buffers)) self.children.mut_iter().transform(|x| &mut x.child).any(|x| x.add_buffers(pipeline_id, new_buffers))
} }
// Deletes a specified sublayer. Returns false if the layer is not found. // Deletes a specified sublayer, including hidden children. Returns false if the layer is not found.
pub fn delete(&mut self, pipeline_id: PipelineId) -> bool { pub fn delete(&mut self, pipeline_id: PipelineId) -> bool {
match self.children.rposition(|x| x.child.pipeline.id == pipeline_id) { match self.children.rposition(|x| x.child.pipeline.id == pipeline_id) {
Some(index) => { Some(index) => {
@ -319,16 +367,15 @@ impl CompositorLayer {
} }
} }
// Adds a child. // Adds a child.
pub fn add_child(&mut self, pipeline: Pipeline, page_size: 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>) {
let container = @mut ContainerLayer(); let container = @mut ContainerLayer();
container.scissor = Some(clipping_rect); container.scissor = Some(clipping_rect);
container.common.set_transform(identity().translate(clipping_rect.origin.x, container.common.set_transform(identity().translate(clipping_rect.origin.x,
clipping_rect.origin.y, clipping_rect.origin.y,
0.0)); 0.0));
let child = ~CompositorLayer(pipeline, page_size, tile_size, max_mem); let child = ~CompositorLayer::new(pipeline, page_size, tile_size, max_mem);
container.add_child(ContainerLayerKind(child.root_layer)); container.add_child(ContainerLayerKind(child.root_layer));
self.children.push(CompositorLayerChild { self.children.push(CompositorLayerChild {
child: child, child: child,

View file

@ -278,9 +278,9 @@ impl CompositorTask {
None => fail!("Compositor: Received new layer without initialized pipeline"), None => fail!("Compositor: Received new layer without initialized pipeline"),
}; };
let page_size = Size2D(new_size.width as f32, new_size.height as f32); let page_size = Size2D(new_size.width as f32, new_size.height as f32);
let new_layer = CompositorLayer(p.clone(), page_size, let new_layer = CompositorLayer::new(p.clone(), Some(page_size),
self.opts.tile_size, Some(10000000u)); self.opts.tile_size, Some(10000000u));
let current_child = root_layer.first_child; let current_child = root_layer.first_child;
// This assumes there is at most one child, which should be the case. // This assumes there is at most one child, which should be the case.
match current_child { match current_child {

View file

@ -20,7 +20,8 @@ pub struct Quadtree<T> {
// The root node of the quadtree // The root node of the quadtree
root: ~QuadtreeNode<T>, root: ~QuadtreeNode<T>,
// The size of the layer in pixels. Tiles will be clipped to this size. // The size of the layer in pixels. Tiles will be clipped to this size.
// Note that the underlying quadtree has a larger size. // Note that the underlying quadtree has a potentailly larger size, since it is rounded
// to the next highest power of two.
clip_size: Size2D<uint>, clip_size: Size2D<uint>,
// The maximum size of the tiles requested in pixels. Tiles requested will be // The maximum size of the tiles requested in pixels. Tiles requested will be
// of a size anywhere between half this value and this value. // of a size anywhere between half this value and this value.
@ -205,8 +206,11 @@ impl<T: Tile> Quadtree<T> {
let longer = width.max(&height); let longer = width.max(&height);
let new_num_tiles = div_ceil(longer, self.max_tile_size); let new_num_tiles = div_ceil(longer, self.max_tile_size);
let new_size = next_power_of_two(new_num_tiles); 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; let difference = (new_size as f32 / self.root.size as f32).log2() as int;
if difference > 0 { if difference > 0 { // doubling
let difference = difference as uint; let difference = difference as uint;
for range(0, difference) |i| { for range(0, difference) |i| {
let new_root = ~QuadtreeNode { let new_root = ~QuadtreeNode {
@ -219,7 +223,7 @@ impl<T: Tile> Quadtree<T> {
}; };
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));
} }
} else if difference < 0 { } else if difference < 0 { // halving
let difference = difference.abs() as uint; let difference = difference.abs() as uint;
for difference.times { for difference.times {
let remove = replace(&mut self.root.quadrants[TL as int], None); let remove = replace(&mut self.root.quadrants[TL as int], None);
@ -663,8 +667,8 @@ pub fn test_resize() {
} }
let mut q = Quadtree::new(6, 6, 1, None); let mut q = Quadtree::new(6, 6, 1, None);
q.add_tile(0, 0, 1f32, T{a: 0}); q.add_tile_pixel(0, 0, 1f32, T{a: 0});
q.add_tile(5, 5, 1f32, T{a: 1}); q.add_tile_pixel(5, 5, 1f32, T{a: 1});
q.resize(8, 1); q.resize(8, 1);
assert!(q.root.size == 8.0); assert!(q.root.size == 8.0);
q.resize(18, 1); q.resize(18, 1);
@ -693,26 +697,26 @@ pub fn test() {
} }
let mut q = Quadtree::new(8, 8, 2, Some(4)); let mut q = Quadtree::new(8, 8, 2, Some(4));
q.add_tile(0, 0, 1f32, T{a: 0}); q.add_tile_pixel(0, 0, 1f32, T{a: 0});
q.add_tile(0, 0, 2f32, T{a: 1}); q.add_tile_pixel(0, 0, 2f32, T{a: 1});
q.add_tile(0, 0, 2f32, T{a: 2}); q.add_tile_pixel(0, 0, 2f32, T{a: 2});
q.add_tile(2, 0, 2f32, T{a: 3}); q.add_tile_pixel(2, 0, 2f32, T{a: 3});
assert!(q.root.tile_mem == 3); assert!(q.root.tile_mem == 3);
assert!(q.get_all_tiles().len() == 3); assert!(q.get_all_tiles().len() == 3);
q.add_tile(0, 2, 2f32, T{a: 4}); q.add_tile_pixel(0, 2, 2f32, T{a: 4});
q.add_tile(2, 2, 2f32, T{a: 5}); q.add_tile_pixel(2, 2, 2f32, T{a: 5});
assert!(q.root.tile_mem == 4); assert!(q.root.tile_mem == 4);
let (request, _) = q.get_tile_rects(Rect(Point2D(0, 0), Size2D(2, 2)), 2f32); let (request, _) = q.get_tile_rects_pixel(Rect(Point2D(0, 0), Size2D(2, 2)), 2f32);
assert!(request.is_empty()); assert!(request.is_empty());
let (request, _) = q.get_tile_rects(Rect(Point2D(0, 0), Size2D(2, 2)), 1.9); let (request, _) = q.get_tile_rects_pixel(Rect(Point2D(0, 0), Size2D(2, 2)), 1.9);
assert!(request.is_empty()); assert!(request.is_empty());
let (request, _) = q.get_tile_rects(Rect(Point2D(0, 0), Size2D(2, 2)), 1f32); let (request, _) = q.get_tile_rects_pixel(Rect(Point2D(0, 0), Size2D(2, 2)), 1f32);
assert!(request.len() == 4); assert!(request.len() == 4);
q.add_tile(0, 0, 0.5, T{a: 6}); q.add_tile_pixel(0, 0, 0.5, T{a: 6});
q.add_tile(0, 0, 1f32, T{a: 7}); q.add_tile_pixel(0, 0, 1f32, T{a: 7});
let (_, redisplay) = q.get_tile_rects(Rect(Point2D(0, 0), Size2D(2, 2)), 0.5); let (_, redisplay) = q.get_tile_rects_pixel(Rect(Point2D(0, 0), Size2D(2, 2)), 0.5);
assert!(redisplay); assert!(redisplay);
assert!(q.root.tile_mem == 1); assert!(q.root.tile_mem == 1);
} }

@ -1 +1 @@
Subproject commit a23cb0bc99a933efa9104c2471ccd14638744ea4 Subproject commit 5b9a447946b736802a0791eb1c24687849ad3984

@ -1 +1 @@
Subproject commit a8843ea084262773c31916e3a52c6dacea135153 Subproject commit 177277d4aaeb56913f2eb04670679cae078cf70b

@ -1 +1 @@
Subproject commit b5cb5593f4938a578eebda56c905bbaff33efe66 Subproject commit e83cca0e287db58a27ee77947a8d4dc3617a6ed0