Bug 1466549 - Update webrender to aff9f409f3d6a3518c38c1f7755657f564c1083a. r=Gankro

MozReview-Commit-ID: 2Vauiblv7eW

--HG--
extra : rebase_source : e538ab000dccbd1e532bb80f8f5d09f22d3968e4
This commit is contained in:
Kartikaya Gupta 2018-06-06 07:34:22 -04:00
parent 5f8ce0f4bc
commit 3ac673052b
30 changed files with 731 additions and 2232 deletions

View File

@ -1,97 +1,53 @@
# Clipping in WebRender
# Clipping and Positioning 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.
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
## 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.
Clips are defined using the ClipRegion in both cases.
# The `ClipScrollTree`
```rust
pub struct ClipRegion {
pub main: LayoutRect,
pub complex: ItemRange<ComplexClip>,
pub image_mask: Option<ImageMask>,
}
```
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.
`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.
# `ClipChain`s
## Item Clips
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.
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`.
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.
## Clip Display Items
# The Future
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 clip display item has a `ClipRegion` as well as several other fields:
```rust
pub struct ClipDisplayItem {
pub id: ClipId,
pub parent_id: ClipId,
}
```
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
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:
```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.

View File

@ -95,10 +95,10 @@ void brush_vs(
// 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;

View File

@ -13,8 +13,8 @@ flat varying vec4 vColor1[2];
// 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
@ -31,6 +31,10 @@ 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;
@ -55,6 +59,10 @@ varying vec2 vPos;
#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;
@ -64,6 +72,8 @@ 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;
@ -120,6 +130,7 @@ 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;
@ -161,10 +172,11 @@ void main(void) {
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;
@ -175,6 +187,20 @@ void main(void) {
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);
}
@ -276,12 +302,12 @@ vec4 evaluate_color_for_style_in_edge(
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) {
@ -292,6 +318,30 @@ void main(void) {
// 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);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -6,23 +6,22 @@ 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};
@ -41,8 +40,6 @@ const OPAQUE_TASK_ADDRESS: RenderTaskAddress = RenderTaskAddress(0x7fff);
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum TransformBatchKind {
TextRun(GlyphFormat),
BorderCorner,
BorderEdge,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
@ -322,6 +319,24 @@ impl BatchList {
}
}
// 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()
}
@ -630,7 +645,6 @@ impl AlphaBatchBuilder {
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));
@ -1021,10 +1035,10 @@ impl AlphaBatchBuilder {
}
}
}
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,
@ -1033,15 +1047,14 @@ impl AlphaBatchBuilder {
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,
@ -1050,7 +1063,6 @@ impl AlphaBatchBuilder {
task_address,
clip_task_address,
z,
ctx,
gpu_cache,
&mut self.batch_list,
);
@ -1060,7 +1072,6 @@ impl AlphaBatchBuilder {
ctx.resource_cache,
gpu_cache,
deferred_resolves,
ctx.cached_gradients,
) {
self.add_brush_to_batch(
brush,
@ -1084,67 +1095,6 @@ impl AlphaBatchBuilder {
}
}
}
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];
@ -1379,12 +1329,14 @@ impl AlphaBatchBuilder {
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,
@ -1393,7 +1345,6 @@ fn add_gradient_tiles(
task_address: RenderTaskAddress,
clip_task_address: RenderTaskAddress,
z: ZBufferId,
ctx: &RenderTargetContext,
gpu_cache: &GpuCache,
batch_list: &mut BatchList,
) {
@ -1407,7 +1358,6 @@ fn add_gradient_tiles(
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 {
@ -1481,7 +1431,6 @@ impl BrushPrimitive {
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, .. } => {
@ -1575,8 +1524,7 @@ impl BrushPrimitive {
[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(),
@ -1587,8 +1535,7 @@ impl BrushPrimitive {
],
))
}
BrushKind::LinearGradient { gradient_index, .. } => {
let stops_handle = &cached_gradients[gradient_index.0].handle;
BrushKind::LinearGradient { ref stops_handle, .. } => {
Some((
BrushBatchKind::LinearGradient,
BatchTextures::no_texture(),
@ -1668,7 +1615,6 @@ 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
}
@ -1829,8 +1775,6 @@ pub struct ClipBatcher {
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>,
}
@ -1840,8 +1784,6 @@ impl ClipBatcher {
ClipBatcher {
rectangles: Vec::new(),
images: FastHashMap::default(),
border_clears: Vec::new(),
borders: Vec::new(),
box_shadows: FastHashMap::default(),
line_decorations: Vec::new(),
}
@ -1953,30 +1895,6 @@ impl ClipBatcher {
..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,
})
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@
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;
@ -87,11 +87,6 @@ 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),
}
@ -341,7 +336,6 @@ impl ClipSources {
.and_then(|r| inner_rect.and_then(|ref inner| r.intersection(inner)));
}
ClipSource::BoxShadow(..) |
ClipSource::BorderCorner { .. } |
ClipSource::LineDecoration(..) => {
can_calculate_inner_rect = false;
break;
@ -400,9 +394,6 @@ impl ClipSources {
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([

View File

@ -20,13 +20,14 @@ 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};
@ -189,9 +190,6 @@ pub struct DisplayListFlattener<'a> {
/// 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> {
@ -204,6 +202,7 @@ impl<'a> DisplayListFlattener<'a> {
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();
@ -224,7 +223,6 @@ impl<'a> DisplayListFlattener<'a> {
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(),
@ -254,7 +252,8 @@ impl<'a> DisplayListFlattener<'a> {
view.inner_rect,
background_color,
view.window_size,
flattener
scene_id,
flattener,
)
}
@ -636,7 +635,6 @@ impl<'a> DisplayListFlattener<'a> {
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,
@ -679,7 +677,6 @@ impl<'a> DisplayListFlattener<'a> {
&prim_info,
info,
item.gradient_stops(),
item.display_list().get(item.gradient_stops()).count(),
);
}
SpecificDisplayItem::PushStackingContext(ref info) => {
@ -1494,7 +1491,6 @@ impl<'a> DisplayListFlattener<'a> {
info: &LayoutPrimitiveInfo,
border_item: &BorderDisplayItem,
gradient_stops: ItemRange<GradientStop>,
gradient_stops_count: usize,
) {
let rect = info.rect;
let create_segments = |outset: SideOffsets2D<f32>| {
@ -1739,7 +1735,6 @@ impl<'a> DisplayListFlattener<'a> {
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(),
@ -1775,14 +1770,10 @@ impl<'a> DisplayListFlattener<'a> {
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 {
@ -1811,12 +1802,11 @@ impl<'a> DisplayListFlattener<'a> {
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(),
@ -1842,9 +1832,6 @@ impl<'a> DisplayListFlattener<'a> {
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 {
@ -1860,7 +1847,7 @@ impl<'a> DisplayListFlattener<'a> {
start_radius,
end_radius,
ratio_xy,
gradient_index,
stops_handle: GpuCacheHandle::new(),
stretch_size,
tile_spacing,
visible_tiles: Vec::new(),
@ -2062,7 +2049,8 @@ pub fn build_scene(config: &FrameBuilderConfig, request: SceneRequest) -> BuiltS
&request.view,
&request.output_pipelines,
config,
&mut new_scene
&mut new_scene,
request.scene_id,
);
BuiltScene {

View File

@ -2,21 +2,24 @@
* 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);

View File

@ -14,7 +14,7 @@ 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};
@ -41,15 +41,16 @@ 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>>,
@ -65,7 +66,6 @@ pub struct FrameBuildingState<'a> {
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,
}
@ -83,6 +83,7 @@ pub struct PictureContext<'a> {
pub struct PictureState {
pub tasks: Vec<RenderTaskId>,
pub has_non_root_coord_system: bool,
pub local_rect_changed: bool,
}
impl PictureState {
@ -90,6 +91,7 @@ impl PictureState {
PictureState {
tasks: Vec::new(),
has_non_root_coord_system: false,
local_rect_changed: false,
}
}
}
@ -118,13 +120,13 @@ 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,
@ -138,17 +140,18 @@ impl FrameBuilder {
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,
}
}
@ -185,6 +188,7 @@ impl FrameBuilder {
.display_list;
let frame_context = FrameBuildingContext {
scene_id: self.scene_id,
device_pixel_scale,
scene_properties,
pipelines,
@ -201,7 +205,6 @@ impl FrameBuilder {
resource_cache,
gpu_cache,
special_render_passes,
cached_gradients: &mut self.cached_gradients,
};
let pic_context = PictureContext {
@ -377,7 +380,6 @@ impl FrameBuilder {
clip_scroll_tree,
use_dual_source_blending,
node_data: &node_data,
cached_gradients: &self.cached_gradients,
};
pass.build(

View File

@ -495,11 +495,6 @@ impl<'a> GpuDataRequest<'a> {
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> {

View File

@ -80,7 +80,7 @@ pub struct BlurInstance {
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))]
@ -107,6 +107,7 @@ pub struct BorderInstance {
pub flags: i32,
pub widths: DeviceSize,
pub radius: DeviceSize,
pub clip_params: [f32; 8],
}
/// A clipping primitive drawn into the clipping mask.

View File

@ -338,7 +338,6 @@ fn get_regions_for_clip_scroll_node(
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");

View File

@ -132,16 +132,7 @@ impl TextureUpdateList {
/// 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 {

View File

@ -80,7 +80,11 @@ pub struct PictureCacheKey {
// 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
@ -374,6 +378,7 @@ impl PicturePrimitive {
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,
@ -467,6 +472,14 @@ impl PicturePrimitive {
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

View File

@ -9,7 +9,7 @@ use api::{GlyphRasterSpace, LayoutPoint, LayoutRect, LayoutSize, LayoutToWorldTr
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;
@ -83,21 +83,6 @@ impl PrimitiveOpacity {
}
}
#[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
@ -150,7 +135,6 @@ pub struct PictureIndex(pub usize);
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum PrimitiveKind {
TextRun,
Border,
Brush,
}
@ -297,7 +281,7 @@ pub enum BrushKind {
image_rendering: ImageRendering,
},
RadialGradient {
gradient_index: CachedGradientIndex,
stops_handle: GpuCacheHandle,
stops_range: ItemRange<GradientStop>,
extend_mode: ExtendMode,
center: LayoutPoint,
@ -309,9 +293,8 @@ pub enum BrushKind {
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,
@ -557,19 +540,6 @@ pub enum ImageSource {
},
}
#[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
@ -1085,7 +1055,6 @@ impl ClipData {
#[derive(Debug)]
pub enum PrimitiveContainer {
TextRun(TextRunPrimitiveCpu),
Border(BorderPrimitiveCpu),
Brush(BrushPrimitive),
}
@ -1118,9 +1087,6 @@ impl PrimitiveContainer {
}
}
}
PrimitiveContainer::Border(..) => {
true
}
}
}
@ -1166,9 +1132,6 @@ impl PrimitiveContainer {
}
}
}
PrimitiveContainer::Border(..) => {
panic!("bug: other primitive containers not expected here");
}
}
}
}
@ -1178,7 +1141,6 @@ pub struct PrimitiveStore {
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,
@ -1190,7 +1152,6 @@ impl 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,
@ -1202,7 +1163,6 @@ impl 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,
@ -1297,17 +1257,6 @@ impl PrimitiveStore {
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);
@ -1365,8 +1314,7 @@ impl PrimitiveStore {
BrushKind::Clear => {}
}
}
PrimitiveKind::TextRun |
PrimitiveKind::Border => {}
PrimitiveKind::TextRun => {}
}
None
@ -1415,8 +1363,7 @@ impl PrimitiveStore {
}
};
}
PrimitiveKind::TextRun |
PrimitiveKind::Border => {
PrimitiveKind::TextRun => {
unreachable!("bug: invalid prim type for opacity collapse");
}
}
@ -1445,7 +1392,7 @@ impl PrimitiveStore {
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;
@ -1513,6 +1460,10 @@ impl PrimitiveStore {
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);
}
}
}
@ -1536,7 +1487,6 @@ impl PrimitiveStore {
}
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
@ -1806,7 +1756,6 @@ impl PrimitiveStore {
}
}
BrushKind::RadialGradient {
gradient_index,
stops_range,
center,
start_radius,
@ -1815,11 +1764,12 @@ impl PrimitiveStore {
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,
@ -1858,7 +1808,6 @@ impl PrimitiveStore {
}
}
BrushKind::LinearGradient {
gradient_index,
stops_range,
reverse_stops,
start_point,
@ -1866,12 +1815,13 @@ impl PrimitiveStore {
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,
@ -1948,10 +1898,6 @@ impl PrimitiveStore {
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);
@ -2078,7 +2024,6 @@ impl PrimitiveStore {
continue;
}
ClipSource::BorderCorner(..) |
ClipSource::LineDecoration(..) |
ClipSource::Image(..) => {
rect_clips_only = false;
@ -2473,6 +2418,7 @@ impl PrimitiveStore {
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;
}
}
}
@ -2672,13 +2618,12 @@ impl PrimitiveStore {
}
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,

View File

@ -1214,3 +1214,50 @@ impl Profiler {
}
}
}
#[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),
);
}
}

View File

@ -111,11 +111,6 @@ struct Document {
// 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.
@ -163,7 +158,6 @@ impl Document {
frame_builder: None,
output_pipelines: FastHashSet::default(),
render_on_scroll,
render_on_hittest: false,
hit_tester: None,
dynamic_properties: SceneProperties::new(),
}
@ -176,7 +170,7 @@ impl Document {
}
// 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 ||
@ -199,7 +193,7 @@ impl Document {
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();
@ -213,6 +207,7 @@ impl Document {
&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);
@ -233,6 +228,7 @@ impl Document {
transaction_msg: TransactionMsg,
document_ops: &DocumentOps,
document_id: DocumentId,
scene_id: u64,
resource_cache: &ResourceCache,
scene_tx: &Sender<SceneBuilderRequest>,
) {
@ -250,6 +246,7 @@ impl Document {
view: self.view.clone(),
font_instances: resource_cache.get_font_instances(),
output_pipelines: self.output_pipelines.clone(),
scene_id,
})
} else {
None
@ -269,6 +266,7 @@ impl Document {
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;
@ -292,7 +290,10 @@ impl Document {
frame
};
RenderedDocument::new(frame)
RenderedDocument {
frame,
is_new_scene,
}
}
pub fn updated_pipeline_info(&mut self) -> PipelineInfo {
@ -397,6 +398,7 @@ struct PlainRenderBackend {
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
@ -424,6 +426,7 @@ pub struct RenderBackend {
recorder: Option<Box<ApiRecordingReceiver>>,
sampler: Option<Box<AsyncPropertySampler + Send>>,
last_scene_id: u64,
enable_render_on_scroll: bool,
}
@ -460,6 +463,7 @@ impl RenderBackend {
notifier,
recorder,
sampler,
last_scene_id: 0,
enable_render_on_scroll,
}
}
@ -688,6 +692,12 @@ impl RenderBackend {
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;
@ -712,7 +722,6 @@ impl RenderBackend {
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();
@ -746,7 +755,8 @@ impl RenderBackend {
document_id,
transaction_msg,
&mut frame_counter,
&mut profile_counters
&mut profile_counters,
DocumentOps::render(),
);
}
},
@ -945,7 +955,8 @@ impl RenderBackend {
document_id,
doc_msgs,
frame_counter,
profile_counters
profile_counters,
DocumentOps::nop(),
)
}
}
@ -959,8 +970,9 @@ impl RenderBackend {
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();
@ -975,11 +987,14 @@ impl RenderBackend {
}
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,
);
@ -993,12 +1008,12 @@ impl RenderBackend {
);
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
@ -1006,7 +1021,7 @@ impl RenderBackend {
// 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());
}
@ -1053,6 +1068,7 @@ impl RenderBackend {
&mut self.resource_cache,
&mut self.gpu_cache,
&mut profile_counters.resources,
op.build,
);
debug!("generated frame for document {:?} with {} passes",
@ -1077,7 +1093,6 @@ impl RenderBackend {
);
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
@ -1247,6 +1262,7 @@ impl RenderBackend {
&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`,
@ -1269,6 +1285,7 @@ impl RenderBackend {
.map(|(id, doc)| (*id, doc.view.clone()))
.collect(),
resources,
last_scene_id: self.last_scene_id,
};
config.serialize(&backend, "backend");
@ -1328,6 +1345,7 @@ impl RenderBackend {
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);
@ -1350,7 +1368,6 @@ impl RenderBackend {
frame_builder: Some(FrameBuilder::empty()),
output_pipelines: FastHashSet::default(),
render_on_scroll: None,
render_on_hittest: false,
dynamic_properties: SceneProperties::new(),
hit_tester: None,
};
@ -1359,14 +1376,16 @@ impl RenderBackend {
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,
)
}
};

View File

@ -453,8 +453,7 @@ impl RenderTask {
ClipSource::Rectangle(..) |
ClipSource::RoundedRectangle(..) |
ClipSource::Image(..) |
ClipSource::LineDecoration(..) |
ClipSource::BorderCorner(..) => {}
ClipSource::LineDecoration(..) => {}
}
}
}

View File

@ -82,7 +82,7 @@ cfg_if! {
if #[cfg(feature = "debug_renderer")] {
use api::ColorU;
use debug_render::DebugRenderer;
use profiler::Profiler;
use profiler::{Profiler, ChangeIndicator};
use query::GpuTimer;
}
}
@ -148,14 +148,6 @@ 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,
@ -183,16 +175,12 @@ impl TransformBatchKind {
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,
}
}
}
@ -248,6 +236,8 @@ bitflags! {
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;
}
}
@ -438,6 +428,16 @@ pub(crate) mod desc {
count: 2,
kind: VertexAttributeKind::F32,
},
VertexAttribute {
name: "aClipParams1",
count: 4,
kind: VertexAttributeKind::F32,
},
VertexAttribute {
name: "aClipParams2",
count: 4,
kind: VertexAttributeKind::F32,
},
],
};
@ -621,7 +621,6 @@ pub(crate) enum VertexArrayKind {
Primitive,
Blur,
Clip,
DashAndDot,
VectorStencil,
VectorCover,
Border,
@ -1375,6 +1374,11 @@ pub struct Renderer {
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>,
@ -1777,6 +1781,10 @@ impl Renderer {
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,
@ -1866,6 +1874,11 @@ impl Renderer {
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) {
@ -1979,16 +1992,6 @@ impl Renderer {
"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",
@ -2151,6 +2154,12 @@ impl Renderer {
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");
}
@ -2394,6 +2403,23 @@ impl Renderer {
);
}
}
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) {
@ -2614,6 +2640,12 @@ impl Renderer {
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);
@ -3166,41 +3198,6 @@ impl Renderer {
{
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();
@ -4553,7 +4550,6 @@ fn get_vao<'a>(vertex_array_kind: VertexArrayKind,
VertexArrayKind::Primitive => &vaos.prim_vao,
VertexArrayKind::Clip => &vaos.clip_vao,
VertexArrayKind::Blur => &vaos.blur_vao,
VertexArrayKind::DashAndDot => &vaos.dash_and_dot_vao,
VertexArrayKind::VectorStencil => &gpu_glyph_renderer.vector_stencil_vao,
VertexArrayKind::VectorCover => &gpu_glyph_renderer.vector_cover_vao,
VertexArrayKind::Border => &vaos.border_vao,
@ -4569,7 +4565,6 @@ fn get_vao<'a>(vertex_array_kind: VertexArrayKind,
VertexArrayKind::Primitive => &vaos.prim_vao,
VertexArrayKind::Clip => &vaos.clip_vao,
VertexArrayKind::Blur => &vaos.blur_vao,
VertexArrayKind::DashAndDot => &vaos.dash_and_dot_vao,
VertexArrayKind::VectorStencil | VertexArrayKind::VectorCover => unreachable!(),
VertexArrayKind::Border => &vaos.border_vao,
}

View File

@ -57,6 +57,7 @@ pub struct SceneRequest {
pub font_instances: FontInstanceMap,
pub output_pipelines: FastHashSet<PipelineId>,
pub removed_pipelines: Vec<PipelineId>,
pub scene_id: u64,
}
pub struct BuiltScene {

View File

@ -50,7 +50,6 @@ pub const IMAGE_BUFFER_KINDS: [ImageBufferKind; 4] = [
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";
@ -261,53 +260,6 @@ impl BrushShader {
}
}
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,
@ -400,7 +352,6 @@ fn create_prim_shader(
VertexArrayKind::Primitive => desc::PRIM_INSTANCES,
VertexArrayKind::Blur => desc::BLUR,
VertexArrayKind::Clip => desc::CLIP,
VertexArrayKind::DashAndDot => desc::BORDER_CORNER_DASH_AND_DOT,
VertexArrayKind::VectorStencil => desc::VECTOR_STENCIL,
VertexArrayKind::VectorCover => desc::VECTOR_COVER,
VertexArrayKind::Border => desc::BORDER,
@ -482,7 +433,6 @@ pub struct 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
@ -494,8 +444,6 @@ pub struct Shaders {
// 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,
}
@ -611,14 +559,6 @@ impl Shaders {
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,
&[],
@ -699,20 +639,6 @@ impl Shaders {
}
}
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",
@ -746,13 +672,10 @@ impl Shaders {
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,
})
}
@ -804,7 +727,7 @@ impl Shaders {
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 => {
@ -816,14 +739,7 @@ impl Shaders {
};
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)
}
}
}
}
@ -839,7 +755,6 @@ impl Shaders {
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);
@ -853,8 +768,6 @@ impl Shaders {
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);
}

View File

@ -17,7 +17,7 @@ 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};
@ -49,7 +49,6 @@ pub struct RenderTargetContext<'a, 'rc> {
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))]

View File

@ -36,10 +36,6 @@ const SHADERS: &[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,
@ -49,15 +45,11 @@ const SHADERS: &[Shader] = &[
name: "cs_blur",
features: CACHE_FEATURES,
},
Shader {
name: "cs_border_segment",
features: CACHE_FEATURES,
},
// Prim shaders
Shader {
name: "ps_border_corner",
features: PRIM_FEATURES,
},
Shader {
name: "ps_border_edge",
features: PRIM_FEATURES,
},
Shader {
name: "ps_split_composite",
features: PRIM_FEATURES,

View File

@ -561,6 +561,10 @@ pub enum DebugCommand {
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.

View File

@ -1 +1 @@
8e697f8cb1f1aab2e5f6b9b903eb7191340b10c5
aff9f409f3d6a3518c38c1f7755657f564c1083a

View File

@ -49,6 +49,7 @@ impl<'a> RawtestHarness<'a> {
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();
}
@ -720,6 +721,51 @@ impl<'a> RawtestHarness<'a> {
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";