Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for clip in/out. #1015

Merged
merged 2 commits into from Mar 29, 2017
Merged
Changes from 1 commit
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

Prev

Address review comments.

  • Loading branch information
gw3583 committed Mar 28, 2017
commit 44a7cd2451a63b40418c04ae4f75b89ba96d8bcf
@@ -4,7 +4,7 @@

use euclid::Point3D;
use geometry::ray_intersects_rect;
use mask_cache::{ClipSource, MaskCacheInfo};
use mask_cache::{ClipSource, MaskCacheInfo, RegionMode};
use prim_store::GpuBlock32;
use renderer::VertexDataStore;
use spring::{DAMPING, STIFFNESS, Spring};
@@ -47,7 +47,7 @@ impl ClipInfo {
-> ClipInfo {
// We pass true here for the MaskCacheInfo because this type of
// mask needs an extra clip for the clip rectangle.
let clip_sources = vec![ClipSource::Region(clip_region.clone(), true)];
let clip_sources = vec![ClipSource::Region(clip_region.clone(), RegionMode::IncludeRect)];
ClipInfo {
mask_cache_info: MaskCacheInfo::new(&clip_sources, clip_store),
clip_sources: clip_sources,
@@ -7,7 +7,7 @@ use batch_builder::BorderSideHelpers;
use frame::FrameId;
use gpu_store::GpuStoreAddress;
use internal_types::{HardwareCompositeOp, SourceTexture};
use mask_cache::{ClipMode, ClipSource, MaskCacheInfo};
use mask_cache::{ClipMode, ClipSource, MaskCacheInfo, RegionMode};
use prim_store::{BorderPrimitiveCpu, BorderPrimitiveGpu, BoxShadowPrimitiveGpu};
use prim_store::{GradientPrimitiveCpu, GradientPrimitiveGpu, ImagePrimitiveCpu, ImagePrimitiveGpu};
use prim_store::{ImagePrimitiveKind, PrimitiveContainer, PrimitiveGeometry, PrimitiveIndex};
@@ -166,7 +166,7 @@ impl FrameBuilder {
};
let mut clip_sources = Vec::new();
if clip_region.is_complex() {
clip_sources.push(ClipSource::Region(clip_region.clone(), false));
clip_sources.push(ClipSource::Region(clip_region.clone(), RegionMode::ExcludeRect));
}
// TODO(gw): Perhaps in the future it's worth passing in an array
// so that callers can provide an arbitrary number
@@ -20,14 +20,21 @@ pub enum ClipMode {
ClipOut, // Pixels outside the region are visible.
}

#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum RegionMode {
IncludeRect,
ExcludeRect,
}

#[derive(Clone, Debug)]
pub enum ClipSource {
Complex(LayerRect, f32, ClipMode),
// The bool here specifies whether to consider the rect
// The RegionMode here specifies whether to consider the rect
// from the clip region as part of the mask. This is true
// for clip/scroll nodes, but false for primitives, where
// the clip rect is handled in local space.
Region(ClipRegion, bool),
Region(ClipRegion, RegionMode),
}

impl ClipSource {
@@ -80,8 +87,6 @@ impl Geometry {
/// more clever with some proper region handling.
#[derive(Clone, Debug, PartialEq)]
pub enum MaskBounds {
/// We haven't calculated the bounds yet.
Unknown,
/// We know both the outer and inner rect. This is the
/// fast path for, e.g. a simple rounded rect.
OuterInner(Geometry, Geometry),
@@ -93,18 +98,12 @@ pub enum MaskBounds {
None,
}

impl MaskBounds {
fn new() -> MaskBounds {
MaskBounds::Unknown
}
}

#[derive(Clone, Debug)]
pub struct MaskCacheInfo {
pub clip_range: ClipAddressRange,
pub effective_clip_count: usize,
pub image: Option<(ImageMask, GpuStoreAddress)>,
pub bounds: MaskBounds,
pub bounds: Option<MaskBounds>,
pub is_aligned: bool,
}

@@ -128,14 +127,14 @@ impl MaskCacheInfo {
&ClipSource::Complex(..) => {
clip_count += 1;
},
&ClipSource::Region(ref region, rect_clip) => {
&ClipSource::Region(ref region, region_mode) => {
if let Some(info) = region.image_mask {
debug_assert!(image.is_none()); // TODO(gw): Support >1 image mask!
image = Some((info, clip_store.alloc(MASK_DATA_GPU_SIZE)));
}

clip_count += region.complex.length;
if rect_clip {
if region_mode == RegionMode::IncludeRect {
clip_count += 1;
}
},
@@ -155,7 +154,7 @@ impl MaskCacheInfo {
clip_range: clip_range,
effective_clip_count: clip_range.item_count,
image: image,
bounds: MaskBounds::new(),
bounds: None,
is_aligned: true,
})
}
@@ -170,11 +169,11 @@ impl MaskCacheInfo {

// If we haven't cached this info, or if the transform type has changed
// we need to re-calculate the number of clips.
if self.bounds == MaskBounds::Unknown || self.is_aligned != is_aligned {
if self.bounds.is_none() || self.is_aligned != is_aligned {
let mut local_rect = Some(LayerRect::new(LayerPoint::new(-MAX_CLIP, -MAX_CLIP),
LayerSize::new(2.0 * MAX_CLIP, 2.0 * MAX_CLIP)));
let mut local_inner: Option<LayerRect> = None;
let mut clip_out = false;
let mut has_clip_out = false;

self.effective_clip_count = 0;
self.is_aligned = is_aligned;
@@ -185,7 +184,7 @@ impl MaskCacheInfo {
// Once we encounter a clip-out, we just assume the worst
// case clip mask size, for now.
if mode == ClipMode::ClipOut {
clip_out = true;
has_clip_out = true;
}
debug_assert!(self.effective_clip_count < self.clip_range.item_count);
let address = self.clip_range.start + self.effective_clip_count * CLIP_DATA_GPU_SIZE;
@@ -198,7 +197,7 @@ impl MaskCacheInfo {
local_inner = ComplexClipRegion::new(rect, BorderRadius::uniform(radius))
.get_inner_rect();
}
&ClipSource::Region(ref region, rect_clip) => {
&ClipSource::Region(ref region, region_mode) => {
local_rect = local_rect.and_then(|r| r.intersection(&region.main));
local_inner = match region.image_mask {
Some(ref mask) if !mask.repeat => {
@@ -210,7 +209,7 @@ impl MaskCacheInfo {
};

let clips = aux_lists.complex_clip_regions(&region.complex);
if !self.is_aligned && rect_clip {
if !self.is_aligned && region_mode == RegionMode::IncludeRect {
// we have an extra clip rect coming from the transformed layer
debug_assert!(self.effective_clip_count < self.clip_range.item_count);
let address = self.clip_range.start + self.effective_clip_count * CLIP_DATA_GPU_SIZE;
@@ -238,11 +237,11 @@ impl MaskCacheInfo {

// Work out the type of mask geometry we have, based on the
// list of clip sources above.
if clip_out {
if has_clip_out {
// For clip-out, the mask rect is not known.
self.bounds = MaskBounds::None;
self.bounds = Some(MaskBounds::None);
} else {
// local inner is only valid if there's a single clip (for now).
// TODO(gw): local inner is only valid if there's a single clip (for now).
// This can be improved in the future, with some proper
// rectangle region handling.

This comment has been minimized.

@kvark

kvark Mar 28, 2017

Member

nit: make it a TODO item

if sources.len() > 1 {
@@ -253,25 +252,24 @@ impl MaskCacheInfo {

self.bounds = match local_inner {
Some(local_inner) => {
MaskBounds::OuterInner(Geometry::new(local_rect),
Geometry::new(local_inner))
Some(MaskBounds::OuterInner(Geometry::new(local_rect),
Geometry::new(local_inner)))
}
None => {
MaskBounds::Outer(Geometry::new(local_rect))
Some(MaskBounds::Outer(Geometry::new(local_rect)))
}
};
}
}

// Update the device space bounding rects of the mask
// geometry.
match self.bounds {
MaskBounds::Unknown => unreachable!(),
MaskBounds::None => {}
MaskBounds::Outer(ref mut outer) => {
match self.bounds.as_mut().unwrap() {
&mut MaskBounds::None => {}
&mut MaskBounds::Outer(ref mut outer) => {
outer.update(transform, device_pixel_ratio);
}
MaskBounds::OuterInner(ref mut outer, ref mut inner) => {
&mut MaskBounds::OuterInner(ref mut outer, ref mut inner) => {
outer.update(transform, device_pixel_ratio);
inner.update(transform, device_pixel_ratio);

This comment has been minimized.

@kvark

kvark Mar 28, 2017

Member

I don't think this is correct. The update here would compute the bounding box of the inner area, which is useless to us (and confusing). Not sure if we actually use it anywhere, but it doesn't appear that inner fits the whole Geometry struct concept.

}
@@ -109,10 +109,6 @@ pub enum PrimitiveCacheKey {
#[derive(Debug)]
pub struct PrimitiveMetadata {
pub is_opaque: bool,
// TODO(gw): It's only a very small subset of primitives
// that end up with > 1 clip region. Consider an optimization
// for this case - perhaps an enum that either holds one
// clip or a Vec of clips?
pub clips: Vec<ClipSource>,
pub clip_cache_info: Option<MaskCacheInfo>,
pub prim_kind: PrimitiveKind,
@@ -1004,7 +1000,7 @@ impl PrimitiveStore {

pub fn set_clip_source(&mut self, index: PrimitiveIndex, source: Option<ClipSource>) {
let metadata = &mut self.cpu_metadata[index.0];
match source {
metadata.clips = match source {
Some(source) => {
let (rect, is_complex) = match source {
ClipSource::Complex(rect, radius, _) => (rect, radius > 0.0),
@@ -1015,10 +1011,10 @@ impl PrimitiveStore {
if is_complex {
metadata.clip_cache_info = None; //CLIP TODO: re-use the existing GPU allocation
}
metadata.clips = vec![source];
vec![source]
}
None => {
metadata.clips.clear();
vec![]
}
}
}
@@ -193,20 +193,19 @@ impl RenderTask {
// is in the intersection of all of all the outer bounds,
// and if it's completely inside the intersection of all of the inner bounds.

// If we encounter a clip with unknown bounds, we'll just use
// TODO(gw): If we encounter a clip with unknown bounds, we'll just use
// the original rect. This is overly conservative, but can

This comment has been minimized.

@kvark

kvark Mar 28, 2017

Member

agreed. Let's make it a real //TODO item

// be optimized later.
let mut result = Some(actual_rect);
for &(_, ref clip) in clips {
match clip.bounds {
MaskBounds::Unknown => unreachable!(),
MaskBounds::OuterInner(ref outer, _) |
MaskBounds::Outer(ref outer) => {
match clip.bounds.as_ref().unwrap() {
&MaskBounds::OuterInner(ref outer, _) |
&MaskBounds::Outer(ref outer) => {
result = result.and_then(|rect| {
rect.intersection(&outer.bounding_rect)
});
}
MaskBounds::None => {
&MaskBounds::None => {
result = Some(actual_rect);
break;
}
@@ -224,11 +223,10 @@ impl RenderTask {
let inner_rect = clips.iter()
.fold(Some(task_rect), |current, clip| {
current.and_then(|rect| {
let inner_rect = match clip.1.bounds {
MaskBounds::Unknown |
MaskBounds::Outer(..) |
MaskBounds::None => DeviceIntRect::zero(),
MaskBounds::OuterInner(_, ref inner) => inner.bounding_rect
let inner_rect = match clip.1.bounds.as_ref().unwrap() {
&MaskBounds::Outer(..) |
&MaskBounds::None => DeviceIntRect::zero(),
&MaskBounds::OuterInner(_, ref inner) => inner.bounding_rect
};
rect.intersection(&inner_rect)
})
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.