diff --git a/sample/src/main.rs b/sample/src/main.rs index 3de19c7b2d..15a1cd84d2 100644 --- a/sample/src/main.rs +++ b/sample/src/main.rs @@ -102,6 +102,22 @@ fn main() { let mut builder = webrender_traits::DisplayListBuilder::new(pipeline_id); let bounds = LayoutRect::new(LayoutPoint::new(0.0, 0.0), LayoutSize::new(width as f32, height as f32)); + + // Push outer stacking context. + // No (effective) clip on the stacking context + let clip_region = { + builder.new_clip_region(&bounds, vec![], None) + }; + builder.push_stacking_context(webrender_traits::ScrollPolicy::Scrollable, + bounds, + clip_region, + 0, + &LayoutTransform::identity(), + &LayoutTransform::identity(), + webrender_traits::MixBlendMode::Normal, + Vec::new()); + + let clip_region = { let complex = webrender_traits::ComplexClipRegion::new( LayoutRect::new(LayoutPoint::new(50.0, 50.0), LayoutSize::new(100.0, 100.0)), @@ -151,7 +167,6 @@ fn main() { border_side, webrender_traits::BorderRadius::uniform(20.0)); - if false { // draw text? let font_bytes = load_file("res/FreeSans.ttf"); let font_key = api.add_raw_font(font_bytes); @@ -232,6 +247,20 @@ fn main() { builder.pop_stacking_context(); + let two_clips = { + let complex1 = webrender_traits::ComplexClipRegion::new( + LayoutRect::new(LayoutPoint::new(400.0, 100.0), LayoutSize::new(50.0, 100.0)), + webrender_traits::BorderRadius::uniform(10.0)); + let complex2 = webrender_traits::ComplexClipRegion::new( + LayoutRect::new(LayoutPoint::new(400.0, 100.0), LayoutSize::new(100.0, 50.0)), + webrender_traits::BorderRadius::uniform(20.0)); + builder.new_clip_region(&bounds, vec![complex1, complex2], None) + }; + builder.push_rect(LayoutRect::new(LayoutPoint::new(400.0, 100.0), LayoutSize::new(100.0, 100.0)), + two_clips, + ColorF::new(1.0, 1.0, 0.0, 1.0)); + builder.pop_stacking_context(); + api.set_root_display_list( Some(root_background_color), epoch, diff --git a/webrender/res/clip_shared.glsl b/webrender/res/clip_shared.glsl index 3b406cbf8c..bb69a5d18a 100644 --- a/webrender/res/clip_shared.glsl +++ b/webrender/res/clip_shared.glsl @@ -8,13 +8,11 @@ in int aClipRenderTaskIndex; in int aClipLayerIndex; in int aClipDataIndex; -in int aClipBaseTaskIndex; struct CacheClipInstance { int render_task_index; int layer_index; int data_index; - int base_task_index; }; CacheClipInstance fetch_clip_item(int index) { @@ -23,30 +21,62 @@ CacheClipInstance fetch_clip_item(int index) { cci.render_task_index = aClipRenderTaskIndex; cci.layer_index = aClipLayerIndex; cci.data_index = aClipDataIndex; - cci.base_task_index = aClipBaseTaskIndex; return cci; } // The transformed vertex function that always covers the whole clip area, // which is the intersection of all clip instances of a given primitive -TransformVertexInfo write_clip_tile_vertex(vec4 local_clip_rect, +TransformVertexInfo write_clip_tile_vertex(vec4 local_rect, Layer layer, ClipArea area) { - vec2 lp0_base = local_clip_rect.xy; - vec2 lp1_base = local_clip_rect.xy + local_clip_rect.zw; + vec2 lp0_base = local_rect.xy; + vec2 lp1_base = local_rect.xy + local_rect.zw; vec2 lp0 = clamp_rect(lp0_base, layer.local_clip_rect); vec2 lp1 = clamp_rect(lp1_base, layer.local_clip_rect); vec4 clipped_local_rect = vec4(lp0, lp1 - lp0); - vec2 final_pos = mix(area.task_bounds.xy, area.task_bounds.zw, aPosition.xy); + vec2 p0 = lp0; + vec2 p1 = vec2(lp1.x, lp0.y); + vec2 p2 = vec2(lp0.x, lp1.y); + vec2 p3 = lp1; + + vec4 t0 = layer.transform * vec4(p0, 0, 1); + vec4 t1 = layer.transform * vec4(p1, 0, 1); + vec4 t2 = layer.transform * vec4(p2, 0, 1); + vec4 t3 = layer.transform * vec4(p3, 0, 1); + + vec2 tp0 = t0.xy / t0.w; + vec2 tp1 = t1.xy / t1.w; + vec2 tp2 = t2.xy / t2.w; + vec2 tp3 = t3.xy / t3.w; + + // compute a CSS space aligned bounding box + vec2 min_pos = min(min(tp0.xy, tp1.xy), min(tp2.xy, tp3.xy)); + vec2 max_pos = max(max(tp0.xy, tp1.xy), max(tp2.xy, tp3.xy)); + + // clamp to the tile boundaries, in device space + vec2 min_pos_clamped = clamp(min_pos * uDevicePixelRatio, + area.screen_origin_target_index.xy, + area.screen_origin_target_index.xy + area.task_bounds.zw - area.task_bounds.xy); + + vec2 max_pos_clamped = clamp(max_pos * uDevicePixelRatio, + area.screen_origin_target_index.xy, + area.screen_origin_target_index.xy + area.task_bounds.zw - area.task_bounds.xy); + + // compute the device space position of this vertex + vec2 clamped_pos = mix(min_pos_clamped, + max_pos_clamped, + aPosition.xy); // compute the point position in side the layer, in CSS space - vec2 clamped_pos = final_pos + area.screen_origin_target_index.xy - area.task_bounds.xy; vec4 layer_pos = get_layer_pos(clamped_pos / uDevicePixelRatio, layer); - gl_Position = uTransform * vec4(final_pos, 0.0, 1); + // apply the task offset + vec2 final_pos = clamped_pos - area.screen_origin_target_index.xy + area.task_bounds.xy; + + gl_Position = uTransform * vec4(final_pos, 0.0, 1.0); return TransformVertexInfo(layer_pos.xyw, clamped_pos, clipped_local_rect); } diff --git a/webrender/res/cs_clip_copy.fs.glsl b/webrender/res/cs_clip_copy.fs.glsl deleted file mode 100644 index 3d25b312d2..0000000000 --- a/webrender/res/cs_clip_copy.fs.glsl +++ /dev/null @@ -1,8 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -void main(void) { - float alpha = texelFetch(sCache, ivec3(vClipMaskUv), 0).a; - oFragColor = vec4(alpha, 0.0, 0.0, 1.0); -} diff --git a/webrender/res/cs_clip_copy.vs.glsl b/webrender/res/cs_clip_copy.vs.glsl deleted file mode 100644 index 2b738dc791..0000000000 --- a/webrender/res/cs_clip_copy.vs.glsl +++ /dev/null @@ -1,19 +0,0 @@ -#line 1 -/* This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -void main(void) { - CacheClipInstance cci = fetch_clip_item(gl_InstanceID); - ClipArea area = fetch_clip_area(cci.render_task_index); - ClipArea source = fetch_clip_area(cci.base_task_index); - - vec2 final_pos = mix(area.task_bounds.xy, area.task_bounds.zw, aPosition.xy); - - gl_Position = uTransform * vec4(final_pos, 0.0, 1.0); - - // convert to the source task space via the screen space - vec2 tuv = final_pos - area.task_bounds.xy + area.screen_origin_target_index.xy + - source.task_bounds.xy - source.screen_origin_target_index.xy; - vClipMaskUv = vec3(tuv, source.screen_origin_target_index.z); -} diff --git a/webrender/res/cs_clip_rectangle.fs.glsl b/webrender/res/cs_clip_rectangle.fs.glsl index 695c8b8f35..b8ab43436a 100644 --- a/webrender/res/cs_clip_rectangle.fs.glsl +++ b/webrender/res/cs_clip_rectangle.fs.glsl @@ -3,37 +3,16 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ float rounded_rect(vec2 pos) { - vec2 ref_tl = vClipRect.xy + vec2( vClipRadius.x, vClipRadius.x); - vec2 ref_tr = vClipRect.zy + vec2(-vClipRadius.y, vClipRadius.y); - vec2 ref_br = vClipRect.zw + vec2(-vClipRadius.z, -vClipRadius.z); - vec2 ref_bl = vClipRect.xw + vec2( vClipRadius.w, -vClipRadius.w); - - float d_tl = distance(pos, ref_tl); - float d_tr = distance(pos, ref_tr); - float d_br = distance(pos, ref_br); - float d_bl = distance(pos, ref_bl); - float pixels_per_fragment = length(fwidth(pos.xy)); float nudge = 0.5 * pixels_per_fragment; - vec4 distances = vec4(d_tl, d_tr, d_br, d_bl) - vClipRadius + nudge; - - bvec4 is_out = bvec4(pos.x < ref_tl.x && pos.y < ref_tl.y, - pos.x > ref_tr.x && pos.y < ref_tr.y, - pos.x > ref_br.x && pos.y > ref_br.y, - pos.x < ref_bl.x && pos.y > ref_bl.y); - float distance_from_border = dot(vec4(is_out), - max(vec4(0.0, 0.0, 0.0, 0.0), distances)); + // TODO(gw): Support ellipse clip! + float d = max(0.0, distance(pos, vClipRefPoint_Radius.xy)); + d = (d - vClipRefPoint_Radius.z + nudge) / pixels_per_fragment; - // Move the distance back into pixels. - distance_from_border /= pixels_per_fragment; - // Apply a more gradual fade out to transparent. - //distance_from_border -= 0.5; - - return 1.0 - smoothstep(0.0, 1.0, distance_from_border); + return 1.0 - smoothstep(0.0, 1.0, d); } - void main(void) { float alpha = 1.f; vec2 local_pos = init_transform_fs(vPos, vLocalRect, alpha); diff --git a/webrender/res/cs_clip_rectangle.glsl b/webrender/res/cs_clip_rectangle.glsl index 9e73553cc8..305d6fea2a 100644 --- a/webrender/res/cs_clip_rectangle.glsl +++ b/webrender/res/cs_clip_rectangle.glsl @@ -7,4 +7,4 @@ varying vec3 vPos; flat varying vec4 vLocalRect; flat varying vec4 vClipRect; -flat varying vec4 vClipRadius; +flat varying vec4 vClipRefPoint_Radius; diff --git a/webrender/res/cs_clip_rectangle.vs.glsl b/webrender/res/cs_clip_rectangle.vs.glsl index e81ab59e33..bacec0704e 100644 --- a/webrender/res/cs_clip_rectangle.vs.glsl +++ b/webrender/res/cs_clip_rectangle.vs.glsl @@ -3,26 +3,9 @@ * 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/. */ -struct ClipRect { - vec4 rect; - vec4 dummy; -}; - -ClipRect fetch_clip_rect(int index) { - ClipRect rect; - - ivec2 uv = get_fetch_uv_2(index); - - rect.rect = texelFetchOffset(sData32, uv, 0, ivec2(0, 0)); - //rect.dummy = texelFetchOffset(sData32, uv, 0, ivec2(1, 0)); - rect.dummy = vec4(0.0, 0.0, 0.0, 0.0); - - return rect; -} - struct ClipCorner { vec4 rect; - vec4 outer_inner_radius; + vec4 clip_ref_radius; }; ClipCorner fetch_clip_corner(int index) { @@ -31,37 +14,17 @@ ClipCorner fetch_clip_corner(int index) { ivec2 uv = get_fetch_uv_2(index); corner.rect = texelFetchOffset(sData32, uv, 0, ivec2(0, 0)); - corner.outer_inner_radius = texelFetchOffset(sData32, uv, 0, ivec2(1, 0)); + corner.clip_ref_radius = texelFetchOffset(sData32, uv, 0, ivec2(1, 0)); return corner; } -struct ClipData { - ClipRect rect; - ClipCorner top_left; - ClipCorner top_right; - ClipCorner bottom_left; - ClipCorner bottom_right; -}; - -ClipData fetch_clip(int index) { - ClipData clip; - - clip.rect = fetch_clip_rect(index + 0); - clip.top_left = fetch_clip_corner(index + 1); - clip.top_right = fetch_clip_corner(index + 2); - clip.bottom_left = fetch_clip_corner(index + 3); - clip.bottom_right = fetch_clip_corner(index + 4); - - return clip; -} - void main(void) { CacheClipInstance cci = fetch_clip_item(gl_InstanceID); ClipArea area = fetch_clip_area(cci.render_task_index); Layer layer = fetch_layer(cci.layer_index); - ClipData clip = fetch_clip(cci.data_index); - vec4 local_rect = clip.rect.rect; + ClipCorner clip = fetch_clip_corner(cci.data_index); + vec4 local_rect = clip.rect; TransformVertexInfo vi = write_clip_tile_vertex(local_rect, layer, @@ -70,8 +33,5 @@ void main(void) { vPos = vi.local_pos; vClipRect = vec4(local_rect.xy, local_rect.xy + local_rect.zw); - vClipRadius = vec4(clip.top_left.outer_inner_radius.x, - clip.top_right.outer_inner_radius.x, - clip.bottom_right.outer_inner_radius.x, - clip.bottom_left.outer_inner_radius.x); + vClipRefPoint_Radius = clip.clip_ref_radius; } diff --git a/webrender/src/device.rs b/webrender/src/device.rs index 8bef2deef3..d1a463e515 100644 --- a/webrender/src/device.rs +++ b/webrender/src/device.rs @@ -268,7 +268,6 @@ impl VertexFormat { for (i, &attrib) in [ClipAttribute::RenderTaskIndex, ClipAttribute::LayerIndex, ClipAttribute::DataIndex, - ClipAttribute::BaseTaskIndex, ].into_iter().enumerate() { gl::enable_vertex_attrib_array(attrib as gl::GLuint); gl::vertex_attrib_divisor(attrib as gl::GLuint, 1); @@ -401,7 +400,6 @@ impl Program { gl::bind_attrib_location(self.id, ClipAttribute::RenderTaskIndex as gl::GLuint, "aClipRenderTaskIndex"); gl::bind_attrib_location(self.id, ClipAttribute::LayerIndex as gl::GLuint, "aClipLayerIndex"); gl::bind_attrib_location(self.id, ClipAttribute::DataIndex as gl::GLuint, "aClipDataIndex"); - gl::bind_attrib_location(self.id, ClipAttribute::BaseTaskIndex as gl::GLuint, "aClipBaseTaskIndex"); gl::link_program(self.id); if gl::get_program_iv(self.id, gl::LINK_STATUS) == (0 as gl::GLint) { diff --git a/webrender/src/internal_types.rs b/webrender/src/internal_types.rs index e37edbc354..46b4d1cb5a 100644 --- a/webrender/src/internal_types.rs +++ b/webrender/src/internal_types.rs @@ -266,7 +266,6 @@ pub enum ClipAttribute { RenderTaskIndex, LayerIndex, DataIndex, - BaseTaskIndex, } #[derive(Debug, Clone, Copy)] diff --git a/webrender/src/mask_cache.rs b/webrender/src/mask_cache.rs index fd458bbfe8..57812d5ed4 100644 --- a/webrender/src/mask_cache.rs +++ b/webrender/src/mask_cache.rs @@ -3,14 +3,15 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use gpu_store::{GpuStore, GpuStoreAddress}; -use prim_store::{ClipData, GpuBlock32, PrimitiveStore}; -use prim_store::{CLIP_DATA_GPU_SIZE, MASK_DATA_GPU_SIZE}; +use prim_store::{ClipData, GpuBlock32}; use util::{rect_from_points_f, TransformedRect}; -use webrender_traits::{AuxiliaryLists, BorderRadius, ClipRegion, ComplexClipRegion, ImageMask}; -use webrender_traits::{DeviceIntRect, DeviceIntSize, LayerRect, LayerToWorldTransform}; +use webrender_traits::{AuxiliaryLists, ClipRegion, ImageMask}; +use webrender_traits::{DeviceIntRect, LayerRect, LayerToWorldTransform}; const MAX_COORD: f32 = 1.0e+16; +pub const CORNERS_PER_CLIP_REGION: usize = 4; + #[derive(Clone, Debug)] pub enum ClipSource { NoClip, @@ -37,20 +38,31 @@ impl<'a> From<&'a ClipRegion> for ClipSource { } } -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub struct ClipAddressRange { - pub start: GpuStoreAddress, - pub item_count: u32, +#[derive(Clone, Debug)] +pub struct ImageMaskComponent { + pub gpu_address: GpuStoreAddress, + pub image_mask: ImageMask, +} + +#[derive(Clone, Debug)] +pub struct CornerMaskComponent { + pub gpu_address: GpuStoreAddress, + + // TODO(gw): We will store a local rect and world + // bounding rect for each clip component. + // This is how we can work out the region + // of interest for each primitive with + // a complex clip region, and then we + // will only allocate those regions of + // clip interest in the mask rendering. } #[derive(Clone, Debug)] pub struct MaskCacheInfo { - pub clip_range: ClipAddressRange, - pub image: Option<(ImageMask, GpuStoreAddress)>, pub local_rect: Option, - pub local_inner: Option, - pub inner_rect: DeviceIntRect, - pub outer_rect: DeviceIntRect, + pub bounding_rect: DeviceIntRect, + pub corner_components: Vec, + pub image_components: Vec, } impl MaskCacheInfo { @@ -59,38 +71,41 @@ impl MaskCacheInfo { pub fn new(source: &ClipSource, clip_store: &mut GpuStore) -> Option { - let (image, clip_range) = match source { - &ClipSource::NoClip => return None, - &ClipSource::Complex(..) => ( - None, - ClipAddressRange { - start: clip_store.alloc(CLIP_DATA_GPU_SIZE), - item_count: 1, + let mut mask = MaskCacheInfo { + corner_components: Vec::new(), + image_components: Vec::new(), + local_rect: None, + bounding_rect: DeviceIntRect::zero(), + }; + + match source { + &ClipSource::NoClip => { + return None; + } + &ClipSource::Complex(..) => { + for _ in 0..CORNERS_PER_CLIP_REGION { + mask.corner_components.push(CornerMaskComponent { + gpu_address: clip_store.alloc(1), + }); } - ), - &ClipSource::Region(ref region) => ( - region.image_mask.map(|info| - (info, clip_store.alloc(MASK_DATA_GPU_SIZE)) - ), - ClipAddressRange { - start: if region.complex.length > 0 { - clip_store.alloc(CLIP_DATA_GPU_SIZE * region.complex.length) - } else { - GpuStoreAddress(0) - }, - item_count: region.complex.length as u32, + } + &ClipSource::Region(ref region) => { + if let Some(image_mask) = region.image_mask { + mask.image_components.push(ImageMaskComponent { + gpu_address: clip_store.alloc(1), + image_mask: image_mask, + }); } - ), + + for _ in 0..region.complex.length * CORNERS_PER_CLIP_REGION { + mask.corner_components.push(CornerMaskComponent { + gpu_address: clip_store.alloc(1), + }); + } + } }; - Some(MaskCacheInfo { - clip_range: clip_range, - image: image, - local_rect: None, - local_inner: None, - inner_rect: DeviceIntRect::zero(), - outer_rect: DeviceIntRect::zero(), - }) + Some(mask) } pub fn update(&mut self, @@ -99,59 +114,38 @@ impl MaskCacheInfo { clip_store: &mut GpuStore, device_pixel_ratio: f32, aux_lists: &AuxiliaryLists) { - if self.local_rect.is_none() { let mut local_rect; - let mut local_inner: Option; match source { &ClipSource::NoClip => unreachable!(), &ClipSource::Complex(rect, radius) => { - let slice = clip_store.get_slice_mut(self.clip_range.start, CLIP_DATA_GPU_SIZE); let data = ClipData::uniform(rect, radius); - PrimitiveStore::populate_clip_data(slice, data); - debug_assert_eq!(self.clip_range.item_count, 1); + for (corner, component) in data.corners.iter().zip(self.corner_components.iter()) { + let gpu_block = clip_store.get_mut(component.gpu_address); + *gpu_block = GpuBlock32::from(*corner); + } local_rect = Some(rect); - local_inner = ComplexClipRegion::new(rect, BorderRadius::uniform(radius)) - .get_inner_rect(); } &ClipSource::Region(ref region) => { local_rect = Some(LayerRect::from_untyped(&rect_from_points_f(-MAX_COORD, -MAX_COORD, MAX_COORD, MAX_COORD))); - local_inner = match region.image_mask { - Some(ref mask) if !mask.repeat => { - local_rect = local_rect.and_then(|r| r.intersection(&mask.rect)); - None - }, - Some(_) => None, - None => local_rect, - }; let clips = aux_lists.complex_clip_regions(®ion.complex); - assert_eq!(self.clip_range.item_count, clips.len() as u32); - let slice = clip_store.get_slice_mut(self.clip_range.start, CLIP_DATA_GPU_SIZE * clips.len()); - for (clip, chunk) in clips.iter().zip(slice.chunks_mut(CLIP_DATA_GPU_SIZE)) { + debug_assert_eq!(self.corner_components.len(), clips.len() * CORNERS_PER_CLIP_REGION); + for (clip, chunk) in clips.iter().zip(self.corner_components.chunks_mut(CORNERS_PER_CLIP_REGION)) { let data = ClipData::from_clip_region(clip); - PrimitiveStore::populate_clip_data(chunk, data); + for (corner, component) in data.corners.iter().zip(chunk) { + let gpu_block = clip_store.get_mut(component.gpu_address); + *gpu_block = GpuBlock32::from(*corner); + } local_rect = local_rect.and_then(|r| r.intersection(&clip.rect)); - local_inner = local_inner.and_then(|r| clip.get_inner_rect() - .and_then(|ref inner| r.intersection(&inner))); } } }; self.local_rect = Some(local_rect.unwrap_or(LayerRect::zero())); - self.local_inner = local_inner; } let transformed = TransformedRect::new(self.local_rect.as_ref().unwrap(), &transform, device_pixel_ratio); - self.outer_rect = transformed.bounding_rect; - - self.inner_rect = if let Some(ref inner_rect) = self.local_inner { - let transformed = TransformedRect::new(inner_rect, - &transform, - device_pixel_ratio); - transformed.inner_rect - } else { - DeviceIntRect::new(self.outer_rect.origin, DeviceIntSize::zero()) - } + self.bounding_rect = transformed.bounding_rect; } } diff --git a/webrender/src/prim_store.rs b/webrender/src/prim_store.rs index c36343b033..5f4207879d 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -20,9 +20,6 @@ use webrender_traits::{DeviceRect, DevicePoint, DeviceSize}; use webrender_traits::{LayerRect, LayerSize, LayerPoint}; use webrender_traits::LayerToWorldTransform; -pub const CLIP_DATA_GPU_SIZE: usize = 5; -pub const MASK_DATA_GPU_SIZE: usize = 1; - /// Stores two coordinates in texel space. The coordinates /// are stored in texel coordinates because the texture atlas /// may grow. Storing them as texel coords and normalizing @@ -112,6 +109,7 @@ pub struct PrimitiveMetadata { // that implements a 2-pass separable blur on a // text run. pub render_task: Option, + pub clip_task: Option, } #[derive(Debug, Clone)] @@ -281,31 +279,26 @@ struct GlyphPrimitive { padding: LayerPoint, } -#[derive(Debug, Clone)] +#[derive(Debug, Copy, Clone)] #[repr(C)] -struct ClipRect { - rect: LayerRect, - padding: [f32; 4], -} - -#[derive(Debug, Clone)] -#[repr(C)] -struct ClipCorner { - rect: LayerRect, - outer_radius_x: f32, - outer_radius_y: f32, - inner_radius_x: f32, - inner_radius_y: f32, +pub struct ClipCorner { + pub rect: LayerRect, + pub clip_ref_x: f32, + pub clip_ref_y: f32, + pub radius_x: f32, + pub radius_y: f32, } impl ClipCorner { - fn uniform(rect: LayerRect, outer_radius: f32, inner_radius: f32) -> ClipCorner { + fn uniform(rect: LayerRect, + radius: f32, + clip_ref: LayerPoint) -> ClipCorner { ClipCorner { rect: rect, - outer_radius_x: outer_radius, - outer_radius_y: outer_radius, - inner_radius_x: inner_radius, - inner_radius_y: inner_radius, + radius_x: radius, + radius_y: radius, + clip_ref_x: clip_ref.x, + clip_ref_y: clip_ref.y, } } } @@ -319,86 +312,72 @@ pub struct ImageMaskData { #[derive(Debug, Clone)] pub struct ClipData { - rect: ClipRect, - top_left: ClipCorner, - top_right: ClipCorner, - bottom_left: ClipCorner, - bottom_right: ClipCorner, + pub corners: [ClipCorner; 4], } impl ClipData { pub fn from_clip_region(clip: &ComplexClipRegion) -> ClipData { + let rect_tl = LayerRect::new(LayerPoint::new(clip.rect.origin.x, clip.rect.origin.y), + LayerSize::new(clip.radii.top_left.width, clip.radii.top_left.height)); + let rect_tr = LayerRect::new(LayerPoint::new(clip.rect.origin.x + clip.rect.size.width - clip.radii.top_right.width, clip.rect.origin.y), + LayerSize::new(clip.radii.top_right.width, clip.radii.top_right.height)); + let rect_bl = LayerRect::new(LayerPoint::new(clip.rect.origin.x, clip.rect.origin.y + clip.rect.size.height - clip.radii.bottom_left.height), + LayerSize::new(clip.radii.bottom_left.width, clip.radii.bottom_left.height)); + let rect_br = LayerRect::new(LayerPoint::new(clip.rect.origin.x + clip.rect.size.width - clip.radii.bottom_right.width, + clip.rect.origin.y + clip.rect.size.height - clip.radii.bottom_right.height), + LayerSize::new(clip.radii.bottom_right.width, clip.radii.bottom_right.height)); + ClipData { - rect: ClipRect { - rect: clip.rect, - padding: [0.0, 0.0, 0.0, 0.0], - }, - top_left: ClipCorner { - rect: LayerRect::new( - LayerPoint::new(clip.rect.origin.x, clip.rect.origin.y), - LayerSize::new(clip.radii.top_left.width, clip.radii.top_left.height)), - outer_radius_x: clip.radii.top_left.width, - outer_radius_y: clip.radii.top_left.height, - inner_radius_x: 0.0, - inner_radius_y: 0.0, - }, - top_right: ClipCorner { - rect: LayerRect::new( - LayerPoint::new(clip.rect.origin.x + clip.rect.size.width - clip.radii.top_right.width, clip.rect.origin.y), - LayerSize::new(clip.radii.top_right.width, clip.radii.top_right.height)), - outer_radius_x: clip.radii.top_right.width, - outer_radius_y: clip.radii.top_right.height, - inner_radius_x: 0.0, - inner_radius_y: 0.0, - }, - bottom_left: ClipCorner { - rect: LayerRect::new( - LayerPoint::new(clip.rect.origin.x, clip.rect.origin.y + clip.rect.size.height - clip.radii.bottom_left.height), - LayerSize::new(clip.radii.bottom_left.width, clip.radii.bottom_left.height)), - outer_radius_x: clip.radii.bottom_left.width, - outer_radius_y: clip.radii.bottom_left.height, - inner_radius_x: 0.0, - inner_radius_y: 0.0, - }, - bottom_right: ClipCorner { - rect: LayerRect::new( - LayerPoint::new(clip.rect.origin.x + clip.rect.size.width - clip.radii.bottom_right.width, - clip.rect.origin.y + clip.rect.size.height - clip.radii.bottom_right.height), - LayerSize::new(clip.radii.bottom_right.width, clip.radii.bottom_right.height)), - outer_radius_x: clip.radii.bottom_right.width, - outer_radius_y: clip.radii.bottom_right.height, - inner_radius_x: 0.0, - inner_radius_y: 0.0, - }, + corners: [ + ClipCorner { + rect: rect_tl, + radius_x: clip.radii.top_left.width, + radius_y: clip.radii.top_left.height, + clip_ref_x: rect_tl.origin.x + rect_tl.size.width, + clip_ref_y: rect_tl.origin.y + rect_tl.size.height, + }, + ClipCorner { + rect: rect_tr, + radius_x: clip.radii.top_right.width, + radius_y: clip.radii.top_right.height, + clip_ref_x: rect_tr.origin.x, + clip_ref_y: rect_tr.origin.y + rect_tr.size.height, + }, + ClipCorner { + rect: rect_bl, + radius_x: clip.radii.bottom_left.width, + radius_y: clip.radii.bottom_left.height, + clip_ref_x: rect_bl.origin.x + rect_bl.size.width, + clip_ref_y: rect_bl.origin.y, + }, + ClipCorner { + rect: rect_br, + radius_x: clip.radii.bottom_right.width, + radius_y: clip.radii.bottom_right.height, + clip_ref_x: rect_br.origin.x, + clip_ref_y: rect_br.origin.y, + }, + ], } } pub fn uniform(rect: LayerRect, radius: f32) -> ClipData { + let rect_tl = LayerRect::new(LayerPoint::new(rect.origin.x, rect.origin.y), + LayerSize::new(radius, radius)); + let rect_tr = LayerRect::new(LayerPoint::new(rect.origin.x + rect.size.width - radius, rect.origin.y), + LayerSize::new(radius, radius)); + let rect_bl = LayerRect::new(LayerPoint::new(rect.origin.x, rect.origin.y + rect.size.height - radius), + LayerSize::new(radius, radius)); + let rect_br = LayerRect::new(LayerPoint::new(rect.origin.x + rect.size.width - radius, rect.origin.y + rect.size.height - radius), + LayerSize::new(radius, radius)); + ClipData { - rect: ClipRect { - rect: rect, - padding: [0.0; 4], - }, - top_left: ClipCorner::uniform( - LayerRect::new( - LayerPoint::new(rect.origin.x, rect.origin.y), - LayerSize::new(radius, radius)), - radius, 0.0), - top_right: ClipCorner::uniform( - LayerRect::new( - LayerPoint::new(rect.origin.x + rect.size.width - radius, rect.origin.y), - LayerSize::new(radius, radius)), - radius, 0.0), - bottom_left: ClipCorner::uniform( - LayerRect::new( - LayerPoint::new(rect.origin.x, rect.origin.y + rect.size.height - radius), - LayerSize::new(radius, radius)), - radius, 0.0), - bottom_right: ClipCorner::uniform( - LayerRect::new( - LayerPoint::new(rect.origin.x + rect.size.width - radius, rect.origin.y + rect.size.height - radius), - LayerSize::new(radius, radius)), - radius, 0.0), + corners: [ + ClipCorner::uniform(rect_tl, radius, rect_tl.origin), + ClipCorner::uniform(rect_tr, radius, rect_tr.bottom_left()), + ClipCorner::uniform(rect_bl, radius, rect_bl.top_right()), + ClipCorner::uniform(rect_br, radius, rect_br.bottom_right()), + ], } } } @@ -458,14 +437,6 @@ impl PrimitiveStore { } } - pub fn populate_clip_data(data: &mut [GpuBlock32], clip: ClipData) { - data[0] = GpuBlock32::from(clip.rect); - data[1] = GpuBlock32::from(clip.top_left); - data[2] = GpuBlock32::from(clip.top_right); - data[3] = GpuBlock32::from(clip.bottom_left); - data[4] = GpuBlock32::from(clip.bottom_right); - } - pub fn add_primitive(&mut self, geometry: PrimitiveGeometry, clip_source: Box, @@ -490,6 +461,7 @@ impl PrimitiveStore { gpu_data_address: GpuStoreAddress(0), gpu_data_count: 0, render_task: None, + clip_task: None, }; metadata @@ -509,6 +481,7 @@ impl PrimitiveStore { gpu_data_address: gpu_glyphs_address, gpu_data_count: text_cpu.glyph_range.length as i32, render_task: None, + clip_task: None, }; self.cpu_text_runs.push(text_cpu); @@ -529,6 +502,7 @@ impl PrimitiveStore { gpu_data_address: GpuStoreAddress(0), gpu_data_count: 0, render_task: None, + clip_task: None, }; self.cpu_images.push(image_cpu); @@ -547,6 +521,7 @@ impl PrimitiveStore { gpu_data_address: GpuStoreAddress(0), gpu_data_count: 0, render_task: None, + clip_task: None, }; self.cpu_yuv_images.push(image_cpu); @@ -565,6 +540,7 @@ impl PrimitiveStore { gpu_data_address: GpuStoreAddress(0), gpu_data_count: 0, render_task: None, + clip_task: None, }; self.cpu_borders.push(border_cpu); @@ -584,6 +560,7 @@ impl PrimitiveStore { gpu_data_address: gpu_stops_address, gpu_data_count: gradient_cpu.stops_range.length as i32, render_task: None, + clip_task: None, }; self.cpu_gradients.push(gradient_cpu); @@ -628,6 +605,7 @@ impl PrimitiveStore { gpu_data_address: gpu_data_address, gpu_data_count: instance_rects.len() as i32, render_task: Some(render_task), + clip_task: None, }; for rect in instance_rects { @@ -648,14 +626,14 @@ impl PrimitiveStore { fn resolve_clip_cache_internal(gpu_data32: &mut GpuStore, clip_info: &MaskCacheInfo, resource_cache: &ResourceCache) { - if let Some((ref mask, gpu_address)) = clip_info.image { - let cache_item = resource_cache.get_cached_image(mask.image, ImageRendering::Auto); - let mask_data = gpu_data32.get_slice_mut(gpu_address, MASK_DATA_GPU_SIZE); - mask_data[0] = GpuBlock32::from(ImageMaskData { + for mask in &clip_info.image_components { + let cache_item = resource_cache.get_cached_image(mask.image_mask.image, ImageRendering::Auto); + let mask_data = gpu_data32.get_mut(mask.gpu_address); + *mask_data = GpuBlock32::from(ImageMaskData { uv_rect: DeviceRect::new(cache_item.uv0, DeviceSize::new(cache_item.uv1.x - cache_item.uv0.x, cache_item.uv1.y - cache_item.uv0.y)), - local_rect: mask.rect, + local_rect: mask.image_mask.rect, }); } } @@ -1115,14 +1093,6 @@ impl From for GpuBlock64 { } } -impl From for GpuBlock32 { - fn from(data: ClipRect) -> GpuBlock32 { - unsafe { - mem::transmute::(data) - } - } -} - impl From for GpuBlock32 { fn from(data: ImageMaskData) -> GpuBlock32 { unsafe { diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index 7133cda25f..d0d5b6a659 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -288,7 +288,6 @@ pub struct Renderer { /// These are "cache clip shaders". These shaders are used to /// draw clip instances into the cached clip mask. The results /// of these shaders are also used by the primitive shaders. - cs_clip_copy: LazilyCompiledShader, cs_clip_rectangle: LazilyCompiledShader, cs_clip_image: LazilyCompiledShader, @@ -419,11 +418,6 @@ impl Renderer { &mut device, options.precache_shaders); - let cs_clip_copy = LazilyCompiledShader::new(ShaderKind::ClipCache, - "cs_clip_copy", - &[], - &mut device, - options.precache_shaders); let cs_clip_rectangle = LazilyCompiledShader::new(ShaderKind::ClipCache, "cs_clip_rectangle", &[], @@ -617,7 +611,6 @@ impl Renderer { cs_box_shadow: cs_box_shadow, cs_text_run: cs_text_run, cs_blur: cs_blur, - cs_clip_copy: cs_clip_copy, cs_clip_rectangle: cs_clip_rectangle, cs_clip_image: cs_clip_image, ps_rectangle: ps_rectangle, @@ -1138,17 +1131,7 @@ impl Renderer { { let _gm = self.gpu_profile.add_marker(GPU_TAG_CACHE_CLIP); let vao = self.clip_vao_id; - // Optionally, copy the contents from another task - if !target.clip_batcher.copies.is_empty() { - self.device.set_blend(false); - let shader = self.cs_clip_copy.get(&mut self.device); - self.draw_instanced_batch(&target.clip_batcher.copies, - vao, - shader, - &BatchTextures::no_texture(), - &projection); - } - // now switch to multiplicative blending + // switch to multiplicative blending self.device.set_blend(true); self.device.set_blend_mode_multiply(); // draw rounded cornered rectangles diff --git a/webrender/src/scene.rs b/webrender/src/scene.rs index bcf7213342..703c4295ef 100644 --- a/webrender/src/scene.rs +++ b/webrender/src/scene.rs @@ -7,8 +7,7 @@ use std::collections::HashMap; use std::hash::BuildHasherDefault; use tiling::AuxiliaryListsMap; use webrender_traits::{AuxiliaryLists, BuiltDisplayList, PipelineId, Epoch, ColorF}; -use webrender_traits::{DisplayItem, SpecificDisplayItem, StackingContext}; -use webrender_traits::LayerSize; +use webrender_traits::{DisplayItem, LayerSize}; /// A representation of the layout within the display port for a given document or iframe. #[derive(Debug)] diff --git a/webrender/src/tiling.rs b/webrender/src/tiling.rs index f119ee8b23..cac37207a6 100644 --- a/webrender/src/tiling.rs +++ b/webrender/src/tiling.rs @@ -15,7 +15,7 @@ use prim_store::{PrimitiveGeometry, RectanglePrimitive, PrimitiveContainer}; use prim_store::{BorderPrimitiveCpu, BorderPrimitiveGpu, BoxShadowPrimitiveGpu}; use prim_store::{ImagePrimitiveCpu, ImagePrimitiveGpu, YuvImagePrimitiveCpu, YuvImagePrimitiveGpu, ImagePrimitiveKind, }; use prim_store::{PrimitiveKind, PrimitiveIndex, PrimitiveMetadata, TexelRect}; -use prim_store::{CLIP_DATA_GPU_SIZE, DeferredResolve}; +use prim_store::{DeferredResolve}; use prim_store::{GradientPrimitiveCpu, GradientPrimitiveGpu, GradientType}; use prim_store::{PrimitiveCacheKey, TextRunPrimitiveGpu, TextRunPrimitiveCpu}; use prim_store::{PrimitiveStore, GpuBlock16, GpuBlock32, GpuBlock64, GpuBlock128}; @@ -42,10 +42,6 @@ use webrender_traits::{LayerRect, LayerPoint, LayerSize}; use webrender_traits::{LayerToScrollTransform, LayerToWorldTransform, WorldToLayerTransform}; use webrender_traits::{WorldPoint4D, ScrollLayerPixel, as_scroll_parent_rect}; -// Removes the clip task dependencies and instead -// draws all the clip instances that affect a primitive -const CLIP_TASK_COLLAPSE: bool = true; - // Special sentinel value recognized by the shader. It is considered to be // a dummy task that doesn't mask out anything. const OPAQUE_TASK_INDEX: RenderTaskIndex = RenderTaskIndex(i32::MAX as usize); @@ -73,8 +69,6 @@ trait AlphaBatchHelpers { batch: &mut PrimitiveBatch, layer_index: StackingContextIndex, task_index: RenderTaskIndex, - tile_id: TileUniqueId, - base_mask_task_index: RenderTaskIndex, render_tasks: &RenderTaskCollection, pass_index: RenderPassIndex, z_sort_index: i32); @@ -191,8 +185,6 @@ impl AlphaBatchHelpers for PrimitiveStore { batch: &mut PrimitiveBatch, layer_index: StackingContextIndex, task_index: RenderTaskIndex, - tile_id: TileUniqueId, - base_mask_task_index: RenderTaskIndex, render_tasks: &RenderTaskCollection, child_pass_index: RenderPassIndex, z_sort_index: i32) { @@ -200,13 +192,12 @@ impl AlphaBatchHelpers for PrimitiveStore { let layer_index = layer_index.0 as i32; let global_prim_id = prim_index.0 as i32; let prim_address = metadata.gpu_prim_index; - let clip_task_key = RenderTaskKey::CacheMask(MaskCacheKey::Primitive(prim_index), tile_id); - let clip_task_index = if metadata.clip_cache_info.is_some() && - render_tasks.has_dynamic_task(&clip_task_key, child_pass_index) { + let clip_task_key = RenderTaskKey::CacheMask(MaskCacheKey::Primitive(prim_index)); + let clip_task_index = if metadata.clip_task.is_some() { let cache_task_id = RenderTaskId::Dynamic(clip_task_key); render_tasks.get_task_index(&cache_task_id, child_pass_index) } else { - base_mask_task_index + OPAQUE_TASK_INDEX }; let task_index = task_index.0 as i32; let clip_task_index = clip_task_index.0 as i32; @@ -386,12 +377,9 @@ struct RenderPassIndex(isize); #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)] pub struct RenderTaskIndex(usize); -type TileUniqueId = usize; - #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub enum MaskCacheKey { Primitive(PrimitiveIndex), - Layer(StackingContextIndex), } @@ -400,7 +388,7 @@ pub enum RenderTaskKey { /// Draw this primitive to a cache target. CachePrimitive(PrimitiveCacheKey), /// Draw the tile alpha mask for a primitive. - CacheMask(MaskCacheKey, TileUniqueId), + CacheMask(MaskCacheKey), /// Apply a vertical blur pass of given radius for this primitive. VerticalBlur(i32, PrimitiveIndex), /// Apply a horizontal blur pass of given radius for this primitive. @@ -476,11 +464,6 @@ impl RenderTaskCollection { } } } - - fn has_dynamic_task(&self, key: &RenderTaskKey, pass_index: RenderPassIndex) -> bool { - //TODO: remove clone - self.dynamic_tasks.contains_key(&(key.clone(), pass_index)) - } } #[derive(Debug, Clone)] @@ -517,7 +500,6 @@ struct AlphaBatchTask { task_id: RenderTaskId, opaque_items: Vec, alpha_items: Vec, - tile_id: TileUniqueId, } /// Encapsulates the logic of building batches for items that are blended. @@ -565,8 +547,7 @@ impl AlphaBatcher { let layer = &ctx.layer_store[sc_index.0]; let prim_metadata = ctx.prim_store.get_metadata(prim_index); let transform_kind = layer.xf_rect.as_ref().unwrap().kind; - let needs_clipping = prim_metadata.clip_cache_info.is_some() || - ctx.layer_masks_tasks.get(&(task.tile_id, sc_index)).is_some(); + let needs_clipping = prim_metadata.clip_task.is_some(); let needs_blending = transform_kind == TransformedRectKind::Complex || !prim_metadata.is_opaque || needs_clipping; @@ -634,16 +615,10 @@ impl AlphaBatcher { debug_assert!(ok) } &AlphaRenderItem::Primitive(sc_index, prim_index, z) => { - let mask_task_index = match ctx.layer_masks_tasks.get(&(task.tile_id, sc_index)) { - Some(ref mask_task_id) => render_tasks.get_task_index(mask_task_id, child_pass_index), - None => OPAQUE_TASK_INDEX, - }; ctx.prim_store.add_prim_to_batch(prim_index, batch, sc_index, task_index, - task.tile_id, - mask_task_index, render_tasks, child_pass_index, z); @@ -660,8 +635,7 @@ impl AlphaBatcher { let layer = &ctx.layer_store[sc_index.0]; let prim_metadata = ctx.prim_store.get_metadata(prim_index); let transform_kind = layer.xf_rect.as_ref().unwrap().kind; - let needs_clipping = prim_metadata.clip_cache_info.is_some() || - ctx.layer_masks_tasks.get(&(task.tile_id, sc_index)).is_some(); + let needs_clipping = prim_metadata.clip_task.is_some(); let needs_blending = transform_kind == TransformedRectKind::Complex || !prim_metadata.is_opaque || needs_clipping; @@ -712,16 +686,10 @@ impl AlphaBatcher { &AlphaRenderItem::Composite(..) => unreachable!(), &AlphaRenderItem::Blend(..) => unreachable!(), &AlphaRenderItem::Primitive(sc_index, prim_index, z) => { - let mask_task_index = match ctx.layer_masks_tasks.get(&(task.tile_id, sc_index)) { - Some(ref mask_task_id) => render_tasks.get_task_index(mask_task_id, child_pass_index), - None => OPAQUE_TASK_INDEX, - }; ctx.prim_store.add_prim_to_batch(prim_index, batch, sc_index, task_index, - task.tile_id, - mask_task_index, render_tasks, child_pass_index, z); @@ -738,8 +706,6 @@ impl AlphaBatcher { /// Batcher managing draw calls into the clip mask (in the RT cache). #[derive(Debug)] pub struct ClipBatcher { - /// Copy draws get the existing mask from a parent layer. - pub copies: Vec, /// Rectangle draws fill up the rectangles with rounded corners. pub rectangles: Vec, /// Image draws apply the image masking. @@ -749,7 +715,6 @@ pub struct ClipBatcher { impl ClipBatcher { fn new() -> ClipBatcher { ClipBatcher { - copies: Vec::new(), rectangles: Vec::new(), images: HashMap::new(), } @@ -757,7 +722,6 @@ impl ClipBatcher { fn add(&mut self, task_index: RenderTaskIndex, - base_task_index: Option, clips: &[(StackingContextIndex, MaskCacheInfo)], resource_cache: &ResourceCache) { @@ -767,33 +731,22 @@ impl ClipBatcher { task_id: task_index.0 as i32, layer_index: layer_id.0 as i32, address: GpuStoreAddress(0), - base_task_id: 0, }; - // copy on the first clip only - if info as *const _ == &clips[0].1 as *const _ { - if let Some(layer_task_id) = base_task_index { - self.copies.push(CacheClipInstance { - base_task_id: layer_task_id.0 as i32, - ..instance - }); - } - } - self.rectangles.extend((0 .. info.clip_range.item_count as usize) - .map(|region_id| { - let offset = info.clip_range.start.0 + ((CLIP_DATA_GPU_SIZE * region_id) as i32); - CacheClipInstance { - address: GpuStoreAddress(offset), + for corner in &info.corner_components { + self.rectangles.push(CacheClipInstance { + address: corner.gpu_address, ..instance - } - })); + }); + } - if let Some((ref mask, address)) = info.image { - let cache_item = resource_cache.get_cached_image(mask.image, ImageRendering::Auto); + for mask in &info.image_components { + let cache_item = resource_cache.get_cached_image(mask.image_mask.image, + ImageRendering::Auto); self.images.entry(cache_item.texture_id) .or_insert(Vec::new()) .push(CacheClipInstance { - address: address, + address: mask.gpu_address, ..instance }) } @@ -805,7 +758,6 @@ impl ClipBatcher { struct CompileTileContext<'a> { layer_store: &'a [StackingContext], prim_store: &'a PrimitiveStore, - tile_id: TileUniqueId, render_task_id_counter: AtomicUsize, } @@ -813,7 +765,6 @@ struct RenderTargetContext<'a> { layer_store: &'a [StackingContext], prim_store: &'a PrimitiveStore, resource_cache: &'a ResourceCache, - layer_masks_tasks: HashMap<(TileUniqueId, StackingContextIndex), RenderTaskId>, } /// A render target represents a number of rendering operations on a surface. @@ -872,7 +823,6 @@ impl RenderTarget { task_id: task.id, opaque_items: info.opaque_items, alpha_items: info.alpha_items, - tile_id: info.tile_id, }); } RenderTaskKind::VerticalBlur(_, prim_index) => { @@ -957,10 +907,7 @@ impl RenderTarget { } RenderTaskKind::CacheMask(ref task_info) => { let task_index = render_tasks.get_task_index(&task.id, pass_index); - let base_task_id = task_info.base_task_id.map(|ref task_id| - render_tasks.get_task_index(task_id, pass_index) - ); - self.clip_batcher.add(task_index, base_task_id, + self.clip_batcher.add(task_index, &task_info.clips, &ctx.resource_cache); } @@ -1004,7 +951,7 @@ impl RenderPass { let mut new_target = RenderTarget::new(); let origin = new_target.page_allocator .allocate(&size) - .expect("Each render task must allocate <= size of one target!"); + .expect(&format!("Each render task must allocate <= size of one target! ({:?})", size)); targets.push(new_target); origin } @@ -1079,13 +1026,11 @@ pub struct AlphaRenderTask { actual_rect: DeviceIntRect, opaque_items: Vec, alpha_items: Vec, - tile_id: TileUniqueId, } #[derive(Debug, Clone)] pub struct CacheMaskTask { actual_rect: DeviceIntRect, - base_task_id: Option, clips: Vec<(StackingContextIndex, MaskCacheInfo)>, } @@ -1093,8 +1038,6 @@ pub struct CacheMaskTask { enum MaskResult { /// The mask is completely outside the region Outside, - /// The mask completely covers the region - Covering, /// The mask is inside and needs to be processed Inside(RenderTask), } @@ -1131,7 +1074,6 @@ impl RenderTask { actual_rect: actual_rect, alpha_items: Vec::new(), opaque_items: Vec::new(), - tile_id: ctx.tile_id, }), } } @@ -1148,45 +1090,33 @@ impl RenderTask { } fn new_mask(actual_rect: DeviceIntRect, - dependent: Option<&RenderTask>, mask_key: MaskCacheKey, - top_clip: (StackingContextIndex, &MaskCacheInfo), - layer_clips: &[(StackingContextIndex, MaskCacheInfo)], - tile_id: TileUniqueId) + top_clip: Option<(StackingContextIndex, MaskCacheInfo)>, + layer_clips: &[(StackingContextIndex, MaskCacheInfo)]) -> MaskResult { - - let extra = (top_clip.0, top_clip.1.clone()); - // We scan through the clip stack and detect if our actual rectangle // 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. - let result = layer_clips.iter().chain(Some(&extra)) - .fold(Some((actual_rect, true)), |current, clip| { - current.and_then(|(rect, covering)| - rect.intersection(&clip.1.outer_rect) - .map(|r| (r, covering & clip.1.inner_rect.contains_rect(&actual_rect)))) + let result = layer_clips.iter().chain(&top_clip) + .fold(Some(actual_rect), |current, clip| { + current.and_then(|rect| rect.intersection(&clip.1.bounding_rect)) }); let task_rect = match result { None => return MaskResult::Outside, - Some((_, true)) => return MaskResult::Covering, - Some((rect, false)) => rect, + Some(rect) => rect, }; let clips = layer_clips.iter() .map(|lc| lc.clone()) - .chain(Some(extra)) + .chain(top_clip) .collect(); MaskResult::Inside(RenderTask { - id: RenderTaskId::Dynamic(RenderTaskKey::CacheMask(mask_key, tile_id)), - children: match dependent { - Some(task) => vec![task.clone()], - None => Vec::new(), - }, + id: RenderTaskId::Dynamic(RenderTaskKey::CacheMask(mask_key)), + children: Vec::new(), location: RenderTaskLocation::Dynamic(None, task_rect.size), kind: RenderTaskKind::CacheMask(CacheMaskTask { actual_rect: task_rect, - base_task_id: dependent.map(|task| task.id), clips: clips, }), }) @@ -1482,7 +1412,6 @@ pub struct CacheClipInstance { task_id: i32, layer_index: i32, address: GpuStoreAddress, - base_task_id: i32, } #[derive(Debug, Clone)] @@ -1796,27 +1725,6 @@ pub struct Frame { pub deferred_resolves: Vec, } -#[derive(Debug)] -struct LayerMasksTasks { - task_ids: Vec>, -} - -impl LayerMasksTasks { - fn new() -> LayerMasksTasks { - LayerMasksTasks { - task_ids: Vec::new(), - } - } - - fn add(&mut self, index: StackingContextIndex, task_id: RenderTaskId) { - while self.task_ids.len() <= index.0 { - self.task_ids.push(None); - } - assert!(self.task_ids[index.0].is_none()); - self.task_ids[index.0] = Some(task_id); - } -} - /// Some extra per-tile information stored for debugging purposes. #[derive(Debug)] struct CompiledScreenTileInfo { @@ -1829,15 +1737,11 @@ struct CompiledScreenTile { main_render_task: RenderTask, required_pass_count: usize, info: CompiledScreenTileInfo, - unique_id: TileUniqueId, - layer_masks_tasks: LayerMasksTasks, } impl CompiledScreenTile { fn new(main_render_task: RenderTask, - info: CompiledScreenTileInfo, - unique_id: TileUniqueId, - layer_masks_tasks: LayerMasksTasks) + info: CompiledScreenTileInfo) -> CompiledScreenTile { let mut required_pass_count = 0; main_render_task.max_depth(0, &mut required_pass_count); @@ -1846,8 +1750,6 @@ impl CompiledScreenTile { main_render_task: main_render_task, required_pass_count: required_pass_count, info: info, - unique_id: unique_id, - layer_masks_tasks: layer_masks_tasks, } } @@ -1914,10 +1816,6 @@ impl ScreenTile { let mut sc_stack = Vec::new(); let mut current_task = RenderTask::new_alpha_batch(self.rect, ctx); let mut alpha_task_stack = Vec::new(); - let mut clip_info_stack = Vec::new(); - let mut clip_task_stack = Vec::new(); - let mut num_clips_to_skip = 0; - let mut layer_masks_tasks = LayerMasksTasks::new(); for cmd in self.cmds { match cmd { @@ -1936,33 +1834,6 @@ impl ScreenTile { alpha_task_stack.push(prev_task); } } - - // Create a task for the layer mask, if needed, - // i.e. if there are rounded corners or image masks for the layer. - if let Some(ref clip_info) = layer.clip_cache_info { - if CLIP_TASK_COLLAPSE { - clip_info_stack.push((sc_index, clip_info.clone())); - } else { - let mask_opt = RenderTask::new_mask(self.rect, - clip_task_stack.last(), - MaskCacheKey::Layer(sc_index), - (sc_index, clip_info), - &clip_info_stack, - ctx.tile_id); - match mask_opt { - MaskResult::Inside(mask_task) => { - current_task.children.push(mask_task.clone()); - clip_task_stack.push(mask_task); - num_clips_to_skip = 0; - } - _ => num_clips_to_skip += 1, - } - } - } - // Register the layer mask task within the context - if let Some(ref mask_task) = clip_task_stack.last() { - layer_masks_tasks.add(sc_index, mask_task.id); - } } TileCommand::PopLayer => { let sc_index = sc_stack.pop().unwrap(); @@ -1996,48 +1867,24 @@ impl ScreenTile { current_task = composite_task; } } - - if layer.clip_cache_info.is_some() { - if CLIP_TASK_COLLAPSE { - clip_info_stack.pop().unwrap(); - } else { - if num_clips_to_skip > 0 { - num_clips_to_skip -= 1; - } else { - clip_task_stack.pop().unwrap(); - } - } - } } TileCommand::DrawPrimitive(prim_index) => { let sc_index = *sc_stack.last().unwrap(); let prim_metadata = ctx.prim_store.get_metadata(prim_index); - // Add a task to render the updated image mask - if let Some(ref clip_info) = prim_metadata.clip_cache_info { - let mask_opt = RenderTask::new_mask(self.rect, - clip_task_stack.last(), - MaskCacheKey::Primitive(prim_index), - (sc_index, clip_info), - &clip_info_stack, - ctx.tile_id); - match mask_opt { - MaskResult::Outside => panic!("Primitive be culled by `assign_prims_to_screen_tiles` already"), - MaskResult::Covering => (), //do nothing - MaskResult::Inside(task) => current_task.children.push(task), - } - } - // Add any dynamic render tasks needed to render this primitive if let Some(ref render_task) = prim_metadata.render_task { current_task.children.push(render_task.clone()); } + if let Some(ref clip_task) = prim_metadata.clip_task { + current_task.children.push(clip_task.clone()); + } actual_prim_count += 1; let layer = &ctx.layer_store[sc_index.0]; let transform_kind = layer.xf_rect.as_ref().unwrap().kind; - let needs_clipping = layer.clip_cache_info.is_some() || prim_metadata.clip_cache_info.is_some(); + let needs_clipping = prim_metadata.clip_task.is_some(); let needs_blending = transform_kind == TransformedRectKind::Complex || !prim_metadata.is_opaque || needs_clipping; @@ -2054,7 +1901,6 @@ impl ScreenTile { } debug_assert!(alpha_task_stack.is_empty()); - debug_assert!(clip_task_stack.is_empty()); let info = CompiledScreenTileInfo { cmd_count: cmd_count, @@ -2062,7 +1908,7 @@ impl ScreenTile { }; current_task.location = RenderTaskLocation::Fixed(self.rect); - Some(CompiledScreenTile::new(current_task, info, ctx.tile_id, layer_masks_tasks)) + Some(CompiledScreenTile::new(current_task, info)) } } @@ -2543,6 +2389,7 @@ impl FrameBuilder { // TODO(gw): Remove this stack once the layers refactor is done! let mut layer_stack: Vec = Vec::new(); + let mut clip_info_stack = Vec::new(); for cmd in &self.cmds { match cmd { @@ -2620,6 +2467,10 @@ impl FrameBuilder { resource_cache.request_image(mask.image, ImageRendering::Auto); //Note: no need to add the layer for resolve, all layers get resolved } + + // Create a task for the layer mask, if needed, + // i.e. if there are rounded corners or image masks for the layer. + clip_info_stack.push((sc_index, clip_info.clone())); } } @@ -2641,8 +2492,6 @@ impl FrameBuilder { &packed_layer.transform, &packed_layer.local_clip_rect, device_pixel_ratio) { - profile_counters.visible_primitives.inc(); - if self.prim_store.prepare_prim_for_render(prim_index, resource_cache, &packed_layer.transform, @@ -2654,10 +2503,49 @@ impl FrameBuilder { &packed_layer.local_clip_rect, device_pixel_ratio); } + + // If the primitive is visible, consider culling it via clip rect(s). + // If it is visible but has clips, create the clip task for it. + if let Some(prim_bounding_rect) = self.prim_store + .cpu_bounding_rects[prim_index.0] { + let prim_metadata = &mut self.prim_store.cpu_metadata[prim_index.0]; + let prim_clip_info = prim_metadata.clip_cache_info.as_ref().map(|info| { + (*sc_index, info.clone()) + }); + + // Try to create a mask if we may need to. + if prim_clip_info.is_some() || !clip_info_stack.is_empty() { + let mask_opt = RenderTask::new_mask(prim_bounding_rect, + MaskCacheKey::Primitive(prim_index), + prim_clip_info, + &clip_info_stack); + match mask_opt { + MaskResult::Outside => { + // Primitive is completely clipped out. + prim_metadata.clip_task = None; + continue; + } + MaskResult::Inside(task) => { + // Got a valid clip task, so store it for this primitive. + prim_metadata.clip_task = Some(task); + } + } + } + + profile_counters.visible_primitives.inc(); + } } } } &PrimitiveRunCmd::PopStackingContext => { + let sc_index = *layer_stack.last().unwrap(); + let layer = &mut self.layer_store[sc_index.0]; + if layer.is_visible() { + if layer.clip_cache_info.is_some() { + clip_info_stack.pop().unwrap(); + } + } + layer_stack.pop().unwrap(); } } @@ -2716,7 +2604,7 @@ impl FrameBuilder { } if let Some(ref clip_info) = layer.clip_cache_info { - clip_rect_stack.push(clip_info.outer_rect); + clip_rect_stack.push(clip_info.bounding_rect); } let tile_range = layer.tile_range.as_ref().unwrap(); @@ -2749,7 +2637,7 @@ impl FrameBuilder { }; // check the clip bounding rectangle if let Some(ref clip_info) = self.prim_store.get_metadata(prim_index).clip_cache_info { - p_rect = match p_rect.intersection(&clip_info.outer_rect) { + p_rect = match p_rect.intersection(&clip_info.bounding_rect) { Some(r) => r, None => continue, } @@ -2896,10 +2784,9 @@ impl FrameBuilder { let mut max_passes_needed = 0; let mut render_tasks = { - let mut ctx = CompileTileContext { + let ctx = CompileTileContext { layer_store: &self.layer_store, prim_store: &self.prim_store, - tile_id: 0, // This doesn't need to be atomic right now (all the screen tiles are // compiled on a single thread). However, in the future each of the @@ -2915,8 +2802,7 @@ impl FrameBuilder { } // Build list of passes, target allocs that each tile needs. - for (tile_id, screen_tile) in screen_tiles.into_iter().enumerate() { - ctx.tile_id = tile_id; + for screen_tile in screen_tiles { let rect = screen_tile.rect; if let Some(compiled_screen_tile) = screen_tile.compile(&ctx) { max_passes_needed = cmp::max(max_passes_needed, @@ -2952,11 +2838,10 @@ impl FrameBuilder { let mut passes = Vec::new(); if !compiled_screen_tiles.is_empty() { - let mut ctx = RenderTargetContext { + let ctx = RenderTargetContext { layer_store: &self.layer_store, prim_store: &self.prim_store, resource_cache: resource_cache, - layer_masks_tasks: HashMap::new(), }; // Do the allocations now, assigning each tile's tasks to a render @@ -2966,14 +2851,7 @@ impl FrameBuilder { index == max_passes_needed-1)); } - for mut compiled_screen_tile in compiled_screen_tiles { - // Grab the mask task indices from the compile tile and append into the context map - for (i, mask_task_opt) in compiled_screen_tile.layer_masks_tasks.task_ids.drain(..).enumerate() { - if let Some(mask_task_id) = mask_task_opt { - let key = (compiled_screen_tile.unique_id, StackingContextIndex(i)); - ctx.layer_masks_tasks.insert(key, mask_task_id); - } - } + for compiled_screen_tile in compiled_screen_tiles { compiled_screen_tile.build(&mut passes); }