Commit 49842aaa authored by Glenn Watson's avatar Glenn Watson
Browse files

Bug 1741781 - Simplify complex clip reject/accept logic r=gfx-reviewers,nical

Remove the dependencies on transforming both the prim and clips
into world space, by relying on the fact that in the complex
transform case, the clip spatial nodes are ancestors of the
primitive spatial node.

This allows us to work in the same of the clip spatial node. For
now, this is only applied to the CPU accept/reject code, however
in future will allow us to optimize and simplify the GPU mask
rendering code significantly.

Differential Revision: https://phabricator.services.mozilla.com/D131464
parent b5eeb2df
Loading
Loading
Loading
Loading
+17 −97
Original line number Diff line number Diff line
@@ -110,8 +110,7 @@ use crate::render_task_cache::to_cache_size;
use crate::resource_cache::{ImageRequest, ResourceCache};
use crate::space::SpaceMapper;
use crate::util::{clamp_to_scale_factor, MaxRect, extract_inner_rect_safe, project_rect, ScaleOffset, VecHelper};
use euclid::approxeq::ApproxEq;
use std::{iter, ops, u32, mem};
use std::{ops, u32, mem};

// Type definitions for interning clip nodes.

@@ -540,7 +539,7 @@ impl ClipNodeRange {
enum ClipSpaceConversion {
    Local,
    ScaleOffset(ScaleOffset),
    Transform(LayoutToWorldTransform),
    Transform(LayoutTransform),
}

impl ClipSpaceConversion {
@@ -564,9 +563,11 @@ impl ClipSpaceConversion {
                .accumulate(&clip_spatial_node.content_transform);
            ClipSpaceConversion::ScaleOffset(scale_offset)
        } else {
            assert!(spatial_tree.is_ancestor(clip_spatial_node_index, prim_spatial_node_index));

            ClipSpaceConversion::Transform(
                spatial_tree
                    .get_world_transform(clip_spatial_node_index)
                    .get_relative_transform(prim_spatial_node_index, clip_spatial_node_index)
                    .into_transform()
            )
        }
@@ -1294,12 +1295,10 @@ impl ClipStore {
        &mut self,
        local_prim_rect: LayoutRect,
        prim_to_pic_mapper: &SpaceMapper<LayoutPixel, PicturePixel>,
        pic_to_world_mapper: &SpaceMapper<PicturePixel, WorldPixel>,
        spatial_tree: &SpatialTree,
        gpu_cache: &mut GpuCache,
        resource_cache: &mut ResourceCache,
        device_pixel_scale: DevicePixelScale,
        world_rect: &WorldRect,
        clip_data_store: &mut ClipDataStore,
        request_resources: bool,
        is_chased: bool,
@@ -1315,7 +1314,6 @@ impl ClipStore {

        let local_bounding_rect = local_prim_rect.intersection(&local_clip_rect)?;
        let mut pic_clip_rect = prim_to_pic_mapper.map(&local_bounding_rect)?;
        let world_clip_rect = pic_to_world_mapper.map(&pic_clip_rect)?;

        // Now, we've collected all the clip nodes that *potentially* affect this
        // primitive region, and reduced the size of the prim region as much as possible.
@@ -1342,9 +1340,8 @@ impl ClipStore {
                ClipSpaceConversion::Transform(ref transform) => {
                    has_non_local_clips = true;
                    node.item.kind.get_clip_result_complex(
                        &local_bounding_rect,
                        transform,
                        &world_clip_rect,
                        world_rect,
                    )
                }
            };
@@ -1803,15 +1800,9 @@ impl ClipItemKind {

    fn get_clip_result_complex(
        &self,
        transform: &LayoutToWorldTransform,
        prim_world_rect: &WorldRect,
        world_rect: &WorldRect,
        local_prim_rect: &LayoutRect,
        prim_to_clip_transform: &LayoutTransform,
    ) -> ClipResult {
        let visible_rect = match prim_world_rect.intersection(world_rect) {
            Some(rect) => rect,
            None => return ClipResult::Reject,
        };

        let (clip_rect, inner_rect, mode) = match *self {
            ClipItemKind::Rectangle { rect, mode } => {
                (rect, Some(rect), mode)
@@ -1829,8 +1820,15 @@ impl ClipItemKind {
            }
        };

        let prim_rect = match project_rect(prim_to_clip_transform, local_prim_rect, &clip_rect) {
            Some(rect) => rect,
            None => {
                return ClipResult::Reject;
            }
        };

        if let Some(ref inner_clip_rect) = inner_rect {
            if let Some(()) = projected_rect_contains(inner_clip_rect, transform, &visible_rect) {
            if inner_clip_rect.contains_box(&prim_rect) {
                return match mode {
                    ClipMode::Clip => ClipResult::Accept,
                    ClipMode::ClipOut => ClipResult::Reject,
@@ -1838,28 +1836,7 @@ impl ClipItemKind {
            }
        }

        match mode {
            ClipMode::Clip => {
                let outer_clip_rect = match project_rect(
                    transform,
                    &clip_rect,
                    &world_rect,
                ) {
                    Some(outer_clip_rect) => outer_clip_rect,
                    None => return ClipResult::Partial,
                };

                match outer_clip_rect.intersection(prim_world_rect) {
                    Some(..) => {
                        ClipResult::Partial
                    }
                    None => {
                        ClipResult::Reject
                    }
                }
            }
            ClipMode::ClipOut => ClipResult::Partial,
        }
        return ClipResult::Partial;
    }

    // Check how a given clip source affects a local primitive region.
@@ -2101,42 +2078,6 @@ pub fn polygon_contains_point(
    }
}

pub fn projected_rect_contains(
    source_rect: &LayoutRect,
    transform: &LayoutToWorldTransform,
    target_rect: &WorldRect,
) -> Option<()> {
    let points = [
        transform.transform_point2d(source_rect.top_left())?,
        transform.transform_point2d(source_rect.top_right())?,
        transform.transform_point2d(source_rect.bottom_right())?,
        transform.transform_point2d(source_rect.bottom_left())?,
    ];
    let target_points = [
        target_rect.top_left(),
        target_rect.top_right(),
        target_rect.bottom_right(),
        target_rect.bottom_left(),
    ];
    // iterate the edges of the transformed polygon
    for (a, b) in points
        .iter()
        .cloned()
        .zip(points[1..].iter().cloned().chain(iter::once(points[0])))
    {
        // If this edge is redundant, it's a weird, case, and we shouldn't go
        // length in trying to take the fast path (e.g. when the whole rectangle is a point).
        // If any of edges of the target rectangle crosses the edge, it's not completely
        // inside our transformed polygon either.
        if a.approx_eq(&b) || target_points.iter().any(|&c| (b - a).cross(c - a) < 0.0) {
            return None
        }
    }

    Some(())
}


// Add a clip node into the list of clips to be processed
// for the current clip chain. Returns false if the clip
// results in the entire primitive being culled out.
@@ -2178,8 +2119,6 @@ fn add_clip_node_to_current_chain(
                };
            }
            ClipSpaceConversion::Transform(..) => {
                assert!(spatial_tree.is_ancestor(node.spatial_node_index, prim_spatial_node_index));

                // Map the local clip rect directly into the same space as the picture
                // surface. This will often be the same space as the clip itself, which
                // results in a reduction in allocated clip mask size.
@@ -2224,25 +2163,6 @@ fn add_clip_node_to_current_chain(
    true
}

#[cfg(test)]
mod tests {
    use super::projected_rect_contains;
    use euclid::{Transform3D, rect};

    #[test]
    fn test_empty_projected_rect() {
        assert_eq!(
            None,
            projected_rect_contains(
                &rect(10.0, 10.0, 0.0, 0.0).to_box2d(),
                &Transform3D::identity(),
                &rect(20.0, 20.0, 10.0, 10.0).to_box2d(),
            ),
            "Empty rectangle is considered to include a non-empty!"
        );
    }
}

/// PolygonKeys get interned, because it's a convenient way to move the data
/// for the polygons out of the ClipItemKind and ClipItemKeyKind enums. The
/// polygon data is both interned and retrieved by the scene builder, and not
+0 −2
Original line number Diff line number Diff line
@@ -2164,12 +2164,10 @@ impl TileCacheInstance {
            let clip_chain_instance = frame_state.clip_store.build_clip_chain_instance(
                pic_rect.cast_unit(),
                &map_local_to_surface,
                &pic_to_world_mapper,
                frame_context.spatial_tree,
                frame_state.gpu_cache,
                frame_state.resource_cache,
                frame_context.global_device_pixel_scale,
                &frame_context.global_screen_world_rect,
                &mut frame_state.data_stores.clip,
                true,
                false,
+0 −4
Original line number Diff line number Diff line
@@ -1095,8 +1095,6 @@ fn update_clip_task_for_brush(
        );
        clip_mask_instances.push(clip_mask_kind);
    } else {
        let dirty_world_rect = frame_state.current_dirty_region().combined;

        for segment in segments {
            // Build a clip chain for the smaller segment rect. This will
            // often manage to eliminate most/all clips, and sometimes
@@ -1112,12 +1110,10 @@ fn update_clip_task_for_brush(
                .build_clip_chain_instance(
                    segment.local_rect.translate(prim_origin.to_vector()),
                    &pic_state.map_local_to_pic,
                    &pic_state.map_pic_to_world,
                    &frame_context.spatial_tree,
                    frame_state.gpu_cache,
                    frame_state.resource_cache,
                    device_pixel_scale,
                    &dirty_world_rect,
                    &mut data_stores.clip,
                    false,
                    instance.is_chased(),
+0 −2
Original line number Diff line number Diff line
@@ -398,12 +398,10 @@ pub fn update_primitive_visibility(
                    .build_clip_chain_instance(
                        local_rect,
                        &map_local_to_surface,
                        &map_surface_to_world,
                        &frame_context.spatial_tree,
                        frame_state.gpu_cache,
                        frame_state.resource_cache,
                        surface.device_pixel_scale,
                        &world_culling_rect,
                        &mut frame_state.data_stores.clip,
                        true,
                        prim_instance.is_chased(),