mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +01:00
auto merge of #729 : eschweic/servo/manage-buffers, r=metajack
Prevents unnecessary copying of buffers, and implements buffer recycling.
This commit is contained in:
commit
83fba3d9a4
8 changed files with 311 additions and 95 deletions
135
src/components/gfx/buffer_map.rs
Normal file
135
src/components/gfx/buffer_map.rs
Normal file
|
@ -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<T> {
|
||||||
|
/// A HashMap that stores the Buffers.
|
||||||
|
map: HashMap<BufferKey, BufferValue<T>>,
|
||||||
|
/// 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<uint>) -> BufferKey {
|
||||||
|
BufferKey([input.width, input.height])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A helper struct to keep track of buffers in the HashMap
|
||||||
|
struct BufferValue<T> {
|
||||||
|
/// An array of buffers, all the same size
|
||||||
|
buffers: ~[T],
|
||||||
|
/// The counter when this size was last requested
|
||||||
|
last_action: uint,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Tile> BufferMap<T> {
|
||||||
|
// Creates a new BufferMap with a given buffer limit.
|
||||||
|
pub fn new(max_mem: uint) -> BufferMap<T> {
|
||||||
|
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<BufferKey> = 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<uint>) -> Option<T> {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
|
@ -51,6 +51,7 @@ pub mod font_list;
|
||||||
|
|
||||||
// Misc.
|
// Misc.
|
||||||
pub mod opts;
|
pub mod opts;
|
||||||
|
mod buffer_map;
|
||||||
|
|
||||||
// Platform-specific implementations.
|
// Platform-specific implementations.
|
||||||
#[path="platform/mod.rs"]
|
#[path="platform/mod.rs"]
|
||||||
|
|
|
@ -19,7 +19,7 @@ use servo_net::image::base::Image;
|
||||||
use extra::arc::Arc;
|
use extra::arc::Arc;
|
||||||
|
|
||||||
pub struct RenderContext<'self> {
|
pub struct RenderContext<'self> {
|
||||||
canvas: &'self LayerBuffer,
|
canvas: &'self ~LayerBuffer,
|
||||||
font_ctx: @mut FontContext,
|
font_ctx: @mut FontContext,
|
||||||
opts: &'self Opts
|
opts: &'self Opts
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,8 +24,7 @@ use extra::arc::Arc;
|
||||||
use servo_util::time::{ProfilerChan, profile};
|
use servo_util::time::{ProfilerChan, profile};
|
||||||
use servo_util::time;
|
use servo_util::time;
|
||||||
|
|
||||||
use extra::arc;
|
use buffer_map::BufferMap;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub struct RenderLayer<T> {
|
pub struct RenderLayer<T> {
|
||||||
|
@ -35,7 +34,8 @@ pub struct RenderLayer<T> {
|
||||||
|
|
||||||
pub enum Msg<T> {
|
pub enum Msg<T> {
|
||||||
RenderMsg(RenderLayer<T>),
|
RenderMsg(RenderLayer<T>),
|
||||||
ReRenderMsg(~[BufferRequest], f32, PipelineId, Epoch),
|
ReRenderMsg(~[BufferRequest], f32, Epoch),
|
||||||
|
UnusedBufferMsg(~[~LayerBuffer]),
|
||||||
PaintPermissionGranted,
|
PaintPermissionGranted,
|
||||||
PaintPermissionRevoked,
|
PaintPermissionRevoked,
|
||||||
ExitMsg(Chan<()>),
|
ExitMsg(Chan<()>),
|
||||||
|
@ -91,9 +91,11 @@ struct RenderTask<C,T> {
|
||||||
/// Permission to send paint messages to the compositor
|
/// Permission to send paint messages to the compositor
|
||||||
paint_permission: bool,
|
paint_permission: bool,
|
||||||
/// Cached copy of last layers rendered
|
/// Cached copy of last layers rendered
|
||||||
last_paint_msg: Option<(arc::Arc<LayerBufferSet>, Size2D<uint>)>,
|
last_paint_msg: Option<~LayerBufferSet>,
|
||||||
/// A counter for epoch messages
|
/// A counter for epoch messages
|
||||||
epoch: Epoch,
|
epoch: Epoch,
|
||||||
|
/// A data structure to store unused LayerBuffers
|
||||||
|
buffer_map: BufferMap<~LayerBuffer>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
|
impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
|
||||||
|
@ -129,6 +131,7 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
|
||||||
paint_permission: false,
|
paint_permission: false,
|
||||||
last_paint_msg: None,
|
last_paint_msg: None,
|
||||||
epoch: Epoch(0),
|
epoch: Epoch(0),
|
||||||
|
buffer_map: BufferMap::new(10000000),
|
||||||
};
|
};
|
||||||
|
|
||||||
render_task.start();
|
render_task.start();
|
||||||
|
@ -147,13 +150,19 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
|
||||||
}
|
}
|
||||||
self.render_layer = Some(render_layer);
|
self.render_layer = Some(render_layer);
|
||||||
}
|
}
|
||||||
ReRenderMsg(tiles, scale, id, epoch) => {
|
ReRenderMsg(tiles, scale, epoch) => {
|
||||||
if self.epoch == epoch {
|
if self.epoch == epoch {
|
||||||
self.render(tiles, scale, id);
|
self.render(tiles, scale);
|
||||||
} else {
|
} else {
|
||||||
debug!("renderer epoch mismatch: %? != %?", self.epoch, epoch);
|
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 => {
|
PaintPermissionGranted => {
|
||||||
self.paint_permission = true;
|
self.paint_permission = true;
|
||||||
match self.render_layer {
|
match self.render_layer {
|
||||||
|
@ -163,6 +172,16 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
|
||||||
}
|
}
|
||||||
None => {}
|
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 => {
|
PaintPermissionRevoked => {
|
||||||
self.paint_permission = false;
|
self.paint_permission = false;
|
||||||
|
@ -175,7 +194,7 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&mut self, tiles: ~[BufferRequest], scale: f32, id: PipelineId) {
|
fn render(&mut self, tiles: ~[BufferRequest], scale: f32) {
|
||||||
let render_layer;
|
let render_layer;
|
||||||
match self.render_layer {
|
match self.render_layer {
|
||||||
Some(ref r_layer) => {
|
Some(ref r_layer) => {
|
||||||
|
@ -196,15 +215,24 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
|
||||||
let width = tile.screen_rect.size.width;
|
let width = tile.screen_rect.size.width;
|
||||||
let height = tile.screen_rect.size.height;
|
let height = tile.screen_rect.size.height;
|
||||||
|
|
||||||
let buffer = LayerBuffer {
|
let buffer = match self.buffer_map.find(tile.screen_rect.size) {
|
||||||
draw_target: DrawTarget::new_with_fbo(self.opts.render_backend,
|
Some(buffer) => {
|
||||||
self.share_gl_context,
|
let mut buffer = buffer;
|
||||||
Size2D(width as i32, height as i32),
|
buffer.rect = tile.page_rect;
|
||||||
B8G8R8A8),
|
buffer.screen_pos = tile.screen_rect;
|
||||||
rect: tile.page_rect,
|
buffer.resolution = scale;
|
||||||
screen_pos: tile.screen_rect,
|
buffer
|
||||||
resolution: scale,
|
}
|
||||||
stride: (width * 4) as uint
|
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<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let layer_buffer_set = LayerBufferSet {
|
let layer_buffer_set = ~LayerBufferSet {
|
||||||
buffers: new_buffers,
|
buffers: new_buffers,
|
||||||
};
|
};
|
||||||
let layer_buffer_set = arc::Arc::new(layer_buffer_set);
|
|
||||||
|
|
||||||
debug!("render_task: returning surface");
|
debug!("render_task: returning surface");
|
||||||
if self.paint_permission {
|
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");
|
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);
|
self.compositor.set_render_state(IdleRenderState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,12 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use std::cell::Cell;
|
||||||
use geom::point::Point2D;
|
use geom::point::Point2D;
|
||||||
use geom::size::Size2D;
|
use geom::size::Size2D;
|
||||||
use geom::rect::Rect;
|
use geom::rect::Rect;
|
||||||
use geom::matrix::identity;
|
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::compositor_msg::{LayerBuffer, LayerBufferSet, Epoch};
|
||||||
use servo_msg::constellation_msg::PipelineId;
|
use servo_msg::constellation_msg::PipelineId;
|
||||||
use script::dom::event::{ClickEvent, MouseDownEvent, MouseUpEvent};
|
use script::dom::event::{ClickEvent, MouseDownEvent, MouseUpEvent};
|
||||||
|
@ -207,10 +208,13 @@ impl CompositorLayer {
|
||||||
no quadtree initialized", self.pipeline.id),
|
no quadtree initialized", self.pipeline.id),
|
||||||
Tree(ref mut quadtree) => quadtree,
|
Tree(ref mut quadtree) => quadtree,
|
||||||
};
|
};
|
||||||
let (request, r) = quadtree.get_tile_rects_page(rect, scale);
|
let (request, unused) = quadtree.get_tile_rects_page(rect, scale);
|
||||||
redisplay = r; // workaround to make redisplay visible outside block
|
redisplay = !unused.is_empty(); // workaround to make redisplay visible outside block
|
||||||
if !request.is_empty() {
|
if redisplay { // send back unused tiles
|
||||||
self.pipeline.render_chan.send(ReRenderMsg(request, scale, self.pipeline.id.clone(), self.epoch));
|
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 {
|
if redisplay {
|
||||||
|
@ -399,7 +403,8 @@ 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.
|
||||||
// If the epoch of the message does not match the layer's epoch, the message is ignored.
|
// 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.pipeline.id == pipeline_id {
|
||||||
if self.epoch != epoch {
|
if self.epoch != epoch {
|
||||||
debug!("compositor epoch mismatch: %? != %?, id: %?", self.epoch, epoch, self.pipeline.id);
|
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
|
{ // block here to prevent double mutable borrow of self
|
||||||
let quadtree = match self.quadtree {
|
let quadtree = match self.quadtree {
|
||||||
NoTree(_, _) => fail!("CompositorLayer: cannot get buffer request for %?,
|
NoTree(_, _) => fail!("CompositorLayer: cannot add buffers, no quadtree initialized"),
|
||||||
no quadtree initialized", self.pipeline.id),
|
|
||||||
Tree(ref mut quadtree) => quadtree,
|
Tree(ref mut quadtree) => quadtree,
|
||||||
};
|
};
|
||||||
|
|
||||||
for buffer in new_buffers.buffers.iter() {
|
let mut unused_tiles = ~[];
|
||||||
// TODO: This may return old buffers, which should be sent back to the renderer.
|
// move_rev_iter is more efficient
|
||||||
quadtree.add_tile_pixel(buffer.screen_pos.origin.x, buffer.screen_pos.origin.y,
|
for buffer in cell.take().buffers.move_rev_iter() {
|
||||||
buffer.resolution, ~buffer.clone());
|
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();
|
self.build_layer_tree();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// ID does not match ours, so recurse on descendents (including hidden children).
|
// 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.
|
// Deletes a specified sublayer, including hidden children. Returns false if the layer is not found.
|
||||||
|
|
|
@ -40,7 +40,6 @@ use servo_util::time::ProfilerChan;
|
||||||
|
|
||||||
use extra::future::from_value;
|
use extra::future::from_value;
|
||||||
use extra::time::precise_time_s;
|
use extra::time::precise_time_s;
|
||||||
use extra::arc;
|
|
||||||
|
|
||||||
use constellation::SendableFrameTree;
|
use constellation::SendableFrameTree;
|
||||||
use compositing::compositor_layer::CompositorLayer;
|
use compositing::compositor_layer::CompositorLayer;
|
||||||
|
@ -79,7 +78,7 @@ impl RenderListener for CompositorChan {
|
||||||
port.recv()
|
port.recv()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(&self, id: PipelineId, layer_buffer_set: arc::Arc<LayerBufferSet>, epoch: Epoch) {
|
fn paint(&self, id: PipelineId, layer_buffer_set: ~LayerBufferSet, epoch: Epoch) {
|
||||||
self.chan.send(Paint(id, layer_buffer_set, epoch))
|
self.chan.send(Paint(id, layer_buffer_set, epoch))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,7 +147,7 @@ pub enum Msg {
|
||||||
InvalidateRect(PipelineId, Rect<uint>),
|
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>, Epoch),
|
Paint(PipelineId, ~LayerBufferSet, Epoch),
|
||||||
/// Alerts the compositor to the current status of page loading.
|
/// Alerts the compositor to the current status of page loading.
|
||||||
ChangeReadyState(ReadyState),
|
ChangeReadyState(ReadyState),
|
||||||
/// Alerts the compositor to the current status of rendering.
|
/// Alerts the compositor to the current status of rendering.
|
||||||
|
@ -338,7 +337,7 @@ impl CompositorTask {
|
||||||
|
|
||||||
match compositor_layer {
|
match compositor_layer {
|
||||||
Some(ref mut 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;
|
recomposite = true;
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
|
|
@ -111,43 +111,48 @@ impl<T: Tile> Quadtree<T> {
|
||||||
|
|
||||||
/// 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
|
/// If the tile pushes the total memory over its maximum, tiles will be removed
|
||||||
/// until total memory is below the maximum again.
|
/// 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) {
|
pub fn add_tile_pixel(&mut self, x: uint, y: uint, scale: f32, tile: T) -> ~[T] {
|
||||||
self.root.add_tile(x as f32 / scale, y as f32 / scale, tile, self.max_tile_size as f32 / scale);
|
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 {
|
match self.max_mem {
|
||||||
Some(max) => {
|
Some(max) => {
|
||||||
while self.root.tile_mem > max {
|
while self.root.tile_mem > max {
|
||||||
let r = 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 {
|
match r {
|
||||||
(Some(_), _, _) => {}
|
(Some(tile), _, _) => tiles.push(tile),
|
||||||
_ => fail!("Quadtree: No valid tiles to remove"),
|
_ => fail!("Quadtree: No valid tiles to remove"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {}
|
None => {} // Nothing to do
|
||||||
}
|
}
|
||||||
|
tiles
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a tile associtated with a given page position.
|
/// Add a tile associtated with a given page position.
|
||||||
/// If the tile pushes the total memory over its maximum, tiles will be removed
|
/// If the tile pushes the total memory over its maximum, tiles will be removed
|
||||||
/// until total memory is below the maximum again.
|
/// 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) {
|
pub fn add_tile_page(&mut self, x: f32, y: f32, scale: f32, tile: T) -> ~[T] {
|
||||||
self.root.add_tile(x, y, tile, self.max_tile_size as f32 / scale);
|
let (_, tiles) = self.root.add_tile(x, y, tile, self.max_tile_size as f32 / scale);
|
||||||
|
let mut tiles = tiles;
|
||||||
match self.max_mem {
|
match self.max_mem {
|
||||||
Some(max) => {
|
Some(max) => {
|
||||||
while self.root.tile_mem > max {
|
while self.root.tile_mem > max {
|
||||||
let r = self.root.remove_tile(x, y);
|
let r = self.root.remove_tile(x, y);
|
||||||
match r {
|
match r {
|
||||||
(Some(_), _, _) => {}
|
(Some(tile), _, _) => tiles.push(tile),
|
||||||
_ => fail!("Quadtree: No valid tiles to remove"),
|
_ => 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 {
|
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.root.get_tile_rect(x as f32 / scale, y as f32 / scale,
|
||||||
self.clip_size.width as f32,
|
self.clip_size.width as f32,
|
||||||
|
@ -155,7 +160,7 @@ impl<T: Tile> Quadtree<T> {
|
||||||
scale, self.max_tile_size as f32 / scale)
|
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 {
|
pub fn get_tile_rect_page(&mut self, x: f32, y: f32, scale: f32) -> BufferRequest {
|
||||||
self.root.get_tile_rect(x, y,
|
self.root.get_tile_rect(x, y,
|
||||||
self.clip_size.width as f32,
|
self.clip_size.width as f32,
|
||||||
|
@ -163,7 +168,7 @@ impl<T: Tile> Quadtree<T> {
|
||||||
scale, self.max_tile_size as f32 / scale)
|
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] {
|
pub fn get_all_tiles<'r>(&'r self) -> ~[&'r T] {
|
||||||
self.root.get_all_tiles()
|
self.root.get_all_tiles()
|
||||||
}
|
}
|
||||||
|
@ -189,26 +194,26 @@ impl<T: Tile> Quadtree<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Given a window rect in pixel coordinates, this function returns a list of BufferRequests for tiles that
|
/// 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
|
/// 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.
|
||||||
/// When this happens, higher resolution tiles will be removed from the quadtree.
|
/// When this happens, higher resolution tiles will be removed from the quadtree.
|
||||||
pub fn get_tile_rects_pixel(&mut self, window: Rect<int>, scale: f32) -> (~[BufferRequest], bool) {
|
pub fn get_tile_rects_pixel(&mut self, window: Rect<int>, scale: f32) -> (~[BufferRequest], ~[T]) {
|
||||||
let (ret, redisplay, _) = self.root.get_tile_rects(
|
let (ret, unused, _) = 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)),
|
||||||
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, false);
|
scale, self.max_tile_size as f32 / scale, false);
|
||||||
(ret, redisplay)
|
(ret, unused)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Same function as above, using page coordinates for the window
|
/// Same function as above, using page coordinates for the window.
|
||||||
pub fn get_tile_rects_page(&mut self, window: Rect<f32>, scale: f32) -> (~[BufferRequest], bool) {
|
pub fn get_tile_rects_page(&mut self, window: Rect<f32>, scale: f32) -> (~[BufferRequest], ~[T]) {
|
||||||
let (ret, redisplay, _) = self.root.get_tile_rects(
|
let (ret, unused, _) = 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, false);
|
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.
|
/// Creates a new quadtree at the specified size. This should be called when the window changes size.
|
||||||
|
@ -305,7 +310,7 @@ impl<T: Tile> QuadtreeNode<T> {
|
||||||
status: Normal,
|
status: Normal,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determine which child contains a given point in page coords.
|
/// Determine which child contains a given point in page coords.
|
||||||
fn get_quadrant(&self, x: f32, y: f32) -> Quadrant {
|
fn get_quadrant(&self, x: f32, y: f32) -> Quadrant {
|
||||||
if x < self.origin.x + self.size / 2.0 {
|
if x < self.origin.x + self.size / 2.0 {
|
||||||
|
@ -357,8 +362,9 @@ impl<T: Tile> 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.
|
||||||
/// Returns an the difference in tile memory between the new quadtree node and the old quadtree node.
|
/// 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 {
|
/// 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);
|
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
|
||||||
|
@ -369,21 +375,28 @@ impl<T: Tile> 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;
|
let old_size = self.tile_mem;
|
||||||
self.tile_mem = tile.get_mem();
|
self.tile_mem = tile.get_mem();
|
||||||
self.tile = Some(tile);
|
let mut unused_tiles = match replace(&mut self.tile, Some(tile)) {
|
||||||
// FIXME: This should be inline, but currently won't compile
|
Some(old_tile) => ~[old_tile],
|
||||||
let quads = [TL, TR, BL, BR];
|
None => ~[],
|
||||||
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.status = Normal;
|
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
|
} 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) => {
|
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;
|
self.tile_mem = (self.tile_mem as int + delta) as uint;
|
||||||
delta
|
(delta, unused)
|
||||||
}
|
}
|
||||||
None => { // Make new child
|
None => { // Make new child
|
||||||
let new_size = self.size / 2.0;
|
let new_size = self.size / 2.0;
|
||||||
|
@ -396,10 +409,10 @@ impl<T: Tile> 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);
|
||||||
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.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
|
(delta, unused)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -516,13 +529,13 @@ 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.
|
/// 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
|
/// The override bool will be true if a parent node was marked as invalid; child nodes will be
|
||||||
/// treated as invalid as well.
|
/// 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, override: bool) ->
|
fn get_tile_rects(&mut self, window: Rect<f32>, clip: Size2D<f32>, scale: f32, tile_size: f32, override: bool) ->
|
||||||
(~[BufferRequest], bool, int) {
|
(~[BufferRequest], ~[T], 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;
|
||||||
|
@ -536,7 +549,7 @@ impl<T: Tile> QuadtreeNode<T> {
|
||||||
if w_x + w_width < s_x || w_x > s_x + s_size
|
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_y + w_height < s_y || w_y > s_y + s_size
|
||||||
|| w_x >= clip.width || w_y >= clip.height {
|
|| w_x >= clip.width || w_y >= clip.height {
|
||||||
return (~[], false, 0);
|
return (~[], ~[], 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// clip window to visible region
|
// clip window to visible region
|
||||||
|
@ -545,7 +558,7 @@ 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.status == Rendering || self.status == Hidden => (~[], false, 0),
|
_ if self.status == Rendering || self.status == Hidden => (~[], ~[], 0),
|
||||||
Some(ref tile) if tile.is_valid(scale) && !override
|
Some(ref tile) if tile.is_valid(scale) && !override
|
||||||
&& self.status != Invalid => {
|
&& self.status != Invalid => {
|
||||||
let redisplay = match self.quadrants {
|
let redisplay = match self.quadrants {
|
||||||
|
@ -553,20 +566,25 @@ impl<T: Tile> QuadtreeNode<T> {
|
||||||
_ => true,
|
_ => true,
|
||||||
};
|
};
|
||||||
let mut delta = 0;
|
let mut delta = 0;
|
||||||
|
let mut unused_tiles = ~[];
|
||||||
if redisplay {
|
if redisplay {
|
||||||
let old_mem = self.tile_mem;
|
let old_mem = self.tile_mem;
|
||||||
// FIXME: This should be inline, but currently won't compile
|
for child in self.quadrants.mut_iter() {
|
||||||
let quads = [TL, TR, BL, BR];
|
match *child {
|
||||||
for quad in quads.iter() {
|
Some(ref mut node) => {
|
||||||
self.quadrants[*quad as int] = None;
|
unused_tiles.push_all_move(node.collect_tiles());
|
||||||
|
}
|
||||||
|
None => {} // Nothing to do
|
||||||
|
}
|
||||||
|
*child = None;
|
||||||
}
|
}
|
||||||
self.tile_mem = tile.get_mem();
|
self.tile_mem = tile.get_mem();
|
||||||
delta = self.tile_mem as int - old_mem as int;
|
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<T: Tile> QuadtreeNode<T> {
|
||||||
|
|
||||||
let quads_to_check = build_sized(4, builder);
|
let quads_to_check = build_sized(4, builder);
|
||||||
|
|
||||||
let mut ret = ~[];
|
let mut request = ~[];
|
||||||
let mut redisplay = false;
|
let mut unused = ~[];
|
||||||
let mut delta = 0;
|
let mut delta = 0;
|
||||||
|
|
||||||
for quad in quads_to_check.iter() {
|
for quad in quads_to_check.iter() {
|
||||||
|
@ -627,7 +645,7 @@ impl<T: Tile> QuadtreeNode<T> {
|
||||||
|
|
||||||
let override = override || self.status == Invalid;
|
let override = override || self.status == Invalid;
|
||||||
self.status = Normal;
|
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),
|
Some(ref mut child) => child.get_tile_rects(new_window, clip, scale, tile_size, override),
|
||||||
None => {
|
None => {
|
||||||
// Create new child
|
// Create new child
|
||||||
|
@ -648,11 +666,28 @@ impl<T: Tile> QuadtreeNode<T> {
|
||||||
};
|
};
|
||||||
|
|
||||||
delta = delta + c_delta;
|
delta = delta + c_delta;
|
||||||
ret = ret + c_ret;
|
request = request + c_request;
|
||||||
redisplay = redisplay || c_redisplay;
|
unused.push_all_move(c_unused);
|
||||||
}
|
}
|
||||||
self.tile_mem = (self.tile_mem as int + delta) as uint;
|
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
|
/// 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 {
|
fn is_valid(&self, _: f32) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
fn get_size_2d(&self) -> Size2D<uint> {
|
||||||
|
Size2D(0u, 0u)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut q = Quadtree::new(6, 6, 1, None);
|
let mut q = Quadtree::new(6, 6, 1, None);
|
||||||
|
@ -775,6 +813,9 @@ pub fn test() {
|
||||||
fn is_valid(&self, _: f32) -> bool {
|
fn is_valid(&self, _: f32) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
fn get_size_2d(&self) -> Size2D<uint> {
|
||||||
|
Size2D(0u, 0u)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut q = Quadtree::new(8, 8, 2, Some(4));
|
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, 0.5, T{a: 6});
|
||||||
q.add_tile_pixel(0, 0, 1f32, T{a: 7});
|
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);
|
let (_, unused) = q.get_tile_rects_pixel(Rect(Point2D(0, 0), Size2D(2, 2)), 0.5);
|
||||||
assert!(redisplay);
|
assert!(!unused.is_empty());
|
||||||
assert!(q.root.tile_mem == 1);
|
assert!(q.root.tile_mem == 1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,6 @@ use geom::rect::Rect;
|
||||||
use geom::size::Size2D;
|
use geom::size::Size2D;
|
||||||
|
|
||||||
use constellation_msg::PipelineId;
|
use constellation_msg::PipelineId;
|
||||||
use extra::arc;
|
|
||||||
|
|
||||||
|
|
||||||
#[deriving(Clone)]
|
#[deriving(Clone)]
|
||||||
pub struct LayerBuffer {
|
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
|
/// A set of layer buffers. This is an atomic unit used to switch between the front and back
|
||||||
/// buffers.
|
/// buffers.
|
||||||
|
#[deriving(Clone)]
|
||||||
pub struct LayerBufferSet {
|
pub struct LayerBufferSet {
|
||||||
buffers: ~[LayerBuffer]
|
buffers: ~[~LayerBuffer]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The status of the renderer.
|
/// The status of the renderer.
|
||||||
|
@ -72,7 +71,7 @@ pub trait RenderListener {
|
||||||
fn set_layer_page_size(&self, PipelineId, Size2D<uint>, Epoch);
|
fn set_layer_page_size(&self, PipelineId, Size2D<uint>, Epoch);
|
||||||
fn set_layer_clip_rect(&self, PipelineId, Rect<uint>);
|
fn set_layer_clip_rect(&self, PipelineId, Rect<uint>);
|
||||||
fn delete_layer(&self, PipelineId);
|
fn delete_layer(&self, PipelineId);
|
||||||
fn paint(&self, id: PipelineId, layer_buffer_set: arc::Arc<LayerBufferSet>, Epoch);
|
fn paint(&self, id: PipelineId, layer_buffer_set: ~LayerBufferSet, Epoch);
|
||||||
fn set_render_state(&self, render_state: RenderState);
|
fn set_render_state(&self, render_state: RenderState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,6 +88,8 @@ pub trait Tile {
|
||||||
fn get_mem(&self) -> uint;
|
fn get_mem(&self) -> uint;
|
||||||
/// Returns true if the tile is displayable at the given scale
|
/// Returns true if the tile is displayable at the given scale
|
||||||
fn is_valid(&self, f32) -> bool;
|
fn is_valid(&self, f32) -> bool;
|
||||||
|
/// Returns the Size2D of the tile
|
||||||
|
fn get_size_2d(&self) -> Size2D<uint>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tile for ~LayerBuffer {
|
impl Tile for ~LayerBuffer {
|
||||||
|
@ -98,5 +99,8 @@ impl Tile for ~LayerBuffer {
|
||||||
}
|
}
|
||||||
fn is_valid(&self, scale: f32) -> bool {
|
fn is_valid(&self, scale: f32) -> bool {
|
||||||
self.resolution.approx_eq(&scale)
|
self.resolution.approx_eq(&scale)
|
||||||
}
|
}
|
||||||
|
fn get_size_2d(&self) -> Size2D<uint> {
|
||||||
|
self.screen_pos.size
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue