Refactor compositor; implement compositor iframe support

This commit is contained in:
eschweic 2013-07-29 09:46:19 -07:00
parent 0e737741b3
commit 974ed79144
7 changed files with 482 additions and 303 deletions

View file

@ -32,7 +32,7 @@ pub struct RenderLayer {
pub enum Msg {
RenderMsg(RenderLayer),
ReRenderMsg(~[BufferRequest], f32),
ReRenderMsg(~[BufferRequest], f32, PipelineId),
PaintPermissionGranted,
PaintPermissionRevoked,
ExitMsg(Chan<()>),
@ -135,18 +135,18 @@ impl<C: RenderListener + Send> RenderTask<C> {
match self.port.recv() {
RenderMsg(render_layer) => {
if self.paint_permission {
self.compositor.new_layer(render_layer.size, self.opts.tile_size);
self.compositor.new_layer(self.id, render_layer.size);
}
self.render_layer = Some(render_layer);
}
ReRenderMsg(tiles, scale) => {
self.render(tiles, scale);
ReRenderMsg(tiles, scale, id) => {
self.render(tiles, scale, id);
}
PaintPermissionGranted => {
self.paint_permission = true;
match self.render_layer {
Some(ref render_layer) => {
self.compositor.new_layer(render_layer.size, self.opts.tile_size);
self.compositor.new_layer(self.id, render_layer.size);
}
None => {}
}
@ -162,7 +162,7 @@ impl<C: RenderListener + Send> RenderTask<C> {
}
}
fn render(&mut self, tiles: ~[BufferRequest], scale: f32) {
fn render(&mut self, tiles: ~[BufferRequest], scale: f32, id: PipelineId) {
let render_layer;
match self.render_layer {
Some(ref r_layer) => {
@ -234,7 +234,7 @@ impl<C: RenderListener + Send> RenderTask<C> {
debug!("render_task: returning surface");
if self.paint_permission {
self.compositor.paint(self.id, layer_buffer_set.clone(), render_layer.size);
self.compositor.paint(id, layer_buffer_set.clone());
}
debug!("caching paint msg");
self.last_paint_msg = Some((layer_buffer_set, render_layer.size));

View file

@ -6,24 +6,55 @@ use geom::point::Point2D;
use geom::size::Size2D;
use geom::rect::Rect;
use geom::matrix::identity;
use gfx::render_task::BufferRequest;
use gfx::render_task::ReRenderMsg;
use servo_msg::compositor_msg::{LayerBuffer, LayerBufferSet};
use servo_msg::constellation_msg::PipelineId;
use script::dom::event::{ClickEvent, MouseDownEvent, MouseUpEvent};
use script::script_task::SendEventMsg;
use windowing::{MouseWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent, MouseWindowMouseUpEvent};
use compositing::quadtree::Quadtree;
use layers::layers::{ContainerLayer, TextureLayerKind, TextureLayer, TextureManager};
use layers::layers::{ContainerLayerKind, ContainerLayer, TextureLayerKind, TextureLayer, TextureManager};
use pipeline::Pipeline;
/// The CompositorLayer represents an element on a page that has a unique scroll
/// or animation behavior. This can include absolute positioned elements, iframes, etc.
/// Each layer can also have child elements.
pub struct CompositorLayer {
id: uint,
/// This layer's pipeline. BufferRequests will be sent through this.
pipeline: Pipeline,
/// The rect that this layer occupies in page coordinates. The offset
/// is blind to any parents this layer may have; it only refers to the
/// scroll position of the page.
page_rect: Rect<f32>,
z_order: int,
/// This layer's children. These could be iframes or any element which
/// differs in scroll behavior from its parent. Each is associated with a
/// ContainerLayer which determines its position relative to its parent and
/// clipping rect. Children are stored in the order in which they are drawn.
children: ~[CompositorLayerChild],
/// This layer's quadtree. This is where all buffers are stored for this layer.
quadtree: Quadtree<~LayerBuffer>,
/// The root layer of this CompositorLayer's layer tree. Buffers are collected
/// from the quadtree and inserted here when the layer is painted to the screen.
root_layer: @mut ContainerLayer,
// TODO: Eventually, it may be useful to have an ID associated with each layer.
}
pub fn CompositorLayer(id: uint, page_size: Size2D<f32>, init_offset: Point2D<f32>, z_order: int, tile_size: uint, max_mem: Option<uint>) -> CompositorLayer {
/// Helper struct for keeping CompositorLayer children organized.
struct CompositorLayerChild {
/// The child itself.
child: ~CompositorLayer,
/// A ContainerLayer managed by the parent node. This deals with clipping and
/// positioning, and is added above the child's layer tree.
container: @mut ContainerLayer,
}
pub fn CompositorLayer(pipeline: Pipeline, page_size: Size2D<f32>, tile_size: uint,
max_mem: Option<uint>) -> CompositorLayer {
CompositorLayer {
id: id,
page_rect: Rect(init_offset, page_size),
z_order: z_order,
pipeline: pipeline,
page_rect: Rect(Point2D(0f32, 0f32), page_size),
children: ~[],
quadtree: Quadtree::new(page_size.width as uint, page_size.height as uint,
tile_size,
max_mem),
@ -32,38 +63,171 @@ pub fn CompositorLayer(id: uint, page_size: Size2D<f32>, init_offset: Point2D<f3
}
impl CompositorLayer {
// Given the current window size, determine which tiles need to be redisplayed.
// Returns a BufferRequest array as well as a bool that is true if the layer tree should
// be rebuilt for this layer.
fn get_buffer_request(&mut self, window_size: Size2D<int>, scale: f32) -> (~[BufferRequest], bool) {
let rect = Rect(Point2D(-(self.page_rect.origin.x as int),
-(self.page_rect.origin.y as int)),
window_size);
self.quadtree.get_tile_rects(rect, scale)
// 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
// true; otherwise returns false, so a parent layer can scroll instead.
pub fn scroll(&mut self, delta: Point2D<f32>, cursor: Point2D<f32>, window_size: Size2D<f32>) -> bool {
let cursor = cursor - self.page_rect.origin;
for self.children.mut_iter().advance |child| {
match child.container.scissor {
None => {
error!("CompositorLayer: unable to perform cursor hit test for layer");
}
Some(rect) => {
if cursor.x >= rect.origin.x && cursor.x < rect.origin.x + rect.size.width
&& cursor.y >= rect.origin.y && cursor.y < rect.origin.y + rect.size.height
&& child.child.scroll(delta, cursor - rect.origin, rect.size) {
return true;
}
}
}
}
// Move the layer by as relative specified amount. Called during a scroll event.
fn translate(&mut self, delta: Point2D<f32>) {
let old_origin = self.page_rect.origin;
self.page_rect.origin = self.page_rect.origin + delta;
// bounds checking
let min_x = (window_size.width - self.page_rect.size.width).min(&0.0);
self.page_rect.origin.x = self.page_rect.origin.x.clamp(&min_x, &0.0);
let min_y = (window_size.height - self.page_rect.size.height).min(&0.0);
self.page_rect.origin.y = self.page_rect.origin.y.clamp(&min_y, &0.0);
// check to see if we scrolled
if old_origin - self.page_rect.origin == Point2D(0f32, 0f32) {
return false;
}
// Move the layer to an absolute position.
fn set_offset(&mut self, new_offset: Point2D<f32>) {
self.page_rect.origin = new_offset;
self.root_layer.common.set_transform(identity().translate(self.page_rect.origin.x,
self.page_rect.origin.y,
0.0));
true
}
// Takes in a MouseWindowEvent, determines if it should be passed to children, and
// sends the event off to the appropriate pipeline. NB: the cursor position is in
// page coordinates.
pub fn send_mouse_event(&self, event: MouseWindowEvent, cursor: Point2D<f32>) {
let cursor = cursor - self.page_rect.origin;
// FIXME: maybe we want rev_iter() instead? Depends on draw order
for self.children.iter().advance |child| {
match child.container.scissor {
None => {
error!("CompositorLayer: unable to perform cursor hit test for layer");
}
Some(rect) => {
if cursor.x >= rect.origin.x && cursor.x < rect.origin.x + rect.size.width
&& cursor.y >= rect.origin.y && cursor.y < rect.origin.y + rect.size.height {
child.child.send_mouse_event(event, cursor - rect.origin);
return;
}
}
}
}
// This mouse event is mine!
let message = match event {
MouseWindowClickEvent(button, _) => ClickEvent(button, cursor),
MouseWindowMouseDownEvent(button, _) => MouseDownEvent(button, cursor),
MouseWindowMouseUpEvent(button, _) => MouseUpEvent(button, cursor),
};
self.pipeline.script_chan.send(SendEventMsg(self.pipeline.id.clone(), message));
}
// Given the current window size, determine which tiles need to be redisplayed
// and sends them off the the appropriate renderer.
// Returns a bool that is true if the scene should be repainted.
pub fn get_buffer_request(&mut self, window_rect: Rect<f32>, scale: f32) -> bool {
let rect = Rect(Point2D(-self.page_rect.origin.x + window_rect.origin.x,
-self.page_rect.origin.y + window_rect.origin.y),
window_rect.size);
let (request, redisplay) = self.quadtree.get_tile_rects_page(rect, scale);
if !request.is_empty() {
self.pipeline.render_chan.send(ReRenderMsg(request, scale, self.pipeline.id.clone()));
}
if redisplay {
self.build_layer_tree();
}
let transform = |x: &mut CompositorLayerChild| -> bool {
match x.container.scissor {
Some(scissor) => {
let new_rect = window_rect.intersection(&scissor);
match new_rect {
Some(new_rect) => {
x.child.get_buffer_request(new_rect, scale)
}
None => {
false //Layer is offscreen
}
}
}
None => {
fail!("CompositorLayer: Child layer not clipped");
}
}
};
self.children.mut_iter()
.transform(transform)
.fold(false, |a, b| a || b) || redisplay
}
// Move the sublayer to an absolute position in page coordinates relative to its parent,
// and clip the layer to the specified size in page coordinates.
// 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 self.children.iter().advance |child_node| {
if pipeline_id != child_node.child.pipeline.id {
loop;
}
let con = child_node.container;
con.common.set_transform(identity().translate(new_rect.origin.x,
new_rect.origin.y,
0.0));
con.scissor = Some(new_rect);
return true;
}
// ID does not match any of our immediate children, so recurse on descendents.
self.children.mut_iter().transform(|x| &mut x.child).any(|x| x.set_clipping_rect(pipeline_id, new_rect))
}
// Called when the layer changes size (NOT as a result of a zoom event).
fn resize(&mut self, new_size: Size2D<f32>) {
// 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 {
if self.pipeline.id == pipeline_id {
self.page_rect.size = new_size;
// TODO: might get buffers back here
self.quadtree.resize(new_size.width as uint, new_size.height as uint);
// Call scroll for bounds checking of the page shrunk.
self.scroll(Point2D(0f32, 0f32), Point2D(-1f32, -1f32), window_size);
return true;
}
fn get_layer_tree(&mut self, scale: f32) -> @mut ContainerLayer {
// ID does not match ours, so recurse on descendents.
let transform = |x: &mut CompositorLayerChild| -> bool {
match x.container.scissor {
Some(scissor) => {
x.child.resize(pipeline_id, new_size, scissor.size)
}
None => {
fail!("CompositorLayer: Child layer not clipped");
}
}
};
self.children.mut_iter().any(transform)
}
// Collect buffers from the quadtree. This method IS NOT recursive, so child CompositorLayers
// are not rebuilt directly from this method.
pub fn build_layer_tree(&mut self) {
// Iterate over the children of the container layer.
let mut current_layer_child = self.root_layer.first_child;
// Delete old layer
// Delete old layer.
while current_layer_child.is_some() {
let trash = current_layer_child.get();
do current_layer_child.get().with_common |common| {
@ -72,10 +236,25 @@ impl CompositorLayer {
self.root_layer.remove_child(trash);
}
// Add child layers.
for self.children.mut_iter().advance |child| {
current_layer_child = match current_layer_child {
None => {
child.container.common.parent = None;
child.container.common.prev_sibling = None;
child.container.common.next_sibling = None;
self.root_layer.add_child(ContainerLayerKind(child.container));
None
}
Some(_) => {
fail!("CompositorLayer: Layer tree failed to delete");
}
};
}
// Add new tiles.
let all_tiles = self.quadtree.get_all_tiles();
for all_tiles.iter().advance |buffer| {
let width = buffer.screen_pos.size.width as uint;
let height = buffer.screen_pos.size.height as uint;
debug!("osmain: compositing buffer rect %?", &buffer.rect);
// Find or create a texture layer.
@ -100,28 +279,61 @@ impl CompositorLayer {
Some(_) => fail!(~"found unexpected layer kind"),
};
let origin = buffer.rect.origin;
let origin = Point2D(origin.x as f32, origin.y as f32);
let rect = buffer.rect;
// Set the layer's transform.
let transform = identity().translate(origin.x * scale + self.page_rect.origin.x, origin.y * scale + self.page_rect.origin.y, 0.0);
let transform = transform.scale(width as f32 * scale / buffer.resolution, height as f32 * scale / buffer.resolution, 1.0);
let transform = identity().translate(rect.origin.x, rect.origin.y, 0.0);
let transform = transform.scale(rect.size.width, rect.size.height, 1.0);
texture_layer.common.set_transform(transform);
}
self.root_layer
}
// Add LayerBuffers to this layer.
// TODO: This may return old buffers, which should be sent back to the renderer.
fn add_buffers(&mut self, new_buffers: &mut LayerBufferSet) {
// 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 self.pipeline.id == pipeline_id {
for new_buffers.buffers.iter().advance |buffer| {
self.quadtree.add_tile(buffer.screen_pos.origin.x, buffer.screen_pos.origin.y,
// TODO: This may return old buffers, which should be sent back to the renderer.
self.quadtree.add_tile_pixel(buffer.screen_pos.origin.x, buffer.screen_pos.origin.y,
buffer.resolution, ~buffer.clone());
}
self.build_layer_tree();
return true;
}
// ID does not match ours, so recurse on descendents.
self.children.mut_iter().transform(|x| &mut x.child).any(|x| x.add_buffers(pipeline_id, new_buffers))
}
// TODO: send buffers back to renderer when later is deleted
// Deletes a specified sublayer. Returns false if the layer is not found.
pub fn delete(&mut self, pipeline_id: PipelineId) -> bool {
match self.children.rposition(|x| x.child.pipeline.id == pipeline_id) {
Some(index) => {
// TODO: send buffers back to renderer when layer is deleted
self.children.remove(index);
self.build_layer_tree();
true
}
None => {
self.children.mut_iter().transform(|x| &mut x.child).any(|x| x.delete(pipeline_id))
}
}
}
// Adds a child.
pub fn add_child(&mut self, pipeline: Pipeline, page_size: Size2D<f32>, tile_size: uint,
max_mem: Option<uint>, clipping_rect: Rect<f32>) {
let container = @mut ContainerLayer();
container.scissor = Some(clipping_rect);
container.common.set_transform(identity().translate(clipping_rect.origin.x,
clipping_rect.origin.y,
0.0));
let child = ~CompositorLayer(pipeline, page_size, tile_size, max_mem);
container.add_child(ContainerLayerKind(child.root_layer));
self.children.push(CompositorLayerChild {
child: child,
container: container,
});
}
}

View file

@ -3,19 +3,19 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use platform::{Application, Window};
use script::dom::event::{Event_, ClickEvent, MouseDownEvent, MouseUpEvent, ResizeEvent};
use script::dom::event::ResizeEvent;
use script::script_task::{LoadMsg, NavigateMsg, SendEventMsg};
pub use windowing;
use windowing::{ApplicationMethods, WindowEvent, WindowMethods};
use windowing::{IdleWindowEvent, ResizeWindowEvent, LoadUrlWindowEvent, MouseWindowEventClass};
use windowing::{ScrollWindowEvent, ZoomWindowEvent, NavigationWindowEvent, FinishedWindowEvent};
use windowing::{QuitWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent, MouseWindowMouseUpEvent};
use servo_msg::compositor_msg::{RenderListener, LayerBuffer, LayerBufferSet, RenderState};
use servo_msg::compositor_msg::{RenderListener, LayerBufferSet, RenderState};
use servo_msg::compositor_msg::{ReadyState, ScriptListener};
use servo_msg::constellation_msg::PipelineId;
use servo_msg::constellation_msg;
use gfx::render_task::ReRenderMsg;
use gfx::opts::Opts;
use azure::azure_hl::{DataSourceSurface, DrawTarget, SourceSurfaceMethods, current_gl_context};
@ -35,7 +35,6 @@ use geom::size::Size2D;
use geom::rect::Rect;
use layers::layers::{ARGB32Format, ContainerLayer, ContainerLayerKind, Format};
use layers::layers::{ImageData, WithDataFn};
use layers::layers::{TextureLayerKind, TextureLayer, TextureManager};
use layers::rendergl;
use layers::scene::Scene;
use opengles::gl2;
@ -44,17 +43,17 @@ use servo_util::{time, url};
use servo_util::time::profile;
use servo_util::time::ProfilerChan;
use extra::arc;
pub use windowing;
use extra::time::precise_time_s;
use compositing::quadtree::Quadtree;
use extra::arc;
use constellation::SendableFrameTree;
use pipeline::Pipeline;
use compositing::compositor_layer::CompositorLayer;
mod quadtree;
mod compositor_layer;
/// The implementation of the layers-based compositor.
#[deriving(Clone)]
pub struct CompositorChan {
@ -81,18 +80,18 @@ impl RenderListener for CompositorChan {
port.recv()
}
fn paint(&self, id: PipelineId, layer_buffer_set: arc::ARC<LayerBufferSet>, new_size: Size2D<uint>) {
self.chan.send(Paint(id, layer_buffer_set, new_size))
fn paint(&self, id: PipelineId, layer_buffer_set: arc::ARC<LayerBufferSet>) {
self.chan.send(Paint(id, layer_buffer_set))
}
fn new_layer(&self, page_size: Size2D<uint>, tile_size: uint) {
self.chan.send(NewLayer(page_size, tile_size))
fn new_layer(&self, id: PipelineId, page_size: Size2D<uint>) {
self.chan.send(NewLayer(id, page_size))
}
fn resize_layer(&self, page_size: Size2D<uint>) {
self.chan.send(ResizeLayer(page_size))
fn resize_layer(&self, id: PipelineId, page_size: Size2D<uint>) {
self.chan.send(ResizeLayer(id, page_size))
}
fn delete_layer(&self) {
self.chan.send(DeleteLayer)
fn delete_layer(&self, id: PipelineId) {
self.chan.send(DeleteLayer(id))
}
fn set_render_state(&self, render_state: RenderState) {
@ -128,16 +127,16 @@ pub enum Msg {
/// Requests the compositors GL context.
GetGLContext(Chan<AzGLContext>),
// TODO: Attach layer ids and epochs to these messages
// TODO: Attach 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,
NewLayer(PipelineId, Size2D<uint>),
/// Alerts the compositor that the specified layer has changed size.
ResizeLayer(PipelineId, Size2D<uint>),
/// Alerts the compositor that the specified layer has been deleted.
DeleteLayer(PipelineId),
/// Requests that the compositor paint the given layer buffer set for the given page size.
Paint(PipelineId, arc::ARC<LayerBufferSet>, Size2D<uint>),
Paint(PipelineId, arc::ARC<LayerBufferSet>),
/// Alerts the compositor to the current status of page loading.
ChangeReadyState(ReadyState),
/// Alerts the compositor to the current status of rendering.
@ -222,119 +221,30 @@ impl CompositorTask {
let root_layer = @mut ContainerLayer();
let window_size = window.size();
let mut scene = Scene(ContainerLayerKind(root_layer), window_size, identity());
let mut window_size = Size2D(window_size.width as int, window_size.height as int);
let mut done = false;
let mut recomposite = false;
// FIXME: This should not be a separate offset applied after the fact but rather should be
// applied to the layers themselves on a per-layer basis. However, this won't work until scroll
// positions are sent to content.
let mut world_offset = Point2D(0f32, 0f32);
let mut page_size = Size2D(0f32, 0f32);
let mut window_size = Size2D(window_size.width as int,
window_size.height as int);
// Keeps track of the current zoom factor
let mut world_zoom = 1f32;
// Keeps track of local zoom factor. Reset to 1 after a rerender event.
let mut local_zoom = 1f32;
// Channel to the outermost frame's pipeline.
// FIXME: Compositor currently only asks for tiles to composite from this pipeline,
// Subframes need to be handled, as well. Additionally, events are only forwarded
// to this pipeline, but they should be routed to the appropriate pipeline via
// the constellation.
let mut pipeline: Option<Pipeline> = None;
// Quadtree for this layer
// FIXME: This should be one-per-layer
let mut quadtree: Option<Quadtree<~LayerBuffer>> = None;
// Keeps track of if we have performed a zoom event and how recently.
let mut zoom_action = false;
let mut zoom_time = 0f;
// Extract tiles from the given quadtree and build and display the render tree.
let build_layer_tree: &fn(&Quadtree<~LayerBuffer>) = |quad: &Quadtree<~LayerBuffer>| {
// Iterate over the children of the container layer.
let mut current_layer_child = root_layer.first_child;
// Channel to the outermost frame's pipeline.
// FIXME: Events are only forwarded to this pipeline, but they should be
// routed to the appropriate pipeline via the constellation.
let mut pipeline: Option<Pipeline> = None;
// Delete old layer
while current_layer_child.is_some() {
let trash = current_layer_child.get();
do current_layer_child.get().with_common |common| {
current_layer_child = common.next_sibling;
}
root_layer.remove_child(trash);
}
let all_tiles = quad.get_all_tiles();
for all_tiles.iter().advance |buffer| {
let width = buffer.screen_pos.size.width as uint;
let height = buffer.screen_pos.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 {
None => {
debug!("osmain: adding new texture layer");
texture_layer = @mut TextureLayer::new(@buffer.draw_target.clone() as @TextureManager,
buffer.screen_pos.size);
root_layer.add_child(TextureLayerKind(texture_layer));
None
}
Some(TextureLayerKind(existing_texture_layer)) => {
texture_layer = existing_texture_layer;
texture_layer.manager = @buffer.draw_target.clone() as @TextureManager;
// Move on to the next sibling.
do current_layer_child.get().with_common |common| {
common.next_sibling
}
}
Some(_) => fail!(~"found unexpected layer kind"),
};
let origin = buffer.rect.origin;
let origin = Point2D(origin.x as f32, origin.y as f32);
// Set the layer's transform.
let transform = identity().translate(origin.x * world_zoom, origin.y * world_zoom, 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);
}
// Reset zoom
local_zoom = 1f32;
root_layer.common.set_transform(identity().translate(-world_offset.x,
-world_offset.y,
0.0));
recomposite = true;
};
// The root CompositorLayer
let mut compositor_layer: Option<CompositorLayer> = None;
// Get BufferRequests from each layer.
let ask_for_tiles = || {
match quadtree {
Some(ref mut quad) => {
let (tile_request, redisplay) = quad.get_tile_rects(Rect(Point2D(world_offset.x as int,
world_offset.y as int),
window_size), world_zoom);
if !tile_request.is_empty() {
match pipeline {
Some(ref pipeline) => {
pipeline.render_chan.send(ReRenderMsg(tile_request, world_zoom));
}
_ => {
println("Warning: Compositor: Cannot send tile request, no render chan initialized");
}
}
} else if redisplay {
build_layer_tree(quad);
}
}
_ => {
fail!("Compositor: Tried to ask for tiles without an initialized quadtree");
}
let window_size_page = Size2D(window_size.width as f32 / world_zoom,
window_size.height as f32 / world_zoom);
for compositor_layer.mut_iter().advance |layer| {
recomposite = layer.get_buffer_request(Rect(Point2D(0f32, 0f32), window_size_page),
world_zoom) || recomposite;
}
};
@ -359,46 +269,66 @@ impl CompositorTask {
GetGLContext(chan) => chan.send(current_gl_context()),
NewLayer(new_size, tile_size) => {
page_size = Size2D(new_size.width as f32, new_size.height as f32);
quadtree = Some(Quadtree::new(new_size.width.max(&(window_size.width as uint)),
new_size.height.max(&(window_size.height as uint)),
tile_size, Some(10000000u)));
NewLayer(_id, new_size) => {
// FIXME: This should create an additional layer instead of replacing the current one.
// Once ResizeLayer messages are set up, we can switch to the new functionality.
let p = match pipeline {
Some(ref pipeline) => pipeline,
None => fail!("Compositor: Received new layer without initialized pipeline"),
};
let page_size = Size2D(new_size.width as f32, new_size.height as f32);
let new_layer = CompositorLayer(p.clone(), page_size,
self.opts.tile_size, Some(10000000u));
let current_child = root_layer.first_child;
// This assumes there is at most one child, which should be the case.
match current_child {
Some(old_layer) => root_layer.remove_child(old_layer),
None => {}
}
root_layer.add_child(ContainerLayerKind(new_layer.root_layer));
compositor_layer = Some(new_layer);
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 {
Some(ref pipeline) => if id != pipeline.id { loop; },
None => { loop; },
ResizeLayer(id, new_size) => {
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, Size2D(new_size.width as f32,
new_size.height as f32),
page_window));
ask_for_tiles();
}
None => {}
}
}
DeleteLayer(id) => {
match compositor_layer {
Some(ref mut layer) => {
assert!(layer.delete(id));
ask_for_tiles();
}
None => {}
}
}
Paint(id, new_layer_buffer_set) => {
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"),
match compositor_layer {
Some(ref mut layer) => {
assert!(layer.add_buffers(id, new_layer_buffer_set.get()));
recomposite = true;
}
None => {
fail!("Compositor: given paint command with no CompositorLayer initialized");
}
let new_layer_buffer_set = new_layer_buffer_set.get();
for new_layer_buffer_set.buffers.iter().advance |buffer| {
// FIXME: Don't copy the buffers here
quad.add_tile(buffer.screen_pos.origin.x, buffer.screen_pos.origin.y,
buffer.resolution, ~buffer.clone());
}
page_size = Size2D(new_size.width as f32, new_size.height as f32);
build_layer_tree(quad);
// TODO: Recycle the old buffers; send them back to the renderer to reuse if
// it wishes.
}
@ -433,56 +363,27 @@ impl CompositorTask {
}
MouseWindowEventClass(mouse_window_event) => {
let event: Event_;
let world_mouse_point = |layer_mouse_point: Point2D<f32>| {
layer_mouse_point + world_offset
let point = match mouse_window_event {
MouseWindowClickEvent(_, p) => Point2D(p.x / world_zoom, p.y / world_zoom),
MouseWindowMouseDownEvent(_, p) => Point2D(p.x / world_zoom, p.y / world_zoom),
MouseWindowMouseUpEvent(_, p) => Point2D(p.x / world_zoom, p.y / world_zoom),
};
match mouse_window_event {
MouseWindowClickEvent(button, layer_mouse_point) => {
event = ClickEvent(button, world_mouse_point(layer_mouse_point));
}
MouseWindowMouseDownEvent(button, layer_mouse_point) => {
event = MouseDownEvent(button, world_mouse_point(layer_mouse_point));
}
MouseWindowMouseUpEvent(button, layer_mouse_point) => {
event = MouseUpEvent(button, world_mouse_point(layer_mouse_point));
}
}
match pipeline {
Some(ref pipeline) => pipeline.script_chan.send(SendEventMsg(pipeline.id.clone(), event)),
None => error!("Compositor: Recieved mouse event without initialized layout chan"),
for compositor_layer.iter().advance |layer| {
layer.send_mouse_event(mouse_window_event, point);
}
}
ScrollWindowEvent(delta) => {
// FIXME (Rust #2528): Can't use `-=`.
let world_offset_copy = world_offset;
world_offset = world_offset_copy - delta;
// Clamp the world offset to the screen size.
let max_x = (page_size.width * world_zoom - window_size.width as f32).max(&0.0);
world_offset.x = world_offset.x.clamp(&0.0, &max_x).round();
let max_y = (page_size.height * world_zoom - window_size.height as f32).max(&0.0);
world_offset.y = world_offset.y.clamp(&0.0, &max_y).round();
debug!("compositor: scrolled to %?", world_offset);
let mut scroll_transform = identity();
scroll_transform = scroll_transform.translate(window_size.width as f32 / 2f32 * local_zoom - world_offset.x,
window_size.height as f32 / 2f32 * local_zoom - world_offset.y,
0.0);
scroll_transform = scroll_transform.scale(local_zoom, local_zoom, 1f32);
scroll_transform = scroll_transform.translate(window_size.width as f32 / -2f32,
window_size.height as f32 / -2f32,
0.0);
root_layer.common.set_transform(scroll_transform);
ScrollWindowEvent(delta, cursor) => {
// TODO: modify delta to snap scroll to pixels.
let page_delta = Point2D(delta.x as f32 / world_zoom, delta.y as f32 / world_zoom);
let page_cursor: Point2D<f32> = Point2D(cursor.x as f32 / world_zoom,
cursor.y as f32 / world_zoom);
let page_window = Size2D(window_size.width as f32 / world_zoom,
window_size.height as f32 / world_zoom);
for compositor_layer.mut_iter().advance |layer| {
recomposite = layer.scroll(page_delta, page_cursor, page_window) || recomposite;
}
ask_for_tiles();
recomposite = true;
}
ZoomWindowEvent(magnification) => {
@ -492,33 +393,18 @@ impl CompositorTask {
// Determine zoom amount
world_zoom = (world_zoom * magnification).max(&1.0);
local_zoom = local_zoom * world_zoom/old_world_zoom;
root_layer.common.set_transform(identity().scale(world_zoom, world_zoom, 1f32));
// Update world offset
let corner_to_center_x = world_offset.x + window_size.width as f32 / 2f32;
let new_corner_to_center_x = corner_to_center_x * world_zoom / old_world_zoom;
world_offset.x = world_offset.x + new_corner_to_center_x - corner_to_center_x;
let corner_to_center_y = world_offset.y + window_size.height as f32 / 2f32;
let new_corner_to_center_y = corner_to_center_y * world_zoom / old_world_zoom;
world_offset.y = world_offset.y + new_corner_to_center_y - corner_to_center_y;
// Clamp to page bounds when zooming out
let max_x = (page_size.width * world_zoom - window_size.width as f32).max(&0.0);
world_offset.x = world_offset.x.clamp(&0.0, &max_x).round();
let max_y = (page_size.height * world_zoom - window_size.height as f32).max(&0.0);
world_offset.y = world_offset.y.clamp(&0.0, &max_y).round();
// Apply transformations
let mut zoom_transform = identity();
zoom_transform = zoom_transform.translate(window_size.width as f32 / 2f32 * local_zoom - world_offset.x,
window_size.height as f32 / 2f32 * local_zoom - world_offset.y,
0.0);
zoom_transform = zoom_transform.scale(local_zoom, local_zoom, 1f32);
zoom_transform = zoom_transform.translate(window_size.width as f32 / -2f32,
window_size.height as f32 / -2f32,
0.0);
root_layer.common.set_transform(zoom_transform);
// Scroll as needed
let page_delta = Point2D(window_size.width as f32 * (1.0 / world_zoom - 1.0 / old_world_zoom) * 0.5,
window_size.height as f32 * (1.0 / world_zoom - 1.0 / old_world_zoom) * 0.5);
// TODO: modify delta to snap scroll to pixels.
let page_cursor = Point2D(-1f32, -1f32); // Make sure this hits the base layer
let page_window = Size2D(window_size.width as f32 / world_zoom,
window_size.height as f32 / world_zoom);
for compositor_layer.mut_iter().advance |layer| {
layer.scroll(page_delta, page_cursor, page_window);
}
recomposite = true;
}

View file

@ -85,13 +85,19 @@ impl<T: Tile> Quadtree<T> {
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> {
pub fn get_tile_pixel<'r>(&'r self, x: uint, y: uint, scale: f32) -> &'r Option<T> {
self.root.get_tile(x as f32 / scale, y as f32 / scale)
}
/// Get a tile at a given page position.
pub fn get_tile_page<'r>(&'r self, x: f32, y: f32) -> &'r Option<T> {
self.root.get_tile(x, y)
}
/// Add a tile associtated with a given pixel position and scale.
/// If the tile pushes the total memory over its maximum, tiles will be removed
/// until total memory is below the maximum again.
pub fn add_tile(&mut self, x: uint, y: uint, scale: f32, tile: T) {
pub fn add_tile_pixel(&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);
match self.max_mem {
Some(max) => {
@ -106,32 +112,73 @@ impl<T: Tile> Quadtree<T> {
None => {}
}
}
/// Add a tile associtated with a given page position.
/// If the tile pushes the total memory over its maximum, tiles will be removed
/// until total memory is below the maximum again.
pub fn add_tile_page(&mut self, x: f32, y: f32, scale: f32, tile: T) {
self.root.add_tile(x, y, tile, self.max_tile_size as f32 / scale);
match self.max_mem {
Some(max) => {
while self.root.tile_mem > max {
let r = self.root.remove_tile(x, y);
match r {
(Some(_), _, _) => {}
_ => fail!("Quadtree: No valid tiles to remove"),
}
}
}
None => {}
}
}
/// Get the tile rect in screen and page coordinates for a given pixel position
pub fn get_tile_rect(&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.clip_size.width as f32,
self.clip_size.height as f32,
scale, self.max_tile_size as f32 / scale)
}
/// 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 {
self.root.get_tile_rect(x, y,
self.clip_size.width as f32,
self.clip_size.height as f32,
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()
}
/// Ask a tile to be deleted from the quadtree. This tries to delete a tile that is far from the
/// given point in pixel coordinates.
pub fn remove_tile(&mut self, x: uint, y: uint, scale: f32) -> T {
pub fn remove_tile_pixel(&mut self, x: uint, y: uint, scale: f32) -> T {
let r = self.root.remove_tile(x as f32 / scale, y as f32 / scale);
match r {
(Some(tile), _, _) => tile,
_ => fail!("Quadtree: No valid tiles to remove"),
}
}
/// Ask a tile to be deleted from the quadtree. This tries to delete a tile that is far from the
/// given point in page coordinates.
pub fn remove_tile_page(&mut self, x: f32, y: f32) -> T {
let r = self.root.remove_tile(x, y);
match r {
(Some(tile), _, _) => tile,
_ => fail!("Quadtree: No valid tiles to remove"),
}
}
/// 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
/// 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.
/// When this happens, higher resolution tiles will be removed from the quadtree.
pub fn get_tile_rects(&mut self, window: Rect<int>, scale: f32) -> (~[BufferRequest], bool) {
pub fn get_tile_rects_pixel(&mut self, window: Rect<int>, scale: f32) -> (~[BufferRequest], bool) {
let (ret, redisplay, _) = self.root.get_tile_rects(
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)),
@ -139,6 +186,17 @@ impl<T: Tile> Quadtree<T> {
scale, self.max_tile_size as f32 / scale);
(ret, redisplay)
}
/// 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) {
let (ret, redisplay, _) = self.root.get_tile_rects(
window,
Size2D(self.clip_size.width as f32, self.clip_size.height as f32),
scale, self.max_tile_size as f32 / scale);
(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

View file

@ -89,15 +89,29 @@ impl WindowMethods<Application> for Window {
}
do window.glfw_window.set_mouse_button_callback |win, button, action, _mods| {
let (x, y) = win.get_cursor_pos();
//handle hidpi displays, since GLFW returns non-hi-def coordinates.
let (backing_size, _) = win.get_framebuffer_size();
let (window_size, _) = win.get_size();
let hidpi = (backing_size as f32) / (window_size as f32);
let x = x as f32 * hidpi;
let y = y as f32 * hidpi;
if button < 3 {
window.handle_mouse(button, action, x as i32, y as i32);
}
}
do window.glfw_window.set_scroll_callback |_win, x_offset, y_offset| {
do window.glfw_window.set_scroll_callback |win, x_offset, y_offset| {
let dx = (x_offset as f32) * 30.0;
let dy = (y_offset as f32) * 30.0;
event_queue.push(ScrollWindowEvent(Point2D(dx, dy)));
let (x, y) = win.get_cursor_pos();
//handle hidpi displays, since GLFW returns non-hi-def coordinates.
let (backing_size, _) = win.get_framebuffer_size();
let (window_size, _) = win.get_size();
let hidpi = (backing_size as f32) / (window_size as f32);
let x = x as f32 * hidpi;
let y = y as f32 * hidpi;
event_queue.push(ScrollWindowEvent(Point2D(dx, dy), Point2D(x as i32, y as i32)));
}
window
@ -148,6 +162,12 @@ impl WindowMethods<Application> for Window {
self.render_state = render_state;
self.update_window_title()
}
fn hidpi_factor(@mut self) -> f32 {
let (backing_size, _) = self.glfw_window.get_framebuffer_size();
let (window_size, _) = self.glfw_window.get_size();
(backing_size as f32) / (window_size as f32)
}
}
impl Window {

View file

@ -32,8 +32,8 @@ pub enum WindowEvent {
LoadUrlWindowEvent(~str),
/// Sent when a mouse hit test is to be performed.
MouseWindowEventClass(MouseWindowEvent),
/// Sent when the user scrolls.
ScrollWindowEvent(Point2D<f32>),
/// Sent when the user scrolls. Includes the current cursor position.
ScrollWindowEvent(Point2D<f32>, Point2D<i32>),
/// Sent when the user zooms.
ZoomWindowEvent(f32),
/// Sent when the user uses chrome navigation (i.e. backspace or shift-backspace).
@ -64,5 +64,8 @@ pub trait WindowMethods<A> {
pub fn set_ready_state(@mut self, ready_state: ReadyState);
/// Sets the render state of the current page.
pub fn set_render_state(@mut self, render_state: RenderState);
/// Returns the hidpi factor of the monitor.
pub fn hidpi_factor(@mut self) -> f32;
}

View file

@ -58,10 +58,10 @@ 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);
fn resize_layer(&self, Size2D<uint>);
fn delete_layer(&self);
fn paint(&self, id: PipelineId, layer_buffer_set: arc::ARC<LayerBufferSet>, new_size: Size2D<uint>);
fn new_layer(&self, PipelineId, Size2D<uint>);
fn resize_layer(&self, PipelineId, Size2D<uint>);
fn delete_layer(&self, PipelineId);
fn paint(&self, id: PipelineId, layer_buffer_set: arc::ARC<LayerBufferSet>);
fn set_render_state(&self, render_state: RenderState);
}