auto merge of #3722 : pcwalton/servo/flow-construction-overhaul, r=glennw

This is a grab bag of performance improvements that significantly improve style recalculation, layout, and painting on a few static pages.

Let me know if you'd like me to split this PR up.

r? @glennw
This commit is contained in:
bors-servo 2014-10-21 10:06:37 -06:00
commit 156ca98236
15 changed files with 378 additions and 349 deletions

View file

@ -288,10 +288,10 @@ impl DisplayList {
pub fn draw_into_context(&self,
render_context: &mut RenderContext,
current_transform: &Matrix2D<AzFloat>,
current_clip_rect: &Rect<Au>) {
current_clip_stack: &mut Vec<Rect<Au>>) {
debug!("Beginning display list.");
for item in self.list.iter() {
item.draw_into_context(render_context, current_transform, current_clip_rect)
item.draw_into_context(render_context, current_transform, current_clip_stack)
}
debug!("Ending display list.");
}
@ -504,14 +504,19 @@ impl DisplayItem {
fn draw_into_context(&self,
render_context: &mut RenderContext,
current_transform: &Matrix2D<AzFloat>,
current_clip_rect: &Rect<Au>) {
current_clip_stack: &mut Vec<Rect<Au>>) {
// This should have been flattened to the content stacking level first.
assert!(self.base().level == ContentStackingLevel);
// TODO(pcwalton): This will need some tweaking to deal with more complex clipping regions.
let clip_rect = &self.base().clip_rect;
let need_to_clip = current_clip_rect != clip_rect;
if need_to_clip {
if current_clip_stack.len() == 0 || current_clip_stack.last().unwrap() != clip_rect {
while current_clip_stack.len() != 0 {
render_context.draw_pop_clip();
drop(current_clip_stack.pop());
}
render_context.draw_push_clip(clip_rect);
current_clip_stack.push(*clip_rect);
}
match *self {
@ -608,10 +613,6 @@ impl DisplayItem {
PseudoDisplayItemClass(_) => {}
}
if need_to_clip {
render_context.draw_pop_clip();
}
}
pub fn base<'a>(&'a self) -> &'a BaseDisplayItem {

View file

@ -100,14 +100,19 @@ pub struct Font {
}
impl Font {
pub fn shape_text(&mut self, text: String, is_whitespace: bool) -> Arc<GlyphStore> {
pub fn shape_text(&mut self, text: &str, is_whitespace: bool) -> Arc<GlyphStore> {
self.make_shaper();
let shaper = &self.shaper;
self.shape_cache.find_or_create(&text, |txt| {
let mut glyphs = GlyphStore::new(text.as_slice().char_len() as int, is_whitespace);
shaper.as_ref().unwrap().shape_text(txt.as_slice(), &mut glyphs);
Arc::new(glyphs)
})
match self.shape_cache.find_equiv(&text) {
None => {}
Some(glyphs) => return (*glyphs).clone(),
}
let mut glyphs = GlyphStore::new(text.char_len() as int, is_whitespace);
shaper.as_ref().unwrap().shape_text(text, &mut glyphs);
let glyphs = Arc::new(glyphs);
self.shape_cache.insert(text.to_string(), glyphs.clone());
glyphs
}
fn make_shaper<'a>(&'a mut self) -> &'a Shaper {

View file

@ -43,7 +43,7 @@ static SMALL_CAPS_SCALE_FACTOR: f64 = 0.8; // Matches FireFox (see gfxFont.
struct LayoutFontCacheEntry {
family: String,
font: Rc<RefCell<Font>>,
font: Option<Rc<RefCell<Font>>>,
}
struct FallbackFontCacheEntry {
@ -132,13 +132,21 @@ impl FontContext {
let mut cache_hit = false;
for cached_font_entry in self.layout_font_cache.iter() {
if cached_font_entry.family.as_slice() == family.name() {
let cached_font = cached_font_entry.font.borrow();
if cached_font.descriptor == desc &&
cached_font.requested_pt_size == style.font_size.to_subpx() &&
cached_font.variant == style.font_variant {
fonts.push(cached_font_entry.font.clone());
cache_hit = true;
break;
match cached_font_entry.font {
None => {
cache_hit = true;
break;
}
Some(ref cached_font_ref) => {
let cached_font = cached_font_ref.borrow();
if cached_font.descriptor == desc &&
cached_font.requested_pt_size == style.font_size.to_subpx() &&
cached_font.variant == style.font_variant {
fonts.push((*cached_font_ref).clone());
cache_hit = true;
break;
}
}
}
}
}
@ -156,11 +164,16 @@ impl FontContext {
let layout_font = Rc::new(RefCell::new(layout_font));
self.layout_font_cache.push(LayoutFontCacheEntry {
family: family.name().to_string(),
font: layout_font.clone(),
font: Some(layout_font.clone()),
});
fonts.push(layout_font);
}
None => {}
None => {
self.layout_font_cache.push(LayoutFontCacheEntry {
family: family.name().to_string(),
font: None,
});
}
}
}
}

View file

@ -25,14 +25,13 @@ use servo_msg::compositor_msg::{LayerMetadata, RenderListener, RenderingRenderSt
use servo_msg::constellation_msg::{ConstellationChan, Failure, FailureMsg, PipelineId};
use servo_msg::constellation_msg::{RendererReadyMsg};
use servo_msg::platform::surface::NativeSurfaceAzureMethods;
use servo_util::geometry::{Au, mod};
use servo_util::geometry;
use servo_util::opts;
use servo_util::smallvec::{SmallVec, SmallVec1};
use servo_util::task::spawn_named_with_send_on_failure;
use servo_util::time::{TimeProfilerChan, profile};
use servo_util::time;
use std::comm::{Receiver, Sender, channel};
use std::i32;
use sync::Arc;
use font_cache_task::FontCacheTask;
@ -359,9 +358,8 @@ impl<C:RenderListener + Send> RenderTask<C> {
None,
self.time_profiler_chan.clone(),
|| {
let clip_rect = Rect(Point2D(Au(i32::MIN), Au(i32::MIN)),
Size2D(Au(i32::MAX), Au(i32::MAX)));
display_list.draw_into_context(&mut ctx, &matrix, &clip_rect);
let mut clip_stack = Vec::new();
display_list.draw_into_context(&mut ctx, &matrix, &mut clip_stack);
ctx.draw_target.flush();
});
}

View file

@ -162,7 +162,7 @@ impl<'a> TextRun {
// Create a glyph store for this slice if it's nonempty.
if can_break_before && byte_i > byte_last_boundary {
let slice = text.slice(byte_last_boundary, byte_i).to_string();
let slice = text.slice(byte_last_boundary, byte_i);
debug!("creating glyph store for slice {} (ws? {}), {} - {} in run {}",
slice, !cur_slice_is_whitespace, byte_last_boundary, byte_i, text);
glyphs.push(GlyphRun {
@ -179,7 +179,7 @@ impl<'a> TextRun {
// Create a glyph store for the final slice if it's nonempty.
if byte_i > byte_last_boundary {
let slice = text.slice_from(byte_last_boundary).to_string();
let slice = text.slice_from(byte_last_boundary);
debug!("creating glyph store for final slice {} (ws? {}), {} - {} in run {}",
slice, cur_slice_is_whitespace, byte_last_boundary, text.len(), text);
glyphs.push(GlyphRun {

View file

@ -17,15 +17,17 @@ pub enum CompressionMode {
// High level TODOs:
//
// * Issue #113: consider incoming text state (arabic, etc)
// and propogate outgoing text state (dual of above)
// and propagate outgoing text state (dual of above)
//
// * Issue #114: record skipped and kept chars for mapping original to new text
//
// * Untracked: various edge cases for bidi, CJK, etc.
pub fn transform_text(text: &str, mode: CompressionMode,
pub fn transform_text(text: &str,
mode: CompressionMode,
incoming_whitespace: bool,
new_line_pos: &mut Vec<CharIndex>) -> (String, bool) {
let mut out_str = String::new();
output_text: &mut String,
new_line_pos: &mut Vec<CharIndex>)
-> bool {
let out_whitespace = match mode {
CompressNone | DiscardNewline => {
let mut new_line_index = CharIndex(0);
@ -46,7 +48,7 @@ pub fn transform_text(text: &str, mode: CompressionMode,
if ch != '\n' {
new_line_index = new_line_index + CharIndex(1);
}
out_str.push_char(ch);
output_text.push_char(ch);
}
}
text.len() > 0 && is_in_whitespace(text.char_at_reverse(0), mode)
@ -65,14 +67,14 @@ pub fn transform_text(text: &str, mode: CompressionMode,
// TODO: record skipped char
} else {
// TODO: record kept char
out_str.push_char(ch);
output_text.push_char(ch);
}
} else { /* next_in_whitespace; possibly add a space char */
if in_whitespace {
// TODO: record skipped char
} else {
// TODO: record kept char
out_str.push_char(' ');
output_text.push_char(' ');
}
}
// save whitespace context for next char
@ -82,7 +84,7 @@ pub fn transform_text(text: &str, mode: CompressionMode,
}
};
return (out_str, out_whitespace);
return out_whitespace;
fn is_in_whitespace(ch: char, mode: CompressionMode) -> bool {
match (ch, mode) {
@ -155,7 +157,8 @@ fn test_transform_compress_none() {
for test in test_strs.iter() {
let mut new_line_pos = vec!();
let (trimmed_str, _out) = transform_text(*test, mode, true, &mut new_line_pos);
let mut trimmed_str = String::new();
transform_text(*test, mode, true, &mut trimmed_str, &mut new_line_pos);
assert_eq!(trimmed_str.as_slice(), *test)
}
}
@ -187,7 +190,8 @@ fn test_transform_discard_newline() {
for (test, oracle) in test_strs.iter().zip(oracle_strs.iter()) {
let mut new_line_pos = vec!();
let (trimmed_str, _out) = transform_text(*test, mode, true, &mut new_line_pos);
let mut trimmed_str = String::new();
transform_text(*test, mode, true, &mut trimmed_str, &mut new_line_pos);
assert_eq!(trimmed_str.as_slice(), *oracle)
}
}
@ -279,7 +283,8 @@ fn test_transform_compress_whitespace_newline_no_incoming() {
for (test, oracle) in test_strs.iter().zip(oracle_strs.iter()) {
let mut new_line_pos = vec!();
let (trimmed_str, _out) = transform_text(*test, mode, false, &mut new_line_pos);
let mut trimmed_str = String::new();
transform_text(*test, mode, false, &mut trimmed_str, &mut new_line_pos);
assert_eq!(trimmed_str.as_slice(), *oracle)
}
}