mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-13 21:35:39 +00:00
Bug 1466549 - Update webrender to aff9f409f3d6a3518c38c1f7755657f564c1083a. r=Gankro
MozReview-Commit-ID: 2Vauiblv7eW --HG-- extra : rebase_source : e538ab000dccbd1e532bb80f8f5d09f22d3968e4
This commit is contained in:
parent
5f8ce0f4bc
commit
3ac673052b
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
@ -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([
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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> {
|
||||
|
@ -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.
|
||||
|
@ -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");
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
@ -453,8 +453,7 @@ impl RenderTask {
|
||||
ClipSource::Rectangle(..) |
|
||||
ClipSource::RoundedRectangle(..) |
|
||||
ClipSource::Image(..) |
|
||||
ClipSource::LineDecoration(..) |
|
||||
ClipSource::BorderCorner(..) => {}
|
||||
ClipSource::LineDecoration(..) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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))]
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -1 +1 @@
|
||||
8e697f8cb1f1aab2e5f6b9b903eb7191340b10c5
|
||||
aff9f409f3d6a3518c38c1f7755657f564c1083a
|
||||
|
@ -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";
|
||||
|
Loading…
Reference in New Issue
Block a user