mirror of
https://github.com/servo/servo.git
synced 2025-08-07 06:25:32 +01:00
Update remove_tile, add automatic tile removal, fix tests for quadtree
This commit is contained in:
parent
3bb47133d9
commit
5468885545
3 changed files with 201 additions and 124 deletions
|
@ -305,12 +305,9 @@ impl CompositorTask {
|
||||||
let ask_for_tiles: @fn() = || {
|
let ask_for_tiles: @fn() = || {
|
||||||
match *quadtree {
|
match *quadtree {
|
||||||
Some(ref mut quad) => {
|
Some(ref mut quad) => {
|
||||||
let valid = |tile: &~LayerBuffer| -> bool {
|
|
||||||
tile.resolution == *world_zoom
|
|
||||||
};
|
|
||||||
let (tile_request, redisplay) = quad.get_tile_rects(Rect(Point2D(world_offset.x as int,
|
let (tile_request, redisplay) = quad.get_tile_rects(Rect(Point2D(world_offset.x as int,
|
||||||
world_offset.y as int),
|
world_offset.y as int),
|
||||||
*window_size), valid, *world_zoom);
|
*window_size), *world_zoom);
|
||||||
|
|
||||||
if !tile_request.is_empty() {
|
if !tile_request.is_empty() {
|
||||||
match *render_chan {
|
match *render_chan {
|
||||||
|
@ -415,10 +412,9 @@ impl CompositorTask {
|
||||||
|
|
||||||
NewLayer(new_size, tile_size) => {
|
NewLayer(new_size, tile_size) => {
|
||||||
*page_size = Size2D(new_size.width as f32, new_size.height as f32);
|
*page_size = Size2D(new_size.width as f32, new_size.height as f32);
|
||||||
*quadtree = Some(Quadtree::new(0, 0,
|
*quadtree = Some(Quadtree::new(new_size.width.max(&(window_size.width as uint)),
|
||||||
new_size.width.max(&(window_size.width as uint)),
|
|
||||||
new_size.height.max(&(window_size.height as uint)),
|
new_size.height.max(&(window_size.height as uint)),
|
||||||
tile_size));
|
tile_size, Some(10000000u)));
|
||||||
ask_for_tiles();
|
ask_for_tiles();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -569,7 +565,7 @@ impl CompositorTask {
|
||||||
composite();
|
composite();
|
||||||
}
|
}
|
||||||
|
|
||||||
timer::sleep(&uv_global_loop::get(), 100);
|
timer::sleep(&uv_global_loop::get(), 10);
|
||||||
|
|
||||||
// If a pinch-zoom happened recently, ask for tiles at the new resolution
|
// If a pinch-zoom happened recently, ask for tiles at the new resolution
|
||||||
if *zoom_action && precise_time_s() - *zoom_time > 0.3 {
|
if *zoom_action && precise_time_s() - *zoom_time > 0.3 {
|
||||||
|
|
|
@ -10,13 +10,16 @@ use geom::size::Size2D;
|
||||||
use geom::rect::Rect;
|
use geom::rect::Rect;
|
||||||
use std::uint::{div_ceil, next_power_of_two};
|
use std::uint::{div_ceil, next_power_of_two};
|
||||||
use std::vec::build_sized;
|
use std::vec::build_sized;
|
||||||
|
use std::util::replace;
|
||||||
use gfx::render_task::BufferRequest;
|
use gfx::render_task::BufferRequest;
|
||||||
|
use servo_msg::compositor_msg::Tile;
|
||||||
|
|
||||||
/// Parent to all quadtree nodes. Stores variables needed at all levels. All method calls
|
/// Parent to all quadtree nodes. Stores variables needed at all levels. All method calls
|
||||||
/// at this level are in pixel coordinates.
|
/// at this level are in pixel coordinates.
|
||||||
pub struct Quadtree<T> {
|
pub struct Quadtree<T> {
|
||||||
root: QuadtreeNode<T>,
|
root: QuadtreeNode<T>,
|
||||||
max_tile_size: uint,
|
max_tile_size: uint,
|
||||||
|
max_mem: Option<uint>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A node in the tree. All method calls at this level are in page coordinates.
|
/// A node in the tree. All method calls at this level are in page coordinates.
|
||||||
|
@ -31,6 +34,8 @@ struct QuadtreeNode<T> {
|
||||||
quadrants: [Option<~QuadtreeNode<T>>, ..4],
|
quadrants: [Option<~QuadtreeNode<T>>, ..4],
|
||||||
/// If this node is marked for rendering
|
/// If this node is marked for rendering
|
||||||
render_flag: bool,
|
render_flag: bool,
|
||||||
|
/// Combined size of self.tile and tiles of all descendants
|
||||||
|
tile_mem: uint,
|
||||||
}
|
}
|
||||||
|
|
||||||
priv enum Quadrant {
|
priv enum Quadrant {
|
||||||
|
@ -40,9 +45,12 @@ priv enum Quadrant {
|
||||||
BR = 3,
|
BR = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Quadtree<T> {
|
impl<T: Tile> Quadtree<T> {
|
||||||
/// Public method to create a new Quadtree
|
/// Public method to create a new Quadtree
|
||||||
pub fn new(x: uint, y: uint, width: uint, height: uint, tile_size: uint) -> Quadtree<T> {
|
/// 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(width: uint, height: 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
|
// Spaces must be squares and powers of 2, so expand the space until it is
|
||||||
let longer = width.max(&height);
|
let longer = width.max(&height);
|
||||||
let num_tiles = div_ceil(longer, tile_size);
|
let num_tiles = div_ceil(longer, tile_size);
|
||||||
|
@ -52,12 +60,14 @@ impl<T> Quadtree<T> {
|
||||||
Quadtree {
|
Quadtree {
|
||||||
root: QuadtreeNode {
|
root: QuadtreeNode {
|
||||||
tile: None,
|
tile: None,
|
||||||
origin: Point2D(x as f32, y as f32),
|
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,
|
render_flag: false,
|
||||||
|
tile_mem: 0,
|
||||||
},
|
},
|
||||||
max_tile_size: tile_size,
|
max_tile_size: tile_size,
|
||||||
|
max_mem: max_mem,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,8 +80,22 @@ impl<T> Quadtree<T> {
|
||||||
self.root.get_tile(x as f32 / scale, y as f32 / scale)
|
self.root.get_tile(x as f32 / scale, y as f32 / scale)
|
||||||
}
|
}
|
||||||
/// Add a tile associtated with a given pixel position and scale.
|
/// 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(&mut self, x: uint, y: uint, scale: f32, tile: T) {
|
pub fn add_tile(&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);
|
self.root.add_tile(x as f32 / scale, y as f32 / scale, tile, self.max_tile_size as f32 / scale);
|
||||||
|
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(_), _, _) => {}
|
||||||
|
_ => fail!("Quadtree: No valid tiles to remove"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/// 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(&mut self, x: uint, y: uint, scale: f32) -> BufferRequest {
|
pub fn get_tile_rect(&mut self, x: uint, y: uint, scale: f32) -> BufferRequest {
|
||||||
|
@ -83,21 +107,24 @@ impl<T> Quadtree<T> {
|
||||||
}
|
}
|
||||||
/// Ask a tile to be deleted from the quadtree. This tries to delete a tile that is far from the
|
/// Ask a tile to be deleted from the quadtree. This tries to delete a tile that is far from the
|
||||||
/// given point in pixel coordinates.
|
/// given point in pixel coordinates.
|
||||||
pub fn remove_tile(&mut self, x: uint, y: uint, scale: f32) {
|
pub fn remove_tile(&mut self, x: uint, y: uint, scale: f32) -> T {
|
||||||
self.root.remove_tile(x as f32 / scale, y as f32 / scale);
|
let r = self.root.remove_tile(x as f32 / scale, y as f32 / scale);
|
||||||
|
match r {
|
||||||
|
(Some(tile), _, _) => tile,
|
||||||
|
_ => fail!("Quadtree: No valid tiles to remove"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/// Given a window rect in pixel coordinates and a function to check if an existing tile is "valid"
|
/// Given a window rect in pixel coordinates, this function returns a list of BufferRequests for tiles that
|
||||||
/// (i.e. is the correct resolution), 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 boolean 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
|
/// 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.
|
/// user zooms out and cached tiles need to be displayed on top of higher resolution tiles.
|
||||||
pub fn get_tile_rects(&mut self, window: Rect<int>, valid: &fn(&T) -> bool, scale: f32) ->
|
/// When this happens, higher resolution tiles will be removed from the quadtree.
|
||||||
(~[BufferRequest], bool) {
|
pub fn get_tile_rects(&mut self, window: Rect<int>, scale: f32) -> (~[BufferRequest], bool) {
|
||||||
|
let (ret, redisplay, _) = self.root.get_tile_rects(
|
||||||
self.root.get_tile_rects(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)),
|
||||||
valid, scale, self.max_tile_size as f32 / scale)
|
scale, self.max_tile_size as f32 / scale);
|
||||||
|
(ret, redisplay)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate html to visualize the tree. For debugging purposes only.
|
/// Generate html to visualize the tree. For debugging purposes only.
|
||||||
|
@ -108,7 +135,7 @@ impl<T> Quadtree<T> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> QuadtreeNode<T> {
|
impl<T: Tile> QuadtreeNode<T> {
|
||||||
/// Private method to create new children
|
/// Private method to create new children
|
||||||
fn new_child(x: f32, y: f32, size: f32) -> QuadtreeNode<T> {
|
fn new_child(x: f32, y: f32, size: f32) -> QuadtreeNode<T> {
|
||||||
QuadtreeNode {
|
QuadtreeNode {
|
||||||
|
@ -117,6 +144,7 @@ impl<T> QuadtreeNode<T> {
|
||||||
size: size,
|
size: size,
|
||||||
quadrants: [None, None, None, None],
|
quadrants: [None, None, None, None],
|
||||||
render_flag: false,
|
render_flag: false,
|
||||||
|
tile_mem: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,7 +199,8 @@ impl<T> QuadtreeNode<T> {
|
||||||
|
|
||||||
/// Add a tile associated with a given position in page coords. If the tile size exceeds the maximum,
|
/// 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.
|
/// the node will be split and the method will recurse until the tile size is within limits.
|
||||||
fn add_tile(&mut self, x: f32, y: f32, tile: T, tile_size: f32) {
|
/// 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 {
|
||||||
debug!("Quadtree: Adding: (%?, %?) size:%?px", self.origin.x, self.origin.y, self.size);
|
debug!("Quadtree: Adding: (%?, %?) size:%?px", self.origin.x, self.origin.y, self.size);
|
||||||
|
|
||||||
if x >= self.origin.x + self.size || x < self.origin.x
|
if x >= self.origin.x + self.size || x < self.origin.x
|
||||||
|
@ -180,6 +209,8 @@ impl<T> QuadtreeNode<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.size <= tile_size { // We are the child
|
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);
|
self.tile = Some(tile);
|
||||||
// FIXME: This should be inline, but currently won't compile
|
// FIXME: This should be inline, but currently won't compile
|
||||||
let quads = [TL, TR, BL, BR];
|
let quads = [TL, TR, BL, BR];
|
||||||
|
@ -187,10 +218,15 @@ impl<T> QuadtreeNode<T> {
|
||||||
self.quadrants[*quad as int] = None;
|
self.quadrants[*quad as int] = None;
|
||||||
}
|
}
|
||||||
self.render_flag = false;
|
self.render_flag = false;
|
||||||
|
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);
|
||||||
match self.quadrants[quad as int] {
|
match self.quadrants[quad as int] {
|
||||||
Some(ref mut child) => child.add_tile(x, y, tile, tile_size),
|
Some(ref mut child) => {
|
||||||
|
let delta = child.add_tile(x, y, tile, tile_size);
|
||||||
|
self.tile_mem = (self.tile_mem as int + delta) as uint;
|
||||||
|
delta
|
||||||
|
}
|
||||||
None => { // Make new child
|
None => { // Make new child
|
||||||
let new_size = self.size / 2.0;
|
let new_size = self.size / 2.0;
|
||||||
let new_x = match quad {
|
let new_x = match quad {
|
||||||
|
@ -202,28 +238,17 @@ impl<T> QuadtreeNode<T> {
|
||||||
BL | BR => self.origin.y + new_size,
|
BL | BR => self.origin.y + new_size,
|
||||||
};
|
};
|
||||||
let mut c = ~QuadtreeNode::new_child(new_x, new_y, new_size);
|
let mut c = ~QuadtreeNode::new_child(new_x, new_y, new_size);
|
||||||
c.add_tile(x, y, tile, tile_size);
|
let delta = 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);
|
self.quadrants[quad as int] = Some(c);
|
||||||
|
delta
|
||||||
// If my tile is completely occluded, get rid of it.
|
|
||||||
// FIXME: figure out a better way to determine if a tile is completely occluded
|
|
||||||
// e.g. this alg doesn't work if a tile is covered by its grandchildren
|
|
||||||
match self.quadrants {
|
|
||||||
[Some(ref tl_child), Some(ref tr_child), Some(ref bl_child), Some(ref br_child)] => {
|
|
||||||
match (&tl_child.tile, &tr_child.tile, &bl_child.tile, &br_child.tile) {
|
|
||||||
(&Some(_), &Some(_), &Some(_), &Some(_)) => self.tile = None,
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a tile rect in screen and page coords for a given position in page coords
|
/// 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, scale: f32, tile_size: f32) -> BufferRequest {
|
fn get_tile_rect(&mut self, x: f32, y: f32, scale: f32, tile_size: f32) -> BufferRequest {
|
||||||
if x >= self.origin.x + self.size || x < self.origin.x
|
if x >= self.origin.x + self.size || x < self.origin.x
|
||||||
|| y >= self.origin.y + self.size || y < self.origin.y {
|
|| y >= self.origin.y + self.size || y < self.origin.y {
|
||||||
fail!("Quadtree: Tried to query a tile rect outside of range");
|
fail!("Quadtree: Tried to query a tile rect outside of range");
|
||||||
|
@ -259,78 +284,81 @@ impl<T> QuadtreeNode<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes a tile that is far from the given input point in page coords. Returns true if the child
|
/// Removes a tile that is far from the given input point in page coords. Returns the tile removed,
|
||||||
/// has no tiles and needs to be deleted.
|
/// a bool that is true if the child has no tiles and needs to be deleted, and an integer showing the
|
||||||
fn remove_tile(&mut self, x: f32, y: f32) -> bool {
|
/// amount of memory changed by the operation. Unfortunately, the tile has to be an option, because
|
||||||
match (&self.tile, &self.quadrants) {
|
/// there are occasionally leaves without tiles. However, the option will always be Some as long as
|
||||||
(&Some(_), &[None, None, None, None]) => {
|
/// this quadtree node or at least one of its descendants is not empty.
|
||||||
self.tile = None;
|
fn remove_tile(&mut self, x: f32, y: f32) -> (Option<T>, bool, int) {
|
||||||
return true;
|
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"),
|
||||||
}
|
}
|
||||||
(&Some(_), _) => {
|
|
||||||
self.tile = None;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a hacky heuristic to find a tile that is "far away". There are better methods.
|
// 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 quad = self.get_quadrant(x, y);
|
||||||
let my_child = match quad {
|
let queue = match quad {
|
||||||
TL => {
|
TL => [BR, BL, TR, TL],
|
||||||
match (&self.quadrants[BR as int], &self.quadrants[BL as int], &self.quadrants[TR as int]) {
|
TR => [BL, BR, TL, TR],
|
||||||
(&Some(_), _, _) => BR,
|
BL => [TR, TL, BR, BL],
|
||||||
(&None, &Some(_), _) => BL,
|
BR => [TL, TR, BL, BR],
|
||||||
(&None, &None, &Some(_)) => TR,
|
|
||||||
_ => TL,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TR => {
|
|
||||||
match (&self.quadrants[BL as int], &self.quadrants[BR as int], &self.quadrants[TL as int]) {
|
|
||||||
(&Some(_), _, _) => BL,
|
|
||||||
(&None, &Some(_), _) => BR,
|
|
||||||
(&None, &None, &Some(_)) => TL,
|
|
||||||
_ => TR,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BL => {
|
|
||||||
match (&self.quadrants[TR as int], &self.quadrants[TL as int], &self.quadrants[BR as int]) {
|
|
||||||
(&Some(_), _, _) => TR,
|
|
||||||
(&None, &Some(_), _) => TL,
|
|
||||||
(&None, &None, &Some(_)) => BR,
|
|
||||||
_ => BL,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BR => {
|
|
||||||
match (&self.quadrants[TL as int], &self.quadrants[TR as int], &self.quadrants[BL as int]) {
|
|
||||||
(&Some(_), _, _) => TL,
|
|
||||||
(&None, &Some(_), _) => TR,
|
|
||||||
(&None, &None, &Some(_)) => BL,
|
|
||||||
_ => BR,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
match self.quadrants[my_child as int] {
|
let mut del_quad: Option<Quadrant> = None;
|
||||||
Some(ref mut child) if !child.remove_tile(x, y) => {
|
let mut ret = (None, false, 0);
|
||||||
return false;
|
|
||||||
}
|
for queue.iter().advance |quad| {
|
||||||
Some(_) => {} // fall through
|
match self.quadrants[*quad as int] {
|
||||||
None => fail!("Quadtree: child query failure"),
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
// child.remove_tile() returned true
|
ret = (tile, flag, delta);
|
||||||
self.quadrants[my_child as int] = None;
|
break;
|
||||||
match self.quadrants {
|
}
|
||||||
[None, None, None, None] => true,
|
None => {},
|
||||||
_ => false,
|
}
|
||||||
|
}
|
||||||
|
None => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match del_quad {
|
||||||
|
Some(quad) => {
|
||||||
|
self.quadrants[quad as int] = 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 and a tile validation function, returns a BufferRequest array
|
/// Given a window rect in page coordinates, returns a BufferRequest array,
|
||||||
/// and a redisplay boolean. See QuadTree function description for more details.
|
/// a redisplay boolean, and the difference in tile memory between the new and old quadtree nodes.
|
||||||
fn get_tile_rects(&mut self, window: Rect<f32>, valid: &fn(&T) -> bool, scale: f32, tile_size: f32) ->
|
/// NOTE: this method will sometimes modify the tree by deleting tiles.
|
||||||
(~[BufferRequest], bool) {
|
/// See the QuadTree function description for more details.
|
||||||
|
fn get_tile_rects(&mut self, window: Rect<f32>, scale: f32, tile_size: f32) ->
|
||||||
|
(~[BufferRequest], bool, int) {
|
||||||
|
|
||||||
let w_x = window.origin.x;
|
let w_x = window.origin.x;
|
||||||
let w_y = window.origin.y;
|
let w_y = window.origin.y;
|
||||||
|
@ -349,22 +377,27 @@ impl<T> 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),
|
_ if self.render_flag => (~[], false, 0),
|
||||||
Some(ref tile) if valid(tile) => {
|
Some(ref tile) if tile.is_valid(scale) => {
|
||||||
let redisplay = match self.quadrants {
|
let redisplay = match self.quadrants {
|
||||||
[None, None, None, None] => false,
|
[None, None, None, None] => false,
|
||||||
_ => true,
|
_ => true,
|
||||||
};
|
};
|
||||||
|
let mut delta = 0;
|
||||||
if redisplay {
|
if redisplay {
|
||||||
|
let old_mem = self.tile_mem;
|
||||||
// FIXME: This should be inline, but currently won't compile
|
// FIXME: This should be inline, but currently won't compile
|
||||||
let quads = [TL, TR, BL, BR];
|
let quads = [TL, TR, BL, BR];
|
||||||
for quads.iter().advance |quad| {
|
for quads.iter().advance |quad| {
|
||||||
self.quadrants[*quad as int] = None;
|
self.quadrants[*quad as int] = None;
|
||||||
}
|
}
|
||||||
|
self.tile_mem = tile.get_mem();
|
||||||
|
delta = self.tile_mem as int - old_mem as int;
|
||||||
|
|
||||||
}
|
}
|
||||||
(~[], redisplay)
|
(~[], redisplay, delta)
|
||||||
}
|
}
|
||||||
_ => (~[self.get_tile_rect(s_x, s_y, scale, tile_size)], false),
|
_ => (~[self.get_tile_rect(s_x, s_y, scale, tile_size)], false, 0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -400,6 +433,7 @@ impl<T> QuadtreeNode<T> {
|
||||||
|
|
||||||
let mut ret = ~[];
|
let mut ret = ~[];
|
||||||
let mut redisplay = false;
|
let mut redisplay = false;
|
||||||
|
let mut delta = 0;
|
||||||
|
|
||||||
for quads_to_check.iter().advance |quad| {
|
for quads_to_check.iter().advance |quad| {
|
||||||
// Recurse into child
|
// Recurse into child
|
||||||
|
@ -422,8 +456,8 @@ impl<T> QuadtreeNode<T> {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let (c_ret, c_redisplay) = 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, |x| valid(x), scale, tile_size),
|
Some(ref mut child) => child.get_tile_rects(new_window, scale, tile_size),
|
||||||
None => {
|
None => {
|
||||||
// Create new child
|
// Create new child
|
||||||
let new_size = self.size / 2.0;
|
let new_size = self.size / 2.0;
|
||||||
|
@ -436,17 +470,18 @@ impl<T> 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 (a, b) = child.get_tile_rects(new_window, |x| valid(x), scale, tile_size);
|
let (a, b, c) = child.get_tile_rects(new_window, scale, tile_size);
|
||||||
self.quadrants[*quad as int] = Some(child);
|
self.quadrants[*quad as int] = Some(child);
|
||||||
(a, b)
|
(a, b, c)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
delta = delta + c_delta;
|
||||||
ret = ret + c_ret;
|
ret = ret + c_ret;
|
||||||
redisplay = redisplay || c_redisplay;
|
redisplay = redisplay || c_redisplay;
|
||||||
}
|
}
|
||||||
|
self.tile_mem = (self.tile_mem as int + delta) as uint;
|
||||||
(ret, redisplay)
|
(ret, redisplay, delta)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -492,15 +527,42 @@ impl<T> QuadtreeNode<T> {
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_add_tile() {
|
pub fn test() {
|
||||||
let mut t = Quadtree::new(50, 30, 20, 20, 10);
|
struct T {
|
||||||
assert!(t.get_tile(50, 30, 1.0).is_none());
|
a: int,
|
||||||
t.add_tile(50, 30, 1.0, 1);
|
}
|
||||||
assert!(t.get_tile(50, 30, 1.0).get() == 1);
|
|
||||||
assert!(t.get_tile(59, 39, 1.0).get() == 1);
|
impl Tile for T {
|
||||||
assert!(t.get_tile(60, 40, 1.0).is_none());
|
fn get_mem(&self) -> uint {
|
||||||
assert!(t.get_tile(110, 70, 2.0).get() == 1);
|
1
|
||||||
t.add_tile(100, 60, 2.0, 2);
|
}
|
||||||
assert!(t.get_tile(109, 69, 2.0).get() == 2);
|
|
||||||
assert!(t.get_tile(110, 70, 2.0).get() == 1);
|
fn is_valid(&self, _: f32) -> bool {
|
||||||
}
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut q = Quadtree::new(8, 8, 2, Some(4));
|
||||||
|
q.add_tile(0, 0, 1f32, T{a: 0});
|
||||||
|
q.add_tile(0, 0, 2f32, T{a: 1});
|
||||||
|
q.add_tile(0, 0, 2f32, T{a: 2});
|
||||||
|
q.add_tile(2, 0, 2f32, T{a: 3});
|
||||||
|
assert!(q.root.tile_mem == 3);
|
||||||
|
assert!(q.get_all_tiles().len() == 3);
|
||||||
|
q.add_tile(0, 2, 2f32, T{a: 4});
|
||||||
|
q.add_tile(2, 2, 2f32, T{a: 5});
|
||||||
|
assert!(q.root.tile_mem == 4);
|
||||||
|
|
||||||
|
let (request, _) = q.get_tile_rects(Rect(Point2D(0, 0), Size2D(2, 2)), 2f32);
|
||||||
|
assert!(request.is_empty());
|
||||||
|
let (request, _) = q.get_tile_rects(Rect(Point2D(0, 0), Size2D(2, 2)), 1.9);
|
||||||
|
assert!(request.is_empty());
|
||||||
|
let (request, _) = q.get_tile_rects(Rect(Point2D(0, 0), Size2D(2, 2)), 1f32);
|
||||||
|
assert!(request.len() == 4);
|
||||||
|
|
||||||
|
q.add_tile(0, 0, 0.5, T{a: 6});
|
||||||
|
q.add_tile(0, 0, 1f32, T{a: 7});
|
||||||
|
let (_, redisplay) = q.get_tile_rects(Rect(Point2D(0, 0), Size2D(2, 2)), 0.5);
|
||||||
|
assert!(redisplay);
|
||||||
|
assert!(q.root.tile_mem == 1);
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ use geom::size::Size2D;
|
||||||
|
|
||||||
use extra::arc;
|
use extra::arc;
|
||||||
|
|
||||||
|
|
||||||
#[deriving(Clone)]
|
#[deriving(Clone)]
|
||||||
pub struct LayerBuffer {
|
pub struct LayerBuffer {
|
||||||
draw_target: DrawTarget,
|
draw_target: DrawTarget,
|
||||||
|
@ -68,3 +69,21 @@ pub trait RenderListener {
|
||||||
pub trait ScriptListener : Clone {
|
pub trait ScriptListener : Clone {
|
||||||
fn set_ready_state(&self, ReadyState);
|
fn set_ready_state(&self, ReadyState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The interface used by the quadtree to get info about LayerBuffers
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tile for ~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.approx_eq(&scale)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue