diff --git a/gfx/wr/webrender/src/clip.rs b/gfx/wr/webrender/src/clip.rs index 6dc298bdf327..561e2ffba985 100644 --- a/gfx/wr/webrender/src/clip.rs +++ b/gfx/wr/webrender/src/clip.rs @@ -17,7 +17,7 @@ use crate::prim_store::{ClipData, ImageMaskData, SpaceMapper, VisibleMaskImageTi use crate::prim_store::{PointKey, SizeKey, RectangleKey}; use crate::render_task::to_cache_size; use crate::resource_cache::{ImageRequest, ResourceCache}; -use std::{cmp, u32}; +use std::{cmp, ops, u32}; use crate::util::{extract_inner_rect_safe, project_rect, ScaleOffset}; /* @@ -240,6 +240,18 @@ pub struct ClipNodeRange { pub count: u32, } +impl ClipNodeRange { + fn to_range(&self) -> ops::Range { + let start = self.first as usize; + let end = start + self.count as usize; + + ops::Range { + start, + end, + } + } +} + /// A helper struct for converting between coordinate systems /// of clip sources and primitives. // todo(gw): optimize: @@ -254,6 +266,38 @@ enum ClipSpaceConversion { Transform(LayoutToWorldTransform), } +impl ClipSpaceConversion { + /// Construct a new clip space converter between two spatial nodes. + fn new( + prim_spatial_node_index: SpatialNodeIndex, + clip_spatial_node_index: SpatialNodeIndex, + clip_scroll_tree: &ClipScrollTree, + ) -> Self { + //Note: this code is different from `get_relative_transform` in a way that we only try + // getting the relative transform if it's Local or ScaleOffset, + // falling back to the world transform otherwise. + let clip_spatial_node = &clip_scroll_tree + .spatial_nodes[clip_spatial_node_index.0 as usize]; + let prim_spatial_node = &clip_scroll_tree + .spatial_nodes[prim_spatial_node_index.0 as usize]; + + if prim_spatial_node_index == clip_spatial_node_index { + ClipSpaceConversion::Local + } else if prim_spatial_node.coordinate_system_id == clip_spatial_node.coordinate_system_id { + let scale_offset = prim_spatial_node.coordinate_system_relative_scale_offset + .inverse() + .accumulate(&clip_spatial_node.coordinate_system_relative_scale_offset); + ClipSpaceConversion::ScaleOffset(scale_offset) + } else { + ClipSpaceConversion::Transform( + clip_scroll_tree + .get_world_transform(clip_spatial_node_index) + .into_transform() + ) + } + } +} + // Temporary information that is cached and reused // during building of a clip chain instance. #[derive(MallocSizeOf)] @@ -464,7 +508,9 @@ impl ClipNode { pub struct ClipStore { pub clip_chain_nodes: Vec, clip_node_instances: Vec, - clip_node_info: Vec, + + active_clip_node_info: Vec, + active_local_clip_rect: Option, } // A clip chain instance is what gets built for a given clip @@ -550,7 +596,9 @@ impl ClipStore { ClipStore { clip_chain_nodes: Vec::new(), clip_node_instances: Vec::new(), - clip_node_info: Vec::new(), + + active_clip_node_info: Vec::new(), + active_local_clip_rect: None, } } @@ -585,31 +633,20 @@ impl ClipStore { &self.clip_node_instances[(node_range.first + index) as usize] } - // The main interface other code uses. Given a local primitive, positioning - // information, and a clip chain id, build an optimized clip chain instance. - pub fn build_clip_chain_instance( + /// Setup the active clip chains for building a clip chain instance. + pub fn set_active_clips( &mut self, - clip_chains: &[ClipChainId], - local_prim_rect: LayoutRect, local_prim_clip_rect: LayoutRect, spatial_node_index: SpatialNodeIndex, - prim_to_pic_mapper: &SpaceMapper, - pic_to_world_mapper: &SpaceMapper, + clip_chains: &[ClipChainId], clip_scroll_tree: &ClipScrollTree, - gpu_cache: &mut GpuCache, - resource_cache: &mut ResourceCache, - device_pixel_scale: DevicePixelScale, - world_rect: &WorldRect, clip_data_store: &mut ClipDataStore, - request_resources: bool, - ) -> Option { + ) { + self.active_clip_node_info.clear(); + self.active_local_clip_rect = None; + let mut local_clip_rect = local_prim_clip_rect; - // Walk the clip chain to build local rects, and collect the - // smallest possible local/device clip area. - - self.clip_node_info.clear(); - for clip_chain_root in clip_chains { let mut current_clip_chain_id = *clip_chain_root; @@ -621,17 +658,71 @@ impl ClipStore { clip_chain_node, spatial_node_index, &mut local_clip_rect, - &mut self.clip_node_info, + &mut self.active_clip_node_info, clip_data_store, clip_scroll_tree, ) { - return None; + return; } current_clip_chain_id = clip_chain_node.parent_clip_chain_id; } } + self.active_local_clip_rect = Some(local_clip_rect); + } + + /// Setup the active clip chains, based on an existing primitive clip chain instance. + pub fn set_active_clips_from_clip_chain( + &mut self, + prim_clip_chain: &ClipChainInstance, + prim_spatial_node_index: SpatialNodeIndex, + clip_scroll_tree: &ClipScrollTree, + ) { + // TODO(gw): Although this does less work than set_active_clips(), it does + // still do some unnecessary work (such as the clip space conversion). + // We could consider optimizing this if it ever shows up in a profile. + + self.active_clip_node_info.clear(); + self.active_local_clip_rect = Some(prim_clip_chain.local_clip_rect); + + let clip_instances = &self + .clip_node_instances[prim_clip_chain.clips_range.to_range()]; + for clip_instance in clip_instances { + let conversion = ClipSpaceConversion::new( + prim_spatial_node_index, + clip_instance.spatial_node_index, + clip_scroll_tree, + ); + self.active_clip_node_info.push(ClipNodeInfo { + handle: clip_instance.handle, + local_pos: clip_instance.local_pos, + spatial_node_index: clip_instance.spatial_node_index, + conversion, + }); + } + } + + /// The main interface external code uses. Given a local primitive, positioning + /// information, and a clip chain id, build an optimized clip chain instance. + pub fn build_clip_chain_instance( + &mut self, + local_prim_rect: LayoutRect, + prim_to_pic_mapper: &SpaceMapper, + pic_to_world_mapper: &SpaceMapper, + clip_scroll_tree: &ClipScrollTree, + gpu_cache: &mut GpuCache, + resource_cache: &mut ResourceCache, + device_pixel_scale: DevicePixelScale, + world_rect: &WorldRect, + clip_data_store: &mut ClipDataStore, + request_resources: bool, + ) -> Option { + let local_clip_rect = match self.active_local_clip_rect { + Some(rect) => rect, + None => return None, + }; + let local_bounding_rect = local_prim_rect.intersection(&local_clip_rect)?; let pic_clip_rect = prim_to_pic_mapper.map(&local_bounding_rect)?; let world_clip_rect = pic_to_world_mapper.map(&pic_clip_rect)?; @@ -646,7 +737,7 @@ impl ClipStore { let mut needs_mask = false; // For each potential clip node - for node_info in self.clip_node_info.drain(..) { + for node_info in self.active_clip_node_info.drain(..) { let node = &mut clip_data_store[node_info.handle]; // See how this clip affects the prim region. @@ -1352,28 +1443,14 @@ fn add_clip_node_to_current_chain( clip_scroll_tree: &ClipScrollTree, ) -> bool { let clip_node = &clip_data_store[node.handle]; - let clip_spatial_node = &clip_scroll_tree.spatial_nodes[node.spatial_node_index.0 as usize]; - let ref_spatial_node = &clip_scroll_tree.spatial_nodes[spatial_node_index.0 as usize]; // Determine the most efficient way to convert between coordinate // systems of the primitive and clip node. - //Note: this code is different from `get_relative_transform` in a way that we only try - // getting the relative transform if it's Local or ScaleOffset, - // falling back to the world transform otherwise. - let conversion = if spatial_node_index == node.spatial_node_index { - ClipSpaceConversion::Local - } else if ref_spatial_node.coordinate_system_id == clip_spatial_node.coordinate_system_id { - let scale_offset = ref_spatial_node.coordinate_system_relative_scale_offset - .inverse() - .accumulate(&clip_spatial_node.coordinate_system_relative_scale_offset); - ClipSpaceConversion::ScaleOffset(scale_offset) - } else { - ClipSpaceConversion::Transform( - clip_scroll_tree - .get_world_transform(node.spatial_node_index) - .into_transform() - ) - }; + let conversion = ClipSpaceConversion::new( + spatial_node_index, + node.spatial_node_index, + clip_scroll_tree, + ); // If we can convert spaces, try to reduce the size of the region // requested, and cache the conversion information for the next step. diff --git a/gfx/wr/webrender/src/frame_builder.rs b/gfx/wr/webrender/src/frame_builder.rs index 9e00f77234a1..786b1889e48b 100644 --- a/gfx/wr/webrender/src/frame_builder.rs +++ b/gfx/wr/webrender/src/frame_builder.rs @@ -157,7 +157,6 @@ pub struct FrameBuildingState<'a> { pub segment_builder: SegmentBuilder, pub surfaces: &'a mut Vec, pub dirty_region_stack: Vec, - pub clip_chain_stack: ClipChainStack, } impl<'a> FrameBuildingState<'a> { @@ -425,7 +424,6 @@ impl FrameBuilder { segment_builder: SegmentBuilder::new(), surfaces, dirty_region_stack: Vec::new(), - clip_chain_stack: ClipChainStack::new(), }; let root_render_task = RenderTask::new_picture( diff --git a/gfx/wr/webrender/src/prim_store/mod.rs b/gfx/wr/webrender/src/prim_store/mod.rs index b2e744afb65f..285c03004c89 100644 --- a/gfx/wr/webrender/src/prim_store/mod.rs +++ b/gfx/wr/webrender/src/prim_store/mod.rs @@ -1981,13 +1981,18 @@ impl PrimitiveStore { } } + frame_state.clip_store.set_active_clips( + prim_instance.local_clip_rect, + prim_instance.spatial_node_index, + frame_state.clip_chain_stack.current_clips(), + &frame_context.clip_scroll_tree, + &mut frame_state.data_stores.clip, + ); + let clip_chain = frame_state .clip_store .build_clip_chain_instance( - frame_state.clip_chain_stack.current_clips(), local_rect, - prim_instance.local_clip_rect, - prim_instance.spatial_node_index, &map_local_to_surface, &map_surface_to_world, &frame_context.clip_scroll_tree, @@ -2557,15 +2562,6 @@ impl PrimitiveStore { Some((pic_context_for_children, mut pic_state_for_children, mut prim_list)) => { let is_passthrough = pic_context_for_children.is_passthrough; - // Similar to the logic in the visibility pass, push either the - // picture clip chain or a new root, depending on whether this - // picture is backed by a surface. - if pic_context_for_children.is_composite { - frame_state.clip_chain_stack.push_surface(); - } else { - frame_state.clip_chain_stack.push_clip(prim_instance.clip_chain_id); - } - self.prepare_primitives( &mut prim_list, &pic_context_for_children, @@ -2576,13 +2572,6 @@ impl PrimitiveStore { scratch, ); - // And now undo the clip stack logic above. - if pic_context_for_children.is_composite { - frame_state.clip_chain_stack.pop_surface(); - } else { - frame_state.clip_chain_stack.pop_clip(); - } - // Restore the dependencies (borrow check dance) self.pictures[pic_context_for_children.pic_index.0] .restore_context( @@ -2600,9 +2589,6 @@ impl PrimitiveStore { }; if !is_passthrough { - // Push the per-primitive clip chain onto the current active stack - frame_state.clip_chain_stack.push_clip(prim_instance.clip_chain_id); - prim_instance.update_clip_task( pic_context.raster_spatial_node_index, pic_context, @@ -2614,9 +2600,6 @@ impl PrimitiveStore { scratch, ); - // Pop the primitive clip chain. - frame_state.clip_chain_stack.pop_clip(); - if prim_instance.is_chased() { println!("\tconsidered visible and ready with local pos {:?}", prim_instance.prim_origin); } @@ -3656,16 +3639,19 @@ impl PrimitiveInstance { // Build a clip chain for the smaller segment rect. This will // often manage to eliminate most/all clips, and sometimes // clip the segment completely. + frame_state.clip_store.set_active_clips_from_clip_chain( + &prim_info.clip_chain, + self.spatial_node_index, + &frame_context.clip_scroll_tree, + ); + let segment_clip_chain = frame_state .clip_store .build_clip_chain_instance( - frame_state.clip_chain_stack.current_clips(), segment.local_rect.translate(&LayoutVector2D::new( self.prim_origin.x, self.prim_origin.y, )), - self.local_clip_rect, - self.spatial_node_index, &pic_state.map_local_to_pic, &pic_state.map_pic_to_world, &frame_context.clip_scroll_tree,