servo: Merge #2685 - Revert "Merge pull request #2609 from brson/parallel-render" (from larsbergstrom:revert_2609)
authorLars Bergstrom <lars@lars.com>
Fri, 20 Jun 2014 15:55:46 -0700
changeset 334540 45341642d7043e3466c15cca06c99ec749d323d2
parent 334539 0a474e829bb5ad8267c4b1371fd463ae75827e9f
child 334541 6fa3f507274e32380c3a1e31754e60042b72b1e7
push id31307
push usergszorc@mozilla.com
push dateSat, 04 Feb 2017 00:59:06 +0000
treeherdermozilla-central@94079d43835f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
servo: Merge #2685 - Revert "Merge pull request #2609 from brson/parallel-render" (from larsbergstrom:revert_2609) Source-Repo: https://github.com/servo/servo Source-Revision: afb6bf48e761d2dcaf307a123fe4a8f2a79b645f
servo/src/components/embedding/core.rs
servo/src/components/gfx/render_task.rs
servo/src/components/main/servo.rs
servo/src/components/msg/compositor_msg.rs
servo/src/components/util/opts.rs
--- a/servo/src/components/embedding/core.rs
+++ b/servo/src/components/embedding/core.rs
@@ -54,17 +54,16 @@ pub extern "C" fn cef_run_message_loop()
         profiler_period: None,
         layout_threads: 1,
         //layout_threads: cmp::max(rt::default_sched_threads() * 3 / 4, 1),
         exit_after_load: false,
         output_file: None,
         headless: false,
         hard_fail: false,
         bubble_widths_separately: false,
-        native_threading: false
     };
     native::start(0, 0 as **u8, proc() {
        servo::run(opts);
     });
 }
 
 #[no_mangle]
 pub extern "C" fn cef_quit_message_loop() {
--- a/servo/src/components/gfx/render_task.rs
+++ b/servo/src/components/gfx/render_task.rs
@@ -100,55 +100,49 @@ pub enum GraphicsContext {
     GpuGraphicsContext,
 }
 
 pub struct RenderTask<C> {
     id: PipelineId,
     port: Receiver<Msg>,
     compositor: C,
     constellation_chan: ConstellationChan,
+    font_ctx: Box<FontContext>,
     opts: Opts,
 
     /// A channel to the profiler.
     profiler_chan: ProfilerChan,
 
     /// 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>,
 
     /// Permission to send paint messages to the compositor
     paint_permission: bool,
 
     /// A counter for epoch messages
     epoch: Epoch,
 
-    /// Renderer workers
-    worker_txs: Vec<Sender<WorkerMsg>>,
-
-    /// The receiver on which we receive rendered buffers from the workers
-    worker_result_rx: Receiver<Box<LayerBuffer>>
+    /// A data structure to store unused LayerBuffers
+    buffer_map: BufferMap<Box<LayerBuffer>>,
 }
 
 // If we implement this as a function, we get borrowck errors from borrowing
 // the whole RenderTask struct.
 macro_rules! native_graphics_context(
     ($task:expr) => (
         $task.native_graphics_context.as_ref().expect("Need a graphics context to do rendering")
     )
 )
 
-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,
                      epoch: Epoch,
                      render_layers: &[RenderLayer]) {
     let metadata = render_layers.iter().map(|render_layer| {
         LayerMetadata {
             id: render_layer.id,
@@ -169,51 +163,57 @@ impl<C:RenderListener + Send> RenderTask
                   opts: Opts,
                   profiler_chan: ProfilerChan,
                   shutdown_chan: Sender<()>) {
         let mut builder = TaskBuilder::new().named("RenderTask");
         let ConstellationChan(c) = constellation_chan.clone();
         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,
 
                     graphics_context: if cpu_painting {
                         CpuGraphicsContext
                     } else {
                         GpuGraphicsContext
                     },
 
+                    native_graphics_context: native_graphics_context,
+
                     render_layers: SmallVec1::new(),
 
                     paint_permission: false,
                     epoch: Epoch(0),
-                    worker_txs: vec![],
-                    worker_result_rx: worker_result_rx
+                    buffer_map: BufferMap::new(10000000),
                 };
 
-                // 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;                
+                render_task.start();
 
-                render_task.start();
+                // Destroy all the buffers.
+                match render_task.native_graphics_context.as_ref() {
+                    Some(ctx) => render_task.buffer_map.clear(ctx),
+                    None => (),
+                }
             }
 
             debug!("render_task: shutdown_chan send");
             shutdown_chan.send(());
         });
     }
 
     fn start(&mut self) {
@@ -241,17 +241,17 @@ impl<C:RenderListener + Send> RenderTask
                     if self.epoch == epoch {
                         self.render(tiles, scale, layer_id);
                     } else {
                         debug!("renderer epoch mismatch: {:?} != {:?}", self.epoch, epoch);
                     }
                 }
                 UnusedBufferMsg(unused_buffers) => {
                     for buffer in unused_buffers.move_iter().rev() {
-                        self.worker_txs.get(buffer.render_idx).send(WorkerUnusedBuffer(buffer));
+                        self.buffer_map.insert(native_graphics_context!(self), buffer);
                     }
                 }
                 PaintPermissionGranted => {
                     self.paint_permission = true;
 
                     // Here we assume that the main layer—the layer responsible for the page size—
                     // is the first layer. This is a pretty fragile assumption. It will be fixed
                     // once we use the layers-based scrolling infrastructure for all scrolling.
@@ -262,248 +262,167 @@ impl<C:RenderListener + Send> RenderTask
                                           self.epoch,
                                           self.render_layers.as_slice());
                     }
                 }
                 PaintPermissionRevoked => {
                     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;
                 }
             }
         }
     }
 
-    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!();
 
             // Find the appropriate render layer.
             let render_layer = match self.render_layers.iter().find(|layer| layer.id == layer_id) {
                 Some(render_layer) => render_layer,
                 None => return,
             };
 
             self.compositor.set_render_state(RenderingRenderState);
 
-            // Distribute the tiles to the workers
-            let num_tiles = tiles.len();
-            let mut worker_idx = 0;
+            // 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
+                    }
+                };
 
-            for tile in tiles.move_iter() {
+                {
+                    // 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);
+
+                    // Clear the buffer.
+                    ctx.clear();
+
+                    // 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;
-
-                self.worker_txs.get(worker_idx).send(WorkerRender((tile, display_list, layer_position_x, layer_position_y, scale)));
+                // 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();
 
-                // Round-robin the work
-                worker_idx = (worker_idx + 1) % self.worker_txs.len();
-            }
+                                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);
+                        });
 
-            for _ in range(0, num_tiles) {
-                new_buffers.push(self.worker_result_rx.recv());
+                        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
+                        }
+                    }
+                };
+
+                new_buffers.push(buffer);
             }
 
             let layer_buffer_set = box LayerBufferSet {
                 buffers: new_buffers,
             };
 
             debug!("render_task: returning surface");
             if self.paint_permission {
@@ -512,8 +431,9 @@ impl<C:RenderListener + Send> RenderTask
                 debug!("render_task: RendererReadyMsg send");
                 let ConstellationChan(ref mut c) = self.constellation_chan;
                 c.send(RendererReadyMsg(self.id));
             }
             self.compositor.set_render_state(IdleRenderState);
         })
     }
 }
+
--- a/servo/src/components/main/servo.rs
+++ b/servo/src/components/main/servo.rs
@@ -46,33 +46,43 @@ extern crate sync;
 extern crate time;
 extern crate url;
 
 #[cfg(target_os="macos")]
 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;
 
 
 #[cfg(not(test), not(target_os="android"))]
 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;
 
 
 #[path="compositing/compositor_task.rs"]
 pub mod compositing;
 
 pub mod css {
     mod node_util;
@@ -146,60 +156,30 @@ pub extern "C" fn android_start(argc: in
                 o.cpu_painting = true;
                 run(o);
             },
             None => {}
         }
     })
 }
 
-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);
-    }
-}
-
+#[cfg(not(test))]
 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);
 
     let opts_clone = opts.clone();
     let profiler_chan_clone = profiler_chan.clone();
 
     let (result_chan, result_port) = channel();
-    spawn_main(opts.clone(), compositor_port, profiler_chan, result_port, proc() {
+    pool.spawn(TaskOpts::new(), proc() {
         let opts = &opts_clone;
         // Create a Servo instance.
         let resource_task = ResourceTask();
         // If we are emitting an output file, then we need to block on
         // image load or we risk emitting an output file missing the
         // image.
         let image_cache_task = if opts.output_file.is_some() {
                 SyncImageCacheTask(resource_task.clone())
@@ -225,10 +205,20 @@ pub fn run(opts: opts::Opts) {
 
             let ConstellationChan(ref chan) = constellation_chan;
             chan.send(InitLoadUrlMsg(url));
         }
 
         // 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();
 }
 
--- a/servo/src/components/msg/compositor_msg.rs
+++ b/servo/src/components/msg/compositor_msg.rs
@@ -25,19 +25,16 @@ pub struct LayerBuffer {
     /// The rect in pixels that will be drawn to the screen.
     pub screen_pos: Rect<uint>,
 
     /// The scale at which this tile is rendered
     pub resolution: f32,
 
     /// 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
 /// buffers.
 pub struct LayerBufferSet {
     pub buffers: Vec<Box<LayerBuffer>>
 }
 
--- a/servo/src/components/util/opts.rs
+++ b/servo/src/components/util/opts.rs
@@ -56,51 +56,47 @@ pub struct Opts {
     pub headless: bool,
     pub hard_fail: bool,
 
     /// True if we should bubble intrinsic widths sequentially (`-b`). If this is true, then
     /// intrinsic widths are computed as a separate pass instead of during flow construction. You
     /// 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]) {
     let message = format!("Usage: {} [ options ... ] [URL]\n\twhere options include", app);
     println!("{}", getopts::usage(message.as_slice(), opts));
 }
 
 fn args_fail(msg: &str) {
     io::stderr().write_line(msg).unwrap();
     os::set_exit_status(1);
 }
 
 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("t", "threads", "Number of render threads", "[n-cores]"),
-        getopts::optopt("y", "layout-threads", "Number of layout threads", "1"),
+        getopts::optopt("y", "layout-threads", "Number of threads to use for layout", "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,
         Err(f) => {
             args_fail(f.to_err_msg().as_slice());
             return None;
         }
     };
@@ -143,52 +139,39 @@ pub fn from_cmdline_args(args: &[String]
     };
 
     let device_pixels_per_px = opt_match.opt_str("device-pixel-ratio").map(|dppx_str|
         ScaleFactor(from_str(dppx_str.as_slice()).unwrap())
     );
 
     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 => {
-            // 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 }
-        }
+        None => 1,      // FIXME: Number of cores.
     };
 
     // if only flag is present, default to 5 second period
     let profiler_period = opt_match.opt_default("p", "5").map(|period| {
         from_str(period.as_slice()).unwrap()
     });
 
     let cpu_painting = opt_match.opt_present("c");
 
     let layout_threads: uint = match opt_match.opt_str("y") {
         Some(layout_threads_str) => from_str(layout_threads_str.as_slice()).unwrap(),
         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,
         n_render_threads: n_render_threads,
         cpu_painting: cpu_painting,
         tile_size: tile_size,
         device_pixels_per_px: device_pixels_per_px,
         profiler_period: profiler_period,
         layout_threads: layout_threads,
         exit_after_load: opt_match.opt_present("x"),
         output_file: opt_match.opt_str("o"),
         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
     })
 }