auto merge of #553 : pcwalton/servo/cpu-rendering, r=pcwalton

r? @metajack
This commit is contained in:
bors-servo 2013-10-25 17:01:22 -07:00
commit 4eb1e88e8f
25 changed files with 675 additions and 277 deletions

View file

@ -2,10 +2,12 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use geom::size::Size2D;
use layers::platform::surface::NativePaintingGraphicsContext;
use servo_msg::compositor_msg::Tile;
use std::hashmap::HashMap;
use std::to_bytes::Cb;
use geom::size::Size2D;
use servo_msg::compositor_msg::Tile;
use std::util;
/// This is a struct used to store buffers when they are not in use.
/// The render task can quickly query for a particular size of buffer when it
@ -15,7 +17,7 @@ pub struct BufferMap<T> {
map: HashMap<BufferKey, BufferValue<T>>,
/// The current amount of memory stored by the BufferMap's buffers.
mem: uint,
/// The maximum allowed memory. Unused buffers willl be deleted
/// The maximum allowed memory. Unused buffers will be deleted
/// when this threshold is exceeded.
max_mem: uint,
/// A monotonically increasing counter to track how recently tile sizes were used.
@ -64,8 +66,8 @@ impl<T: Tile> BufferMap<T> {
}
}
// Insert a new buffer into the map.
pub fn insert(&mut self, new_buffer: T) {
/// Insert a new buffer into the map.
pub fn insert(&mut self, graphics_context: &NativePaintingGraphicsContext, new_buffer: T) {
let new_key = BufferKey::get(new_buffer.get_size_2d());
// If all our buffers are the same size and we're already at our
@ -95,7 +97,9 @@ impl<T: Tile> BufferMap<T> {
};
if {
let list = &mut self.map.get_mut(&old_key).buffers;
self.mem -= list.pop().get_mem();
let condemned_buffer = list.pop();
self.mem -= condemned_buffer.get_mem();
condemned_buffer.destroy(graphics_context);
list.is_empty()
}
{ // then
@ -132,4 +136,15 @@ impl<T: Tile> BufferMap<T> {
ret
}
/// Destroys all buffers.
pub fn clear(&mut self, graphics_context: &NativePaintingGraphicsContext) {
let map = util::replace(&mut self.map, HashMap::new());
for (_, value) in map.move_iter() {
for tile in value.buffers.move_iter() {
tile.destroy(graphics_context)
}
}
self.mem = 0
}
}

View file

@ -11,9 +11,10 @@
#[feature(globs)];
extern mod azure;
extern mod geom;
extern mod stb_image;
extern mod extra;
extern mod geom;
extern mod layers;
extern mod stb_image;
extern mod servo_net (name = "net");
extern mod servo_util (name = "util");
extern mod style;

View file

@ -7,39 +7,57 @@
use azure::azure_hl::{BackendType, CairoBackend, CoreGraphicsBackend};
use azure::azure_hl::{CoreGraphicsAcceleratedBackend, Direct2DBackend, SkiaBackend};
use extra::getopts;
use std::result;
/// Global flags for Servo, currently set on the command line.
#[deriving(Clone)]
pub struct Opts {
/// The initial URLs to load.
urls: ~[~str],
/// The rendering backend to use (`-r`).
render_backend: BackendType,
/// How many threads to use for CPU rendering (`-t`).
///
/// FIXME(pcwalton): This is not currently used. All rendering is sequential.
n_render_threads: uint,
/// True to use CPU painting, false to use GPU painting via Skia-GL (`-c`). Note that
/// compositing is always done on the GPU.
cpu_painting: bool,
/// The maximum size of each tile in pixels (`-s`).
tile_size: uint,
/// `None` to disable the profiler or `Some` with an interval in seconds to enable it and cause
/// it to produce output on that interval (`-p`).
profiler_period: Option<f64>,
/// True to exit after the page load (`-x`).
exit_after_load: bool,
output_file: Option<~str>,
headless: bool,
}
pub fn from_cmdline_args(args: &[~str]) -> Opts {
use extra::getopts;
let args = args.tail();
let opts = ~[
getopts::optopt("o"), // output file
getopts::optopt("r"), // rendering backend
getopts::optopt("s"), // size of tiles
getopts::optopt("t"), // threads to render with
getopts::optflagopt("p"), // profiler flag and output interval
getopts::optflag("x"), // exit after load flag
getopts::optflag("z"), // headless mode
getopts::optflag("c"), // CPU rendering
getopts::optopt("o"), // output file
getopts::optopt("r"), // rendering backend
getopts::optopt("s"), // size of tiles
getopts::optopt("t"), // threads to render with
getopts::optflagopt("p"), // profiler flag and output interval
getopts::optflag("x"), // exit after load flag
getopts::optflag("z"), // headless mode
];
let opt_match = match getopts::getopts(args, opts) {
result::Ok(m) => m,
result::Err(f) => fail!(f.to_err_msg()),
Ok(m) => m,
Err(f) => fail!(f.to_err_msg()),
};
let urls = if opt_match.free.is_empty() {
@ -82,10 +100,13 @@ pub fn from_cmdline_args(args: &[~str]) -> Opts {
from_str(period).unwrap()
};
let cpu_painting = opt_match.opt_present("c");
Opts {
urls: urls,
render_backend: render_backend,
n_render_threads: n_render_threads,
cpu_painting: cpu_painting,
tile_size: tile_size,
profiler_period: profiler_period,
exit_after_load: opt_match.opt_present("x"),

View file

@ -101,10 +101,8 @@ impl FontHandleMethods for FontHandle {
};
#[fixed_stack_segment]
fn create_face_from_buffer(lib: FT_Library,
cbuf: *u8, cbuflen: uint, pt_size: f64)
-> Result<FT_Face, ()> {
fn create_face_from_buffer(lib: FT_Library, cbuf: *u8, cbuflen: uint, pt_size: f64)
-> Result<FT_Face, ()> {
unsafe {
let mut face: FT_Face = ptr::null();
let face_index = 0 as FT_Long;

View file

@ -2,8 +2,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use servo_msg::compositor_msg::LayerBuffer;
use servo_util::geometry::Au;
use font_context::FontContext;
use style::computed_values::border_style;
use opts::Opts;
@ -13,30 +11,35 @@ use azure::azure_hl::{DrawSurfaceOptions, DrawTarget, Linear, StrokeOptions};
use azure::{AZ_CAP_BUTT, AZ_CAP_ROUND};
use azure::AZ_JOIN_BEVEL;
use azure::AzFloat;
use std::vec;
use std::libc::types::common::c99::uint16_t;
use std::libc::size_t;
use extra::arc::Arc;
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use geom::side_offsets::SideOffsets2D;
use servo_net::image::base::Image;
use extra::arc::Arc;
use servo_util::geometry::Au;
use std::vec;
use std::libc::types::common::c99::uint16_t;
use std::libc::size_t;
pub struct RenderContext<'self> {
canvas: &'self ~LayerBuffer,
draw_target: &'self DrawTarget,
font_ctx: @mut FontContext,
opts: &'self Opts
opts: &'self Opts,
/// The rectangle that this context encompasses in page coordinates.
page_rect: Rect<f32>,
/// The rectangle that this context encompasses in screen coordinates (pixels).
screen_rect: Rect<uint>,
}
impl<'self> RenderContext<'self> {
pub fn get_draw_target(&self) -> &'self DrawTarget {
&self.canvas.draw_target
self.draw_target
}
pub fn draw_solid_color(&self, bounds: &Rect<Au>, color: Color) {
self.canvas.draw_target.make_current();
self.canvas.draw_target.fill_rect(&bounds.to_azure_rect(), &ColorPattern(color));
self.draw_target.make_current();
self.draw_target.fill_rect(&bounds.to_azure_rect(), &ColorPattern(color));
}
pub fn draw_border(&self,
@ -48,7 +51,7 @@ impl<'self> RenderContext<'self> {
let rect = bounds.to_azure_rect();
let border = border.to_float_px();
self.canvas.draw_target.make_current();
self.draw_target.make_current();
let mut dash: [AzFloat, ..2] = [0 as AzFloat, 0 as AzFloat];
let mut stroke_opts = StrokeOptions(0 as AzFloat, 10 as AzFloat);
@ -57,28 +60,44 @@ impl<'self> RenderContext<'self> {
let y = rect.origin.y + border.top * 0.5;
let start = Point2D(rect.origin.x, y);
let end = Point2D(rect.origin.x + rect.size.width, y);
self.canvas.draw_target.stroke_line(start, end, &ColorPattern(color.top), &stroke_opts, &draw_opts);
self.draw_target.stroke_line(start,
end,
&ColorPattern(color.top),
&stroke_opts,
&draw_opts);
// draw right border
RenderContext::apply_border_style(style.right, border.right, dash, &mut stroke_opts);
let x = rect.origin.x + rect.size.width - border.right * 0.5;
let start = Point2D(x, rect.origin.y);
let end = Point2D(x, rect.origin.y + rect.size.height);
self.canvas.draw_target.stroke_line(start, end, &ColorPattern(color.right), &stroke_opts, &draw_opts);
self.draw_target.stroke_line(start,
end,
&ColorPattern(color.right),
&stroke_opts,
&draw_opts);
// draw bottom border
RenderContext::apply_border_style(style.bottom, border.bottom, dash, &mut stroke_opts);
let y = rect.origin.y + rect.size.height - border.bottom * 0.5;
let start = Point2D(rect.origin.x, y);
let end = Point2D(rect.origin.x + rect.size.width, y);
self.canvas.draw_target.stroke_line(start, end, &ColorPattern(color.bottom), &stroke_opts, &draw_opts);
self.draw_target.stroke_line(start,
end,
&ColorPattern(color.bottom),
&stroke_opts,
&draw_opts);
// draw left border
RenderContext::apply_border_style(style.left, border.left, dash, &mut stroke_opts);
let x = rect.origin.x + border.left * 0.5;
let start = Point2D(x, rect.origin.y);
let end = Point2D(x, rect.origin.y + rect.size.height);
self.canvas.draw_target.stroke_line(start, end, &ColorPattern(color.left), &stroke_opts, &draw_opts);
self.draw_target.stroke_line(start,
end,
&ColorPattern(color.left),
&stroke_opts,
&draw_opts);
}
pub fn draw_image(&self, bounds: Rect<Au>, image: Arc<~Image>) {
@ -86,8 +105,8 @@ impl<'self> RenderContext<'self> {
let size = Size2D(image.width as i32, image.height as i32);
let stride = image.width * 4;
self.canvas.draw_target.make_current();
let draw_target_ref = &self.canvas.draw_target;
self.draw_target.make_current();
let draw_target_ref = &self.draw_target;
let azure_surface = draw_target_ref.create_source_surface_from_data(image.data, size,
stride as i32, B8G8R8A8);
let source_rect = Rect(Point2D(0 as AzFloat, 0 as AzFloat),
@ -104,12 +123,12 @@ impl<'self> RenderContext<'self> {
pub fn clear(&self) {
let pattern = ColorPattern(Color(1.0, 1.0, 1.0, 1.0));
let rect = Rect(Point2D(self.canvas.rect.origin.x as AzFloat,
self.canvas.rect.origin.y as AzFloat),
Size2D(self.canvas.screen_pos.size.width as AzFloat,
self.canvas.screen_pos.size.height as AzFloat));
self.canvas.draw_target.make_current();
self.canvas.draw_target.fill_rect(&rect, &pattern);
let rect = Rect(Point2D(self.page_rect.origin.x as AzFloat,
self.page_rect.origin.y as AzFloat),
Size2D(self.screen_rect.size.width as AzFloat,
self.screen_rect.size.height as AzFloat));
self.draw_target.make_current();
self.draw_target.fill_rect(&rect, &pattern);
}
fn apply_border_style(style: border_style::T, border_width: AzFloat, dash: &mut [AzFloat], stroke_opts: &mut StrokeOptions){

View file

@ -4,28 +4,31 @@
// The task that handles all rendering/painting.
use azure::{AzFloat, AzGLContext};
use azure::azure_hl::{B8G8R8A8, DrawTarget};
use display_list::DisplayList;
use servo_msg::compositor_msg::{RenderListener, IdleRenderState, RenderingRenderState, LayerBuffer};
use servo_msg::compositor_msg::{LayerBufferSet, Epoch};
use servo_msg::constellation_msg::{ConstellationChan, PipelineId, RendererReadyMsg};
use font_context::FontContext;
use azure::azure_hl::{B8G8R8A8, DrawTarget, StolenGLResources};
use azure::AzFloat;
use geom::matrix2d::Matrix2D;
use geom::size::Size2D;
use geom::rect::Rect;
use opts::Opts;
use render_context::RenderContext;
use std::comm::{Chan, Port, SharedChan};
use std::task::spawn_with;
use extra::arc::Arc;
use geom::size::Size2D;
use layers::platform::surface::{NativePaintingGraphicsContext, NativeSurface};
use layers::platform::surface::{NativeSurfaceMethods};
use layers;
use servo_msg::compositor_msg::{Epoch, IdleRenderState, LayerBuffer, LayerBufferSet};
use servo_msg::compositor_msg::{RenderListener, RenderingRenderState};
use servo_msg::constellation_msg::{ConstellationChan, PipelineId, RendererReadyMsg};
use servo_msg::platform::surface::NativeSurfaceAzureMethods;
use servo_util::time::{ProfilerChan, profile};
use servo_util::time;
use buffer_map::BufferMap;
use std::comm::{Chan, Port, SharedChan};
use std::task::spawn_with;
use std::util;
use extra::arc::Arc;
use buffer_map::BufferMap;
use display_list::DisplayList;
use font_context::FontContext;
use opts::Opts;
use render_context::RenderContext;
pub struct RenderLayer<T> {
display_list: Arc<DisplayList<T>>,
@ -82,6 +85,13 @@ impl<T: Send> GenericSmartChan<Msg<T>> for RenderChan<T> {
}
}
/// If we're using GPU rendering, this provides the metadata needed to create a GL context that
/// is compatible with that of the main thread.
enum GraphicsContext {
CpuGraphicsContext,
GpuGraphicsContext,
}
pub struct RenderTask<C,T> {
id: PipelineId,
port: Port<Msg<T>>,
@ -93,16 +103,21 @@ pub struct RenderTask<C,T> {
/// A channel to the profiler.
profiler_chan: ProfilerChan,
share_gl_context: AzGLContext,
/// The graphics context to use.
graphics_context: GraphicsContext,
/// The native graphics context.
native_graphics_context: NativePaintingGraphicsContext,
/// The layer to be rendered
render_layer: Option<RenderLayer<T>>,
/// Permission to send paint messages to the compositor
paint_permission: bool,
/// Cached copy of last layers rendered
last_paint_msg: Option<~LayerBufferSet>,
/// A counter for epoch messages
epoch: Epoch,
/// A data structure to store unused LayerBuffers
buffer_map: BufferMap<~LayerBuffer>,
}
@ -114,11 +129,13 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
constellation_chan: ConstellationChan,
opts: Opts,
profiler_chan: ProfilerChan) {
do spawn_with((port, compositor, constellation_chan, opts, profiler_chan))
|(port, compositor, constellation_chan, opts, profiler_chan)| {
let share_gl_context = compositor.get_gl_context();
let graphics_metadata = compositor.get_graphics_metadata();
let cpu_painting = opts.cpu_painting;
let native_graphics_context =
NativePaintingGraphicsContext::from_metadata(&graphics_metadata);
// FIXME: rust/#5967
let mut render_task = RenderTask {
@ -131,16 +148,26 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
profiler_chan.clone()),
opts: opts,
profiler_chan: profiler_chan,
share_gl_context: share_gl_context,
graphics_context: if cpu_painting {
CpuGraphicsContext
} else {
GpuGraphicsContext
},
native_graphics_context: native_graphics_context,
render_layer: None,
paint_permission: false,
last_paint_msg: None,
epoch: Epoch(0),
buffer_map: BufferMap::new(10000000),
};
render_task.start();
// Destroy all the buffers.
render_task.buffer_map.clear(&render_task.native_graphics_context)
}
}
@ -157,7 +184,6 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
self.constellation_chan.send(RendererReadyMsg(self.id));
}
self.render_layer = Some(render_layer);
self.last_paint_msg = None;
}
ReRenderMsg(tiles, scale, epoch) => {
if self.epoch == epoch {
@ -169,7 +195,7 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
UnusedBufferMsg(unused_buffers) => {
// move_rev_iter is more efficient
for buffer in unused_buffers.move_rev_iter() {
self.buffer_map.insert(buffer);
self.buffer_map.insert(&self.native_graphics_context, buffer);
}
}
PaintPermissionGranted => {
@ -181,16 +207,6 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
}
None => {}
}
// FIXME: This sends the last paint request, anticipating what
// the compositor will ask for. However, even if it sends the right
// tiles, the compositor still asks for them, and they will be
// re-rendered redundantly.
match self.last_paint_msg {
Some(ref layer_buffer_set) => {
self.compositor.paint(self.id, layer_buffer_set.clone(), self.epoch);
}
None => {} // Nothing to do
}
}
PaintPermissionRevoked => {
self.paint_permission = false;
@ -221,7 +237,6 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
self.compositor.set_render_state(RenderingRenderState);
do time::profile(time::RenderingCategory, self.profiler_chan.clone()) {
// FIXME: Try not to create a new array here.
let mut new_buffers = ~[];
@ -231,42 +246,41 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
let width = tile.screen_rect.size.width;
let height = tile.screen_rect.size.height;
let buffer = match self.buffer_map.find(tile.screen_rect.size) {
Some(buffer) => {
let mut buffer = buffer;
buffer.rect = tile.page_rect;
buffer.screen_pos = tile.screen_rect;
buffer.resolution = scale;
buffer
let size = Size2D(width as i32, height as i32);
let draw_target = match self.graphics_context {
CpuGraphicsContext => {
DrawTarget::new(self.opts.render_backend, size, B8G8R8A8)
}
None => ~LayerBuffer {
draw_target: DrawTarget::new_with_fbo(self.opts.render_backend,
self.share_gl_context,
Size2D(width as i32, height as i32),
B8G8R8A8),
rect: tile.page_rect,
screen_pos: tile.screen_rect,
resolution: scale,
stride: (width * 4) as uint
GpuGraphicsContext => {
// FIXME(pcwalton): Cache the components of draw targets
// (texture color buffer, renderbuffers) instead of recreating them.
let draw_target =
DrawTarget::new_with_fbo(self.opts.render_backend,
&self.native_graphics_context,
size,
B8G8R8A8);
draw_target.make_current();
draw_target
}
};
{
// Build the render context.
let ctx = RenderContext {
canvas: &buffer,
draw_target: &draw_target,
font_ctx: self.font_ctx,
opts: &self.opts
opts: &self.opts,
page_rect: tile.page_rect,
screen_rect: tile.screen_rect,
};
// 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);
let matrix = matrix.translate(-(tile.page_rect.origin.x) as AzFloat,
-(tile.page_rect.origin.y) as AzFloat);
ctx.canvas.draw_target.set_transform(&matrix);
ctx.draw_target.set_transform(&matrix);
// Clear the buffer.
ctx.clear();
@ -274,14 +288,78 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
// Draw the display list.
do profile(time::RenderingDrawingCategory, self.profiler_chan.clone()) {
render_layer.display_list.get().draw_into_context(&ctx);
ctx.canvas.draw_target.flush();
ctx.draw_target.flush();
}
}
// Extract the texture from the draw target and place it into its slot in the
// buffer. If using CPU rendering, upload it first.
//
// FIXME(pcwalton): We should supply the texture and native surface *to* the
// draw target in GPU rendering mode, so that it doesn't have to recreate it.
let buffer = match self.graphics_context {
CpuGraphicsContext => {
let buffer = match self.buffer_map.find(tile.screen_rect.size) {
Some(buffer) => {
let mut buffer = buffer;
buffer.rect = tile.page_rect;
buffer.screen_pos = tile.screen_rect;
buffer.resolution = scale;
buffer.native_surface.mark_wont_leak();
buffer
}
None => {
// Create an empty native surface. We mark it as not leaking
// in case it dies in transit to the compositor task.
let mut native_surface: NativeSurface =
layers::platform::surface::NativeSurfaceMethods::new(
&self.native_graphics_context,
Size2D(width as i32, height as i32),
width as i32 * 4);
native_surface.mark_wont_leak();
~LayerBuffer {
native_surface: native_surface,
rect: tile.page_rect,
screen_pos: tile.screen_rect,
resolution: scale,
stride: (width * 4) as uint
}
}
};
do draw_target.snapshot().get_data_surface().with_data |data| {
buffer.native_surface.upload(&self.native_graphics_context, data);
debug!("RENDERER uploading to native surface %d",
buffer.native_surface.get_id() as int);
}
buffer
}
GpuGraphicsContext => {
draw_target.make_current();
let StolenGLResources {
surface: native_surface
} = draw_target.steal_gl_resources().unwrap();
// We mark the native surface as not leaking in case the surfaces
// die on their way to the compositor task.
let mut native_surface: NativeSurface =
NativeSurfaceAzureMethods::from_azure_surface(native_surface);
native_surface.mark_wont_leak();
~LayerBuffer {
native_surface: native_surface,
rect: tile.page_rect,
screen_pos: tile.screen_rect,
resolution: scale,
stride: (width * 4) as uint
}
}
};
new_buffers.push(buffer);
}
}
let layer_buffer_set = ~LayerBufferSet {
@ -290,12 +368,10 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
debug!("render_task: returning surface");
if self.paint_permission {
self.compositor.paint(self.id, layer_buffer_set.clone(), self.epoch);
self.compositor.paint(self.id, layer_buffer_set, self.epoch);
} else {
self.constellation_chan.send(RendererReadyMsg(self.id));
}
debug!("caching paint msg");
self.last_paint_msg = Some(layer_buffer_set);
self.compositor.set_render_state(IdleRenderState);
}
}

View file

@ -2,21 +2,27 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::cell::Cell;
use geom::point::Point2D;
use geom::size::Size2D;
use geom::rect::Rect;
use compositing::quadtree::{Quadtree, Normal, Invalid, Hidden};
use constellation::{SendableChildFrameTree, SendableFrameTree};
use geom::matrix::identity;
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use gfx::render_task::{ReRenderMsg, UnusedBufferMsg};
use servo_msg::compositor_msg::{LayerBuffer, LayerBufferSet, Epoch};
use servo_msg::constellation_msg::PipelineId;
use layers::layers::{ContainerLayerKind, ContainerLayer, Flip, NoFlip, TextureLayer};
use layers::layers::{TextureLayerKind, VerticalFlip};
use layers::platform::surface::{NativeCompositingGraphicsContext, NativeSurfaceMethods};
use layers::texturegl::{Texture, TextureTarget, TextureTargetRectangle};
use pipeline::Pipeline;
use script::dom::event::{ClickEvent, MouseDownEvent, MouseUpEvent};
use script::script_task::SendEventMsg;
use servo_msg::compositor_msg::{LayerBuffer, LayerBufferSet, Epoch, Tile};
use servo_msg::constellation_msg::PipelineId;
use std::cell::Cell;
use windowing::{MouseWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent, MouseWindowMouseUpEvent};
use compositing::quadtree::{Quadtree, Normal, Invalid, Hidden};
use layers::layers::{ContainerLayerKind, ContainerLayer, TextureLayerKind, TextureLayer, TextureManager};
use pipeline::Pipeline;
use constellation::{SendableChildFrameTree, SendableFrameTree};
#[cfg(not(target_os="macos"))]
use layers::texturegl::TextureTarget2D;
/// The CompositorLayer represents an element on a page that has a unique scroll
/// or animation behavior. This can include absolute positioned elements, iframes, etc.
@ -24,31 +30,42 @@ use constellation::{SendableChildFrameTree, SendableFrameTree};
pub struct CompositorLayer {
/// This layer's pipeline. BufferRequests and mouse events will be sent through this.
pipeline: Pipeline,
/// The size of the underlying page in page coordinates. This is an option
/// because we may not know the size of the page until layout is finished completely.
/// if we have no size yet, the layer is hidden until a size message is recieved.
page_size: Option<Size2D<f32>>,
/// The offset of the page due to scrolling. (0,0) is when the window sees the
/// top left corner of the page.
scroll_offset: Point2D<f32>,
/// 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: MaybeQuadtree,
/// 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,
/// 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: Epoch,
/// The behavior of this layer when a scroll message is received.
scroll_behavior: ScrollBehavior,
/// True if CPU rendering is enabled, false if we're using GPU rendering.
cpu_painting: bool,
}
/// Helper struct for keeping CompositorLayer children organized.
@ -76,12 +93,15 @@ enum ScrollBehavior {
FixedPosition,
}
impl CompositorLayer {
/// Creates a new CompositorLayer with an optional page size. If no page size is given,
/// the layer is initially hidden and initialized without a quadtree.
pub fn new(pipeline: Pipeline, page_size: Option<Size2D<f32>>, tile_size: uint, max_mem: Option<uint>)
-> CompositorLayer {
pub fn new(pipeline: Pipeline,
page_size: Option<Size2D<f32>>,
tile_size: uint,
max_mem: Option<uint>,
cpu_painting: bool)
-> CompositorLayer {
CompositorLayer {
pipeline: pipeline,
page_size: page_size,
@ -89,8 +109,8 @@ impl CompositorLayer {
children: ~[],
quadtree: match page_size {
None => NoTree(tile_size, max_mem),
Some(page_size) => Tree(Quadtree::new(page_size.width as uint,
page_size.height as uint,
Some(page_size) => Tree(Quadtree::new(Size2D(page_size.width as uint,
page_size.height as uint),
tile_size,
max_mem)),
},
@ -98,15 +118,18 @@ impl CompositorLayer {
hidden: true,
epoch: Epoch(0),
scroll_behavior: Scroll,
cpu_painting: cpu_painting,
}
}
/// Constructs a CompositorLayer tree from a frame tree.
pub fn from_frame_tree(frame_tree: SendableFrameTree,
tile_size: uint,
max_mem: Option<uint>) -> CompositorLayer {
max_mem: Option<uint>,
cpu_painting: bool)
-> CompositorLayer {
let SendableFrameTree { pipeline, children } = frame_tree;
let mut layer = CompositorLayer::new(pipeline, None, tile_size, max_mem);
let mut layer = CompositorLayer::new(pipeline, None, tile_size, max_mem, cpu_painting);
layer.children = (do children.move_iter().map |child| {
let SendableChildFrameTree { frame_tree, rect } = child;
let container = @mut ContainerLayer();
@ -121,7 +144,10 @@ impl CompositorLayer {
None => {}
}
let child_layer = ~CompositorLayer::from_frame_tree(frame_tree, tile_size, max_mem);
let child_layer = ~CompositorLayer::from_frame_tree(frame_tree,
tile_size,
max_mem,
cpu_painting);
container.add_child_start(ContainerLayerKind(child_layer.root_layer));
CompositorLayerChild {
@ -137,7 +163,8 @@ impl CompositorLayer {
// 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 {
pub fn scroll(&mut self, delta: Point2D<f32>, cursor: Point2D<f32>, window_size: Size2D<f32>)
-> bool {
let cursor = cursor - self.scroll_offset;
for child in self.children.mut_iter().filter(|x| !x.child.hidden) {
match child.container.scissor {
@ -218,7 +245,11 @@ impl CompositorLayer {
// Given the current window size, determine which tiles need to be (re)rendered
// 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 {
pub fn get_buffer_request(&mut self,
graphics_context: &NativeCompositingGraphicsContext,
window_rect: Rect<f32>,
scale: f32)
-> bool {
let rect = Rect(Point2D(-self.scroll_offset.x + window_rect.origin.x,
-self.scroll_offset.y + window_rect.origin.y),
window_rect.size);
@ -239,7 +270,7 @@ impl CompositorLayer {
}
}
if redisplay {
self.build_layer_tree();
self.build_layer_tree(graphics_context);
}
let transform = |x: &mut CompositorLayerChild| -> bool {
match x.container.scissor {
@ -252,7 +283,7 @@ impl CompositorLayer {
// to make the child_rect appear in coordinates local to it.
let child_rect = Rect(new_rect.origin.sub(&scissor.origin),
new_rect.size);
x.child.get_buffer_request(child_rect, scale)
x.child.get_buffer_request(graphics_context, child_rect, scale)
}
None => {
false //Layer is offscreen
@ -324,10 +355,10 @@ impl CompositorLayer {
new_size.height as uint)));
}
NoTree(tile_size, max_mem) => {
self.quadtree = Tree(Quadtree::new(new_size.width as uint,
new_size.height as uint,
self.quadtree = Tree(Quadtree::new(Size2D(new_size.width as uint,
new_size.height as uint),
tile_size,
max_mem));
max_mem))
}
}
// Call scroll for bounds checking if the page shrunk. Use (-1, -1) as the cursor position
@ -341,6 +372,23 @@ impl CompositorLayer {
}
}
// Returns whether the layer should be vertically flipped. and
#[cfg(target_os="macos")]
fn texture_flip_and_target(cpu_painting: bool, size: Size2D<uint>) -> (Flip, TextureTarget) {
let flip = if cpu_painting {
NoFlip
} else {
VerticalFlip
};
(flip, TextureTargetRectangle(size))
}
#[cfg(not(target_os="macos"))]
fn texture_flip_and_target(_: bool, _: Size2D<uint>) -> (Flip, TextureTarget) {
(NoFlip, TextureTarget2D)
}
// A helper method to resize sublayers.
fn resize_helper(&mut self, pipeline_id: PipelineId, new_size: Size2D<f32>, epoch: Epoch) -> bool {
let found = match self.children.iter().position(|x| pipeline_id == x.child.pipeline.id) {
@ -355,10 +403,10 @@ impl CompositorLayer {
new_size.height as uint)));
}
NoTree(tile_size, max_mem) => {
child.quadtree = Tree(Quadtree::new(new_size.width as uint,
new_size.height as uint,
child.quadtree = Tree(Quadtree::new(Size2D(new_size.width as uint,
new_size.height as uint),
tile_size,
max_mem));
max_mem))
}
}
match child_node.container.scissor {
@ -386,7 +434,7 @@ impl CompositorLayer {
// 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) {
pub fn build_layer_tree(&mut self, graphics_context: &NativeCompositingGraphicsContext) {
// Iterate over the children of the container layer.
let mut current_layer_child = self.root_layer.first_child;
@ -410,19 +458,37 @@ impl CompositorLayer {
for buffer in all_tiles.iter() {
debug!("osmain: compositing buffer rect %?", &buffer.rect);
let size = Size2D(buffer.screen_pos.size.width as int,
buffer.screen_pos.size.height as int);
// 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);
// Determine, in a platform-specific way, whether we should flip the texture
// and which target to use.
let (flip, target) =
CompositorLayer::texture_flip_and_target(self.cpu_painting,
buffer.screen_pos.size);
// Make a new texture and bind the layer buffer's surface to it.
let texture = Texture::new(target);
debug!("COMPOSITOR binding to native surface %d",
buffer.native_surface.get_id() as int);
buffer.native_surface.bind_to_texture(graphics_context, &texture, size);
// Make a texture layer and add it.
texture_layer = @mut TextureLayer::new(texture, buffer.screen_pos.size, flip);
self.root_layer.add_child_end(TextureLayerKind(texture_layer));
None
}
Some(TextureLayerKind(existing_texture_layer)) => {
texture_layer = existing_texture_layer;
texture_layer.manager = @buffer.draw_target.clone() as @TextureManager;
let texture = &texture_layer.texture;
buffer.native_surface.bind_to_texture(graphics_context, texture, size);
// Move on to the next sibling.
do current_layer_child.unwrap().with_common |common| {
@ -432,9 +498,8 @@ impl CompositorLayer {
Some(_) => fail!(~"found unexpected layer kind"),
};
let rect = buffer.rect;
// Set the layer's transform.
let rect = buffer.rect;
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);
@ -455,21 +520,32 @@ impl CompositorLayer {
}
};
}
}
// Add LayerBuffers to the specified layer. Returns false if the layer is not found.
// If the epoch of the message does not match the layer's epoch, the message is ignored.
pub fn add_buffers(&mut self, pipeline_id: PipelineId, new_buffers: ~LayerBufferSet, epoch: Epoch) -> bool {
// Add LayerBuffers to the specified layer. Returns the layer buffer set back if the layer that
// matches the given pipeline ID was not found; otherwise returns None and consumes the layer
// buffer set.
//
// If the epoch of the message does not match the layer's epoch, the message is ignored, the
// layer buffer set is consumed, and None is returned.
pub fn add_buffers(&mut self,
graphics_context: &NativeCompositingGraphicsContext,
pipeline_id: PipelineId,
new_buffers: ~LayerBufferSet,
epoch: Epoch)
-> Option<~LayerBufferSet> {
let cell = Cell::new(new_buffers);
if self.pipeline.id == pipeline_id {
if self.epoch != epoch {
debug!("compositor epoch mismatch: %? != %?, id: %?", self.epoch, epoch, self.pipeline.id);
debug!("compositor epoch mismatch: %? != %?, id: %?",
self.epoch,
epoch,
self.pipeline.id);
self.pipeline.render_chan.send(UnusedBufferMsg(cell.take().buffers));
return true;
return None;
}
{ // block here to prevent double mutable borrow of self
{
// Block here to prevent double mutable borrow of self.
let quadtree = match self.quadtree {
NoTree(*) => fail!("CompositorLayer: cannot add buffers, no quadtree initialized"),
Tree(ref mut quadtree) => quadtree,
@ -486,22 +562,30 @@ impl CompositorLayer {
self.pipeline.render_chan.send(UnusedBufferMsg(unused_tiles));
}
}
self.build_layer_tree();
true
} else {
// ID does not match ours, so recurse on descendents (including hidden children).
self.children.mut_iter().map(|x| &mut x.child)
.any(|x| {
let buffers = cell.take();
let result = x.add_buffers(pipeline_id, buffers.clone(), epoch);
cell.put_back(buffers);
result
})
self.build_layer_tree(graphics_context);
return None;
}
// ID does not match ours, so recurse on descendents (including hidden children).
for child_layer in self.children.mut_iter() {
match child_layer.child.add_buffers(graphics_context,
pipeline_id,
cell.take(),
epoch) {
None => return None,
Some(buffers) => cell.put_back(buffers),
}
}
// Not found. Give the caller the buffers back.
Some(cell.take())
}
// Deletes a specified sublayer, including hidden children. Returns false if the layer is not found.
pub fn delete(&mut self, pipeline_id: PipelineId) -> bool {
pub fn delete(&mut self,
graphics_context: &NativeCompositingGraphicsContext,
pipeline_id: PipelineId)
-> bool {
match self.children.iter().position(|x| x.child.pipeline.id == pipeline_id) {
Some(i) => {
let mut child = self.children.remove(i);
@ -516,18 +600,16 @@ impl CompositorLayer {
}
}
}
match child.child.quadtree {
NoTree(*) => {} // Nothing to do
Tree(ref mut quadtree) => {
// Send back all tiles to renderer.
child.child.pipeline.render_chan.send(UnusedBufferMsg(quadtree.collect_tiles()));
}
}
self.build_layer_tree();
// Send back all tiles to renderer.
child.child.clear();
self.build_layer_tree(graphics_context);
true
}
None => {
self.children.mut_iter().map(|x| &mut x.child).any(|x| x.delete(pipeline_id))
self.children.mut_iter().map(|x| &mut x.child)
.any(|x| x.delete(graphics_context, pipeline_id))
}
}
}
@ -554,7 +636,11 @@ impl CompositorLayer {
container.common.set_transform(identity().translate(clipping_rect.origin.x,
clipping_rect.origin.y,
0.0));
let child = ~CompositorLayer::new(pipeline, page_size, tile_size, max_mem);
let child = ~CompositorLayer::new(pipeline,
page_size,
tile_size,
max_mem,
self.cpu_painting);
container.add_child_start(ContainerLayerKind(child.root_layer));
self.children.push(CompositorLayerChild {
child: child,
@ -582,4 +668,57 @@ impl CompositorLayer {
child.child.set_occlusions();
}
}
/// Destroys all quadtree tiles, sending the buffers back to the renderer to be destroyed or
/// reused.
fn clear(&mut self) {
match self.quadtree {
NoTree(*) => {}
Tree(ref mut quadtree) => {
let mut tiles = quadtree.collect_tiles();
// We have no way of knowing without a race whether the render task is even up and
// running, but mark the tiles as not leaking. If the render task died, then the
// tiles are going to be cleaned up.
for tile in tiles.mut_iter() {
tile.mark_wont_leak()
}
self.pipeline.render_chan.send(UnusedBufferMsg(tiles))
}
}
}
/// Destroys all quadtree tiles of all layers, including child layers, sending the buffers
/// back to the renderer to be destroyed or reused.
pub fn clear_all(&mut self) {
self.clear();
for kid in self.children.mut_iter() {
kid.child.clear_all()
}
}
/// Destroys all tiles of all layers, including children, *without* sending them back to the
/// renderer. You must call this only when the render task is destined to be going down;
/// otherwise, you will leak tiles.
///
/// This is used during shutdown, when we know the render task is going away.
pub fn forget_all_tiles(&mut self) {
match self.quadtree {
NoTree(*) => {}
Tree(ref mut quadtree) => {
let tiles = quadtree.collect_tiles();
for tile in tiles.move_iter() {
let mut tile = tile;
tile.mark_wont_leak()
}
}
}
for kid in self.children.mut_iter() {
kid.child.forget_all_tiles();
}
}
}

View file

@ -4,21 +4,25 @@
pub use windowing;
use servo_msg::compositor_msg::{RenderListener, LayerBufferSet, RenderState};
use servo_msg::compositor_msg::{ReadyState, ScriptListener, Epoch};
use servo_msg::constellation_msg::{ConstellationChan, PipelineId};
use gfx::opts::Opts;
use azure::azure::AzGLContext;
use std::comm;
use std::comm::{Chan, SharedChan, Port};
use std::num::Orderable;
use geom::point::Point2D;
use geom::size::Size2D;
use geom::rect::Rect;
use servo_util::time::ProfilerChan;
use constellation::SendableFrameTree;
use windowing::WindowMethods;
use azure::azure_hl::SourceSurfaceMethods;
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use gfx::opts::Opts;
use layers::platform::surface::{NativeCompositingGraphicsContext, NativeGraphicsMetadata};
use servo_msg::compositor_msg::{Epoch, RenderListener, LayerBufferSet, RenderState, ReadyState};
use servo_msg::compositor_msg::{ScriptListener, Tile};
use servo_msg::constellation_msg::{ConstellationChan, PipelineId};
use servo_util::time::ProfilerChan;
use std::comm::{Chan, SharedChan, Port};
use std::comm;
use std::num::Orderable;
#[cfg(target_os="linux")]
use azure::azure_hl;
mod quadtree;
mod compositor_layer;
@ -36,7 +40,6 @@ pub struct CompositorChan {
/// Implementation of the abstract `ScriptListener` interface.
impl ScriptListener for CompositorChan {
fn set_ready_state(&self, ready_state: ReadyState) {
let msg = ChangeReadyState(ready_state);
self.chan.send(msg);
@ -54,10 +57,9 @@ impl ScriptListener for CompositorChan {
/// Implementation of the abstract `RenderListener` interface.
impl RenderListener for CompositorChan {
fn get_gl_context(&self) -> AzGLContext {
fn get_graphics_metadata(&self) -> NativeGraphicsMetadata {
let (port, chan) = comm::stream();
self.chan.send(GetGLContext(chan));
self.chan.send(GetGraphicsMetadata(chan));
port.recv()
}
@ -115,8 +117,10 @@ pub enum Msg {
Exit,
/// Requests the window size
GetSize(Chan<Size2D<int>>),
/// Requests the compositors GL context.
GetGLContext(Chan<AzGLContext>),
/// Requests the compositor's graphics metadata. Graphics metadata is what the renderer needs
/// to create surfaces that the compositor can see. On Linux this is the X display; on Mac this
/// is the pixel format.
GetGraphicsMetadata(Chan<NativeGraphicsMetadata>),
/// Alerts the compositor that there is a new layer to be rendered.
NewLayer(PipelineId, Size2D<f32>),
@ -160,6 +164,18 @@ impl CompositorTask {
}
}
/// Creates a graphics context. Platform-specific.
///
/// FIXME(pcwalton): Probably could be less platform-specific, using the metadata abstraction.
#[cfg(target_os="linux")]
fn create_graphics_context() -> NativeCompositingGraphicsContext {
NativeCompositingGraphicsContext::from_display(azure_hl::current_display())
}
#[cfg(not(target_os="linux"))]
fn create_graphics_context() -> NativeCompositingGraphicsContext {
NativeCompositingGraphicsContext::new()
}
pub fn run(&self) {
if self.opts.headless {
run_headless::run_compositor(self);

View file

@ -8,12 +8,16 @@
use geom::point::Point2D;
use geom::size::Size2D;
use geom::rect::Rect;
use gfx::render_task::BufferRequest;
use std::uint::{div_ceil, next_power_of_two};
use std::vec;
use std::util::replace;
use gfx::render_task::BufferRequest;
use std::vec::build;
use servo_msg::compositor_msg::Tile;
#[cfg(test)]
use layers::platform::surface::NativePaintingGraphicsContext;
static HEADER: &'static str = "<!DOCTYPE html><html>";
/// Parent to all quadtree nodes. Stores variables needed at all levels. All method calls
@ -75,9 +79,9 @@ impl<T: Tile> Quadtree<T> {
/// Takes in the initial width and height of the space, a maximum tile size, and
/// a maximum amount of memory. Tiles will be deleted if this memory is exceeded.
/// Set max_mem to None to turn off automatic tile removal.
pub fn new(width: uint, height: uint, tile_size: uint, max_mem: Option<uint>) -> Quadtree<T> {
pub fn new(clip_size: Size2D<uint>, tile_size: uint, max_mem: Option<uint>) -> Quadtree<T> {
// Spaces must be squares and powers of 2, so expand the space until it is
let longer = width.max(&height);
let longer = clip_size.width.max(&clip_size.height);
let num_tiles = div_ceil(longer, tile_size);
let power_of_two = next_power_of_two(num_tiles);
let size = power_of_two * tile_size;
@ -91,7 +95,7 @@ impl<T: Tile> Quadtree<T> {
tile_mem: 0,
status: Normal,
},
clip_size: Size2D(width, height),
clip_size: clip_size,
max_tile_size: tile_size,
max_mem: max_mem,
}
@ -111,7 +115,7 @@ impl<T: Tile> Quadtree<T> {
self.root.get_tile(x, y)
}
/// Add a tile associtated with a given pixel position and scale.
/// Add a tile associated 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. These tiles are returned.
pub fn add_tile_pixel(&mut self, x: uint, y: uint, scale: f32, tile: T) -> ~[T] {
@ -133,7 +137,7 @@ impl<T: Tile> Quadtree<T> {
tiles
}
/// Add a tile associtated with a given page position.
/// Add a tile associated 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. These tiles are returned.
pub fn add_tile_page(&mut self, x: f32, y: f32, scale: f32, tile: T) -> ~[T] {
@ -302,7 +306,6 @@ impl<T: Tile> Quadtree<T> {
pub fn get_html(&self) -> ~str {
fmt!("%s<body>%s</body></html>", HEADER, self.root.get_html())
}
}
impl<T: Tile> QuadtreeNode<T> {
@ -769,7 +772,6 @@ impl<T: Tile> QuadtreeNode<T> {
}
return ret;
}
}
#[test]
@ -789,9 +791,11 @@ pub fn test_resize() {
fn get_size_2d(&self) -> Size2D<uint> {
Size2D(0u, 0u)
}
fn mark_wont_leak(&mut self) {}
fn destroy(self, _: &NativePaintingGraphicsContext) {}
}
let mut q = Quadtree::new(6, 6, 1, None);
let mut q = Quadtree::new(Size2D(6u, 6), 1, None);
q.add_tile_pixel(0, 0, 1f32, T{a: 0});
q.add_tile_pixel(5, 5, 1f32, T{a: 1});
q.bad_resize(8, 1);
@ -822,9 +826,11 @@ pub fn test() {
fn get_size_2d(&self) -> Size2D<uint> {
Size2D(0u, 0u)
}
fn mark_wont_leak(&mut self) {}
fn destroy(self, _: &NativePaintingGraphicsContext) {}
}
let mut q = Quadtree::new(8, 8, 2, Some(4));
let mut q = Quadtree::new(Size2D(8u, 8), 2, Some(4));
q.add_tile_pixel(0, 0, 1f32, T{a: 0});
q.add_tile_pixel(0, 0, 2f32, T{a: 1});
q.add_tile_pixel(0, 0, 2f32, T{a: 2});

View file

@ -12,7 +12,8 @@ use windowing::{QuitWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEven
use servo_msg::constellation_msg::{ConstellationChan, NavigateMsg, ResizedWindowMsg, LoadUrlMsg};
use servo_msg::constellation_msg;
use azure::azure_hl::{DataSourceSurface, DrawTarget, SourceSurfaceMethods, current_gl_context};
use azure::azure_hl::SourceSurfaceMethods;
use azure::azure_hl;
use std::comm::Port;
use std::num::Orderable;
use std::vec;
@ -22,8 +23,7 @@ use geom::matrix::identity;
use geom::point::Point2D;
use geom::size::Size2D;
use geom::rect::Rect;
use layers::layers::{ARGB32Format, ContainerLayer, ContainerLayerKind, Format};
use layers::layers::{ImageData, WithDataFn};
use layers::layers::{ContainerLayer, ContainerLayerKind};
use layers::rendergl;
use layers::scene::Scene;
use opengles::gl2;
@ -38,31 +38,6 @@ use compositing::compositor_layer::CompositorLayer;
use compositing::*;
/// Azure surface wrapping to work with the layers infrastructure.
struct AzureDrawTargetImageData {
draw_target: DrawTarget,
data_source_surface: DataSourceSurface,
size: Size2D<uint>,
}
impl ImageData for AzureDrawTargetImageData {
fn size(&self) -> Size2D<uint> {
self.size
}
fn stride(&self) -> uint {
self.data_source_surface.stride() as uint
}
fn format(&self) -> Format {
// FIXME: This is not always correct. We should query the Azure draw target for the format.
ARGB32Format
}
fn with_data(&self, f: WithDataFn) {
do self.data_source_surface.with_data |data| {
f(data);
}
}
}
/// Starts the compositor, which listens for messages on the specified port.
pub fn run_compositor(compositor: &CompositorTask) {
let app: Application = ApplicationMethods::new();
@ -79,6 +54,7 @@ pub fn run_compositor(compositor: &CompositorTask) {
let mut window_size = Size2D(window_size.width as uint, window_size.height as uint);
let mut done = false;
let mut recomposite = false;
let graphics_context = CompositorTask::create_graphics_context();
// Keeps track of the current zoom factor
let mut world_zoom = 1f32;
@ -95,8 +71,9 @@ pub fn run_compositor(compositor: &CompositorTask) {
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;
let rect = Rect(Point2D(0f32, 0f32), window_size_page);
recomposite = layer.get_buffer_request(&graphics_context, rect, world_zoom) ||
recomposite;
} else {
debug!("Compositor: root layer is hidden!");
}
@ -123,10 +100,17 @@ pub fn run_compositor(compositor: &CompositorTask) {
let layer = CompositorLayer::from_frame_tree(frame_tree,
compositor.opts.tile_size,
Some(10000000u));
Some(10000000u),
compositor.opts.cpu_painting);
root_layer.add_child_start(ContainerLayerKind(layer.root_layer));
compositor_layer = Some(layer);
// If there's already a root layer, destroy it cleanly.
match compositor_layer {
None => {}
Some(ref mut compositor_layer) => compositor_layer.clear_all(),
}
compositor_layer = Some(layer);
constellation_chan = Some(new_constellation_chan);
}
@ -135,7 +119,7 @@ pub fn run_compositor(compositor: &CompositorTask) {
chan.send(Size2D(size.width as int, size.height as int));
}
GetGLContext(chan) => chan.send(current_gl_context()),
GetGraphicsMetadata(chan) => chan.send(azure_hl::current_graphics_metadata()),
NewLayer(_id, new_size) => {
// FIXME: This should create an additional layer instead of replacing the current one.
@ -146,8 +130,11 @@ pub fn run_compositor(compositor: &CompositorTask) {
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::new(p, Some(page_size),
compositor.opts.tile_size, Some(10000000u));
let new_layer = CompositorLayer::new(p,
Some(page_size),
compositor.opts.tile_size,
Some(10000000u),
compositor.opts.cpu_painting);
let current_child = root_layer.first_child;
// This assumes there is at most one child, which should be the case.
@ -186,7 +173,7 @@ pub fn run_compositor(compositor: &CompositorTask) {
DeleteLayer(id) => {
match compositor_layer {
Some(ref mut layer) => {
assert!(layer.delete(id));
assert!(layer.delete(&graphics_context, id));
ask_for_tiles();
}
None => {}
@ -196,9 +183,16 @@ pub fn run_compositor(compositor: &CompositorTask) {
Paint(id, new_layer_buffer_set, epoch) => {
debug!("osmain: received new frame");
// From now on, if we destroy the buffers, they will leak.
let mut new_layer_buffer_set = new_layer_buffer_set;
new_layer_buffer_set.mark_will_leak();
match compositor_layer {
Some(ref mut layer) => {
assert!(layer.add_buffers(id, new_layer_buffer_set, epoch));
assert!(layer.add_buffers(&graphics_context,
id,
new_layer_buffer_set,
epoch).is_none());
recomposite = true;
}
None => {
@ -401,5 +395,11 @@ pub fn run_compositor(compositor: &CompositorTask) {
}
compositor.shutdown_chan.send(())
compositor.shutdown_chan.send(());
// Clear out the compositor layers so that painting tasks can destroy the buffers.
match compositor_layer {
None => {}
Some(ref mut layer) => layer.forget_all_tiles(),
}
}

View file

@ -2,11 +2,11 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use geom::size::Size2D;
use std::ptr;
use compositing::*;
use geom::size::Size2D;
use std::unstable::intrinsics;
/// Starts the compositor, which listens for messages on the specified port.
///
/// This is the null compositor which doesn't draw anything to the screen.
@ -20,8 +20,10 @@ pub fn run_compositor(compositor: &CompositorTask) {
chan.send(Size2D(500, 500));
}
GetGLContext(chan) => {
chan.send(ptr::null());
GetGraphicsMetadata(chan) => {
unsafe {
chan.send(intrinsics::uninit());
}
}
SetIds(_, response_chan, _) => {

View file

@ -39,6 +39,8 @@ extern mod extra;
extern mod core_graphics;
#[cfg(target_os="macos")]
extern mod core_text;
#[cfg(target_os="macos")]
extern mod io_surface;
use compositing::{CompositorChan, CompositorTask};
use constellation::Constellation;

View file

@ -2,38 +2,46 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use azure::azure_hl::DrawTarget;
use azure::azure::AzGLContext;
use geom::rect::Rect;
use geom::size::Size2D;
use layers::platform::surface::{NativeGraphicsMetadata, NativePaintingGraphicsContext};
use layers::platform::surface::{NativeSurface, NativeSurfaceMethods};
use constellation_msg::PipelineId;
#[deriving(Clone)]
pub struct LayerBuffer {
draw_target: DrawTarget,
/// The native surface which can be shared between threads or processes. On Mac this is an
/// `IOSurface`; on Linux this is an X Pixmap; on Android this is an `EGLImageKHR`.
native_surface: NativeSurface,
// The rect in the containing RenderLayer that this represents.
/// The rect in the containing RenderLayer that this represents.
rect: Rect<f32>,
// 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>,
// The scale at which this tile is rendered
/// The scale at which this tile is rendered
resolution: f32,
// NB: stride is in pixels, like OpenGL GL_UNPACK_ROW_LENGTH.
/// NB: stride is in pixels, like OpenGL GL_UNPACK_ROW_LENGTH.
stride: uint,
}
/// A set of layer buffers. This is an atomic unit used to switch between the front and back
/// buffers.
#[deriving(Clone)]
pub struct LayerBufferSet {
buffers: ~[~LayerBuffer]
}
impl LayerBufferSet {
/// Notes all buffer surfaces will leak if not destroyed via a call to `destroy`.
pub fn mark_will_leak(&mut self) {
for buffer in self.buffers.mut_iter() {
buffer.native_surface.mark_will_leak()
}
}
}
/// The status of the renderer.
#[deriving(Eq)]
pub enum RenderState {
@ -66,7 +74,7 @@ impl Epoch {
/// The interface used by the renderer to acquire draw targets for each render frame and
/// submit them to be drawn to the display.
pub trait RenderListener {
fn get_gl_context(&self) -> AzGLContext;
fn get_graphics_metadata(&self) -> NativeGraphicsMetadata;
fn new_layer(&self, PipelineId, Size2D<uint>);
fn set_layer_page_size(&self, PipelineId, Size2D<uint>, Epoch);
fn set_layer_clip_rect(&self, PipelineId, Rect<uint>);
@ -83,7 +91,7 @@ pub trait ScriptListener : Clone {
fn close(&self);
}
/// The interface used by the quadtree to get info about LayerBuffers
/// The interface used by the quadtree and buffer map to get info about layer buffers.
pub trait Tile {
/// Returns the amount of memory used by the tile
fn get_mem(&self) -> uint;
@ -91,6 +99,13 @@ pub trait Tile {
fn is_valid(&self, f32) -> bool;
/// Returns the Size2D of the tile
fn get_size_2d(&self) -> Size2D<uint>;
/// Marks the layer buffer as not leaking. See comments on
/// `NativeSurfaceMethods::mark_wont_leak` for how this is used.
fn mark_wont_leak(&mut self);
/// Destroys the layer buffer. Painting task only.
fn destroy(self, graphics_context: &NativePaintingGraphicsContext);
}
impl Tile for ~LayerBuffer {
@ -104,4 +119,12 @@ impl Tile for ~LayerBuffer {
fn get_size_2d(&self) -> Size2D<uint> {
self.screen_pos.size
}
fn mark_wont_leak(&mut self) {
self.native_surface.mark_wont_leak()
}
fn destroy(self, graphics_context: &NativePaintingGraphicsContext) {
let mut this = self;
this.native_surface.destroy(graphics_context)
}
}

View file

@ -8,11 +8,33 @@
url = "http://servo.org/")];
#[crate_type = "lib"];
extern mod azure;
extern mod std;
extern mod geom;
extern mod extra;
extern mod geom;
extern mod layers;
extern mod std;
#[cfg(target_os="macos")]
extern mod core_foundation;
#[cfg(target_os="macos")]
extern mod io_surface;
pub mod compositor_msg;
pub mod constellation_msg;
pub mod platform {
#[cfg(target_os="macos")]
pub mod macos {
#[cfg(target_os="macos")]
pub mod surface;
}
#[cfg(target_os="linux")]
pub mod linux {
#[cfg(target_os="linux")]
pub mod surface;
}
pub mod surface;
}

View file

@ -0,0 +1,20 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! X11-specific implementation of cross-process surfaces. This uses X pixmaps.
use platform::surface::NativeSurfaceAzureMethods;
use azure::AzSkiaGrGLSharedSurfaceRef;
use layers::platform::surface::NativeSurface;
use std::cast;
impl NativeSurfaceAzureMethods for NativeSurface {
fn from_azure_surface(surface: AzSkiaGrGLSharedSurfaceRef) -> NativeSurface {
unsafe {
NativeSurface::from_pixmap(cast::transmute(surface))
}
}
}

View file

@ -0,0 +1,26 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! Mac OS-specific implementation of cross-process surfaces. This uses `IOSurface`, introduced
//! in Mac OS X 10.6 Snow Leopard.
use platform::surface::NativeSurfaceAzureMethods;
use azure::AzSkiaGrGLSharedSurfaceRef;
use core_foundation::base::CFWrapper;
use io_surface::IOSurface;
use layers::platform::surface::NativeSurface;
use std::cast;
impl NativeSurfaceAzureMethods for NativeSurface {
fn from_azure_surface(surface: AzSkiaGrGLSharedSurfaceRef) -> NativeSurface {
unsafe {
let io_surface = IOSurface {
contents: CFWrapper::wrap_owned(cast::transmute(surface)),
};
NativeSurface::from_io_surface(io_surface)
}
}
}

View file

@ -0,0 +1,12 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! Declarations of types for cross-process surfaces.
use azure::AzSkiaGrGLSharedSurfaceRef;
pub trait NativeSurfaceAzureMethods {
fn from_azure_surface(surface: AzSkiaGrGLSharedSurfaceRef) -> Self;
}

@ -1 +1 @@
Subproject commit deefb42dd238f54f95bf09f47d19e1c2acfd1c74
Subproject commit 71ca7ff52c7f342c95608c00bb0823aa8f39cd34

@ -1 +1 @@
Subproject commit 45e3f33cf6a3109b4dbb7bf9e0cb814d6e1e4b55
Subproject commit a4bd491ddfd5d17bdcb228cf515cce776e2b8239

@ -1 +1 @@
Subproject commit 614d65361a29c0100bd662102e1b8d33698ea330
Subproject commit b932f0d91dad470baefafe291d4958de4ff01d47

@ -1 +1 @@
Subproject commit b5212699f9ad364898b685f5f945b821ee528771
Subproject commit 82d56dfc632fe458d126d049fd5abe856170f48d

@ -1 +1 @@
Subproject commit 716c3326d3d6073c48ecfb301c2ab6e7eeeccfcf
Subproject commit 191fab2cf685671ef73ec13e85bdd889b0e93662

@ -1 +1 @@
Subproject commit 71dcba600f546d6c4daec56659e02c8db9fdf003
Subproject commit a5da47f086c06a5ff9e179203fc8dbd5b7bb3c04

@ -1 +1 @@
Subproject commit 96665d2033e5cda1ae419ce70a4071b61bc58b09
Subproject commit 70ba8e70d7828e572dd1a4dd8807e9730db56137

@ -1 +1 @@
Subproject commit aeae6a5cd5a6ecb7fd2d3b2f806e9661ce3b2e3b
Subproject commit 73b73c23696a373b84289711b537eaa00065e4f9