diff --git a/webrender/src/clip.rs b/webrender/src/clip.rs index 58e64fcd33..26142de207 100644 --- a/webrender/src/clip.rs +++ b/webrender/src/clip.rs @@ -3,12 +3,20 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{BorderRadius, ComplexClipRegion, ImageMask, ImageRendering}; -use api::{LayerPoint, LayerRect, LayerToWorldTransform, LocalClip}; +use api::{DeviceIntRect, LayerPoint, LayerRect, LayerSize, LayerToWorldTransform, LocalClip}; use border::BorderCornerClipSource; -use gpu_cache::GpuCache; -use mask_cache::MaskCacheInfo; +use freelist::{FreeList, FreeListHandle, WeakFreeListHandle}; +use gpu_cache::{GpuCache, GpuCacheHandle, ToGpuBlocks}; +use prim_store::{ClipData, ImageMaskData}; use resource_cache::ResourceCache; use std::ops::Not; +use util::{extract_inner_rect_safe, TransformedRect}; + +const MAX_CLIP: f32 = 1000000.0; + +pub type ClipStore = FreeList; +pub type ClipSourcesHandle = FreeListHandle; +pub type ClipSourcesWeakHandle = WeakFreeListHandle; #[derive(Clone, Debug)] pub struct ClipRegion { @@ -101,21 +109,26 @@ impl From for ClipSources { #[derive(Debug)] pub struct ClipSources { - clips: Vec, - mask_cache_info: MaskCacheInfo, + pub clips: Vec<(ClipSource, GpuCacheHandle)>, + pub bounds: MaskBounds, } impl ClipSources { pub fn new(clips: Vec) -> ClipSources { - let mask_cache_info = MaskCacheInfo::new(&clips); + let clips = clips.into_iter() + .map(|clip| (clip, GpuCacheHandle::new())) + .collect(); ClipSources { clips, - mask_cache_info, + bounds: MaskBounds { + inner: None, + outer: None, + }, } } - pub fn clips(&self) -> &[ClipSource] { + pub fn clips(&self) -> &[(ClipSource, GpuCacheHandle)] { &self.clips } @@ -128,13 +141,88 @@ impl ClipSources { return; } - self.mask_cache_info - .update(&self.clips, - layer_transform, - gpu_cache, - device_pixel_ratio); + // compute the local bounds + if self.bounds.inner.is_none() { + let mut local_rect = Some(LayerRect::new(LayerPoint::new(-MAX_CLIP, -MAX_CLIP), + LayerSize::new(2.0 * MAX_CLIP, 2.0 * MAX_CLIP))); + let mut local_inner = local_rect; + let mut has_clip_out = false; + let mut has_border_clip = false; + + for &(ref source, _) in &self.clips { + match *source { + ClipSource::Image(ref mask) => { + if !mask.repeat { + local_rect = local_rect.and_then(|r| r.intersection(&mask.rect)); + } + local_inner = None; + } + ClipSource::Rectangle(rect) => { + local_rect = local_rect.and_then(|r| r.intersection(&rect)); + local_inner = 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 { + has_clip_out = true; + } + + local_rect = local_rect.and_then(|r| r.intersection(rect)); + + let inner_rect = extract_inner_rect_safe(rect, radius); + local_inner = local_inner.and_then(|r| inner_rect.and_then(|ref inner| r.intersection(inner))); + } + ClipSource::BorderCorner{..} => { + has_border_clip = true; + } + } + } + + // Work out the type of mask geometry we have, based on the + // list of clip sources above. + self.bounds = if has_clip_out || has_border_clip { + // For clip-out, the mask rect is not known. + MaskBounds { + outer: None, + inner: Some(LayerRect::zero().into()), + } + } else { + MaskBounds { + outer: Some(local_rect.unwrap_or(LayerRect::zero()).into()), + inner: Some(local_inner.unwrap_or(LayerRect::zero()).into()), + } + }; + } + + // update the screen bounds + self.bounds.update(layer_transform, device_pixel_ratio); + + 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); + } + ClipSource::Rectangle(rect) => { + let data = ClipData::uniform(rect, 0.0, ClipMode::Clip); + data.write(&mut request); + } + ClipSource::RoundedRectangle(ref rect, ref radius, mode) => { + let data = ClipData::rounded_rect(rect, radius, mode); + data.write(&mut request); + } + ClipSource::BorderCorner(ref mut source) => { + source.write(request); + } + } + } + } - for clip in &self.clips { + for &(ref clip, _) in &self.clips { if let ClipSource::Image(ref mask) = *clip { resource_cache.request_image(mask.image, ImageRendering::Auto, @@ -145,14 +233,52 @@ impl ClipSources { } pub fn is_masking(&self) -> bool { - self.mask_cache_info.is_masking() + !self.clips.is_empty() } +} + +/// Represents a local rect and a device space +/// rectangles that are either outside or inside bounds. +#[derive(Clone, Debug, PartialEq)] +pub struct Geometry { + pub local_rect: LayerRect, + pub device_rect: DeviceIntRect, +} - pub fn clone_mask_cache_info(&self, keep_aligned: bool) -> MaskCacheInfo { - if keep_aligned { - self.mask_cache_info.clone() - } else { - self.mask_cache_info.strip_aligned() +impl From for Geometry { + fn from(local_rect: LayerRect) -> Self { + Geometry { + local_rect, + device_rect: DeviceIntRect::zero(), + } + } +} + +/// 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. +#[derive(Clone, Debug, PartialEq)] +pub struct MaskBounds { + pub outer: Option, + pub inner: Option, +} + +impl MaskBounds { + pub fn update(&mut self, transform: &LayerToWorldTransform, device_pixel_ratio: f32) { + if let Some(ref mut outer) = self.outer { + let transformed = TransformedRect::new(&outer.local_rect, + transform, + device_pixel_ratio); + outer.device_rect = transformed.bounding_rect; + } + if let Some(ref mut inner) = self.inner { + let transformed = TransformedRect::new(&inner.local_rect, + transform, + device_pixel_ratio); + inner.device_rect = transformed.inner_rect; } } } diff --git a/webrender/src/clip_scroll_node.rs b/webrender/src/clip_scroll_node.rs index 2b61b0aaf4..1d3effa14c 100644 --- a/webrender/src/clip_scroll_node.rs +++ b/webrender/src/clip_scroll_node.rs @@ -6,7 +6,7 @@ use api::{ClipId, DeviceIntRect, LayerPixel, LayerPoint, LayerRect, LayerSize}; use api::{LayerToScrollTransform, LayerToWorldTransform, LayerVector2D, PipelineId}; use api::{ScrollClamping, ScrollEventPhase, ScrollLocation, ScrollSensitivity, StickyFrameInfo}; use api::WorldPoint; -use clip::{ClipRegion, ClipSources}; +use clip::{ClipRegion, ClipSources, ClipSourcesHandle, ClipStore}; use clip_scroll_tree::TransformUpdateState; use geometry::ray_intersects_rect; use spring::{DAMPING, STIFFNESS, Spring}; @@ -22,7 +22,7 @@ const CAN_OVERSCROLL: bool = false; #[derive(Debug)] pub struct ClipInfo { /// The clips for this node. - pub clip_sources: ClipSources, + pub clip_sources: ClipSourcesHandle, /// The packed layer index for this node, which is used to render a clip mask /// for it, if necessary. @@ -39,10 +39,12 @@ pub struct ClipInfo { } impl ClipInfo { - pub fn new(clip_region: ClipRegion, packed_layer_index: PackedLayerIndex) -> ClipInfo { + pub fn new(clip_region: ClipRegion, + packed_layer_index: PackedLayerIndex, + clip_store: &mut ClipStore) -> ClipInfo { let clip_rect = LayerRect::new(clip_region.origin, clip_region.main.size); ClipInfo { - clip_sources: ClipSources::from(clip_region), + clip_sources: clip_store.insert(ClipSources::from(clip_region)), packed_layer_index, screen_bounding_rect: None, clip_rect: clip_rect, diff --git a/webrender/src/clip_scroll_tree.rs b/webrender/src/clip_scroll_tree.rs index ae5c225281..1997f933fc 100644 --- a/webrender/src/clip_scroll_tree.rs +++ b/webrender/src/clip_scroll_tree.rs @@ -2,6 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use clip::ClipStore; use clip_scroll_node::{ClipScrollNode, NodeType, ScrollingState}; use internal_types::{FastHashSet, FastHashMap}; use print_tree::{PrintTree, PrintTreePrinter}; @@ -370,7 +371,10 @@ impl ClipScrollTree { } } - fn print_node(&self, id: &ClipId, pt: &mut T) { + fn print_node(&self, + id: &ClipId, + pt: &mut T, + clip_store: &ClipStore) { let node = self.nodes.get(id).unwrap(); match node.node_type { @@ -378,7 +382,7 @@ impl ClipScrollTree { pt.new_level("Clip".to_owned()); pt.add_item(format!("screen_bounding_rect: {:?}", info.screen_bounding_rect)); - let clips = info.clip_sources.clips(); + let clips = clip_store.get(&info.clip_sources).clips(); pt.new_level(format!("Clip Sources [{}]", clips.len())); for source in clips { pt.add_item(format!("{:?}", source)); @@ -406,23 +410,23 @@ impl ClipScrollTree { pt.add_item(format!("world_content_transform: {:?}", node.world_content_transform)); for child_id in &node.children { - self.print_node(child_id, pt); + self.print_node(child_id, pt, clip_store); } pt.end_level(); } #[allow(dead_code)] - pub fn print(&self) { + pub fn print(&self, clip_store: &ClipStore) { if !self.nodes.is_empty() { let mut pt = PrintTree::new("clip_scroll tree"); - self.print_with(&mut pt); + self.print_with(clip_store, &mut pt,); } } - pub fn print_with(&self, pt: &mut T) { + pub fn print_with(&self, clip_store: &ClipStore, pt: &mut T) { if !self.nodes.is_empty() { - self.print_node(&self.root_reference_frame_id, pt); + self.print_node(&self.root_reference_frame_id, pt, clip_store); } } } diff --git a/webrender/src/frame.rs b/webrender/src/frame.rs index 789c8ef516..586d3da9bc 100644 --- a/webrender/src/frame.rs +++ b/webrender/src/frame.rs @@ -186,7 +186,7 @@ pub struct Frame { pub pipeline_epoch_map: FastHashMap, id: FrameId, frame_builder_config: FrameBuilderConfig, - frame_builder: Option, + pub frame_builder: Option, } trait FilterOpHelpers { diff --git a/webrender/src/frame_builder.rs b/webrender/src/frame_builder.rs index 174437cbf1..090234f945 100644 --- a/webrender/src/frame_builder.rs +++ b/webrender/src/frame_builder.rs @@ -11,7 +11,7 @@ use api::{LayerToScrollTransform, LayerVector2D, LayoutVector2D, LineOrientation use api::{LocalClip, PipelineId, RepeatMode, ScrollSensitivity, SubpixelDirection, TextShadow}; use api::{TileOffset, TransformStyle, WorldPixel, YuvColorSpace, YuvData}; use app_units::Au; -use clip::{ClipMode, ClipRegion, ClipSource, ClipSources}; +use clip::{ClipMode, ClipRegion, ClipSource, ClipSources, ClipStore}; use frame::FrameId; use gpu_cache::GpuCache; use internal_types::{FastHashMap, HardwareCompositeOp}; @@ -110,6 +110,7 @@ pub struct FrameBuilder { screen_size: DeviceUintSize, background_color: Option, prim_store: PrimitiveStore, + pub clip_store: ClipStore, cmds: Vec, config: FrameBuilderConfig, @@ -155,6 +156,7 @@ impl FrameBuilder { reference_frame_stack: recycle_vec(prev.reference_frame_stack), stacking_context_stack: recycle_vec(prev.stacking_context_stack), prim_store: prev.prim_store.recycle(), + clip_store: prev.clip_store.recycle(), screen_size, background_color, config, @@ -173,6 +175,7 @@ impl FrameBuilder { reference_frame_stack: Vec::new(), stacking_context_stack: Vec::new(), prim_store: PrimitiveStore::new(), + clip_store: ClipStore::new(), screen_size, background_color, config, @@ -207,7 +210,7 @@ impl FrameBuilder { clip_sources.push(ClipSource::RoundedRectangle(region.rect, region.radii, ClipMode::Clip)); } - let clip_sources = ClipSources::new(clip_sources); + let clip_sources = self.clip_store.insert(ClipSources::new(clip_sources)); let prim_index = self.prim_store.add_primitive(rect, &local_clip.clip_rect(), @@ -402,7 +405,9 @@ impl FrameBuilder { pipeline_id: PipelineId, clip_region: ClipRegion, clip_scroll_tree: &mut ClipScrollTree) { - let clip_info = ClipInfo::new(clip_region, PackedLayerIndex(self.packed_layers.len())); + let clip_info = ClipInfo::new(clip_region, + PackedLayerIndex(self.packed_layers.len()), + &mut self.clip_store); let node = ClipScrollNode::new(pipeline_id, parent_id, clip_info); clip_scroll_tree.add_node(node, new_node_id); self.packed_layers.push(PackedLayer::empty()); @@ -1631,7 +1636,7 @@ impl FrameBuilder { resource_cache, }; - pass.build(&ctx, gpu_cache, &mut render_tasks, &mut deferred_resolves); + pass.build(&ctx, gpu_cache, &mut render_tasks, &mut deferred_resolves, &self.clip_store); profile_counters.passes.inc(); profile_counters.color_targets.add(pass.color_targets.target_count()); @@ -1764,10 +1769,11 @@ impl<'a> LayerRectCalculationAndCullingPass<'a> { None }; - node_clip_info.clip_sources.update(&transform, - self.gpu_cache, - self.resource_cache, - self.device_pixel_ratio); + let clip_sources = self.frame_builder.clip_store.get_mut(&node_clip_info.clip_sources); + clip_sources.update(&transform, + self.gpu_cache, + self.resource_cache, + self.device_pixel_ratio); } } @@ -1876,10 +1882,19 @@ impl<'a> LayerRectCalculationAndCullingPass<'a> { next_node_needs_region_mask |= !info.transform.preserves_2d_axis_alignment(); continue }, - NodeType::Clip(ref clip) if clip.clip_sources.is_masking() => clip, - _ => continue, + NodeType::Clip(ref clip) => { + clip + } + NodeType::StickyFrame(..) | NodeType::ScrollFrame(..) => { + continue; + } }; + let clip_sources = self.frame_builder.clip_store.get(&clip.clip_sources); + if !clip_sources.is_masking() { + continue; + } + // apply the screen bounds of the clip node //Note: these are based on the local combined viewport, so can be tighter if let Some((_kind, ref screen_rect)) = clip.screen_bounding_rect { @@ -1889,10 +1904,8 @@ impl<'a> LayerRectCalculationAndCullingPass<'a> { } } - let clip_info = clip.clip_sources.clone_mask_cache_info(next_node_needs_region_mask); - // apply the outer device bounds of the clip stack - if let Some(ref outer) = clip_info.bounds.outer { + if let Some(ref outer) = clip_sources.bounds.outer { bounding_rect = match bounding_rect.intersection(&outer.device_rect) { Some(rect) => rect, None => return None, @@ -1900,7 +1913,11 @@ impl<'a> LayerRectCalculationAndCullingPass<'a> { } //TODO-LCCR: bake a single LCCR instead of all aligned rects? - self.current_clip_stack.push((clip.packed_layer_index, clip_info)); + self.current_clip_stack.push(ClipWorkItem { + layer_index: clip.packed_layer_index, + clip_sources: self.frame_builder.clip_store.create_weak_handle(&clip.clip_sources), + apply_rectangles: next_node_needs_region_mask, + }); next_node_needs_region_mask = false; } @@ -1972,18 +1989,18 @@ impl<'a> LayerRectCalculationAndCullingPass<'a> { self.device_pixel_ratio, display_list, TextRunMode::Normal, - &mut self.render_tasks); + &mut self.render_tasks, + &mut self.frame_builder.clip_store); stacking_context.screen_bounds = stacking_context.screen_bounds.union(&prim_screen_rect); stacking_context.isolated_items_bounds = stacking_context.isolated_items_bounds.union(&prim_local_rect); // Try to create a mask if we may need to. - let clip_task = if prim_metadata.clips.is_masking() { - let info = prim_metadata.clips.clone_mask_cache_info(false); - + let prim_clips = self.frame_builder.clip_store.get(&prim_metadata.clip_sources); + let clip_task = if prim_clips.is_masking() { // Take into account the actual clip info of the primitive, and // mutate the current bounds accordingly. - let mask_rect = match info.bounds.outer { + let mask_rect = match prim_clips.bounds.outer { Some(ref outer) => { match prim_screen_rect.intersection(&outer.device_rect) { Some(rect) => rect, @@ -1993,13 +2010,18 @@ impl<'a> LayerRectCalculationAndCullingPass<'a> { _ => prim_screen_rect, }; - let extra = (packed_layer_index, info); + let extra = ClipWorkItem { + layer_index: packed_layer_index, + clip_sources: self.frame_builder.clip_store.create_weak_handle(&prim_metadata.clip_sources), + apply_rectangles: false, + }; RenderTask::new_mask(None, mask_rect, &self.current_clip_stack, Some(extra), - prim_screen_rect) + prim_screen_rect, + &self.frame_builder.clip_store) } else if !self.current_clip_stack.is_empty() { // If the primitive doesn't have a specific clip, key the task ID off the // stacking context. This means that two primitives which are only clipped @@ -2009,7 +2031,8 @@ impl<'a> LayerRectCalculationAndCullingPass<'a> { clip_bounds, &self.current_clip_stack, None, - prim_screen_rect) + prim_screen_rect, + &self.frame_builder.clip_store) } else { None }; diff --git a/webrender/src/freelist.rs b/webrender/src/freelist.rs index d43d7145a7..79c4e3d2ee 100644 --- a/webrender/src/freelist.rs +++ b/webrender/src/freelist.rs @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::marker::PhantomData; +use util::recycle_vec; // TODO(gw): Add an occupied list head, for fast // iteration of the occupied list to implement @@ -17,6 +18,16 @@ pub struct FreeListHandle { _marker: PhantomData, } +impl Clone for WeakFreeListHandle { + fn clone(&self) -> WeakFreeListHandle { + WeakFreeListHandle { + index: self.index, + epoch: self.epoch, + _marker: PhantomData, + } + } +} + #[derive(Debug)] pub struct WeakFreeListHandle { index: u32, @@ -48,6 +59,13 @@ impl FreeList { } } + pub fn recycle(self) -> FreeList { + FreeList { + slots: recycle_vec(self.slots), + free_list_head: None, + } + } + #[allow(dead_code)] pub fn get(&self, id: &FreeListHandle) -> &T { self.slots[id.index as usize] diff --git a/webrender/src/lib.rs b/webrender/src/lib.rs index 1eacc5f05b..e1595ed2d3 100644 --- a/webrender/src/lib.rs +++ b/webrender/src/lib.rs @@ -69,7 +69,6 @@ mod glyph_rasterizer; mod gpu_cache; mod gpu_types; mod internal_types; -mod mask_cache; mod prim_store; mod print_tree; mod profiler; diff --git a/webrender/src/mask_cache.rs b/webrender/src/mask_cache.rs deleted file mode 100644 index b04f3f721e..0000000000 --- a/webrender/src/mask_cache.rs +++ /dev/null @@ -1,266 +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::{DeviceIntRect, ImageMask, LayerPoint, LayerRect}; -use api::{LayerSize, LayerToWorldTransform}; -use border::BorderCornerClipSource; -use clip::{ClipMode, ClipSource}; -use gpu_cache::{GpuCache, GpuCacheHandle, ToGpuBlocks}; -use prim_store::{CLIP_DATA_GPU_BLOCKS, ClipData, ImageMaskData}; -use util::{extract_inner_rect_safe, TransformedRect}; - -const MAX_CLIP: f32 = 1000000.0; - -#[derive(Debug, Copy, Clone)] -pub struct ClipAddressRange { - pub location: GpuCacheHandle, - item_count: usize, -} - -impl ClipAddressRange { - fn new(count: usize) -> Self { - ClipAddressRange { - location: GpuCacheHandle::new(), - item_count: count, - } - } - - pub fn is_empty(&self) -> bool { - self.item_count == 0 - } - - pub fn get_count(&self) -> usize { - self.item_count - } - - fn get_block_count(&self) -> Option { - if self.item_count != 0 { - Some(self.item_count * CLIP_DATA_GPU_BLOCKS) - } else { - None - } - } -} - -/// Represents a local rect and a device space -/// rectangles that are either outside or inside bounds. -#[derive(Clone, Debug, PartialEq)] -pub struct Geometry { - pub local_rect: LayerRect, - pub device_rect: DeviceIntRect, -} - -impl From for Geometry { - fn from(local_rect: LayerRect) -> Self { - Geometry { - local_rect, - device_rect: DeviceIntRect::zero(), - } - } -} - -/// 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. -#[derive(Clone, Debug, PartialEq)] -pub struct MaskBounds { - pub outer: Option, - pub inner: Option, -} - -impl MaskBounds { - pub fn update(&mut self, transform: &LayerToWorldTransform, device_pixel_ratio: f32) { - if let Some(ref mut outer) = self.outer { - let transformed = TransformedRect::new(&outer.local_rect, - transform, - device_pixel_ratio); - outer.device_rect = transformed.bounding_rect; - } - if let Some(ref mut inner) = self.inner { - let transformed = TransformedRect::new(&inner.local_rect, - transform, - device_pixel_ratio); - inner.device_rect = transformed.inner_rect; - } - } -} - -#[derive(Clone, Debug)] -pub struct MaskCacheInfo { - /// Clip items that are always applied - pub complex_clip_range: ClipAddressRange, - /// Clip items that are only applied if the clip space is transformed from - /// the local space of target primitive/layer. - pub layer_clip_range: ClipAddressRange, - pub image: Option<(ImageMask, GpuCacheHandle)>, - pub border_corners: Vec<(BorderCornerClipSource, GpuCacheHandle)>, - pub bounds: MaskBounds, -} - -impl MaskCacheInfo { - /// Create a new mask cache info. It allocates the GPU store data but leaves - /// it uninitialized for the following `update()` call to deal with. - pub fn new(clips: &[ClipSource]) -> MaskCacheInfo { - let mut image = None; - let mut border_corners = Vec::new(); - let mut complex_clip_count = 0; - let mut layer_clip_count = 0; - - // Work out how much clip data space we need to allocate - // and if we have an image mask. - for clip in clips { - match *clip { - ClipSource::RoundedRectangle(..) => { - complex_clip_count += 1; - } - ClipSource::Rectangle(..) => { - layer_clip_count += 1; - } - ClipSource::Image(image_mask) => { - debug_assert!(image.is_none()); // TODO(gw): Support >1 image mask! - image = Some((image_mask, GpuCacheHandle::new())); - } - ClipSource::BorderCorner(ref source) => { - border_corners.push((source.clone(), GpuCacheHandle::new())); - } - } - } - - MaskCacheInfo { - complex_clip_range: ClipAddressRange::new(complex_clip_count), - layer_clip_range: ClipAddressRange::new(layer_clip_count), - image, - border_corners, - bounds: MaskBounds { - inner: None, - outer: None, - }, - } - } - - pub fn update(&mut self, - sources: &[ClipSource], - transform: &LayerToWorldTransform, - gpu_cache: &mut GpuCache, - device_pixel_ratio: f32) - -> &MaskBounds { - // Step[1] - compute the local bounds - //TODO: move to initialization stage? - if self.bounds.inner.is_none() { - let mut local_rect = Some(LayerRect::new(LayerPoint::new(-MAX_CLIP, -MAX_CLIP), - LayerSize::new(2.0 * MAX_CLIP, 2.0 * MAX_CLIP))); - let mut local_inner = local_rect; - let mut has_clip_out = false; - let has_border_clip = !self.border_corners.is_empty(); - - for source in sources { - match *source { - ClipSource::Image(ref mask) => { - if !mask.repeat { - local_rect = local_rect.and_then(|r| r.intersection(&mask.rect)); - } - local_inner = None; - } - ClipSource::Rectangle(rect) => { - local_rect = local_rect.and_then(|r| r.intersection(&rect)); - local_inner = 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 { - has_clip_out = true; - break; - } - - local_rect = local_rect.and_then(|r| r.intersection(rect)); - - let inner_rect = extract_inner_rect_safe(rect, radius); - local_inner = local_inner.and_then(|r| inner_rect.and_then(|ref inner| r.intersection(inner))); - } - ClipSource::BorderCorner{..} => {} - } - } - - // Work out the type of mask geometry we have, based on the - // list of clip sources above. - self.bounds = if has_clip_out || has_border_clip { - // For clip-out, the mask rect is not known. - MaskBounds { - outer: None, - inner: Some(LayerRect::zero().into()), - } - } else { - MaskBounds { - outer: Some(local_rect.unwrap_or(LayerRect::zero()).into()), - inner: Some(local_inner.unwrap_or(LayerRect::zero()).into()), - } - }; - } - - // Step[2] - update GPU cache data - - if let Some(block_count) = self.complex_clip_range.get_block_count() { - if let Some(mut request) = gpu_cache.request(&mut self.complex_clip_range.location) { - for source in sources { - if let ClipSource::RoundedRectangle(ref rect, ref radius, mode) = *source { - let data = ClipData::rounded_rect(rect, radius, mode); - data.write(&mut request); - } - } - assert_eq!(request.close(), block_count); - } - } - - if let Some(block_count) = self.layer_clip_range.get_block_count() { - if let Some(mut request) = gpu_cache.request(&mut self.layer_clip_range.location) { - for source in sources { - if let ClipSource::Rectangle(rect) = *source { - let data = ClipData::uniform(rect, 0.0, ClipMode::Clip); - data.write(&mut request); - } - } - assert_eq!(request.close(), block_count); - } - } - - for &mut (ref mut border_source, ref mut gpu_location) in &mut self.border_corners { - if let Some(request) = gpu_cache.request(gpu_location) { - border_source.write(request); - } - } - - if let Some((ref mask, ref mut gpu_location)) = self.image { - if let Some(request) = gpu_cache.request(gpu_location) { - let data = ImageMaskData { - local_rect: mask.rect, - }; - data.write_gpu_blocks(request); - } - } - - // Step[3] - update the screen bounds - self.bounds.update(transform, device_pixel_ratio); - &self.bounds - } - - /// Check if this `MaskCacheInfo` actually carries any masks. - pub fn is_masking(&self) -> bool { - self.image.is_some() || - self.complex_clip_range.item_count != 0 || - self.layer_clip_range.item_count != 0 || - !self.border_corners.is_empty() - } - - /// Return a clone of this object without any layer-aligned clip items - pub fn strip_aligned(&self) -> Self { - MaskCacheInfo { - layer_clip_range: ClipAddressRange::new(0), - .. self.clone() - } - } -} diff --git a/webrender/src/prim_store.rs b/webrender/src/prim_store.rs index 54ca730c3a..bfc7b56140 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -9,7 +9,7 @@ use api::{GlyphKey, LayerToWorldTransform, TileOffset, YuvColorSpace, YuvFormat} use api::{device_length, FontInstance, LayerVector2D, LineOrientation, LineStyle}; use app_units::Au; use border::BorderCornerInstance; -use clip::{ClipMode, ClipSources}; +use clip::{ClipMode, ClipSourcesHandle, ClipStore}; use euclid::{Size2D}; use gpu_cache::{GpuCacheAddress, GpuBlockData, GpuCache, GpuCacheHandle, GpuDataRequest, ToGpuBlocks}; use renderer::MAX_VERTEX_TEXTURE_WIDTH; @@ -18,9 +18,6 @@ use resource_cache::{ImageProperties, ResourceCache}; use std::{mem, usize}; use util::{pack_as_float, TransformedRect, recycle_vec}; - -pub const CLIP_DATA_GPU_BLOCKS: usize = 10; - #[derive(Debug, Copy, Clone)] pub struct PrimitiveOpacity { pub is_opaque: bool, @@ -134,7 +131,7 @@ impl GpuCacheHandle { #[derive(Debug)] pub struct PrimitiveMetadata { pub opacity: PrimitiveOpacity, - pub clips: ClipSources, + pub clip_sources: ClipSourcesHandle, pub prim_kind: PrimitiveKind, pub cpu_prim_index: SpecificPrimitiveIndex, pub gpu_location: GpuCacheHandle, @@ -804,7 +801,7 @@ impl PrimitiveStore { pub fn add_primitive(&mut self, local_rect: &LayerRect, local_clip_rect: &LayerRect, - clips: ClipSources, + clip_sources: ClipSourcesHandle, container: PrimitiveContainer) -> PrimitiveIndex { let prim_index = self.cpu_metadata.len(); self.cpu_bounding_rects.push(None); @@ -813,7 +810,7 @@ impl PrimitiveStore { PrimitiveContainer::Rectangle(rect) => { let metadata = PrimitiveMetadata { opacity: PrimitiveOpacity::from_alpha(rect.color.a), - clips, + clip_sources, prim_kind: PrimitiveKind::Rectangle, cpu_prim_index: SpecificPrimitiveIndex(self.cpu_rectangles.len()), gpu_location: GpuCacheHandle::new(), @@ -830,7 +827,7 @@ impl PrimitiveStore { PrimitiveContainer::Line(line) => { let metadata = PrimitiveMetadata { opacity: PrimitiveOpacity::translucent(), - clips, + clip_sources, prim_kind: PrimitiveKind::Line, cpu_prim_index: SpecificPrimitiveIndex(self.cpu_lines.len()), gpu_location: GpuCacheHandle::new(), @@ -846,7 +843,7 @@ impl PrimitiveStore { PrimitiveContainer::TextRun(text_cpu) => { let metadata = PrimitiveMetadata { opacity: PrimitiveOpacity::translucent(), - clips, + clip_sources, prim_kind: PrimitiveKind::TextRun, cpu_prim_index: SpecificPrimitiveIndex(self.cpu_text_runs.len()), gpu_location: GpuCacheHandle::new(), @@ -862,7 +859,7 @@ impl PrimitiveStore { PrimitiveContainer::TextShadow(text_shadow) => { let metadata = PrimitiveMetadata { opacity: PrimitiveOpacity::translucent(), - clips, + clip_sources, prim_kind: PrimitiveKind::TextShadow, cpu_prim_index: SpecificPrimitiveIndex(self.cpu_text_shadows.len()), gpu_location: GpuCacheHandle::new(), @@ -878,7 +875,7 @@ impl PrimitiveStore { PrimitiveContainer::Image(image_cpu) => { let metadata = PrimitiveMetadata { opacity: PrimitiveOpacity::translucent(), - clips, + clip_sources, prim_kind: PrimitiveKind::Image, cpu_prim_index: SpecificPrimitiveIndex(self.cpu_images.len()), gpu_location: GpuCacheHandle::new(), @@ -894,7 +891,7 @@ impl PrimitiveStore { PrimitiveContainer::YuvImage(image_cpu) => { let metadata = PrimitiveMetadata { opacity: PrimitiveOpacity::opaque(), - clips, + clip_sources, prim_kind: PrimitiveKind::YuvImage, cpu_prim_index: SpecificPrimitiveIndex(self.cpu_yuv_images.len()), gpu_location: GpuCacheHandle::new(), @@ -910,7 +907,7 @@ impl PrimitiveStore { PrimitiveContainer::Border(border_cpu) => { let metadata = PrimitiveMetadata { opacity: PrimitiveOpacity::translucent(), - clips, + clip_sources, prim_kind: PrimitiveKind::Border, cpu_prim_index: SpecificPrimitiveIndex(self.cpu_borders.len()), gpu_location: GpuCacheHandle::new(), @@ -926,7 +923,7 @@ impl PrimitiveStore { PrimitiveContainer::AlignedGradient(gradient_cpu) => { let metadata = PrimitiveMetadata { opacity: PrimitiveOpacity::translucent(), - clips, + clip_sources, prim_kind: PrimitiveKind::AlignedGradient, cpu_prim_index: SpecificPrimitiveIndex(self.cpu_gradients.len()), gpu_location: GpuCacheHandle::new(), @@ -943,7 +940,7 @@ impl PrimitiveStore { let metadata = PrimitiveMetadata { // TODO: calculate if the gradient is actually opaque opacity: PrimitiveOpacity::translucent(), - clips, + clip_sources, prim_kind: PrimitiveKind::AngleGradient, cpu_prim_index: SpecificPrimitiveIndex(self.cpu_gradients.len()), gpu_location: GpuCacheHandle::new(), @@ -960,7 +957,7 @@ impl PrimitiveStore { let metadata = PrimitiveMetadata { // TODO: calculate if the gradient is actually opaque opacity: PrimitiveOpacity::translucent(), - clips, + clip_sources, prim_kind: PrimitiveKind::RadialGradient, cpu_prim_index: SpecificPrimitiveIndex(self.cpu_radial_gradients.len()), gpu_location: GpuCacheHandle::new(), @@ -976,7 +973,7 @@ impl PrimitiveStore { PrimitiveContainer::BoxShadow(box_shadow) => { let metadata = PrimitiveMetadata { opacity: PrimitiveOpacity::translucent(), - clips, + clip_sources, prim_kind: PrimitiveKind::BoxShadow, cpu_prim_index: SpecificPrimitiveIndex(self.cpu_box_shadows.len()), gpu_location: GpuCacheHandle::new(), @@ -1035,7 +1032,8 @@ impl PrimitiveStore { device_pixel_ratio: f32, display_list: &BuiltDisplayList, text_run_mode: TextRunMode, - render_tasks: &mut RenderTaskTree) + render_tasks: &mut RenderTaskTree, + clip_store: &mut ClipStore) -> &mut PrimitiveMetadata { let (prim_kind, cpu_prim_index) = { let metadata = &self.cpu_metadata[prim_index.0]; @@ -1056,15 +1054,16 @@ impl PrimitiveStore { device_pixel_ratio, display_list, TextRunMode::Shadow, - render_tasks); + render_tasks, + clip_store); } } let metadata = &mut self.cpu_metadata[prim_index.0]; - metadata.clips.update(layer_transform, - gpu_cache, - resource_cache, - device_pixel_ratio); + clip_store.get_mut(&metadata.clip_sources).update(layer_transform, + gpu_cache, + resource_cache, + device_pixel_ratio); match metadata.prim_kind { PrimitiveKind::Rectangle | diff --git a/webrender/src/render_backend.rs b/webrender/src/render_backend.rs index e124428cd6..e2c51d18ae 100644 --- a/webrender/src/render_backend.rs +++ b/webrender/src/render_backend.rs @@ -613,9 +613,12 @@ impl RenderBackend { for (_, doc) in &self.documents { let debug_node = debug_server::TreeNode::new("document clip_scroll tree"); let mut builder = debug_server::TreeNodeBuilder::new(debug_node); - doc.frame.clip_scroll_tree.print_with(&mut builder); + // TODO(gw): Restructure the storage of clip-scroll tree, clip store + // etc so this isn't so untidy. + let clip_store = &doc.frame.frame_builder.as_ref().unwrap().clip_store; + doc.frame.clip_scroll_tree.print_with(clip_store, &mut builder); - debug_root.add(builder.build()); + debug_root.add(builder.build()); } serde_json::to_string(&debug_root).unwrap() diff --git a/webrender/src/render_task.rs b/webrender/src/render_task.rs index 1ad7cf3a33..9036428c46 100644 --- a/webrender/src/render_task.rs +++ b/webrender/src/render_task.rs @@ -2,9 +2,9 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use clip::{ClipSource, ClipSourcesWeakHandle, ClipStore}; use gpu_cache::GpuCacheHandle; use internal_types::HardwareCompositeOp; -use mask_cache::MaskCacheInfo; use prim_store::{BoxShadowPrimitiveCacheKey, PrimitiveIndex}; use std::{cmp, f32, i32, usize}; use tiling::{ClipScrollGroupIndex, PackedLayerIndex, RenderPass, RenderTargetIndex}; @@ -157,7 +157,44 @@ pub enum MaskGeometryKind { // TODO(gw): Add more types here (e.g. 4 rectangles outside the inner rect) } -pub type ClipWorkItem = (PackedLayerIndex, MaskCacheInfo); +#[derive(Debug, Clone)] +pub struct ClipWorkItem { + pub layer_index: PackedLayerIndex, + pub clip_sources: ClipSourcesWeakHandle, + pub apply_rectangles: bool, +} + +impl ClipWorkItem { + fn get_geometry_kind(&self, clip_store: &ClipStore) -> MaskGeometryKind { + let clips = clip_store.get_opt(&self.clip_sources) + .expect("bug: clip handle should be valid") + .clips(); + let mut rounded_rect_count = 0; + + for &(ref clip, _) in clips { + match *clip { + ClipSource::Rectangle(..) => { + if self.apply_rectangles { + return MaskGeometryKind::Default; + } + } + ClipSource::RoundedRectangle(..) => { + rounded_rect_count += 1; + } + ClipSource::Image(..) | + ClipSource::BorderCorner(..) => { + return MaskGeometryKind::Default; + } + } + } + + if rounded_rect_count == 1 { + MaskGeometryKind::CornersOnly + } else { + MaskGeometryKind::Default + } + } +} #[derive(Debug)] pub struct CacheMaskTask { @@ -245,13 +282,17 @@ impl RenderTask { task_rect: DeviceIntRect, raw_clips: &[ClipWorkItem], extra_clip: Option, - prim_rect: DeviceIntRect) + prim_rect: DeviceIntRect, + clip_store: &ClipStore) -> Option { // Filter out all the clip instances that don't contribute to the result let mut inner_rect = Some(task_rect); let clips: Vec<_> = raw_clips.iter() .chain(extra_clip.iter()) - .filter(|&&(_, ref clip_info)| { + .filter(|work_item| { + let clip_info = clip_store.get_opt(&work_item.clip_sources) + .expect("bug: clip item should exist"); + // If this clip does not contribute to a mask, then ensure // it gets filtered out here. Otherwise, if a mask is // created (by a different clip in the list), the allocated @@ -291,13 +332,7 @@ impl RenderTask { return None; } if clips.len() == 1 { - let (_, ref info) = clips[0]; - if info.border_corners.is_empty() && - info.image.is_none() && - info.complex_clip_range.get_count() == 1 && - info.layer_clip_range.get_count() == 0 { - geometry_kind = MaskGeometryKind::CornersOnly; - } + geometry_kind = clips[0].get_geometry_kind(clip_store); } } diff --git a/webrender/src/tiling.rs b/webrender/src/tiling.rs index b7abeb21c1..7d186b718d 100644 --- a/webrender/src/tiling.rs +++ b/webrender/src/tiling.rs @@ -3,16 +3,16 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use border::{BorderCornerInstance, BorderCornerSide}; +use clip::{ClipSource, ClipStore}; use device::Texture; use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle, GpuCacheUpdateList}; use gpu_types::BoxShadowCacheInstance; use internal_types::BatchTextures; use internal_types::{FastHashMap, SourceTexture}; -use mask_cache::MaskCacheInfo; -use prim_store::{CLIP_DATA_GPU_BLOCKS, DeferredResolve}; +use prim_store::{DeferredResolve}; use prim_store::{PrimitiveIndex, PrimitiveKind, PrimitiveMetadata, PrimitiveStore}; use profiler::FrameProfileCounters; -use render_task::{AlphaRenderItem, MaskGeometryKind, MaskSegment}; +use render_task::{AlphaRenderItem, ClipWorkItem, MaskGeometryKind, MaskSegment}; use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskKey, RenderTaskKind}; use render_task::{RenderTaskLocation, RenderTaskTree}; use renderer::BlendMode; @@ -669,100 +669,96 @@ impl ClipBatcher { } } - fn add<'a>(&mut self, - task_address: RenderTaskAddress, - clips: &[(PackedLayerIndex, MaskCacheInfo)], - resource_cache: &ResourceCache, - gpu_cache: &GpuCache, - geometry_kind: MaskGeometryKind) { - - for &(packed_layer_index, ref info) in clips.iter() { + fn add(&mut self, + task_address: RenderTaskAddress, + clips: &[ClipWorkItem], + resource_cache: &ResourceCache, + gpu_cache: &GpuCache, + geometry_kind: MaskGeometryKind, + clip_store: &ClipStore) { + for work_item in clips.iter() { let instance = CacheClipInstance { render_task_address: task_address.0 as i32, - layer_index: packed_layer_index.0 as i32, + layer_index: work_item.layer_index.0 as i32, segment: 0, clip_data_address: GpuCacheAddress::invalid(), resource_address: GpuCacheAddress::invalid(), }; - - if !info.complex_clip_range.is_empty() { - let base_gpu_address = gpu_cache.get_address(&info.complex_clip_range.location); - - for clip_index in 0 .. info.complex_clip_range.get_count() { - let gpu_address = base_gpu_address + CLIP_DATA_GPU_BLOCKS * clip_index; - match geometry_kind { - MaskGeometryKind::Default => { + let info = clip_store.get_opt(&work_item.clip_sources) + .expect("bug: clip handle should be valid"); + + for &(ref source, ref handle) in &info.clips { + let gpu_address = gpu_cache.get_address(handle); + + match *source { + ClipSource::Image(ref mask) => { + let cache_item = resource_cache.get_cached_image(mask.image, ImageRendering::Auto, None); + self.images.entry(cache_item.texture_id) + .or_insert(Vec::new()) + .push(CacheClipInstance { + clip_data_address: gpu_address, + resource_address: gpu_cache.get_address(&cache_item.uv_rect_handle), + ..instance + }); + } + ClipSource::Rectangle(..) => { + if work_item.apply_rectangles { self.rectangles.push(CacheClipInstance { clip_data_address: gpu_address, segment: MaskSegment::All as i32, ..instance }); } - MaskGeometryKind::CornersOnly => { - self.rectangles.extend_from_slice(&[ - CacheClipInstance { - clip_data_address: gpu_address, - segment: MaskSegment::TopLeftCorner as i32, - ..instance - }, - CacheClipInstance { - clip_data_address: gpu_address, - segment: MaskSegment::TopRightCorner as i32, - ..instance - }, - CacheClipInstance { - clip_data_address: gpu_address, - segment: MaskSegment::BottomLeftCorner as i32, - ..instance - }, - CacheClipInstance { + } + ClipSource::RoundedRectangle(..) => { + match geometry_kind { + MaskGeometryKind::Default => { + self.rectangles.push(CacheClipInstance { clip_data_address: gpu_address, - segment: MaskSegment::BottomRightCorner as i32, + segment: MaskSegment::All as i32, ..instance - }, - ]); + }); + } + MaskGeometryKind::CornersOnly => { + self.rectangles.extend_from_slice(&[ + CacheClipInstance { + clip_data_address: gpu_address, + segment: MaskSegment::TopLeftCorner as i32, + ..instance + }, + CacheClipInstance { + clip_data_address: gpu_address, + segment: MaskSegment::TopRightCorner as i32, + ..instance + }, + CacheClipInstance { + clip_data_address: gpu_address, + segment: MaskSegment::BottomLeftCorner as i32, + ..instance + }, + CacheClipInstance { + clip_data_address: gpu_address, + segment: MaskSegment::BottomRightCorner as i32, + ..instance + }, + ]); + } + } + } + ClipSource::BorderCorner(ref source) => { + self.border_clears.push(CacheClipInstance { + clip_data_address: gpu_address, + segment: 0, + ..instance + }); + for clip_index in 0..source.actual_clip_count { + self.borders.push(CacheClipInstance { + clip_data_address: gpu_address, + segment: 1 + clip_index as i32, + ..instance + }) } } - } - } - - if !info.layer_clip_range.is_empty() { - let base_gpu_address = gpu_cache.get_address(&info.layer_clip_range.location); - - for clip_index in 0 .. info.layer_clip_range.get_count() { - let gpu_address = base_gpu_address + CLIP_DATA_GPU_BLOCKS * clip_index; - self.rectangles.push(CacheClipInstance { - clip_data_address: gpu_address, - segment: MaskSegment::All as i32, - ..instance - }); - } - } - - if let Some((ref mask, ref gpu_location)) = info.image { - let cache_item = resource_cache.get_cached_image(mask.image, ImageRendering::Auto, None); - self.images.entry(cache_item.texture_id) - .or_insert(Vec::new()) - .push(CacheClipInstance { - clip_data_address: gpu_cache.get_address(gpu_location), - resource_address: gpu_cache.get_address(&cache_item.uv_rect_handle), - ..instance - }) - } - - for &(ref source, ref gpu_location) in &info.border_corners { - let gpu_address = gpu_cache.get_address(gpu_location); - self.border_clears.push(CacheClipInstance { - clip_data_address: gpu_address, - segment: 0, - ..instance - }); - for clip_index in 0..source.actual_clip_count { - self.borders.push(CacheClipInstance { - clip_data_address: gpu_address, - segment: 1 + clip_index as i32, - ..instance - }) } } } @@ -829,7 +825,8 @@ pub trait RenderTarget { task_id: RenderTaskId, ctx: &RenderTargetContext, gpu_cache: &GpuCache, - render_tasks: &RenderTaskTree); + render_tasks: &RenderTaskTree, + clip_store: &ClipStore); fn used_rect(&self) -> DeviceIntRect; } @@ -875,8 +872,12 @@ impl RenderTargetList { task_id: RenderTaskId, ctx: &RenderTargetContext, gpu_cache: &GpuCache, - render_tasks: &mut RenderTaskTree) { - self.targets.last_mut().unwrap().add_task(task_id, ctx, gpu_cache, render_tasks); + render_tasks: &mut RenderTaskTree, + clip_store: &ClipStore) { + self.targets + .last_mut() + .unwrap() + .add_task(task_id, ctx, gpu_cache, render_tasks, clip_store); } fn allocate(&mut self, alloc_size: DeviceUintSize) -> (DeviceUintPoint, RenderTargetIndex) { @@ -957,7 +958,8 @@ impl RenderTarget for ColorRenderTarget { task_id: RenderTaskId, ctx: &RenderTargetContext, gpu_cache: &GpuCache, - render_tasks: &RenderTaskTree) { + render_tasks: &RenderTaskTree, + _: &ClipStore) { let task = render_tasks.get(task_id); match task.kind { @@ -1094,7 +1096,8 @@ impl RenderTarget for AlphaRenderTarget { task_id: RenderTaskId, ctx: &RenderTargetContext, gpu_cache: &GpuCache, - render_tasks: &RenderTaskTree) { + render_tasks: &RenderTaskTree, + clip_store: &ClipStore) { let task = render_tasks.get(task_id); match task.kind { RenderTaskKind::Alias(..) => { @@ -1128,7 +1131,8 @@ impl RenderTarget for AlphaRenderTarget { &task_info.clips, &ctx.resource_cache, gpu_cache, - task_info.geometry_kind); + task_info.geometry_kind, + clip_store); } } } @@ -1185,7 +1189,8 @@ impl RenderPass { ctx: &RenderTargetContext, gpu_cache: &mut GpuCache, render_tasks: &mut RenderTaskTree, - deferred_resolves: &mut Vec) { + deferred_resolves: &mut Vec, + clip_store: &ClipStore) { profile_scope!("RenderPass::build"); // Step through each task, adding to batches as appropriate. @@ -1241,8 +1246,18 @@ impl RenderPass { }; match target_kind { - RenderTargetKind::Color => self.color_targets.add_task(task_id, ctx, gpu_cache, render_tasks), - RenderTargetKind::Alpha => self.alpha_targets.add_task(task_id, ctx, gpu_cache, render_tasks), + RenderTargetKind::Color => self.color_targets + .add_task(task_id, + ctx, + gpu_cache, + render_tasks, + clip_store), + RenderTargetKind::Alpha => self.alpha_targets + .add_task(task_id, + ctx, + gpu_cache, + render_tasks, + clip_store), } }