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]
}
/// 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<uint>);
fn set_render_state(&self, render_state: RenderState);
}

View file

@ -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<C: Compositor + Owned> Renderer<C> {
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<C: Compositor + Owned> Renderer<C> {
debug!("renderer: returning surface");
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::{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<ScriptMsg>,
profiler_chan: ProfilerChan)
pub fn new(script_chan: SharedChan<ScriptMsg>, 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<uint>),
/// 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<Msg>,
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<uint>) {
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.

View file

@ -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(),

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::{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

View file

@ -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<c_int>,
ready_state: ReadyState,
render_state: RenderState,
throbber_frame: u8,
}
@ -78,6 +80,7 @@ impl WindowMethods<Application> for Window {
mouse_down_point: @mut Point2D(0, 0),
ready_state: FinishedLoading,
render_state: IdleRenderState,
throbber_frame: 0,
};
@ -190,6 +193,12 @@ impl WindowMethods<Application> 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"),
}
}

View file

@ -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<A> {
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);
}

View file

@ -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);
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
/// 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(),
};