From 422862a648e2b6e92c86b7a6c10bfb5aafdbb005 Mon Sep 17 00:00:00 2001 From: Glenn Watson Date: Tue, 10 Jan 2017 15:21:38 +1000 Subject: [PATCH] Use segments when drawing clip masks, decouple from tiles. --- webrender/res/clip_shared.glsl | 55 +++- webrender/res/cs_clip_copy.fs.glsl | 8 - webrender/res/cs_clip_copy.vs.glsl | 19 -- webrender/res/cs_clip_image.vs.glsl | 3 +- webrender/res/cs_clip_rectangle.vs.glsl | 3 +- webrender/res/prim_shared.glsl | 7 +- webrender/src/device.rs | 4 +- webrender/src/internal_types.rs | 2 +- webrender/src/prim_store.rs | 8 + webrender/src/renderer.rs | 21 +- webrender/src/scene.rs | 3 +- webrender/src/tiling.rs | 406 +++++++++++------------- webrender_traits/src/display_item.rs | 11 +- 13 files changed, 266 insertions(+), 284 deletions(-) delete mode 100644 webrender/res/cs_clip_copy.fs.glsl delete mode 100644 webrender/res/cs_clip_copy.vs.glsl diff --git a/webrender/res/clip_shared.glsl b/webrender/res/clip_shared.glsl index 3b406cbf8c..cc9018b105 100644 --- a/webrender/res/clip_shared.glsl +++ b/webrender/res/clip_shared.glsl @@ -5,16 +5,22 @@ #ifdef WR_VERTEX_SHADER +#define SEGMENT_ALL 0 +#define SEGMENT_CORNER_TL 1 +#define SEGMENT_CORNER_TR 2 +#define SEGMENT_CORNER_BL 3 +#define SEGMENT_CORNER_BR 4 + in int aClipRenderTaskIndex; in int aClipLayerIndex; in int aClipDataIndex; -in int aClipBaseTaskIndex; +in int aClipSegmentIndex; struct CacheClipInstance { int render_task_index; int layer_index; int data_index; - int base_task_index; + int segment_index; }; CacheClipInstance fetch_clip_item(int index) { @@ -23,7 +29,7 @@ CacheClipInstance fetch_clip_item(int index) { cci.render_task_index = aClipRenderTaskIndex; cci.layer_index = aClipLayerIndex; cci.data_index = aClipDataIndex; - cci.base_task_index = aClipBaseTaskIndex; + cci.segment_index = aClipSegmentIndex; return cci; } @@ -32,7 +38,8 @@ CacheClipInstance fetch_clip_item(int index) { // which is the intersection of all clip instances of a given primitive TransformVertexInfo write_clip_tile_vertex(vec4 local_clip_rect, Layer layer, - ClipArea area) { + ClipArea area, + int segment_index) { vec2 lp0_base = local_clip_rect.xy; vec2 lp1_base = local_clip_rect.xy + local_clip_rect.zw; @@ -40,15 +47,45 @@ TransformVertexInfo write_clip_tile_vertex(vec4 local_clip_rect, vec2 lp1 = clamp_rect(lp1_base, layer.local_clip_rect); vec4 clipped_local_rect = vec4(lp0, lp1 - lp0); - vec2 final_pos = mix(area.task_bounds.xy, area.task_bounds.zw, aPosition.xy); + vec2 outer_p0 = area.screen_origin_target_index.xy; + vec2 outer_p1 = outer_p0 + area.task_bounds.zw - area.task_bounds.xy; + vec2 inner_p0 = area.inner_rect.xy; + vec2 inner_p1 = area.inner_rect.zw; + + vec2 p0, p1; + switch (segment_index) { + case SEGMENT_ALL: + p0 = outer_p0; + p1 = outer_p1; + break; + case SEGMENT_CORNER_TL: + p0 = outer_p0; + p1 = inner_p0; + break; + case SEGMENT_CORNER_BL: + p0 = vec2(outer_p0.x, outer_p1.y); + p1 = vec2(inner_p0.x, inner_p1.y); + break; + case SEGMENT_CORNER_TR: + p0 = vec2(outer_p1.x, outer_p1.y); + p1 = vec2(inner_p1.x, inner_p1.y); + break; + case SEGMENT_CORNER_BR: + p0 = vec2(outer_p1.x, outer_p0.y); + p1 = vec2(inner_p1.x, inner_p0.y); + break; + } + + vec2 actual_pos = mix(p0, p1, aPosition.xy); + + vec4 layer_pos = get_layer_pos(actual_pos / uDevicePixelRatio, layer); // compute the point position in side the layer, in CSS space - vec2 clamped_pos = final_pos + area.screen_origin_target_index.xy - area.task_bounds.xy; - vec4 layer_pos = get_layer_pos(clamped_pos / uDevicePixelRatio, layer); + vec2 vertex_pos = actual_pos + area.task_bounds.xy - area.screen_origin_target_index.xy; - gl_Position = uTransform * vec4(final_pos, 0.0, 1); + gl_Position = uTransform * vec4(vertex_pos, 0.0, 1); - return TransformVertexInfo(layer_pos.xyw, clamped_pos, clipped_local_rect); + return TransformVertexInfo(layer_pos.xyw, actual_pos, clipped_local_rect); } #endif //WR_VERTEX_SHADER diff --git a/webrender/res/cs_clip_copy.fs.glsl b/webrender/res/cs_clip_copy.fs.glsl deleted file mode 100644 index 3d25b312d2..0000000000 --- a/webrender/res/cs_clip_copy.fs.glsl +++ /dev/null @@ -1,8 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -void main(void) { - float alpha = texelFetch(sCache, ivec3(vClipMaskUv), 0).a; - oFragColor = vec4(alpha, 0.0, 0.0, 1.0); -} diff --git a/webrender/res/cs_clip_copy.vs.glsl b/webrender/res/cs_clip_copy.vs.glsl deleted file mode 100644 index 2b738dc791..0000000000 --- a/webrender/res/cs_clip_copy.vs.glsl +++ /dev/null @@ -1,19 +0,0 @@ -#line 1 -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -void main(void) { - CacheClipInstance cci = fetch_clip_item(gl_InstanceID); - ClipArea area = fetch_clip_area(cci.render_task_index); - ClipArea source = fetch_clip_area(cci.base_task_index); - - vec2 final_pos = mix(area.task_bounds.xy, area.task_bounds.zw, aPosition.xy); - - gl_Position = uTransform * vec4(final_pos, 0.0, 1.0); - - // convert to the source task space via the screen space - vec2 tuv = final_pos - area.task_bounds.xy + area.screen_origin_target_index.xy + - source.task_bounds.xy - source.screen_origin_target_index.xy; - vClipMaskUv = vec3(tuv, source.screen_origin_target_index.z); -} diff --git a/webrender/res/cs_clip_image.vs.glsl b/webrender/res/cs_clip_image.vs.glsl index 3d6a5a2dbf..1293cf70f4 100644 --- a/webrender/res/cs_clip_image.vs.glsl +++ b/webrender/res/cs_clip_image.vs.glsl @@ -28,7 +28,8 @@ void main(void) { TransformVertexInfo vi = write_clip_tile_vertex(local_rect, layer, - area); + area, + cci.segment_index); vLocalRect = vi.clipped_local_rect; vPos = vi.local_pos; diff --git a/webrender/res/cs_clip_rectangle.vs.glsl b/webrender/res/cs_clip_rectangle.vs.glsl index e81ab59e33..15529e1db6 100644 --- a/webrender/res/cs_clip_rectangle.vs.glsl +++ b/webrender/res/cs_clip_rectangle.vs.glsl @@ -65,7 +65,8 @@ void main(void) { TransformVertexInfo vi = write_clip_tile_vertex(local_rect, layer, - area); + area, + cci.segment_index); vLocalRect = vi.clipped_local_rect; vPos = vi.local_pos; diff --git a/webrender/res/prim_shared.glsl b/webrender/res/prim_shared.glsl index 1695c29b50..f34795a158 100644 --- a/webrender/res/prim_shared.glsl +++ b/webrender/res/prim_shared.glsl @@ -38,7 +38,7 @@ varying vec3 vClipMaskUv; #ifdef WR_VERTEX_SHADER #define VECS_PER_LAYER 13 -#define VECS_PER_RENDER_TASK 2 +#define VECS_PER_RENDER_TASK 3 #define VECS_PER_PRIM_GEOM 2 #define GRADIENT_HORIZONTAL 0 @@ -128,6 +128,7 @@ Layer fetch_layer(int index) { struct RenderTaskData { vec4 data0; vec4 data1; + vec4 data2; }; RenderTaskData fetch_render_task(int index) { @@ -137,6 +138,7 @@ RenderTaskData fetch_render_task(int index) { task.data0 = texelFetchOffset(sRenderTasks, uv, 0, ivec2(0, 0)); task.data1 = texelFetchOffset(sRenderTasks, uv, 0, ivec2(1, 0)); + task.data2 = texelFetchOffset(sRenderTasks, uv, 0, ivec2(2, 0)); return task; } @@ -159,6 +161,7 @@ Tile fetch_tile(int index) { struct ClipArea { vec4 task_bounds; vec4 screen_origin_target_index; + vec4 inner_rect; }; ClipArea fetch_clip_area(int index) { @@ -167,10 +170,12 @@ ClipArea fetch_clip_area(int index) { if (index == 0x7FFFFFFF) { //special sentinel task index area.task_bounds = vec4(0.0, 0.0, 0.0, 0.0); area.screen_origin_target_index = vec4(0.0, 0.0, 0.0, 0.0); + area.inner_rect = vec4(0.0); } else { RenderTaskData task = fetch_render_task(index); area.task_bounds = task.data0; area.screen_origin_target_index = task.data1; + area.inner_rect = task.data2; } return area; diff --git a/webrender/src/device.rs b/webrender/src/device.rs index 8bef2deef3..5af069c0be 100644 --- a/webrender/src/device.rs +++ b/webrender/src/device.rs @@ -268,7 +268,7 @@ impl VertexFormat { for (i, &attrib) in [ClipAttribute::RenderTaskIndex, ClipAttribute::LayerIndex, ClipAttribute::DataIndex, - ClipAttribute::BaseTaskIndex, + ClipAttribute::SegmentIndex, ].into_iter().enumerate() { gl::enable_vertex_attrib_array(attrib as gl::GLuint); gl::vertex_attrib_divisor(attrib as gl::GLuint, 1); @@ -401,7 +401,7 @@ impl Program { gl::bind_attrib_location(self.id, ClipAttribute::RenderTaskIndex as gl::GLuint, "aClipRenderTaskIndex"); gl::bind_attrib_location(self.id, ClipAttribute::LayerIndex as gl::GLuint, "aClipLayerIndex"); gl::bind_attrib_location(self.id, ClipAttribute::DataIndex as gl::GLuint, "aClipDataIndex"); - gl::bind_attrib_location(self.id, ClipAttribute::BaseTaskIndex as gl::GLuint, "aClipBaseTaskIndex"); + gl::bind_attrib_location(self.id, ClipAttribute::SegmentIndex as gl::GLuint, "aClipSegmentIndex"); gl::link_program(self.id); if gl::get_program_iv(self.id, gl::LINK_STATUS) == (0 as gl::GLint) { diff --git a/webrender/src/internal_types.rs b/webrender/src/internal_types.rs index f415ed4eda..2d05b7c1dc 100644 --- a/webrender/src/internal_types.rs +++ b/webrender/src/internal_types.rs @@ -266,7 +266,7 @@ pub enum ClipAttribute { RenderTaskIndex, LayerIndex, DataIndex, - BaseTaskIndex, + SegmentIndex, } #[derive(Debug, Clone, Copy)] diff --git a/webrender/src/prim_store.rs b/webrender/src/prim_store.rs index c36343b033..7af239c6ca 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -112,6 +112,7 @@ pub struct PrimitiveMetadata { // that implements a 2-pass separable blur on a // text run. pub render_task: Option, + pub clip_task: Option, } #[derive(Debug, Clone)] @@ -490,6 +491,7 @@ impl PrimitiveStore { gpu_data_address: GpuStoreAddress(0), gpu_data_count: 0, render_task: None, + clip_task: None, }; metadata @@ -509,6 +511,7 @@ impl PrimitiveStore { gpu_data_address: gpu_glyphs_address, gpu_data_count: text_cpu.glyph_range.length as i32, render_task: None, + clip_task: None, }; self.cpu_text_runs.push(text_cpu); @@ -529,6 +532,7 @@ impl PrimitiveStore { gpu_data_address: GpuStoreAddress(0), gpu_data_count: 0, render_task: None, + clip_task: None, }; self.cpu_images.push(image_cpu); @@ -547,6 +551,7 @@ impl PrimitiveStore { gpu_data_address: GpuStoreAddress(0), gpu_data_count: 0, render_task: None, + clip_task: None, }; self.cpu_yuv_images.push(image_cpu); @@ -565,6 +570,7 @@ impl PrimitiveStore { gpu_data_address: GpuStoreAddress(0), gpu_data_count: 0, render_task: None, + clip_task: None, }; self.cpu_borders.push(border_cpu); @@ -584,6 +590,7 @@ impl PrimitiveStore { gpu_data_address: gpu_stops_address, gpu_data_count: gradient_cpu.stops_range.length as i32, render_task: None, + clip_task: None, }; self.cpu_gradients.push(gradient_cpu); @@ -628,6 +635,7 @@ impl PrimitiveStore { gpu_data_address: gpu_data_address, gpu_data_count: instance_rects.len() as i32, render_task: Some(render_task), + clip_task: None, }; for rect in instance_rects { diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index f47e34a202..7f07509ee7 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -288,7 +288,6 @@ pub struct Renderer { /// These are "cache clip shaders". These shaders are used to /// draw clip instances into the cached clip mask. The results /// of these shaders are also used by the primitive shaders. - cs_clip_copy: LazilyCompiledShader, cs_clip_rectangle: LazilyCompiledShader, cs_clip_image: LazilyCompiledShader, @@ -421,11 +420,6 @@ impl Renderer { &mut device, options.precache_shaders); - let cs_clip_copy = LazilyCompiledShader::new(ShaderKind::ClipCache, - "cs_clip_copy", - &[], - &mut device, - options.precache_shaders); let cs_clip_rectangle = LazilyCompiledShader::new(ShaderKind::ClipCache, "cs_clip_rectangle", &[], @@ -619,7 +613,6 @@ impl Renderer { cs_box_shadow: cs_box_shadow, cs_text_run: cs_text_run, cs_blur: cs_blur, - cs_clip_copy: cs_clip_copy, cs_clip_rectangle: cs_clip_rectangle, cs_clip_image: cs_clip_image, ps_rectangle: ps_rectangle, @@ -1060,7 +1053,7 @@ impl Renderer { // tasks can assume that pixels are transparent if not // rendered. (This is relied on by the compositing support // for mix-blend-mode etc). - [1.0, 0.0, 0.0, 0.0], + [1.0, 1.0, 1.0, 0.0], Matrix4D::ortho(0.0, target_size.width, 0.0, @@ -1137,17 +1130,7 @@ impl Renderer { { let _gm = self.gpu_profile.add_marker(GPU_TAG_CACHE_CLIP); let vao = self.clip_vao_id; - // Optionally, copy the contents from another task - if !target.clip_batcher.copies.is_empty() { - self.device.set_blend(false); - let shader = self.cs_clip_copy.get(&mut self.device); - self.draw_instanced_batch(&target.clip_batcher.copies, - vao, - shader, - &BatchTextures::no_texture(), - &projection); - } - // now switch to multiplicative blending + // switch to multiplicative blending self.device.set_blend(true); self.device.set_blend_mode_multiply(); // draw rounded cornered rectangles diff --git a/webrender/src/scene.rs b/webrender/src/scene.rs index bcf7213342..703c4295ef 100644 --- a/webrender/src/scene.rs +++ b/webrender/src/scene.rs @@ -7,8 +7,7 @@ use std::collections::HashMap; use std::hash::BuildHasherDefault; use tiling::AuxiliaryListsMap; use webrender_traits::{AuxiliaryLists, BuiltDisplayList, PipelineId, Epoch, ColorF}; -use webrender_traits::{DisplayItem, SpecificDisplayItem, StackingContext}; -use webrender_traits::LayerSize; +use webrender_traits::{DisplayItem, LayerSize}; /// A representation of the layout within the display port for a given document or iframe. #[derive(Debug)] diff --git a/webrender/src/tiling.rs b/webrender/src/tiling.rs index 449728b72d..bd693c9f04 100644 --- a/webrender/src/tiling.rs +++ b/webrender/src/tiling.rs @@ -42,15 +42,11 @@ use webrender_traits::{LayerRect, LayerPoint, LayerSize}; use webrender_traits::{LayerToScrollTransform, LayerToWorldTransform, WorldToLayerTransform}; use webrender_traits::{WorldPoint4D, ScrollLayerPixel, as_scroll_parent_rect}; -// Removes the clip task dependencies and instead -// draws all the clip instances that affect a primitive -const CLIP_TASK_COLLAPSE: bool = true; - // Special sentinel value recognized by the shader. It is considered to be // a dummy task that doesn't mask out anything. const OPAQUE_TASK_INDEX: RenderTaskIndex = RenderTaskIndex(i32::MAX as usize); -const FLOATS_PER_RENDER_TASK_INFO: usize = 8; +const FLOATS_PER_RENDER_TASK_INFO: usize = 12; pub type LayerMap = HashMap bool { - //TODO: remove clone - self.dynamic_tasks.contains_key(&(key.clone(), pass_index)) - } } #[derive(Debug, Clone)] @@ -517,7 +500,6 @@ struct AlphaBatchTask { task_id: RenderTaskId, opaque_items: Vec, alpha_items: Vec, - tile_id: TileUniqueId, } /// Encapsulates the logic of building batches for items that are blended. @@ -565,8 +547,7 @@ impl AlphaBatcher { let layer = &ctx.layer_store[sc_index.0]; let prim_metadata = ctx.prim_store.get_metadata(prim_index); let transform_kind = layer.xf_rect.as_ref().unwrap().kind; - let needs_clipping = prim_metadata.clip_cache_info.is_some() || - ctx.layer_masks_tasks.get(&(task.tile_id, sc_index)).is_some(); + let needs_clipping = prim_metadata.clip_task.is_some(); let needs_blending = transform_kind == TransformedRectKind::Complex || !prim_metadata.is_opaque || needs_clipping; @@ -634,16 +615,10 @@ impl AlphaBatcher { debug_assert!(ok) } &AlphaRenderItem::Primitive(sc_index, prim_index, z) => { - let mask_task_index = match ctx.layer_masks_tasks.get(&(task.tile_id, sc_index)) { - Some(ref mask_task_id) => render_tasks.get_task_index(mask_task_id, child_pass_index), - None => OPAQUE_TASK_INDEX, - }; ctx.prim_store.add_prim_to_batch(prim_index, batch, sc_index, task_index, - task.tile_id, - mask_task_index, render_tasks, child_pass_index, z); @@ -660,8 +635,7 @@ impl AlphaBatcher { let layer = &ctx.layer_store[sc_index.0]; let prim_metadata = ctx.prim_store.get_metadata(prim_index); let transform_kind = layer.xf_rect.as_ref().unwrap().kind; - let needs_clipping = prim_metadata.clip_cache_info.is_some() || - ctx.layer_masks_tasks.get(&(task.tile_id, sc_index)).is_some(); + let needs_clipping = prim_metadata.clip_task.is_some(); let needs_blending = transform_kind == TransformedRectKind::Complex || !prim_metadata.is_opaque || needs_clipping; @@ -712,16 +686,10 @@ impl AlphaBatcher { &AlphaRenderItem::Composite(..) => unreachable!(), &AlphaRenderItem::Blend(..) => unreachable!(), &AlphaRenderItem::Primitive(sc_index, prim_index, z) => { - let mask_task_index = match ctx.layer_masks_tasks.get(&(task.tile_id, sc_index)) { - Some(ref mask_task_id) => render_tasks.get_task_index(mask_task_id, child_pass_index), - None => OPAQUE_TASK_INDEX, - }; ctx.prim_store.add_prim_to_batch(prim_index, batch, sc_index, task_index, - task.tile_id, - mask_task_index, render_tasks, child_pass_index, z); @@ -738,8 +706,6 @@ impl AlphaBatcher { /// Batcher managing draw calls into the clip mask (in the RT cache). #[derive(Debug)] pub struct ClipBatcher { - /// Copy draws get the existing mask from a parent layer. - pub copies: Vec, /// Rectangle draws fill up the rectangles with rounded corners. pub rectangles: Vec, /// Image draws apply the image masking. @@ -749,7 +715,6 @@ pub struct ClipBatcher { impl ClipBatcher { fn new() -> ClipBatcher { ClipBatcher { - copies: Vec::new(), rectangles: Vec::new(), images: HashMap::new(), } @@ -757,36 +722,54 @@ impl ClipBatcher { fn add(&mut self, task_index: RenderTaskIndex, - base_task_index: Option, clips: &[(StackingContextIndex, MaskCacheInfo)], - resource_cache: &ResourceCache) { + resource_cache: &ResourceCache, + geometry_kind: MaskGeometryKind) { - // TODO: don't draw clipping instances covering the whole tile for &(layer_id, ref info) in clips.iter() { let instance = CacheClipInstance { task_id: task_index.0 as i32, layer_index: layer_id.0 as i32, address: GpuStoreAddress(0), - base_task_id: 0, + segment: 0, }; - // copy on the first clip only - if info as *const _ == &clips[0].1 as *const _ { - if let Some(layer_task_id) = base_task_index { - self.copies.push(CacheClipInstance { - base_task_id: layer_task_id.0 as i32, - ..instance - }); - } - } - self.rectangles.extend((0 .. info.clip_range.item_count as usize) - .map(|region_id| { - let offset = info.clip_range.start.0 + ((CLIP_DATA_GPU_SIZE * region_id) as i32); - CacheClipInstance { - address: GpuStoreAddress(offset), - ..instance + for clip_index in 0..info.clip_range.item_count as usize { + let offset = info.clip_range.start.0 + ((CLIP_DATA_GPU_SIZE * clip_index) as i32); + match geometry_kind { + MaskGeometryKind::Default => { + self.rectangles.push(CacheClipInstance { + address: GpuStoreAddress(offset), + segment: MaskSegment::All as i32, + ..instance + }); + } + MaskGeometryKind::CornersOnly => { + self.rectangles.extend(&[ + CacheClipInstance { + address: GpuStoreAddress(offset), + segment: MaskSegment::Corner_TopLeft as i32, + ..instance + }, + CacheClipInstance { + address: GpuStoreAddress(offset), + segment: MaskSegment::Corner_TopRight as i32, + ..instance + }, + CacheClipInstance { + address: GpuStoreAddress(offset), + segment: MaskSegment::Corner_BottomLeft as i32, + ..instance + }, + CacheClipInstance { + address: GpuStoreAddress(offset), + segment: MaskSegment::Corner_BottomRight as i32, + ..instance + }, + ]); + } } - })); + } if let Some((ref mask, address)) = info.image { let cache_item = resource_cache.get_cached_image(mask.image, ImageRendering::Auto); @@ -805,7 +788,6 @@ impl ClipBatcher { struct CompileTileContext<'a> { layer_store: &'a [StackingContext], prim_store: &'a PrimitiveStore, - tile_id: TileUniqueId, render_task_id_counter: AtomicUsize, } @@ -813,7 +795,6 @@ struct RenderTargetContext<'a> { layer_store: &'a [StackingContext], prim_store: &'a PrimitiveStore, resource_cache: &'a ResourceCache, - layer_masks_tasks: HashMap<(TileUniqueId, StackingContextIndex), RenderTaskId>, } /// A render target represents a number of rendering operations on a surface. @@ -873,7 +854,6 @@ impl RenderTarget { task_id: task.id, opaque_items: info.opaque_items, alpha_items: info.alpha_items, - tile_id: info.tile_id, }); } RenderTaskKind::VerticalBlur(_, prim_index) => { @@ -958,12 +938,10 @@ impl RenderTarget { } RenderTaskKind::CacheMask(ref task_info) => { let task_index = render_tasks.get_task_index(&task.id, pass_index); - let base_task_id = task_info.base_task_id.map(|ref task_id| - render_tasks.get_task_index(task_id, pass_index) - ); - self.clip_batcher.add(task_index, base_task_id, + self.clip_batcher.add(task_index, &task_info.clips, - &ctx.resource_cache); + &ctx.resource_cache, + task_info.geometry_kind); } } } @@ -1005,7 +983,7 @@ impl RenderPass { let mut new_target = RenderTarget::new(); let origin = new_target.page_allocator .allocate(&size) - .expect("Each render task must allocate <= size of one target!"); + .expect(&format!("Each render task must allocate <= size of one target! ({:?})", size)); targets.push(new_target); origin } @@ -1080,22 +1058,40 @@ pub struct AlphaRenderTask { actual_rect: DeviceIntRect, opaque_items: Vec, alpha_items: Vec, - tile_id: TileUniqueId, +} + +#[derive(Debug, Copy, Clone)] +#[repr(C)] +enum MaskSegment { + // This must match the SEGMENT_ values + // in clip_shared.glsl! + All = 0, + Corner_TopLeft, + Corner_TopRight, + Corner_BottomLeft, + Corner_BottomRight, +} + +#[derive(Debug, Copy, Clone)] +#[repr(C)] +enum MaskGeometryKind { + Default, // Draw the entire rect + CornersOnly, // Draw the corners (simple axis aligned mask) + // TODO(gw): Add more types here (e.g. 4 rectangles outside the inner rect) } #[derive(Debug, Clone)] pub struct CacheMaskTask { actual_rect: DeviceIntRect, - base_task_id: Option, + inner_rect: DeviceIntRect, clips: Vec<(StackingContextIndex, MaskCacheInfo)>, + geometry_kind: MaskGeometryKind, } #[derive(Debug)] enum MaskResult { /// The mask is completely outside the region Outside, - /// The mask completely covers the region - Covering, /// The mask is inside and needs to be processed Inside(RenderTask), } @@ -1132,7 +1128,6 @@ impl RenderTask { actual_rect: actual_rect, alpha_items: Vec::new(), opaque_items: Vec::new(), - tile_id: ctx.tile_id, }), } } @@ -1149,46 +1144,60 @@ impl RenderTask { } fn new_mask(actual_rect: DeviceIntRect, - dependent: Option<&RenderTask>, mask_key: MaskCacheKey, - top_clip: Option<(StackingContextIndex, &MaskCacheInfo)>, - layer_clips: &[(StackingContextIndex, MaskCacheInfo)], - tile_id: TileUniqueId) + clips: &[(StackingContextIndex, MaskCacheInfo)], + layers: &[StackingContext]) -> MaskResult { - - let extra = top_clip.map(|c| (c.0, c.1.clone())); + if clips.is_empty() { + return MaskResult::Outside; + } // We scan through the clip stack and detect if our actual rectangle // is in the intersection of all of all the outer bounds, // and if it's completely inside the intersection of all of the inner bounds. - let result = layer_clips.iter().chain(extra.as_ref()) - .fold(Some((actual_rect, true)), |current, clip| { - current.and_then(|(rect, covering)| - rect.intersection(&clip.1.outer_rect) - .map(|r| (r, covering & clip.1.inner_rect.contains_rect(&actual_rect)))) + let result = clips.iter() + .fold(Some(actual_rect), |current, clip| { + current.and_then(|rect| rect.intersection(&clip.1.outer_rect)) }); let task_rect = match result { None => return MaskResult::Outside, - Some((_, true)) => return MaskResult::Covering, - Some((rect, false)) => rect, + Some(rect) => rect, }; - let clips = layer_clips.iter() - .map(|lc| lc.clone()) - .chain(extra) - .collect(); + + let inner_rect = clips.iter() + .fold(Some(task_rect), |current, clip| { + current.and_then(|rect| rect.intersection(&clip.1.inner_rect)) + }); + + // TODO(gw): This optimization is very conservative for now. + // For now, only draw optimized geometry if it is + // a single aligned rect mask with rounded corners. + // In the future, we'll expand this to handle the + // more complex types of clip mask geometry. + let mut geometry_kind = MaskGeometryKind::Default; + + if inner_rect.is_some() && clips.len() == 1 { + let (sc_index, ref clip_info) = clips[0]; + + if clip_info.image.is_none() && + clip_info.clip_range.item_count == 1 && + layers[sc_index.0].xf_rect.as_ref().unwrap().kind == TransformedRectKind::AxisAligned { + geometry_kind = MaskGeometryKind::CornersOnly; + } + } + + let inner_rect = inner_rect.unwrap_or(DeviceIntRect::zero()); MaskResult::Inside(RenderTask { - id: RenderTaskId::Dynamic(RenderTaskKey::CacheMask(mask_key, tile_id)), - children: match dependent { - Some(task) => vec![task.clone()], - None => Vec::new(), - }, + id: RenderTaskId::Dynamic(RenderTaskKey::CacheMask(mask_key)), + children: Vec::new(), location: RenderTaskLocation::Dynamic(None, task_rect.size), kind: RenderTaskKind::CacheMask(CacheMaskTask { actual_rect: task_rect, - base_task_id: dependent.map(|task| task.id), - clips: clips, + inner_rect: inner_rect, + clips: clips.to_vec(), + geometry_kind: geometry_kind, }), }) } @@ -1264,6 +1273,10 @@ impl RenderTask { task.actual_rect.size.height as f32, target_index.0 as f32, 0.0, + 0.0, + 0.0, + 0.0, + 0.0, ], } } @@ -1278,6 +1291,10 @@ impl RenderTask { 0.0, 0.0, 0.0, + 0.0, + 0.0, + 0.0, + 0.0, ], } } @@ -1293,6 +1310,10 @@ impl RenderTask { task.actual_rect.origin.y as f32, target_index.0 as f32, 0.0, + task.inner_rect.origin.x as f32, + task.inner_rect.origin.y as f32, + (task.inner_rect.origin.x + task.inner_rect.size.width) as f32, + (task.inner_rect.origin.y + task.inner_rect.size.height) as f32, ], } } @@ -1308,6 +1329,10 @@ impl RenderTask { blur_radius.0 as f32, 0.0, 0.0, + 0.0, + 0.0, + 0.0, + 0.0, ] } } @@ -1483,7 +1508,7 @@ pub struct CacheClipInstance { task_id: i32, layer_index: i32, address: GpuStoreAddress, - base_task_id: i32, + segment: i32, } #[derive(Debug, Clone)] @@ -1797,27 +1822,6 @@ pub struct Frame { pub deferred_resolves: Vec, } -#[derive(Debug)] -struct LayerMasksTasks { - task_ids: Vec>, -} - -impl LayerMasksTasks { - fn new() -> LayerMasksTasks { - LayerMasksTasks { - task_ids: Vec::new(), - } - } - - fn add(&mut self, index: StackingContextIndex, task_id: RenderTaskId) { - while self.task_ids.len() <= index.0 { - self.task_ids.push(None); - } - assert!(self.task_ids[index.0].is_none()); - self.task_ids[index.0] = Some(task_id); - } -} - /// Some extra per-tile information stored for debugging purposes. #[derive(Debug)] struct CompiledScreenTileInfo { @@ -1830,15 +1834,11 @@ struct CompiledScreenTile { main_render_task: RenderTask, required_pass_count: usize, info: CompiledScreenTileInfo, - unique_id: TileUniqueId, - layer_masks_tasks: LayerMasksTasks, } impl CompiledScreenTile { fn new(main_render_task: RenderTask, - info: CompiledScreenTileInfo, - unique_id: TileUniqueId, - layer_masks_tasks: LayerMasksTasks) + info: CompiledScreenTileInfo) -> CompiledScreenTile { let mut required_pass_count = 0; main_render_task.max_depth(0, &mut required_pass_count); @@ -1847,8 +1847,6 @@ impl CompiledScreenTile { main_render_task: main_render_task, required_pass_count: required_pass_count, info: info, - unique_id: unique_id, - layer_masks_tasks: layer_masks_tasks, } } @@ -1915,10 +1913,6 @@ impl ScreenTile { let mut sc_stack = Vec::new(); let mut current_task = RenderTask::new_alpha_batch(self.rect, ctx); let mut alpha_task_stack = Vec::new(); - let mut clip_info_stack = Vec::new(); - let mut clip_task_stack = Vec::new(); - let mut num_clips_to_skip = 0; - let mut layer_masks_tasks = LayerMasksTasks::new(); for cmd in self.cmds { match cmd { @@ -1937,33 +1931,6 @@ impl ScreenTile { alpha_task_stack.push(prev_task); } } - - // Create a task for the layer mask, if needed, - // i.e. if there are rounded corners or image masks for the layer. - if let Some(ref clip_info) = layer.clip_cache_info { - if CLIP_TASK_COLLAPSE { - clip_info_stack.push((sc_index, clip_info.clone())); - } else { - let mask_opt = RenderTask::new_mask(self.rect, - clip_task_stack.last(), - MaskCacheKey::Layer(sc_index), - Some((sc_index, clip_info)), - &clip_info_stack, - ctx.tile_id); - match mask_opt { - MaskResult::Inside(mask_task) => { - current_task.children.push(mask_task.clone()); - clip_task_stack.push(mask_task); - num_clips_to_skip = 0; - } - _ => num_clips_to_skip += 1, - } - } - } - // Register the layer mask task within the context - if let Some(ref mask_task) = clip_task_stack.last() { - layer_masks_tasks.add(sc_index, mask_task.id); - } } TileCommand::PopLayer => { let sc_index = sc_stack.pop().unwrap(); @@ -1997,56 +1964,24 @@ impl ScreenTile { current_task = composite_task; } } - - if layer.clip_cache_info.is_some() { - if CLIP_TASK_COLLAPSE { - clip_info_stack.pop().unwrap(); - } else { - if num_clips_to_skip > 0 { - num_clips_to_skip -= 1; - } else { - clip_task_stack.pop().unwrap(); - } - } - } } TileCommand::DrawPrimitive(prim_index) => { let sc_index = *sc_stack.last().unwrap(); let prim_metadata = ctx.prim_store.get_metadata(prim_index); - // Add a task to render the updated image mask - if prim_metadata.clip_cache_info.is_some() || !clip_info_stack.is_empty() { - let top = prim_metadata.clip_cache_info.as_ref().map(|clip_info| (sc_index, clip_info)); - let mask_opt = RenderTask::new_mask(self.rect, - clip_task_stack.last(), - MaskCacheKey::Primitive(prim_index), - top, - &clip_info_stack, - ctx.tile_id); - match mask_opt { - MaskResult::Outside => panic!("Primitive be culled by `assign_prims_to_screen_tiles` already"), - MaskResult::Covering => (), //do nothing - MaskResult::Inside(mask_task) => { - // special case of the primitive not having any clips associated - // but with something on the stack - if CLIP_TASK_COLLAPSE && prim_metadata.clip_cache_info.is_none() { - layer_masks_tasks.add(sc_index, mask_task.id); - } - current_task.children.push(mask_task); - }, - } - } - // Add any dynamic render tasks needed to render this primitive if let Some(ref render_task) = prim_metadata.render_task { current_task.children.push(render_task.clone()); } + if let Some(ref clip_task) = prim_metadata.clip_task { + current_task.children.push(clip_task.clone()); + } actual_prim_count += 1; let layer = &ctx.layer_store[sc_index.0]; let transform_kind = layer.xf_rect.as_ref().unwrap().kind; - let needs_clipping = layer.clip_cache_info.is_some() || prim_metadata.clip_cache_info.is_some(); + let needs_clipping = prim_metadata.clip_task.is_some(); let needs_blending = transform_kind == TransformedRectKind::Complex || !prim_metadata.is_opaque || needs_clipping; @@ -2063,7 +1998,6 @@ impl ScreenTile { } debug_assert!(alpha_task_stack.is_empty()); - debug_assert!(clip_task_stack.is_empty()); let info = CompiledScreenTileInfo { cmd_count: cmd_count, @@ -2071,7 +2005,7 @@ impl ScreenTile { }; current_task.location = RenderTaskLocation::Fixed(self.rect); - Some(CompiledScreenTile::new(current_task, info, ctx.tile_id, layer_masks_tasks)) + Some(CompiledScreenTile::new(current_task, info)) } } @@ -2552,6 +2486,7 @@ impl FrameBuilder { // TODO(gw): Remove this stack once the layers refactor is done! let mut layer_stack: Vec = Vec::new(); + let mut clip_info_stack = Vec::new(); for cmd in &self.cmds { match cmd { @@ -2629,12 +2564,16 @@ impl FrameBuilder { resource_cache.request_image(mask.image, ImageRendering::Auto); //Note: no need to add the layer for resolve, all layers get resolved } + + // Create a task for the layer mask, if needed, + // i.e. if there are rounded corners or image masks for the layer. + clip_info_stack.push((sc_index, clip_info.clone())); } } &PrimitiveRunCmd::PrimitiveRun(prim_index, prim_count) => { let sc_index = layer_stack.last().unwrap(); - let layer = &mut self.layer_store[sc_index.0]; + let layer = &self.layer_store[sc_index.0]; if !layer.is_visible() { continue; } @@ -2650,8 +2589,6 @@ impl FrameBuilder { &packed_layer.transform, &packed_layer.local_clip_rect, device_pixel_ratio) { - profile_counters.visible_primitives.inc(); - if self.prim_store.prepare_prim_for_render(prim_index, resource_cache, &packed_layer.transform, @@ -2663,10 +2600,59 @@ impl FrameBuilder { &packed_layer.local_clip_rect, device_pixel_ratio); } + + // If the primitive is visible, consider culling it via clip rect(s). + // If it is visible but has clips, create the clip task for it. + if let Some(prim_bounding_rect) = self.prim_store + .cpu_bounding_rects[prim_index.0] { + let prim_metadata = &mut self.prim_store.cpu_metadata[prim_index.0]; + let prim_clip_info = prim_metadata.clip_cache_info.as_ref(); + let mut visible = true; + + if let Some(info) = prim_clip_info { + clip_info_stack.push((*sc_index, info.clone())); + } + + // Try to create a mask if we may need to. + if prim_clip_info.is_some() || !clip_info_stack.is_empty() { + let mask_opt = RenderTask::new_mask(prim_bounding_rect, + MaskCacheKey::Primitive(prim_index), + &clip_info_stack, + &self.layer_store); + match mask_opt { + MaskResult::Outside => { + // Primitive is completely clipped out. + prim_metadata.clip_task = None; + self.prim_store.cpu_bounding_rects[prim_index.0] = None; + visible = false; + } + MaskResult::Inside(task) => { + // Got a valid clip task, so store it for this primitive. + prim_metadata.clip_task = Some(task); + } + } + } + + if let Some(..) = prim_clip_info { + clip_info_stack.pop(); + } + + if visible { + profile_counters.visible_primitives.inc(); + } + } } } } &PrimitiveRunCmd::PopStackingContext => { + let sc_index = *layer_stack.last().unwrap(); + let layer = &mut self.layer_store[sc_index.0]; + if layer.can_contribute_to_scene() { + if layer.clip_cache_info.is_some() { + clip_info_stack.pop().unwrap(); + } + } + layer_stack.pop().unwrap(); } } @@ -2905,10 +2891,9 @@ impl FrameBuilder { let mut max_passes_needed = 0; let mut render_tasks = { - let mut ctx = CompileTileContext { + let ctx = CompileTileContext { layer_store: &self.layer_store, prim_store: &self.prim_store, - tile_id: 0, // This doesn't need to be atomic right now (all the screen tiles are // compiled on a single thread). However, in the future each of the @@ -2924,8 +2909,7 @@ impl FrameBuilder { } // Build list of passes, target allocs that each tile needs. - for (tile_id, screen_tile) in screen_tiles.into_iter().enumerate() { - ctx.tile_id = tile_id; + for screen_tile in screen_tiles { let rect = screen_tile.rect; if let Some(compiled_screen_tile) = screen_tile.compile(&ctx) { max_passes_needed = cmp::max(max_passes_needed, @@ -2961,11 +2945,10 @@ impl FrameBuilder { let mut passes = Vec::new(); if !compiled_screen_tiles.is_empty() { - let mut ctx = RenderTargetContext { + let ctx = RenderTargetContext { layer_store: &self.layer_store, prim_store: &self.prim_store, resource_cache: resource_cache, - layer_masks_tasks: HashMap::new(), }; // Do the allocations now, assigning each tile's tasks to a render @@ -2975,14 +2958,7 @@ impl FrameBuilder { index == max_passes_needed-1)); } - for mut compiled_screen_tile in compiled_screen_tiles { - // Grab the mask task indices from the compile tile and append into the context map - for (i, mask_task_opt) in compiled_screen_tile.layer_masks_tasks.task_ids.drain(..).enumerate() { - if let Some(mask_task_id) = mask_task_opt { - let key = (compiled_screen_tile.unique_id, StackingContextIndex(i)); - ctx.layer_masks_tasks.insert(key, mask_task_id); - } - } + for compiled_screen_tile in compiled_screen_tiles { compiled_screen_tile.build(&mut passes); } diff --git a/webrender_traits/src/display_item.rs b/webrender_traits/src/display_item.rs index 69b6221679..9fc8b9fa63 100644 --- a/webrender_traits/src/display_item.rs +++ b/webrender_traits/src/display_item.rs @@ -129,17 +129,16 @@ impl ComplexClipRegion { } //TODO: move to `util` module? - /// Return a maximum aligned rectangle that is fully inside the clip region. + /// Return an aligned rectangle that is fully inside the clip region. pub fn get_inner_rect(&self) -> Option { - 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); + self.radii.top_left.width.max(self.radii.bottom_left.width); let xr = self.rect.origin.x + self.rect.size.width - - k * self.radii.top_right.width.max(self.radii.bottom_right.width); + self.radii.top_right.width.max(self.radii.bottom_right.width); let yt = self.rect.origin.y + - k * self.radii.top_left.height.max(self.radii.top_right.height); + self.radii.top_left.height.max(self.radii.top_right.height); let yb = self.rect.origin.y + self.rect.size.height - - k * self.radii.bottom_left.height.max(self.radii.bottom_right.height); + self.radii.bottom_left.height.max(self.radii.bottom_right.height); if xl <= xr && yt <= yb { Some(LayoutRect::new(LayoutPoint::new(xl, yt), LayoutSize::new(xr-xl, yb-yt))) } else {