Implement epochs; fix integration bugs

This commit is contained in:
eschweic 2013-08-08 20:49:58 -07:00 committed by Tim Kuehn
parent f2c00f7e28
commit eb6973c7dc
9 changed files with 114 additions and 48 deletions

View file

@ -32,7 +32,7 @@ pub struct RenderLayer {
pub enum Msg {
RenderMsg(RenderLayer),
ReRenderMsg(~[BufferRequest], f32, PipelineId),
ReRenderMsg(~[BufferRequest], f32, PipelineId, uint),
PaintPermissionGranted,
PaintPermissionRevoked,
ExitMsg(Chan<()>),
@ -89,6 +89,8 @@ struct RenderTask<C> {
paint_permission: bool,
/// Cached copy of last layers rendered
last_paint_msg: Option<(arc::Arc<LayerBufferSet>, Size2D<uint>)>,
/// A counter for epoch messages
epoch_counter: uint,
}
impl<C: RenderListener + Send> RenderTask<C> {
@ -123,6 +125,7 @@ impl<C: RenderListener + Send> RenderTask<C> {
paint_permission: false,
last_paint_msg: None,
epoch_counter: 0,
};
render_task.start();
@ -136,18 +139,24 @@ impl<C: RenderListener + Send> RenderTask<C> {
match self.port.recv() {
RenderMsg(render_layer) => {
if self.paint_permission {
self.compositor.set_layer_page_size(self.id, render_layer.size);
self.epoch_counter += 1;
self.compositor.set_layer_page_size(self.id, render_layer.size, self.epoch_counter);
}
self.render_layer = Some(render_layer);
}
ReRenderMsg(tiles, scale, id) => {
ReRenderMsg(tiles, scale, id, epoch) => {
if self.epoch_counter == epoch {
self.render(tiles, scale, id);
} else {
debug!("renderer epoch mismatch: %? != %?", self.epoch_counter, epoch);
}
}
PaintPermissionGranted => {
self.paint_permission = true;
match self.render_layer {
Some(ref render_layer) => {
self.compositor.set_layer_page_size(self.id, render_layer.size);
self.epoch_counter += 1;
self.compositor.set_layer_page_size(self.id, render_layer.size, self.epoch_counter);
}
None => {}
}
@ -235,7 +244,7 @@ impl<C: RenderListener + Send> RenderTask<C> {
debug!("render_task: returning surface");
if self.paint_permission {
self.compositor.paint(id, layer_buffer_set.clone());
self.compositor.paint(id, layer_buffer_set.clone(), self.epoch_counter);
}
debug!("caching paint msg");
self.last_paint_msg = Some((layer_buffer_set, render_layer.size));

View file

@ -43,6 +43,9 @@ pub struct CompositorLayer {
/// When set to true, this layer is ignored by its parents. This is useful for
/// soft deletion or when waiting on a page size.
hidden: bool,
/// A monotonically increasing counter that keeps track of the current epoch.
/// add_buffer() calls that don't match the current epoch will be ignored.
epoch: uint,
}
/// Helper struct for keeping CompositorLayer children organized.
@ -79,7 +82,8 @@ impl CompositorLayer {
max_mem)),
},
root_layer: @mut ContainerLayer(),
hidden: true,
hidden: page_size.is_none(),
epoch: 0,
}
}
@ -89,20 +93,38 @@ impl CompositorLayer {
max_mem: Option<uint>) -> CompositorLayer {
let SendableFrameTree { pipeline, children } = frame_tree;
let mut layer = CompositorLayer::new(pipeline, None, tile_size, max_mem);
layer.children = (do children.consume_iter().transform |child| {
layer.children = (do children.move_iter().map |child| {
let SendableChildFrameTree { frame_tree, rect } = child;
let container = @mut ContainerLayer();
container.scissor = rect;
match rect {
Some(rect) => {
container.scissor = Some(Rect(Point2D(100f32, 200f32), Size2D(700f32, 800f32)));
container.common.transform = identity().translate(100f32, 200f32, 0f32);
// FIXME: The top two lines are temporary until layout window sizes are fixed.
// When they are, uncomment the next 2 lines:
// container.scissor = Some(rect);
// container.common.transform = identity().translate(rect.origin.x,
// rect.origin.y,
// 0f32);
}
None => {}
};
let child_layer = ~CompositorLayer::from_frame_tree(frame_tree, tile_size, max_mem);
container.add_child(ContainerLayerKind(child_layer.root_layer));
CompositorLayerChild {
child: ~CompositorLayer::from_frame_tree(frame_tree,
tile_size,
max_mem),
child: child_layer,
container: container,
}
}).collect();
layer
}
// Move the layer by as relative specified amount in page coordinates. Does not change
// the position of the layer relative to its parent. This also takes in a cursor position
// to see if the mouse is over child layers first. If a layer successfully scrolled, returns
@ -195,7 +217,7 @@ impl CompositorLayer {
let (request, r) = quadtree.get_tile_rects_page(rect, scale);
redisplay = r; // workaround to make redisplay visible outside block
if !request.is_empty() {
self.pipeline.render_chan.send(ReRenderMsg(request, scale, self.pipeline.id.clone()));
self.pipeline.render_chan.send(ReRenderMsg(request, scale, self.pipeline.id.clone(), self.epoch));
}
}
if redisplay {
@ -230,7 +252,7 @@ impl CompositorLayer {
// If the layer is hidden and has a defined page size, unhide it.
// This method returns false if the specified layer is not found.
pub fn set_clipping_rect(&mut self, pipeline_id: PipelineId, new_rect: Rect<f32>) -> bool {
for child_node in self.children.iter() {
for child_node in self.children.mut_iter() {
if pipeline_id != child_node.child.pipeline.id {
loop;
}
@ -254,8 +276,9 @@ impl CompositorLayer {
// Set the layer's page size. This signals that the renderer is ready for BufferRequests.
// If the layer is hidden and has a defined clipping rect, unhide it.
// This method returns false if the specified layer is not found.
pub fn resize(&mut self, pipeline_id: PipelineId, new_size: Size2D<f32>, window_size: Size2D<f32>) -> bool {
pub fn resize(&mut self, pipeline_id: PipelineId, new_size: Size2D<f32>, window_size: Size2D<f32>, epoch: uint) -> bool {
if self.pipeline.id == pipeline_id {
self.epoch = epoch;
self.page_size = Some(new_size);
// TODO: might get buffers back here
match self.quadtree {
@ -271,16 +294,17 @@ impl CompositorLayer {
self.hidden = false;
return true;
}
self.resize_helper(pipeline_id, new_size)
self.resize_helper(pipeline_id, new_size, epoch)
}
// A helper method to resize sublayers.
fn resize_helper(&mut self, pipeline_id: PipelineId, new_size: Size2D<f32>) -> bool {
for self.children.mut_iter().advance |child_node| {
fn resize_helper(&mut self, pipeline_id: PipelineId, new_size: Size2D<f32>, epoch: uint) -> bool {
for child_node in self.children.mut_iter() {
if pipeline_id != child_node.child.pipeline.id {
loop;
}
let child = &mut child_node.child;
child.epoch = epoch;
child.page_size = Some(new_size);
// TODO: might get buffers back here
match child.quadtree {
@ -303,7 +327,7 @@ impl CompositorLayer {
}
// ID does not match ours, so recurse on descendents (including hidden children)
self.children.mut_iter().transform(|x| &mut x.child).any(|x| x.resize_helper(pipeline_id, new_size))
self.children.mut_iter().map(|x| &mut x.child).any(|x| x.resize_helper(pipeline_id, new_size, epoch))
}
// Collect buffers from the quadtree. This method IS NOT recursive, so child CompositorLayers
@ -381,8 +405,14 @@ impl CompositorLayer {
}
// Add LayerBuffers to the specified layer. Returns false if the layer is not found.
pub fn add_buffers(&mut self, pipeline_id: PipelineId, new_buffers: &LayerBufferSet) -> bool {
// 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: uint) -> bool {
if self.pipeline.id == pipeline_id {
if self.epoch != epoch {
debug!("compositor epoch mismatch: %? != %?, id: %?", self.epoch, epoch, self.pipeline.id);
// TODO: send buffers back
return true;
}
{ // block here to prevent double mutable borrow of self
let quadtree = match self.quadtree {
NoTree(_, _) => fail!("CompositorLayer: cannot get buffer request for %?,
@ -400,7 +430,7 @@ impl CompositorLayer {
return true;
}
// ID does not match ours, so recurse on descendents (including hidden children).
self.children.mut_iter().map(|x| &mut x.child).any(|x| x.add_buffers(pipeline_id, new_buffers))
self.children.mut_iter().map(|x| &mut x.child).any(|x| x.add_buffers(pipeline_id, new_buffers, epoch))
}
// Deletes a specified sublayer, including hidden children. Returns false if the layer is not found.

View file

@ -79,17 +79,17 @@ impl RenderListener for CompositorChan {
port.recv()
}
fn paint(&self, id: PipelineId, layer_buffer_set: arc::Arc<LayerBufferSet>) {
self.chan.send(Paint(id, layer_buffer_set))
fn paint(&self, id: PipelineId, layer_buffer_set: arc::Arc<LayerBufferSet>, epoch: uint) {
self.chan.send(Paint(id, layer_buffer_set, epoch))
}
fn new_layer(&self, id: PipelineId, page_size: Size2D<uint>) {
let Size2D { width, height } = page_size;
self.chan.send(NewLayer(id, Size2D(width as f32, height as f32)))
}
fn set_layer_page_size(&self, id: PipelineId, page_size: Size2D<uint>) {
fn set_layer_page_size(&self, id: PipelineId, page_size: Size2D<uint>, epoch: uint) {
let Size2D { width, height } = page_size;
self.chan.send(SetLayerPageSize(id, Size2D(width as f32, height as f32)))
self.chan.send(SetLayerPageSize(id, Size2D(width as f32, height as f32), epoch))
}
fn set_layer_clip_rect(&self, id: PipelineId, new_rect: Rect<uint>) {
let new_rect = Rect(Point2D(new_rect.origin.x as f32,
@ -136,11 +136,10 @@ pub enum Msg {
/// Requests the compositors GL context.
GetGLContext(Chan<AzGLContext>),
// TODO: Attach epochs to these messages
/// Alerts the compositor that there is a new layer to be rendered.
NewLayer(PipelineId, Size2D<f32>),
/// Alerts the compositor that the specified layer's page has changed size.
SetLayerPageSize(PipelineId, Size2D<f32>),
SetLayerPageSize(PipelineId, Size2D<f32>, uint),
/// Alerts the compositor that the specified layer's clipping rect has changed.
SetLayerClipRect(PipelineId, Rect<f32>),
/// Alerts the compositor that the specified layer has been deleted.
@ -149,7 +148,7 @@ pub enum Msg {
InvalidateRect(PipelineId, Rect<uint>),
/// Requests that the compositor paint the given layer buffer set for the given page size.
Paint(PipelineId, arc::Arc<LayerBufferSet>),
Paint(PipelineId, arc::Arc<LayerBufferSet>, uint),
/// Alerts the compositor to the current status of page loading.
ChangeReadyState(ReadyState),
/// Alerts the compositor to the current status of rendering.
@ -235,8 +234,12 @@ impl CompositorTask {
let window_size_page = Size2D(window_size.width as f32 / world_zoom,
window_size.height as f32 / world_zoom);
for layer in compositor_layer.mut_iter() {
if !layer.hidden {
recomposite = layer.get_buffer_request(Rect(Point2D(0f32, 0f32), window_size_page),
world_zoom) || recomposite;
} else {
debug!("layer is hidden!"); //eschweic
}
}
};
@ -298,13 +301,12 @@ impl CompositorTask {
ask_for_tiles();
}
SetLayerPageSize(id, new_size) => {
println(fmt!("Compositor: id %? sent new layer of size %?", id, new_size));
SetLayerPageSize(id, new_size, epoch) => {
match compositor_layer {
Some(ref mut layer) => {
let page_window = Size2D(window_size.width as f32 / world_zoom,
window_size.height as f32 / world_zoom);
assert!(layer.resize(id, new_size, page_window));
assert!(layer.resize(id, new_size, page_window, epoch));
ask_for_tiles();
}
None => {}
@ -331,12 +333,12 @@ impl CompositorTask {
}
}
Paint(id, new_layer_buffer_set) => {
Paint(id, new_layer_buffer_set, epoch) => {
debug!("osmain: received new frame");
match compositor_layer {
Some(ref mut layer) => {
assert!(layer.add_buffers(id, new_layer_buffer_set.get()));
assert!(layer.add_buffers(id, new_layer_buffer_set.get(), epoch));
recomposite = true;
}
None => {

View file

@ -211,11 +211,30 @@ impl<T: Tile> Quadtree<T> {
(ret, redisplay)
}
/// Resize the quadtree. This can add more space, changing the root node, or it can shrink, making
/// an internal node the new root.
/// TODO: return tiles after shrinking
/// Creates a new quadtree at the specified size. This should be called when the window changes size.
/// TODO: return old tiles.
pub fn resize(&mut self, width: uint, height: uint) {
// Spaces must be squares and powers of 2, so expand the space until it is
let longer = width.max(&height);
let num_tiles = div_ceil(longer, self.max_tile_size);
let power_of_two = next_power_of_two(num_tiles);
let size = power_of_two * self.max_tile_size;
self.root = ~QuadtreeNode {
tile: None,
origin: Point2D(0f32, 0f32),
size: size as f32,
quadrants: [None, None, None, None],
status: Normal,
tile_mem: 0,
};
self.clip_size = Size2D(width, height);
}
/// Resize the underlying quadtree without removing tiles already in place.
/// Might be useful later on, but resize() should be used for now.
/// TODO: return tiles after shrinking
pub fn bad_resize(&mut self, width: uint, height: uint) {
self.clip_size = Size2D(width, height);
let longer = width.max(&height);
let new_num_tiles = div_ceil(longer, self.max_tile_size);

View file

@ -26,6 +26,7 @@ use servo_util::time::ProfilerChan;
use std::hashmap::{HashMap, HashSet};
use std::util::replace;
use extra::future::{Future, from_value};
use extra::url::Url;
/// Maintains the pipelines and navigation context and grants permission to composite
pub struct Constellation {
@ -108,7 +109,7 @@ impl FrameTree {
/// Returns the frame tree whose key is id
fn find_mut(@mut self, id: PipelineId) -> Option<@mut FrameTree> {
do self.iter().find_ |frame_tree| {
do self.iter().find |frame_tree| {
id == frame_tree.pipeline.id
}
}
@ -118,7 +119,7 @@ impl FrameTree {
fn replace_child(@mut self,
id: PipelineId,
new_child: @mut FrameTree)
-> Result<@mut FrameTree, @mut FrameTree> {
-> Either<@mut FrameTree, @mut FrameTree> {
let mut child = (do self.iter().filter_map |frame_tree| {
(do frame_tree.children.iter().find |child| {
child.frame_tree.pipeline.id == id
@ -407,7 +408,7 @@ impl Constellation {
for current_frame in self.current_frame().iter() {
debug!("Constellation: Sending size for frame in current frame tree.");
let source_frame = current_frame.find_mut(pipeline_id);
for source_frame.iter().advance |source_frame| {
for source_frame in source_frame.iter() {
for child_frame_tree in source_frame.children.mut_iter() {
let pipeline = &child_frame_tree.frame_tree.pipeline;
if pipeline.subpage_id.expect("Constellation: child frame does not have a
@ -646,6 +647,11 @@ impl Constellation {
// TODO(tkuehn): In fact, this kind of message might be provably
// impossible to occur.
if current_frame.contains(pipeline_id) {
//debug!("updating compositor frame tree with %?", current_frame);
//self.set_ids(current_frame);
for frame in current_frame.iter() {
frame.pipeline.grant_paint_permission();
}
return;
}
}

View file

@ -161,7 +161,7 @@ impl Pipeline {
}
pub fn reload(&mut self) {
do self.url.clone().map_consume() |url| {
do self.url.clone().map_move() |url| {
self.load(url);
};
}

View file

@ -59,10 +59,10 @@ pub enum ReadyState {
pub trait RenderListener {
fn get_gl_context(&self) -> AzGLContext;
fn new_layer(&self, PipelineId, Size2D<uint>);
fn set_layer_page_size(&self, PipelineId, Size2D<uint>);
fn set_layer_page_size(&self, PipelineId, Size2D<uint>, uint);
fn set_layer_clip_rect(&self, PipelineId, Rect<uint>);
fn delete_layer(&self, PipelineId);
fn paint(&self, id: PipelineId, layer_buffer_set: arc::Arc<LayerBufferSet>);
fn paint(&self, id: PipelineId, layer_buffer_set: arc::Arc<LayerBufferSet>, uint);
fn set_render_state(&self, render_state: RenderState);
}

View file

@ -31,7 +31,7 @@ struct IFrameSize {
impl IFrameSize {
pub fn set_rect(&mut self, rect: Rect<f32>) {
let future_chan = replace(&mut self.future_chan, None);
do future_chan.map_consume |future_chan| {
do future_chan.map_move |future_chan| {
let Size2D { width, height } = rect.size;
future_chan.send(Size2D(width as uint, height as uint));
};

View file

@ -401,7 +401,7 @@ pub fn parse_html(cx: *JSContext,
future_chan: Some(chan),
constellation_chan: constellation_chan.clone(),
});
iframe_chan.send(HtmlDiscoveredIFrame(iframe_url, subpage_id, size_future));
iframe_chan.send(HtmlDiscoveredIFrame((iframe_url, subpage_id, size_future)));
}
}
}