aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authoreschweic <eschweickart@mozilla.com>2013-07-01 15:20:29 -0700
committereschweic <eschweickart@mozilla.com>2013-07-10 17:12:52 -0700
commit6bebda4f26c97c2204d23ce6340962a65cb727ad (patch)
treebf2ddefed3ddc5b44839678c4551e04a6b21ca54 /src
parentad7dc32fc8a52a3f1a93bc02446a0fb58ec9a716 (diff)
downloadservo-6bebda4f26c97c2204d23ce6340962a65cb727ad.tar.gz
servo-6bebda4f26c97c2204d23ce6340962a65cb727ad.zip
Implement progressive rendering
Diffstat (limited to 'src')
-rw-r--r--src/components/gfx/render_task.rs113
-rw-r--r--src/components/main/compositing/mod.rs152
-rw-r--r--src/components/main/compositing/quadtree.rs132
-rw-r--r--src/components/msg/compositor_msg.rs9
4 files changed, 321 insertions, 85 deletions
diff --git a/src/components/gfx/render_task.rs b/src/components/gfx/render_task.rs
index 05cbd0bc079..1fdbc91aa74 100644
--- a/src/components/gfx/render_task.rs
+++ b/src/components/gfx/render_task.rs
@@ -33,7 +33,7 @@ pub struct RenderLayer {
pub enum Msg {
RenderMsg(RenderLayer),
- ReRenderMsg(f32),
+ ReRenderMsg(~Rect<uint>], f32),
PaintPermissionGranted,
PaintPermissionRevoked,
ExitMsg(Chan<()>),
@@ -119,11 +119,11 @@ impl<C: RenderListener + Send> RenderTask<C> {
loop {
match self.port.recv() {
RenderMsg(render_layer) => {
+ self.compositor.new_layer(render_layer.size, self.opts.tile_size);
self.render_layer = Some(render_layer);
- self.render(1.0);
}
- ReRenderMsg(scale) => {
- self.render(scale);
+ ReRenderMsg(tiles, scale) => {
+ self.render(tiles, scale);
}
PaintPermissionGranted => {
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");
let render_layer;
- match (self.render_layer) {
- None => return,
+ match self.render_layer {
Some(ref r_layer) => {
render_layer = r_layer;
}
+ _ => return, // nothing to do
}
self.compositor.set_render_state(RenderingRenderState);
@@ -166,63 +166,56 @@ impl<C: RenderListener + Send> RenderTask<C> {
// Divide up the layer into tiles.
do time::profile(time::RenderingPrepBuffCategory, self.profiler_chan.clone()) {
- let mut y = 0;
- while y < (render_layer.size.height as f32 * scale).ceil() as uint {
- let mut x = 0;
- while x < (render_layer.size.width as f32 * scale).ceil() as uint {
- // Figure out the dimension of this tile.
- 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 screen_rect = Rect(Point2D(x, y), Size2D(width, height));
-
- let buffer = LayerBuffer {
- draw_target: DrawTarget::new_with_fbo(self.opts.render_backend,
- self.share_gl_context,
- Size2D(width as i32,
- height as i32),
- B8G8R8A8),
- rect: tile_rect,
- screen_pos: screen_rect,
- stride: (width * 4) as uint
+ for tiles.each |tile_rect| {
+ let x = tile_rect.origin.x;
+ let y = tile_rect.origin.y;
+ let width = tile_rect.size.width;
+ let height = tile_rect.size.height;
+
+ let rect = Rect(Point2D(x as f32 / scale, y as f32 / scale), Size2D(width as f32, height as f32));
+
+ let buffer = LayerBuffer {
+ draw_target: DrawTarget::new_with_fbo(self.opts.render_backend,
+ self.share_gl_context,
+ Size2D(width as i32, height as i32),
+ B8G8R8A8),
+ rect: rect,
+ screen_pos: *tile_rect,
+ resolution: scale,
+ stride: (width * 4) as uint
+ };
+
+
+ {
+ // Build the render context.
+ let ctx = RenderContext {
+ canvas: &buffer,
+ font_ctx: self.font_ctx,
+ opts: &self.opts
};
-
- {
- // 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.
- 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();
-
- // Draw the display list.
- do profile(time::RenderingDrawingCategory, self.profiler_chan.clone()) {
- render_layer.display_list.draw_into_context(&ctx);
- ctx.canvas.draw_target.flush();
- }
+
+ // Apply the translation to render the tile we want.
+ 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();
+
+ // Draw the display list.
+ 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 {
diff --git a/src/components/main/compositing/mod.rs b/src/components/main/compositing/mod.rs
index 2dc4794a420..efb089e92e6 100644
--- a/src/components/main/compositing/mod.rs
+++ b/src/components/main/compositing/mod.rs
@@ -10,7 +10,7 @@ use windowing::{ApplicationMethods, WindowMethods, WindowMouseEvent, WindowClick
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::constellation_msg::{CompositorAck, ConstellationChan};
use servo_msg::constellation_msg;
@@ -28,6 +28,7 @@ use extra::timer;
use geom::matrix::identity;
use geom::point::Point2D;
use geom::size::Size2D;
+use geom::rect::Rect; //eschweic
use layers::layers::{ARGB32Format, ContainerLayer, ContainerLayerKind, Format};
use layers::layers::{ImageData, WithDataFn};
use layers::layers::{TextureLayerKind, TextureLayer, TextureManager};
@@ -40,6 +41,10 @@ use servo_util::time::ProfilerChan;
use extra::arc;
pub use windowing;
+//eschweic
+use compositing::quadtree::Quadtree;
+mod quadtree;
+
/// The implementation of the layers-based compositor.
#[deriving(Clone)]
pub struct CompositorChan {
@@ -70,6 +75,17 @@ impl RenderListener for CompositorChan {
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) {
self.chan.send(ChangeRenderState(render_state))
}
@@ -102,6 +118,16 @@ pub enum Msg {
GetSize(Chan<Size2D<int>>),
/// Requests the compositors GL context.
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.
Paint(uint, arc::ARC<LayerBufferSet>, Size2D<uint>),
/// Alerts the compositor to the current status of page loading.
@@ -199,9 +225,61 @@ impl CompositorTask {
let local_zoom = @mut 1f32;
// Channel to the current renderer.
// FIXME: This probably shouldn't be stored like this.
+
let render_chan: @mut Option<RenderChan> = @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 layout_chan_clone = layout_chan.clone();
do window.set_navigation_callback |direction| {
@@ -247,18 +325,32 @@ impl CompositorTask {
}
WindowMouseDownEvent(button, layer_mouse_point) => {
event = MouseDownEvent(button, world_mouse_point(layer_mouse_point));
- }
- WindowMouseUpEvent(button, layer_mouse_point) => {
- // rerender layer at new zoom level
- // FIXME: this should happen when the user stops zooming, definitely not here
- match *render_chan {
- Some(ref r_chan) => {
- r_chan.send(ReRenderMsg(*world_zoom));
+ //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 => {} // Nothing to do
+ None => {}
}
+ }
+ WindowMouseUpEvent(button, layer_mouse_point) => {
+
+ //FIXME: this should not be here eschweic
+ ask_for_tiles();
+
+
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>| {
// Handle messages
while port.peek() {
@@ -291,6 +384,21 @@ impl CompositorTask {
}
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) => {
match *pipeline_id {
@@ -300,6 +408,12 @@ impl CompositorTask {
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);
let new_layer_buffer_set = new_layer_buffer_set.get();
@@ -307,12 +421,20 @@ impl CompositorTask {
// Iterate over the children of the container layer.
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 height = buffer.rect.size.height as uint;
debug!("osmain: compositing buffer rect %?", &buffer.rect);
-
+
// Find or create a texture layer.
let texture_layer;
current_layer_child = match current_layer_child {
@@ -339,9 +461,10 @@ impl CompositorTask {
let origin = Point2D(origin.x as f32, origin.y as f32);
// Set the layer's transform.
- let transform = identity().translate(origin.x, origin.y, 0.0);
- let transform = transform.scale(width as f32, height as f32, 1.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 * *world_zoom / buffer.resolution, height as f32 * *world_zoom / buffer.resolution, 1.0);
texture_layer.common.set_transform(transform);
+
}
// Delete leftover layers
@@ -409,6 +532,8 @@ impl CompositorTask {
root_layer.common.set_transform(scroll_transform);
+// ask_for_tiles();
+
*recomposite = true;
}
@@ -447,6 +572,7 @@ impl CompositorTask {
window_size.height as f32 / -2f32,
0.0);
root_layer.common.set_transform(zoom_transform);
+// ask_for_tiles();
*recomposite = true;
}
diff --git a/src/components/main/compositing/quadtree.rs b/src/components/main/compositing/quadtree.rs
index 8c3e426e5b6..e9fc4e65d85 100644
--- a/src/components/main/compositing/quadtree.rs
+++ b/src/components/main/compositing/quadtree.rs
@@ -52,7 +52,11 @@ impl<T> Quadtree<T> {
max_tile_size: tile_size,
}
}
-
+
+ /// 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.
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)
@@ -61,12 +65,24 @@ impl<T> Quadtree<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);
}
-
+ /// 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> {
- // Private method to create new children
+ /// Private method to create new children
fn new_child(x: f32, y: f32, size: f32) -> QuadtreeNode<T> {
QuadtreeNode {
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,
/// 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) {
@@ -115,16 +151,16 @@ impl<T> QuadtreeNode<T> {
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);
- for vec::each([TL, TR, BL, BR]) |quad| {
+ for [TL, TR, BL, BR].each |quad| {
self.quadrants[*quad as int] = None;
}
- } else { //send tile to children
+ } else { // Send tile to children
let quad = self.get_quadrant(x, y);
match self.quadrants[quad as int] {
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_x = match quad {
TL | BL => self.origin.x,
@@ -138,19 +174,93 @@ impl<T> QuadtreeNode<T> {
c.add_tile(x, y, tile, tile_size);
self.quadrants[quad as int] = Some(c);
- // If we have 4 children, we probably shouldn't be hanging onto a tile
- // Though this isn't always true if we have grandchildren
+ // 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(_), Some(_), Some(_), Some(_)] => {
- self.tile = None;
+ [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 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]
diff --git a/src/components/msg/compositor_msg.rs b/src/components/msg/compositor_msg.rs
index d9dd02f2671..0763833357c 100644
--- a/src/components/msg/compositor_msg.rs
+++ b/src/components/msg/compositor_msg.rs
@@ -19,8 +19,12 @@ pub struct LayerBuffer {
// The rect in pixels that will be drawn to the screen.
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.
- stride: uint
+ stride: uint,
+
}
/// 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.
pub trait RenderListener {
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 set_render_state(&self, render_state: RenderState);
}