diff --git a/sample/src/main.rs b/sample/src/main.rs index 90288f0e31..90c55dcea0 100644 --- a/sample/src/main.rs +++ b/sample/src/main.rs @@ -1,20 +1,21 @@ +extern crate app_units; extern crate webrender; extern crate glutin; extern crate gleam; extern crate webrender_traits; extern crate euclid; +use app_units::Au; use euclid::{Size2D, Point2D, Rect, Matrix4D}; use gleam::gl; use std::path::PathBuf; -use std::ffi::CStr; use webrender_traits::{AuxiliaryListsBuilder, ColorF, Epoch, GlyphInstance}; use webrender_traits::{ImageData, ImageFormat, PipelineId, RendererKind}; use std::fs::File; use std::io::Read; use std::env; -fn _load_file(name: &str) -> Vec { +fn load_file(name: &str) -> Vec { let mut file = File::open(name).unwrap(); let mut buffer = vec![]; file.read_to_end(&mut buffer).unwrap(); @@ -95,10 +96,6 @@ fn main() { let (mut renderer, sender) = webrender::renderer::Renderer::new(opts); let api = sender.create_api(); -// let font_path = "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf"; -// let font_bytes = load_file(font_path); -// let font_key = api.add_raw_font(font_bytes); - let notifier = Box::new(Notifier::new(window.create_window_proxy())); renderer.set_render_notifier(notifier); @@ -110,111 +107,126 @@ fn main() { let mut builder = webrender_traits::DisplayListBuilder::new(); let bounds = Rect::new(Point2D::new(0.0, 0.0), Size2D::new(width as f32, height as f32)); + let clip_region = { + let radius = webrender_traits::BorderRadius::uniform(20.0); + let complex = webrender_traits::ComplexClipRegion::new( + Rect::new(Point2D::new(50.0, 50.0), Size2D::new(100.0, 100.0)), + radius); + + webrender_traits::ClipRegion::new(&bounds, + vec![complex], + None, + &mut auxiliary_lists_builder) + }; + builder.push_stacking_context( webrender_traits::StackingContext::new(webrender_traits::ScrollPolicy::Scrollable, - bounds, bounds, 0, &Matrix4D::identity(), &Matrix4D::identity(), webrender_traits::MixBlendMode::Normal, Vec::new(), - &mut auxiliary_lists_builder)); + &mut auxiliary_lists_builder), + clip_region); - let clip_region = { + let sub_clip = { let mask = webrender_traits::ImageMask { image: api.add_image(2, 2, None, ImageFormat::A8, ImageData::new(vec![0,80, 180, 255])), rect: Rect::new(Point2D::new(75.0, 75.0), Size2D::new(100.0, 100.0)), repeat: false, }; - let radius = webrender_traits::BorderRadius::uniform(20.0); - let complex = webrender_traits::ComplexClipRegion::new( - Rect::new(Point2D::new(50.0, 50.0), Size2D::new(100.0, 100.0)), - radius); - webrender_traits::ClipRegion::new(&bounds, - vec![complex], + Vec::new(), Some(mask), &mut auxiliary_lists_builder) }; builder.push_rect(Rect::new(Point2D::new(100.0, 100.0), Size2D::new(100.0, 100.0)), - clip_region, + sub_clip, + ColorF::new(0.0, 1.0, 0.0, 1.0)); + builder.push_rect(Rect::new(Point2D::new(250.0, 100.0), Size2D::new(100.0, 100.0)), + sub_clip, ColorF::new(0.0, 1.0, 0.0, 1.0)); - let _text_bounds = Rect::new(Point2D::new(100.0, 200.0), Size2D::new(700.0, 300.0)); - - let _glyphs = vec![ - GlyphInstance { - index: 48, - x: 100.0, - y: 100.0, - }, - GlyphInstance { - index: 68, - x: 150.0, - y: 100.0, - }, - GlyphInstance { - index: 80, - x: 200.0, - y: 100.0, - }, - GlyphInstance { - index: 82, - x: 250.0, - y: 100.0, - }, - GlyphInstance { - index: 81, - x: 300.0, - y: 100.0, - }, - GlyphInstance { - index: 3, - x: 350.0, - y: 100.0, - }, - GlyphInstance { - index: 86, - x: 400.0, - y: 100.0, - }, - GlyphInstance { - index: 79, - x: 450.0, - y: 100.0, - }, - GlyphInstance { - index: 72, - x: 500.0, - y: 100.0, - }, - GlyphInstance { - index: 83, - x: 550.0, - y: 100.0, - }, - GlyphInstance { - index: 87, - x: 600.0, - y: 100.0, - }, - GlyphInstance { - index: 17, - x: 650.0, - y: 100.0, - }, - ]; - -// builder.push_text(text_bounds, -// clip_region, -// glyphs, -// font_key, -// ColorF::new(1.0, 1.0, 0.0, 1.0), -// Au::from_px(32), -// Au::from_px(0), -// &mut frame_builder.auxiliary_lists_builder); + if false { // draw text? + let font_bytes = load_file("res/FreeSans.ttf"); + let font_key = api.add_raw_font(font_bytes); + + let text_bounds = Rect::new(Point2D::new(100.0, 200.0), Size2D::new(700.0, 300.0)); + + let glyphs = vec![ + GlyphInstance { + index: 48, + x: 100.0, + y: 100.0, + }, + GlyphInstance { + index: 68, + x: 150.0, + y: 100.0, + }, + GlyphInstance { + index: 80, + x: 200.0, + y: 100.0, + }, + GlyphInstance { + index: 82, + x: 250.0, + y: 100.0, + }, + GlyphInstance { + index: 81, + x: 300.0, + y: 100.0, + }, + GlyphInstance { + index: 3, + x: 350.0, + y: 100.0, + }, + GlyphInstance { + index: 86, + x: 400.0, + y: 100.0, + }, + GlyphInstance { + index: 79, + x: 450.0, + y: 100.0, + }, + GlyphInstance { + index: 72, + x: 500.0, + y: 100.0, + }, + GlyphInstance { + index: 83, + x: 550.0, + y: 100.0, + }, + GlyphInstance { + index: 87, + x: 600.0, + y: 100.0, + }, + GlyphInstance { + index: 17, + x: 650.0, + y: 100.0, + }, + ]; + + builder.push_text(text_bounds, + webrender_traits::ClipRegion::simple(&bounds), + glyphs, + font_key, + ColorF::new(1.0, 1.0, 0.0, 1.0), + Au::from_px(32), + Au::from_px(0), + &mut auxiliary_lists_builder); + } builder.pop_stacking_context(); diff --git a/webrender/res/clip_shared.glsl b/webrender/res/clip_shared.glsl index 728ce8dea8..a86a3602b3 100644 --- a/webrender/res/clip_shared.glsl +++ b/webrender/res/clip_shared.glsl @@ -9,6 +9,7 @@ struct CacheClipInstance { int render_task_index; int layer_index; int data_index; + int base_task_index; }; CacheClipInstance fetch_clip_item(int index) { @@ -21,6 +22,7 @@ CacheClipInstance fetch_clip_item(int index) { cci.render_task_index = data0.x; cci.layer_index = data0.y; cci.data_index = data0.z; + cci.base_task_index = data0.w; return cci; } diff --git a/webrender/res/cs_clip_copy.fs.glsl b/webrender/res/cs_clip_copy.fs.glsl new file mode 100644 index 0000000000..304cfdbfa0 --- /dev/null +++ b/webrender/res/cs_clip_copy.fs.glsl @@ -0,0 +1,8 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +void main(void) { + float alpha = texelFetch(sCache, ivec3(vClipMaskUv), 0).a; + oFragColor = vec4(1.0, 1.0, 1.0, alpha); +} diff --git a/webrender/res/cs_clip_copy.vs.glsl b/webrender/res/cs_clip_copy.vs.glsl new file mode 100644 index 0000000000..2b738dc791 --- /dev/null +++ b/webrender/res/cs_clip_copy.vs.glsl @@ -0,0 +1,19 @@ +#line 1 +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +void main(void) { + CacheClipInstance cci = fetch_clip_item(gl_InstanceID); + ClipArea area = fetch_clip_area(cci.render_task_index); + ClipArea source = fetch_clip_area(cci.base_task_index); + + vec2 final_pos = mix(area.task_bounds.xy, area.task_bounds.zw, aPosition.xy); + + gl_Position = uTransform * vec4(final_pos, 0.0, 1.0); + + // convert to the source task space via the screen space + vec2 tuv = final_pos - area.task_bounds.xy + area.screen_origin_target_index.xy + + source.task_bounds.xy - source.screen_origin_target_index.xy; + vClipMaskUv = vec3(tuv, source.screen_origin_target_index.z); +} diff --git a/webrender/src/frame.rs b/webrender/src/frame.rs index d605f87a29..34b60e2282 100644 --- a/webrender/src/frame.rs +++ b/webrender/src/frame.rs @@ -15,7 +15,7 @@ use resource_cache::ResourceCache; use scene::Scene; use std::collections::{HashMap, HashSet}; use std::hash::BuildHasherDefault; -use tiling::{AuxiliaryListsMap, FrameBuilder, FrameBuilderConfig, PrimitiveFlags}; +use tiling::{AuxiliaryListsMap, FrameBuilder, FrameBuilderConfig, LayerMap, PrimitiveFlags}; use util::MatrixHelpers; use webrender_traits::{AuxiliaryLists, PipelineId, Epoch, ScrollPolicy, ScrollLayerId}; use webrender_traits::{ClipRegion, ColorF, DisplayItem, StackingContext, FilterOp, MixBlendMode}; @@ -42,15 +42,11 @@ struct FlattenContext<'a> { builder: &'a mut FrameBuilder, } -pub type LayerMap = HashMap>; - // TODO: doc pub struct Frame { pub layers: LayerMap, pub pipeline_epoch_map: HashMap>, - pub pipeline_auxiliary_lists: HashMap>, + pub pipeline_auxiliary_lists: AuxiliaryListsMap, pub root_scroll_layer_id: Option, pending_scroll_offsets: HashMap<(PipelineId, ServoScrollRootId), LayerPoint>, id: FrameId, @@ -60,13 +56,15 @@ pub struct Frame { } trait DisplayListHelpers { - fn starting_stacking_context<'a>(&'a self) -> Option<&'a StackingContext>; + fn starting_stacking_context<'a>(&'a self) -> Option<(&'a StackingContext, &'a ClipRegion)>; } impl DisplayListHelpers for Vec { - fn starting_stacking_context<'a>(&'a self) -> Option<&'a StackingContext> { + fn starting_stacking_context<'a>(&'a self) -> Option<(&'a StackingContext, &'a ClipRegion)> { self.first().and_then(|item| match item.item { - SpecificDisplayItem::PushStackingContext(ref item) => Some(&item.stacking_context), + SpecificDisplayItem::PushStackingContext(ref specific_item) => { + Some((&specific_item.stacking_context, &item.clip)) + }, _ => None, }) } @@ -458,8 +456,8 @@ impl Frame { self.pipeline_epoch_map.insert(root_pipeline_id, root_pipeline.epoch); - let root_stacking_context = match display_list.starting_stacking_context() { - Some(stacking_context) => stacking_context, + let (root_stacking_context, root_clip) = match display_list.starting_stacking_context() { + Some(some) => some, None => { warn!("Pipeline display list does not start with a stacking context."); return; @@ -474,7 +472,7 @@ impl Frame { let root_fixed_layer_id = ScrollLayerId::create_fixed(root_pipeline_id); let root_viewport = LayerRect::new(LayerPoint::zero(), LayerSize::from_untyped(&root_pipeline.viewport_size)); let layer = Layer::new(&root_viewport, - LayerSize::from_untyped(&root_stacking_context.overflow.size), + LayerSize::from_untyped(&root_clip.main.size), &LayerToScrollTransform::identity(), root_pipeline_id); self.layers.insert(root_fixed_layer_id, layer.clone()); @@ -500,7 +498,8 @@ impl Frame { root_scroll_layer_id, LayerToScrollTransform::identity(), 0, - &root_stacking_context); + &root_stacking_context, + root_clip); } self.frame_builder = Some(frame_builder); @@ -563,7 +562,7 @@ impl Frame { LayerSize::new(content_size.width + clip.origin.x, content_size.height + clip.origin.y)); context.builder.push_layer(layer_rect, - layer_rect, + &ClipRegion::simple(&layer_rect.to_untyped()), LayerToScrollTransform::identity(), pipeline_id, current_scroll_layer_id, @@ -588,7 +587,8 @@ impl Frame { current_scroll_layer_id: ScrollLayerId, layer_relative_transform: LayerToScrollTransform, level: i32, - stacking_context: &StackingContext) { + stacking_context: &StackingContext, + clip_region: &ClipRegion) { // Avoid doing unnecessary work for empty stacking contexts. if traversal.current_stacking_context_empty() { traversal.skip_current_stacking_context(); @@ -628,14 +628,6 @@ impl Frame { ScrollPolicy::Scrollable => current_scroll_layer_id, }; - // TODO(gw): Int with overflow etc - context.builder.push_layer(LayerRect::from_untyped(&stacking_context.overflow), - LayerRect::from_untyped(&stacking_context.overflow), - transform, - pipeline_id, - scroll_layer_id, - &composition_operations); - if level == 0 { // Add a large white rectangle as the root display item if there is no root stacking // context background color. This is removed by the occlusion culling for most tiles, @@ -655,12 +647,33 @@ impl Frame { root_background_color = ColorF::new(1.0, 1.0, 1.0, 1.0); } - context.builder.add_solid_rectangle(&LayerRect::from_untyped(&stacking_context.bounds), - &ClipRegion::simple(&stacking_context.bounds), + // Adding a dummy layer for this rectangle in order to disable clipping. + let no_clip = ClipRegion::simple(&clip_region.main); + context.builder.push_layer(LayerRect::from_untyped(&clip_region.main), + &no_clip, + transform, + pipeline_id, + scroll_layer_id, + &composition_operations); + + //Note: we don't use the original clip region here, + // it's already processed by the layer we just pushed. + context.builder.add_solid_rectangle(&LayerRect::from_untyped(&clip_region.main), + &no_clip, &root_background_color, PrimitiveFlags::None); + + context.builder.pop_layer(); } + // TODO(gw): Int with overflow etc + context.builder.push_layer(LayerRect::from_untyped(&clip_region.main), + &clip_region, + transform, + pipeline_id, + scroll_layer_id, + &composition_operations); + self.flatten_items(traversal, pipeline_id, context, @@ -700,8 +713,8 @@ impl Frame { None => return, }; - let iframe_stacking_context = match display_list.starting_stacking_context() { - Some(stacking_context) => stacking_context, + let (iframe_stacking_context, iframe_clip) = match display_list.starting_stacking_context() { + Some(some) => some, None => { warn!("Pipeline display list does not start with a stacking context."); return; @@ -718,8 +731,8 @@ impl Frame { let iframe_fixed_layer_id = ScrollLayerId::create_fixed(pipeline_id); let iframe_scroll_layer_id = ScrollLayerId::root(pipeline_id); - let layer = Layer::new(&iframe_rect, - LayerSize::from_untyped(&iframe_stacking_context.overflow.size), + let layer = Layer::new(iframe_rect, + LayerSize::from_untyped(&iframe_clip.main.size), &transform, pipeline_id); self.layers.insert(iframe_fixed_layer_id, layer.clone()); @@ -735,7 +748,8 @@ impl Frame { iframe_scroll_layer_id, LayerToScrollTransform::identity(), 0, - &iframe_stacking_context); + &iframe_stacking_context, + iframe_clip); } fn flatten_items<'a>(&mut self, @@ -811,7 +825,8 @@ impl Frame { current_scroll_layer_id, layer_relative_transform, level + 1, - &info.stacking_context); + &info.stacking_context, + &item.clip); } SpecificDisplayItem::PushScrollLayer(ref info) => { self.flatten_scroll_layer(traversal, diff --git a/webrender/src/mask_cache.rs b/webrender/src/mask_cache.rs index 98cfbf72bc..0472607a38 100644 --- a/webrender/src/mask_cache.rs +++ b/webrender/src/mask_cache.rs @@ -3,16 +3,40 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use gpu_store::{GpuStore, GpuStoreAddress}; -use prim_store::{ClipData, GpuBlock32, PrimitiveClipSource, PrimitiveStore}; +use prim_store::{ClipData, GpuBlock32, PrimitiveStore}; use prim_store::{CLIP_DATA_GPU_SIZE, MASK_DATA_GPU_SIZE}; -use std::i32; use tiling::StackingContextIndex; use util::{rect_from_points_f, TransformedRect}; -use webrender_traits::{AuxiliaryLists, BorderRadius, ComplexClipRegion, ImageMask}; +use webrender_traits::{AuxiliaryLists, BorderRadius, ClipRegion, ComplexClipRegion, ImageMask}; use webrender_traits::{DeviceIntRect, DeviceIntSize, LayerRect, LayerToWorldTransform}; const MAX_COORD: f32 = 1.0e+16; -pub const OPAQUE_TASK_INDEX: i32 = i32::MAX; + +#[derive(Clone, Debug)] +pub enum ClipSource { + NoClip, + Complex(LayerRect, f32), + Region(ClipRegion), +} + +impl ClipSource { + pub fn to_rect(&self) -> Option { + match self { + &ClipSource::NoClip => None, + &ClipSource::Complex(rect, _) => Some(rect), + &ClipSource::Region(ref region) => Some(LayerRect::from_untyped(®ion.main)), + } + } +} +impl<'a> From<&'a ClipRegion> for ClipSource { + fn from(clip_region: &'a ClipRegion) -> ClipSource { + if clip_region.is_complex() { + ClipSource::Region(clip_region.clone()) + } else { + ClipSource::NoClip + } + } +} #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub struct ClipAddressRange { @@ -58,20 +82,20 @@ pub struct MaskCacheInfo { impl MaskCacheInfo { /// Create a new mask cache info. It allocates the GPU store data but leaves /// it unitialized for the following `update()` call to deal with. - pub fn new(source: &PrimitiveClipSource, + pub fn new(source: &ClipSource, layer_id: StackingContextIndex, clip_store: &mut GpuStore) -> Option { let mut clip_key = MaskCacheKey::empty(layer_id); let image = match source { - &PrimitiveClipSource::NoClip => None, - &PrimitiveClipSource::Complex(..) => { + &ClipSource::NoClip => None, + &ClipSource::Complex(..) => { clip_key.clip_range.item_count = 1; clip_key.clip_range.start = clip_store.alloc(CLIP_DATA_GPU_SIZE); None } - &PrimitiveClipSource::Region(ref region) => { + &ClipSource::Region(ref region) => { let num = region.complex.length; if num != 0 { clip_key.clip_range.item_count = num as u32; @@ -100,7 +124,7 @@ impl MaskCacheInfo { } pub fn update(&mut self, - source: &PrimitiveClipSource, + source: &ClipSource, transform: &LayerToWorldTransform, clip_store: &mut GpuStore, device_pixel_ratio: f32, @@ -110,8 +134,8 @@ impl MaskCacheInfo { let mut local_rect; let mut local_inner: Option; match source { - &PrimitiveClipSource::NoClip => unreachable!(), - &PrimitiveClipSource::Complex(rect, radius) => { + &ClipSource::NoClip => unreachable!(), + &ClipSource::Complex(rect, radius) => { let slice = clip_store.get_slice_mut(self.key.clip_range.start, CLIP_DATA_GPU_SIZE); let data = ClipData::uniform(rect, radius); PrimitiveStore::populate_clip_data(slice, data); @@ -121,7 +145,7 @@ impl MaskCacheInfo { BorderRadius::uniform(radius)) .get_inner_rect().map(|r|{ LayerRect::from_untyped(&r) }); } - &PrimitiveClipSource::Region(ref region) => { + &ClipSource::Region(ref region) => { local_rect = Some(LayerRect::from_untyped(&rect_from_points_f(-MAX_COORD, -MAX_COORD, MAX_COORD, MAX_COORD))); local_inner = match region.image_mask { Some(ref mask) if !mask.repeat => { diff --git a/webrender/src/prim_store.rs b/webrender/src/prim_store.rs index 14330f666b..e614051564 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -6,7 +6,7 @@ use app_units::Au; use euclid::Size2D; use gpu_store::{GpuStore, GpuStoreAddress}; use internal_types::SourceTexture; -use mask_cache::{MaskCacheInfo, MaskCacheKey}; +use mask_cache::{ClipSource, MaskCacheInfo}; use resource_cache::{ImageProperties, ResourceCache}; use std::mem; use std::usize; @@ -92,18 +92,11 @@ pub enum PrimitiveCacheKey { TextShadow(PrimitiveIndex), } -#[derive(Debug)] -pub enum PrimitiveClipSource { - NoClip, - Complex(LayerRect, f32), - Region(ClipRegion), -} - // TODO(gw): Pack the fields here better! #[derive(Debug)] pub struct PrimitiveMetadata { pub is_opaque: bool, - pub clip_source: Box, + pub clip_source: Box, pub clip_cache_info: Option, pub prim_kind: PrimitiveKind, pub cpu_prim_index: SpecificPrimitiveIndex, @@ -464,7 +457,7 @@ impl PrimitiveStore { pub fn add_primitive(&mut self, geometry: PrimitiveGeometry, - clip_source: Box, + clip_source: Box, clip_info: Option, container: PrimitiveContainer) -> PrimitiveIndex { let prim_index = self.cpu_metadata.len(); @@ -645,21 +638,34 @@ impl PrimitiveStore { PrimitiveIndex(prim_index) } + fn resolve_clip_cache_internal(gpu_data32: &mut GpuStore, + clip_info: &MaskCacheInfo, + resource_cache: &ResourceCache) { + if let (Some(gpu_address), Some(ref mask)) = (clip_info.key.image, clip_info.image) { + let cache_item = resource_cache.get_cached_image(mask.image, ImageRendering::Auto); + let mask_data = gpu_data32.get_slice_mut(gpu_address, MASK_DATA_GPU_SIZE); + mask_data[0] = GpuBlock32::from(ImageMaskData { + uv_rect: DeviceRect::new(cache_item.uv0, + DeviceSize::new(cache_item.uv1.x - cache_item.uv0.x, + cache_item.uv1.y - cache_item.uv0.y)), + local_rect: LayerRect::from_untyped(&mask.rect), + }); + } + } + + pub fn resolve_clip_cache(&mut self, + clip_info: &MaskCacheInfo, + resource_cache: &ResourceCache) { + Self::resolve_clip_cache_internal(&mut self.gpu_data32, clip_info, resource_cache) + } + pub fn resolve_primitives(&mut self, resource_cache: &ResourceCache) -> Vec { let mut deferred_resolves = Vec::new(); for prim_index in self.prims_to_resolve.drain(..) { let metadata = &mut self.cpu_metadata[prim_index.0]; - - if let Some(MaskCacheInfo{ key: MaskCacheKey { image: Some(gpu_address), .. }, image: Some(ref mask), .. }) = metadata.clip_cache_info { - let cache_item = resource_cache.get_cached_image(mask.image, ImageRendering::Auto); - let mask_data = self.gpu_data32.get_slice_mut(gpu_address, MASK_DATA_GPU_SIZE); - mask_data[0] = GpuBlock32::from(ImageMaskData { - uv_rect: DeviceRect::new(cache_item.uv0, - DeviceSize::new(cache_item.uv1.x - cache_item.uv0.x, - cache_item.uv1.y - cache_item.uv0.y)), - local_rect: LayerRect::from_untyped(&mask.rect), - }); + if let Some(ref clip_info) = metadata.clip_cache_info { + Self::resolve_clip_cache_internal(&mut self.gpu_data32, clip_info, resource_cache); } match metadata.prim_kind { @@ -761,12 +767,12 @@ impl PrimitiveStore { &self.cpu_bounding_rects[index.0] } - pub fn set_clip_source(&mut self, index: PrimitiveIndex, source: PrimitiveClipSource) { + pub fn set_clip_source(&mut self, index: PrimitiveIndex, source: ClipSource) { let metadata = &mut self.cpu_metadata[index.0]; let (rect, is_complex) = match source { - PrimitiveClipSource::NoClip => (None, false), - PrimitiveClipSource::Complex(rect, radius) => (Some(rect), radius > 0.0), - PrimitiveClipSource::Region(ref region) => (Some(LayerRect::from_untyped(®ion.main)), region.is_complex()), + ClipSource::NoClip => (None, false), + ClipSource::Complex(rect, radius) => (Some(rect), radius > 0.0), + ClipSource::Region(ref region) => (Some(LayerRect::from_untyped(®ion.main)), region.is_complex()), }; if let Some(rect) = rect { self.gpu_geometry.get_mut(GpuStoreAddress(index.0 as i32)) @@ -826,7 +832,7 @@ impl PrimitiveStore { &mut self.gpu_data32, device_pixel_ratio, auxiliary_lists); - if let &PrimitiveClipSource::Region(ClipRegion{ image_mask: Some(ref mask), .. }) = metadata.clip_source.as_ref() { + if let &ClipSource::Region(ClipRegion{ image_mask: Some(ref mask), .. }) = metadata.clip_source.as_ref() { resource_cache.request_image(mask.image, ImageRendering::Auto); prim_needs_resolve = true; } diff --git a/webrender/src/render_backend.rs b/webrender/src/render_backend.rs index f88a1d744c..fe7737e321 100644 --- a/webrender/src/render_backend.rs +++ b/webrender/src/render_backend.rs @@ -7,6 +7,7 @@ use frame::Frame; use internal_types::{FontTemplate, GLContextHandleWrapper, GLContextWrapper}; use internal_types::{SourceTexture, ResultMsg, RendererFrame}; use profiler::BackendProfileCounters; +use record; use resource_cache::ResourceCache; use scene::Scene; use std::collections::HashMap; @@ -19,7 +20,6 @@ use webrender_traits::{ApiMsg, AuxiliaryLists, BuiltDisplayList, IdNamespace, Im use webrender_traits::{FlushNotifier, RenderNotifier, RenderDispatcher, WebGLCommand, WebGLContextId}; use webrender_traits::{DeviceIntSize}; use webrender_traits::channel::{PayloadHelperMethods, PayloadReceiver, PayloadSender, MsgReceiver}; -use record; use tiling::FrameBuilderConfig; use offscreen_gl_context::GLContextDispatcher; diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index f09b575cb6..9e52fe9db6 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -334,11 +334,15 @@ pub struct Renderer { // draw intermediate results to cache targets. The results // of these shaders are then used by the primitive shaders. cs_box_shadow: LazilyCompiledShader, + cs_text_run: LazilyCompiledShader, + cs_blur: LazilyCompiledShader, + /// These are "cache clip shaders". These shaders are used to + /// draw clip instances into the cached clip mask. The results + /// of these shaders are also used by the primitive shaders. cs_clip_clear: LazilyCompiledShader, + cs_clip_copy: LazilyCompiledShader, cs_clip_rectangle: LazilyCompiledShader, cs_clip_image: LazilyCompiledShader, - cs_text_run: LazilyCompiledShader, - cs_blur: LazilyCompiledShader, // The are "primitive shaders". These shaders draw and blend // final results on screen. They are aware of tile boundaries. @@ -368,6 +372,7 @@ pub struct Renderer { max_prim_blends: usize, max_prim_composites: usize, max_cache_instances: usize, + max_clip_instances: usize, max_blurs: usize, notifier: Arc>>>, @@ -470,12 +475,31 @@ impl Renderer { &[], &mut device, options.precache_shaders); + let cs_text_run = LazilyCompiledShader::new(ShaderKind::Cache, + "cs_text_run", + max_cache_instances, + &[], + &mut device, + options.precache_shaders); + let cs_blur = LazilyCompiledShader::new(ShaderKind::Cache, + "cs_blur", + max_blurs, + &[], + &mut device, + options.precache_shaders); + let cs_clip_clear = LazilyCompiledShader::new(ShaderKind::ClipCache, "cs_clip_clear", max_clip_instances, &[], &mut device, options.precache_shaders); + let cs_clip_copy = LazilyCompiledShader::new(ShaderKind::ClipCache, + "cs_clip_copy", + max_clip_instances, + &[], + &mut device, + options.precache_shaders); let cs_clip_rectangle = LazilyCompiledShader::new(ShaderKind::ClipCache, "cs_clip_rectangle", max_clip_instances, @@ -488,18 +512,6 @@ impl Renderer { &[], &mut device, options.precache_shaders); - let cs_text_run = LazilyCompiledShader::new(ShaderKind::Cache, - "cs_text_run", - max_cache_instances, - &[], - &mut device, - options.precache_shaders); - let cs_blur = LazilyCompiledShader::new(ShaderKind::Cache, - "cs_blur", - max_blurs, - &[], - &mut device, - options.precache_shaders); let ps_rectangle = PrimitiveShader::new("ps_rectangle", max_ubo_vectors, @@ -709,11 +721,12 @@ impl Renderer { device_pixel_ratio: options.device_pixel_ratio, tile_clear_shader: tile_clear_shader, cs_box_shadow: cs_box_shadow, + cs_text_run: cs_text_run, + cs_blur: cs_blur, cs_clip_clear: cs_clip_clear, + cs_clip_copy: cs_clip_copy, cs_clip_rectangle: cs_clip_rectangle, cs_clip_image: cs_clip_image, - cs_text_run: cs_text_run, - cs_blur: cs_blur, ps_rectangle: ps_rectangle, ps_rectangle_clip: ps_rectangle_clip, ps_text_run: ps_text_run, @@ -731,6 +744,7 @@ impl Renderer { max_prim_blends: max_prim_blends, max_prim_composites: max_prim_composites, max_cache_instances: max_cache_instances, + max_clip_instances: max_clip_instances, max_blurs: max_blurs, notifier: notifier, flush_notifier: flush_notifier, @@ -1154,7 +1168,7 @@ impl Renderer { self.device.set_blend(false); if !target.clip_batcher.clears.is_empty() { let shader = self.cs_clip_clear.get(&mut self.device); - let max_prim_items = self.max_clear_tiles; + let max_prim_items = self.max_clip_instances; self.draw_ubo_batch(&target.clip_batcher.clears, shader, 1, @@ -1162,13 +1176,35 @@ impl Renderer { max_prim_items, &projection); } + // alternatively, copy the contents from another task + if !target.clip_batcher.copies.is_empty() { + let shader = self.cs_clip_copy.get(&mut self.device); + let max_prim_items = self.max_clip_instances; + self.draw_ubo_batch(&target.clip_batcher.copies, + shader, + 1, + &BatchTextures::no_texture(), + max_prim_items, + &projection); + } + // the fast path for clear + rect, which is just the rectangle without blending + if !target.clip_batcher.rectangles_noblend.is_empty() { + let shader = self.cs_clip_rectangle.get(&mut self.device); + let max_prim_items = self.max_clip_instances; + self.draw_ubo_batch(&target.clip_batcher.rectangles_noblend, + shader, + 1, + &BatchTextures::no_texture(), + max_prim_items, + &projection); + } // now switch to multiplicative blending self.device.set_blend(true); self.device.set_blend_mode_multiply(); - let max_prim_items = self.max_cache_instances; // draw rounded cornered rectangles if !target.clip_batcher.rectangles.is_empty() { let shader = self.cs_clip_rectangle.get(&mut self.device); + let max_prim_items = self.max_clip_instances; self.draw_ubo_batch(&target.clip_batcher.rectangles, shader, 1, @@ -1181,6 +1217,7 @@ impl Renderer { let texture_id = self.resolve_source_texture(mask_texture_id); self.device.bind_texture(TextureSampler::Mask, texture_id); let shader = self.cs_clip_image.get(&mut self.device); + let max_prim_items = self.max_clip_instances; self.draw_ubo_batch(items, shader, 1, diff --git a/webrender/src/tiling.rs b/webrender/src/tiling.rs index 22ebf8ed29..bb46f03a3f 100644 --- a/webrender/src/tiling.rs +++ b/webrender/src/tiling.rs @@ -11,12 +11,12 @@ use gpu_store::GpuStoreAddress; use internal_types::{ANGLE_FLOAT_TO_FIXED, LowLevelFilterOp, CompositionOp}; use internal_types::{BatchTextures, CacheTextureId, SourceTexture}; use layer::Layer; -use mask_cache::{OPAQUE_TASK_INDEX, MaskCacheKey, MaskCacheInfo}; +use mask_cache::{ClipSource, MaskCacheKey, MaskCacheInfo}; use prim_store::{PrimitiveGeometry, RectanglePrimitive, PrimitiveContainer}; use prim_store::{BorderPrimitiveCpu, BorderPrimitiveGpu, BoxShadowPrimitiveGpu}; use prim_store::{ImagePrimitiveCpu, ImagePrimitiveGpu, YuvImagePrimitiveCpu, YuvImagePrimitiveGpu, ImagePrimitiveKind, }; use prim_store::{PrimitiveKind, PrimitiveIndex, PrimitiveMetadata, TexelRect}; -use prim_store::{CLIP_DATA_GPU_SIZE, DeferredResolve, PrimitiveClipSource}; +use prim_store::{CLIP_DATA_GPU_SIZE, DeferredResolve}; use prim_store::{GradientPrimitiveCpu, GradientPrimitiveGpu, GradientType}; use prim_store::{PrimitiveCacheKey, TextRunPrimitiveGpu, TextRunPrimitiveCpu}; use prim_store::{PrimitiveStore, GpuBlock16, GpuBlock32, GpuBlock64, GpuBlock128}; @@ -65,8 +65,9 @@ trait AlphaBatchHelpers { prim_index: PrimitiveIndex, batch: &mut PrimitiveBatch, layer_index: StackingContextIndex, - task_index: i32, + task_index: RenderTaskIndex, tile_id: TileUniqueId, + base_mask_task_index: RenderTaskIndex, render_tasks: &RenderTaskCollection, pass_index: RenderPassIndex); } @@ -159,14 +160,6 @@ impl AlphaBatchHelpers for PrimitiveStore { transform: &LayerToWorldTransform, device_pixel_ratio: f32) -> bool { let metadata = self.get_metadata(prim_index); - - // bail out if the clip rectangle is outside of the tile - if let Some(ref clip_info) = metadata.clip_cache_info { - if !clip_info.outer_rect.intersects(tile_rect) { - return false; - } - } - match metadata.prim_kind { PrimitiveKind::Rectangle | PrimitiveKind::TextRun | @@ -189,8 +182,9 @@ impl AlphaBatchHelpers for PrimitiveStore { prim_index: PrimitiveIndex, batch: &mut PrimitiveBatch, layer_index: StackingContextIndex, - task_index: i32, + task_index: RenderTaskIndex, tile_id: TileUniqueId, + base_mask_task_index: RenderTaskIndex, render_tasks: &RenderTaskCollection, child_pass_index: RenderPassIndex) { let metadata = self.get_metadata(prim_index); @@ -203,13 +197,15 @@ impl AlphaBatchHelpers for PrimitiveStore { if render_tasks.has_dynamic_task(&cache_task_key, child_pass_index) { let cache_task_id = RenderTaskId::Dynamic(cache_task_key); let cache_task_index = render_tasks.get_task_index(&cache_task_id, child_pass_index); - cache_task_index.0 as i32 + cache_task_index } else { - OPAQUE_TASK_INDEX + base_mask_task_index } }, - None => OPAQUE_TASK_INDEX + None => base_mask_task_index }; + let task_index = task_index.0 as i32; + let clip_task_index = clip_task_index.0 as i32; match &mut batch.data { &mut PrimitiveBatchData::Blend(..) | @@ -374,7 +370,7 @@ pub struct RenderTargetIndex(usize); #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] struct RenderPassIndex(isize); -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)] pub struct RenderTaskIndex(usize); type TileUniqueId = usize; @@ -527,7 +523,7 @@ impl AlphaBatcher { child_pass_index: RenderPassIndex) { let mut batches: Vec = vec![]; for task in &mut self.tasks { - let task_index = render_tasks.get_static_task_index(&task.task_id).0 as i32; + let task_index = render_tasks.get_static_task_index(&task.task_id); let mut existing_batch_index = 0; for item in task.items.drain(..) { @@ -543,7 +539,8 @@ impl AlphaBatcher { let layer = &ctx.layer_store[sc_index.0]; let prim_metadata = ctx.prim_store.get_metadata(prim_index); let transform_kind = layer.xf_rect.as_ref().unwrap().kind; - let needs_clipping = prim_metadata.clip_cache_info.is_some(); + let needs_clipping = prim_metadata.clip_cache_info.is_some() || + ctx.layer_masks_tasks.get(&(task.tile_id, sc_index)).is_some(); let needs_blending = transform_kind == TransformedRectKind::Complex || !prim_metadata.is_opaque || needs_clipping; @@ -609,11 +606,16 @@ impl AlphaBatcher { debug_assert!(ok) } AlphaRenderItem::Primitive(sc_index, prim_index) => { + let mask_task_index = match ctx.layer_masks_tasks.get(&(task.tile_id, sc_index)) { + Some(ref mask_task_id) => render_tasks.get_task_index(mask_task_id, child_pass_index), + None => RenderTaskIndex(i32::MAX as usize), // special sentinel value recognized by the shader + }; ctx.prim_store.add_prim_to_batch(prim_index, batch, sc_index, task_index, task.tile_id, + mask_task_index, render_tasks, child_pass_index); } @@ -625,10 +627,19 @@ impl AlphaBatcher { } } +/// Batcher managing draw calls into the clip mask (in the RT cache). #[derive(Debug)] pub struct ClipBatcher { + /// Clear draws initialize the target area to full opacity (1.0) + /// So that the following primitive can be blended with MULtiplication. pub clears: Vec, + /// Copy draws get the existing mask from a parent layer. + pub copies: Vec, + /// A fast path for masks that only have clear + rectangle. + pub rectangles_noblend: Vec, + /// Rectangle draws fill up the rectangles with rounded corners. pub rectangles: Vec, + /// Image draws apply the image masking. pub images: HashMap>, } @@ -636,41 +647,65 @@ impl ClipBatcher { fn new() -> ClipBatcher { ClipBatcher { clears: Vec::new(), + copies: Vec::new(), + rectangles_noblend: Vec::new(), rectangles: Vec::new(), images: HashMap::new(), } } fn add(&mut self, - task_index: i32, + task_index: RenderTaskIndex, + base_task_index: Option, key: &MaskCacheKey, - task_info: &CacheMaskTask, - resource_cache: &ResourceCache) { + image: Option) { + + let mut start_rect_id = 0; // TODO: don't draw clipping instances covering the whole tile - self.clears.push(CacheClipInstance { - task_id: task_index, - layer_index: key.layer_id.0 as i32, - address: GpuStoreAddress(0), - pad: 0, - }); - self.rectangles.extend((0 .. key.clip_range.item_count as usize) + if let Some(layer_task_id) = base_task_index { + self.copies.push(CacheClipInstance { + task_id: task_index.0 as i32, + layer_index: key.layer_id.0 as i32, + address: GpuStoreAddress(0), + base_task_id: layer_task_id.0 as i32, + }); + } else if key.clip_range.item_count > 0 { + // draw the first rectangle without blending in order + // to avoid clearing the area first + start_rect_id = 1; + self.rectangles_noblend.push(CacheClipInstance { + task_id: task_index.0 as i32, + layer_index: key.layer_id.0 as i32, + address: key.clip_range.start, + base_task_id: 0, + }) + } else { + self.clears.push(CacheClipInstance { + task_id: task_index.0 as i32, + layer_index: key.layer_id.0 as i32, + address: GpuStoreAddress(0), + base_task_id: 0, + }); + } + + self.rectangles.extend((start_rect_id .. key.clip_range.item_count as usize) .map(|region_id| { + let offset = key.clip_range.start.0 + ((CLIP_DATA_GPU_SIZE * region_id) as i32); CacheClipInstance { - task_id: task_index, + task_id: task_index.0 as i32, layer_index: key.layer_id.0 as i32, - address: GpuStoreAddress(key.clip_range.start.0 + ((CLIP_DATA_GPU_SIZE * region_id) as i32)), - pad: 0, + address: GpuStoreAddress(offset), + base_task_id: 0, } })); - if let (Some(address), Some(mask_key)) = (key.image, task_info.image) { - let cache_item = resource_cache.get_cached_image(mask_key, ImageRendering::Auto); - self.images.entry(cache_item.texture_id) - .or_insert(Vec::new()) - .push(CacheClipInstance { - task_id: task_index, + if let (Some(address), Some(texture)) = (key.image, image) { + self.images.entry(texture) + .or_insert(Vec::new()) + .push(CacheClipInstance { + task_id: task_index.0 as i32, layer_index: key.layer_id.0 as i32, address: address, - pad: 0, + base_task_id: 0, }) } } @@ -688,6 +723,7 @@ struct RenderTargetContext<'a> { layer_store: &'a [StackingContext], prim_store: &'a PrimitiveStore, resource_cache: &'a ResourceCache, + layer_masks_tasks: HashMap<(TileUniqueId, StackingContextIndex), RenderTaskId>, } /// A render target represents a number of rendering operations on a surface. @@ -823,12 +859,19 @@ impl RenderTarget { } } RenderTaskKind::CacheMask(ref task_info) => { - let key = match task.id { - RenderTaskId::Dynamic(RenderTaskKey::CacheMask(ref key, _)) => key, + let (key, _tile_id) = match task.id { + RenderTaskId::Dynamic(RenderTaskKey::CacheMask(ref key, tile_id)) => (key, tile_id), _ => unreachable!() }; - let task_index = render_tasks.get_task_index(&task.id, pass_index).0 as i32; - self.clip_batcher.add(task_index, key, task_info, ctx.resource_cache); + let task_index = render_tasks.get_task_index(&task.id, pass_index); + let base_task_id = task_info.base_task_id.map(|ref task_id| + render_tasks.get_task_index(task_id, pass_index) + ); + let image = task_info.image.map(|image_key| { + let cache_item = ctx.resource_cache.get_cached_image(image_key, ImageRendering::Auto); + cache_item.texture_id + }); + self.clip_batcher.add(task_index, base_task_id, key, image); } } } @@ -951,8 +994,10 @@ pub struct AlphaRenderTask { pub struct CacheMaskTask { actual_rect: DeviceIntRect, image: Option, + base_task_id: Option, } +#[derive(Debug)] enum MaskResult { /// The mask is completely outside the region Outside, @@ -1011,6 +1056,7 @@ impl RenderTask { fn new_mask(actual_rect: DeviceIntRect, cache_info: &MaskCacheInfo, + dependent: Option<&RenderTask>, tile_id: TileUniqueId) -> MaskResult { let task_rect = match actual_rect.intersection(&cache_info.outer_rect) { @@ -1020,11 +1066,15 @@ impl RenderTask { }; MaskResult::Inside(RenderTask { id: RenderTaskId::Dynamic(RenderTaskKey::CacheMask(cache_info.key, tile_id)), - children: Vec::new(), + children: match dependent { + Some(task) => vec![task.clone()], + None => Vec::new(), + }, location: RenderTaskLocation::Dynamic(None, task_rect.size), kind: RenderTaskKind::CacheMask(CacheMaskTask { actual_rect: task_rect, image: cache_info.image.map(|mask| mask.image), + base_task_id: dependent.map(|task| task.id), }), }) } @@ -1328,7 +1378,7 @@ pub struct CacheClipInstance { task_id: i32, layer_index: i32, address: GpuStoreAddress, - pad: i32, + base_task_id: i32, } #[derive(Debug, Clone)] @@ -1488,8 +1538,9 @@ struct StackingContext { scroll_layer_id: ScrollLayerId, xf_rect: Option, composite_kind: CompositeKind, - local_clip_rect: LayerRect, tile_range: Option, + clip_source: ClipSource, + clip_cache_info: Option, } #[derive(Debug, Clone)] @@ -1622,8 +1673,26 @@ pub struct Frame { pub deferred_resolves: Vec, } -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] -pub struct ScreenTileIndex(usize); +#[derive(Debug)] +struct LayerMasksTasks { + task_ids: Vec>, +} + +impl LayerMasksTasks { + fn new() -> LayerMasksTasks { + LayerMasksTasks { + task_ids: Vec::new(), + } + } + + fn add(&mut self, index: StackingContextIndex, task_id: RenderTaskId) { + while self.task_ids.len() <= index.0 { + self.task_ids.push(None); + } + assert!(self.task_ids[index.0].is_none()); + self.task_ids[index.0] = Some(task_id); + } +} /// Some extra per-tile information stored for debugging purposes. #[derive(Debug)] @@ -1637,11 +1706,16 @@ struct CompiledScreenTile { main_render_task: RenderTask, required_pass_count: usize, info: CompiledScreenTileInfo, + unique_id: TileUniqueId, + layer_masks_tasks: LayerMasksTasks, } impl CompiledScreenTile { fn new(main_render_task: RenderTask, - info: CompiledScreenTileInfo) -> CompiledScreenTile { + info: CompiledScreenTileInfo, + unique_id: TileUniqueId, + layer_masks_tasks: LayerMasksTasks) + -> CompiledScreenTile { let mut required_pass_count = 0; main_render_task.max_depth(0, &mut required_pass_count); @@ -1649,10 +1723,13 @@ impl CompiledScreenTile { main_render_task: main_render_task, required_pass_count: required_pass_count, info: info, + unique_id: unique_id, + layer_masks_tasks: layer_masks_tasks, } } fn build(self, passes: &mut Vec) { + //println!("{:#?}", self.main_render_task); self.main_render_task.assign_to_passes(passes.len() - 1, passes); } @@ -1727,6 +1804,9 @@ impl ScreenTile { let mut sc_stack = Vec::new(); let mut current_task = RenderTask::new_alpha_batch(self.rect, ctx); let mut alpha_task_stack = Vec::new(); + let mut clip_task_stack = Vec::new(); + let mut num_clips_to_skip = 0; + let mut layer_masks_tasks = LayerMasksTasks::new(); for cmd in self.cmds { match cmd { @@ -1745,6 +1825,27 @@ impl ScreenTile { alpha_task_stack.push(prev_task); } } + + // Create a task for the layer mask, if needed, + // i.e. if there are rounded corners or image masks for the layer. + if let Some(ref clip_info) = layer.clip_cache_info { + let mask_opt = RenderTask::new_mask(self.rect, + clip_info, + clip_task_stack.last(), + ctx.tile_id); + match mask_opt { + MaskResult::Inside(mask_task) => { + current_task.children.push(mask_task.clone()); + clip_task_stack.push(mask_task); + num_clips_to_skip = 0; + } + _ => num_clips_to_skip += 1, + } + } + // Register the layer mask task within the context + if let Some(ref mask_task) = clip_task_stack.last() { + layer_masks_tasks.add(sc_index, mask_task.id); + } } TileCommand::PopLayer => { let sc_index = sc_stack.pop().unwrap(); @@ -1774,6 +1875,14 @@ impl ScreenTile { current_task = composite_task; } } + + if layer.clip_cache_info.is_some() { + if num_clips_to_skip > 0 { + num_clips_to_skip -= 1; + } else { + clip_task_stack.pop().unwrap(); + } + } } TileCommand::DrawPrimitive(prim_index) => { let sc_index = *sc_stack.last().unwrap(); @@ -1798,8 +1907,12 @@ impl ScreenTile { // Add a task to render the updated image mask if let Some(ref clip_info) = prim_metadata.clip_cache_info { - match RenderTask::new_mask(self.rect, clip_info, ctx.tile_id) { - MaskResult::Outside => panic!("Primitive be culled by `prim_affects_tile` already"), + let mask_opt = RenderTask::new_mask(self.rect, + clip_info, + clip_task_stack.last(), + ctx.tile_id); + match mask_opt { + MaskResult::Outside => panic!("Primitive be culled by `assign_prims_to_screen_tiles` already"), MaskResult::Covering => (), //do nothing MaskResult::Inside(task) => current_task.children.push(task), } @@ -1817,6 +1930,7 @@ impl ScreenTile { } debug_assert!(alpha_task_stack.is_empty()); + debug_assert!(clip_task_stack.is_empty()); let info = if self.is_simple { CompiledScreenTileInfo::SimpleAlpha(actual_prim_count) @@ -1825,7 +1939,7 @@ impl ScreenTile { }; current_task.location = RenderTaskLocation::Fixed(self.rect); - Some(CompiledScreenTile::new(current_task, info)) + Some(CompiledScreenTile::new(current_task, info, ctx.tile_id, layer_masks_tasks)) } } @@ -1858,9 +1972,9 @@ impl FrameBuilder { local_clip_rect: LayerRect::from_untyped(&clip_region.main), }; let clip_source = if clip_region.is_complex() { - PrimitiveClipSource::Region(clip_region.clone()) + ClipSource::Region(clip_region.clone()) } else { - PrimitiveClipSource::NoClip + ClipSource::NoClip }; let clip_info = MaskCacheInfo::new(&clip_source, StackingContextIndex(self.layer_store.len() - 1), @@ -1888,13 +2002,18 @@ impl FrameBuilder { pub fn push_layer(&mut self, rect: LayerRect, - clip_rect: LayerRect, + clip_region: &ClipRegion, transform: LayerToScrollTransform, pipeline_id: PipelineId, scroll_layer_id: ScrollLayerId, composition_operations: &[CompositionOp]) { let sc_index = StackingContextIndex(self.layer_store.len()); + let clip_source = clip_region.into(); + let clip_info = MaskCacheInfo::new(&clip_source, + sc_index, + &mut self.prim_store.gpu_data32); + let sc = StackingContext { local_rect: rect, local_transform: transform, @@ -1902,8 +2021,9 @@ impl FrameBuilder { pipeline_id: pipeline_id, xf_rect: None, composite_kind: CompositeKind::new(composition_operations), - local_clip_rect: clip_rect, tile_range: None, + clip_source: clip_source, + clip_cache_info: clip_info, }; self.layer_store.push(sc); @@ -2324,9 +2444,10 @@ impl FrameBuilder { let inv_layer_transform = layer.local_transform.inverse().unwrap(); let local_viewport_rect = as_scroll_parent_rect(&scroll_layer.combined_local_viewport_rect); let viewport_rect = inv_layer_transform.transform_rect(&local_viewport_rect); + let local_clip_rect = layer.clip_source.to_rect().unwrap_or(layer.local_rect); let layer_local_rect = layer.local_rect .intersection(&viewport_rect) - .and_then(|rect| rect.intersection(&layer.local_clip_rect)); + .and_then(|rect| rect.intersection(&local_clip_rect)); if let Some(layer_local_rect) = layer_local_rect { let layer_xf_rect = TransformedRect::new(&layer_local_rect, @@ -2363,6 +2484,21 @@ impl FrameBuilder { }); } } + + if let Some(ref mut clip_info) = layer.clip_cache_info { + let auxiliary_lists = auxiliary_lists_map.get(&layer.pipeline_id) + .expect("No auxiliary lists?"); + clip_info.update(&layer.clip_source, + &packed_layer.transform, + &mut self.prim_store.gpu_data32, + self.device_pixel_ratio, + auxiliary_lists); + if let ClipSource::Region(ClipRegion{ image_mask: Some(ref mask), .. }) = layer.clip_source { + resource_cache.request_image(mask.image, ImageRendering::Auto); + //Note: no need to add the layer for resolve, all layers get resolved + } + } + } &PrimitiveRunCmd::PrimitiveRun(prim_index, prim_count) => { let sc_index = layer_stack.last().unwrap(); @@ -2443,6 +2579,7 @@ impl FrameBuilder { screen_tiles: &mut Vec, x_tile_count: i32) { let mut layer_stack: Vec = Vec::new(); + let mut clip_rect_stack = Vec::new(); for cmd in &self.cmds { match cmd { @@ -2454,6 +2591,10 @@ impl FrameBuilder { continue; } + if let Some(ref clip_info) = layer.clip_cache_info { + clip_rect_stack.push(clip_info.outer_rect); + } + let tile_range = layer.tile_range.as_ref().unwrap(); for ly in tile_range.y0..tile_range.y1 { for lx in tile_range.x0..tile_range.x1 { @@ -2476,29 +2617,48 @@ impl FrameBuilder { for i in 0..prim_count { let prim_index = PrimitiveIndex(first_prim_index.0 + i); - if let &Some(p_rect) = self.prim_store.get_bounding_rect(prim_index) { - // TODO(gw): Ensure that certain primitives (such as background-image) only get - // assigned to tiles where their containing layer intersects with. - // Does this cause any problems / demonstrate other bugs? - // Restrict the tiles by clamping to the layer tile indices... - - let p_tile_x0 = p_rect.origin.x / SCREEN_TILE_SIZE; - let p_tile_y0 = p_rect.origin.y / SCREEN_TILE_SIZE; - let p_tile_x1 = (p_rect.origin.x + p_rect.size.width + SCREEN_TILE_SIZE - 1) / SCREEN_TILE_SIZE; - let p_tile_y1 = (p_rect.origin.y + p_rect.size.height + SCREEN_TILE_SIZE - 1) / SCREEN_TILE_SIZE; - - for py in cmp::max(p_tile_y0, tile_range.y0) .. cmp::min(p_tile_y1, tile_range.y1) { - for px in cmp::max(p_tile_x0, tile_range.x0) .. cmp::min(p_tile_x1, tile_range.x1) { - let tile = &mut screen_tiles[(py * x_tile_count + px) as usize]; - - // TODO(gw): Support narrow phase for 3d transform elements! - if xf_rect.kind == TransformedRectKind::Complex || - self.prim_store.prim_affects_tile(prim_index, - &tile.rect, - &packed_layer.transform, - self.device_pixel_ratio) { - tile.push_primitive(prim_index); - } + + // check the bounding box + let mut p_rect = match self.prim_store.get_bounding_rect(prim_index) { + &Some(r) => r, + &None => continue, + }; + // check the clip bounding rectangle + if let Some(ref clip_info) = self.prim_store.get_metadata(prim_index).clip_cache_info { + p_rect = match p_rect.intersection(&clip_info.outer_rect) { + Some(r) => r, + None => continue, + } + } else + // check the parent layer clip rectangle + if let Some(clip_rect) = clip_rect_stack.last() { + p_rect = match p_rect.intersection(clip_rect) { + Some(r) => r, + None => continue, + } + } + + // TODO(gw): Ensure that certain primitives (such as background-image) only get + // assigned to tiles where their containing layer intersects with. + // Does this cause any problems / demonstrate other bugs? + // Restrict the tiles by clamping to the layer tile indices... + + let p_tile_x0 = p_rect.origin.x / SCREEN_TILE_SIZE; + let p_tile_y0 = p_rect.origin.y / SCREEN_TILE_SIZE; + let p_tile_x1 = (p_rect.origin.x + p_rect.size.width + SCREEN_TILE_SIZE - 1) / SCREEN_TILE_SIZE; + let p_tile_y1 = (p_rect.origin.y + p_rect.size.height + SCREEN_TILE_SIZE - 1) / SCREEN_TILE_SIZE; + + for py in cmp::max(p_tile_y0, tile_range.y0) .. cmp::min(p_tile_y1, tile_range.y1) { + for px in cmp::max(p_tile_x0, tile_range.x0) .. cmp::min(p_tile_x1, tile_range.x1) { + let tile = &mut screen_tiles[(py * x_tile_count + px) as usize]; + + // TODO(gw): Support narrow phase for 3d transform elements! + if xf_rect.kind == TransformedRectKind::Complex || + self.prim_store.prim_affects_tile(prim_index, + &tile.rect, + &packed_layer.transform, + self.device_pixel_ratio) { + tile.push_primitive(prim_index); } } } @@ -2512,6 +2672,10 @@ impl FrameBuilder { continue; } + if layer.clip_cache_info.is_some() { + clip_rect_stack.pop(); + } + let tile_range = layer.tile_range.as_ref().unwrap(); for ly in tile_range.y0..tile_range.y1 { for lx in tile_range.x0..tile_range.x1 { @@ -2561,10 +2725,9 @@ impl FrameBuilder { geom.local_clip_rect = geom.local_rect; let clip_source = if scrollbar_prim.border_radius == 0.0 { - PrimitiveClipSource::NoClip + ClipSource::NoClip } else { - PrimitiveClipSource::Complex(geom.local_rect, - scrollbar_prim.border_radius) + ClipSource::Complex(geom.local_rect, scrollbar_prim.border_radius) }; self.prim_store.set_clip_source(scrollbar_prim.prim_index, clip_source); *self.prim_store.gpu_geometry.get_mut(GpuStoreAddress(scrollbar_prim.prim_index.0 as i32)) = geom; @@ -2663,15 +2826,23 @@ impl FrameBuilder { resource_cache.block_until_all_resources_added(); + for layer in self.layer_store.iter() { + if let Some(ref clip_info) = layer.clip_cache_info { + self.prim_store.resolve_clip_cache(clip_info, resource_cache); + } + } + + let deferred_resolves = self.prim_store.resolve_primitives(resource_cache); let mut passes = Vec::new(); if !compiled_screen_tiles.is_empty() { - let ctx = RenderTargetContext { + let mut ctx = RenderTargetContext { layer_store: &self.layer_store, prim_store: &self.prim_store, resource_cache: resource_cache, + layer_masks_tasks: HashMap::new(), }; // Do the allocations now, assigning each tile's tasks to a render @@ -2681,7 +2852,14 @@ impl FrameBuilder { index == max_passes_needed-1)); } - for compiled_screen_tile in compiled_screen_tiles { + for mut compiled_screen_tile in compiled_screen_tiles { + // Grab the mask task indices from the compile tile and append into the context map + for (i, mask_task_opt) in compiled_screen_tile.layer_masks_tasks.task_ids.drain(..).enumerate() { + if let Some(mask_task_id) = mask_task_opt { + let key = (compiled_screen_tile.unique_id, StackingContextIndex(i)); + ctx.layer_masks_tasks.insert(key, mask_task_id); + } + } compiled_screen_tile.build(&mut passes); } diff --git a/webrender_traits/src/display_item.rs b/webrender_traits/src/display_item.rs index 77aa52bbb6..b6c2409644 100644 --- a/webrender_traits/src/display_item.rs +++ b/webrender_traits/src/display_item.rs @@ -70,6 +70,10 @@ impl ClipRegion { image_mask: None, } } + + pub fn is_complex(&self) -> bool { + self.complex.length !=0 || self.image_mask.is_some() + } } impl ColorF { @@ -104,7 +108,7 @@ impl ComplexClipRegion { //TODO: move to `util` module? /// Return a maximum aligned rectangle that is fully inside the clip region. pub fn get_inner_rect(&self) -> Option> { - let k = 0.5; //rough, could be better + let k = 0.3; //roughly higher than `1.0 - sqrt(0.5)` let xl = self.rect.origin.x + k * self.radii.top_left.width.max(self.radii.bottom_left.width); let xr = self.rect.origin.x + self.rect.size.width - diff --git a/webrender_traits/src/display_list.rs b/webrender_traits/src/display_list.rs index 9a79207fe1..7f3f2dbe01 100644 --- a/webrender_traits/src/display_list.rs +++ b/webrender_traits/src/display_list.rs @@ -249,13 +249,15 @@ impl DisplayListBuilder { self.list.push(display_item); } - pub fn push_stacking_context(&mut self, stacking_context: StackingContext) { + pub fn push_stacking_context(&mut self, + stacking_context: StackingContext, + clip: ClipRegion) { let item = DisplayItem { item: SpecificDisplayItem::PushStackingContext(PushStackingContextDisplayItem { stacking_context: stacking_context }), rect: Rect::zero(), - clip: ClipRegion::simple(&Rect::zero()), + clip: clip, }; self.list.push(item); } diff --git a/webrender_traits/src/stacking_context.rs b/webrender_traits/src/stacking_context.rs index 7dd3e686a7..d47decc64a 100644 --- a/webrender_traits/src/stacking_context.rs +++ b/webrender_traits/src/stacking_context.rs @@ -9,7 +9,6 @@ use {FilterOp, MixBlendMode, ScrollPolicy, StackingContext}; impl StackingContext { pub fn new(scroll_policy: ScrollPolicy, bounds: Rect, - overflow: Rect, z_index: i32, transform: &Matrix4D, perspective: &Matrix4D, @@ -20,7 +19,6 @@ impl StackingContext { StackingContext { scroll_policy: scroll_policy, bounds: bounds, - overflow: overflow, z_index: z_index, transform: transform.clone(), perspective: perspective.clone(), diff --git a/webrender_traits/src/types.rs b/webrender_traits/src/types.rs index 7db1883e74..6835076e8c 100644 --- a/webrender_traits/src/types.rs +++ b/webrender_traits/src/types.rs @@ -187,12 +187,6 @@ pub struct ClipRegion { pub image_mask: Option, } -impl ClipRegion { - pub fn is_complex(&self) -> bool { - self.complex.length !=0 || self.image_mask.is_some() - } -} - #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] pub struct ComplexClipRegion { /// The boundaries of the rectangle. @@ -516,7 +510,6 @@ pub enum SpecificDisplayItem { pub struct StackingContext { pub scroll_policy: ScrollPolicy, pub bounds: Rect, - pub overflow: Rect, pub z_index: i32, pub transform: Matrix4D, pub perspective: Matrix4D,