--- a/gfx/webrender/doc/CLIPPING.md
+++ b/gfx/webrender/doc/CLIPPING.md
@@ -1,97 +1,53 @@
-# Clipping in WebRender
-
-The WebRender display list allows defining clips in two different ways. The
-first is specified directly on each display item and cannot be reused between
-items. The second is specified using the `SpecificDisplayItem::Clip` display item
-and can be reused between items as well as used to define scrollable regions.
-
-## Clips
-
-Clips are defined using the ClipRegion in both cases.
+# Clipping and Positioning in WebRender
-```rust
-pub struct ClipRegion {
- pub main: LayoutRect,
- pub complex: ItemRange<ComplexClip>,
- pub image_mask: Option<ImageMask>,
-}
-```
-
-`main` defines a rectangular clip, while the other members make that rectangle
-smaller. `complex`, if it is not empty, defines the boundaries of a rounded
-rectangle. While `image_mask` defines the positioning, repetition, and data of
-a masking image.
+Each non-structural WebRender display list item has
+ * A `ClipId` of a positioning node
+ * A `ClipId` of a clip chain
+ * An item-specific rectangular clip rectangle
-## Item Clips
+The positioning node determines how that item is positioned. It's assumed that the
+positioning node and the item are children of the same reference frame. The clip
+chain determines how that item is clipped. This should be fully independent of
+how the node is positioned and items can be clipped by any `ClipChain` regardless
+of the reference frame of their member clips. Finally, the item-specific
+clipping rectangle is applied directly to the item and should never result in the
+creation of a clip mask itself.
-Item clips are simply a `ClipRegion` structure defined directly on the
-`DisplayItem`. The important thing to note about these clips is that all the
-coordinate in `ClipRegion` **are in the same coordinate space as the item
-itself**. This different than for clips defined by `SpecificDisplayItem::Clip`.
-
-## Clip Display Items
+# The `ClipScrollTree`
-Clip display items allow items to share clips in order to increase performance
-(shared clips are only rasterized once) and to allow for scrolling regions.
-Display items can be assigned a clip display item using the `clip_id`
-field. An item can be assigned any clip that is defined by its parent stacking
-context or any of the ancestors. The behavior of assigning an id outside of
-this hierarchy is undefined, because that situation does not occur in CSS
+The ClipScrollTree contains two sorts of elements. The first sort are nodes
+that affect the positioning of display primitives and other clips. These
+nodes can currently be reference frames, scroll frames, or sticky frames.
+The second sort of node is a clip node, which specifies some combination of
+a clipping rectangle, a collection of rounded clipping rectangles, and an
+optional image mask. These nodes are created and added to the display list
+during display list flattening.
-The clip display item has a `ClipRegion` as well as several other fields:
-
-```rust
-pub struct ClipDisplayItem {
- pub id: ClipId,
- pub parent_id: ClipId,
-}
-```
+# `ClipChain`s
-A `ClipDisplayItem` also gets a clip and bounds from the `BaseDisplayItem`. The
-clip is shared among all items that use this `ClipDisplayItem`. Critically,
-**coordinates in this ClipRegion are defined relative to the bounds of the
-ClipDisplayItem itself**. Additionally, WebRender only supports clip rectangles
-that start at the origin of the `BaseDisplayItem` bounds.
-
-The `BaseDisplayItem` bounds are known as the *content rectangle* of the clip. If
-the content rectangle is larger than *main* clipping rectangle, the clip will
-be a scrolling clip and participate in scrolling event capture and
-transformation.
-
-`ClipDisplayItems` are positioned, like all other items, relatively to their
-containing stacking context, yet they also live in a parallel tree defined by
-their `parent_id`. Child clips also include any clipping and scrolling that
-their ancestors do. In this way, a clip is positioned by a stacking context,
-but that position may be adjusted by any scroll offset of its parent clips.
-
-## Clip ids
+A `ClipChain` represents some collection of `ClipId`s of clipping nodes in the
+`ClipScrollTree`. The collection defines a clip mask which can be applied
+to a given display item primitive. A `ClipChain` is automatically created
+for every clipping node in the `ClipScrollTree` from the particular node
+and every ancestor clipping node. Additionally, the API exposes functionality
+to create a `ClipChain` given an arbitrary list of clipping nodes and the
+`ClipId` of a parent `ClipChain`. These custom `ClipChain`s will not take
+into account ancestor clipping nodes in the `ClipScrollTree` when clipping
+the item.
-All clips defined by a `ClipDisplayItem` have an id. It is useful to associate
-an external id with WebRender id in order to allow for tracking and updating
-scroll positions using the WebRender API. In order to make this as cheap as
-possible and to avoid having to create a `HashMap` to map between the two types
-of ids, the WebRender API provides an optional id argument in
-`DisplayListBuilder::define_clip`. The only types of ids that are supported
-here are those created with `ClipId::new(...)`. If this argument is not
-provided `define_clip` will return a uniquely generated id. Thus, the following
-should always be true:
+Important information about `ClipChain`s:
+ * The `ClipId`s in the list must refer to clipping nodes in the `ClipScrollTree`.
+ The list should not contain `ClipId` of positioning nodes or other `ClipChain`s.
+ * The `ClipId` of a clip node serves at the `ClipId` of that node's automatically
+ generated `ClipChain` as well.
+
+# The Future
-```rust
-let id = ClipId::new(my_internal_id, pipeline_id);
-let generated_id = define_clip(content_rect, clip, id);
-assert!(id == generated_id);
-```
-
-Note that calling `define_clip` multiple times with the same `clip_id` value
-results in undefined behaviour, and should be avoided. There is a debug mode
-assertion to catch this.
-
-## Pending changes
-1. Normalize the way that clipping coordinates are defined. Having them
- specified in two different ways makes for a confusing API. This should be
- fixed. ([github issue](https://github.com/servo/webrender/issues/1090))
-
-1. It should be possible to specify more than one predefined clip for an item.
- This is necessary for items that live in a scrolling frame, but are also
- clipped by a clip that lives outside that frame.
- ([github issue](https://github.com/servo/webrender/issues/840))
+In general, the clipping API is becoming more and more stable as it has become
+more flexible. Some ideas for improving the API further:
+ * Creating a distinction between ids that refer to `ClipScrollTree` nodes and individual
+ `ClipChain`s. This would make it harder to accidentally misuse the API, but require
+ `DisplayListBuilder::define_clip` to return two different types of ids.
+ * Separate out the clipping nodes from the positioning nodes. Perhaps WebRender only
+ needs an API where `ClipChains` are defined individually. This could potentially
+ prevent unnecessary `ClipChain` creation during display list flattening.
--- a/gfx/webrender/res/brush_image.glsl
+++ b/gfx/webrender/res/brush_image.glsl
@@ -90,20 +90,20 @@ void brush_vs(
local_rect = segment_rect;
stretch_size = local_rect.size;
// Note: Here we can assume that texels in device
// space map to local space, due to how border-image
// works. That assumption may not hold if this
// is used for other purposes in the future.
if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_X) != 0) {
- stretch_size.x = texel_rect.z - texel_rect.x;
+ stretch_size.x = (texel_rect.z - texel_rect.x) / uDevicePixelRatio;
}
if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_Y) != 0) {
- stretch_size.y = texel_rect.w - texel_rect.y;
+ stretch_size.y = (texel_rect.w - texel_rect.y) / uDevicePixelRatio;
}
uv0 = res.uv_rect.p0 + texel_rect.xy;
uv1 = res.uv_rect.p0 + texel_rect.zw;
}
vUv.z = res.layer;
--- a/gfx/webrender/res/cs_border_segment.glsl
+++ b/gfx/webrender/res/cs_border_segment.glsl
@@ -8,34 +8,38 @@
// are the colors of each edge making up the corner.
flat varying vec4 vColor0[2];
flat varying vec4 vColor1[2];
// A point + tangent defining the line where the edge
// transition occurs. Used for corners only.
flat varying vec4 vColorLine;
-// x = segment, y = styles, z = edge axes
-flat varying ivec3 vConfig;
+// x = segment, y = styles, z = edge axes, w = clip mode
+flat varying ivec4 vConfig;
// xy = Local space position of the clip center.
// zw = Scale the rect origin by this to get the outer
// corner from the segment rectangle.
flat varying vec4 vClipCenter_Sign;
// An outer and inner elliptical radii for border
// corner clipping.
flat varying vec4 vClipRadii;
// Reference point for determine edge clip lines.
flat varying vec4 vEdgeReference;
// Stores widths/2 and widths/3 to save doing this in FS.
flat varying vec4 vPartialWidths;
+// Clipping parameters for dot or dash.
+flat varying vec4 vClipParams1;
+flat varying vec4 vClipParams2;
+
// Local space position
varying vec2 vPos;
#define SEGMENT_TOP_LEFT 0
#define SEGMENT_TOP_RIGHT 1
#define SEGMENT_BOTTOM_RIGHT 2
#define SEGMENT_BOTTOM_LEFT 3
#define SEGMENT_LEFT 4
@@ -50,25 +54,31 @@ varying vec2 vPos;
#define BORDER_STYLE_DOTTED 3
#define BORDER_STYLE_DASHED 4
#define BORDER_STYLE_HIDDEN 5
#define BORDER_STYLE_GROOVE 6
#define BORDER_STYLE_RIDGE 7
#define BORDER_STYLE_INSET 8
#define BORDER_STYLE_OUTSET 9
+#define CLIP_NONE 0
+#define CLIP_DASH 1
+#define CLIP_DOT 2
+
#ifdef WR_VERTEX_SHADER
in vec2 aTaskOrigin;
in vec4 aRect;
in vec4 aColor0;
in vec4 aColor1;
in int aFlags;
in vec2 aWidths;
in vec2 aRadii;
+in vec4 aClipParams1;
+in vec4 aClipParams2;
vec2 get_outer_corner_scale(int segment) {
vec2 p;
switch (segment) {
case SEGMENT_TOP_LEFT:
p = vec2(0.0, 0.0);
break;
@@ -115,16 +125,17 @@ vec4[2] get_colors_for_side(vec4 color,
return result;
}
void main(void) {
int segment = aFlags & 0xff;
int style0 = (aFlags >> 8) & 0xff;
int style1 = (aFlags >> 16) & 0xff;
+ int clip_mode = (aFlags >> 24) & 0xff;
vec2 outer_scale = get_outer_corner_scale(segment);
vec2 outer = outer_scale * aRect.zw;
vec2 clip_sign = 1.0 - 2.0 * outer_scale;
// Set some flags used by the FS to determine the
// orientation of the two edges in this corner.
ivec2 edge_axis;
@@ -156,30 +167,45 @@ void main(void) {
case SEGMENT_LEFT:
case SEGMENT_RIGHT:
default:
edge_axis = ivec2(0, 0);
edge_reference = vec2(0.0);
break;
}
- vConfig = ivec3(
+ vConfig = ivec4(
segment,
style0 | (style1 << 16),
- edge_axis.x | (edge_axis.y << 16)
+ edge_axis.x | (edge_axis.y << 16),
+ clip_mode
);
vPartialWidths = vec4(aWidths / 3.0, aWidths / 2.0);
vPos = aRect.zw * aPosition.xy;
vColor0 = get_colors_for_side(aColor0, style0);
vColor1 = get_colors_for_side(aColor1, style1);
vClipCenter_Sign = vec4(outer + clip_sign * aRadii, clip_sign);
vClipRadii = vec4(aRadii, max(aRadii - aWidths, 0.0));
vColorLine = vec4(outer, aWidths.y * -clip_sign.y, aWidths.x * clip_sign.x);
vEdgeReference = vec4(edge_reference, edge_reference + aWidths);
+ vClipParams1 = aClipParams1;
+ vClipParams2 = aClipParams2;
+
+ // For the case of dot clips, optimize the number of pixels that
+ // are hit to just include the dot itself.
+ // TODO(gw): We should do something similar in the future for
+ // dash clips!
+ if (clip_mode == CLIP_DOT) {
+ // Expand by a small amount to allow room for AA around
+ // the dot.
+ float expanded_radius = aClipParams1.z + 2.0;
+ vPos = vClipParams1.xy + expanded_radius * (2.0 * aPosition.xy - 1.0);
+ vPos = clamp(vPos, vec2(0.0), aRect.zw);
+ }
gl_Position = uTransform * vec4(aTaskOrigin + aRect.xy + vPos, 0.0, 1.0);
}
#endif
#ifdef WR_FRAGMENT_SHADER
vec4 evaluate_color_for_style_in_corner(
vec2 clip_relative_pos,
@@ -271,32 +297,56 @@ vec4 evaluate_color_for_style_in_edge(
break;
}
return color[0];
}
void main(void) {
float aa_range = compute_aa_range(vPos);
- float d = -1.0;
vec4 color0, color1;
int segment = vConfig.x;
ivec2 style = ivec2(vConfig.y & 0xffff, vConfig.y >> 16);
ivec2 edge_axis = ivec2(vConfig.z & 0xffff, vConfig.z >> 16);
+ int clip_mode = vConfig.w;
float mix_factor = 0.0;
if (edge_axis.x != edge_axis.y) {
float d_line = distance_to_line(vColorLine.xy, vColorLine.zw, vPos);
mix_factor = distance_aa(aa_range, -d_line);
}
// Check if inside corner clip-region
vec2 clip_relative_pos = vPos - vClipCenter_Sign.xy;
bool in_clip_region = all(lessThan(vClipCenter_Sign.zw * clip_relative_pos, vec2(0.0)));
+ float d = -1.0;
+
+ switch (clip_mode) {
+ case CLIP_DOT: {
+ // Set clip distance based or dot position and radius.
+ d = distance(vClipParams1.xy, vPos) - vClipParams1.z;
+ break;
+ }
+ case CLIP_DASH: {
+ // Get SDF for the two line/tangent clip lines,
+ // do SDF subtract to get clip distance.
+ float d0 = distance_to_line(vClipParams1.xy,
+ vClipParams1.zw,
+ vPos);
+ float d1 = distance_to_line(vClipParams2.xy,
+ vClipParams2.zw,
+ vPos);
+ d = max(d0, -d1);
+ break;
+ }
+ case CLIP_NONE:
+ default:
+ break;
+ }
if (in_clip_region) {
float d_radii_a = distance_to_ellipse(clip_relative_pos, vClipRadii.xy, aa_range);
float d_radii_b = distance_to_ellipse(clip_relative_pos, vClipRadii.zw, aa_range);
float d_radii = max(d_radii_a, -d_radii_b);
d = max(d, d_radii);
color0 = evaluate_color_for_style_in_corner(
deleted file mode 100644
--- a/gfx/webrender/res/cs_clip_border.glsl
+++ /dev/null
@@ -1,204 +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/. */
-
-#include shared,clip_shared
-
-varying vec3 vPos;
-
-flat varying vec2 vClipCenter;
-
-flat varying vec4 vPoint_Tangent0;
-flat varying vec4 vPoint_Tangent1;
-flat varying vec3 vDotParams;
-flat varying vec2 vAlphaMask;
-flat varying vec4 vTaskRect;
-
-#ifdef WR_VERTEX_SHADER
-
-in vec4 aDashOrDot0;
-in vec4 aDashOrDot1;
-
-// Matches BorderCorner enum in border.rs
-#define CORNER_TOP_LEFT 0
-#define CORNER_TOP_RIGHT 1
-#define CORNER_BOTTOM_LEFT 2
-#define CORNER_BOTTOM_RIGHT 3
-
-// Matches BorderCornerClipKind enum in border.rs
-#define CLIP_MODE_DASH 0
-#define CLIP_MODE_DOT 1
-
-// Header for a border corner clip.
-struct BorderCorner {
- RectWithSize rect;
- vec2 clip_center;
- int corner;
- int clip_mode;
-};
-
-BorderCorner fetch_border_corner(ivec2 address) {
- vec4 data[2] = fetch_from_resource_cache_2_direct(address);
- return BorderCorner(RectWithSize(data[0].xy, data[0].zw),
- data[1].xy,
- int(data[1].z),
- int(data[1].w));
-}
-
-// Per-dash clip information.
-struct BorderClipDash {
- vec4 point_tangent_0;
- vec4 point_tangent_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) {
- return BorderClipDot(aDashOrDot0.xyz);
-}
-
-void main(void) {
- ClipMaskInstance cmi = fetch_clip_item();
- ClipArea area = fetch_clip_area(cmi.render_task_address);
- ClipScrollNode scroll_node = fetch_clip_scroll_node(cmi.scroll_node_id);
-
- // Fetch the header information for this corner clip.
- BorderCorner corner = fetch_border_corner(cmi.clip_data_address);
- vClipCenter = corner.clip_center;
-
- // Get local vertex position for the corner rect.
- // TODO(gw): We could reduce the number of pixels written here by calculating a tight
- // fitting bounding box of the dash itself like we do for dots below.
- vec2 pos = corner.rect.p0 + aPosition.xy * corner.rect.size;
-
- if (cmi.segment == 0) {
- // The first segment is used to zero out the border corner.
- vAlphaMask = vec2(0.0);
- vDotParams = vec3(0.0);
- vPoint_Tangent0 = vec4(1.0);
- vPoint_Tangent1 = vec4(1.0);
- } else {
- vec2 sign_modifier;
- switch (corner.corner) {
- case CORNER_TOP_LEFT:
- sign_modifier = vec2(-1.0);
- break;
- case CORNER_TOP_RIGHT:
- sign_modifier = vec2(1.0, -1.0);
- break;
- case CORNER_BOTTOM_RIGHT:
- sign_modifier = vec2(1.0);
- break;
- case CORNER_BOTTOM_LEFT:
- sign_modifier = vec2(-1.0, 1.0);
- break;
- default:
- sign_modifier = vec2(0.0);
- };
-
- 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);
- 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);
- vPoint_Tangent0 = vec4(1.0);
- vPoint_Tangent1 = vec4(1.0);
- vDotParams = vec3(cdot.center_radius.xy * sign_modifier, cdot.center_radius.z);
- vAlphaMask = vec2(1.0, 1.0);
-
- // Generate a tighter bounding rect for dots based on their position. Dot
- // centers are given relative to clip center, so we need to move the dot
- // rectangle into the clip space with an origin at the top left. First,
- // we expand the radius slightly to ensure we get full coverage on all the pixels
- // of the dots.
- float expanded_radius = cdot.center_radius.z + 2.0;
- pos = (vClipCenter + vDotParams.xy - vec2(expanded_radius));
- pos += (aPosition.xy * vec2(expanded_radius * 2.0));
- pos = clamp(pos, corner.rect.p0, corner.rect.p0 + corner.rect.size);
-
- break;
- }
- default:
- vPoint_Tangent0 = vPoint_Tangent1 = vec4(1.0);
- vDotParams = vec3(0.0);
- vAlphaMask = vec2(0.0);
- }
- }
-
- // Transform to world pos
- vec4 world_pos = scroll_node.transform * vec4(pos, 0.0, 1.0);
- world_pos.xyz /= world_pos.w;
-
- // Scale into device pixels.
- vec2 device_pos = world_pos.xy * uDevicePixelRatio;
-
- // Position vertex within the render task area.
- vec2 task_rect_origin = area.common_data.task_rect.p0;
- vec2 final_pos = device_pos - area.screen_origin + task_rect_origin;
-
- // We pass the task rectangle to the fragment shader so that we can do one last clip
- // in order to ensure that we don't draw outside the task rectangle.
- vTaskRect.xy = task_rect_origin;
- vTaskRect.zw = task_rect_origin + area.common_data.task_rect.size;
-
- // Calculate the local space position for this vertex.
- vec4 node_pos = get_node_pos(world_pos.xy, scroll_node);
- vPos = node_pos.xyw;
-
- gl_Position = uTransform * vec4(final_pos, 0.0, 1.0);
-}
-#endif
-
-#ifdef WR_FRAGMENT_SHADER
-void main(void) {
- vec2 local_pos = vPos.xy / vPos.z;
-
- // Get local space position relative to the clip center.
- vec2 clip_relative_pos = local_pos - vClipCenter;
-
- // Get the signed distances to the two clip lines.
- float d0 = distance_to_line(vPoint_Tangent0.xy,
- vPoint_Tangent0.zw,
- clip_relative_pos);
- float d1 = distance_to_line(vPoint_Tangent1.xy,
- vPoint_Tangent1.zw,
- clip_relative_pos);
-
- // Get AA widths based on zoom / scale etc.
- float aa_range = compute_aa_range(local_pos);
-
- // SDF subtract edges for dash clip
- float dash_distance = max(d0, -d1);
-
- // Get distance from dot.
- float dot_distance = distance(clip_relative_pos, vDotParams.xy) - vDotParams.z;
-
- // Select between dot/dash clip based on mode.
- float d = mix(dash_distance, dot_distance, vAlphaMask.x);
-
- // Apply AA.
- d = distance_aa(aa_range, d);
-
- // Completely mask out clip if zero'ing out the rect.
- d = d * vAlphaMask.y;
-
- // Make sure that we don't draw outside the task rectangle.
- d = d * point_inside_rect(gl_FragCoord.xy, vTaskRect.xy, vTaskRect.zw);
-
- oFragColor = vec4(d, 0.0, 0.0, 1.0);
-}
-#endif
deleted file mode 100644
--- a/gfx/webrender/res/ps_border_corner.glsl
+++ /dev/null
@@ -1,426 +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/. */
-
-#include shared,prim_shared,shared_border,ellipse
-
-// Edge color transition
-flat varying vec4 vColor00;
-flat varying vec4 vColor01;
-flat varying vec4 vColor10;
-flat varying vec4 vColor11;
-flat varying vec4 vColorEdgeLine;
-
-// Border radius
-flat varying vec2 vClipCenter;
-flat varying vec4 vRadii0;
-flat varying vec4 vRadii1;
-flat varying vec2 vClipSign;
-flat varying vec4 vEdgeDistance;
-flat varying float vSDFSelect;
-
-flat varying float vIsBorderRadiusLessThanBorderWidth;
-
-// Border style
-flat varying float vAlphaSelect;
-
-varying vec2 vLocalPos;
-
-#ifdef WR_VERTEX_SHADER
-// 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;
- }
-
- return invalid;
-}
-
-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 (style) {
- case BORDER_STYLE_RIDGE:
- case BORDER_STYLE_GROOVE:
- vRadii1.xy = radii - adjusted_widths;
- // See comment in default branch
- vRadii1.zw = vec2(-100.0);
- break;
- case BORDER_STYLE_DOUBLE:
- vRadii1.xy = get_radii(radii - adjusted_widths, -widths);
- vRadii1.zw = get_radii(radii - widths + adjusted_widths, -widths);
- break;
- default:
- // These aren't needed, so we set them to some reasonably large
- // negative value so later computations will discard them. This
- // avoids branches and numerical issues in the fragment shader.
- vRadii1.xy = vec2(-100.0);
- vRadii1.zw = vec2(-100.0);
- break;
- }
-}
-
-void set_edge_line(vec2 border_width,
- vec2 outer_corner,
- vec2 gradient_sign) {
- vec2 gradient = border_width * gradient_sign;
- vColorEdgeLine = vec4(outer_corner, vec2(-gradient.y, gradient.x));
-}
-
-void write_color(vec4 color0, vec4 color1, int style, vec2 delta, int instance_kind) {
- vec4 modulate;
-
- switch (style) {
- case BORDER_STYLE_GROOVE:
- modulate = vec4(1.0 - 0.3 * delta.x,
- 1.0 + 0.3 * delta.x,
- 1.0 - 0.3 * delta.y,
- 1.0 + 0.3 * delta.y);
-
- break;
- case BORDER_STYLE_RIDGE:
- modulate = vec4(1.0 + 0.3 * delta.x,
- 1.0 - 0.3 * delta.x,
- 1.0 + 0.3 * delta.y,
- 1.0 - 0.3 * delta.y);
- break;
- default:
- modulate = vec4(1.0);
- 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;
- default: break;
- }
-
- vColor00 = vec4(clamp(color0.rgb * modulate.x, vec3(0.0), vec3(color0.a)), color0.a);
- vColor01 = vec4(clamp(color0.rgb * modulate.y, vec3(0.0), vec3(color0.a)), color0.a);
- vColor10 = vec4(clamp(color1.rgb * modulate.z, vec3(0.0), vec3(color1.a)), color1.a);
- vColor11 = vec4(clamp(color1.rgb * modulate.w, vec3(0.0), vec3(color1.a)), color1.a);
-}
-
-int select_style(int color_select, vec2 fstyle) {
- ivec2 style = ivec2(fstyle);
-
- switch (color_select) {
- case SIDE_BOTH:
- {
- // TODO(gw): A temporary hack! While we don't support
- // border corners that have dots or dashes
- // with another style, pretend they are solid
- // border corners.
- bool has_dots = style.x == BORDER_STYLE_DOTTED ||
- style.y == BORDER_STYLE_DOTTED;
- bool has_dashes = style.x == BORDER_STYLE_DASHED ||
- style.y == BORDER_STYLE_DASHED;
- if (style.x != style.y && (has_dots || has_dashes))
- return BORDER_STYLE_SOLID;
- return style.x;
- }
- case SIDE_FIRST:
- return style.x;
- case SIDE_SECOND:
- return style.y;
- default:
- return 0;
- }
-}
-
-void main(void) {
- Primitive prim = load_primitive();
- Border border = fetch_border(prim.specific_prim_address);
- int sub_part = prim.user_data0;
- BorderCorners corners = get_border_corners(border, prim.local_rect);
-
- vec2 p0, p1;
-
- // TODO(gw): We'll need to pass through multiple styles
- // once we support style transitions per corner.
- int style;
- vec4 edge_distances;
- vec4 color0, color1;
- vec2 color_delta;
- vec4 edge_mask;
-
- // TODO(gw): Now that all border styles are supported, the
- // statement below can be tidied up quite a bit.
-
- switch (sub_part) {
- case 0: {
- p0 = corners.tl_outer;
- p1 = corners.tl_inner;
- color0 = border.colors[0];
- color1 = border.colors[1];
- vClipCenter = corners.tl_outer + border.radii[0].xy;
- vClipSign = vec2(1.0);
- style = select_style(prim.user_data1, 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));
- edge_distances = vec4(p0 + adjusted_widths.xy,
- p0 + inv_adjusted_widths.xy);
- color_delta = vec2(1.0);
- vIsBorderRadiusLessThanBorderWidth = any(lessThan(border.radii[0].xy,
- border.widths.xy)) ? 1.0 : 0.0;
- edge_mask = vec4(1.0, 1.0, 0.0, 0.0);
- break;
- }
- case 1: {
- p0 = vec2(corners.tr_inner.x, corners.tr_outer.y);
- p1 = vec2(corners.tr_outer.x, corners.tr_inner.y);
- color0 = border.colors[1];
- color1 = border.colors[2];
- vClipCenter = corners.tr_outer + vec2(-border.radii[0].z, border.radii[0].w);
- vClipSign = vec2(-1.0, 1.0);
- style = select_style(prim.user_data1, 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));
- edge_distances = vec4(p1.x - adjusted_widths.z,
- p0.y + adjusted_widths.y,
- p1.x - border.widths.z + adjusted_widths.z,
- p0.y + inv_adjusted_widths.y);
- color_delta = vec2(1.0, -1.0);
- vIsBorderRadiusLessThanBorderWidth = any(lessThan(border.radii[0].zw,
- border.widths.zy)) ? 1.0 : 0.0;
- edge_mask = vec4(0.0, 1.0, 1.0, 0.0);
- break;
- }
- case 2: {
- p0 = corners.br_inner;
- p1 = corners.br_outer;
- color0 = border.colors[2];
- color1 = border.colors[3];
- vClipCenter = corners.br_outer - border.radii[1].xy;
- vClipSign = vec2(-1.0, -1.0);
- style = select_style(prim.user_data1, 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));
- edge_distances = vec4(p1.x - adjusted_widths.z,
- p1.y - adjusted_widths.w,
- p1.x - border.widths.z + adjusted_widths.z,
- p1.y - border.widths.w + adjusted_widths.w);
- color_delta = vec2(-1.0);
- vIsBorderRadiusLessThanBorderWidth = any(lessThan(border.radii[1].xy,
- border.widths.zw)) ? 1.0 : 0.0;
- edge_mask = vec4(0.0, 0.0, 1.0, 1.0);
- break;
- }
- case 3: {
- p0 = vec2(corners.bl_outer.x, corners.bl_inner.y);
- p1 = vec2(corners.bl_inner.x, corners.bl_outer.y);
- color0 = border.colors[3];
- color1 = border.colors[0];
- vClipCenter = corners.bl_outer + vec2(border.radii[1].z, -border.radii[1].w);
- vClipSign = vec2(1.0, -1.0);
- style = select_style(prim.user_data1, 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));
- edge_distances = vec4(p0.x + adjusted_widths.x,
- p1.y - adjusted_widths.w,
- p0.x + inv_adjusted_widths.x,
- p1.y - border.widths.w + adjusted_widths.w);
- color_delta = vec2(-1.0, 1.0);
- vIsBorderRadiusLessThanBorderWidth = any(lessThan(border.radii[1].zw,
- border.widths.xw)) ? 1.0 : 0.0;
- edge_mask = vec4(1.0, 0.0, 0.0, 1.0);
- break;
- }
- default:
- p0 = p1 = vec2(0.0);
- color0 = color1 = vec4(1.0);
- vClipCenter = vClipSign = vec2(0.0);
- style = 0;
- edge_distances = edge_mask = vec4(0.0);
- color_delta = vec2(0.0);
- vIsBorderRadiusLessThanBorderWidth = 0.0;
- }
-
- switch (style) {
- case BORDER_STYLE_DOUBLE: {
- vEdgeDistance = edge_distances;
- vAlphaSelect = 0.0;
- vSDFSelect = 0.0;
- break;
- }
- case BORDER_STYLE_GROOVE:
- case BORDER_STYLE_RIDGE:
- vEdgeDistance = vec4(edge_distances.xy, 0.0, 0.0);
- vAlphaSelect = 1.0;
- vSDFSelect = 1.0;
- break;
- case BORDER_STYLE_DOTTED:
- // Disable normal clip radii for dotted corners, since
- // all the clipping is handled by the clip mask.
- vClipSign = vec2(0.0);
- vEdgeDistance = vec4(0.0);
- vAlphaSelect = 1.0;
- vSDFSelect = 0.0;
- break;
- default: {
- vEdgeDistance = vec4(0.0);
- vAlphaSelect = 1.0;
- vSDFSelect = 0.0;
- break;
- }
- }
-
- write_color(color0, color1, style, color_delta, prim.user_data1);
-
- RectWithSize segment_rect;
- segment_rect.p0 = p0;
- segment_rect.size = p1 - p0;
-
-#ifdef WR_FEATURE_TRANSFORM
- VertexInfo vi = write_transform_vertex(segment_rect,
- prim.local_rect,
- prim.local_clip_rect,
- edge_mask,
- prim.z,
- prim.scroll_node,
- prim.task,
- true);
-#else
- VertexInfo vi = write_vertex(segment_rect,
- prim.local_clip_rect,
- prim.z,
- prim.scroll_node,
- prim.task,
- prim.local_rect);
-#endif
-
- vLocalPos = vi.local_pos;
- write_clip(vi.screen_pos, prim.clip_area);
-}
-#endif
-
-#ifdef WR_FRAGMENT_SHADER
-void main(void) {
- float alpha = 1.0;
-#ifdef WR_FEATURE_TRANSFORM
- alpha = init_transform_fs(vLocalPos);
-#endif
-
- alpha *= do_clip();
-
- float aa_range = compute_aa_range(vLocalPos);
-
- float distance_for_color;
- float color_mix_factor;
-
- // Only apply the clip AA if inside the clip region. This is
- // necessary for correctness when the border width is greater
- // than the border radius.
- if (vIsBorderRadiusLessThanBorderWidth == 0.0 ||
- all(lessThan(vLocalPos * vClipSign, vClipCenter * vClipSign))) {
- vec2 p = vLocalPos - vClipCenter;
-
- // The coordinate system is snapped to pixel boundaries. To sample the distance,
- // however, we are interested in the center of the pixels which introduces an
- // error of half a pixel towards the exterior of the curve (See issue #1750).
- // This error is corrected by offsetting the distance by half a device pixel.
- // This not entirely correct: it leaves an error that varries between
- // 0 and (sqrt(2) - 1)/2 = 0.2 pixels but it is hardly noticeable and is better
- // than the constant sqrt(2)/2 px error without the correction.
- // To correct this exactly we would need to offset p by half a pixel in the
- // direction of the center of the ellipse (a different offset for each corner).
-
- // Get signed distance from the inner/outer clips.
- float d0 = distance_to_ellipse(p, vRadii0.xy, aa_range);
- float d1 = distance_to_ellipse(p, vRadii0.zw, aa_range);
- float d2 = distance_to_ellipse(p, vRadii1.xy, aa_range);
- float d3 = distance_to_ellipse(p, vRadii1.zw, aa_range);
-
- // SDF subtract main radii
- float d_main = max(d0, -d1);
-
- // SDF subtract inner radii (double style borders)
- float d_inner = max(d2, -d3);
-
- // Select how to combine the SDF based on border style.
- float d = mix(max(d_main, -d_inner), d_main, vSDFSelect);
-
- // Only apply AA to fragments outside the signed distance field.
- alpha = min(alpha, distance_aa(aa_range, d));
-
- // Get the groove/ridge mix factor.
- color_mix_factor = distance_aa(aa_range, d2);
- } else {
- // Handle the case where the fragment is outside the clip
- // region in a corner. This occurs when border width is
- // greater than border radius.
-
- // Get linear distances along horizontal and vertical edges.
- vec2 d0 = vClipSign.xx * (vLocalPos.xx - vEdgeDistance.xz);
- vec2 d1 = vClipSign.yy * (vLocalPos.yy - vEdgeDistance.yw);
- // Apply union to get the outer edge signed distance.
- float da = min(d0.x, d1.x);
- // Apply intersection to get the inner edge signed distance.
- float db = max(-d0.y, -d1.y);
- // Apply union to get both edges.
- float d = min(da, db);
- // Select fragment on/off based on signed distance.
- // No AA here, since we know we're on a straight edge
- // and the width is rounded to a whole CSS pixel.
- alpha = min(alpha, mix(vAlphaSelect, 1.0, d < 0.0));
-
- // Get the groove/ridge mix factor.
- // TODO(gw): Support AA for groove/ridge border edge with transforms.
- color_mix_factor = mix(0.0, 1.0, da > 0.0);
- }
-
- // Mix inner/outer color.
- vec4 color0 = mix(vColor00, vColor01, color_mix_factor);
- vec4 color1 = mix(vColor10, vColor11, color_mix_factor);
-
- // Select color based on side of line. Get distance from the
- // reference line, and then apply AA along the edge.
- float ld = distance_to_line(vColorEdgeLine.xy, vColorEdgeLine.zw, vLocalPos);
- float m = distance_aa(aa_range, -ld);
- vec4 color = mix(color0, color1, m);
-
- oFragColor = color * alpha;
-}
-#endif
deleted file mode 100644
--- a/gfx/webrender/res/ps_border_edge.glsl
+++ /dev/null
@@ -1,334 +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/. */
-
-#include shared,prim_shared,shared_border
-
-flat varying vec4 vColor0;
-flat varying vec4 vColor1;
-flat varying vec2 vEdgeDistance;
-flat varying float vAxisSelect;
-flat varying float vAlphaSelect;
-flat varying vec4 vClipParams;
-flat varying float vClipSelect;
-
-varying vec2 vLocalPos;
-
-#ifdef WR_VERTEX_SHADER
-void write_edge_distance(float p0,
- float original_width,
- float adjusted_width,
- float style,
- float axis_select,
- float sign_adjust) {
- switch (int(style)) {
- case BORDER_STYLE_DOUBLE:
- vEdgeDistance = vec2(p0 + adjusted_width,
- p0 + original_width - adjusted_width);
- break;
- case BORDER_STYLE_GROOVE:
- case BORDER_STYLE_RIDGE:
- vEdgeDistance = vec2(p0 + adjusted_width, sign_adjust);
- break;
- default:
- vEdgeDistance = vec2(0.0);
- break;
- }
-
- vAxisSelect = axis_select;
-}
-
-void write_alpha_select(float style) {
- switch (int(style)) {
- case BORDER_STYLE_DOUBLE:
- vAlphaSelect = 0.0;
- break;
- default:
- vAlphaSelect = 1.0;
- break;
- }
-}
-
-// write_color function is duplicated to work around a Mali-T880 GPU driver program link error.
-// See https://github.com/servo/webrender/issues/1403 for more info.
-// TODO: convert back to a single function once the driver issues are resolved, if ever.
-void write_color0(vec4 color, float style, bool flip) {
- vec2 modulate;
-
- switch (int(style)) {
- case BORDER_STYLE_GROOVE:
- {
- modulate = flip ? vec2(1.3, 0.7) : vec2(0.7, 1.3);
- break;
- }
- case BORDER_STYLE_RIDGE:
- {
- modulate = flip ? vec2(0.7, 1.3) : vec2(1.3, 0.7);
- break;
- }
- default:
- modulate = vec2(1.0);
- break;
- }
-
- vColor0 = vec4(min(color.rgb * modulate.x, vec3(color.a)), color.a);
-}
-
-void write_color1(vec4 color, float style, bool flip) {
- vec2 modulate;
-
- switch (int(style)) {
- case BORDER_STYLE_GROOVE:
- {
- modulate = flip ? vec2(1.3, 0.7) : vec2(0.7, 1.3);
- break;
- }
- case BORDER_STYLE_RIDGE:
- {
- modulate = flip ? vec2(0.7, 1.3) : vec2(1.3, 0.7);
- break;
- }
- default:
- modulate = vec2(1.0);
- break;
- }
-
- vColor1 = vec4(min(color.rgb * modulate.y, vec3(color.a)), color.a);
-}
-
-void write_clip_params(float style,
- float border_width,
- float edge_length,
- float edge_offset,
- float center_line,
- bool start_corner_has_radius,
- bool end_corner_has_radius) {
- // x = offset
- // y = dash on + off length
- // z = dash length
- // w = center line of edge cross-axis (for dots only)
- switch (int(style)) {
- case BORDER_STYLE_DASHED: {
- float desired_dash_length = border_width * 3.0;
- // Consider half total length since there is an equal on/off for each dash.
- float dash_count = ceil(0.5 * edge_length / desired_dash_length);
- float dash_length = 0.5 * edge_length / dash_count;
- vClipParams = vec4(edge_offset - 0.5 * dash_length,
- 2.0 * dash_length,
- dash_length,
- 0.0);
- vClipSelect = 0.0;
- break;
- }
- case BORDER_STYLE_DOTTED: {
- float diameter = border_width;
- float radius = 0.5 * diameter;
-
- // If this edge connects a corner with a radius to a corner without a radius, we
- // act as if we have space for one more dot. This will position the dots so that
- // there is a half dot on one of the ends.
- float full_edge_length = edge_length +
- (float(start_corner_has_radius ^^ end_corner_has_radius) * diameter);
-
- float dot_count = ceil(0.5 * full_edge_length / diameter);
- float empty_space = full_edge_length - (dot_count * diameter);
- float distance_between_centers = diameter + empty_space / dot_count;
-
- // If the starting corner has a radius, we want to position the half dot right
- // against that edge.
- float starting_offset =
- edge_offset + radius + (-diameter * float(start_corner_has_radius));
-
- vClipParams = vec4(starting_offset,
- distance_between_centers,
- radius,
- center_line);
-
- vClipSelect = 1.0;
- break;
- }
- default:
- vClipParams = vec4(1.0);
- vClipSelect = 0.0;
- break;
- }
-}
-
-bool hasRadius(vec2 radius) {
- return any(notEqual(radius, vec2(0.0)));
-}
-
-void main(void) {
- Primitive prim = load_primitive();
- Border border = fetch_border(prim.specific_prim_address);
- int sub_part = prim.user_data0;
- BorderCorners corners = get_border_corners(border, prim.local_rect);
- vec4 color = border.colors[sub_part];
-
- // TODO(gw): Now that all border styles are supported, the
- // statement below can be tidied up quite a bit.
-
- float style;
- bool color_flip;
-
- RectWithSize segment_rect;
- vec4 edge_mask;
-
- switch (sub_part) {
- 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);
- style = border.style.x;
- color_flip = false;
- write_clip_params(border.style.x,
- border.widths.x,
- segment_rect.size.y,
- segment_rect.p0.y,
- segment_rect.p0.x + 0.5 * segment_rect.size.x,
- hasRadius(border.radii[0].xy),
- hasRadius(border.radii[1].zw));
- edge_mask = vec4(1.0, 0.0, 1.0, 0.0);
- break;
- }
- 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);
- style = border.style.y;
- color_flip = false;
- write_clip_params(border.style.y,
- border.widths.y,
- segment_rect.size.x,
- segment_rect.p0.x,
- segment_rect.p0.y + 0.5 * segment_rect.size.y,
- hasRadius(border.radii[0].xy),
- hasRadius(border.radii[0].zw));
- edge_mask = vec4(0.0, 1.0, 0.0, 1.0);
- break;
- }
- 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);
- style = border.style.z;
- color_flip = true;
- write_clip_params(border.style.z,
- border.widths.z,
- segment_rect.size.y,
- segment_rect.p0.y,
- segment_rect.p0.x + 0.5 * segment_rect.size.x,
- hasRadius(border.radii[0].zw),
- hasRadius(border.radii[1].xy));
- edge_mask = vec4(1.0, 0.0, 1.0, 0.0);
- break;
- }
- 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);
- style = border.style.w;
- color_flip = true;
- write_clip_params(border.style.w,
- border.widths.w,
- segment_rect.size.x,
- segment_rect.p0.x,
- segment_rect.p0.y + 0.5 * segment_rect.size.y,
- hasRadius(border.radii[1].zw),
- hasRadius(border.radii[1].xy));
- edge_mask = vec4(0.0, 1.0, 0.0, 1.0);
- break;
- }
- default:
- segment_rect.p0 = segment_rect.size = vec2(0.0);
- style = 0.0;
- color_flip = false;
- edge_mask = vec4(0.0);
- }
-
- write_alpha_select(style);
- write_color0(color, style, color_flip);
- write_color1(color, style, color_flip);
-
-#ifdef WR_FEATURE_TRANSFORM
- VertexInfo vi = write_transform_vertex(segment_rect,
- prim.local_rect,
- prim.local_clip_rect,
- edge_mask,
- prim.z,
- prim.scroll_node,
- prim.task,
- true);
-#else
- VertexInfo vi = write_vertex(segment_rect,
- prim.local_clip_rect,
- prim.z,
- prim.scroll_node,
- prim.task,
- prim.local_rect);
-#endif
-
- vLocalPos = vi.local_pos;
- write_clip(vi.screen_pos, prim.clip_area);
-}
-#endif
-
-#ifdef WR_FRAGMENT_SHADER
-void main(void) {
- float alpha = 1.0;
-#ifdef WR_FEATURE_TRANSFORM
- alpha = init_transform_fs(vLocalPos);
-#endif
-
- alpha *= do_clip();
-
- // Find the appropriate distance to apply the step over.
- float aa_range = compute_aa_range(vLocalPos);
-
- // Applies the math necessary to draw a style: double
- // border. In the case of a solid border, the vertex
- // shader sets interpolator values that make this have
- // no effect.
-
- // Select the x/y coord, depending on which axis this edge is.
- vec2 pos = mix(vLocalPos.xy, vLocalPos.yx, vAxisSelect);
-
- // Get signed distance from each of the inner edges.
- float d0 = pos.x - vEdgeDistance.x;
- float d1 = vEdgeDistance.y - pos.x;
-
- // SDF union to select both outer edges.
- float d = min(d0, d1);
-
- // Select fragment on/off based on signed distance.
- // No AA here, since we know we're on a straight edge
- // and the width is rounded to a whole CSS pixel.
- alpha = min(alpha, mix(vAlphaSelect, 1.0, d < 0.0));
-
- // Mix color based on first distance.
- // TODO(gw): Support AA for groove/ridge border edge with transforms.
- vec4 color = mix(vColor0, vColor1, bvec4(d0 * vEdgeDistance.y > 0.0));
-
- // Apply dashing / dotting parameters.
-
- // Get the main-axis position relative to closest dot or dash.
- float x = mod(pos.y - vClipParams.x, vClipParams.y);
-
- // Calculate dash alpha (on/off) based on dash length
- float dash_alpha = step(x, vClipParams.z);
-
- // Get the dot alpha
- vec2 dot_relative_pos = vec2(x, pos.x) - vClipParams.zw;
- float dot_distance = length(dot_relative_pos) - vClipParams.z;
- float dot_alpha = distance_aa(aa_range, dot_distance);
-
- // Select between dot/dash alpha based on clip mode.
- alpha = min(alpha, mix(dash_alpha, dot_alpha, vClipSelect));
-
- oFragColor = color * alpha;
-}
-#endif
deleted file mode 100644
--- a/gfx/webrender/res/shared_border.glsl
+++ /dev/null
@@ -1,98 +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/. */
-
-// Border styles as defined in webrender_api/types.rs
-#define BORDER_STYLE_NONE 0
-#define BORDER_STYLE_SOLID 1
-#define BORDER_STYLE_DOUBLE 2
-#define BORDER_STYLE_DOTTED 3
-#define BORDER_STYLE_DASHED 4
-#define BORDER_STYLE_HIDDEN 5
-#define BORDER_STYLE_GROOVE 6
-#define BORDER_STYLE_RIDGE 7
-#define BORDER_STYLE_INSET 8
-#define BORDER_STYLE_OUTSET 9
-
-#ifdef WR_VERTEX_SHADER
-
-struct Border {
- vec4 style;
- vec4 widths;
- vec4 colors[4];
- vec4 radii[2];
-};
-
-struct BorderCorners {
- vec2 tl_outer;
- vec2 tl_inner;
- vec2 tr_outer;
- vec2 tr_inner;
- vec2 br_outer;
- vec2 br_inner;
- vec2 bl_outer;
- vec2 bl_inner;
-};
-
-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.
-
- // The CSS spec doesn't define what width each of the segments
- // in a style: double border should be. It only says that the
- // sum of the segments should be equal to the total border
- // width. We pick to make the segments (almost) equal thirds
- // for now - we can adjust this if we find other browsers pick
- // different values in some cases.
- // SEE: https://drafts.csswg.org/css-backgrounds-3/#double
- return max(floor(0.5 + border.widths / 3.0), 1.0);
- case BORDER_STYLE_GROOVE:
- case BORDER_STYLE_RIDGE:
- return floor(0.5 + border.widths * 0.5);
- default:
- return border.widths;
- }
-}
-
-Border fetch_border(int address) {
- vec4 data[8] = fetch_from_resource_cache_8(address);
- return Border(data[0], data[1],
- vec4[4](data[2], data[3], data[4], data[5]),
- vec4[2](data[6], data[7]));
-}
-
-BorderCorners get_border_corners(Border border, RectWithSize local_rect) {
- vec2 tl_outer = local_rect.p0;
- vec2 tl_inner = tl_outer + vec2(max(border.radii[0].x, border.widths.x),
- max(border.radii[0].y, border.widths.y));
-
- vec2 tr_outer = vec2(local_rect.p0.x + local_rect.size.x,
- local_rect.p0.y);
- vec2 tr_inner = tr_outer + vec2(-max(border.radii[0].z, border.widths.z),
- max(border.radii[0].w, border.widths.y));
-
- vec2 br_outer = vec2(local_rect.p0.x + local_rect.size.x,
- local_rect.p0.y + local_rect.size.y);
- vec2 br_inner = br_outer - vec2(max(border.radii[1].x, border.widths.z),
- max(border.radii[1].y, border.widths.w));
-
- vec2 bl_outer = vec2(local_rect.p0.x,
- local_rect.p0.y + local_rect.size.y);
- vec2 bl_inner = bl_outer + vec2(max(border.radii[1].z, border.widths.x),
- -max(border.radii[1].w, border.widths.w));
-
- return BorderCorners(
- tl_outer,
- tl_inner,
- tr_outer,
- tr_inner,
- br_outer,
- br_inner,
- bl_outer,
- bl_inner
- );
-}
-
-#endif
--- a/gfx/webrender/src/batch.rs
+++ b/gfx/webrender/src/batch.rs
@@ -1,33 +1,32 @@
/* 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/. */
use api::{AlphaType, ClipMode, DeviceIntRect, DeviceIntSize};
use api::{DeviceUintRect, DeviceUintPoint, ExternalImageType, FilterOp, ImageRendering, LayoutRect};
use api::{DeviceIntPoint, YuvColorSpace, YuvFormat};
use api::{LayoutToWorldTransform, WorldPixel};
-use border::{BorderCornerInstance, BorderCornerSide, BorderEdgeKind};
use clip::{ClipSource, ClipStore, ClipWorkItem};
use clip_scroll_tree::{CoordinateSystemId};
use euclid::{TypedTransform3D, vec3};
use glyph_rasterizer::GlyphFormat;
-use gpu_cache::{GpuCache, GpuCacheAddress};
-use gpu_types::{BrushFlags, BrushInstance, ClipChainRectIndex, ClipMaskBorderCornerDotDash};
+use gpu_cache::{GpuCache, GpuCacheHandle, GpuCacheAddress};
+use gpu_types::{BrushFlags, BrushInstance, ClipChainRectIndex};
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 plane_split::{BspSplitter, Polygon, Splitter};
-use prim_store::{BrushKind, BrushPrimitive, BrushSegmentTaskId, CachedGradient, DeferredResolve};
+use prim_store::{BrushKind, BrushPrimitive, BrushSegmentTaskId, DeferredResolve};
use prim_store::{EdgeAaSegmentMask, ImageSource, PictureIndex, PrimitiveIndex, PrimitiveKind};
use prim_store::{PrimitiveMetadata, PrimitiveRun, PrimitiveStore, VisibleGradientTile};
-use prim_store::{BorderSource, CachedGradientIndex};
+use prim_store::{BorderSource};
use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskKind, RenderTaskTree};
use renderer::{BlendMode, ImageBufferKind};
use renderer::{BLOCKS_PER_UV_RECT, ShaderColorMode};
use resource_cache::{CacheItem, GlyphFetchResult, ImageRequest, ResourceCache};
use scene::FilterOpHelpers;
use std::{usize, f32, i32};
use tiling::{RenderTargetContext};
use util::{MatrixHelpers, TransformedRectKind};
@@ -36,18 +35,16 @@ use util::{MatrixHelpers, TransformedRec
// a dummy task that doesn't mask out anything.
const OPAQUE_TASK_ADDRESS: RenderTaskAddress = RenderTaskAddress(0x7fff);
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum TransformBatchKind {
TextRun(GlyphFormat),
- BorderCorner,
- BorderEdge,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum BrushBatchKind {
Solid,
Image(ImageBufferKind),
@@ -317,16 +314,34 @@ impl BatchList {
BlendMode::SubpixelWithBgColor |
BlendMode::SubpixelDualSource => {
self.alpha_batch_list
.get_suitable_batch(key, task_relative_bounding_rect)
}
}
}
+ // Remove any batches that were added but didn't get any instances
+ // added to them.
+ fn remove_unused_batches(&mut self) {
+ if self.opaque_batch_list
+ .batches
+ .last()
+ .map_or(false, |batch| batch.instances.is_empty()) {
+ self.opaque_batch_list.batches.pop().unwrap();
+ }
+
+ if self.alpha_batch_list
+ .batches
+ .last()
+ .map_or(false, |batch| batch.instances.is_empty()) {
+ self.alpha_batch_list.batches.pop().unwrap();
+ }
+ }
+
fn finalize(&mut self) {
self.opaque_batch_list.finalize()
}
}
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct PrimitiveBatch {
@@ -625,17 +640,16 @@ impl AlphaBatchBuilder {
};
let prim_cache_address = if is_multiple_primitives {
GpuCacheAddress::invalid()
} else {
gpu_cache.get_address(&prim_metadata.gpu_location)
};
- let no_textures = BatchTextures::no_texture();
let clip_task_address = prim_metadata
.clip_task_id
.map_or(OPAQUE_TASK_ADDRESS, |id| render_tasks.get_task_address(id));
let base_instance = SimplePrimitiveInstance::new(
prim_cache_address,
task_address,
clip_task_address,
clip_chain_rect_index,
@@ -1016,56 +1030,53 @@ impl AlphaBatchBuilder {
task_address,
z,
user_data,
tile.edge_flags
);
}
}
}
- BrushKind::LinearGradient { gradient_index, ref visible_tiles, .. } if !visible_tiles.is_empty() => {
+ BrushKind::LinearGradient { ref stops_handle, ref visible_tiles, .. } if !visible_tiles.is_empty() => {
add_gradient_tiles(
visible_tiles,
- gradient_index,
+ stops_handle,
BrushBatchKind::LinearGradient,
specified_blend_mode,
&task_relative_bounding_rect,
clip_chain_rect_index,
scroll_id,
task_address,
clip_task_address,
z,
- ctx,
gpu_cache,
&mut self.batch_list,
);
}
- BrushKind::RadialGradient { gradient_index, ref visible_tiles, .. } if !visible_tiles.is_empty() => {
+ BrushKind::RadialGradient { ref stops_handle, ref visible_tiles, .. } if !visible_tiles.is_empty() => {
add_gradient_tiles(
visible_tiles,
- gradient_index,
+ stops_handle,
BrushBatchKind::RadialGradient,
specified_blend_mode,
&task_relative_bounding_rect,
clip_chain_rect_index,
scroll_id,
task_address,
clip_task_address,
z,
- ctx,
gpu_cache,
&mut self.batch_list,
);
}
_ => {
if let Some((batch_kind, textures, user_data)) = brush.get_batch_params(
ctx.resource_cache,
gpu_cache,
deferred_resolves,
- ctx.cached_gradients,
) {
self.add_brush_to_batch(
brush,
prim_metadata,
batch_kind,
specified_blend_mode,
non_segmented_blend_mode,
textures,
@@ -1079,77 +1090,16 @@ impl AlphaBatchBuilder {
z,
render_tasks,
user_data,
);
}
}
}
}
- PrimitiveKind::Border => {
- let border_cpu =
- &ctx.prim_store.cpu_borders[prim_metadata.cpu_prim_index.0];
- // TODO(gw): Select correct blend mode for edges and corners!!
-
- if border_cpu.corner_instances.iter().any(|&kind| kind != BorderCornerInstance::None) {
- let corner_kind = BatchKind::Transformable(
- transform_kind,
- TransformBatchKind::BorderCorner,
- );
- let corner_key = BatchKey::new(corner_kind, non_segmented_blend_mode, no_textures);
- let batch = self.batch_list
- .get_suitable_batch(corner_key, &task_relative_bounding_rect);
-
- for (i, instance_kind) in border_cpu.corner_instances.iter().enumerate() {
- let sub_index = i as i32;
- match *instance_kind {
- BorderCornerInstance::None => {}
- BorderCornerInstance::Single => {
- batch.push(base_instance.build(
- sub_index,
- BorderCornerSide::Both as i32,
- 0,
- ));
- }
- BorderCornerInstance::Double => {
- batch.push(base_instance.build(
- sub_index,
- BorderCornerSide::First as i32,
- 0,
- ));
- batch.push(base_instance.build(
- sub_index,
- BorderCornerSide::Second as i32,
- 0,
- ));
- }
- }
- }
- }
-
- if border_cpu.edges.iter().any(|&kind| kind != BorderEdgeKind::None) {
- let edge_kind = BatchKind::Transformable(
- transform_kind,
- TransformBatchKind::BorderEdge,
- );
- let edge_key = BatchKey::new(edge_kind, non_segmented_blend_mode, no_textures);
- let batch = self.batch_list
- .get_suitable_batch(edge_key, &task_relative_bounding_rect);
-
- for (border_segment, instance_kind) in border_cpu.edges.iter().enumerate() {
- match *instance_kind {
- BorderEdgeKind::None => {},
- BorderEdgeKind::Solid |
- BorderEdgeKind::Clip => {
- batch.push(base_instance.build(border_segment as i32, 0, 0));
- }
- }
- }
- }
- }
PrimitiveKind::TextRun => {
let text_cpu =
&ctx.prim_store.cpu_text_runs[prim_metadata.cpu_prim_index.0];
let font = text_cpu.get_font(
ctx.device_pixel_scale,
Some(scroll_node.transform),
);
@@ -1374,45 +1324,45 @@ impl AlphaBatchBuilder {
blend_mode: non_segmented_blend_mode,
kind: BatchKind::Brush(batch_kind),
textures,
};
let batch = self.batch_list.get_suitable_batch(batch_key, task_relative_bounding_rect);
batch.push(PrimitiveInstance::from(base_instance));
}
}
+
+ self.batch_list.remove_unused_batches();
}
}
fn add_gradient_tiles(
visible_tiles: &[VisibleGradientTile],
- gradient_index: CachedGradientIndex,
+ stops_handle: &GpuCacheHandle,
kind: BrushBatchKind,
blend_mode: BlendMode,
task_relative_bounding_rect: &DeviceIntRect,
clip_chain_rect_index: ClipChainRectIndex,
scroll_id: ClipScrollNodeIndex,
task_address: RenderTaskAddress,
clip_task_address: RenderTaskAddress,
z: ZBufferId,
- ctx: &RenderTargetContext,
gpu_cache: &GpuCache,
batch_list: &mut BatchList,
) {
batch_list.add_bounding_rect(task_relative_bounding_rect);
let batch = batch_list.get_suitable_batch(
BatchKey {
blend_mode: blend_mode,
kind: BatchKind::Brush(kind),
textures: BatchTextures::no_texture(),
},
task_relative_bounding_rect
);
- let stops_handle = &ctx.cached_gradients[gradient_index.0].handle;
let user_data = [stops_handle.as_int(gpu_cache), 0, 0];
let base_instance = BrushInstance {
picture_address: task_address,
prim_address: GpuCacheAddress::invalid(),
clip_chain_rect_index,
scroll_id,
clip_task_address,
@@ -1476,17 +1426,16 @@ impl BrushPrimitive {
}
}
fn get_batch_params(
&self,
resource_cache: &ResourceCache,
gpu_cache: &mut GpuCache,
deferred_resolves: &mut Vec<DeferredResolve>,
- cached_gradients: &[CachedGradient],
) -> Option<(BrushBatchKind, BatchTextures, [i32; 3])> {
match self.kind {
BrushKind::Image { request, ref source, .. } => {
let cache_item = match *source {
ImageSource::Default => {
resolve_image(
request,
resource_cache,
@@ -1570,30 +1519,28 @@ impl BrushPrimitive {
}
BrushKind::Clear => {
Some((
BrushBatchKind::Solid,
BatchTextures::no_texture(),
[0; 3],
))
}
- BrushKind::RadialGradient { gradient_index, .. } => {
- let stops_handle = &cached_gradients[gradient_index.0].handle;
+ BrushKind::RadialGradient { ref stops_handle, .. } => {
Some((
BrushBatchKind::RadialGradient,
BatchTextures::no_texture(),
[
stops_handle.as_int(gpu_cache),
0,
0,
],
))
}
- BrushKind::LinearGradient { gradient_index, .. } => {
- let stops_handle = &cached_gradients[gradient_index.0].handle;
+ BrushKind::LinearGradient { ref stops_handle, .. } => {
Some((
BrushBatchKind::LinearGradient,
BatchTextures::no_texture(),
[
stops_handle.as_int(gpu_cache),
0,
0,
],
@@ -1663,17 +1610,16 @@ trait AlphaBatchHelpers {
metadata: &PrimitiveMetadata,
) -> BlendMode;
}
impl AlphaBatchHelpers for PrimitiveStore {
fn get_blend_mode(&self, metadata: &PrimitiveMetadata) -> BlendMode {
match metadata.prim_kind {
// Can only resolve the TextRun's blend mode once glyphs are fetched.
- PrimitiveKind::Border |
PrimitiveKind::TextRun => {
BlendMode::PremultipliedAlpha
}
PrimitiveKind::Brush => {
let brush = &self.cpu_brushes[metadata.cpu_prim_index.0];
match brush.kind {
BrushKind::Clear => {
@@ -1824,29 +1770,25 @@ fn make_polygon(
#[derive(Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct ClipBatcher {
/// Rectangle draws fill up the rectangles with rounded corners.
pub rectangles: Vec<ClipMaskInstance>,
/// Image draws apply the image masking.
pub images: FastHashMap<SourceTexture, Vec<ClipMaskInstance>>,
- pub border_clears: Vec<ClipMaskBorderCornerDotDash>,
- pub borders: Vec<ClipMaskBorderCornerDotDash>,
pub box_shadows: FastHashMap<SourceTexture, Vec<ClipMaskInstance>>,
pub line_decorations: Vec<ClipMaskInstance>,
}
impl ClipBatcher {
pub fn new() -> Self {
ClipBatcher {
rectangles: Vec::new(),
images: FastHashMap::default(),
- border_clears: Vec::new(),
- borders: Vec::new(),
box_shadows: FastHashMap::default(),
line_decorations: Vec::new(),
}
}
pub fn add_clip_region(
&mut self,
task_address: RenderTaskAddress,
@@ -1948,40 +1890,16 @@ impl ClipBatcher {
}
}
ClipSource::RoundedRectangle(..) => {
self.rectangles.push(ClipMaskInstance {
clip_data_address: gpu_address,
..instance
});
}
- ClipSource::BorderCorner(ref source) => {
- let instance = ClipMaskBorderCornerDotDash {
- clip_mask_instance: ClipMaskInstance {
- clip_data_address: gpu_address,
- 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,
- })
- }
- }
}
}
}
}
}
fn get_buffer_kind(texture: SourceTexture) -> ImageBufferKind {
match texture {
--- a/gfx/webrender/src/border.rs
+++ b/gfx/webrender/src/border.rs
@@ -1,47 +1,22 @@
/* 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/. */
-use api::{BorderRadius, BorderSide, BorderStyle, BorderWidths, ColorF, LayoutPoint};
+use api::{BorderRadius, BorderSide, BorderStyle, BorderWidths, ColorF};
use api::{ColorU, DeviceRect, DeviceSize, LayoutSizeAu, LayoutPrimitiveInfo, LayoutToDeviceScale};
-use api::{DevicePoint, DeviceIntSize, LayoutRect, LayoutSize, NormalBorder};
+use api::{DevicePixel, DeviceVector2D, DevicePoint, DeviceIntSize, LayoutRect, LayoutSize, NormalBorder};
use app_units::Au;
-use clip::ClipSource;
use ellipse::Ellipse;
use display_list_flattener::DisplayListFlattener;
use gpu_types::{BorderInstance, BorderSegment, BrushFlags};
-use gpu_cache::GpuDataRequest;
-use prim_store::{BorderPrimitiveCpu, BrushClipMaskKind, BrushKind, BrushPrimitive, BrushSegment, BrushSegmentDescriptor};
+use prim_store::{BrushKind, BrushPrimitive, BrushSegment};
use prim_store::{BorderSource, EdgeAaSegmentMask, PrimitiveContainer, ScrollNodeAndClipChain};
-use util::{lerp, pack_as_float, RectHelpers};
-
-#[repr(u8)]
-#[derive(Debug, Copy, Clone, PartialEq)]
-pub enum BorderCornerInstance {
- None,
- 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,
- TopRight,
- BottomLeft,
- BottomRight,
-}
+use util::{lerp, RectHelpers};
trait AuSizeConverter {
fn to_au(&self) -> LayoutSizeAu;
}
impl AuSizeConverter for LayoutSize {
fn to_au(&self) -> LayoutSizeAu {
LayoutSizeAu::new(
@@ -123,215 +98,16 @@ pub struct BorderCacheKey {
pub right: BorderSideAu,
pub top: BorderSideAu,
pub bottom: BorderSideAu,
pub radius: BorderRadiusAu,
pub widths: BorderWidthsAu,
pub scale: Au,
}
-#[derive(Clone, Debug, PartialEq)]
-pub enum BorderCornerKind {
- None,
- Solid,
- Clip(BorderCornerInstance),
- Mask(
- BorderCornerClipData,
- LayoutSize,
- LayoutSize,
- BorderCornerClipKind,
- ),
-}
-
-impl BorderCornerKind {
- fn new_mask(
- kind: BorderCornerClipKind,
- width0: f32,
- width1: f32,
- corner: BorderCorner,
- radius: LayoutSize,
- border_rect: LayoutRect,
- ) -> BorderCornerKind {
- let size = LayoutSize::new(width0.max(radius.width), width1.max(radius.height));
- let (origin, clip_center) = match corner {
- BorderCorner::TopLeft => {
- let origin = border_rect.origin;
- let clip_center = origin + size;
- (origin, clip_center)
- }
- BorderCorner::TopRight => {
- let origin = LayoutPoint::new(
- border_rect.origin.x + border_rect.size.width - size.width,
- border_rect.origin.y,
- );
- let clip_center = origin + LayoutSize::new(0.0, size.height);
- (origin, clip_center)
- }
- BorderCorner::BottomRight => {
- let origin = border_rect.origin + (border_rect.size - size);
- let clip_center = origin;
- (origin, clip_center)
- }
- BorderCorner::BottomLeft => {
- let origin = LayoutPoint::new(
- border_rect.origin.x,
- border_rect.origin.y + border_rect.size.height - size.height,
- );
- let clip_center = origin + LayoutSize::new(size.width, 0.0);
- (origin, clip_center)
- }
- };
- let clip_data = BorderCornerClipData {
- corner_rect: LayoutRect::new(origin, size),
- clip_center,
- corner: pack_as_float(corner as u32),
- kind: pack_as_float(kind as u32),
- };
- BorderCornerKind::Mask(clip_data, radius, LayoutSize::new(width0, width1), kind)
- }
-
- fn get_radius(&self, original_radius: &LayoutSize) -> LayoutSize {
- match *self {
- BorderCornerKind::Solid => *original_radius,
- BorderCornerKind::Clip(..) => *original_radius,
- BorderCornerKind::Mask(_, ref radius, _, _) => *radius,
- BorderCornerKind::None => *original_radius,
- }
- }
-}
-
-#[derive(Copy, Clone, Debug, PartialEq)]
-pub enum BorderEdgeKind {
- None,
- Solid,
- Clip,
-}
-
-fn get_corner(
- edge0: &BorderSide,
- width0: f32,
- edge1: &BorderSide,
- width1: f32,
- radius: &LayoutSize,
- corner: BorderCorner,
- border_rect: &LayoutRect,
-) -> BorderCornerKind {
- // If both widths are zero, a corner isn't formed.
- if width0 == 0.0 && width1 == 0.0 {
- return BorderCornerKind::None;
- }
-
- // If both edges are transparent, no corner is formed.
- if edge0.color.a == 0.0 && edge1.color.a == 0.0 {
- return BorderCornerKind::None;
- }
-
- match (edge0.style, edge1.style) {
- // If both edges are none or hidden, no corner is needed.
- (BorderStyle::None, BorderStyle::None) |
- (BorderStyle::None, BorderStyle::Hidden) |
- (BorderStyle::Hidden, BorderStyle::None) |
- (BorderStyle::Hidden, BorderStyle::Hidden) => {
- BorderCornerKind::None
- }
-
- // If one of the edges is none or hidden, we just draw one style.
- (BorderStyle::None, _) |
- (_, BorderStyle::None) |
- (BorderStyle::Hidden, _) |
- (_, BorderStyle::Hidden) => {
- BorderCornerKind::Clip(BorderCornerInstance::Single)
- }
-
- // If both borders are solid, we can draw them with a simple rectangle if
- // both the colors match and there is no radius.
- (BorderStyle::Solid, BorderStyle::Solid) => {
- if edge0.color == edge1.color && radius.width == 0.0 && radius.height == 0.0 {
- BorderCornerKind::Solid
- } else {
- BorderCornerKind::Clip(BorderCornerInstance::Single)
- }
- }
-
- // Inset / outset borders just modify the color of edges, so can be
- // drawn with the normal border corner shader.
- (BorderStyle::Outset, BorderStyle::Outset) |
- (BorderStyle::Inset, BorderStyle::Inset) |
- (BorderStyle::Double, BorderStyle::Double) |
- (BorderStyle::Groove, BorderStyle::Groove) |
- (BorderStyle::Ridge, BorderStyle::Ridge) => {
- BorderCornerKind::Clip(BorderCornerInstance::Single)
- }
-
- // Dashed and dotted border corners get drawn into a clip mask.
- (BorderStyle::Dashed, BorderStyle::Dashed) => BorderCornerKind::new_mask(
- BorderCornerClipKind::Dash,
- width0,
- width1,
- corner,
- *radius,
- *border_rect,
- ),
- (BorderStyle::Dotted, BorderStyle::Dotted) => {
- let mut radius = *radius;
- if radius.width < width0 {
- radius.width = 0.0;
- }
- if radius.height < width1 {
- radius.height = 0.0;
- }
- BorderCornerKind::new_mask(
- BorderCornerClipKind::Dot,
- width0,
- width1,
- corner,
- radius,
- *border_rect,
- )
- }
-
- // Draw border transitions with dots and/or dashes as
- // solid segments. The old border path didn't support
- // this anyway, so we might as well start using the new
- // border path here, since the dashing in the edges is
- // much higher quality anyway.
- (BorderStyle::Dotted, _) |
- (_, BorderStyle::Dotted) |
- (BorderStyle::Dashed, _) |
- (_, BorderStyle::Dashed) => BorderCornerKind::Clip(BorderCornerInstance::Single),
-
- // 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),
- }
-}
-
-fn get_edge(edge: &BorderSide, width: f32, height: f32) -> (BorderEdgeKind, f32) {
- if width == 0.0 || height <= 0.0 {
- return (BorderEdgeKind::None, 0.0);
- }
-
- match edge.style {
- BorderStyle::None | BorderStyle::Hidden => (BorderEdgeKind::None, 0.0),
-
- BorderStyle::Solid | BorderStyle::Inset | BorderStyle::Outset => {
- (BorderEdgeKind::Solid, width)
- }
-
- BorderStyle::Double |
- BorderStyle::Groove |
- BorderStyle::Ridge |
- BorderStyle::Dashed |
- BorderStyle::Dotted => (BorderEdgeKind::Clip, width),
- }
-}
-
pub fn ensure_no_corner_overlap(
radius: &mut BorderRadius,
rect: &LayoutRect,
) {
let mut ratio = 1.0;
let top_left_radius = &mut radius.top_left;
let top_right_radius = &mut radius.top_right;
let bottom_right_radius = &mut radius.bottom_right;
@@ -368,354 +144,53 @@ pub fn ensure_no_corner_overlap(
bottom_left_radius.height *= ratio;
bottom_right_radius.width *= ratio;
bottom_right_radius.height *= ratio;
}
}
impl<'a> DisplayListFlattener<'a> {
- fn add_normal_border_primitive(
- &mut self,
- info: &LayoutPrimitiveInfo,
- border: &NormalBorder,
- radius: &BorderRadius,
- widths: &BorderWidths,
- clip_and_scroll: ScrollNodeAndClipChain,
- corner_instances: [BorderCornerInstance; 4],
- edges: [BorderEdgeKind; 4],
- clip_sources: Vec<ClipSource>,
- ) {
- let left = &border.left;
- let right = &border.right;
- let top = &border.top;
- let bottom = &border.bottom;
-
- // These colors are used during inset/outset scaling.
- let left_color = left.border_color(1.0, 2.0 / 3.0, 0.3, 0.7).premultiplied();
- let top_color = top.border_color(1.0, 2.0 / 3.0, 0.3, 0.7).premultiplied();
- let right_color = right.border_color(2.0 / 3.0, 1.0, 0.7, 0.3).premultiplied();
- let bottom_color = bottom.border_color(2.0 / 3.0, 1.0, 0.7, 0.3).premultiplied();
-
- let prim_cpu = BorderPrimitiveCpu {
- corner_instances,
- edges,
-
- // TODO(gw): In the future, we will build these on demand
- // from the deserialized display list, rather
- // than creating it immediately.
- gpu_blocks: [
- [
- pack_as_float(left.style as u32),
- pack_as_float(top.style as u32),
- pack_as_float(right.style as u32),
- pack_as_float(bottom.style as u32),
- ].into(),
- [widths.left, widths.top, widths.right, widths.bottom].into(),
- left_color.into(),
- top_color.into(),
- right_color.into(),
- bottom_color.into(),
- [
- radius.top_left.width,
- radius.top_left.height,
- radius.top_right.width,
- radius.top_right.height,
- ].into(),
- [
- radius.bottom_right.width,
- radius.bottom_right.height,
- radius.bottom_left.width,
- radius.bottom_left.height,
- ].into(),
- ],
- };
-
- self.add_primitive(
- clip_and_scroll,
- info,
- clip_sources,
- PrimitiveContainer::Border(prim_cpu),
- );
- }
-
- // TODO(gw): This allows us to move border types over to the
- // simplified shader model one at a time. Once all borders
- // are converted, this can be removed, along with the complex
- // border code path.
pub fn add_normal_border(
&mut self,
info: &LayoutPrimitiveInfo,
border: &NormalBorder,
widths: &BorderWidths,
clip_and_scroll: ScrollNodeAndClipChain,
) {
- // The border shader is quite expensive. For simple borders, we can just draw
- // the border with a few rectangles. This generally gives better batching, and
- // a GPU win in fragment shader time.
- // More importantly, the software (OSMesa) implementation we run tests on is
- // particularly slow at running our complex border shader, compared to the
- // rectangle shader. This has the effect of making some of our tests time
- // out more often on CI (the actual cause is simply too many Servo processes and
- // threads being run on CI at once).
-
let mut border = *border;
ensure_no_corner_overlap(&mut border.radius, &info.rect);
- let radius = &border.radius;
- let left = &border.left;
- let right = &border.right;
- let top = &border.top;
- let bottom = &border.bottom;
-
- let brush_border_supported = [left, top, right, bottom].iter().all(|edge| {
- match edge.style {
- BorderStyle::Solid |
- BorderStyle::Hidden |
- BorderStyle::None |
- BorderStyle::Double |
- BorderStyle::Inset |
- BorderStyle::Groove |
- BorderStyle::Ridge |
- BorderStyle::Outset => {
- true
- }
-
- BorderStyle::Dotted |
- BorderStyle::Dashed => {
- false
- }
- }
- });
-
- if brush_border_supported {
- let prim = BrushPrimitive::new(
- BrushKind::Border {
- source: BorderSource::Border {
- border,
- widths: *widths,
- cache_key: BorderCacheKey {
- left: border.left.into(),
- top: border.top.into(),
- right: border.right.into(),
- bottom: border.bottom.into(),
- widths: (*widths).into(),
- radius: border.radius.into(),
- scale: Au::from_f32_px(0.0),
- },
- task_info: None,
- handle: None,
+ let prim = BrushPrimitive::new(
+ BrushKind::Border {
+ source: BorderSource::Border {
+ border,
+ widths: *widths,
+ cache_key: BorderCacheKey {
+ left: border.left.into(),
+ top: border.top.into(),
+ right: border.right.into(),
+ bottom: border.bottom.into(),
+ widths: (*widths).into(),
+ radius: border.radius.into(),
+ scale: Au::from_f32_px(0.0),
},
+ task_info: None,
+ handle: None,
},
- None,
- );
-
- self.add_primitive(
- clip_and_scroll,
- info,
- Vec::new(),
- PrimitiveContainer::Brush(prim),
- );
- return;
- }
-
- let corners = [
- get_corner(
- left,
- widths.left,
- top,
- widths.top,
- &radius.top_left,
- BorderCorner::TopLeft,
- &info.rect,
- ),
- get_corner(
- right,
- widths.right,
- top,
- widths.top,
- &radius.top_right,
- BorderCorner::TopRight,
- &info.rect,
- ),
- get_corner(
- right,
- widths.right,
- bottom,
- widths.bottom,
- &radius.bottom_right,
- BorderCorner::BottomRight,
- &info.rect,
- ),
- get_corner(
- left,
- widths.left,
- bottom,
- widths.bottom,
- &radius.bottom_left,
- BorderCorner::BottomLeft,
- &info.rect,
- ),
- ];
-
- let (left_edge, left_len) = get_edge(left, widths.left,
- info.rect.size.height - radius.top_left.height - radius.bottom_left.height);
- let (top_edge, top_len) = get_edge(top, widths.top,
- info.rect.size.width - radius.top_left.width - radius.top_right.width);
- let (right_edge, right_len) = get_edge(right, widths.right,
- info.rect.size.height - radius.top_right.height - radius.bottom_right.height);
- let (bottom_edge, bottom_len) = get_edge(bottom, widths.bottom,
- info.rect.size.width - radius.bottom_right.width - radius.bottom_left.width);
-
- let edges = [left_edge, top_edge, right_edge, bottom_edge];
-
- // Use a simple rectangle case when all edges and corners are either
- // solid or none.
- let all_corners_simple = corners.iter().all(|c| {
- *c == BorderCornerKind::Solid || *c == BorderCornerKind::None
- });
- let all_edges_simple = edges.iter().all(|e| {
- *e == BorderEdgeKind::Solid || *e == BorderEdgeKind::None
- });
-
- let has_no_curve = radius.is_zero();
+ },
+ None,
+ );
- if has_no_curve && all_corners_simple && all_edges_simple {
- let p0 = info.rect.origin;
- let p1 = LayoutPoint::new(
- info.rect.origin.x + left_len,
- info.rect.origin.y + top_len,
- );
- let p2 = LayoutPoint::new(
- info.rect.origin.x + info.rect.size.width - right_len,
- info.rect.origin.y + info.rect.size.height - bottom_len,
- );
- let p3 = info.rect.bottom_right();
-
- let segment = |x0, y0, x1, y1| BrushSegment::new(
- LayoutRect::from_floats(x0, y0, x1, y1),
- true,
- EdgeAaSegmentMask::all(), // Note: this doesn't seem right, needs revision
- [0.0; 4],
- BrushFlags::empty(),
- );
-
- // Add a solid rectangle for each visible edge/corner combination.
- if top_edge == BorderEdgeKind::Solid {
- let descriptor = BrushSegmentDescriptor {
- segments: vec![
- segment(p0.x, p0.y, p1.x, p1.y),
- segment(p2.x, p0.y, p3.x, p1.y),
- segment(p1.x, p0.y, p2.x, p1.y),
- ],
- clip_mask_kind: BrushClipMaskKind::Unknown,
- };
-
- self.add_solid_rectangle(
- clip_and_scroll,
- info,
- border.top.color,
- Some(descriptor),
- Vec::new(),
- );
- }
-
- if left_edge == BorderEdgeKind::Solid {
- let descriptor = BrushSegmentDescriptor {
- segments: vec![
- segment(p0.x, p1.y, p1.x, p2.y),
- ],
- clip_mask_kind: BrushClipMaskKind::Unknown,
- };
-
- self.add_solid_rectangle(
- clip_and_scroll,
- info,
- border.left.color,
- Some(descriptor),
- Vec::new(),
- );
- }
-
- if right_edge == BorderEdgeKind::Solid {
- let descriptor = BrushSegmentDescriptor {
- segments: vec![
- segment(p2.x, p1.y, p3.x, p2.y),
- ],
- clip_mask_kind: BrushClipMaskKind::Unknown,
- };
-
- self.add_solid_rectangle(
- clip_and_scroll,
- info,
- border.right.color,
- Some(descriptor),
- Vec::new(),
- );
- }
-
- if bottom_edge == BorderEdgeKind::Solid {
- let descriptor = BrushSegmentDescriptor {
- segments: vec![
- segment(p1.x, p2.y, p2.x, p3.y),
- segment(p2.x, p2.y, p3.x, p3.y),
- segment(p0.x, p2.y, p1.x, p3.y),
- ],
- clip_mask_kind: BrushClipMaskKind::Unknown,
- };
-
- self.add_solid_rectangle(
- clip_and_scroll,
- info,
- border.bottom.color,
- Some(descriptor),
- Vec::new(),
- );
- }
- } else {
- // Create clip masks for border corners, if required.
- let mut extra_clips = Vec::new();
- let mut corner_instances = [BorderCornerInstance::Single; 4];
-
- let radius = &border.radius;
- let radius = BorderRadius {
- top_left: corners[0].get_radius(&radius.top_left),
- top_right: corners[1].get_radius(&radius.top_right),
- bottom_right: corners[2].get_radius(&radius.bottom_right),
- bottom_left: corners[3].get_radius(&radius.bottom_left),
- };
-
- for (i, corner) in corners.iter().enumerate() {
- match *corner {
- BorderCornerKind::Mask(corner_data, mut 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;
- }
- BorderCornerKind::Solid => {}
- BorderCornerKind::None => {
- corner_instances[i] = BorderCornerInstance::None;
- }
- }
- }
-
- self.add_normal_border_primitive(
- info,
- &border,
- &radius,
- widths,
- clip_and_scroll,
- corner_instances,
- edges,
- extra_clips,
- );
- }
+ self.add_primitive(
+ clip_and_scroll,
+ info,
+ Vec::new(),
+ PrimitiveContainer::Brush(prim),
+ );
}
}
pub trait BorderSideHelpers {
fn border_color(
&self,
scale_factor_0: f32,
scale_factor_1: f32,
@@ -751,36 +226,34 @@ impl BorderSideHelpers for BorderSide {
}
}
}
/// The kind of border corner clip.
#[repr(C)]
#[derive(Copy, Debug, Clone, PartialEq)]
pub enum BorderCornerClipKind {
- Dash,
- Dot,
+ Dash = 1,
+ Dot = 2,
}
/// The source data for a border corner clip mask.
#[derive(Debug, Clone)]
pub struct BorderCornerClipSource {
- pub corner_data: BorderCornerClipData,
pub max_clip_count: usize,
kind: BorderCornerClipKind,
- widths: LayoutSize,
- ellipse: Ellipse,
- pub dot_dash_data: Vec<[f32; 8]>,
+ widths: DeviceSize,
+ radius: DeviceSize,
+ ellipse: Ellipse<DevicePixel>,
}
impl BorderCornerClipSource {
pub fn new(
- corner_data: BorderCornerClipData,
- corner_radius: LayoutSize,
- widths: LayoutSize,
+ corner_radius: DeviceSize,
+ widths: DeviceSize,
kind: BorderCornerClipKind,
) -> BorderCornerClipSource {
// Work out a dash length (and therefore dash count)
// based on the width of the border edges. The "correct"
// dash length is not mentioned in the CSS borders
// spec. The calculation below is similar, but not exactly
// the same as what Gecko uses.
// TODO(gw): Iterate on this to get it closer to what Gecko
@@ -797,17 +270,17 @@ impl BorderCornerClipSource {
// Get the ideal number of dashes for that arc length.
// This is scaled by 0.5 since there is an on/off length
// for each dash.
let desired_count = 0.5 * ellipse.total_arc_length / desired_dash_arc_length;
// Round that up to the nearest integer, so that the dash length
// doesn't exceed the ratio above. Add one extra dash to cover
// the last half-dash of the arc.
- (ellipse, 1 + desired_count.ceil() as usize)
+ (ellipse, desired_count.ceil() as usize)
}
BorderCornerClipKind::Dot => {
let mut corner_radius = corner_radius;
if corner_radius.width < (widths.width / 2.0) {
corner_radius.width = 0.0;
}
if corner_radius.height < (widths.height / 2.0) {
corner_radius.height = 0.0;
@@ -827,80 +300,130 @@ impl BorderCornerClipSource {
let min_diameter = widths.width.min(widths.height);
// Get the number of circles (assuming spacing of one diameter
// between dots).
let max_dot_count = 0.5 * ellipse.total_arc_length / min_diameter;
// Add space for one extra dot since they are centered at the
// start of the arc.
- (ellipse, 1 + max_dot_count.ceil() as usize)
+ (ellipse, max_dot_count.ceil() as usize)
}
}
};
BorderCornerClipSource {
kind,
- corner_data,
max_clip_count,
ellipse,
widths,
- dot_dash_data: Vec::new(),
+ radius: corner_radius,
}
}
- pub fn write(&mut self, mut request: GpuDataRequest) {
- self.corner_data.write(&mut request);
- assert_eq!(request.close(), 2);
+ // TODO(gw): The naming and structure of BorderCornerClipSource
+ // don't really make sense. I've left it this way
+ // for now in order to reduce the size of the
+ // patch a bit. In the future, when we spent some
+ // time working on dot/dash placement, we should
+ // restructure this code to be more consistent
+ // with how border rendering works now.
+ pub fn write(self, segment: BorderSegment) -> Vec<[f32; 8]> {
+ let mut dot_dash_data = Vec::new();
+
+ let outer_scale = match segment {
+ BorderSegment::TopLeft => DeviceVector2D::new(0.0, 0.0),
+ BorderSegment::TopRight => DeviceVector2D::new(1.0, 0.0),
+ BorderSegment::BottomRight => DeviceVector2D::new(1.0, 1.0),
+ BorderSegment::BottomLeft => DeviceVector2D::new(0.0, 1.0),
+ _ => unreachable!(),
+ };
+ let outer = DevicePoint::new(
+ outer_scale.x * self.radius.width,
+ outer_scale.y * self.radius.height,
+ );
+ let clip_sign = DeviceVector2D::new(
+ 1.0 - 2.0 * outer_scale.x,
+ 1.0 - 2.0 * outer_scale.y,
+ );
match self.kind {
BorderCornerClipKind::Dash => {
// Get the correct dash arc length.
let dash_arc_length =
- 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;
+ 0.5 * self.ellipse.total_arc_length / self.max_clip_count as f32;
+ // Start the first dash at one quarter the length of a single dash
+ // along the arc line. This is arbitrary but looks reasonable in
+ // most cases. We need to spend some time working on a more
+ // sophisticated dash placement algorithm that takes into account
+ // the offset of the dashes along edge segments.
+ let mut current_arc_length = 0.25 * dash_arc_length;
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 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);
- self.dot_dash_data.push([
- point0.x, point0.y, tangent0.x, tangent0.y,
- point1.x, point1.y, tangent1.x, tangent1.y
+ let point0 = DevicePoint::new(
+ outer.x + clip_sign.x * (self.radius.width - point0.x),
+ outer.y + clip_sign.y * (self.radius.height - point0.y),
+ );
+
+ let tangent0 = DeviceVector2D::new(
+ -tangent0.x * clip_sign.x,
+ -tangent0.y * clip_sign.y,
+ );
+
+ let point1 = DevicePoint::new(
+ outer.x + clip_sign.x * (self.radius.width - point1.x),
+ outer.y + clip_sign.y * (self.radius.height - point1.y),
+ );
+
+ let tangent1 = DeviceVector2D::new(
+ -tangent1.x * clip_sign.x,
+ -tangent1.y * clip_sign.y,
+ );
+
+ 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);
- self.dot_dash_data.clear();
- self.dot_dash_data.push([
+ 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();
let mut back_dots = Vec::new();
let mut leftover_arc_length = 0.0;
// Alternate between adding dots at the start and end of the
// ellipse arc. This ensures that we always end up with an exact
// half dot at each end of the arc, to match up with the edges.
- forward_dots.push(DotInfo::new(0.0, self.widths.width));
+ forward_dots.push(DotInfo::new(self.widths.width, self.widths.width));
back_dots.push(DotInfo::new(
- self.ellipse.total_arc_length,
+ self.ellipse.total_arc_length - self.widths.height,
self.widths.height,
));
for dot_index in 0 .. self.max_clip_count {
let prev_forward_pos = *forward_dots.last().unwrap();
let prev_back_pos = *back_dots.last().unwrap();
// Select which end of the arc to place a dot from.
@@ -942,69 +465,46 @@ impl BorderCornerClipSource {
// Now step through the dots, and distribute any extra
// 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.
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] {
+ let create_dot_data = |ellipse: &Ellipse<DevicePixel>, 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.,]
+
+ let center = DevicePoint::new(
+ outer.x + clip_sign.x * (self.radius.width - center.x),
+ outer.y + clip_sign.y * (self.radius.height - center.y),
+ );
+
+ [center.x, center.y, radius, 0.0, 0.0, 0.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_data = create_dot_data(&self.ellipse, dot.arc_pos + extra_dist, 0.5 * dot.diameter);
- self.dot_dash_data.push(dot_data);
+ 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_data = create_dot_data(&self.ellipse, dot.arc_pos - extra_dist, 0.5 * dot.diameter);
- self.dot_dash_data.push(dot_data);
+ dot_dash_data.push(dot_data);
}
}
}
- }
-}
-/// Represents the common GPU data for writing a
-/// clip mask for a border corner.
-#[derive(Debug, Copy, Clone, PartialEq)]
-#[repr(C)]
-pub struct BorderCornerClipData {
- /// Local space rect of the border corner.
- corner_rect: LayoutRect,
- /// Local space point that is the center of the
- /// circle or ellipse that we are clipping against.
- clip_center: LayoutPoint,
- /// The shader needs to know which corner, to
- /// be able to flip the dash tangents to the
- /// right orientation.
- corner: f32, // Of type BorderCorner enum
- kind: f32, // Of type BorderCornerClipKind enum
-}
-
-impl BorderCornerClipData {
- fn write(&self, request: &mut GpuDataRequest) {
- request.push(self.corner_rect);
- request.push([
- self.clip_center.x,
- self.clip_center.y,
- self.corner,
- self.kind,
- ]);
+ dot_dash_data
}
}
#[derive(Copy, Clone, Debug)]
struct DotInfo {
arc_pos: f32,
diameter: f32,
}
@@ -1024,16 +524,84 @@ pub struct BorderSegmentInfo {
}
#[derive(Debug)]
pub struct BorderRenderTaskInfo {
pub border_segments: Vec<BorderSegmentInfo>,
pub size: DeviceIntSize,
}
+// Information needed to place and draw a border edge.
+struct EdgeInfo {
+ // Offset in local space to place the edge from origin.
+ local_offset: f32,
+ // Size of the edge in local space.
+ local_size: f32,
+ // Size in device pixels needed in the render task.
+ device_size: f32,
+}
+
+impl EdgeInfo {
+ fn new(
+ local_offset: f32,
+ local_size: f32,
+ device_size: f32,
+ ) -> EdgeInfo {
+ EdgeInfo {
+ local_offset,
+ local_size,
+ device_size,
+ }
+ }
+}
+
+// Get the needed size in device pixels for an edge,
+// based on the border style of that edge. This is used
+// to determine how big the render task should be.
+fn get_edge_info(
+ style: BorderStyle,
+ side_width: f32,
+ avail_size: f32,
+ scale: f32,
+) -> EdgeInfo {
+ // To avoid division by zero below.
+ if side_width <= 0.0 {
+ return EdgeInfo::new(0.0, 0.0, 0.0);
+ }
+
+ match style {
+ BorderStyle::Dashed => {
+ let dash_size = 3.0 * side_width;
+ let approx_dash_count = (avail_size - dash_size) / dash_size;
+ let dash_count = 1.0 + 2.0 * (approx_dash_count / 2.0).floor();
+ let used_size = dash_count * dash_size;
+ let extra_space = avail_size - used_size;
+ let device_size = 2.0 * dash_size * scale;
+ let offset = (extra_space * 0.5).round();
+ EdgeInfo::new(offset, used_size, device_size)
+ }
+ BorderStyle::Dotted => {
+ let dot_and_space_size = 2.0 * side_width;
+ if avail_size < dot_and_space_size * 0.75 {
+ return EdgeInfo::new(0.0, 0.0, 0.0);
+ }
+ let approx_dot_count = avail_size / dot_and_space_size;
+ let dot_count = approx_dot_count.floor().max(1.0);
+ let used_size = dot_count * dot_and_space_size;
+ let extra_space = avail_size - used_size;
+ let device_size = dot_and_space_size * scale;
+ let offset = (extra_space * 0.5).round();
+ EdgeInfo::new(offset, used_size, device_size)
+ }
+ _ => {
+ EdgeInfo::new(0.0, avail_size, 8.0)
+ }
+ }
+}
+
impl BorderRenderTaskInfo {
pub fn new(
rect: &LayoutRect,
border: &NormalBorder,
widths: &BorderWidths,
scale: LayoutToDeviceScale,
brush_segments: &mut Vec<BrushSegment>,
) -> Self {
@@ -1078,102 +646,123 @@ impl BorderRenderTaskInfo {
border.radius.bottom_right.width.max(widths.right),
border.radius.bottom_right.height.max(widths.bottom),
);
let local_size_bl = LayoutSize::new(
border.radius.bottom_left.width.max(widths.left),
border.radius.bottom_left.height.max(widths.bottom),
);
- // TODO(gw): The inner and outer widths don't matter for simple
- // border types. Once we push dashing and dotted styles
- // through border brushes, we need to calculate an
- // appropriate length here.
- let width_inner = 16.0;
- let height_inner = 16.0;
+ let top_edge_info = get_edge_info(
+ border.top.style,
+ widths.top,
+ rect.size.width - local_size_tl.width - local_size_tr.width,
+ scale.0,
+ );
+ let bottom_edge_info = get_edge_info(
+ border.bottom.style,
+ widths.bottom,
+ rect.size.width - local_size_bl.width - local_size_br.width,
+ scale.0,
+ );
+ let inner_width = top_edge_info.device_size.max(bottom_edge_info.device_size).ceil();
+
+ let left_edge_info = get_edge_info(
+ border.left.style,
+ widths.left,
+ rect.size.height - local_size_tl.height - local_size_bl.height,
+ scale.0,
+ );
+ let right_edge_info = get_edge_info(
+ border.right.style,
+ widths.right,
+ rect.size.height - local_size_tr.height - local_size_br.height,
+ scale.0,
+ );
+ let inner_height = left_edge_info.device_size.max(right_edge_info.device_size).ceil();
let size = DeviceSize::new(
- dp_size_tl.width.max(dp_size_bl.width) + width_inner + dp_size_tr.width.max(dp_size_br.width),
- dp_size_tl.height.max(dp_size_tr.height) + height_inner + dp_size_bl.height.max(dp_size_br.height),
+ dp_size_tl.width.max(dp_size_bl.width) + inner_width + dp_size_tr.width.max(dp_size_br.width),
+ dp_size_tl.height.max(dp_size_tr.height) + inner_height + dp_size_bl.height.max(dp_size_br.height),
);
add_edge_segment(
LayoutRect::from_floats(
rect.origin.x,
- rect.origin.y + local_size_tl.height,
+ rect.origin.y + local_size_tl.height + left_edge_info.local_offset,
rect.origin.x + widths.left,
- rect.origin.y + rect.size.height - local_size_bl.height,
+ rect.origin.y + local_size_tl.height + left_edge_info.local_offset + left_edge_info.local_size,
),
DeviceRect::from_floats(
0.0,
dp_size_tl.height,
dp_width_left,
- size.height - dp_size_bl.height,
+ dp_size_tl.height + left_edge_info.device_size,
),
&border.left,
BorderSegment::Left,
EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::RIGHT,
&mut border_segments,
BrushFlags::SEGMENT_RELATIVE | BrushFlags::SEGMENT_REPEAT_Y,
brush_segments,
);
add_edge_segment(
LayoutRect::from_floats(
- rect.origin.x + local_size_tl.width,
+ rect.origin.x + local_size_tl.width + top_edge_info.local_offset,
rect.origin.y,
- rect.origin.x + rect.size.width - local_size_tr.width,
+ rect.origin.x + local_size_tl.width + top_edge_info.local_offset + top_edge_info.local_size,
rect.origin.y + widths.top,
),
DeviceRect::from_floats(
dp_size_tl.width,
0.0,
- size.width - dp_size_tr.width,
+ dp_size_tl.width + top_edge_info.device_size,
dp_width_top,
),
&border.top,
BorderSegment::Top,
EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::BOTTOM,
&mut border_segments,
BrushFlags::SEGMENT_RELATIVE | BrushFlags::SEGMENT_REPEAT_X,
brush_segments,
);
add_edge_segment(
LayoutRect::from_floats(
rect.origin.x + rect.size.width - widths.right,
- rect.origin.y + local_size_tr.height,
+ rect.origin.y + local_size_tr.height + right_edge_info.local_offset,
rect.origin.x + rect.size.width,
- rect.origin.y + rect.size.height - local_size_br.height,
+ rect.origin.y + local_size_tr.height + right_edge_info.local_offset + right_edge_info.local_size,
),
DeviceRect::from_floats(
size.width - dp_width_right,
dp_size_tr.height,
size.width,
- size.height - dp_size_br.height,
+ dp_size_tr.height + right_edge_info.device_size,
),
&border.right,
BorderSegment::Right,
EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::LEFT,
&mut border_segments,
BrushFlags::SEGMENT_RELATIVE | BrushFlags::SEGMENT_REPEAT_Y,
brush_segments,
);
add_edge_segment(
LayoutRect::from_floats(
- rect.origin.x + local_size_bl.width,
+ rect.origin.x + local_size_bl.width + bottom_edge_info.local_offset,
rect.origin.y + rect.size.height - widths.bottom,
- rect.origin.x + rect.size.width - local_size_br.width,
+ rect.origin.x + local_size_bl.width + bottom_edge_info.local_offset + bottom_edge_info.local_size,
rect.origin.y + rect.size.height,
),
DeviceRect::from_floats(
dp_size_bl.width,
size.height - dp_width_bottom,
- size.width - dp_size_br.width,
+ dp_size_bl.width + bottom_edge_info.device_size,
size.height,
),
&border.bottom,
BorderSegment::Bottom,
EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::TOP,
&mut border_segments,
BrushFlags::SEGMENT_RELATIVE | BrushFlags::SEGMENT_REPEAT_X,
brush_segments,
@@ -1272,20 +861,17 @@ impl BorderRenderTaskInfo {
);
BorderRenderTaskInfo {
border_segments,
size: size.to_i32(),
}
}
- pub fn build_instances(
- &self,
- border: &NormalBorder,
- ) -> Vec<BorderInstance> {
+ pub fn build_instances(&self, border: &NormalBorder) -> Vec<BorderInstance> {
let mut instances = Vec::new();
for info in &self.border_segments {
let (side0, side1, flip0, flip1) = match info.segment {
BorderSegment::Left => (&border.left, &border.left, false, false),
BorderSegment::Top => (&border.top, &border.top, false, false),
BorderSegment::Right => (&border.right, &border.right, true, true),
BorderSegment::Bottom => (&border.bottom, &border.bottom, true, true),
@@ -1368,31 +954,139 @@ fn add_segment(
style1: BorderStyle,
color0: ColorF,
color1: ColorF,
segment: BorderSegment,
instances: &mut Vec<BorderInstance>,
widths: DeviceSize,
radius: DeviceSize,
) {
- let flags = (segment as i32) |
- ((style0 as i32) << 8) |
- ((style1 as i32) << 16);
+ let base_flags = (segment as i32) |
+ ((style0 as i32) << 8) |
+ ((style1 as i32) << 16);
let base_instance = BorderInstance {
task_origin: DevicePoint::zero(),
local_rect: task_rect,
- flags,
+ flags: base_flags,
color0: color0.premultiplied(),
color1: color1.premultiplied(),
widths,
radius,
+ clip_params: [0.0; 8],
};
- instances.push(base_instance);
+ match segment {
+ BorderSegment::TopLeft |
+ BorderSegment::TopRight |
+ BorderSegment::BottomLeft |
+ BorderSegment::BottomRight => {
+ // TODO(gw): Similarly to the old border code, we don't correctly handle a a corner
+ // that is dashed on one edge, and dotted on another. We can handle this
+ // in the future by submitting two instances, each one with one side
+ // color set to have an alpha of 0.
+ if (style0 == BorderStyle::Dotted && style1 == BorderStyle::Dashed) ||
+ (style0 == BorderStyle::Dashed && style0 == BorderStyle::Dotted) {
+ warn!("TODO: Handle a corner with dotted / dashed transition.");
+ }
+
+ let clip_kind = match style0 {
+ BorderStyle::Dashed => Some(BorderCornerClipKind::Dash),
+ BorderStyle::Dotted => Some(BorderCornerClipKind::Dot),
+ _ => None,
+ };
+
+ match clip_kind {
+ Some(clip_kind) => {
+ let clip_source = BorderCornerClipSource::new(
+ radius,
+ widths,
+ clip_kind,
+ );
+
+ // TODO(gw): Restructure the BorderCornerClipSource code
+ // so that we don't allocate a Vec here.
+ let clip_list = clip_source.write(segment);
+
+ for params in clip_list {
+ instances.push(BorderInstance {
+ flags: base_flags | ((clip_kind as i32) << 24),
+ clip_params: params,
+ ..base_instance
+ });
+ }
+ }
+ None => {
+ instances.push(base_instance);
+ }
+ }
+ }
+ BorderSegment::Top |
+ BorderSegment::Bottom |
+ BorderSegment::Right |
+ BorderSegment::Left => {
+ let is_vertical = segment == BorderSegment::Left ||
+ segment == BorderSegment::Right;
+
+ match style0 {
+ BorderStyle::Dashed => {
+ let rect = if is_vertical {
+ let half_dash_size = task_rect.size.height * 0.5;
+ let y0 = task_rect.origin.y;
+ let y1 = y0 + half_dash_size.round();
+
+ DeviceRect::from_floats(
+ task_rect.origin.x,
+ y0,
+ task_rect.origin.x + task_rect.size.width,
+ y1,
+ )
+ } else {
+ let half_dash_size = task_rect.size.width * 0.5;
+ let x0 = task_rect.origin.x;
+ let x1 = x0 + half_dash_size.round();
+
+ DeviceRect::from_floats(
+ x0,
+ task_rect.origin.y,
+ x1,
+ task_rect.origin.y + task_rect.size.height,
+ )
+ };
+
+ instances.push(BorderInstance {
+ local_rect: rect,
+ ..base_instance
+ });
+ }
+ BorderStyle::Dotted => {
+ let (x, y, r) = if is_vertical {
+ (widths.width * 0.5,
+ widths.width,
+ widths.width * 0.5)
+ } else {
+ (widths.height,
+ widths.height * 0.5,
+ widths.height * 0.5)
+ };
+
+ instances.push(BorderInstance {
+ flags: base_flags | ((BorderCornerClipKind::Dot as i32) << 24),
+ clip_params: [
+ x, y, r, 0.0, 0.0, 0.0, 0.0, 0.0,
+ ],
+ ..base_instance
+ });
+ }
+ _ => {
+ instances.push(base_instance);
+ }
+ }
+ }
+ }
}
fn add_corner_segment(
image_rect: LayoutRect,
task_rect: DeviceRect,
side0: &BorderSide,
side1: &BorderSide,
widths: DeviceSize,
--- a/gfx/webrender/src/clip.rs
+++ b/gfx/webrender/src/clip.rs
@@ -1,16 +1,16 @@
/* 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/. */
use api::{BorderRadius, ClipMode, ComplexClipRegion, DeviceIntRect, DevicePixelScale, ImageMask};
use api::{ImageRendering, LayoutRect, LayoutSize, LayoutPoint, LayoutVector2D, LocalClip};
use api::{BoxShadowClipMode, LayoutToWorldScale, LineOrientation, LineStyle};
-use border::{BorderCornerClipSource, ensure_no_corner_overlap};
+use border::{ensure_no_corner_overlap};
use box_shadow::{BLUR_SAMPLE_SCALE, BoxShadowClipSource, BoxShadowCacheKey};
use clip_scroll_tree::{ClipChainIndex, CoordinateSystemId};
use ellipse::Ellipse;
use freelist::{FreeList, FreeListHandle, WeakFreeListHandle};
use gpu_cache::{GpuCache, GpuCacheHandle, ToGpuBlocks};
use gpu_types::{BoxShadowStretchMode, ClipScrollNodeIndex};
use prim_store::{ClipData, ImageMaskData};
use render_task::to_cache_size;
@@ -82,21 +82,16 @@ impl ClipRegion {
}
}
#[derive(Debug)]
pub enum ClipSource {
Rectangle(LayoutRect, ClipMode),
RoundedRectangle(LayoutRect, BorderRadius, ClipMode),
Image(ImageMask),
- /// TODO(gw): This currently only handles dashed style
- /// clips, where the border style is dashed for both
- /// adjacent border edges. Expand to handle dotted style
- /// and different styles per edge.
- BorderCorner(BorderCornerClipSource),
BoxShadow(BoxShadowClipSource),
LineDecoration(LineDecorationClipSource),
}
impl From<ClipRegion> for ClipSources {
fn from(region: ClipRegion) -> ClipSources {
let mut clips = Vec::new();
@@ -336,17 +331,16 @@ impl ClipSources {
can_calculate_outer_rect = true;
local_outer = local_outer.and_then(|r| r.intersection(rect));
let inner_rect = extract_inner_rect_safe(rect, radius);
local_inner = local_inner
.and_then(|r| inner_rect.and_then(|ref inner| r.intersection(inner)));
}
ClipSource::BoxShadow(..) |
- ClipSource::BorderCorner { .. } |
ClipSource::LineDecoration(..) => {
can_calculate_inner_rect = false;
break;
}
}
}
let outer = if can_calculate_outer_rect {
@@ -395,19 +389,16 @@ impl ClipSources {
ClipSource::Rectangle(rect, mode) => {
let data = ClipData::uniform(rect, 0.0, mode);
data.write(&mut request);
}
ClipSource::RoundedRectangle(ref rect, ref radius, mode) => {
let data = ClipData::rounded_rect(rect, radius, mode);
data.write(&mut request);
}
- ClipSource::BorderCorner(ref mut source) => {
- source.write(request);
- }
ClipSource::LineDecoration(ref info) => {
request.push(info.rect);
request.push([
info.wavy_line_thickness,
pack_as_float(info.style as u32),
pack_as_float(info.orientation as u32),
0.0,
]);
--- a/gfx/webrender/src/display_list_flattener.rs
+++ b/gfx/webrender/src/display_list_flattener.rs
@@ -15,23 +15,24 @@ use api::{Shadow, SpecificDisplayItem, S
use api::{TransformStyle, YuvColorSpace, YuvData};
use app_units::Au;
use clip::{ClipRegion, ClipSource, ClipSources, ClipStore};
use clip_scroll_node::{ClipScrollNode, NodeType, StickyFrameInfo};
use clip_scroll_tree::{ClipChainIndex, ClipScrollNodeIndex, ClipScrollTree};
use euclid::{SideOffsets2D, vec2};
use frame_builder::{FrameBuilder, FrameBuilderConfig};
use glyph_rasterizer::FontInstance;
+use gpu_cache::GpuCacheHandle;
use gpu_types::BrushFlags;
use hit_test::{HitTestingItem, HitTestingRun};
use image::simplify_repeated_primitive;
use internal_types::{FastHashMap, FastHashSet};
use picture::PictureCompositeMode;
-use prim_store::{BrushClipMaskKind, BrushKind, BrushPrimitive, BrushSegmentDescriptor, CachedGradient};
-use prim_store::{CachedGradientIndex, EdgeAaSegmentMask, ImageSource};
+use prim_store::{BrushClipMaskKind, BrushKind, BrushPrimitive, BrushSegmentDescriptor};
+use prim_store::{EdgeAaSegmentMask, ImageSource};
use prim_store::{BorderSource, BrushSegment, PictureIndex, PrimitiveContainer, PrimitiveIndex, PrimitiveStore};
use prim_store::{OpacityBinding, ScrollNodeAndClipChain, TextRunPrimitiveCpu};
use render_backend::{DocumentView};
use resource_cache::{FontInstanceMap, ImageRequest};
use scene::{Scene, ScenePipeline, StackingContextHelpers};
use scene_builder::{BuiltScene, SceneRequest};
use std::{f32, mem, usize};
use tiling::{CompositeOps, ScrollbarPrimitive};
@@ -184,31 +185,29 @@ pub struct DisplayListFlattener<'a> {
pub hit_testing_runs: Vec<HitTestingRun>,
/// The store which holds all complex clipping information.
pub clip_store: ClipStore,
/// The configuration to use for the FrameBuilder. We consult this in
/// order to determine the default font.
pub config: FrameBuilderConfig,
-
- /// The gradients collecting during display list flattening.
- pub cached_gradients: Vec<CachedGradient>,
}
impl<'a> DisplayListFlattener<'a> {
pub fn create_frame_builder(
old_builder: FrameBuilder,
scene: &Scene,
clip_scroll_tree: &mut ClipScrollTree,
font_instances: FontInstanceMap,
view: &DocumentView,
output_pipelines: &FastHashSet<PipelineId>,
frame_builder_config: &FrameBuilderConfig,
new_scene: &mut Scene,
+ scene_id: u64,
) -> FrameBuilder {
// We checked that the root pipeline is available on the render backend.
let root_pipeline_id = scene.root_pipeline_id.unwrap();
let root_pipeline = scene.pipelines.get(&root_pipeline_id).unwrap();
let root_epoch = scene.pipeline_epochs[&root_pipeline_id];
let background_color = root_pipeline
@@ -219,17 +218,16 @@ impl<'a> DisplayListFlattener<'a> {
scene,
clip_scroll_tree,
font_instances,
config: *frame_builder_config,
pipeline_epochs: Vec::new(),
output_pipelines,
id_to_index_mapper: ClipIdToIndexMapper::default(),
hit_testing_runs: recycle_vec(old_builder.hit_testing_runs),
- cached_gradients: recycle_vec(old_builder.cached_gradients),
scrollbar_prims: recycle_vec(old_builder.scrollbar_prims),
reference_frame_stack: Vec::new(),
picture_stack: Vec::new(),
shadow_stack: Vec::new(),
sc_stack: Vec::new(),
prim_store: old_builder.prim_store.recycle(),
clip_store: old_builder.clip_store.recycle(),
};
@@ -249,17 +247,18 @@ impl<'a> DisplayListFlattener<'a> {
new_scene.pipeline_epochs.insert(root_pipeline_id, root_epoch);
new_scene.pipeline_epochs.extend(flattener.pipeline_epochs.drain(..));
new_scene.pipelines = scene.pipelines.clone();
FrameBuilder::with_display_list_flattener(
view.inner_rect,
background_color,
view.window_size,
- flattener
+ scene_id,
+ flattener,
)
}
fn get_complex_clips(
&self,
pipeline_id: PipelineId,
complex_clips: ItemRange<ComplexClipRegion>,
) -> Vec<ComplexClipRegion> {
@@ -631,17 +630,16 @@ impl<'a> DisplayListFlattener<'a> {
}
SpecificDisplayItem::Gradient(ref info) => {
self.add_gradient(
clip_and_scroll,
&prim_info,
info.gradient.start_point,
info.gradient.end_point,
item.gradient_stops(),
- item.display_list().get(item.gradient_stops()).count(),
info.gradient.extend_mode,
info.tile_size,
info.tile_spacing,
);
}
SpecificDisplayItem::RadialGradient(ref info) => {
self.add_radial_gradient(
clip_and_scroll,
@@ -674,17 +672,16 @@ impl<'a> DisplayListFlattener<'a> {
);
}
SpecificDisplayItem::Border(ref info) => {
self.add_border(
clip_and_scroll,
&prim_info,
info,
item.gradient_stops(),
- item.display_list().get(item.gradient_stops()).count(),
);
}
SpecificDisplayItem::PushStackingContext(ref info) => {
let mut subtraversal = item.sub_iter();
self.flatten_stacking_context(
&mut subtraversal,
pipeline_id,
&item,
@@ -1489,17 +1486,16 @@ impl<'a> DisplayListFlattener<'a> {
}
pub fn add_border(
&mut self,
clip_and_scroll: ScrollNodeAndClipChain,
info: &LayoutPrimitiveInfo,
border_item: &BorderDisplayItem,
gradient_stops: ItemRange<GradientStop>,
- gradient_stops_count: usize,
) {
let rect = info.rect;
let create_segments = |outset: SideOffsets2D<f32>| {
// Calculate the modified rect as specific by border-image-outset
let origin = LayoutPoint::new(rect.origin.x - outset.left, rect.origin.y - outset.top);
let size = LayoutSize::new(
rect.size.width + outset.left + outset.right,
rect.size.height + outset.top + outset.bottom,
@@ -1734,17 +1730,16 @@ impl<'a> DisplayListFlattener<'a> {
info.rect = segment;
self.add_gradient(
clip_and_scroll,
&info,
border.gradient.start_point - segment_rel,
border.gradient.end_point - segment_rel,
gradient_stops,
- gradient_stops_count,
border.gradient.extend_mode,
segment.size,
LayoutSize::zero(),
);
},
BorderDetails::RadialGradient(ref border) => {
for segment in create_segments(border.outset) {
let segment_rel = segment.origin - rect.origin;
@@ -1770,24 +1765,20 @@ impl<'a> DisplayListFlattener<'a> {
pub fn add_gradient(
&mut self,
clip_and_scroll: ScrollNodeAndClipChain,
info: &LayoutPrimitiveInfo,
start_point: LayoutPoint,
end_point: LayoutPoint,
stops: ItemRange<GradientStop>,
- stops_count: usize,
extend_mode: ExtendMode,
stretch_size: LayoutSize,
mut tile_spacing: LayoutSize,
) {
- let gradient_index = CachedGradientIndex(self.cached_gradients.len());
- self.cached_gradients.push(CachedGradient::new());
-
let mut prim_rect = info.rect;
simplify_repeated_primitive(&stretch_size, &mut tile_spacing, &mut prim_rect);
let info = LayoutPrimitiveInfo {
rect: prim_rect,
.. *info
};
// Try to ensure that if the gradient is specified in reverse, then so long as the stops
@@ -1806,22 +1797,21 @@ impl<'a> DisplayListFlattener<'a> {
(end_point, start_point)
} else {
(start_point, end_point)
};
let prim = BrushPrimitive::new(
BrushKind::LinearGradient {
stops_range: stops,
- stops_count,
extend_mode,
reverse_stops,
start_point: sp,
end_point: ep,
- gradient_index,
+ stops_handle: GpuCacheHandle::new(),
stretch_size,
tile_spacing,
visible_tiles: Vec::new(),
},
None,
);
let prim = PrimitiveContainer::Brush(prim);
@@ -1837,35 +1827,32 @@ impl<'a> DisplayListFlattener<'a> {
start_radius: f32,
end_radius: f32,
ratio_xy: f32,
stops: ItemRange<GradientStop>,
extend_mode: ExtendMode,
stretch_size: LayoutSize,
mut tile_spacing: LayoutSize,
) {
- let gradient_index = CachedGradientIndex(self.cached_gradients.len());
- self.cached_gradients.push(CachedGradient::new());
-
let mut prim_rect = info.rect;
simplify_repeated_primitive(&stretch_size, &mut tile_spacing, &mut prim_rect);
let info = LayoutPrimitiveInfo {
rect: prim_rect,
.. *info
};
let prim = BrushPrimitive::new(
BrushKind::RadialGradient {
stops_range: stops,
extend_mode,
center,
start_radius,
end_radius,
ratio_xy,
- gradient_index,
+ stops_handle: GpuCacheHandle::new(),
stretch_size,
tile_spacing,
visible_tiles: Vec::new(),
},
None,
);
self.add_primitive(
@@ -2057,17 +2044,18 @@ pub fn build_scene(config: &FrameBuilder
let frame_builder = DisplayListFlattener::create_frame_builder(
FrameBuilder::empty(), // WIP, we're not really recycling anything here, clean this up.
&request.scene,
&mut clip_scroll_tree,
request.font_instances,
&request.view,
&request.output_pipelines,
config,
- &mut new_scene
+ &mut new_scene,
+ request.scene_id,
);
BuiltScene {
scene: new_scene,
frame_builder,
clip_scroll_tree,
removed_pipelines: request.removed_pipelines,
}
--- a/gfx/webrender/src/ellipse.rs
+++ b/gfx/webrender/src/ellipse.rs
@@ -1,27 +1,30 @@
/* 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/. */
-use api::{LayoutPoint, LayoutSize, LayoutVector2D};
+use api::{LayoutPoint, LayoutVector2D};
+use euclid::TypedSize2D;
use std::f32::consts::FRAC_PI_2;
+#[cfg(test)]
+use api::LayoutSize;
/// Number of steps to integrate arc length over.
const STEP_COUNT: usize = 20;
/// Represents an ellipse centred at a local space origin.
#[derive(Debug, Clone)]
-pub struct Ellipse {
- pub radius: LayoutSize,
+pub struct Ellipse<U> {
+ pub radius: TypedSize2D<f32, U>,
pub total_arc_length: f32,
}
-impl Ellipse {
- pub fn new(radius: LayoutSize) -> Ellipse {
+impl<U> Ellipse<U> {
+ pub fn new(radius: TypedSize2D<f32, U>) -> Ellipse<U> {
// Approximate the total length of the first quadrant of this ellipse.
let total_arc_length = get_simpson_length(FRAC_PI_2, radius.width, radius.height);
Ellipse {
radius,
total_arc_length,
}
}
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -9,17 +9,17 @@ use clip::{ClipChain, ClipStore};
use clip_scroll_node::{ClipScrollNode};
use clip_scroll_tree::{ClipScrollNodeIndex, ClipScrollTree};
use display_list_flattener::{DisplayListFlattener};
use gpu_cache::GpuCache;
use gpu_types::{ClipChainRectIndex, ClipScrollNodeData, UvRectKind};
use hit_test::{HitTester, HitTestingRun};
use internal_types::{FastHashMap};
use picture::PictureSurface;
-use prim_store::{CachedGradient, PrimitiveIndex, PrimitiveRun, PrimitiveStore};
+use prim_store::{PrimitiveIndex, PrimitiveRun, PrimitiveStore};
use profiler::{FrameProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters};
use render_backend::FrameId;
use render_task::{RenderTask, RenderTaskId, RenderTaskLocation, RenderTaskTree};
use resource_cache::{ResourceCache};
use scene::{ScenePipeline, SceneProperties};
use std::{mem, f32};
use std::sync::Arc;
use tiling::{Frame, RenderPass, RenderPassKind, RenderTargetContext};
@@ -36,41 +36,41 @@ pub struct FrameBuilderConfig {
pub dual_source_blending_is_enabled: bool,
}
/// A builder structure for `tiling::Frame`
pub struct FrameBuilder {
screen_rect: DeviceUintRect,
background_color: Option<ColorF>,
window_size: DeviceUintSize,
+ scene_id: u64,
pub prim_store: PrimitiveStore,
pub clip_store: ClipStore,
pub hit_testing_runs: Vec<HitTestingRun>,
pub config: FrameBuilderConfig,
- pub cached_gradients: Vec<CachedGradient>,
pub scrollbar_prims: Vec<ScrollbarPrimitive>,
}
pub struct FrameBuildingContext<'a> {
+ pub scene_id: u64,
pub device_pixel_scale: DevicePixelScale,
pub scene_properties: &'a SceneProperties,
pub pipelines: &'a FastHashMap<PipelineId, Arc<ScenePipeline>>,
pub screen_rect: DeviceIntRect,
pub clip_scroll_tree: &'a ClipScrollTree,
pub node_data: &'a [ClipScrollNodeData],
}
pub struct FrameBuildingState<'a> {
pub render_tasks: &'a mut RenderTaskTree,
pub profile_counters: &'a mut FrameProfileCounters,
pub clip_store: &'a mut ClipStore,
pub local_clip_rects: &'a mut Vec<LayoutRect>,
pub resource_cache: &'a mut ResourceCache,
pub gpu_cache: &'a mut GpuCache,
- pub cached_gradients: &'a mut [CachedGradient],
pub special_render_passes: &'a mut SpecialRenderPasses,
}
pub struct PictureContext<'a> {
pub pipeline_id: PipelineId,
pub prim_runs: Vec<PrimitiveRun>,
pub original_reference_frame_index: Option<ClipScrollNodeIndex>,
pub display_list: &'a BuiltDisplayList,
@@ -78,23 +78,25 @@ pub struct PictureContext<'a> {
pub apply_local_clip_rect: bool,
pub inflation_factor: f32,
pub allow_subpixel_aa: bool,
}
pub struct PictureState {
pub tasks: Vec<RenderTaskId>,
pub has_non_root_coord_system: bool,
+ pub local_rect_changed: bool,
}
impl PictureState {
pub fn new() -> PictureState {
PictureState {
tasks: Vec::new(),
has_non_root_coord_system: false,
+ local_rect_changed: false,
}
}
}
pub struct PrimitiveRunContext<'a> {
pub clip_chain: &'a ClipChain,
pub scroll_node: &'a ClipScrollNode,
pub clip_chain_rect_index: ClipChainRectIndex,
@@ -113,47 +115,48 @@ impl<'a> PrimitiveRunContext<'a> {
}
}
}
impl FrameBuilder {
pub fn empty() -> Self {
FrameBuilder {
hit_testing_runs: Vec::new(),
- cached_gradients: Vec::new(),
scrollbar_prims: Vec::new(),
prim_store: PrimitiveStore::new(),
clip_store: ClipStore::new(),
screen_rect: DeviceUintRect::zero(),
window_size: DeviceUintSize::zero(),
background_color: None,
+ scene_id: 0,
config: FrameBuilderConfig {
enable_scrollbars: false,
default_font_render_mode: FontRenderMode::Mono,
dual_source_blending_is_enabled: true,
dual_source_blending_is_supported: false,
},
}
}
pub fn with_display_list_flattener(
screen_rect: DeviceUintRect,
background_color: Option<ColorF>,
window_size: DeviceUintSize,
+ scene_id: u64,
flattener: DisplayListFlattener,
) -> Self {
FrameBuilder {
hit_testing_runs: flattener.hit_testing_runs,
- cached_gradients: flattener.cached_gradients,
scrollbar_prims: flattener.scrollbar_prims,
prim_store: flattener.prim_store,
clip_store: flattener.clip_store,
screen_rect,
background_color,
window_size,
+ scene_id,
config: flattener.config,
}
}
/// Compute the contribution (bounding rectangles, and resources) of layers and their
/// primitives in screen space.
fn build_layer_screen_rects_and_cull_layers(
&mut self,
@@ -180,33 +183,33 @@ impl FrameBuilder {
&clip_scroll_tree.nodes[clip_scroll_tree.root_reference_frame_index().0];
let display_list = &pipelines
.get(&root_clip_scroll_node.pipeline_id)
.expect("No display list?")
.display_list;
let frame_context = FrameBuildingContext {
+ scene_id: self.scene_id,
device_pixel_scale,
scene_properties,
pipelines,
screen_rect: self.screen_rect.to_i32(),
clip_scroll_tree,
node_data,
};
let mut frame_state = FrameBuildingState {
render_tasks,
profile_counters,
clip_store: &mut self.clip_store,
local_clip_rects,
resource_cache,
gpu_cache,
special_render_passes,
- cached_gradients: &mut self.cached_gradients,
};
let pic_context = PictureContext {
pipeline_id: root_clip_scroll_node.pipeline_id,
prim_runs: mem::replace(&mut self.prim_store.pictures[0].runs, Vec::new()),
original_reference_frame_index: None,
display_list,
inv_world_transform: None,
@@ -372,17 +375,16 @@ impl FrameBuilder {
for pass in &mut passes {
let mut ctx = RenderTargetContext {
device_pixel_scale,
prim_store: &self.prim_store,
resource_cache,
clip_scroll_tree,
use_dual_source_blending,
node_data: &node_data,
- cached_gradients: &self.cached_gradients,
};
pass.build(
&mut ctx,
gpu_cache,
&mut render_tasks,
&mut deferred_resolves,
&self.clip_store,
--- a/gfx/webrender/src/gpu_cache.rs
+++ b/gfx/webrender/src/gpu_cache.rs
@@ -490,21 +490,16 @@ impl<'a> GpuDataRequest<'a> {
pub fn extend_from_slice(&mut self, blocks: &[GpuBlockData]) {
self.texture.pending_blocks.extend_from_slice(blocks);
}
pub fn current_used_block_num(&self) -> usize {
self.texture.pending_blocks.len() - self.start_index
}
-
- /// Consume the request and return the number of blocks written
- pub fn close(self) -> usize {
- self.texture.pending_blocks.len() - self.start_index
- }
}
impl<'a> Drop for GpuDataRequest<'a> {
fn drop(&mut self) {
// Push the data to the texture pending updates list.
let block_count = self.current_used_block_num();
debug_assert!(block_count <= self.max_block_count);
--- a/gfx/webrender/src/gpu_types.rs
+++ b/gfx/webrender/src/gpu_types.rs
@@ -75,17 +75,17 @@ pub enum BlurDirection {
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct BlurInstance {
pub task_address: RenderTaskAddress,
pub src_task_address: RenderTaskAddress,
pub blur_direction: BlurDirection,
}
-#[derive(Debug, Copy, Clone)]
+#[derive(Debug, Copy, Clone, PartialEq)]
#[repr(C)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum BorderSegment {
TopLeft,
TopRight,
BottomRight,
BottomLeft,
@@ -102,16 +102,17 @@ pub enum BorderSegment {
pub struct BorderInstance {
pub task_origin: DevicePoint,
pub local_rect: DeviceRect,
pub color0: PremultipliedColorF,
pub color1: PremultipliedColorF,
pub flags: i32,
pub widths: DeviceSize,
pub radius: DeviceSize,
+ pub clip_params: [f32; 8],
}
/// A clipping primitive drawn into the clipping mask.
/// Could be an image or a rectangle, which defines the
/// way `address` is treated.
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
--- a/gfx/webrender/src/hit_test.rs
+++ b/gfx/webrender/src/hit_test.rs
@@ -333,17 +333,16 @@ fn get_regions_for_clip_scroll_node(
};
clips.iter().map(|source| {
match source.0 {
ClipSource::Rectangle(ref rect, mode) => HitTestRegion::Rectangle(*rect, mode),
ClipSource::RoundedRectangle(ref rect, ref radii, ref mode) =>
HitTestRegion::RoundedRectangle(*rect, *radii, *mode),
ClipSource::Image(ref mask) => HitTestRegion::Rectangle(mask.rect, ClipMode::Clip),
- ClipSource::BorderCorner(_) |
ClipSource::LineDecoration(_) |
ClipSource::BoxShadow(_) => {
unreachable!("Didn't expect to hit test against BorderCorner / BoxShadow / LineDecoration");
}
}
}).collect()
}
--- a/gfx/webrender/src/internal_types.rs
+++ b/gfx/webrender/src/internal_types.rs
@@ -127,26 +127,17 @@ impl TextureUpdateList {
pub fn push(&mut self, update: TextureUpdate) {
self.updates.push(update);
}
}
/// Wraps a tiling::Frame, but conceptually could hold more information
pub struct RenderedDocument {
pub frame: tiling::Frame,
-}
-
-impl RenderedDocument {
- pub fn new(
- frame: tiling::Frame,
- ) -> Self {
- RenderedDocument {
- frame,
- }
- }
+ pub is_new_scene: bool,
}
pub enum DebugOutput {
FetchDocuments(String),
FetchClipScrollTree(String),
#[cfg(feature = "capture")]
SaveCapture(CaptureConfig, Vec<ExternalCaptureImage>),
#[cfg(feature = "replay")]
--- a/gfx/webrender/src/picture.rs
+++ b/gfx/webrender/src/picture.rs
@@ -75,17 +75,21 @@ pub struct PictureCacheKey {
// we want the cache to remain valid as it
// is scrolled and/or translated by animation.
// This is valid while we have the restriction
// in place that only pictures that use the
// root coordinate system are cached - once
// we relax that, we'll need to consider some
// extra parameters, depending on transform.
- // The unique identifier for this picture.
+ // This is a globally unique id of the scene this picture
+ // is associated with, to avoid picture id collisions.
+ scene_id: u64,
+
+ // The unique (for the scene_id) identifier for this picture.
// TODO(gw): Currently, these will not be
// shared across new display lists,
// so will only remain valid during
// scrolling. Next step will be to
// allow deep comparisons on pictures
// between display lists, allowing
// pictures that are the same to be
// cached across display lists!
@@ -369,16 +373,17 @@ impl PicturePrimitive {
);
// Request a render task that will cache the output in the
// texture cache.
let cache_item = frame_state.resource_cache.request_render_task(
RenderTaskCacheKey {
size: device_rect.size,
kind: RenderTaskCacheKeyKind::Picture(PictureCacheKey {
+ scene_id: frame_context.scene_id,
picture_id: self.id,
unclipped_size: prim_screen_rect.unclipped.size,
pic_relative_render_rect,
}),
},
frame_state.gpu_cache,
frame_state.render_tasks,
None,
@@ -462,16 +467,24 @@ impl PicturePrimitive {
);
self.secondary_render_task_id = Some(picture_task_id);
let render_task_id = frame_state.render_tasks.add(blur_render_task);
pic_state.tasks.push(render_task_id);
self.surface = Some(PictureSurface::RenderTask(render_task_id));
+ // If the local rect of the contents changed, force the cache handle
+ // to be invalidated so that the primitive data below will get
+ // uploaded to the GPU this frame. This can occur during property
+ // animation.
+ if pic_state.local_rect_changed {
+ frame_state.gpu_cache.invalidate(&mut self.extra_gpu_data_handle);
+ }
+
if let Some(mut request) = frame_state.gpu_cache.request(&mut self.extra_gpu_data_handle) {
// TODO(gw): This is very hacky code below! It stores an extra
// brush primitive below for the special case of a
// drop-shadow where we need a different local
// rect for the shadow. To tidy this up in future,
// we could consider abstracting the code in prim_store.rs
// that writes a brush primitive header.
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -4,17 +4,17 @@
use api::{AlphaType, BorderRadius, BoxShadowClipMode, BuiltDisplayList, ClipMode, ColorF, ComplexClipRegion};
use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, Epoch, ExtendMode};
use api::{FilterOp, GlyphInstance, GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag, TileOffset};
use api::{GlyphRasterSpace, LayoutPoint, LayoutRect, LayoutSize, LayoutToWorldTransform, LayoutVector2D};
use api::{PipelineId, PremultipliedColorF, PropertyBinding, Shadow, YuvColorSpace, YuvFormat, DeviceIntSideOffsets};
use api::{BorderWidths, LayoutToWorldScale, NormalBorder};
use app_units::Au;
-use border::{BorderCacheKey, BorderCornerInstance, BorderRenderTaskInfo, BorderEdgeKind};
+use border::{BorderCacheKey, BorderRenderTaskInfo};
use box_shadow::BLUR_SAMPLE_SCALE;
use clip_scroll_tree::{ClipChainIndex, ClipScrollNodeIndex, CoordinateSystemId};
use clip_scroll_node::ClipScrollNode;
use clip::{ClipChain, ClipChainNode, ClipChainNodeIter, ClipChainNodeRef, ClipSource};
use clip::{ClipSourcesHandle, ClipWorkItem};
use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureContext, PictureState};
use frame_builder::PrimitiveRunContext;
use glyph_rasterizer::{FontInstance, FontTransform, GlyphKey};
@@ -78,31 +78,16 @@ impl PrimitiveOpacity {
pub fn from_alpha(alpha: f32) -> PrimitiveOpacity {
PrimitiveOpacity {
is_opaque: alpha == 1.0,
}
}
}
-#[derive(Debug, Copy, Clone)]
-pub struct CachedGradientIndex(pub usize);
-
-pub struct CachedGradient {
- pub handle: GpuCacheHandle,
-}
-
-impl CachedGradient {
- pub fn new() -> CachedGradient {
- CachedGradient {
- handle: GpuCacheHandle::new(),
- }
- }
-}
-
// Represents the local space rect of a list of
// primitive runs. For most primitive runs, the
// primitive runs are attached to the parent they
// are declared in. However, when a primitive run
// is part of a 3d rendering context, it may get
// hoisted to a higher level in the picture tree.
// When this happens, we need to also calculate the
// local space rects in the original space. This
@@ -145,17 +130,16 @@ pub struct PrimitiveIndex(pub usize);
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct PictureIndex(pub usize);
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum PrimitiveKind {
TextRun,
- Border,
Brush,
}
impl GpuCacheHandle {
pub fn as_int(&self, gpu_cache: &GpuCache) -> i32 {
gpu_cache.get_address(self).as_int()
}
}
@@ -292,31 +276,30 @@ pub enum BrushKind {
},
YuvImage {
yuv_key: [ImageKey; 3],
format: YuvFormat,
color_space: YuvColorSpace,
image_rendering: ImageRendering,
},
RadialGradient {
- gradient_index: CachedGradientIndex,
+ stops_handle: GpuCacheHandle,
stops_range: ItemRange<GradientStop>,
extend_mode: ExtendMode,
center: LayoutPoint,
start_radius: f32,
end_radius: f32,
ratio_xy: f32,
stretch_size: LayoutSize,
tile_spacing: LayoutSize,
visible_tiles: Vec<VisibleGradientTile>,
},
LinearGradient {
- gradient_index: CachedGradientIndex,
+ stops_handle: GpuCacheHandle,
stops_range: ItemRange<GradientStop>,
- stops_count: usize,
extend_mode: ExtendMode,
reverse_stops: bool,
start_point: LayoutPoint,
end_point: LayoutPoint,
stretch_size: LayoutSize,
tile_spacing: LayoutSize,
visible_tiles: Vec<VisibleGradientTile>,
},
@@ -552,29 +535,16 @@ pub enum ImageSource {
// An image that is pre-rendered into the texture cache
// via a render task.
Cache {
size: DeviceIntSize,
handle: Option<RenderTaskCacheEntryHandle>,
},
}
-#[derive(Debug)]
-pub struct BorderPrimitiveCpu {
- pub corner_instances: [BorderCornerInstance; 4],
- pub edges: [BorderEdgeKind; 4],
- pub gpu_blocks: [GpuBlockData; 8],
-}
-
-impl ToGpuBlocks for BorderPrimitiveCpu {
- fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
- request.extend_from_slice(&self.gpu_blocks);
- }
-}
-
// The gradient entry index for the first color stop
pub const GRADIENT_DATA_FIRST_STOP: usize = 0;
// The gradient entry index for the last color stop
pub const GRADIENT_DATA_LAST_STOP: usize = GRADIENT_DATA_SIZE - 1;
// The start of the gradient data table
pub const GRADIENT_DATA_TABLE_BEGIN: usize = GRADIENT_DATA_FIRST_STOP + 1;
// The exclusive bound of the gradient data table
@@ -1080,17 +1050,16 @@ impl ClipData {
corner.write(request);
}
}
}
#[derive(Debug)]
pub enum PrimitiveContainer {
TextRun(TextRunPrimitiveCpu),
- Border(BorderPrimitiveCpu),
Brush(BrushPrimitive),
}
impl PrimitiveContainer {
// Return true if the primary primitive is visible.
// Used to trivially reject non-visible primitives.
// TODO(gw): Currently, primitives other than those
// listed here are handled before the
@@ -1113,19 +1082,16 @@ impl PrimitiveContainer {
BrushKind::YuvImage { .. } |
BrushKind::RadialGradient { .. } |
BrushKind::Border { .. } |
BrushKind::LinearGradient { .. } => {
true
}
}
}
- PrimitiveContainer::Border(..) => {
- true
- }
}
}
// Create a clone of this PrimitiveContainer, applying whatever
// changes are necessary to the primitive to support rendering
// it as part of the supplied shadow.
pub fn create_shadow(&self, shadow: &Shadow) -> PrimitiveContainer {
match *self {
@@ -1161,53 +1127,47 @@ impl PrimitiveContainer {
BrushKind::YuvImage { .. } |
BrushKind::Border { .. } |
BrushKind::RadialGradient { .. } |
BrushKind::LinearGradient { .. } => {
panic!("bug: other brush kinds not expected here yet");
}
}
}
- PrimitiveContainer::Border(..) => {
- panic!("bug: other primitive containers not expected here");
- }
}
}
}
pub struct PrimitiveStore {
/// CPU side information only.
pub cpu_brushes: Vec<BrushPrimitive>,
pub cpu_text_runs: Vec<TextRunPrimitiveCpu>,
pub cpu_metadata: Vec<PrimitiveMetadata>,
- pub cpu_borders: Vec<BorderPrimitiveCpu>,
pub pictures: Vec<PicturePrimitive>,
next_picture_id: u64,
}
impl PrimitiveStore {
pub fn new() -> PrimitiveStore {
PrimitiveStore {
cpu_metadata: Vec::new(),
cpu_brushes: Vec::new(),
cpu_text_runs: Vec::new(),
- cpu_borders: Vec::new(),
pictures: Vec::new(),
next_picture_id: 0,
}
}
pub fn recycle(self) -> Self {
PrimitiveStore {
cpu_metadata: recycle_vec(self.cpu_metadata),
cpu_brushes: recycle_vec(self.cpu_brushes),
cpu_text_runs: recycle_vec(self.cpu_text_runs),
- cpu_borders: recycle_vec(self.cpu_borders),
pictures: recycle_vec(self.pictures),
next_picture_id: self.next_picture_id,
}
}
pub fn add_image_picture(
&mut self,
@@ -1292,27 +1252,16 @@ impl PrimitiveStore {
prim_kind: PrimitiveKind::TextRun,
cpu_prim_index: SpecificPrimitiveIndex(self.cpu_text_runs.len()),
..base_metadata
};
self.cpu_text_runs.push(text_cpu);
metadata
}
- PrimitiveContainer::Border(border_cpu) => {
- let metadata = PrimitiveMetadata {
- opacity: PrimitiveOpacity::translucent(),
- prim_kind: PrimitiveKind::Border,
- cpu_prim_index: SpecificPrimitiveIndex(self.cpu_borders.len()),
- ..base_metadata
- };
-
- self.cpu_borders.push(border_cpu);
- metadata
- }
};
self.cpu_metadata.push(metadata);
PrimitiveIndex(prim_index)
}
// Internal method that retrieves the primitive index of a primitive
@@ -1360,18 +1309,17 @@ impl PrimitiveStore {
}
BrushKind::Border { .. } |
BrushKind::YuvImage { .. } |
BrushKind::LinearGradient { .. } |
BrushKind::RadialGradient { .. } |
BrushKind::Clear => {}
}
}
- PrimitiveKind::TextRun |
- PrimitiveKind::Border => {}
+ PrimitiveKind::TextRun => {}
}
None
}
// Apply any optimizations to drawing this picture. Currently,
// we just support collapsing pictures with an opacity filter
// by pushing that opacity value into the color of a primitive
@@ -1410,18 +1358,17 @@ impl PrimitiveStore {
BrushKind::YuvImage { .. } |
BrushKind::Border { .. } |
BrushKind::LinearGradient { .. } |
BrushKind::RadialGradient { .. } => {
unreachable!("bug: invalid prim type for opacity collapse");
}
};
}
- PrimitiveKind::TextRun |
- PrimitiveKind::Border => {
+ PrimitiveKind::TextRun => {
unreachable!("bug: invalid prim type for opacity collapse");
}
}
// The opacity filter has been collapsed, so mark this picture
// as a pass though. This means it will no longer allocate an
// intermediate surface or incur an extra blend / blit. Instead,
// the collapsed primitive will be drawn directly into the
@@ -1440,17 +1387,17 @@ impl PrimitiveStore {
fn build_prim_segments_if_needed(
&mut self,
prim_index: PrimitiveIndex,
pic_state: &mut PictureState,
frame_state: &mut FrameBuildingState,
frame_context: &FrameBuildingContext,
) {
- let metadata = &self.cpu_metadata[prim_index.0];
+ let metadata = &mut self.cpu_metadata[prim_index.0];
if metadata.prim_kind != PrimitiveKind::Brush {
return;
}
let brush = &mut self.cpu_brushes[metadata.cpu_prim_index.0];
if let BrushKind::Border { ref mut source, .. } = brush.kind {
@@ -1508,16 +1455,20 @@ impl PrimitiveStore {
}
));
if needs_update {
brush.segment_desc = Some(BrushSegmentDescriptor {
segments: new_segments,
clip_mask_kind: BrushClipMaskKind::Unknown,
});
+
+ // The segments have changed, so force the GPU cache to
+ // re-upload the primitive information.
+ frame_state.gpu_cache.invalidate(&mut metadata.gpu_location);
}
}
}
}
fn prepare_prim_for_render_inner(
&mut self,
prim_index: PrimitiveIndex,
@@ -1531,17 +1482,16 @@ impl PrimitiveStore {
let mut is_tiled = false;
let metadata = &mut self.cpu_metadata[prim_index.0];
#[cfg(debug_assertions)]
{
metadata.prepared_frame_id = frame_state.render_tasks.frame_id();
}
match metadata.prim_kind {
- PrimitiveKind::Border => {}
PrimitiveKind::TextRun => {
let text = &mut self.cpu_text_runs[metadata.cpu_prim_index.0];
// The transform only makes sense for screen space rasterization
let transform = Some(prim_run_context.scroll_node.world_content_transform.into());
text.prepare_for_render(
frame_context.device_pixel_scale,
transform,
pic_context.allow_subpixel_aa,
@@ -1801,30 +1751,30 @@ impl PrimitiveStore {
}
BorderSource::Border { .. } => {
// Handled earlier since we need to update the segment
// descriptor *before* update_clip_task() is called.
}
}
}
BrushKind::RadialGradient {
- gradient_index,
stops_range,
center,
start_radius,
end_radius,
ratio_xy,
extend_mode,
stretch_size,
tile_spacing,
+ ref mut stops_handle,
ref mut visible_tiles,
..
} => {
build_gradient_stops_request(
- gradient_index,
+ stops_handle,
stops_range,
false,
frame_state,
pic_context,
);
if tile_spacing != LayoutSize::zero() {
is_tiled = true;
@@ -1853,30 +1803,30 @@ impl PrimitiveStore {
stretch_size.height,
]);
request.write_segment(*rect, [0.0; 4]);
},
);
}
}
BrushKind::LinearGradient {
- gradient_index,
stops_range,
reverse_stops,
start_point,
end_point,
extend_mode,
stretch_size,
tile_spacing,
+ ref mut stops_handle,
ref mut visible_tiles,
..
} => {
build_gradient_stops_request(
- gradient_index,
+ stops_handle,
stops_range,
reverse_stops,
frame_state,
pic_context,
);
if tile_spacing != LayoutSize::zero() {
is_tiled = true;
@@ -1943,20 +1893,16 @@ impl PrimitiveStore {
// Mark this GPU resource as required for this frame.
if let Some(mut request) = frame_state.gpu_cache.request(&mut metadata.gpu_location) {
// has to match VECS_PER_BRUSH_PRIM
request.push(metadata.local_rect);
request.push(metadata.local_clip_rect);
match metadata.prim_kind {
- PrimitiveKind::Border => {
- let border = &self.cpu_borders[metadata.cpu_prim_index.0];
- border.write_gpu_blocks(request);
- }
PrimitiveKind::TextRun => {
let text = &self.cpu_text_runs[metadata.cpu_prim_index.0];
text.write_gpu_blocks(&mut request);
}
PrimitiveKind::Brush => {
let brush = &self.cpu_brushes[metadata.cpu_prim_index.0];
brush.write_gpu_blocks(&mut request, metadata.local_rect);
@@ -2073,17 +2019,16 @@ impl PrimitiveStore {
-0.5 * info.shadow_rect_alloc_size.width,
-0.5 * info.shadow_rect_alloc_size.height,
),
inner_clip_mode,
);
continue;
}
- ClipSource::BorderCorner(..) |
ClipSource::LineDecoration(..) |
ClipSource::Image(..) => {
rect_clips_only = false;
// TODO(gw): We can easily extend the segment builder
// to support these clip sources in the
// future, but they are rarely used.
clip_mask_kind = BrushClipMaskKind::Global;
@@ -2468,16 +2413,17 @@ impl PrimitiveStore {
let metadata = &mut self.cpu_metadata[prim_index.0];
let new_local_rect = pic.update_local_rect(result);
if new_local_rect != metadata.local_rect {
metadata.local_rect = new_local_rect;
frame_state.gpu_cache.invalidate(&mut metadata.gpu_location);
+ pic_state.local_rect_changed = true;
}
}
}
let (local_rect, unclipped_device_rect) = {
let metadata = &mut self.cpu_metadata[prim_index.0];
if metadata.local_rect.size.width <= 0.0 ||
metadata.local_rect.size.height <= 0.0 {
@@ -2667,23 +2613,22 @@ impl PrimitiveStore {
}
}
result
}
}
fn build_gradient_stops_request(
- gradient_index: CachedGradientIndex,
+ stops_handle: &mut GpuCacheHandle,
stops_range: ItemRange<GradientStop>,
reverse_stops: bool,
frame_state: &mut FrameBuildingState,
pic_context: &PictureContext
) {
- let stops_handle = &mut frame_state.cached_gradients[gradient_index.0].handle;
if let Some(mut request) = frame_state.gpu_cache.request(stops_handle) {
let gradient_builder = GradientGpuBlockBuilder::new(
stops_range,
pic_context.display_list,
);
gradient_builder.build(
reverse_stops,
&mut request,
--- a/gfx/webrender/src/profiler.rs
+++ b/gfx/webrender/src/profiler.rs
@@ -1209,8 +1209,55 @@ impl Profiler {
renderer_timers,
gpu_samplers,
screen_fraction,
debug_renderer,
);
}
}
}
+
+#[cfg(feature = "debug_renderer")]
+pub struct ChangeIndicator {
+ counter: u32,
+}
+
+#[cfg(feature = "debug_renderer")]
+impl ChangeIndicator {
+ pub fn new() -> Self {
+ ChangeIndicator {
+ counter: 0
+ }
+ }
+
+ pub fn changed(&mut self) {
+ self.counter = (self.counter + 1) % 15;
+ }
+
+ pub fn draw(
+ &self,
+ x: f32, y: f32,
+ color: ColorU,
+ debug_renderer: &mut DebugRenderer
+ ) {
+ let margin = 0.0;
+ let w = 10.0;
+ let h = 5.0;
+ let tx = self.counter as f32 * w;
+ debug_renderer.add_quad(
+ x - margin,
+ y - margin,
+ x + 15.0 * w + margin,
+ y + h + margin,
+ ColorU::new(0, 0, 0, 150),
+ ColorU::new(0, 0, 0, 150),
+ );
+
+ debug_renderer.add_quad(
+ x + tx,
+ y,
+ x + tx + w,
+ y + h,
+ color,
+ ColorU::new(25, 25, 25, 255),
+ );
+ }
+}
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -106,21 +106,16 @@ struct Document {
// made available as output textures.
output_pipelines: FastHashSet<PipelineId>,
// A helper switch to prevent any frames rendering triggered by scrolling
// messages between `SetDisplayList` and `GenerateFrame`.
// If we allow them, then a reftest that scrolls a few layers before generating
// the first frame would produce inconsistent rendering results, because
// scroll events are not necessarily received in deterministic order.
render_on_scroll: Option<bool>,
- // A helper flag to prevent any hit-tests from happening between calls
- // to build_scene and rendering the document. In between these two calls,
- // hit-tests produce inconsistent results because the clip_scroll_tree
- // is out of sync with the display list.
- render_on_hittest: bool,
/// A data structure to allow hit testing against rendered frames. This is updated
/// every time we produce a fully rendered frame.
hit_tester: Option<HitTester>,
/// Properties that are resolved during frame building and can be changed at any time
/// without requiring the scene to be re-built.
dynamic_properties: SceneProperties,
@@ -158,30 +153,29 @@ impl Document {
device_pixel_ratio: default_device_pixel_ratio,
},
clip_scroll_tree: ClipScrollTree::new(),
frame_id: FrameId(0),
frame_builder_config,
frame_builder: None,
output_pipelines: FastHashSet::default(),
render_on_scroll,
- render_on_hittest: false,
hit_tester: None,
dynamic_properties: SceneProperties::new(),
}
}
fn can_render(&self) -> bool { self.frame_builder.is_some() }
fn has_pixels(&self) -> bool {
!self.view.window_size.is_empty_or_negative()
}
// TODO: We will probably get rid of this soon and always forward to the scene building thread.
- fn build_scene(&mut self, resource_cache: &mut ResourceCache) {
+ fn build_scene(&mut self, resource_cache: &mut ResourceCache, scene_id: u64) {
let max_texture_size = resource_cache.max_texture_size();
if self.view.window_size.width > max_texture_size ||
self.view.window_size.height > max_texture_size {
error!("ERROR: Invalid window dimensions {}x{}. Please call api.set_window_size()",
self.view.window_size.width,
self.view.window_size.height,
);
@@ -194,30 +188,31 @@ impl Document {
Some(root_pipeline_id) => root_pipeline_id,
None => return,
};
if !self.pending.scene.pipelines.contains_key(&root_pipeline_id) {
return;
}
- // The DisplayListFlattener will re-create the up-to-date current scene's pipeline epoch
+ // The DisplayListFlattener re-create the up-to-date current scene's pipeline epoch
// map and clip scroll tree from the information in the pending scene.
self.current.scene.pipeline_epochs.clear();
let old_scrolling_states = self.clip_scroll_tree.drain();
let frame_builder = DisplayListFlattener::create_frame_builder(
old_builder,
&self.pending.scene,
&mut self.clip_scroll_tree,
resource_cache.get_font_instances(),
&self.view,
&self.output_pipelines,
&self.frame_builder_config,
&mut self.current.scene,
+ scene_id,
);
self.clip_scroll_tree.finalize_and_apply_pending_scroll_offsets(old_scrolling_states);
if !self.current.removed_pipelines.is_empty() {
warn!("Built the scene several times without rendering it.");
}
@@ -228,16 +223,17 @@ impl Document {
self.frame_id.0 += 1;
}
fn forward_transaction_to_scene_builder(
&mut self,
transaction_msg: TransactionMsg,
document_ops: &DocumentOps,
document_id: DocumentId,
+ scene_id: u64,
resource_cache: &ResourceCache,
scene_tx: &Sender<SceneBuilderRequest>,
) {
// Do as much of the error handling as possible here before dispatching to
// the scene builder thread.
let build_scene: bool = document_ops.build
&& self.pending.scene.root_pipeline_id.map(
|id| { self.pending.scene.pipelines.contains_key(&id) }
@@ -245,16 +241,17 @@ impl Document {
let scene_request = if build_scene {
Some(SceneRequest {
scene: self.pending.scene.clone(),
removed_pipelines: replace(&mut self.pending.removed_pipelines, Vec::new()),
view: self.view.clone(),
font_instances: resource_cache.get_font_instances(),
output_pipelines: self.output_pipelines.clone(),
+ scene_id,
})
} else {
None
};
scene_tx.send(SceneBuilderRequest::Transaction {
scene: scene_request,
resource_updates: transaction_msg.resource_updates,
@@ -264,16 +261,17 @@ impl Document {
}).unwrap();
}
fn render(
&mut self,
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
resource_profile: &mut ResourceProfileCounters,
+ is_new_scene: bool,
) -> RenderedDocument {
let accumulated_scale_factor = self.view.accumulated_scale_factor();
let pan = self.view.pan.to_f32() / accumulated_scale_factor;
let frame = {
let frame_builder = self.frame_builder.as_mut().unwrap();
let frame = frame_builder.build(
resource_cache,
@@ -287,17 +285,20 @@ impl Document {
&mut resource_profile.texture_cache,
&mut resource_profile.gpu_cache,
&self.dynamic_properties,
);
self.hit_tester = Some(frame_builder.create_hit_tester(&self.clip_scroll_tree));
frame
};
- RenderedDocument::new(frame)
+ RenderedDocument {
+ frame,
+ is_new_scene,
+ }
}
pub fn updated_pipeline_info(&mut self) -> PipelineInfo {
let removed_pipelines = replace(&mut self.current.removed_pipelines, Vec::new());
PipelineInfo {
epochs: self.current.scene.pipeline_epochs.clone(),
removed_pipelines,
}
@@ -392,16 +393,17 @@ static NEXT_NAMESPACE_ID: AtomicUsize =
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
struct PlainRenderBackend {
default_device_pixel_ratio: f32,
enable_render_on_scroll: bool,
frame_config: FrameBuilderConfig,
documents: FastHashMap<DocumentId, DocumentView>,
resources: PlainResources,
+ last_scene_id: u64,
}
/// The render backend is responsible for transforming high level display lists into
/// GPU-friendly work which is then submitted to the renderer in the form of a frame::Frame.
///
/// The render backend operates on its own thread.
pub struct RenderBackend {
api_rx: MsgReceiver<ApiMsg>,
@@ -419,16 +421,17 @@ pub struct RenderBackend {
frame_config: FrameBuilderConfig,
documents: FastHashMap<DocumentId, Document>,
notifier: Box<RenderNotifier>,
recorder: Option<Box<ApiRecordingReceiver>>,
sampler: Option<Box<AsyncPropertySampler + Send>>,
+ last_scene_id: u64,
enable_render_on_scroll: bool,
}
impl RenderBackend {
pub fn new(
api_rx: MsgReceiver<ApiMsg>,
payload_rx: Receiver<Payload>,
result_tx: Sender<ResultMsg>,
@@ -455,16 +458,17 @@ impl RenderBackend {
default_device_pixel_ratio,
resource_cache,
gpu_cache: GpuCache::new(),
frame_config,
documents: FastHashMap::default(),
notifier,
recorder,
sampler,
+ last_scene_id: 0,
enable_render_on_scroll,
}
}
fn process_scene_msg(
&mut self,
document_id: DocumentId,
message: SceneMsg,
@@ -683,16 +687,22 @@ impl RenderBackend {
}
}
}
fn next_namespace_id(&self) -> IdNamespace {
IdNamespace(NEXT_NAMESPACE_ID.fetch_add(1, Ordering::Relaxed) as u32)
}
+ pub fn make_unique_scene_id(&mut self) -> u64 {
+ // 2^64 scenes ought to be enough for anybody!
+ self.last_scene_id += 1;
+ self.last_scene_id
+ }
+
pub fn run(&mut self, mut profile_counters: BackendProfileCounters) {
let mut frame_counter: u32 = 0;
let mut keep_going = true;
if let Some(ref sampler) = self.sampler {
sampler.register();
}
@@ -707,17 +717,16 @@ impl RenderBackend {
resource_updates,
frame_ops,
render,
result_tx,
} => {
if let Some(doc) = self.documents.get_mut(&document_id) {
if let Some(mut built_scene) = built_scene.take() {
doc.new_async_scene_ready(built_scene);
- doc.render_on_hittest = true;
}
if let Some(tx) = result_tx {
let (resume_tx, resume_rx) = channel();
tx.send(SceneSwapResult::Complete(resume_tx)).unwrap();
// Block until the post-swap hook has completed on
// the scene builder thread. We need to do this before
// we can sample from the sampler hook which might happen
// in the update_document call below.
@@ -741,17 +750,18 @@ impl RenderBackend {
use_scene_builder_thread: false,
};
if !transaction_msg.is_empty() {
self.update_document(
document_id,
transaction_msg,
&mut frame_counter,
- &mut profile_counters
+ &mut profile_counters,
+ DocumentOps::render(),
);
}
},
SceneBuilderResult::FlushComplete(tx) => {
tx.send(()).ok();
}
SceneBuilderResult::Stopped => {
panic!("We haven't sent a Stop yet, how did we get a Stopped back?");
@@ -940,78 +950,83 @@ impl RenderBackend {
ApiMsg::ShutDown => {
return false;
}
ApiMsg::UpdateDocument(document_id, doc_msgs) => {
self.update_document(
document_id,
doc_msgs,
frame_counter,
- profile_counters
+ profile_counters,
+ DocumentOps::nop(),
)
}
}
true
}
fn update_document(
&mut self,
document_id: DocumentId,
mut transaction_msg: TransactionMsg,
frame_counter: &mut u32,
profile_counters: &mut BackendProfileCounters,
+ initial_op: DocumentOps,
) {
- let mut op = DocumentOps::nop();
+ let mut op = initial_op;
for scene_msg in transaction_msg.scene_ops.drain(..) {
let _timer = profile_counters.total_time.timer();
op.combine(
self.process_scene_msg(
document_id,
scene_msg,
*frame_counter,
&mut profile_counters.ipc,
)
);
}
if transaction_msg.use_scene_builder_thread {
+ let scene_id = self.make_unique_scene_id();
let doc = self.documents.get_mut(&document_id).unwrap();
+
doc.forward_transaction_to_scene_builder(
transaction_msg,
&op,
document_id,
+ scene_id,
&self.resource_cache,
&self.scene_tx,
);
return;
}
self.resource_cache.update_resources(
transaction_msg.resource_updates,
&mut profile_counters.resources,
);
if op.build {
+ let scene_id = self.make_unique_scene_id();
let doc = self.documents.get_mut(&document_id).unwrap();
let _timer = profile_counters.total_time.timer();
profile_scope!("build scene");
- doc.build_scene(&mut self.resource_cache);
- doc.render_on_hittest = true;
+ doc.build_scene(&mut self.resource_cache, scene_id);
}
// If we have a sampler, get more frame ops from it and add them
// to the transaction. This is a hook to allow the WR user code to
// fiddle with things after a potentially long scene build, but just
// before rendering. This is useful for rendering with the latest
// async transforms.
- if transaction_msg.generate_frame {
+ if op.render || transaction_msg.generate_frame {
if let Some(ref sampler) = self.sampler {
transaction_msg.frame_ops.append(&mut sampler.sample());
}
}
for frame_msg in transaction_msg.frame_ops {
let _timer = profile_counters.total_time.timer();
op.combine(self.process_frame_msg(document_id, frame_msg));
@@ -1048,16 +1063,17 @@ impl RenderBackend {
// borrow ck hack for profile_counters
let (pending_update, rendered_document) = {
let _timer = profile_counters.total_time.timer();
let rendered_document = doc.render(
&mut self.resource_cache,
&mut self.gpu_cache,
&mut profile_counters.resources,
+ op.build,
);
debug!("generated frame for document {:?} with {} passes",
document_id, rendered_document.frame.passes.len());
let msg = ResultMsg::UpdateGpuCache(self.gpu_cache.extract_updates());
self.result_tx.send(msg).unwrap();
@@ -1072,17 +1088,16 @@ impl RenderBackend {
let msg = ResultMsg::PublishDocument(
document_id,
rendered_document,
pending_update,
profile_counters.clone()
);
self.result_tx.send(msg).unwrap();
profile_counters.reset();
- doc.render_on_hittest = false;
} else if op.render {
// WR-internal optimization to avoid doing a bunch of render work if
// there's no pixels. We still want to pretend to render and request
// a composite to make sure that the callbacks (particularly the
// new_frame_ready callback below) has the right flags.
let msg = ResultMsg::PublishPipelineInfo(doc.updated_pipeline_info());
self.result_tx.send(msg).unwrap();
}
@@ -1242,16 +1257,17 @@ impl RenderBackend {
let file_name = format!("scene-{}-{}", (id.0).0, id.1);
config.serialize(&doc.current.scene, file_name);
}
if config.bits.contains(CaptureBits::FRAME) {
let rendered_document = doc.render(
&mut self.resource_cache,
&mut self.gpu_cache,
&mut profile_counters.resources,
+ true,
);
//TODO: write down doc's pipeline info?
// it has `pipeline_epoch_map`,
// which may capture necessary details for some cases.
let file_name = format!("frame-{}-{}", (id.0).0, id.1);
config.serialize(&rendered_document.frame, file_name);
}
}
@@ -1264,16 +1280,17 @@ impl RenderBackend {
default_device_pixel_ratio: self.default_device_pixel_ratio,
enable_render_on_scroll: self.enable_render_on_scroll,
frame_config: self.frame_config.clone(),
documents: self.documents
.iter()
.map(|(id, doc)| (*id, doc.view.clone()))
.collect(),
resources,
+ last_scene_id: self.last_scene_id,
};
config.serialize(&backend, "backend");
if config.bits.contains(CaptureBits::FRAME) {
// After we rendered the frames, there are pending updates to both
// GPU cache and resources. Instead of serializing them, we are going to make sure
// they are applied on the `Renderer` side.
@@ -1323,16 +1340,17 @@ impl RenderBackend {
None => GpuCache::new(),
};
self.documents.clear();
self.default_device_pixel_ratio = backend.default_device_pixel_ratio;
self.frame_config = backend.frame_config;
self.enable_render_on_scroll = backend.enable_render_on_scroll;
+ let mut last_scene_id = backend.last_scene_id;
for (id, view) in backend.documents {
debug!("\tdocument {:?}", id);
let scene_name = format!("scene-{}-{}", (id.0).0, id.1);
let scene = CaptureConfig::deserialize::<Scene, _>(root, &scene_name)
.expect(&format!("Unable to open {}.ron", scene_name));
let mut doc = Document {
current: SceneData {
@@ -1345,33 +1363,34 @@ impl RenderBackend {
},
view,
clip_scroll_tree: ClipScrollTree::new(),
frame_id: FrameId(0),
frame_builder_config: self.frame_config.clone(),
frame_builder: Some(FrameBuilder::empty()),
output_pipelines: FastHashSet::default(),
render_on_scroll: None,
- render_on_hittest: false,
dynamic_properties: SceneProperties::new(),
hit_tester: None,
};
let frame_name = format!("frame-{}-{}", (id.0).0, id.1);
let render_doc = match CaptureConfig::deserialize::<Frame, _>(root, frame_name) {
Some(frame) => {
info!("\tloaded a built frame with {} passes", frame.passes.len());
- RenderedDocument::new(frame)
+ RenderedDocument { frame, is_new_scene: true }
}
None => {
- doc.build_scene(&mut self.resource_cache);
+ last_scene_id += 1;
+ doc.build_scene(&mut self.resource_cache, last_scene_id);
doc.render(
&mut self.resource_cache,
&mut self.gpu_cache,
&mut profile_counters.resources,
+ true,
)
}
};
let msg_update = ResultMsg::UpdateGpuCache(self.gpu_cache.extract_updates());
self.result_tx.send(msg_update).unwrap();
let msg_publish = ResultMsg::PublishDocument(
--- a/gfx/webrender/src/render_task.rs
+++ b/gfx/webrender/src/render_task.rs
@@ -448,18 +448,17 @@ impl RenderTask {
root_task_id
}
));
}
ClipSource::Rectangle(..) |
ClipSource::RoundedRectangle(..) |
ClipSource::Image(..) |
- ClipSource::LineDecoration(..) |
- ClipSource::BorderCorner(..) => {}
+ ClipSource::LineDecoration(..) => {}
}
}
}
RenderTask {
children,
location: RenderTaskLocation::Dynamic(None, Some(outer_rect.size)),
kind: RenderTaskKind::CacheMask(CacheMaskTask {
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -77,17 +77,17 @@ cfg_if! {
use api::channel::MsgSender;
}
}
cfg_if! {
if #[cfg(feature = "debug_renderer")] {
use api::ColorU;
use debug_render::DebugRenderer;
- use profiler::Profiler;
+ use profiler::{Profiler, ChangeIndicator};
use query::GpuTimer;
}
}
pub const MAX_VERTEX_TEXTURE_WIDTH: usize = 1024;
/// Enabling this toggle would force the GPU cache scattered texture to
/// be resized every frame, which enables GPU debuggers to see if this
/// is performed correctly.
@@ -143,24 +143,16 @@ const GPU_TAG_SETUP_DATA: GpuProfileTag
const GPU_TAG_PRIM_SPLIT_COMPOSITE: GpuProfileTag = GpuProfileTag {
label: "SplitComposite",
color: debug_colors::DARKBLUE,
};
const GPU_TAG_PRIM_TEXT_RUN: GpuProfileTag = GpuProfileTag {
label: "TextRun",
color: debug_colors::BLUE,
};
-const GPU_TAG_PRIM_BORDER_CORNER: GpuProfileTag = GpuProfileTag {
- label: "BorderCorner",
- color: debug_colors::DARKSLATEGREY,
-};
-const GPU_TAG_PRIM_BORDER_EDGE: GpuProfileTag = GpuProfileTag {
- label: "BorderEdge",
- color: debug_colors::LAVENDER,
-};
const GPU_TAG_BLUR: GpuProfileTag = GpuProfileTag {
label: "Blur",
color: debug_colors::VIOLET,
};
const GPU_TAG_BLIT: GpuProfileTag = GpuProfileTag {
label: "Blit",
color: debug_colors::LIME,
};
@@ -178,26 +170,22 @@ const GPU_SAMPLER_TAG_TRANSPARENT: GpuPr
color: debug_colors::BLACK,
};
impl TransformBatchKind {
#[cfg(feature = "debugger")]
fn debug_name(&self) -> &'static str {
match *self {
TransformBatchKind::TextRun(..) => "TextRun",
- TransformBatchKind::BorderCorner => "BorderCorner",
- TransformBatchKind::BorderEdge => "BorderEdge",
}
}
fn sampler_tag(&self) -> GpuProfileTag {
match *self {
TransformBatchKind::TextRun(..) => GPU_TAG_PRIM_TEXT_RUN,
- TransformBatchKind::BorderCorner => GPU_TAG_PRIM_BORDER_CORNER,
- TransformBatchKind::BorderEdge => GPU_TAG_PRIM_BORDER_EDGE,
}
}
}
impl BatchKind {
#[cfg(feature = "debugger")]
fn debug_name(&self) -> &'static str {
match *self {
@@ -243,16 +231,18 @@ bitflags! {
const RENDER_TARGET_DBG = 1 << 1;
const TEXTURE_CACHE_DBG = 1 << 2;
const GPU_TIME_QUERIES = 1 << 3;
const GPU_SAMPLE_QUERIES= 1 << 4;
const DISABLE_BATCHING = 1 << 5;
const EPOCHS = 1 << 6;
const COMPACT_PROFILER = 1 << 7;
const ECHO_DRIVER_MESSAGES = 1 << 8;
+ const NEW_FRAME_INDICATOR = 1 << 9;
+ const NEW_SCENE_INDICATOR = 1 << 10;
}
}
fn flag_changed(before: DebugFlags, after: DebugFlags, select: DebugFlags) -> Option<bool> {
if before & select != after & select {
Some(after.contains(select))
} else {
None
@@ -433,16 +423,26 @@ pub(crate) mod desc {
count: 2,
kind: VertexAttributeKind::F32,
},
VertexAttribute {
name: "aRadii",
count: 2,
kind: VertexAttributeKind::F32,
},
+ VertexAttribute {
+ name: "aClipParams1",
+ count: 4,
+ kind: VertexAttributeKind::F32,
+ },
+ VertexAttribute {
+ name: "aClipParams2",
+ count: 4,
+ kind: VertexAttributeKind::F32,
+ },
],
};
pub const CLIP: VertexDescriptor = VertexDescriptor {
vertex_attributes: &[
VertexAttribute {
name: "aPosition",
count: 2,
@@ -616,17 +616,16 @@ pub(crate) mod desc {
};
}
#[derive(Debug, Copy, Clone)]
pub(crate) enum VertexArrayKind {
Primitive,
Blur,
Clip,
- DashAndDot,
VectorStencil,
VectorCover,
Border,
}
#[derive(Clone, Debug, PartialEq)]
pub enum GraphicsApi {
OpenGL,
@@ -1370,16 +1369,21 @@ pub struct Renderer {
enable_clear_scissor: bool,
#[cfg(feature = "debug_renderer")]
debug: LazyInitializedDebugRenderer,
debug_flags: DebugFlags,
backend_profile_counters: BackendProfileCounters,
profile_counters: RendererProfileCounters,
#[cfg(feature = "debug_renderer")]
profiler: Profiler,
+ #[cfg(feature = "debug_renderer")]
+ new_frame_indicator: ChangeIndicator,
+ #[cfg(feature = "debug_renderer")]
+ new_scene_indicator: ChangeIndicator,
+
last_time: u64,
pub gpu_profile: GpuProfiler<GpuProfileTag>,
vaos: RendererVAOs,
node_data_texture: VertexDataTexture,
local_clip_rects_texture: VertexDataTexture,
render_task_texture: VertexDataTexture,
@@ -1772,16 +1776,20 @@ impl Renderer {
shaders,
#[cfg(feature = "debug_renderer")]
debug: LazyInitializedDebugRenderer::new(),
debug_flags,
backend_profile_counters: BackendProfileCounters::new(),
profile_counters: RendererProfileCounters::new(),
#[cfg(feature = "debug_renderer")]
profiler: Profiler::new(),
+ #[cfg(feature = "debug_renderer")]
+ new_frame_indicator: ChangeIndicator::new(),
+ #[cfg(feature = "debug_renderer")]
+ new_scene_indicator: ChangeIndicator::new(),
max_texture_size: max_device_size,
max_recorded_profiles: options.max_recorded_profiles,
clear_color: options.clear_color,
enable_clear_scissor: options.enable_clear_scissor,
last_time: 0,
gpu_profile,
gpu_glyph_renderer,
vaos: RendererVAOs {
@@ -1861,16 +1869,21 @@ impl Renderer {
self.pipeline_info.removed_pipelines.extend(pipeline_info.removed_pipelines.drain(..));
}
ResultMsg::PublishDocument(
document_id,
mut doc,
texture_update_list,
profile_counters,
) => {
+ if doc.is_new_scene {
+ #[cfg(feature = "debug_renderer")]
+ self.new_scene_indicator.changed();
+ }
+
// Add a new document to the active set, expressed as a `Vec` in order
// to re-order based on `DocumentLayer` during rendering.
match self.active_documents.iter().position(|&(id, _)| id == document_id) {
Some(pos) => {
// If the document we are replacing must be drawn
// (in order to update the texture cache), issue
// a render just to off-screen targets.
if self.active_documents[pos].1.frame.must_be_drawn() {
@@ -1976,26 +1989,16 @@ impl Renderer {
);
debug_target.add(
debug_server::BatchKind::Cache,
"Zero Clears",
target.zero_clears.len(),
);
debug_target.add(
debug_server::BatchKind::Clip,
- "Clear",
- target.clip_batcher.border_clears.len(),
- );
- debug_target.add(
- debug_server::BatchKind::Clip,
- "Borders",
- target.clip_batcher.borders.len(),
- );
- debug_target.add(
- debug_server::BatchKind::Clip,
"BoxShadows",
target.clip_batcher.box_shadows.len(),
);
debug_target.add(
debug_server::BatchKind::Clip,
"LineDecorations",
target.clip_batcher.line_decorations.len(),
);
@@ -2146,16 +2149,22 @@ impl Renderer {
self.set_debug_flag(DebugFlags::RENDER_TARGET_DBG, enable);
}
DebugCommand::EnableGpuTimeQueries(enable) => {
self.set_debug_flag(DebugFlags::GPU_TIME_QUERIES, enable);
}
DebugCommand::EnableGpuSampleQueries(enable) => {
self.set_debug_flag(DebugFlags::GPU_SAMPLE_QUERIES, enable);
}
+ DebugCommand::EnableNewFrameIndicator(enable) => {
+ self.set_debug_flag(DebugFlags::NEW_FRAME_INDICATOR, enable);
+ }
+ DebugCommand::EnableNewSceneIndicator(enable) => {
+ self.set_debug_flag(DebugFlags::NEW_SCENE_INDICATOR, enable);
+ }
DebugCommand::EnableDualSourceBlending(_) => {
panic!("Should be handled by render backend");
}
DebugCommand::FetchDocuments |
DebugCommand::FetchClipScrollTree => {}
DebugCommand::FetchRenderTasks => {
let json = self.get_render_tasks_for_debugger();
self.debug_server.send(json);
@@ -2389,16 +2398,33 @@ impl Renderer {
&mut profile_timers,
&profile_samplers,
screen_fraction,
self.debug.get_mut(&mut self.device),
self.debug_flags.contains(DebugFlags::COMPACT_PROFILER),
);
}
}
+
+ if self.debug_flags.contains(DebugFlags::NEW_FRAME_INDICATOR) {
+ self.new_frame_indicator.changed();
+ self.new_frame_indicator.draw(
+ 0.0, 0.0,
+ ColorU::new(0, 110, 220, 255),
+ self.debug.get_mut(&mut self.device)
+ );
+ }
+
+ if self.debug_flags.contains(DebugFlags::NEW_SCENE_INDICATOR) {
+ self.new_scene_indicator.draw(
+ 160.0, 0.0,
+ ColorU::new(220, 30, 10, 255),
+ self.debug.get_mut(&mut self.device)
+ );
+ }
}
if self.debug_flags.contains(DebugFlags::ECHO_DRIVER_MESSAGES) {
self.device.echo_driver_messages();
}
self.backend_profile_counters.reset();
self.profile_counters.reset();
@@ -2609,16 +2635,22 @@ impl Renderer {
}
pub(crate) fn draw_instanced_batch_with_previously_bound_textures<T>(
&mut self,
data: &[T],
vertex_array_kind: VertexArrayKind,
stats: &mut RendererStats,
) {
+ // If we end up with an empty draw call here, that means we have
+ // probably introduced unnecessary batch breaks during frame
+ // building - so we should be catching this earlier and removing
+ // the batch.
+ debug_assert!(!data.is_empty());
+
let vao = get_vao(vertex_array_kind, &self.vaos, &self.gpu_glyph_renderer);
self.device.bind_vao(vao);
let batched = !self.debug_flags.contains(DebugFlags::DISABLE_BATCHING);
if batched {
self.device
@@ -3161,51 +3193,16 @@ impl Renderer {
}
self.handle_scaling(render_tasks, &target.scalings, SourceTexture::CacheA8);
// Draw the clip items into the tiled alpha mask.
{
let _timer = self.gpu_profile.start_timer(GPU_TAG_CACHE_CLIP);
- // If we have border corner clips, the first step is to clear out the
- // area in the clip mask. This allows drawing multiple invididual clip
- // in regions below.
- if !target.clip_batcher.border_clears.is_empty() {
- let _gm2 = self.gpu_profile.start_marker("clip borders [clear]");
- self.device.set_blend(false);
- self.shaders.cs_clip_border
- .bind(&mut self.device, projection, &mut self.renderer_errors);
- self.draw_instanced_batch(
- &target.clip_batcher.border_clears,
- VertexArrayKind::DashAndDot,
- &BatchTextures::no_texture(),
- stats,
- );
- }
-
- // Draw any dots or dashes for border corners.
- if !target.clip_batcher.borders.is_empty() {
- let _gm2 = self.gpu_profile.start_marker("clip borders");
- // We are masking in parts of the corner (dots or dashes) here.
- // Blend mode is set to max to allow drawing multiple dots.
- // The individual dots and dashes in a border never overlap, so using
- // a max blend mode here is fine.
- self.device.set_blend(true);
- self.device.set_blend_mode_max();
- self.shaders.cs_clip_border
- .bind(&mut self.device, projection, &mut self.renderer_errors);
- self.draw_instanced_batch(
- &target.clip_batcher.borders,
- VertexArrayKind::DashAndDot,
- &BatchTextures::no_texture(),
- stats,
- );
- }
-
// switch to multiplicative blending
self.device.set_blend(true);
self.device.set_blend_mode_multiply();
// draw rounded cornered rectangles
if !target.clip_batcher.rectangles.is_empty() {
let _gm2 = self.gpu_profile.start_marker("clip rectangles");
self.shaders.cs_clip_rectangle.bind(
@@ -4548,29 +4545,27 @@ impl Renderer {
fn get_vao<'a>(vertex_array_kind: VertexArrayKind,
vaos: &'a RendererVAOs,
gpu_glyph_renderer: &'a GpuGlyphRenderer)
-> &'a VAO {
match vertex_array_kind {
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,
VertexArrayKind::Border => &vaos.border_vao,
}
}
#[cfg(not(feature = "pathfinder"))]
fn get_vao<'a>(vertex_array_kind: VertexArrayKind,
vaos: &'a RendererVAOs,
_: &'a GpuGlyphRenderer)
-> &'a VAO {
match vertex_array_kind {
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!(),
VertexArrayKind::Border => &vaos.border_vao,
}
}
--- a/gfx/webrender/src/scene_builder.rs
+++ b/gfx/webrender/src/scene_builder.rs
@@ -52,16 +52,17 @@ pub enum SceneSwapResult {
/// Contains the render backend data needed to build a scene.
pub struct SceneRequest {
pub scene: Scene,
pub view: DocumentView,
pub font_instances: FontInstanceMap,
pub output_pipelines: FastHashSet<PipelineId>,
pub removed_pipelines: Vec<PipelineId>,
+ pub scene_id: u64,
}
pub struct BuiltScene {
pub scene: Scene,
pub frame_builder: FrameBuilder,
pub clip_scroll_tree: ClipScrollTree,
pub removed_pipelines: Vec<PipelineId>,
}
--- a/gfx/webrender/src/shade.rs
+++ b/gfx/webrender/src/shade.rs
@@ -45,17 +45,16 @@ impl ImageBufferKind {
pub const IMAGE_BUFFER_KINDS: [ImageBufferKind; 4] = [
ImageBufferKind::Texture2D,
ImageBufferKind::TextureRect,
ImageBufferKind::TextureExternal,
ImageBufferKind::Texture2DArray,
];
-const TRANSFORM_FEATURE: &str = "TRANSFORM";
const ALPHA_FEATURE: &str = "ALPHA_PASS";
const DITHERING_FEATURE: &str = "DITHERING";
const DUAL_SOURCE_FEATURE: &str = "DUAL_SOURCE_BLENDING";
pub(crate) enum ShaderKind {
Primitive,
Cache(VertexArrayKind),
ClipCache,
@@ -256,63 +255,16 @@ impl BrushShader {
self.opaque.deinit(device);
self.alpha.deinit(device);
if let Some(dual_source) = self.dual_source {
dual_source.deinit(device);
}
}
}
-struct PrimitiveShader {
- simple: LazilyCompiledShader,
- transform: LazilyCompiledShader,
-}
-
-impl PrimitiveShader {
- fn new(
- name: &'static str,
- device: &mut Device,
- features: &[&'static str],
- precache: bool,
- ) -> Result<Self, ShaderError> {
- let simple = LazilyCompiledShader::new(
- ShaderKind::Primitive,
- name,
- features,
- device,
- precache,
- )?;
-
- let mut transform_features = features.to_vec();
- transform_features.push(TRANSFORM_FEATURE);
-
- let transform = LazilyCompiledShader::new(
- ShaderKind::Primitive,
- name,
- &transform_features,
- device,
- precache,
- )?;
-
- Ok(PrimitiveShader { simple, transform })
- }
-
- fn get(&mut self, transform_kind: TransformedRectKind) -> &mut LazilyCompiledShader {
- match transform_kind {
- TransformedRectKind::AxisAligned => &mut self.simple,
- TransformedRectKind::Complex => &mut self.transform,
- }
- }
-
- fn deinit(self, device: &mut Device) {
- self.simple.deinit(device);
- self.transform.deinit(device);
- }
-}
-
pub struct TextShader {
simple: LazilyCompiledShader,
transform: LazilyCompiledShader,
glyph_transform: LazilyCompiledShader,
}
impl TextShader {
fn new(
@@ -395,17 +347,16 @@ fn create_prim_shader(
}
debug!("PrimShader {}", name);
let vertex_descriptor = match vertex_format {
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,
VertexArrayKind::Border => desc::BORDER,
};
let program = device.create_program(name, &prefix, &vertex_descriptor);
if let Ok(ref program) = program {
@@ -477,30 +428,27 @@ pub struct Shaders {
brush_linear_gradient: BrushShader,
/// 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.
pub cs_clip_rectangle: LazilyCompiledShader,
pub cs_clip_box_shadow: LazilyCompiledShader,
pub cs_clip_image: LazilyCompiledShader,
- pub cs_clip_border: LazilyCompiledShader,
pub cs_clip_line: LazilyCompiledShader,
// The are "primitive shaders". These shaders draw and blend
// final results on screen. They are aware of tile boundaries.
// Most draw directly to the framebuffer, but some use inputs
// from the cache shaders to draw. Specifically, the box
// shadow primitive shader stretches the box shadow cache
// output, and the cache_image shader blits the results of
// a cache shader (e.g. blur) to the screen.
pub ps_text_run: TextShader,
pub ps_text_run_dual_source: TextShader,
- ps_border_corner: PrimitiveShader,
- ps_border_edge: PrimitiveShader,
ps_split_composite: LazilyCompiledShader,
}
impl Shaders {
pub fn new(
device: &mut Device,
gl_type: GlType,
@@ -606,24 +554,16 @@ impl Shaders {
let cs_clip_image = LazilyCompiledShader::new(
ShaderKind::ClipCache,
"cs_clip_image",
&[],
device,
options.precache_shaders,
)?;
- let cs_clip_border = LazilyCompiledShader::new(
- ShaderKind::ClipCache,
- "cs_clip_border",
- &[],
- device,
- options.precache_shaders,
- )?;
-
let ps_text_run = TextShader::new("ps_text_run",
device,
&[],
options.precache_shaders,
)?;
let ps_text_run_dual_source = TextShader::new("ps_text_run",
device,
@@ -694,30 +634,16 @@ impl Shaders {
);
brush_yuv_image[index] = Some(shader);
yuv_features.clear();
}
}
}
}
- let ps_border_corner = PrimitiveShader::new(
- "ps_border_corner",
- device,
- &[],
- options.precache_shaders,
- )?;
-
- let ps_border_edge = PrimitiveShader::new(
- "ps_border_edge",
- device,
- &[],
- options.precache_shaders,
- )?;
-
let cs_border_segment = LazilyCompiledShader::new(
ShaderKind::Cache(VertexArrayKind::Border),
"cs_border_segment",
&[],
device,
options.precache_shaders,
)?;
@@ -741,23 +667,20 @@ impl Shaders {
brush_image,
brush_blend,
brush_mix_blend,
brush_yuv_image,
brush_radial_gradient,
brush_linear_gradient,
cs_clip_rectangle,
cs_clip_box_shadow,
- cs_clip_border,
cs_clip_image,
cs_clip_line,
ps_text_run,
ps_text_run_dual_source,
- ps_border_corner,
- ps_border_edge,
ps_split_composite,
})
}
fn get_yuv_shader_index(
buffer_kind: ImageBufferKind,
format: YuvFormat,
color_space: YuvColorSpace,
@@ -799,63 +722,53 @@ impl Shaders {
self.brush_yuv_image[shader_index]
.as_mut()
.expect("Unsupported YUV shader kind")
}
};
brush_shader.get(key.blend_mode)
}
BatchKind::Transformable(transform_kind, batch_kind) => {
- let prim_shader = match batch_kind {
+ match batch_kind {
TransformBatchKind::TextRun(glyph_format) => {
let text_shader = match key.blend_mode {
BlendMode::SubpixelDualSource => {
&mut self.ps_text_run_dual_source
}
_ => {
&mut self.ps_text_run
}
};
return text_shader.get(glyph_format, transform_kind);
}
- TransformBatchKind::BorderCorner => {
- &mut self.ps_border_corner
- }
- TransformBatchKind::BorderEdge => {
- &mut self.ps_border_edge
- }
- };
- prim_shader.get(transform_kind)
+ }
}
}
}
pub fn deinit(self, device: &mut Device) {
self.cs_blur_a8.deinit(device);
self.cs_blur_rgba8.deinit(device);
self.brush_solid.deinit(device);
self.brush_blend.deinit(device);
self.brush_mix_blend.deinit(device);
self.brush_radial_gradient.deinit(device);
self.brush_linear_gradient.deinit(device);
self.cs_clip_rectangle.deinit(device);
self.cs_clip_box_shadow.deinit(device);
self.cs_clip_image.deinit(device);
- self.cs_clip_border.deinit(device);
self.cs_clip_line.deinit(device);
self.ps_text_run.deinit(device);
self.ps_text_run_dual_source.deinit(device);
for shader in self.brush_image {
if let Some(shader) = shader {
shader.deinit(device);
}
}
for shader in self.brush_yuv_image {
if let Some(shader) = shader {
shader.deinit(device);
}
}
- self.ps_border_corner.deinit(device);
- self.ps_border_edge.deinit(device);
self.cs_border_segment.deinit(device);
self.ps_split_composite.deinit(device);
}
}
--- a/gfx/webrender/src/tiling.rs
+++ b/gfx/webrender/src/tiling.rs
@@ -12,17 +12,17 @@ use device::{FrameId, Texture};
#[cfg(feature = "pathfinder")]
use euclid::{TypedPoint2D, TypedVector2D};
use gpu_cache::{GpuCache};
use gpu_types::{BorderInstance, BlurDirection, BlurInstance};
use gpu_types::{ClipScrollNodeData, ZBufferIdGenerator};
use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture};
#[cfg(feature = "pathfinder")]
use pathfinder_partitioner::mesh::Mesh;
-use prim_store::{CachedGradient, PrimitiveIndex, PrimitiveKind, PrimitiveStore};
+use prim_store::{PrimitiveIndex, PrimitiveKind, PrimitiveStore};
use prim_store::{BrushKind, DeferredResolve};
use profiler::FrameProfileCounters;
use render_task::{BlitSource, RenderTaskAddress, RenderTaskId, RenderTaskKind};
use render_task::{BlurTask, ClearMode, GlyphTask, RenderTaskLocation, RenderTaskTree};
use resource_cache::ResourceCache;
use std::{cmp, usize, f32, i32, mem};
use texture_allocator::GuillotineAllocator;
#[cfg(feature = "pathfinder")]
@@ -44,17 +44,16 @@ pub struct RenderTargetIndex(pub usize);
pub struct RenderTargetContext<'a, 'rc> {
pub device_pixel_scale: DevicePixelScale,
pub prim_store: &'a PrimitiveStore,
pub resource_cache: &'rc mut ResourceCache,
pub clip_scroll_tree: &'a ClipScrollTree,
pub use_dual_source_blending: bool,
pub node_data: &'a [ClipScrollNodeData],
- pub cached_gradients: &'a [CachedGradient],
}
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
struct TextureAllocator {
// TODO(gw): Replace this with a simpler allocator for
// render target allocation - this use case doesn't need
// to deal with coalescing etc that the general texture
--- a/gfx/webrender/tests/angle_shader_validation.rs
+++ b/gfx/webrender/tests/angle_shader_validation.rs
@@ -32,37 +32,29 @@ const SHADERS: &[Shader] = &[
name: "cs_clip_image",
features: CLIP_FEATURES,
},
Shader {
name: "cs_clip_box_shadow",
features: CLIP_FEATURES,
},
Shader {
- name: "cs_clip_border",
- features: CLIP_FEATURES,
- },
- Shader {
name: "cs_clip_line",
features: CLIP_FEATURES,
},
// Cache shaders
Shader {
name: "cs_blur",
features: CACHE_FEATURES,
},
- // Prim shaders
Shader {
- name: "ps_border_corner",
- features: PRIM_FEATURES,
+ name: "cs_border_segment",
+ features: CACHE_FEATURES,
},
- Shader {
- name: "ps_border_edge",
- features: PRIM_FEATURES,
- },
+ // Prim shaders
Shader {
name: "ps_split_composite",
features: PRIM_FEATURES,
},
Shader {
name: "ps_text_run",
features: PRIM_FEATURES,
},
--- a/gfx/webrender_api/src/api.rs
+++ b/gfx/webrender_api/src/api.rs
@@ -556,16 +556,20 @@ pub enum DebugCommand {
/// Display intermediate render targets on screen.
EnableRenderTargetDebug(bool),
/// Display GPU timing results.
EnableGpuTimeQueries(bool),
/// Display GPU overdraw results
EnableGpuSampleQueries(bool),
/// Configure if dual-source blending is used, if available.
EnableDualSourceBlending(bool),
+ /// Show an indicator that moves every time a frame is rendered.
+ EnableNewFrameIndicator(bool),
+ /// Show an indicator that moves every time a scene is built.
+ EnableNewSceneIndicator(bool),
/// Fetch current documents and display lists.
FetchDocuments,
/// Fetch current passes and batches.
FetchPasses,
/// Fetch clip-scroll tree.
FetchClipScrollTree,
/// Fetch render tasks.
FetchRenderTasks,
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-8e697f8cb1f1aab2e5f6b9b903eb7191340b10c5
+aff9f409f3d6a3518c38c1f7755657f564c1083a
--- a/gfx/wrench/src/rawtest.rs
+++ b/gfx/wrench/src/rawtest.rs
@@ -44,16 +44,17 @@ impl<'a> RawtestHarness<'a> {
self.test_hit_testing();
self.test_retained_blob_images_test();
self.test_blob_update_test();
self.test_blob_update_epoch_test();
self.test_tile_decomposition();
self.test_very_large_blob();
self.test_offscreen_blob();
self.test_save_restore();
+ self.test_blur_cache();
self.test_capture();
self.test_zero_height_window();
}
fn render_and_get_pixels(&mut self, window_rect: DeviceUintRect) -> Vec<u8> {
self.rx.recv().unwrap();
self.wrench.render();
self.wrench.renderer.read_pixels_rgba8(window_rect)
@@ -715,16 +716,61 @@ impl<'a> RawtestHarness<'a> {
};
let first = do_test(false);
let second = do_test(true);
assert_eq!(first, second);
}
+ // regression test for #2769
+ // "async scene building: cache collisions from reused picture ids"
+ fn test_blur_cache(&mut self) {
+ println!("\tblur cache...");
+ let window_size = self.window.get_inner_size();
+
+ let test_size = DeviceUintSize::new(400, 400);
+
+ let window_rect = DeviceUintRect::new(
+ DeviceUintPoint::new(0, window_size.height - test_size.height),
+ test_size,
+ );
+ let layout_size = LayoutSize::new(400., 400.);
+
+ let mut do_test = |shadow_is_red| {
+ let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
+ let shadow_color = if shadow_is_red {
+ ColorF::new(1.0, 0.0, 0.0, 1.0)
+ } else {
+ ColorF::new(0.0, 1.0, 0.0, 1.0)
+ };
+
+ builder.push_shadow(&PrimitiveInfo::new(rect(100., 100., 100., 100.)),
+ Shadow {
+ offset: LayoutVector2D::new(1.0, 1.0),
+ blur_radius: 1.0,
+ color: shadow_color,
+ });
+ builder.push_line(&PrimitiveInfo::new(rect(110., 110., 50., 2.)),
+ 0.0, LineOrientation::Horizontal,
+ &ColorF::new(0.0, 0.0, 0.0, 1.0), LineStyle::Solid);
+ builder.pop_all_shadows();
+
+ let txn = Transaction::new();
+ self.submit_dl(&mut Epoch(0), layout_size, builder, &txn.resource_updates);
+
+ self.render_and_get_pixels(window_rect)
+ };
+
+ let first = do_test(false);
+ let second = do_test(true);
+
+ assert_ne!(first, second);
+ }
+
fn test_capture(&mut self) {
println!("\tcapture...");
let path = "../captures/test";
let layout_size = LayoutSize::new(400., 400.);
let dim = self.window.get_inner_size();
let window_rect = DeviceUintRect::new(
point(0, dim.height - layout_size.height as u32),
size(layout_size.width as u32, layout_size.height as u32),