From 368251e4fd9d6f66b5f9cfcc0968a45edd772c06 Mon Sep 17 00:00:00 2001 From: Matt Woodrow Date: Tue, 25 Sep 2018 13:43:51 +1200 Subject: [PATCH 1/3] Estimate the number of primitives that will be created during flattening and pre-allocate the vector to that size --- webrender/src/display_list_flattener.rs | 7 +++++++ webrender_api/src/display_list.rs | 12 ++++++++++++ 2 files changed, 19 insertions(+) diff --git a/webrender/src/display_list_flattener.rs b/webrender/src/display_list_flattener.rs index 43ed734789..3951621d0f 100644 --- a/webrender/src/display_list_flattener.rs +++ b/webrender/src/display_list_flattener.rs @@ -162,6 +162,9 @@ pub struct DisplayListFlattener<'a> { /// Reference to the clip interner for this document. clip_interner: &'a mut ClipDataInterner, + + /// The estimated count of primtives we expect to encounter during flattening. + prim_count_estimate: usize, } impl<'a> DisplayListFlattener<'a> { @@ -201,6 +204,7 @@ impl<'a> DisplayListFlattener<'a> { clip_store: ClipStore::new(), picture_id_generator, clip_interner, + prim_count_estimate: 0, }; flattener.push_root( @@ -286,6 +290,9 @@ impl<'a> DisplayListFlattener<'a> { } } + self.prim_count_estimate += pipeline.display_list.prim_count_estimate(); + self.prim_store.primitives.reserve(self.prim_count_estimate); + self.flatten_items(&mut pipeline.display_list.iter(), pipeline_id, LayoutVector2D::zero()); if self.config.enable_scrollbars { diff --git a/webrender_api/src/display_list.rs b/webrender_api/src/display_list.rs index 94af6e17de..fdd3b9032a 100644 --- a/webrender_api/src/display_list.rs +++ b/webrender_api/src/display_list.rs @@ -88,6 +88,8 @@ pub struct BuiltDisplayListDescriptor { total_clip_nodes: usize, /// The amount of spatial nodes created while building this display list. total_spatial_nodes: usize, + /// An estimate of the number of primitives that will be created by this display list. + prim_count_estimate: usize, } pub struct BuiltDisplayListIter<'a> { @@ -145,6 +147,10 @@ impl BuiltDisplayList { &self.descriptor } + pub fn prim_count_estimate(&self) -> usize { + self.descriptor.prim_count_estimate + } + pub fn times(&self) -> (u64, u64, u64) { ( self.descriptor.builder_start_time, @@ -603,6 +609,7 @@ impl<'de> Deserialize<'de> for BuiltDisplayList { send_start_time: 0, total_clip_nodes, total_spatial_nodes, + prim_count_estimate: 0, }, }) } @@ -839,6 +846,7 @@ pub struct DisplayListBuilder { clip_stack: Vec, next_clip_index: usize, next_spatial_index: usize, + prim_count_estimate: usize, next_clip_chain_id: u64, builder_start_time: u64, @@ -868,6 +876,7 @@ impl DisplayListBuilder { ], next_clip_index: FIRST_CLIP_NODE_INDEX, next_spatial_index: FIRST_SPATIAL_NODE_INDEX, + prim_count_estimate: 0, next_clip_chain_id: 0, builder_start_time: start_time, content_size, @@ -953,6 +962,7 @@ impl DisplayListBuilder { /// display items. Pushing unexpected or invalid items here may /// result in WebRender panicking or behaving in unexpected ways. pub fn push_item(&mut self, item: SpecificDisplayItem, info: &LayoutPrimitiveInfo) { + self.prim_count_estimate += 1; serialize_fast( &mut self.data, &DisplayItem { @@ -969,6 +979,7 @@ impl DisplayListBuilder { info: &LayoutPrimitiveInfo, scrollinfo: ClipAndScrollInfo ) { + self.prim_count_estimate += 1; serialize_fast( &mut self.data, &DisplayItem { @@ -1501,6 +1512,7 @@ impl DisplayListBuilder { send_start_time: 0, total_clip_nodes: self.next_clip_index, total_spatial_nodes: self.next_spatial_index, + prim_count_estimate: self.prim_count_estimate, }, data: self.data, }, From b7c77e1077335c9721931847f58e7306c16dec78 Mon Sep 17 00:00:00 2001 From: Matt Woodrow Date: Tue, 25 Sep 2018 13:54:15 +1200 Subject: [PATCH 2/3] Share a single SegmentBuilder for the entire frame build so that we can reuse the items vector --- webrender/src/frame_builder.rs | 3 ++ webrender/src/prim_store.rs | 4 +-- webrender/src/segment.rs | 63 ++++++++++++++++++++++------------ 3 files changed, 47 insertions(+), 23 deletions(-) diff --git a/webrender/src/frame_builder.rs b/webrender/src/frame_builder.rs index 8741ab2541..e26c5008f8 100644 --- a/webrender/src/frame_builder.rs +++ b/webrender/src/frame_builder.rs @@ -19,6 +19,7 @@ use render_backend::FrameId; use render_task::{RenderTask, RenderTaskId, RenderTaskLocation, RenderTaskTree}; use resource_cache::{ResourceCache}; use scene::{ScenePipeline, SceneProperties}; +use segment::SegmentBuilder; use spatial_node::SpatialNode; use std::f32; use std::sync::Arc; @@ -85,6 +86,7 @@ pub struct FrameBuildingState<'a> { pub special_render_passes: &'a mut SpecialRenderPasses, pub transforms: &'a mut TransformPalette, pub clip_data_store: &'a mut ClipDataStore, + pub segment_builder: SegmentBuilder, } pub struct PictureContext { @@ -224,6 +226,7 @@ impl FrameBuilder { special_render_passes, transforms: transform_palette, clip_data_store, + segment_builder: SegmentBuilder::new(), }; let prim_context = PrimitiveContext::new( diff --git a/webrender/src/prim_store.rs b/webrender/src/prim_store.rs index 2c7b026663..ad1249e69a 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -29,7 +29,6 @@ use render_task::{RenderTaskCacheKeyKind, RenderTaskId, RenderTaskCacheEntryHand use renderer::{MAX_VERTEX_TEXTURE_WIDTH}; use resource_cache::{ImageProperties, ImageRequest, ResourceCache}; use scene::SceneProperties; -use segment::SegmentBuilder; use std::{cmp, fmt, mem, usize}; use util::{ScaleOffset, MatrixHelpers, pack_as_float, project_rect, raster_rect_to_device_pixels}; @@ -2079,7 +2078,8 @@ fn write_brush_segment_description( // the clip sources here. let mut rect_clips_only = true; - let mut segment_builder = SegmentBuilder::new( + let segment_builder = &mut frame_state.segment_builder; + segment_builder.initialize( metadata.local_rect, None, metadata.local_clip_rect diff --git a/webrender/src/segment.rs b/webrender/src/segment.rs index 4b7ebd9657..23c3a4f56d 100644 --- a/webrender/src/segment.rs +++ b/webrender/src/segment.rs @@ -176,26 +176,41 @@ pub struct SegmentBuilder { items: Vec, inner_rect: Option, bounding_rect: Option, + + #[cfg(debug_assertions)] + initialized: bool, } impl SegmentBuilder { // Create a new segment builder, supplying the primitive // local rect and associated local clip rect. - pub fn new( + pub fn new() -> SegmentBuilder { + SegmentBuilder { + items: Vec::with_capacity(4), + bounding_rect: None, + inner_rect: None, + #[cfg(debug_assertions)] + initialized: false, + } + } + + pub fn initialize( + &mut self, local_rect: LayoutRect, inner_rect: Option, local_clip_rect: LayoutRect, - ) -> SegmentBuilder { - let mut builder = SegmentBuilder { - items: Vec::new(), - bounding_rect: Some(local_rect), - inner_rect, - }; - - builder.push_clip_rect(local_rect, None, ClipMode::Clip); - builder.push_clip_rect(local_clip_rect, None, ClipMode::Clip); - - builder + ) { + self.items.clear(); + self.inner_rect = inner_rect; + self.bounding_rect = Some(local_rect); + + self.push_clip_rect(local_rect, None, ClipMode::Clip); + self.push_clip_rect(local_clip_rect, None, ClipMode::Clip); + + #[cfg(debug_assertions)] + { + self.initialized = true; + } } // Push a region defined by an inner and outer rect where there @@ -389,23 +404,23 @@ impl SegmentBuilder { } // Consume this segment builder and produce a list of segments. - pub fn build(self, mut f: F) where F: FnMut(&Segment) { + pub fn build(&mut self, mut f: F) where F: FnMut(&Segment) { + #[cfg(debug_assertions)] + debug_assert!(self.initialized); let bounding_rect = match self.bounding_rect { Some(bounding_rect) => bounding_rect, None => return, }; - let mut items = self.items; - // First, filter out any items that don't intersect // with the visible bounding rect. - items.retain(|item| item.rect.intersects(&bounding_rect)); + self.items.retain(|item| item.rect.intersects(&bounding_rect)); // Create events for each item let mut x_events = Vec::new(); let mut y_events = Vec::new(); - for (item_index, item) in items.iter().enumerate() { + for (item_index, item) in self.items.iter().enumerate() { let p0 = item.rect.origin; let p1 = item.rect.bottom_right(); @@ -478,7 +493,7 @@ impl SegmentBuilder { cur_y, region_x, region_y, - &items, + &self.items, )); prev_x = cur_x; @@ -489,7 +504,7 @@ impl SegmentBuilder { ex.update( ItemFlags::X_ACTIVE, - &mut items, + &mut self.items, &mut region_x, ); } @@ -500,7 +515,7 @@ impl SegmentBuilder { ey.update( ItemFlags::Y_ACTIVE, - &mut items, + &mut self.items, &mut region_y, ); } @@ -530,6 +545,11 @@ impl SegmentBuilder { } } } + + #[cfg(debug_assertions)] + { + self.initialized = false; + } } } @@ -650,7 +670,8 @@ mod test { clips: &[(LayoutRect, Option, ClipMode)], expected_segments: &mut [Segment] ) { - let mut sb = SegmentBuilder::new( + let mut sb = SegmentBuilder::new(); + sb.initialize( local_rect, inner_rect, local_clip_rect, From f10cd10825faa9a4cdbd592e22d746ae1474c4d0 Mon Sep 17 00:00:00 2001 From: Matt Woodrow Date: Tue, 25 Sep 2018 14:16:37 +1200 Subject: [PATCH 3/3] Use SmallVec for some Vecs used during frame building that are usually small and don't need an allocation --- webrender/src/border.rs | 10 +++++----- webrender/src/display_list_flattener.rs | 6 +++--- webrender/src/prim_store.rs | 9 ++++++--- webrender/src/segment.rs | 7 ++++--- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/webrender/src/border.rs b/webrender/src/border.rs index 96507691d7..106ad1267b 100644 --- a/webrender/src/border.rs +++ b/webrender/src/border.rs @@ -11,7 +11,7 @@ use ellipse::Ellipse; use euclid::SideOffsets2D; use display_list_flattener::DisplayListFlattener; use gpu_types::{BorderInstance, BorderSegment, BrushFlags}; -use prim_store::{BrushKind, BrushPrimitive, BrushSegment}; +use prim_store::{BrushKind, BrushPrimitive, BrushSegment, BrushSegmentVec}; use prim_store::{EdgeAaSegmentMask, PrimitiveContainer, ScrollNodeAndClipChain}; use util::{lerp, RectHelpers}; @@ -682,7 +682,7 @@ impl BorderRenderTaskInfo { border: &NormalBorder, widths: &LayoutSideOffsets, scale: LayoutToDeviceScale, - brush_segments: &mut Vec, + brush_segments: &mut BrushSegmentVec, ) -> Option { let mut border_segments = Vec::new(); @@ -1062,7 +1062,7 @@ fn add_brush_segment( task_rect: DeviceRect, brush_flags: BrushFlags, edge_flags: EdgeAaSegmentMask, - brush_segments: &mut Vec, + brush_segments: &mut BrushSegmentVec, ) { if image_rect.size.width <= 0. || image_rect.size.width <= 0. { return; @@ -1212,7 +1212,7 @@ fn add_corner_segment( segment: BorderSegment, edge_flags: EdgeAaSegmentMask, border_segments: &mut Vec, - brush_segments: &mut Vec, + brush_segments: &mut BrushSegmentVec, ) { if side0.color.a <= 0.0 && side1.color.a <= 0.0 { return; @@ -1250,7 +1250,7 @@ fn add_edge_segment( edge_flags: EdgeAaSegmentMask, border_segments: &mut Vec, brush_flags: BrushFlags, - brush_segments: &mut Vec, + brush_segments: &mut BrushSegmentVec, ) { if side.color.a <= 0.0 { return; diff --git a/webrender/src/display_list_flattener.rs b/webrender/src/display_list_flattener.rs index 3951621d0f..981d4d908a 100644 --- a/webrender/src/display_list_flattener.rs +++ b/webrender/src/display_list_flattener.rs @@ -26,7 +26,7 @@ use internal_types::{FastHashMap, FastHashSet}; use picture::{PictureCompositeMode, PictureIdGenerator, PicturePrimitive}; use prim_store::{BrushKind, BrushPrimitive, BrushSegmentDescriptor}; use prim_store::{EdgeAaSegmentMask, ImageSource, PrimitiveOpacity}; -use prim_store::{BorderSource, BrushSegment, PrimitiveContainer, PrimitiveIndex, PrimitiveStore}; +use prim_store::{BorderSource, BrushSegment, BrushSegmentVec, PrimitiveContainer, PrimitiveIndex, PrimitiveStore}; use prim_store::{OpacityBinding, ScrollNodeAndClipChain, TextRunPrimitive}; use render_backend::{DocumentView}; use resource_cache::{FontInstanceMap, ImageRequest}; @@ -1648,7 +1648,7 @@ impl<'a> DisplayListFlattener<'a> { let br_inner = br_outer - vec2(border_item.widths.right, border_item.widths.bottom); fn add_segment( - segments: &mut Vec, + segments: &mut BrushSegmentVec, rect: LayoutRect, uv_rect: TexelRect, repeat_horizontal: RepeatMode, @@ -1687,7 +1687,7 @@ impl<'a> DisplayListFlattener<'a> { } // Build the list of image segments - let mut segments = vec![]; + let mut segments = BrushSegmentVec::new(); // Top left add_segment( diff --git a/webrender/src/prim_store.rs b/webrender/src/prim_store.rs index ad1249e69a..e209c38a29 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -31,6 +31,7 @@ use resource_cache::{ImageProperties, ImageRequest, ResourceCache}; use scene::SceneProperties; use std::{cmp, fmt, mem, usize}; use util::{ScaleOffset, MatrixHelpers, pack_as_float, project_rect, raster_rect_to_device_pixels}; +use smallvec::SmallVec; const MIN_BRUSH_SPLIT_AREA: f32 = 256.0 * 256.0; @@ -540,9 +541,11 @@ impl BrushSegment { } } +pub type BrushSegmentVec = SmallVec<[BrushSegment; 8]>; + #[derive(Debug)] pub struct BrushSegmentDescriptor { - pub segments: Vec, + pub segments: BrushSegmentVec, } #[derive(Debug)] @@ -2188,7 +2191,7 @@ fn write_brush_segment_description( // patterns of this and the segment // builder significantly better, by // retaining it across primitives. - let mut segments = Vec::new(); + let mut segments = BrushSegmentVec::new(); segment_builder.build(|segment| { segments.push( @@ -2880,7 +2883,7 @@ impl Primitive { // size in cache_key. let needs_update = scale_au != cache_key.scale; - let mut new_segments = Vec::new(); + let mut new_segments = BrushSegmentVec::new(); let local_rect = &self.metadata.local_rect; if needs_update { diff --git a/webrender/src/segment.rs b/webrender/src/segment.rs index 23c3a4f56d..583db384a7 100644 --- a/webrender/src/segment.rs +++ b/webrender/src/segment.rs @@ -7,6 +7,7 @@ use app_units::Au; use prim_store::EdgeAaSegmentMask; use std::{cmp, usize}; use util::{extract_inner_rect_safe, RectHelpers}; +use smallvec::SmallVec; bitflags! { pub struct ItemFlags: u8 { @@ -417,8 +418,8 @@ impl SegmentBuilder { self.items.retain(|item| item.rect.intersects(&bounding_rect)); // Create events for each item - let mut x_events = Vec::new(); - let mut y_events = Vec::new(); + let mut x_events : SmallVec<[Event; 4]> = SmallVec::new(); + let mut y_events : SmallVec<[Event; 4]> = SmallVec::new(); for (item_index, item) in self.items.iter().enumerate() { let p0 = item.rect.origin; @@ -471,7 +472,7 @@ impl SegmentBuilder { let mut prev_y = clamp(p0.y, y_events[0].value, p1.y); let mut region_y = 0; - let mut segments = Vec::new(); + let mut segments : SmallVec<[_; 4]> = SmallVec::new(); let mut x_count = 0; let mut y_count = 0;