servo: Merge #3809 - Clip display list based on frame viewport (from mrobinson:display-list-optimization); r=pcwalton

Instead of creating a display list for the entire page, only create one
for an area that expands around the viewport. On my machine this makes
incremental layout of http://timecube.com 50% faster.

Source-Repo: https://github.com/servo/servo
Source-Revision: 26045d7fcbab8851fbefe2851cd904203f8fd8dd
This commit is contained in:
Martin Robinson 2014-11-12 17:36:32 -07:00
Родитель 7f266cc2c6
Коммит 8cec6a7fbe
11 изменённых файлов: 130 добавлений и 34 удалений

2
servo/Cargo.lock сгенерированный
Просмотреть файл

@ -174,7 +174,7 @@ source = "git+https://github.com/servo/libfreetype2#5b6499164106f094937565595c7b
[[package]]
name = "geom"
version = "0.1.0"
source = "git+https://github.com/servo/rust-geom#b001a76e907befaae1d0d6dd259418a22092da86"
source = "git+https://github.com/servo/rust-geom#e5e74911ac6d3201009879b72499d6c681302611"
[[package]]
name = "gfx"

Просмотреть файл

@ -38,10 +38,12 @@ use layers::scene::Scene;
use png;
use gleam::gl::types::{GLint, GLsizei};
use gleam::gl;
use script_traits::{ViewportMsg, ScriptControlChan};
use servo_msg::compositor_msg::{Blank, Epoch, FinishedLoading, IdleRenderState, LayerId};
use servo_msg::compositor_msg::{ReadyState, RenderingRenderState, RenderState, Scrollable};
use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, LoadUrlMsg, NavigateMsg};
use servo_msg::constellation_msg::{LoadData, PipelineId, ResizedWindowMsg, WindowSizeData};
use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, LoadUrlMsg};
use servo_msg::constellation_msg::{NavigateMsg, LoadData, PipelineId, ResizedWindowMsg};
use servo_msg::constellation_msg::{WindowSizeData};
use servo_msg::constellation_msg;
use servo_util::geometry::{PagePx, ScreenPx, ViewportPx};
use servo_util::memory::MemoryProfilerChan;
@ -266,6 +268,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
self.set_frame_tree(&frame_tree,
response_chan,
new_constellation_chan);
self.send_viewport_rects_for_all_layers();
}
(FrameTreeUpdateMsg(frame_tree_diff, response_channel), NotShuttingDown) => {
@ -775,6 +778,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
}
fn process_pending_scroll_events(&mut self) {
let had_scroll_events = self.pending_scroll_events.len() > 0;
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;
@ -789,6 +793,10 @@ impl<Window: WindowMethods> IOCompositor<Window> {
self.start_scrolling_timer_if_necessary();
self.send_buffer_requests_for_all_layers();
}
if had_scroll_events {
self.send_viewport_rects_for_all_layers();
}
}
fn device_pixels_per_screen_px(&self) -> ScaleFactor<ScreenPx, DevicePixel, f32> {
@ -845,6 +853,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
None => { }
}
self.send_viewport_rects_for_all_layers();
self.composite_if_necessary();
}
@ -902,6 +911,27 @@ impl<Window: WindowMethods> IOCompositor<Window> {
}
}
fn send_viewport_rect_for_layer(&self, layer: Rc<Layer<CompositorData>>) {
if layer.extra_data.borrow().id == LayerId::null() {
let layer_rect = Rect(-layer.extra_data.borrow().scroll_offset.to_untyped(),
layer.bounds.borrow().size.to_untyped());
let pipeline = &layer.extra_data.borrow().pipeline;
let ScriptControlChan(ref chan) = pipeline.script_chan;
chan.send(ViewportMsg(pipeline.id.clone(), layer_rect));
}
for kid in layer.children().iter() {
self.send_viewport_rect_for_layer(kid.clone());
}
}
fn send_viewport_rects_for_all_layers(&self) {
match self.scene.root {
Some(ref root) => self.send_viewport_rect_for_layer(root.clone()),
None => {},
}
}
/// 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();

Просмотреть файл

@ -48,7 +48,7 @@ use geom::Size2D;
use gfx::display_list::BlockLevel;
use serialize::{Encoder, Encodable};
use servo_msg::compositor_msg::LayerId;
use servo_util::geometry::{Au, MAX_AU, MAX_RECT};
use servo_util::geometry::{Au, MAX_AU};
use servo_util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize};
use servo_util::opts;
use std::cmp::{max, min};
@ -1665,10 +1665,6 @@ impl Flow for BlockFlow {
// FIXME(#2795): Get the real container size
let container_size = Size2D::zero();
if self.is_root() {
self.base.clip_rect = MAX_RECT;
}
if self.base.flags.is_absolutely_positioned() {
let position_start = self.base.position.start.to_physical(self.base.writing_mode,
container_size);

Просмотреть файл

@ -499,6 +499,10 @@ impl FragmentDisplayListBuilding for Fragment {
return
}
if !absolute_fragment_bounds.intersects(clip_rect) {
return;
}
debug!("Fragment::build_display_list: intersected. Adding display item...");
if self.is_primary_fragment() {

Просмотреть файл

@ -625,6 +625,8 @@ impl LayoutTask {
LogicalPoint::zero(writing_mode).to_physical(writing_mode,
rw_data.screen_size);
flow::mut_base(layout_root.deref_mut()).clip_rect = data.page_clip_rect;
let rw_data = rw_data.deref_mut();
match rw_data.parallel_traversal {
None => {

Просмотреть файл

@ -28,37 +28,37 @@
//! a datatype.
use dom::bindings::js::JS;
use dom::bindings::utils::{Reflectable, Reflector};
use dom::bindings::utils::{Reflectable, Reflector, WindowProxyHandler};
use dom::node::{Node, TrustedNodeAddress};
use collections::hash::{Hash, Hasher};
use geom::rect::Rect;
use html5ever::tree_builder::QuirksMode;
use http::headers::request::HeaderCollection as RequestHeaderCollection;
use http::headers::response::HeaderCollection as ResponseHeaderCollection;
use http::method::Method;
use js::jsapi::{JSObject, JSTracer, JS_CallTracer, JSTRACE_OBJECT};
use js::jsval::JSVal;
use js::rust::Cx;
use layout_interface::{LayoutRPC, LayoutChan};
use libc;
use std::rc::Rc;
use std::cell::{Cell, RefCell};
use url::Url;
use msg::constellation_msg::{PipelineId, SubpageId, WindowSizeData};
use net::image_cache_task::ImageCacheTask;
use script_traits::ScriptControlChan;
use std::collections::hashmap::HashMap;
use collections::hash::{Hash, Hasher};
use style::PropertyDeclarationBlock;
use std::comm::{Receiver, Sender};
use string_cache::{Atom, Namespace};
use js::rust::Cx;
use http::headers::response::HeaderCollection as ResponseHeaderCollection;
use http::headers::request::HeaderCollection as RequestHeaderCollection;
use http::method::Method;
use std::io::timer::Timer;
use script_traits::UntrustedNodeAddress;
use servo_msg::compositor_msg::ScriptListener;
use servo_msg::constellation_msg::ConstellationChan;
use servo_util::smallvec::{SmallVec1, SmallVec};
use servo_util::str::LengthOrPercentageOrAuto;
use layout_interface::{LayoutRPC, LayoutChan};
use dom::node::{Node, TrustedNodeAddress};
use dom::bindings::utils::WindowProxyHandler;
use html5ever::tree_builder::QuirksMode;
use std::cell::{Cell, RefCell};
use std::collections::hashmap::HashMap;
use std::comm::{Receiver, Sender};
use std::io::timer::Timer;
use std::rc::Rc;
use string_cache::{Atom, Namespace};
use style::PropertyDeclarationBlock;
use url::Url;
/// A trait to allow tracing (only) DOM objects.
pub trait JSTraceable {
@ -200,6 +200,7 @@ no_jsmanaged_fields!(uint, u8, u16, u32, u64)
no_jsmanaged_fields!(int, i8, i16, i32, i64)
no_jsmanaged_fields!(Sender<T>)
no_jsmanaged_fields!(Receiver<T>)
no_jsmanaged_fields!(Rect<T>)
no_jsmanaged_fields!(ImageCacheTask, ScriptControlChan)
no_jsmanaged_fields!(Atom, Namespace, Timer)
no_jsmanaged_fields!(PropertyDeclarationBlock)

Просмотреть файл

@ -109,6 +109,8 @@ pub struct Reflow {
pub id: uint,
/// The type of query if any to perform during this reflow.
pub query_type: ReflowQueryType,
/// A clipping rectangle for the page, an enlarged rectangle containing the viewport.
pub page_clip_rect: Rect<Au>,
}
/// Encapsulates a channel to the layout task.

Просмотреть файл

@ -19,19 +19,21 @@ use layout_interface::{
};
use script_traits::{UntrustedNodeAddress, ScriptControlChan};
use geom::{Point2D, Rect};
use geom::{Point2D, Rect, Size2D};
use js::rust::Cx;
use servo_msg::compositor_msg::PerformingLayout;
use servo_msg::compositor_msg::ScriptListener;
use servo_msg::constellation_msg::{ConstellationChan, WindowSizeData};
use servo_msg::constellation_msg::{PipelineId, SubpageId};
use servo_net::resource_task::ResourceTask;
use servo_util::geometry::Au;
use servo_util::geometry::{Au, MAX_RECT};
use servo_util::geometry;
use servo_util::str::DOMString;
use servo_util::smallvec::{SmallVec1, SmallVec};
use std::cell::Cell;
use std::comm::{channel, Receiver, Empty, Disconnected};
use std::mem::replace;
use std::num::abs;
use std::rc::Rc;
use url::Url;
@ -98,6 +100,10 @@ pub struct Page {
/// Number of unnecessary potential reflows that were skipped since the last reflow
pub avoided_reflows: Cell<int>,
/// An enlarged rectangle around the page contents visible in the viewport, used
/// to prevent creating display list items for content that is far away from the viewport.
pub page_clip_rect: Cell<Rect<Au>>,
}
pub struct PageIterator {
@ -164,6 +170,7 @@ impl Page {
damaged: Cell::new(false),
pending_reflows: Cell::new(0),
avoided_reflows: Cell::new(0),
page_clip_rect: Cell::new(MAX_RECT),
}
}
@ -236,6 +243,49 @@ impl Page {
}
None
}
fn should_move_clip_rect(&self, clip_rect: Rect<Au>, new_viewport: Rect<f32>) -> bool{
let clip_rect = Rect(Point2D(geometry::to_frac_px(clip_rect.origin.x) as f32,
geometry::to_frac_px(clip_rect.origin.y) as f32),
Size2D(geometry::to_frac_px(clip_rect.size.width) as f32,
geometry::to_frac_px(clip_rect.size.height) as f32));
// We only need to move the clip rect if the viewport is getting near the edge of
// our preexisting clip rect. We use half of the size of the viewport as a heuristic
// for "close."
static VIEWPORT_SCROLL_MARGIN_SIZE: f32 = 0.5;
let viewport_scroll_margin = new_viewport.size * VIEWPORT_SCROLL_MARGIN_SIZE;
abs(clip_rect.origin.x - new_viewport.origin.x) <= viewport_scroll_margin.width ||
abs(clip_rect.max_x() - new_viewport.max_x()) <= viewport_scroll_margin.width ||
abs(clip_rect.origin.y - new_viewport.origin.y) <= viewport_scroll_margin.height ||
abs(clip_rect.max_y() - new_viewport.max_y()) <= viewport_scroll_margin.height
}
pub fn set_page_clip_rect_with_new_viewport(&self, viewport: Rect<f32>) -> bool {
// We use a clipping rectangle that is five times the size of the of the viewport,
// so that we don't collect display list items for areas too far outside the viewport,
// but also don't trigger reflows every time the viewport changes.
static VIEWPORT_EXPANSION: f32 = 2.0; // 2 lengths on each side plus original length is 5 total.
let proposed_clip_rect = geometry::f32_rect_to_au_rect(
viewport.inflate(viewport.size.width * VIEWPORT_EXPANSION,
viewport.size.height * VIEWPORT_EXPANSION));
let clip_rect = self.page_clip_rect.get();
if proposed_clip_rect == clip_rect {
return false;
}
let had_clip_rect = clip_rect != MAX_RECT;
if had_clip_rect && !self.should_move_clip_rect(clip_rect, viewport) {
return false;
}
self.page_clip_rect.set(proposed_clip_rect);
// If we didn't have a clip rect, the previous display doesn't need rebuilding
// because it was built for infinite clip (MAX_RECT).
had_clip_rect
}
}
impl Iterator<Rc<Page>> for PageIterator {
@ -375,6 +425,7 @@ impl Page {
script_join_chan: join_chan,
id: last_reflow_id.get(),
query_type: query_type,
page_clip_rect: self.page_clip_rect.get(),
};
let LayoutChan(ref chan) = self.layout_chan;

Просмотреть файл

@ -40,9 +40,9 @@ use devtools_traits::{DevtoolScriptControlMsg, EvaluateJS, EvaluateJSReply, GetD
use devtools_traits::{GetChildren, GetLayout};
use script_traits::{CompositorEvent, ResizeEvent, ReflowEvent, ClickEvent, MouseDownEvent};
use script_traits::{MouseMoveEvent, MouseUpEvent, ConstellationControlMsg, ScriptTaskFactory};
use script_traits::{ResizeMsg, AttachLayoutMsg, LoadMsg, SendEventMsg, ResizeInactiveMsg};
use script_traits::{ExitPipelineMsg, NewLayoutInfo, OpaqueScriptLayoutChannel, ScriptControlChan};
use script_traits::{ReflowCompleteMsg, UntrustedNodeAddress};
use script_traits::{ResizeMsg, AttachLayoutMsg, LoadMsg, ViewportMsg, SendEventMsg};
use script_traits::{ResizeInactiveMsg, ExitPipelineMsg, NewLayoutInfo, OpaqueScriptLayoutChannel};
use script_traits::{ScriptControlChan, ReflowCompleteMsg, UntrustedNodeAddress};
use servo_msg::compositor_msg::{FinishedLoading, LayerId, Loading};
use servo_msg::compositor_msg::{ScriptListener};
use servo_msg::constellation_msg::{ConstellationChan, LoadCompleteMsg, LoadUrlMsg, NavigationDirection};
@ -495,6 +495,13 @@ impl ScriptTask {
pending.push_all_move(node_addresses);
needs_reflow.insert(id);
}
FromConstellation(ViewportMsg(id, rect)) => {
let mut page = self.page.borrow_mut();
let inner_page = page.find(id).expect("Page rect message sent to nonexistent pipeline");
if inner_page.set_page_clip_rect_with_new_viewport(rect) {
needs_reflow.insert(id);
}
}
_ => {
sequential.push(event);
}
@ -530,6 +537,7 @@ impl ScriptTask {
FromConstellation(ReflowCompleteMsg(id, reflow_id)) => self.handle_reflow_complete_msg(id, reflow_id),
FromConstellation(ResizeInactiveMsg(id, new_size)) => self.handle_resize_inactive_msg(id, new_size),
FromConstellation(ExitPipelineMsg(id)) => if self.handle_exit_pipeline_msg(id) { return false },
FromConstellation(ViewportMsg(..)) => fail!("should have handled ViewportMsg already"),
FromScript(ExitWindowMsg(id)) => self.handle_exit_window_msg(id),
FromConstellation(ResizeMsg(..)) => fail!("should have handled ResizeMsg already"),
FromScript(XHRProgressMsg(addr, progress)) => XMLHttpRequest::handle_progress(addr, progress),

Просмотреть файл

@ -32,6 +32,7 @@ use servo_util::smallvec::SmallVec1;
use std::any::Any;
use geom::point::Point2D;
use geom::rect::Rect;
use serialize::{Encodable, Encoder};
@ -62,6 +63,7 @@ pub enum ConstellationControlMsg {
SendEventMsg(PipelineId, CompositorEvent),
/// Notifies script that reflow is finished.
ReflowCompleteMsg(PipelineId, uint),
ViewportMsg(PipelineId, Rect<f32>),
}
/// Events from the compositor that the script task needs to know about

2
servo/ports/cef/Cargo.lock сгенерированный
Просмотреть файл

@ -184,7 +184,7 @@ source = "git+https://github.com/servo/libfreetype2#5b6499164106f094937565595c7b
[[package]]
name = "geom"
version = "0.1.0"
source = "git+https://github.com/servo/rust-geom#b001a76e907befaae1d0d6dd259418a22092da86"
source = "git+https://github.com/servo/rust-geom#e5e74911ac6d3201009879b72499d6c681302611"
[[package]]
name = "gfx"