diff options
author | bors-servo <release+servo@mozilla.com> | 2013-10-25 17:01:22 -0700 |
---|---|---|
committer | bors-servo <release+servo@mozilla.com> | 2013-10-25 17:01:22 -0700 |
commit | 4eb1e88e8f997fbf9f40ed030c6c096da115354c (patch) | |
tree | be96d1f487dc93d09139189bd332af9261158e27 /src | |
parent | d5bd4bfdb71415fc399b986359ba6ad0ff3ac3de (diff) | |
parent | 3d0bfa50407d820435cbf8ae127d567816f0a8eb (diff) | |
download | servo-4eb1e88e8f997fbf9f40ed030c6c096da115354c.tar.gz servo-4eb1e88e8f997fbf9f40ed030c6c096da115354c.zip |
auto merge of #553 : pcwalton/servo/cpu-rendering, r=pcwalton
r? @metajack
Diffstat (limited to 'src')
25 files changed, 659 insertions, 261 deletions
diff --git a/src/components/gfx/buffer_map.rs b/src/components/gfx/buffer_map.rs index 06dd1e2255e..abb995b39ba 100644 --- a/src/components/gfx/buffer_map.rs +++ b/src/components/gfx/buffer_map.rs @@ -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 std::hashmap::HashMap; -use std::to_bytes::Cb; 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 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 + } } diff --git a/src/components/gfx/gfx.rc b/src/components/gfx/gfx.rc index c6c8b9c2d4b..0a2f2ff7cc3 100644 --- a/src/components/gfx/gfx.rc +++ b/src/components/gfx/gfx.rc @@ -11,9 +11,10 @@ #[feature(globs)]; extern mod azure; +extern mod extra; extern mod geom; +extern mod layers; extern mod stb_image; -extern mod extra; extern mod servo_net (name = "net"); extern mod servo_util (name = "util"); extern mod style; diff --git a/src/components/gfx/opts.rs b/src/components/gfx/opts.rs index ea8eb47bf51..108f6feb651 100644 --- a/src/components/gfx/opts.rs +++ b/src/components/gfx/opts.rs @@ -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"), diff --git a/src/components/gfx/platform/linux/font.rs b/src/components/gfx/platform/linux/font.rs index 27634bc9541..f17ca694813 100644 --- a/src/components/gfx/platform/linux/font.rs +++ b/src/components/gfx/platform/linux/font.rs @@ -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; diff --git a/src/components/gfx/render_context.rs b/src/components/gfx/render_context.rs index 024737b3296..bb88a81c8fe 100644 --- a/src/components/gfx/render_context.rs +++ b/src/components/gfx/render_context.rs @@ -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){ diff --git a/src/components/gfx/render_task.rs b/src/components/gfx/render_task.rs index 5cb8742d8b5..4f88465cf13 100644 --- a/src/components/gfx/render_task.rs +++ b/src/components/gfx/render_task.rs @@ -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 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 std::comm::{Chan, Port, SharedChan}; use std::task::spawn_with; +use std::util; use extra::arc::Arc; -use servo_util::time::{ProfilerChan, profile}; -use servo_util::time; - 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 = ~[]; @@ -230,43 +245,42 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> { for tile in tiles.iter() { 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); } } diff --git a/src/components/main/compositing/compositor_layer.rs b/src/components/main/compositing/compositor_layer.rs index a52a519e019..13d22f6602a 100644 --- a/src/components/main/compositing/compositor_layer.rs +++ b/src/components/main/compositing/compositor_layer.rs @@ -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 compositing::quadtree::{Quadtree, Normal, Invalid, Hidden}; +use constellation::{SendableChildFrameTree, SendableFrameTree}; +use geom::matrix::identity; use geom::point::Point2D; -use geom::size::Size2D; use geom::rect::Rect; -use geom::matrix::identity; +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. @@ -75,13 +92,16 @@ enum ScrollBehavior { /// passed on to child layers. 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 @@ -340,6 +371,23 @@ impl CompositorLayer { self.resize_helper(pipeline_id, new_size, epoch) } } + + // 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 { @@ -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; @@ -409,21 +457,39 @@ impl CompositorLayer { let all_tiles = quadtree.get_all_tiles(); 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| { common.next_sibling @@ -431,10 +497,9 @@ 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(); + } + } } + diff --git a/src/components/main/compositing/mod.rs b/src/components/main/compositing/mod.rs index ea1225e6798..49d98708be8 100644 --- a/src/components/main/compositing/mod.rs +++ b/src/components/main/compositing/mod.rs @@ -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 constellation::SendableFrameTree; +use windowing::WindowMethods; -use azure::azure::AzGLContext; -use std::comm; -use std::comm::{Chan, SharedChan, Port}; -use std::num::Orderable; +use azure::azure_hl::SourceSurfaceMethods; use geom::point::Point2D; -use geom::size::Size2D; 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; -use constellation::SendableFrameTree; +#[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); diff --git a/src/components/main/compositing/quadtree.rs b/src/components/main/compositing/quadtree.rs index 6ca058cc883..a46524e28f2 100644 --- a/src/components/main/compositing/quadtree.rs +++ b/src/components/main/compositing/quadtree.rs @@ -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}); diff --git a/src/components/main/compositing/run.rs b/src/components/main/compositing/run.rs index 7fda95b3d83..0a472af39f4 100644 --- a/src/components/main/compositing/run.rs +++ b/src/components/main/compositing/run.rs @@ -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(), + } } diff --git a/src/components/main/compositing/run_headless.rs b/src/components/main/compositing/run_headless.rs index 477c6d22891..8aed8f7d1b1 100644 --- a/src/components/main/compositing/run_headless.rs +++ b/src/components/main/compositing/run_headless.rs @@ -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, _) => { diff --git a/src/components/main/servo.rc b/src/components/main/servo.rc index e770e6c3972..f09114c4080 100755 --- a/src/components/main/servo.rc +++ b/src/components/main/servo.rc @@ -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; diff --git a/src/components/msg/compositor_msg.rs b/src/components/msg/compositor_msg.rs index 89a01604766..9a0e5883575 100644 --- a/src/components/msg/compositor_msg.rs +++ b/src/components/msg/compositor_msg.rs @@ -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) + } } + diff --git a/src/components/msg/msg.rc b/src/components/msg/msg.rc index 318d91fbdc6..3811f5bffa7 100644 --- a/src/components/msg/msg.rc +++ b/src/components/msg/msg.rc @@ -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; +} + diff --git a/src/components/msg/platform/linux/surface.rs b/src/components/msg/platform/linux/surface.rs new file mode 100644 index 00000000000..3ebd1d5b643 --- /dev/null +++ b/src/components/msg/platform/linux/surface.rs @@ -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)) + } + } +} + diff --git a/src/components/msg/platform/macos/surface.rs b/src/components/msg/platform/macos/surface.rs new file mode 100644 index 00000000000..3bf5f9b5ab7 --- /dev/null +++ b/src/components/msg/platform/macos/surface.rs @@ -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) + } + } +} + diff --git a/src/components/msg/platform/surface.rs b/src/components/msg/platform/surface.rs new file mode 100644 index 00000000000..eee8dfa5598 --- /dev/null +++ b/src/components/msg/platform/surface.rs @@ -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; +} + diff --git a/src/platform/linux/rust-xlib b/src/platform/linux/rust-xlib -Subproject deefb42dd238f54f95bf09f47d19e1c2acfd1c7 +Subproject 71ca7ff52c7f342c95608c00bb0823aa8f39cd3 diff --git a/src/platform/macos/rust-io-surface b/src/platform/macos/rust-io-surface -Subproject 45e3f33cf6a3109b4dbb7bf9e0cb814d6e1e4b5 +Subproject a4bd491ddfd5d17bdcb228cf515cce776e2b823 diff --git a/src/support/azure/rust-azure b/src/support/azure/rust-azure -Subproject 614d65361a29c0100bd662102e1b8d33698ea33 +Subproject b932f0d91dad470baefafe291d4958de4ff01d4 diff --git a/src/support/geom/rust-geom b/src/support/geom/rust-geom -Subproject b5212699f9ad364898b685f5f945b821ee52877 +Subproject 82d56dfc632fe458d126d049fd5abe856170f48 diff --git a/src/support/layers/rust-layers b/src/support/layers/rust-layers -Subproject 716c3326d3d6073c48ecfb301c2ab6e7eeeccfc +Subproject 191fab2cf685671ef73ec13e85bdd889b0e9366 diff --git a/src/support/opengles/rust-opengles b/src/support/opengles/rust-opengles -Subproject 71dcba600f546d6c4daec56659e02c8db9fdf00 +Subproject a5da47f086c06a5ff9e179203fc8dbd5b7bb3c0 diff --git a/src/support/sharegl/sharegl b/src/support/sharegl/sharegl -Subproject 96665d2033e5cda1ae419ce70a4071b61bc58b0 +Subproject 70ba8e70d7828e572dd1a4dd8807e9730db5613 diff --git a/src/support/skia/skia b/src/support/skia/skia -Subproject aeae6a5cd5a6ecb7fd2d3b2f806e9661ce3b2e3 +Subproject 73b73c23696a373b84289711b537eaa00065e4f |