diff --git a/src/components/gfx/buffer_map.rs b/src/components/gfx/buffer_map.rs new file mode 100644 index 00000000000..06dd1e2255e --- /dev/null +++ b/src/components/gfx/buffer_map.rs @@ -0,0 +1,135 @@ +/* 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 std::hashmap::HashMap; +use std::to_bytes::Cb; +use geom::size::Size2D; +use servo_msg::compositor_msg::Tile; + +/// This is a struct used to store buffers when they are not in use. +/// The render task can quickly query for a particular size of buffer when it +/// needs it. +pub struct BufferMap { + /// A HashMap that stores the Buffers. + map: HashMap>, + /// The current amount of memory stored by the BufferMap's buffers. + mem: uint, + /// The maximum allowed memory. Unused buffers willl be deleted + /// when this threshold is exceeded. + max_mem: uint, + /// A monotonically increasing counter to track how recently tile sizes were used. + counter: uint, +} + +/// A key with which to store buffers. It is based on the size of the buffer. +struct BufferKey([uint, ..2]); + +impl IterBytes for BufferKey { + fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool { + let i = if lsb0 {0} else {1}; + self[i].iter_bytes(lsb0, |x| f(x)) && self[1 - i].iter_bytes(lsb0, |x| f(x)) + } +} + +impl Eq for BufferKey { + fn eq(&self, other: &BufferKey) -> bool { + self[0] == other[0] && self[1] == other[1] + } +} + +/// Create a key from a given size +impl BufferKey { + fn get(input: Size2D) -> BufferKey { + BufferKey([input.width, input.height]) + } +} + +/// A helper struct to keep track of buffers in the HashMap +struct BufferValue { + /// An array of buffers, all the same size + buffers: ~[T], + /// The counter when this size was last requested + last_action: uint, +} + +impl BufferMap { + // Creates a new BufferMap with a given buffer limit. + pub fn new(max_mem: uint) -> BufferMap { + BufferMap { + map: HashMap::new(), + mem: 0u, + max_mem: max_mem, + counter: 0u, + } + } + + // Insert a new buffer into the map. + pub fn insert(&mut self, new_buffer: T) { + let new_key = BufferKey::get(new_buffer.get_size_2d()); + + // If all our buffers are the same size and we're already at our + // memory limit, no need to store this new buffer; just let it drop. + if self.mem + new_buffer.get_mem() > self.max_mem && self.map.len() == 1 && + self.map.contains_key(&new_key) { + return; + } + + self.mem += new_buffer.get_mem(); + // use lazy insertion function to prevent unnecessary allocation + self.map.find_or_insert_with(new_key, |_| BufferValue { + buffers: ~[], + last_action: self.counter + }).buffers.push(new_buffer); + + let mut opt_key: Option = None; + while self.mem > self.max_mem { + let old_key = match opt_key { + Some(key) => key, + None => { + match self.map.iter().min_by(|&(_, x)| x.last_action) { + Some((k, _)) => *k, + None => fail!("BufferMap: tried to delete with no elements in map"), + } + } + }; + if { + let list = &mut self.map.get_mut(&old_key).buffers; + self.mem -= list.pop().get_mem(); + list.is_empty() + } + { // then + self.map.pop(&old_key); // Don't store empty vectors! + opt_key = None; + } else { + opt_key = Some(old_key); + } + } + } + + // Try to find a buffer for the given size. + pub fn find(&mut self, size: Size2D) -> Option { + let mut flag = false; // True if key needs to be popped after retrieval. + let key = BufferKey::get(size); + let ret = match self.map.find_mut(&key) { + Some(ref mut buffer_val) => { + buffer_val.last_action = self.counter; + self.counter += 1; + + let buffer = buffer_val.buffers.pop(); + self.mem -= buffer.get_mem(); + if buffer_val.buffers.is_empty() { + flag = true; + } + Some(buffer) + } + None => None, + }; + + if flag { + self.map.pop(&key); // Don't store empty vectors! + } + + ret + } +} diff --git a/src/components/gfx/gfx.rc b/src/components/gfx/gfx.rc index 4b86374cd9a..98cdee6232a 100644 --- a/src/components/gfx/gfx.rc +++ b/src/components/gfx/gfx.rc @@ -51,6 +51,7 @@ pub mod font_list; // Misc. pub mod opts; +mod buffer_map; // Platform-specific implementations. #[path="platform/mod.rs"] diff --git a/src/components/gfx/render_context.rs b/src/components/gfx/render_context.rs index e7b44a661e6..3468c2ede5a 100644 --- a/src/components/gfx/render_context.rs +++ b/src/components/gfx/render_context.rs @@ -19,7 +19,7 @@ use servo_net::image::base::Image; use extra::arc::Arc; pub struct RenderContext<'self> { - canvas: &'self LayerBuffer, + canvas: &'self ~LayerBuffer, font_ctx: @mut FontContext, opts: &'self Opts } diff --git a/src/components/gfx/render_task.rs b/src/components/gfx/render_task.rs index 1586c28b860..0706d494667 100644 --- a/src/components/gfx/render_task.rs +++ b/src/components/gfx/render_task.rs @@ -24,8 +24,7 @@ use extra::arc::Arc; use servo_util::time::{ProfilerChan, profile}; use servo_util::time; -use extra::arc; - +use buffer_map::BufferMap; pub struct RenderLayer { @@ -35,7 +34,8 @@ pub struct RenderLayer { pub enum Msg { RenderMsg(RenderLayer), - ReRenderMsg(~[BufferRequest], f32, PipelineId, Epoch), + ReRenderMsg(~[BufferRequest], f32, Epoch), + UnusedBufferMsg(~[~LayerBuffer]), PaintPermissionGranted, PaintPermissionRevoked, ExitMsg(Chan<()>), @@ -91,9 +91,11 @@ struct RenderTask { /// Permission to send paint messages to the compositor paint_permission: bool, /// Cached copy of last layers rendered - last_paint_msg: Option<(arc::Arc, Size2D)>, + last_paint_msg: Option<~LayerBufferSet>, /// A counter for epoch messages epoch: Epoch, + /// A data structure to store unused LayerBuffers + buffer_map: BufferMap<~LayerBuffer>, } impl RenderTask { @@ -129,6 +131,7 @@ impl RenderTask { paint_permission: false, last_paint_msg: None, epoch: Epoch(0), + buffer_map: BufferMap::new(10000000), }; render_task.start(); @@ -147,13 +150,19 @@ impl RenderTask { } self.render_layer = Some(render_layer); } - ReRenderMsg(tiles, scale, id, epoch) => { + ReRenderMsg(tiles, scale, epoch) => { if self.epoch == epoch { - self.render(tiles, scale, id); + self.render(tiles, scale); } else { debug!("renderer epoch mismatch: %? != %?", self.epoch, epoch); } } + UnusedBufferMsg(unused_buffers) => { + // move_rev_iter is more efficient + for buffer in unused_buffers.move_rev_iter() { + self.buffer_map.insert(buffer); + } + } PaintPermissionGranted => { self.paint_permission = true; match self.render_layer { @@ -163,6 +172,16 @@ impl RenderTask { } None => {} } + // FIXME: This sends the last paint request, anticipating what + // the compositor will ask for. However, even if it sends the right + // tiles, the compositor still asks for them, and they will be + // re-rendered redundantly. + match self.last_paint_msg { + Some(ref layer_buffer_set) => { + self.compositor.paint(self.id, layer_buffer_set.clone(), self.epoch); + } + None => {} // Nothing to do + } } PaintPermissionRevoked => { self.paint_permission = false; @@ -175,7 +194,7 @@ impl RenderTask { } } - fn render(&mut self, tiles: ~[BufferRequest], scale: f32, id: PipelineId) { + fn render(&mut self, tiles: ~[BufferRequest], scale: f32) { let render_layer; match self.render_layer { Some(ref r_layer) => { @@ -196,15 +215,24 @@ impl RenderTask { let width = tile.screen_rect.size.width; let height = tile.screen_rect.size.height; - let buffer = LayerBuffer { - draw_target: DrawTarget::new_with_fbo(self.opts.render_backend, - self.share_gl_context, - Size2D(width as i32, height as i32), - B8G8R8A8), - rect: tile.page_rect, - screen_pos: tile.screen_rect, - resolution: scale, - stride: (width * 4) as uint + let buffer = match self.buffer_map.find(tile.screen_rect.size) { + Some(buffer) => { + let mut buffer = buffer; + buffer.rect = tile.page_rect; + buffer.screen_pos = tile.screen_rect; + buffer.resolution = scale; + buffer + } + None => ~LayerBuffer { + draw_target: DrawTarget::new_with_fbo(self.opts.render_backend, + self.share_gl_context, + Size2D(width as i32, height as i32), + B8G8R8A8), + rect: tile.page_rect, + screen_pos: tile.screen_rect, + resolution: scale, + stride: (width * 4) as uint + } }; @@ -240,17 +268,16 @@ impl RenderTask { } - let layer_buffer_set = LayerBufferSet { + let layer_buffer_set = ~LayerBufferSet { buffers: new_buffers, }; - let layer_buffer_set = arc::Arc::new(layer_buffer_set); debug!("render_task: returning surface"); if self.paint_permission { - self.compositor.paint(id, layer_buffer_set.clone(), self.epoch); + self.compositor.paint(self.id, layer_buffer_set.clone(), self.epoch); } debug!("caching paint msg"); - self.last_paint_msg = Some((layer_buffer_set, render_layer.size)); + self.last_paint_msg = Some(layer_buffer_set); self.compositor.set_render_state(IdleRenderState); } } diff --git a/src/components/main/compositing/compositor_layer.rs b/src/components/main/compositing/compositor_layer.rs index 5d11c5d2860..de1e972c57c 100644 --- a/src/components/main/compositing/compositor_layer.rs +++ b/src/components/main/compositing/compositor_layer.rs @@ -2,11 +2,12 @@ * 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 std::cell::Cell; use geom::point::Point2D; use geom::size::Size2D; use geom::rect::Rect; use geom::matrix::identity; -use gfx::render_task::ReRenderMsg; +use gfx::render_task::{ReRenderMsg, UnusedBufferMsg}; use servo_msg::compositor_msg::{LayerBuffer, LayerBufferSet, Epoch}; use servo_msg::constellation_msg::PipelineId; use script::dom::event::{ClickEvent, MouseDownEvent, MouseUpEvent}; @@ -207,10 +208,13 @@ impl CompositorLayer { no quadtree initialized", self.pipeline.id), 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(), self.epoch)); + let (request, unused) = quadtree.get_tile_rects_page(rect, scale); + redisplay = !unused.is_empty(); // workaround to make redisplay visible outside block + if redisplay { // send back unused tiles + self.pipeline.render_chan.send(UnusedBufferMsg(unused)); + } + if !request.is_empty() { // ask for tiles + self.pipeline.render_chan.send(ReRenderMsg(request, scale, self.epoch)); } } if redisplay { @@ -399,7 +403,8 @@ impl CompositorLayer { // Add LayerBuffers to the specified layer. Returns false if the layer is not found. // If the epoch of the message does not match the layer's epoch, the message is ignored. - pub fn add_buffers(&mut self, pipeline_id: PipelineId, new_buffers: &LayerBufferSet, epoch: Epoch) -> bool { + pub fn add_buffers(&mut self, pipeline_id: PipelineId, new_buffers: ~LayerBufferSet, epoch: Epoch) -> bool { + let cell = Cell::new(new_buffers); if self.pipeline.id == pipeline_id { if self.epoch != epoch { debug!("compositor epoch mismatch: %? != %?, id: %?", self.epoch, epoch, self.pipeline.id); @@ -408,22 +413,26 @@ impl CompositorLayer { } { // block here to prevent double mutable borrow of self let quadtree = match self.quadtree { - NoTree(_, _) => fail!("CompositorLayer: cannot get buffer request for %?, - no quadtree initialized", self.pipeline.id), + NoTree(_, _) => fail!("CompositorLayer: cannot add buffers, no quadtree initialized"), Tree(ref mut quadtree) => quadtree, }; - for buffer in new_buffers.buffers.iter() { - // 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()); + let mut unused_tiles = ~[]; + // move_rev_iter is more efficient + for buffer in cell.take().buffers.move_rev_iter() { + 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 + self.pipeline.render_chan.send(UnusedBufferMsg(unused_tiles)); } } self.build_layer_tree(); 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.add_buffers(pipeline_id, new_buffers, epoch)) + self.children.mut_iter().map(|x| &mut x.child).any(|x| x.add_buffers(pipeline_id, cell.take(), epoch)) } // Deletes a specified sublayer, including hidden children. Returns false if the layer is not found. diff --git a/src/components/main/compositing/mod.rs b/src/components/main/compositing/mod.rs index a480ee76b39..4d96d9ebc82 100644 --- a/src/components/main/compositing/mod.rs +++ b/src/components/main/compositing/mod.rs @@ -40,7 +40,6 @@ use servo_util::time::ProfilerChan; use extra::future::from_value; use extra::time::precise_time_s; -use extra::arc; use constellation::SendableFrameTree; use compositing::compositor_layer::CompositorLayer; @@ -79,7 +78,7 @@ impl RenderListener for CompositorChan { port.recv() } - fn paint(&self, id: PipelineId, layer_buffer_set: arc::Arc, epoch: Epoch) { + fn paint(&self, id: PipelineId, layer_buffer_set: ~LayerBufferSet, epoch: Epoch) { self.chan.send(Paint(id, layer_buffer_set, epoch)) } @@ -148,7 +147,7 @@ pub enum Msg { InvalidateRect(PipelineId, Rect), /// Requests that the compositor paint the given layer buffer set for the given page size. - Paint(PipelineId, arc::Arc, Epoch), + Paint(PipelineId, ~LayerBufferSet, Epoch), /// Alerts the compositor to the current status of page loading. ChangeReadyState(ReadyState), /// Alerts the compositor to the current status of rendering. @@ -338,7 +337,7 @@ impl CompositorTask { match compositor_layer { Some(ref mut layer) => { - assert!(layer.add_buffers(id, new_layer_buffer_set.get(), epoch)); + assert!(layer.add_buffers(id, new_layer_buffer_set, epoch)); recomposite = true; } None => { diff --git a/src/components/main/compositing/quadtree.rs b/src/components/main/compositing/quadtree.rs index 364f44b5b42..84c5813d9c0 100644 --- a/src/components/main/compositing/quadtree.rs +++ b/src/components/main/compositing/quadtree.rs @@ -111,43 +111,48 @@ impl Quadtree { /// Add a tile associtated 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. - pub fn add_tile_pixel(&mut self, x: uint, y: uint, scale: f32, tile: T) { - self.root.add_tile(x as f32 / scale, y as f32 / scale, tile, self.max_tile_size as f32 / scale); + /// 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) -> ~[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(_), _, _) => {} + (Some(tile), _, _) => tiles.push(tile), _ => fail!("Quadtree: No valid tiles to remove"), } } } - None => {} + None => {} // Nothing to do } + tiles } /// Add a tile associtated with a given page position. /// If the tile pushes the total memory over its maximum, tiles will be removed - /// until total memory is below the maximum again. - pub fn add_tile_page(&mut self, x: f32, y: f32, scale: f32, tile: T) { - self.root.add_tile(x, y, tile, self.max_tile_size as f32 / scale); + /// until total memory is below the maximum again. These tiles are returned. + pub fn add_tile_page(&mut self, x: f32, y: f32, scale: f32, tile: T) -> ~[T] { + let (_, tiles) = self.root.add_tile(x, y, 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, y); match r { - (Some(_), _, _) => {} + (Some(tile), _, _) => tiles.push(tile), _ => fail!("Quadtree: No valid tiles to remove"), } } } - None => {} + None => {} // Nothing to do } + tiles } - /// Get the tile rect in screen and page coordinates for a given pixel position + /// Get the tile rect in screen and page coordinates for a given pixel position. pub fn get_tile_rect_pixel(&mut self, x: uint, y: uint, scale: f32) -> BufferRequest { self.root.get_tile_rect(x as f32 / scale, y as f32 / scale, self.clip_size.width as f32, @@ -155,7 +160,7 @@ impl Quadtree { scale, self.max_tile_size as f32 / scale) } - /// Get the tile rect in screen and page coordinates for a given page position + /// Get the tile rect in screen and page coordinates for a given page position. pub fn get_tile_rect_page(&mut self, x: f32, y: f32, scale: f32) -> BufferRequest { self.root.get_tile_rect(x, y, self.clip_size.width as f32, @@ -163,7 +168,7 @@ impl Quadtree { scale, self.max_tile_size as f32 / scale) } - /// Get all the tiles in the tree + /// Get all the tiles in the tree. pub fn get_all_tiles<'r>(&'r self) -> ~[&'r T] { self.root.get_all_tiles() } @@ -189,26 +194,26 @@ impl Quadtree { } /// 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 boolean if the window needs to be redisplayed, i.e. if + /// 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. - pub fn get_tile_rects_pixel(&mut self, window: Rect, scale: f32) -> (~[BufferRequest], bool) { - let (ret, redisplay, _) = self.root.get_tile_rects( + pub fn get_tile_rects_pixel(&mut self, window: Rect, scale: f32) -> (~[BufferRequest], ~[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, redisplay) + (ret, unused) } - /// Same function as above, using page coordinates for the window - pub fn get_tile_rects_page(&mut self, window: Rect, scale: f32) -> (~[BufferRequest], bool) { - let (ret, redisplay, _) = self.root.get_tile_rects( + /// Same function as above, using page coordinates for the window. + pub fn get_tile_rects_page(&mut self, window: Rect, scale: f32) -> (~[BufferRequest], ~[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, redisplay) + (ret, unused) } /// Creates a new quadtree at the specified size. This should be called when the window changes size. @@ -305,7 +310,7 @@ impl QuadtreeNode { 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 { @@ -357,8 +362,9 @@ impl QuadtreeNode { /// 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. - fn add_tile(&mut self, x: f32, y: f32, tile: T, tile_size: f32) -> int { + /// 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, ~[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 @@ -369,21 +375,28 @@ impl QuadtreeNode { if self.size <= tile_size { // We are the child let old_size = self.tile_mem; self.tile_mem = tile.get_mem(); - self.tile = Some(tile); - // FIXME: This should be inline, but currently won't compile - let quads = [TL, TR, BL, BR]; - for quad in quads.iter() { - self.quadrants[*quad as int] = None; + let mut unused_tiles = match replace(&mut self.tile, Some(tile)) { + Some(old_tile) => ~[old_tile], + None => ~[], + }; + 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 + (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 int] { Some(ref mut child) => { - let delta = child.add_tile(x, y, tile, tile_size); + let (delta, unused) = child.add_tile(x, y, tile, tile_size); self.tile_mem = (self.tile_mem as int + delta) as uint; - delta + (delta, unused) } None => { // Make new child let new_size = self.size / 2.0; @@ -396,10 +409,10 @@ impl QuadtreeNode { BL | BR => self.origin.y + new_size, }; let mut c = ~QuadtreeNode::new_child(new_x, new_y, new_size); - let delta = c.add_tile(x, y, tile, tile_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 int] = Some(c); - delta + (delta, unused) } } } @@ -516,13 +529,13 @@ 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. + /// 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, clip: Size2D, scale: f32, tile_size: f32, override: bool) -> - (~[BufferRequest], bool, int) { + (~[BufferRequest], ~[T], int) { let w_x = window.origin.x; let w_y = window.origin.y; @@ -536,7 +549,7 @@ impl QuadtreeNode { 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 (~[], false, 0); + return (~[], ~[], 0); } // clip window to visible region @@ -545,7 +558,7 @@ impl QuadtreeNode { if s_size <= tile_size { // We are the child return match self.tile { - _ if self.status == Rendering || self.status == Hidden => (~[], false, 0), + _ if self.status == Rendering || self.status == Hidden => (~[], ~[], 0), Some(ref tile) if tile.is_valid(scale) && !override && self.status != Invalid => { let redisplay = match self.quadrants { @@ -553,20 +566,25 @@ impl QuadtreeNode { _ => true, }; let mut delta = 0; + let mut unused_tiles = ~[]; if redisplay { let old_mem = self.tile_mem; - // FIXME: This should be inline, but currently won't compile - let quads = [TL, TR, BL, BR]; - for quad in quads.iter() { - self.quadrants[*quad as int] = None; + 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; } - (~[], redisplay, delta) + (~[], unused_tiles, delta) } - _ => (~[self.get_tile_rect(s_x, s_y, clip.width, clip.height, scale, tile_size)], false, 0), + _ => (~[self.get_tile_rect(s_x, s_y, clip.width, clip.height, scale, tile_size)], ~[], 0), } } @@ -600,8 +618,8 @@ impl QuadtreeNode { let quads_to_check = build_sized(4, builder); - let mut ret = ~[]; - let mut redisplay = false; + let mut request = ~[]; + let mut unused = ~[]; let mut delta = 0; for quad in quads_to_check.iter() { @@ -627,7 +645,7 @@ impl QuadtreeNode { let override = override || self.status == Invalid; self.status = Normal; - let (c_ret, c_redisplay, c_delta) = match self.quadrants[*quad as int] { + let (c_request, c_unused, c_delta) = match self.quadrants[*quad as int] { Some(ref mut child) => child.get_tile_rects(new_window, clip, scale, tile_size, override), None => { // Create new child @@ -648,11 +666,28 @@ impl QuadtreeNode { }; delta = delta + c_delta; - ret = ret + c_ret; - redisplay = redisplay || c_redisplay; + request = request + c_request; + unused.push_all_move(c_unused); } self.tile_mem = (self.tile_mem as int + delta) as uint; - (ret, redisplay, delta) + (request, unused, delta) + } + + /// Remove all tiles from the tree. Use this to collect all tiles before deleting a branch. + fn collect_tiles(&mut self) -> ~[T] { + let mut ret = match replace(&mut self.tile, None) { + Some(tile) => ~[tile], + None => ~[], + }; + 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 @@ -745,6 +780,9 @@ pub fn test_resize() { fn is_valid(&self, _: f32) -> bool { true } + fn get_size_2d(&self) -> Size2D { + Size2D(0u, 0u) + } } let mut q = Quadtree::new(6, 6, 1, None); @@ -775,6 +813,9 @@ pub fn test() { fn is_valid(&self, _: f32) -> bool { true } + fn get_size_2d(&self) -> Size2D { + Size2D(0u, 0u) + } } let mut q = Quadtree::new(8, 8, 2, Some(4)); @@ -797,7 +838,7 @@ pub fn test() { q.add_tile_pixel(0, 0, 0.5, T{a: 6}); q.add_tile_pixel(0, 0, 1f32, T{a: 7}); - let (_, redisplay) = q.get_tile_rects_pixel(Rect(Point2D(0, 0), Size2D(2, 2)), 0.5); - assert!(redisplay); + 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/msg/compositor_msg.rs b/src/components/msg/compositor_msg.rs index 949f418c317..58a3b66a1b7 100644 --- a/src/components/msg/compositor_msg.rs +++ b/src/components/msg/compositor_msg.rs @@ -8,8 +8,6 @@ use geom::rect::Rect; use geom::size::Size2D; use constellation_msg::PipelineId; -use extra::arc; - #[deriving(Clone)] pub struct LayerBuffer { @@ -31,8 +29,9 @@ pub struct LayerBuffer { /// A set of layer buffers. This is an atomic unit used to switch between the front and back /// buffers. +#[deriving(Clone)] pub struct LayerBufferSet { - buffers: ~[LayerBuffer] + buffers: ~[~LayerBuffer] } /// The status of the renderer. @@ -72,7 +71,7 @@ pub trait RenderListener { fn set_layer_page_size(&self, PipelineId, Size2D, Epoch); fn set_layer_clip_rect(&self, PipelineId, Rect); fn delete_layer(&self, PipelineId); - fn paint(&self, id: PipelineId, layer_buffer_set: arc::Arc, Epoch); + fn paint(&self, id: PipelineId, layer_buffer_set: ~LayerBufferSet, Epoch); fn set_render_state(&self, render_state: RenderState); } @@ -89,6 +88,8 @@ pub trait 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; } impl Tile for ~LayerBuffer { @@ -98,5 +99,8 @@ impl Tile for ~LayerBuffer { } fn is_valid(&self, scale: f32) -> bool { self.resolution.approx_eq(&scale) - } + } + fn get_size_2d(&self) -> Size2D { + self.screen_pos.size + } }