mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +01:00
Implement progressive rendering
This commit is contained in:
parent
ad7dc32fc8
commit
6bebda4f26
4 changed files with 322 additions and 86 deletions
|
@ -33,7 +33,7 @@ pub struct RenderLayer {
|
||||||
|
|
||||||
pub enum Msg {
|
pub enum Msg {
|
||||||
RenderMsg(RenderLayer),
|
RenderMsg(RenderLayer),
|
||||||
ReRenderMsg(f32),
|
ReRenderMsg(~Rect<uint>], f32),
|
||||||
PaintPermissionGranted,
|
PaintPermissionGranted,
|
||||||
PaintPermissionRevoked,
|
PaintPermissionRevoked,
|
||||||
ExitMsg(Chan<()>),
|
ExitMsg(Chan<()>),
|
||||||
|
@ -119,11 +119,11 @@ impl<C: RenderListener + Send> RenderTask<C> {
|
||||||
loop {
|
loop {
|
||||||
match self.port.recv() {
|
match self.port.recv() {
|
||||||
RenderMsg(render_layer) => {
|
RenderMsg(render_layer) => {
|
||||||
|
self.compositor.new_layer(render_layer.size, self.opts.tile_size);
|
||||||
self.render_layer = Some(render_layer);
|
self.render_layer = Some(render_layer);
|
||||||
self.render(1.0);
|
|
||||||
}
|
}
|
||||||
ReRenderMsg(scale) => {
|
ReRenderMsg(tiles, scale) => {
|
||||||
self.render(scale);
|
self.render(tiles, scale);
|
||||||
}
|
}
|
||||||
PaintPermissionGranted => {
|
PaintPermissionGranted => {
|
||||||
self.paint_permission = true;
|
self.paint_permission = true;
|
||||||
|
@ -146,15 +146,15 @@ impl<C: RenderListener + Send> RenderTask<C> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&mut self, scale: f32) {
|
fn render(&mut self, tiles: ~[Rect<uint>], scale: f32) {
|
||||||
debug!("render_task: rendering");
|
debug!("render_task: rendering");
|
||||||
|
|
||||||
let render_layer;
|
let render_layer;
|
||||||
match (self.render_layer) {
|
match self.render_layer {
|
||||||
None => return,
|
|
||||||
Some(ref r_layer) => {
|
Some(ref r_layer) => {
|
||||||
render_layer = r_layer;
|
render_layer = r_layer;
|
||||||
}
|
}
|
||||||
|
_ => return, // nothing to do
|
||||||
}
|
}
|
||||||
|
|
||||||
self.compositor.set_render_state(RenderingRenderState);
|
self.compositor.set_render_state(RenderingRenderState);
|
||||||
|
@ -166,63 +166,56 @@ impl<C: RenderListener + Send> RenderTask<C> {
|
||||||
|
|
||||||
// Divide up the layer into tiles.
|
// Divide up the layer into tiles.
|
||||||
do time::profile(time::RenderingPrepBuffCategory, self.profiler_chan.clone()) {
|
do time::profile(time::RenderingPrepBuffCategory, self.profiler_chan.clone()) {
|
||||||
let mut y = 0;
|
for tiles.each |tile_rect| {
|
||||||
while y < (render_layer.size.height as f32 * scale).ceil() as uint {
|
let x = tile_rect.origin.x;
|
||||||
let mut x = 0;
|
let y = tile_rect.origin.y;
|
||||||
while x < (render_layer.size.width as f32 * scale).ceil() as uint {
|
let width = tile_rect.size.width;
|
||||||
// Figure out the dimension of this tile.
|
let height = tile_rect.size.height;
|
||||||
let right = uint::min(x + tile_size, (render_layer.size.width as f32 * scale).ceil() as uint);
|
|
||||||
let bottom = uint::min(y + tile_size, (render_layer.size.height as f32 * scale).ceil() as uint);
|
|
||||||
let width = right - x;
|
|
||||||
let height = bottom - y;
|
|
||||||
|
|
||||||
let tile_rect = Rect(Point2D(x as f32 / scale, y as f32 / scale), Size2D(width as f32, height as f32));
|
let rect = Rect(Point2D(x as f32 / scale, y as f32 / scale), Size2D(width as f32, height as f32));
|
||||||
let screen_rect = Rect(Point2D(x, y), Size2D(width, height));
|
|
||||||
|
|
||||||
let buffer = LayerBuffer {
|
let buffer = LayerBuffer {
|
||||||
draw_target: DrawTarget::new_with_fbo(self.opts.render_backend,
|
draw_target: DrawTarget::new_with_fbo(self.opts.render_backend,
|
||||||
self.share_gl_context,
|
self.share_gl_context,
|
||||||
Size2D(width as i32,
|
Size2D(width as i32, height as i32),
|
||||||
height as i32),
|
B8G8R8A8),
|
||||||
B8G8R8A8),
|
rect: rect,
|
||||||
rect: tile_rect,
|
screen_pos: *tile_rect,
|
||||||
screen_pos: screen_rect,
|
resolution: scale,
|
||||||
stride: (width * 4) as uint
|
stride: (width * 4) as uint
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
// Build the render context.
|
||||||
|
let ctx = RenderContext {
|
||||||
|
canvas: &buffer,
|
||||||
|
font_ctx: self.font_ctx,
|
||||||
|
opts: &self.opts
|
||||||
};
|
};
|
||||||
|
|
||||||
{
|
// Apply the translation to render the tile we want.
|
||||||
// Build the render context.
|
let matrix: Matrix2D<AzFloat> = Matrix2D::identity();
|
||||||
let ctx = RenderContext {
|
let matrix = matrix.scale(scale as AzFloat, scale as AzFloat);
|
||||||
canvas: &buffer,
|
let matrix = matrix.translate(-(buffer.rect.origin.x) as AzFloat,
|
||||||
font_ctx: self.font_ctx,
|
-(buffer.rect.origin.y) as AzFloat);
|
||||||
opts: &self.opts
|
|
||||||
};
|
|
||||||
|
|
||||||
// Apply the translation to render the tile we want.
|
ctx.canvas.draw_target.set_transform(&matrix);
|
||||||
let matrix: Matrix2D<AzFloat> = Matrix2D::identity();
|
|
||||||
let matrix = matrix.scale(scale as AzFloat, scale as AzFloat);
|
|
||||||
let matrix = matrix.translate(-(buffer.rect.origin.x) as AzFloat,
|
|
||||||
-(buffer.rect.origin.y) as AzFloat);
|
|
||||||
|
|
||||||
ctx.canvas.draw_target.set_transform(&matrix);
|
// Clear the buffer.
|
||||||
|
ctx.clear();
|
||||||
|
|
||||||
// Clear the buffer.
|
// Draw the display list.
|
||||||
ctx.clear();
|
do profile(time::RenderingDrawingCategory, self.profiler_chan.clone()) {
|
||||||
|
render_layer.display_list.draw_into_context(&ctx);
|
||||||
// Draw the display list.
|
ctx.canvas.draw_target.flush();
|
||||||
do profile(time::RenderingDrawingCategory, self.profiler_chan.clone()) {
|
|
||||||
render_layer.display_list.draw_into_context(&ctx);
|
|
||||||
ctx.canvas.draw_target.flush();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
new_buffers.push(buffer);
|
|
||||||
|
|
||||||
x += tile_size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
y += tile_size;
|
new_buffers.push(buffer);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let layer_buffer_set = LayerBufferSet {
|
let layer_buffer_set = LayerBufferSet {
|
||||||
|
|
|
@ -10,7 +10,7 @@ use windowing::{ApplicationMethods, WindowMethods, WindowMouseEvent, WindowClick
|
||||||
use windowing::{WindowMouseDownEvent, WindowMouseUpEvent};
|
use windowing::{WindowMouseDownEvent, WindowMouseUpEvent};
|
||||||
|
|
||||||
|
|
||||||
use servo_msg::compositor_msg::{RenderListener, LayerBufferSet, RenderState};
|
use servo_msg::compositor_msg::{RenderListener, LayerBuffer, LayerBufferSet, RenderState};
|
||||||
use servo_msg::compositor_msg::{ReadyState, ScriptListener};
|
use servo_msg::compositor_msg::{ReadyState, ScriptListener};
|
||||||
use servo_msg::constellation_msg::{CompositorAck, ConstellationChan};
|
use servo_msg::constellation_msg::{CompositorAck, ConstellationChan};
|
||||||
use servo_msg::constellation_msg;
|
use servo_msg::constellation_msg;
|
||||||
|
@ -28,6 +28,7 @@ use extra::timer;
|
||||||
use geom::matrix::identity;
|
use geom::matrix::identity;
|
||||||
use geom::point::Point2D;
|
use geom::point::Point2D;
|
||||||
use geom::size::Size2D;
|
use geom::size::Size2D;
|
||||||
|
use geom::rect::Rect; //eschweic
|
||||||
use layers::layers::{ARGB32Format, ContainerLayer, ContainerLayerKind, Format};
|
use layers::layers::{ARGB32Format, ContainerLayer, ContainerLayerKind, Format};
|
||||||
use layers::layers::{ImageData, WithDataFn};
|
use layers::layers::{ImageData, WithDataFn};
|
||||||
use layers::layers::{TextureLayerKind, TextureLayer, TextureManager};
|
use layers::layers::{TextureLayerKind, TextureLayer, TextureManager};
|
||||||
|
@ -40,6 +41,10 @@ use servo_util::time::ProfilerChan;
|
||||||
use extra::arc;
|
use extra::arc;
|
||||||
pub use windowing;
|
pub use windowing;
|
||||||
|
|
||||||
|
//eschweic
|
||||||
|
use compositing::quadtree::Quadtree;
|
||||||
|
mod quadtree;
|
||||||
|
|
||||||
/// The implementation of the layers-based compositor.
|
/// The implementation of the layers-based compositor.
|
||||||
#[deriving(Clone)]
|
#[deriving(Clone)]
|
||||||
pub struct CompositorChan {
|
pub struct CompositorChan {
|
||||||
|
@ -70,6 +75,17 @@ impl RenderListener for CompositorChan {
|
||||||
self.chan.send(Paint(id, layer_buffer_set, new_size))
|
self.chan.send(Paint(id, layer_buffer_set, new_size))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//eschweic
|
||||||
|
fn new_layer(&self, page_size: Size2D<uint>, tile_size: uint) {
|
||||||
|
self.chan.send(NewLayer(page_size, tile_size))
|
||||||
|
}
|
||||||
|
fn resize_layer(&self, page_size: Size2D<uint>) {
|
||||||
|
self.chan.send(ResizeLayer(page_size))
|
||||||
|
}
|
||||||
|
fn delete_layer(&self) {
|
||||||
|
self.chan.send(DeleteLayer)
|
||||||
|
}
|
||||||
|
|
||||||
fn set_render_state(&self, render_state: RenderState) {
|
fn set_render_state(&self, render_state: RenderState) {
|
||||||
self.chan.send(ChangeRenderState(render_state))
|
self.chan.send(ChangeRenderState(render_state))
|
||||||
}
|
}
|
||||||
|
@ -102,6 +118,16 @@ pub enum Msg {
|
||||||
GetSize(Chan<Size2D<int>>),
|
GetSize(Chan<Size2D<int>>),
|
||||||
/// Requests the compositors GL context.
|
/// Requests the compositors GL context.
|
||||||
GetGLContext(Chan<AzGLContext>),
|
GetGLContext(Chan<AzGLContext>),
|
||||||
|
|
||||||
|
//eschweic
|
||||||
|
// FIXME: Attach layer ids and epochs to these messages
|
||||||
|
/// Alerts the compositor that there is a new layer to be rendered.
|
||||||
|
NewLayer(Size2D<uint>, uint),
|
||||||
|
/// Alerts the compositor that the current layer has changed size.
|
||||||
|
ResizeLayer(Size2D<uint>),
|
||||||
|
/// Alerts the compositor that the current layer has been deleted.
|
||||||
|
DeleteLayer,
|
||||||
|
|
||||||
/// 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(uint, arc::ARC<LayerBufferSet>, Size2D<uint>),
|
Paint(uint, arc::ARC<LayerBufferSet>, Size2D<uint>),
|
||||||
/// Alerts the compositor to the current status of page loading.
|
/// Alerts the compositor to the current status of page loading.
|
||||||
|
@ -199,9 +225,61 @@ impl CompositorTask {
|
||||||
let local_zoom = @mut 1f32;
|
let local_zoom = @mut 1f32;
|
||||||
// Channel to the current renderer.
|
// Channel to the current renderer.
|
||||||
// FIXME: This probably shouldn't be stored like this.
|
// FIXME: This probably shouldn't be stored like this.
|
||||||
|
|
||||||
let render_chan: @mut Option<RenderChan> = @mut None;
|
let render_chan: @mut Option<RenderChan> = @mut None;
|
||||||
let pipeline_id: @mut Option<uint> = @mut None;
|
let pipeline_id: @mut Option<uint> = @mut None;
|
||||||
|
|
||||||
|
// Quadtree for this layer
|
||||||
|
// FIXME: This should be one-per-layer
|
||||||
|
let quadtree: @mut Option<Quadtree<LayerBuffer>> = @mut None;
|
||||||
|
|
||||||
|
|
||||||
|
let ask_for_tiles: @fn() = || {
|
||||||
|
match *quadtree {
|
||||||
|
Some(ref quad) => {
|
||||||
|
let mut tile_size = quad.get_tile_size(); // temporary solution
|
||||||
|
let mut tile_request = ~[]; //FIXME: try not to allocate if possible
|
||||||
|
|
||||||
|
let mut y = world_offset.y as uint;
|
||||||
|
while y < world_offset.y as uint + window_size.height + tile_size {
|
||||||
|
let mut x = world_offset.x as uint;
|
||||||
|
while x < world_offset.x as uint + window_size.width + tile_size {
|
||||||
|
match *(quad.get_tile(x, y, *world_zoom)) {
|
||||||
|
Some(ref current_tile) => {
|
||||||
|
if current_tile.resolution == *world_zoom {
|
||||||
|
x += tile_size;
|
||||||
|
loop; // we already have this tile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {} // fall through
|
||||||
|
}
|
||||||
|
let (tile_pos, new_tile_size) = quad.get_tile_rect(x, y, *world_zoom);
|
||||||
|
tile_size = new_tile_size;
|
||||||
|
x = tile_pos.x;
|
||||||
|
y = tile_pos.y;
|
||||||
|
|
||||||
|
// TODO: clamp tiles to page bounds
|
||||||
|
// TODO: add null buffer/checkerboard tile to stop a flood of requests
|
||||||
|
println(fmt!("requesting tile: (%?, %?): %?", x, y, tile_size));
|
||||||
|
tile_request.push(Rect(Point2D(x, y), Size2D(tile_size, tile_size)));
|
||||||
|
|
||||||
|
x += tile_size;
|
||||||
|
}
|
||||||
|
y += tile_size;
|
||||||
|
}
|
||||||
|
if !tile_request.is_empty() {
|
||||||
|
match *render_chan {
|
||||||
|
Some(ref chan) => {
|
||||||
|
chan.send(ReRenderMsg(tile_request, *world_zoom));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let update_layout_callbacks: @fn(LayoutChan) = |layout_chan: LayoutChan| {
|
let update_layout_callbacks: @fn(LayoutChan) = |layout_chan: LayoutChan| {
|
||||||
let layout_chan_clone = layout_chan.clone();
|
let layout_chan_clone = layout_chan.clone();
|
||||||
do window.set_navigation_callback |direction| {
|
do window.set_navigation_callback |direction| {
|
||||||
|
@ -247,17 +325,31 @@ impl CompositorTask {
|
||||||
}
|
}
|
||||||
WindowMouseDownEvent(button, layer_mouse_point) => {
|
WindowMouseDownEvent(button, layer_mouse_point) => {
|
||||||
event = MouseDownEvent(button, world_mouse_point(layer_mouse_point));
|
event = MouseDownEvent(button, world_mouse_point(layer_mouse_point));
|
||||||
|
|
||||||
|
//eschweic
|
||||||
|
match *quadtree {
|
||||||
|
Some(ref quad) => {
|
||||||
|
|
||||||
|
/* let wmp = world_mouse_point(layer_mouse_point);
|
||||||
|
println(fmt!("mouse: (%?, %?):", wmp.x as uint, wmp.y as uint));
|
||||||
|
let buffer = quad.get_tile(wmp.x as uint, wmp.y as uint, *world_zoom);
|
||||||
|
match *buffer {
|
||||||
|
None => println("None"),
|
||||||
|
Some(ref buffer) => println(fmt!("Some: (%?, %?), %?, %?", buffer.screen_pos.origin.x, buffer.screen_pos.origin.y, buffer.screen_pos.size.width, buffer.resolution)),
|
||||||
|
|
||||||
|
} */
|
||||||
|
|
||||||
|
println(quad.get_html());
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
WindowMouseUpEvent(button, layer_mouse_point) => {
|
WindowMouseUpEvent(button, layer_mouse_point) => {
|
||||||
|
|
||||||
// rerender layer at new zoom level
|
//FIXME: this should not be here eschweic
|
||||||
// FIXME: this should happen when the user stops zooming, definitely not here
|
ask_for_tiles();
|
||||||
match *render_chan {
|
|
||||||
Some(ref r_chan) => {
|
|
||||||
r_chan.send(ReRenderMsg(*world_zoom));
|
|
||||||
}
|
|
||||||
None => {} // Nothing to do
|
|
||||||
}
|
|
||||||
|
|
||||||
event = MouseUpEvent(button, world_mouse_point(layer_mouse_point));
|
event = MouseUpEvent(button, world_mouse_point(layer_mouse_point));
|
||||||
}
|
}
|
||||||
|
@ -266,6 +358,7 @@ impl CompositorTask {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
let check_for_messages: @fn(&Port<Msg>) = |port: &Port<Msg>| {
|
let check_for_messages: @fn(&Port<Msg>) = |port: &Port<Msg>| {
|
||||||
// Handle messages
|
// Handle messages
|
||||||
while port.peek() {
|
while port.peek() {
|
||||||
|
@ -292,6 +385,21 @@ impl CompositorTask {
|
||||||
|
|
||||||
GetGLContext(chan) => chan.send(current_gl_context()),
|
GetGLContext(chan) => chan.send(current_gl_context()),
|
||||||
|
|
||||||
|
//eschweic
|
||||||
|
NewLayer(new_size, tile_size) => {
|
||||||
|
*page_size = Size2D(new_size.width as f32, new_size.height as f32);
|
||||||
|
*quadtree = Some(Quadtree::new(0, 0, new_size.width, new_size.height, tile_size));
|
||||||
|
ask_for_tiles();
|
||||||
|
|
||||||
|
}
|
||||||
|
ResizeLayer(new_size) => {
|
||||||
|
*page_size = Size2D(new_size.width as f32, new_size.height as f32);
|
||||||
|
// TODO: update quadtree, ask for tiles
|
||||||
|
}
|
||||||
|
DeleteLayer => {
|
||||||
|
// TODO: create secondary layer tree, keep displaying until new tiles come in
|
||||||
|
}
|
||||||
|
|
||||||
Paint(id, new_layer_buffer_set, new_size) => {
|
Paint(id, new_layer_buffer_set, new_size) => {
|
||||||
match *pipeline_id {
|
match *pipeline_id {
|
||||||
Some(pipeline_id) => if id != pipeline_id { loop; },
|
Some(pipeline_id) => if id != pipeline_id { loop; },
|
||||||
|
@ -300,6 +408,12 @@ impl CompositorTask {
|
||||||
|
|
||||||
debug!("osmain: received new frame");
|
debug!("osmain: received new frame");
|
||||||
|
|
||||||
|
let quad;
|
||||||
|
match *quadtree {
|
||||||
|
Some(ref mut q) => quad = q,
|
||||||
|
None => fail!("Compositor: given paint command with no quadtree initialized"),
|
||||||
|
}
|
||||||
|
|
||||||
*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);
|
||||||
|
|
||||||
let new_layer_buffer_set = new_layer_buffer_set.get();
|
let new_layer_buffer_set = new_layer_buffer_set.get();
|
||||||
|
@ -307,7 +421,15 @@ impl CompositorTask {
|
||||||
// Iterate over the children of the container layer.
|
// Iterate over the children of the container layer.
|
||||||
let mut current_layer_child = root_layer.first_child;
|
let mut current_layer_child = root_layer.first_child;
|
||||||
|
|
||||||
for new_layer_buffer_set.buffers.iter().advance |buffer| {
|
// Replace the image layer data with the buffer data.
|
||||||
|
let buffers = util::replace(&mut new_layer_buffer_set.buffers, ~[]);
|
||||||
|
|
||||||
|
do vec::consume(buffers) |_, buffer| {
|
||||||
|
quad.add_tile(buffer.screen_pos.origin.x, buffer.screen_pos.origin.y,
|
||||||
|
*world_zoom, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
for quad.get_all_tiles().each |buffer| {
|
||||||
let width = buffer.rect.size.width as uint;
|
let width = buffer.rect.size.width as uint;
|
||||||
let height = buffer.rect.size.height as uint;
|
let height = buffer.rect.size.height as uint;
|
||||||
|
|
||||||
|
@ -339,9 +461,10 @@ impl CompositorTask {
|
||||||
let origin = Point2D(origin.x as f32, origin.y as f32);
|
let origin = Point2D(origin.x as f32, origin.y as f32);
|
||||||
|
|
||||||
// Set the layer's transform.
|
// Set the layer's transform.
|
||||||
let transform = identity().translate(origin.x, origin.y, 0.0);
|
let transform = identity().translate(origin.x * *world_zoom / buffer.resolution, origin.y * *world_zoom / buffer.resolution, 0.0);
|
||||||
let transform = transform.scale(width as f32, height as f32, 1.0);
|
let transform = transform.scale(width as f32 * *world_zoom / buffer.resolution, height as f32 * *world_zoom / buffer.resolution, 1.0);
|
||||||
texture_layer.common.set_transform(transform);
|
texture_layer.common.set_transform(transform);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete leftover layers
|
// Delete leftover layers
|
||||||
|
@ -409,6 +532,8 @@ impl CompositorTask {
|
||||||
|
|
||||||
root_layer.common.set_transform(scroll_transform);
|
root_layer.common.set_transform(scroll_transform);
|
||||||
|
|
||||||
|
// ask_for_tiles();
|
||||||
|
|
||||||
*recomposite = true;
|
*recomposite = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -447,6 +572,7 @@ impl CompositorTask {
|
||||||
window_size.height as f32 / -2f32,
|
window_size.height as f32 / -2f32,
|
||||||
0.0);
|
0.0);
|
||||||
root_layer.common.set_transform(zoom_transform);
|
root_layer.common.set_transform(zoom_transform);
|
||||||
|
// ask_for_tiles();
|
||||||
|
|
||||||
*recomposite = true;
|
*recomposite = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,10 @@ impl<T> Quadtree<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the maximum allowed tile size
|
||||||
|
pub fn get_tile_size(&self) -> uint {
|
||||||
|
self.max_tile_size
|
||||||
|
}
|
||||||
/// Get a tile at a given pixel position and scale.
|
/// Get a tile at a given pixel position and scale.
|
||||||
pub fn get_tile<'r>(&'r self, x: uint, y: uint, scale: f32) -> &'r Option<T> {
|
pub fn get_tile<'r>(&'r self, x: uint, y: uint, scale: f32) -> &'r Option<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)
|
||||||
|
@ -61,12 +65,24 @@ impl<T> Quadtree<T> {
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
/// Get the tile size/offset for a given pixel position
|
||||||
|
pub fn get_tile_rect(&self, x: uint, y: uint, scale: f32) -> (Point2D<uint>, uint) {
|
||||||
|
self.root.get_tile_rect(x as f32 / scale, y as f32 / scale, scale, self.max_tile_size as f32 / scale)
|
||||||
|
}
|
||||||
|
/// Get all the tiles in the tree
|
||||||
|
pub fn get_all_tiles<'r>(&'r self) -> ~[&'r T] {
|
||||||
|
self.root.get_all_tiles()
|
||||||
|
}
|
||||||
|
/// Generate html to visualize the tree
|
||||||
|
pub fn get_html(&self) -> ~str {
|
||||||
|
let header = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"> <html xmlns=\"http://www.w3.org/1999/xhtml\">";
|
||||||
|
fmt!("%s<body>%s</body></html>", header, self.root.get_html())
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> QuadtreeNode<T> {
|
impl<T> 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 {
|
||||||
tile: None,
|
tile: None,
|
||||||
|
@ -105,6 +121,26 @@ impl<T> QuadtreeNode<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get all tiles in the tree, parents first.
|
||||||
|
/// FIXME: this could probably be more efficient
|
||||||
|
fn get_all_tiles<'r>(&'r self) -> ~[&'r T] {
|
||||||
|
let mut ret = ~[];
|
||||||
|
|
||||||
|
match self.tile {
|
||||||
|
Some (ref tile) => ret = ~[tile],
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
for self.quadrants.each |quad| {
|
||||||
|
match *quad {
|
||||||
|
Some(ref child) => ret = ret + child.get_all_tiles(),
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/// 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) {
|
fn add_tile(&mut self, x: f32, y: f32, tile: T, tile_size: f32) {
|
||||||
|
@ -115,16 +151,16 @@ impl<T> QuadtreeNode<T> {
|
||||||
fail!("Quadtree: Tried to add tile to invalid region");
|
fail!("Quadtree: Tried to add tile to invalid region");
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.size <= tile_size { // We are the child
|
if self.size <= tile_size { // We are the child
|
||||||
self.tile = Some(tile);
|
self.tile = Some(tile);
|
||||||
for vec::each([TL, TR, BL, BR]) |quad| {
|
for [TL, TR, BL, BR].each |quad| {
|
||||||
self.quadrants[*quad as int] = None;
|
self.quadrants[*quad as int] = None;
|
||||||
}
|
}
|
||||||
} 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) => child.add_tile(x, y, tile, tile_size),
|
||||||
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 {
|
||||||
TL | BL => self.origin.x,
|
TL | BL => self.origin.x,
|
||||||
|
@ -138,19 +174,93 @@ impl<T> QuadtreeNode<T> {
|
||||||
c.add_tile(x, y, tile, tile_size);
|
c.add_tile(x, y, tile, tile_size);
|
||||||
self.quadrants[quad as int] = Some(c);
|
self.quadrants[quad as int] = Some(c);
|
||||||
|
|
||||||
// If we have 4 children, we probably shouldn't be hanging onto a tile
|
// If my tile is completely occluded, get rid of it.
|
||||||
// Though this isn't always true if we have grandchildren
|
// 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 {
|
match self.quadrants {
|
||||||
[Some(_), Some(_), Some(_), Some(_)] => {
|
[Some(ref tl_child), Some(ref tr_child), Some(ref bl_child), Some(ref br_child)] => {
|
||||||
self.tile = None;
|
match (&tl_child.tile, &tr_child.tile, &bl_child.tile, &br_child.tile) {
|
||||||
|
(&Some(_), &Some(_), &Some(_), &Some(_)) => self.tile = None,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get an origin and a width/height for a future tile for a given position in page coords
|
||||||
|
fn get_tile_rect(&self, x: f32, y: f32, scale: f32, tile_size: f32) -> (Point2D<uint>, uint) {
|
||||||
|
if x >= self.origin.x + self.size || x < self.origin.x
|
||||||
|
|| y >= self.origin.y + self.size || y < self.origin.y {
|
||||||
|
fail!("Quadtree: Tried to query a tile rect outside of range");
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.size <= tile_size {
|
||||||
|
let self_x = (self.origin.x * scale).ceil() as uint;
|
||||||
|
let self_y = (self.origin.y * scale).ceil() as uint;
|
||||||
|
let self_size = (self.size * scale).ceil() as uint;
|
||||||
|
return (Point2D(self_x, self_y), self_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
let index = self.get_quadrant(x,y) as int;
|
||||||
|
match self.quadrants[index] {
|
||||||
|
None => {
|
||||||
|
// calculate where the new tile should go
|
||||||
|
let factor = self.size / tile_size;
|
||||||
|
let divisor = uint::next_power_of_two(factor.ceil() as uint);
|
||||||
|
let new_size_page = self.size / (divisor as f32);
|
||||||
|
let new_size_pixel = (new_size_page * scale).ceil() as uint;
|
||||||
|
|
||||||
|
let new_x_page = self.origin.x + new_size_page * ((x - self.origin.x) / new_size_page).floor();
|
||||||
|
let new_y_page = self.origin.y + new_size_page * ((y - self.origin.y) / new_size_page).floor();
|
||||||
|
let new_x_pixel = (new_x_page * scale).ceil() as uint;
|
||||||
|
let new_y_pixel = (new_y_page * scale).ceil() as uint;
|
||||||
|
|
||||||
|
(Point2D(new_x_pixel, new_y_pixel), new_size_pixel)
|
||||||
|
}
|
||||||
|
Some(ref child) => child.get_tile_rect(x, y, scale, tile_size),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate html to visualize the tree.
|
||||||
|
/// This is really inefficient, but it's for testing only.
|
||||||
|
fn get_html(&self) -> ~str {
|
||||||
|
let mut ret = ~"";
|
||||||
|
match self.tile {
|
||||||
|
Some(ref tile) => {
|
||||||
|
ret = fmt!("%s%?", ret, tile);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
ret = fmt!("%sNO TILE", ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match self.quadrants {
|
||||||
|
[None, None, None, None] => {}
|
||||||
|
_ => {
|
||||||
|
ret = fmt!("%s<table border=1><tr>", ret);
|
||||||
|
for [TL, TR, BL, BR].each |quad| {
|
||||||
|
match self.quadrants[*quad as int] {
|
||||||
|
Some(ref child) => {
|
||||||
|
ret = fmt!("%s<td>%s</td>", ret, child.get_html());
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
ret = fmt!("%s<td>EMPTY CHILD</td>", ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match *quad {
|
||||||
|
TR => ret = fmt!("%s</tr><tr>", ret),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret = fmt!("%s</table>\n", ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -19,8 +19,12 @@ pub struct LayerBuffer {
|
||||||
// The rect in pixels that will be drawn to the screen.
|
// The rect in pixels that will be drawn to the screen.
|
||||||
screen_pos: Rect<uint>,
|
screen_pos: Rect<uint>,
|
||||||
|
|
||||||
|
// The scale at which this tile is rendered
|
||||||
|
resolution: f32, //eschweic
|
||||||
|
|
||||||
// NB: stride is in pixels, like OpenGL GL_UNPACK_ROW_LENGTH.
|
// NB: stride is in pixels, like OpenGL GL_UNPACK_ROW_LENGTH.
|
||||||
stride: uint
|
stride: uint,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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
|
||||||
|
@ -49,6 +53,9 @@ pub enum ReadyState {
|
||||||
/// submit them to be drawn to the display.
|
/// submit them to be drawn to the display.
|
||||||
pub trait RenderListener {
|
pub trait RenderListener {
|
||||||
fn get_gl_context(&self) -> AzGLContext;
|
fn get_gl_context(&self) -> AzGLContext;
|
||||||
|
fn new_layer(&self, Size2D<uint>, uint); //eschweic
|
||||||
|
fn resize_layer(&self, Size2D<uint>);
|
||||||
|
fn delete_layer(&self);
|
||||||
fn paint(&self, id: uint, layer_buffer_set: arc::ARC<LayerBufferSet>, new_size: Size2D<uint>);
|
fn paint(&self, id: uint, layer_buffer_set: arc::ARC<LayerBufferSet>, new_size: Size2D<uint>);
|
||||||
fn set_render_state(&self, render_state: RenderState);
|
fn set_render_state(&self, render_state: RenderState);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue