aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorbors-servo <release+servo@mozilla.com>2013-10-25 17:01:22 -0700
committerbors-servo <release+servo@mozilla.com>2013-10-25 17:01:22 -0700
commit4eb1e88e8f997fbf9f40ed030c6c096da115354c (patch)
treebe96d1f487dc93d09139189bd332af9261158e27 /src
parentd5bd4bfdb71415fc399b986359ba6ad0ff3ac3de (diff)
parent3d0bfa50407d820435cbf8ae127d567816f0a8eb (diff)
downloadservo-4eb1e88e8f997fbf9f40ed030c6c096da115354c.tar.gz
servo-4eb1e88e8f997fbf9f40ed030c6c096da115354c.zip
auto merge of #553 : pcwalton/servo/cpu-rendering, r=pcwalton
r? @metajack
Diffstat (limited to 'src')
-rw-r--r--src/components/gfx/buffer_map.rs27
-rw-r--r--src/components/gfx/gfx.rc3
-rw-r--r--src/components/gfx/opts.rs47
-rw-r--r--src/components/gfx/platform/linux/font.rs6
-rw-r--r--src/components/gfx/render_context.rs67
-rw-r--r--src/components/gfx/render_task.rs204
-rw-r--r--src/components/main/compositing/compositor_layer.rs269
-rw-r--r--src/components/main/compositing/mod.rs48
-rw-r--r--src/components/main/compositing/quadtree.rs26
-rw-r--r--src/components/main/compositing/run.rs76
-rw-r--r--src/components/main/compositing/run_headless.rs12
-rwxr-xr-xsrc/components/main/servo.rc2
-rw-r--r--src/components/msg/compositor_msg.rs47
-rw-r--r--src/components/msg/msg.rc28
-rw-r--r--src/components/msg/platform/linux/surface.rs20
-rw-r--r--src/components/msg/platform/macos/surface.rs26
-rw-r--r--src/components/msg/platform/surface.rs12
m---------src/platform/linux/rust-xlib0
m---------src/platform/macos/rust-io-surface0
m---------src/support/azure/rust-azure0
m---------src/support/geom/rust-geom0
m---------src/support/layers/rust-layers0
m---------src/support/opengles/rust-opengles0
m---------src/support/sharegl/sharegl0
m---------src/support/skia/skia0
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