Add a spinner for layout

This commit is contained in:
Patrick Walton 2013-06-11 16:10:39 -07:00
parent 96b9be6c33
commit 204c5b663a
9 changed files with 77 additions and 23 deletions

View file

@ -24,9 +24,17 @@ pub struct LayerBufferSet {
buffers: ~[LayerBuffer] 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 /// The interface used to by the renderer to acquire draw targets for each rendered frame and
/// submit them to be drawn to the display. /// submit them to be drawn to the display.
pub trait Compositor { pub trait Compositor {
fn paint(&self, layer_buffer_set: LayerBufferSet, new_size: Size2D<uint>); fn paint(&self, layer_buffer_set: LayerBufferSet, new_size: Size2D<uint>);
fn set_render_state(&self, render_state: RenderState);
} }

View file

@ -5,7 +5,7 @@
// The task that handles all rendering/painting. // The task that handles all rendering/painting.
use azure::AzFloat; use azure::AzFloat;
use compositor::Compositor; use compositor::{Compositor, IdleRenderState, RenderingRenderState};
use font_context::FontContext; use font_context::FontContext;
use geom::matrix2d::Matrix2D; use geom::matrix2d::Matrix2D;
use opts::Opts; use opts::Opts;
@ -122,6 +122,7 @@ impl<C: Compositor + Owned> Renderer<C> {
fn render(&mut self, render_layer: RenderLayer) { fn render(&mut self, render_layer: RenderLayer) {
debug!("renderer: rendering"); debug!("renderer: rendering");
self.compositor.set_render_state(RenderingRenderState);
do profile(time::RenderingCategory, self.profiler_chan.clone()) { do profile(time::RenderingCategory, self.profiler_chan.clone()) {
let layer_buffer_set = do render_layers(&render_layer, let layer_buffer_set = do render_layers(&render_layer,
&self.opts, &self.opts,
@ -168,6 +169,7 @@ impl<C: Compositor + Owned> Renderer<C> {
debug!("renderer: returning surface"); debug!("renderer: returning surface");
self.compositor.paint(layer_buffer_set, render_layer.size); self.compositor.paint(layer_buffer_set, render_layer.size);
self.compositor.set_render_state(IdleRenderState);
} }
} }
} }

View file

@ -8,6 +8,7 @@ use script::script_task::{LoadMsg, ScriptMsg, SendEventMsg};
use windowing::{ApplicationMethods, WindowMethods, WindowMouseEvent, WindowClickEvent}; use windowing::{ApplicationMethods, WindowMethods, WindowMouseEvent, WindowClickEvent};
use windowing::{WindowMouseDownEvent, WindowMouseUpEvent}; use windowing::{WindowMouseDownEvent, WindowMouseUpEvent};
use gfx::compositor::RenderState;
use script::dom::event::{Event, ClickEvent, MouseDownEvent, MouseUpEvent}; use script::dom::event::{Event, ClickEvent, MouseDownEvent, MouseUpEvent};
use script::compositor_interface::{ReadyState, CompositorInterface}; use script::compositor_interface::{ReadyState, CompositorInterface};
use script::compositor_interface; use script::compositor_interface;
@ -20,7 +21,7 @@ use core::util;
use geom::matrix::identity; use geom::matrix::identity;
use geom::point::Point2D; use geom::point::Point2D;
use geom::size::Size2D; 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::{ARGB32Format, BasicImageData, ContainerLayer, ContainerLayerKind, Format};
use layers::layers::{Image, ImageData, ImageLayer, ImageLayerKind, RGB24Format, WithDataFn}; use layers::layers::{Image, ImageData, ImageLayer, ImageLayerKind, RGB24Format, WithDataFn};
use layers::rendergl; use layers::rendergl;
@ -39,8 +40,8 @@ pub struct CompositorTask {
} }
impl CompositorInterface for CompositorTask { impl CompositorInterface for CompositorTask {
fn send_compositor_msg(&self, msg: ReadyState) { fn set_ready_state(&self, ready_state: ReadyState) {
let msg = ChangeReadyState(msg); let msg = ChangeReadyState(ready_state);
self.chan.send(msg); self.chan.send(msg);
} }
} }
@ -48,8 +49,7 @@ impl CompositorInterface for CompositorTask {
impl CompositorTask { impl CompositorTask {
/// Starts the compositor. Returns an interface that can be used to communicate with the /// 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. /// compositor and a port which allows notification when the compositor shuts down.
pub fn new(script_chan: SharedChan<ScriptMsg>, pub fn new(script_chan: SharedChan<ScriptMsg>, profiler_chan: ProfilerChan)
profiler_chan: ProfilerChan)
-> (CompositorTask, Port<()>) { -> (CompositorTask, Port<()>) {
let script_chan = Cell(script_chan); let script_chan = Cell(script_chan);
let (shutdown_port, shutdown_chan) = stream(); let (shutdown_port, shutdown_chan) = stream();
@ -76,8 +76,10 @@ pub enum Msg {
Exit, Exit,
/// Requests that the compositor paint the given layer buffer set for the given page size. /// Requests that the compositor paint the given layer buffer set for the given page size.
Paint(LayerBufferSet, Size2D<uint>), Paint(LayerBufferSet, Size2D<uint>),
/// Alerts the compositor to the current status of page loading /// Alerts the compositor to the current status of page loading.
ChangeReadyState(ReadyState), ChangeReadyState(ReadyState),
/// Alerts the compositor to the current status of rendering.
ChangeRenderState(RenderState),
} }
/// Azure surface wrapping to work with the layers infrastructure. /// Azure surface wrapping to work with the layers infrastructure.
@ -151,6 +153,7 @@ fn run_main_loop(port: Port<Msg>,
Exit => *done = true, Exit => *done = true,
ChangeReadyState(ready_state) => window.set_ready_state(ready_state), 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) => { Paint(new_layer_buffer_set, new_size) => {
debug!("osmain: received new frame"); debug!("osmain: received new frame");
@ -356,6 +359,9 @@ impl Compositor for CompositorTask {
fn paint(&self, layer_buffer_set: LayerBufferSet, new_size: Size2D<uint>) { fn paint(&self, layer_buffer_set: LayerBufferSet, new_size: Size2D<uint>) {
self.chan.send(Paint(layer_buffer_set, new_size)) 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. /// A function for spawning into the platform's main thread.

View file

@ -78,7 +78,7 @@ impl Engine {
script_chan.take(), script_chan.take(),
engine_chan_clone.clone(), engine_chan_clone.clone(),
|msg: ReadyState| { |msg: ReadyState| {
compositor_clone.send_compositor_msg(msg) compositor_clone.set_ready_state(msg)
}, },
layout_task.clone(), layout_task.clone(),
resource_task.clone(), resource_task.clone(),

View file

@ -38,7 +38,7 @@ use script::layout_interface::{ContentBoxesQuery, ContentBoxesResponse, ExitMsg,
use script::layout_interface::{LayoutResponse, LayoutTask, MatchSelectorsDocumentDamage, Msg}; use script::layout_interface::{LayoutResponse, LayoutTask, MatchSelectorsDocumentDamage, Msg};
use script::layout_interface::{QueryMsg, Reflow, ReflowDocumentDamage, ReflowForDisplay}; use script::layout_interface::{QueryMsg, Reflow, ReflowDocumentDamage, ReflowForDisplay};
use script::layout_interface::{ReflowMsg}; 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::image_cache_task::{ImageCacheTask, ImageResponseMsg};
use servo_net::local_image_cache::LocalImageCache; use servo_net::local_image_cache::LocalImageCache;
use servo_util::tree::{TreeNodeRef, TreeUtils}; use servo_util::tree::{TreeNodeRef, TreeUtils};
@ -255,7 +255,11 @@ impl Layout {
debug!("%?", layout_root.dump()); debug!("%?", layout_root.dump());
// Tell script that we're done. // 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_join_chan.send(());
data.script_chan.send(ReflowCompleteMsg);
} }
/// Handles a query from the script task. This is the main routine that DOM functions like /// Handles a query from the script task. This is the main routine that DOM functions like

View file

@ -16,10 +16,11 @@ use core::cell::Cell;
use core::libc::c_int; use core::libc::c_int;
use geom::point::Point2D; use geom::point::Point2D;
use geom::size::Size2D; 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::{ACTIVE_CTRL, DOUBLE, HAVE_PRECISE_MOUSE_WHEEL, WindowHeight, WindowWidth};
use glut::glut; use glut::glut;
use glut::machack; use glut::machack;
use script::compositor_interface::{FinishedLoading, Loading, Rendering, ReadyState}; use script::compositor_interface::{FinishedLoading, Loading, PerformingLayout, ReadyState};
static THROBBER: [char, ..8] = [ '⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷' ]; static THROBBER: [char, ..8] = [ '⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷' ];
@ -51,6 +52,7 @@ pub struct Window {
mouse_down_point: @mut Point2D<c_int>, mouse_down_point: @mut Point2D<c_int>,
ready_state: ReadyState, ready_state: ReadyState,
render_state: RenderState,
throbber_frame: u8, throbber_frame: u8,
} }
@ -78,6 +80,7 @@ impl WindowMethods<Application> for Window {
mouse_down_point: @mut Point2D(0, 0), mouse_down_point: @mut Point2D(0, 0),
ready_state: FinishedLoading, ready_state: FinishedLoading,
render_state: IdleRenderState,
throbber_frame: 0, throbber_frame: 0,
}; };
@ -190,6 +193,12 @@ impl WindowMethods<Application> for Window {
self.ready_state = ready_state; self.ready_state = ready_state;
self.update_window_title() 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 { impl Window {
@ -200,10 +209,19 @@ impl Window {
Loading => { Loading => {
glut::set_window_title(self.glut_window, fmt!("%c Loading — Servo", throbber)) glut::set_window_title(self.glut_window, fmt!("%c Loading — Servo", throbber))
} }
Rendering => { PerformingLayout => {
glut::set_window_title(self.glut_window, fmt!("%c Rendering — Servo", throbber)) 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"),
} }
} }

View file

@ -6,6 +6,7 @@
use geom::point::Point2D; use geom::point::Point2D;
use geom::size::Size2D; use geom::size::Size2D;
use gfx::compositor::RenderState;
use script::compositor_interface::ReadyState; use script::compositor_interface::ReadyState;
pub enum WindowMouseEvent { pub enum WindowMouseEvent {
@ -64,5 +65,7 @@ pub trait WindowMethods<A> {
pub fn set_needs_display(@mut self); pub fn set_needs_display(@mut self);
/// Sets the ready state of the current page. /// Sets the ready state of the current page.
pub fn set_ready_state(@mut self, ready_state: ReadyState); 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);
} }

View file

@ -8,12 +8,12 @@
pub enum ReadyState { pub enum ReadyState {
/// Informs the compositor that a page is loading. Used for setting status /// Informs the compositor that a page is loading. Used for setting status
Loading, Loading,
/// Informs the compositor that a page is rendering. Used for setting status /// Informs the compositor that a page is performing layout. Used for setting status
Rendering, PerformingLayout,
/// Informs the compositor that a page is finished loading. Used for setting status /// Informs the compositor that a page is finished loading. Used for setting status
FinishedLoading, FinishedLoading,
} }
pub trait CompositorInterface: Clone { pub trait CompositorInterface : Clone {
fn send_compositor_msg(&self, ReadyState); fn set_ready_state(&self, ReadyState);
} }

View file

@ -5,7 +5,7 @@
/// The script task is the task that owns the DOM in memory, runs JavaScript, and spawns parsing /// The script task is the task that owns the DOM in memory, runs JavaScript, and spawns parsing
/// and layout tasks. /// and layout tasks.
use compositor_interface::{ReadyState, Loading, Rendering, FinishedLoading}; use compositor_interface::{ReadyState, Loading, PerformingLayout, FinishedLoading};
use dom::bindings::utils::GlobalStaticData; use dom::bindings::utils::GlobalStaticData;
use dom::document::Document; use dom::document::Document;
use dom::element::Element; use dom::element::Element;
@ -54,6 +54,8 @@ pub enum ScriptMsg {
SendEventMsg(Event), SendEventMsg(Event),
/// Fires a JavaScript timeout. /// Fires a JavaScript timeout.
FireTimerMsg(~TimerData), FireTimerMsg(~TimerData),
/// Notifies script that reflow is finished.
ReflowCompleteMsg,
/// Exits the engine. /// Exits the engine.
ExitMsg, ExitMsg,
} }
@ -262,6 +264,10 @@ impl ScriptContext {
self.handle_fire_timer_msg(timer_data); self.handle_fire_timer_msg(timer_data);
true true
} }
ReflowCompleteMsg => {
self.handle_reflow_complete_msg();
true
}
ExitMsg => { ExitMsg => {
self.handle_exit_msg(); self.handle_exit_msg();
false false
@ -306,6 +312,12 @@ impl ScriptContext {
self.reflow(ReflowForScriptQuery) 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. /// Handles a request to exit the script task and shut down layout.
fn handle_exit_msg(&mut self) { fn handle_exit_msg(&mut self) {
self.join_layout(); self.join_layout();
@ -318,7 +330,7 @@ impl ScriptContext {
// tells the compositor when loading starts and finishes // tells the compositor when loading starts and finishes
// FIXME ~compositor_interface doesn't work right now, which is why this is necessary // 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); (self.compositor_task)(msg);
} }
@ -333,7 +345,7 @@ impl ScriptContext {
self.bindings_initialized = true self.bindings_initialized = true
} }
self.send_compositor_msg(Loading); self.set_ready_state(Loading);
// Parse HTML. // Parse HTML.
// //
// Note: We can parse the next document in parallel with any previous documents. // Note: We can parse the next document in parallel with any previous documents.
@ -374,7 +386,6 @@ impl ScriptContext {
url: url url: url
}); });
self.send_compositor_msg(Rendering);
// Perform the initial reflow. // Perform the initial reflow.
self.damage = Some(DocumentDamage { self.damage = Some(DocumentDamage {
root: root_node, root: root_node,
@ -392,7 +403,6 @@ impl ScriptContext {
~"???", ~"???",
1); 1);
} }
self.send_compositor_msg(FinishedLoading);
} }
/// Sends a ping to layout and waits for the response. The response will arrive when the /// 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. // Now, join the layout so that they will see the latest changes we have made.
self.join_layout(); 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. // Layout will let us know when it's done.
let (join_port, join_chan) = comm::stream(); let (join_port, join_chan) = comm::stream();
self.layout_join_port = Some(join_port); self.layout_join_port = Some(join_port);
@ -438,8 +451,8 @@ impl ScriptContext {
document_root: root_frame.document.root, document_root: root_frame.document.root,
url: copy root_frame.url, url: copy root_frame.url,
goal: goal, goal: goal,
script_chan: self.script_chan.clone(),
window_size: self.window_size, window_size: self.window_size,
script_chan: self.script_chan.clone(),
script_join_chan: join_chan, script_join_chan: join_chan,
damage: replace(&mut self.damage, None).unwrap(), damage: replace(&mut self.damage, None).unwrap(),
}; };