Bug 1553701 - Remove the clip chain stack during the prepare_prims pass in WR. r=kvark,nical

During the visibility pass, the main clip chain instance for each
primitive is created. In the prim prepare pass, a clip chain instance
is generated for each segment (of primitives that are segmented).

This previously required maintaining the active clip chain stack
during both passes. However, this is not ideal for a number of
reasons: the code is somewhat complicated / error prone and the
segment clip chain building step does more work than required.

This patch changes the segment clip chain building code to set up
the active clip nodes based on the result of the initial clip
chain built for the overall primitive during the visibility pass.

This means that it's no longer necessary to maintain the active
clip chain stack during the prepare pass. This simplifies some
upcoming picture caching changes related to avoiding redundant
cache invalidations, which is the main motivation for the change.

Differential Revision: https://phabricator.services.mozilla.com/D32250

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Glenn Watson 2019-05-24 03:09:28 +00:00
parent bb778aa169
commit 8d73dca1e7
3 changed files with 134 additions and 73 deletions

View File

@ -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<usize> {
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<ClipChainNode>,
clip_node_instances: Vec<ClipNodeInstance>,
clip_node_info: Vec<ClipNodeInfo>,
active_clip_node_info: Vec<ClipNodeInfo>,
active_local_clip_rect: Option<LayoutRect>,
}
// 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<LayoutPixel, PicturePixel>,
pic_to_world_mapper: &SpaceMapper<PicturePixel, WorldPixel>,
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<ClipChainInstance> {
) {
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<LayoutPixel, PicturePixel>,
pic_to_world_mapper: &SpaceMapper<PicturePixel, WorldPixel>,
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<ClipChainInstance> {
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.

View File

@ -157,7 +157,6 @@ pub struct FrameBuildingState<'a> {
pub segment_builder: SegmentBuilder,
pub surfaces: &'a mut Vec<SurfaceInfo>,
pub dirty_region_stack: Vec<DirtyRegion>,
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(

View File

@ -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,