diff --git a/src/components/gfx/compositor.rs b/src/components/gfx/compositor.rs index 3dfb99b4385..ea87d548661 100644 --- a/src/components/gfx/compositor.rs +++ b/src/components/gfx/compositor.rs @@ -24,9 +24,17 @@ pub struct LayerBufferSet { buffers: ~[LayerBuffer] } +/// The status of the renderer. +#[deriving(Eq)] +pub enum RenderState { + IdleRenderState, + RenderingRenderState, +} + /// The interface used to by the renderer to acquire draw targets for each rendered frame and /// submit them to be drawn to the display. pub trait Compositor { fn paint(&self, layer_buffer_set: LayerBufferSet, new_size: Size2D); + fn set_render_state(&self, render_state: RenderState); } diff --git a/src/components/gfx/render_task.rs b/src/components/gfx/render_task.rs index aae8b8ecbd2..4b76e2ac0f6 100644 --- a/src/components/gfx/render_task.rs +++ b/src/components/gfx/render_task.rs @@ -5,7 +5,7 @@ // The task that handles all rendering/painting. use azure::AzFloat; -use compositor::Compositor; +use compositor::{Compositor, IdleRenderState, RenderingRenderState}; use font_context::FontContext; use geom::matrix2d::Matrix2D; use opts::Opts; @@ -122,6 +122,7 @@ impl Renderer { fn render(&mut self, render_layer: RenderLayer) { debug!("renderer: rendering"); + self.compositor.set_render_state(RenderingRenderState); do profile(time::RenderingCategory, self.profiler_chan.clone()) { let layer_buffer_set = do render_layers(&render_layer, &self.opts, @@ -168,6 +169,7 @@ impl Renderer { debug!("renderer: returning surface"); self.compositor.paint(layer_buffer_set, render_layer.size); + self.compositor.set_render_state(IdleRenderState); } } } diff --git a/src/components/main/compositing/mod.rs b/src/components/main/compositing/mod.rs index 33e02afbae0..452f4bbb06d 100644 --- a/src/components/main/compositing/mod.rs +++ b/src/components/main/compositing/mod.rs @@ -8,6 +8,7 @@ use script::script_task::{LoadMsg, ScriptMsg, SendEventMsg}; use windowing::{ApplicationMethods, WindowMethods, WindowMouseEvent, WindowClickEvent}; use windowing::{WindowMouseDownEvent, WindowMouseUpEvent}; +use gfx::compositor::RenderState; use script::dom::event::{Event, ClickEvent, MouseDownEvent, MouseUpEvent}; use script::compositor_interface::{ReadyState, CompositorInterface}; use script::compositor_interface; @@ -20,7 +21,7 @@ use core::util; use geom::matrix::identity; use geom::point::Point2D; use geom::size::Size2D; -use gfx::compositor::{Compositor, LayerBufferSet}; +use gfx::compositor::{Compositor, LayerBufferSet, RenderState}; use layers::layers::{ARGB32Format, BasicImageData, ContainerLayer, ContainerLayerKind, Format}; use layers::layers::{Image, ImageData, ImageLayer, ImageLayerKind, RGB24Format, WithDataFn}; use layers::rendergl; @@ -39,8 +40,8 @@ pub struct CompositorTask { } impl CompositorInterface for CompositorTask { - fn send_compositor_msg(&self, msg: ReadyState) { - let msg = ChangeReadyState(msg); + fn set_ready_state(&self, ready_state: ReadyState) { + let msg = ChangeReadyState(ready_state); self.chan.send(msg); } } @@ -48,8 +49,7 @@ impl CompositorInterface for CompositorTask { impl CompositorTask { /// Starts the compositor. Returns an interface that can be used to communicate with the /// compositor and a port which allows notification when the compositor shuts down. - pub fn new(script_chan: SharedChan, - profiler_chan: ProfilerChan) + pub fn new(script_chan: SharedChan, profiler_chan: ProfilerChan) -> (CompositorTask, Port<()>) { let script_chan = Cell(script_chan); let (shutdown_port, shutdown_chan) = stream(); @@ -76,8 +76,10 @@ pub enum Msg { Exit, /// Requests that the compositor paint the given layer buffer set for the given page size. Paint(LayerBufferSet, Size2D), - /// Alerts the compositor to the current status of page loading + /// Alerts the compositor to the current status of page loading. ChangeReadyState(ReadyState), + /// Alerts the compositor to the current status of rendering. + ChangeRenderState(RenderState), } /// Azure surface wrapping to work with the layers infrastructure. @@ -151,6 +153,7 @@ fn run_main_loop(port: Port, Exit => *done = true, ChangeReadyState(ready_state) => window.set_ready_state(ready_state), + ChangeRenderState(render_state) => window.set_render_state(render_state), Paint(new_layer_buffer_set, new_size) => { debug!("osmain: received new frame"); @@ -356,6 +359,9 @@ impl Compositor for CompositorTask { fn paint(&self, layer_buffer_set: LayerBufferSet, new_size: Size2D) { self.chan.send(Paint(layer_buffer_set, new_size)) } + fn set_render_state(&self, render_state: RenderState) { + self.chan.send(ChangeRenderState(render_state)) + } } /// A function for spawning into the platform's main thread. diff --git a/src/components/main/engine.rs b/src/components/main/engine.rs index 8abbcaef096..921688bd061 100644 --- a/src/components/main/engine.rs +++ b/src/components/main/engine.rs @@ -78,7 +78,7 @@ impl Engine { script_chan.take(), engine_chan_clone.clone(), |msg: ReadyState| { - compositor_clone.send_compositor_msg(msg) + compositor_clone.set_ready_state(msg) }, layout_task.clone(), resource_task.clone(), diff --git a/src/components/main/layout/layout_task.rs b/src/components/main/layout/layout_task.rs index b96656abbd6..9b896c1253e 100644 --- a/src/components/main/layout/layout_task.rs +++ b/src/components/main/layout/layout_task.rs @@ -38,7 +38,7 @@ use script::layout_interface::{ContentBoxesQuery, ContentBoxesResponse, ExitMsg, use script::layout_interface::{LayoutResponse, LayoutTask, MatchSelectorsDocumentDamage, Msg}; use script::layout_interface::{QueryMsg, Reflow, ReflowDocumentDamage, ReflowForDisplay}; use script::layout_interface::{ReflowMsg}; -use script::script_task::{ScriptMsg, SendEventMsg}; +use script::script_task::{ReflowCompleteMsg, ScriptMsg, SendEventMsg}; use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg}; use servo_net::local_image_cache::LocalImageCache; use servo_util::tree::{TreeNodeRef, TreeUtils}; @@ -255,7 +255,11 @@ impl Layout { debug!("%?", layout_root.dump()); // Tell script that we're done. + // + // FIXME(pcwalton): This should probably be *one* channel, but we can't fix this without + // either select or a filtered recv() that only looks for messages of a given type. data.script_join_chan.send(()); + data.script_chan.send(ReflowCompleteMsg); } /// Handles a query from the script task. This is the main routine that DOM functions like diff --git a/src/components/main/platform/common/glut_windowing.rs b/src/components/main/platform/common/glut_windowing.rs index 275e20bdf42..0b35f24ae7c 100644 --- a/src/components/main/platform/common/glut_windowing.rs +++ b/src/components/main/platform/common/glut_windowing.rs @@ -16,10 +16,11 @@ use core::cell::Cell; use core::libc::c_int; use geom::point::Point2D; use geom::size::Size2D; +use gfx::compositor::{IdleRenderState, RenderState, RenderingRenderState}; use glut::glut::{ACTIVE_CTRL, DOUBLE, HAVE_PRECISE_MOUSE_WHEEL, WindowHeight, WindowWidth}; use glut::glut; use glut::machack; -use script::compositor_interface::{FinishedLoading, Loading, Rendering, ReadyState}; +use script::compositor_interface::{FinishedLoading, Loading, PerformingLayout, ReadyState}; static THROBBER: [char, ..8] = [ '⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷' ]; @@ -51,6 +52,7 @@ pub struct Window { mouse_down_point: @mut Point2D, ready_state: ReadyState, + render_state: RenderState, throbber_frame: u8, } @@ -78,6 +80,7 @@ impl WindowMethods for Window { mouse_down_point: @mut Point2D(0, 0), ready_state: FinishedLoading, + render_state: IdleRenderState, throbber_frame: 0, }; @@ -190,6 +193,12 @@ impl WindowMethods for Window { self.ready_state = ready_state; self.update_window_title() } + + /// Sets the render state. + pub fn set_render_state(@mut self, render_state: RenderState) { + self.render_state = render_state; + self.update_window_title() + } } impl Window { @@ -200,10 +209,19 @@ impl Window { Loading => { glut::set_window_title(self.glut_window, fmt!("%c Loading — Servo", throbber)) } - Rendering => { - glut::set_window_title(self.glut_window, fmt!("%c Rendering — Servo", throbber)) + PerformingLayout => { + glut::set_window_title(self.glut_window, + fmt!("%c Performing Layout — Servo", throbber)) + } + FinishedLoading => { + match self.render_state { + RenderingRenderState => { + glut::set_window_title(self.glut_window, + fmt!("%c Rendering — Servo", throbber)) + } + IdleRenderState => glut::set_window_title(self.glut_window, "Servo"), + } } - FinishedLoading => glut::set_window_title(self.glut_window, "Servo"), } } diff --git a/src/components/main/windowing.rs b/src/components/main/windowing.rs index 8cced181ed3..888851cbbf1 100644 --- a/src/components/main/windowing.rs +++ b/src/components/main/windowing.rs @@ -6,6 +6,7 @@ use geom::point::Point2D; use geom::size::Size2D; +use gfx::compositor::RenderState; use script::compositor_interface::ReadyState; pub enum WindowMouseEvent { @@ -64,5 +65,7 @@ pub trait WindowMethods { pub fn set_needs_display(@mut self); /// Sets the ready state of the current page. pub fn set_ready_state(@mut self, ready_state: ReadyState); + /// Sets the render state of the current page. + pub fn set_render_state(@mut self, render_state: RenderState); } diff --git a/src/components/script/compositor_interface.rs b/src/components/script/compositor_interface.rs index 78e74f5fee1..8e48e9f1492 100644 --- a/src/components/script/compositor_interface.rs +++ b/src/components/script/compositor_interface.rs @@ -8,12 +8,12 @@ pub enum ReadyState { /// Informs the compositor that a page is loading. Used for setting status Loading, - /// Informs the compositor that a page is rendering. Used for setting status - Rendering, + /// Informs the compositor that a page is performing layout. Used for setting status + PerformingLayout, /// Informs the compositor that a page is finished loading. Used for setting status FinishedLoading, } -pub trait CompositorInterface: Clone { - fn send_compositor_msg(&self, ReadyState); +pub trait CompositorInterface : Clone { + fn set_ready_state(&self, ReadyState); } diff --git a/src/components/script/script_task.rs b/src/components/script/script_task.rs index d3aba93a8f9..69890abb5cb 100644 --- a/src/components/script/script_task.rs +++ b/src/components/script/script_task.rs @@ -5,7 +5,7 @@ /// The script task is the task that owns the DOM in memory, runs JavaScript, and spawns parsing /// and layout tasks. -use compositor_interface::{ReadyState, Loading, Rendering, FinishedLoading}; +use compositor_interface::{ReadyState, Loading, PerformingLayout, FinishedLoading}; use dom::bindings::utils::GlobalStaticData; use dom::document::Document; use dom::element::Element; @@ -54,6 +54,8 @@ pub enum ScriptMsg { SendEventMsg(Event), /// Fires a JavaScript timeout. FireTimerMsg(~TimerData), + /// Notifies script that reflow is finished. + ReflowCompleteMsg, /// Exits the engine. ExitMsg, } @@ -262,6 +264,10 @@ impl ScriptContext { self.handle_fire_timer_msg(timer_data); true } + ReflowCompleteMsg => { + self.handle_reflow_complete_msg(); + true + } ExitMsg => { self.handle_exit_msg(); false @@ -306,6 +312,12 @@ impl ScriptContext { self.reflow(ReflowForScriptQuery) } + /// Handles a notification that reflow completed. + fn handle_reflow_complete_msg(&mut self) { + self.layout_join_port = None; + self.set_ready_state(FinishedLoading) + } + /// Handles a request to exit the script task and shut down layout. fn handle_exit_msg(&mut self) { self.join_layout(); @@ -318,7 +330,7 @@ impl ScriptContext { // tells the compositor when loading starts and finishes // FIXME ~compositor_interface doesn't work right now, which is why this is necessary - fn send_compositor_msg(&self, msg: ReadyState) { + fn set_ready_state(&self, msg: ReadyState) { (self.compositor_task)(msg); } @@ -333,7 +345,7 @@ impl ScriptContext { self.bindings_initialized = true } - self.send_compositor_msg(Loading); + self.set_ready_state(Loading); // Parse HTML. // // Note: We can parse the next document in parallel with any previous documents. @@ -374,7 +386,6 @@ impl ScriptContext { url: url }); - self.send_compositor_msg(Rendering); // Perform the initial reflow. self.damage = Some(DocumentDamage { root: root_node, @@ -392,7 +403,6 @@ impl ScriptContext { ~"???", 1); } - self.send_compositor_msg(FinishedLoading); } /// Sends a ping to layout and waits for the response. The response will arrive when the @@ -426,6 +436,9 @@ impl ScriptContext { // Now, join the layout so that they will see the latest changes we have made. self.join_layout(); + // Tell the user that we're performing layout. + self.set_ready_state(PerformingLayout); + // Layout will let us know when it's done. let (join_port, join_chan) = comm::stream(); self.layout_join_port = Some(join_port); @@ -438,8 +451,8 @@ impl ScriptContext { document_root: root_frame.document.root, url: copy root_frame.url, goal: goal, - script_chan: self.script_chan.clone(), window_size: self.window_size, + script_chan: self.script_chan.clone(), script_join_chan: join_chan, damage: replace(&mut self.damage, None).unwrap(), };