From ebfee8fdb22f5d0f095687eaa3e7435d5da7714d Mon Sep 17 00:00:00 2001 From: Glenn Watson Date: Wed, 8 Aug 2018 13:03:26 +1000 Subject: [PATCH 1/2] There's a large number of related changes in this patch. The overall goal is to make clip chain handling not depend on screen rectangle generation. This will make it much easier to implement local space picture rasterization for perspective elements. In addition to that, we unify how user / hierarchical and primitive clip chain nodes are handled. Clip chain instances are built on demand now, as required by primitives. Right now, none of the clip chain information is cached between primitives. In general, now that the calculations are done in local space, they are much faster - however we can easily start to cache the common clip-chain state (per raster coordinate system) if it ever shows up in a profile. Additionally, since we only build clip chains on demand, and clip chains for non- visible primitives are not built, which is a decent saving on many pages. Remove ClipNode altogether. Instead, during flattening we map directly from a ClipId to a ClipChainId as appropriate. Clip chains are now created during flattening and the links remain constant. It's only during construction of a clip chain instance that we include and exclude parts of the specified clip chain. This unifies how we handle user defined clip chains and legacy hierarchical clip chains. At the same time, unify how the optional primitive specific complex clip is handled with clip chains. Now, if a primitive has a complex clip, it's added as a normal clip chain node, and linked to the parent clip chain that this primitive would otherwise be attached to. Primitive runs now only depend on the spatial node. This results in far fewer primitive run breaks. ClipWorkItems are now stored as a single contiguous array in the ClipStore. To achieve this they are stored as an index buffer, and a clip node range is simply an (index, count). These are a lot cheaper to pass to clip masks, and also require fewer heap allocations. Port hit testing to use the same clip chain data structures that the main clip code uses, now that the hierarchical and user defined clip chains are handled in a uniform way. Remove the need for Arc in clip chain code, since clip chains are now stored in a contiguous index buffer style structure. --- webrender/src/batch.rs | 119 +- webrender/src/clip.rs | 1043 +++++++++++------ webrender/src/clip_node.rs | 78 -- webrender/src/clip_scroll_tree.rs | 131 +-- webrender/src/display_list_flattener.rs | 247 ++-- webrender/src/frame_builder.rs | 15 +- webrender/src/hit_test.rs | 146 +-- webrender/src/lib.rs | 1 - webrender/src/picture.rs | 8 +- webrender/src/prim_store.rs | 560 ++++----- webrender/src/render_task.rs | 116 +- webrender/src/tiling.rs | 3 +- webrender/src/util.rs | 8 - .../clip/clip-45-degree-rotation-ref.png | Bin 4032 -> 13254 bytes 14 files changed, 1208 insertions(+), 1267 deletions(-) delete mode 100644 webrender/src/clip_node.rs diff --git a/webrender/src/batch.rs b/webrender/src/batch.rs index 4aded290da..135e14a556 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, ClipSource, 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.source { + ClipSource::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(..) => { + } + 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 + .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 + }); + } + ClipSource::Rectangle(_, mode) => { + if !flags.contains(ClipNodeFlags::SAME_COORD_SYSTEM) || + mode == ClipMode::ClipOut { self.rectangles.push(ClipMaskInstance { clip_data_address: gpu_address, ..instance }); } } + ClipSource::RoundedRectangle(..) => { + self.rectangles.push(ClipMaskInstance { + clip_data_address: gpu_address, + ..instance + }); + } } } } diff --git a/webrender/src/clip.rs b/webrender/src/clip.rs index 6882673908..269af7684a 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. + + ClipSource - 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 ClipSource with attached positioning information (a spatial node index). + Stored as a contiguous array of nodes within the ClipStore. + + +-----------------------+-----------------------+-----------------------+ + | ClipSource | ClipSource | ClipSource | + | Spatial Node Index | Spatial Node Index | Spatial Node Index | + | GPU cache handle | GPU cache handle | GPU cache handle | + +-----------------------+-----------------------+-----------------------+ + 0 1 2 + + +----------------+ | | + | ClipSourcesId |____| | + | index: 1 | | + | count: 2 |___________________________________________________| + +----------------+ + + ClipSourcesId - A clip sources id identifies a range of clip nodes. It is stored + as an (index, count). + + ClipChain - A clip chain node contains a range of ClipNodes (a ClipSourcesId) + 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 | + +----------------+ | +----------------+ | +----------------+ | +----------------+ + | ClipSourcesId | | | ClipSourcesId | | | ClipSourcesId | | | ClipSourcesId | + | 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 ClipKind { + // 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 sources id represents one or more ClipSource structs. +// They are stored in a contiguous array inside the ClipStore, +// and identified by an (offset, count). +#[derive(Debug, Copy, Clone)] +pub struct ClipSourcesId { + 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 source: ClipSource, + 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_sources_id: ClipSourcesId, + 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 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 ClipSourcesIndex(usize); +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_to_clip_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.source { + ClipSource::Image(ref mask) => { + let data = ImageMaskData { local_rect: mask.rect }; + data.write_gpu_blocks(request); + } + 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); + } + ClipSource::Rectangle(rect, mode) => { + let data = ClipData::uniform(rect, 0.0, mode); + data.write(&mut request); + } + ClipSource::RoundedRectangle(ref rect, ref radius, mode) => { + let data = ClipData::rounded_rect(rect, radius, mode); + data.write(&mut request); + } + 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, + ]); + } + } + } + + match self.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); + } + } + _ => {} + } + } +} + +// 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_sources( + &mut self, + clip_sources: Vec, + spatial_node_index: SpatialNodeIndex, + ) -> ClipSourcesId { + debug_assert!(!clip_sources.is_empty()); + + let id = ClipSourcesId { + index: ClipNodeIndex(self.clip_nodes.len() as u32), + count: clip_sources.len() as u32, + }; + + let nodes: Vec = clip_sources + .into_iter() + .map(|source| { + ClipNode { + source, + spatial_node_index, + gpu_cache_handle: GpuCacheHandle::new(), + } + }) + .collect(); + + self.clip_nodes.extend(nodes); + id } -} -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_sources_id: ClipSourcesId, + parent_clip_chain_id: ClipChainId, + ) -> ClipChainId { + let id = ClipChainId(self.clip_chain_nodes.len() as u32); + self.clip_chain_nodes.push(ClipChainNode { + clip_sources_id, + 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_sources_id.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_sources_id.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 wher + // there is perspective occurring. 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.source.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 spatial_node_index == clip_node.spatial_node_index || + 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_to_clip_space(¤t_local_clip_rect) { + + // See how this clip affects the prim region. + let clip_kind = node.source.get_clip_kind(&prim_rect); + + match clip_kind { + ClipKind::Accept => { + // Doesn't affect the primitive at all, so skip adding to list + } + ClipKind::Reject => { + // Completely clips the supplied prim rect + return None; + } + ClipKind::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, + }) } } @@ -310,283 +872,120 @@ impl ClipSource { _ => 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 { + ClipSource::Rectangle(clip_rect, ClipMode::Clip) => Some(clip_rect), + ClipSource::Rectangle(_, ClipMode::ClipOut) => None, + ClipSource::RoundedRectangle(clip_rect, _, ClipMode::Clip) => Some(clip_rect), + ClipSource::RoundedRectangle(_, _, ClipMode::ClipOut) => None, + ClipSource::Image(ref mask) if mask.repeat => None, + ClipSource::Image(ref mask) => Some(mask.rect), + ClipSource::BoxShadow(..) => None, + ClipSource::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_kind( + &self, + prim_rect: &LayoutRect, + ) -> ClipKind { + match *self { + ClipSource::Rectangle(ref clip_rect, ClipMode::Clip) => { + if clip_rect.contains_rect(prim_rect) { + return ClipKind::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(..) => { + ClipKind::Partial + } + None => { + ClipKind::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 + ClipSource::Rectangle(ref clip_rect, ClipMode::ClipOut) => { + if clip_rect.contains_rect(prim_rect) { + return ClipKind::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(_) => { + ClipKind::Partial + } + None => { + ClipKind::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 - } + ClipSource::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 ClipKind::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(..) => { + ClipKind::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 => { + ClipKind::Reject } - ClipSource::Rectangle(rect, mode) => { - let data = ClipData::uniform(rect, 0.0, mode); - data.write(&mut request); + } + } + ClipSource::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 ClipKind::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(_) => { + ClipKind::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 => { + ClipKind::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); + ClipSource::Image(ref mask) => { + if mask.repeat { + ClipKind::Partial + } else { + match mask.rect.intersection(prim_rect) { + Some(..) => { + ClipKind::Partial + } + None => { + ClipKind::Reject + } } } - _ => {} + } + ClipSource::BoxShadow(..) | + ClipSource::LineDecoration(..) => { + ClipKind::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..7dca9bf13d 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, ClipSource, 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_sources_id = self + .clip_store + .get_clip_chain(item_clip_chain_id) + .clip_sources_id; + // 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_sources_id, 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, spatial_node_index: SpatialNodeIndex, - ) -> Option { + default_clip_chain_id: ClipChainId, + ) -> ClipChainId { if clip_sources.is_empty() { - None + default_clip_chain_id } else { - Some(self.clip_store.insert(ClipSources::new(clip_sources, spatial_node_index))) + // Add a range of clip sources. + let clip_sources_id = self + .clip_store + .add_clip_sources(clip_sources, spatial_node_index); + + // Add clip chain node that references the clip source range. + self.clip_store.add_clip_chain( + clip_sources_id, + default_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 @@ -827,37 +854,45 @@ impl<'a> DisplayListFlattener<'a> { .iter() .map(|cs| cs.offset(&shadow.offset)) .collect(); - let clip_sources = self.add_clip_sources( + let clip_chain_id = self.build_clip_chain( clip_sources, 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( + let clip_chain_id = self.build_clip_chain( clip_sources, 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 sources, + // 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(ClipSource::Rectangle(clip_region.main, ClipMode::Clip)); + let clip_image = clip_region.image_mask.map(ClipSource::Image); + let clips_complex = clip_region.complex_clips + .into_iter() + .map(|complex| ClipSource::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_sources_id = self + .clip_store + .add_clip_sources(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_sources_id, 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)); } @@ -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 { @@ -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..d079d0b014 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, ClipSource, 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.source { + 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(_) => 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,50 @@ 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(); + for clip_chain in &clip_store.clip_chain_nodes { + self.clip_chains.push(clip_chain.clone()); } } 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_sources_id.count { + let clip_node_index = ClipNodeIndex(descriptor.clip_sources_id.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 +201,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 +216,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 +247,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 +291,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 +368,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..1e75c9442d 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, ClipSource}; 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.source.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.source.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.source { + ClipSource::RoundedRectangle(rect, radii, clip_mode) => { + rect_clips_only = false; + (rect, Some(radii), clip_mode) + } + ClipSource::Rectangle(rect, mode) => { + (rect, None, mode) + } + ClipSource::BoxShadow(ref info) => { + rect_clips_only = false; + + // For inset box shadows, we can clip out any + // pixels that are inside the shadow region + // and are beyond the inner rect, as they can't + // 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; + } + ClipSource::LineDecoration(..) | ClipSource::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..42142faf97 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::{ClipSource, 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.source { + 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(..) => {} } } @@ -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 4cfd3ed8e10dd2261bc08ef6cec54d89b7229c7f..d554807aff205e2d38096996aa26a2c43f79a19f 100644 GIT binary patch literal 13254 zcmeHO`%_c*w*O|kg`l8GI0{?_*_RdMP(6XvFvt2%(@w3)5dl$X`H+O_2NHPkz4n&oqSM4=zeyuuONX z`_83*#C%AbdRqvBbmLNok-^y^Vo7L?@8ed_J?po(A z4KqyA;3ml|_mfrn;&fh;QD!u1_AgWzSK&?;1sq!s-`M(YUNH?>(msl|B$A2Z&SJDu+OnjFDLyLor&*|>FuHSxXG52_I2;1ah^g+cC@*8UOVf@QOKoj<&oNtUnxa^x4Zk(oh z2!DmSCY~@w6ds8W*FY@%M+QaE!pN>Yf2r8-wB- z4?hDcp5^y_@!Ak5-63SFUx7hEJ*pMLxDT99{ekUW0b*d5Yv8Wd>h8|*0Cv8QY%ew4 zdt#z^%hOR&XnRjHG{GJ$%iDVfK)$O%rE_|pyageOmBPhC&ZkKbqBuZYo$1>BG4!FC z<)=ouf6bz8XwO8QTibiw;BFSfomT0qp$8*T>G<9!I_g~fk>zuv+_hkwZ$`GZrh9z@ zznyPUI=c!}Jms$+E3>4TtgXE&PgC*947XpYKO09IyDkHDD%yJzXiU;A-MOv_)f8gA zpUhHVvUaFwSNF60c8$9#k0Ra_EZnYe&K*q?Jduq$H?;SZgR@wf#cZ_pf$>h3uZ?xz z@ER+HviF>Kq9~HBaxp2#^$^~l@hl&%c5esj`+|jwN1b;7w(UjKIkmkf5h9fZh~*1i z1pu3ilUX8E`qmig+m?ko3);0n-D;M<8|{7@h_$7m&bjT{dw{bu67`MgH9vtDvm;pe zBc#_{4e~lH`}dl60k#g6pwyRHC}mu%O!rp1wgtR>sX^Kt*Zt)XJfF2^n5^9yl)}*c zs4=qFd>NdrQwSj%_uGG>l-hSPj?|{Ys#u!I>TI;;UFY?ckoYfhail^&=O_(c5+qB= zb={u?!Twn`_)x?*OOO0pc*Fz71Y;AtG4tk8)q#4bPw8=L&t> zG(dNUwI4899bpiD7vrdCD%1fpj+rtiPNqAJqId2^<+9$8$IzUvTr{u7IkH(n;j#9m zCTqzQ>Mj3cRIcg`xdF6K4i@qv-B&MCF#K}H@z18h3;QT@bqmqF_njkKX&Hp?<)Xh* zzc!F!tyjsMs9jqL-M1nN_2jrJrco-y5v+ZJ$;!>5!9NWacBtJ~!)UM*Iq0szIkJ?# zqrxPaGoxK=TSOCkkF|#wt=w!dRtm?X-Kl#ivO{t)Poe({M4rUj{Y}`K-iq0p~=6V#uRIVZGh>j7tt zTr5`T4O&p2!rF_CR&f<@=*KuhnhNb%Aa7+HDJH9!4Dx!$v9Vq2kkiCMzyD2^?;2hU zCDFW^hz?lA$)G;tx*y*gatji#T>mwi;%0V1NtR3r5&l(Zmjl>$PBHb)kzv5%nJ;qC zSiK>hHn({ABe~eFEs3SUFD#x%f{mfU>?0l!=dXbCh7H58v jBsUHPVq;E4jT5q` z5@RD62mS{!)&T1#HxMK?Fs{NS!^Xj2{LSwq?II{ymPJb225A@HBxyfS+gM0`aU4$b zL^?(CczOXzOHG~Ky0(ls`v#CKzsX|bd;@Kk#@F5>$RiMwuaH|w;7E&k|1@`V4Lp5ki`0E2~YfTpE$G8BZlyanb62-sqxO*QN-8<#$}U;@dS!wYf>1A z98Q(ThmXL@^Uaj#x0_+N{DlEW$*NPq7f@5JT zwn>1%UDzg}k~+ZKw=Cfh2-qg!7zo7XC+lF>@p%I+DFbKo^!?hw$7RE%b8J`!^0U8$ z86tmp&-b%)AY@}f0YV<~<9ASDbs8340Rh|avq8W%dwhrlx;N;2XdN7|?#3(Hh5hsiF3I^8w|mgt z_n|yc%xWr>^h58YFpjRlIAy31q0~#~UA}y9gorOt!g-@r(&6@>WgPgPXV0e5Nq02j zx91ipm}d_o3i(HAtiH0@2w(GrnI`H1H=i9>t$u88h~%>K(ON_-6`om zr5p;{0+oPYU~w}g#lFfcL2!i82)gU1q^y%NJy|2NDw8rip;-wL$yL8J_0xIg|Rl&{`t-bmdZ?;nUU=pW^?5#gc1M?duj(h1zLSZNxV~VdXW6h`Lm& zux45Y@i6~mnt07J(z)AqHIGZKi6ZRtIgq&yyt4kn9Hd~6=4GNj(R!a(N zKp{F?BB2pz&urKtDKvmWdbUJDl2_p$9t@X30Uy~?Orm$Gz!M%v2*#XDF>}LY&gaye z;{}?Nr2@J9aL&+6_{7+QcR_f15V?k_xw>qcP*F5KezN->(IrHzW9@T|Vm-VJmKs!! z7o~o{vK^mo)A^r)e@5n9pyqz3#`AGxRvDT|l~1IjJs-#e`=E!%%bdg2oDCwABeqH8 zA}Fv=Rl<%)=2{XMW1jIg)B(o$7L}#uD#3U~BtGo3`+5O3K7tQ3iX6bsH=%NcGW1Lw z?TgQp!suw`+BQfd1K%{#`Gn)NE$SAddHA^21(Y2Q5aX1FHXSr}qRg4D=EAZlX9^)N zj=AQh&V+jbVu-@9Hic@75%>;e6dj2)G5cnGhf3#lwKSL`S>}9A%_YHGp*xC_kTUdF zz}lFH!ts^~uRhw7gTnFt-b0x{^J3BHOm<%neJAjuA^79mA^^_Q0CBI%Ko?YmXGKYz z(sCHItMLUej%iQ>xH*i2P3Kq8^%y1Pq1t!lwh`d0RW1fA4HF^u8hjJZWItI*iMX{N zB}FMM_)aJ7COqE5{+iBvjdur#!NGHqI}FBs0V1Acy+T)L zbS4UE=d+zTP_b=zj$ss!0cRIJLX8TgMFX7S!%m2XDP24k2zx&I0S_;qS31W-8b?@uR2*|077sFCoS1GerHcpJ94yQ^C@-HvOMqmS zr6#c%>UB#l9?NC#nkjJcAhN}z+joO;Ic9Xa+5wwAx)Os*Cn}wDXlz6Vr^n>3Q0iQG zA{PS}uvg%qC{|-Rjdf3h=S0q6_do%~WIX)UFnZWu>}wbkPx@Yt zuN;y{rrZzyTkUnW22aPA4H@rJ2{k`-EAgF9(&md9Zt2mPUT0yrXgeD+@NGwWES$vC zdz;FOv?>~XIBlmFS;7%KyZ_Yd%pS;ab50H!H;~c)kns&{ylvZ%@lllXFUpGrhr=;IC|MUmj9lPrjhZ8j|rBHsx)Xz2NY69(twsO>cBd z1qv*_kYPUv6`jKJy5_M4xWk|Zd|#3XMrxM#{3FO^o)I~UOhumWg^bDVO3%vZdl!7~g701My$i^9t?)E~?~Ekz;M*5``+{#@ z@a+q}eZe6Q2_tm}r@WR)lVS*mZ(qA};@50>N zq7XeLM!^gns`IJH^HU0xBj-I2g#el72N{Tq@;UoAa>gd!HVbSr6a?bll&bfDfpWpX>?<6Pp zd+pg-X<;FQLJ$NA%Sbo<4}wSm@%Jo9go(Y zyF>k3(^k=uwdmXbZB-Nxyp&jYRg=2pYDdoegwDAizt^dlIddASQqg|s?SI5`=6Rp| zgs9$J>)Np4<&%f!&ySq+)mM^7;i{8+EL}B?)X&OE?_XV7#=Uz!9$hL|F| zoL!?pZNXx{tF^G7=Rw!Jc|kpQyZ({8RgB*k>gyaUIj#*9jIZNc0!sXlT(y6DFRu5y z5=UEsrNfiiT0R7>GmKF2vtxmlc0xeY+TR5>Gg_hI^WoCkVF69F?`0}?E}g~F7;3eA z=W@3-+tNizw2FWx)VD5+J2#odl9}fU^=Lx1Md7$BwRPHYqs+4obzg8>3oKpZB@1Ty zT?C)A4H=~i?3v&@VCHJKir$?wZg7q(QA!sOsg8fYb(_#8?ddBwB4?&v>6bjkBe)%~KeExLPY$9h5snscop+ z`k|#L3N=anuEl(UO0pm^;0yQF)gYs`i!1Ae3ubOW3OhE1c|xdD2~|IZgO%EDyRAlx zVWi|cL!e-c?^iR|ox)yEVXkWE9yLYk$-m_4e1$41+_6?h#M$vLWH@RX=66lwU8&rX zWLBgZ#>_QOV;LiJGL37_V8tpys6eKdi^2q70nqImNM^=nawn&-5eBkBPtVa%A1I=Z zhN~}zH>Sw&HjF5;c{W+BH>9?`?xIn=aTw~B`&$U#o+QbFk%2sm;4MdvTgBlt(>;x2 zQrYc_s8qQ+O5W(USUaS)Qnw-2rwNhxBLW%ej4hR8%IO9$=p>nr68ha>gbnt^Ge=zP&#x>K$gkm?ndtGsbm?>>gjQIGtCFB zkY!VTMTOA*DY~Ovlxj(#7P+sXk}>?Tjvi+-BYe7T}Ydb`U1nVvFySAlCK5i zPlfZ9yK9I~@|oQEPj}Y@K8V!P&uku*CHT6LdqgzO;)5bJ^tCvZ$|t! z+OuyP=+VI_E5~P>D%_8fkbz*UV$4F0Ka<8F)Nc{aY*=_b zy3bI!pF5|9OG4xTxjxd!Z2=CDZ+1j+T02;|C&9?oLz!3QQO^CadzCi0IS(|=|FK!JG#s!t-2(0) zMEv#$=Rt^^)b?r42Z;LgP{~1v)TUXGxo{&Fiq>!=pz#MuqvgnMA&K%f8rb@e4 z!luUClJQE=bnZ|Q_c_#k@>hT7T!^gg!MvA%@%AtCgS!i$hq-YrrE=8UCnp+D&zURb+UA6Ln`%b>ph6p?*!F)$QP5?=I9)((S{lD{FA z_~P1zH^P%YC%y5U$KOA%xmG74-s`UgCB#qgIW`=bcJl+X2B^OnN^TaB>?VA}U~$gp(XhDk*A7?&=6Z`w4PoZM!yh)h_nm+L?*55u8su?_o;=g^{`LExH@x)x z`R;BChg`qAlqmx-;O%qIh95Bk@m$8tfl~I9_ajkhyRMDTc{8waGKZYpSICr&3B*b< z?(Zx8%mEFx8IfeSV&+u|aCG>{$4uG3V0r$uI9Co?Vb;*dvf?2Qe=!>(L354U1m(Z{ zIT%5I_7k7;_#0Rr_bk-BYAEc!_s0bCj89ae(n^<2P&PrZ{buUsrm;id+7A*(lV|q9 za(qpPFLuP)CPK}PGhycGdf4rb(32@`uuSB9FDO603zr$*!RO4_2A3IS3N?2`L$IQM z5}DG{tCM<9P)_Rw=XJPp5_`e*7YN69Q=#1@%c=$CtzNr>6E-(>4}vZp)yI)3FJO1} z*$Q9m7B_UUtT4~f)@Wf3%VG~DsDdMh-U$Cv#6 z2i)P!|dmj$CW^fWeOj@|oQehDw)Pn!*C7j2z-C zP6LO7I=4n>)l+7SIGhFoQR5IPc2z^Jw|TNq=}Vb;lD~vQl0V@u)rgVNbmcao$3Ph{ z;+}?FX!B&C(x(c?DsWOsHm#F+a{VRWBEPVfA~JA*=H^H7im$~PTAFAS+#0ICjBxA8 z<2LhRR2sm{r2i7ojpP+oVxpFgcL;7h6(%DpbmVcnc_Au&D>H)wbt95IiBAX)JT!66 ziM~3kxF(wRej{8+0mrer*~~o9Q9&4SSx1(HHzrH5qYB45yN5=lT{6#2aH@mIY$^K6 zui%hhzT&IC7Nr_mXb|pcsErtL(?FiGnd3nQmYG3Fs6>7l!&g;_Qa!CE>+I(FsQWjI z0pyA-F85pEo&h8d^-L|fjA+b&+10Vu<}pca7c50_{)B-NtE?U9=i?S6G4r88laG7qV^UaMT@Yv2GQkwX`XQWMN)XYr~^%D;h0Q zTe`(M8$>FJ7WMg}0vVayfDDE)fz$;uW0XVQ*assf%J5Q~M=P~OS*$;~i!`WdD(?cB zZGhyvK0e>jKwc#>y-HZQMI1hbYX<3WDjN|4avuqzU%5hkkpSsWCAAqX)qc#X_E$701mewHrj#~YP?V-iSG0$~@kcOqw^Nt2G6VW5v=xZI9U!?Htp z;|#ws0ZptHh&s``1KFw}O$I8_K(CkKAj*vb;3L2B z3v@<}5U&dqV3LDIrgo6Z*V)#HCuED$%Z;bF82cSK* zLZw|K%8*+zAR+}7B|5cx1rklzASa_hwSwmisYv9-6C zOTwcF)%IFFNVN0pc(DlwDRqqB_~CRw2-RX`Rx40rqp9ze5IF&mt^EM9-fvvG7X=8o z27dB4Ku7~Me9tV-5)AK(9peG}+Kx{x?yvT3TnMk0uxXXwwS8%ybmmTEpdfD+SYV|? zJl5I*ESyYbb8mM5nK=eZSh@npJR-nVwgo)7tfPe8rGROk%HG-i0uIdVoh@ Date: Fri, 10 Aug 2018 08:08:37 +1000 Subject: [PATCH 2/2] Address review comments. --- webrender/src/batch.rs | 14 +- webrender/src/box_shadow.rs | 16 +- webrender/src/clip.rs | 196 ++++++++++++------------ webrender/src/display_list_flattener.rs | 50 +++--- webrender/src/hit_test.rs | 23 ++- webrender/src/prim_store.rs | 16 +- webrender/src/render_task.rs | 14 +- 7 files changed, 164 insertions(+), 165 deletions(-) diff --git a/webrender/src/batch.rs b/webrender/src/batch.rs index 135e14a556..4e06e2c9ed 100644 --- a/webrender/src/batch.rs +++ b/webrender/src/batch.rs @@ -5,7 +5,7 @@ use api::{AlphaType, ClipMode, DeviceIntRect, DeviceIntSize, DeviceIntPoint}; use api::{DeviceUintRect, DeviceUintPoint, ExternalImageType, FilterOp, ImageRendering}; use api::{YuvColorSpace, YuvFormat, WorldPixel}; -use clip::{ClipNodeFlags, ClipNodeRange, ClipSource, ClipStore}; +use clip::{ClipNodeFlags, ClipNodeRange, ClipItem, ClipStore}; use euclid::vec3; use glyph_rasterizer::GlyphFormat; use gpu_cache::{GpuCache, GpuCacheHandle, GpuCacheAddress}; @@ -1780,8 +1780,8 @@ impl ClipBatcher { let gpu_address = gpu_cache.get_address(&clip_node.gpu_cache_handle); - match clip_node.source { - ClipSource::Image(ref mask) => { + match clip_node.item { + ClipItem::Image(ref mask) => { if let Ok(cache_item) = resource_cache.get_cached_image( ImageRequest { key: mask.image, @@ -1803,13 +1803,13 @@ impl ClipBatcher { continue; } } - ClipSource::LineDecoration(..) => { + ClipItem::LineDecoration(..) => { self.line_decorations.push(ClipMaskInstance { clip_data_address: gpu_address, ..instance }); } - ClipSource::BoxShadow(ref info) => { + ClipItem::BoxShadow(ref info) => { let rt_handle = info .cache_handle .as_ref() @@ -1829,7 +1829,7 @@ impl ClipBatcher { ..instance }); } - ClipSource::Rectangle(_, mode) => { + ClipItem::Rectangle(_, mode) => { if !flags.contains(ClipNodeFlags::SAME_COORD_SYSTEM) || mode == ClipMode::ClipOut { self.rectangles.push(ClipMaskInstance { @@ -1838,7 +1838,7 @@ impl ClipBatcher { }); } } - ClipSource::RoundedRectangle(..) => { + 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 269af7684a..224325d8ce 100644 --- a/webrender/src/clip.rs +++ b/webrender/src/clip.rs @@ -26,29 +26,29 @@ use util::{extract_inner_rect_safe, pack_as_float, recycle_vec, MatrixHelpers}; ClipStore - Main interface used by other modules. - ClipSource - A single clip item (e.g. a rounded rect, or a box shadow). - These are an exposed API type, stored inline in a ClipNode. + 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 ClipSource with attached positioning information (a spatial node index). + ClipNode - A ClipItem with attached positioning information (a spatial node index). Stored as a contiguous array of nodes within the ClipStore. +-----------------------+-----------------------+-----------------------+ - | ClipSource | ClipSource | ClipSource | + | ClipItem | ClipItem | ClipItem | | Spatial Node Index | Spatial Node Index | Spatial Node Index | | GPU cache handle | GPU cache handle | GPU cache handle | +-----------------------+-----------------------+-----------------------+ 0 1 2 +----------------+ | | - | ClipSourcesId |____| | + | ClipItemRange |____| | | index: 1 | | | count: 2 |___________________________________________________| +----------------+ - ClipSourcesId - A clip sources id identifies a range of clip nodes. It is stored + 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 ClipSourcesId) + 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. @@ -56,7 +56,7 @@ use util::{extract_inner_rect_safe, pack_as_float, recycle_vec, MatrixHelpers}; +----------------+ ____+----------------+ ____+----------------+ ____+----------------+ | ClipChain | | | ClipChain | | | ClipChain | | | ClipChain | +----------------+ | +----------------+ | +----------------+ | +----------------+ - | ClipSourcesId | | | ClipSourcesId | | | ClipSourcesId | | | ClipSourcesId | + | ClipItemRange | | | ClipItemRange | | | ClipItemRange | | | ClipItemRange | | Parent Id |___| | Parent Id |___| | Parent Id |___| | Parent Id | +----------------+ +----------------+ +----------------+ +----------------+ @@ -89,7 +89,7 @@ use util::{extract_inner_rect_safe, pack_as_float, recycle_vec, MatrixHelpers}; // Result of comparing a clip node instance against a local rect. #[derive(Debug)] -enum ClipKind { +enum ClipResult { // The clip does not affect the region at all. Accept, // The clip prevents the region from being drawn. @@ -99,11 +99,11 @@ enum ClipKind { Partial, } -// A clip sources id represents one or more ClipSource structs. +// 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 ClipSourcesId { +pub struct ClipItemRange { pub index: ClipNodeIndex, pub count: u32, } @@ -114,7 +114,7 @@ pub struct ClipSourcesId { // can be found. pub struct ClipNode { pub spatial_node_index: SpatialNodeIndex, - pub source: ClipSource, + pub item: ClipItem, pub gpu_cache_handle: GpuCacheHandle, } @@ -144,7 +144,7 @@ impl ClipChainId { // and a link to a parent clip chain node, or ClipChainId::NONE. #[derive(Clone)] pub struct ClipChainNode { - pub clip_sources_id: ClipSourcesId, + pub clip_item_range: ClipItemRange, pub parent_clip_chain_id: ClipChainId, } @@ -222,7 +222,7 @@ impl ClipSpaceConversion { } } - fn transform_to_clip_space(&self, rect: &LayoutRect) -> Option { + fn transform_from_prim_space(&self, rect: &LayoutRect) -> Option { match *self { ClipSpaceConversion::Local => { Some(*rect) @@ -253,12 +253,12 @@ impl ClipNode { device_pixel_scale: DevicePixelScale, ) { if let Some(mut request) = gpu_cache.request(&mut self.gpu_cache_handle) { - match self.source { - ClipSource::Image(ref mask) => { + match self.item { + ClipItem::Image(ref mask) => { let data = ImageMaskData { local_rect: mask.rect }; data.write_gpu_blocks(request); } - ClipSource::BoxShadow(ref info) => { + ClipItem::BoxShadow(ref info) => { request.push([ info.shadow_rect_alloc_size.width, info.shadow_rect_alloc_size.height, @@ -273,15 +273,15 @@ impl ClipNode { ]); request.push(info.prim_shadow_rect); } - ClipSource::Rectangle(rect, mode) => { + ClipItem::Rectangle(rect, mode) => { let data = ClipData::uniform(rect, 0.0, mode); data.write(&mut request); } - ClipSource::RoundedRectangle(ref rect, ref radius, mode) => { + ClipItem::RoundedRectangle(ref rect, ref radius, mode) => { let data = ClipData::rounded_rect(rect, radius, mode); data.write(&mut request); } - ClipSource::LineDecoration(ref info) => { + ClipItem::LineDecoration(ref info) => { request.push(info.rect); request.push([ info.wavy_line_thickness, @@ -293,8 +293,8 @@ impl ClipNode { } } - match self.source { - ClipSource::Image(ref mask) => { + match self.item { + ClipItem::Image(ref mask) => { resource_cache.request_image( ImageRequest { key: mask.image, @@ -304,7 +304,7 @@ impl ClipNode { gpu_cache, ); } - ClipSource::BoxShadow(ref mut info) => { + 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." @@ -335,7 +335,9 @@ impl ClipNode { data.write(&mut request); } } - _ => {} + ClipItem::Rectangle(..) | + ClipItem::RoundedRectangle(..) | + ClipItem::LineDecoration(..) => {} } } } @@ -377,31 +379,30 @@ impl ClipStore { } } - pub fn add_clip_sources( + pub fn add_clip_items( &mut self, - clip_sources: Vec, + clip_items: Vec, spatial_node_index: SpatialNodeIndex, - ) -> ClipSourcesId { - debug_assert!(!clip_sources.is_empty()); + ) -> ClipItemRange { + debug_assert!(!clip_items.is_empty()); - let id = ClipSourcesId { + let range = ClipItemRange { index: ClipNodeIndex(self.clip_nodes.len() as u32), - count: clip_sources.len() as u32, + count: clip_items.len() as u32, }; - let nodes: Vec = clip_sources + let nodes = clip_items .into_iter() - .map(|source| { + .map(|item| { ClipNode { - source, + item, spatial_node_index, gpu_cache_handle: GpuCacheHandle::new(), } - }) - .collect(); + }); self.clip_nodes.extend(nodes); - id + range } pub fn get_clip_chain(&self, clip_chain_id: ClipChainId) -> &ClipChainNode { @@ -410,12 +411,12 @@ impl ClipStore { pub fn add_clip_chain( &mut self, - clip_sources_id: ClipSourcesId, + 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_sources_id, + clip_item_range, parent_clip_chain_id, }); id @@ -470,11 +471,11 @@ impl ClipStore { // 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_sources_id.count; + 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_sources_id.index.0 + i); + 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]; @@ -487,8 +488,8 @@ impl ClipStore { ref_spatial_node.coordinate_system_relative_offset; Some(ClipSpaceConversion::Offset(offset)) } else { - // TODO(gw): We still have issues with clip nodes and primitives wher - // there is perspective occurring. We intend to fix these + // 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 @@ -512,7 +513,7 @@ impl ClipStore { // 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.source.get_local_clip_rect() { + 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) { @@ -520,8 +521,7 @@ impl ClipStore { None => return None, }; - if spatial_node_index == clip_node.spatial_node_index || - ref_spatial_node.coordinate_system_id == clip_spatial_node.coordinate_system_id { + 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 => { @@ -558,20 +558,20 @@ impl ClipStore { // Convert the prim rect into the clip nodes local space if let Some(prim_rect) = node_info .conversion - .transform_to_clip_space(¤t_local_clip_rect) { + .transform_from_prim_space(¤t_local_clip_rect) { // See how this clip affects the prim region. - let clip_kind = node.source.get_clip_kind(&prim_rect); + let clip_result = node.item.get_clip_result(&prim_rect); - match clip_kind { - ClipKind::Accept => { + match clip_result { + ClipResult::Accept => { // Doesn't affect the primitive at all, so skip adding to list } - ClipKind::Reject => { + ClipResult::Reject => { // Completely clips the supplied prim rect return None; } - ClipKind::Partial => { + ClipResult::Partial => { // Needs a mask -> add to clip node indices // TODO(gw): Ensure this only runs once on each node per frame? @@ -706,7 +706,7 @@ impl ClipRegion> { } #[derive(Debug)] -pub enum ClipSource { +pub enum ClipItem { Rectangle(LayoutRect, ClipMode), RoundedRectangle(LayoutRect, BorderRadius, ClipMode), Image(ImageMask), @@ -714,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, @@ -737,8 +737,8 @@ impl ClipSource { style: LineStyle, orientation: LineOrientation, wavy_line_thickness: f32, - ) -> ClipSource { - ClipSource::LineDecoration( + ) -> Self { + ClipItem::LineDecoration( LineDecorationClipSource { rect, style, @@ -754,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( @@ -828,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, @@ -845,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 }) @@ -861,14 +861,14 @@ 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, } } @@ -879,52 +879,52 @@ impl ClipSource { // any clip mask that eventually gets drawn. fn get_local_clip_rect(&self) -> Option { match *self { - ClipSource::Rectangle(clip_rect, ClipMode::Clip) => Some(clip_rect), - ClipSource::Rectangle(_, ClipMode::ClipOut) => None, - ClipSource::RoundedRectangle(clip_rect, _, ClipMode::Clip) => Some(clip_rect), - ClipSource::RoundedRectangle(_, _, ClipMode::ClipOut) => None, - ClipSource::Image(ref mask) if mask.repeat => None, - ClipSource::Image(ref mask) => Some(mask.rect), - ClipSource::BoxShadow(..) => None, - ClipSource::LineDecoration(..) => None, + 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, } } // Check how a given clip source affects a local primitive region. - fn get_clip_kind( + fn get_clip_result( &self, prim_rect: &LayoutRect, - ) -> ClipKind { + ) -> ClipResult { match *self { - ClipSource::Rectangle(ref clip_rect, ClipMode::Clip) => { + ClipItem::Rectangle(ref clip_rect, ClipMode::Clip) => { if clip_rect.contains_rect(prim_rect) { - return ClipKind::Accept; + return ClipResult::Accept; } match clip_rect.intersection(prim_rect) { Some(..) => { - ClipKind::Partial + ClipResult::Partial } None => { - ClipKind::Reject + ClipResult::Reject } } } - ClipSource::Rectangle(ref clip_rect, ClipMode::ClipOut) => { + ClipItem::Rectangle(ref clip_rect, ClipMode::ClipOut) => { if clip_rect.contains_rect(prim_rect) { - return ClipKind::Reject; + return ClipResult::Reject; } match clip_rect.intersection(prim_rect) { Some(_) => { - ClipKind::Partial + ClipResult::Partial } None => { - ClipKind::Accept + ClipResult::Accept } } } - ClipSource::RoundedRectangle(ref clip_rect, ref radius, ClipMode::Clip) => { + 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 @@ -932,20 +932,20 @@ impl ClipSource { 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 ClipKind::Accept; + return ClipResult::Accept; } } match clip_rect.intersection(prim_rect) { Some(..) => { - ClipKind::Partial + ClipResult::Partial } None => { - ClipKind::Reject + ClipResult::Reject } } } - ClipSource::RoundedRectangle(ref clip_rect, ref radius, ClipMode::ClipOut) => { + 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 @@ -953,36 +953,36 @@ impl ClipSource { 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 ClipKind::Reject; + return ClipResult::Reject; } } match clip_rect.intersection(prim_rect) { Some(_) => { - ClipKind::Partial + ClipResult::Partial } None => { - ClipKind::Accept + ClipResult::Accept } } } - ClipSource::Image(ref mask) => { + ClipItem::Image(ref mask) => { if mask.repeat { - ClipKind::Partial + ClipResult::Partial } else { match mask.rect.intersection(prim_rect) { Some(..) => { - ClipKind::Partial + ClipResult::Partial } None => { - ClipKind::Reject + ClipResult::Reject } } } } - ClipSource::BoxShadow(..) | - ClipSource::LineDecoration(..) => { - ClipKind::Partial + ClipItem::BoxShadow(..) | + ClipItem::LineDecoration(..) => { + ClipResult::Partial } } } diff --git a/webrender/src/display_list_flattener.rs b/webrender/src/display_list_flattener.rs index 7dca9bf13d..7907c2c356 100644 --- a/webrender/src/display_list_flattener.rs +++ b/webrender/src/display_list_flattener.rs @@ -13,7 +13,7 @@ use api::{LineOrientation, LineStyle, LocalClip, NinePatchBorderSource, Pipeline use api::{PropertyBinding, ReferenceFrame, RepeatMode, ScrollFrameDisplayItem, ScrollSensitivity}; use api::{Shadow, SpecificDisplayItem, StackingContext, StickyFrameDisplayItem, TexelRect}; use api::{ClipMode, TransformStyle, YuvColorSpace, YuvData}; -use clip::{ClipChainId, ClipRegion, ClipSource, ClipStore}; +use clip::{ClipChainId, ClipRegion, ClipItem, ClipStore}; use clip_scroll_tree::{ClipScrollTree, SpatialNodeIndex}; use euclid::vec2; use frame_builder::{ChasePrimitive, FrameBuilder, FrameBuilderConfig}; @@ -692,15 +692,15 @@ impl<'a> DisplayListFlattener<'a> { .id_to_index_mapper .get_clip_chain_id(&item); // Get the id of the clip sources entry for that clip chain node. - let clip_sources_id = self + let clip_item_range = self .clip_store .get_clip_chain(item_clip_chain_id) - .clip_sources_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_sources_id, parent_clip_chain_id); + .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; @@ -754,22 +754,22 @@ impl<'a> DisplayListFlattener<'a> { // just return the parent clip chain id directly. fn build_clip_chain( &mut self, - clip_sources: Vec, + clip_items: Vec, spatial_node_index: SpatialNodeIndex, - default_clip_chain_id: ClipChainId, + parent_clip_chain_id: ClipChainId, ) -> ClipChainId { - if clip_sources.is_empty() { - default_clip_chain_id + if clip_items.is_empty() { + parent_clip_chain_id } else { // Add a range of clip sources. - let clip_sources_id = self + let clip_item_range = self .clip_store - .add_clip_sources(clip_sources, spatial_node_index); + .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_sources_id, - default_clip_chain_id, + clip_item_range, + parent_clip_chain_id, ) } } @@ -836,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() { @@ -850,12 +850,12 @@ 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_chain_id = self.build_clip_chain( - clip_sources, + clip_items, clip_and_scroll.spatial_node_index, clip_and_scroll.clip_chain_id, ); @@ -879,7 +879,7 @@ impl<'a> DisplayListFlattener<'a> { if container.is_visible() { let clip_chain_id = self.build_clip_chain( - clip_sources, + clip_items, clip_and_scroll.spatial_node_index, clip_and_scroll.clip_chain_id, ); @@ -1281,7 +1281,7 @@ impl<'a> DisplayListFlattener<'a> { where I: IntoIterator { - // Add a new ClipNode - this is a ClipId that identifies a list of clip sources, + // 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. @@ -1294,11 +1294,11 @@ impl<'a> DisplayListFlattener<'a> { // 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(ClipSource::Rectangle(clip_region.main, ClipMode::Clip)); - let clip_image = clip_region.image_mask.map(ClipSource::Image); + 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| ClipSource::new_rounded_rect( + .map(|complex| ClipItem::new_rounded_rect( complex.rect, complex.radii, complex.mode, @@ -1306,9 +1306,9 @@ impl<'a> DisplayListFlattener<'a> { let clips = clip_rect.chain(clip_image).chain(clips_complex).collect(); // Add those clip sources to the clip store. - let clip_sources_id = self + let clip_item_range = self .clip_store - .add_clip_sources(clips, spatial_node); + .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 @@ -1317,7 +1317,7 @@ impl<'a> DisplayListFlattener<'a> { // Add the new clip chain entry let clip_chain_id = self .clip_store - .add_clip_chain(clip_sources_id, parent_clip_chain_index); + .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); @@ -1418,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 @@ -1514,7 +1514,7 @@ impl<'a> DisplayListFlattener<'a> { LineStyle::Dotted | LineStyle::Dashed => { vec![ - ClipSource::new_line_decoration( + ClipItem::new_line_decoration( info.rect, style, orientation, diff --git a/webrender/src/hit_test.rs b/webrender/src/hit_test.rs index d079d0b014..ebc37338a8 100644 --- a/webrender/src/hit_test.rs +++ b/webrender/src/hit_test.rs @@ -4,7 +4,7 @@ use api::{BorderRadius, ClipMode, HitTestFlags, HitTestItem, HitTestResult, ItemTag, LayoutPoint}; use api::{LayoutPrimitiveInfo, LayoutRect, PipelineId, WorldPoint}; -use clip::{ClipNodeIndex, ClipChainNode, ClipNode, ClipSource, ClipStore}; +use clip::{ClipNodeIndex, ClipChainNode, ClipNode, ClipItem, ClipStore}; use clip::{ClipChainId, rounded_rectangle_contains_point}; use clip_scroll_tree::{SpatialNodeIndex, ClipScrollTree}; use internal_types::FastHashMap; @@ -36,13 +36,13 @@ pub struct HitTestClipNode { impl HitTestClipNode { fn new(node: &ClipNode) -> Self { - let region = match node.source { - ClipSource::Rectangle(ref rect, mode) => HitTestRegion::Rectangle(*rect, mode), - ClipSource::RoundedRectangle(ref rect, ref radii, ref mode) => + 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), - ClipSource::Image(ref mask) => HitTestRegion::Rectangle(mask.rect, ClipMode::Clip), - ClipSource::LineDecoration(_) | - ClipSource::BoxShadow(_) => HitTestRegion::Invalid, + ClipItem::Image(ref mask) => HitTestRegion::Rectangle(mask.rect, ClipMode::Clip), + ClipItem::LineDecoration(_) | + ClipItem::BoxShadow(_) => HitTestRegion::Invalid, }; HitTestClipNode { @@ -148,9 +148,8 @@ impl HitTester { self.clip_nodes.push(HitTestClipNode::new(node)); } - for clip_chain in &clip_store.clip_chain_nodes { - self.clip_chains.push(clip_chain.clone()); - } + self.clip_chains + .extend_from_slice(&clip_store.clip_chain_nodes); } fn is_point_clipped_in_for_clip_chain( @@ -179,8 +178,8 @@ impl HitTester { return false; } - for i in 0 .. descriptor.clip_sources_id.count { - let clip_node_index = ClipNodeIndex(descriptor.clip_sources_id.index.0 + i); + 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; diff --git a/webrender/src/prim_store.rs b/webrender/src/prim_store.rs index 1e75c9442d..90d489b1dd 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -12,7 +12,7 @@ use app_units::Au; use border::{BorderCacheKey, BorderRenderTaskInfo}; use box_shadow::BLUR_SAMPLE_SCALE; use clip_scroll_tree::{CoordinateSystemId, SpatialNodeIndex}; -use clip::{ClipNodeFlags, ClipChainId, ClipChainInstance, ClipSource}; +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}; @@ -2065,7 +2065,7 @@ impl PrimitiveStore { // 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 clip_node.source.is_image_or_line_decoration_clip() { + if clip_node.item.is_image_or_line_decoration_clip() { clip_mask_kind = BrushClipMaskKind::Global; } @@ -2078,21 +2078,21 @@ impl PrimitiveStore { // 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 !clip_node.source.is_rect() { + if !clip_node.item.is_rect() { clip_mask_kind = BrushClipMaskKind::Global; } continue; } - let (local_clip_rect, radius, mode) = match clip_node.source { - ClipSource::RoundedRectangle(rect, radii, 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) } - ClipSource::Rectangle(rect, mode) => { + ClipItem::Rectangle(rect, mode) => { (rect, None, mode) } - ClipSource::BoxShadow(ref info) => { + ClipItem::BoxShadow(ref info) => { rect_clips_only = false; // For inset box shadows, we can clip out any @@ -2119,7 +2119,7 @@ impl PrimitiveStore { continue; } - ClipSource::LineDecoration(..) | ClipSource::Image(..) => { + ClipItem::LineDecoration(..) | ClipItem::Image(..) => { rect_clips_only = false; continue; } diff --git a/webrender/src/render_task.rs b/webrender/src/render_task.rs index 42142faf97..622fda50e5 100644 --- a/webrender/src/render_task.rs +++ b/webrender/src/render_task.rs @@ -7,7 +7,7 @@ use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceSize, DeviceIntSid use api::FontRenderMode; use border::BorderCacheKey; use box_shadow::{BoxShadowCacheKey}; -use clip::{ClipSource, ClipStore, ClipNodeRange}; +use clip::{ClipItem, ClipStore, ClipNodeRange}; use device::TextureFilter; #[cfg(feature = "pathfinder")] use euclid::{TypedPoint2D, TypedVector2D}; @@ -401,8 +401,8 @@ impl RenderTask { // this iteration for the majority of cases. 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.source { - ClipSource::BoxShadow(ref mut info) => { + 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") @@ -446,10 +446,10 @@ impl RenderTask { } )); } - ClipSource::Rectangle(..) | - ClipSource::RoundedRectangle(..) | - ClipSource::Image(..) | - ClipSource::LineDecoration(..) => {} + ClipItem::Rectangle(..) | + ClipItem::RoundedRectangle(..) | + ClipItem::Image(..) | + ClipItem::LineDecoration(..) => {} } }