From 3ac673052b4488a412fed1a454a8297698ac3251 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Wed, 6 Jun 2018 07:34:22 -0400 Subject: [PATCH] Bug 1466549 - Update webrender to aff9f409f3d6a3518c38c1f7755657f564c1083a. r=Gankro MozReview-Commit-ID: 2Vauiblv7eW --HG-- extra : rebase_source : e538ab000dccbd1e532bb80f8f5d09f22d3968e4 --- gfx/webrender/doc/CLIPPING.md | 132 +-- gfx/webrender/res/brush_image.glsl | 4 +- gfx/webrender/res/cs_border_segment.glsl | 60 +- gfx/webrender/res/cs_clip_border.glsl | 204 ---- gfx/webrender/res/ps_border_corner.glsl | 426 -------- gfx/webrender/res/ps_border_edge.glsl | 334 ------ gfx/webrender/res/shared_border.glsl | 98 -- gfx/webrender/src/batch.rs | 144 +-- gfx/webrender/src/border.rs | 978 ++++++------------ gfx/webrender/src/clip.rs | 11 +- gfx/webrender/src/display_list_flattener.rs | 32 +- gfx/webrender/src/ellipse.rs | 13 +- gfx/webrender/src/frame_builder.rs | 16 +- gfx/webrender/src/gpu_cache.rs | 5 - gfx/webrender/src/gpu_types.rs | 3 +- gfx/webrender/src/hit_test.rs | 1 - gfx/webrender/src/internal_types.rs | 11 +- gfx/webrender/src/picture.rs | 15 +- gfx/webrender/src/prim_store.rs | 87 +- gfx/webrender/src/profiler.rs | 47 + gfx/webrender/src/render_backend.rs | 59 +- gfx/webrender/src/render_task.rs | 3 +- gfx/webrender/src/renderer.rs | 117 +-- gfx/webrender/src/scene_builder.rs | 1 + gfx/webrender/src/shade.rs | 91 +- gfx/webrender/src/tiling.rs | 3 +- .../tests/angle_shader_validation.rs | 16 +- gfx/webrender_api/src/api.rs | 4 + gfx/webrender_bindings/revision.txt | 2 +- gfx/wrench/src/rawtest.rs | 46 + 30 files changed, 731 insertions(+), 2232 deletions(-) delete mode 100644 gfx/webrender/res/cs_clip_border.glsl delete mode 100644 gfx/webrender/res/ps_border_corner.glsl delete mode 100644 gfx/webrender/res/ps_border_edge.glsl delete mode 100644 gfx/webrender/res/shared_border.glsl diff --git a/gfx/webrender/doc/CLIPPING.md b/gfx/webrender/doc/CLIPPING.md index 2b243f7cc3a7..831d540910ad 100644 --- a/gfx/webrender/doc/CLIPPING.md +++ b/gfx/webrender/doc/CLIPPING.md @@ -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, - pub image_mask: Option, -} -``` +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. diff --git a/gfx/webrender/res/brush_image.glsl b/gfx/webrender/res/brush_image.glsl index 34cf4fdb60b9..6fb7685b9963 100644 --- a/gfx/webrender/res/brush_image.glsl +++ b/gfx/webrender/res/brush_image.glsl @@ -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; diff --git a/gfx/webrender/res/cs_border_segment.glsl b/gfx/webrender/res/cs_border_segment.glsl index c5cd8eb563f9..6b4e31bd65bd 100644 --- a/gfx/webrender/res/cs_border_segment.glsl +++ b/gfx/webrender/res/cs_border_segment.glsl @@ -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); diff --git a/gfx/webrender/res/cs_clip_border.glsl b/gfx/webrender/res/cs_clip_border.glsl deleted file mode 100644 index 294ed6e832ad..000000000000 --- a/gfx/webrender/res/cs_clip_border.glsl +++ /dev/null @@ -1,204 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include shared,clip_shared - -varying vec3 vPos; - -flat varying vec2 vClipCenter; - -flat varying vec4 vPoint_Tangent0; -flat varying vec4 vPoint_Tangent1; -flat varying vec3 vDotParams; -flat varying vec2 vAlphaMask; -flat varying vec4 vTaskRect; - -#ifdef WR_VERTEX_SHADER - -in vec4 aDashOrDot0; -in vec4 aDashOrDot1; - -// Matches BorderCorner enum in border.rs -#define CORNER_TOP_LEFT 0 -#define CORNER_TOP_RIGHT 1 -#define CORNER_BOTTOM_LEFT 2 -#define CORNER_BOTTOM_RIGHT 3 - -// Matches BorderCornerClipKind enum in border.rs -#define CLIP_MODE_DASH 0 -#define CLIP_MODE_DOT 1 - -// Header for a border corner clip. -struct BorderCorner { - RectWithSize rect; - vec2 clip_center; - int corner; - int clip_mode; -}; - -BorderCorner fetch_border_corner(ivec2 address) { - vec4 data[2] = fetch_from_resource_cache_2_direct(address); - return BorderCorner(RectWithSize(data[0].xy, data[0].zw), - data[1].xy, - int(data[1].z), - int(data[1].w)); -} - -// Per-dash clip information. -struct BorderClipDash { - vec4 point_tangent_0; - vec4 point_tangent_1; -}; - -BorderClipDash fetch_border_clip_dash(ivec2 address) { - return BorderClipDash(aDashOrDot0, aDashOrDot1); -} - -// Per-dot clip information. -struct BorderClipDot { - vec3 center_radius; -}; - -BorderClipDot fetch_border_clip_dot(ivec2 address) { - return BorderClipDot(aDashOrDot0.xyz); -} - -void main(void) { - ClipMaskInstance cmi = fetch_clip_item(); - ClipArea area = fetch_clip_area(cmi.render_task_address); - ClipScrollNode scroll_node = fetch_clip_scroll_node(cmi.scroll_node_id); - - // Fetch the header information for this corner clip. - BorderCorner corner = fetch_border_corner(cmi.clip_data_address); - vClipCenter = corner.clip_center; - - // Get local vertex position for the corner rect. - // TODO(gw): We could reduce the number of pixels written here by calculating a tight - // fitting bounding box of the dash itself like we do for dots below. - vec2 pos = corner.rect.p0 + aPosition.xy * corner.rect.size; - - if (cmi.segment == 0) { - // The first segment is used to zero out the border corner. - vAlphaMask = vec2(0.0); - vDotParams = vec3(0.0); - vPoint_Tangent0 = vec4(1.0); - vPoint_Tangent1 = vec4(1.0); - } else { - vec2 sign_modifier; - switch (corner.corner) { - case CORNER_TOP_LEFT: - sign_modifier = vec2(-1.0); - break; - case CORNER_TOP_RIGHT: - sign_modifier = vec2(1.0, -1.0); - break; - case CORNER_BOTTOM_RIGHT: - sign_modifier = vec2(1.0); - break; - case CORNER_BOTTOM_LEFT: - sign_modifier = vec2(-1.0, 1.0); - break; - default: - sign_modifier = vec2(0.0); - }; - - switch (corner.clip_mode) { - case CLIP_MODE_DASH: { - // Fetch the information about this particular dash. - BorderClipDash dash = fetch_border_clip_dash(cmi.clip_data_address); - vPoint_Tangent0 = dash.point_tangent_0 * sign_modifier.xyxy; - vPoint_Tangent1 = dash.point_tangent_1 * sign_modifier.xyxy; - vDotParams = vec3(0.0); - vAlphaMask = vec2(0.0, 1.0); - break; - } - case CLIP_MODE_DOT: { - BorderClipDot cdot = fetch_border_clip_dot(cmi.clip_data_address); - vPoint_Tangent0 = vec4(1.0); - vPoint_Tangent1 = vec4(1.0); - vDotParams = vec3(cdot.center_radius.xy * sign_modifier, cdot.center_radius.z); - vAlphaMask = vec2(1.0, 1.0); - - // Generate a tighter bounding rect for dots based on their position. Dot - // centers are given relative to clip center, so we need to move the dot - // rectangle into the clip space with an origin at the top left. First, - // we expand the radius slightly to ensure we get full coverage on all the pixels - // of the dots. - float expanded_radius = cdot.center_radius.z + 2.0; - pos = (vClipCenter + vDotParams.xy - vec2(expanded_radius)); - pos += (aPosition.xy * vec2(expanded_radius * 2.0)); - pos = clamp(pos, corner.rect.p0, corner.rect.p0 + corner.rect.size); - - break; - } - default: - vPoint_Tangent0 = vPoint_Tangent1 = vec4(1.0); - vDotParams = vec3(0.0); - vAlphaMask = vec2(0.0); - } - } - - // Transform to world pos - vec4 world_pos = scroll_node.transform * vec4(pos, 0.0, 1.0); - world_pos.xyz /= world_pos.w; - - // Scale into device pixels. - vec2 device_pos = world_pos.xy * uDevicePixelRatio; - - // Position vertex within the render task area. - vec2 task_rect_origin = area.common_data.task_rect.p0; - vec2 final_pos = device_pos - area.screen_origin + task_rect_origin; - - // We pass the task rectangle to the fragment shader so that we can do one last clip - // in order to ensure that we don't draw outside the task rectangle. - vTaskRect.xy = task_rect_origin; - vTaskRect.zw = task_rect_origin + area.common_data.task_rect.size; - - // Calculate the local space position for this vertex. - vec4 node_pos = get_node_pos(world_pos.xy, scroll_node); - vPos = node_pos.xyw; - - gl_Position = uTransform * vec4(final_pos, 0.0, 1.0); -} -#endif - -#ifdef WR_FRAGMENT_SHADER -void main(void) { - vec2 local_pos = vPos.xy / vPos.z; - - // Get local space position relative to the clip center. - vec2 clip_relative_pos = local_pos - vClipCenter; - - // Get the signed distances to the two clip lines. - float d0 = distance_to_line(vPoint_Tangent0.xy, - vPoint_Tangent0.zw, - clip_relative_pos); - float d1 = distance_to_line(vPoint_Tangent1.xy, - vPoint_Tangent1.zw, - clip_relative_pos); - - // Get AA widths based on zoom / scale etc. - float aa_range = compute_aa_range(local_pos); - - // SDF subtract edges for dash clip - float dash_distance = max(d0, -d1); - - // Get distance from dot. - float dot_distance = distance(clip_relative_pos, vDotParams.xy) - vDotParams.z; - - // Select between dot/dash clip based on mode. - float d = mix(dash_distance, dot_distance, vAlphaMask.x); - - // Apply AA. - d = distance_aa(aa_range, d); - - // Completely mask out clip if zero'ing out the rect. - d = d * vAlphaMask.y; - - // Make sure that we don't draw outside the task rectangle. - d = d * point_inside_rect(gl_FragCoord.xy, vTaskRect.xy, vTaskRect.zw); - - oFragColor = vec4(d, 0.0, 0.0, 1.0); -} -#endif diff --git a/gfx/webrender/res/ps_border_corner.glsl b/gfx/webrender/res/ps_border_corner.glsl deleted file mode 100644 index 28d484d45b98..000000000000 --- a/gfx/webrender/res/ps_border_corner.glsl +++ /dev/null @@ -1,426 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include shared,prim_shared,shared_border,ellipse - -// Edge color transition -flat varying vec4 vColor00; -flat varying vec4 vColor01; -flat varying vec4 vColor10; -flat varying vec4 vColor11; -flat varying vec4 vColorEdgeLine; - -// Border radius -flat varying vec2 vClipCenter; -flat varying vec4 vRadii0; -flat varying vec4 vRadii1; -flat varying vec2 vClipSign; -flat varying vec4 vEdgeDistance; -flat varying float vSDFSelect; - -flat varying float vIsBorderRadiusLessThanBorderWidth; - -// Border style -flat varying float vAlphaSelect; - -varying vec2 vLocalPos; - -#ifdef WR_VERTEX_SHADER -// Matches BorderCornerSide enum in border.rs -#define SIDE_BOTH 0 -#define SIDE_FIRST 1 -#define SIDE_SECOND 2 - -vec2 get_radii(vec2 radius, vec2 invalid) { - if (all(greaterThan(radius, vec2(0.0)))) { - return radius; - } - - return invalid; -} - -void set_radii(int style, - vec2 radii, - vec2 widths, - vec2 adjusted_widths) { - vRadii0.xy = get_radii(radii, 2.0 * widths); - vRadii0.zw = get_radii(radii - widths, -widths); - - switch (style) { - case BORDER_STYLE_RIDGE: - case BORDER_STYLE_GROOVE: - vRadii1.xy = radii - adjusted_widths; - // See comment in default branch - vRadii1.zw = vec2(-100.0); - break; - case BORDER_STYLE_DOUBLE: - vRadii1.xy = get_radii(radii - adjusted_widths, -widths); - vRadii1.zw = get_radii(radii - widths + adjusted_widths, -widths); - break; - default: - // These aren't needed, so we set them to some reasonably large - // negative value so later computations will discard them. This - // avoids branches and numerical issues in the fragment shader. - vRadii1.xy = vec2(-100.0); - vRadii1.zw = vec2(-100.0); - break; - } -} - -void set_edge_line(vec2 border_width, - vec2 outer_corner, - vec2 gradient_sign) { - vec2 gradient = border_width * gradient_sign; - vColorEdgeLine = vec4(outer_corner, vec2(-gradient.y, gradient.x)); -} - -void write_color(vec4 color0, vec4 color1, int style, vec2 delta, int instance_kind) { - vec4 modulate; - - switch (style) { - case BORDER_STYLE_GROOVE: - modulate = vec4(1.0 - 0.3 * delta.x, - 1.0 + 0.3 * delta.x, - 1.0 - 0.3 * delta.y, - 1.0 + 0.3 * delta.y); - - break; - case BORDER_STYLE_RIDGE: - modulate = vec4(1.0 + 0.3 * delta.x, - 1.0 - 0.3 * delta.x, - 1.0 + 0.3 * delta.y, - 1.0 - 0.3 * delta.y); - break; - default: - modulate = vec4(1.0); - break; - } - - // Optionally mask out one side of the border corner, - // depending on the instance kind. - switch (instance_kind) { - case SIDE_FIRST: - color0.a = 0.0; - break; - case SIDE_SECOND: - color1.a = 0.0; - break; - default: break; - } - - vColor00 = vec4(clamp(color0.rgb * modulate.x, vec3(0.0), vec3(color0.a)), color0.a); - vColor01 = vec4(clamp(color0.rgb * modulate.y, vec3(0.0), vec3(color0.a)), color0.a); - vColor10 = vec4(clamp(color1.rgb * modulate.z, vec3(0.0), vec3(color1.a)), color1.a); - vColor11 = vec4(clamp(color1.rgb * modulate.w, vec3(0.0), vec3(color1.a)), color1.a); -} - -int select_style(int color_select, vec2 fstyle) { - ivec2 style = ivec2(fstyle); - - switch (color_select) { - case SIDE_BOTH: - { - // TODO(gw): A temporary hack! While we don't support - // border corners that have dots or dashes - // with another style, pretend they are solid - // border corners. - bool has_dots = style.x == BORDER_STYLE_DOTTED || - style.y == BORDER_STYLE_DOTTED; - bool has_dashes = style.x == BORDER_STYLE_DASHED || - style.y == BORDER_STYLE_DASHED; - if (style.x != style.y && (has_dots || has_dashes)) - return BORDER_STYLE_SOLID; - return style.x; - } - case SIDE_FIRST: - return style.x; - case SIDE_SECOND: - return style.y; - default: - return 0; - } -} - -void main(void) { - Primitive prim = load_primitive(); - Border border = fetch_border(prim.specific_prim_address); - int sub_part = prim.user_data0; - BorderCorners corners = get_border_corners(border, prim.local_rect); - - vec2 p0, p1; - - // TODO(gw): We'll need to pass through multiple styles - // once we support style transitions per corner. - int style; - vec4 edge_distances; - vec4 color0, color1; - vec2 color_delta; - vec4 edge_mask; - - // TODO(gw): Now that all border styles are supported, the - // statement below can be tidied up quite a bit. - - switch (sub_part) { - case 0: { - p0 = corners.tl_outer; - p1 = corners.tl_inner; - color0 = border.colors[0]; - color1 = border.colors[1]; - vClipCenter = corners.tl_outer + border.radii[0].xy; - vClipSign = vec2(1.0); - style = select_style(prim.user_data1, border.style.yx); - vec4 adjusted_widths = get_effective_border_widths(border, style); - vec4 inv_adjusted_widths = border.widths - adjusted_widths; - set_radii(style, - border.radii[0].xy, - border.widths.xy, - adjusted_widths.xy); - set_edge_line(border.widths.xy, - corners.tl_outer, - vec2(1.0, 1.0)); - edge_distances = vec4(p0 + adjusted_widths.xy, - p0 + inv_adjusted_widths.xy); - color_delta = vec2(1.0); - vIsBorderRadiusLessThanBorderWidth = any(lessThan(border.radii[0].xy, - border.widths.xy)) ? 1.0 : 0.0; - edge_mask = vec4(1.0, 1.0, 0.0, 0.0); - break; - } - case 1: { - p0 = vec2(corners.tr_inner.x, corners.tr_outer.y); - p1 = vec2(corners.tr_outer.x, corners.tr_inner.y); - color0 = border.colors[1]; - color1 = border.colors[2]; - vClipCenter = corners.tr_outer + vec2(-border.radii[0].z, border.radii[0].w); - vClipSign = vec2(-1.0, 1.0); - style = select_style(prim.user_data1, border.style.zy); - vec4 adjusted_widths = get_effective_border_widths(border, style); - vec4 inv_adjusted_widths = border.widths - adjusted_widths; - set_radii(style, - border.radii[0].zw, - border.widths.zy, - adjusted_widths.zy); - set_edge_line(border.widths.zy, - corners.tr_outer, - vec2(-1.0, 1.0)); - edge_distances = vec4(p1.x - adjusted_widths.z, - p0.y + adjusted_widths.y, - p1.x - border.widths.z + adjusted_widths.z, - p0.y + inv_adjusted_widths.y); - color_delta = vec2(1.0, -1.0); - vIsBorderRadiusLessThanBorderWidth = any(lessThan(border.radii[0].zw, - border.widths.zy)) ? 1.0 : 0.0; - edge_mask = vec4(0.0, 1.0, 1.0, 0.0); - break; - } - case 2: { - p0 = corners.br_inner; - p1 = corners.br_outer; - color0 = border.colors[2]; - color1 = border.colors[3]; - vClipCenter = corners.br_outer - border.radii[1].xy; - vClipSign = vec2(-1.0, -1.0); - style = select_style(prim.user_data1, border.style.wz); - vec4 adjusted_widths = get_effective_border_widths(border, style); - vec4 inv_adjusted_widths = border.widths - adjusted_widths; - set_radii(style, - border.radii[1].xy, - border.widths.zw, - adjusted_widths.zw); - set_edge_line(border.widths.zw, - corners.br_outer, - vec2(-1.0, -1.0)); - edge_distances = vec4(p1.x - adjusted_widths.z, - p1.y - adjusted_widths.w, - p1.x - border.widths.z + adjusted_widths.z, - p1.y - border.widths.w + adjusted_widths.w); - color_delta = vec2(-1.0); - vIsBorderRadiusLessThanBorderWidth = any(lessThan(border.radii[1].xy, - border.widths.zw)) ? 1.0 : 0.0; - edge_mask = vec4(0.0, 0.0, 1.0, 1.0); - break; - } - case 3: { - p0 = vec2(corners.bl_outer.x, corners.bl_inner.y); - p1 = vec2(corners.bl_inner.x, corners.bl_outer.y); - color0 = border.colors[3]; - color1 = border.colors[0]; - vClipCenter = corners.bl_outer + vec2(border.radii[1].z, -border.radii[1].w); - vClipSign = vec2(1.0, -1.0); - style = select_style(prim.user_data1, border.style.xw); - vec4 adjusted_widths = get_effective_border_widths(border, style); - vec4 inv_adjusted_widths = border.widths - adjusted_widths; - set_radii(style, - border.radii[1].zw, - border.widths.xw, - adjusted_widths.xw); - set_edge_line(border.widths.xw, - corners.bl_outer, - vec2(1.0, -1.0)); - edge_distances = vec4(p0.x + adjusted_widths.x, - p1.y - adjusted_widths.w, - p0.x + inv_adjusted_widths.x, - p1.y - border.widths.w + adjusted_widths.w); - color_delta = vec2(-1.0, 1.0); - vIsBorderRadiusLessThanBorderWidth = any(lessThan(border.radii[1].zw, - border.widths.xw)) ? 1.0 : 0.0; - edge_mask = vec4(1.0, 0.0, 0.0, 1.0); - break; - } - default: - p0 = p1 = vec2(0.0); - color0 = color1 = vec4(1.0); - vClipCenter = vClipSign = vec2(0.0); - style = 0; - edge_distances = edge_mask = vec4(0.0); - color_delta = vec2(0.0); - vIsBorderRadiusLessThanBorderWidth = 0.0; - } - - switch (style) { - case BORDER_STYLE_DOUBLE: { - vEdgeDistance = edge_distances; - vAlphaSelect = 0.0; - vSDFSelect = 0.0; - break; - } - case BORDER_STYLE_GROOVE: - case BORDER_STYLE_RIDGE: - vEdgeDistance = vec4(edge_distances.xy, 0.0, 0.0); - vAlphaSelect = 1.0; - vSDFSelect = 1.0; - break; - case BORDER_STYLE_DOTTED: - // Disable normal clip radii for dotted corners, since - // all the clipping is handled by the clip mask. - vClipSign = vec2(0.0); - vEdgeDistance = vec4(0.0); - vAlphaSelect = 1.0; - vSDFSelect = 0.0; - break; - default: { - vEdgeDistance = vec4(0.0); - vAlphaSelect = 1.0; - vSDFSelect = 0.0; - break; - } - } - - write_color(color0, color1, style, color_delta, prim.user_data1); - - RectWithSize segment_rect; - segment_rect.p0 = p0; - segment_rect.size = p1 - p0; - -#ifdef WR_FEATURE_TRANSFORM - VertexInfo vi = write_transform_vertex(segment_rect, - prim.local_rect, - prim.local_clip_rect, - edge_mask, - prim.z, - prim.scroll_node, - prim.task, - true); -#else - VertexInfo vi = write_vertex(segment_rect, - prim.local_clip_rect, - prim.z, - prim.scroll_node, - prim.task, - prim.local_rect); -#endif - - vLocalPos = vi.local_pos; - write_clip(vi.screen_pos, prim.clip_area); -} -#endif - -#ifdef WR_FRAGMENT_SHADER -void main(void) { - float alpha = 1.0; -#ifdef WR_FEATURE_TRANSFORM - alpha = init_transform_fs(vLocalPos); -#endif - - alpha *= do_clip(); - - float aa_range = compute_aa_range(vLocalPos); - - float distance_for_color; - float color_mix_factor; - - // Only apply the clip AA if inside the clip region. This is - // necessary for correctness when the border width is greater - // than the border radius. - if (vIsBorderRadiusLessThanBorderWidth == 0.0 || - all(lessThan(vLocalPos * vClipSign, vClipCenter * vClipSign))) { - vec2 p = vLocalPos - vClipCenter; - - // The coordinate system is snapped to pixel boundaries. To sample the distance, - // however, we are interested in the center of the pixels which introduces an - // error of half a pixel towards the exterior of the curve (See issue #1750). - // This error is corrected by offsetting the distance by half a device pixel. - // This not entirely correct: it leaves an error that varries between - // 0 and (sqrt(2) - 1)/2 = 0.2 pixels but it is hardly noticeable and is better - // than the constant sqrt(2)/2 px error without the correction. - // To correct this exactly we would need to offset p by half a pixel in the - // direction of the center of the ellipse (a different offset for each corner). - - // Get signed distance from the inner/outer clips. - float d0 = distance_to_ellipse(p, vRadii0.xy, aa_range); - float d1 = distance_to_ellipse(p, vRadii0.zw, aa_range); - float d2 = distance_to_ellipse(p, vRadii1.xy, aa_range); - float d3 = distance_to_ellipse(p, vRadii1.zw, aa_range); - - // SDF subtract main radii - float d_main = max(d0, -d1); - - // SDF subtract inner radii (double style borders) - float d_inner = max(d2, -d3); - - // Select how to combine the SDF based on border style. - float d = mix(max(d_main, -d_inner), d_main, vSDFSelect); - - // Only apply AA to fragments outside the signed distance field. - alpha = min(alpha, distance_aa(aa_range, d)); - - // Get the groove/ridge mix factor. - color_mix_factor = distance_aa(aa_range, d2); - } else { - // Handle the case where the fragment is outside the clip - // region in a corner. This occurs when border width is - // greater than border radius. - - // Get linear distances along horizontal and vertical edges. - vec2 d0 = vClipSign.xx * (vLocalPos.xx - vEdgeDistance.xz); - vec2 d1 = vClipSign.yy * (vLocalPos.yy - vEdgeDistance.yw); - // Apply union to get the outer edge signed distance. - float da = min(d0.x, d1.x); - // Apply intersection to get the inner edge signed distance. - float db = max(-d0.y, -d1.y); - // Apply union to get both edges. - float d = min(da, db); - // Select fragment on/off based on signed distance. - // No AA here, since we know we're on a straight edge - // and the width is rounded to a whole CSS pixel. - alpha = min(alpha, mix(vAlphaSelect, 1.0, d < 0.0)); - - // Get the groove/ridge mix factor. - // TODO(gw): Support AA for groove/ridge border edge with transforms. - color_mix_factor = mix(0.0, 1.0, da > 0.0); - } - - // Mix inner/outer color. - vec4 color0 = mix(vColor00, vColor01, color_mix_factor); - vec4 color1 = mix(vColor10, vColor11, color_mix_factor); - - // Select color based on side of line. Get distance from the - // reference line, and then apply AA along the edge. - float ld = distance_to_line(vColorEdgeLine.xy, vColorEdgeLine.zw, vLocalPos); - float m = distance_aa(aa_range, -ld); - vec4 color = mix(color0, color1, m); - - oFragColor = color * alpha; -} -#endif diff --git a/gfx/webrender/res/ps_border_edge.glsl b/gfx/webrender/res/ps_border_edge.glsl deleted file mode 100644 index 5154e9bd3e83..000000000000 --- a/gfx/webrender/res/ps_border_edge.glsl +++ /dev/null @@ -1,334 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include shared,prim_shared,shared_border - -flat varying vec4 vColor0; -flat varying vec4 vColor1; -flat varying vec2 vEdgeDistance; -flat varying float vAxisSelect; -flat varying float vAlphaSelect; -flat varying vec4 vClipParams; -flat varying float vClipSelect; - -varying vec2 vLocalPos; - -#ifdef WR_VERTEX_SHADER -void write_edge_distance(float p0, - float original_width, - float adjusted_width, - float style, - float axis_select, - float sign_adjust) { - switch (int(style)) { - case BORDER_STYLE_DOUBLE: - vEdgeDistance = vec2(p0 + adjusted_width, - p0 + original_width - adjusted_width); - break; - case BORDER_STYLE_GROOVE: - case BORDER_STYLE_RIDGE: - vEdgeDistance = vec2(p0 + adjusted_width, sign_adjust); - break; - default: - vEdgeDistance = vec2(0.0); - break; - } - - vAxisSelect = axis_select; -} - -void write_alpha_select(float style) { - switch (int(style)) { - case BORDER_STYLE_DOUBLE: - vAlphaSelect = 0.0; - break; - default: - vAlphaSelect = 1.0; - break; - } -} - -// write_color function is duplicated to work around a Mali-T880 GPU driver program link error. -// See https://github.com/servo/webrender/issues/1403 for more info. -// TODO: convert back to a single function once the driver issues are resolved, if ever. -void write_color0(vec4 color, float style, bool flip) { - vec2 modulate; - - switch (int(style)) { - case BORDER_STYLE_GROOVE: - { - modulate = flip ? vec2(1.3, 0.7) : vec2(0.7, 1.3); - break; - } - case BORDER_STYLE_RIDGE: - { - modulate = flip ? vec2(0.7, 1.3) : vec2(1.3, 0.7); - break; - } - default: - modulate = vec2(1.0); - break; - } - - vColor0 = vec4(min(color.rgb * modulate.x, vec3(color.a)), color.a); -} - -void write_color1(vec4 color, float style, bool flip) { - vec2 modulate; - - switch (int(style)) { - case BORDER_STYLE_GROOVE: - { - modulate = flip ? vec2(1.3, 0.7) : vec2(0.7, 1.3); - break; - } - case BORDER_STYLE_RIDGE: - { - modulate = flip ? vec2(0.7, 1.3) : vec2(1.3, 0.7); - break; - } - default: - modulate = vec2(1.0); - break; - } - - vColor1 = vec4(min(color.rgb * modulate.y, vec3(color.a)), color.a); -} - -void write_clip_params(float style, - float border_width, - float edge_length, - float edge_offset, - float center_line, - bool start_corner_has_radius, - bool end_corner_has_radius) { - // x = offset - // y = dash on + off length - // z = dash length - // w = center line of edge cross-axis (for dots only) - switch (int(style)) { - case BORDER_STYLE_DASHED: { - float desired_dash_length = border_width * 3.0; - // Consider half total length since there is an equal on/off for each dash. - float dash_count = ceil(0.5 * edge_length / desired_dash_length); - float dash_length = 0.5 * edge_length / dash_count; - vClipParams = vec4(edge_offset - 0.5 * dash_length, - 2.0 * dash_length, - dash_length, - 0.0); - vClipSelect = 0.0; - break; - } - case BORDER_STYLE_DOTTED: { - float diameter = border_width; - float radius = 0.5 * diameter; - - // If this edge connects a corner with a radius to a corner without a radius, we - // act as if we have space for one more dot. This will position the dots so that - // there is a half dot on one of the ends. - float full_edge_length = edge_length + - (float(start_corner_has_radius ^^ end_corner_has_radius) * diameter); - - float dot_count = ceil(0.5 * full_edge_length / diameter); - float empty_space = full_edge_length - (dot_count * diameter); - float distance_between_centers = diameter + empty_space / dot_count; - - // If the starting corner has a radius, we want to position the half dot right - // against that edge. - float starting_offset = - edge_offset + radius + (-diameter * float(start_corner_has_radius)); - - vClipParams = vec4(starting_offset, - distance_between_centers, - radius, - center_line); - - vClipSelect = 1.0; - break; - } - default: - vClipParams = vec4(1.0); - vClipSelect = 0.0; - break; - } -} - -bool hasRadius(vec2 radius) { - return any(notEqual(radius, vec2(0.0))); -} - -void main(void) { - Primitive prim = load_primitive(); - Border border = fetch_border(prim.specific_prim_address); - int sub_part = prim.user_data0; - BorderCorners corners = get_border_corners(border, prim.local_rect); - vec4 color = border.colors[sub_part]; - - // TODO(gw): Now that all border styles are supported, the - // statement below can be tidied up quite a bit. - - float style; - bool color_flip; - - RectWithSize segment_rect; - vec4 edge_mask; - - switch (sub_part) { - case 0: { - segment_rect.p0 = vec2(corners.tl_outer.x, corners.tl_inner.y); - segment_rect.size = vec2(border.widths.x, corners.bl_inner.y - corners.tl_inner.y); - vec4 adjusted_widths = get_effective_border_widths(border, int(border.style.x)); - write_edge_distance(segment_rect.p0.x, border.widths.x, adjusted_widths.x, border.style.x, 0.0, 1.0); - style = border.style.x; - color_flip = false; - write_clip_params(border.style.x, - border.widths.x, - segment_rect.size.y, - segment_rect.p0.y, - segment_rect.p0.x + 0.5 * segment_rect.size.x, - hasRadius(border.radii[0].xy), - hasRadius(border.radii[1].zw)); - edge_mask = vec4(1.0, 0.0, 1.0, 0.0); - break; - } - case 1: { - segment_rect.p0 = vec2(corners.tl_inner.x, corners.tl_outer.y); - segment_rect.size = vec2(corners.tr_inner.x - corners.tl_inner.x, border.widths.y); - vec4 adjusted_widths = get_effective_border_widths(border, int(border.style.y)); - write_edge_distance(segment_rect.p0.y, border.widths.y, adjusted_widths.y, border.style.y, 1.0, 1.0); - style = border.style.y; - color_flip = false; - write_clip_params(border.style.y, - border.widths.y, - segment_rect.size.x, - segment_rect.p0.x, - segment_rect.p0.y + 0.5 * segment_rect.size.y, - hasRadius(border.radii[0].xy), - hasRadius(border.radii[0].zw)); - edge_mask = vec4(0.0, 1.0, 0.0, 1.0); - break; - } - case 2: { - segment_rect.p0 = vec2(corners.tr_outer.x - border.widths.z, corners.tr_inner.y); - segment_rect.size = vec2(border.widths.z, corners.br_inner.y - corners.tr_inner.y); - vec4 adjusted_widths = get_effective_border_widths(border, int(border.style.z)); - write_edge_distance(segment_rect.p0.x, border.widths.z, adjusted_widths.z, border.style.z, 0.0, -1.0); - style = border.style.z; - color_flip = true; - write_clip_params(border.style.z, - border.widths.z, - segment_rect.size.y, - segment_rect.p0.y, - segment_rect.p0.x + 0.5 * segment_rect.size.x, - hasRadius(border.radii[0].zw), - hasRadius(border.radii[1].xy)); - edge_mask = vec4(1.0, 0.0, 1.0, 0.0); - break; - } - case 3: { - segment_rect.p0 = vec2(corners.bl_inner.x, corners.bl_outer.y - border.widths.w); - segment_rect.size = vec2(corners.br_inner.x - corners.bl_inner.x, border.widths.w); - vec4 adjusted_widths = get_effective_border_widths(border, int(border.style.w)); - write_edge_distance(segment_rect.p0.y, border.widths.w, adjusted_widths.w, border.style.w, 1.0, -1.0); - style = border.style.w; - color_flip = true; - write_clip_params(border.style.w, - border.widths.w, - segment_rect.size.x, - segment_rect.p0.x, - segment_rect.p0.y + 0.5 * segment_rect.size.y, - hasRadius(border.radii[1].zw), - hasRadius(border.radii[1].xy)); - edge_mask = vec4(0.0, 1.0, 0.0, 1.0); - break; - } - default: - segment_rect.p0 = segment_rect.size = vec2(0.0); - style = 0.0; - color_flip = false; - edge_mask = vec4(0.0); - } - - write_alpha_select(style); - write_color0(color, style, color_flip); - write_color1(color, style, color_flip); - -#ifdef WR_FEATURE_TRANSFORM - VertexInfo vi = write_transform_vertex(segment_rect, - prim.local_rect, - prim.local_clip_rect, - edge_mask, - prim.z, - prim.scroll_node, - prim.task, - true); -#else - VertexInfo vi = write_vertex(segment_rect, - prim.local_clip_rect, - prim.z, - prim.scroll_node, - prim.task, - prim.local_rect); -#endif - - vLocalPos = vi.local_pos; - write_clip(vi.screen_pos, prim.clip_area); -} -#endif - -#ifdef WR_FRAGMENT_SHADER -void main(void) { - float alpha = 1.0; -#ifdef WR_FEATURE_TRANSFORM - alpha = init_transform_fs(vLocalPos); -#endif - - alpha *= do_clip(); - - // Find the appropriate distance to apply the step over. - float aa_range = compute_aa_range(vLocalPos); - - // Applies the math necessary to draw a style: double - // border. In the case of a solid border, the vertex - // shader sets interpolator values that make this have - // no effect. - - // Select the x/y coord, depending on which axis this edge is. - vec2 pos = mix(vLocalPos.xy, vLocalPos.yx, vAxisSelect); - - // Get signed distance from each of the inner edges. - float d0 = pos.x - vEdgeDistance.x; - float d1 = vEdgeDistance.y - pos.x; - - // SDF union to select both outer edges. - float d = min(d0, d1); - - // Select fragment on/off based on signed distance. - // No AA here, since we know we're on a straight edge - // and the width is rounded to a whole CSS pixel. - alpha = min(alpha, mix(vAlphaSelect, 1.0, d < 0.0)); - - // Mix color based on first distance. - // TODO(gw): Support AA for groove/ridge border edge with transforms. - vec4 color = mix(vColor0, vColor1, bvec4(d0 * vEdgeDistance.y > 0.0)); - - // Apply dashing / dotting parameters. - - // Get the main-axis position relative to closest dot or dash. - float x = mod(pos.y - vClipParams.x, vClipParams.y); - - // Calculate dash alpha (on/off) based on dash length - float dash_alpha = step(x, vClipParams.z); - - // Get the dot alpha - vec2 dot_relative_pos = vec2(x, pos.x) - vClipParams.zw; - float dot_distance = length(dot_relative_pos) - vClipParams.z; - float dot_alpha = distance_aa(aa_range, dot_distance); - - // Select between dot/dash alpha based on clip mode. - alpha = min(alpha, mix(dash_alpha, dot_alpha, vClipSelect)); - - oFragColor = color * alpha; -} -#endif diff --git a/gfx/webrender/res/shared_border.glsl b/gfx/webrender/res/shared_border.glsl deleted file mode 100644 index a774673b2b05..000000000000 --- a/gfx/webrender/res/shared_border.glsl +++ /dev/null @@ -1,98 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -// Border styles as defined in webrender_api/types.rs -#define BORDER_STYLE_NONE 0 -#define BORDER_STYLE_SOLID 1 -#define BORDER_STYLE_DOUBLE 2 -#define BORDER_STYLE_DOTTED 3 -#define BORDER_STYLE_DASHED 4 -#define BORDER_STYLE_HIDDEN 5 -#define BORDER_STYLE_GROOVE 6 -#define BORDER_STYLE_RIDGE 7 -#define BORDER_STYLE_INSET 8 -#define BORDER_STYLE_OUTSET 9 - -#ifdef WR_VERTEX_SHADER - -struct Border { - vec4 style; - vec4 widths; - vec4 colors[4]; - vec4 radii[2]; -}; - -struct BorderCorners { - vec2 tl_outer; - vec2 tl_inner; - vec2 tr_outer; - vec2 tr_inner; - vec2 br_outer; - vec2 br_inner; - vec2 bl_outer; - vec2 bl_inner; -}; - -vec4 get_effective_border_widths(Border border, int style) { - switch (style) { - case BORDER_STYLE_DOUBLE: - // Calculate the width of a border segment in a style: double - // border. Round to the nearest CSS pixel. - - // The CSS spec doesn't define what width each of the segments - // in a style: double border should be. It only says that the - // sum of the segments should be equal to the total border - // width. We pick to make the segments (almost) equal thirds - // for now - we can adjust this if we find other browsers pick - // different values in some cases. - // SEE: https://drafts.csswg.org/css-backgrounds-3/#double - return max(floor(0.5 + border.widths / 3.0), 1.0); - case BORDER_STYLE_GROOVE: - case BORDER_STYLE_RIDGE: - return floor(0.5 + border.widths * 0.5); - default: - return border.widths; - } -} - -Border fetch_border(int address) { - vec4 data[8] = fetch_from_resource_cache_8(address); - return Border(data[0], data[1], - vec4[4](data[2], data[3], data[4], data[5]), - vec4[2](data[6], data[7])); -} - -BorderCorners get_border_corners(Border border, RectWithSize local_rect) { - vec2 tl_outer = local_rect.p0; - vec2 tl_inner = tl_outer + vec2(max(border.radii[0].x, border.widths.x), - max(border.radii[0].y, border.widths.y)); - - vec2 tr_outer = vec2(local_rect.p0.x + local_rect.size.x, - local_rect.p0.y); - vec2 tr_inner = tr_outer + vec2(-max(border.radii[0].z, border.widths.z), - max(border.radii[0].w, border.widths.y)); - - vec2 br_outer = vec2(local_rect.p0.x + local_rect.size.x, - local_rect.p0.y + local_rect.size.y); - vec2 br_inner = br_outer - vec2(max(border.radii[1].x, border.widths.z), - max(border.radii[1].y, border.widths.w)); - - vec2 bl_outer = vec2(local_rect.p0.x, - local_rect.p0.y + local_rect.size.y); - vec2 bl_inner = bl_outer + vec2(max(border.radii[1].z, border.widths.x), - -max(border.radii[1].w, border.widths.w)); - - return BorderCorners( - tl_outer, - tl_inner, - tr_outer, - tr_inner, - br_outer, - br_inner, - bl_outer, - bl_inner - ); -} - -#endif diff --git a/gfx/webrender/src/batch.rs b/gfx/webrender/src/batch.rs index 941bcebeae09..5072a52d4f94 100644 --- a/gfx/webrender/src/batch.rs +++ b/gfx/webrender/src/batch.rs @@ -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, - 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, /// Image draws apply the image masking. pub images: FastHashMap>, - pub border_clears: Vec, - pub borders: Vec, pub box_shadows: FastHashMap>, pub line_decorations: Vec, } @@ -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, - }) - } - } } } } diff --git a/gfx/webrender/src/border.rs b/gfx/webrender/src/border.rs index e4ff8febf78c..6337072ca173 100644 --- a/gfx/webrender/src/border.rs +++ b/gfx/webrender/src/border.rs @@ -2,41 +2,16 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{BorderRadius, BorderSide, BorderStyle, BorderWidths, ColorF, LayoutPoint}; +use api::{BorderRadius, BorderSide, BorderStyle, BorderWidths, ColorF}; use api::{ColorU, DeviceRect, DeviceSize, LayoutSizeAu, LayoutPrimitiveInfo, LayoutToDeviceScale}; -use api::{DevicePoint, DeviceIntSize, LayoutRect, LayoutSize, NormalBorder}; +use api::{DevicePixel, DeviceVector2D, DevicePoint, DeviceIntSize, LayoutRect, LayoutSize, NormalBorder}; use app_units::Au; -use clip::ClipSource; use ellipse::Ellipse; use display_list_flattener::DisplayListFlattener; use gpu_types::{BorderInstance, BorderSegment, BrushFlags}; -use gpu_cache::GpuDataRequest; -use prim_store::{BorderPrimitiveCpu, BrushClipMaskKind, BrushKind, BrushPrimitive, BrushSegment, BrushSegmentDescriptor}; +use prim_store::{BrushKind, BrushPrimitive, BrushSegment}; use prim_store::{BorderSource, EdgeAaSegmentMask, PrimitiveContainer, ScrollNodeAndClipChain}; -use util::{lerp, pack_as_float, RectHelpers}; - -#[repr(u8)] -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum BorderCornerInstance { - None, - Single, // Single instance needed - corner styles are same or similar. - Double, // Different corner styles. Draw two instances, one per style. -} - -#[repr(C)] -pub enum BorderCornerSide { - Both, - First, - Second, -} - -#[repr(C)] -enum BorderCorner { - TopLeft, - TopRight, - BottomLeft, - BottomRight, -} +use util::{lerp, RectHelpers}; trait AuSizeConverter { fn to_au(&self) -> LayoutSizeAu; @@ -128,205 +103,6 @@ pub struct BorderCacheKey { pub scale: Au, } -#[derive(Clone, Debug, PartialEq)] -pub enum BorderCornerKind { - None, - Solid, - Clip(BorderCornerInstance), - Mask( - BorderCornerClipData, - LayoutSize, - LayoutSize, - BorderCornerClipKind, - ), -} - -impl BorderCornerKind { - fn new_mask( - kind: BorderCornerClipKind, - width0: f32, - width1: f32, - corner: BorderCorner, - radius: LayoutSize, - border_rect: LayoutRect, - ) -> BorderCornerKind { - let size = LayoutSize::new(width0.max(radius.width), width1.max(radius.height)); - let (origin, clip_center) = match corner { - BorderCorner::TopLeft => { - let origin = border_rect.origin; - let clip_center = origin + size; - (origin, clip_center) - } - BorderCorner::TopRight => { - let origin = LayoutPoint::new( - border_rect.origin.x + border_rect.size.width - size.width, - border_rect.origin.y, - ); - let clip_center = origin + LayoutSize::new(0.0, size.height); - (origin, clip_center) - } - BorderCorner::BottomRight => { - let origin = border_rect.origin + (border_rect.size - size); - let clip_center = origin; - (origin, clip_center) - } - BorderCorner::BottomLeft => { - let origin = LayoutPoint::new( - border_rect.origin.x, - border_rect.origin.y + border_rect.size.height - size.height, - ); - let clip_center = origin + LayoutSize::new(size.width, 0.0); - (origin, clip_center) - } - }; - let clip_data = BorderCornerClipData { - corner_rect: LayoutRect::new(origin, size), - clip_center, - corner: pack_as_float(corner as u32), - kind: pack_as_float(kind as u32), - }; - BorderCornerKind::Mask(clip_data, radius, LayoutSize::new(width0, width1), kind) - } - - fn get_radius(&self, original_radius: &LayoutSize) -> LayoutSize { - match *self { - BorderCornerKind::Solid => *original_radius, - BorderCornerKind::Clip(..) => *original_radius, - BorderCornerKind::Mask(_, ref radius, _, _) => *radius, - BorderCornerKind::None => *original_radius, - } - } -} - -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum BorderEdgeKind { - None, - Solid, - Clip, -} - -fn get_corner( - edge0: &BorderSide, - width0: f32, - edge1: &BorderSide, - width1: f32, - radius: &LayoutSize, - corner: BorderCorner, - border_rect: &LayoutRect, -) -> BorderCornerKind { - // If both widths are zero, a corner isn't formed. - if width0 == 0.0 && width1 == 0.0 { - return BorderCornerKind::None; - } - - // If both edges are transparent, no corner is formed. - if edge0.color.a == 0.0 && edge1.color.a == 0.0 { - return BorderCornerKind::None; - } - - match (edge0.style, edge1.style) { - // If both edges are none or hidden, no corner is needed. - (BorderStyle::None, BorderStyle::None) | - (BorderStyle::None, BorderStyle::Hidden) | - (BorderStyle::Hidden, BorderStyle::None) | - (BorderStyle::Hidden, BorderStyle::Hidden) => { - BorderCornerKind::None - } - - // If one of the edges is none or hidden, we just draw one style. - (BorderStyle::None, _) | - (_, BorderStyle::None) | - (BorderStyle::Hidden, _) | - (_, BorderStyle::Hidden) => { - BorderCornerKind::Clip(BorderCornerInstance::Single) - } - - // If both borders are solid, we can draw them with a simple rectangle if - // both the colors match and there is no radius. - (BorderStyle::Solid, BorderStyle::Solid) => { - if edge0.color == edge1.color && radius.width == 0.0 && radius.height == 0.0 { - BorderCornerKind::Solid - } else { - BorderCornerKind::Clip(BorderCornerInstance::Single) - } - } - - // Inset / outset borders just modify the color of edges, so can be - // drawn with the normal border corner shader. - (BorderStyle::Outset, BorderStyle::Outset) | - (BorderStyle::Inset, BorderStyle::Inset) | - (BorderStyle::Double, BorderStyle::Double) | - (BorderStyle::Groove, BorderStyle::Groove) | - (BorderStyle::Ridge, BorderStyle::Ridge) => { - BorderCornerKind::Clip(BorderCornerInstance::Single) - } - - // Dashed and dotted border corners get drawn into a clip mask. - (BorderStyle::Dashed, BorderStyle::Dashed) => BorderCornerKind::new_mask( - BorderCornerClipKind::Dash, - width0, - width1, - corner, - *radius, - *border_rect, - ), - (BorderStyle::Dotted, BorderStyle::Dotted) => { - let mut radius = *radius; - if radius.width < width0 { - radius.width = 0.0; - } - if radius.height < width1 { - radius.height = 0.0; - } - BorderCornerKind::new_mask( - BorderCornerClipKind::Dot, - width0, - width1, - corner, - radius, - *border_rect, - ) - } - - // Draw border transitions with dots and/or dashes as - // solid segments. The old border path didn't support - // this anyway, so we might as well start using the new - // border path here, since the dashing in the edges is - // much higher quality anyway. - (BorderStyle::Dotted, _) | - (_, BorderStyle::Dotted) | - (BorderStyle::Dashed, _) | - (_, BorderStyle::Dashed) => BorderCornerKind::Clip(BorderCornerInstance::Single), - - // Everything else can be handled by drawing the corner twice, - // where the shader outputs zero alpha for the side it's not - // drawing. This is somewhat inefficient in terms of pixels - // written, but it's a fairly rare case, and we can optimize - // this case later. - _ => BorderCornerKind::Clip(BorderCornerInstance::Double), - } -} - -fn get_edge(edge: &BorderSide, width: f32, height: f32) -> (BorderEdgeKind, f32) { - if width == 0.0 || height <= 0.0 { - return (BorderEdgeKind::None, 0.0); - } - - match edge.style { - BorderStyle::None | BorderStyle::Hidden => (BorderEdgeKind::None, 0.0), - - BorderStyle::Solid | BorderStyle::Inset | BorderStyle::Outset => { - (BorderEdgeKind::Solid, width) - } - - BorderStyle::Double | - BorderStyle::Groove | - BorderStyle::Ridge | - BorderStyle::Dashed | - BorderStyle::Dotted => (BorderEdgeKind::Clip, width), - } -} - pub fn ensure_no_corner_overlap( radius: &mut BorderRadius, rect: &LayoutRect, @@ -373,74 +149,6 @@ pub fn ensure_no_corner_overlap( } impl<'a> DisplayListFlattener<'a> { - fn add_normal_border_primitive( - &mut self, - info: &LayoutPrimitiveInfo, - border: &NormalBorder, - radius: &BorderRadius, - widths: &BorderWidths, - clip_and_scroll: ScrollNodeAndClipChain, - corner_instances: [BorderCornerInstance; 4], - edges: [BorderEdgeKind; 4], - clip_sources: Vec, - ) { - let left = &border.left; - let right = &border.right; - let top = &border.top; - let bottom = &border.bottom; - - // These colors are used during inset/outset scaling. - let left_color = left.border_color(1.0, 2.0 / 3.0, 0.3, 0.7).premultiplied(); - let top_color = top.border_color(1.0, 2.0 / 3.0, 0.3, 0.7).premultiplied(); - let right_color = right.border_color(2.0 / 3.0, 1.0, 0.7, 0.3).premultiplied(); - let bottom_color = bottom.border_color(2.0 / 3.0, 1.0, 0.7, 0.3).premultiplied(); - - let prim_cpu = BorderPrimitiveCpu { - corner_instances, - edges, - - // TODO(gw): In the future, we will build these on demand - // from the deserialized display list, rather - // than creating it immediately. - gpu_blocks: [ - [ - pack_as_float(left.style as u32), - pack_as_float(top.style as u32), - pack_as_float(right.style as u32), - pack_as_float(bottom.style as u32), - ].into(), - [widths.left, widths.top, widths.right, widths.bottom].into(), - left_color.into(), - top_color.into(), - right_color.into(), - bottom_color.into(), - [ - radius.top_left.width, - radius.top_left.height, - radius.top_right.width, - radius.top_right.height, - ].into(), - [ - radius.bottom_right.width, - radius.bottom_right.height, - radius.bottom_left.width, - radius.bottom_left.height, - ].into(), - ], - }; - - self.add_primitive( - clip_and_scroll, - info, - clip_sources, - PrimitiveContainer::Border(prim_cpu), - ); - } - - // TODO(gw): This allows us to move border types over to the - // simplified shader model one at a time. Once all borders - // are converted, this can be removed, along with the complex - // border code path. pub fn add_normal_border( &mut self, info: &LayoutPrimitiveInfo, @@ -448,269 +156,36 @@ impl<'a> DisplayListFlattener<'a> { widths: &BorderWidths, clip_and_scroll: ScrollNodeAndClipChain, ) { - // The border shader is quite expensive. For simple borders, we can just draw - // the border with a few rectangles. This generally gives better batching, and - // a GPU win in fragment shader time. - // More importantly, the software (OSMesa) implementation we run tests on is - // particularly slow at running our complex border shader, compared to the - // rectangle shader. This has the effect of making some of our tests time - // out more often on CI (the actual cause is simply too many Servo processes and - // threads being run on CI at once). - let mut border = *border; ensure_no_corner_overlap(&mut border.radius, &info.rect); - let radius = &border.radius; - let left = &border.left; - let right = &border.right; - let top = &border.top; - let bottom = &border.bottom; - - let brush_border_supported = [left, top, right, bottom].iter().all(|edge| { - match edge.style { - BorderStyle::Solid | - BorderStyle::Hidden | - BorderStyle::None | - BorderStyle::Double | - BorderStyle::Inset | - BorderStyle::Groove | - BorderStyle::Ridge | - BorderStyle::Outset => { - true - } - - BorderStyle::Dotted | - BorderStyle::Dashed => { - false - } - } - }); - - if brush_border_supported { - let prim = BrushPrimitive::new( - BrushKind::Border { - source: BorderSource::Border { - border, - widths: *widths, - cache_key: BorderCacheKey { - left: border.left.into(), - top: border.top.into(), - right: border.right.into(), - bottom: border.bottom.into(), - widths: (*widths).into(), - radius: border.radius.into(), - scale: Au::from_f32_px(0.0), - }, - task_info: None, - handle: None, + let prim = BrushPrimitive::new( + BrushKind::Border { + source: BorderSource::Border { + border, + widths: *widths, + cache_key: BorderCacheKey { + left: border.left.into(), + top: border.top.into(), + right: border.right.into(), + bottom: border.bottom.into(), + widths: (*widths).into(), + radius: border.radius.into(), + scale: Au::from_f32_px(0.0), }, + task_info: None, + handle: None, }, - None, - ); + }, + None, + ); - self.add_primitive( - clip_and_scroll, - info, - Vec::new(), - PrimitiveContainer::Brush(prim), - ); - return; - } - - let corners = [ - get_corner( - left, - widths.left, - top, - widths.top, - &radius.top_left, - BorderCorner::TopLeft, - &info.rect, - ), - get_corner( - right, - widths.right, - top, - widths.top, - &radius.top_right, - BorderCorner::TopRight, - &info.rect, - ), - get_corner( - right, - widths.right, - bottom, - widths.bottom, - &radius.bottom_right, - BorderCorner::BottomRight, - &info.rect, - ), - get_corner( - left, - widths.left, - bottom, - widths.bottom, - &radius.bottom_left, - BorderCorner::BottomLeft, - &info.rect, - ), - ]; - - let (left_edge, left_len) = get_edge(left, widths.left, - info.rect.size.height - radius.top_left.height - radius.bottom_left.height); - let (top_edge, top_len) = get_edge(top, widths.top, - info.rect.size.width - radius.top_left.width - radius.top_right.width); - let (right_edge, right_len) = get_edge(right, widths.right, - info.rect.size.height - radius.top_right.height - radius.bottom_right.height); - let (bottom_edge, bottom_len) = get_edge(bottom, widths.bottom, - info.rect.size.width - radius.bottom_right.width - radius.bottom_left.width); - - let edges = [left_edge, top_edge, right_edge, bottom_edge]; - - // Use a simple rectangle case when all edges and corners are either - // solid or none. - let all_corners_simple = corners.iter().all(|c| { - *c == BorderCornerKind::Solid || *c == BorderCornerKind::None - }); - let all_edges_simple = edges.iter().all(|e| { - *e == BorderEdgeKind::Solid || *e == BorderEdgeKind::None - }); - - let has_no_curve = radius.is_zero(); - - if has_no_curve && all_corners_simple && all_edges_simple { - let p0 = info.rect.origin; - let p1 = LayoutPoint::new( - info.rect.origin.x + left_len, - info.rect.origin.y + top_len, - ); - let p2 = LayoutPoint::new( - info.rect.origin.x + info.rect.size.width - right_len, - info.rect.origin.y + info.rect.size.height - bottom_len, - ); - let p3 = info.rect.bottom_right(); - - let segment = |x0, y0, x1, y1| BrushSegment::new( - LayoutRect::from_floats(x0, y0, x1, y1), - true, - EdgeAaSegmentMask::all(), // Note: this doesn't seem right, needs revision - [0.0; 4], - BrushFlags::empty(), - ); - - // Add a solid rectangle for each visible edge/corner combination. - if top_edge == BorderEdgeKind::Solid { - let descriptor = BrushSegmentDescriptor { - segments: vec![ - segment(p0.x, p0.y, p1.x, p1.y), - segment(p2.x, p0.y, p3.x, p1.y), - segment(p1.x, p0.y, p2.x, p1.y), - ], - clip_mask_kind: BrushClipMaskKind::Unknown, - }; - - self.add_solid_rectangle( - clip_and_scroll, - info, - border.top.color, - Some(descriptor), - Vec::new(), - ); - } - - if left_edge == BorderEdgeKind::Solid { - let descriptor = BrushSegmentDescriptor { - segments: vec![ - segment(p0.x, p1.y, p1.x, p2.y), - ], - clip_mask_kind: BrushClipMaskKind::Unknown, - }; - - self.add_solid_rectangle( - clip_and_scroll, - info, - border.left.color, - Some(descriptor), - Vec::new(), - ); - } - - if right_edge == BorderEdgeKind::Solid { - let descriptor = BrushSegmentDescriptor { - segments: vec![ - segment(p2.x, p1.y, p3.x, p2.y), - ], - clip_mask_kind: BrushClipMaskKind::Unknown, - }; - - self.add_solid_rectangle( - clip_and_scroll, - info, - border.right.color, - Some(descriptor), - Vec::new(), - ); - } - - if bottom_edge == BorderEdgeKind::Solid { - let descriptor = BrushSegmentDescriptor { - segments: vec![ - segment(p1.x, p2.y, p2.x, p3.y), - segment(p2.x, p2.y, p3.x, p3.y), - segment(p0.x, p2.y, p1.x, p3.y), - ], - clip_mask_kind: BrushClipMaskKind::Unknown, - }; - - self.add_solid_rectangle( - clip_and_scroll, - info, - border.bottom.color, - Some(descriptor), - Vec::new(), - ); - } - } else { - // Create clip masks for border corners, if required. - let mut extra_clips = Vec::new(); - let mut corner_instances = [BorderCornerInstance::Single; 4]; - - let radius = &border.radius; - let radius = BorderRadius { - top_left: corners[0].get_radius(&radius.top_left), - top_right: corners[1].get_radius(&radius.top_right), - bottom_right: corners[2].get_radius(&radius.bottom_right), - bottom_left: corners[3].get_radius(&radius.bottom_left), - }; - - for (i, corner) in corners.iter().enumerate() { - match *corner { - BorderCornerKind::Mask(corner_data, mut corner_radius, widths, kind) => { - let clip_source = - BorderCornerClipSource::new(corner_data, corner_radius, widths, kind); - extra_clips.push(ClipSource::BorderCorner(clip_source)); - } - BorderCornerKind::Clip(instance_kind) => { - corner_instances[i] = instance_kind; - } - BorderCornerKind::Solid => {} - BorderCornerKind::None => { - corner_instances[i] = BorderCornerInstance::None; - } - } - } - - self.add_normal_border_primitive( - info, - &border, - &radius, - widths, - clip_and_scroll, - corner_instances, - edges, - extra_clips, - ); - } + self.add_primitive( + clip_and_scroll, + info, + Vec::new(), + PrimitiveContainer::Brush(prim), + ); } } @@ -756,26 +231,24 @@ impl BorderSideHelpers for BorderSide { #[repr(C)] #[derive(Copy, Debug, Clone, PartialEq)] pub enum BorderCornerClipKind { - Dash, - Dot, + Dash = 1, + Dot = 2, } /// The source data for a border corner clip mask. #[derive(Debug, Clone)] pub struct BorderCornerClipSource { - pub corner_data: BorderCornerClipData, pub max_clip_count: usize, kind: BorderCornerClipKind, - widths: LayoutSize, - ellipse: Ellipse, - pub dot_dash_data: Vec<[f32; 8]>, + widths: DeviceSize, + radius: DeviceSize, + ellipse: Ellipse, } impl BorderCornerClipSource { pub fn new( - corner_data: BorderCornerClipData, - corner_radius: LayoutSize, - widths: LayoutSize, + corner_radius: DeviceSize, + widths: DeviceSize, kind: BorderCornerClipKind, ) -> BorderCornerClipSource { // Work out a dash length (and therefore dash count) @@ -802,7 +275,7 @@ impl BorderCornerClipSource { // Round that up to the nearest integer, so that the dash length // doesn't exceed the ratio above. Add one extra dash to cover // the last half-dash of the arc. - (ellipse, 1 + desired_count.ceil() as usize) + (ellipse, desired_count.ceil() as usize) } BorderCornerClipKind::Dot => { let mut corner_radius = corner_radius; @@ -832,32 +305,57 @@ impl BorderCornerClipSource { // Add space for one extra dot since they are centered at the // start of the arc. - (ellipse, 1 + max_dot_count.ceil() as usize) + (ellipse, max_dot_count.ceil() as usize) } } }; BorderCornerClipSource { kind, - corner_data, max_clip_count, ellipse, widths, - dot_dash_data: Vec::new(), + radius: corner_radius, } } - pub fn write(&mut self, mut request: GpuDataRequest) { - self.corner_data.write(&mut request); - assert_eq!(request.close(), 2); + // TODO(gw): The naming and structure of BorderCornerClipSource + // don't really make sense. I've left it this way + // for now in order to reduce the size of the + // patch a bit. In the future, when we spent some + // time working on dot/dash placement, we should + // restructure this code to be more consistent + // with how border rendering works now. + pub fn write(self, segment: BorderSegment) -> Vec<[f32; 8]> { + let mut dot_dash_data = Vec::new(); + + let outer_scale = match segment { + BorderSegment::TopLeft => DeviceVector2D::new(0.0, 0.0), + BorderSegment::TopRight => DeviceVector2D::new(1.0, 0.0), + BorderSegment::BottomRight => DeviceVector2D::new(1.0, 1.0), + BorderSegment::BottomLeft => DeviceVector2D::new(0.0, 1.0), + _ => unreachable!(), + }; + let outer = DevicePoint::new( + outer_scale.x * self.radius.width, + outer_scale.y * self.radius.height, + ); + let clip_sign = DeviceVector2D::new( + 1.0 - 2.0 * outer_scale.x, + 1.0 - 2.0 * outer_scale.y, + ); match self.kind { BorderCornerClipKind::Dash => { // Get the correct dash arc length. let dash_arc_length = - 0.5 * self.ellipse.total_arc_length / (self.max_clip_count - 1) as f32; - self.dot_dash_data.clear(); - let mut current_arc_length = -0.5 * dash_arc_length; + 0.5 * self.ellipse.total_arc_length / self.max_clip_count as f32; + // Start the first dash at one quarter the length of a single dash + // along the arc line. This is arbitrary but looks reasonable in + // most cases. We need to spend some time working on a more + // sophisticated dash placement algorithm that takes into account + // the offset of the dashes along edge segments. + let mut current_arc_length = 0.25 * dash_arc_length; for _ in 0 .. self.max_clip_count { let arc_length0 = current_arc_length; current_arc_length += dash_arc_length; @@ -871,16 +369,41 @@ impl BorderCornerClipSource { let (point0, tangent0) = self.ellipse.get_point_and_tangent(alpha); let (point1, tangent1) = self.ellipse.get_point_and_tangent(beta); - self.dot_dash_data.push([ - point0.x, point0.y, tangent0.x, tangent0.y, - point1.x, point1.y, tangent1.x, tangent1.y + let point0 = DevicePoint::new( + outer.x + clip_sign.x * (self.radius.width - point0.x), + outer.y + clip_sign.y * (self.radius.height - point0.y), + ); + + let tangent0 = DeviceVector2D::new( + -tangent0.x * clip_sign.x, + -tangent0.y * clip_sign.y, + ); + + let point1 = DevicePoint::new( + outer.x + clip_sign.x * (self.radius.width - point1.x), + outer.y + clip_sign.y * (self.radius.height - point1.y), + ); + + let tangent1 = DeviceVector2D::new( + -tangent1.x * clip_sign.x, + -tangent1.y * clip_sign.y, + ); + + dot_dash_data.push([ + point0.x, + point0.y, + tangent0.x, + tangent0.y, + point1.x, + point1.y, + tangent1.x, + tangent1.y, ]); } } BorderCornerClipKind::Dot if self.max_clip_count == 1 => { let dot_diameter = lerp(self.widths.width, self.widths.height, 0.5); - self.dot_dash_data.clear(); - self.dot_dash_data.push([ + dot_dash_data.push([ self.widths.width / 2.0, self.widths.height / 2.0, 0.5 * dot_diameter, 0., 0., 0., 0., 0., ]); @@ -893,9 +416,9 @@ impl BorderCornerClipSource { // Alternate between adding dots at the start and end of the // ellipse arc. This ensures that we always end up with an exact // half dot at each end of the arc, to match up with the edges. - forward_dots.push(DotInfo::new(0.0, self.widths.width)); + forward_dots.push(DotInfo::new(self.widths.width, self.widths.width)); back_dots.push(DotInfo::new( - self.ellipse.total_arc_length, + self.ellipse.total_arc_length - self.widths.height, self.widths.height, )); @@ -947,59 +470,36 @@ impl BorderCornerClipSource { let number_of_dots = forward_dots.len() + back_dots.len(); let extra_space_per_dot = leftover_arc_length / (number_of_dots - 1) as f32; - self.dot_dash_data.clear(); - - let create_dot_data = |ellipse: &Ellipse, arc_length: f32, radius: f32| -> [f32; 8] { + let create_dot_data = |ellipse: &Ellipse, arc_length: f32, radius: f32| -> [f32; 8] { // Represents the GPU data for drawing a single dot to a clip mask. The order // these are specified must stay in sync with the way this data is read in the // dot clip shader. let theta = ellipse.find_angle_for_arc_length(arc_length); let (center, _) = ellipse.get_point_and_tangent(theta); - [center.x, center.y, radius, 0., 0., 0., 0., 0.,] + + let center = DevicePoint::new( + outer.x + clip_sign.x * (self.radius.width - center.x), + outer.y + clip_sign.y * (self.radius.height - center.y), + ); + + [center.x, center.y, radius, 0.0, 0.0, 0.0, 0.0, 0.0] }; for (i, dot) in forward_dots.iter().enumerate() { let extra_dist = i as f32 * extra_space_per_dot; let dot_data = create_dot_data(&self.ellipse, dot.arc_pos + extra_dist, 0.5 * dot.diameter); - self.dot_dash_data.push(dot_data); + dot_dash_data.push(dot_data); } for (i, dot) in back_dots.iter().enumerate() { let extra_dist = i as f32 * extra_space_per_dot; let dot_data = create_dot_data(&self.ellipse, dot.arc_pos - extra_dist, 0.5 * dot.diameter); - self.dot_dash_data.push(dot_data); + dot_dash_data.push(dot_data); } } } - } -} -/// Represents the common GPU data for writing a -/// clip mask for a border corner. -#[derive(Debug, Copy, Clone, PartialEq)] -#[repr(C)] -pub struct BorderCornerClipData { - /// Local space rect of the border corner. - corner_rect: LayoutRect, - /// Local space point that is the center of the - /// circle or ellipse that we are clipping against. - clip_center: LayoutPoint, - /// The shader needs to know which corner, to - /// be able to flip the dash tangents to the - /// right orientation. - corner: f32, // Of type BorderCorner enum - kind: f32, // Of type BorderCornerClipKind enum -} - -impl BorderCornerClipData { - fn write(&self, request: &mut GpuDataRequest) { - request.push(self.corner_rect); - request.push([ - self.clip_center.x, - self.clip_center.y, - self.corner, - self.kind, - ]); + dot_dash_data } } @@ -1029,6 +529,74 @@ pub struct BorderRenderTaskInfo { pub size: DeviceIntSize, } +// Information needed to place and draw a border edge. +struct EdgeInfo { + // Offset in local space to place the edge from origin. + local_offset: f32, + // Size of the edge in local space. + local_size: f32, + // Size in device pixels needed in the render task. + device_size: f32, +} + +impl EdgeInfo { + fn new( + local_offset: f32, + local_size: f32, + device_size: f32, + ) -> EdgeInfo { + EdgeInfo { + local_offset, + local_size, + device_size, + } + } +} + +// Get the needed size in device pixels for an edge, +// based on the border style of that edge. This is used +// to determine how big the render task should be. +fn get_edge_info( + style: BorderStyle, + side_width: f32, + avail_size: f32, + scale: f32, +) -> EdgeInfo { + // To avoid division by zero below. + if side_width <= 0.0 { + return EdgeInfo::new(0.0, 0.0, 0.0); + } + + match style { + BorderStyle::Dashed => { + let dash_size = 3.0 * side_width; + let approx_dash_count = (avail_size - dash_size) / dash_size; + let dash_count = 1.0 + 2.0 * (approx_dash_count / 2.0).floor(); + let used_size = dash_count * dash_size; + let extra_space = avail_size - used_size; + let device_size = 2.0 * dash_size * scale; + let offset = (extra_space * 0.5).round(); + EdgeInfo::new(offset, used_size, device_size) + } + BorderStyle::Dotted => { + let dot_and_space_size = 2.0 * side_width; + if avail_size < dot_and_space_size * 0.75 { + return EdgeInfo::new(0.0, 0.0, 0.0); + } + let approx_dot_count = avail_size / dot_and_space_size; + let dot_count = approx_dot_count.floor().max(1.0); + let used_size = dot_count * dot_and_space_size; + let extra_space = avail_size - used_size; + let device_size = dot_and_space_size * scale; + let offset = (extra_space * 0.5).round(); + EdgeInfo::new(offset, used_size, device_size) + } + _ => { + EdgeInfo::new(0.0, avail_size, 8.0) + } + } +} + impl BorderRenderTaskInfo { pub fn new( rect: &LayoutRect, @@ -1083,30 +651,51 @@ impl BorderRenderTaskInfo { border.radius.bottom_left.height.max(widths.bottom), ); - // TODO(gw): The inner and outer widths don't matter for simple - // border types. Once we push dashing and dotted styles - // through border brushes, we need to calculate an - // appropriate length here. - let width_inner = 16.0; - let height_inner = 16.0; + let top_edge_info = get_edge_info( + border.top.style, + widths.top, + rect.size.width - local_size_tl.width - local_size_tr.width, + scale.0, + ); + let bottom_edge_info = get_edge_info( + border.bottom.style, + widths.bottom, + rect.size.width - local_size_bl.width - local_size_br.width, + scale.0, + ); + let inner_width = top_edge_info.device_size.max(bottom_edge_info.device_size).ceil(); + + let left_edge_info = get_edge_info( + border.left.style, + widths.left, + rect.size.height - local_size_tl.height - local_size_bl.height, + scale.0, + ); + let right_edge_info = get_edge_info( + border.right.style, + widths.right, + rect.size.height - local_size_tr.height - local_size_br.height, + scale.0, + ); + let inner_height = left_edge_info.device_size.max(right_edge_info.device_size).ceil(); let size = DeviceSize::new( - dp_size_tl.width.max(dp_size_bl.width) + width_inner + dp_size_tr.width.max(dp_size_br.width), - dp_size_tl.height.max(dp_size_tr.height) + height_inner + dp_size_bl.height.max(dp_size_br.height), + dp_size_tl.width.max(dp_size_bl.width) + inner_width + dp_size_tr.width.max(dp_size_br.width), + dp_size_tl.height.max(dp_size_tr.height) + inner_height + dp_size_bl.height.max(dp_size_br.height), ); add_edge_segment( LayoutRect::from_floats( rect.origin.x, - rect.origin.y + local_size_tl.height, + rect.origin.y + local_size_tl.height + left_edge_info.local_offset, rect.origin.x + widths.left, - rect.origin.y + rect.size.height - local_size_bl.height, + rect.origin.y + local_size_tl.height + left_edge_info.local_offset + left_edge_info.local_size, ), DeviceRect::from_floats( 0.0, dp_size_tl.height, dp_width_left, - size.height - dp_size_bl.height, + dp_size_tl.height + left_edge_info.device_size, ), &border.left, BorderSegment::Left, @@ -1118,15 +707,15 @@ impl BorderRenderTaskInfo { add_edge_segment( LayoutRect::from_floats( - rect.origin.x + local_size_tl.width, + rect.origin.x + local_size_tl.width + top_edge_info.local_offset, rect.origin.y, - rect.origin.x + rect.size.width - local_size_tr.width, + rect.origin.x + local_size_tl.width + top_edge_info.local_offset + top_edge_info.local_size, rect.origin.y + widths.top, ), DeviceRect::from_floats( dp_size_tl.width, 0.0, - size.width - dp_size_tr.width, + dp_size_tl.width + top_edge_info.device_size, dp_width_top, ), &border.top, @@ -1140,15 +729,15 @@ impl BorderRenderTaskInfo { add_edge_segment( LayoutRect::from_floats( rect.origin.x + rect.size.width - widths.right, - rect.origin.y + local_size_tr.height, + rect.origin.y + local_size_tr.height + right_edge_info.local_offset, rect.origin.x + rect.size.width, - rect.origin.y + rect.size.height - local_size_br.height, + rect.origin.y + local_size_tr.height + right_edge_info.local_offset + right_edge_info.local_size, ), DeviceRect::from_floats( size.width - dp_width_right, dp_size_tr.height, size.width, - size.height - dp_size_br.height, + dp_size_tr.height + right_edge_info.device_size, ), &border.right, BorderSegment::Right, @@ -1160,15 +749,15 @@ impl BorderRenderTaskInfo { add_edge_segment( LayoutRect::from_floats( - rect.origin.x + local_size_bl.width, + rect.origin.x + local_size_bl.width + bottom_edge_info.local_offset, rect.origin.y + rect.size.height - widths.bottom, - rect.origin.x + rect.size.width - local_size_br.width, + rect.origin.x + local_size_bl.width + bottom_edge_info.local_offset + bottom_edge_info.local_size, rect.origin.y + rect.size.height, ), DeviceRect::from_floats( dp_size_bl.width, size.height - dp_width_bottom, - size.width - dp_size_br.width, + dp_size_bl.width + bottom_edge_info.device_size, size.height, ), &border.bottom, @@ -1277,10 +866,7 @@ impl BorderRenderTaskInfo { } } - pub fn build_instances( - &self, - border: &NormalBorder, - ) -> Vec { + pub fn build_instances(&self, border: &NormalBorder) -> Vec { let mut instances = Vec::new(); for info in &self.border_segments { @@ -1373,21 +959,129 @@ fn add_segment( widths: DeviceSize, radius: DeviceSize, ) { - let flags = (segment as i32) | - ((style0 as i32) << 8) | - ((style1 as i32) << 16); + let base_flags = (segment as i32) | + ((style0 as i32) << 8) | + ((style1 as i32) << 16); let base_instance = BorderInstance { task_origin: DevicePoint::zero(), local_rect: task_rect, - flags, + flags: base_flags, color0: color0.premultiplied(), color1: color1.premultiplied(), widths, radius, + clip_params: [0.0; 8], }; - instances.push(base_instance); + match segment { + BorderSegment::TopLeft | + BorderSegment::TopRight | + BorderSegment::BottomLeft | + BorderSegment::BottomRight => { + // TODO(gw): Similarly to the old border code, we don't correctly handle a a corner + // that is dashed on one edge, and dotted on another. We can handle this + // in the future by submitting two instances, each one with one side + // color set to have an alpha of 0. + if (style0 == BorderStyle::Dotted && style1 == BorderStyle::Dashed) || + (style0 == BorderStyle::Dashed && style0 == BorderStyle::Dotted) { + warn!("TODO: Handle a corner with dotted / dashed transition."); + } + + let clip_kind = match style0 { + BorderStyle::Dashed => Some(BorderCornerClipKind::Dash), + BorderStyle::Dotted => Some(BorderCornerClipKind::Dot), + _ => None, + }; + + match clip_kind { + Some(clip_kind) => { + let clip_source = BorderCornerClipSource::new( + radius, + widths, + clip_kind, + ); + + // TODO(gw): Restructure the BorderCornerClipSource code + // so that we don't allocate a Vec here. + let clip_list = clip_source.write(segment); + + for params in clip_list { + instances.push(BorderInstance { + flags: base_flags | ((clip_kind as i32) << 24), + clip_params: params, + ..base_instance + }); + } + } + None => { + instances.push(base_instance); + } + } + } + BorderSegment::Top | + BorderSegment::Bottom | + BorderSegment::Right | + BorderSegment::Left => { + let is_vertical = segment == BorderSegment::Left || + segment == BorderSegment::Right; + + match style0 { + BorderStyle::Dashed => { + let rect = if is_vertical { + let half_dash_size = task_rect.size.height * 0.5; + let y0 = task_rect.origin.y; + let y1 = y0 + half_dash_size.round(); + + DeviceRect::from_floats( + task_rect.origin.x, + y0, + task_rect.origin.x + task_rect.size.width, + y1, + ) + } else { + let half_dash_size = task_rect.size.width * 0.5; + let x0 = task_rect.origin.x; + let x1 = x0 + half_dash_size.round(); + + DeviceRect::from_floats( + x0, + task_rect.origin.y, + x1, + task_rect.origin.y + task_rect.size.height, + ) + }; + + instances.push(BorderInstance { + local_rect: rect, + ..base_instance + }); + } + BorderStyle::Dotted => { + let (x, y, r) = if is_vertical { + (widths.width * 0.5, + widths.width, + widths.width * 0.5) + } else { + (widths.height, + widths.height * 0.5, + widths.height * 0.5) + }; + + instances.push(BorderInstance { + flags: base_flags | ((BorderCornerClipKind::Dot as i32) << 24), + clip_params: [ + x, y, r, 0.0, 0.0, 0.0, 0.0, 0.0, + ], + ..base_instance + }); + } + _ => { + instances.push(base_instance); + } + } + } + } } fn add_corner_segment( diff --git a/gfx/webrender/src/clip.rs b/gfx/webrender/src/clip.rs index f3ac2beb8f3a..388d89da74b4 100644 --- a/gfx/webrender/src/clip.rs +++ b/gfx/webrender/src/clip.rs @@ -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([ diff --git a/gfx/webrender/src/display_list_flattener.rs b/gfx/webrender/src/display_list_flattener.rs index 1e37cc08df60..4881105188bd 100644 --- a/gfx/webrender/src/display_list_flattener.rs +++ b/gfx/webrender/src/display_list_flattener.rs @@ -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, } impl<'a> DisplayListFlattener<'a> { @@ -204,6 +202,7 @@ impl<'a> DisplayListFlattener<'a> { output_pipelines: &FastHashSet, 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, - gradient_stops_count: usize, ) { let rect = info.rect; let create_segments = |outset: SideOffsets2D| { @@ -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, - 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 { diff --git a/gfx/webrender/src/ellipse.rs b/gfx/webrender/src/ellipse.rs index 9d4ce09669a0..9a55ef369ae2 100644 --- a/gfx/webrender/src/ellipse.rs +++ b/gfx/webrender/src/ellipse.rs @@ -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 { + pub radius: TypedSize2D, pub total_arc_length: f32, } -impl Ellipse { - pub fn new(radius: LayoutSize) -> Ellipse { +impl Ellipse { + pub fn new(radius: TypedSize2D) -> Ellipse { // 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); diff --git a/gfx/webrender/src/frame_builder.rs b/gfx/webrender/src/frame_builder.rs index 6398720831da..df3a54ea3690 100644 --- a/gfx/webrender/src/frame_builder.rs +++ b/gfx/webrender/src/frame_builder.rs @@ -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, window_size: DeviceUintSize, + scene_id: u64, pub prim_store: PrimitiveStore, pub clip_store: ClipStore, pub hit_testing_runs: Vec, pub config: FrameBuilderConfig, - pub cached_gradients: Vec, pub scrollbar_prims: Vec, } pub struct FrameBuildingContext<'a> { + pub scene_id: u64, pub device_pixel_scale: DevicePixelScale, pub scene_properties: &'a SceneProperties, pub pipelines: &'a FastHashMap>, @@ -65,7 +66,6 @@ pub struct FrameBuildingState<'a> { pub local_clip_rects: &'a mut Vec, 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, 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, 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( diff --git a/gfx/webrender/src/gpu_cache.rs b/gfx/webrender/src/gpu_cache.rs index 1084bd6a15a1..067094d5976b 100644 --- a/gfx/webrender/src/gpu_cache.rs +++ b/gfx/webrender/src/gpu_cache.rs @@ -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> { diff --git a/gfx/webrender/src/gpu_types.rs b/gfx/webrender/src/gpu_types.rs index 741e8033af09..eb6da40914a3 100644 --- a/gfx/webrender/src/gpu_types.rs +++ b/gfx/webrender/src/gpu_types.rs @@ -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. diff --git a/gfx/webrender/src/hit_test.rs b/gfx/webrender/src/hit_test.rs index abd53f567dbf..c017918b62cc 100644 --- a/gfx/webrender/src/hit_test.rs +++ b/gfx/webrender/src/hit_test.rs @@ -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"); diff --git a/gfx/webrender/src/internal_types.rs b/gfx/webrender/src/internal_types.rs index fb581e6460c4..e4fba9f11844 100644 --- a/gfx/webrender/src/internal_types.rs +++ b/gfx/webrender/src/internal_types.rs @@ -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 { diff --git a/gfx/webrender/src/picture.rs b/gfx/webrender/src/picture.rs index 79494a2b832b..e6480c430255 100644 --- a/gfx/webrender/src/picture.rs +++ b/gfx/webrender/src/picture.rs @@ -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 diff --git a/gfx/webrender/src/prim_store.rs b/gfx/webrender/src/prim_store.rs index 897d1d87b2f6..2f0e41de4a66 100644 --- a/gfx/webrender/src/prim_store.rs +++ b/gfx/webrender/src/prim_store.rs @@ -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, extend_mode: ExtendMode, center: LayoutPoint, @@ -309,9 +293,8 @@ pub enum BrushKind { visible_tiles: Vec, }, LinearGradient { - gradient_index: CachedGradientIndex, + stops_handle: GpuCacheHandle, stops_range: ItemRange, - 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, pub cpu_text_runs: Vec, pub cpu_metadata: Vec, - pub cpu_borders: Vec, pub pictures: Vec, 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, 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, diff --git a/gfx/webrender/src/profiler.rs b/gfx/webrender/src/profiler.rs index 8906163206b4..cdd20e846570 100644 --- a/gfx/webrender/src/profiler.rs +++ b/gfx/webrender/src/profiler.rs @@ -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), + ); + } +} diff --git a/gfx/webrender/src/render_backend.rs b/gfx/webrender/src/render_backend.rs index 9ea92bf2c782..5c6d2f5e70cf 100644 --- a/gfx/webrender/src/render_backend.rs +++ b/gfx/webrender/src/render_backend.rs @@ -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, - // 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, ) { @@ -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, 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>, sampler: Option>, + 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::(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, ) } }; diff --git a/gfx/webrender/src/render_task.rs b/gfx/webrender/src/render_task.rs index 22f95fe3c581..fbb20e5f300c 100644 --- a/gfx/webrender/src/render_task.rs +++ b/gfx/webrender/src/render_task.rs @@ -453,8 +453,7 @@ impl RenderTask { ClipSource::Rectangle(..) | ClipSource::RoundedRectangle(..) | ClipSource::Image(..) | - ClipSource::LineDecoration(..) | - ClipSource::BorderCorner(..) => {} + ClipSource::LineDecoration(..) => {} } } } diff --git a/gfx/webrender/src/renderer.rs b/gfx/webrender/src/renderer.rs index 279d46422022..a97342b4db59 100644 --- a/gfx/webrender/src/renderer.rs +++ b/gfx/webrender/src/renderer.rs @@ -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, @@ -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, } diff --git a/gfx/webrender/src/scene_builder.rs b/gfx/webrender/src/scene_builder.rs index fec42e28a3e8..b5b5bdec6470 100644 --- a/gfx/webrender/src/scene_builder.rs +++ b/gfx/webrender/src/scene_builder.rs @@ -57,6 +57,7 @@ pub struct SceneRequest { pub font_instances: FontInstanceMap, pub output_pipelines: FastHashSet, pub removed_pipelines: Vec, + pub scene_id: u64, } pub struct BuiltScene { diff --git a/gfx/webrender/src/shade.rs b/gfx/webrender/src/shade.rs index 9e33d3a3575e..f1ae579d9b7a 100644 --- a/gfx/webrender/src/shade.rs +++ b/gfx/webrender/src/shade.rs @@ -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 { - 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); } diff --git a/gfx/webrender/src/tiling.rs b/gfx/webrender/src/tiling.rs index 9891f460941c..a823c625692f 100644 --- a/gfx/webrender/src/tiling.rs +++ b/gfx/webrender/src/tiling.rs @@ -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))] diff --git a/gfx/webrender/tests/angle_shader_validation.rs b/gfx/webrender/tests/angle_shader_validation.rs index bd49dcef5bc0..4a37d9f9db86 100644 --- a/gfx/webrender/tests/angle_shader_validation.rs +++ b/gfx/webrender/tests/angle_shader_validation.rs @@ -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, diff --git a/gfx/webrender_api/src/api.rs b/gfx/webrender_api/src/api.rs index 10cffb78cf8b..ea966d81c3e7 100644 --- a/gfx/webrender_api/src/api.rs +++ b/gfx/webrender_api/src/api.rs @@ -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. diff --git a/gfx/webrender_bindings/revision.txt b/gfx/webrender_bindings/revision.txt index 024c05450f2a..746ad892e11f 100644 --- a/gfx/webrender_bindings/revision.txt +++ b/gfx/webrender_bindings/revision.txt @@ -1 +1 @@ -8e697f8cb1f1aab2e5f6b9b903eb7191340b10c5 +aff9f409f3d6a3518c38c1f7755657f564c1083a diff --git a/gfx/wrench/src/rawtest.rs b/gfx/wrench/src/rawtest.rs index 68559b0a56ce..d5b70d5b4bd6 100644 --- a/gfx/wrench/src/rawtest.rs +++ b/gfx/wrench/src/rawtest.rs @@ -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";