mirror of
https://github.com/servo/servo.git
synced 2025-08-05 21:50:18 +01:00
Introduce 'IOCompositor' to clean up compositor code.
This commit is contained in:
parent
8487c02a39
commit
cba47ba740
2 changed files with 512 additions and 304 deletions
|
@ -31,7 +31,6 @@ mod compositor_layer;
|
||||||
mod run;
|
mod run;
|
||||||
mod run_headless;
|
mod run_headless;
|
||||||
|
|
||||||
|
|
||||||
/// The implementation of the layers-based compositor.
|
/// The implementation of the layers-based compositor.
|
||||||
#[deriving(Clone)]
|
#[deriving(Clone)]
|
||||||
pub struct CompositorChan {
|
pub struct CompositorChan {
|
||||||
|
@ -191,10 +190,10 @@ impl CompositorTask {
|
||||||
|
|
||||||
match compositor.mode {
|
match compositor.mode {
|
||||||
Windowed(ref app) => {
|
Windowed(ref app) => {
|
||||||
run::run_compositor(app,
|
run::IOCompositor::create(app,
|
||||||
opts,
|
opts,
|
||||||
port,
|
port,
|
||||||
&constellation_chan,
|
constellation_chan.clone(),
|
||||||
profiler_chan);
|
profiler_chan);
|
||||||
}
|
}
|
||||||
Headless => {
|
Headless => {
|
||||||
|
|
|
@ -2,18 +2,21 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use constellation::SendableFrameTree;
|
||||||
use compositing::compositor_layer::CompositorLayer;
|
use compositing::compositor_layer::CompositorLayer;
|
||||||
use compositing::*;
|
use compositing::*;
|
||||||
|
|
||||||
use platform::{Application, Window};
|
use platform::{Application, Window};
|
||||||
|
|
||||||
use windowing::{WindowEvent, WindowMethods};
|
use windowing::{WindowEvent, WindowMethods,
|
||||||
use windowing::{IdleWindowEvent, ResizeWindowEvent, LoadUrlWindowEvent, MouseWindowEventClass};
|
WindowNavigateMsg,
|
||||||
use windowing::{ScrollWindowEvent, ZoomWindowEvent, NavigationWindowEvent, FinishedWindowEvent};
|
IdleWindowEvent, RefreshWindowEvent, ResizeWindowEvent, LoadUrlWindowEvent,
|
||||||
use windowing::{QuitWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent, MouseWindowMouseUpEvent};
|
MouseWindowEventClass,ScrollWindowEvent, ZoomWindowEvent, NavigationWindowEvent,
|
||||||
use windowing::RefreshWindowEvent;
|
FinishedWindowEvent, QuitWindowEvent,
|
||||||
|
MouseWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent, MouseWindowMouseUpEvent};
|
||||||
|
|
||||||
use azure::azure_hl::SourceSurfaceMethods;
|
|
||||||
|
use azure::azure_hl::{SourceSurfaceMethods, Color};
|
||||||
use azure::azure_hl;
|
use azure::azure_hl;
|
||||||
use extra::time::precise_time_s;
|
use extra::time::precise_time_s;
|
||||||
use geom::matrix::identity;
|
use geom::matrix::identity;
|
||||||
|
@ -22,12 +25,14 @@ use geom::rect::Rect;
|
||||||
use geom::size::Size2D;
|
use geom::size::Size2D;
|
||||||
use gfx::opts::Opts;
|
use gfx::opts::Opts;
|
||||||
use layers::layers::{ContainerLayer, ContainerLayerKind};
|
use layers::layers::{ContainerLayer, ContainerLayerKind};
|
||||||
|
use layers::platform::surface::NativeCompositingGraphicsContext;
|
||||||
use layers::rendergl;
|
use layers::rendergl;
|
||||||
|
use layers::rendergl::RenderContext;
|
||||||
use layers::scene::Scene;
|
use layers::scene::Scene;
|
||||||
use opengles::gl2;
|
use opengles::gl2;
|
||||||
use png;
|
use png;
|
||||||
use servo_msg::compositor_msg::IdleRenderState;
|
use servo_msg::compositor_msg::{Epoch, IdleRenderState, LayerBufferSet, RenderState};
|
||||||
use servo_msg::constellation_msg::{ConstellationChan, NavigateMsg, ResizedWindowMsg, LoadUrlMsg};
|
use servo_msg::constellation_msg::{ConstellationChan, NavigateMsg, ResizedWindowMsg, LoadUrlMsg, PipelineId};
|
||||||
use servo_msg::constellation_msg;
|
use servo_msg::constellation_msg;
|
||||||
use servo_util::time::{profile, ProfilerChan};
|
use servo_util::time::{profile, ProfilerChan};
|
||||||
use servo_util::{time, url};
|
use servo_util::{time, url};
|
||||||
|
@ -37,68 +42,221 @@ use std::path::Path;
|
||||||
use std::rt::io::timer::Timer;
|
use std::rt::io::timer::Timer;
|
||||||
use std::vec;
|
use std::vec;
|
||||||
|
|
||||||
/// Starts the compositor, which listens for messages on the specified port.
|
|
||||||
pub fn run_compositor(app: &Application,
|
pub struct IOCompositor {
|
||||||
|
/// The application window.
|
||||||
|
window: @mut Window,
|
||||||
|
|
||||||
|
/// The port on which we receive messages.
|
||||||
|
port: Port<Msg>,
|
||||||
|
|
||||||
|
/// The render context.
|
||||||
|
context: RenderContext,
|
||||||
|
|
||||||
|
/// The root ContainerLayer.
|
||||||
|
root_layer: @mut ContainerLayer,
|
||||||
|
|
||||||
|
/// The canvas to paint a page.
|
||||||
|
scene: Scene,
|
||||||
|
|
||||||
|
/// The application window size.
|
||||||
|
window_size: Size2D<uint>,
|
||||||
|
|
||||||
|
/// The platform-specific graphics context.
|
||||||
|
graphics_context: NativeCompositingGraphicsContext,
|
||||||
|
|
||||||
|
/// Tracks whether the renderer has finished its first rendering
|
||||||
|
composite_ready: bool,
|
||||||
|
|
||||||
|
/// Tracks whether we should close compositor.
|
||||||
|
done: bool,
|
||||||
|
|
||||||
|
/// Tracks whether we need to re-composite a page.
|
||||||
|
recomposite: bool,
|
||||||
|
|
||||||
|
/// Keeps track of the current zoom factor.
|
||||||
|
world_zoom: f32,
|
||||||
|
|
||||||
|
/// Tracks whether the zoom action has happend recently.
|
||||||
|
zoom_action: bool,
|
||||||
|
|
||||||
|
/// The time of the last zoom action has started.
|
||||||
|
zoom_time: f64,
|
||||||
|
|
||||||
|
/// The command line option flags.
|
||||||
|
opts: Opts,
|
||||||
|
|
||||||
|
/// The root CompositorLayer
|
||||||
|
compositor_layer: Option<CompositorLayer>,
|
||||||
|
|
||||||
|
/// The channel on which messages can be sent to the constellation.
|
||||||
|
constellation_chan: ConstellationChan,
|
||||||
|
|
||||||
|
/// The channel on which messages can be sent to the profiler.
|
||||||
|
profiler_chan: ProfilerChan
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IOCompositor {
|
||||||
|
|
||||||
|
pub fn new(app: &Application,
|
||||||
opts: Opts,
|
opts: Opts,
|
||||||
port: Port<Msg>,
|
port: Port<Msg>,
|
||||||
constellation_chan: &ConstellationChan,
|
constellation_chan: ConstellationChan,
|
||||||
profiler_chan: ProfilerChan) {
|
profiler_chan: ProfilerChan) -> IOCompositor {
|
||||||
let window: @mut Window = WindowMethods::new(app);
|
let window: @mut Window = WindowMethods::new(app);
|
||||||
|
|
||||||
// Create an initial layer tree.
|
// Create an initial layer tree.
|
||||||
//
|
//
|
||||||
// TODO: There should be no initial layer tree until the renderer creates one from the display
|
// TODO: There should be no initial layer tree until the renderer creates one from the display
|
||||||
// list. This is only here because we don't have that logic in the renderer yet.
|
// list. This is only here because we don't have that logic in the renderer yet.
|
||||||
let context = rendergl::init_render_context();
|
|
||||||
let root_layer = @mut ContainerLayer();
|
let root_layer = @mut ContainerLayer();
|
||||||
let window_size = window.size();
|
let window_size = window.size();
|
||||||
let mut scene = Scene(ContainerLayerKind(root_layer), window_size, identity());
|
|
||||||
let mut window_size = Size2D(window_size.width as uint, window_size.height as uint);
|
|
||||||
let mut done = false;
|
|
||||||
let mut recomposite = false;
|
|
||||||
let graphics_context = CompositorTask::create_graphics_context();
|
|
||||||
|
|
||||||
// Tracks whether the renderer has finished its first rendering
|
IOCompositor {
|
||||||
let mut composite_ready = false;
|
window: window,
|
||||||
|
port: port,
|
||||||
// Keeps track of the current zoom factor
|
opts: opts,
|
||||||
let mut world_zoom = 1f32;
|
context: rendergl::init_render_context(),
|
||||||
let mut zoom_action = false;
|
root_layer: root_layer,
|
||||||
let mut zoom_time = 0f64;
|
scene: Scene(ContainerLayerKind(root_layer), window_size, identity()),
|
||||||
|
window_size: Size2D(window_size.width as uint, window_size.height as uint),
|
||||||
// The root CompositorLayer
|
graphics_context: CompositorTask::create_graphics_context(),
|
||||||
let mut compositor_layer: Option<CompositorLayer> = None;
|
composite_ready: false,
|
||||||
let mut constellation_chan: ConstellationChan = constellation_chan.clone();
|
done: false,
|
||||||
|
recomposite: false,
|
||||||
// Get BufferRequests from each layer.
|
world_zoom: 1f32,
|
||||||
let ask_for_tiles = || {
|
zoom_action: false,
|
||||||
let window_size_page = Size2D(window_size.width as f32 / world_zoom,
|
zoom_time: 0f64,
|
||||||
window_size.height as f32 / world_zoom);
|
compositor_layer: None,
|
||||||
for layer in compositor_layer.mut_iter() {
|
constellation_chan: constellation_chan.clone(),
|
||||||
if !layer.hidden {
|
profiler_chan: profiler_chan
|
||||||
let rect = Rect(Point2D(0f32, 0f32), window_size_page);
|
|
||||||
recomposite = layer.get_buffer_request(&graphics_context, rect, world_zoom) ||
|
|
||||||
recomposite;
|
|
||||||
} else {
|
|
||||||
debug!("Compositor: root layer is hidden!");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
let check_for_messages: &fn(&Port<Msg>) = |port: &Port<Msg>| {
|
pub fn create(app: &Application,
|
||||||
|
opts: Opts,
|
||||||
|
port: Port<Msg>,
|
||||||
|
constellation_chan: ConstellationChan,
|
||||||
|
profiler_chan: ProfilerChan) {
|
||||||
|
let mut compositor = IOCompositor::new(app,
|
||||||
|
opts,
|
||||||
|
port,
|
||||||
|
constellation_chan,
|
||||||
|
profiler_chan);
|
||||||
|
|
||||||
|
// 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.constellation_chan.send(ResizedWindowMsg(self.window_size));
|
||||||
|
|
||||||
|
// Enter the main event loop.
|
||||||
|
let mut tm = Timer::new().unwrap();
|
||||||
|
while !self.done {
|
||||||
|
// Check for new messages coming from the rendering task.
|
||||||
|
self.handle_message();
|
||||||
|
|
||||||
|
// Check for messages coming from the windowing system.
|
||||||
|
self.handle_window_message(self.window.recv());
|
||||||
|
|
||||||
|
// If asked to recomposite and renderer has run at least once
|
||||||
|
if self.recomposite && self.composite_ready {
|
||||||
|
self.recomposite = false;
|
||||||
|
self.composite();
|
||||||
|
}
|
||||||
|
|
||||||
|
tm.sleep(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.ask_for_tiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear out the compositor layers so that painting tasks can destroy the buffers.
|
||||||
|
match self.compositor_layer {
|
||||||
|
None => {}
|
||||||
|
Some(ref mut 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.peek() { self.port.recv(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_message(&mut self) {
|
||||||
// Handle messages
|
// Handle messages
|
||||||
while port.peek() {
|
while self.port.peek() {
|
||||||
match port.recv() {
|
match self.port.recv() {
|
||||||
Exit => done = true,
|
Exit => {
|
||||||
|
self.done = true
|
||||||
|
},
|
||||||
|
|
||||||
|
ChangeReadyState(ready_state) => {
|
||||||
|
self.window.set_ready_state(ready_state);
|
||||||
|
}
|
||||||
|
|
||||||
ChangeReadyState(ready_state) => window.set_ready_state(ready_state),
|
|
||||||
ChangeRenderState(render_state) => {
|
ChangeRenderState(render_state) => {
|
||||||
window.set_render_state(render_state);
|
self.change_render_state(render_state);
|
||||||
if render_state == IdleRenderState { composite_ready = true; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SetUnRenderedColor(_id, color) => {
|
SetUnRenderedColor(_id, color) => {
|
||||||
match compositor_layer {
|
self.set_unrendered_color(_id, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SetIds(frame_tree, response_chan, new_constellation_chan) => {
|
||||||
|
self.set_ids(frame_tree, response_chan, new_constellation_chan);
|
||||||
|
}
|
||||||
|
|
||||||
|
GetGraphicsMetadata(chan) => {
|
||||||
|
chan.send(Some(azure_hl::current_graphics_metadata()));
|
||||||
|
}
|
||||||
|
|
||||||
|
NewLayer(_id, new_size) => {
|
||||||
|
self.create_new_layer(_id, new_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetLayerPageSize(id, new_size, epoch) => {
|
||||||
|
self.set_layer_page_size(id, new_size, epoch);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetLayerClipRect(id, new_rect) => {
|
||||||
|
self.set_layer_clip_rect(id, new_rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
DeleteLayer(id) => {
|
||||||
|
self.delete_layer(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Paint(id, new_layer_buffer_set, epoch) => {
|
||||||
|
self.paint(id, new_layer_buffer_set, epoch);
|
||||||
|
}
|
||||||
|
|
||||||
|
InvalidateRect(id, rect) => {
|
||||||
|
self.invalidate_rect(id, rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollFragmentPoint(id, point) => {
|
||||||
|
self.scroll_fragment_to_point(id, point);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change_render_state(&mut self, render_state: RenderState) {
|
||||||
|
self.window.set_render_state(render_state);
|
||||||
|
if render_state == IdleRenderState {
|
||||||
|
self.composite_ready = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_unrendered_color(&mut self, _id: PipelineId, color: Color) {
|
||||||
|
match self.compositor_layer {
|
||||||
Some(ref mut layer) => {
|
Some(ref mut layer) => {
|
||||||
layer.unrendered_color = color;
|
layer.unrendered_color = color;
|
||||||
}
|
}
|
||||||
|
@ -106,114 +264,140 @@ pub fn run_compositor(app: &Application,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_ids(&mut self,
|
||||||
SetIds(frame_tree, response_chan, new_constellation_chan) => {
|
frame_tree: SendableFrameTree,
|
||||||
|
response_chan: Chan<()>,
|
||||||
|
new_constellation_chan: ConstellationChan) {
|
||||||
response_chan.send(());
|
response_chan.send(());
|
||||||
|
|
||||||
// This assumes there is at most one child, which should be the case.
|
// This assumes there is at most one child, which should be the case.
|
||||||
match root_layer.first_child {
|
match self.root_layer.first_child {
|
||||||
Some(old_layer) => root_layer.remove_child(old_layer),
|
Some(old_layer) => self.root_layer.remove_child(old_layer),
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
let layer = CompositorLayer::from_frame_tree(frame_tree,
|
let layer = CompositorLayer::from_frame_tree(frame_tree,
|
||||||
opts.tile_size,
|
self.opts.tile_size,
|
||||||
Some(10000000u),
|
Some(10000000u),
|
||||||
opts.cpu_painting);
|
self.opts.cpu_painting);
|
||||||
root_layer.add_child_start(ContainerLayerKind(layer.root_layer));
|
self.root_layer.add_child_start(ContainerLayerKind(layer.root_layer));
|
||||||
|
|
||||||
// If there's already a root layer, destroy it cleanly.
|
// If there's already a root layer, destroy it cleanly.
|
||||||
match compositor_layer {
|
match self.compositor_layer {
|
||||||
None => {}
|
None => {}
|
||||||
Some(ref mut compositor_layer) => compositor_layer.clear_all(),
|
Some(ref mut compositor_layer) => compositor_layer.clear_all(),
|
||||||
}
|
}
|
||||||
|
|
||||||
compositor_layer = Some(layer);
|
self.compositor_layer = Some(layer);
|
||||||
|
|
||||||
// Initialize the new constellation channel by sending it the root window size.
|
// Initialize the new constellation channel by sending it the root window size.
|
||||||
let window_size = window.size();
|
let window_size = self.window.size();
|
||||||
let window_size = Size2D(window_size.width as uint,
|
let window_size = Size2D(window_size.width as uint,
|
||||||
window_size.height as uint);
|
window_size.height as uint);
|
||||||
new_constellation_chan.send(ResizedWindowMsg(window_size));
|
new_constellation_chan.send(ResizedWindowMsg(window_size));
|
||||||
|
|
||||||
constellation_chan = new_constellation_chan;
|
self.constellation_chan = new_constellation_chan;
|
||||||
}
|
}
|
||||||
|
|
||||||
GetGraphicsMetadata(chan) => chan.send(Some(azure_hl::current_graphics_metadata())),
|
fn create_new_layer(&mut self, _id: PipelineId, new_size: Size2D<f32>) {
|
||||||
|
|
||||||
NewLayer(_id, new_size) => {
|
|
||||||
// FIXME: This should create an additional layer instead of replacing the current one.
|
// FIXME: This should create an additional layer instead of replacing the current one.
|
||||||
// Once ResizeLayer messages are set up, we can switch to the new functionality.
|
// Once ResizeLayer messages are set up, we can switch to the new functionality.
|
||||||
|
|
||||||
let p = match compositor_layer {
|
let p = match self.compositor_layer {
|
||||||
Some(ref compositor_layer) => compositor_layer.pipeline.clone(),
|
Some(ref compositor_layer) => compositor_layer.pipeline.clone(),
|
||||||
None => fail!("Compositor: Received new layer without initialized pipeline"),
|
None => fail!("Compositor: Received new layer without initialized pipeline"),
|
||||||
};
|
};
|
||||||
let page_size = Size2D(new_size.width as f32, new_size.height as f32);
|
let page_size = Size2D(new_size.width as f32, new_size.height as f32);
|
||||||
let new_layer = CompositorLayer::new(p,
|
let new_layer = CompositorLayer::new(p,
|
||||||
Some(page_size),
|
Some(page_size),
|
||||||
opts.tile_size,
|
self.opts.tile_size,
|
||||||
Some(10000000u),
|
Some(10000000u),
|
||||||
opts.cpu_painting);
|
self.opts.cpu_painting);
|
||||||
|
|
||||||
let current_child = root_layer.first_child;
|
let current_child = self.root_layer.first_child;
|
||||||
// This assumes there is at most one child, which should be the case.
|
// This assumes there is at most one child, which should be the case.
|
||||||
match current_child {
|
match current_child {
|
||||||
Some(old_layer) => root_layer.remove_child(old_layer),
|
Some(old_layer) => self.root_layer.remove_child(old_layer),
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
root_layer.add_child_start(ContainerLayerKind(new_layer.root_layer));
|
self.root_layer.add_child_start(ContainerLayerKind(new_layer.root_layer));
|
||||||
compositor_layer = Some(new_layer);
|
self.compositor_layer = Some(new_layer);
|
||||||
|
|
||||||
ask_for_tiles();
|
self.ask_for_tiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
SetLayerPageSize(id, new_size, epoch) => {
|
fn set_layer_page_size(&mut self,
|
||||||
match compositor_layer {
|
id: PipelineId,
|
||||||
|
new_size: Size2D<f32>,
|
||||||
|
epoch: Epoch) {
|
||||||
|
let ask: bool = match self.compositor_layer {
|
||||||
Some(ref mut layer) => {
|
Some(ref mut layer) => {
|
||||||
|
let window_size = &self.window_size;
|
||||||
|
let world_zoom = self.world_zoom;
|
||||||
let page_window = Size2D(window_size.width as f32 / world_zoom,
|
let page_window = Size2D(window_size.width as f32 / world_zoom,
|
||||||
window_size.height as f32 / world_zoom);
|
window_size.height as f32 / world_zoom);
|
||||||
assert!(layer.resize(id, new_size, page_window, epoch));
|
assert!(layer.resize(id, new_size, page_window, epoch));
|
||||||
ask_for_tiles();
|
true
|
||||||
}
|
}
|
||||||
None => {}
|
None => {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if ask {
|
||||||
|
self.ask_for_tiles();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SetLayerClipRect(id, new_rect) => {
|
fn set_layer_clip_rect(&mut self, id: PipelineId, new_rect: Rect<f32>) {
|
||||||
match compositor_layer {
|
let ask: bool = match self.compositor_layer {
|
||||||
Some(ref mut layer) => {
|
Some(ref mut layer) => {
|
||||||
assert!(layer.set_clipping_rect(id, new_rect));
|
assert!(layer.set_clipping_rect(id, new_rect));
|
||||||
ask_for_tiles();
|
true
|
||||||
}
|
}
|
||||||
None => {}
|
None => {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if ask {
|
||||||
|
self.ask_for_tiles();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DeleteLayer(id) => {
|
fn delete_layer(&mut self, id: PipelineId) {
|
||||||
match compositor_layer {
|
let ask: bool = match self.compositor_layer {
|
||||||
Some(ref mut layer) => {
|
Some(ref mut layer) => {
|
||||||
assert!(layer.delete(&graphics_context, id));
|
assert!(layer.delete(&self.graphics_context, id));
|
||||||
ask_for_tiles();
|
true
|
||||||
}
|
}
|
||||||
None => {}
|
None => {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if ask {
|
||||||
|
self.ask_for_tiles();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Paint(id, new_layer_buffer_set, epoch) => {
|
fn paint(&mut self,
|
||||||
|
id: PipelineId,
|
||||||
|
new_layer_buffer_set: ~LayerBufferSet,
|
||||||
|
epoch: Epoch) {
|
||||||
debug!("osmain: received new frame");
|
debug!("osmain: received new frame");
|
||||||
|
|
||||||
// From now on, if we destroy the buffers, they will leak.
|
// From now on, if we destroy the buffers, they will leak.
|
||||||
let mut new_layer_buffer_set = new_layer_buffer_set;
|
let mut new_layer_buffer_set = new_layer_buffer_set;
|
||||||
new_layer_buffer_set.mark_will_leak();
|
new_layer_buffer_set.mark_will_leak();
|
||||||
|
|
||||||
match compositor_layer {
|
match self.compositor_layer {
|
||||||
Some(ref mut layer) => {
|
Some(ref mut layer) => {
|
||||||
assert!(layer.add_buffers(&graphics_context,
|
assert!(layer.add_buffers(&self.graphics_context,
|
||||||
id,
|
id,
|
||||||
new_layer_buffer_set,
|
new_layer_buffer_set,
|
||||||
epoch).is_none());
|
epoch).is_none());
|
||||||
recomposite = true;
|
self.recomposite = true;
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
fail!("Compositor: given paint command with no CompositorLayer initialized");
|
fail!("Compositor: given paint command with no CompositorLayer initialized");
|
||||||
|
@ -223,95 +407,153 @@ pub fn run_compositor(app: &Application,
|
||||||
// it wishes.
|
// it wishes.
|
||||||
}
|
}
|
||||||
|
|
||||||
InvalidateRect(id, rect) => {
|
fn invalidate_rect(&mut self, id: PipelineId, rect: Rect<uint>) {
|
||||||
match compositor_layer {
|
let ask: bool = match self.compositor_layer {
|
||||||
Some(ref mut layer) => {
|
Some(ref mut layer) => {
|
||||||
layer.invalidate_rect(id, Rect(Point2D(rect.origin.x as f32,
|
let point = Point2D(rect.origin.x as f32,
|
||||||
rect.origin.y as f32),
|
rect.origin.y as f32);
|
||||||
Size2D(rect.size.width as f32,
|
let size = Size2D(rect.size.width as f32,
|
||||||
rect.size.height as f32)));
|
rect.size.height as f32);
|
||||||
ask_for_tiles();
|
layer.invalidate_rect(id, Rect(point, size));
|
||||||
}
|
true
|
||||||
None => {} // Nothing to do
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ScrollFragmentPoint(id, point) => {
|
|
||||||
let page_window = Size2D(window_size.width as f32 / world_zoom,
|
|
||||||
window_size.height as f32 / world_zoom);
|
|
||||||
match compositor_layer {
|
|
||||||
Some(ref mut layer) if layer.pipeline.id == id => {
|
|
||||||
recomposite = layer.move(point, page_window) | recomposite;
|
|
||||||
ask_for_tiles();
|
|
||||||
}
|
|
||||||
Some(_) | None => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
None => {
|
||||||
|
// Nothing to do
|
||||||
|
false
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let check_for_window_messages: &fn(WindowEvent) = |event| {
|
if ask {
|
||||||
|
self.ask_for_tiles();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scroll_fragment_to_point(&mut self, id: PipelineId, point: Point2D<f32>) {
|
||||||
|
let world_zoom = self.world_zoom;
|
||||||
|
let page_window = Size2D(self.window_size.width as f32 / world_zoom,
|
||||||
|
self.window_size.height as f32 / world_zoom);
|
||||||
|
let ask: bool = match self.compositor_layer {
|
||||||
|
Some(ref mut layer) if layer.pipeline.id == id => {
|
||||||
|
let recomposite = layer.move(point, page_window) | self.recomposite;
|
||||||
|
self.recomposite = recomposite;
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
Some(_) | None => {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if ask {
|
||||||
|
self.ask_for_tiles();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_window_message(&mut self, event: WindowEvent) {
|
||||||
match event {
|
match event {
|
||||||
IdleWindowEvent => {}
|
IdleWindowEvent => {}
|
||||||
|
|
||||||
RefreshWindowEvent => {
|
RefreshWindowEvent => {
|
||||||
recomposite = true;
|
self.recomposite = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResizeWindowEvent(width, height) => {
|
ResizeWindowEvent(width, height) => {
|
||||||
|
self.on_resize_window_event(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadUrlWindowEvent(url_string) => {
|
||||||
|
self.on_load_url_window_event(url_string);
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseWindowEventClass(mouse_window_event) => {
|
||||||
|
self.on_mouse_window_event_class(mouse_window_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollWindowEvent(delta, cursor) => {
|
||||||
|
self.on_scroll_window_event(delta, cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
ZoomWindowEvent(magnification) => {
|
||||||
|
self.on_zoom_window_event(magnification);
|
||||||
|
}
|
||||||
|
|
||||||
|
NavigationWindowEvent(direction) => {
|
||||||
|
self.on_navigation_window_event(direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
FinishedWindowEvent => {
|
||||||
|
let exit = self.opts.exit_after_load;
|
||||||
|
if exit {
|
||||||
|
self.done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QuitWindowEvent => {
|
||||||
|
self.done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_resize_window_event(&mut self, width: uint, height: uint) {
|
||||||
let new_size = Size2D(width, height);
|
let new_size = Size2D(width, height);
|
||||||
if window_size != new_size {
|
if self.window_size != new_size {
|
||||||
debug!("osmain: window resized to {:u}x{:u}", width, height);
|
debug!("osmain: window resized to {:u}x{:u}", width, height);
|
||||||
window_size = new_size;
|
self.window_size = new_size;
|
||||||
constellation_chan.send(ResizedWindowMsg(new_size))
|
self.constellation_chan.send(ResizedWindowMsg(new_size))
|
||||||
} else {
|
} else {
|
||||||
debug!("osmain: dropping window resize since size is still {:u}x{:u}", width, height);
|
debug!("osmain: dropping window resize since size is still {:u}x{:u}", width, height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LoadUrlWindowEvent(url_string) => {
|
fn on_load_url_window_event(&self, url_string: ~str) {
|
||||||
debug!("osmain: loading URL `{:s}`", url_string);
|
debug!("osmain: loading URL `{:s}`", url_string);
|
||||||
let root_pipeline_id = match compositor_layer {
|
let root_pipeline_id = match self.compositor_layer {
|
||||||
Some(ref layer) => layer.pipeline.id.clone(),
|
Some(ref layer) => layer.pipeline.id.clone(),
|
||||||
None => fail!("Compositor: Received LoadUrlWindowEvent without initialized compositor layers"),
|
None => fail!("Compositor: Received LoadUrlWindowEvent without initialized compositor layers"),
|
||||||
};
|
};
|
||||||
constellation_chan.send(LoadUrlMsg(root_pipeline_id,
|
|
||||||
url::make_url(url_string.to_str(), None)))
|
let msg = LoadUrlMsg(root_pipeline_id, url::make_url(url_string.to_str(), None));
|
||||||
|
self.constellation_chan.send(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseWindowEventClass(mouse_window_event) => {
|
fn on_mouse_window_event_class(&self, mouse_window_event: MouseWindowEvent) {
|
||||||
|
let world_zoom = self.world_zoom;
|
||||||
let point = match mouse_window_event {
|
let point = match mouse_window_event {
|
||||||
MouseWindowClickEvent(_, p) => Point2D(p.x / world_zoom, p.y / world_zoom),
|
MouseWindowClickEvent(_, p) => Point2D(p.x / world_zoom, p.y / world_zoom),
|
||||||
MouseWindowMouseDownEvent(_, p) => Point2D(p.x / world_zoom, p.y / world_zoom),
|
MouseWindowMouseDownEvent(_, p) => Point2D(p.x / world_zoom, p.y / world_zoom),
|
||||||
MouseWindowMouseUpEvent(_, p) => Point2D(p.x / world_zoom, p.y / world_zoom),
|
MouseWindowMouseUpEvent(_, p) => Point2D(p.x / world_zoom, p.y / world_zoom),
|
||||||
};
|
};
|
||||||
for layer in compositor_layer.iter() {
|
for layer in self.compositor_layer.iter() {
|
||||||
layer.send_mouse_event(mouse_window_event, point);
|
layer.send_mouse_event(mouse_window_event, point);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollWindowEvent(delta, cursor) => {
|
fn on_scroll_window_event(&mut self, delta: Point2D<f32>, cursor: Point2D<i32>) {
|
||||||
|
let world_zoom = self.world_zoom;
|
||||||
// TODO: modify delta to snap scroll to pixels.
|
// TODO: modify delta to snap scroll to pixels.
|
||||||
let page_delta = Point2D(delta.x as f32 / world_zoom, delta.y as f32 / world_zoom);
|
let page_delta = Point2D(delta.x as f32 / world_zoom, delta.y as f32 / world_zoom);
|
||||||
let page_cursor: Point2D<f32> = Point2D(cursor.x as f32 / world_zoom,
|
let page_cursor: Point2D<f32> = Point2D(cursor.x as f32 / world_zoom,
|
||||||
cursor.y as f32 / world_zoom);
|
cursor.y as f32 / world_zoom);
|
||||||
let page_window = Size2D(window_size.width as f32 / world_zoom,
|
let page_window = Size2D(self.window_size.width as f32 / world_zoom,
|
||||||
window_size.height as f32 / world_zoom);
|
self.window_size.height as f32 / world_zoom);
|
||||||
for layer in compositor_layer.mut_iter() {
|
for layer in self.compositor_layer.mut_iter() {
|
||||||
recomposite = layer.scroll(page_delta, page_cursor, page_window) || recomposite;
|
let recomposite = layer.scroll(page_delta, page_cursor, page_window) || self.recomposite;
|
||||||
|
self.recomposite = recomposite;
|
||||||
}
|
}
|
||||||
ask_for_tiles();
|
self.ask_for_tiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
ZoomWindowEvent(magnification) => {
|
fn on_zoom_window_event(&mut self, magnification: f32) {
|
||||||
zoom_action = true;
|
self.zoom_action = true;
|
||||||
zoom_time = precise_time_s();
|
self.zoom_time = precise_time_s();
|
||||||
let old_world_zoom = world_zoom;
|
let old_world_zoom = self.world_zoom;
|
||||||
|
let window_size = &self.window_size;
|
||||||
|
|
||||||
// Determine zoom amount
|
// Determine zoom amount
|
||||||
world_zoom = (world_zoom * magnification).max(&1.0);
|
self.world_zoom = (self.world_zoom * magnification).max(&1.0);
|
||||||
root_layer.common.set_transform(identity().scale(world_zoom, world_zoom, 1f32));
|
let world_zoom = self.world_zoom;
|
||||||
|
|
||||||
|
self.root_layer.common.set_transform(identity().scale(world_zoom, world_zoom, 1f32));
|
||||||
|
|
||||||
// Scroll as needed
|
// Scroll as needed
|
||||||
let page_delta = Point2D(window_size.width as f32 * (1.0 / world_zoom - 1.0 / old_world_zoom) * 0.5,
|
let page_delta = Point2D(window_size.width as f32 * (1.0 / world_zoom - 1.0 / old_world_zoom) * 0.5,
|
||||||
|
@ -320,60 +562,62 @@ pub fn run_compositor(app: &Application,
|
||||||
let page_cursor = Point2D(-1f32, -1f32); // Make sure this hits the base layer
|
let page_cursor = Point2D(-1f32, -1f32); // Make sure this hits the base layer
|
||||||
let page_window = Size2D(window_size.width as f32 / world_zoom,
|
let page_window = Size2D(window_size.width as f32 / world_zoom,
|
||||||
window_size.height as f32 / world_zoom);
|
window_size.height as f32 / world_zoom);
|
||||||
for layer in compositor_layer.mut_iter() {
|
for layer in self.compositor_layer.mut_iter() {
|
||||||
layer.scroll(page_delta, page_cursor, page_window);
|
layer.scroll(page_delta, page_cursor, page_window);
|
||||||
}
|
}
|
||||||
|
|
||||||
recomposite = true;
|
self.recomposite = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
NavigationWindowEvent(direction) => {
|
fn on_navigation_window_event(&self, direction: WindowNavigateMsg) {
|
||||||
let direction = match direction {
|
let direction = match direction {
|
||||||
windowing::Forward => constellation_msg::Forward,
|
windowing::Forward => constellation_msg::Forward,
|
||||||
windowing::Back => constellation_msg::Back,
|
windowing::Back => constellation_msg::Back,
|
||||||
};
|
};
|
||||||
constellation_chan.send(NavigateMsg(direction))
|
self.constellation_chan.send(NavigateMsg(direction))
|
||||||
}
|
}
|
||||||
|
|
||||||
FinishedWindowEvent => {
|
/// Get BufferRequests from each layer.
|
||||||
if opts.exit_after_load {
|
fn ask_for_tiles(&mut self) {
|
||||||
done = true;
|
let world_zoom = self.world_zoom;
|
||||||
|
let window_size_page = Size2D(self.window_size.width as f32 / world_zoom,
|
||||||
|
self.window_size.height as f32 / world_zoom);
|
||||||
|
for layer in self.compositor_layer.mut_iter() {
|
||||||
|
if !layer.hidden {
|
||||||
|
let rect = Rect(Point2D(0f32, 0f32), window_size_page);
|
||||||
|
let recomposite = layer.get_buffer_request(&self.graphics_context, rect, world_zoom) ||
|
||||||
|
self.recomposite;
|
||||||
|
self.recomposite = recomposite;
|
||||||
|
} else {
|
||||||
|
debug!("Compositor: root layer is hidden!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QuitWindowEvent => {
|
fn composite(&mut self) {
|
||||||
done = true;
|
do profile(time::CompositingCategory, self.profiler_chan.clone()) {
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
let profiler_chan = profiler_chan.clone();
|
|
||||||
let write_png = opts.output_file.is_some();
|
|
||||||
let exit = opts.exit_after_load;
|
|
||||||
let composite = || {
|
|
||||||
do profile(time::CompositingCategory, profiler_chan.clone()) {
|
|
||||||
debug!("compositor: compositing");
|
debug!("compositor: compositing");
|
||||||
// Adjust the layer dimensions as necessary to correspond to the size of the window.
|
// Adjust the layer dimensions as necessary to correspond to the size of the window.
|
||||||
scene.size = window.size();
|
self.scene.size = self.window.size();
|
||||||
// Render the scene.
|
// Render the scene.
|
||||||
match compositor_layer {
|
match self.compositor_layer {
|
||||||
Some(ref mut layer) => {
|
Some(ref mut layer) => {
|
||||||
scene.background_color.r = layer.unrendered_color.r;
|
self.scene.background_color.r = layer.unrendered_color.r;
|
||||||
scene.background_color.g = layer.unrendered_color.g;
|
self.scene.background_color.g = layer.unrendered_color.g;
|
||||||
scene.background_color.b = layer.unrendered_color.b;
|
self.scene.background_color.b = layer.unrendered_color.b;
|
||||||
scene.background_color.a = layer.unrendered_color.a;
|
self.scene.background_color.a = layer.unrendered_color.a;
|
||||||
}
|
}
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
rendergl::render_scene(context, &scene);
|
rendergl::render_scene(self.context, &self.scene);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render to PNG. We must read from the back buffer (ie, before
|
// Render to PNG. We must read from the back buffer (ie, before
|
||||||
// window.present()) as OpenGL ES 2 does not have glReadBuffer().
|
// self.window.present()) as OpenGL ES 2 does not have glReadBuffer().
|
||||||
|
let write_png = self.opts.output_file.is_some();
|
||||||
if write_png {
|
if write_png {
|
||||||
let (width, height) = (window_size.width as uint, window_size.height as uint);
|
let (width, height) = (self.window_size.width as uint, self.window_size.height as uint);
|
||||||
let path = from_str::<Path>(*opts.output_file.get_ref()).unwrap();
|
let path = from_str::<Path>(*self.opts.output_file.get_ref()).unwrap();
|
||||||
let mut pixels = gl2::read_pixels(0, 0,
|
let mut pixels = gl2::read_pixels(0, 0,
|
||||||
width as gl2::GLsizei,
|
width as gl2::GLsizei,
|
||||||
height as gl2::GLsizei,
|
height as gl2::GLsizei,
|
||||||
|
@ -397,49 +641,14 @@ pub fn run_compositor(app: &Application,
|
||||||
let res = png::store_png(&img, &path);
|
let res = png::store_png(&img, &path);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
|
|
||||||
done = true;
|
self.done = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.present();
|
self.window.present();
|
||||||
|
|
||||||
if exit { done = true; }
|
let exit = self.opts.exit_after_load;
|
||||||
};
|
if exit {
|
||||||
|
self.done = true;
|
||||||
// Tell the constellation about the initial window size.
|
|
||||||
constellation_chan.send(ResizedWindowMsg(window_size));
|
|
||||||
|
|
||||||
// Enter the main event loop.
|
|
||||||
let mut tm = Timer::new().unwrap();
|
|
||||||
while !done {
|
|
||||||
// Check for new messages coming from the rendering task.
|
|
||||||
check_for_messages(&port);
|
|
||||||
|
|
||||||
// Check for messages coming from the windowing system.
|
|
||||||
check_for_window_messages(window.recv());
|
|
||||||
|
|
||||||
// If asked to recomposite and renderer has run at least once
|
|
||||||
if recomposite && composite_ready {
|
|
||||||
recomposite = false;
|
|
||||||
composite();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tm.sleep(10);
|
|
||||||
|
|
||||||
// If a pinch-zoom happened recently, ask for tiles at the new resolution
|
|
||||||
if zoom_action && precise_time_s() - zoom_time > 0.3 {
|
|
||||||
zoom_action = false;
|
|
||||||
ask_for_tiles();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear out the compositor layers so that painting tasks can destroy the buffers.
|
|
||||||
match compositor_layer {
|
|
||||||
None => {}
|
|
||||||
Some(ref mut layer) => layer.forget_all_tiles(),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Drain compositor port, sometimes messages contain channels that are blocking
|
|
||||||
// another task from finishing (i.e. SetIds)
|
|
||||||
while port.peek() { port.recv(); }
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue