This commit is contained in:
eschweic 2013-06-17 14:03:27 -07:00
commit def01b414f
16 changed files with 645 additions and 570 deletions

View file

@ -32,9 +32,9 @@ pub enum RenderState {
RenderingRenderState,
}
/// The interface used to by the renderer to acquire draw targets for each rendered frame and
/// The interface used by the renderer to acquire draw targets for each rendered frame and
/// submit them to be drawn to the display.
pub trait Compositor {
pub trait RenderListener {
fn get_gl_context(&self) -> AzGLContext;
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, AzGLContext};
use compositor::{Compositor, IdleRenderState, RenderingRenderState};
use compositor::{RenderListener, IdleRenderState, RenderingRenderState};
use font_context::FontContext;
use geom::matrix2d::Matrix2D;
use opts::Opts;
@ -22,24 +22,41 @@ use servo_net::util::spawn_listener;
use servo_util::time::{ProfilerChan, profile};
use servo_util::time;
pub enum Msg {
pub enum Msg<C> {
AttachCompositorMsg(C),
RenderMsg(RenderLayer),
ExitMsg(Chan<()>),
}
#[deriving(Clone)]
pub struct RenderTask {
channel: SharedChan<Msg>,
pub struct RenderChan<C> {
chan: SharedChan<Msg<C>>,
}
impl RenderTask {
pub fn new<C:Compositor + Owned>(compositor: C,
impl<C: RenderListener + Owned> Clone for RenderChan<C> {
pub fn clone(&self) -> RenderChan<C> {
RenderChan {
chan: self.chan.clone(),
}
}
}
impl<C: RenderListener + Owned> RenderChan<C> {
pub fn new(chan: Chan<Msg<C>>) -> RenderChan<C> {
RenderChan {
chan: SharedChan::new(chan),
}
}
pub fn send(&self, msg: Msg<C>) {
self.chan.send(msg);
}
}
pub fn create_render_task<C: RenderListener + Owned>(port: Port<Msg<C>>,
compositor: C,
opts: Opts,
profiler_chan: ProfilerChan)
-> RenderTask {
profiler_chan: ProfilerChan) {
let compositor_cell = Cell(compositor);
let opts_cell = Cell(opts);
let (port, chan) = comm::stream();
let port = Cell(port);
do spawn {
@ -84,11 +101,6 @@ impl RenderTask {
renderer.start();
}
RenderTask {
channel: SharedChan::new(chan),
}
}
}
/// Data that needs to be kept around for each render thread.
@ -99,7 +111,7 @@ priv struct ThreadRenderContext {
}
priv struct Renderer<C> {
port: Port<Msg>,
port: Port<Msg<C>>,
compositor: C,
thread_pool: TaskPool<ThreadRenderContext>,
opts: Opts,
@ -110,12 +122,13 @@ priv struct Renderer<C> {
share_gl_context: AzGLContext,
}
impl<C: Compositor + Owned> Renderer<C> {
impl<C: RenderListener + Owned> Renderer<C> {
fn start(&mut self) {
debug!("renderer: beginning rendering loop");
loop {
match self.port.recv() {
AttachCompositorMsg(compositor) => self.compositor = compositor,
RenderMsg(render_layer) => self.render(render_layer),
ExitMsg(response_ch) => {
response_ch.send(());

View file

@ -2,16 +2,15 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use compositing::resize_rate_limiter::ResizeRateLimiter;
use platform::{Application, Window};
use script::script_task::{LoadMsg, ScriptMsg, SendEventMsg};
use script::script_task::{LoadMsg, 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;
use script::dom::event::{Event, ClickEvent, MouseDownEvent, MouseUpEvent, ResizeEvent};
use script::compositor_interface::{ReadyState, ScriptListener};
use script::script_task::{ScriptChan, SendEventMsg};
use script::layout_interface::{LayoutChan, RouteScriptMsg};
use azure::azure_hl::{DataSourceSurface, DrawTarget, SourceSurfaceMethods, current_gl_context};
use azure::azure::AzGLContext;
@ -22,7 +21,7 @@ use core::util;
use geom::matrix::identity;
use geom::point::Point2D;
use geom::size::Size2D;
use gfx::compositor::{Compositor, LayerBufferSet, RenderState};
use gfx::compositor::{RenderListener, LayerBufferSet, RenderState};
use layers::layers::{ARGB32Format, ContainerLayer, ContainerLayerKind, Format};
use layers::layers::{ImageData, WithDataFn};
use layers::layers::{TextureLayerKind, TextureLayer, TextureManager};
@ -32,43 +31,44 @@ use servo_util::{time, url};
use servo_util::time::profile;
use servo_util::time::ProfilerChan;
mod resize_rate_limiter;
/// The implementation of the layers-based compositor.
#[deriving(Clone)]
pub struct CompositorTask {
pub struct CompositorChan {
/// A channel on which messages can be sent to the compositor.
chan: SharedChan<Msg>,
}
impl CompositorInterface for CompositorTask {
/// Implementation of the abstract `ScriptListener` interface.
impl ScriptListener for CompositorChan {
fn set_ready_state(&self, ready_state: ReadyState) {
let msg = ChangeReadyState(ready_state);
self.chan.send(msg);
}
}
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)
-> (CompositorTask, Port<()>) {
let script_chan = Cell(script_chan);
let (shutdown_port, shutdown_chan) = stream();
let shutdown_chan = Cell(shutdown_chan);
/// Implementation of the abstract `RenderListener` interface.
impl RenderListener for CompositorChan {
fn get_gl_context(&self) -> AzGLContext {
let (port, chan) = comm::stream();
self.chan.send(GetGLContext(chan));
port.recv()
}
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))
}
}
let chan: Chan<Msg> = do on_osmain |port| {
debug!("preparing to enter main loop");
run_main_loop(port,
script_chan.take(),
shutdown_chan.take(),
profiler_chan.clone());
};
let task = CompositorTask {
impl CompositorChan {
pub fn new(chan: Chan<Msg>) -> CompositorChan {
CompositorChan {
chan: SharedChan::new(chan),
};
(task, shutdown_port)
}
}
pub fn send(&self, msg: Msg) {
self.chan.send(msg);
}
}
@ -84,6 +84,8 @@ pub enum Msg {
ChangeReadyState(ReadyState),
/// Alerts the compositor to the current status of rendering.
ChangeRenderState(RenderState),
/// Sets the channel to the current layout task
SetLayoutChan(LayoutChan),
}
/// Azure surface wrapping to work with the layers infrastructure.
@ -111,13 +113,42 @@ impl ImageData for AzureDrawTargetImageData {
}
}
fn run_main_loop(port: Port<Msg>,
script_chan: SharedChan<ScriptMsg>,
shutdown_chan: Chan<()>,
profiler_chan: ProfilerChan) {
pub struct CompositorTask {
port: Port<Msg>,
profiler_chan: ProfilerChan,
shutdown_chan: SharedChan<()>,
}
impl CompositorTask {
pub fn new(port: Port<Msg>,
profiler_chan: ProfilerChan,
shutdown_chan: Chan<()>)
-> CompositorTask {
CompositorTask {
port: port,
profiler_chan: profiler_chan,
shutdown_chan: SharedChan::new(shutdown_chan),
}
}
/// Starts the compositor, which listens for messages on the specified port.
pub fn create(port: Port<Msg>,
profiler_chan: ProfilerChan,
shutdown_chan: Chan<()>) {
let port = Cell(port);
let shutdown_chan = Cell(shutdown_chan);
do on_osmain {
let compositor_task = CompositorTask::new(port.take(),
profiler_chan.clone(),
shutdown_chan.take());
debug!("preparing to enter main loop");
compositor_task.run_main_loop();
};
}
fn run_main_loop(&self) {
let app: Application = ApplicationMethods::new();
let window: @mut Window = WindowMethods::new(&app);
let resize_rate_limiter = @mut ResizeRateLimiter(script_chan.clone());
// Create an initial layer tree.
//
@ -138,9 +169,47 @@ fn run_main_loop(port: Port<Msg>,
// Keeps track of the current zoom factor
let world_zoom = @mut 1f32;
let check_for_messages: @fn() = || {
// Periodically check if the script task responded to our last resize event
resize_rate_limiter.check_resize_response();
let update_layout_callbacks: @fn(LayoutChan) = |layout_chan: LayoutChan| {
let layout_chan_clone = layout_chan.clone();
// Hook the windowing system's resize callback up to the resize rate limiter.
do window.set_resize_callback |width, height| {
debug!("osmain: window resized to %ux%u", width, height);
*window_size = Size2D(width, height);
layout_chan_clone.chan.send(RouteScriptMsg(SendEventMsg(ResizeEvent(width, height))));
}
let layout_chan_clone = layout_chan.clone();
// When the user enters a new URL, load it.
do window.set_load_url_callback |url_string| {
debug!("osmain: loading URL `%s`", url_string);
layout_chan_clone.chan.send(RouteScriptMsg(LoadMsg(url::make_url(url_string.to_str(), None))));
}
let layout_chan_clone = layout_chan.clone();
// When the user triggers a mouse event, perform appropriate hit testing
do window.set_mouse_callback |window_mouse_event: WindowMouseEvent| {
let event: Event;
let world_mouse_point = |layer_mouse_point: Point2D<f32>| {
layer_mouse_point + *world_offset
};
match window_mouse_event {
WindowClickEvent(button, layer_mouse_point) => {
event = ClickEvent(button, world_mouse_point(layer_mouse_point));
}
WindowMouseDownEvent(button, layer_mouse_point) => {
event = MouseDownEvent(button, world_mouse_point(layer_mouse_point));
}
WindowMouseUpEvent(button, layer_mouse_point) => {
event = MouseUpEvent(button, world_mouse_point(layer_mouse_point));
}
}
layout_chan_clone.chan.send(RouteScriptMsg(SendEventMsg(event)));
}
};
let check_for_messages: @fn(&Port<Msg>) = |port: &Port<Msg>| {
// Handle messages
while port.peek() {
match port.recv() {
@ -149,6 +218,10 @@ fn run_main_loop(port: Port<Msg>,
ChangeReadyState(ready_state) => window.set_ready_state(ready_state),
ChangeRenderState(render_state) => window.set_render_state(render_state),
SetLayoutChan(layout_chan) => {
update_layout_callbacks(layout_chan);
}
GetGLContext(chan) => chan.send(current_gl_context()),
Paint(new_layer_buffer_set, new_size) => {
@ -211,6 +284,7 @@ fn run_main_loop(port: Port<Msg>,
}
};
let profiler_chan = self.profiler_chan.clone();
do window.set_composite_callback {
do profile(time::CompositingCategory, profiler_chan.clone()) {
debug!("compositor: compositing");
@ -224,43 +298,6 @@ fn run_main_loop(port: Port<Msg>,
window.present();
}
// Hook the windowing system's resize callback up to the resize rate limiter.
do window.set_resize_callback |width, height| {
debug!("osmain: window resized to %ux%u", width, height);
*window_size = Size2D(width, height);
resize_rate_limiter.window_resized(width, height)
}
let script_chan_clone = script_chan.clone();
// When the user enters a new URL, load it.
do window.set_load_url_callback |url_string| {
debug!("osmain: loading URL `%s`", url_string);
script_chan_clone.send(LoadMsg(url::make_url(url_string.to_str(), None)))
}
let script_chan_clone = script_chan.clone();
// When the user triggers a mouse event, perform appropriate hit testing
do window.set_mouse_callback |window_mouse_event: WindowMouseEvent| {
let event: Event;
let world_mouse_point = |layer_mouse_point: Point2D<f32>| {
layer_mouse_point + *world_offset
};
match window_mouse_event {
WindowClickEvent(button, layer_mouse_point) => {
event = ClickEvent(button, world_mouse_point(layer_mouse_point));
}
WindowMouseDownEvent(button, layer_mouse_point) => {
event = MouseDownEvent(button, world_mouse_point(layer_mouse_point));
}
WindowMouseUpEvent(button, layer_mouse_point) => {
event = MouseUpEvent(button, world_mouse_point(layer_mouse_point));
}
}
script_chan_clone.send(SendEventMsg(event));
}
// When the user scrolls, move the layer around.
do window.set_scroll_callback |delta| {
// FIXME (Rust #2528): Can't use `-=`.
@ -329,46 +366,25 @@ fn run_main_loop(port: Port<Msg>,
window.set_needs_display()
}
// Enter the main event loop.
while !*done {
// Check for new messages coming from the rendering task.
check_for_messages();
check_for_messages(&self.port);
// Check for messages coming from the windowing system.
window.check_loop();
}
shutdown_chan.send(())
}
/// Implementation of the abstract `Compositor` interface.
impl Compositor for CompositorTask {
fn get_gl_context(&self) -> AzGLContext {
let (port, chan) = comm::stream();
self.chan.send(GetGLContext(chan));
port.recv()
}
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))
self.shutdown_chan.send(())
}
}
/// A function for spawning into the platform's main thread.
fn on_osmain<T: Owned>(f: ~fn(port: Port<T>)) -> Chan<T> {
let (setup_port, setup_chan) = comm::stream();
fn on_osmain(f: ~fn()) {
// FIXME: rust#6399
let mut main_task = task::task();
main_task.sched_mode(task::PlatformThread);
do main_task.spawn {
let (port, chan) = comm::stream();
setup_chan.send(chan);
f(port);
f();
}
setup_port.recv()
}

View file

@ -7,20 +7,20 @@
/// before sending the next. If the window is resized multiple times before an event is handled
/// then some events will never be sent.
use core::comm::{Port, SharedChan};
use core::comm::{Port};
use script::dom::event::ResizeEvent;
use script::script_task::{ScriptMsg, SendEventMsg};
use script::script_task::{ScriptChan, ScriptMsg, SendEventMsg};
pub struct ResizeRateLimiter {
/// The channel we send resize events on
priv script_chan: SharedChan<ScriptMsg>,
priv script_chan: ScriptChan,
/// The port we are waiting on for a response to the last resize event
priv last_response_port: Option<Port<()>>,
/// The next window resize event we should fire
priv next_resize_event: Option<(uint, uint)>
}
pub fn ResizeRateLimiter(script_chan: SharedChan<ScriptMsg>) -> ResizeRateLimiter {
pub fn ResizeRateLimiter(script_chan: ScriptChan) -> ResizeRateLimiter {
ResizeRateLimiter {
script_chan: script_chan,
last_response_port: None,

View file

@ -2,101 +2,112 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use compositing::CompositorTask;
use compositing::{CompositorChan, SetLayoutChan};
use layout::layout_task;
use core::cell::Cell;
use core::comm::{Port, SharedChan};
use core::comm::Port;
use gfx::opts::Opts;
use gfx::render_task::RenderTask;
use gfx::render_task::RenderChan;
use gfx::render_task;
use script::compositor_interface::{CompositorInterface, ReadyState};
use script::engine_interface::{EngineTask, ExitMsg, LoadUrlMsg, Msg};
use script::layout_interface::LayoutTask;
use script::compositor_interface::{ScriptListener, ReadyState};
use script::engine_interface::{EngineChan, ExitMsg, LoadUrlMsg, Msg};
use script::layout_interface::LayoutChan;
use script::layout_interface;
use script::script_task::{ExecuteMsg, LoadMsg, ScriptMsg, ScriptTask};
use script::script_task::{ExecuteMsg, LoadMsg, ScriptMsg, ScriptContext, ScriptChan};
use script::script_task;
use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient};
use servo_net::resource_task::ResourceTask;
use servo_net::resource_task;
use servo_util::time::{ProfilerChan, ProfilerPort, ProfilerTask, ForcePrintMsg};
use servo_util::time::{ProfilerChan};
pub struct Engine {
request_port: Port<Msg>,
compositor: CompositorTask,
render_task: RenderTask,
compositor_chan: CompositorChan,
render_chan: RenderChan<CompositorChan>,
resource_task: ResourceTask,
image_cache_task: ImageCacheTask,
layout_task: LayoutTask,
script_task: ScriptTask,
profiler_task: ProfilerTask,
}
impl Drop for Engine {
fn finalize(&self) {
self.profiler_task.chan.send(ForcePrintMsg);
}
layout_chan: LayoutChan,
script_chan: ScriptChan,
profiler_chan: ProfilerChan,
}
impl Engine {
pub fn start(compositor: CompositorTask,
pub fn start(compositor_chan: CompositorChan,
opts: &Opts,
script_port: Port<ScriptMsg>,
script_chan: SharedChan<ScriptMsg>,
resource_task: ResourceTask,
image_cache_task: ImageCacheTask,
profiler_port: ProfilerPort,
profiler_chan: ProfilerChan)
-> EngineTask {
let (script_port, script_chan) = (Cell(script_port), Cell(script_chan));
let (engine_port, engine_chan) = comm::stream();
let (engine_port, engine_chan) = (Cell(engine_port), SharedChan::new(engine_chan));
let engine_chan_clone = engine_chan.clone();
let compositor = Cell(compositor);
let profiler_port = Cell(profiler_port);
-> EngineChan {
macro_rules! closure_stream(
($Msg:ty, $Chan:ident) => (
{
let (port, chan) = comm::stream::<$Msg>();
(Cell(port), $Chan::new(chan))
}
);
)
// Create the script port and channel.
let (script_port, script_chan) = closure_stream!(ScriptMsg, ScriptChan);
// Create the engine port and channel.
let (engine_port, engine_chan) = closure_stream!(Msg, EngineChan);
// Create the layout port and channel.
let (layout_port, layout_chan) = closure_stream!(layout_interface::Msg, LayoutChan);
let (render_port, render_chan) = comm::stream::<render_task::Msg<CompositorChan>>();
let (render_port, render_chan) = (Cell(render_port), RenderChan::new(render_chan));
compositor_chan.send(SetLayoutChan(layout_chan.clone()));
let compositor_chan = Cell(compositor_chan);
let opts = Cell(copy *opts);
{
let engine_chan = engine_chan.clone();
do task::spawn {
let compositor = compositor.take();
let render_task = RenderTask::new(compositor.clone(),
let compositor_chan = compositor_chan.take();
render_task::create_render_task(render_port.take(),
compositor_chan.clone(),
opts.with_ref(|o| copy *o),
profiler_chan.clone());
let opts = opts.take();
let profiler_task = ProfilerTask::new(profiler_port.take(),
profiler_chan.clone(),
opts.profiler_period);
let layout_task = layout_task::create_layout_task(render_task.clone(),
layout_task::create_layout_task(layout_port.take(),
script_chan.clone(),
render_chan.clone(),
image_cache_task.clone(),
opts,
profiler_task.chan.clone());
profiler_chan.clone());
let compositor_clone = compositor.clone();
let script_task = ScriptTask::new(script_port.take(),
script_chan.take(),
engine_chan_clone.clone(),
let compositor_chan_clone = compositor_chan.clone();
ScriptContext::create_script_context(layout_chan.clone(),
script_port.take(),
script_chan.clone(),
engine_chan.clone(),
|msg: ReadyState| {
compositor_clone.set_ready_state(msg)
compositor_chan_clone.set_ready_state(msg)
},
layout_task.clone(),
resource_task.clone(),
image_cache_task.clone());
Engine {
request_port: engine_port.take(),
compositor: compositor.clone(),
render_task: render_task,
compositor_chan: compositor_chan.clone(),
render_chan: render_chan.clone(),
resource_task: resource_task.clone(),
image_cache_task: image_cache_task.clone(),
layout_task: layout_task,
script_task: script_task,
profiler_task: profiler_task,
layout_chan: layout_chan.clone(),
script_chan: script_chan.clone(),
profiler_chan: profiler_chan.clone(),
}.run();
}
engine_chan.clone()
}
engine_chan
}
fn run(&self) {
@ -109,20 +120,20 @@ impl Engine {
match request {
LoadUrlMsg(url) => {
if url.path.ends_with(".js") {
self.script_task.chan.send(ExecuteMsg(url))
self.script_chan.send(ExecuteMsg(url))
} else {
self.script_task.chan.send(LoadMsg(url))
self.script_chan.send(LoadMsg(url))
}
return true
}
ExitMsg(sender) => {
self.script_task.chan.send(script_task::ExitMsg);
self.layout_task.chan.send(layout_interface::ExitMsg);
self.script_chan.send(script_task::ExitMsg);
self.layout_chan.send(layout_interface::ExitMsg);
let (response_port, response_chan) = comm::stream();
self.render_task.channel.send(render_task::ExitMsg(response_chan));
self.render_chan.send(render_task::ExitMsg(response_chan));
response_port.recv();
self.image_cache_task.exit();

View file

@ -5,6 +5,7 @@
//! The layout task. Performs layout on the DOM, builds display lists and sends them to be
/// rendered.
use compositing::CompositorChan;
use css::matching::MatchMethods;
use css::select::new_css_select_ctx;
use layout::aux::{LayoutData, LayoutAuxMethods};
@ -13,11 +14,10 @@ use layout::box_builder::LayoutTreeBuilder;
use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, FlowDisplayListBuilderMethods};
use layout::flow::FlowContext;
use util::task::spawn_listener;
use core::cast::transmute;
use core::cell::Cell;
use core::comm::{Chan, Port, SharedChan};
use core::comm::{Chan, Port};
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
@ -26,7 +26,7 @@ use gfx::font_context::FontContext;
use gfx::geometry::Au;
use gfx::opts::Opts;
use gfx::render_layers::RenderLayer;
use gfx::render_task::{RenderMsg, RenderTask};
use gfx::render_task::{RenderMsg, RenderChan};
use newcss::select::SelectCtx;
use newcss::stylesheet::Stylesheet;
use newcss::types::OriginAuthor;
@ -35,10 +35,10 @@ use script::dom::node::{AbstractNode, LayoutView};
use script::layout_interface::{AddStylesheetMsg, ContentBoxQuery};
use script::layout_interface::{HitTestQuery, ContentBoxResponse, HitTestResponse};
use script::layout_interface::{ContentBoxesQuery, ContentBoxesResponse, ExitMsg, LayoutQuery};
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::{ReflowCompleteMsg, ScriptMsg, SendEventMsg};
use script::layout_interface::{LayoutResponse, MatchSelectorsDocumentDamage, Msg};
use script::layout_interface::{QueryMsg, RouteScriptMsg, Reflow, ReflowDocumentDamage};
use script::layout_interface::{ReflowForDisplay, ReflowMsg};
use script::script_task::{ReflowCompleteMsg, ScriptChan, ScriptMsg, SendEventMsg};
use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg};
use servo_net::local_image_cache::LocalImageCache;
use servo_util::tree::{TreeNodeRef, TreeUtils};
@ -46,30 +46,30 @@ use servo_util::time::{ProfilerChan, profile, time};
use servo_util::time;
use std::net::url::Url;
pub fn create_layout_task(render_task: RenderTask,
pub fn create_layout_task(port: Port<Msg>,
script_chan: ScriptChan,
render_chan: RenderChan<CompositorChan>,
img_cache_task: ImageCacheTask,
opts: Opts,
profiler_chan: ProfilerChan)
-> LayoutTask {
let chan = do spawn_listener::<Msg> |from_script| {
let mut layout = Layout::new(render_task.clone(),
profiler_chan: ProfilerChan) {
let port = Cell(port);
do spawn {
let mut layout = Layout::new(port.take(),
script_chan.clone(),
render_chan.clone(),
img_cache_task.clone(),
from_script,
&opts,
profiler_chan.clone());
layout.start();
};
LayoutTask {
chan: SharedChan::new(chan),
}
}
struct Layout {
render_task: RenderTask,
port: Port<Msg>,
script_chan: ScriptChan,
render_chan: RenderChan<CompositorChan>,
image_cache_task: ImageCacheTask,
local_image_cache: @mut LocalImageCache,
from_script: Port<Msg>,
font_ctx: @mut FontContext,
doc_url: Option<Url>,
screen_size: Option<Size2D<Au>>,
@ -82,19 +82,21 @@ struct Layout {
}
impl Layout {
fn new(render_task: RenderTask,
fn new(port: Port<Msg>,
script_chan: ScriptChan,
render_chan: RenderChan<CompositorChan>,
image_cache_task: ImageCacheTask,
from_script: Port<Msg>,
opts: &Opts,
profiler_chan: ProfilerChan)
-> Layout {
let fctx = @mut FontContext::new(opts.render_backend, true, profiler_chan.clone());
Layout {
render_task: render_task,
port: port,
script_chan: script_chan,
render_chan: render_chan,
image_cache_task: image_cache_task.clone(),
local_image_cache: @mut LocalImageCache(image_cache_task),
from_script: from_script,
font_ctx: fctx,
doc_url: None,
screen_size: None,
@ -125,7 +127,7 @@ impl Layout {
}
fn handle_request(&mut self) -> bool {
match self.from_script.recv() {
match self.port.recv() {
AddStylesheetMsg(sheet) => self.handle_add_stylesheet(sheet),
ReflowMsg(data) => {
let data = Cell(data);
@ -137,9 +139,12 @@ impl Layout {
QueryMsg(query, chan) => {
let chan = Cell(chan);
do profile(time::LayoutQueryCategory, self.profiler_chan.clone()) {
self.handle_query(query, chan.take())
self.handle_query(query, chan.take());
}
}
RouteScriptMsg(script_msg) => {
self.route_script_msg(script_msg);
}
ExitMsg => {
debug!("layout: ExitMsg received");
return false
@ -248,7 +253,7 @@ impl Layout {
size: Size2D(root_size.width.to_px() as uint, root_size.height.to_px() as uint)
};
self.render_task.channel.send(RenderMsg(render_layer));
self.render_chan.send(RenderMsg(render_layer));
} // time(layout: display list building)
}
@ -371,12 +376,18 @@ impl Layout {
}
}
// TODO(tkuehn): once there are multiple script tasks, this is where the layout task will
// determine which script task should receive the message. The prototype will need to change
fn route_script_msg(&self, script_msg: ScriptMsg) {
self.script_chan.send(script_msg);
}
// When images can't be loaded in time to display they trigger
// this callback in some task somewhere. This will send a message
// to the script task, and ultimately cause the image to be
// re-requested. We probably don't need to go all the way back to
// the script task for this.
fn make_on_image_available_cb(&self, script_chan: SharedChan<ScriptMsg>)
fn make_on_image_available_cb(&self, script_chan: ScriptChan)
-> @fn() -> ~fn(ImageResponseMsg) {
// This has a crazy signature because the image cache needs to
// make multiple copies of the callback, and the dom event

View file

@ -12,7 +12,6 @@ use windowing::{ResizeCallback, ScrollCallback, WindowMethods, WindowMouseEvent,
use windowing::{WindowMouseDownEvent, WindowMouseUpEvent, ZoomCallback};
use alert::{Alert, AlertMethods};
use core::cell::Cell;
use core::libc::c_int;
use geom::point::Point2D;
use geom::size::Size2D;

View file

@ -33,14 +33,15 @@ extern mod core_graphics;
#[cfg(target_os="macos")]
extern mod core_text;
use compositing::CompositorTask;
use compositing::{CompositorChan, CompositorTask};
use engine::Engine;
use script::engine_interface::{ExitMsg, LoadUrlMsg};
use core::comm::SharedChan;
use gfx::opts;
use servo_net::image_cache_task::ImageCacheTask;
use servo_net::resource_task::ResourceTask;
use servo_util::time::{Profiler, ProfilerChan, PrintMsg};
use std::uv_global_loop;
pub use gfx::opts::Opts;
pub use gfx::text;
@ -87,33 +88,42 @@ fn main() {
}
fn run(opts: &Opts) {
// Create the script channel.
let (script_port, script_chan) = comm::stream();
let script_chan = SharedChan::new(script_chan);
let (shutdown_port, shutdown_chan) = comm::stream();
// Create the profiler channel.
let (profiler_port, profiler_chan) = comm::stream();
let profiler_chan = SharedChan::new(profiler_chan);
let profiler_chan = ProfilerChan::new(profiler_chan);
Profiler::create(profiler_port);
do opts.profiler_period.map |period| {
let profiler_chan = profiler_chan.clone();
let period = *period;
do spawn {
loop {
std::timer::sleep(&uv_global_loop::get(),
(period * 1000f64) as uint);
profiler_chan.send(PrintMsg);
}
}
};
// Create the compositor.
let (compositor, shutdown_port) = CompositorTask::new(script_chan.clone(),
profiler_chan.clone());
let (compositor_port, compositor_chan) = comm::stream();
let compositor_chan = CompositorChan::new(compositor_chan);
CompositorTask::create(compositor_port, profiler_chan.clone(), shutdown_chan);
// Create a Servo instance.
let resource_task = ResourceTask();
let image_cache_task = ImageCacheTask(resource_task.clone());
let engine_task = Engine::start(compositor.clone(),
let engine_chan = Engine::start(compositor_chan.clone(),
opts,
script_port,
script_chan,
resource_task,
image_cache_task,
profiler_port,
profiler_chan);
profiler_chan.clone());
// Send the URL command to the engine task.
for opts.urls.each |filename| {
engine_task.send(LoadUrlMsg(make_url(copy *filename, None)))
engine_chan.send(LoadUrlMsg(make_url(copy *filename, None)))
}
// Wait for the compositor to shut down.
@ -122,7 +132,7 @@ fn run(opts: &Opts) {
// Shut the engine down.
debug!("master: Shut down");
let (exit_response_from_engine, exit_chan) = comm::stream();
engine_task.send(ExitMsg(exit_chan));
engine_chan.send(ExitMsg(exit_chan));
exit_response_from_engine.recv();
}

View file

@ -14,6 +14,8 @@ pub enum ReadyState {
FinishedLoading,
}
pub trait CompositorInterface : Clone {
/// The interface used by the script task to tell the compositor to update its ready state,
/// which is used in displaying the appropriate message in the window's title.
pub trait ScriptListener : Clone {
fn set_ready_state(&self, ReadyState);
}

View file

@ -10,7 +10,7 @@ use dom::bindings::utils::{DOMString, ErrorResult, WrapperCache};
use geom::point::Point2D;
pub enum Event {
ResizeEvent(uint, uint, comm::Chan<()>),
ResizeEvent(uint, uint),
ReflowEvent,
ClickEvent(uint, Point2D<f32>),
MouseDownEvent(uint, Point2D<f32>),

View file

@ -6,9 +6,9 @@ use dom::bindings::utils::WrapperCache;
use dom::bindings::window;
use layout_interface::ReflowForScriptQuery;
use script_task::{ExitMsg, FireTimerMsg, ScriptMsg, ScriptContext};
use script_task::{ExitMsg, FireTimerMsg, ScriptChan, ScriptContext};
use core::comm::{Chan, SharedChan};
use core::comm::Chan;
use js::jsapi::JSVal;
use std::timer;
use std::uv_global_loop;
@ -23,7 +23,7 @@ pub enum TimerControlMsg {
// only used for querying layout from arbitrary script.
pub struct Window {
timer_chan: Chan<TimerControlMsg>,
script_chan: SharedChan<ScriptMsg>,
script_chan: ScriptChan,
script_context: *mut ScriptContext,
wrapper: WrapperCache
}
@ -88,9 +88,9 @@ pub impl Window {
}
}
pub fn new(script_chan: SharedChan<ScriptMsg>, script_context: *mut ScriptContext)
pub fn new(script_chan: ScriptChan, script_context: *mut ScriptContext)
-> @mut Window {
let script_chan_copy = script_chan.clone();
let script_chan_clone = script_chan.clone();
let win = @mut Window {
wrapper: WrapperCache::new(),
script_chan: script_chan,
@ -100,8 +100,8 @@ pub impl Window {
loop {
match timer_port.recv() {
TimerMessage_Close => break,
TimerMessage_Fire(td) => script_chan_copy.send(FireTimerMsg(td)),
TimerMessage_TriggerExit => script_chan_copy.send(ExitMsg),
TimerMessage_Fire(td) => script_chan_clone.chan.send(FireTimerMsg(td)),
TimerMessage_TriggerExit => script_chan_clone.chan.send(ExitMsg),
}
}
}

View file

@ -8,7 +8,21 @@
use core::comm::{Chan, SharedChan};
use std::net::url::Url;
pub type EngineTask = SharedChan<Msg>;
#[deriving(Clone)]
pub struct EngineChan {
chan: SharedChan<Msg>,
}
impl EngineChan {
pub fn new(chan: Chan<Msg>) -> EngineChan {
EngineChan {
chan: SharedChan::new(chan),
}
}
pub fn send(&self, msg: Msg) {
self.chan.send(msg);
}
}
pub enum Msg {
LoadUrlMsg(Url),

View file

@ -7,7 +7,7 @@
/// from layout.
use dom::node::{AbstractNode, ScriptView, LayoutView};
use script_task::ScriptMsg;
use script_task::{ScriptMsg, ScriptChan};
use core::comm::{Chan, SharedChan};
use geom::rect::Rect;
@ -32,6 +32,9 @@ pub enum Msg {
/// FIXME(pcwalton): As noted below, this isn't very type safe.
QueryMsg(LayoutQuery, Chan<Result<LayoutResponse,()>>),
/// Routes a message (usually from the compositor) to the appropriate script task
RouteScriptMsg(ScriptMsg),
/// Requests that the layout task shut down and exit.
ExitMsg,
}
@ -110,7 +113,7 @@ pub struct Reflow {
/// The URL of the page.
url: Url,
/// The channel through which messages can be sent back to the script task.
script_chan: SharedChan<ScriptMsg>,
script_chan: ScriptChan,
/// The current window size.
window_size: Size2D<uint>,
/// The channel that we send a notification to.
@ -119,7 +122,17 @@ pub struct Reflow {
/// Encapsulates a channel to the layout task.
#[deriving(Clone)]
pub struct LayoutTask {
pub struct LayoutChan {
chan: SharedChan<Msg>,
}
impl LayoutChan {
pub fn new(chan: Chan<Msg>) -> LayoutChan {
LayoutChan {
chan: SharedChan::new(chan),
}
}
pub fn send(&self, msg: Msg) {
self.chan.send(msg);
}
}

View file

@ -13,11 +13,11 @@ use dom::event::{Event, ResizeEvent, ReflowEvent, ClickEvent, MouseDownEvent, Mo
use dom::node::{AbstractNode, ScriptView, define_bindings};
use dom::window::Window;
use layout_interface::{AddStylesheetMsg, DocumentDamage, DocumentDamageLevel, HitTestQuery};
use layout_interface::{HitTestResponse, LayoutQuery, LayoutResponse, LayoutTask};
use layout_interface::{HitTestResponse, LayoutQuery, LayoutResponse, LayoutChan};
use layout_interface::{MatchSelectorsDocumentDamage, QueryMsg, Reflow, ReflowDocumentDamage};
use layout_interface::{ReflowForDisplay, ReflowForScriptQuery, ReflowGoal, ReflowMsg};
use layout_interface;
use engine_interface::{EngineTask, LoadUrlMsg};
use engine_interface::{EngineChan, LoadUrlMsg};
use core::cast::transmute;
use core::cell::Cell;
@ -61,41 +61,21 @@ pub enum ScriptMsg {
}
/// Encapsulates external communication with the script task.
pub struct ScriptTask {
#[deriving(Clone)]
pub struct ScriptChan {
/// The channel used to send messages to the script task.
chan: SharedChan<ScriptMsg>,
}
impl ScriptTask {
impl ScriptChan {
/// Creates a new script task.
pub fn new(script_port: Port<ScriptMsg>,
script_chan: SharedChan<ScriptMsg>,
engine_task: EngineTask,
//FIXME(rust #5192): workaround for lack of working ~Trait
compositor_task: ~fn(ReadyState),
layout_task: LayoutTask,
resource_task: ResourceTask,
image_cache_task: ImageCacheTask)
-> ScriptTask {
let (script_chan_copy, script_port) = (script_chan.clone(), Cell(script_port));
let compositor_task = Cell(compositor_task);
// FIXME: rust#6399
let mut the_task = task();
the_task.sched_mode(SingleThreaded);
do the_task.spawn {
let script_context = ScriptContext::new(layout_task.clone(),
script_port.take(),
script_chan_copy.clone(),
engine_task.clone(),
compositor_task.take(),
resource_task.clone(),
image_cache_task.clone());
script_context.start();
pub fn new(chan: Chan<ScriptMsg>) -> ScriptChan {
ScriptChan {
chan: SharedChan::new(chan)
}
ScriptTask {
chan: script_chan
}
pub fn send(&self, msg: ScriptMsg) {
self.chan.send(msg);
}
}
@ -112,7 +92,7 @@ pub struct Frame {
/// FIXME: Rename to `Page`, following WebKit?
pub struct ScriptContext {
/// A handle to the layout task.
layout_task: LayoutTask,
layout_chan: LayoutChan,
/// A handle to the image cache task.
image_cache_task: ImageCacheTask,
/// A handle to the resource task.
@ -125,10 +105,10 @@ pub struct ScriptContext {
script_port: Port<ScriptMsg>,
/// A channel for us to hand out when we want some other task to be able to send us script
/// messages.
script_chan: SharedChan<ScriptMsg>,
script_chan: ScriptChan,
/// For communicating load url messages to the engine
engine_task: EngineTask,
engine_chan: EngineChan,
/// For communicating loading messages to the compositor
compositor_task: ~fn(ReadyState),
@ -180,10 +160,10 @@ impl Drop for ScriptContext {
impl ScriptContext {
/// Creates a new script context.
pub fn new(layout_task: LayoutTask,
pub fn new(layout_chan: LayoutChan,
script_port: Port<ScriptMsg>,
script_chan: SharedChan<ScriptMsg>,
engine_task: EngineTask,
script_chan: ScriptChan,
engine_chan: EngineChan,
compositor_task: ~fn(ReadyState),
resource_task: ResourceTask,
img_cache_task: ImageCacheTask)
@ -200,7 +180,7 @@ impl ScriptContext {
};
let script_context = @mut ScriptContext {
layout_task: layout_task,
layout_chan: layout_chan,
image_cache_task: img_cache_task,
resource_task: resource_task,
@ -208,7 +188,7 @@ impl ScriptContext {
script_port: script_port,
script_chan: script_chan,
engine_task: engine_task,
engine_chan: engine_chan,
compositor_task: compositor_task,
js_runtime: js_runtime,
@ -245,6 +225,30 @@ impl ScriptContext {
}
}
pub fn create_script_context(layout_chan: LayoutChan,
script_port: Port<ScriptMsg>,
script_chan: ScriptChan,
engine_chan: EngineChan,
compositor_task: ~fn(ReadyState),
resource_task: ResourceTask,
image_cache_task: ImageCacheTask) {
let script_port = Cell(script_port);
let compositor_task = Cell(compositor_task);
// FIXME: rust#6399
let mut the_task = task();
the_task.sched_mode(SingleThreaded);
do the_task.spawn {
let script_context = ScriptContext::new(layout_chan.clone(),
script_port.take(),
script_chan.clone(),
engine_chan.clone(),
compositor_task.take(),
resource_task.clone(),
image_cache_task.clone());
script_context.start();
}
}
/// Handles an incoming control message.
fn handle_msg(&mut self) -> bool {
match self.script_port.recv() {
@ -325,7 +329,7 @@ impl ScriptContext {
frame.document.teardown();
}
self.layout_task.chan.send(layout_interface::ExitMsg)
self.layout_chan.send(layout_interface::ExitMsg)
}
// tells the compositor when loading starts and finishes
@ -361,7 +365,7 @@ impl ScriptContext {
// in the script task.
loop {
match html_parsing_result.style_port.recv() {
Some(sheet) => self.layout_task.chan.send(AddStylesheetMsg(sheet)),
Some(sheet) => self.layout_chan.send(AddStylesheetMsg(sheet)),
None => break,
}
}
@ -457,7 +461,7 @@ impl ScriptContext {
damage: replace(&mut self.damage, None).unwrap(),
};
self.layout_task.chan.send(ReflowMsg(reflow))
self.layout_chan.send(ReflowMsg(reflow))
}
}
@ -482,7 +486,7 @@ impl ScriptContext {
self.join_layout();
let (response_port, response_chan) = comm::stream();
self.layout_task.chan.send(QueryMsg(query, response_chan));
self.layout_chan.send(QueryMsg(query, response_chan));
response_port.recv()
}
@ -511,7 +515,7 @@ impl ScriptContext {
/// TODO: Actually perform DOM event dispatch.
fn handle_event(&mut self, event: Event) {
match event {
ResizeEvent(new_width, new_height, response_chan) => {
ResizeEvent(new_width, new_height) => {
debug!("script got resize event: %u, %u", new_width, new_height);
self.window_size = Size2D(new_width, new_height);
@ -525,8 +529,6 @@ impl ScriptContext {
if self.root_frame.is_some() {
self.reflow(ReflowForDisplay)
}
response_chan.send(())
}
// FIXME(pcwalton): This reflows the entire document and is not incremental-y.
@ -595,7 +597,7 @@ impl ScriptContext {
None => None
};
let url = make_url(attr.value.clone(), current_url);
self.engine_task.send(LoadUrlMsg(url));
self.engine_chan.send(LoadUrlMsg(url));
}
}
}

View file

@ -8,8 +8,22 @@ use core::cell::Cell;
use core::comm::{Port, SharedChan};
use std::sort::tim_sort;
pub type ProfilerChan = SharedChan<ProfilerMsg>;
pub type ProfilerPort = Port<ProfilerMsg>;
// front-end representation of the profiler used to communicate with the profiler
#[deriving(Clone)]
pub struct ProfilerChan {
chan: SharedChan<ProfilerMsg>,
}
impl ProfilerChan {
pub fn new(chan: Chan<ProfilerMsg>) -> ProfilerChan {
ProfilerChan {
chan: SharedChan::new(chan),
}
}
pub fn send(&self, msg: ProfilerMsg) {
self.chan.send(msg);
}
}
#[deriving(Eq)]
pub enum ProfilerCategory {
@ -36,25 +50,17 @@ pub enum ProfilerMsg {
// Normal message used for reporting time
TimeMsg(ProfilerCategory, f64),
// Message used to force print the profiling metrics
ForcePrintMsg,
}
// front-end representation of the profiler used to communicate with the profiler context
pub struct ProfilerTask {
chan: ProfilerChan,
PrintMsg,
}
// back end of the profiler that handles data aggregation and performance metrics
pub struct ProfilerContext {
port: ProfilerPort,
pub struct Profiler {
port: Port<ProfilerMsg>,
buckets: ~[(ProfilerCategory, ~[f64])],
verbose: bool,
period: f64,
last_print: f64,
last_msg: Option<ProfilerMsg>,
}
impl ProfilerCategory {
// convenience function to not have to cast every time
pub fn num_buckets() -> uint {
NUM_BUCKETS as uint
@ -103,36 +109,20 @@ impl ProfilerCategory {
}
}
impl ProfilerTask {
pub fn new(profiler_port: ProfilerPort,
profiler_chan: ProfilerChan,
period: Option<f64>)
-> ProfilerTask {
let profiler_port = Cell(profiler_port);
impl Profiler {
pub fn create(port: Port<ProfilerMsg>) {
let port = Cell(port);
do spawn {
let mut profiler_context = ProfilerContext::new(profiler_port.take(), period);
profiler_context.start();
let mut profiler = Profiler::new(port.take());
profiler.start();
}
}
ProfilerTask {
chan: profiler_chan
}
}
}
impl ProfilerContext {
pub fn new(port: ProfilerPort, period: Option<f64>) -> ProfilerContext {
let (verbose, period) = match period {
Some(period) => (true, period),
None => (false, 0f64)
};
ProfilerContext {
pub fn new(port: Port<ProfilerMsg>) -> Profiler {
Profiler {
port: port,
buckets: ProfilerCategory::empty_buckets(),
verbose: verbose,
period: period,
last_print: 0f64,
last_msg: None,
}
}
@ -145,25 +135,19 @@ impl ProfilerContext {
priv fn handle_msg(&mut self, msg: ProfilerMsg) {
match msg {
TimeMsg(category, t) => {
TimeMsg(category, t) => match self.buckets[category as uint] {
// FIXME(#3874): this should be a let (cat, ref mut bucket) = ...,
// not a match
match self.buckets[category as uint] {
(_, ref mut data) => {
data.push(t);
}
}
if self.verbose {
let cur_time = precise_time_ns() as f64 / 1000000000f64;
if cur_time - self.last_print > self.period {
self.last_print = cur_time;
self.print_buckets();
}
}
}
ForcePrintMsg => self.print_buckets(),
},
PrintMsg => match self.last_msg {
Some(TimeMsg(*)) => self.print_buckets(),
_ => {}
},
};
self.last_msg = Some(msg);
}
priv fn print_buckets(&mut self) {

@ -1 +1 @@
Subproject commit da248d3f5b3ed6d9e804c543563be8e34baf1673
Subproject commit d722188de3876ed748382965eb4f300fc1b78bf8