Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pass dot / dash locations to clip shader in the VAO #2652

Merged
merged 1 commit into from Apr 23, 2018
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

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.
  • Loading branch information
mrobinson committed Apr 23, 2018
commit 429cc01b40b96753cda088806bd4b9f4c24404c1
@@ -4,6 +4,9 @@

#include shared,clip_shared

in vec4 aDashOrDot0;
in vec4 aDashOrDot1;

varying vec3 vPos;

flat varying vec2 vClipCenter;
@@ -46,19 +49,17 @@ 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.
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,15 +99,15 @@ 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);
vAlphaMask = vec2(0.0, 1.0);
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);
@@ -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<ClipMaskInstance>,
/// Image draws apply the image masking.
pub images: FastHashMap<SourceTexture, Vec<ClipMaskInstance>>,
pub border_clears: Vec<ClipMaskInstance>,
pub borders: Vec<ClipMaskInstance>,
pub border_clears: Vec<ClipMaskBorderCornerDotDash>,
pub borders: Vec<ClipMaskBorderCornerDotDash>,
pub box_shadows: FastHashMap<SourceTexture, Vec<ClipMaskInstance>>,
pub line_decorations: Vec<ClipMaskInstance>,
}
@@ -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,
})
}
}
@@ -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,
@@ -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))]
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.