From cb07e6ee54a2b29cd78d0c438c08f072d5ee456d Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 5 Nov 2015 19:06:08 -0800 Subject: [PATCH] Reuse batches across paints on a per-node basis. This didn't really improve performance from what I saw, but it's a cleaner and more general optimization than the one we had previously, which only reused batches if the entire display list didn't change. In any case, it's a precursor to more aggressive optimizations we could do to reuse portions of batches within AABB tree nodes. --- Cargo.toml | 1 + src/aabbtree.rs | 97 ++++++++++++++++++++++++++++++++++++++----- src/layer.rs | 12 ++++-- src/lib.rs | 3 ++ src/render_backend.rs | 45 ++++---------------- src/types.rs | 6 +++ 6 files changed, 115 insertions(+), 49 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index eeab2aad4b..b35bd226c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ serde_macros = "0.5" fnv="*" scoped_threadpool = "0.1.6" app_units = "0.1" +log = "*" simd = { git = "https://github.com/huonw/simd" } [target.x86_64-apple-darwin.dependencies] diff --git a/src/aabbtree.rs b/src/aabbtree.rs index bf598c53e3..f7cb45288a 100644 --- a/src/aabbtree.rs +++ b/src/aabbtree.rs @@ -1,8 +1,9 @@ use euclid::{Point2D, Rect, Size2D}; use internal_types::{CompiledNode, DisplayItemKey}; +use render_backend::FlatDrawList; use resource_list::ResourceList; use std::mem; -use types::NodeIndex; +use types::{DrawList, NodeIndex}; use util; pub struct AABBTreeNode { @@ -45,11 +46,55 @@ impl AABBTreeNode { 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); + fn reuse_compiled_data_from_old_node_if_possible(&mut self, + old_aabb_tree_node: &mut AABBTreeNode, + these_draw_lists: &Vec, + old_draw_lists: &Vec) { + if self.compiled_node.is_some() || self.resource_list.is_some() { + debug!("couldn't reuse batch: already compiled ({:?})", self.src_items.len()); + return } + + if self.src_items.len() != old_aabb_tree_node.src_items.len() { + debug!("couldn't reuse batch: different numbers of items ({:?})", + self.src_items.len()); + return + } + + if old_aabb_tree_node.compiled_node.is_none() { + debug!("couldn't reuse batch: no old compiled node ({:?})", + self.src_items.len()); + return + } + + for (this_item_key, old_item_key) in self.src_items.iter().zip(old_aabb_tree_node.src_items + .iter()) { + let this_draw_list_index = this_item_key.draw_list_index.0 as usize; + let old_draw_list_index = old_item_key.draw_list_index.0 as usize; + debug_assert!(this_draw_list_index < these_draw_lists.len()); + if old_draw_list_index >= old_draw_lists.len() { + debug!("couldn't reuse batch: old draw list no longer present ({})", + self.src_items.len()); + return + } + + let this_draw_list = &these_draw_lists[this_draw_list_index]; + let old_draw_list = &old_draw_lists[old_draw_list_index]; + let this_index = this_item_key.item_index.0 as usize; + let old_index = old_item_key.item_index.0 as usize; + debug_assert!(this_index < this_draw_list.draw_list.items.len()); + debug_assert!(old_index < old_draw_list.items.len()); + if !this_draw_list.draw_list.items[this_index].is_identical_to( + &old_draw_list.items[old_index]) { + debug!("couldn't reuse batch: different numbers of items ({:?})", + self.src_items.len()); + return + } + } + + debug!("reusing batch! ({})", self.src_items.len()); + mem::swap(&mut self.compiled_node, &mut old_aabb_tree_node.compiled_node); + mem::swap(&mut self.resource_list, &mut old_aabb_tree_node.resource_list); } } @@ -237,12 +282,44 @@ impl AABBTree { } } - 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) + fn reuse_compiled_data_from_old_nodes_if_possible(&mut self, + old_aabb_tree: &mut AABBTree, + these_draw_lists: &Vec, + old_draw_lists: &Vec, + this_index: NodeIndex, + old_index: NodeIndex) { + let child_indices = { + let this_node = self.node_mut(this_index); + let old_node = old_aabb_tree.node_mut(old_index); + this_node.reuse_compiled_data_from_old_node_if_possible(old_node, + these_draw_lists, + old_draw_lists); + (this_node.children, old_node.children) + }; + + if let (Some(this_child_index), Some(old_child_index)) = child_indices { + self.reuse_compiled_data_from_old_nodes_if_possible(old_aabb_tree, + these_draw_lists, + old_draw_lists, + this_child_index, + old_child_index); + self.reuse_compiled_data_from_old_nodes_if_possible(old_aabb_tree, + these_draw_lists, + old_draw_lists, + NodeIndex(this_child_index.0 + 1), + NodeIndex(old_child_index.0 + 1)); } } + + pub fn reuse_compiled_data_from_old_tree_if_possible(&mut self, + old_aabb_tree: &mut AABBTree, + these_draw_lists: &Vec, + old_draw_lists: &Vec) { + self.reuse_compiled_data_from_old_nodes_if_possible(old_aabb_tree, + these_draw_lists, + old_draw_lists, + NodeIndex(0), + NodeIndex(0)); + } } diff --git a/src/layer.rs b/src/layer.rs index f60803452d..5ed801081a 100644 --- a/src/layer.rs +++ b/src/layer.rs @@ -1,7 +1,8 @@ use aabbtree::AABBTree; use euclid::{Point2D, Rect, Size2D}; use internal_types::{BatchUpdate, BatchUpdateList, BatchUpdateOp}; -use types::NodeIndex; +use render_backend::FlatDrawList; +use types::{DrawList, NodeIndex}; pub struct Layer { // TODO: Remove pub from here if possible in the future @@ -48,8 +49,13 @@ impl Layer { 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) + pub fn reuse_compiled_data_from_old_layer_if_possible(&mut self, + other_layer: &mut Layer, + these_draw_lists: &Vec, + old_draw_lists: &Vec) { + self.aabb_tree.reuse_compiled_data_from_old_tree_if_possible(&mut other_layer.aabb_tree, + these_draw_lists, + old_draw_lists) } #[allow(dead_code)] diff --git a/src/lib.rs b/src/lib.rs index 522f232d66..b5d00a764f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,9 @@ #![feature(hashmap_hasher)] #![feature(vec_push_all)] +#[macro_use] +extern crate log; + extern crate app_units; extern crate euclid; extern crate fnv; diff --git a/src/render_backend.rs b/src/render_backend.rs index c71fc444e6..0606e2526c 100644 --- a/src/render_backend.rs +++ b/src/render_backend.rs @@ -201,7 +201,7 @@ impl StackingContextHelpers for StackingContext { } #[derive(Clone)] -struct DrawContext { +pub struct DrawContext { render_target_index: RenderTargetIndex, overflow: Rect, device_pixel_ratio: f32, @@ -209,7 +209,7 @@ struct DrawContext { scroll_layer_id: ScrollLayerId, } -struct FlatDrawList { +pub struct FlatDrawList { pub id: Option, pub draw_context: DrawContext, pub draw_list: DrawList, @@ -697,7 +697,7 @@ impl Scene { } } - fn build_layers(&mut self, scene_rect: &Rect, can_reuse_old_batches: bool) { + fn build_layers(&mut self, scene_rect: &Rect, old_draw_lists: &Vec) { let _pf = util::ProfileScope::new(" build_layers"); let mut old_layers = mem::replace(&mut self.layers, @@ -730,11 +730,11 @@ impl Scene { } } - 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 (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.reuse_compiled_data_from_old_layer_if_possible(old_layer, + &mut self.flat_draw_lists, + old_draw_lists) } } @@ -961,30 +961,6 @@ 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 DrawCommandBuilder { @@ -1408,13 +1384,10 @@ impl RenderBackend { } } - 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, can_reuse_old_batches); + self.scene.build_layers(&root_sc.stacking_context.overflow, &old_draw_lists); // FIXME(pcwalton): This should be done somewhere else, probably when building the // layer. diff --git a/src/types.rs b/src/types.rs index 3415276647..fcf7dc8ae4 100644 --- a/src/types.rs +++ b/src/types.rs @@ -315,6 +315,12 @@ pub struct DisplayItem { pub node_index: Option, } +impl DisplayItem { + pub fn is_identical_to(&self, other: &DisplayItem) -> bool { + self.item == other.item && self.rect == other.rect && self.clip == other.clip + } +} + pub enum DisplayListMode { Default, PseudoFloat,