From 429cc01b40b96753cda088806bd4b9f4c24404c1 Mon Sep 17 00:00:00 2001 From: Martin Robinson Date: Thu, 12 Apr 2018 16:16:03 +0200 Subject: [PATCH] Pass dot / dash locations to clip shader in the VAO Instead of loading dot and dash positions into the GPU cache, send them as VAO arguments. This avoids exceeding the maximum GPU cache block size with very large border corners with hundreds or thousands of dot / dashes. This fixes the first panic from #2469, though performance is still abysmal. Later changes can address performance issues. --- webrender/res/cs_clip_border.glsl | 17 ++-- webrender/src/batch.rs | 37 +++++--- webrender/src/border.rs | 139 ++++++++---------------------- webrender/src/gpu_types.rs | 10 +++ webrender/src/renderer.rs | 54 +++++++++++- webrender/src/shade.rs | 1 + 6 files changed, 134 insertions(+), 124 deletions(-) diff --git a/webrender/res/cs_clip_border.glsl b/webrender/res/cs_clip_border.glsl index 8358871f26..33ec855d9e 100644 --- a/webrender/res/cs_clip_border.glsl +++ b/webrender/res/cs_clip_border.glsl @@ -4,6 +4,9 @@ #include shared,clip_shared +in vec4 aDashOrDot0; +in vec4 aDashOrDot1; + varying vec3 vPos; flat varying vec2 vClipCenter; @@ -46,9 +49,8 @@ struct BorderClipDash { vec4 point_tangent_1; }; -BorderClipDash fetch_border_clip_dash(ivec2 address, int segment) { - vec4 data[2] = fetch_from_resource_cache_2_direct(address + ivec2(2 + 2 * (segment - 1), 0)); - return BorderClipDash(data[0], data[1]); +BorderClipDash fetch_border_clip_dash(ivec2 address) { + return BorderClipDash(aDashOrDot0, aDashOrDot1); } // Per-dot clip information. @@ -56,9 +58,8 @@ struct BorderClipDot { vec3 center_radius; }; -BorderClipDot fetch_border_clip_dot(ivec2 address, int segment) { - vec4 data = fetch_from_resource_cache_1_direct(address + ivec2(2 + (segment - 1), 0)); - return BorderClipDot(data.xyz); +BorderClipDot fetch_border_clip_dot(ivec2 address) { + return BorderClipDot(aDashOrDot0.xyz); } void main(void) { @@ -98,7 +99,7 @@ void main(void) { switch (corner.clip_mode) { case CLIP_MODE_DASH: { // Fetch the information about this particular dash. - BorderClipDash dash = fetch_border_clip_dash(cmi.clip_data_address, cmi.segment); + BorderClipDash dash = fetch_border_clip_dash(cmi.clip_data_address); vPoint_Tangent0 = dash.point_tangent_0 * sign_modifier.xyxy; vPoint_Tangent1 = dash.point_tangent_1 * sign_modifier.xyxy; vDotParams = vec3(0.0); @@ -106,7 +107,7 @@ void main(void) { break; } case CLIP_MODE_DOT: { - BorderClipDot cdot = fetch_border_clip_dot(cmi.clip_data_address, cmi.segment); + BorderClipDot cdot = fetch_border_clip_dot(cmi.clip_data_address); vPoint_Tangent0 = vec4(1.0); vPoint_Tangent1 = vec4(1.0); vDotParams = vec3(cdot.center_radius.xy * sign_modifier, cdot.center_radius.z); diff --git a/webrender/src/batch.rs b/webrender/src/batch.rs index b32811baf7..5b53a6f712 100644 --- a/webrender/src/batch.rs +++ b/webrender/src/batch.rs @@ -12,9 +12,10 @@ use clip_scroll_tree::{CoordinateSystemId}; use euclid::{TypedTransform3D, vec3}; use glyph_rasterizer::GlyphFormat; use gpu_cache::{GpuCache, GpuCacheAddress}; -use gpu_types::{BrushFlags, BrushInstance, ClipChainRectIndex, ZBufferId, ZBufferIdGenerator}; -use gpu_types::{ClipMaskInstance, ClipScrollNodeIndex, RasterizationSpace}; -use gpu_types::{CompositePrimitiveInstance, PrimitiveInstance, SimplePrimitiveInstance}; +use gpu_types::{BrushFlags, BrushInstance, ClipChainRectIndex, ClipMaskBorderCornerDotDash}; +use gpu_types::{ClipMaskInstance, ClipScrollNodeIndex, CompositePrimitiveInstance}; +use gpu_types::{PrimitiveInstance, RasterizationSpace, SimplePrimitiveInstance, ZBufferId}; +use gpu_types::ZBufferIdGenerator; use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture}; use picture::{PictureCompositeMode, PicturePrimitive, PictureSurface}; use picture::{IMAGE_BRUSH_BLOCKS, IMAGE_BRUSH_EXTRA_BLOCKS}; @@ -1645,8 +1646,8 @@ pub struct ClipBatcher { pub rectangles: Vec, /// Image draws apply the image masking. pub images: FastHashMap>, - pub border_clears: Vec, - pub borders: Vec, + pub border_clears: Vec, + pub borders: Vec, pub box_shadows: FastHashMap>, pub line_decorations: Vec, } @@ -1770,16 +1771,26 @@ impl ClipBatcher { }); } ClipSource::BorderCorner(ref source) => { - self.border_clears.push(ClipMaskInstance { - clip_data_address: gpu_address, - segment: 0, - ..instance - }); - for clip_index in 0 .. source.actual_clip_count { - self.borders.push(ClipMaskInstance { + let instance = ClipMaskBorderCornerDotDash { + clip_mask_instance: ClipMaskInstance { clip_data_address: gpu_address, - segment: 1 + clip_index as i32, + segment: 0, ..instance + }, + dot_dash_data: [0.; 8], + }; + + self.border_clears.push(instance); + + for data in source.dot_dash_data.iter() { + self.borders.push(ClipMaskBorderCornerDotDash { + clip_mask_instance: ClipMaskInstance { + // The shader understands segment=0 as the clear, so the + // segment here just needs to be non-zero. + segment: 1, + ..instance.clip_mask_instance + }, + dot_dash_data: *data, }) } } diff --git a/webrender/src/border.rs b/webrender/src/border.rs index 789c150925..e5de20b941 100644 --- a/webrender/src/border.rs +++ b/webrender/src/border.rs @@ -677,10 +677,10 @@ pub enum BorderCornerClipKind { pub struct BorderCornerClipSource { pub corner_data: BorderCornerClipData, pub max_clip_count: usize, - pub actual_clip_count: usize, kind: BorderCornerClipKind, widths: LayoutSize, ellipse: Ellipse, + pub dot_dash_data: Vec<[f32; 8]>, } impl BorderCornerClipSource { @@ -753,45 +753,49 @@ impl BorderCornerClipSource { kind, corner_data, max_clip_count, - actual_clip_count: 0, ellipse, widths, + dot_dash_data: Vec::new(), } } pub fn write(&mut self, mut request: GpuDataRequest) { self.corner_data.write(&mut request); + assert_eq!(request.close(), 2); match self.kind { BorderCornerClipKind::Dash => { // Get the correct dash arc length. - self.actual_clip_count = self.max_clip_count; let dash_arc_length = - 0.5 * self.ellipse.total_arc_length / (self.actual_clip_count - 1) as f32; + 0.5 * self.ellipse.total_arc_length / (self.max_clip_count - 1) as f32; + self.dot_dash_data.clear(); let mut current_arc_length = -0.5 * dash_arc_length; - for _ in 0 .. self.actual_clip_count { + for _ in 0 .. self.max_clip_count { let arc_length0 = current_arc_length; current_arc_length += dash_arc_length; let arc_length1 = current_arc_length; current_arc_length += dash_arc_length; - let dash_data = - BorderCornerDashClipData::new(arc_length0, arc_length1, &self.ellipse); - dash_data.write(&mut request); - } + let alpha = self.ellipse.find_angle_for_arc_length(arc_length0); + let beta = self.ellipse.find_angle_for_arc_length(arc_length1); + + let (point0, tangent0) = self.ellipse.get_point_and_tangent(alpha); + let (point1, tangent1) = self.ellipse.get_point_and_tangent(beta); - assert_eq!(request.close(), 2 + 2 * self.actual_clip_count); + self.dot_dash_data.push([ + point0.x, point0.y, tangent0.x, tangent0.y, + point1.x, point1.y, tangent1.x, tangent1.y + ]); + } } BorderCornerClipKind::Dot if self.max_clip_count == 1 => { let dot_diameter = lerp(self.widths.width, self.widths.height, 0.5); - let dot = BorderCornerDotClipData { - center: LayoutPoint::new(self.widths.width / 2.0, self.widths.height / 2.0), - radius: 0.5 * dot_diameter, - }; - self.actual_clip_count = 1; - dot.write(&mut request); - assert_eq!(request.close(), 3); + self.dot_dash_data.clear(); + self.dot_dash_data.push([ + self.widths.width / 2.0, self.widths.height / 2.0, 0.5 * dot_diameter, 0., + 0., 0., 0., 0., + ]); } BorderCornerClipKind::Dot => { let mut forward_dots = Vec::new(); @@ -852,30 +856,31 @@ impl BorderCornerClipSource { // leftover space on the arc between them evenly. Once // the final arc position is determined, generate the correct // arc positions and angles that get passed to the clip shader. - self.actual_clip_count = forward_dots.len() + back_dots.len(); - let extra_space_per_dot = leftover_arc_length / (self.actual_clip_count - 1) as f32; + let number_of_dots = forward_dots.len() + back_dots.len(); + let extra_space_per_dot = leftover_arc_length / (number_of_dots - 1) as f32; + + self.dot_dash_data.clear(); + + let create_dot_data = |ellipse: &Ellipse, arc_length: f32, radius: f32| -> [f32; 8] { + // Represents the GPU data for drawing a single dot to a clip mask. The order + // these are specified must stay in sync with the way this data is read in the + // dot clip shader. + let theta = ellipse.find_angle_for_arc_length(arc_length); + let (center, _) = ellipse.get_point_and_tangent(theta); + [center.x, center.y, radius, 0., 0., 0., 0., 0.,] + }; for (i, dot) in forward_dots.iter().enumerate() { let extra_dist = i as f32 * extra_space_per_dot; - let dot = BorderCornerDotClipData::new( - dot.arc_pos + extra_dist, - 0.5 * dot.diameter, - &self.ellipse, - ); - dot.write(&mut request); + let dot_data = create_dot_data(&self.ellipse, dot.arc_pos + extra_dist, 0.5 * dot.diameter); + self.dot_dash_data.push(dot_data); } for (i, dot) in back_dots.iter().enumerate() { let extra_dist = i as f32 * extra_space_per_dot; - let dot = BorderCornerDotClipData::new( - dot.arc_pos - extra_dist, - 0.5 * dot.diameter, - &self.ellipse, - ); - dot.write(&mut request); + let dot_data = create_dot_data(&self.ellipse, dot.arc_pos - extra_dist, 0.5 * dot.diameter); + self.dot_dash_data.push(dot_data); } - - assert_eq!(request.close(), 2 + self.actual_clip_count); } } } @@ -910,74 +915,6 @@ impl BorderCornerClipData { } } -/// Represents the GPU data for drawing a single dash -/// to a clip mask. A dash clip is defined by two lines. -/// We store a point on the ellipse curve, and a tangent -/// to that point, which allows for efficient line-distance -/// calculations in the fragment shader. -#[derive(Debug, Clone)] -#[repr(C)] -pub struct BorderCornerDashClipData { - pub point0: LayoutPoint, - pub tangent0: LayoutPoint, - pub point1: LayoutPoint, - pub tangent1: LayoutPoint, -} - -impl BorderCornerDashClipData { - pub fn new(arc_length0: f32, arc_length1: f32, ellipse: &Ellipse) -> BorderCornerDashClipData { - let alpha = ellipse.find_angle_for_arc_length(arc_length0); - let beta = ellipse.find_angle_for_arc_length(arc_length1); - - let (p0, t0) = ellipse.get_point_and_tangent(alpha); - let (p1, t1) = ellipse.get_point_and_tangent(beta); - - BorderCornerDashClipData { - point0: p0, - tangent0: t0, - point1: p1, - tangent1: t1, - } - } - - fn write(&self, request: &mut GpuDataRequest) { - request.push([ - self.point0.x, - self.point0.y, - self.tangent0.x, - self.tangent0.y, - ]); - request.push([ - self.point1.x, - self.point1.y, - self.tangent1.x, - self.tangent1.y, - ]); - } -} - -/// Represents the GPU data for drawing a single dot -/// to a clip mask. -#[derive(Debug, Clone)] -#[repr(C)] -pub struct BorderCornerDotClipData { - pub center: LayoutPoint, - pub radius: f32, -} - -impl BorderCornerDotClipData { - pub fn new(arc_length: f32, radius: f32, ellipse: &Ellipse) -> BorderCornerDotClipData { - let theta = ellipse.find_angle_for_arc_length(arc_length); - let (center, _) = ellipse.get_point_and_tangent(theta); - - BorderCornerDotClipData { center, radius } - } - - fn write(&self, request: &mut GpuDataRequest) { - request.push([self.center.x, self.center.y, self.radius, 0.0]); - } -} - #[derive(Copy, Clone, Debug)] struct DotInfo { arc_pos: f32, diff --git a/webrender/src/gpu_types.rs b/webrender/src/gpu_types.rs index b9a4ea9cdc..5eb15a0956 100644 --- a/webrender/src/gpu_types.rs +++ b/webrender/src/gpu_types.rs @@ -83,6 +83,16 @@ pub struct ClipMaskInstance { pub resource_address: GpuCacheAddress, } +/// A border corner dot or dash drawn into the clipping mask. +#[derive(Debug, Copy, Clone)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[repr(C)] +pub struct ClipMaskBorderCornerDotDash { + pub clip_mask_instance: ClipMaskInstance, + pub dot_dash_data: [f32; 8], +} + // 32 bytes per instance should be enough for anyone! #[derive(Debug, Clone)] #[cfg_attr(feature = "capture", derive(Serialize))] diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index f8b0a51eb1..7f0efb6154 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -433,6 +433,48 @@ pub(crate) mod desc { ], }; + pub const BORDER_CORNER_DASH_AND_DOT: VertexDescriptor = VertexDescriptor { + vertex_attributes: &[ + VertexAttribute { + name: "aPosition", + count: 2, + kind: VertexAttributeKind::F32, + }, + ], + instance_attributes: &[ + VertexAttribute { + name: "aClipRenderTaskAddress", + count: 1, + kind: VertexAttributeKind::I32, + }, + VertexAttribute { + name: "aScrollNodeId", + count: 1, + kind: VertexAttributeKind::I32, + }, + VertexAttribute { + name: "aClipSegment", + count: 1, + kind: VertexAttributeKind::I32, + }, + VertexAttribute { + name: "aClipDataResourceAddress", + count: 4, + kind: VertexAttributeKind::U16, + }, + VertexAttribute { + name: "aDashOrDot0", + count: 4, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aDashOrDot1", + count: 4, + kind: VertexAttributeKind::F32, + }, + ], + }; + pub const GPU_CACHE_UPDATE: VertexDescriptor = VertexDescriptor { vertex_attributes: &[ VertexAttribute { @@ -539,6 +581,7 @@ pub(crate) enum VertexArrayKind { Primitive, Blur, Clip, + DashAndDot, VectorStencil, VectorCover, } @@ -1260,6 +1303,7 @@ pub struct RendererVAOs { prim_vao: VAO, blur_vao: VAO, clip_vao: VAO, + dash_and_dot_vao: VAO, } /// The renderer is responsible for submitting to the GPU the work prepared by the @@ -1546,6 +1590,8 @@ impl Renderer { let blur_vao = device.create_vao_with_new_instances(&desc::BLUR, &prim_vao); let clip_vao = device.create_vao_with_new_instances(&desc::CLIP, &prim_vao); + let dash_and_dot_vao = + device.create_vao_with_new_instances(&desc::BORDER_CORNER_DASH_AND_DOT, &prim_vao); let texture_cache_upload_pbo = device.create_pbo(); let texture_resolver = SourceTextureResolver::new(&mut device); @@ -1698,6 +1744,7 @@ impl Renderer { prim_vao, blur_vao, clip_vao, + dash_and_dot_vao, }, node_data_texture, local_clip_rects_texture, @@ -3065,7 +3112,7 @@ impl Renderer { .bind(&mut self.device, projection, &mut self.renderer_errors); self.draw_instanced_batch( &target.clip_batcher.border_clears, - VertexArrayKind::Clip, + VertexArrayKind::DashAndDot, &BatchTextures::no_texture(), stats, ); @@ -3084,7 +3131,7 @@ impl Renderer { .bind(&mut self.device, projection, &mut self.renderer_errors); self.draw_instanced_batch( &target.clip_batcher.borders, - VertexArrayKind::Clip, + VertexArrayKind::DashAndDot, &BatchTextures::no_texture(), stats, ); @@ -3817,6 +3864,7 @@ impl Renderer { self.device.delete_vao(self.vaos.prim_vao); self.device.delete_vao(self.vaos.clip_vao); self.device.delete_vao(self.vaos.blur_vao); + self.device.delete_vao(self.vaos.dash_and_dot_vao); #[cfg(feature = "debug_renderer")] { @@ -4409,6 +4457,7 @@ fn get_vao<'a>(vertex_array_kind: VertexArrayKind, VertexArrayKind::Primitive => &vaos.prim_vao, VertexArrayKind::Clip => &vaos.clip_vao, VertexArrayKind::Blur => &vaos.blur_vao, + VertexArrayKind::DashAndDot => &vaos.dash_and_dot_vao, VertexArrayKind::VectorStencil => &gpu_glyph_renderer.vector_stencil_vao, VertexArrayKind::VectorCover => &gpu_glyph_renderer.vector_cover_vao, } @@ -4423,6 +4472,7 @@ fn get_vao<'a>(vertex_array_kind: VertexArrayKind, VertexArrayKind::Primitive => &vaos.prim_vao, VertexArrayKind::Clip => &vaos.clip_vao, VertexArrayKind::Blur => &vaos.blur_vao, + VertexArrayKind::DashAndDot => &vaos.dash_and_dot_vao, VertexArrayKind::VectorStencil | VertexArrayKind::VectorCover => unreachable!(), } } diff --git a/webrender/src/shade.rs b/webrender/src/shade.rs index f83d870dfe..1b9eae7ea9 100644 --- a/webrender/src/shade.rs +++ b/webrender/src/shade.rs @@ -400,6 +400,7 @@ fn create_prim_shader( VertexArrayKind::Primitive => desc::PRIM_INSTANCES, VertexArrayKind::Blur => desc::BLUR, VertexArrayKind::Clip => desc::CLIP, + VertexArrayKind::DashAndDot => desc::BORDER_CORNER_DASH_AND_DOT, VertexArrayKind::VectorStencil => desc::VECTOR_STENCIL, VertexArrayKind::VectorCover => desc::VECTOR_COVER, };