diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/components/embedding/core.rs | 1 | ||||
-rw-r--r-- | src/components/gfx/render_task.rs | 372 | ||||
-rw-r--r-- | src/components/main/servo.rs | 60 | ||||
-rw-r--r-- | src/components/msg/compositor_msg.rs | 3 | ||||
-rw-r--r-- | src/components/util/opts.rs | 27 |
5 files changed, 287 insertions, 176 deletions
diff --git a/src/components/embedding/core.rs b/src/components/embedding/core.rs index 4529d3799f6..301e5312942 100644 --- a/src/components/embedding/core.rs +++ b/src/components/embedding/core.rs @@ -59,6 +59,7 @@ pub extern "C" fn cef_run_message_loop() { headless: false, hard_fail: false, bubble_widths_separately: false, + native_threading: false }; native::start(0, 0 as **u8, proc() { servo::run(opts); diff --git a/src/components/gfx/render_task.rs b/src/components/gfx/render_task.rs index f8411e3714a..9420c08d9ae 100644 --- a/src/components/gfx/render_task.rs +++ b/src/components/gfx/render_task.rs @@ -105,7 +105,6 @@ pub struct RenderTask<C> { port: Receiver<Msg>, compositor: C, constellation_chan: ConstellationChan, - font_ctx: Box<FontContext>, opts: Opts, /// A channel to the profiler. @@ -114,9 +113,6 @@ pub struct RenderTask<C> { /// The graphics context to use. graphics_context: GraphicsContext, - /// The native graphics context. - native_graphics_context: Option<NativePaintingGraphicsContext>, - /// The layers to be rendered. render_layers: SmallVec1<RenderLayer>, @@ -126,8 +122,11 @@ pub struct RenderTask<C> { /// A counter for epoch messages epoch: Epoch, - /// A data structure to store unused LayerBuffers - buffer_map: BufferMap<Box<LayerBuffer>>, + /// Renderer workers + worker_txs: Vec<Sender<WorkerMsg>>, + + /// The receiver on which we receive rendered buffers from the workers + worker_result_rx: Receiver<Box<LayerBuffer>> } // If we implement this as a function, we get borrowck errors from borrowing @@ -138,6 +137,13 @@ macro_rules! native_graphics_context( ) ) +enum WorkerMsg { + // This is tupled so all the data can be pulled out of the message as one variable + WorkerRender((BufferRequest, Arc<DisplayList>, uint, uint, f32)), + WorkerUnusedBuffer(Box<LayerBuffer>), + WorkerExit(Sender<()>) +} + fn initialize_layers<C:RenderListener>( compositor: &mut C, pipeline_id: PipelineId, @@ -168,22 +174,17 @@ impl<C:RenderListener + Send> RenderTask<C> { send_on_failure(&mut builder, FailureMsg(failure_msg), c); builder.spawn(proc() { - { // Ensures RenderTask and graphics context are destroyed before shutdown msg - let native_graphics_context = compositor.get_graphics_metadata().map( - |md| NativePaintingGraphicsContext::from_metadata(&md)); + { let cpu_painting = opts.cpu_painting; + let (worker_result_tx, worker_result_rx) = channel(); + // FIXME: rust/#5967 let mut render_task = RenderTask { id: id, port: port, compositor: compositor, constellation_chan: constellation_chan, - font_ctx: box FontContext::new(FontContextInfo { - backend: opts.render_backend.clone(), - needs_font_list: false, - profiler_chan: profiler_chan.clone(), - }), opts: opts, profiler_chan: profiler_chan, @@ -193,22 +194,21 @@ impl<C:RenderListener + Send> RenderTask<C> { GpuGraphicsContext }, - native_graphics_context: native_graphics_context, - render_layers: SmallVec1::new(), paint_permission: false, epoch: Epoch(0), - buffer_map: BufferMap::new(10000000), + worker_txs: vec![], + worker_result_rx: worker_result_rx }; - render_task.start(); + // Now spawn the workers. We're only doing this after creating + // the RenderTask object because spawn_workers was originally + // written to be run afterward, and was refactored like so. + let worker_txs = render_task.spawn_workers(worker_result_tx); + render_task.worker_txs = worker_txs; - // Destroy all the buffers. - match render_task.native_graphics_context.as_ref() { - Some(ctx) => render_task.buffer_map.clear(ctx), - None => (), - } + render_task.start(); } debug!("render_task: shutdown_chan send"); @@ -246,7 +246,7 @@ impl<C:RenderListener + Send> RenderTask<C> { } UnusedBufferMsg(unused_buffers) => { for buffer in unused_buffers.move_iter().rev() { - self.buffer_map.insert(native_graphics_context!(self), buffer); + self.worker_txs.get(buffer.render_idx).send(WorkerUnusedBuffer(buffer)); } } PaintPermissionGranted => { @@ -267,6 +267,11 @@ impl<C:RenderListener + Send> RenderTask<C> { self.paint_permission = false; } ExitMsg(response_ch) => { + for worker_tx in self.worker_txs.iter() { + let (tx, rx) = channel(); + worker_tx.send(WorkerExit(tx)); + rx.recv(); + } debug!("render_task: exitmsg response send"); response_ch.map(|ch| ch.send(())); break; @@ -275,12 +280,196 @@ impl<C:RenderListener + Send> RenderTask<C> { } } + fn spawn_workers(&mut self, result_tx: Sender<Box<LayerBuffer>>) -> Vec<Sender<WorkerMsg>> { + let mut worker_chans = vec![]; + for render_idx in range(0, self.opts.n_render_threads) { + let (tx, rx) = channel(); + let result_tx = result_tx.clone(); + + let opts = self.opts.clone(); + let graphics_context = self.graphics_context; + let render_backend = self.opts.render_backend; + let native_graphics_context = self.compositor.get_graphics_metadata().map( + |md| NativePaintingGraphicsContext::from_metadata(&md)); + let native_graphics_context = native_graphics_context.expect("need native graphics context"); + let native_graphics_context = Some(native_graphics_context); + let font_ctx_info = FontContextInfo { + backend: self.opts.render_backend, + needs_font_list: false, + profiler_chan: self.profiler_chan.clone(), + }; + let profiler_chan = self.profiler_chan.clone(); + let buffer_map: BufferMap<Box<LayerBuffer>> = BufferMap::new(10000000); + + spawn(proc() { + let mut buffer_map = buffer_map; + let mut native_graphics_context = native_graphics_context; + loop { + let render_msg: WorkerMsg = rx.recv(); + let render_data = match render_msg { + WorkerRender(render_data) => render_data, + WorkerUnusedBuffer(buffer) => { + buffer_map.insert(native_graphics_context.get_ref(), buffer); + continue + } + WorkerExit(tx) => { + // Cleanup and tell the RenderTask we're done + buffer_map.clear(native_graphics_context.get_ref()); + drop(native_graphics_context.take_unwrap()); + tx.send(()); + break + } + }; + let (tile, + display_list, + layer_position_x, + layer_position_y, + scale) = render_data; + + // Optimize the display list for this tile. + let page_rect_au = geometry::f32_rect_to_au_rect(tile.page_rect); + let optimizer = DisplayListOptimizer::new(display_list, + page_rect_au); + let display_list = optimizer.optimize(); + + let width = tile.screen_rect.size.width; + let height = tile.screen_rect.size.height; + + let size = Size2D(width as i32, height as i32); + let draw_target = match graphics_context { + CpuGraphicsContext => { + DrawTarget::new(render_backend, size, B8G8R8A8) + } + GpuGraphicsContext => { + // FIXME(pcwalton): Cache the components of draw targets + // (texture color buffer, renderbuffers) instead of recreating them. + let draw_target = + DrawTarget::new_with_fbo(render_backend, + native_graphics_context.get_ref(), + size, + B8G8R8A8); + draw_target.make_current(); + draw_target + } + }; + + { + let mut font_ctx = box FontContext::new(font_ctx_info.clone()); + // Build the render context. + let mut ctx = RenderContext { + draw_target: &draw_target, + font_ctx: &mut font_ctx, + opts: &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(-(tile.page_rect.origin.x) as AzFloat, + -(tile.page_rect.origin.y) as AzFloat); + let matrix = matrix.translate(-(layer_position_x as AzFloat), + -(layer_position_y as AzFloat)); + + ctx.draw_target.set_transform(&matrix); + + // Clear the buffer. + ctx.clear(); + + // Draw the display list. + profile(time::RenderingDrawingCategory, profiler_chan.clone(), || { + display_list.draw_into_context(&mut ctx); + 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 graphics_context { + CpuGraphicsContext => { + let maybe_buffer = buffer_map.find(tile.screen_rect.size); + let buffer = match maybe_buffer { + 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( + native_graphics_context.get_ref(), + Size2D(width as i32, height as i32), + width as i32 * 4); + native_surface.mark_wont_leak(); + + box LayerBuffer { + native_surface: native_surface, + rect: tile.page_rect, + screen_pos: tile.screen_rect, + resolution: scale, + stride: (width * 4) as uint, + render_idx: render_idx + } + } + }; + + draw_target.snapshot().get_data_surface().with_data(|data| { + buffer.native_surface.upload(native_graphics_context.get_ref(), 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(); + + box LayerBuffer { + native_surface: native_surface, + rect: tile.page_rect, + screen_pos: tile.screen_rect, + resolution: scale, + stride: (width * 4) as uint, + render_idx: render_idx + } + } + }; + + result_tx.send(buffer); + } + }); + worker_chans.push(tx) + } + + return worker_chans; + } + /// Renders one layer and sends the tiles back to the layer. /// /// FIXME(pcwalton): We will probably want to eventually send all layers belonging to a page in /// one transaction, to avoid the user seeing inconsistent states. fn render(&mut self, tiles: Vec<BufferRequest>, scale: f32, layer_id: LayerId) { + let mut tiles = Some(tiles); time::profile(time::RenderingCategory, self.profiler_chan.clone(), || { + let tiles = tiles.take_unwrap(); // FIXME: Try not to create a new array here. let mut new_buffers = vec!(); @@ -292,132 +481,24 @@ impl<C:RenderListener + Send> RenderTask<C> { self.compositor.set_render_state(RenderingRenderState); - // Divide up the layer into tiles. - for tile in tiles.iter() { - // Optimize the display list for this tile. - let page_rect_au = geometry::f32_rect_to_au_rect(tile.page_rect); - let optimizer = DisplayListOptimizer::new(render_layer.display_list.clone(), - page_rect_au); - let display_list = optimizer.optimize(); - - let width = tile.screen_rect.size.width; - let height = tile.screen_rect.size.height; - - 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) - } - 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, - native_graphics_context!(self), - size, - B8G8R8A8); - draw_target.make_current(); - draw_target - } - }; - - { - // Build the render context. - let mut ctx = RenderContext { - draw_target: &draw_target, - font_ctx: &mut self.font_ctx, - 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(-(tile.page_rect.origin.x) as AzFloat, - -(tile.page_rect.origin.y) as AzFloat); - let matrix = matrix.translate(-(render_layer.position.origin.x as AzFloat), - -(render_layer.position.origin.y as AzFloat)); - - ctx.draw_target.set_transform(&matrix); + // Distribute the tiles to the workers + let num_tiles = tiles.len(); + let mut worker_idx = 0; - // Clear the buffer. - ctx.clear(); + for tile in tiles.move_iter() { - // Draw the display list. - profile(time::RenderingDrawingCategory, self.profiler_chan.clone(), || { - display_list.draw_into_context(&mut ctx); - ctx.draw_target.flush(); - }); - } + let display_list = render_layer.display_list.clone(); + let layer_position_x = render_layer.position.origin.x; + let layer_position_y = render_layer.position.origin.y; - // 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( - native_graphics_context!(self), - Size2D(width as i32, height as i32), - width as i32 * 4); - native_surface.mark_wont_leak(); - - box LayerBuffer { - native_surface: native_surface, - rect: tile.page_rect, - screen_pos: tile.screen_rect, - resolution: scale, - stride: (width * 4) as uint - } - } - }; + self.worker_txs.get(worker_idx).send(WorkerRender((tile, display_list, layer_position_x, layer_position_y, scale))); - draw_target.snapshot().get_data_surface().with_data(|data| { - buffer.native_surface.upload(native_graphics_context!(self), 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(); - - box LayerBuffer { - native_surface: native_surface, - rect: tile.page_rect, - screen_pos: tile.screen_rect, - resolution: scale, - stride: (width * 4) as uint - } - } - }; + // Round-robin the work + worker_idx = (worker_idx + 1) % self.worker_txs.len(); + } - new_buffers.push(buffer); + for _ in range(0, num_tiles) { + new_buffers.push(self.worker_result_rx.recv()); } let layer_buffer_set = box LayerBufferSet { @@ -436,4 +517,3 @@ impl<C:RenderListener + Send> RenderTask<C> { }) } } - diff --git a/src/components/main/servo.rs b/src/components/main/servo.rs index c7a2a6150b7..d7c6fda9b89 100644 --- a/src/components/main/servo.rs +++ b/src/components/main/servo.rs @@ -51,23 +51,15 @@ extern crate core_graphics; #[cfg(target_os="macos")] extern crate core_text; -#[cfg(not(test))] use compositing::{CompositorChan, CompositorTask}; -#[cfg(not(test))] use constellation::Constellation; -#[cfg(not(test))] use servo_msg::constellation_msg::{ConstellationChan, InitLoadUrlMsg}; -#[cfg(not(test))] use servo_net::image_cache_task::{ImageCacheTask, SyncImageCacheTask}; -#[cfg(not(test))] use servo_net::resource_task::ResourceTask; -#[cfg(not(test))] use servo_util::time::Profiler; -#[cfg(not(test))] use servo_util::opts; -#[cfg(not(test))] use servo_util::url::parse_url; @@ -75,9 +67,7 @@ use servo_util::url::parse_url; use std::os; #[cfg(not(test), target_os="android")] use std::str; -#[cfg(not(test))] use std::task::TaskOpts; -#[cfg(not(test))] use url::Url; @@ -161,11 +151,41 @@ pub extern "C" fn android_start(argc: int, argv: **u8) -> int { }) } -#[cfg(not(test))] +fn spawn_main(opts: opts::Opts, + compositor_port: Receiver<compositing::Msg>, + profiler_chan: servo_util::time::ProfilerChan, + result_port: Receiver<ConstellationChan>, + p: proc(): Send) { + if !opts.native_threading { + let mut pool_config = green::PoolConfig::new(); + pool_config.event_loop_factory = rustuv::event_loop; + let mut pool = green::SchedPool::new(pool_config); + + pool.spawn(TaskOpts::new(), p); + + let constellation_chan = result_port.recv(); + + debug!("preparing to enter main loop"); + CompositorTask::create(opts, + compositor_port, + constellation_chan, + profiler_chan); + + pool.shutdown(); + + } else { + native::task::spawn(p); + let constellation_chan = result_port.recv(); + + debug!("preparing to enter main loop"); + CompositorTask::create(opts, + compositor_port, + constellation_chan, + profiler_chan); + } +} + pub fn run(opts: opts::Opts) { - let mut pool_config = green::PoolConfig::new(); - pool_config.event_loop_factory = rustuv::event_loop; - let mut pool = green::SchedPool::new(pool_config); let (compositor_port, compositor_chan) = CompositorChan::new(); let profiler_chan = Profiler::create(opts.profiler_period); @@ -174,7 +194,7 @@ pub fn run(opts: opts::Opts) { let profiler_chan_clone = profiler_chan.clone(); let (result_chan, result_port) = channel(); - pool.spawn(TaskOpts::new(), proc() { + spawn_main(opts.clone(), compositor_port, profiler_chan, result_port, proc() { let opts = &opts_clone; // Create a Servo instance. let resource_task = ResourceTask(); @@ -210,15 +230,5 @@ pub fn run(opts: opts::Opts) { // Send the constallation Chan as the result result_chan.send(constellation_chan); }); - - let constellation_chan = result_port.recv(); - - debug!("preparing to enter main loop"); - CompositorTask::create(opts, - compositor_port, - constellation_chan, - profiler_chan); - - pool.shutdown(); } diff --git a/src/components/msg/compositor_msg.rs b/src/components/msg/compositor_msg.rs index d96798b3184..8e2991e1eee 100644 --- a/src/components/msg/compositor_msg.rs +++ b/src/components/msg/compositor_msg.rs @@ -30,6 +30,9 @@ pub struct LayerBuffer { /// NB: stride is in pixels, like OpenGL GL_UNPACK_ROW_LENGTH. pub stride: uint, + + /// Used by the RenderTask to route buffers to the correct graphics context for recycling + pub render_idx: uint } /// A set of layer buffers. This is an atomic unit used to switch between the front and back diff --git a/src/components/util/opts.rs b/src/components/util/opts.rs index 166ff0a9a9a..111111f124c 100644 --- a/src/components/util/opts.rs +++ b/src/components/util/opts.rs @@ -61,6 +61,9 @@ pub struct Opts { /// may wish to turn this flag on in order to benchmark style recalculation against other /// browser engines. pub bubble_widths_separately: bool, + + /// Use native threads instead of green threads + pub native_threading: bool } fn print_usage(app: &str, opts: &[getopts::OptGroup]) { @@ -77,21 +80,22 @@ pub fn from_cmdline_args(args: &[String]) -> Option<Opts> { let app_name = args[0].to_str(); let args = args.tail(); - let opts = vec!( + let opts = vec![ getopts::optflag("c", "cpu", "CPU rendering"), getopts::optopt("o", "output", "Output file", "output.png"), getopts::optopt("r", "rendering", "Rendering backend", "direct2d|core-graphics|core-graphics-accelerated|cairo|skia."), getopts::optopt("s", "size", "Size of tiles", "512"), getopts::optopt("", "device-pixel-ratio", "Device pixels per px", ""), - getopts::optopt("t", "threads", "Number of render threads", "1"), getopts::optflagopt("p", "profile", "Profiler flag and output interval", "10"), getopts::optflag("x", "exit", "Exit after load flag"), - getopts::optopt("y", "layout-threads", "Number of threads to use for layout", "1"), + getopts::optopt("t", "threads", "Number of render threads", "[n-cores]"), + getopts::optopt("y", "layout-threads", "Number of layout threads", "1"), getopts::optflag("z", "headless", "Headless mode"), getopts::optflag("f", "hard-fail", "Exit on task failure instead of displaying about:failure"), getopts::optflag("b", "bubble-widths", "Bubble intrinsic widths separately like other engines"), + getopts::optflag("n", "native-threading", "Use native threading instead of green threading"), getopts::optflag("h", "help", "Print this message") - ); + ]; let opt_match = match getopts::getopts(args, opts.as_slice()) { Ok(m) => m, @@ -144,7 +148,17 @@ pub fn from_cmdline_args(args: &[String]) -> Option<Opts> { let n_render_threads: uint = match opt_match.opt_str("t") { Some(n_render_threads_str) => from_str(n_render_threads_str.as_slice()).unwrap(), - None => 1, // FIXME: Number of cores. + None => { + // FIXME (rust/14707): This still isn't exposed publicly via std::rt?? + // FIXME (rust/14704): Terrible name for this lint, which here is allowing + // Rust types, not C types + #[allow(ctypes)] + extern { + fn rust_get_num_cpus() -> uint; + } + + unsafe { rust_get_num_cpus() as uint } + } }; // if only flag is present, default to 5 second period @@ -159,6 +173,8 @@ pub fn from_cmdline_args(args: &[String]) -> Option<Opts> { None => cmp::max(rt::default_sched_threads() * 3 / 4, 1), }; + let native_threading = opt_match.opt_present("h") || opt_match.opt_present("help"); + Some(Opts { urls: urls, render_backend: render_backend, @@ -173,5 +189,6 @@ pub fn from_cmdline_args(args: &[String]) -> Option<Opts> { headless: opt_match.opt_present("z"), hard_fail: opt_match.opt_present("f"), bubble_widths_separately: opt_match.opt_present("b"), + native_threading: native_threading }) } |