diff --git a/webrender/src/batch.rs b/webrender/src/batch.rs index 4aded290da..4e06e2c9ed 100644 --- a/webrender/src/batch.rs +++ b/webrender/src/batch.rs @@ -5,8 +5,7 @@ use api::{AlphaType, ClipMode, DeviceIntRect, DeviceIntSize, DeviceIntPoint}; use api::{DeviceUintRect, DeviceUintPoint, ExternalImageType, FilterOp, ImageRendering}; use api::{YuvColorSpace, YuvFormat, WorldPixel}; -use clip::{ClipSource, ClipStore, ClipWorkItem}; -use clip_scroll_tree::{CoordinateSystemId}; +use clip::{ClipNodeFlags, ClipNodeRange, ClipItem, ClipStore}; use euclid::vec3; use glyph_rasterizer::GlyphFormat; use gpu_cache::{GpuCache, GpuCacheHandle, GpuCacheAddress}; @@ -476,7 +475,7 @@ impl AlphaBatchBuilder { for run in &pic.runs { let transform_id = ctx .transforms - .get_id(run.clip_and_scroll.spatial_node_index); + .get_id(run.spatial_node_index); self.add_run_to_batch( run, transform_id, @@ -1762,68 +1761,35 @@ impl ClipBatcher { pub fn add( &mut self, task_address: RenderTaskAddress, - clips: &[ClipWorkItem], - coordinate_system_id: CoordinateSystemId, + clip_node_range: ClipNodeRange, resource_cache: &ResourceCache, gpu_cache: &GpuCache, clip_store: &ClipStore, transforms: &TransformPalette, ) { - let mut coordinate_system_id = coordinate_system_id; - for work_item in clips.iter() { - let info = &clip_store[work_item.clip_sources_index]; + for i in 0 .. clip_node_range.count { + let (clip_node, flags) = clip_store.get_node_from_range(&clip_node_range, i); + let instance = ClipMaskInstance { render_task_address: task_address, - transform_id: transforms.get_id(info.spatial_node_index), + transform_id: transforms.get_id(clip_node.spatial_node_index), segment: 0, clip_data_address: GpuCacheAddress::invalid(), resource_address: GpuCacheAddress::invalid(), }; - for &(ref source, ref handle) in &info.clips { - let gpu_address = gpu_cache.get_address(handle); + let gpu_address = gpu_cache.get_address(&clip_node.gpu_cache_handle); - match *source { - ClipSource::Image(ref mask) => { - if let Ok(cache_item) = resource_cache.get_cached_image( - ImageRequest { - key: mask.image, - rendering: ImageRendering::Auto, - tile: None, - } - ) { - self.images - .entry(cache_item.texture_id) - .or_insert(Vec::new()) - .push(ClipMaskInstance { - clip_data_address: gpu_address, - resource_address: gpu_cache.get_address(&cache_item.uv_rect_handle), - ..instance - }); - } else { - warn!("Warnings: skip a image mask"); - debug!("Key:{:?} Rect::{:?}", mask.image, mask.rect); - continue; + match clip_node.item { + ClipItem::Image(ref mask) => { + if let Ok(cache_item) = resource_cache.get_cached_image( + ImageRequest { + key: mask.image, + rendering: ImageRendering::Auto, + tile: None, } - } - ClipSource::LineDecoration(..) => { - self.line_decorations.push(ClipMaskInstance { - clip_data_address: gpu_address, - ..instance - }); - } - ClipSource::BoxShadow(ref info) => { - let rt_handle = info - .cache_handle - .as_ref() - .expect("bug: render task handle not allocated"); - let rt_cache_entry = resource_cache - .get_cached_render_task(rt_handle); - let cache_item = resource_cache - .get_texture_cache_item(&rt_cache_entry.handle); - debug_assert_ne!(cache_item.texture_id, SourceTexture::Invalid); - - self.box_shadows + ) { + self.images .entry(cache_item.texture_id) .or_insert(Vec::new()) .push(ClipMaskInstance { @@ -1831,24 +1797,53 @@ impl ClipBatcher { resource_address: gpu_cache.get_address(&cache_item.uv_rect_handle), ..instance }); + } else { + warn!("Warnings: skip a image mask"); + debug!("Key:{:?} Rect::{:?}", mask.image, mask.rect); + continue; } - 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 - }); - coordinate_system_id = work_item.coordinate_system_id; - } - } - ClipSource::RoundedRectangle(..) => { + } + ClipItem::LineDecoration(..) => { + self.line_decorations.push(ClipMaskInstance { + clip_data_address: gpu_address, + ..instance + }); + } + ClipItem::BoxShadow(ref info) => { + let rt_handle = info + .cache_handle + .as_ref() + .expect("bug: render task handle not allocated"); + let rt_cache_entry = resource_cache + .get_cached_render_task(rt_handle); + let cache_item = resource_cache + .get_texture_cache_item(&rt_cache_entry.handle); + debug_assert_ne!(cache_item.texture_id, SourceTexture::Invalid); + + self.box_shadows + .entry(cache_item.texture_id) + .or_insert(Vec::new()) + .push(ClipMaskInstance { + clip_data_address: gpu_address, + resource_address: gpu_cache.get_address(&cache_item.uv_rect_handle), + ..instance + }); + } + ClipItem::Rectangle(_, mode) => { + if !flags.contains(ClipNodeFlags::SAME_COORD_SYSTEM) || + mode == ClipMode::ClipOut { self.rectangles.push(ClipMaskInstance { clip_data_address: gpu_address, ..instance }); } } + ClipItem::RoundedRectangle(..) => { + self.rectangles.push(ClipMaskInstance { + clip_data_address: gpu_address, + ..instance + }); + } } } } diff --git a/webrender/src/box_shadow.rs b/webrender/src/box_shadow.rs index 9ad1006eb0..1edcfd34da 100644 --- a/webrender/src/box_shadow.rs +++ b/webrender/src/box_shadow.rs @@ -4,7 +4,7 @@ use api::{BorderRadius, BoxShadowClipMode, ClipMode, ColorF, DeviceIntSize, LayoutPrimitiveInfo}; use api::{LayoutRect, LayoutSize, LayoutVector2D}; -use clip::ClipSource; +use clip::ClipItem; use display_list_flattener::DisplayListFlattener; use gpu_cache::GpuCacheHandle; use gpu_types::BoxShadowStretchMode; @@ -122,7 +122,7 @@ impl<'a> DisplayListFlattener<'a> { } // TODO(gw): Add a fast path for ClipOut + zero border radius! - clips.push(ClipSource::new_rounded_rect( + clips.push(ClipItem::new_rounded_rect( prim_info.rect, border_radius, ClipMode::ClipOut @@ -132,7 +132,7 @@ impl<'a> DisplayListFlattener<'a> { } BoxShadowClipMode::Inset => { if shadow_rect.is_well_formed_and_nonempty() { - clips.push(ClipSource::new_rounded_rect( + clips.push(ClipItem::new_rounded_rect( shadow_rect, shadow_radius, ClipMode::ClipOut @@ -143,7 +143,7 @@ impl<'a> DisplayListFlattener<'a> { } }; - clips.push(ClipSource::new_rounded_rect(final_prim_rect, clip_radius, ClipMode::Clip)); + clips.push(ClipItem::new_rounded_rect(final_prim_rect, clip_radius, ClipMode::Clip)); self.add_primitive( clip_and_scroll, @@ -163,7 +163,7 @@ impl<'a> DisplayListFlattener<'a> { // Add a normal clip mask to clip out the contents // of the surrounding primitive. - extra_clips.push(ClipSource::new_rounded_rect( + extra_clips.push(ClipItem::new_rounded_rect( prim_info.rect, border_radius, prim_clip_mode, @@ -174,14 +174,14 @@ impl<'a> DisplayListFlattener<'a> { let dest_rect = shadow_rect.inflate(blur_offset, blur_offset); // Draw the box-shadow as a solid rect, using a box-shadow - // clip mask source. + // clip mask item. let prim = BrushPrimitive::new( BrushKind::new_solid(*color), None, ); - // Create the box-shadow clip source. - let shadow_clip_source = ClipSource::new_box_shadow( + // Create the box-shadow clip item. + let shadow_clip_source = ClipItem::new_box_shadow( shadow_rect, shadow_radius, dest_rect, diff --git a/webrender/src/clip.rs b/webrender/src/clip.rs index 6882673908..224325d8ce 100644 --- a/webrender/src/clip.rs +++ b/webrender/src/clip.rs @@ -4,60 +4,622 @@ use api::{BorderRadius, ClipMode, ComplexClipRegion, DeviceIntRect, DevicePixelScale, ImageMask}; use api::{ImageRendering, LayoutRect, LayoutSize, LayoutPoint, LayoutVector2D, LocalClip}; -use api::{BoxShadowClipMode, LayoutToWorldScale, LineOrientation, LineStyle}; +use api::{BoxShadowClipMode, LayoutToWorldScale, LineOrientation, LineStyle, LayoutTransform}; use border::{ensure_no_corner_overlap}; use box_shadow::{BLUR_SAMPLE_SCALE, BoxShadowClipSource, BoxShadowCacheKey}; -use clip_scroll_tree::{ClipChainIndex, CoordinateSystemId, SpatialNodeIndex}; +use clip_scroll_tree::{CoordinateSystemId, SpatialNodeIndex}; use ellipse::Ellipse; use gpu_cache::{GpuCache, GpuCacheHandle, ToGpuBlocks}; use gpu_types::BoxShadowStretchMode; use prim_store::{ClipData, ImageMaskData}; use render_task::to_cache_size; use resource_cache::{ImageRequest, ResourceCache}; -use util::{LayoutToWorldFastTransform, MaxRect, TransformedRectKind}; -use util::{calculate_screen_bounding_rect, extract_inner_rect_safe, pack_as_float, recycle_vec}; -use std::{iter, ops}; -use std::sync::Arc; +use spatial_node::SpatialNode; +use std::u32; +use util::{extract_inner_rect_safe, pack_as_float, recycle_vec, MatrixHelpers}; + +/* + + Module Overview + + There are a number of data structures involved in the clip module: + + ClipStore - Main interface used by other modules. + + ClipItem - A single clip item (e.g. a rounded rect, or a box shadow). + These are an exposed API type, stored inline in a ClipNode. + + ClipNode - A ClipItem with attached positioning information (a spatial node index). + Stored as a contiguous array of nodes within the ClipStore. + + +-----------------------+-----------------------+-----------------------+ + | ClipItem | ClipItem | ClipItem | + | Spatial Node Index | Spatial Node Index | Spatial Node Index | + | GPU cache handle | GPU cache handle | GPU cache handle | + +-----------------------+-----------------------+-----------------------+ + 0 1 2 + + +----------------+ | | + | ClipItemRange |____| | + | index: 1 | | + | count: 2 |___________________________________________________| + +----------------+ + + ClipItemRange - A clip item range identifies a range of clip nodes. It is stored + as an (index, count). + + ClipChain - A clip chain node contains a range of ClipNodes (a ClipItemRange) + and a parent link to an optional ClipChain. Both legacy hierchical clip + chains and user defined API clip chains use the same data structure. + ClipChainId is an index into an array, or ClipChainId::NONE for no parent. + + +----------------+ ____+----------------+ ____+----------------+ ____+----------------+ + | ClipChain | | | ClipChain | | | ClipChain | | | ClipChain | + +----------------+ | +----------------+ | +----------------+ | +----------------+ + | ClipItemRange | | | ClipItemRange | | | ClipItemRange | | | ClipItemRange | + | Parent Id |___| | Parent Id |___| | Parent Id |___| | Parent Id | + +----------------+ +----------------+ +----------------+ +----------------+ + + ClipChainInstance - A ClipChain that has been built for a specific primitive + positioning node. + + When given a clip chain ID, and a local primitive rect + spatial node, the clip module + creates a clip chain instance. This is a struct with various pieces of useful information + (such as a local clip rect and affected local bounding rect). It also contains a (index, count) + range specifier into an index buffer of the ClipNode structures that are actually relevant + for this clip chain instance. The index buffer structure allows a single array to be used for + all of the clip-chain instances built in a single frame. Each entry in the index buffer + also stores some flags relevant to the clip node in this positioning context. + + +----------------------+ + | ClipChainInstance | + +----------------------+ + | local_clip_rect | + | local_bounding_rect |________________________________________________________________________ + | clips_range |_______________ | + +----------------------+ | | + | | + +------------------+------------------+------------------+------------------+------------------+ + | ClipNodeInstance | ClipNodeInstance | ClipNodeInstance | ClipNodeInstance | ClipNodeInstance | + +------------------+------------------+------------------+------------------+------------------+ + | flags | flags | flags | flags | flags | + | ClipNodeIndex | ClipNodeIndex | ClipNodeIndex | ClipNodeIndex | ClipNodeIndex | + +------------------+------------------+------------------+------------------+------------------+ + + */ + +// Result of comparing a clip node instance against a local rect. +#[derive(Debug)] +enum ClipResult { + // The clip does not affect the region at all. + Accept, + // The clip prevents the region from being drawn. + Reject, + // The clip affects part of the region. This may + // require a clip mask, depending on other factors. + Partial, +} +// A clip item range represents one or more ClipItem structs. +// They are stored in a contiguous array inside the ClipStore, +// and identified by an (offset, count). #[derive(Debug, Copy, Clone)] +pub struct ClipItemRange { + pub index: ClipNodeIndex, + pub count: u32, +} + +// A clip node is a single clip source, along with some +// positioning information and implementation details +// that control where the GPU data for this clip source +// can be found. +pub struct ClipNode { + pub spatial_node_index: SpatialNodeIndex, + pub item: ClipItem, + pub gpu_cache_handle: GpuCacheHandle, +} + +// Flags that are attached to instances of clip nodes. +bitflags! { + pub struct ClipNodeFlags: u8 { + const SAME_SPATIAL_NODE = 0x1; + const SAME_COORD_SYSTEM = 0x2; + } +} + +// Identifier for a clip chain. Clip chains are stored +// in a contiguous array in the clip store. They are +// identified by a simple index into that array. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct ClipChainId(pub u32); + +// The root of each clip chain is the NONE id. The +// value is specifically set to u32::MAX so that if +// any code accidentally tries to access the root +// node, a bounds error will occur. +impl ClipChainId { + pub const NONE: Self = ClipChainId(u32::MAX); +} + +// A clip chain node is an id for a range of clip sources, +// and a link to a parent clip chain node, or ClipChainId::NONE. +#[derive(Clone)] +pub struct ClipChainNode { + pub clip_item_range: ClipItemRange, + pub parent_clip_chain_id: ClipChainId, +} + +// An index into the clip_nodes array. +#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct ClipSourcesIndex(usize); +pub struct ClipNodeIndex(pub u32); + +// When a clip node is found to be valid for a +// clip chain instance, it's stored in an index +// buffer style structure. This struct contains +// an index to the node data itself, as well as +// some flags describing how this clip node instance +// is positioned. +#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct ClipNodeInstance(pub u32); + +impl ClipNodeInstance { + fn new(index: ClipNodeIndex, flags: ClipNodeFlags) -> ClipNodeInstance { + ClipNodeInstance( + (index.0 & 0x00ffffff) | ((flags.bits() as u32) << 24) + ) + } + + fn flags(&self) -> ClipNodeFlags { + ClipNodeFlags::from_bits_truncate((self.0 >> 24) as u8) + } + + fn index(&self) -> usize { + (self.0 & 0x00ffffff) as usize + } +} + +// A range of clip node instances that were found by +// building a clip chain instance. +#[derive(Debug, Copy, Clone)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct ClipNodeRange { + pub first: u32, + pub count: u32, +} + +// A helper struct for converting between coordinate systems +// of clip sources and primitives. +// todo(gw): optimize: +// separate arrays for matrices +// cache and only build as needed. +#[derive(Debug)] +enum ClipSpaceConversion { + Local, + Offset(LayoutVector2D), + Transform(LayoutTransform, LayoutTransform), +} + +impl ClipSpaceConversion { + fn transform_to_prim_space(&self, rect: &LayoutRect) -> Option { + match *self { + ClipSpaceConversion::Local => { + Some(*rect) + } + ClipSpaceConversion::Offset(ref offset) => { + Some(rect.translate(offset)) + } + ClipSpaceConversion::Transform(ref transform, _) => { + if transform.has_perspective_component() { + None + } else { + transform.transform_rect(rect) + } + } + } + } + + fn transform_from_prim_space(&self, rect: &LayoutRect) -> Option { + match *self { + ClipSpaceConversion::Local => { + Some(*rect) + } + ClipSpaceConversion::Offset(offset) => { + Some(rect.translate(&-offset)) + } + ClipSpaceConversion::Transform(_, ref inv_transform) => { + inv_transform.transform_rect(rect) + } + } + } +} + +// Temporary information that is cached and reused +// during building of a clip chain instance. +struct ClipNodeInfo { + conversion: ClipSpaceConversion, + node_index: ClipNodeIndex, + has_non_root_coord_system: bool, +} + +impl ClipNode { + pub fn update( + &mut self, + gpu_cache: &mut GpuCache, + resource_cache: &mut ResourceCache, + device_pixel_scale: DevicePixelScale, + ) { + if let Some(mut request) = gpu_cache.request(&mut self.gpu_cache_handle) { + match self.item { + ClipItem::Image(ref mask) => { + let data = ImageMaskData { local_rect: mask.rect }; + data.write_gpu_blocks(request); + } + ClipItem::BoxShadow(ref info) => { + request.push([ + info.shadow_rect_alloc_size.width, + info.shadow_rect_alloc_size.height, + info.clip_mode as i32 as f32, + 0.0, + ]); + request.push([ + info.stretch_mode_x as i32 as f32, + info.stretch_mode_y as i32 as f32, + 0.0, + 0.0, + ]); + request.push(info.prim_shadow_rect); + } + ClipItem::Rectangle(rect, mode) => { + let data = ClipData::uniform(rect, 0.0, mode); + data.write(&mut request); + } + ClipItem::RoundedRectangle(ref rect, ref radius, mode) => { + let data = ClipData::rounded_rect(rect, radius, mode); + data.write(&mut request); + } + ClipItem::LineDecoration(ref info) => { + request.push(info.rect); + request.push([ + info.wavy_line_thickness, + pack_as_float(info.style as u32), + pack_as_float(info.orientation as u32), + 0.0, + ]); + } + } + } + + match self.item { + ClipItem::Image(ref mask) => { + resource_cache.request_image( + ImageRequest { + key: mask.image, + rendering: ImageRendering::Auto, + tile: None, + }, + gpu_cache, + ); + } + ClipItem::BoxShadow(ref mut info) => { + // Quote from https://drafts.csswg.org/css-backgrounds-3/#shadow-blur + // "the image that would be generated by applying to the shadow a + // Gaussian blur with a standard deviation equal to half the blur radius." + let blur_radius_dp = (info.blur_radius * 0.5 * device_pixel_scale.0).round(); + + // Create the cache key for this box-shadow render task. + let content_scale = LayoutToWorldScale::new(1.0) * device_pixel_scale; + let cache_size = to_cache_size(info.shadow_rect_alloc_size * content_scale); + let bs_cache_key = BoxShadowCacheKey { + blur_radius_dp: blur_radius_dp as i32, + clip_mode: info.clip_mode, + rect_size: (info.shadow_rect_alloc_size * content_scale).round().to_i32(), + br_top_left: (info.shadow_radius.top_left * content_scale).round().to_i32(), + br_top_right: (info.shadow_radius.top_right * content_scale).round().to_i32(), + br_bottom_right: (info.shadow_radius.bottom_right * content_scale).round().to_i32(), + br_bottom_left: (info.shadow_radius.bottom_left * content_scale).round().to_i32(), + }; + + info.cache_key = Some((cache_size, bs_cache_key)); + + if let Some(mut request) = gpu_cache.request(&mut info.clip_data_handle) { + let data = ClipData::rounded_rect( + &info.minimal_shadow_rect, + &info.shadow_radius, + ClipMode::Clip, + ); + + data.write(&mut request); + } + } + ClipItem::Rectangle(..) | + ClipItem::RoundedRectangle(..) | + ClipItem::LineDecoration(..) => {} + } + } +} +// The main clipping public interface that other modules access. pub struct ClipStore { - clip_sources: Vec, + pub clip_nodes: Vec, + pub clip_chain_nodes: Vec, + clip_node_indices: Vec, + clip_node_info: Vec, +} + +// A clip chain instance is what gets built for a given clip +// chain id + local primitive region + positioning node. +pub struct ClipChainInstance { + pub clips_range: ClipNodeRange, + pub local_clip_rect: LayoutRect, + pub has_clips_from_other_coordinate_systems: bool, + pub has_non_root_coord_system: bool, + pub local_bounding_rect: LayoutRect, } impl ClipStore { pub fn new() -> Self { ClipStore { - clip_sources: Vec::new(), + clip_nodes: Vec::new(), + clip_chain_nodes: Vec::new(), + clip_node_indices: Vec::new(), + clip_node_info: Vec::new(), } } pub fn recycle(self) -> Self { ClipStore { - clip_sources: recycle_vec(self.clip_sources), + clip_nodes: recycle_vec(self.clip_nodes), + clip_chain_nodes: recycle_vec(self.clip_chain_nodes), + clip_node_indices: recycle_vec(self.clip_node_indices), + clip_node_info: recycle_vec(self.clip_node_info), } } - pub fn insert(&mut self, clip_sources: ClipSources) -> ClipSourcesIndex { - let index = ClipSourcesIndex(self.clip_sources.len()); - self.clip_sources.push(clip_sources); - index + pub fn add_clip_items( + &mut self, + clip_items: Vec, + spatial_node_index: SpatialNodeIndex, + ) -> ClipItemRange { + debug_assert!(!clip_items.is_empty()); + + let range = ClipItemRange { + index: ClipNodeIndex(self.clip_nodes.len() as u32), + count: clip_items.len() as u32, + }; + + let nodes = clip_items + .into_iter() + .map(|item| { + ClipNode { + item, + spatial_node_index, + gpu_cache_handle: GpuCacheHandle::new(), + } + }); + + self.clip_nodes.extend(nodes); + range } -} -impl ops::Index for ClipStore { - type Output = ClipSources; - fn index(&self, index: ClipSourcesIndex) -> &Self::Output { - &self.clip_sources[index.0] + pub fn get_clip_chain(&self, clip_chain_id: ClipChainId) -> &ClipChainNode { + &self.clip_chain_nodes[clip_chain_id.0 as usize] } -} -impl ops::IndexMut for ClipStore { - fn index_mut(&mut self, index: ClipSourcesIndex) -> &mut Self::Output { - &mut self.clip_sources[index.0] + pub fn add_clip_chain( + &mut self, + clip_item_range: ClipItemRange, + parent_clip_chain_id: ClipChainId, + ) -> ClipChainId { + let id = ClipChainId(self.clip_chain_nodes.len() as u32); + self.clip_chain_nodes.push(ClipChainNode { + clip_item_range, + parent_clip_chain_id, + }); + id + } + + pub fn get_node_from_range( + &self, + node_range: &ClipNodeRange, + index: u32, + ) -> (&ClipNode, ClipNodeFlags) { + let instance = self.clip_node_indices[(node_range.first + index) as usize]; + (&self.clip_nodes[instance.index()], instance.flags()) + } + + pub fn get_node_from_range_mut( + &mut self, + node_range: &ClipNodeRange, + index: u32, + ) -> (&mut ClipNode, ClipNodeFlags) { + let instance = self.clip_node_indices[(node_range.first + index) as usize]; + (&mut self.clip_nodes[instance.index()], instance.flags()) + } + + // 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( + &mut self, + clip_chain_id: ClipChainId, + local_prim_rect: LayoutRect, + local_prim_clip_rect: LayoutRect, + spatial_node_index: SpatialNodeIndex, + spatial_nodes: &[SpatialNode], + gpu_cache: &mut GpuCache, + resource_cache: &mut ResourceCache, + device_pixel_scale: DevicePixelScale, + ) -> Option { + // Trivial check to see if the primitive is clipped out by the + // local clip rect of the primitive itself. + let mut local_bounding_rect = match local_prim_rect.intersection(&local_prim_clip_rect) { + Some(rect) => rect, + None => return None, + }; + let mut current_local_clip_rect = local_prim_clip_rect; + + // Walk the clip chain to build local rects, and collect the + // smallest possible local clip area. + + self.clip_node_info.clear(); + let ref_spatial_node = &spatial_nodes[spatial_node_index.0]; + let mut current_clip_chain_id = clip_chain_id; + + // for each clip chain node + while current_clip_chain_id != ClipChainId::NONE { + let clip_chain_node = &self.clip_chain_nodes[current_clip_chain_id.0 as usize]; + let node_count = clip_chain_node.clip_item_range.count; + + // for each clip node (clip source) in this clip chain node + for i in 0 .. node_count { + let clip_node_index = ClipNodeIndex(clip_chain_node.clip_item_range.index.0 + i); + let clip_node = &self.clip_nodes[clip_node_index.0 as usize]; + let clip_spatial_node = &spatial_nodes[clip_node.spatial_node_index.0 as usize]; + + // Determine the most efficient way to convert between coordinate + // systems of the primitive and clip node. + let conversion = if spatial_node_index == clip_node.spatial_node_index { + Some(ClipSpaceConversion::Local) + } else if ref_spatial_node.coordinate_system_id == clip_spatial_node.coordinate_system_id { + let offset = clip_spatial_node.coordinate_system_relative_offset - + ref_spatial_node.coordinate_system_relative_offset; + Some(ClipSpaceConversion::Offset(offset)) + } else { + // TODO(gw): We still have issues with clip nodes and primitives where + // there is a perspective transform. We intend to fix these + // cases as a follow up. + let relative_transform = ref_spatial_node + .world_content_transform + .to_transform() + .inverse() + .map(|inv| { + inv.pre_mul(&clip_spatial_node.world_content_transform.to_transform()) + }); + let inv_relative_transform = relative_transform + .and_then(|rt| rt.inverse()); + match (relative_transform, inv_relative_transform) { + (Some(relative_transform), Some(inv_relative_transform)) => { + Some(ClipSpaceConversion::Transform(relative_transform, inv_relative_transform)) + } + _ => { + None + } + } + }; + + // If we can convert spaces, try to reduce the size of the region + // requested, and cache the conversion information for the next step. + if let Some(conversion) = conversion { + if let Some(clip_rect) = clip_node.item.get_local_clip_rect() { + let clip_rect = conversion.transform_to_prim_space(&clip_rect); + if let Some(clip_rect) = clip_rect { + local_bounding_rect = match local_bounding_rect.intersection(&clip_rect) { + Some(new_local_bounding_rect) => new_local_bounding_rect, + None => return None, + }; + + if ref_spatial_node.coordinate_system_id == clip_spatial_node.coordinate_system_id { + current_local_clip_rect = match current_local_clip_rect.intersection(&clip_rect) { + Some(new_local_clip_rect) => new_local_clip_rect, + None => { + return None + } + } + } + } + } + self.clip_node_info.push(ClipNodeInfo { + conversion, + node_index: clip_node_index, + has_non_root_coord_system: clip_spatial_node.coordinate_system_id != CoordinateSystemId::root(), + }) + } + } + + current_clip_chain_id = clip_chain_node.parent_clip_chain_id; + } + + // 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. + + // Run through the clip nodes, and see which ones affect this prim region. + + let first_clip_node_index = self.clip_node_indices.len() as u32; + let mut has_non_root_coord_system = false; + let mut has_clips_from_other_coordinate_systems = false; + + // For each potential clip node + for node_info in self.clip_node_info.drain(..) { + let node = &mut self.clip_nodes[node_info.node_index.0 as usize]; + + // Convert the prim rect into the clip nodes local space + if let Some(prim_rect) = node_info + .conversion + .transform_from_prim_space(¤t_local_clip_rect) { + + // See how this clip affects the prim region. + let clip_result = node.item.get_clip_result(&prim_rect); + + match clip_result { + ClipResult::Accept => { + // Doesn't affect the primitive at all, so skip adding to list + } + ClipResult::Reject => { + // Completely clips the supplied prim rect + return None; + } + ClipResult::Partial => { + // Needs a mask -> add to clip node indices + + // TODO(gw): Ensure this only runs once on each node per frame? + node.update( + gpu_cache, + resource_cache, + device_pixel_scale, + ); + + // Calculate some flags that are required for the segment + // building logic. + let flags = match node_info.conversion { + ClipSpaceConversion::Local => { + ClipNodeFlags::SAME_SPATIAL_NODE | ClipNodeFlags::SAME_COORD_SYSTEM + } + ClipSpaceConversion::Offset(..) => { + ClipNodeFlags::SAME_COORD_SYSTEM + } + ClipSpaceConversion::Transform(..) => { + has_clips_from_other_coordinate_systems = true; + ClipNodeFlags::empty() + } + }; + + // Store this in the index buffer for this clip chain instance. + self.clip_node_indices + .push(ClipNodeInstance::new(node_info.node_index, flags)); + + has_non_root_coord_system |= node_info.has_non_root_coord_system; + } + } + } + } + + // Get the range identifying the clip nodes in the index buffer. + let clips_range = ClipNodeRange { + first: first_clip_node_index, + count: self.clip_node_indices.len() as u32 - first_clip_node_index, + }; + + // Return a valid clip chain instance + Some(ClipChainInstance { + clips_range, + has_clips_from_other_coordinate_systems, + has_non_root_coord_system, + local_clip_rect: current_local_clip_rect, + local_bounding_rect, + }) } } @@ -144,7 +706,7 @@ impl ClipRegion> { } #[derive(Debug)] -pub enum ClipSource { +pub enum ClipItem { Rectangle(LayoutRect, ClipMode), RoundedRectangle(LayoutRect, BorderRadius, ClipMode), Image(ImageMask), @@ -152,17 +714,17 @@ pub enum ClipSource { LineDecoration(LineDecorationClipSource), } -impl ClipSource { +impl ClipItem { pub fn new_rounded_rect( rect: LayoutRect, mut radii: BorderRadius, clip_mode: ClipMode - ) -> ClipSource { + ) -> Self { if radii.is_zero() { - ClipSource::Rectangle(rect, clip_mode) + ClipItem::Rectangle(rect, clip_mode) } else { ensure_no_corner_overlap(&mut radii, &rect); - ClipSource::RoundedRectangle( + ClipItem::RoundedRectangle( rect, radii, clip_mode, @@ -175,8 +737,8 @@ impl ClipSource { style: LineStyle, orientation: LineOrientation, wavy_line_thickness: f32, - ) -> ClipSource { - ClipSource::LineDecoration( + ) -> Self { + ClipItem::LineDecoration( LineDecorationClipSource { rect, style, @@ -192,7 +754,7 @@ impl ClipSource { prim_shadow_rect: LayoutRect, blur_radius: f32, clip_mode: BoxShadowClipMode, - ) -> ClipSource { + ) -> Self { // Get the fractional offsets required to match the // source rect with a minimal rect. let fract_offset = LayoutPoint::new( @@ -266,7 +828,7 @@ impl ClipSource { 2.0 * blur_region + minimal_shadow_rect.size.height.ceil(), ); - ClipSource::BoxShadow(BoxShadowClipSource { + ClipItem::BoxShadow(BoxShadowClipSource { shadow_rect_alloc_size, shadow_radius, prim_shadow_rect, @@ -283,10 +845,10 @@ impl ClipSource { // Return a modified clip source that is the same as self // but offset in local-space by a specified amount. - pub fn offset(&self, offset: &LayoutVector2D) -> ClipSource { + pub fn offset(&self, offset: &LayoutVector2D) -> Self { match *self { - ClipSource::LineDecoration(ref info) => { - ClipSource::LineDecoration(LineDecorationClipSource { + ClipItem::LineDecoration(ref info) => { + ClipItem::LineDecoration(LineDecorationClipSource { rect: info.rect.translate(offset), ..*info }) @@ -299,294 +861,131 @@ impl ClipSource { pub fn is_rect(&self) -> bool { match *self { - ClipSource::Rectangle(..) => true, + ClipItem::Rectangle(..) => true, _ => false, } } pub fn is_image_or_line_decoration_clip(&self) -> bool { match *self { - ClipSource::Image(..) | ClipSource::LineDecoration(..) => true, + ClipItem::Image(..) | ClipItem::LineDecoration(..) => true, _ => false, } } -} - -struct BoundsAccumulator { - local_outer: Option, - local_inner: Option, - can_calculate_inner_rect: bool, - can_calculate_outer_rect: bool, -} - -impl BoundsAccumulator { - fn new() -> Self { - BoundsAccumulator { - local_outer: Some(LayoutRect::max_rect()), - local_inner: Some(LayoutRect::max_rect()), - can_calculate_inner_rect: true, - can_calculate_outer_rect: false, + // Get an optional clip rect that a clip source can provide to + // reduce the size of a primitive region. This is typically + // used to eliminate redundant clips, and reduce the size of + // any clip mask that eventually gets drawn. + fn get_local_clip_rect(&self) -> Option { + match *self { + ClipItem::Rectangle(clip_rect, ClipMode::Clip) => Some(clip_rect), + ClipItem::Rectangle(_, ClipMode::ClipOut) => None, + ClipItem::RoundedRectangle(clip_rect, _, ClipMode::Clip) => Some(clip_rect), + ClipItem::RoundedRectangle(_, _, ClipMode::ClipOut) => None, + ClipItem::Image(ref mask) if mask.repeat => None, + ClipItem::Image(ref mask) => Some(mask.rect), + ClipItem::BoxShadow(..) => None, + ClipItem::LineDecoration(..) => None, } } - fn add(&mut self, source: &ClipSource) { - // Depending on the complexity of the clip, we may either know the outer and/or inner - // rect, or neither or these. In the case of a clip-out, we currently set the mask bounds - // to be unknown. This is conservative, but ensures correctness. In the future we can make - // this a lot more clever with some proper region handling. - if !self.can_calculate_inner_rect { - return - } + // Check how a given clip source affects a local primitive region. + fn get_clip_result( + &self, + prim_rect: &LayoutRect, + ) -> ClipResult { + match *self { + ClipItem::Rectangle(ref clip_rect, ClipMode::Clip) => { + if clip_rect.contains_rect(prim_rect) { + return ClipResult::Accept; + } - match *source { - ClipSource::Image(ref mask) => { - if !mask.repeat { - self.can_calculate_outer_rect = true; - self.local_outer = self.local_outer.and_then(|r| r.intersection(&mask.rect)); + match clip_rect.intersection(prim_rect) { + Some(..) => { + ClipResult::Partial + } + None => { + ClipResult::Reject + } } - self.local_inner = None; } - 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 { - self.can_calculate_inner_rect = false; - return + ClipItem::Rectangle(ref clip_rect, ClipMode::ClipOut) => { + if clip_rect.contains_rect(prim_rect) { + return ClipResult::Reject; } - self.can_calculate_outer_rect = true; - self.local_outer = self.local_outer.and_then(|r| r.intersection(&rect)); - self.local_inner = self.local_inner.and_then(|r| r.intersection(&rect)); - } - ClipSource::RoundedRectangle(ref rect, ref radius, mode) => { - // Once we encounter a clip-out, we just assume the worst - // case clip mask size, for now. - if mode == ClipMode::ClipOut { - self.can_calculate_inner_rect = false; - return + match clip_rect.intersection(prim_rect) { + Some(_) => { + ClipResult::Partial + } + None => { + ClipResult::Accept + } } - - self.can_calculate_outer_rect = true; - self.local_outer = self.local_outer.and_then(|r| r.intersection(rect)); - - let inner_rect = extract_inner_rect_safe(rect, radius); - self.local_inner = self.local_inner - .and_then(|r| inner_rect.and_then(|ref inner| r.intersection(inner))); - } - ClipSource::BoxShadow(..) | - ClipSource::LineDecoration(..) => { - self.can_calculate_inner_rect = false; } - } - } - - fn finish(self) -> (LayoutRect, Option) { - ( - if self.can_calculate_inner_rect { - self.local_inner.unwrap_or_else(LayoutRect::zero) - } else { - LayoutRect::zero() - }, - if self.can_calculate_outer_rect { - Some(self.local_outer.unwrap_or_else(LayoutRect::zero)) - } else { - None - }, - ) - } -} - - -#[derive(Debug)] -pub struct ClipSources { - pub clips: Vec<(ClipSource, GpuCacheHandle)>, - pub local_inner_rect: LayoutRect, - pub local_outer_rect: Option, - pub only_rectangular_clips: bool, - pub has_image_or_line_decoration_clip: bool, - pub spatial_node_index: SpatialNodeIndex, -} - -impl ClipSources { - pub fn new(clip_iter: I, spatial_node_index: SpatialNodeIndex) -> Self - where - I: IntoIterator, - { - let mut clips = Vec::new(); - let mut bounds_accum = BoundsAccumulator::new(); - let mut has_image_or_line_decoration_clip = false; - let mut only_rectangular_clips = true; - - for clip in clip_iter { - bounds_accum.add(&clip); - has_image_or_line_decoration_clip |= clip.is_image_or_line_decoration_clip(); - only_rectangular_clips &= clip.is_rect(); - clips.push((clip, GpuCacheHandle::new())); - } - - only_rectangular_clips &= !has_image_or_line_decoration_clip; - let (local_inner_rect, local_outer_rect) = bounds_accum.finish(); - - ClipSources { - clips, - local_inner_rect, - local_outer_rect, - only_rectangular_clips, - has_image_or_line_decoration_clip, - spatial_node_index, - } - } - - pub fn from_region( - region: ClipRegion, - spatial_node_index: SpatialNodeIndex, - ) -> ClipSources - where - I: IntoIterator - { - let clip_rect = iter::once(ClipSource::Rectangle(region.main, ClipMode::Clip)); - let clip_image = region.image_mask.map(ClipSource::Image); - let clips_complex = region.complex_clips - .into_iter() - .map(|complex| ClipSource::new_rounded_rect( - complex.rect, - complex.radii, - complex.mode, - )); - - let clips_all = clip_rect.chain(clip_image).chain(clips_complex); - ClipSources::new(clips_all, spatial_node_index) - } - - pub fn clips(&self) -> &[(ClipSource, GpuCacheHandle)] { - &self.clips - } + ClipItem::RoundedRectangle(ref clip_rect, ref radius, ClipMode::Clip) => { + // TODO(gw): Consider caching this in the ClipNode + // if it ever shows in profiles. + // TODO(gw): extract_inner_rect_safe is overly + // conservative for this code! + let inner_clip_rect = extract_inner_rect_safe(clip_rect, radius); + if let Some(inner_clip_rect) = inner_clip_rect { + if inner_clip_rect.contains_rect(prim_rect) { + return ClipResult::Accept; + } + } - pub fn update( - &mut self, - gpu_cache: &mut GpuCache, - resource_cache: &mut ResourceCache, - device_pixel_scale: DevicePixelScale, - ) { - for &mut (ref mut source, ref mut handle) in &mut self.clips { - if let Some(mut request) = gpu_cache.request(handle) { - match *source { - ClipSource::Image(ref mask) => { - let data = ImageMaskData { local_rect: mask.rect }; - data.write_gpu_blocks(request); + match clip_rect.intersection(prim_rect) { + Some(..) => { + ClipResult::Partial } - ClipSource::BoxShadow(ref info) => { - request.push([ - info.shadow_rect_alloc_size.width, - info.shadow_rect_alloc_size.height, - info.clip_mode as i32 as f32, - 0.0, - ]); - request.push([ - info.stretch_mode_x as i32 as f32, - info.stretch_mode_y as i32 as f32, - 0.0, - 0.0, - ]); - request.push(info.prim_shadow_rect); + None => { + ClipResult::Reject } - ClipSource::Rectangle(rect, mode) => { - let data = ClipData::uniform(rect, 0.0, mode); - data.write(&mut request); + } + } + ClipItem::RoundedRectangle(ref clip_rect, ref radius, ClipMode::ClipOut) => { + // TODO(gw): Consider caching this in the ClipNode + // if it ever shows in profiles. + // TODO(gw): extract_inner_rect_safe is overly + // conservative for this code! + let inner_clip_rect = extract_inner_rect_safe(clip_rect, radius); + if let Some(inner_clip_rect) = inner_clip_rect { + if inner_clip_rect.contains_rect(prim_rect) { + return ClipResult::Reject; } - ClipSource::RoundedRectangle(ref rect, ref radius, mode) => { - let data = ClipData::rounded_rect(rect, radius, mode); - data.write(&mut request); + } + + match clip_rect.intersection(prim_rect) { + Some(_) => { + ClipResult::Partial } - ClipSource::LineDecoration(ref info) => { - request.push(info.rect); - request.push([ - info.wavy_line_thickness, - pack_as_float(info.style as u32), - pack_as_float(info.orientation as u32), - 0.0, - ]); + None => { + ClipResult::Accept } } } - - match *source { - ClipSource::Image(ref mask) => { - resource_cache.request_image( - ImageRequest { - key: mask.image, - rendering: ImageRendering::Auto, - tile: None, - }, - gpu_cache, - ); - } - ClipSource::BoxShadow(ref mut info) => { - // Quote from https://drafts.csswg.org/css-backgrounds-3/#shadow-blur - // "the image that would be generated by applying to the shadow a - // Gaussian blur with a standard deviation equal to half the blur radius." - let blur_radius_dp = (info.blur_radius * 0.5 * device_pixel_scale.0).round(); - - // Create the cache key for this box-shadow render task. - let content_scale = LayoutToWorldScale::new(1.0) * device_pixel_scale; - let cache_size = to_cache_size(info.shadow_rect_alloc_size * content_scale); - let bs_cache_key = BoxShadowCacheKey { - blur_radius_dp: blur_radius_dp as i32, - clip_mode: info.clip_mode, - rect_size: (info.shadow_rect_alloc_size * content_scale).round().to_i32(), - br_top_left: (info.shadow_radius.top_left * content_scale).round().to_i32(), - br_top_right: (info.shadow_radius.top_right * content_scale).round().to_i32(), - br_bottom_right: (info.shadow_radius.bottom_right * content_scale).round().to_i32(), - br_bottom_left: (info.shadow_radius.bottom_left * content_scale).round().to_i32(), - }; - - info.cache_key = Some((cache_size, bs_cache_key)); - - if let Some(mut request) = gpu_cache.request(&mut info.clip_data_handle) { - let data = ClipData::rounded_rect( - &info.minimal_shadow_rect, - &info.shadow_radius, - ClipMode::Clip, - ); - - data.write(&mut request); + ClipItem::Image(ref mask) => { + if mask.repeat { + ClipResult::Partial + } else { + match mask.rect.intersection(prim_rect) { + Some(..) => { + ClipResult::Partial + } + None => { + ClipResult::Reject + } } } - _ => {} + } + ClipItem::BoxShadow(..) | + ClipItem::LineDecoration(..) => { + ClipResult::Partial } } } - - pub fn get_screen_bounds( - &self, - transform: &LayoutToWorldFastTransform, - device_pixel_scale: DevicePixelScale, - screen_rect: Option<&DeviceIntRect>, - ) -> (DeviceIntRect, Option) { - // If this translation isn't axis aligned or has a perspective component, don't try to - // calculate the inner rectangle. The rectangle that we produce would include potentially - // clipped screen area. - // TODO(mrobinson): We should eventually try to calculate an inner region or some inner - // rectangle so that we can do screen inner rectangle optimizations for these kind of - // cilps. - let can_calculate_inner_rect = - transform.kind() == TransformedRectKind::AxisAligned && - !transform.has_perspective_component(); - let screen_inner_rect = if can_calculate_inner_rect { - calculate_screen_bounding_rect(transform, &self.local_inner_rect, device_pixel_scale, screen_rect) - .unwrap_or(DeviceIntRect::zero()) - } else { - DeviceIntRect::zero() - }; - - let screen_outer_rect = self.local_outer_rect.map(|outer_rect| - calculate_screen_bounding_rect(transform, &outer_rect, device_pixel_scale, screen_rect) - .unwrap_or(DeviceIntRect::zero()) - ); - - (screen_inner_rect, screen_outer_rect) - } } /// Represents a local rect and a device space @@ -643,95 +1042,3 @@ pub fn rounded_rectangle_contains_point( true } - -pub type ClipChainNodeRef = Option>; - -#[derive(Debug, Clone)] -pub struct ClipChainNode { - pub work_item: ClipWorkItem, - pub local_clip_rect: LayoutRect, - pub screen_outer_rect: DeviceIntRect, - pub screen_inner_rect: DeviceIntRect, - pub prev: ClipChainNodeRef, -} - -#[derive(Debug, Clone)] -pub struct ClipChain { - pub parent_index: Option, - pub combined_outer_screen_rect: DeviceIntRect, - pub combined_inner_screen_rect: DeviceIntRect, - pub nodes: ClipChainNodeRef, - pub has_non_root_coord_system: bool, -} - -impl ClipChain { - pub fn empty(screen_rect: &DeviceIntRect) -> Self { - ClipChain { - parent_index: None, - combined_inner_screen_rect: *screen_rect, - combined_outer_screen_rect: *screen_rect, - nodes: None, - has_non_root_coord_system: false, - } - } - - pub fn new_with_added_node(&self, new_node: &ClipChainNode) -> Self { - // If the new node's inner rectangle completely surrounds our outer rectangle, - // we can discard the new node entirely since it isn't going to affect anything. - if new_node.screen_inner_rect.contains_rect(&self.combined_outer_screen_rect) { - return self.clone(); - } - - let mut new_chain = self.clone(); - new_chain.add_node(new_node.clone()); - new_chain - } - - pub fn add_node(&mut self, mut new_node: ClipChainNode) { - new_node.prev = self.nodes.clone(); - - // If this clip's outer rectangle is completely enclosed by the clip - // chain's inner rectangle, then the only clip that matters from this point - // on is this clip. We can disconnect this clip from the parent clip chain. - if self.combined_inner_screen_rect.contains_rect(&new_node.screen_outer_rect) { - new_node.prev = None; - } - - self.combined_outer_screen_rect = - self.combined_outer_screen_rect.intersection(&new_node.screen_outer_rect) - .unwrap_or_else(DeviceIntRect::zero); - self.combined_inner_screen_rect = - self.combined_inner_screen_rect.intersection(&new_node.screen_inner_rect) - .unwrap_or_else(DeviceIntRect::zero); - - self.has_non_root_coord_system |= new_node.work_item.coordinate_system_id != CoordinateSystemId::root(); - - self.nodes = Some(Arc::new(new_node)); - } -} - -pub struct ClipChainNodeIter { - pub current: ClipChainNodeRef, -} - -impl Iterator for ClipChainNodeIter { - type Item = Arc; - - fn next(&mut self) -> ClipChainNodeRef { - let previous = self.current.clone(); - self.current = match self.current { - Some(ref item) => item.prev.clone(), - None => return None, - }; - previous - } -} - -#[derive(Debug, Clone)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct ClipWorkItem { - pub clip_sources_index: ClipSourcesIndex, - pub coordinate_system_id: CoordinateSystemId, -} - diff --git a/webrender/src/clip_node.rs b/webrender/src/clip_node.rs deleted file mode 100644 index bcb8b2ce5f..0000000000 --- a/webrender/src/clip_node.rs +++ /dev/null @@ -1,78 +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/. */ - -use api::DevicePixelScale; -use clip::{ClipChain, ClipChainNode, ClipSourcesIndex, ClipStore, ClipWorkItem}; -use clip_scroll_tree::{ClipChainIndex}; -use gpu_cache::GpuCache; -use resource_cache::ResourceCache; -use spatial_node::SpatialNode; - -#[derive(Debug)] -pub struct ClipNode { - /// A handle to this clip nodes clips in the ClipStore. - pub clip_sources_index: ClipSourcesIndex, - - /// An index to a ClipChain defined by this ClipNode's hiearchy in the display - /// list. - pub clip_chain_index: ClipChainIndex, - - /// The index of the parent ClipChain of this node's hiearchical ClipChain. - pub parent_clip_chain_index: ClipChainIndex, - - /// A copy of the ClipChainNode this node would produce. We need to keep a copy, - /// because the ClipChain may not contain our node if is optimized out, but API - /// defined ClipChains will still need to access it. - pub clip_chain_node: Option, -} - -impl ClipNode { - pub fn update( - &mut self, - device_pixel_scale: DevicePixelScale, - clip_store: &mut ClipStore, - resource_cache: &mut ResourceCache, - gpu_cache: &mut GpuCache, - clip_chains: &mut [ClipChain], - spatial_nodes: &[SpatialNode], - ) { - let clip_sources = &mut clip_store[self.clip_sources_index]; - clip_sources.update(gpu_cache, resource_cache, device_pixel_scale); - let spatial_node = &spatial_nodes[clip_sources.spatial_node_index.0]; - - let (screen_inner_rect, screen_outer_rect) = clip_sources.get_screen_bounds( - &spatial_node.world_content_transform, - device_pixel_scale, - None, - ); - - // All clipping SpatialNodes should have outer rectangles, because they never - // use the BorderCorner clip type and they always have at last one non-ClipOut - // Rectangle ClipSource. - let screen_outer_rect = screen_outer_rect - .expect("Clipping node didn't have outer rect."); - let local_outer_rect = clip_sources.local_outer_rect - .expect("Clipping node didn't have outer rect."); - - let new_node = ClipChainNode { - work_item: ClipWorkItem { - clip_sources_index: self.clip_sources_index, - coordinate_system_id: spatial_node.coordinate_system_id, - }, - local_clip_rect: local_outer_rect - .translate(&spatial_node.coordinate_system_relative_offset), - screen_outer_rect, - screen_inner_rect, - prev: None, - }; - - let mut clip_chain = - clip_chains[self.parent_clip_chain_index.0] - .new_with_added_node(&new_node); - - self.clip_chain_node = Some(new_node); - clip_chain.parent_index = Some(self.parent_clip_chain_index); - clip_chains[self.clip_chain_index.0] = clip_chain; - } -} diff --git a/webrender/src/clip_scroll_tree.rs b/webrender/src/clip_scroll_tree.rs index 763cd62c3c..5be29e37f1 100644 --- a/webrender/src/clip_scroll_tree.rs +++ b/webrender/src/clip_scroll_tree.rs @@ -2,16 +2,13 @@ * 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::{DeviceIntRect, DevicePixelScale, ExternalScrollId, LayoutPoint, LayoutRect, LayoutVector2D}; -use api::{PipelineId, ScrollClamping, ScrollLocation, ScrollNodeState}; +use api::{ExternalScrollId, LayoutPoint, LayoutRect, LayoutVector2D}; +use api::{PipelineId, ScrollClamping, ScrollNodeState, ScrollLocation}; use api::{LayoutSize, LayoutTransform, PropertyBinding, ScrollSensitivity, WorldPoint}; -use clip::{ClipChain, ClipSourcesIndex, ClipStore}; -use clip_node::ClipNode; -use gpu_cache::GpuCache; +use clip::{ClipStore}; use gpu_types::TransformPalette; use internal_types::{FastHashMap, FastHashSet}; use print_tree::{PrintTree, PrintTreePrinter}; -use resource_cache::ResourceCache; use scene::SceneProperties; use spatial_node::{ScrollFrameInfo, SpatialNode, SpatialNodeType, StickyFrameInfo}; use util::LayoutToWorldFastTransform; @@ -32,9 +29,6 @@ pub struct CoordinateSystemId(pub u32); #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct SpatialNodeIndex(pub usize); -#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)] -pub struct ClipNodeIndex(pub usize); - const ROOT_REFERENCE_FRAME_INDEX: SpatialNodeIndex = SpatialNodeIndex(0); const TOPMOST_SCROLL_NODE_INDEX: SpatialNodeIndex = SpatialNodeIndex(1); @@ -53,36 +47,11 @@ impl CoordinateSystemId { } } -pub struct ClipChainDescriptor { - pub index: ClipChainIndex, - pub parent: Option, - pub clips: Vec, -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct ClipChainIndex(pub usize); - -impl ClipChainIndex { - pub const NO_CLIP: Self = ClipChainIndex(0); -} - pub struct ClipScrollTree { /// Nodes which determine the positions (offsets and transforms) for primitives /// and clips. pub spatial_nodes: Vec, - /// Nodes which clip primitives. - pub clip_nodes: Vec, - - /// A Vec of all descriptors that describe ClipChains in the order in which they are - /// encountered during display list flattening. ClipChains are expected to never be - /// the children of ClipChains later in the list. - pub clip_chains_descriptors: Vec, - - /// A vector of all ClipChains in this ClipScrollTree including those from - /// ClipChainDescriptors and also those defined by the clipping node hierarchy. - pub clip_chains: Vec, - pub pending_scroll_offsets: FastHashMap, /// A set of pipelines which should be discarded the next time this @@ -116,9 +85,6 @@ impl ClipScrollTree { pub fn new() -> Self { ClipScrollTree { spatial_nodes: Vec::new(), - clip_nodes: Vec::new(), - clip_chains_descriptors: Vec::new(), - clip_chains: vec![ClipChain::empty(&DeviceIntRect::zero())], pending_scroll_offsets: FastHashMap::default(), pipelines_to_discard: FastHashSet::default(), } @@ -167,10 +133,7 @@ impl ClipScrollTree { } } - self.clip_nodes.clear(); self.pipelines_to_discard.clear(); - self.clip_chains = vec![ClipChain::empty(&DeviceIntRect::zero())]; - self.clip_chains_descriptors.clear(); scroll_states } @@ -220,11 +183,6 @@ impl ClipScrollTree { pub fn update_tree( &mut self, - screen_rect: &DeviceIntRect, - device_pixel_scale: DevicePixelScale, - clip_store: &mut ClipStore, - resource_cache: &mut ResourceCache, - gpu_cache: &mut GpuCache, pan: WorldPoint, scene_properties: &SceneProperties, ) -> TransformPalette { @@ -233,8 +191,6 @@ impl ClipScrollTree { return transform_palette; } - self.clip_chains[0] = ClipChain::empty(screen_rect); - let root_reference_frame_index = self.root_reference_frame_index(); let mut state = TransformUpdateState { parent_reference_frame_transform: LayoutVector2D::new(pan.x, pan.y).into(), @@ -255,18 +211,6 @@ impl ClipScrollTree { scene_properties, ); - for clip_node in &mut self.clip_nodes { - clip_node.update( - device_pixel_scale, - clip_store, - resource_cache, - gpu_cache, - &mut self.clip_chains, - &self.spatial_nodes, - ); - } - self.build_clip_chains(screen_rect); - transform_palette } @@ -309,32 +253,6 @@ impl ClipScrollTree { } } - pub fn build_clip_chains(&mut self, screen_rect: &DeviceIntRect) { - for descriptor in &self.clip_chains_descriptors { - // A ClipChain is an optional parent (which is another ClipChain) and a list of - // SpatialNode clipping nodes. Here we start the ClipChain with a clone of the - // parent's node, if necessary. - let mut chain = match descriptor.parent { - Some(index) => self.clip_chains[index.0].clone(), - None => ClipChain::empty(screen_rect), - }; - - // Now we walk through each ClipNode in the vector and extract their ClipChain nodes to - // construct the final list. - for clip_index in &descriptor.clips { - match self.clip_nodes[clip_index.0] { - ClipNode { clip_chain_node: Some(ref node), .. } => { - chain.add_node(node.clone()); - } - ClipNode { .. } => warn!("Found uninitialized clipping ClipNode."), - }; - } - - chain.parent_index = descriptor.parent; - self.clip_chains[descriptor.index.0] = chain; - } - } - pub fn finalize_and_apply_pending_scroll_offsets(&mut self, old_states: ScrollStates) { for node in &mut self.spatial_nodes { let external_id = match node.node_type { @@ -352,22 +270,6 @@ impl ClipScrollTree { } } - pub fn add_clip_node( - &mut self, - parent_clip_chain_index: ClipChainIndex, - clip_sources_index: ClipSourcesIndex, - ) -> (ClipNodeIndex, ClipChainIndex) { - let clip_chain_index = self.allocate_clip_chain(); - let node = ClipNode { - parent_clip_chain_index, - clip_sources_index, - clip_chain_index, - clip_chain_node: None, - }; - let node_index = self.push_clip_node(node); - (node_index, clip_chain_index) - } - pub fn add_scroll_frame( &mut self, parent_index: SpatialNodeIndex, @@ -420,22 +322,6 @@ impl ClipScrollTree { self.add_spatial_node(node) } - pub fn add_clip_chain_descriptor( - &mut self, - parent: Option, - clips: Vec - ) -> ClipChainIndex { - let index = self.allocate_clip_chain(); - self.clip_chains_descriptors.push(ClipChainDescriptor { index, parent, clips }); - index - } - - pub fn push_clip_node(&mut self, node: ClipNode) -> ClipNodeIndex { - let index = ClipNodeIndex(self.clip_nodes.len()); - self.clip_nodes.push(node); - index - } - pub fn add_spatial_node(&mut self, node: SpatialNode) -> SpatialNodeIndex { let index = SpatialNodeIndex(self.spatial_nodes.len()); @@ -502,15 +388,4 @@ impl ClipScrollTree { self.print_node(self.root_reference_frame_index(), pt, clip_store); } } - - pub fn allocate_clip_chain(&mut self) -> ClipChainIndex { - debug_assert!(!self.clip_chains.is_empty()); - let new_clip_chain =self.clip_chains[0].clone(); - self.clip_chains.push(new_clip_chain); - ClipChainIndex(self.clip_chains.len() - 1) - } - - pub fn get_clip_chain(&self, index: ClipChainIndex) -> &ClipChain { - &self.clip_chains[index.0] - } } diff --git a/webrender/src/display_list_flattener.rs b/webrender/src/display_list_flattener.rs index 0ab767a6fd..7907c2c356 100644 --- a/webrender/src/display_list_flattener.rs +++ b/webrender/src/display_list_flattener.rs @@ -12,9 +12,9 @@ use api::{LayoutPrimitiveInfo, LayoutRect, LayoutSize, LayoutTransform, LayoutVe use api::{LineOrientation, LineStyle, LocalClip, NinePatchBorderSource, PipelineId}; use api::{PropertyBinding, ReferenceFrame, RepeatMode, ScrollFrameDisplayItem, ScrollSensitivity}; use api::{Shadow, SpecificDisplayItem, StackingContext, StickyFrameDisplayItem, TexelRect}; -use api::{TransformStyle, YuvColorSpace, YuvData}; -use clip::{ClipRegion, ClipSource, ClipSources, ClipSourcesIndex, ClipStore}; -use clip_scroll_tree::{ClipChainIndex, ClipNodeIndex, ClipScrollTree, SpatialNodeIndex}; +use api::{ClipMode, TransformStyle, YuvColorSpace, YuvData}; +use clip::{ClipChainId, ClipRegion, ClipItem, ClipStore}; +use clip_scroll_tree::{ClipScrollTree, SpatialNodeIndex}; use euclid::vec2; use frame_builder::{ChasePrimitive, FrameBuilder, FrameBuilderConfig}; use glyph_rasterizer::FontInstance; @@ -33,7 +33,7 @@ use resource_cache::{FontInstanceMap, ImageRequest}; use scene::{Scene, ScenePipeline, StackingContextHelpers}; use scene_builder::{BuiltScene, SceneRequest}; use spatial_node::{SpatialNodeType, StickyFrameInfo}; -use std::{f32, mem}; +use std::{f32, iter, mem}; use tiling::{CompositeOps, ScrollbarPrimitive}; use util::{MaxRect, RectHelpers, recycle_vec}; @@ -49,20 +49,19 @@ static DEFAULT_SCROLLBAR_COLOR: ColorF = ColorF { /// responsible for mapping both ClipId to ClipChainIndex and ClipId to SpatialNodeIndex. #[derive(Default)] pub struct ClipIdToIndexMapper { - clip_chain_map: FastHashMap, - clip_node_map: FastHashMap, + clip_chain_map: FastHashMap, spatial_node_map: FastHashMap, } impl ClipIdToIndexMapper { - pub fn add_clip_chain(&mut self, id: ClipId, index: ClipChainIndex) { + pub fn add_clip_chain(&mut self, id: ClipId, index: ClipChainId) { let _old_value = self.clip_chain_map.insert(id, index); debug_assert!(_old_value.is_none()); } pub fn map_to_parent_clip_chain(&mut self, id: ClipId, parent_id: &ClipId) { - let parent_chain_index = self.get_clip_chain_index(parent_id); - self.add_clip_chain(id, parent_chain_index); + let parent_chain_id = self.get_clip_chain_id(parent_id); + self.add_clip_chain(id, parent_chain_id); } pub fn map_spatial_node(&mut self, id: ClipId, index: SpatialNodeIndex) { @@ -70,30 +69,10 @@ impl ClipIdToIndexMapper { debug_assert!(_old_value.is_none()); } - pub fn map_clip_node(&mut self, id: ClipId, index: ClipNodeIndex) { - let _old_value = self.clip_node_map.insert(id, index); - debug_assert!(_old_value.is_none()); - } - - pub fn get_clip_chain_index(&self, id: &ClipId) -> ClipChainIndex { + pub fn get_clip_chain_id(&self, id: &ClipId) -> ClipChainId { self.clip_chain_map[id] } - pub fn get_clip_node_index(&self, id: ClipId) -> ClipNodeIndex { - match id { - ClipId::Clip(..) => { - self.clip_node_map[&id] - } - ClipId::Spatial(..) => { - // We could theoretically map back to the containing clip node with the current - // design, but we will eventually fully separate out clipping from spatial nodes - // in the display list. We don't ever need to do this anyway. - panic!("Tried to use positioning node as clip node."); - } - ClipId::ClipChain(_) => panic!("Tried to use ClipChain as scroll node."), - } - } - pub fn get_spatial_node_index(&self, id: ClipId) -> SpatialNodeIndex { match id { ClipId::Clip(..) | @@ -140,7 +119,7 @@ pub struct DisplayListFlattener<'a> { shadow_stack: Vec<(Shadow, PictureIndex)>, /// The stack keeping track of the root clip chains associated with pipelines. - pipeline_clip_chain_stack: Vec, + pipeline_clip_chain_stack: Vec, /// A list of scrollbar primitives. pub scrollbar_prims: Vec, @@ -192,7 +171,7 @@ impl<'a> DisplayListFlattener<'a> { picture_stack: Vec::new(), shadow_stack: Vec::new(), sc_stack: Vec::new(), - pipeline_clip_chain_stack: Vec::new(), + pipeline_clip_chain_stack: vec![ClipChainId::NONE], prim_store: old_builder.prim_store.recycle(), clip_store: old_builder.clip_store.recycle(), }; @@ -286,7 +265,7 @@ impl<'a> DisplayListFlattener<'a> { let scrollbar_rect = LayoutRect::new(LayoutPoint::zero(), LayoutSize::new(10.0, 70.0)); let container_rect = LayoutRect::new(LayoutPoint::zero(), *frame_size); self.add_scroll_bar( - reference_frame_info, + reference_frame_info.spatial_node_index, &LayoutPrimitiveInfo::new(scrollbar_rect), DEFAULT_SCROLLBAR_COLOR, ScrollbarInfo(scroll_frame_info.spatial_node_index, container_rect), @@ -684,18 +663,52 @@ impl<'a> DisplayListFlattener<'a> { self.add_clip_node(info.id, clip_and_scroll_ids.scroll_node_id, clip_region); } SpecificDisplayItem::ClipChain(ref info) => { - let items = self.get_clip_chain_items(pipeline_id, item.clip_chain_items()) - .map(|id| self.id_to_index_mapper.get_clip_node_index(id)) - .collect(); - let parent = match info.parent { - Some(id) => Some( - self.id_to_index_mapper.get_clip_chain_index(&ClipId::ClipChain(id)) - ), - None => self.pipeline_clip_chain_stack.last().cloned(), + // For a user defined clip-chain the parent (if specified) must + // refer to another user defined clip-chain. If none is specified, + // the parent is the root clip-chain for the given pipeline. This + // is used to provide a root clip chain for iframes. + let mut parent_clip_chain_id = match info.parent { + Some(id) => { + self.id_to_index_mapper.get_clip_chain_id(&ClipId::ClipChain(id)) + } + None => { + self.pipeline_clip_chain_stack.last().cloned().unwrap() + } }; - let clip_chain_index = - self.clip_scroll_tree.add_clip_chain_descriptor(parent, items); - self.id_to_index_mapper.add_clip_chain(ClipId::ClipChain(info.id), clip_chain_index); + + // Create a linked list of clip chain nodes. To do this, we will + // create a clip chain node + clip source for each listed clip id, + // and link these together, with the root of this list parented to + // the parent clip chain node found above. For this API, the clip + // id that is specified for an existing clip chain node is used to + // get the index of the clip sources that define that clip node. + + let mut clip_chain_id = parent_clip_chain_id; + + // For each specified clip id + for item in self.get_clip_chain_items(pipeline_id, item.clip_chain_items()) { + // Map the ClipId to an existing clip chain node. + let item_clip_chain_id = self + .id_to_index_mapper + .get_clip_chain_id(&item); + // Get the id of the clip sources entry for that clip chain node. + let clip_item_range = self + .clip_store + .get_clip_chain(item_clip_chain_id) + .clip_item_range; + // Add a new clip chain node, which references the same clip sources, and + // parent it to the current parent. + clip_chain_id = self + .clip_store + .add_clip_chain(clip_item_range, parent_clip_chain_id); + // For the next clip node, use this new clip chain node as the parent, + // to form a linked list. + parent_clip_chain_id = clip_chain_id; + } + + // Map the last entry in the clip chain to the supplied ClipId. This makes + // this ClipId available as a source to other user defined clip chains. + self.id_to_index_mapper.add_clip_chain(ClipId::ClipChain(info.id), clip_chain_id); }, SpecificDisplayItem::ScrollFrame(ref info) => { self.flatten_scroll_frame( @@ -735,15 +748,29 @@ impl<'a> DisplayListFlattener<'a> { None } - fn add_clip_sources( + // Given a list of clip sources, a positioning node and + // a parent clip chain, return a new clip chain entry. + // If the supplied list of clip sources is empty, then + // just return the parent clip chain id directly. + fn build_clip_chain( &mut self, - clip_sources: Vec, + clip_items: Vec, spatial_node_index: SpatialNodeIndex, - ) -> Option { - if clip_sources.is_empty() { - None + parent_clip_chain_id: ClipChainId, + ) -> ClipChainId { + if clip_items.is_empty() { + parent_clip_chain_id } else { - Some(self.clip_store.insert(ClipSources::new(clip_sources, spatial_node_index))) + // Add a range of clip sources. + let clip_item_range = self + .clip_store + .add_clip_items(clip_items, spatial_node_index); + + // Add clip chain node that references the clip source range. + self.clip_store.add_clip_chain( + clip_item_range, + parent_clip_chain_id, + ) } } @@ -753,7 +780,7 @@ impl<'a> DisplayListFlattener<'a> { pub fn create_primitive( &mut self, info: &LayoutPrimitiveInfo, - clip_sources: Option, + clip_chain_id: ClipChainId, container: PrimitiveContainer, ) -> PrimitiveIndex { let stacking_context = self.sc_stack.last().expect("bug: no stacking context!"); @@ -762,7 +789,7 @@ impl<'a> DisplayListFlattener<'a> { &info.rect, &info.clip_rect, info.is_backface_visible && stacking_context.is_backface_visible, - clip_sources, + clip_chain_id, info.tag, container, ) @@ -795,12 +822,12 @@ impl<'a> DisplayListFlattener<'a> { pub fn add_primitive_to_draw_list( &mut self, prim_index: PrimitiveIndex, - clip_and_scroll: ScrollNodeAndClipChain, + spatial_node_index: SpatialNodeIndex, ) { // Add primitive to the top-most Picture on the stack. let pic_index = self.picture_stack.last().unwrap(); let pic = &mut self.prim_store.pictures[pic_index.0]; - pic.add_primitive(prim_index, clip_and_scroll); + pic.add_primitive(prim_index, spatial_node_index); } /// Convenience interface that creates a primitive entry and adds it @@ -809,7 +836,7 @@ impl<'a> DisplayListFlattener<'a> { &mut self, clip_and_scroll: ScrollNodeAndClipChain, info: &LayoutPrimitiveInfo, - clip_sources: Vec, + clip_items: Vec, container: PrimitiveContainer, ) { if !self.shadow_stack.is_empty() { @@ -823,41 +850,49 @@ impl<'a> DisplayListFlattener<'a> { info.clip_rect = info.clip_rect.translate(&shadow.offset); // Offset any local clip sources by the shadow offset. - let clip_sources: Vec = clip_sources + let clip_items: Vec = clip_items .iter() .map(|cs| cs.offset(&shadow.offset)) .collect(); - let clip_sources = self.add_clip_sources( - clip_sources, + let clip_chain_id = self.build_clip_chain( + clip_items, clip_and_scroll.spatial_node_index, + clip_and_scroll.clip_chain_id, ); // Construct and add a primitive for the given shadow. let shadow_prim_index = self.create_primitive( &info, - clip_sources, + clip_chain_id, container.create_shadow(shadow), ); // Add the new primitive to the shadow picture. let shadow_pic = &mut self.prim_store.pictures[shadow_pic_index.0]; - shadow_pic.add_primitive(shadow_prim_index, clip_and_scroll); + shadow_pic.add_primitive( + shadow_prim_index, + clip_and_scroll.spatial_node_index, + ); } self.shadow_stack = shadow_stack; } if container.is_visible() { - let clip_sources = self.add_clip_sources( - clip_sources, + let clip_chain_id = self.build_clip_chain( + clip_items, clip_and_scroll.spatial_node_index, + clip_and_scroll.clip_chain_id, ); - let prim_index = self.create_primitive(info, clip_sources, container); + let prim_index = self.create_primitive(info, clip_chain_id, container); if cfg!(debug_assertions) && ChasePrimitive::LocalRect(info.rect) == self.config.chase_primitive { println!("Chasing {:?}", prim_index); self.prim_store.chase_id = Some(prim_index); } self.add_primitive_to_hit_testing_list(info, clip_and_scroll); - self.add_primitive_to_draw_list(prim_index, clip_and_scroll); + self.add_primitive_to_draw_list( + prim_index, + clip_and_scroll.spatial_node_index, + ); } } @@ -872,14 +907,11 @@ impl<'a> DisplayListFlattener<'a> { clipping_node: Option, glyph_raster_space: GlyphRasterSpace, ) { + let spatial_node_index = self.id_to_index_mapper.get_spatial_node_index(spatial_node); let clip_chain_id = match clipping_node { - Some(ref clipping_node) => self.id_to_index_mapper.get_clip_chain_index(clipping_node), - None => ClipChainIndex::NO_CLIP, + Some(ref clipping_node) => self.id_to_index_mapper.get_clip_chain_id(clipping_node), + None => ClipChainId::NONE, }; - let clip_and_scroll = ScrollNodeAndClipChain::new( - self.id_to_index_mapper.get_spatial_node_index(spatial_node), - clip_chain_id - ); // Construct the necessary set of Picture primitives // to draw this stacking context. @@ -972,7 +1004,7 @@ impl<'a> DisplayListFlattener<'a> { &LayoutRect::zero(), &max_clip, is_backface_visible, - None, + clip_chain_id, None, PrimitiveContainer::Brush(prim), ); @@ -980,7 +1012,7 @@ impl<'a> DisplayListFlattener<'a> { let parent_pic_index = *self.picture_stack.last().unwrap(); let pic = &mut self.prim_store.pictures[parent_pic_index.0]; - pic.add_primitive(prim_index, clip_and_scroll); + pic.add_primitive(prim_index, spatial_node_index); self.picture_stack.push(container_index); @@ -1021,7 +1053,7 @@ impl<'a> DisplayListFlattener<'a> { &LayoutRect::zero(), &max_clip, is_backface_visible, - None, + clip_chain_id, None, PrimitiveContainer::Brush(src_prim), ); @@ -1029,7 +1061,7 @@ impl<'a> DisplayListFlattener<'a> { let parent_pic = &mut self.prim_store.pictures[parent_pic_index.0]; parent_pic_index = src_pic_index; - parent_pic.add_primitive(src_prim_index, clip_and_scroll); + parent_pic.add_primitive(src_prim_index, spatial_node_index); self.picture_stack.push(src_pic_index); } @@ -1051,14 +1083,14 @@ impl<'a> DisplayListFlattener<'a> { &LayoutRect::zero(), &max_clip, is_backface_visible, - None, + clip_chain_id, None, PrimitiveContainer::Brush(src_prim), ); let parent_pic = &mut self.prim_store.pictures[parent_pic_index.0]; parent_pic_index = src_pic_index; - parent_pic.add_primitive(src_prim_index, clip_and_scroll); + parent_pic.add_primitive(src_prim_index, spatial_node_index); self.picture_stack.push(src_pic_index); } @@ -1108,13 +1140,13 @@ impl<'a> DisplayListFlattener<'a> { &LayoutRect::zero(), &max_clip, is_backface_visible, - None, + clip_chain_id, None, PrimitiveContainer::Brush(sc_prim), ); let parent_pic = &mut self.prim_store.pictures[parent_pic_index.0]; - parent_pic.add_primitive(sc_prim_index, clip_and_scroll); + parent_pic.add_primitive(sc_prim_index, spatial_node_index); // Add this as the top-most picture for primitives to be added to. self.picture_stack.push(pic_index); @@ -1191,7 +1223,7 @@ impl<'a> DisplayListFlattener<'a> { match parent_id { Some(ref parent_id) => self.id_to_index_mapper.map_to_parent_clip_chain(reference_frame_id, parent_id), - _ => self.id_to_index_mapper.add_clip_chain(reference_frame_id, ClipChainIndex::NO_CLIP), + _ => self.id_to_index_mapper.add_clip_chain(reference_frame_id, ClipChainId::NONE), } index } @@ -1245,24 +1277,52 @@ impl<'a> DisplayListFlattener<'a> { new_node_id: ClipId, parent_id: ClipId, clip_region: ClipRegion, - ) -> ClipChainIndex + ) -> ClipChainId where I: IntoIterator { - let parent_clip_chain_index = self.id_to_index_mapper.get_clip_chain_index(&parent_id); + // Add a new ClipNode - this is a ClipId that identifies a list of clip items, + // and the positioning node associated with those clip sources. + + // Map from parent ClipId to existing clip-chain. + let parent_clip_chain_index = self + .id_to_index_mapper + .get_clip_chain_id(&parent_id); + // Map the ClipId for the positioning node to a spatial node index. let spatial_node = self.id_to_index_mapper.get_spatial_node_index(parent_id); - let clip_sources = ClipSources::from_region(clip_region, spatial_node); - let handle = self.clip_store.insert(clip_sources); - - let (node_index, clip_chain_index) = self.clip_scroll_tree.add_clip_node( - parent_clip_chain_index, - handle, - ); - self.id_to_index_mapper.map_spatial_node(new_node_id, spatial_node); - self.id_to_index_mapper.map_clip_node(new_node_id, node_index); - self.id_to_index_mapper.add_clip_chain(new_node_id, clip_chain_index); - clip_chain_index + // Build the clip sources from the supplied region. + // TODO(gw): We should fix this up to take advantage of the recent + // work to avoid heap allocations where possible! + let clip_rect = iter::once(ClipItem::Rectangle(clip_region.main, ClipMode::Clip)); + let clip_image = clip_region.image_mask.map(ClipItem::Image); + let clips_complex = clip_region.complex_clips + .into_iter() + .map(|complex| ClipItem::new_rounded_rect( + complex.rect, + complex.radii, + complex.mode, + )); + let clips = clip_rect.chain(clip_image).chain(clips_complex).collect(); + + // Add those clip sources to the clip store. + let clip_item_range = self + .clip_store + .add_clip_items(clips, spatial_node); + + // Add a mapping for this ClipId in case it's referenced as a positioning node. + self.id_to_index_mapper + .map_spatial_node(new_node_id, spatial_node); + + // Add the new clip chain entry + let clip_chain_id = self + .clip_store + .add_clip_chain(clip_item_range, parent_clip_chain_index); + + // Map the supplied ClipId -> clip chain id. + self.id_to_index_mapper.add_clip_chain(new_node_id, clip_chain_id); + + clip_chain_id } pub fn add_scroll_frame( @@ -1333,14 +1393,17 @@ impl<'a> DisplayListFlattener<'a> { &LayoutRect::zero(), &max_clip, info.is_backface_visible, - None, + clip_and_scroll.clip_chain_id, None, PrimitiveContainer::Brush(shadow_prim), ); // Add the shadow primitive. This must be done before pushing this // picture on to the shadow stack, to avoid infinite recursion! - self.add_primitive_to_draw_list(shadow_prim_index, clip_and_scroll); + self.add_primitive_to_draw_list( + shadow_prim_index, + clip_and_scroll.spatial_node_index, + ); self.shadow_stack.push((shadow, shadow_pic_index)); } @@ -1355,7 +1418,7 @@ impl<'a> DisplayListFlattener<'a> { info: &LayoutPrimitiveInfo, color: ColorF, segments: Option, - extra_clips: Vec, + extra_clips: Vec, ) { if color.a == 0.0 { // Don't add transparent rectangles to the draw list, but do consider them for hit @@ -1397,7 +1460,7 @@ impl<'a> DisplayListFlattener<'a> { pub fn add_scroll_bar( &mut self, - clip_and_scroll: ScrollNodeAndClipChain, + spatial_node_index: SpatialNodeIndex, info: &LayoutPrimitiveInfo, color: ColorF, scrollbar_info: ScrollbarInfo, @@ -1413,13 +1476,13 @@ impl<'a> DisplayListFlattener<'a> { let prim_index = self.create_primitive( info, - None, + ClipChainId::NONE, PrimitiveContainer::Brush(prim), ); self.add_primitive_to_draw_list( prim_index, - clip_and_scroll, + spatial_node_index, ); self.scrollbar_prims.push(ScrollbarPrimitive { @@ -1451,7 +1514,7 @@ impl<'a> DisplayListFlattener<'a> { LineStyle::Dotted | LineStyle::Dashed => { vec![ - ClipSource::new_line_decoration( + ClipItem::new_line_decoration( info.rect, style, orientation, @@ -1926,7 +1989,7 @@ impl<'a> DisplayListFlattener<'a> { pub fn map_clip_and_scroll(&mut self, info: &ClipAndScrollInfo) -> ScrollNodeAndClipChain { ScrollNodeAndClipChain::new( self.id_to_index_mapper.get_spatial_node_index(info.scroll_node_id), - self.id_to_index_mapper.get_clip_chain_index(&info.clip_node_id()) + self.id_to_index_mapper.get_clip_chain_id(&info.clip_node_id()) ) } diff --git a/webrender/src/frame_builder.rs b/webrender/src/frame_builder.rs index 8319c793a5..f6b2544d79 100644 --- a/webrender/src/frame_builder.rs +++ b/webrender/src/frame_builder.rs @@ -5,7 +5,7 @@ use api::{BuiltDisplayList, ColorF, DeviceIntPoint, DeviceIntRect, DevicePixelScale}; use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, DocumentLayer, FontRenderMode}; use api::{LayoutPoint, LayoutRect, LayoutSize, PipelineId, WorldPoint}; -use clip::{ClipChain, ClipStore}; +use clip::{ClipStore}; use clip_scroll_tree::{ClipScrollTree, SpatialNodeIndex}; use display_list_flattener::{DisplayListFlattener}; use gpu_cache::GpuCache; @@ -72,7 +72,6 @@ pub struct FrameBuildingContext<'a> { pub pipelines: &'a FastHashMap>, pub screen_rect: DeviceIntRect, pub clip_scroll_tree: &'a ClipScrollTree, - pub clip_chains: &'a [ClipChain], pub transforms: &'a TransformPalette, pub max_local_clip: LayoutRect, } @@ -114,25 +113,19 @@ impl PictureState { } pub struct PrimitiveRunContext<'a> { - pub clip_chain: &'a ClipChain, pub scroll_node: &'a SpatialNode, pub spatial_node_index: SpatialNodeIndex, pub transform: Transform<'a>, - pub local_clip_rect: LayoutRect, } impl<'a> PrimitiveRunContext<'a> { pub fn new( - clip_chain: &'a ClipChain, scroll_node: &'a SpatialNode, spatial_node_index: SpatialNodeIndex, - local_clip_rect: LayoutRect, transform: Transform<'a>, ) -> Self { PrimitiveRunContext { - clip_chain, scroll_node, - local_clip_rect, spatial_node_index, transform, } @@ -219,7 +212,6 @@ impl FrameBuilder { pipelines, screen_rect: self.screen_rect.to_i32(), clip_scroll_tree, - clip_chains: &clip_scroll_tree.clip_chains, transforms: transform_palette, max_local_clip: LayoutRect::new( LayoutPoint::new(-MAX_CLIP_COORD, -MAX_CLIP_COORD), @@ -331,11 +323,6 @@ impl FrameBuilder { gpu_cache.begin_frame(); let transform_palette = clip_scroll_tree.update_tree( - &self.screen_rect.to_i32(), - device_pixel_scale, - &mut self.clip_store, - resource_cache, - gpu_cache, pan, scene_properties, ); diff --git a/webrender/src/hit_test.rs b/webrender/src/hit_test.rs index 7b47f75a3c..ebc37338a8 100644 --- a/webrender/src/hit_test.rs +++ b/webrender/src/hit_test.rs @@ -4,9 +4,9 @@ use api::{BorderRadius, ClipMode, HitTestFlags, HitTestItem, HitTestResult, ItemTag, LayoutPoint}; use api::{LayoutPrimitiveInfo, LayoutRect, PipelineId, WorldPoint}; -use clip::{ClipSource, ClipStore, rounded_rectangle_contains_point}; -use clip_node::ClipNode; -use clip_scroll_tree::{ClipChainIndex, ClipNodeIndex, SpatialNodeIndex, ClipScrollTree}; +use clip::{ClipNodeIndex, ClipChainNode, ClipNode, ClipItem, ClipStore}; +use clip::{ClipChainId, rounded_rectangle_contains_point}; +use clip_scroll_tree::{SpatialNodeIndex, ClipScrollTree}; use internal_types::FastHashMap; use prim_store::ScrollNodeAndClipChain; use util::LayoutToWorldFastTransform; @@ -31,48 +31,23 @@ pub struct HitTestClipNode { /// A particular point must be inside all of these regions to be considered clipped in /// for the purposes of a hit test. - regions: Vec, + region: HitTestRegion, } impl HitTestClipNode { - fn new(node: &ClipNode, clip_store: &ClipStore) -> Self { - let clips = &clip_store[node.clip_sources_index]; - let regions = clips.clips().iter().map(|source| { - match source.0 { - 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, ClipMode::Clip), - ClipSource::LineDecoration(_) | - ClipSource::BoxShadow(_) => { - unreachable!("Didn't expect to hit test against BorderCorner / BoxShadow / LineDecoration"); - } - } - }).collect(); + fn new(node: &ClipNode) -> Self { + let region = match node.item { + ClipItem::Rectangle(ref rect, mode) => HitTestRegion::Rectangle(*rect, mode), + ClipItem::RoundedRectangle(ref rect, ref radii, ref mode) => + HitTestRegion::RoundedRectangle(*rect, *radii, *mode), + ClipItem::Image(ref mask) => HitTestRegion::Rectangle(mask.rect, ClipMode::Clip), + ClipItem::LineDecoration(_) | + ClipItem::BoxShadow(_) => HitTestRegion::Invalid, + }; HitTestClipNode { - spatial_node: clips.spatial_node_index, - regions, - } - } -} - -/// A description of a clip chain in the HitTester. This is used to describe -/// hierarchical clip scroll nodes as well as ClipChains, so that they can be -/// handled the same way during hit testing. Once we represent all ClipChains -/// using ClipChainDescriptors, we can get rid of this and just use the -/// ClipChainDescriptor here. -#[derive(Clone)] -struct HitTestClipChainDescriptor { - parent: Option, - clips: Vec, -} - -impl HitTestClipChainDescriptor { - fn empty() -> HitTestClipChainDescriptor { - HitTestClipChainDescriptor { - parent: None, - clips: Vec::new(), + spatial_node: node.spatial_node_index, + region, } } } @@ -100,6 +75,7 @@ impl HitTestingItem { pub struct HitTestingRun(pub Vec, pub ScrollNodeAndClipChain); enum HitTestRegion { + Invalid, Rectangle(LayoutRect, ClipMode), RoundedRectangle(LayoutRect, BorderRadius, ClipMode), } @@ -115,6 +91,7 @@ impl HitTestRegion { rounded_rectangle_contains_point(point, &rect, &radii), HitTestRegion::RoundedRectangle(rect, radii, ClipMode::ClipOut) => !rounded_rectangle_contains_point(point, &rect, &radii), + HitTestRegion::Invalid => true, } } } @@ -123,7 +100,7 @@ pub struct HitTester { runs: Vec, spatial_nodes: Vec, clip_nodes: Vec, - clip_chains: Vec, + clip_chains: Vec, pipeline_root_nodes: FastHashMap, } @@ -151,10 +128,7 @@ impl HitTester { ) { self.spatial_nodes.clear(); self.clip_chains.clear(); - self.clip_chains.resize( - clip_scroll_tree.clip_chains.len(), - HitTestClipChainDescriptor::empty() - ); + self.clip_nodes.clear(); for (index, node) in clip_scroll_tree.spatial_nodes.iter().enumerate() { let index = SpatialNodeIndex(index); @@ -170,50 +144,49 @@ impl HitTester { }); } - for (index, node) in clip_scroll_tree.clip_nodes.iter().enumerate() { - self.clip_nodes.push(HitTestClipNode::new(node, clip_store)); - let clip_chain = self.clip_chains.get_mut(node.clip_chain_index.0).unwrap(); - clip_chain.parent = - clip_scroll_tree.get_clip_chain(node.clip_chain_index).parent_index; - clip_chain.clips = vec![ClipNodeIndex(index)]; + for node in &clip_store.clip_nodes { + self.clip_nodes.push(HitTestClipNode::new(node)); } - for descriptor in &clip_scroll_tree.clip_chains_descriptors { - let clip_chain = self.clip_chains.get_mut(descriptor.index.0).unwrap(); - clip_chain.parent = clip_scroll_tree.get_clip_chain(descriptor.index).parent_index; - clip_chain.clips = descriptor.clips.clone(); - } + self.clip_chains + .extend_from_slice(&clip_store.clip_chain_nodes); } fn is_point_clipped_in_for_clip_chain( &self, point: WorldPoint, - clip_chain_index: ClipChainIndex, + clip_chain_id: ClipChainId, test: &mut HitTest ) -> bool { - if let Some(result) = test.get_from_clip_chain_cache(clip_chain_index) { + if clip_chain_id == ClipChainId::NONE { + return true; + } + + if let Some(result) = test.get_from_clip_chain_cache(clip_chain_id) { return result == ClippedIn::ClippedIn; } - let descriptor = &self.clip_chains[clip_chain_index.0]; - let parent_clipped_in = match descriptor.parent { - None => true, - Some(parent) => self.is_point_clipped_in_for_clip_chain(point, parent, test), - }; + let descriptor = &self.clip_chains[clip_chain_id.0 as usize]; + let parent_clipped_in = self.is_point_clipped_in_for_clip_chain( + point, + descriptor.parent_clip_chain_id, + test, + ); if !parent_clipped_in { - test.set_in_clip_chain_cache(clip_chain_index, ClippedIn::NotClippedIn); + test.set_in_clip_chain_cache(clip_chain_id, ClippedIn::NotClippedIn); return false; } - for clip_node_index in &descriptor.clips { - if !self.is_point_clipped_in_for_clip_node(point, *clip_node_index, test) { - test.set_in_clip_chain_cache(clip_chain_index, ClippedIn::NotClippedIn); + for i in 0 .. descriptor.clip_item_range.count { + let clip_node_index = ClipNodeIndex(descriptor.clip_item_range.index.0 + i); + if !self.is_point_clipped_in_for_clip_node(point, clip_node_index, test) { + test.set_in_clip_chain_cache(clip_chain_id, ClippedIn::NotClippedIn); return false; } } - test.set_in_clip_chain_cache(clip_chain_index, ClippedIn::ClippedIn); + test.set_in_clip_chain_cache(clip_chain_id, ClippedIn::ClippedIn); true } @@ -227,8 +200,10 @@ impl HitTester { return *clipped_in == ClippedIn::ClippedIn; } - let node = &self.clip_nodes[node_index.0]; - let transform = self.spatial_nodes[node.spatial_node.0].world_viewport_transform; + let node = &self.clip_nodes[node_index.0 as usize]; + let transform = self + .spatial_nodes[node.spatial_node.0 as usize] + .world_viewport_transform; let transformed_point = match transform .inverse() .and_then(|inverted| inverted.transform_point2d(&point)) @@ -240,11 +215,9 @@ impl HitTester { } }; - for region in &node.regions { - if !region.contains(&transformed_point) { - test.node_cache.insert(node_index, ClippedIn::NotClippedIn); - return false; - } + if !node.region.contains(&transformed_point) { + test.node_cache.insert(node_index, ClippedIn::NotClippedIn); + return false; } test.node_cache.insert(node_index, ClippedIn::ClippedIn); @@ -273,9 +246,9 @@ impl HitTester { continue; } - let clip_chain_index = clip_and_scroll.clip_chain_index; + let clip_chain_id = clip_and_scroll.clip_chain_id; clipped_in |= - self.is_point_clipped_in_for_clip_chain(point, clip_chain_index, &mut test); + self.is_point_clipped_in_for_clip_chain(point, clip_chain_id, &mut test); if !clipped_in { break; } @@ -317,9 +290,9 @@ impl HitTester { continue; } - let clip_chain_index = clip_and_scroll.clip_chain_index; + let clip_chain_id = clip_and_scroll.clip_chain_id; clipped_in = clipped_in || - self.is_point_clipped_in_for_clip_chain(point, clip_chain_index, &mut test); + self.is_point_clipped_in_for_clip_chain(point, clip_chain_id, &mut test); if !clipped_in { break; } @@ -394,19 +367,21 @@ impl HitTest { } } - fn get_from_clip_chain_cache(&mut self, index: ClipChainIndex) -> Option { - if index.0 >= self.clip_chain_cache.len() { + fn get_from_clip_chain_cache(&mut self, index: ClipChainId) -> Option { + let index = index.0 as usize; + if index >= self.clip_chain_cache.len() { None } else { - self.clip_chain_cache[index.0] + self.clip_chain_cache[index] } } - fn set_in_clip_chain_cache(&mut self, index: ClipChainIndex, value: ClippedIn) { - if index.0 >= self.clip_chain_cache.len() { - self.clip_chain_cache.resize(index.0 + 1, None); + fn set_in_clip_chain_cache(&mut self, index: ClipChainId, value: ClippedIn) { + let index = index.0 as usize; + if index >= self.clip_chain_cache.len() { + self.clip_chain_cache.resize(index + 1, None); } - self.clip_chain_cache[index.0] = Some(value); + self.clip_chain_cache[index] = Some(value); } fn get_absolute_point(&self, hit_tester: &HitTester) -> WorldPoint { diff --git a/webrender/src/lib.rs b/webrender/src/lib.rs index 3e567adbeb..d622192b11 100644 --- a/webrender/src/lib.rs +++ b/webrender/src/lib.rs @@ -60,7 +60,6 @@ mod box_shadow; #[cfg(any(feature = "capture", feature = "replay"))] mod capture; mod clip; -mod clip_node; mod clip_scroll_tree; mod debug_colors; #[cfg(feature = "debug_renderer")] diff --git a/webrender/src/picture.rs b/webrender/src/picture.rs index 5b6cf0f227..6711db84a7 100644 --- a/webrender/src/picture.rs +++ b/webrender/src/picture.rs @@ -11,7 +11,7 @@ use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureState, Prim use gpu_cache::{GpuCacheHandle}; use gpu_types::UvRectKind; use prim_store::{PrimitiveIndex, PrimitiveRun, PrimitiveRunLocalRect}; -use prim_store::{PrimitiveMetadata, ScrollNodeAndClipChain, Transform}; +use prim_store::{PrimitiveMetadata, Transform}; use render_task::{ClearMode, RenderTask, RenderTaskCacheEntryHandle}; use render_task::{RenderTaskCacheKey, RenderTaskCacheKeyKind, RenderTaskId, RenderTaskLocation}; use scene::{FilterOpHelpers, SceneProperties}; @@ -205,10 +205,10 @@ impl PicturePrimitive { pub fn add_primitive( &mut self, prim_index: PrimitiveIndex, - clip_and_scroll: ScrollNodeAndClipChain + spatial_node_index: SpatialNodeIndex, ) { if let Some(ref mut run) = self.runs.last_mut() { - if run.clip_and_scroll == clip_and_scroll && + if run.spatial_node_index == spatial_node_index && run.base_prim_index.0 + run.count == prim_index.0 { run.count += 1; return; @@ -218,7 +218,7 @@ impl PicturePrimitive { self.runs.push(PrimitiveRun { base_prim_index: prim_index, count: 1, - clip_and_scroll, + spatial_node_index, }); } diff --git a/webrender/src/prim_store.rs b/webrender/src/prim_store.rs index a28ed42a22..90d489b1dd 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -2,18 +2,17 @@ * 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, BorderRadius, BoxShadowClipMode, BuiltDisplayList, ClipMode, ColorF}; +use api::{AlphaType, BorderRadius, BuiltDisplayList, ClipMode, ColorF}; use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, ExtendMode}; use api::{FilterOp, GlyphInstance, GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag, TileOffset}; use api::{GlyphRasterSpace, LayoutPoint, LayoutRect, LayoutSize, LayoutToWorldTransform, LayoutVector2D}; use api::{PipelineId, PremultipliedColorF, PropertyBinding, Shadow, YuvColorSpace, YuvFormat, DeviceIntSideOffsets}; -use api::{BorderWidths, LayoutToWorldScale, NormalBorder}; +use api::{BorderWidths, BoxShadowClipMode, LayoutToWorldScale, NormalBorder}; use app_units::Au; use border::{BorderCacheKey, BorderRenderTaskInfo}; use box_shadow::BLUR_SAMPLE_SCALE; -use clip_scroll_tree::{ClipChainIndex, CoordinateSystemId, SpatialNodeIndex}; -use clip::{ClipChain, ClipChainNode, ClipChainNodeIter, ClipChainNodeRef, ClipSource}; -use clip::{ClipSourcesIndex, ClipWorkItem}; +use clip_scroll_tree::{CoordinateSystemId, SpatialNodeIndex}; +use clip::{ClipNodeFlags, ClipChainId, ClipChainInstance, ClipItem}; use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureContext, PictureState}; use frame_builder::PrimitiveRunContext; use glyph_rasterizer::{FontInstance, FontTransform, GlyphKey, FONT_SIZE_LIMIT}; @@ -30,9 +29,7 @@ use renderer::{MAX_VERTEX_TEXTURE_WIDTH}; use resource_cache::{ImageProperties, ImageRequest, ResourceCache}; use scene::SceneProperties; use segment::SegmentBuilder; -use spatial_node::SpatialNode; use std::{mem, usize}; -use std::sync::Arc; use util::{MatrixHelpers, calculate_screen_bounding_rect}; use util::{pack_as_float, recycle_vec, TransformedRectKind}; @@ -43,17 +40,17 @@ pub const VECS_PER_SEGMENT: usize = 2; #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct ScrollNodeAndClipChain { pub spatial_node_index: SpatialNodeIndex, - pub clip_chain_index: ClipChainIndex, + pub clip_chain_id: ClipChainId, } impl ScrollNodeAndClipChain { pub fn new( spatial_node_index: SpatialNodeIndex, - clip_chain_index: ClipChainIndex + clip_chain_id: ClipChainId ) -> Self { ScrollNodeAndClipChain { spatial_node_index, - clip_chain_index, + clip_chain_id, } } } @@ -73,7 +70,7 @@ pub struct Transform<'a> { pub struct PrimitiveRun { pub base_prim_index: PrimitiveIndex, pub count: usize, - pub clip_and_scroll: ScrollNodeAndClipChain, + pub spatial_node_index: SpatialNodeIndex, } impl PrimitiveRun { @@ -188,7 +185,7 @@ pub struct ScreenRect { #[derive(Debug)] pub struct PrimitiveMetadata { pub opacity: PrimitiveOpacity, - pub clip_sources_index: Option, + pub clip_chain_id: ClipChainId, pub prim_kind: PrimitiveKind, pub cpu_prim_index: SpecificPrimitiveIndex, pub gpu_location: GpuCacheHandle, @@ -1281,14 +1278,14 @@ impl PrimitiveStore { local_rect: &LayoutRect, local_clip_rect: &LayoutRect, is_backface_visible: bool, - clip_sources_index: Option, + clip_chain_id: ClipChainId, tag: Option, container: PrimitiveContainer, ) -> PrimitiveIndex { let prim_index = self.cpu_metadata.len(); let base_metadata = PrimitiveMetadata { - clip_sources_index, + clip_chain_id, gpu_location: GpuCacheHandle::new(), clip_task_id: None, local_rect: *local_rect, @@ -1731,6 +1728,7 @@ impl PrimitiveStore { let visible_rect = compute_conservative_visible_rect( prim_run_context, frame_context, + &metadata.screen_rect.unwrap().clipped, &tight_clip_rect ); @@ -2010,9 +2008,7 @@ impl PrimitiveStore { fn write_brush_segment_description( brush: &mut BrushPrimitive, metadata: &PrimitiveMetadata, - prim_run_context: &PrimitiveRunContext, - clips: &Vec, - has_clips_from_other_coordinate_systems: bool, + clip_chain: &ClipChainInstance, frame_state: &mut FrameBuildingState, ) { match brush.segment_desc { @@ -2052,25 +2048,24 @@ impl PrimitiveStore { // If this primitive is clipped by clips from a different coordinate system, then we // need to apply a clip mask for the entire primitive. - let mut clip_mask_kind = if has_clips_from_other_coordinate_systems { + let mut clip_mask_kind = if clip_chain.has_clips_from_other_coordinate_systems { BrushClipMaskKind::Global } else { BrushClipMaskKind::Individual }; // Segment the primitive on all the local-space clip sources that we can. - for clip_item in clips { - if clip_item.coordinate_system_id != prim_run_context.scroll_node.coordinate_system_id { + for i in 0 .. clip_chain.clips_range.count { + let (clip_node, flags) = frame_state.clip_store.get_node_from_range(&clip_chain.clips_range, i); + + if !flags.contains(ClipNodeFlags::SAME_COORD_SYSTEM) { continue; } - let local_clips = &frame_state.clip_store[clip_item.clip_sources_index]; - rect_clips_only = rect_clips_only && local_clips.only_rectangular_clips; - // TODO(gw): We can easily extend the segment builder to support these clip sources in // the future, but they are rarely used. // We must do this check here in case we continue early below. - if local_clips.has_image_or_line_decoration_clip { + if clip_node.item.is_image_or_line_decoration_clip() { clip_mask_kind = BrushClipMaskKind::Global; } @@ -2079,54 +2074,58 @@ impl PrimitiveStore { // of doing that, only segment with clips that have the same positioning node. // TODO(mrobinson, #2858): It may make sense to include these nodes, resegmenting only // when necessary while scrolling. - if local_clips.spatial_node_index != prim_run_context.spatial_node_index { + if !flags.contains(ClipNodeFlags::SAME_SPATIAL_NODE) { // We don't need to generate a global clip mask for rectangle clips because we are // in the same coordinate system and rectangular clips are handled by the local // clip chain rectangle. - if !local_clips.only_rectangular_clips { + if !clip_node.item.is_rect() { clip_mask_kind = BrushClipMaskKind::Global; } continue; } - for &(ref clip, _) in &local_clips.clips { - let (local_clip_rect, radius, mode) = match *clip { - ClipSource::RoundedRectangle(rect, radii, clip_mode) => { - (rect, Some(radii), clip_mode) - } - ClipSource::Rectangle(rect, mode) => { - (rect, None, mode) - } - ClipSource::BoxShadow(ref info) => { - // 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 - // be affected by the blur radius. - let inner_clip_mode = match info.clip_mode { - BoxShadowClipMode::Outset => None, - BoxShadowClipMode::Inset => Some(ClipMode::ClipOut), - }; - - // Push a region into the segment builder where the - // box-shadow can have an effect on the result. This - // ensures clip-mask tasks get allocated for these - // pixel regions, even if no other clips affect them. - segment_builder.push_mask_region( - info.prim_shadow_rect, - info.prim_shadow_rect.inflate( - -0.5 * info.shadow_rect_alloc_size.width, - -0.5 * info.shadow_rect_alloc_size.height, - ), - inner_clip_mode, - ); + let (local_clip_rect, radius, mode) = match clip_node.item { + ClipItem::RoundedRectangle(rect, radii, clip_mode) => { + rect_clips_only = false; + (rect, Some(radii), clip_mode) + } + ClipItem::Rectangle(rect, mode) => { + (rect, None, mode) + } + ClipItem::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 + // be affected by the blur radius. + let inner_clip_mode = match info.clip_mode { + BoxShadowClipMode::Outset => None, + BoxShadowClipMode::Inset => Some(ClipMode::ClipOut), + }; - continue; - } - ClipSource::LineDecoration(..) | ClipSource::Image(..) => continue, - }; + // Push a region into the segment builder where the + // box-shadow can have an effect on the result. This + // ensures clip-mask tasks get allocated for these + // pixel regions, even if no other clips affect them. + segment_builder.push_mask_region( + info.prim_shadow_rect, + info.prim_shadow_rect.inflate( + -0.5 * info.shadow_rect_alloc_size.width, + -0.5 * info.shadow_rect_alloc_size.height, + ), + inner_clip_mode, + ); - segment_builder.push_clip_rect(local_clip_rect, radius, mode); - } + continue; + } + ClipItem::LineDecoration(..) | ClipItem::Image(..) => { + rect_clips_only = false; + continue; + } + }; + + segment_builder.push_clip_rect(local_clip_rect, radius, mode); } if is_large || rect_clips_only { @@ -2166,14 +2165,13 @@ impl PrimitiveStore { &mut self, prim_run_context: &PrimitiveRunContext, prim_index: PrimitiveIndex, - clips: &Vec, + clip_chain: &ClipChainInstance, combined_outer_rect: &DeviceIntRect, - has_clips_from_other_coordinate_systems: bool, pic_state: &mut PictureState, frame_context: &FrameBuildingContext, frame_state: &mut FrameBuildingState, ) -> bool { - assert!(frame_context.screen_rect.contains_rect(combined_outer_rect)); + debug_assert!(frame_context.screen_rect.contains_rect(combined_outer_rect)); let metadata = &self.cpu_metadata[prim_index.0]; let brush = match metadata.prim_kind { @@ -2188,9 +2186,7 @@ impl PrimitiveStore { PrimitiveStore::write_brush_segment_description( brush, metadata, - prim_run_context, - clips, - has_clips_from_other_coordinate_systems, + clip_chain, frame_state, ); @@ -2221,19 +2217,20 @@ impl PrimitiveStore { } }; - let clip_task = RenderTask::new_mask( - bounds, - clips.clone(), - prim_run_context.scroll_node.coordinate_system_id, - frame_state.clip_store, - frame_state.gpu_cache, - frame_state.resource_cache, - frame_state.render_tasks, - ); + if clip_chain.clips_range.count > 0 { + let clip_task = RenderTask::new_mask( + bounds, + clip_chain.clips_range, + frame_state.clip_store, + frame_state.gpu_cache, + frame_state.resource_cache, + frame_state.render_tasks, + ); - let clip_task_id = frame_state.render_tasks.add(clip_task); - pic_state.tasks.push(clip_task_id); - segment.clip_task_id = BrushSegmentTaskId::RenderTaskId(clip_task_id); + let clip_task_id = frame_state.render_tasks.add(clip_task); + pic_state.tasks.push(clip_task_id); + segment.clip_task_id = BrushSegmentTaskId::RenderTaskId(clip_task_id); + } } true @@ -2256,6 +2253,7 @@ impl PrimitiveStore { prim_index: PrimitiveIndex, prim_run_context: &PrimitiveRunContext, prim_screen_rect: &DeviceIntRect, + clip_chain: &ClipChainInstance, pic_state: &mut PictureState, frame_context: &FrameBuildingContext, frame_state: &mut FrameBuildingState, @@ -2266,130 +2264,12 @@ impl PrimitiveStore { // Reset clips from previous frames since we may clip differently each frame. self.reset_clip_task(prim_index); - let prim_screen_rect = match prim_screen_rect.intersection(&frame_context.screen_rect) { - Some(rect) => rect, - None => { - if cfg!(debug_assertions) && Some(prim_index) == self.chase_id { - println!("\tculled by the intersection with frame rect {:?}", - frame_context.screen_rect); - } - self.cpu_metadata[prim_index.0].screen_rect = None; - return false; - } - }; - - let mut combined_outer_rect = - prim_screen_rect.intersection(&prim_run_context.clip_chain.combined_outer_screen_rect); - let clip_chain = prim_run_context.clip_chain.nodes.clone(); - if cfg!(debug_assertions) && Some(prim_index) == self.chase_id { - println!("\tbase screen {:?}, combined clip chain {:?}", - prim_screen_rect, prim_run_context.clip_chain.combined_outer_screen_rect); - } - - let prim_coordinate_system_id = prim_run_context.scroll_node.coordinate_system_id; - let transform = &prim_run_context.scroll_node.world_content_transform; - let extra_clip = { - let metadata = &self.cpu_metadata[prim_index.0]; - metadata.clip_sources_index.map(|clip_sources_index| { - let prim_clips = &mut frame_state.clip_store[clip_sources_index]; - prim_clips.update( - frame_state.gpu_cache, - frame_state.resource_cache, - frame_context.device_pixel_scale, - ); - let (screen_inner_rect, screen_outer_rect) = prim_clips.get_screen_bounds( - transform, - frame_context.device_pixel_scale, - Some(&prim_screen_rect), - ); - - if let Some(outer) = screen_outer_rect { - combined_outer_rect = combined_outer_rect.and_then(|r| r.intersection(&outer)); - } - if cfg!(debug_assertions) && Some(prim_index) == self.chase_id { - println!("\tfound extra clip with screen bounds {:?}", screen_outer_rect); - } - - Arc::new(ClipChainNode { - work_item: ClipWorkItem { - clip_sources_index, - coordinate_system_id: prim_coordinate_system_id, - }, - // The local_clip_rect a property of ClipChain nodes that are ClipNodes. - // It's used to calculate a local clipping rectangle before we reach this - // point, so we can set it to zero here. It should be unused from this point - // on. - local_clip_rect: LayoutRect::zero(), - screen_inner_rect, - screen_outer_rect: screen_outer_rect.unwrap_or(prim_screen_rect), - prev: None, - }) - }) - }; - - // If everything is clipped out, then we don't need to render this primitive. - let combined_outer_rect = match combined_outer_rect { - Some(rect) if !rect.is_empty() => rect, - _ => { - if cfg!(debug_assertions) && Some(prim_index) == self.chase_id { - println!("\tculled by the empty combined screen rect"); - } - self.cpu_metadata[prim_index.0].screen_rect = None; - return false; - } - }; - - let mut has_clips_from_other_coordinate_systems = false; - let mut combined_inner_rect = frame_context.screen_rect; - let clips = convert_clip_chain_to_clip_vector( - clip_chain, - extra_clip, - &combined_outer_rect, - &mut combined_inner_rect, - prim_run_context.scroll_node.coordinate_system_id, - &mut has_clips_from_other_coordinate_systems - ); - - // This can happen if we had no clips or if all the clips were optimized away. In - // some cases we still need to create a clip mask in order to create a rectangular - // clip in screen space coordinates. - if clips.is_empty() { - // If we don't have any clips from other coordinate systems, the local clip - // calculated from the clip chain should be sufficient to ensure proper clipping. - if !has_clips_from_other_coordinate_systems { - if cfg!(debug_assertions) && Some(prim_index) == self.chase_id { - println!("\tneed no task: all clips are within the coordinate system"); - } - return true; - } - - // If we have filtered all clips and the screen rect isn't any smaller, we can just - // skip masking entirely. - if combined_outer_rect == prim_screen_rect { - if cfg!(debug_assertions) && Some(prim_index) == self.chase_id { - println!("\tneed no task: combined rect is not smaller"); - } - return true; - } - // Otherwise we create an empty mask, but with an empty inner rect to avoid further - // optimization of the empty mask. - combined_inner_rect = DeviceIntRect::zero(); - } - - if combined_inner_rect.contains_rect(&prim_screen_rect) { - if cfg!(debug_assertions) && Some(prim_index) == self.chase_id { - println!("\tneed no task: contained within the clip inner rect"); - } - return true; - } - // First try to render this primitive's mask using optimized brush rendering. if self.update_clip_task_for_brush( prim_run_context, prim_index, - &clips, - &combined_outer_rect, - has_clips_from_other_coordinate_systems, + &clip_chain, + prim_screen_rect, pic_state, frame_context, frame_state, @@ -2400,23 +2280,24 @@ impl PrimitiveStore { return true; } - let clip_task = RenderTask::new_mask( - combined_outer_rect, - clips, - prim_coordinate_system_id, - frame_state.clip_store, - frame_state.gpu_cache, - frame_state.resource_cache, - frame_state.render_tasks, - ); + if clip_chain.clips_range.count > 0 { + let clip_task = RenderTask::new_mask( + *prim_screen_rect, + clip_chain.clips_range, + frame_state.clip_store, + frame_state.gpu_cache, + frame_state.resource_cache, + frame_state.render_tasks, + ); - let clip_task_id = frame_state.render_tasks.add(clip_task); - if cfg!(debug_assertions) && Some(prim_index) == self.chase_id { - println!("\tcreated task {:?} with combined outer rect {:?}", - clip_task_id, combined_outer_rect); + let clip_task_id = frame_state.render_tasks.add(clip_task); + if cfg!(debug_assertions) && Some(prim_index) == self.chase_id { + println!("\tcreated task {:?} with combined outer rect {:?}", + clip_task_id, prim_screen_rect); + } + self.cpu_metadata[prim_index.0].clip_task_id = Some(clip_task_id); + pic_state.tasks.push(clip_task_id); } - self.cpu_metadata[prim_index.0].clip_task_id = Some(clip_task_id); - pic_state.tasks.push(clip_task_id); true } @@ -2435,7 +2316,7 @@ impl PrimitiveStore { // Do some basic checks first, that can early out // without even knowing the local rect. - let (prim_kind, cpu_prim_index) = { + let (prim_kind, cpu_prim_index, clip_chain_id) = { let metadata = &self.cpu_metadata[prim_index.0]; if !metadata.is_backface_visible && prim_run_context.transform.backface_is_visible { @@ -2445,7 +2326,7 @@ impl PrimitiveStore { return None; } - (metadata.prim_kind, metadata.cpu_prim_index) + (metadata.prim_kind, metadata.cpu_prim_index, metadata.clip_chain_id) }; // If we have dependencies, we need to prepare them first, in order @@ -2529,7 +2410,7 @@ impl PrimitiveStore { } } - let (local_rect, unclipped_device_rect) = { + let (local_rect, local_clip_rect) = { let metadata = &mut self.cpu_metadata[prim_index.0]; if metadata.local_rect.size.width <= 0.0 || metadata.local_rect.size.height <= 0.0 { @@ -2559,36 +2440,94 @@ impl PrimitiveStore { } }; - let unclipped = match calculate_screen_bounding_rect( - &prim_run_context.scroll_node.world_content_transform, - &local_rect, + (local_rect, metadata.local_clip_rect) + }; + + let clip_chain = frame_state + .clip_store + .build_clip_chain_instance( + clip_chain_id, + local_rect, + local_clip_rect, + prim_run_context.spatial_node_index, + &frame_context.clip_scroll_tree.spatial_nodes, + frame_state.gpu_cache, + frame_state.resource_cache, frame_context.device_pixel_scale, - None, //TODO: inflate `frame_context.screen_rect` appropriately - ) { - Some(rect) => rect, - None => { - if cfg!(debug_assertions) && Some(prim_index) == self.chase_id { - println!("\tculled for being behind the near plane of transform: {:?}", - prim_run_context.scroll_node.world_content_transform); - } - return None + ); + + let clip_chain = match clip_chain { + Some(clip_chain) => clip_chain, + None => { + self.cpu_metadata[prim_index.0].screen_rect = None; + return None; + } + }; + + if self.chase_id == Some(prim_index) { + println!("\teffective clip chain from {:?} {}", + clip_chain.clips_range, + if pic_context.apply_local_clip_rect { "(applied)" } else { "" }, + ); + } + + pic_state.has_non_root_coord_system |= clip_chain.has_non_root_coord_system; + + let unclipped_device_rect = match calculate_screen_bounding_rect( + &prim_run_context.scroll_node.world_content_transform, + &local_rect, + frame_context.device_pixel_scale, + None, //TODO: inflate `frame_context.screen_rect` appropriately + ) { + Some(rect) => rect, + None => { + if cfg!(debug_assertions) && Some(prim_index) == self.chase_id { + println!("\tculled for being behind the near plane of transform: {:?}", + prim_run_context.scroll_node.world_content_transform); } - }; + return None + } + }; - let clipped = unclipped - .intersection(&prim_run_context.clip_chain.combined_outer_screen_rect)?; + let clipped_device_rect = match calculate_screen_bounding_rect( + &prim_run_context.scroll_node.world_content_transform, + &clip_chain.local_bounding_rect, + frame_context.device_pixel_scale, + None, + ) { + Some(rect) => rect, + None => { + if cfg!(debug_assertions) && Some(prim_index) == self.chase_id { + println!("\tculled for being behind the near plane of transform: {:?}", + prim_run_context.scroll_node.world_content_transform); + } + return None + } + }; + + let clipped_device_rect = match clipped_device_rect.intersection(&frame_context.screen_rect) { + Some(clipped_device_rect) => clipped_device_rect, + None => return None, + }; + + let ccr = { + let metadata = &mut self.cpu_metadata[prim_index.0]; metadata.screen_rect = Some(ScreenRect { - clipped, - unclipped, + clipped: clipped_device_rect, + unclipped: unclipped_device_rect, }); - metadata.combined_local_clip_rect = prim_run_context - .local_clip_rect - .intersection(&metadata.local_clip_rect) - .unwrap_or(LayoutRect::zero()); + metadata.combined_local_clip_rect = if pic_context.apply_local_clip_rect { + clip_chain.local_clip_rect + } else { + local_clip_rect + }; - (local_rect, unclipped) + match metadata.combined_local_clip_rect.intersection(&local_rect) { + Some(ccr) => ccr, + None => return None, + } }; self.build_prim_segments_if_needed( @@ -2601,7 +2540,8 @@ impl PrimitiveStore { if may_need_clip_mask && !self.update_clip_task( prim_index, prim_run_context, - &unclipped_device_rect, + &clipped_device_rect, + &clip_chain, pic_state, frame_context, frame_state, @@ -2623,7 +2563,7 @@ impl PrimitiveStore { frame_state, ); - Some(local_rect) + Some(ccr) } // TODO(gw): Make this simpler / more efficient by tidying @@ -2652,14 +2592,12 @@ impl PrimitiveStore { // lookups ever show up in a profile). let scroll_node = &frame_context .clip_scroll_tree - .spatial_nodes[run.clip_and_scroll.spatial_node_index.0]; - let clip_chain = &frame_context - .clip_chains[run.clip_and_scroll.clip_chain_index.0]; + .spatial_nodes[run.spatial_node_index.0]; if run.is_chasing(self.chase_id) { println!("\tpreparing a run of length {} in pipeline {:?}", run.count, pic_context.pipeline_id); - println!("\trun {:?}", run.clip_and_scroll); + println!("\trun {:?}", run.spatial_node_index); println!("\ttransform {:?}", scroll_node.world_content_transform.to_transform()); } @@ -2667,7 +2605,6 @@ impl PrimitiveStore { // systems, due to either the scroll node or the clip-chain. pic_state.has_non_root_coord_system |= scroll_node.coordinate_system_id != CoordinateSystemId::root(); - pic_state.has_non_root_coord_system |= clip_chain.has_non_root_coord_system; if !scroll_node.invertible { if run.is_chasing(self.chase_id) { @@ -2676,13 +2613,6 @@ impl PrimitiveStore { continue; } - if clip_chain.combined_outer_screen_rect.is_empty() { - if run.is_chasing(self.chase_id) { - println!("\tculled for out of screen bounds"); - } - continue; - } - let parent_relative_transform = pic_context .inv_world_transform .map(|inv_parent| { @@ -2702,46 +2632,13 @@ impl PrimitiveStore { inv_parent.pre_mul(&scroll_node.world_content_transform) }); - if run.is_chasing(self.chase_id) { - println!("\teffective clip chain from {:?} {}", - scroll_node.coordinate_system_id, - if pic_context.apply_local_clip_rect { "(applied)" } else { "" }, - ); - let iter = ClipChainNodeIter { current: clip_chain.nodes.clone() }; - for node in iter { - println!("\t\t{:?} {:?}", - node.work_item.coordinate_system_id, - node.local_clip_rect, - ); - } - } - - let clip_chain_rect = if pic_context.apply_local_clip_rect { - get_local_clip_rect_for_nodes(scroll_node, clip_chain) - } else { - None - }; - - let local_clip_chain_rect = match clip_chain_rect { - Some(rect) if rect.is_empty() => { - if run.is_chasing(self.chase_id) { - println!("\tculled by empty chain rect"); - } - continue - }, - Some(rect) => rect, - None => frame_context.max_local_clip, - }; - let transform = frame_context .transforms - .get_transform(run.clip_and_scroll.spatial_node_index); + .get_transform(run.spatial_node_index); let child_prim_run_context = PrimitiveRunContext::new( - clip_chain, scroll_node, - run.clip_and_scroll.spatial_node_index, - local_clip_chain_rect, + run.spatial_node_index, transform, ); @@ -2758,21 +2655,8 @@ impl PrimitiveStore { ) { frame_state.profile_counters.visible_primitives.inc(); - let clipped_rect = match clip_chain_rect { - Some(ref chain_rect) => match prim_local_rect.intersection(chain_rect) { - Some(rect) => rect, - None => { - if cfg!(debug_assertions) && self.chase_id == Some(prim_index) { - println!("\tculled by chain rect {:?}", chain_rect); - } - continue - }, - }, - None => prim_local_rect, - }; - if let Some(ref matrix) = parent_relative_transform { - match matrix.transform_rect(&clipped_rect) { + match matrix.transform_rect(&prim_local_rect) { Some(bounds) => { result.local_rect_in_actual_parent_space = result.local_rect_in_actual_parent_space.union(&bounds); @@ -2784,7 +2668,7 @@ impl PrimitiveStore { } if let Some(ref matrix) = original_relative_transform { - match matrix.transform_rect(&clipped_rect) { + match matrix.transform_rect(&prim_local_rect) { Some(bounds) => { result.local_rect_in_original_parent_space = result.local_rect_in_original_parent_space.union(&bounds); @@ -2840,9 +2724,15 @@ fn decompose_repeated_primitive( .combined_local_clip_rect .intersection(&metadata.local_rect).unwrap(); + let unclipped_device_rect = &metadata + .screen_rect + .unwrap() + .unclipped; + let visible_rect = compute_conservative_visible_rect( prim_run_context, frame_context, + unclipped_device_rect, &tight_clip_rect ); let stride = *stretch_size + *tile_spacing; @@ -2883,10 +2773,10 @@ fn decompose_repeated_primitive( fn compute_conservative_visible_rect( prim_run_context: &PrimitiveRunContext, frame_context: &FrameBuildingContext, + clipped_device_rect: &DeviceIntRect, local_clip_rect: &LayoutRect, ) -> LayoutRect { - let world_screen_rect = prim_run_context - .clip_chain.combined_outer_screen_rect + let world_screen_rect = clipped_device_rect .to_f32() / frame_context.device_pixel_scale; if let Some(layer_screen_rect) = prim_run_context @@ -2913,64 +2803,6 @@ fn edge_flags_for_tile_spacing(tile_spacing: &LayoutSize) -> EdgeAaSegmentMask { flags } -fn convert_clip_chain_to_clip_vector( - clip_chain_nodes: ClipChainNodeRef, - extra_clip: ClipChainNodeRef, - combined_outer_rect: &DeviceIntRect, - combined_inner_rect: &mut DeviceIntRect, - prim_coordinate_system: CoordinateSystemId, - has_clips_from_other_coordinate_systems: &mut bool, -) -> Vec { - // Filter out all the clip instances that don't contribute to the result. - ClipChainNodeIter { current: extra_clip } - .chain(ClipChainNodeIter { current: clip_chain_nodes }) - .filter_map(|node| { - *has_clips_from_other_coordinate_systems |= - prim_coordinate_system != node.work_item.coordinate_system_id; - - *combined_inner_rect = if !node.screen_inner_rect.is_empty() { - // If this clip's inner area contains the area of the primitive clipped - // by previous clips, then it's not going to affect rendering in any way. - if node.screen_inner_rect.contains_rect(combined_outer_rect) { - return None; - } - combined_inner_rect.intersection(&node.screen_inner_rect) - .unwrap_or_else(DeviceIntRect::zero) - } else { - DeviceIntRect::zero() - }; - - Some(node.work_item.clone()) - }) - .collect() -} - -fn get_local_clip_rect_for_nodes( - scroll_node: &SpatialNode, - clip_chain: &ClipChain, -) -> Option { - ClipChainNodeIter { current: clip_chain.nodes.clone() } - .fold( - None, - |combined_local_clip_rect: Option, node| { - if node.work_item.coordinate_system_id != scroll_node.coordinate_system_id { - return combined_local_clip_rect; - } - - Some(match combined_local_clip_rect { - Some(combined_rect) => - combined_rect - .intersection(&node.local_clip_rect) - .unwrap_or_else(LayoutRect::zero), - None => node.local_clip_rect, - }) - } - ) - .map(|local_rect| { - local_rect.translate(&-scroll_node.coordinate_system_relative_offset) - }) -} - impl<'a> GpuDataRequest<'a> { // Write the GPU cache data for an individual segment. fn write_segment( diff --git a/webrender/src/render_task.rs b/webrender/src/render_task.rs index b6913c7b36..622fda50e5 100644 --- a/webrender/src/render_task.rs +++ b/webrender/src/render_task.rs @@ -7,8 +7,7 @@ use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceSize, DeviceIntSid use api::FontRenderMode; use border::BorderCacheKey; use box_shadow::{BoxShadowCacheKey}; -use clip::{ClipSource, ClipStore, ClipWorkItem}; -use clip_scroll_tree::CoordinateSystemId; +use clip::{ClipItem, ClipStore, ClipNodeRange}; use device::TextureFilter; #[cfg(feature = "pathfinder")] use euclid::{TypedPoint2D, TypedVector2D}; @@ -179,8 +178,7 @@ pub enum RenderTaskLocation { #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct CacheMaskTask { actual_rect: DeviceIntRect, - pub clips: Vec, - pub coordinate_system_id: CoordinateSystemId, + pub clip_node_range: ClipNodeRange, } #[derive(Debug)] @@ -384,8 +382,7 @@ impl RenderTask { pub fn new_mask( outer_rect: DeviceIntRect, - clips: Vec, - prim_coordinate_system_id: CoordinateSystemId, + clip_node_range: ClipNodeRange, clip_store: &mut ClipStore, gpu_cache: &mut GpuCache, resource_cache: &mut ResourceCache, @@ -402,59 +399,57 @@ impl RenderTask { // TODO(gw): If this ever shows up in a profile, we could pre-calculate // whether a ClipSources contains any box-shadows and skip // this iteration for the majority of cases. - for clip_item in &clips { - let clip_sources = &mut clip_store[clip_item.clip_sources_index]; - for &mut (ref mut clip, _) in &mut clip_sources.clips { - match *clip { - ClipSource::BoxShadow(ref mut info) => { - let (cache_size, cache_key) = info.cache_key - .as_ref() - .expect("bug: no cache key set") - .clone(); - let blur_radius_dp = cache_key.blur_radius_dp as f32; - let clip_data_address = gpu_cache.get_address(&info.clip_data_handle); - - // Request a cacheable render task with a blurred, minimal - // sized box-shadow rect. - info.cache_handle = Some(resource_cache.request_render_task( - RenderTaskCacheKey { - size: cache_size, - kind: RenderTaskCacheKeyKind::BoxShadow(cache_key), - }, - gpu_cache, - render_tasks, - None, - false, - |render_tasks| { - // Draw the rounded rect. - let mask_task = RenderTask::new_rounded_rect_mask( - cache_size, - clip_data_address, - ); - - let mask_task_id = render_tasks.add(mask_task); - - // Blur it - let blur_render_task = RenderTask::new_blur( - blur_radius_dp, - mask_task_id, - render_tasks, - RenderTargetKind::Alpha, - ClearMode::Zero, - ); - - let root_task_id = render_tasks.add(blur_render_task); - children.push(root_task_id); - - root_task_id - } - )); - } - ClipSource::Rectangle(..) | - ClipSource::RoundedRectangle(..) | - ClipSource::Image(..) | - ClipSource::LineDecoration(..) => {} + for i in 0 .. clip_node_range.count { + let (clip_node, _) = clip_store.get_node_from_range_mut(&clip_node_range, i); + match clip_node.item { + ClipItem::BoxShadow(ref mut info) => { + let (cache_size, cache_key) = info.cache_key + .as_ref() + .expect("bug: no cache key set") + .clone(); + let blur_radius_dp = cache_key.blur_radius_dp as f32; + let clip_data_address = gpu_cache.get_address(&info.clip_data_handle); + + // Request a cacheable render task with a blurred, minimal + // sized box-shadow rect. + info.cache_handle = Some(resource_cache.request_render_task( + RenderTaskCacheKey { + size: cache_size, + kind: RenderTaskCacheKeyKind::BoxShadow(cache_key), + }, + gpu_cache, + render_tasks, + None, + false, + |render_tasks| { + // Draw the rounded rect. + let mask_task = RenderTask::new_rounded_rect_mask( + cache_size, + clip_data_address, + ); + + let mask_task_id = render_tasks.add(mask_task); + + // Blur it + let blur_render_task = RenderTask::new_blur( + blur_radius_dp, + mask_task_id, + render_tasks, + RenderTargetKind::Alpha, + ClearMode::Zero, + ); + + let root_task_id = render_tasks.add(blur_render_task); + children.push(root_task_id); + + root_task_id + } + )); } + ClipItem::Rectangle(..) | + ClipItem::RoundedRectangle(..) | + ClipItem::Image(..) | + ClipItem::LineDecoration(..) => {} } } @@ -463,8 +458,7 @@ impl RenderTask { location: RenderTaskLocation::Dynamic(None, Some(outer_rect.size)), kind: RenderTaskKind::CacheMask(CacheMaskTask { actual_rect: outer_rect, - clips, - coordinate_system_id: prim_coordinate_system_id, + clip_node_range, }), clear_mode: ClearMode::One, saved_index: None, @@ -900,7 +894,7 @@ impl RenderTask { pt.new_level(format!("Picture of {:?}", task.prim_index)); } RenderTaskKind::CacheMask(ref task) => { - pt.new_level(format!("CacheMask with {} clips", task.clips.len())); + pt.new_level(format!("CacheMask with {} clips", task.clip_node_range.count)); pt.add_item(format!("rect: {:?}", task.actual_rect)); } RenderTaskKind::ClipRegion(..) => { diff --git a/webrender/src/tiling.rs b/webrender/src/tiling.rs index 3960e06e69..cc6d05ff2d 100644 --- a/webrender/src/tiling.rs +++ b/webrender/src/tiling.rs @@ -595,8 +595,7 @@ impl RenderTarget for AlphaRenderTarget { let task_address = render_tasks.get_task_address(task_id); self.clip_batcher.add( task_address, - &task_info.clips, - task_info.coordinate_system_id, + task_info.clip_node_range, ctx.resource_cache, gpu_cache, clip_store, diff --git a/webrender/src/util.rs b/webrender/src/util.rs index 6f4787987e..e00255afcf 100644 --- a/webrender/src/util.rs +++ b/webrender/src/util.rs @@ -451,14 +451,6 @@ impl FastTransform { } } - #[inline(always)] - pub fn has_perspective_component(&self) -> bool { - match *self { - FastTransform::Offset(..) => false, - FastTransform::Transform { ref transform, .. } => transform.has_perspective_component(), - } - } - #[inline(always)] pub fn is_backface_visible(&self) -> bool { match *self { diff --git a/wrench/reftests/clip/clip-45-degree-rotation-ref.png b/wrench/reftests/clip/clip-45-degree-rotation-ref.png index 4cfd3ed8e1..d554807aff 100644 Binary files a/wrench/reftests/clip/clip-45-degree-rotation-ref.png and b/wrench/reftests/clip/clip-45-degree-rotation-ref.png differ