Skip to content

Commit

Permalink
Auto merge of #2618 - glennw:clip-mask-snap-clip-out, r=kvark
Browse files Browse the repository at this point in the history
Support snapping on clip masks, and fast path rectangle + clip out mode.

<!-- Reviewable:start -->
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/webrender/2618)
<!-- Reviewable:end -->
  • Loading branch information
bors-servo committed Apr 6, 2018
2 parents 09109c7 + 076b443 commit fe24137
Show file tree
Hide file tree
Showing 13 changed files with 171 additions and 57 deletions.
22 changes: 20 additions & 2 deletions webrender/res/clip_shared.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,25 @@ RectWithSize intersect_rect(RectWithSize a, RectWithSize b) {
ClipVertexInfo write_clip_tile_vertex(RectWithSize local_clip_rect,
ClipScrollNode scroll_node,
ClipArea area) {
vec2 actual_pos = area.screen_origin + aPosition.xy * area.common_data.task_rect.size;
vec2 device_pos = area.screen_origin + aPosition.xy * area.common_data.task_rect.size;
vec2 actual_pos = device_pos;

if (scroll_node.is_axis_aligned) {
vec4 snap_positions = compute_snap_positions(
scroll_node.transform,
local_clip_rect
);

vec2 snap_offsets = compute_snap_offset_impl(
device_pos,
scroll_node.transform,
local_clip_rect,
RectWithSize(snap_positions.xy, snap_positions.zw - snap_positions.xy),
snap_positions
);

actual_pos -= snap_offsets;
}

vec4 node_pos;

Expand All @@ -64,7 +82,7 @@ ClipVertexInfo write_clip_tile_vertex(RectWithSize local_clip_rect,
}

// compute the point position inside the scroll node, in CSS space
vec2 vertex_pos = actual_pos +
vec2 vertex_pos = device_pos +
area.common_data.task_rect.p0 -
area.screen_origin;

Expand Down
43 changes: 36 additions & 7 deletions webrender/res/prim_shared.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -486,11 +486,8 @@ vec4 get_node_pos(vec2 pos, ClipScrollNode node) {
return untransform(pos, n, a, node.inv_transform);
}

// Compute a snapping offset in world space (adjusted to pixel ratio),
// given local position on the scroll_node and a snap rectangle.
vec2 compute_snap_offset(vec2 local_pos,
mat4 transform,
RectWithSize snap_rect) {
// Compute a snapping offset in world space (adjusted to pixel ratio)
vec4 compute_snap_positions(mat4 transform, RectWithSize snap_rect) {
// Ensure that the snap rect is at *least* one device pixel in size.
// TODO(gw): It's not clear to me that this is "correct". Specifically,
// how should it interact with sub-pixel snap rects when there
Expand All @@ -505,15 +502,47 @@ vec2 compute_snap_offset(vec2 local_pos,
// Snap bounds in world coordinates, adjusted for pixel ratio. XY = top left, ZW = bottom right
vec4 world_snap = uDevicePixelRatio * vec4(world_snap_p0.xy, world_snap_p1.xy) /
vec4(world_snap_p0.ww, world_snap_p1.ww);
return world_snap;
}

vec2 compute_snap_offset_impl(
vec2 reference_pos,
mat4 transform,
RectWithSize snap_rect,
RectWithSize reference_rect,
vec4 snap_positions) {

/// World offsets applied to the corners of the snap rectangle.
vec4 snap_offsets = floor(world_snap + 0.5) - world_snap;
vec4 snap_offsets = floor(snap_positions + 0.5) - snap_positions;

/// Compute the position of this vertex inside the snap rectangle.
vec2 normalized_snap_pos = (local_pos - snap_rect.p0) / snap_rect.size;
vec2 normalized_snap_pos = (reference_pos - reference_rect.p0) / reference_rect.size;

/// Compute the actual world offset for this vertex needed to make it snap.
return mix(snap_offsets.xy, snap_offsets.zw, normalized_snap_pos);
}

// Compute a snapping offset in world space (adjusted to pixel ratio),
// given local position on the scroll_node and a snap rectangle.
vec2 compute_snap_offset(vec2 local_pos,
mat4 transform,
RectWithSize snap_rect) {
vec4 snap_positions = compute_snap_positions(
transform,
snap_rect
);

vec2 snap_offsets = compute_snap_offset_impl(
local_pos,
transform,
snap_rect,
snap_rect,
snap_positions
);

return snap_offsets;
}

struct VertexInfo {
vec2 local_pos;
vec2 screen_pos;
Expand Down
7 changes: 4 additions & 3 deletions webrender/src/batch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use api::{AlphaType, DeviceIntRect, DeviceIntSize};
use api::{AlphaType, ClipMode, DeviceIntRect, DeviceIntSize};
use api::{DeviceUintRect, DeviceUintPoint, DeviceUintSize, ExternalImageType, FilterOp, ImageRendering, LayerRect};
use api::{DeviceIntPoint, SubpixelDirection, YuvColorSpace, YuvFormat};
use api::{LayerToWorldTransform, WorldPixel};
Expand Down Expand Up @@ -1627,8 +1627,9 @@ impl ClipBatcher {
..instance
});
}
ClipSource::Rectangle(..) => {
if work_item.coordinate_system_id != coordinate_system_id {
ClipSource::Rectangle(_, mode) => {
if work_item.coordinate_system_id != coordinate_system_id ||
mode == ClipMode::ClipOut {
self.rectangles.push(ClipMaskInstance {
clip_data_address: gpu_address,
..instance
Expand Down
33 changes: 22 additions & 11 deletions webrender/src/clip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ impl ClipRegion {

#[derive(Debug)]
pub enum ClipSource {
Rectangle(LayerRect),
Rectangle(LayerRect, ClipMode),
RoundedRectangle(LayerRect, BorderRadius, ClipMode),
Image(ImageMask),
/// TODO(gw): This currently only handles dashed style
Expand All @@ -101,7 +101,7 @@ impl From<ClipRegion> for ClipSources {
clips.push(ClipSource::Image(info));
}

clips.push(ClipSource::Rectangle(region.main));
clips.push(ClipSource::Rectangle(region.main, ClipMode::Clip));

for complex in region.complex_clips {
clips.push(ClipSource::new_rounded_rect(
Expand All @@ -121,12 +121,16 @@ impl ClipSource {
mut radii: BorderRadius,
clip_mode: ClipMode
) -> ClipSource {
ensure_no_corner_overlap(&mut radii, &rect);
ClipSource::RoundedRectangle(
rect,
radii,
clip_mode,
)
if radii.is_zero() {
ClipSource::Rectangle(rect, clip_mode)
} else {
ensure_no_corner_overlap(&mut radii, &rect);
ClipSource::RoundedRectangle(
rect,
radii,
clip_mode,
)
}
}

pub fn new_line_decoration(
Expand Down Expand Up @@ -306,7 +310,14 @@ impl ClipSources {
}
local_inner = None;
}
ClipSource::Rectangle(rect) => {
ClipSource::Rectangle(rect, mode) => {
// Once we encounter a clip-out, we just assume the worst
// case clip mask size, for now.
if mode == ClipMode::ClipOut {
can_calculate_inner_rect = false;
break;
}

can_calculate_outer_rect = true;
local_outer = local_outer.and_then(|r| r.intersection(&rect));
local_inner = local_inner.and_then(|r| r.intersection(&rect));
Expand Down Expand Up @@ -378,8 +389,8 @@ impl ClipSources {
]);
request.push(info.prim_shadow_rect);
}
ClipSource::Rectangle(rect) => {
let data = ClipData::uniform(rect, 0.0, ClipMode::Clip);
ClipSource::Rectangle(rect, mode) => {
let data = ClipData::uniform(rect, 0.0, mode);
data.write(&mut request);
}
ClipSource::RoundedRectangle(ref rect, ref radius, mode) => {
Expand Down
11 changes: 7 additions & 4 deletions webrender/src/hit_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,17 @@ impl HitTestingItem {
pub struct HitTestingRun(pub Vec<HitTestingItem>, pub ScrollNodeAndClipChain);

enum HitTestRegion {
Rectangle(LayerRect),
Rectangle(LayerRect, ClipMode),
RoundedRectangle(LayerRect, BorderRadius, ClipMode),
}

impl HitTestRegion {
pub fn contains(&self, point: &LayerPoint) -> bool {
match *self {
HitTestRegion::Rectangle(ref rectangle) => rectangle.contains(point),
HitTestRegion::Rectangle(ref rectangle, ClipMode::Clip) =>
rectangle.contains(point),
HitTestRegion::Rectangle(ref rectangle, ClipMode::ClipOut) =>
!rectangle.contains(point),
HitTestRegion::RoundedRectangle(rect, radii, ClipMode::Clip) =>
rounded_rectangle_contains_point(point, &rect, &radii),
HitTestRegion::RoundedRectangle(rect, radii, ClipMode::ClipOut) =>
Expand Down Expand Up @@ -307,10 +310,10 @@ fn get_regions_for_clip_scroll_node(

clips.iter().map(|source| {
match source.0 {
ClipSource::Rectangle(ref rect) => HitTestRegion::Rectangle(*rect),
ClipSource::Rectangle(ref rect, mode) => HitTestRegion::Rectangle(*rect, mode),
ClipSource::RoundedRectangle(ref rect, ref radii, ref mode) =>
HitTestRegion::RoundedRectangle(*rect, *radii, *mode),
ClipSource::Image(ref mask) => HitTestRegion::Rectangle(mask.rect),
ClipSource::Image(ref mask) => HitTestRegion::Rectangle(mask.rect, ClipMode::Clip),
ClipSource::BorderCorner(_) |
ClipSource::LineDecoration(_) |
ClipSource::BoxShadow(_) => {
Expand Down
77 changes: 47 additions & 30 deletions webrender/src/prim_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1544,12 +1544,21 @@ impl PrimitiveStore {
if !brush.kind.supports_segments() {
return;
}
if metadata.local_rect.size.area() <= MIN_BRUSH_SPLIT_AREA {
return;
}
}
}

// If the brush is small, we generally want to skip building segments
// and just draw it as a single primitive with clip mask. However,
// if the clips are purely rectangles that have no per-fragment
// clip masks, we will segment anyway. This allows us to completely
// skip allocating a clip mask in these cases.
let is_large = metadata.local_rect.size.area() > MIN_BRUSH_SPLIT_AREA;

// TODO(gw): We should probably detect and store this on each
// ClipSources instance, to avoid having to iterate
// the clip sources here.
let mut rect_clips_only = true;

let mut segment_builder = SegmentBuilder::new(
metadata.local_rect,
None,
Expand All @@ -1574,12 +1583,16 @@ impl PrimitiveStore {
for &(ref clip, _) in &local_clips.clips {
let (local_clip_rect, radius, mode) = match *clip {
ClipSource::RoundedRectangle(rect, radii, clip_mode) => {
rect_clips_only = false;

(rect, Some(radii), clip_mode)
}
ClipSource::Rectangle(rect) => {
(rect, None, ClipMode::Clip)
ClipSource::Rectangle(rect, mode) => {
(rect, None, mode)
}
ClipSource::BoxShadow(ref info) => {
rect_clips_only = false;

// For inset box shadows, we can clip out any
// pixels that are inside the shadow region
// and are beyond the inner rect, as they can't
Expand Down Expand Up @@ -1607,6 +1620,8 @@ impl PrimitiveStore {
ClipSource::BorderCorner(..) |
ClipSource::LineDecoration(..) |
ClipSource::Image(..) => {
rect_clips_only = false;

// TODO(gw): We can easily extend the segment builder
// to support these clip sources in the
// future, but they are rarely used.
Expand Down Expand Up @@ -1638,32 +1653,34 @@ impl PrimitiveStore {
}
}

match brush.segment_desc {
Some(ref mut segment_desc) => {
segment_desc.clip_mask_kind = clip_mask_kind;
}
None => {
// TODO(gw): We can probably make the allocation
// patterns of this and the segment
// builder significantly better, by
// retaining it across primitives.
let mut segments = Vec::new();

segment_builder.build(|segment| {
segments.push(
BrushSegment::new(
segment.rect.origin,
segment.rect.size,
segment.has_mask,
segment.edge_flags,
),
);
});
if is_large || rect_clips_only {
match brush.segment_desc {
Some(ref mut segment_desc) => {
segment_desc.clip_mask_kind = clip_mask_kind;
}
None => {
// TODO(gw): We can probably make the allocation
// patterns of this and the segment
// builder significantly better, by
// retaining it across primitives.
let mut segments = Vec::new();

segment_builder.build(|segment| {
segments.push(
BrushSegment::new(
segment.rect.origin,
segment.rect.size,
segment.has_mask,
segment.edge_flags,
),
);
});

brush.segment_desc = Some(BrushSegmentDescriptor {
segments,
clip_mask_kind,
});
brush.segment_desc = Some(BrushSegmentDescriptor {
segments,
clip_mask_kind,
});
}
}
}
}
Expand Down
Binary file modified wrench/reftests/border/degenerate-curve.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified wrench/reftests/boxshadow/inset-subpx.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified wrench/reftests/boxshadow/no-stretch.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions wrench/reftests/clip/reftest.list
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@
== fixed-position-clipping.yaml fixed-position-clipping-ref.yaml
== segmentation-with-other-coordinate-system-clip.yaml segmentation-with-other-coordinate-system-clip-ref.yaml
== stacking-context-clip.yaml stacking-context-clip-ref.yaml
== snapping.yaml snapping-ref.yaml
17 changes: 17 additions & 0 deletions wrench/reftests/clip/snapping-ref.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
root:
items:
- type: clip
bounds: [0, 0, 1000, 1000]
complex:
- rect: [50, 50, 100, 100]
radius: 16
items:
- type: rect
bounds: 50 50 100 100
color: red

- type: rect
bounds: 200 50 100 100
color: green

17 changes: 17 additions & 0 deletions wrench/reftests/clip/snapping.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
root:
items:
- type: clip
bounds: [0, 0, 1000, 1000]
complex:
- rect: [50.3, 50.3, 100, 100]
radius: 16
items:
- type: rect
bounds: 50.3 50.3 100 100
color: red

- type: rect
bounds: 200.3 50.3 100 100
color: green

Binary file modified wrench/reftests/transforms/prim-suite.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit fe24137

Please sign in to comment.