diff options
author | Brian Anderson <banderson@mozilla.com> | 2014-06-06 10:34:30 -0700 |
---|---|---|
committer | Brian Anderson <banderson@mozilla.com> | 2014-06-18 17:35:20 -0700 |
commit | 95a57dfced38d29591660de4f7c6c7f353e1db37 (patch) | |
tree | ef91ffc5751973c312df42effd004dd2c4c8fcd5 /src/components/gfx/render_task.rs | |
parent | bace9fb485438b9b61831d6013c07a273213c7fa (diff) | |
download | servo-95a57dfced38d29591660de4f7c6c7f353e1db37.tar.gz servo-95a57dfced38d29591660de4f7c6c7f353e1db37.zip |
Render in parallel
Diffstat (limited to 'src/components/gfx/render_task.rs')
-rw-r--r-- | src/components/gfx/render_task.rs | 381 |
1 files changed, 235 insertions, 146 deletions
diff --git a/src/components/gfx/render_task.rs b/src/components/gfx/render_task.rs index f8411e3714a..bd0611c1fe7 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 recieve rendered buffers from the workesr + 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,205 @@ impl<C:RenderListener + Send> RenderTask<C> { } } + fn spawn_workers(&mut self, result_tx: Sender<Box<LayerBuffer>>) -> Vec<Sender<WorkerMsg>> { + // 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; + } + + let num_workers = unsafe { rust_get_num_cpus() as uint }; + let mut worker_chans = vec![]; + for render_idx in range(0, num_workers) { + 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 +490,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, - }; + // Distribute the tiles to the workers + let num_tiles = tiles.len(); + let mut worker_idx = 0; - // 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)); + for tile in tiles.move_iter() { - ctx.draw_target.set_transform(&matrix); + 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; - // Clear the buffer. - ctx.clear(); + self.worker_txs.get(worker_idx).send(WorkerRender((tile, display_list, layer_position_x, layer_position_y, scale))); - // Draw the display list. - profile(time::RenderingDrawingCategory, self.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 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 - } - } - }; - - 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 +526,3 @@ impl<C:RenderListener + Send> RenderTask<C> { }) } } - |