diff --git a/webrender/res/prim_shared.glsl b/webrender/res/prim_shared.glsl index 4c65969f57..9fbe2a9b87 100644 --- a/webrender/res/prim_shared.glsl +++ b/webrender/res/prim_shared.glsl @@ -324,8 +324,8 @@ struct Border { vec4 radii[2]; }; -vec4 get_effective_border_widths(Border border) { - switch (int(border.style.x)) { +vec4 get_effective_border_widths(Border border, int style) { + switch (style) { case BORDER_STYLE_DOUBLE: // Calculate the width of a border segment in a style: double // border. Round to the nearest CSS pixel. diff --git a/webrender/res/ps_border_corner.vs.glsl b/webrender/res/ps_border_corner.vs.glsl index b0091b989b..60bf0be0e9 100644 --- a/webrender/res/ps_border_corner.vs.glsl +++ b/webrender/res/ps_border_corner.vs.glsl @@ -3,6 +3,11 @@ * 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/. */ +// Matches BorderCornerSide enum in border.rs +#define SIDE_BOTH 0 +#define SIDE_FIRST 1 +#define SIDE_SECOND 2 + vec2 get_radii(vec2 radius, vec2 invalid) { if (all(greaterThan(radius, vec2(0.0)))) { return radius; @@ -11,14 +16,14 @@ vec2 get_radii(vec2 radius, vec2 invalid) { return invalid; } -void set_radii(float style, +void set_radii(int style, vec2 radii, vec2 widths, vec2 adjusted_widths) { vRadii0.xy = get_radii(radii, 2.0 * widths); vRadii0.zw = get_radii(radii - widths, -widths); - switch (int(style)) { + switch (style) { case BORDER_STYLE_RIDGE: case BORDER_STYLE_GROOVE: vRadii1.xy = radii - adjusted_widths; @@ -42,7 +47,7 @@ void set_edge_line(vec2 border_width, vColorEdgeLine = vec4(outer_corner, vec2(-gradient.y, gradient.x)); } -void write_color(vec4 color0, vec4 color1, int style, vec2 delta) { +void write_color(vec4 color0, vec4 color1, int style, vec2 delta, int instance_kind) { vec4 modulate; switch (style) { @@ -64,20 +69,40 @@ void write_color(vec4 color0, vec4 color1, int style, vec2 delta) { break; } + // Optionally mask out one side of the border corner, + // depending on the instance kind. + switch (instance_kind) { + case SIDE_FIRST: + color0.a = 0.0; + break; + case SIDE_SECOND: + color1.a = 0.0; + break; + } + vColor00 = vec4(color0.rgb * modulate.x, color0.a); vColor01 = vec4(color0.rgb * modulate.y, color0.a); vColor10 = vec4(color1.rgb * modulate.z, color1.a); vColor11 = vec4(color1.rgb * modulate.w, color1.a); } +int select_style(int color_select, vec2 style) { + switch (color_select) { + case SIDE_BOTH: + return int(style.x); + case SIDE_FIRST: + return int(style.x); + case SIDE_SECOND: + return int(style.y); + } +} + void main(void) { Primitive prim = load_primitive(); Border border = fetch_border(prim.prim_index); int sub_part = prim.sub_index; BorderCorners corners = get_border_corners(border, prim.local_rect); - vec4 adjusted_widths = get_effective_border_widths(border); - vec4 inv_adjusted_widths = border.widths - adjusted_widths; vec2 p0, p1; // TODO(gw): We'll need to pass through multiple styles @@ -87,6 +112,9 @@ void main(void) { vec4 color0, color1; vec2 color_delta; + // TODO(gw): Now that all border styles are supported, the switch + // statement below can be tidied up quite a bit. + switch (sub_part) { case 0: { p0 = corners.tl_outer; @@ -95,14 +123,16 @@ void main(void) { color1 = border.colors[1]; vClipCenter = corners.tl_outer + border.radii[0].xy; vClipSign = vec2(1.0); - set_radii(border.style.x, + style = select_style(prim.user_data.x, border.style.yx); + vec4 adjusted_widths = get_effective_border_widths(border, style); + vec4 inv_adjusted_widths = border.widths - adjusted_widths; + set_radii(style, border.radii[0].xy, border.widths.xy, adjusted_widths.xy); set_edge_line(border.widths.xy, corners.tl_outer, vec2(1.0, 1.0)); - style = int(border.style.x); edge_distances = vec4(p0 + adjusted_widths.xy, p0 + inv_adjusted_widths.xy); color_delta = vec2(1.0); @@ -115,14 +145,16 @@ void main(void) { color1 = border.colors[2]; vClipCenter = corners.tr_outer + vec2(-border.radii[0].z, border.radii[0].w); vClipSign = vec2(-1.0, 1.0); - set_radii(border.style.y, + style = select_style(prim.user_data.x, border.style.zy); + vec4 adjusted_widths = get_effective_border_widths(border, style); + vec4 inv_adjusted_widths = border.widths - adjusted_widths; + set_radii(style, border.radii[0].zw, border.widths.zy, adjusted_widths.zy); set_edge_line(border.widths.zy, corners.tr_outer, vec2(-1.0, 1.0)); - style = int(border.style.y); edge_distances = vec4(p1.x - adjusted_widths.z, p0.y + adjusted_widths.y, p1.x - border.widths.z + adjusted_widths.z, @@ -137,14 +169,16 @@ void main(void) { color1 = border.colors[3]; vClipCenter = corners.br_outer - border.radii[1].xy; vClipSign = vec2(-1.0, -1.0); - set_radii(border.style.z, + style = select_style(prim.user_data.x, border.style.wz); + vec4 adjusted_widths = get_effective_border_widths(border, style); + vec4 inv_adjusted_widths = border.widths - adjusted_widths; + set_radii(style, border.radii[1].xy, border.widths.zw, adjusted_widths.zw); set_edge_line(border.widths.zw, corners.br_outer, vec2(-1.0, -1.0)); - style = int(border.style.z); edge_distances = vec4(p1.x - adjusted_widths.z, p1.y - adjusted_widths.w, p1.x - border.widths.z + adjusted_widths.z, @@ -159,14 +193,16 @@ void main(void) { color1 = border.colors[0]; vClipCenter = corners.bl_outer + vec2(border.radii[1].z, -border.radii[1].w); vClipSign = vec2(1.0, -1.0); - set_radii(border.style.w, + style = select_style(prim.user_data.x, border.style.xw); + vec4 adjusted_widths = get_effective_border_widths(border, style); + vec4 inv_adjusted_widths = border.widths - adjusted_widths; + set_radii(style, border.radii[1].zw, border.widths.xw, adjusted_widths.xw); set_edge_line(border.widths.xw, corners.bl_outer, vec2(1.0, -1.0)); - style = int(border.style.w); edge_distances = vec4(p0.x + adjusted_widths.x, p1.y - adjusted_widths.w, p0.x + inv_adjusted_widths.x, @@ -176,7 +212,7 @@ void main(void) { } } - switch (int(style)) { + switch (style) { case BORDER_STYLE_DOUBLE: { vEdgeDistance = edge_distances; vAlphaSelect = 0.0; @@ -205,7 +241,7 @@ void main(void) { } } - write_color(color0, color1, style, color_delta); + write_color(color0, color1, style, color_delta, prim.user_data.x); RectWithSize segment_rect; segment_rect.p0 = p0; diff --git a/webrender/res/ps_border_edge.vs.glsl b/webrender/res/ps_border_edge.vs.glsl index 490fc751aa..53dc3a506b 100644 --- a/webrender/res/ps_border_edge.vs.glsl +++ b/webrender/res/ps_border_edge.vs.glsl @@ -103,14 +103,17 @@ void main(void) { Border border = fetch_border(prim.prim_index); int sub_part = prim.sub_index; BorderCorners corners = get_border_corners(border, prim.local_rect); - vec4 adjusted_widths = get_effective_border_widths(border); vec4 color = border.colors[sub_part]; + // TODO(gw): Now that all border styles are supported, the switch + // statement below can be tidied up quite a bit. + RectWithSize segment_rect; switch (sub_part) { - case 0: + case 0: { segment_rect.p0 = vec2(corners.tl_outer.x, corners.tl_inner.y); segment_rect.size = vec2(border.widths.x, corners.bl_inner.y - corners.tl_inner.y); + vec4 adjusted_widths = get_effective_border_widths(border, int(border.style.x)); write_edge_distance(segment_rect.p0.x, border.widths.x, adjusted_widths.x, border.style.x, 0.0, 1.0); write_alpha_select(border.style.x); write_color(color, border.style.x, false); @@ -120,9 +123,11 @@ void main(void) { segment_rect.p0.y, segment_rect.p0.x + 0.5 * segment_rect.size.x); break; - case 1: + } + case 1: { segment_rect.p0 = vec2(corners.tl_inner.x, corners.tl_outer.y); segment_rect.size = vec2(corners.tr_inner.x - corners.tl_inner.x, border.widths.y); + vec4 adjusted_widths = get_effective_border_widths(border, int(border.style.y)); write_edge_distance(segment_rect.p0.y, border.widths.y, adjusted_widths.y, border.style.y, 1.0, 1.0); write_alpha_select(border.style.y); write_color(color, border.style.y, false); @@ -132,9 +137,11 @@ void main(void) { segment_rect.p0.x, segment_rect.p0.y + 0.5 * segment_rect.size.y); break; - case 2: + } + case 2: { segment_rect.p0 = vec2(corners.tr_outer.x - border.widths.z, corners.tr_inner.y); segment_rect.size = vec2(border.widths.z, corners.br_inner.y - corners.tr_inner.y); + vec4 adjusted_widths = get_effective_border_widths(border, int(border.style.z)); write_edge_distance(segment_rect.p0.x, border.widths.z, adjusted_widths.z, border.style.z, 0.0, -1.0); write_alpha_select(border.style.z); write_color(color, border.style.z, true); @@ -144,9 +151,11 @@ void main(void) { segment_rect.p0.y, segment_rect.p0.x + 0.5 * segment_rect.size.x); break; - case 3: + } + case 3: { segment_rect.p0 = vec2(corners.bl_inner.x, corners.bl_outer.y - border.widths.w); segment_rect.size = vec2(corners.br_inner.x - corners.bl_inner.x, border.widths.w); + vec4 adjusted_widths = get_effective_border_widths(border, int(border.style.w)); write_edge_distance(segment_rect.p0.y, border.widths.w, adjusted_widths.w, border.style.w, 1.0, -1.0); write_alpha_select(border.style.w); write_color(color, border.style.w, true); @@ -156,6 +165,7 @@ void main(void) { segment_rect.p0.x, segment_rect.p0.y + 0.5 * segment_rect.size.y); break; + } } #ifdef WR_FEATURE_TRANSFORM diff --git a/webrender/src/border.rs b/webrender/src/border.rs index 2b41a75e01..bac5230549 100644 --- a/webrender/src/border.rs +++ b/webrender/src/border.rs @@ -11,6 +11,20 @@ use util::{lerp, pack_as_float}; use webrender_traits::{BorderSide, BorderStyle, BorderWidths, ClipAndScrollInfo, ClipRegion}; use webrender_traits::{ColorF, LayerPoint, LayerRect, LayerSize, NormalBorder}; +#[repr(u8)] +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum BorderCornerInstance { + Single, // Single instance needed - corner styles are same or similar. + Double, // Different corner styles. Draw two instances, one per style. +} + +#[repr(C)] +pub enum BorderCornerSide { + Both, + First, + Second, +} + #[repr(C)] enum BorderCorner { TopLeft, @@ -23,7 +37,7 @@ enum BorderCorner { pub enum BorderCornerKind { None, Solid, - Clip, + Clip(BorderCornerInstance), Mask(BorderCornerClipData, LayerSize, LayerSize, BorderCornerClipKind), Unhandled, } @@ -129,7 +143,7 @@ impl NormalBorderHelpers for NormalBorder { if edge0.color == edge1.color && radius.width == 0.0 && radius.height == 0.0 { BorderCornerKind::Solid } else { - BorderCornerKind::Clip + BorderCornerKind::Clip(BorderCornerInstance::Single) } } @@ -139,9 +153,11 @@ impl NormalBorderHelpers for NormalBorder { (BorderStyle::Inset, BorderStyle::Inset) | (BorderStyle::Double, BorderStyle::Double) | (BorderStyle::Groove, BorderStyle::Groove) | - (BorderStyle::Ridge, BorderStyle::Ridge) => BorderCornerKind::Clip, + (BorderStyle::Ridge, BorderStyle::Ridge) => { + BorderCornerKind::Clip(BorderCornerInstance::Single) + } - // Dashed border corners get drawn into a clip mask. + // Dashed and dotted border corners get drawn into a clip mask. (BorderStyle::Dashed, BorderStyle::Dashed) => { BorderCornerKind::new_mask(BorderCornerClipKind::Dash, width0, @@ -150,7 +166,6 @@ impl NormalBorderHelpers for NormalBorder { *radius, *border_rect) } - (BorderStyle::Dotted, BorderStyle::Dotted) => { BorderCornerKind::new_mask(BorderCornerClipKind::Dot, width0, @@ -160,16 +175,20 @@ impl NormalBorderHelpers for NormalBorder { *border_rect) } - // Assume complex for these cases. - // TODO(gw): There are some cases in here that can be handled with a fast path. - // For example, with inset/outset borders, two of the four corners are solid. - (BorderStyle::Dotted, _) | (_, BorderStyle::Dotted) => BorderCornerKind::Unhandled, - (BorderStyle::Dashed, _) | (_, BorderStyle::Dashed) => BorderCornerKind::Unhandled, - (BorderStyle::Double, _) | (_, BorderStyle::Double) => BorderCornerKind::Unhandled, - (BorderStyle::Groove, _) | (_, BorderStyle::Groove) => BorderCornerKind::Unhandled, - (BorderStyle::Ridge, _) | (_, BorderStyle::Ridge) => BorderCornerKind::Unhandled, - (BorderStyle::Outset, _) | (_, BorderStyle::Outset) => BorderCornerKind::Unhandled, - (BorderStyle::Inset, _) | (_, BorderStyle::Inset) => BorderCornerKind::Unhandled, + // TODO(gw): Handle border corners with both dots and dashes. + // Once these are handled, the old border path can + // be removed. + (BorderStyle::Dotted, _) | + (_, BorderStyle::Dotted) | + (BorderStyle::Dashed, _) | + (_, BorderStyle::Dashed) => BorderCornerKind::Unhandled, + + // Everything else can be handled by drawing the corner twice, + // where the shader outputs zero alpha for the side it's not + // drawing. This is somewhat inefficient in terms of pixels + // written, but it's a fairly rare case, and we can optimize + // this case later. + _ => BorderCornerKind::Clip(BorderCornerInstance::Double), } } @@ -205,6 +224,7 @@ impl FrameBuilder { clip_and_scroll: ClipAndScrollInfo, clip_region: &ClipRegion, use_new_border_path: bool, + corner_instances: [BorderCornerInstance; 4], extra_clips: &[ClipSource]) { let radius = &border.radius; let left = &border.left; @@ -220,6 +240,7 @@ impl FrameBuilder { let prim_cpu = BorderPrimitiveCpu { use_new_border_path: use_new_border_path, + corner_instances: corner_instances, }; let prim_gpu = BorderPrimitiveGpu { @@ -313,6 +334,7 @@ impl FrameBuilder { clip_and_scroll, clip_region, false, + [BorderCornerInstance::Single; 4], &[]); return; } @@ -382,14 +404,21 @@ impl FrameBuilder { } else { // Create clip masks for border corners, if required. let mut extra_clips = Vec::new(); - - for corner in corners.iter() { - if let &BorderCornerKind::Mask(corner_data, corner_radius, widths, kind) = corner { - let clip_source = BorderCornerClipSource::new(corner_data, - corner_radius, - widths, - kind); - extra_clips.push(ClipSource::BorderCorner(clip_source)); + let mut corner_instances = [BorderCornerInstance::Single; 4]; + + for (i, corner) in corners.iter().enumerate() { + match corner { + &BorderCornerKind::Mask(corner_data, corner_radius, widths, kind) => { + let clip_source = BorderCornerClipSource::new(corner_data, + corner_radius, + widths, + kind); + extra_clips.push(ClipSource::BorderCorner(clip_source)); + } + &BorderCornerKind::Clip(instance_kind) => { + corner_instances[i] = instance_kind; + } + _ => {} } } @@ -399,6 +428,7 @@ impl FrameBuilder { clip_and_scroll, clip_region, true, + corner_instances, &extra_clips); } } @@ -712,4 +742,4 @@ impl DotInfo { diameter: diameter, } } -} \ No newline at end of file +} diff --git a/webrender/src/prim_store.rs b/webrender/src/prim_store.rs index d02182dc03..36b5a035d2 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -4,6 +4,7 @@ use app_units::Au; use border::{BorderCornerClipData, BorderCornerDashClipData, BorderCornerDotClipData}; +use border::BorderCornerInstance; use euclid::{Size2D}; use gpu_store::GpuStoreAddress; use internal_types::{SourceTexture, PackedTexel}; @@ -219,6 +220,7 @@ pub struct BorderPrimitiveCpu { // TODO(gw): Remove this when all border kinds are switched // over to the new border path! pub use_new_border_path: bool, + pub corner_instances: [BorderCornerInstance; 4], } #[derive(Debug, Clone)] diff --git a/webrender/src/tiling.rs b/webrender/src/tiling.rs index 26f78611a0..d0092b8910 100644 --- a/webrender/src/tiling.rs +++ b/webrender/src/tiling.rs @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use app_units::Au; +use border::{BorderCornerInstance, BorderCornerSide}; use device::TextureId; use fnv::FnvHasher; use gpu_store::GpuStoreAddress; @@ -425,8 +426,23 @@ impl AlphaRenderItem { let edge_key = AlphaBatchKey::new(AlphaBatchKind::BorderEdge, flags, blend_mode, textures); batch_list.with_suitable_batch(&corner_key, item_bounding_rect, |batch| { - for border_segment in 0..4 { - batch.add_instance(base_instance.build(border_segment, 0, 0)); + for (i, instance_kind) in border_cpu.corner_instances.iter().enumerate() { + let sub_index = i as i32; + match *instance_kind { + BorderCornerInstance::Single => { + batch.add_instance(base_instance.build(sub_index, + BorderCornerSide::Both as i32, + 0)); + } + BorderCornerInstance::Double => { + batch.add_instance(base_instance.build(sub_index, + BorderCornerSide::First as i32, + 0)); + batch.add_instance(base_instance.build(sub_index, + BorderCornerSide::Second as i32, + 0)); + } + } } }); diff --git a/wrench/reftests/border/border-suite-3.png b/wrench/reftests/border/border-suite-3.png new file mode 100644 index 0000000000..8b8dea1406 Binary files /dev/null and b/wrench/reftests/border/border-suite-3.png differ diff --git a/wrench/reftests/border/border-suite-3.yaml b/wrench/reftests/border/border-suite-3.yaml new file mode 100644 index 0000000000..2deb821071 --- /dev/null +++ b/wrench/reftests/border/border-suite-3.yaml @@ -0,0 +1,57 @@ +--- +root: + items: + - type: stacking-context + bounds: [0, 0, 1000, 1000] + items: + - type: border + bounds: [ 10, 10, 200, 200 ] + width: 10 + border-type: normal + style: [ solid, double, solid, double ] + color: [ red, green, blue, black ] + radius: 16 + - type: border + bounds: [ 230, 10, 200, 200 ] + width: 16 + border-type: normal + style: [ solid, double, solid, double ] + color: [ red, green, blue, black ] + - type: border + bounds: [ 450, 10, 200, 200 ] + width: 10 + border-type: normal + style: [ solid, double, solid, double ] + color: [ red, green, blue, black ] + radius: { + top-left: [32, 48], + top-right: [64, 32], + bottom-left: [10, 40], + bottom-right: [48, 48], + } + + - type: border + bounds: [ 10, 230, 200, 200 ] + width: 24 + border-type: normal + style: [ inset, double, ridge, groove ] + color: [ red, green, blue, black ] + radius: 16 + - type: border + bounds: [ 230, 230, 200, 200 ] + width: 32 + border-type: normal + style: [ outset, double, ridge, groove ] + color: [ red, green, blue, black ] + - type: border + bounds: [ 450, 230, 200, 200 ] + width: 18 + border-type: normal + style: [ outset, double, ridge, groove ] + color: [ red, green, blue, black ] + radius: { + top-left: [32, 48], + top-right: [64, 32], + bottom-left: [10, 40], + bottom-right: [48, 48], + } diff --git a/wrench/reftests/border/reftest.list b/wrench/reftests/border/reftest.list index 35c2e1eb36..8ab6a13bf2 100644 --- a/wrench/reftests/border/reftest.list +++ b/wrench/reftests/border/reftest.list @@ -3,6 +3,7 @@ == border-radii.yaml border-radii.png == border-suite.yaml border-suite.png == border-suite-2.yaml border-suite-2.png +== border-suite-3.yaml border-suite-3.png fuzzy(255,610) == border-double-simple.yaml border-double-simple-ref.yaml fuzzy(255,24) == border-groove-simple.yaml border-groove-simple-ref.yaml fuzzy(255,24) == border-ridge-simple.yaml border-ridge-simple-ref.yaml