diff --git a/src/aabbtree.rs b/src/aabbtree.rs index 05ee74ab2a..bf598c53e3 100644 --- a/src/aabbtree.rs +++ b/src/aabbtree.rs @@ -1,6 +1,7 @@ use euclid::{Point2D, Rect, Size2D}; use internal_types::{CompiledNode, DisplayItemKey}; use resource_list::ResourceList; +use std::mem; use types::NodeIndex; use util; @@ -43,6 +44,13 @@ impl AABBTreeNode { let key = DisplayItemKey::new(draw_list_index, item_index); self.src_items.push(key); } + + fn take_compiled_data_from(&mut self, other_aabb_tree_node: &mut AABBTreeNode) { + if self.compiled_node.is_none() && self.resource_list.is_none() { + mem::swap(&mut self.compiled_node, &mut other_aabb_tree_node.compiled_node); + mem::swap(&mut self.resource_list, &mut other_aabb_tree_node.resource_list); + } + } } pub struct AABBTree { @@ -228,4 +236,13 @@ impl AABBTree { self.check_node_visibility(NodeIndex(0), &rect); } } + + pub fn take_compiled_data_from(&mut self, other_aabb_tree: &mut AABBTree) { + debug_assert!(self.nodes.len() == other_aabb_tree.nodes.len()); + for (this_node, other_node) in self.nodes.iter_mut().zip(other_aabb_tree.nodes + .iter_mut()) { + this_node.take_compiled_data_from(other_node) + } + } } + diff --git a/src/layer.rs b/src/layer.rs index 299d9d8b27..f60803452d 100644 --- a/src/layer.rs +++ b/src/layer.rs @@ -47,4 +47,13 @@ impl Layer { let adjusted_viewport = viewport_rect.translate(&-self.scroll_offset); self.aabb_tree.cull(&adjusted_viewport); } + + pub fn take_compiled_data_from(&mut self, other_layer: &mut Layer) { + self.aabb_tree.take_compiled_data_from(&mut other_layer.aabb_tree) + } + + #[allow(dead_code)] + pub fn print(&self) { + self.aabb_tree.print(NodeIndex(0), 0); + } } diff --git a/src/render_backend.rs b/src/render_backend.rs index 8b5f83dd82..8723303dc6 100644 --- a/src/render_backend.rs +++ b/src/render_backend.rs @@ -347,10 +347,6 @@ impl Scene { debug_assert!(self.render_target_stack.len() == 0); self.pipeline_epoch_map.clear(); - for (_, layer) in &mut self.layers { - layer.reset(&mut self.pending_updates); - } - // Free any render targets from last frame. // TODO: This should really re-use existing targets here... for render_target in &mut self.render_targets { @@ -703,11 +699,11 @@ impl Scene { } } - fn build_layers(&mut self, scene_rect: &Rect) { + fn build_layers(&mut self, scene_rect: &Rect, can_reuse_old_batches: bool) { let _pf = util::ProfileScope::new(" build_layers"); - let old_layers = mem::replace(&mut self.layers, - HashMap::with_hash_state(Default::default())); + let mut old_layers = mem::replace(&mut self.layers, + HashMap::with_hash_state(Default::default())); // push all visible draw lists into aabb tree for (draw_list_index, flat_draw_list) in self.flat_draw_lists.iter_mut().enumerate() { @@ -735,6 +731,18 @@ impl Scene { item.node_index = layer.insert(&rect, draw_list_index, item_index); } } + + if can_reuse_old_batches { + for (scroll_layer_id, new_layer) in &mut self.layers { + if let Some(ref mut old_layer) = old_layers.get_mut(&scroll_layer_id) { + new_layer.take_compiled_data_from(old_layer) + } + } + } + + for (_, old_layer) in &mut old_layers { + old_layer.reset(&mut self.pending_updates) + } } fn build_frame(&mut self, @@ -1014,7 +1022,7 @@ impl Scene { (*native_font_handle).clone()); } } - job.result = font_context.get_glyph(job.glyph_key.font_key, + job.result = font_context.get_glyph(&job.glyph_key.font_key, job.glyph_key.size, job.glyph_key.index, device_pixel_ratio); @@ -1093,6 +1101,30 @@ impl Scene { println!("unable to find root scroll layer (may be an empty stacking context)"); } } + + fn draw_lists_are_identical(&self, old_draw_lists: &Vec) -> bool { + if self.flat_draw_lists.len() != old_draw_lists.len() { + return false + } + for (new_flat_draw_list, old_draw_list) in + self.flat_draw_lists.iter().zip(old_draw_lists.iter()) { + if new_flat_draw_list.draw_list.items.len() != old_draw_list.items.len() { + return false + } + for (new_flat_draw_item, old_flat_draw_item) in + new_flat_draw_list.draw_list + .items + .iter() + .zip(old_draw_list.items.iter()) { + if new_flat_draw_item.item != old_flat_draw_item.item || + new_flat_draw_item.rect != old_flat_draw_item.rect || + new_flat_draw_item.clip != old_flat_draw_item.clip { + return false + } + } + } + true + } } struct GlyphRasterJob { @@ -1331,9 +1363,11 @@ impl RenderBackend { backend } - fn remove_draw_list(&mut self, draw_list_id: Option) { + fn remove_and_save_draw_list(&mut self, + saved_old_draw_lists: &mut HashMap, + draw_list_id: Option) { if let Some(id) = draw_list_id { - self.draw_list_map.remove(&id).unwrap(); + saved_old_draw_lists.insert(id, self.draw_list_map.remove(&id).unwrap()); } } @@ -1421,14 +1455,8 @@ impl RenderBackend { ApiMsg::SetRootStackingContext(stacking_context, background_color, epoch, pipeline_id) => { let _pf = util::ProfileScope::new("SetRootStackingContext"); - // Return all current draw lists to the hash - for flat_draw_list in self.scene.flat_draw_lists.drain(..) { - if let Some(id) = flat_draw_list.id { - self.draw_list_map.insert(id, flat_draw_list.draw_list); - } - } - - // Remove any old draw lists and display lists for this pipeline + let old_draw_lists = mem::replace(&mut self.scene.flat_draw_lists, + Vec::new()); let old_display_list_keys: Vec<_> = self.display_list_map.iter() .filter(|&(_, ref v)| { v.pipeline_id == pipeline_id && @@ -1437,16 +1465,6 @@ impl RenderBackend { .map(|(k, _)| k.clone()) .collect(); - for key in &old_display_list_keys { - let display_list = self.display_list_map.remove(key).unwrap(); - self.remove_draw_list(display_list.background_and_borders_id); - self.remove_draw_list(display_list.block_backgrounds_and_borders_id); - self.remove_draw_list(display_list.floats_id); - self.remove_draw_list(display_list.content_id); - self.remove_draw_list(display_list.positioned_content_id); - self.remove_draw_list(display_list.outlines_id); - } - self.stacking_contexts.insert(pipeline_id, RootStackingContext { pipeline_id: pipeline_id, epoch: epoch, @@ -1454,21 +1472,17 @@ impl RenderBackend { stacking_context: stacking_context, }); - self.build_scene(); + self.build_scene(old_draw_lists, &old_display_list_keys[..]); self.render(&mut *notifier); } ApiMsg::SetRootPipeline(pipeline_id) => { let _pf = util::ProfileScope::new("SetRootPipeline"); - // Return all current draw lists to the hash - for flat_draw_list in self.scene.flat_draw_lists.drain(..) { - if let Some(id) = flat_draw_list.id { - self.draw_list_map.insert(id, flat_draw_list.draw_list); - } - } - + let old_draw_lists = mem::replace(&mut self.scene.flat_draw_lists, + Vec::new()); self.root_pipeline_id = Some(pipeline_id); - self.build_scene(); + self.build_scene(old_draw_lists, &[]); + self.render(&mut *notifier); } ApiMsg::Scroll(delta) => { @@ -1494,7 +1508,46 @@ impl RenderBackend { } } - fn build_scene(&mut self) { + fn build_scene(&mut self, + old_draw_lists: Vec, + old_display_list_keys: &[DisplayListID]) { + // Return all current draw lists to the hash + let mut old_draw_lists_or_ids = Vec::new(); + for flat_draw_list in old_draw_lists.into_iter() { + match flat_draw_list.id { + Some(id) => { + old_draw_lists_or_ids.push(DrawListOrId::Id(id)); + self.draw_list_map.insert(id, flat_draw_list.draw_list); + } + None => { + old_draw_lists_or_ids.push(DrawListOrId::DrawList(flat_draw_list.draw_list)); + } + } + } + + // Remove any old draw lists and display lists for this pipeline + let mut saved_old_draw_lists = HashMap::new(); + for key in old_display_list_keys { + let display_list = self.display_list_map.remove(key).unwrap(); + self.remove_and_save_draw_list(&mut saved_old_draw_lists, + display_list.background_and_borders_id); + self.remove_and_save_draw_list(&mut saved_old_draw_lists, + display_list.block_backgrounds_and_borders_id); + self.remove_and_save_draw_list(&mut saved_old_draw_lists, display_list.floats_id); + self.remove_and_save_draw_list(&mut saved_old_draw_lists, display_list.content_id); + self.remove_and_save_draw_list(&mut saved_old_draw_lists, + display_list.positioned_content_id); + self.remove_and_save_draw_list(&mut saved_old_draw_lists, display_list.outlines_id); + } + + // Recreate the old flat draw lists. + let old_draw_lists: Vec<_> = old_draw_lists_or_ids.into_iter().flat_map(|draw_list_or_id| { + match draw_list_or_id { + DrawListOrId::DrawList(draw_list) => Some(draw_list), + DrawListOrId::Id(id) => saved_old_draw_lists.remove(&id), + } + }).collect(); + // Flatten the stacking context hierarchy if let Some(root_pipeline_id) = self.root_pipeline_id { if let Some(root_sc) = self.stacking_contexts.get(&root_pipeline_id) { @@ -1519,12 +1572,22 @@ impl RenderBackend { &mut self.texture_cache, &root_sc.stacking_context.overflow); self.scene.pop_render_target(); + } + } + + let can_reuse_old_batches = old_draw_lists.len() > 0 && + self.scene.draw_lists_are_identical(&old_draw_lists); + if let Some(root_pipeline_id) = self.root_pipeline_id { + if let Some(root_sc) = self.stacking_contexts.get(&root_pipeline_id) { // Init the AABB culling tree(s) - self.scene.build_layers(&root_sc.stacking_context.overflow); + self.scene.build_layers(&root_sc.stacking_context.overflow, can_reuse_old_batches); // FIXME(pcwalton): This should be done somewhere else, probably when building the // layer. + let root_scroll_layer_id = root_sc.stacking_context + .scroll_layer_id + .expect("root layer must be a scroll layer!"); if let Some(root_scroll_layer) = self.scene.layers.get_mut(&root_scroll_layer_id) { root_scroll_layer.scroll_boundaries = root_sc.stacking_context.overflow.size; } @@ -3371,3 +3434,8 @@ fn compute_box_shadow_rect(box_bounds: &Rect, rect.inflate(spread_radius, spread_radius) } +enum DrawListOrId { + DrawList(DrawList), + Id(DrawListID), +} + diff --git a/src/types.rs b/src/types.rs index 03cf098e9e..3415276647 100644 --- a/src/types.rs +++ b/src/types.rs @@ -30,7 +30,7 @@ pub enum ImageFormat { RGBA8, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] pub struct ColorF { pub r: f32, pub g: f32, @@ -110,20 +110,20 @@ pub enum BoxShadowClipMode { Inset, } -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub struct BorderSide { pub width: f32, pub color: ColorF, pub style: BorderStyle, } -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub struct GradientStop { pub offset: f32, pub color: ColorF, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] pub struct BorderRadius { pub top_left: Size2D, pub top_right: Size2D, @@ -145,7 +145,7 @@ pub enum BorderStyle { Outset, } -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub struct GlyphInstance { pub index: u32, pub x: f32, @@ -204,7 +204,7 @@ impl StackingContext { } } -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub struct TextDisplayItem { pub glyphs: Vec, pub font_key: FontKey, @@ -213,18 +213,18 @@ pub struct TextDisplayItem { pub blur_radius: Au, } -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub struct ImageDisplayItem { pub image_key: ImageKey, pub stretch_size: Size2D, } -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub struct RectangleDisplayItem { pub color: ColorF, } -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub struct BorderDisplayItem { pub left: BorderSide, pub right: BorderSide, @@ -255,7 +255,7 @@ impl BorderDisplayItem { } } -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub struct BoxShadowDisplayItem { pub box_bounds: Rect, pub offset: Point2D, @@ -266,32 +266,32 @@ pub struct BoxShadowDisplayItem { pub clip_mode: BoxShadowClipMode, } -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub struct GradientDisplayItem { pub start_point: Point2D, pub end_point: Point2D, pub stops: Vec, } -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub struct IframeDisplayItem { pub iframe: PipelineId, } -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub struct ClearDisplayItem { pub clear_color: bool, pub clear_z: bool, pub clear_stencil: bool, } -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub struct CompositeDisplayItem { pub texture_id: RenderTargetID, pub operation: CompositionOp, } -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum SpecificDisplayItem { Rectangle(RectangleDisplayItem), Text(TextDisplayItem), @@ -306,7 +306,7 @@ pub enum SpecificDisplayItem { Clear(ClearDisplayItem), } -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub struct DisplayItem { pub item: SpecificDisplayItem, pub rect: Rect, @@ -582,7 +582,25 @@ pub trait RenderNotifier : Send { fn new_frame_ready(&mut self); } -#[derive(Debug, Clone)] +#[derive(PartialEq, Eq, Hash, Clone, Debug)] +pub struct Glyph { + pub size: Au, + pub blur_radius: Au, + pub index: u32, +} + +impl Glyph { + #[inline] + pub fn new(size: Au, blur_radius: Au, index: u32) -> Glyph { + Glyph { + size: size, + blur_radius: blur_radius, + index: index, + } + } +} + +#[derive(Debug, Clone, PartialEq)] pub struct ClipRegion { pub main: Rect, pub complex: Vec, @@ -597,7 +615,7 @@ impl ClipRegion { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct ComplexClipRegion { /// The boundaries of the rectangle. pub rect: Rect, @@ -620,7 +638,7 @@ pub enum ScrollPolicy { Fixed, } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub enum MixBlendMode { Normal, Multiply, @@ -640,7 +658,7 @@ pub enum MixBlendMode { Luminosity, } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub enum LowLevelFilterOp { Blur(Au, BlurDirection), Brightness(f32), @@ -672,7 +690,7 @@ pub enum BlurDirection { Vertical, } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub enum CompositionOp { MixBlend(MixBlendMode), Filter(LowLevelFilterOp),