mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +01:00
Invert control flow, fix resizing, and improve checkerboarding
significantly by giving tiles some time to paint before we render unrendered content.
This commit is contained in:
parent
e483a189a3
commit
10f7b49cf7
27 changed files with 1195 additions and 678 deletions
|
@ -7,21 +7,23 @@ use compositor_layer::{ScrollPositionChanged, WantsScrollEvents};
|
|||
use compositor_task::{Msg, CompositorTask, Exit, ChangeReadyState, SetIds, LayerProperties};
|
||||
use compositor_task::{GetGraphicsMetadata, CreateOrUpdateRootLayer, CreateOrUpdateDescendantLayer};
|
||||
use compositor_task::{SetLayerOrigin, Paint, ScrollFragmentPoint, LoadComplete};
|
||||
use compositor_task::{ShutdownComplete, ChangeRenderState, RenderMsgDiscarded};
|
||||
use compositor_task::{ShutdownComplete, ChangeRenderState, RenderMsgDiscarded, ScrollTimeout};
|
||||
use compositor_task::{CompositorEventListener, CompositorProxy, CompositorReceiver};
|
||||
use constellation::SendableFrameTree;
|
||||
use pipeline::CompositionPipeline;
|
||||
use scrolling::ScrollingTimerProxy;
|
||||
use windowing;
|
||||
use windowing::{FinishedWindowEvent, IdleWindowEvent, LoadUrlWindowEvent, MouseWindowClickEvent};
|
||||
use windowing::{MouseWindowEvent, MouseWindowEventClass, MouseWindowMouseDownEvent};
|
||||
use windowing::{MouseWindowMouseUpEvent, MouseWindowMoveEventClass, NavigationWindowEvent};
|
||||
use windowing::{QuitWindowEvent, RefreshWindowEvent, ResizeWindowEvent, ScrollWindowEvent};
|
||||
use windowing::{WindowEvent, WindowMethods, WindowNavigateMsg, ZoomWindowEvent};
|
||||
use windowing::PinchZoomWindowEvent;
|
||||
use windowing::{PinchZoomWindowEvent};
|
||||
|
||||
use azure::azure_hl;
|
||||
use std::cmp;
|
||||
use std::mem;
|
||||
use std::num::Zero;
|
||||
use std::time::duration::Duration;
|
||||
use geom::point::{Point2D, TypedPoint2D};
|
||||
use geom::rect::{Rect, TypedRect};
|
||||
use geom::size::TypedSize2D;
|
||||
|
@ -45,20 +47,18 @@ use servo_util::memory::MemoryProfilerChan;
|
|||
use servo_util::opts;
|
||||
use servo_util::time::{profile, TimeProfilerChan};
|
||||
use servo_util::{memory, time};
|
||||
use std::io::timer::sleep;
|
||||
use std::collections::hashmap::HashMap;
|
||||
use std::path::Path;
|
||||
use std::rc::Rc;
|
||||
use time::precise_time_s;
|
||||
use time::{precise_time_ns, precise_time_s};
|
||||
use url::Url;
|
||||
|
||||
|
||||
pub struct IOCompositor<Window: WindowMethods> {
|
||||
/// The application window.
|
||||
window: Rc<Window>,
|
||||
|
||||
/// The port on which we receive messages.
|
||||
port: Receiver<Msg>,
|
||||
port: Box<CompositorReceiver>,
|
||||
|
||||
/// The render context.
|
||||
context: RenderContext,
|
||||
|
@ -82,20 +82,23 @@ pub struct IOCompositor<Window: WindowMethods> {
|
|||
/// The device pixel ratio for this window.
|
||||
hidpi_factor: ScaleFactor<ScreenPx, DevicePixel, f32>,
|
||||
|
||||
/// Tracks whether the renderer has finished its first rendering
|
||||
composite_ready: bool,
|
||||
/// A handle to the scrolling timer.
|
||||
scrolling_timer: ScrollingTimerProxy,
|
||||
|
||||
/// Tracks whether we should composite this frame.
|
||||
composition_request: CompositionRequest,
|
||||
|
||||
/// Tracks whether we are in the process of shutting down, or have shut down and should close
|
||||
/// the compositor.
|
||||
shutdown_state: ShutdownState,
|
||||
|
||||
/// Tracks whether we need to re-composite a page.
|
||||
recomposite: bool,
|
||||
|
||||
/// Tracks outstanding render_msg's sent to the render tasks.
|
||||
outstanding_render_msgs: uint,
|
||||
|
||||
/// Tracks whether the zoom action has happend recently.
|
||||
/// Tracks the last composite time.
|
||||
last_composite_time: u64,
|
||||
|
||||
/// Tracks whether the zoom action has happened recently.
|
||||
zoom_action: bool,
|
||||
|
||||
/// The time of the last zoom action has started.
|
||||
|
@ -112,6 +115,9 @@ pub struct IOCompositor<Window: WindowMethods> {
|
|||
/// many times for a single page.
|
||||
got_load_complete_message: bool,
|
||||
|
||||
/// Whether we have gotten a `SetIds` message.
|
||||
got_set_ids_message: bool,
|
||||
|
||||
/// The channel on which messages can be sent to the constellation.
|
||||
constellation_chan: ConstellationChan,
|
||||
|
||||
|
@ -122,10 +128,25 @@ pub struct IOCompositor<Window: WindowMethods> {
|
|||
memory_profiler_chan: MemoryProfilerChan,
|
||||
|
||||
/// Pending scroll to fragment event, if any
|
||||
fragment_point: Option<Point2D<f32>>
|
||||
fragment_point: Option<Point2D<f32>>,
|
||||
|
||||
/// Pending scroll events.
|
||||
pending_scroll_events: Vec<ScrollEvent>,
|
||||
}
|
||||
|
||||
pub struct ScrollEvent {
|
||||
delta: TypedPoint2D<DevicePixel,f32>,
|
||||
cursor: TypedPoint2D<DevicePixel,i32>,
|
||||
}
|
||||
|
||||
#[deriving(PartialEq)]
|
||||
enum CompositionRequest {
|
||||
NoCompositingNecessary,
|
||||
CompositeOnScrollTimeout(u64),
|
||||
CompositeNow,
|
||||
}
|
||||
|
||||
#[deriving(PartialEq, Show)]
|
||||
enum ShutdownState {
|
||||
NotShuttingDown,
|
||||
ShuttingDown,
|
||||
|
@ -139,11 +160,12 @@ struct HitTestResult {
|
|||
|
||||
impl<Window: WindowMethods> IOCompositor<Window> {
|
||||
fn new(window: Rc<Window>,
|
||||
port: Receiver<Msg>,
|
||||
sender: Box<CompositorProxy+Send>,
|
||||
receiver: Box<CompositorReceiver>,
|
||||
constellation_chan: ConstellationChan,
|
||||
time_profiler_chan: TimeProfilerChan,
|
||||
memory_profiler_chan: MemoryProfilerChan) -> IOCompositor<Window> {
|
||||
|
||||
memory_profiler_chan: MemoryProfilerChan)
|
||||
-> IOCompositor<Window> {
|
||||
// Create an initial layer tree.
|
||||
//
|
||||
// TODO: There should be no initial layer tree until the renderer creates one from the
|
||||
|
@ -155,7 +177,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
let show_debug_borders = opts::get().show_debug_borders;
|
||||
IOCompositor {
|
||||
window: window,
|
||||
port: port,
|
||||
port: receiver,
|
||||
context: rendergl::RenderContext::new(context, show_debug_borders),
|
||||
root_pipeline: None,
|
||||
scene: Scene::new(Rect {
|
||||
|
@ -164,9 +186,10 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
}),
|
||||
window_size: window_size,
|
||||
hidpi_factor: hidpi_factor,
|
||||
composite_ready: false,
|
||||
scrolling_timer: ScrollingTimerProxy::new(sender),
|
||||
composition_request: NoCompositingNecessary,
|
||||
pending_scroll_events: Vec::new(),
|
||||
shutdown_state: NotShuttingDown,
|
||||
recomposite: false,
|
||||
page_zoom: ScaleFactor(1.0),
|
||||
viewport_zoom: ScaleFactor(1.0),
|
||||
zoom_action: false,
|
||||
|
@ -174,167 +197,129 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
ready_states: HashMap::new(),
|
||||
render_states: HashMap::new(),
|
||||
got_load_complete_message: false,
|
||||
got_set_ids_message: false,
|
||||
constellation_chan: constellation_chan,
|
||||
time_profiler_chan: time_profiler_chan,
|
||||
memory_profiler_chan: memory_profiler_chan,
|
||||
fragment_point: None,
|
||||
outstanding_render_msgs: 0,
|
||||
last_composite_time: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create(window: Rc<Window>,
|
||||
port: Receiver<Msg>,
|
||||
sender: Box<CompositorProxy+Send>,
|
||||
receiver: Box<CompositorReceiver>,
|
||||
constellation_chan: ConstellationChan,
|
||||
time_profiler_chan: TimeProfilerChan,
|
||||
memory_profiler_chan: MemoryProfilerChan) {
|
||||
memory_profiler_chan: MemoryProfilerChan)
|
||||
-> IOCompositor<Window> {
|
||||
let mut compositor = IOCompositor::new(window,
|
||||
port,
|
||||
sender,
|
||||
receiver,
|
||||
constellation_chan,
|
||||
time_profiler_chan,
|
||||
memory_profiler_chan);
|
||||
|
||||
// Set the size of the root layer.
|
||||
compositor.update_zoom_transform();
|
||||
|
||||
// Starts the compositor, which listens for messages on the specified port.
|
||||
compositor.run();
|
||||
}
|
||||
|
||||
fn run (&mut self) {
|
||||
// Tell the constellation about the initial window size.
|
||||
self.send_window_size();
|
||||
compositor.send_window_size();
|
||||
|
||||
// Enter the main event loop.
|
||||
while self.shutdown_state != FinishedShuttingDown {
|
||||
// Check for new messages coming from the rendering task.
|
||||
self.handle_message();
|
||||
|
||||
if self.shutdown_state == FinishedShuttingDown {
|
||||
// We have exited the compositor and passing window
|
||||
// messages to script may crash.
|
||||
debug!("Exiting the compositor due to a request from script.");
|
||||
break;
|
||||
}
|
||||
|
||||
// Check for messages coming from the windowing system.
|
||||
let msg = self.window.recv();
|
||||
self.handle_window_message(msg);
|
||||
|
||||
// If asked to recomposite and renderer has run at least once
|
||||
if self.recomposite && self.composite_ready {
|
||||
self.recomposite = false;
|
||||
self.composite();
|
||||
}
|
||||
|
||||
sleep(Duration::milliseconds(10));
|
||||
|
||||
// If a pinch-zoom happened recently, ask for tiles at the new resolution
|
||||
if self.zoom_action && precise_time_s() - self.zoom_time > 0.3 {
|
||||
self.zoom_action = false;
|
||||
self.scene.mark_layer_contents_as_changed_recursively();
|
||||
self.send_buffer_requests_for_all_layers();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Clear out the compositor layers so that painting tasks can destroy the buffers.
|
||||
match self.scene.root {
|
||||
None => {}
|
||||
Some(ref layer) => layer.forget_all_tiles(),
|
||||
}
|
||||
|
||||
// Drain compositor port, sometimes messages contain channels that are blocking
|
||||
// another task from finishing (i.e. SetIds)
|
||||
loop {
|
||||
match self.port.try_recv() {
|
||||
Err(_) => break,
|
||||
Ok(_) => {},
|
||||
}
|
||||
}
|
||||
|
||||
// Tell the profiler and memory profiler to shut down.
|
||||
let TimeProfilerChan(ref time_profiler_chan) = self.time_profiler_chan;
|
||||
time_profiler_chan.send(time::ExitMsg);
|
||||
|
||||
let MemoryProfilerChan(ref memory_profiler_chan) = self.memory_profiler_chan;
|
||||
memory_profiler_chan.send(memory::ExitMsg);
|
||||
compositor
|
||||
}
|
||||
|
||||
fn handle_message(&mut self) {
|
||||
loop {
|
||||
match (self.port.try_recv(), self.shutdown_state) {
|
||||
(_, FinishedShuttingDown) =>
|
||||
fail!("compositor shouldn't be handling messages after shutting down"),
|
||||
fn handle_browser_message(&mut self, msg: Msg) -> bool {
|
||||
match (msg, self.shutdown_state) {
|
||||
(_, FinishedShuttingDown) =>
|
||||
fail!("compositor shouldn't be handling messages after shutting down"),
|
||||
|
||||
(Err(_), _) => break,
|
||||
|
||||
(Ok(Exit(chan)), _) => {
|
||||
debug!("shutting down the constellation");
|
||||
let ConstellationChan(ref con_chan) = self.constellation_chan;
|
||||
con_chan.send(ExitMsg);
|
||||
chan.send(());
|
||||
self.shutdown_state = ShuttingDown;
|
||||
}
|
||||
|
||||
(Ok(ShutdownComplete), _) => {
|
||||
debug!("constellation completed shutdown");
|
||||
self.shutdown_state = FinishedShuttingDown;
|
||||
break;
|
||||
}
|
||||
|
||||
(Ok(ChangeReadyState(pipeline_id, ready_state)), NotShuttingDown) => {
|
||||
self.change_ready_state(pipeline_id, ready_state);
|
||||
}
|
||||
|
||||
(Ok(ChangeRenderState(pipeline_id, render_state)), NotShuttingDown) => {
|
||||
self.change_render_state(pipeline_id, render_state);
|
||||
}
|
||||
|
||||
(Ok(RenderMsgDiscarded), NotShuttingDown) => {
|
||||
self.remove_outstanding_render_msg();
|
||||
}
|
||||
|
||||
(Ok(SetIds(frame_tree, response_chan, new_constellation_chan)), _) => {
|
||||
self.set_frame_tree(&frame_tree,
|
||||
response_chan,
|
||||
new_constellation_chan);
|
||||
}
|
||||
|
||||
(Ok(GetGraphicsMetadata(chan)), NotShuttingDown) => {
|
||||
chan.send(Some(self.window.native_metadata()));
|
||||
}
|
||||
|
||||
(Ok(CreateOrUpdateRootLayer(layer_properties)), NotShuttingDown) => {
|
||||
self.create_or_update_root_layer(layer_properties);
|
||||
}
|
||||
|
||||
(Ok(CreateOrUpdateDescendantLayer(layer_properties)), NotShuttingDown) => {
|
||||
self.create_or_update_descendant_layer(layer_properties);
|
||||
}
|
||||
|
||||
(Ok(SetLayerOrigin(pipeline_id, layer_id, origin)), NotShuttingDown) => {
|
||||
self.set_layer_origin(pipeline_id, layer_id, origin);
|
||||
}
|
||||
|
||||
(Ok(Paint(pipeline_id, epoch, replies)), NotShuttingDown) => {
|
||||
for (layer_id, new_layer_buffer_set) in replies.into_iter() {
|
||||
self.paint(pipeline_id, layer_id, new_layer_buffer_set, epoch);
|
||||
}
|
||||
self.remove_outstanding_render_msg();
|
||||
}
|
||||
|
||||
(Ok(ScrollFragmentPoint(pipeline_id, layer_id, point)), NotShuttingDown) => {
|
||||
self.scroll_fragment_to_point(pipeline_id, layer_id, point);
|
||||
}
|
||||
|
||||
(Ok(LoadComplete(..)), NotShuttingDown) => {
|
||||
self.got_load_complete_message = true;
|
||||
}
|
||||
|
||||
// When we are shutting_down, we need to avoid performing operations
|
||||
// such as Paint that may crash because we have begun tearing down
|
||||
// the rest of our resources.
|
||||
(_, ShuttingDown) => { }
|
||||
(Exit(chan), _) => {
|
||||
debug!("shutting down the constellation");
|
||||
let ConstellationChan(ref con_chan) = self.constellation_chan;
|
||||
con_chan.send(ExitMsg);
|
||||
chan.send(());
|
||||
self.shutdown_state = ShuttingDown;
|
||||
}
|
||||
|
||||
(ShutdownComplete, _) => {
|
||||
debug!("constellation completed shutdown");
|
||||
self.shutdown_state = FinishedShuttingDown;
|
||||
return false;
|
||||
}
|
||||
|
||||
(ChangeReadyState(pipeline_id, ready_state), NotShuttingDown) => {
|
||||
self.change_ready_state(pipeline_id, ready_state);
|
||||
}
|
||||
|
||||
(ChangeRenderState(pipeline_id, render_state), NotShuttingDown) => {
|
||||
self.change_render_state(pipeline_id, render_state);
|
||||
}
|
||||
|
||||
(RenderMsgDiscarded, NotShuttingDown) => {
|
||||
self.remove_outstanding_render_msg();
|
||||
}
|
||||
|
||||
(SetIds(frame_tree, response_chan, new_constellation_chan), NotShuttingDown) => {
|
||||
self.set_frame_tree(&frame_tree,
|
||||
response_chan,
|
||||
new_constellation_chan);
|
||||
}
|
||||
|
||||
(CreateOrUpdateRootLayer(layer_properties), NotShuttingDown) => {
|
||||
self.create_or_update_root_layer(layer_properties);
|
||||
}
|
||||
|
||||
(CreateOrUpdateDescendantLayer(layer_properties), NotShuttingDown) => {
|
||||
self.create_or_update_descendant_layer(layer_properties);
|
||||
}
|
||||
|
||||
(GetGraphicsMetadata(chan), NotShuttingDown) => {
|
||||
chan.send(Some(self.window.native_metadata()));
|
||||
}
|
||||
|
||||
(SetLayerOrigin(pipeline_id, layer_id, origin), NotShuttingDown) => {
|
||||
self.set_layer_origin(pipeline_id, layer_id, origin);
|
||||
}
|
||||
|
||||
(Paint(pipeline_id, epoch, replies), NotShuttingDown) => {
|
||||
for (layer_id, new_layer_buffer_set) in replies.into_iter() {
|
||||
self.paint(pipeline_id, layer_id, new_layer_buffer_set, epoch);
|
||||
}
|
||||
self.remove_outstanding_render_msg();
|
||||
}
|
||||
|
||||
(ScrollFragmentPoint(pipeline_id, layer_id, point), NotShuttingDown) => {
|
||||
self.scroll_fragment_to_point(pipeline_id, layer_id, point);
|
||||
}
|
||||
|
||||
(LoadComplete(..), NotShuttingDown) => {
|
||||
self.got_load_complete_message = true;
|
||||
|
||||
// If we're rendering in headless mode, schedule a recomposite.
|
||||
if opts::get().output_file.is_some() {
|
||||
self.composite_if_necessary();
|
||||
}
|
||||
}
|
||||
|
||||
(ScrollTimeout(timestamp), NotShuttingDown) => {
|
||||
debug!("scroll timeout, drawing unrendered content!");
|
||||
match self.composition_request {
|
||||
CompositeOnScrollTimeout(this_timestamp) if timestamp == this_timestamp => {
|
||||
self.composition_request = CompositeNow
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
// When we are shutting_down, we need to avoid performing operations
|
||||
// such as Paint that may crash because we have begun tearing down
|
||||
// the rest of our resources.
|
||||
(_, ShuttingDown) => { }
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn change_ready_state(&mut self, pipeline_id: PipelineId, ready_state: ReadyState) {
|
||||
|
@ -342,6 +327,11 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
ready_state,
|
||||
|_key, value| *value = ready_state);
|
||||
self.window.set_ready_state(self.get_earliest_pipeline_ready_state());
|
||||
|
||||
// If we're rendering in headless mode, schedule a recomposite.
|
||||
if opts::get().output_file.is_some() {
|
||||
self.composite_if_necessary()
|
||||
}
|
||||
}
|
||||
|
||||
fn get_earliest_pipeline_ready_state(&self) -> ReadyState {
|
||||
|
@ -357,9 +347,6 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
render_state,
|
||||
|_key, value| *value = render_state);
|
||||
self.window.set_render_state(render_state);
|
||||
if render_state == IdleRenderState {
|
||||
self.composite_ready = true;
|
||||
}
|
||||
}
|
||||
|
||||
fn all_pipelines_in_idle_render_state(&self) -> bool {
|
||||
|
@ -417,6 +404,9 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
// Initialize the new constellation channel by sending it the root window size.
|
||||
self.constellation_chan = new_constellation_chan;
|
||||
self.send_window_size();
|
||||
|
||||
self.got_set_ids_message = true;
|
||||
self.composite_if_necessary();
|
||||
}
|
||||
|
||||
fn create_frame_tree_root_layers(&mut self,
|
||||
|
@ -529,7 +519,6 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
}));
|
||||
}
|
||||
|
||||
|
||||
pub fn move_layer(&self,
|
||||
pipeline_id: PipelineId,
|
||||
layer_id: LayerId,
|
||||
|
@ -555,10 +544,21 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
fail!("Compositor: Tried to scroll to fragment with unknown layer.");
|
||||
}
|
||||
|
||||
self.recomposite = true;
|
||||
self.start_scrolling_timer_if_necessary();
|
||||
}
|
||||
None => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn start_scrolling_timer_if_necessary(&mut self) {
|
||||
match self.composition_request {
|
||||
CompositeNow | CompositeOnScrollTimeout(_) => return,
|
||||
NoCompositingNecessary => {}
|
||||
}
|
||||
|
||||
let timestamp = precise_time_ns();
|
||||
self.scrolling_timer.scroll_event_processed(timestamp);
|
||||
self.composition_request = CompositeOnScrollTimeout(timestamp);
|
||||
}
|
||||
|
||||
fn set_layer_origin(&mut self,
|
||||
|
@ -580,7 +580,9 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
layer_id: LayerId,
|
||||
new_layer_buffer_set: Box<LayerBufferSet>,
|
||||
epoch: Epoch) {
|
||||
debug!("compositor received new frame");
|
||||
debug!("compositor received new frame at size {}x{}",
|
||||
self.window_size.width.get(),
|
||||
self.window_size.height.get());
|
||||
|
||||
// From now on, if we destroy the buffers, they will leak.
|
||||
let mut new_layer_buffer_set = new_layer_buffer_set;
|
||||
|
@ -588,8 +590,10 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
|
||||
match self.find_layer_with_pipeline_and_layer_id(pipeline_id, layer_id) {
|
||||
Some(ref layer) => {
|
||||
// FIXME(pcwalton): This is going to cause problems with inconsistent frames since
|
||||
// we only composite one layer at a time.
|
||||
assert!(layer.add_buffers(new_layer_buffer_set, epoch));
|
||||
self.recomposite = true;
|
||||
self.composite_if_necessary();
|
||||
}
|
||||
None => {
|
||||
// FIXME: This may potentially be triggered by a race condition where a
|
||||
|
@ -605,8 +609,9 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
layer_id: LayerId,
|
||||
point: Point2D<f32>) {
|
||||
if self.move_layer(pipeline_id, layer_id, Point2D::from_untyped(&point)) {
|
||||
self.recomposite = true;
|
||||
self.send_buffer_requests_for_all_layers();
|
||||
if self.send_buffer_requests_for_all_layers() {
|
||||
self.start_scrolling_timer_if_necessary();
|
||||
}
|
||||
} else {
|
||||
self.fragment_point = Some(point);
|
||||
}
|
||||
|
@ -617,7 +622,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
IdleWindowEvent => {}
|
||||
|
||||
RefreshWindowEvent => {
|
||||
self.recomposite = true;
|
||||
self.composite_if_necessary()
|
||||
}
|
||||
|
||||
ResizeWindowEvent(size) => {
|
||||
|
@ -685,6 +690,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
|
||||
debug!("osmain: window resized to {:?}", new_size);
|
||||
self.window_size = new_size;
|
||||
|
||||
self.scene.set_root_layer_size(new_size.as_f32());
|
||||
self.send_window_size();
|
||||
}
|
||||
|
@ -698,7 +704,8 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
layers"),
|
||||
};
|
||||
|
||||
let msg = LoadUrlMsg(root_pipeline_id, LoadData::new(Url::parse(url_string.as_slice()).unwrap()));
|
||||
let msg = LoadUrlMsg(root_pipeline_id,
|
||||
LoadData::new(Url::parse(url_string.as_slice()).unwrap()));
|
||||
let ConstellationChan(ref chan) = self.constellation_chan;
|
||||
chan.send(msg);
|
||||
}
|
||||
|
@ -725,18 +732,29 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
fn on_scroll_window_event(&mut self,
|
||||
delta: TypedPoint2D<DevicePixel, f32>,
|
||||
cursor: TypedPoint2D<DevicePixel, i32>) {
|
||||
let delta = delta / self.scene.scale;
|
||||
let cursor = cursor.as_f32() / self.scene.scale;
|
||||
self.pending_scroll_events.push(ScrollEvent {
|
||||
delta: delta,
|
||||
cursor: cursor,
|
||||
});
|
||||
|
||||
let mut scroll = false;
|
||||
match self.scene.root {
|
||||
Some(ref mut layer) => {
|
||||
scroll = layer.handle_scroll_event(delta, cursor) == ScrollPositionChanged;
|
||||
}
|
||||
None => { }
|
||||
self.composite_if_necessary();
|
||||
}
|
||||
|
||||
fn process_pending_scroll_events(&mut self) {
|
||||
for scroll_event in mem::replace(&mut self.pending_scroll_events, Vec::new()).into_iter() {
|
||||
let delta = scroll_event.delta / self.scene.scale;
|
||||
let cursor = scroll_event.cursor.as_f32() / self.scene.scale;
|
||||
|
||||
let scrolled = match self.scene.root {
|
||||
Some(ref mut layer) => {
|
||||
layer.handle_scroll_event(delta, cursor) == ScrollPositionChanged
|
||||
}
|
||||
None => false,
|
||||
};
|
||||
|
||||
self.start_scrolling_timer_if_necessary();
|
||||
self.send_buffer_requests_for_all_layers();
|
||||
}
|
||||
self.recomposite_if(scroll);
|
||||
self.send_buffer_requests_for_all_layers();
|
||||
}
|
||||
|
||||
fn device_pixels_per_screen_px(&self) -> ScaleFactor<ScreenPx, DevicePixel, f32> {
|
||||
|
@ -768,6 +786,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
self.send_window_size();
|
||||
}
|
||||
|
||||
// TODO(pcwalton): I think this should go through the same queuing as scroll events do.
|
||||
fn on_pinch_zoom_window_event(&mut self, magnification: f32) {
|
||||
self.zoom_action = true;
|
||||
self.zoom_time = precise_time_s();
|
||||
|
@ -792,7 +811,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
None => { }
|
||||
}
|
||||
|
||||
self.recomposite = true;
|
||||
self.composite_if_necessary();
|
||||
}
|
||||
|
||||
fn on_navigation_window_event(&self, direction: WindowNavigateMsg) {
|
||||
|
@ -840,9 +859,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
match self.root_pipeline {
|
||||
Some(ref pipeline) => {
|
||||
let unused_buffers = self.scene.collect_unused_buffers();
|
||||
let have_unused_buffers = unused_buffers.len() > 0;
|
||||
self.recomposite = self.recomposite || have_unused_buffers;
|
||||
if have_unused_buffers {
|
||||
if unused_buffers.len() != 0 {
|
||||
let message = UnusedBufferMsg(unused_buffers);
|
||||
let _ = pipeline.render_chan.send_opt(message);
|
||||
}
|
||||
|
@ -851,7 +868,8 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
}
|
||||
}
|
||||
|
||||
fn send_buffer_requests_for_all_layers(&mut self) {
|
||||
/// Returns true if any buffer requests were sent or false otherwise.
|
||||
fn send_buffer_requests_for_all_layers(&mut self) -> bool {
|
||||
let mut layers_and_requests = Vec::new();
|
||||
self.scene.get_buffer_requests(&mut layers_and_requests,
|
||||
Rect(TypedPoint2D(0f32, 0f32), self.window_size.as_f32()));
|
||||
|
@ -860,7 +878,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
self.send_back_unused_buffers();
|
||||
|
||||
if layers_and_requests.len() == 0 {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// We want to batch requests for each pipeline to avoid race conditions
|
||||
|
@ -875,6 +893,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
}
|
||||
|
||||
self.add_outstanding_render_msg(num_render_msgs_sent);
|
||||
true
|
||||
}
|
||||
|
||||
fn is_ready_to_render_image_output(&self) -> bool {
|
||||
|
@ -893,6 +912,11 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
if !self.all_pipelines_in_idle_render_state() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if !self.got_set_ids_message {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -939,10 +963,11 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
});
|
||||
|
||||
if output_image {
|
||||
let path = from_str::<Path>(opts::get().output_file.as_ref().unwrap().as_slice()).unwrap();
|
||||
let path =
|
||||
from_str::<Path>(opts::get().output_file.as_ref().unwrap().as_slice()).unwrap();
|
||||
let mut pixels = gl::read_pixels(0, 0,
|
||||
width as GLsizei,
|
||||
height as GLsizei,
|
||||
width as gl::GLsizei,
|
||||
height as gl::GLsizei,
|
||||
gl::RGB, gl::UNSIGNED_BYTE);
|
||||
|
||||
gl::bind_framebuffer(gl::FRAMEBUFFER, 0);
|
||||
|
@ -976,18 +1001,26 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
self.shutdown_state = ShuttingDown;
|
||||
}
|
||||
|
||||
// Perform the page flip. This will likely block for a while.
|
||||
self.window.present();
|
||||
|
||||
self.last_composite_time = precise_time_ns();
|
||||
|
||||
let exit = opts::get().exit_after_load;
|
||||
if exit {
|
||||
debug!("shutting down the constellation for exit_after_load");
|
||||
let ConstellationChan(ref chan) = self.constellation_chan;
|
||||
chan.send(ExitMsg);
|
||||
}
|
||||
|
||||
self.composition_request = NoCompositingNecessary;
|
||||
self.process_pending_scroll_events();
|
||||
}
|
||||
|
||||
fn recomposite_if(&mut self, result: bool) {
|
||||
self.recomposite = result || self.recomposite;
|
||||
fn composite_if_necessary(&mut self) {
|
||||
if self.composition_request == NoCompositingNecessary {
|
||||
self.composition_request = CompositeNow
|
||||
}
|
||||
}
|
||||
|
||||
fn find_topmost_layer_at_point_for_layer(&self,
|
||||
|
@ -1054,3 +1087,87 @@ fn find_layer_with_pipeline_and_layer_id_for_layer(layer: Rc<Layer<CompositorDat
|
|||
|
||||
return None;
|
||||
}
|
||||
|
||||
impl<Window> CompositorEventListener for IOCompositor<Window> where Window: WindowMethods {
|
||||
fn handle_event(&mut self, msg: WindowEvent) -> bool {
|
||||
// Check for new messages coming from the other tasks in the system.
|
||||
loop {
|
||||
match self.port.try_recv_compositor_msg() {
|
||||
None => break,
|
||||
Some(msg) => {
|
||||
if !self.handle_browser_message(msg) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.shutdown_state == FinishedShuttingDown {
|
||||
// We have exited the compositor and passing window
|
||||
// messages to script may crash.
|
||||
debug!("Exiting the compositor due to a request from script.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle the message coming from the windowing system.
|
||||
self.handle_window_message(msg);
|
||||
|
||||
// If a pinch-zoom happened recently, ask for tiles at the new resolution
|
||||
if self.zoom_action && precise_time_s() - self.zoom_time > 0.3 {
|
||||
self.zoom_action = false;
|
||||
self.scene.mark_layer_contents_as_changed_recursively();
|
||||
self.send_buffer_requests_for_all_layers();
|
||||
}
|
||||
|
||||
match self.composition_request {
|
||||
NoCompositingNecessary | CompositeOnScrollTimeout(_) => {}
|
||||
CompositeNow => self.composite(),
|
||||
}
|
||||
|
||||
self.shutdown_state != FinishedShuttingDown
|
||||
}
|
||||
|
||||
/// Repaints and recomposites synchronously. You must be careful when calling this, as if a
|
||||
/// paint is not scheduled the compositor will hang forever.
|
||||
///
|
||||
/// This is used when resizing the window.
|
||||
fn repaint_synchronously(&mut self) {
|
||||
while self.shutdown_state != ShuttingDown {
|
||||
let msg = self.port.recv_compositor_msg();
|
||||
let is_paint = match msg {
|
||||
Paint(..) => true,
|
||||
_ => false,
|
||||
};
|
||||
let keep_going = self.handle_browser_message(msg);
|
||||
if is_paint {
|
||||
self.composite();
|
||||
break
|
||||
}
|
||||
if !keep_going {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn shutdown(&mut self) {
|
||||
// Clear out the compositor layers so that painting tasks can destroy the buffers.
|
||||
match self.scene.root {
|
||||
None => {}
|
||||
Some(ref layer) => layer.forget_all_tiles(),
|
||||
}
|
||||
|
||||
// Drain compositor port, sometimes messages contain channels that are blocking
|
||||
// another task from finishing (i.e. SetIds)
|
||||
while self.port.try_recv_compositor_msg().is_some() {}
|
||||
|
||||
// Tell the profiler, memory profiler, and scrolling timer to shut down.
|
||||
let TimeProfilerChan(ref time_profiler_chan) = self.time_profiler_chan;
|
||||
time_profiler_chan.send(time::ExitMsg);
|
||||
|
||||
let MemoryProfilerChan(ref memory_profiler_chan) = self.memory_profiler_chan;
|
||||
memory_profiler_chan.send(memory::ExitMsg);
|
||||
|
||||
self.scrolling_timer.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,12 +2,14 @@
|
|||
* 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/. */
|
||||
|
||||
//! Communication with the compositor task.
|
||||
|
||||
pub use windowing;
|
||||
pub use constellation::SendableFrameTree;
|
||||
|
||||
use compositor;
|
||||
use headless;
|
||||
pub use constellation::SendableFrameTree;
|
||||
use windowing::WindowMethods;
|
||||
use windowing::{WindowEvent, WindowMethods};
|
||||
|
||||
use azure::azure_hl::{SourceSurfaceMethods, Color};
|
||||
use geom::point::Point2D;
|
||||
|
@ -21,39 +23,64 @@ use servo_msg::constellation_msg::{ConstellationChan, PipelineId};
|
|||
use servo_util::memory::MemoryProfilerChan;
|
||||
use servo_util::time::TimeProfilerChan;
|
||||
use std::comm::{channel, Sender, Receiver};
|
||||
use std::fmt::{FormatError, Formatter, Show};
|
||||
use std::rc::Rc;
|
||||
|
||||
use url::Url;
|
||||
|
||||
/// The implementation of the layers-based compositor.
|
||||
#[deriving(Clone)]
|
||||
pub struct CompositorChan {
|
||||
/// A channel on which messages can be sent to the compositor.
|
||||
pub chan: Sender<Msg>,
|
||||
/// Sends messages to the compositor. This is a trait supplied by the port because the method used
|
||||
/// to communicate with the compositor may have to kick OS event loops awake, communicate cross-
|
||||
/// process, and so forth.
|
||||
pub trait CompositorProxy : 'static + Send {
|
||||
/// Sends a message to the compositor.
|
||||
fn send(&mut self, msg: Msg);
|
||||
/// Clones the compositor proxy.
|
||||
fn clone_compositor_proxy(&self) -> Box<CompositorProxy+'static+Send>;
|
||||
}
|
||||
|
||||
/// The port that the compositor receives messages on. As above, this is a trait supplied by the
|
||||
/// Servo port.
|
||||
pub trait CompositorReceiver for Sized? : 'static {
|
||||
/// Receives the next message inbound for the compositor. This must not block.
|
||||
fn try_recv_compositor_msg(&mut self) -> Option<Msg>;
|
||||
/// Synchronously waits for, and returns, the next message inbound for the compositor.
|
||||
fn recv_compositor_msg(&mut self) -> Msg;
|
||||
}
|
||||
|
||||
/// A convenience implementation of `CompositorReceiver` for a plain old Rust `Receiver`.
|
||||
impl CompositorReceiver for Receiver<Msg> {
|
||||
fn try_recv_compositor_msg(&mut self) -> Option<Msg> {
|
||||
match self.try_recv() {
|
||||
Ok(msg) => Some(msg),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
fn recv_compositor_msg(&mut self) -> Msg {
|
||||
self.recv()
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of the abstract `ScriptListener` interface.
|
||||
impl ScriptListener for CompositorChan {
|
||||
fn set_ready_state(&self, pipeline_id: PipelineId, ready_state: ReadyState) {
|
||||
impl ScriptListener for Box<CompositorProxy+'static+Send> {
|
||||
fn set_ready_state(&mut self, pipeline_id: PipelineId, ready_state: ReadyState) {
|
||||
let msg = ChangeReadyState(pipeline_id, ready_state);
|
||||
self.chan.send(msg);
|
||||
self.send(msg);
|
||||
}
|
||||
|
||||
fn scroll_fragment_point(&self,
|
||||
fn scroll_fragment_point(&mut self,
|
||||
pipeline_id: PipelineId,
|
||||
layer_id: LayerId,
|
||||
point: Point2D<f32>) {
|
||||
self.chan.send(ScrollFragmentPoint(pipeline_id, layer_id, point));
|
||||
self.send(ScrollFragmentPoint(pipeline_id, layer_id, point));
|
||||
}
|
||||
|
||||
fn close(&self) {
|
||||
fn close(&mut self) {
|
||||
let (chan, port) = channel();
|
||||
self.chan.send(Exit(chan));
|
||||
self.send(Exit(chan));
|
||||
port.recv();
|
||||
}
|
||||
|
||||
fn dup(&self) -> Box<ScriptListener+'static> {
|
||||
box self.clone() as Box<ScriptListener+'static>
|
||||
fn dup(&mut self) -> Box<ScriptListener+'static> {
|
||||
box self.clone_compositor_proxy() as Box<ScriptListener+'static>
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,21 +110,21 @@ impl LayerProperties {
|
|||
}
|
||||
|
||||
/// Implementation of the abstract `RenderListener` interface.
|
||||
impl RenderListener for CompositorChan {
|
||||
fn get_graphics_metadata(&self) -> Option<NativeGraphicsMetadata> {
|
||||
impl RenderListener for Box<CompositorProxy+'static+Send> {
|
||||
fn get_graphics_metadata(&mut self) -> Option<NativeGraphicsMetadata> {
|
||||
let (chan, port) = channel();
|
||||
self.chan.send(GetGraphicsMetadata(chan));
|
||||
self.send(GetGraphicsMetadata(chan));
|
||||
port.recv()
|
||||
}
|
||||
|
||||
fn paint(&self,
|
||||
fn paint(&mut self,
|
||||
pipeline_id: PipelineId,
|
||||
epoch: Epoch,
|
||||
replies: Vec<(LayerId, Box<LayerBufferSet>)>) {
|
||||
self.chan.send(Paint(pipeline_id, epoch, replies));
|
||||
self.send(Paint(pipeline_id, epoch, replies));
|
||||
}
|
||||
|
||||
fn initialize_layers_for_pipeline(&self,
|
||||
fn initialize_layers_for_pipeline(&mut self,
|
||||
pipeline_id: PipelineId,
|
||||
metadata: Vec<LayerMetadata>,
|
||||
epoch: Epoch) {
|
||||
|
@ -108,36 +135,23 @@ impl RenderListener for CompositorChan {
|
|||
for metadata in metadata.iter() {
|
||||
let layer_properties = LayerProperties::new(pipeline_id, epoch, metadata);
|
||||
if first {
|
||||
self.chan.send(CreateOrUpdateRootLayer(layer_properties));
|
||||
self.send(CreateOrUpdateRootLayer(layer_properties));
|
||||
first = false
|
||||
} else {
|
||||
self.chan.send(CreateOrUpdateDescendantLayer(layer_properties));
|
||||
self.send(CreateOrUpdateDescendantLayer(layer_properties));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn render_msg_discarded(&self) {
|
||||
self.chan.send(RenderMsgDiscarded);
|
||||
fn render_msg_discarded(&mut self) {
|
||||
self.send(RenderMsgDiscarded);
|
||||
}
|
||||
|
||||
fn set_render_state(&self, pipeline_id: PipelineId, render_state: RenderState) {
|
||||
self.chan.send(ChangeRenderState(pipeline_id, render_state))
|
||||
fn set_render_state(&mut self, pipeline_id: PipelineId, render_state: RenderState) {
|
||||
self.send(ChangeRenderState(pipeline_id, render_state))
|
||||
}
|
||||
}
|
||||
|
||||
impl CompositorChan {
|
||||
pub fn new() -> (Receiver<Msg>, CompositorChan) {
|
||||
let (chan, port) = channel();
|
||||
let compositor_chan = CompositorChan {
|
||||
chan: chan,
|
||||
};
|
||||
(port, compositor_chan)
|
||||
}
|
||||
|
||||
pub fn send(&self, msg: Msg) {
|
||||
self.chan.send(msg);
|
||||
}
|
||||
}
|
||||
/// Messages from the painting task and the constellation task to the compositor task.
|
||||
pub enum Msg {
|
||||
/// Requests that the compositor shut down.
|
||||
|
@ -177,6 +191,30 @@ pub enum Msg {
|
|||
SetIds(SendableFrameTree, Sender<()>, ConstellationChan),
|
||||
/// The load of a page for a given URL has completed.
|
||||
LoadComplete(PipelineId, Url),
|
||||
/// Indicates that the scrolling timeout with the given starting timestamp has happened and a
|
||||
/// composite should happen. (See the `scrolling` module.)
|
||||
ScrollTimeout(u64),
|
||||
}
|
||||
|
||||
impl Show for Msg {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result<(),FormatError> {
|
||||
match *self {
|
||||
Exit(..) => write!(f, "Exit"),
|
||||
ShutdownComplete(..) => write!(f, "ShutdownComplete"),
|
||||
GetGraphicsMetadata(..) => write!(f, "GetGraphicsMetadata"),
|
||||
CreateOrUpdateRootLayer(..) => write!(f, "CreateOrUpdateRootLayer"),
|
||||
CreateOrUpdateDescendantLayer(..) => write!(f, "CreateOrUpdateDescendantLayer"),
|
||||
SetLayerOrigin(..) => write!(f, "SetLayerOrigin"),
|
||||
ScrollFragmentPoint(..) => write!(f, "ScrollFragmentPoint"),
|
||||
Paint(..) => write!(f, "Paint"),
|
||||
ChangeReadyState(..) => write!(f, "ChangeReadyState"),
|
||||
ChangeRenderState(..) => write!(f, "ChangeRenderState"),
|
||||
RenderMsgDiscarded(..) => write!(f, "RenderMsgDiscarded"),
|
||||
SetIds(..) => write!(f, "SetIds"),
|
||||
LoadComplete(..) => write!(f, "LoadComplete"),
|
||||
ScrollTimeout(..) => write!(f, "ScrollTimeout"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CompositorTask;
|
||||
|
@ -196,27 +234,38 @@ impl CompositorTask {
|
|||
NativeCompositingGraphicsContext::new()
|
||||
}
|
||||
|
||||
pub fn create<Window: WindowMethods>(
|
||||
window: Option<Rc<Window>>,
|
||||
port: Receiver<Msg>,
|
||||
constellation_chan: ConstellationChan,
|
||||
time_profiler_chan: TimeProfilerChan,
|
||||
memory_profiler_chan: MemoryProfilerChan) {
|
||||
|
||||
pub fn create<Window>(window: Option<Rc<Window>>,
|
||||
sender: Box<CompositorProxy+Send>,
|
||||
receiver: Box<CompositorReceiver>,
|
||||
constellation_chan: ConstellationChan,
|
||||
time_profiler_chan: TimeProfilerChan,
|
||||
memory_profiler_chan: MemoryProfilerChan)
|
||||
-> Box<CompositorEventListener + 'static>
|
||||
where Window: WindowMethods + 'static {
|
||||
match window {
|
||||
Some(window) => {
|
||||
compositor::IOCompositor::create(window,
|
||||
port,
|
||||
constellation_chan.clone(),
|
||||
time_profiler_chan,
|
||||
memory_profiler_chan)
|
||||
box compositor::IOCompositor::create(window,
|
||||
sender,
|
||||
receiver,
|
||||
constellation_chan.clone(),
|
||||
time_profiler_chan,
|
||||
memory_profiler_chan)
|
||||
as Box<CompositorEventListener>
|
||||
}
|
||||
None => {
|
||||
headless::NullCompositor::create(port,
|
||||
constellation_chan.clone(),
|
||||
time_profiler_chan,
|
||||
memory_profiler_chan)
|
||||
box headless::NullCompositor::create(receiver,
|
||||
constellation_chan.clone(),
|
||||
time_profiler_chan,
|
||||
memory_profiler_chan)
|
||||
as Box<CompositorEventListener>
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CompositorEventListener {
|
||||
fn handle_event(&mut self, event: WindowEvent) -> bool;
|
||||
fn repaint_synchronously(&mut self);
|
||||
fn shutdown(&mut self);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,53 +2,79 @@
|
|||
* 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 compositor_task::{CompositorChan, LoadComplete, ShutdownComplete, SetLayerOrigin, SetIds};
|
||||
use pipeline::{Pipeline, CompositionPipeline};
|
||||
|
||||
use compositor_task::{CompositorProxy, LoadComplete, ShutdownComplete, SetLayerOrigin, SetIds};
|
||||
use devtools_traits::DevtoolsControlChan;
|
||||
use std::collections::hashmap::{HashMap, HashSet};
|
||||
use geom::rect::{Rect, TypedRect};
|
||||
use geom::scale_factor::ScaleFactor;
|
||||
use gfx::font_cache_task::FontCacheTask;
|
||||
use gfx::render_task;
|
||||
use libc;
|
||||
use pipeline::{Pipeline, CompositionPipeline};
|
||||
use layers::geometry::DevicePixel;
|
||||
use layout_traits::{LayoutControlChan, LayoutTaskFactory, ExitNowMsg};
|
||||
use libc;
|
||||
use script_traits::{ResizeMsg, ResizeInactiveMsg, ExitPipelineMsg};
|
||||
use script_traits::{ScriptControlChan, ScriptTaskFactory};
|
||||
use servo_msg::compositor_msg::LayerId;
|
||||
use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, FailureMsg, Failure, FrameRectMsg};
|
||||
use servo_msg::constellation_msg::{IFrameSandboxState, IFrameUnsandboxed, InitLoadUrlMsg};
|
||||
use servo_msg::constellation_msg::{LoadCompleteMsg, LoadIframeUrlMsg, LoadUrlMsg, Msg, NavigateMsg};
|
||||
use servo_msg::constellation_msg::{LoadData, NavigationType, PipelineId, RendererReadyMsg, ResizedWindowMsg};
|
||||
use servo_msg::constellation_msg::{SubpageId, WindowSizeData};
|
||||
use servo_msg::constellation_msg::{LoadCompleteMsg, LoadIframeUrlMsg, LoadUrlMsg, Msg};
|
||||
use servo_msg::constellation_msg::{LoadData, NavigateMsg, NavigationType, PipelineId};
|
||||
use servo_msg::constellation_msg::{RendererReadyMsg, ResizedWindowMsg, SubpageId, WindowSizeData};
|
||||
use servo_msg::constellation_msg;
|
||||
use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient};
|
||||
use gfx::font_cache_task::FontCacheTask;
|
||||
use servo_net::resource_task::ResourceTask;
|
||||
use servo_net::resource_task;
|
||||
use servo_util::geometry::PagePx;
|
||||
use servo_util::geometry::{PagePx, ViewportPx};
|
||||
use servo_util::opts;
|
||||
use servo_util::time::TimeProfilerChan;
|
||||
use servo_util::task::spawn_named;
|
||||
use servo_util::time::TimeProfilerChan;
|
||||
use std::cell::RefCell;
|
||||
use std::mem::replace;
|
||||
use std::collections::hashmap::{HashMap, HashSet};
|
||||
use std::io;
|
||||
use std::mem::replace;
|
||||
use std::rc::Rc;
|
||||
use url::Url;
|
||||
|
||||
/// Maintains the pipelines and navigation context and grants permission to composite
|
||||
/// Maintains the pipelines and navigation context and grants permission to composite.
|
||||
pub struct Constellation<LTF, STF> {
|
||||
/// A channel through which messages can be sent to this object.
|
||||
pub chan: ConstellationChan,
|
||||
|
||||
/// Receives messages.
|
||||
pub request_port: Receiver<Msg>,
|
||||
pub compositor_chan: CompositorChan,
|
||||
|
||||
/// A channel (the implementation of which is port-specific) through which messages can be sent
|
||||
/// to the compositor.
|
||||
pub compositor_proxy: Box<CompositorProxy>,
|
||||
|
||||
/// A channel through which messages can be sent to the resource task.
|
||||
pub resource_task: ResourceTask,
|
||||
|
||||
/// A channel through which messages can be sent to the image cache task.
|
||||
pub image_cache_task: ImageCacheTask,
|
||||
|
||||
/// A channel through which messages can be sent to the developer tools.
|
||||
devtools_chan: Option<DevtoolsControlChan>,
|
||||
|
||||
/// A list of all the pipelines. (See the `pipeline` module for more details.)
|
||||
pipelines: HashMap<PipelineId, Rc<Pipeline>>,
|
||||
|
||||
/// A channel through which messages can be sent to the font cache.
|
||||
font_cache_task: FontCacheTask,
|
||||
|
||||
navigation_context: NavigationContext,
|
||||
|
||||
/// The next free ID to assign to a pipeline.
|
||||
next_pipeline_id: PipelineId,
|
||||
|
||||
pending_frames: Vec<FrameChange>,
|
||||
|
||||
pending_sizes: HashMap<(PipelineId, SubpageId), TypedRect<PagePx, f32>>,
|
||||
|
||||
/// A channel through which messages can be sent to the time profiler.
|
||||
pub time_profiler_chan: TimeProfilerChan,
|
||||
|
||||
pub window_size: WindowSizeData,
|
||||
}
|
||||
|
||||
|
@ -86,7 +112,11 @@ impl FrameTree {
|
|||
fn to_sendable(&self) -> SendableFrameTree {
|
||||
let sendable_frame_tree = SendableFrameTree {
|
||||
pipeline: self.pipeline.to_sendable(),
|
||||
children: self.children.borrow().iter().map(|frame_tree| frame_tree.to_sendable()).collect(),
|
||||
children: self.children
|
||||
.borrow()
|
||||
.iter()
|
||||
.map(|frame_tree| frame_tree.to_sendable())
|
||||
.collect(),
|
||||
};
|
||||
sendable_frame_tree
|
||||
}
|
||||
|
@ -109,8 +139,8 @@ impl FrameTreeTraversal for Rc<FrameTree> {
|
|||
self.iter().find(|frame_tree| id == frame_tree.pipeline.id)
|
||||
}
|
||||
|
||||
/// Replaces a node of the frame tree in place. Returns the node that was removed or the original node
|
||||
/// if the node to replace could not be found.
|
||||
/// Replaces a node of the frame tree in place. Returns the node that was removed or the
|
||||
/// original node if the node to replace could not be found.
|
||||
fn replace_child(&self, id: PipelineId, new_child: Rc<FrameTree>) -> ReplaceResult {
|
||||
for frame_tree in self.iter() {
|
||||
let mut children = frame_tree.children.borrow_mut();
|
||||
|
@ -239,7 +269,7 @@ impl NavigationContext {
|
|||
}
|
||||
|
||||
impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
|
||||
pub fn start(compositor_chan: CompositorChan,
|
||||
pub fn start(compositor_proxy: Box<CompositorProxy+Send>,
|
||||
resource_task: ResourceTask,
|
||||
image_cache_task: ImageCacheTask,
|
||||
font_cache_task: FontCacheTask,
|
||||
|
@ -249,10 +279,10 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
|
|||
let (constellation_port, constellation_chan) = ConstellationChan::new();
|
||||
let constellation_chan_clone = constellation_chan.clone();
|
||||
spawn_named("Constellation", proc() {
|
||||
let mut constellation : Constellation<LTF, STF> = Constellation {
|
||||
let mut constellation: Constellation<LTF, STF> = Constellation {
|
||||
chan: constellation_chan_clone,
|
||||
request_port: constellation_port,
|
||||
compositor_chan: compositor_chan,
|
||||
compositor_proxy: compositor_proxy,
|
||||
devtools_chan: devtools_chan,
|
||||
resource_task: resource_task,
|
||||
image_cache_task: image_cache_task,
|
||||
|
@ -293,7 +323,7 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
|
|||
let pipe = Pipeline::create::<LTF, STF>(id,
|
||||
subpage_id,
|
||||
self.chan.clone(),
|
||||
self.compositor_chan.clone(),
|
||||
self.compositor_proxy.clone_compositor_proxy(),
|
||||
self.devtools_chan.clone(),
|
||||
self.image_cache_task.clone(),
|
||||
self.font_cache_task.clone(),
|
||||
|
@ -367,7 +397,7 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
|
|||
// script, and reflow messages have been sent.
|
||||
LoadCompleteMsg(pipeline_id, url) => {
|
||||
debug!("constellation got load complete message");
|
||||
self.compositor_chan.send(LoadComplete(pipeline_id, url));
|
||||
self.compositor_proxy.send(LoadComplete(pipeline_id, url));
|
||||
}
|
||||
// Handle a forward or back request
|
||||
NavigateMsg(direction) => {
|
||||
|
@ -387,14 +417,14 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
|
|||
true
|
||||
}
|
||||
|
||||
fn handle_exit(&self) {
|
||||
fn handle_exit(&mut self) {
|
||||
for (_id, ref pipeline) in self.pipelines.iter() {
|
||||
pipeline.exit();
|
||||
}
|
||||
self.image_cache_task.exit();
|
||||
self.resource_task.send(resource_task::Exit);
|
||||
self.font_cache_task.exit();
|
||||
self.compositor_chan.send(ShutdownComplete);
|
||||
self.compositor_proxy.send(ShutdownComplete);
|
||||
}
|
||||
|
||||
fn handle_failure_msg(&mut self, pipeline_id: PipelineId, subpage_id: Option<SubpageId>) {
|
||||
|
@ -491,38 +521,23 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
|
|||
let frames = self.find_all(pipeline_id);
|
||||
|
||||
{
|
||||
// Update a child's frame rect and inform its script task of the change,
|
||||
// if it hasn't been already. Optionally inform the compositor if
|
||||
// resize happens immediately.
|
||||
let update_child_rect = |child_frame_tree: &mut ChildFrameTree, is_active: bool| {
|
||||
child_frame_tree.rect = Some(rect);
|
||||
// NOTE: work around borrowchk issues
|
||||
let pipeline = &child_frame_tree.frame_tree.pipeline;
|
||||
if !already_sent.contains(&pipeline.id) {
|
||||
if is_active {
|
||||
let ScriptControlChan(ref script_chan) = pipeline.script_chan;
|
||||
script_chan.send(ResizeMsg(pipeline.id, WindowSizeData {
|
||||
visible_viewport: rect.size,
|
||||
initial_viewport: rect.size * ScaleFactor(1.0),
|
||||
device_pixel_ratio: self.window_size.device_pixel_ratio,
|
||||
}));
|
||||
self.compositor_chan.send(SetLayerOrigin(pipeline.id,
|
||||
LayerId::null(),
|
||||
rect.to_untyped().origin));
|
||||
} else {
|
||||
already_sent.insert(pipeline.id);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// If the subframe is in the current frame tree, the compositor needs the new size
|
||||
for current_frame in self.current_frame().iter() {
|
||||
for current_frame in self.navigation_context.current.iter() {
|
||||
debug!("Constellation: Sending size for frame in current frame tree.");
|
||||
let source_frame = current_frame.find(pipeline_id);
|
||||
for source_frame in source_frame.iter() {
|
||||
let mut children = source_frame.children.borrow_mut();
|
||||
let found_child = children.iter_mut().find(|child| subpage_eq(child));
|
||||
found_child.map(|child| update_child_rect(child, true));
|
||||
match children.iter_mut().find(|child| subpage_eq(child)) {
|
||||
None => {}
|
||||
Some(child) => {
|
||||
update_child_rect(child,
|
||||
rect,
|
||||
true,
|
||||
&mut already_sent,
|
||||
&mut self.compositor_proxy,
|
||||
self.window_size.device_pixel_ratio)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -530,7 +545,14 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
|
|||
for frame_tree in frames.iter() {
|
||||
let mut children = frame_tree.children.borrow_mut();
|
||||
let found_child = children.iter_mut().find(|child| subpage_eq(child));
|
||||
found_child.map(|child| update_child_rect(child, false));
|
||||
found_child.map(|child| {
|
||||
update_child_rect(child,
|
||||
rect,
|
||||
false,
|
||||
&mut already_sent,
|
||||
&mut self.compositor_proxy,
|
||||
self.window_size.device_pixel_ratio)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -539,8 +561,38 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
|
|||
if already_sent.len() == 0 {
|
||||
self.pending_sizes.insert((pipeline_id, subpage_id), rect);
|
||||
}
|
||||
|
||||
// Update a child's frame rect and inform its script task of the change,
|
||||
// if it hasn't been already. Optionally inform the compositor if
|
||||
// resize happens immediately.
|
||||
fn update_child_rect(child_frame_tree: &mut ChildFrameTree,
|
||||
rect: TypedRect<PagePx,f32>,
|
||||
is_active: bool,
|
||||
already_sent: &mut HashSet<PipelineId>,
|
||||
compositor_proxy: &mut Box<CompositorProxy>,
|
||||
device_pixel_ratio: ScaleFactor<ViewportPx,DevicePixel,f32>) {
|
||||
child_frame_tree.rect = Some(rect);
|
||||
// NOTE: work around borrowchk issues
|
||||
let pipeline = &child_frame_tree.frame_tree.pipeline;
|
||||
if !already_sent.contains(&pipeline.id) {
|
||||
if is_active {
|
||||
let ScriptControlChan(ref script_chan) = pipeline.script_chan;
|
||||
script_chan.send(ResizeMsg(pipeline.id, WindowSizeData {
|
||||
visible_viewport: rect.size,
|
||||
initial_viewport: rect.size * ScaleFactor(1.0),
|
||||
device_pixel_ratio: device_pixel_ratio,
|
||||
}));
|
||||
compositor_proxy.send(SetLayerOrigin(pipeline.id,
|
||||
LayerId::null(),
|
||||
rect.to_untyped().origin));
|
||||
} else {
|
||||
already_sent.insert(pipeline.id);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn handle_load_iframe_url_msg(&mut self,
|
||||
url: Url,
|
||||
source_pipeline_id: PipelineId,
|
||||
|
@ -851,10 +903,10 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
|
|||
}
|
||||
}
|
||||
|
||||
fn set_ids(&self, frame_tree: &Rc<FrameTree>) {
|
||||
fn set_ids(&mut self, frame_tree: &Rc<FrameTree>) {
|
||||
let (chan, port) = channel();
|
||||
debug!("Constellation sending SetIds");
|
||||
self.compositor_chan.send(SetIds(frame_tree.to_sendable(), chan, self.chan.clone()));
|
||||
self.compositor_proxy.send(SetIds(frame_tree.to_sendable(), chan, self.chan.clone()));
|
||||
match port.recv_opt() {
|
||||
Ok(()) => {
|
||||
let mut iter = frame_tree.iter();
|
||||
|
|
|
@ -2,10 +2,11 @@
|
|||
* 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 compositor_task::{Msg, Exit, ChangeReadyState, SetIds};
|
||||
use compositor_task::{GetGraphicsMetadata, CreateOrUpdateRootLayer, CreateOrUpdateDescendantLayer};
|
||||
use compositor_task::{SetLayerOrigin, Paint, ScrollFragmentPoint, LoadComplete};
|
||||
use compositor_task::{ShutdownComplete, ChangeRenderState, RenderMsgDiscarded};
|
||||
use compositor_task::{Exit, ChangeReadyState, LoadComplete, Paint, ScrollFragmentPoint, SetIds};
|
||||
use compositor_task::{SetLayerOrigin, ShutdownComplete, ChangeRenderState, RenderMsgDiscarded};
|
||||
use compositor_task::{CompositorEventListener, CompositorReceiver, ScrollTimeout};
|
||||
use windowing::WindowEvent;
|
||||
|
||||
use geom::scale_factor::ScaleFactor;
|
||||
use geom::size::TypedSize2D;
|
||||
|
@ -21,79 +22,97 @@ use servo_util::time;
|
|||
/// It's intended for headless testing.
|
||||
pub struct NullCompositor {
|
||||
/// The port on which we receive messages.
|
||||
pub port: Receiver<Msg>,
|
||||
pub port: Box<CompositorReceiver>,
|
||||
/// A channel to the constellation.
|
||||
constellation_chan: ConstellationChan,
|
||||
/// A channel to the time profiler.
|
||||
time_profiler_chan: TimeProfilerChan,
|
||||
/// A channel to the memory profiler.
|
||||
memory_profiler_chan: MemoryProfilerChan,
|
||||
}
|
||||
|
||||
impl NullCompositor {
|
||||
fn new(port: Receiver<Msg>) -> NullCompositor {
|
||||
fn new(port: Box<CompositorReceiver>,
|
||||
constellation_chan: ConstellationChan,
|
||||
time_profiler_chan: TimeProfilerChan,
|
||||
memory_profiler_chan: MemoryProfilerChan)
|
||||
-> NullCompositor {
|
||||
NullCompositor {
|
||||
port: port,
|
||||
constellation_chan: constellation_chan,
|
||||
time_profiler_chan: time_profiler_chan,
|
||||
memory_profiler_chan: memory_profiler_chan,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create(port: Receiver<Msg>,
|
||||
pub fn create(port: Box<CompositorReceiver>,
|
||||
constellation_chan: ConstellationChan,
|
||||
time_profiler_chan: TimeProfilerChan,
|
||||
memory_profiler_chan: MemoryProfilerChan) {
|
||||
let compositor = NullCompositor::new(port);
|
||||
memory_profiler_chan: MemoryProfilerChan)
|
||||
-> NullCompositor {
|
||||
let compositor = NullCompositor::new(port,
|
||||
constellation_chan,
|
||||
time_profiler_chan,
|
||||
memory_profiler_chan);
|
||||
|
||||
// Tell the constellation about the initial fake size.
|
||||
{
|
||||
let ConstellationChan(ref chan) = constellation_chan;
|
||||
let ConstellationChan(ref chan) = compositor.constellation_chan;
|
||||
chan.send(ResizedWindowMsg(WindowSizeData {
|
||||
initial_viewport: TypedSize2D(640_f32, 480_f32),
|
||||
visible_viewport: TypedSize2D(640_f32, 480_f32),
|
||||
device_pixel_ratio: ScaleFactor(1.0),
|
||||
}));
|
||||
}
|
||||
compositor.handle_message(constellation_chan);
|
||||
|
||||
// Drain compositor port, sometimes messages contain channels that are blocking
|
||||
// another task from finishing (i.e. SetIds)
|
||||
loop {
|
||||
match compositor.port.try_recv() {
|
||||
Err(_) => break,
|
||||
Ok(_) => {},
|
||||
}
|
||||
}
|
||||
|
||||
time_profiler_chan.send(time::ExitMsg);
|
||||
memory_profiler_chan.send(memory::ExitMsg);
|
||||
}
|
||||
|
||||
fn handle_message(&self, constellation_chan: ConstellationChan) {
|
||||
loop {
|
||||
match self.port.recv() {
|
||||
Exit(chan) => {
|
||||
debug!("shutting down the constellation");
|
||||
let ConstellationChan(ref con_chan) = constellation_chan;
|
||||
con_chan.send(ExitMsg);
|
||||
chan.send(());
|
||||
}
|
||||
|
||||
ShutdownComplete => {
|
||||
debug!("constellation completed shutdown");
|
||||
break
|
||||
}
|
||||
|
||||
GetGraphicsMetadata(chan) => {
|
||||
chan.send(None);
|
||||
}
|
||||
|
||||
SetIds(_, response_chan, _) => {
|
||||
response_chan.send(());
|
||||
}
|
||||
|
||||
// Explicitly list ignored messages so that when we add a new one,
|
||||
// we'll notice and think about whether it needs a response, like
|
||||
// SetIds.
|
||||
|
||||
CreateOrUpdateRootLayer(..) |
|
||||
CreateOrUpdateDescendantLayer(..) |
|
||||
SetLayerOrigin(..) | Paint(..) |
|
||||
ChangeReadyState(..) | ChangeRenderState(..) | ScrollFragmentPoint(..) |
|
||||
LoadComplete(..) | RenderMsgDiscarded(..) => ()
|
||||
}
|
||||
}
|
||||
compositor
|
||||
}
|
||||
}
|
||||
|
||||
impl CompositorEventListener for NullCompositor {
|
||||
fn handle_event(&mut self, _: WindowEvent) -> bool {
|
||||
match self.port.recv_compositor_msg() {
|
||||
Exit(chan) => {
|
||||
debug!("shutting down the constellation");
|
||||
let ConstellationChan(ref con_chan) = self.constellation_chan;
|
||||
con_chan.send(ExitMsg);
|
||||
chan.send(());
|
||||
}
|
||||
|
||||
ShutdownComplete => {
|
||||
debug!("constellation completed shutdown");
|
||||
return false
|
||||
}
|
||||
|
||||
GetGraphicsMetadata(chan) => {
|
||||
chan.send(None);
|
||||
}
|
||||
|
||||
SetIds(_, response_chan, _) => {
|
||||
response_chan.send(());
|
||||
}
|
||||
|
||||
// Explicitly list ignored messages so that when we add a new one,
|
||||
// we'll notice and think about whether it needs a response, like
|
||||
// SetIds.
|
||||
|
||||
CreateOrUpdateRootLayer(..) |
|
||||
CreateOrUpdateDescendantLayer(..) |
|
||||
SetLayerOrigin(..) | Paint(..) |
|
||||
ChangeReadyState(..) | ChangeRenderState(..) | ScrollFragmentPoint(..) |
|
||||
LoadComplete(..) | RenderMsgDiscarded(..) | ScrollTimeout(..) => ()
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn repaint_synchronously(&mut self) {}
|
||||
|
||||
fn shutdown(&mut self) {
|
||||
// Drain compositor port, sometimes messages contain channels that are blocking
|
||||
// another task from finishing (i.e. SetIds)
|
||||
while self.port.try_recv_compositor_msg().is_some() {}
|
||||
|
||||
self.time_profiler_chan.send(time::ExitMsg);
|
||||
self.memory_profiler_chan.send(memory::ExitMsg);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ extern crate "util" as servo_util;
|
|||
extern crate gleam;
|
||||
|
||||
extern crate libc;
|
||||
extern crate native;
|
||||
extern crate time;
|
||||
extern crate url;
|
||||
|
||||
|
@ -38,12 +39,14 @@ extern crate core_graphics;
|
|||
#[cfg(target_os="macos")]
|
||||
extern crate core_text;
|
||||
|
||||
pub use compositor_task::{CompositorChan, CompositorTask};
|
||||
pub use compositor_task::{CompositorEventListener, CompositorProxy, CompositorTask};
|
||||
pub use constellation::Constellation;
|
||||
|
||||
pub mod compositor_task;
|
||||
|
||||
mod compositor_layer;
|
||||
mod scrolling;
|
||||
|
||||
mod compositor;
|
||||
mod headless;
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* 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 CompositorChan;
|
||||
use CompositorProxy;
|
||||
use layout_traits::{LayoutTaskFactory, LayoutControlChan};
|
||||
use script_traits::{ScriptControlChan, ScriptTaskFactory};
|
||||
use script_traits::{AttachLayoutMsg, LoadMsg, NewLayoutInfo, ExitPipelineMsg};
|
||||
|
@ -47,7 +47,7 @@ impl Pipeline {
|
|||
id: PipelineId,
|
||||
subpage_id: Option<SubpageId>,
|
||||
constellation_chan: ConstellationChan,
|
||||
compositor_chan: CompositorChan,
|
||||
compositor_proxy: Box<CompositorProxy+'static+Send>,
|
||||
devtools_chan: Option<DevtoolsControlChan>,
|
||||
image_cache_task: ImageCacheTask,
|
||||
font_cache_task: FontCacheTask,
|
||||
|
@ -73,7 +73,7 @@ impl Pipeline {
|
|||
let (script_chan, script_port) = channel();
|
||||
ScriptTaskFactory::create(None::<&mut STF>,
|
||||
id,
|
||||
box compositor_chan.clone(),
|
||||
compositor_proxy.clone_compositor_proxy(),
|
||||
&layout_pair,
|
||||
ScriptControlChan(script_chan.clone()),
|
||||
script_port,
|
||||
|
@ -101,7 +101,7 @@ impl Pipeline {
|
|||
|
||||
RenderTask::create(id,
|
||||
render_port,
|
||||
compositor_chan.clone(),
|
||||
compositor_proxy,
|
||||
constellation_chan.clone(),
|
||||
font_cache_task.clone(),
|
||||
failure.clone(),
|
||||
|
|
73
components/compositing/scrolling.rs
Normal file
73
components/compositing/scrolling.rs
Normal file
|
@ -0,0 +1,73 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* 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/. */
|
||||
|
||||
//! A timer thread that gives the painting task a little time to catch up when the user scrolls.
|
||||
|
||||
use compositor_task::{CompositorProxy, ScrollTimeout};
|
||||
|
||||
use native::task::NativeTaskBuilder;
|
||||
use std::io::timer;
|
||||
use std::task::TaskBuilder;
|
||||
use std::time::duration::Duration;
|
||||
use time;
|
||||
|
||||
/// The amount of time in nanoseconds that we give to the painting thread to paint new tiles upon
|
||||
/// processing a scroll event that caused new tiles to be revealed. When this expires, we give up
|
||||
/// and composite anyway (showing a "checkerboard") to avoid dropping the frame.
|
||||
static TIMEOUT: i64 = 12_000_000;
|
||||
|
||||
pub struct ScrollingTimerProxy {
|
||||
sender: Sender<ToScrollingTimerMsg>,
|
||||
}
|
||||
|
||||
pub struct ScrollingTimer {
|
||||
compositor_proxy: Box<CompositorProxy>,
|
||||
receiver: Receiver<ToScrollingTimerMsg>,
|
||||
}
|
||||
|
||||
enum ToScrollingTimerMsg {
|
||||
ExitMsg,
|
||||
ScrollEventProcessedMsg(u64),
|
||||
}
|
||||
|
||||
impl ScrollingTimerProxy {
|
||||
pub fn new(compositor_proxy: Box<CompositorProxy+Send>) -> ScrollingTimerProxy {
|
||||
let (to_scrolling_timer_sender, to_scrolling_timer_receiver) = channel();
|
||||
TaskBuilder::new().native().spawn(proc() {
|
||||
let mut scrolling_timer = ScrollingTimer {
|
||||
compositor_proxy: compositor_proxy,
|
||||
receiver: to_scrolling_timer_receiver,
|
||||
};
|
||||
scrolling_timer.run();
|
||||
});
|
||||
ScrollingTimerProxy {
|
||||
sender: to_scrolling_timer_sender,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scroll_event_processed(&mut self, timestamp: u64) {
|
||||
self.sender.send(ScrollEventProcessedMsg(timestamp))
|
||||
}
|
||||
|
||||
pub fn shutdown(&mut self) {
|
||||
self.sender.send(ExitMsg);
|
||||
}
|
||||
}
|
||||
|
||||
impl ScrollingTimer {
|
||||
pub fn run(&mut self) {
|
||||
loop {
|
||||
match self.receiver.recv_opt() {
|
||||
Ok(ScrollEventProcessedMsg(timestamp)) => {
|
||||
let target = timestamp as i64 + TIMEOUT;
|
||||
let delta = target - (time::precise_time_ns() as i64);
|
||||
timer::sleep(Duration::nanoseconds(delta));
|
||||
self.compositor_proxy.send(ScrollTimeout(timestamp));
|
||||
}
|
||||
Ok(ExitMsg) | Err(_) => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
//! Abstract windowing methods. The concrete implementations of these can be found in `platform/`.
|
||||
|
||||
use compositor_task::{CompositorProxy, CompositorReceiver};
|
||||
|
||||
use geom::point::TypedPoint2D;
|
||||
use geom::scale_factor::ScaleFactor;
|
||||
use geom::size::TypedSize2D;
|
||||
|
@ -11,6 +13,8 @@ use layers::geometry::DevicePixel;
|
|||
use layers::platform::surface::NativeGraphicsMetadata;
|
||||
use servo_msg::compositor_msg::{ReadyState, RenderState};
|
||||
use servo_util::geometry::ScreenPx;
|
||||
use std::fmt::{FormatError, Formatter, Show};
|
||||
use std::rc::Rc;
|
||||
|
||||
pub enum MouseWindowEvent {
|
||||
MouseWindowClickEvent(uint, TypedPoint2D<DevicePixel, f32>),
|
||||
|
@ -25,10 +29,12 @@ pub enum WindowNavigateMsg {
|
|||
|
||||
/// Events that the windowing system sends to Servo.
|
||||
pub enum WindowEvent {
|
||||
/// Sent when no message has arrived.
|
||||
/// Sent when no message has arrived, but the event loop was kicked for some reason (perhaps
|
||||
/// by another Servo subsystem).
|
||||
///
|
||||
/// FIXME: This is a bogus event and is only used because we don't have the new
|
||||
/// scheduler integrated with the platform event loop.
|
||||
/// FIXME(pcwalton): This is kind of ugly and may not work well with multiprocess Servo.
|
||||
/// It's possible that this should be something like
|
||||
/// `CompositorMessageWindowEvent(compositor_task::Msg)` instead.
|
||||
IdleWindowEvent,
|
||||
/// Sent when part of the window is marked dirty and needs to be redrawn.
|
||||
RefreshWindowEvent,
|
||||
|
@ -54,6 +60,25 @@ pub enum WindowEvent {
|
|||
QuitWindowEvent,
|
||||
}
|
||||
|
||||
impl Show for WindowEvent {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result<(),FormatError> {
|
||||
match *self {
|
||||
IdleWindowEvent => write!(f, "Idle"),
|
||||
RefreshWindowEvent => write!(f, "Refresh"),
|
||||
ResizeWindowEvent(..) => write!(f, "Resize"),
|
||||
LoadUrlWindowEvent(..) => write!(f, "LoadUrl"),
|
||||
MouseWindowEventClass(..) => write!(f, "Mouse"),
|
||||
MouseWindowMoveEventClass(..) => write!(f, "MouseMove"),
|
||||
ScrollWindowEvent(..) => write!(f, "Scroll"),
|
||||
ZoomWindowEvent(..) => write!(f, "Zoom"),
|
||||
PinchZoomWindowEvent(..) => write!(f, "PinchZoom"),
|
||||
NavigationWindowEvent(..) => write!(f, "Navigation"),
|
||||
FinishedWindowEvent => write!(f, "Finished"),
|
||||
QuitWindowEvent => write!(f, "Quit"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait WindowMethods {
|
||||
/// Returns the size of the window in hardware pixels.
|
||||
fn framebuffer_size(&self) -> TypedSize2D<DevicePixel, uint>;
|
||||
|
@ -62,9 +87,6 @@ pub trait WindowMethods {
|
|||
/// Presents the window to the screen (perhaps by page flipping).
|
||||
fn present(&self);
|
||||
|
||||
/// Spins the event loop and returns the next event.
|
||||
fn recv(&self) -> WindowEvent;
|
||||
|
||||
/// Sets the ready state of the current page.
|
||||
fn set_ready_state(&self, ready_state: ReadyState);
|
||||
/// Sets the render state of the current page.
|
||||
|
@ -75,5 +97,13 @@ pub trait WindowMethods {
|
|||
|
||||
/// Gets the OS native graphics information for this window.
|
||||
fn native_metadata(&self) -> NativeGraphicsMetadata;
|
||||
|
||||
/// Creates a channel to the compositor. The dummy parameter is needed because we don't have
|
||||
/// UFCS in Rust yet.
|
||||
///
|
||||
/// This is part of the windowing system because its implementation often involves OS-specific
|
||||
/// magic to wake the up window's event loop.
|
||||
fn create_compositor_channel(_: &Option<Rc<Self>>)
|
||||
-> (Box<CompositorProxy+Send>, Box<CompositorReceiver>);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue