diff --git a/webrender/res/brush.glsl b/webrender/res/brush.glsl new file mode 100644 index 0000000000..da1fb33826 --- /dev/null +++ b/webrender/res/brush.glsl @@ -0,0 +1,81 @@ +/* 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/. */ + +varying vec2 vLocalPos; +flat varying vec4 vLocalRect; + +#ifdef WR_VERTEX_SHADER + +struct BrushInstance { + int picture_address; + int prim_address; +}; + +BrushInstance load_brush() { + BrushInstance bi; + + bi.picture_address = aData0.x; + bi.prim_address = aData0.y; + + return bi; +} + +/* + The dynamic picture that this brush exists on. Right now, it + contains minimal information. In the future, it will describe + the transform mode of primitives on this picture, among other things. + */ +struct PictureTask { + RectWithSize target_rect; +}; + +PictureTask fetch_picture_task(int index) { + ivec2 uv = get_fetch_uv(index, VECS_PER_RENDER_TASK); + + vec4 target_rect = TEXEL_FETCH(sRenderTasks, uv, 0, ivec2(0, 0)); + + PictureTask task = PictureTask(RectWithSize(target_rect.xy, target_rect.zw)); + + return task; +} + +void main(void) { + // Load the brush instance from vertex attributes. + BrushInstance brush = load_brush(); + + // Fetch the dynamic picture that we are drawing on. + PictureTask pic_task = fetch_picture_task(brush.picture_address); + + // Load the geometry for this brush. For now, this is simply the + // local rect of the primitive. In the future, this will support + // loading segment rects, and other rect formats (glyphs). + PrimitiveGeometry geom = fetch_primitive_geometry(brush.prim_address); + + // Write the (p0,p1) form of the primitive rect and the local position + // of this vertex. Specific brush shaders can use this information to + // interpolate texture coordinates etc. + vLocalRect = vec4(geom.local_rect.p0, geom.local_rect.p0 + geom.local_rect.size); + + // Right now - pictures only support local positions. In the future, this + // will be expanded to support transform picture types (the common kind). + vec2 pos = pic_task.target_rect.p0 + aPosition.xy * pic_task.target_rect.size; + vLocalPos = aPosition.xy * pic_task.target_rect.size / uDevicePixelRatio; + + // Run the specific brush VS code to write interpolators. + brush_vs(brush.prim_address, vLocalRect); + + // Write the final position transformed by the orthographic device-pixel projection. + gl_Position = uTransform * vec4(pos, 0.0, 1.0); +} +#endif + +#ifdef WR_FRAGMENT_SHADER +void main(void) { + // Run the specific brush FS code to output the color. + vec4 color = brush_fs(vLocalPos, vLocalRect); + + // TODO(gw): Handle pre-multiply common code here as required. + oFragColor = color; +} +#endif diff --git a/webrender/res/brush_mask.glsl b/webrender/res/brush_mask.glsl new file mode 100644 index 0000000000..317345b113 --- /dev/null +++ b/webrender/res/brush_mask.glsl @@ -0,0 +1,61 @@ +/* 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,ellipse + +flat varying float vClipMode; +flat varying vec4 vClipCenter_Radius_TL; +flat varying vec4 vClipCenter_Radius_TR; +flat varying vec4 vClipCenter_Radius_BR; +flat varying vec4 vClipCenter_Radius_BL; + +#ifdef WR_VERTEX_SHADER + +struct BrushPrimitive { + float clip_mode; + float radius; +}; + +BrushPrimitive fetch_brush_primitive(int address) { + vec4 data = fetch_from_resource_cache_1(address); + return BrushPrimitive(data.x, data.y); +} + +void brush_vs(int prim_address, vec4 prim_rect) { + // Load the specific primitive. + BrushPrimitive prim = fetch_brush_primitive(prim_address + 2); + + // Write clip parameters + vClipMode = prim.clip_mode; + + vec2 r = vec2(prim.radius); + vClipCenter_Radius_TL = vec4(prim_rect.xy + vec2(r.x, r.y), r); + vClipCenter_Radius_TR = vec4(prim_rect.zy + vec2(-r.x, r.y), r); + vClipCenter_Radius_BR = vec4(prim_rect.zw + vec2(-r.x, -r.y), r); + vClipCenter_Radius_BL = vec4(prim_rect.xw + vec2(r.x, -r.y), r); +} +#endif + +#ifdef WR_FRAGMENT_SHADER +vec4 brush_fs(vec2 local_pos, vec4 local_rect) { + // TODO(gw): The mask code below is super-inefficient. Once we + // start using primitive segments in brush shaders, this can + // be made much faster. + float d = 0.0; + // Check if in valid clip region. + if (local_pos.x >= local_rect.x && local_pos.x < local_rect.z && + local_pos.y >= local_rect.y && local_pos.y < local_rect.w) { + // Apply ellipse clip on each corner. + d = rounded_rect(local_pos, + vClipCenter_Radius_TL, + vClipCenter_Radius_TR, + vClipCenter_Radius_BR, + vClipCenter_Radius_BL); + } + + return vec4(mix(d, 1.0 - d, vClipMode)); +} +#endif + +#include brush diff --git a/webrender/res/cs_blur.glsl b/webrender/res/cs_blur.glsl index 10f4e18de0..7c67b2e11a 100644 --- a/webrender/res/cs_blur.glsl +++ b/webrender/res/cs_blur.glsl @@ -31,7 +31,11 @@ void main(void) { local_rect.xy + local_rect.zw, aPosition.xy); +#if defined WR_FEATURE_COLOR vec2 texture_size = vec2(textureSize(sCacheRGBA8, 0).xy); +#else + vec2 texture_size = vec2(textureSize(sCacheA8, 0).xy); +#endif vUv.z = src_task.data1.x; vBlurRadius = 3 * int(task.data1.y); vSigma = task.data1.y; @@ -58,6 +62,15 @@ void main(void) { #endif #ifdef WR_FRAGMENT_SHADER + +#if defined WR_FEATURE_COLOR +#define SAMPLE_TYPE vec4 +#define SAMPLE_TEXTURE(uv) texture(sCacheRGBA8, uv) +#else +#define SAMPLE_TYPE float +#define SAMPLE_TEXTURE(uv) texture(sCacheA8, uv).r +#endif + // TODO(gw): Write a fast path blur that handles smaller blur radii // with a offset / weight uniform table and a constant // loop iteration count! @@ -66,13 +79,13 @@ void main(void) { // the number of texture fetches needed for a gaussian blur. void main(void) { - vec4 original_color = texture(sCacheRGBA8, vUv); + SAMPLE_TYPE original_color = SAMPLE_TEXTURE(vUv); // TODO(gw): The gauss function gets NaNs when blur radius // is zero. In the future, detect this earlier // and skip the blur passes completely. if (vBlurRadius == 0) { - oFragColor = original_color; + oFragColor = vec4(original_color); return; } @@ -83,7 +96,7 @@ void main(void) { gauss_coefficient.z = gauss_coefficient.y * gauss_coefficient.y; float gauss_coefficient_sum = 0.0; - vec4 avg_color = original_color * gauss_coefficient.x; + SAMPLE_TYPE avg_color = original_color * gauss_coefficient.x; gauss_coefficient_sum += gauss_coefficient.x; gauss_coefficient.xy *= gauss_coefficient.yz; @@ -91,15 +104,15 @@ void main(void) { vec2 offset = vOffsetScale * float(i); vec2 st0 = clamp(vUv.xy - offset, vUvRect.xy, vUvRect.zw); - avg_color += texture(sCacheRGBA8, vec3(st0, vUv.z)) * gauss_coefficient.x; + avg_color += SAMPLE_TEXTURE(vec3(st0, vUv.z)) * gauss_coefficient.x; vec2 st1 = clamp(vUv.xy + offset, vUvRect.xy, vUvRect.zw); - avg_color += texture(sCacheRGBA8, vec3(st1, vUv.z)) * gauss_coefficient.x; + avg_color += SAMPLE_TEXTURE(vec3(st1, vUv.z)) * gauss_coefficient.x; gauss_coefficient_sum += 2.0 * gauss_coefficient.x; gauss_coefficient.xy *= gauss_coefficient.yz; } - oFragColor = avg_color / gauss_coefficient_sum; + oFragColor = vec4(avg_color) / gauss_coefficient_sum; } #endif diff --git a/webrender/res/cs_box_shadow.glsl b/webrender/res/cs_box_shadow.glsl deleted file mode 100644 index ee458dce48..0000000000 --- a/webrender/res/cs_box_shadow.glsl +++ /dev/null @@ -1,188 +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 - -varying vec2 vPos; -flat varying vec2 vBorderRadii; -flat varying float vBlurRadius; -flat varying vec4 vBoxShadowRect; -flat varying float vInverted; - -#ifdef WR_VERTEX_SHADER -in ivec2 aPrimAddress; -in int aTaskIndex; - -void main(void) { - RenderTaskData task = fetch_render_task(aTaskIndex); - BoxShadow bs = fetch_boxshadow_direct(ivec2(aPrimAddress.x + VECS_PER_PRIM_HEADER, aPrimAddress.y)); - - vec2 p0 = task.data0.xy; - vec2 p1 = p0 + task.data0.zw; - - vec2 pos = mix(p0, p1, aPosition.xy); - - vBorderRadii = bs.border_radius_edge_size_blur_radius_inverted.xx; - vBlurRadius = bs.border_radius_edge_size_blur_radius_inverted.z; - vInverted = bs.border_radius_edge_size_blur_radius_inverted.w; - vBoxShadowRect = vec4(bs.bs_rect.xy, bs.bs_rect.xy + bs.bs_rect.zw); - - // The fragment shader expects logical units, beginning at where the - // blur radius begins. - // The first path of the equation gets the virtual position in - // logical pixels within the patch rectangle (accounting for - // bilinear offset). Then we add the start position of the - // box shadow rect and subtract the blur radius to get the - // virtual coordinates that the FS expects. - vPos = (pos - 1.0 - p0) / uDevicePixelRatio + bs.bs_rect.xy - vec2(2.0 * vBlurRadius); - - gl_Position = uTransform * vec4(pos, 0.0, 1.0); -} -#endif - -#ifdef WR_FRAGMENT_SHADER -// See http://asciimath.org to render the equations here. - -// The Gaussian function used for blurring: -// -// G_sigma(x) = 1/sqrt(2 pi sigma^2) e^(-x^2/(2 sigma^2)) -float gauss(float x, float sigma) { - float sigmaPow2 = sigma * sigma; - return 1.0 / sqrt(6.283185307179586 * sigmaPow2) * exp(-(x * x) / (2.0 * sigmaPow2)); -} - -// An approximation of the error function, which is related to the integral of the Gaussian -// function: -// -// "erf"(x) = 2/sqrt(pi) int_0^x e^(-t^2) dt -// ~~ 1 - 1 / (1 + a_1 x + a_2 x^2 + a_3 x^3 + a_4 x^4)^4 -// -// where: -// -// a_1 = 0.278393, a_2 = 0.230389, a_3 = 0.000972, a_4 = 0.078108 -// -// This approximation is accurate to `5 xx 10^-4`, more than accurate enough for our purposes. -// -// See: https://en.wikipedia.org/wiki/Error_function#Approximation_with_elementary_functions -float erf(float x) { - bool negative = x < 0.0; - if (negative) - x = -x; - float x2 = x * x; - float x3 = x2 * x; - float x4 = x2 * x2; - float denom = 1.0 + 0.278393 * x + 0.230389 * x2 + 0.000972 * x3 + 0.078108 * x4; - float result = 1.0 - 1.0 / (denom * denom * denom * denom); - return negative ? -result : result; -} - -// A useful helper for calculating integrals of the Gaussian function via the error function: -// -// "erf"_sigma(x) = 2 int 1/sqrt(2 pi sigma^2) e^(-x^2/(2 sigma^2)) dx -// = "erf"(x/(sigma sqrt(2))) -float erfSigma(float x, float sigma) { - return erf(x / (sigma * 1.4142135623730951)); -} - -// Returns the blurred color value from the box itself (not counting any rounded corners). `p_0` is -// the vector distance to the top left corner of the box; `p_1` is the vector distance to its -// bottom right corner. -// -// "colorFromRect"_sigma(p_0, p_1) -// = int_{p_{0_y}}^{p_{1_y}} int_{p_{1_x}}^{p_{0_x}} G_sigma(y) G_sigma(x) dx dy -// = 1/4 ("erf"_sigma(p_{1_x}) - "erf"_sigma(p_{0_x})) -// ("erf"_sigma(p_{1_y}) - "erf"_sigma(p_{0_y})) -float colorFromRect(vec2 p0, vec2 p1, float sigma) { - return (erfSigma(p1.x, sigma) - erfSigma(p0.x, sigma)) * - (erfSigma(p1.y, sigma) - erfSigma(p0.y, sigma)) / 4.0; -} - -// Returns the `x` coordinate on the ellipse with the given radii for the given `y` coordinate: -// -// "ellipsePoint"(y, y_0, a, b) = a sqrt(1 - ((y - y_0) / b)^2) -float ellipsePoint(float y, float y0, vec2 radii) { - float bStep = (y - y0) / radii.y; - return radii.x * sqrt(1.0 - bStep * bStep); -} - -// A helper function to compute the value that needs to be subtracted to accommodate the border -// corners. -// -// "colorCutout"_sigma(x_{0_l}, x_{0_r}, y_0, y_{min}, y_{max}, a, b) -// = int_{y_{min}}^{y_{max}} -// int_{x_{0_r} + "ellipsePoint"(y, y_0, a, b)}^{x_{0_r} + a} G_sigma(y) G_sigma(x) dx -// + int_{x_{0_l} - a}^{x_{0_l} - "ellipsePoint"(y, y_0, a, b)} G_sigma(y) G_sigma(x) -// dx dy -// = int_{y_{min}}^{y_{max}} 1/2 G_sigma(y) -// ("erf"_sigma(x_{0_r} + a) - "erf"_sigma(x_{0_r} + "ellipsePoint"(y, y_0, a, b)) + -// "erf"_sigma(x_{0_l} - "ellipsePoint"(y, y_0, a, b)) - "erf"_sigma(x_{0_l} - a)) -// -// with the outer integral evaluated numerically. -float colorCutoutGeneral(float x0l, - float x0r, - float y0, - float yMin, - float yMax, - vec2 radii, - float sigma) { - float sum = 0.0; - for (float y = yMin; y <= yMax; y += 1.0) { - float xEllipsePoint = ellipsePoint(y, y0, radii); - sum += gauss(y, sigma) * - (erfSigma(x0r + radii.x, sigma) - erfSigma(x0r + xEllipsePoint, sigma) + - erfSigma(x0l - xEllipsePoint, sigma) - erfSigma(x0l - radii.x, sigma)); - } - return sum / 2.0; -} - -// The value that needs to be subtracted to accommodate the top border corners. -float colorCutoutTop(float x0l, float x0r, float y0, vec2 radii, float sigma) { - return colorCutoutGeneral(x0l, x0r, y0, y0, y0 + radii.y, radii, sigma); -} - -// The value that needs to be subtracted to accommodate the bottom border corners. -float colorCutoutBottom(float x0l, float x0r, float y0, vec2 radii, float sigma) { - return colorCutoutGeneral(x0l, x0r, y0, y0 - radii.y, y0, radii, sigma); -} - -// The blurred color value for the point at `pos` with the top left corner of the box at -// `p_{0_"rect"}` and the bottom right corner of the box at `p_{1_"rect"}`. -float color(vec2 pos, vec2 p0Rect, vec2 p1Rect, vec2 radii, float sigma) { - // Compute the vector distances `p_0` and `p_1`. - vec2 p0 = p0Rect - pos, p1 = p1Rect - pos; - - // Compute the basic color `"colorFromRect"_sigma(p_0, p_1)`. This is all we have to do if - // the box is unrounded. - float cRect = colorFromRect(p0, p1, sigma); - if (radii.x == 0.0 || radii.y == 0.0) - return cRect; - - // Compute the inner corners of the box, taking border radii into account: `x_{0_l}`, - // `y_{0_t}`, `x_{0_r}`, and `y_{0_b}`. - float x0l = p0.x + radii.x; - float y0t = p1.y - radii.y; - float x0r = p1.x - radii.x; - float y0b = p0.y + radii.y; - - // Compute the final color: - // - // "colorFromRect"_sigma(p_0, p_1) - - // ("colorCutoutTop"_sigma(x_{0_l}, x_{0_r}, y_{0_t}, a, b) + - // "colorCutoutBottom"_sigma(x_{0_l}, x_{0_r}, y_{0_b}, a, b)) - float cCutoutTop = colorCutoutTop(x0l, x0r, y0t, radii, sigma); - float cCutoutBottom = colorCutoutBottom(x0l, x0r, y0b, radii, sigma); - return cRect - (cCutoutTop + cCutoutBottom); -} - -void main(void) { - vec2 pos = vPos.xy; - vec2 p0Rect = vBoxShadowRect.xy, p1Rect = vBoxShadowRect.zw; - vec2 radii = vBorderRadii.xy; - float sigma = vBlurRadius / 2.0; - float value = color(pos, p0Rect, p1Rect, radii, sigma); - - value = max(value, 0.0); - oFragColor = dither(vec4(vInverted == 1.0 ? 1.0 - value : value)); -} -#endif diff --git a/webrender/res/cs_clip_rectangle.glsl b/webrender/res/cs_clip_rectangle.glsl index d79ed066e9..0ca4662303 100644 --- a/webrender/res/cs_clip_rectangle.glsl +++ b/webrender/res/cs_clip_rectangle.glsl @@ -81,73 +81,35 @@ void main(void) { RectWithEndpoint clip_rect = to_rect_with_endpoint(local_rect); - vClipCenter_Radius_TL = vec4(clip_rect.p0 + clip.top_left.outer_inner_radius.xy, - clip.top_left.outer_inner_radius.xy); + vec2 r_tl = clip.top_left.outer_inner_radius.xy; + vec2 r_tr = clip.top_right.outer_inner_radius.xy; + vec2 r_br = clip.bottom_right.outer_inner_radius.xy; + vec2 r_bl = clip.bottom_left.outer_inner_radius.xy; - vClipCenter_Radius_TR = vec4(clip_rect.p1.x - clip.top_right.outer_inner_radius.x, - clip_rect.p0.y + clip.top_right.outer_inner_radius.y, - clip.top_right.outer_inner_radius.xy); + vClipCenter_Radius_TL = vec4(clip_rect.p0 + r_tl, r_tl); - vClipCenter_Radius_BR = vec4(clip_rect.p1 - clip.bottom_right.outer_inner_radius.xy, - clip.bottom_right.outer_inner_radius.xy); + vClipCenter_Radius_TR = vec4(clip_rect.p1.x - r_tr.x, + clip_rect.p0.y + r_tr.y, + r_tr); - vClipCenter_Radius_BL = vec4(clip_rect.p0.x + clip.bottom_left.outer_inner_radius.x, - clip_rect.p1.y - clip.bottom_left.outer_inner_radius.y, - clip.bottom_left.outer_inner_radius.xy); + vClipCenter_Radius_BR = vec4(clip_rect.p1 - r_br, r_br); + + vClipCenter_Radius_BL = vec4(clip_rect.p0.x + r_bl.x, + clip_rect.p1.y - r_bl.y, + r_bl); } #endif #ifdef WR_FRAGMENT_SHADER -float clip_against_ellipse_if_needed(vec2 pos, - float current_distance, - vec4 ellipse_center_radius, - vec2 sign_modifier) { - float ellipse_distance = distance_to_ellipse(pos - ellipse_center_radius.xy, - ellipse_center_radius.zw); - return mix(current_distance, - ellipse_distance, - all(lessThan(sign_modifier * pos, sign_modifier * ellipse_center_radius.xy))); -} - -float rounded_rect(vec2 pos) { - // Start with a negative value (means "inside") for all fragments that are not - // in a corner. If the fragment is in a corner, one of the clip_against_ellipse_if_needed - // calls below will update it. - float current_distance = -1.0; - - // Clip against each ellipse. - current_distance = clip_against_ellipse_if_needed(pos, - current_distance, - vClipCenter_Radius_TL, - vec2(1.0)); - - current_distance = clip_against_ellipse_if_needed(pos, - current_distance, - vClipCenter_Radius_TR, - vec2(-1.0, 1.0)); - - current_distance = clip_against_ellipse_if_needed(pos, - current_distance, - vClipCenter_Radius_BR, - vec2(-1.0)); - - current_distance = clip_against_ellipse_if_needed(pos, - current_distance, - vClipCenter_Radius_BL, - vec2(1.0, -1.0)); - - // Apply AA - // See comment in ps_border_corner about the choice of constants. - float aa_range = compute_aa_range(pos); - - return distance_aa(aa_range, current_distance); -} - void main(void) { float alpha = 1.f; vec2 local_pos = init_transform_fs(vPos, alpha); - float clip_alpha = rounded_rect(local_pos); + float clip_alpha = rounded_rect(local_pos, + vClipCenter_Radius_TL, + vClipCenter_Radius_TR, + vClipCenter_Radius_BR, + vClipCenter_Radius_BL); float combined_alpha = min(alpha, clip_alpha); diff --git a/webrender/res/cs_text_run.glsl b/webrender/res/cs_text_run.glsl index 772f8510f8..5dfcae87c2 100644 --- a/webrender/res/cs_text_run.glsl +++ b/webrender/res/cs_text_run.glsl @@ -18,16 +18,16 @@ void main(void) { int glyph_index = prim.user_data0; int resource_address = prim.user_data1; - int text_shadow_address = prim.user_data2; + int picture_address = prim.user_data2; - // Fetch the parent text-shadow for this primitive. This allows the code + // Fetch the owning picture for this primitive. This allows the code // below to normalize the glyph offsets relative to the original text // shadow rect, which is the union of all elements that make up this // text shadow. This allows the text shadow to be rendered at an // arbitrary location in a render target (provided by the render // task render_target_origin field). - PrimitiveGeometry shadow_geom = fetch_primitive_geometry(text_shadow_address); - TextShadow shadow = fetch_text_shadow(text_shadow_address + VECS_PER_PRIM_HEADER); + PrimitiveGeometry shadow_geom = fetch_primitive_geometry(picture_address); + Picture pic = fetch_picture(picture_address + VECS_PER_PRIM_HEADER); Glyph glyph = fetch_glyph(prim.specific_prim_address, glyph_index, @@ -41,7 +41,7 @@ void main(void) { vec2 size = (res.uv_rect.zw - res.uv_rect.xy) * res.scale; vec2 local_pos = glyph.offset + vec2(res.offset.x, -res.offset.y) / uDevicePixelRatio; vec2 origin = prim.task.render_target_origin + - uDevicePixelRatio * (local_pos + shadow.offset - shadow_geom.local_rect.p0); + uDevicePixelRatio * (local_pos + pic.offset - shadow_geom.local_rect.p0); vec4 local_rect = vec4(origin, size); vec2 texture_size = vec2(textureSize(sColor0, 0)); @@ -53,7 +53,7 @@ void main(void) { aPosition.xy); vUv = vec3(mix(st0, st1, aPosition.xy), res.layer); - vColor = shadow.color; + vColor = pic.color; gl_Position = uTransform * vec4(pos, 0.0, 1.0); } diff --git a/webrender/res/ellipse.glsl b/webrender/res/ellipse.glsl index 5d07669402..20486adad8 100644 --- a/webrender/res/ellipse.glsl +++ b/webrender/res/ellipse.glsl @@ -66,4 +66,55 @@ float distance_to_ellipse(vec2 p, vec2 radii) { } } +float clip_against_ellipse_if_needed( + vec2 pos, + float current_distance, + vec4 ellipse_center_radius, + vec2 sign_modifier +) { + float ellipse_distance = distance_to_ellipse(pos - ellipse_center_radius.xy, + ellipse_center_radius.zw); + + return mix(current_distance, + ellipse_distance, + all(lessThan(sign_modifier * pos, sign_modifier * ellipse_center_radius.xy))); +} + +float rounded_rect(vec2 pos, + vec4 clip_center_radius_tl, + vec4 clip_center_radius_tr, + vec4 clip_center_radius_br, + vec4 clip_center_radius_bl) { + // Start with a negative value (means "inside") for all fragments that are not + // in a corner. If the fragment is in a corner, one of the clip_against_ellipse_if_needed + // calls below will update it. + float current_distance = -1.0; + + // Clip against each ellipse. + current_distance = clip_against_ellipse_if_needed(pos, + current_distance, + clip_center_radius_tl, + vec2(1.0)); + + current_distance = clip_against_ellipse_if_needed(pos, + current_distance, + clip_center_radius_tr, + vec2(-1.0, 1.0)); + + current_distance = clip_against_ellipse_if_needed(pos, + current_distance, + clip_center_radius_br, + vec2(-1.0)); + + current_distance = clip_against_ellipse_if_needed(pos, + current_distance, + clip_center_radius_bl, + vec2(1.0, -1.0)); + + // Apply AA + // See comment in ps_border_corner about the choice of constants. + float aa_range = compute_aa_range(pos); + + return distance_aa(aa_range, current_distance); +} #endif diff --git a/webrender/res/prim_shared.glsl b/webrender/res/prim_shared.glsl index c5c6f5d926..edb7970168 100644 --- a/webrender/res/prim_shared.glsl +++ b/webrender/res/prim_shared.glsl @@ -659,15 +659,15 @@ Rectangle fetch_rectangle(int address) { return Rectangle(data); } -struct TextShadow { +struct Picture { vec4 color; vec2 offset; float blur_radius; }; -TextShadow fetch_text_shadow(int address) { +Picture fetch_picture(int address) { vec4 data[2] = fetch_from_resource_cache_2(address); - return TextShadow(data[0], data[1].xy, data[1].z); + return Picture(data[0], data[1].xy, data[1].z); } struct TextRun { @@ -692,23 +692,6 @@ Image fetch_image(int address) { return Image(data[0], data[1]); } -struct BoxShadow { - vec4 src_rect; - vec4 bs_rect; - vec4 color; - vec4 border_radius_edge_size_blur_radius_inverted; -}; - -BoxShadow fetch_boxshadow(int address) { - vec4 data[4] = fetch_from_resource_cache_4(address); - return BoxShadow(data[0], data[1], data[2], data[3]); -} - -BoxShadow fetch_boxshadow_direct(ivec2 address) { - vec4 data[4] = fetch_from_resource_cache_4_direct(address); - return BoxShadow(data[0], data[1], data[2], data[3]); -} - void write_clip(vec2 global_pos, ClipArea area) { vec2 texture_size = vec2(textureSize(sSharedCacheA8, 0).xy); vec2 uv = global_pos + area.task_bounds.xy - area.screen_origin_target_index.xy; diff --git a/webrender/res/ps_box_shadow.glsl b/webrender/res/ps_box_shadow.glsl deleted file mode 100644 index 5a9845218c..0000000000 --- a/webrender/res/ps_box_shadow.glsl +++ /dev/null @@ -1,73 +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 - -flat varying vec4 vColor; - -varying vec3 vUv; -flat varying vec2 vMirrorPoint; -flat varying vec4 vCacheUvRectCoords; - -#ifdef WR_VERTEX_SHADER -#define BS_HEADER_VECS 4 - -RectWithSize fetch_instance_geometry(int address) { - vec4 data = fetch_from_resource_cache_1(address); - return RectWithSize(data.xy, data.zw); -} - -void main(void) { - Primitive prim = load_primitive(); - BoxShadow bs = fetch_boxshadow(prim.specific_prim_address); - RectWithSize segment_rect = fetch_instance_geometry(prim.specific_prim_address + BS_HEADER_VECS + prim.user_data0); - - VertexInfo vi = write_vertex(segment_rect, - prim.local_clip_rect, - prim.z, - prim.layer, - prim.task, - prim.local_rect); - - RenderTaskData child_task = fetch_render_task(prim.user_data1); - vUv.z = child_task.data1.x; - - // Constant offsets to inset from bilinear filtering border. - vec2 patch_origin = child_task.data0.xy + vec2(1.0); - vec2 patch_size_device_pixels = child_task.data0.zw - vec2(2.0); - vec2 patch_size = patch_size_device_pixels / uDevicePixelRatio; - - vUv.xy = (vi.local_pos - prim.local_rect.p0) / patch_size; - vMirrorPoint = 0.5 * prim.local_rect.size / patch_size; - - vec2 texture_size = vec2(textureSize(sSharedCacheA8, 0)); - vCacheUvRectCoords = vec4(patch_origin, patch_origin + patch_size_device_pixels) / texture_size.xyxy; - - vColor = bs.color; - - write_clip(vi.screen_pos, prim.clip_area); -} -#endif - -#ifdef WR_FRAGMENT_SHADER -void main(void) { - vec4 clip_scale = vec4(1.0, 1.0, 1.0, do_clip()); - - // Mirror and stretch the box shadow corner over the entire - // primitives. - vec2 uv = vMirrorPoint - abs(vUv.xy - vMirrorPoint); - - // Ensure that we don't fetch texels outside the box - // shadow corner. This can happen, for example, when - // drawing the outer parts of an inset box shadow. - uv = clamp(uv, vec2(0.0), vec2(1.0)); - - // Map the unit UV to the actual UV rect in the cache. - uv = mix(vCacheUvRectCoords.xy, vCacheUvRectCoords.zw, uv); - - // Modulate the box shadow by the color. - float mask = texture(sSharedCacheA8, vec3(uv, vUv.z)).r; - oFragColor = clip_scale * dither(vColor * vec4(1.0, 1.0, 1.0, mask)); -} -#endif diff --git a/webrender/res/ps_cache_image.glsl b/webrender/res/ps_cache_image.glsl index 97cf8d9d79..f7fdefd36f 100644 --- a/webrender/res/ps_cache_image.glsl +++ b/webrender/res/ps_cache_image.glsl @@ -7,6 +7,10 @@ varying vec3 vUv; flat varying vec4 vUvBounds; +#if defined WR_FEATURE_ALPHA +flat varying vec4 vColor; +#endif + #ifdef WR_VERTEX_SHADER // Draw a cached primitive (e.g. a blurred text run) from the // target cache to the framebuffer, applying tile clip boundaries. @@ -24,7 +28,14 @@ void main(void) { RenderTaskData child_task = fetch_render_task(prim.user_data1); vUv.z = child_task.data1.x; - vec2 texture_size = vec2(textureSize(sCacheRGBA8, 0)); +#if defined WR_FEATURE_COLOR + vec2 texture_size = vec2(textureSize(sColor0, 0).xy); +#else + Picture pic = fetch_picture(prim.specific_prim_address); + + vec2 texture_size = vec2(textureSize(sColor1, 0).xy); + vColor = pic.color; +#endif vec2 uv0 = child_task.data0.xy; vec2 uv1 = (child_task.data0.xy + child_task.data0.zw); @@ -34,12 +45,32 @@ void main(void) { uv1 / texture_size, f); vUvBounds = vec4(uv0 + vec2(0.5), uv1 - vec2(0.5)) / texture_size.xyxy; + + write_clip(vi.screen_pos, prim.clip_area); } #endif #ifdef WR_FRAGMENT_SHADER void main(void) { vec2 uv = clamp(vUv.xy, vUvBounds.xy, vUvBounds.zw); - oFragColor = texture(sColor0, vec3(uv, vUv.z)); + +#if defined WR_FEATURE_COLOR + vec4 color = texture(sColor0, vec3(uv, vUv.z)); +#else + vec4 color = vColor * texture(sColor1, vec3(uv, vUv.z)).r; +#endif + + // Un-premultiply the color from sampling the gradient. + if (color.a > 0.0) { + color.rgb /= color.a; + + // Apply the clip mask + color.a = min(color.a, do_clip()); + + // Pre-multiply the result. + color.rgb *= color.a; + } + + oFragColor = color; } #endif diff --git a/webrender/res/ps_line.glsl b/webrender/res/ps_line.glsl index b907e512f1..1d04f24a90 100644 --- a/webrender/res/ps_line.glsl +++ b/webrender/res/ps_line.glsl @@ -98,19 +98,19 @@ void main(void) { } #ifdef WR_FEATURE_CACHE - int text_shadow_address = prim.user_data0; - PrimitiveGeometry shadow_geom = fetch_primitive_geometry(text_shadow_address); - TextShadow shadow = fetch_text_shadow(text_shadow_address + VECS_PER_PRIM_HEADER); + int picture_address = prim.user_data0; + PrimitiveGeometry picture_geom = fetch_primitive_geometry(picture_address); + Picture pic = fetch_picture(picture_address + VECS_PER_PRIM_HEADER); vec2 device_origin = prim.task.render_target_origin + - uDevicePixelRatio * (prim.local_rect.p0 + shadow.offset - shadow_geom.local_rect.p0); + uDevicePixelRatio * (prim.local_rect.p0 + pic.offset - picture_geom.local_rect.p0); vec2 device_size = uDevicePixelRatio * prim.local_rect.size; vec2 device_pos = mix(device_origin, device_origin + device_size, aPosition.xy); - vColor = shadow.color; + vColor = pic.color; vLocalPos = mix(prim.local_rect.p0, prim.local_rect.p0 + prim.local_rect.size, aPosition.xy); diff --git a/webrender/src/frame_builder.rs b/webrender/src/frame_builder.rs index a5e78b6c3d..642693e5ea 100644 --- a/webrender/src/frame_builder.rs +++ b/webrender/src/frame_builder.rs @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{BorderDetails, BorderDisplayItem, BorderRadius, BoxShadowClipMode, BuiltDisplayList}; -use api::{ClipAndScrollInfo, ClipId, ColorF}; +use api::{ComplexClipRegion, ClipAndScrollInfo, ClipId, ColorF}; use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintRect, DeviceUintSize}; use api::{ExtendMode, FilterOp, FontInstance, FontRenderMode}; use api::{GlyphInstance, GlyphOptions, GradientStop, HitTestFlags, HitTestItem, HitTestResult}; @@ -21,9 +21,9 @@ use euclid::{SideOffsets2D, TypedTransform3D, vec2, vec3}; use frame::FrameId; use gpu_cache::GpuCache; use internal_types::{FastHashMap, FastHashSet, HardwareCompositeOp}; -use picture::PicturePrimitive; +use picture::{PicturePrimitive}; use plane_split::{BspSplitter, Polygon, Splitter}; -use prim_store::{BoxShadowPrimitiveCpu, TexelRect, YuvImagePrimitiveCpu}; +use prim_store::{BrushPrimitive, TexelRect, YuvImagePrimitiveCpu}; use prim_store::{GradientPrimitiveCpu, ImagePrimitiveCpu, LinePrimitive, PrimitiveKind}; use prim_store::{PrimitiveContainer, PrimitiveIndex}; use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu}; @@ -35,10 +35,10 @@ use resource_cache::ResourceCache; use scene::ScenePipeline; use std::{mem, usize, f32, i32}; use tiling::{ClipScrollGroup, ClipScrollGroupIndex, CompositeOps, Frame}; -use tiling::{ContextIsolation, StackingContextIndex}; +use tiling::{ContextIsolation, RenderTargetKind, StackingContextIndex}; use tiling::{PackedLayer, PackedLayerIndex, PrimitiveFlags, PrimitiveRunCmd, RenderPass}; use tiling::{RenderTargetContext, ScrollbarPrimitive, StackingContext}; -use util::{self, RectHelpers, pack_as_float, recycle_vec, subtract_rect}; +use util::{self, pack_as_float, RectHelpers, recycle_vec}; /// Construct a polygon from stacking context boundaries. /// `anchor` here is an index that's going to be preserved in all the @@ -557,7 +557,7 @@ impl FrameBuilder { clip_and_scroll: ClipAndScrollInfo, info: &LayerPrimitiveInfo, ) { - let prim = PicturePrimitive::new_shadow(shadow); + let prim = PicturePrimitive::new_shadow(shadow, RenderTargetKind::Color); // Create an empty shadow primitive. Insert it into // the draw lists immediately so that it will be drawn @@ -1262,47 +1262,10 @@ impl FrameBuilder { } } - pub fn fill_box_shadow_rect( - &mut self, - clip_and_scroll: ClipAndScrollInfo, - info: &LayerPrimitiveInfo, - bs_rect: LayerRect, - color: &ColorF, - border_radius: f32, - clip_mode: BoxShadowClipMode, - ) { - // We can draw a rectangle instead with the proper border radius clipping. - let (bs_clip_mode, rect_to_draw) = match clip_mode { - BoxShadowClipMode::Outset | BoxShadowClipMode::None => (ClipMode::Clip, bs_rect), - BoxShadowClipMode::Inset => (ClipMode::ClipOut, info.rect), - }; - - let box_clip_mode = !bs_clip_mode; - - // Clip the inside and then the outside of the box. - let border_radius = BorderRadius::uniform(border_radius); - let extra_clips = vec![ - ClipSource::RoundedRectangle(bs_rect, border_radius, bs_clip_mode), - ClipSource::RoundedRectangle(info.rect, border_radius, box_clip_mode), - ]; - - let prim = RectanglePrimitive { color: *color }; - - let mut info = info.clone(); - info.rect = rect_to_draw; - - self.add_primitive( - clip_and_scroll, - &info, - extra_clips, - PrimitiveContainer::Rectangle(prim), - ); - } - pub fn add_box_shadow( &mut self, clip_and_scroll: ClipAndScrollInfo, - info: &LayerPrimitiveInfo, + prim_info: &LayerPrimitiveInfo, box_offset: &LayerVector2D, color: &ColorF, blur_radius: f32, @@ -1314,162 +1277,152 @@ impl FrameBuilder { return; } - // The local space box shadow rect. It is the element rect - // translated by the box shadow offset and inflated by the - // box shadow spread. - let inflate_amount = match clip_mode { - BoxShadowClipMode::Outset | BoxShadowClipMode::None => spread_radius, - BoxShadowClipMode::Inset => -spread_radius, + let spread_amount = match clip_mode { + BoxShadowClipMode::Outset => { + spread_radius + } + BoxShadowClipMode::Inset => { + -spread_radius + } }; - let bs_rect = info.rect - .translate(box_offset) - .inflate(inflate_amount, inflate_amount); - // If we have negative inflate amounts. - // Have to explicitly check this since euclid::TypedRect relies on negative rects - let bs_rect_empty = bs_rect.size.width <= 0.0 || bs_rect.size.height <= 0.0; - - // Just draw a rectangle - if (blur_radius == 0.0 && spread_radius == 0.0 && clip_mode == BoxShadowClipMode::None) || - bs_rect_empty - { - self.add_solid_rectangle(clip_and_scroll, info, color, PrimitiveFlags::None); - return; - } + // Adjust the shadow box radius as per: + // https://drafts.csswg.org/css-backgrounds-3/#shadow-shape + let sharpness_scale = if border_radius < spread_radius { + let r = border_radius / spread_amount; + 1.0 + (r - 1.0) * (r - 1.0) * (r - 1.0) + } else { + 1.0 + }; + let shadow_radius = (border_radius + spread_amount * sharpness_scale).max(0.0); + let shadow_rect = prim_info.rect + .translate(box_offset) + .inflate(spread_amount, spread_amount); + + if blur_radius == 0.0 { + let mut clips = Vec::new(); + + let fast_info = match clip_mode { + BoxShadowClipMode::Outset => { + // TODO(gw): Add a fast path for ClipOut + zero border radius! + clips.push(ClipSource::RoundedRectangle( + prim_info.rect, + BorderRadius::uniform(border_radius), + ClipMode::ClipOut + )); + + LayerPrimitiveInfo::with_clip( + shadow_rect, + LocalClip::RoundedRect( + shadow_rect, + ComplexClipRegion::new(shadow_rect, BorderRadius::uniform(shadow_radius)), + ), + ) + } + BoxShadowClipMode::Inset => { + clips.push(ClipSource::RoundedRectangle( + shadow_rect, + BorderRadius::uniform(shadow_radius), + ClipMode::ClipOut + )); - if blur_radius == 0.0 && border_radius != 0.0 { - self.fill_box_shadow_rect( + LayerPrimitiveInfo::with_clip( + prim_info.rect, + LocalClip::RoundedRect( + prim_info.rect, + ComplexClipRegion::new(prim_info.rect, BorderRadius::uniform(border_radius)), + ), + ) + } + }; + + self.add_primitive( clip_and_scroll, - info, - bs_rect, - color, - border_radius, - clip_mode, + &fast_info, + clips, + PrimitiveContainer::Rectangle(RectanglePrimitive { + color: *color, + }), ); - return; - } + } else { + let shadow = Shadow { + blur_radius, + color: *color, + offset: LayerVector2D::zero(), + }; - // Get the outer rectangle, based on the blur radius. - let outside_edge_size = 2.0 * blur_radius; - let inside_edge_size = outside_edge_size.max(border_radius); - let edge_size = outside_edge_size + inside_edge_size; - let outer_rect = bs_rect.inflate(outside_edge_size, outside_edge_size); - - // Box shadows are often used for things like text underline and other - // simple primitives, so we want to draw these simple cases with the - // solid rectangle shader wherever possible, to avoid invoking the - // expensive box-shadow shader. - enum BoxShadowKind { - Simple(Vec), // Can be drawn via simple rectangles only - Shadow(Vec), // Requires the full box-shadow code path - } + let blur_offset = 2.0 * blur_radius; + let mut extra_clips = vec![]; + let mut pic_prim = PicturePrimitive::new_shadow(shadow, RenderTargetKind::Alpha); - let shadow_kind = match clip_mode { - BoxShadowClipMode::Outset | BoxShadowClipMode::None => { - // If a border radius is set, we need to draw inside - // the original box in order to draw where the border - // corners are. A clip-out mask applied below will - // ensure that we don't draw on the box itself. - let inner_box_bounds = info.rect.inflate(-border_radius, -border_radius); - // For outset shadows, subtracting the element rectangle - // from the outer rectangle gives the rectangles we need - // to draw. In the simple case (no blur radius), we can - // just draw these as solid colors. - let mut rects = Vec::new(); - subtract_rect(&outer_rect, &inner_box_bounds, &mut rects); - if edge_size == 0.0 { - BoxShadowKind::Simple(rects) - } else { - BoxShadowKind::Shadow(rects) - } - } - BoxShadowClipMode::Inset => { - // For inset shadows, in the simple case (no blur) we - // can draw the shadow area by subtracting the box - // shadow rect from the element rect (since inset box - // shadows never extend past the element rect). However, - // in the case of an inset box shadow with blur, we - // currently just draw the box shadow over the entire - // rect. The opaque parts of the shadow (past the outside - // edge of the box-shadow) are handled by the shadow - // shader. - // TODO(gw): We should be able to optimize the complex - // inset shadow case to touch fewer pixels. We - // can probably calculate the inner rect that - // can't be affected, and subtract that from - // the element rect? - let mut rects = Vec::new(); - if edge_size == 0.0 { - subtract_rect(&info.rect, &bs_rect, &mut rects); - BoxShadowKind::Simple(rects) - } else { - rects.push(info.rect); - BoxShadowKind::Shadow(rects) - } - } - }; + let pic_info = match clip_mode { + BoxShadowClipMode::Outset => { + let brush_prim = BrushPrimitive { + clip_mode: ClipMode::Clip, + radius: shadow_radius, + }; - match shadow_kind { - BoxShadowKind::Simple(rects) => for rect in &rects { - let mut info = info.clone(); - info.rect = *rect; - self.add_solid_rectangle(clip_and_scroll, &info, color, PrimitiveFlags::None) - }, - BoxShadowKind::Shadow(rects) => { - assert!(blur_radius > 0.0); - if clip_mode == BoxShadowClipMode::Inset { - self.fill_box_shadow_rect( + let brush_rect = LayerRect::new(LayerPoint::new(blur_offset, blur_offset), + shadow_rect.size); + + let brush_info = LayerPrimitiveInfo::new(brush_rect); + + let brush_prim_index = self.create_primitive( clip_and_scroll, - info, - bs_rect, - color, - border_radius, - clip_mode, + &brush_info, + Vec::new(), + PrimitiveContainer::Brush(brush_prim), ); + + pic_prim.add_primitive(brush_prim_index, clip_and_scroll); + + extra_clips.push(ClipSource::RoundedRectangle( + prim_info.rect, + BorderRadius::uniform(border_radius), + ClipMode::ClipOut, + )); + + let pic_rect = shadow_rect.inflate(blur_offset, blur_offset); + LayerPrimitiveInfo::new(pic_rect) } + BoxShadowClipMode::Inset => { + let brush_prim = BrushPrimitive { + clip_mode: ClipMode::ClipOut, + radius: shadow_radius, + }; - let inverted = match clip_mode { - BoxShadowClipMode::Outset | BoxShadowClipMode::None => 0.0, - BoxShadowClipMode::Inset => 1.0, - }; + let mut brush_rect = shadow_rect; + brush_rect.origin.x = brush_rect.origin.x - prim_info.rect.origin.x + blur_offset; + brush_rect.origin.y = brush_rect.origin.y - prim_info.rect.origin.y + blur_offset; - // Outset box shadows with border radius - // need a clip out of the center box. - let extra_clip_mode = match clip_mode { - BoxShadowClipMode::Outset | BoxShadowClipMode::None => ClipMode::ClipOut, - BoxShadowClipMode::Inset => ClipMode::Clip, - }; + let brush_info = LayerPrimitiveInfo::new(brush_rect); + + let brush_prim_index = self.create_primitive( + clip_and_scroll, + &brush_info, + Vec::new(), + PrimitiveContainer::Brush(brush_prim), + ); + + pic_prim.add_primitive(brush_prim_index, clip_and_scroll); - let mut extra_clips = Vec::new(); - if border_radius >= 0.0 { extra_clips.push(ClipSource::RoundedRectangle( - info.rect, + prim_info.rect, BorderRadius::uniform(border_radius), - extra_clip_mode, + ClipMode::Clip, )); - } - let prim_cpu = BoxShadowPrimitiveCpu { - src_rect: info.rect, - bs_rect, - color: *color, - blur_radius, - border_radius, - edge_size, - inverted, - rects, - render_task_id: None, - }; + let pic_rect = prim_info.rect.inflate(blur_offset, blur_offset); + LayerPrimitiveInfo::with_clip_rect(pic_rect, prim_info.rect) + } + }; - let mut info = info.clone(); - info.rect = outer_rect; - self.add_primitive( - clip_and_scroll, - &info, - extra_clips, - PrimitiveContainer::BoxShadow(prim_cpu), - ); - } + self.add_primitive( + clip_and_scroll, + &pic_info, + extra_clips, + PrimitiveContainer::Picture(pic_prim), + ); } } @@ -2097,10 +2050,13 @@ impl FrameBuilder { match *filter { FilterOp::Blur(blur_radius) => { let blur_radius = device_length(blur_radius, device_pixel_ratio); + render_tasks.get_mut(current_task_id) + .inflate(blur_radius.0); let blur_render_task = RenderTask::new_blur( blur_radius, current_task_id, render_tasks, + RenderTargetKind::Color, ); let blur_render_task_id = render_tasks.add(blur_render_task); let item = AlphaRenderItem::HardwareComposite( diff --git a/webrender/src/gpu_types.rs b/webrender/src/gpu_types.rs index fae1a02478..b8e129a6f7 100644 --- a/webrender/src/gpu_types.rs +++ b/webrender/src/gpu_types.rs @@ -17,14 +17,6 @@ impl From for PackedLayerAddress { } } -// Instance structure for box shadows being drawn into target cache. -#[derive(Debug)] -#[repr(C)] -pub struct BoxShadowCacheInstance { - pub prim_address: GpuCacheAddress, - pub task_index: RenderTaskAddress, -} - #[repr(i32)] #[derive(Debug)] pub enum BlurDirection { @@ -145,3 +137,38 @@ impl From for PrimitiveInstance { } } } + +#[repr(C)] +pub struct BrushInstance { + picture_address: RenderTaskAddress, + prim_address: GpuCacheAddress, +} + +impl BrushInstance { + pub fn new( + picture_address: RenderTaskAddress, + prim_address: GpuCacheAddress + ) -> BrushInstance { + BrushInstance { + picture_address, + prim_address, + } + } +} + +impl From for PrimitiveInstance { + fn from(instance: BrushInstance) -> PrimitiveInstance { + PrimitiveInstance { + data: [ + instance.picture_address.0 as i32, + instance.prim_address.as_int(), + 0, + 0, + 0, + 0, + 0, + 0, + ] + } + } +} diff --git a/webrender/src/picture.rs b/webrender/src/picture.rs index 45f34fe69b..c8cb49e49d 100644 --- a/webrender/src/picture.rs +++ b/webrender/src/picture.rs @@ -5,6 +5,7 @@ use api::{ClipAndScrollInfo, Shadow}; use prim_store::PrimitiveIndex; use render_task::RenderTaskId; +use tiling::RenderTargetKind; /* A picture represents a dynamically rendered image. It consists of: @@ -36,6 +37,7 @@ pub struct PicturePrimitive { pub prim_runs: Vec, pub composite_op: CompositeOp, pub render_task_id: Option, + pub kind: RenderTargetKind, // TODO(gw): Add a mode that specifies if this // picture should be rasterized in @@ -43,11 +45,15 @@ pub struct PicturePrimitive { } impl PicturePrimitive { - pub fn new_shadow(shadow: Shadow) -> PicturePrimitive { + pub fn new_shadow( + shadow: Shadow, + kind: RenderTargetKind, + ) -> PicturePrimitive { PicturePrimitive { prim_runs: Vec::new(), composite_op: CompositeOp::Shadow(shadow), render_task_id: None, + kind, } } diff --git a/webrender/src/prim_store.rs b/webrender/src/prim_store.rs index 5a507ef7e7..fc11ad1c98 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -7,10 +7,8 @@ use api::{DevicePoint, ExtendMode, FontInstance, FontRenderMode, GlyphInstance, use api::{GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag, LayerPoint, LayerRect}; use api::{LayerSize, LayerVector2D, LineOrientation, LineStyle}; use api::{TileOffset, YuvColorSpace, YuvFormat, device_length}; -use app_units::Au; use border::BorderCornerInstance; use clip::{ClipMode, ClipSourcesHandle, ClipStore, Geometry}; -use euclid::Size2D; use frame_builder::PrimitiveContext; use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest, ToGpuBlocks}; @@ -111,9 +109,9 @@ pub enum PrimitiveKind { AlignedGradient, AngleGradient, RadialGradient, - BoxShadow, Line, Picture, + Brush, } impl GpuCacheHandle { @@ -166,6 +164,23 @@ impl ToGpuBlocks for RectanglePrimitive { } } +#[derive(Debug)] +pub struct BrushPrimitive { + pub clip_mode: ClipMode, + pub radius: f32, +} + +impl ToGpuBlocks for BrushPrimitive { + fn write_gpu_blocks(&self, mut request: GpuDataRequest) { + request.push([ + self.clip_mode as u32 as f32, + self.radius, + 0.0, + 0.0 + ]); + } +} + #[derive(Debug, Clone)] #[repr(C)] pub struct LinePrimitive { @@ -232,46 +247,6 @@ impl ToGpuBlocks for BorderPrimitiveCpu { } } -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub struct BoxShadowPrimitiveCacheKey { - pub shadow_rect_size: Size2D, - pub border_radius: Au, - pub blur_radius: Au, - pub inverted: bool, -} - -#[derive(Debug)] -pub struct BoxShadowPrimitiveCpu { - // todo(gw): generate on demand - // gpu data - pub src_rect: LayerRect, - pub bs_rect: LayerRect, - pub color: ColorF, - pub border_radius: f32, - pub edge_size: f32, - pub blur_radius: f32, - pub inverted: f32, - pub rects: Vec, - pub render_task_id: Option, -} - -impl ToGpuBlocks for BoxShadowPrimitiveCpu { - fn write_gpu_blocks(&self, mut request: GpuDataRequest) { - request.push(self.src_rect); - request.push(self.bs_rect); - request.push(self.color); - request.push([ - self.border_radius, - self.edge_size, - self.blur_radius, - self.inverted, - ]); - for &rect in &self.rects { - request.push(rect); - } - } -} - #[derive(Debug)] pub struct GradientPrimitiveCpu { pub stops_range: ItemRange, @@ -805,14 +780,15 @@ pub enum PrimitiveContainer { AlignedGradient(GradientPrimitiveCpu), AngleGradient(GradientPrimitiveCpu), RadialGradient(RadialGradientPrimitiveCpu), - BoxShadow(BoxShadowPrimitiveCpu), Picture(PicturePrimitive), Line(LinePrimitive), + Brush(BrushPrimitive), } pub struct PrimitiveStore { /// CPU side information only. pub cpu_rectangles: Vec, + pub cpu_brushes: Vec, pub cpu_text_runs: Vec, pub cpu_pictures: Vec, pub cpu_images: Vec, @@ -821,7 +797,6 @@ pub struct PrimitiveStore { pub cpu_radial_gradients: Vec, pub cpu_metadata: Vec, pub cpu_borders: Vec, - pub cpu_box_shadows: Vec, pub cpu_lines: Vec, } @@ -830,6 +805,7 @@ impl PrimitiveStore { PrimitiveStore { cpu_metadata: Vec::new(), cpu_rectangles: Vec::new(), + cpu_brushes: Vec::new(), cpu_text_runs: Vec::new(), cpu_pictures: Vec::new(), cpu_images: Vec::new(), @@ -837,7 +813,6 @@ impl PrimitiveStore { cpu_gradients: Vec::new(), cpu_radial_gradients: Vec::new(), cpu_borders: Vec::new(), - cpu_box_shadows: Vec::new(), cpu_lines: Vec::new(), } } @@ -846,6 +821,7 @@ impl PrimitiveStore { PrimitiveStore { cpu_metadata: recycle_vec(self.cpu_metadata), cpu_rectangles: recycle_vec(self.cpu_rectangles), + cpu_brushes: recycle_vec(self.cpu_brushes), cpu_text_runs: recycle_vec(self.cpu_text_runs), cpu_pictures: recycle_vec(self.cpu_pictures), cpu_images: recycle_vec(self.cpu_images), @@ -853,7 +829,6 @@ impl PrimitiveStore { cpu_gradients: recycle_vec(self.cpu_gradients), cpu_radial_gradients: recycle_vec(self.cpu_radial_gradients), cpu_borders: recycle_vec(self.cpu_borders), - cpu_box_shadows: recycle_vec(self.cpu_box_shadows), cpu_lines: recycle_vec(self.cpu_lines), } } @@ -897,6 +872,18 @@ impl PrimitiveStore { metadata } + PrimitiveContainer::Brush(brush) => { + let metadata = PrimitiveMetadata { + opacity: PrimitiveOpacity::translucent(), + prim_kind: PrimitiveKind::Brush, + cpu_prim_index: SpecificPrimitiveIndex(self.cpu_brushes.len()), + ..base_metadata + }; + + self.cpu_brushes.push(brush); + + metadata + } PrimitiveContainer::Line(line) => { let metadata = PrimitiveMetadata { opacity: PrimitiveOpacity::translucent(), @@ -998,17 +985,6 @@ impl PrimitiveStore { self.cpu_radial_gradients.push(radial_gradient_cpu); metadata } - PrimitiveContainer::BoxShadow(box_shadow) => { - let metadata = PrimitiveMetadata { - opacity: PrimitiveOpacity::translucent(), - prim_kind: PrimitiveKind::BoxShadow, - cpu_prim_index: SpecificPrimitiveIndex(self.cpu_box_shadows.len()), - ..base_metadata - }; - - self.cpu_box_shadows.push(box_shadow); - metadata - } }; self.cpu_metadata.push(metadata); @@ -1030,10 +1006,6 @@ impl PrimitiveStore { let metadata = &self.cpu_metadata[prim_index.0]; let render_task_id = match metadata.prim_kind { - PrimitiveKind::BoxShadow => { - let box_shadow = &self.cpu_box_shadows[metadata.cpu_prim_index.0]; - box_shadow.render_task_id - } PrimitiveKind::Picture => { let picture = &self.cpu_pictures[metadata.cpu_prim_index.0]; picture.render_task_id @@ -1046,7 +1018,8 @@ impl PrimitiveStore { PrimitiveKind::Border | PrimitiveKind::AngleGradient | PrimitiveKind::RadialGradient | - PrimitiveKind::Line => None, + PrimitiveKind::Line | + PrimitiveKind::Brush => None, }; if let Some(render_task_id) = render_task_id { @@ -1073,45 +1046,6 @@ impl PrimitiveStore { let metadata = &mut self.cpu_metadata[prim_index.0]; match metadata.prim_kind { PrimitiveKind::Rectangle | PrimitiveKind::Border | PrimitiveKind::Line => {} - PrimitiveKind::BoxShadow => { - // TODO(gw): Account for zoom factor! - // Here, we calculate the size of the patch required in order - // to create the box shadow corner. First, scale it by the - // device pixel ratio since the cache shader expects vertices - // in device space. The shader adds a 1-pixel border around - // the patch, in order to prevent bilinear filter artifacts as - // the patch is clamped / mirrored across the box shadow rect. - let box_shadow = &mut self.cpu_box_shadows[metadata.cpu_prim_index.0]; - let edge_size = box_shadow.edge_size.ceil() * prim_context.device_pixel_ratio; - let edge_size = edge_size as i32 + 2; // Account for bilinear filtering - let cache_size = DeviceIntSize::new(edge_size, edge_size); - - let cache_key = BoxShadowPrimitiveCacheKey { - blur_radius: Au::from_f32_px(box_shadow.blur_radius), - border_radius: Au::from_f32_px(box_shadow.border_radius), - inverted: box_shadow.inverted != 0.0, - shadow_rect_size: Size2D::new( - Au::from_f32_px(box_shadow.bs_rect.size.width), - Au::from_f32_px(box_shadow.bs_rect.size.height), - ), - }; - - // Create a render task for this box shadow primitive. This renders a small - // portion of the box shadow to a render target. That portion is then - // stretched over the actual primitive rect by the box shadow primitive - // shader, to reduce the number of pixels that the expensive box - // shadow shader needs to run on. - // TODO(gw): In the future, we can probably merge the box shadow - // primitive (stretch) shader with the generic cached primitive shader. - let render_task = RenderTask::new_box_shadow( - cache_key, - cache_size, - prim_index - ); - - // ignore the new task if we are in a dependency context - box_shadow.render_task_id = render_tasks.map(|rt| rt.add(render_task)); - } PrimitiveKind::Picture => { let picture = &mut self.cpu_pictures[metadata.cpu_prim_index.0]; @@ -1129,10 +1063,18 @@ impl PrimitiveStore { // ignore new tasks if we are in a dependency context picture.render_task_id = render_tasks.map(|rt| { - let picture_task = RenderTask::new_picture(cache_size, prim_index); + let picture_task = RenderTask::new_picture( + cache_size, + prim_index, + picture.kind, + ); let picture_task_id = rt.add(picture_task); - let render_task = - RenderTask::new_blur(blur_radius, picture_task_id, rt); + let render_task = RenderTask::new_blur( + blur_radius, + picture_task_id, + rt, + picture.kind + ); rt.add(render_task) }); } @@ -1184,7 +1126,8 @@ impl PrimitiveStore { } PrimitiveKind::AlignedGradient | PrimitiveKind::AngleGradient | - PrimitiveKind::RadialGradient => {} + PrimitiveKind::RadialGradient | + PrimitiveKind::Brush => {} } // Mark this GPU resource as required for this frame. @@ -1205,10 +1148,6 @@ impl PrimitiveStore { let border = &self.cpu_borders[metadata.cpu_prim_index.0]; border.write_gpu_blocks(request); } - PrimitiveKind::BoxShadow => { - let box_shadow = &self.cpu_box_shadows[metadata.cpu_prim_index.0]; - box_shadow.write_gpu_blocks(request); - } PrimitiveKind::Image => { let image = &self.cpu_images[metadata.cpu_prim_index.0]; image.write_gpu_blocks(request); @@ -1244,6 +1183,10 @@ impl PrimitiveStore { 0.0, ]); } + PrimitiveKind::Brush => { + let brush = &self.cpu_brushes[metadata.cpu_prim_index.0]; + brush.write_gpu_blocks(request); + } } } } diff --git a/webrender/src/render_task.rs b/webrender/src/render_task.rs index 70a37a4aa7..9d397ca7b7 100644 --- a/webrender/src/render_task.rs +++ b/webrender/src/render_task.rs @@ -9,7 +9,7 @@ use clip::{ClipSource, ClipSourcesWeakHandle, ClipStore}; use clip_scroll_tree::CoordinateSystemId; use gpu_cache::GpuCacheHandle; use internal_types::HardwareCompositeOp; -use prim_store::{BoxShadowPrimitiveCacheKey, PrimitiveIndex}; +use prim_store::PrimitiveIndex; use std::{cmp, usize, f32, i32}; use std::rc::Rc; use tiling::{ClipScrollGroupIndex, PackedLayerIndex, RenderPass, RenderTargetIndex}; @@ -139,8 +139,6 @@ impl RenderTaskTree { #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub enum RenderTaskKey { - /// Draw this box shadow to a cache target. - BoxShadow(BoxShadowPrimitiveCacheKey), /// Draw the alpha mask for a shared clip. CacheMask(ClipId), } @@ -256,6 +254,18 @@ pub struct CacheMaskTask { pub coordinate_system_id: CoordinateSystemId, } +#[derive(Debug)] +pub struct PictureTask { + pub prim_index: PrimitiveIndex, + pub target_kind: RenderTargetKind, +} + +#[derive(Debug)] +pub struct BlurTask { + pub blur_radius: DeviceIntLength, + pub target_kind: RenderTargetKind, +} + #[derive(Debug)] pub struct RenderTaskData { pub data: [f32; FLOATS_PER_RENDER_TASK_INFO], @@ -264,11 +274,10 @@ pub struct RenderTaskData { #[derive(Debug)] pub enum RenderTaskKind { Alpha(AlphaRenderTask), - Picture(PrimitiveIndex), - BoxShadow(PrimitiveIndex), + Picture(PictureTask), CacheMask(CacheMaskTask), - VerticalBlur(DeviceIntLength), - HorizontalBlur(DeviceIntLength), + VerticalBlur(BlurTask), + HorizontalBlur(BlurTask), Readback(DeviceIntRect), Alias(RenderTaskId), } @@ -307,25 +316,19 @@ impl RenderTask { Self::new_alpha_batch(rect.origin, location, frame_output_pipeline_id) } - pub fn new_picture(size: DeviceIntSize, prim_index: PrimitiveIndex) -> RenderTask { - RenderTask { - cache_key: None, - children: Vec::new(), - location: RenderTaskLocation::Dynamic(None, size), - kind: RenderTaskKind::Picture(prim_index), - } - } - - pub fn new_box_shadow( - key: BoxShadowPrimitiveCacheKey, + pub fn new_picture( size: DeviceIntSize, prim_index: PrimitiveIndex, + target_kind: RenderTargetKind, ) -> RenderTask { RenderTask { - cache_key: Some(RenderTaskKey::BoxShadow(key)), + cache_key: None, children: Vec::new(), location: RenderTaskLocation::Dynamic(None, size), - kind: RenderTaskKind::BoxShadow(prim_index), + kind: RenderTaskKind::Picture(PictureTask { + prim_index, + target_kind, + }), } } @@ -439,16 +442,18 @@ impl RenderTask { blur_radius: DeviceIntLength, src_task_id: RenderTaskId, render_tasks: &mut RenderTaskTree, + target_kind: RenderTargetKind, ) -> RenderTask { - let src_size = render_tasks.get(src_task_id).get_dynamic_size(); - - let blur_target_size = src_size + DeviceIntSize::new(2 * blur_radius.0, 2 * blur_radius.0); + let blur_target_size = render_tasks.get(src_task_id).get_dynamic_size(); let blur_task_v = RenderTask { cache_key: None, children: vec![src_task_id], location: RenderTaskLocation::Dynamic(None, blur_target_size), - kind: RenderTaskKind::VerticalBlur(blur_radius), + kind: RenderTaskKind::VerticalBlur(BlurTask { + blur_radius, + target_kind, + }), }; let blur_task_v_id = render_tasks.add(blur_task_v); @@ -457,7 +462,10 @@ impl RenderTask { cache_key: None, children: vec![blur_task_v_id], location: RenderTaskLocation::Dynamic(None, blur_target_size), - kind: RenderTaskKind::HorizontalBlur(blur_radius), + kind: RenderTaskKind::HorizontalBlur(BlurTask { + blur_radius, + target_kind, + }), }; blur_task_h @@ -467,7 +475,6 @@ impl RenderTask { match self.kind { RenderTaskKind::Alpha(ref mut task) => task, RenderTaskKind::Picture(..) | - RenderTaskKind::BoxShadow(..) | RenderTaskKind::CacheMask(..) | RenderTaskKind::VerticalBlur(..) | RenderTaskKind::Readback(..) | @@ -480,7 +487,6 @@ impl RenderTask { match self.kind { RenderTaskKind::Alpha(ref task) => task, RenderTaskKind::Picture(..) | - RenderTaskKind::BoxShadow(..) | RenderTaskKind::CacheMask(..) | RenderTaskKind::VerticalBlur(..) | RenderTaskKind::Readback(..) | @@ -521,7 +527,7 @@ impl RenderTask { ], } } - RenderTaskKind::Picture(..) | RenderTaskKind::BoxShadow(..) => { + RenderTaskKind::Picture(..) => { let (target_rect, target_index) = self.get_target_rect(); RenderTaskData { data: [ @@ -559,8 +565,8 @@ impl RenderTask { ], } } - RenderTaskKind::VerticalBlur(blur_radius) | - RenderTaskKind::HorizontalBlur(blur_radius) => { + RenderTaskKind::VerticalBlur(ref task_info) | + RenderTaskKind::HorizontalBlur(ref task_info) => { let (target_rect, target_index) = self.get_target_rect(); RenderTaskData { data: [ @@ -569,7 +575,7 @@ impl RenderTask { target_rect.size.width as f32, target_rect.size.height as f32, target_index.0 as f32, - blur_radius.0 as f32, + task_info.blur_radius.0 as f32, 0.0, 0.0, 0.0, @@ -602,6 +608,33 @@ impl RenderTask { } } + pub fn inflate(&mut self, device_radius: i32) { + match self.kind { + RenderTaskKind::Alpha(ref mut info) => { + match self.location { + RenderTaskLocation::Fixed => { + panic!("bug: inflate only supported for dynamic tasks"); + } + RenderTaskLocation::Dynamic(_, ref mut size) => { + size.width += device_radius * 2; + size.height += device_radius * 2; + info.screen_origin.x -= device_radius; + info.screen_origin.y -= device_radius; + } + } + } + + RenderTaskKind::Readback(..) | + RenderTaskKind::CacheMask(..) | + RenderTaskKind::VerticalBlur(..) | + RenderTaskKind::HorizontalBlur(..) | + RenderTaskKind::Picture(..) | + RenderTaskKind::Alias(..) => { + panic!("bug: inflate only supported for alpha tasks"); + } + } + } + pub fn get_dynamic_size(&self) -> DeviceIntSize { match self.location { RenderTaskLocation::Fixed => DeviceIntSize::zero(), @@ -623,15 +656,21 @@ impl RenderTask { pub fn target_kind(&self) -> RenderTargetKind { match self.kind { RenderTaskKind::Alpha(..) | - RenderTaskKind::Picture(..) | - RenderTaskKind::VerticalBlur(..) | - RenderTaskKind::Readback(..) | - RenderTaskKind::HorizontalBlur(..) => RenderTargetKind::Color, + RenderTaskKind::Readback(..) => RenderTargetKind::Color, - RenderTaskKind::CacheMask(..) | RenderTaskKind::BoxShadow(..) => { + RenderTaskKind::CacheMask(..) => { RenderTargetKind::Alpha } + RenderTaskKind::VerticalBlur(ref task_info) | + RenderTaskKind::HorizontalBlur(ref task_info) => { + task_info.target_kind + } + + RenderTaskKind::Picture(ref task_info) => { + task_info.target_kind + } + RenderTaskKind::Alias(..) => { panic!("BUG: target_kind() called on invalidated task"); } @@ -652,7 +691,7 @@ impl RenderTask { RenderTaskKind::Readback(..) | RenderTaskKind::HorizontalBlur(..) => false, - RenderTaskKind::CacheMask(..) | RenderTaskKind::BoxShadow(..) => true, + RenderTaskKind::CacheMask(..) => true, RenderTaskKind::Alias(..) => { panic!("BUG: is_shared() called on aliased task"); diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index 1a199b6318..d76ada0831 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -67,8 +67,8 @@ use util::TransformedRectKind; pub const MAX_VERTEX_TEXTURE_WIDTH: usize = 1024; -const GPU_TAG_CACHE_BOX_SHADOW: GpuProfileTag = GpuProfileTag { - label: "C_BoxShadow", +const GPU_TAG_BRUSH_MASK: GpuProfileTag = GpuProfileTag { + label: "B_Mask", color: debug_colors::BLACK, }; const GPU_TAG_CACHE_CLIP: GpuProfileTag = GpuProfileTag { @@ -139,10 +139,6 @@ const GPU_TAG_PRIM_RADIAL_GRADIENT: GpuProfileTag = GpuProfileTag { label: "RadialGradient", color: debug_colors::LIGHTPINK, }; -const GPU_TAG_PRIM_BOX_SHADOW: GpuProfileTag = GpuProfileTag { - label: "BoxShadow", - color: debug_colors::CYAN, -}; const GPU_TAG_PRIM_BORDER_CORNER: GpuProfileTag = GpuProfileTag { label: "BorderCorner", color: debug_colors::DARKSLATEGREY, @@ -194,8 +190,7 @@ impl BatchKind { TransformBatchKind::AlignedGradient => "AlignedGradient", TransformBatchKind::AngleGradient => "AngleGradient", TransformBatchKind::RadialGradient => "RadialGradient", - TransformBatchKind::BoxShadow => "BoxShadow", - TransformBatchKind::CacheImage => "CacheImage", + TransformBatchKind::CacheImage(..) => "CacheImage", TransformBatchKind::BorderCorner => "BorderCorner", TransformBatchKind::BorderEdge => "BorderEdge", TransformBatchKind::Line => "Line", @@ -365,35 +360,11 @@ const DESC_CLIP: VertexDescriptor = VertexDescriptor { ], }; -const DESC_CACHE_BOX_SHADOW: VertexDescriptor = VertexDescriptor { - vertex_attributes: &[ - VertexAttribute { - name: "aPosition", - count: 2, - kind: VertexAttributeKind::F32, - }, - ], - instance_attributes: &[ - VertexAttribute { - name: "aPrimAddress", - count: 2, - kind: VertexAttributeKind::U16, - }, - VertexAttribute { - name: "aTaskIndex", - count: 1, - kind: VertexAttributeKind::I32, - }, - ], -}; - #[derive(Debug, Copy, Clone)] enum VertexArrayKind { Primitive, Blur, Clip, - - CacheBoxShadow, } #[derive(Clone, Debug, PartialEq)] @@ -864,6 +835,7 @@ enum ShaderKind { Primitive, Cache(VertexArrayKind), ClipCache, + Brush, } struct LazilyCompiledShader { @@ -917,7 +889,7 @@ impl LazilyCompiledShader { if self.program.is_none() { let program = try!{ match self.kind { - ShaderKind::Primitive => { + ShaderKind::Primitive | ShaderKind::Brush => { create_prim_shader(self.name, device, &self.features, @@ -1044,7 +1016,6 @@ fn create_prim_shader( VertexArrayKind::Primitive => DESC_PRIM_INSTANCES, VertexArrayKind::Blur => DESC_BLUR, VertexArrayKind::Clip => DESC_CLIP, - VertexArrayKind::CacheBoxShadow => DESC_CACHE_BOX_SHADOW, }; let program = device.create_program(name, &prefix, &vertex_descriptor); @@ -1122,10 +1093,11 @@ pub struct Renderer { // These are "cache shaders". These shaders are used to // draw intermediate results to cache targets. The results // of these shaders are then used by the primitive shaders. - cs_box_shadow: LazilyCompiledShader, cs_text_run: LazilyCompiledShader, cs_line: LazilyCompiledShader, - cs_blur: LazilyCompiledShader, + cs_blur_a8: LazilyCompiledShader, + cs_blur_rgba8: LazilyCompiledShader, + brush_mask: LazilyCompiledShader, /// These are "cache clip shaders". These shaders are used to /// draw clip instances into the cached clip mask. The results @@ -1151,8 +1123,8 @@ pub struct Renderer { ps_gradient: PrimitiveShader, ps_angle_gradient: PrimitiveShader, ps_radial_gradient: PrimitiveShader, - ps_box_shadow: PrimitiveShader, - ps_cache_image: PrimitiveShader, + ps_cache_image_rgba8: PrimitiveShader, + ps_cache_image_a8: PrimitiveShader, ps_line: PrimitiveShader, ps_blend: LazilyCompiledShader, @@ -1183,7 +1155,6 @@ pub struct Renderer { prim_vao: VAO, blur_vao: VAO, clip_vao: VAO, - box_shadow_vao: VAO, layer_texture: VertexDataTexture, render_task_texture: VertexDataTexture, @@ -1304,14 +1275,6 @@ impl Renderer { // device-pixel ratio doesn't matter here - we are just creating resources. device.begin_frame(1.0); - let cs_box_shadow = try!{ - LazilyCompiledShader::new(ShaderKind::Cache(VertexArrayKind::CacheBoxShadow), - "cs_box_shadow", - &[], - &mut device, - options.precache_shaders) - }; - let cs_text_run = try!{ LazilyCompiledShader::new(ShaderKind::Cache(VertexArrayKind::Primitive), "cs_text_run", @@ -1328,10 +1291,26 @@ impl Renderer { options.precache_shaders) }; - let cs_blur = try!{ + let brush_mask = try!{ + LazilyCompiledShader::new(ShaderKind::Brush, + "brush_mask", + &[], + &mut device, + options.precache_shaders) + }; + + let cs_blur_a8 = try!{ LazilyCompiledShader::new(ShaderKind::Cache(VertexArrayKind::Blur), "cs_blur", - &[], + &["ALPHA"], + &mut device, + options.precache_shaders) + }; + + let cs_blur_rgba8 = try!{ + LazilyCompiledShader::new(ShaderKind::Cache(VertexArrayKind::Blur), + "cs_blur", + &["COLOR"], &mut device, options.precache_shaders) }; @@ -1470,13 +1449,6 @@ impl Renderer { options.precache_shaders) }; - let ps_box_shadow = try!{ - PrimitiveShader::new("ps_box_shadow", - &mut device, - &[], - options.precache_shaders) - }; - let dithering_feature = ["DITHERING"]; let ps_gradient = try!{ @@ -1512,10 +1484,17 @@ impl Renderer { options.precache_shaders) }; - let ps_cache_image = try!{ + let ps_cache_image_a8 = try!{ PrimitiveShader::new("ps_cache_image", &mut device, - &[], + &["ALPHA"], + options.precache_shaders) + }; + + let ps_cache_image_rgba8 = try!{ + PrimitiveShader::new("ps_cache_image", + &mut device, + &["COLOR"], options.precache_shaders) }; @@ -1663,8 +1642,6 @@ impl Renderer { let blur_vao = device.create_vao_with_new_instances(&DESC_BLUR, &prim_vao); let clip_vao = device.create_vao_with_new_instances(&DESC_CLIP, &prim_vao); - let box_shadow_vao = - device.create_vao_with_new_instances(&DESC_CACHE_BOX_SHADOW, &prim_vao); let texture_cache_upload_pbo = device.create_pbo(); @@ -1733,10 +1710,11 @@ impl Renderer { pending_texture_updates: Vec::new(), pending_gpu_cache_updates: Vec::new(), pending_shader_updates: Vec::new(), - cs_box_shadow, cs_text_run, cs_line, - cs_blur, + cs_blur_a8, + cs_blur_rgba8, + brush_mask, cs_clip_rectangle, cs_clip_border, cs_clip_image, @@ -1747,11 +1725,11 @@ impl Renderer { ps_yuv_image, ps_border_corner, ps_border_edge, - ps_box_shadow, ps_gradient, ps_angle_gradient, ps_radial_gradient, - ps_cache_image, + ps_cache_image_rgba8, + ps_cache_image_a8, ps_blend, ps_hw_composite, ps_split_composite, @@ -1775,7 +1753,6 @@ impl Renderer { gpu_profile, prim_vao, blur_vao, - box_shadow_vao, clip_vao, layer_texture, render_task_texture, @@ -1932,19 +1909,29 @@ impl Renderer { "Borders", target.clip_batcher.borders.len(), ); + debug_target.add( + debug_server::BatchKind::Cache, + "Vertical Blur", + target.vertical_blurs.len(), + ); + debug_target.add( + debug_server::BatchKind::Cache, + "Horizontal Blur", + target.horizontal_blurs.len(), + ); debug_target.add( debug_server::BatchKind::Clip, "Rectangles", target.clip_batcher.rectangles.len(), ); - for (_, items) in target.clip_batcher.images.iter() { - debug_target.add(debug_server::BatchKind::Clip, "Image mask", items.len()); - } debug_target.add( debug_server::BatchKind::Cache, - "Box Shadow", - target.box_shadow_cache_prims.len(), + "Rectangle Brush", + target.rect_cache_prims.len(), ); + for (_, items) in target.clip_batcher.images.iter() { + debug_target.add(debug_server::BatchKind::Clip, "Image mask", items.len()); + } debug_pass.add(debug_target); } @@ -2314,7 +2301,6 @@ impl Renderer { VertexArrayKind::Primitive => &self.prim_vao, VertexArrayKind::Clip => &self.clip_vao, VertexArrayKind::Blur => &self.blur_vao, - VertexArrayKind::CacheBoxShadow => &self.box_shadow_vao, }; self.device.bind_vao(vao); @@ -2492,24 +2478,27 @@ impl Renderer { ); GPU_TAG_PRIM_RADIAL_GRADIENT } - TransformBatchKind::BoxShadow => { - self.ps_box_shadow.bind( - &mut self.device, - transform_kind, - projection, - 0, - &mut self.renderer_errors, - ); - GPU_TAG_PRIM_BOX_SHADOW - } - TransformBatchKind::CacheImage => { - self.ps_cache_image.bind( - &mut self.device, - transform_kind, - projection, - 0, - &mut self.renderer_errors, - ); + TransformBatchKind::CacheImage(target_kind) => { + match target_kind { + RenderTargetKind::Alpha => { + self.ps_cache_image_a8.bind( + &mut self.device, + transform_kind, + projection, + 0, + &mut self.renderer_errors, + ); + } + RenderTargetKind::Color => { + self.ps_cache_image_rgba8.bind( + &mut self.device, + transform_kind, + projection, + 0, + &mut self.renderer_errors, + ); + } + } GPU_TAG_PRIM_CACHE_IMAGE } }, @@ -2639,7 +2628,7 @@ impl Renderer { let _gm = self.gpu_profile.add_marker(GPU_TAG_BLUR); self.device.set_blend(false); - self.cs_blur + self.cs_blur_rgba8 .bind(&mut self.device, projection, 0, &mut self.renderer_errors); if !target.vertical_blurs.is_empty() { @@ -2921,15 +2910,45 @@ impl Renderer { .clear_target_rect(Some(clear_color), None, target.used_rect()); } - // Draw any box-shadow caches for this target. - if !target.box_shadow_cache_prims.is_empty() { + // Draw any blurs for this target. + // Blurs are rendered as a standard 2-pass + // separable implementation. + // TODO(gw): In the future, consider having + // fast path blur shaders for common + // blur radii with fixed weights. + if !target.vertical_blurs.is_empty() || !target.horizontal_blurs.is_empty() { + let _gm = self.gpu_profile.add_marker(GPU_TAG_BLUR); + + self.device.set_blend(false); + self.cs_blur_a8 + .bind(&mut self.device, projection, 0, &mut self.renderer_errors); + + if !target.vertical_blurs.is_empty() { + self.draw_instanced_batch( + &target.vertical_blurs, + VertexArrayKind::Blur, + &BatchTextures::no_texture(), + ); + } + + if !target.horizontal_blurs.is_empty() { + self.draw_instanced_batch( + &target.horizontal_blurs, + VertexArrayKind::Blur, + &BatchTextures::no_texture(), + ); + } + } + + if !target.rect_cache_prims.is_empty() { self.device.set_blend(false); - let _gm = self.gpu_profile.add_marker(GPU_TAG_CACHE_BOX_SHADOW); - self.cs_box_shadow + + let _gm = self.gpu_profile.add_marker(GPU_TAG_BRUSH_MASK); + self.brush_mask .bind(&mut self.device, projection, 0, &mut self.renderer_errors); self.draw_instanced_batch( - &target.box_shadow_cache_prims, - VertexArrayKind::CacheBoxShadow, + &target.rect_cache_prims, + VertexArrayKind::Primitive, &BatchTextures::no_texture(), ); } @@ -3473,12 +3492,12 @@ impl Renderer { self.device.delete_vao(self.prim_vao); self.device.delete_vao(self.clip_vao); self.device.delete_vao(self.blur_vao); - self.device.delete_vao(self.box_shadow_vao); self.debug.deinit(&mut self.device); - self.cs_box_shadow.deinit(&mut self.device); self.cs_text_run.deinit(&mut self.device); self.cs_line.deinit(&mut self.device); - self.cs_blur.deinit(&mut self.device); + self.cs_blur_a8.deinit(&mut self.device); + self.cs_blur_rgba8.deinit(&mut self.device); + self.brush_mask.deinit(&mut self.device); self.cs_clip_rectangle.deinit(&mut self.device); self.cs_clip_image.deinit(&mut self.device); self.cs_clip_border.deinit(&mut self.device); @@ -3503,8 +3522,8 @@ impl Renderer { self.ps_gradient.deinit(&mut self.device); self.ps_angle_gradient.deinit(&mut self.device); self.ps_radial_gradient.deinit(&mut self.device); - self.ps_box_shadow.deinit(&mut self.device); - self.ps_cache_image.deinit(&mut self.device); + self.ps_cache_image_rgba8.deinit(&mut self.device); + self.ps_cache_image_a8.deinit(&mut self.device); self.ps_line.deinit(&mut self.device); self.ps_blend.deinit(&mut self.device); self.ps_hw_composite.deinit(&mut self.device); diff --git a/webrender/src/tiling.rs b/webrender/src/tiling.rs index 7bb21a4687..255be5fd6c 100644 --- a/webrender/src/tiling.rs +++ b/webrender/src/tiling.rs @@ -12,7 +12,7 @@ use clip::{ClipSource, ClipStore}; use clip_scroll_tree::CoordinateSystemId; use device::Texture; use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle, GpuCacheUpdateList}; -use gpu_types::{BlurDirection, BlurInstance, BoxShadowCacheInstance, ClipMaskInstance}; +use gpu_types::{BlurDirection, BlurInstance, BrushInstance, ClipMaskInstance}; use gpu_types::{CompositePrimitiveInstance, PrimitiveInstance, SimplePrimitiveInstance}; use internal_types::{FastHashMap, SourceTexture}; use internal_types::BatchTextures; @@ -401,6 +401,9 @@ impl AlphaRenderItem { let blend_mode = ctx.prim_store.get_blend_mode(prim_metadata, transform_kind); match prim_metadata.prim_kind { + PrimitiveKind::Brush => { + panic!("BUG: brush type not expected in an alpha task (yet)"); + } PrimitiveKind::Border => { let border_cpu = &ctx.prim_store.cpu_borders[prim_metadata.cpu_prim_index.0]; @@ -577,7 +580,7 @@ impl AlphaRenderItem { let textures = BatchTextures::render_target_cache(); let kind = BatchKind::Transformable( transform_kind, - TransformBatchKind::CacheImage, + TransformBatchKind::CacheImage(picture.kind), ); let key = BatchKey::new(kind, blend_mode, textures); let batch = batch_list.get_suitable_batch(key, item_bounding_rect); @@ -696,26 +699,6 @@ impl AlphaRenderItem { uv_rect_addresses[2], )); } - PrimitiveKind::BoxShadow => { - let box_shadow = - &ctx.prim_store.cpu_box_shadows[prim_metadata.cpu_prim_index.0]; - let cache_task_id = box_shadow.render_task_id.unwrap(); - let cache_task_address = render_tasks.get_task_address(cache_task_id); - let textures = BatchTextures::render_target_cache(); - - let kind = - BatchKind::Transformable(transform_kind, TransformBatchKind::BoxShadow); - let key = BatchKey::new(kind, blend_mode, textures); - let batch = batch_list.get_suitable_batch(key, item_bounding_rect); - - for rect_index in 0 .. box_shadow.rects.len() { - batch.push(base_instance.build( - rect_index as i32, - cache_task_address.0 as i32, - 0, - )); - } - } } } AlphaRenderItem::SplitComposite(sc_index, task_id, gpu_handle, z) => { @@ -984,7 +967,7 @@ pub trait RenderTarget { fn used_rect(&self) -> DeviceIntRect; } -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub enum RenderTargetKind { Color, // RGBA32 Alpha, // R8 @@ -1172,8 +1155,8 @@ impl RenderTarget for ColorRenderTarget { blur_direction: BlurDirection::Horizontal, }); } - RenderTaskKind::Picture(prim_index) => { - let prim_metadata = ctx.prim_store.get_metadata(prim_index); + RenderTaskKind::Picture(ref task_info) => { + let prim_metadata = ctx.prim_store.get_metadata(task_info.prim_index); let prim_address = prim_metadata.gpu_location.as_int(gpu_cache); match prim_metadata.prim_kind { @@ -1245,7 +1228,7 @@ impl RenderTarget for ColorRenderTarget { } } } - RenderTaskKind::CacheMask(..) | RenderTaskKind::BoxShadow(..) => { + RenderTaskKind::CacheMask(..) => { panic!("Should not be added to color target!"); } RenderTaskKind::Readback(device_rect) => { @@ -1257,7 +1240,10 @@ impl RenderTarget for ColorRenderTarget { pub struct AlphaRenderTarget { pub clip_batcher: ClipBatcher, - pub box_shadow_cache_prims: Vec, + pub rect_cache_prims: Vec, + // List of blur operations to apply for this render target. + pub vertical_blurs: Vec, + pub horizontal_blurs: Vec, allocator: TextureAllocator, } @@ -1269,7 +1255,9 @@ impl RenderTarget for AlphaRenderTarget { fn new(size: Option) -> AlphaRenderTarget { AlphaRenderTarget { clip_batcher: ClipBatcher::new(), - box_shadow_cache_prims: Vec::new(), + rect_cache_prims: Vec::new(), + vertical_blurs: Vec::new(), + horizontal_blurs: Vec::new(), allocator: TextureAllocator::new(size.expect("bug: alpha targets need size")), } } @@ -1292,24 +1280,59 @@ impl RenderTarget for AlphaRenderTarget { panic!("BUG: add_task() called on invalidated task"); } RenderTaskKind::Alpha(..) | - RenderTaskKind::VerticalBlur(..) | - RenderTaskKind::HorizontalBlur(..) | - RenderTaskKind::Picture(..) | RenderTaskKind::Readback(..) => { panic!("Should not be added to alpha target!"); } - RenderTaskKind::BoxShadow(prim_index) => { - let prim_metadata = ctx.prim_store.get_metadata(prim_index); + RenderTaskKind::VerticalBlur(..) => { + // Find the child render task that we are applying + // a vertical blur on. + self.vertical_blurs.push(BlurInstance { + task_address: render_tasks.get_task_address(task_id), + src_task_address: render_tasks.get_task_address(task.children[0]), + blur_direction: BlurDirection::Vertical, + }); + } + RenderTaskKind::HorizontalBlur(..) => { + // Find the child render task that we are applying + // a horizontal blur on. + self.horizontal_blurs.push(BlurInstance { + task_address: render_tasks.get_task_address(task_id), + src_task_address: render_tasks.get_task_address(task.children[0]), + blur_direction: BlurDirection::Horizontal, + }); + } + RenderTaskKind::Picture(ref task_info) => { + let prim_metadata = ctx.prim_store.get_metadata(task_info.prim_index); match prim_metadata.prim_kind { - PrimitiveKind::BoxShadow => { - self.box_shadow_cache_prims.push(BoxShadowCacheInstance { - prim_address: gpu_cache.get_address(&prim_metadata.gpu_location), - task_index: render_tasks.get_task_address(task_id), - }); + PrimitiveKind::Picture => { + let prim = &ctx.prim_store.cpu_pictures[prim_metadata.cpu_prim_index.0]; + + let task_index = render_tasks.get_task_address(task_id); + + for run in &prim.prim_runs { + for i in 0 .. run.count { + let sub_prim_index = PrimitiveIndex(run.prim_index.0 + i); + + let sub_metadata = ctx.prim_store.get_metadata(sub_prim_index); + let sub_prim_address = + gpu_cache.get_address(&sub_metadata.gpu_location); + + match sub_metadata.prim_kind { + PrimitiveKind::Brush => { + let instance = BrushInstance::new(task_index, sub_prim_address); + self.rect_cache_prims.push(PrimitiveInstance::from(instance)); + } + _ => { + unreachable!("Unexpected sub primitive type"); + } + } + } + } } _ => { - panic!("BUG: invalid prim kind"); + // No other primitives make use of primitive caching yet! + unreachable!() } } } @@ -1502,8 +1525,7 @@ pub enum TransformBatchKind { AlignedGradient, AngleGradient, RadialGradient, - BoxShadow, - CacheImage, + CacheImage(RenderTargetKind), BorderCorner, BorderEdge, Line, diff --git a/webrender/tests/angle_shader_validation.rs b/webrender/tests/angle_shader_validation.rs index f4da1a95f6..4e675f5066 100644 --- a/webrender/tests/angle_shader_validation.rs +++ b/webrender/tests/angle_shader_validation.rs @@ -41,10 +41,6 @@ const SHADERS: &[Shader] = &[ name: "cs_text_run", features: CACHE_FEATURES, }, - Shader { - name: "cs_box_shadow", - features: CACHE_FEATURES, - }, // Prim shaders Shader { name: "ps_line", @@ -58,10 +54,6 @@ const SHADERS: &[Shader] = &[ name: "ps_border_edge", features: PRIM_FEATURES, }, - Shader { - name: "ps_box_shadow", - features: PRIM_FEATURES, - }, Shader { name: "ps_gradient", features: PRIM_FEATURES, @@ -76,7 +68,7 @@ const SHADERS: &[Shader] = &[ }, Shader { name: "ps_cache_image", - features: PRIM_FEATURES, + features: &["COLOR", "ALPHA"], }, Shader { name: "ps_blend", @@ -110,6 +102,11 @@ const SHADERS: &[Shader] = &[ name: "ps_rectangle", features: &["", "TRANSFORM", "CLIP_FEATURE", "TRANSFORM,CLIP_FEATURE"], }, + // Brush shaders + Shader { + name: "brush_mask", + features: &[], + }, ]; const VERSION_STRING: &str = "#version 300 es\n"; diff --git a/webrender_api/src/display_item.rs b/webrender_api/src/display_item.rs index 12c8815578..e5b8bfe4dd 100644 --- a/webrender_api/src/display_item.rs +++ b/webrender_api/src/display_item.rs @@ -286,9 +286,8 @@ pub enum BorderStyle { #[repr(u32)] #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] pub enum BoxShadowClipMode { - None = 0, - Outset = 1, - Inset = 2, + Outset = 0, + Inset = 1, } #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] diff --git a/wrench/reftests/boxshadow/box-shadow-suite-blur.png b/wrench/reftests/boxshadow/box-shadow-suite-blur.png new file mode 100644 index 0000000000..a98073561e Binary files /dev/null and b/wrench/reftests/boxshadow/box-shadow-suite-blur.png differ diff --git a/wrench/reftests/boxshadow/box-shadow-suite-blur.yaml b/wrench/reftests/boxshadow/box-shadow-suite-blur.yaml new file mode 100644 index 0000000000..29ab5a2a43 --- /dev/null +++ b/wrench/reftests/boxshadow/box-shadow-suite-blur.yaml @@ -0,0 +1,154 @@ +--- +root: + items: + - type: stacking-context + bounds: [0, 0, 1000, 1000] + items: + # 1st row + - type: box-shadow + bounds: [ 50, 50, 100, 100 ] + color: red + clip-mode: outset + blur-radius: 5 + + - type: box-shadow + bounds: [ 200, 50, 100, 100 ] + color: red + clip-mode: outset + offset: 20 0 + blur-radius: 5 + + - type: box-shadow + bounds: [ 350, 50, 100, 100 ] + color: red + clip-mode: outset + offset: 0 -40 + blur-radius: 5 + + - type: box-shadow + bounds: [ 500, 50, 100, 100 ] + color: red + clip-mode: outset + spread-radius: 30 + blur-radius: 5 + + - type: box-shadow + bounds: [ 650, 50, 100, 100 ] + color: red + clip-mode: outset + spread-radius: 30 + offset: 50 -10 + blur-radius: 5 + + # 2nd row + - type: box-shadow + bounds: [ 50, 250, 100, 100 ] + color: green + border-radius: 32 + blur-radius: 5 + + - type: box-shadow + bounds: [ 200, 250, 100, 100 ] + color: green + offset: 20 0 + border-radius: 32 + blur-radius: 5 + + - type: box-shadow + bounds: [ 350, 250, 100, 100 ] + color: green + offset: 0 -40 + border-radius: 32 + blur-radius: 5 + + - type: box-shadow + bounds: [ 500, 250, 100, 100 ] + color: green + spread-radius: 30 + border-radius: 32 + blur-radius: 5 + + - type: box-shadow + bounds: [ 650, 250, 100, 100 ] + color: green + spread-radius: 30 + offset: 50 -10 + border-radius: 32 + blur-radius: 5 + + # 3rd row + - type: box-shadow + bounds: [ 50, 450, 100, 100 ] + color: red + clip-mode: inset + blur-radius: 5 + + - type: box-shadow + bounds: [ 200, 450, 100, 100 ] + color: red + clip-mode: inset + offset: 20 0 + blur-radius: 5 + + - type: box-shadow + bounds: [ 350, 450, 100, 100 ] + color: red + clip-mode: inset + offset: 0 -40 + blur-radius: 5 + + - type: box-shadow + bounds: [ 500, 450, 100, 100 ] + color: red + clip-mode: inset + spread-radius: 30 + blur-radius: 5 + + - type: box-shadow + bounds: [ 650, 450, 100, 100 ] + color: red + clip-mode: inset + spread-radius: 30 + offset: 50 -10 + blur-radius: 5 + + # 4th row + - type: box-shadow + bounds: [ 50, 650, 100, 100 ] + color: red + clip-mode: inset + border-radius: 32 + blur-radius: 5 + + - type: box-shadow + bounds: [ 200, 650, 100, 100 ] + color: red + clip-mode: inset + offset: 20 0 + border-radius: 32 + blur-radius: 5 + + - type: box-shadow + bounds: [ 350, 650, 100, 100 ] + color: red + clip-mode: inset + offset: 0 -40 + border-radius: 32 + blur-radius: 5 + + - type: box-shadow + bounds: [ 500, 650, 100, 100 ] + color: red + clip-mode: inset + spread-radius: 30 + border-radius: 32 + blur-radius: 5 + + - type: box-shadow + bounds: [ 650, 650, 100, 100 ] + color: red + clip-mode: inset + spread-radius: 30 + offset: 50 -10 + border-radius: 32 + blur-radius: 5 diff --git a/wrench/reftests/boxshadow/box-shadow-suite-no-blur.png b/wrench/reftests/boxshadow/box-shadow-suite-no-blur.png new file mode 100644 index 0000000000..10d3fb83a2 Binary files /dev/null and b/wrench/reftests/boxshadow/box-shadow-suite-no-blur.png differ diff --git a/wrench/reftests/boxshadow/box-shadow-suite-no-blur.yaml b/wrench/reftests/boxshadow/box-shadow-suite-no-blur.yaml new file mode 100644 index 0000000000..8544da8ccc --- /dev/null +++ b/wrench/reftests/boxshadow/box-shadow-suite-no-blur.yaml @@ -0,0 +1,134 @@ +--- +root: + items: + - type: stacking-context + bounds: [0, 0, 1000, 1000] + items: + # 1st row + - type: box-shadow + bounds: [ 50, 50, 100, 100 ] + color: red + clip-mode: outset + + - type: box-shadow + bounds: [ 200, 50, 100, 100 ] + color: red + clip-mode: outset + offset: 20 0 + + - type: box-shadow + bounds: [ 350, 50, 100, 100 ] + color: red + clip-mode: outset + offset: 0 -40 + + - type: box-shadow + bounds: [ 500, 50, 100, 100 ] + color: red + clip-mode: outset + spread-radius: 30 + + - type: box-shadow + bounds: [ 650, 50, 100, 100 ] + color: red + clip-mode: outset + spread-radius: 30 + offset: 50 -10 + + # 2nd row + - type: box-shadow + bounds: [ 50, 250, 100, 100 ] + color: green + border-radius: 32 + + - type: box-shadow + bounds: [ 200, 250, 100, 100 ] + color: green + offset: 20 0 + border-radius: 32 + + - type: box-shadow + bounds: [ 350, 250, 100, 100 ] + color: green + offset: 0 -40 + border-radius: 32 + + - type: box-shadow + bounds: [ 500, 250, 100, 100 ] + color: green + spread-radius: 30 + border-radius: 32 + + - type: box-shadow + bounds: [ 650, 250, 100, 100 ] + color: green + spread-radius: 30 + offset: 50 -10 + border-radius: 32 + + # 3rd row + - type: box-shadow + bounds: [ 50, 450, 100, 100 ] + color: red + clip-mode: inset + + - type: box-shadow + bounds: [ 200, 450, 100, 100 ] + color: red + clip-mode: inset + offset: 20 0 + + - type: box-shadow + bounds: [ 350, 450, 100, 100 ] + color: red + clip-mode: inset + offset: 0 -40 + + - type: box-shadow + bounds: [ 500, 450, 100, 100 ] + color: red + clip-mode: inset + spread-radius: 30 + + - type: box-shadow + bounds: [ 650, 450, 100, 100 ] + color: red + clip-mode: inset + spread-radius: 30 + offset: 50 -10 + + # 4th row + - type: box-shadow + bounds: [ 50, 650, 100, 100 ] + color: red + clip-mode: inset + border-radius: 32 + + - type: box-shadow + bounds: [ 200, 650, 100, 100 ] + color: red + clip-mode: inset + offset: 20 0 + border-radius: 32 + + - type: box-shadow + bounds: [ 350, 650, 100, 100 ] + color: red + clip-mode: inset + offset: 0 -40 + border-radius: 32 + + - type: box-shadow + bounds: [ 500, 650, 100, 100 ] + color: red + clip-mode: inset + spread-radius: 30 + border-radius: 32 + + - type: box-shadow + bounds: [ 650, 650, 100, 100 ] + color: red + clip-mode: inset + spread-radius: 30 + offset: 50 -10 + border-radius: 32 diff --git a/wrench/reftests/boxshadow/boxshadow-spread-only-ref.png b/wrench/reftests/boxshadow/boxshadow-spread-only-ref.png index 1d0efb12a2..b46b649b7a 100644 Binary files a/wrench/reftests/boxshadow/boxshadow-spread-only-ref.png and b/wrench/reftests/boxshadow/boxshadow-spread-only-ref.png differ diff --git a/wrench/reftests/boxshadow/inset-border-radius.png b/wrench/reftests/boxshadow/inset-border-radius.png new file mode 100644 index 0000000000..77d0976acf Binary files /dev/null and b/wrench/reftests/boxshadow/inset-border-radius.png differ diff --git a/wrench/reftests/boxshadow/inset-large-offset-ref.png b/wrench/reftests/boxshadow/inset-large-offset-ref.png index ef6149cff4..60776b8907 100644 Binary files a/wrench/reftests/boxshadow/inset-large-offset-ref.png and b/wrench/reftests/boxshadow/inset-large-offset-ref.png differ diff --git a/wrench/reftests/boxshadow/reftest.list b/wrench/reftests/boxshadow/reftest.list index a1ed5ea730..ae10061c84 100644 --- a/wrench/reftests/boxshadow/reftest.list +++ b/wrench/reftests/boxshadow/reftest.list @@ -4,6 +4,8 @@ == inset-spread-large.yaml inset-spread-large-ref.yaml == boxshadow-spread-only.yaml boxshadow-spread-only-ref.png fuzzy(1,396) == inset-large-offset.yaml inset-large-offset-ref.png +== inset-border-radius.yaml inset-border-radius.png +== box-shadow-suite-no-blur.yaml box-shadow-suite-no-blur.png +== box-shadow-suite-blur.yaml box-shadow-suite-blur.png +== rounding.yaml rounding-ref.yaml -# fuzzy because the test is a blur. Just want to test that clip out works. -fuzzy(48,3480) == inset-border-radius.yaml inset-border-radius-ref.yaml diff --git a/wrench/reftests/boxshadow/rounding-ref.yaml b/wrench/reftests/boxshadow/rounding-ref.yaml new file mode 100644 index 0000000000..e6e2fb8464 --- /dev/null +++ b/wrench/reftests/boxshadow/rounding-ref.yaml @@ -0,0 +1,18 @@ +--- +root: + items: + - type: stacking-context + bounds: [0, 0, 1000, 1000] + items: + - type: rect + bounds: [100, 100, 500, 500] + color: green + - type: rect + bounds: [200, 200, 300, 300] + color: black + - type: box-shadow + bounds: [200, 200, 300, 300] + blur-radius: 20 + clip-mode: inset + spread-radius: 100 + color: green diff --git a/wrench/reftests/boxshadow/rounding.yaml b/wrench/reftests/boxshadow/rounding.yaml new file mode 100644 index 0000000000..8078b2e1d6 --- /dev/null +++ b/wrench/reftests/boxshadow/rounding.yaml @@ -0,0 +1,15 @@ +--- +root: + items: + - type: stacking-context + bounds: [0, 0, 1000, 1000] + items: + - type: rect + bounds: [100, 100, 500, 500] + color: black + - type: box-shadow + bounds: [100, 100, 500, 500] + blur-radius: 20 + clip-mode: inset + spread-radius: 200 + color: green diff --git a/wrench/reftests/filters/filter-blur.png b/wrench/reftests/filters/filter-blur.png index 1c08d2c20f..9354e3225e 100644 Binary files a/wrench/reftests/filters/filter-blur.png and b/wrench/reftests/filters/filter-blur.png differ diff --git a/wrench/reftests/text/decorations-suite.png b/wrench/reftests/text/decorations-suite.png index b495be2e76..a26f3c313d 100644 Binary files a/wrench/reftests/text/decorations-suite.png and b/wrench/reftests/text/decorations-suite.png differ diff --git a/wrench/reftests/text/reftest.list b/wrench/reftests/text/reftest.list index b37cf4e1f9..86ab5f88e9 100644 --- a/wrench/reftests/text/reftest.list +++ b/wrench/reftests/text/reftest.list @@ -18,7 +18,7 @@ fuzzy(1,100) == decorations-suite.yaml decorations-suite.png == 1658.yaml 1658-ref.yaml == split-batch.yaml split-batch-ref.yaml == shadow-red.yaml shadow-red-ref.yaml -fuzzy(1,164) == shadow-grey.yaml shadow-grey-ref.yaml +fuzzy(1,616) == shadow-grey.yaml shadow-grey-ref.yaml == subtle-shadow.yaml subtle-shadow-ref.yaml == shadow-atomic.yaml shadow-atomic-ref.yaml == shadow-ordering.yaml shadow-ordering-ref.yaml diff --git a/wrench/src/yaml_frame_reader.rs b/wrench/src/yaml_frame_reader.rs index 2198fa8892..ac60158b37 100644 --- a/wrench/src/yaml_frame_reader.rs +++ b/wrench/src/yaml_frame_reader.rs @@ -837,13 +837,12 @@ impl YamlFrameReader { let border_radius = item["border-radius"].as_force_f32().unwrap_or(0.0); let clip_mode = if let Some(mode) = item["clip-mode"].as_str() { match mode { - "none" => BoxShadowClipMode::None, "outset" => BoxShadowClipMode::Outset, "inset" => BoxShadowClipMode::Inset, s => panic!("Unknown box shadow clip mode {}", s), } } else { - BoxShadowClipMode::None + BoxShadowClipMode::Outset }; dl.push_box_shadow( diff --git a/wrench/src/yaml_frame_writer.rs b/wrench/src/yaml_frame_writer.rs index 34bdc22fc3..bf246c7a31 100644 --- a/wrench/src/yaml_frame_writer.rs +++ b/wrench/src/yaml_frame_writer.rs @@ -896,7 +896,6 @@ impl YamlFrameWriter { f32_node(&mut v, "spread-radius", item.spread_radius); f32_node(&mut v, "border-radius", item.border_radius); let clip_mode = match item.clip_mode { - BoxShadowClipMode::None => "none", BoxShadowClipMode::Outset => "outset", BoxShadowClipMode::Inset => "inset", };