Bug 1408461 - Update webrender to commit 7892f5364bc4d35c7a9b42949f0ace4cc54f8b3c. r=jrmuizel

MozReview-Commit-ID: 1n7EzemevQX

--HG--
extra : rebase_source : 113614aa51076f79cb094bb56a36e5a4282a3e31
This commit is contained in:
Kartikaya Gupta 2017-10-18 09:36:53 -04:00
parent d94a6f7526
commit 91bb50513e
41 changed files with 1538 additions and 1357 deletions

View File

@ -175,4 +175,4 @@ Troubleshooting tips:
-------------------------------------------------------------------------------
The version of WebRender currently in the tree is:
a624aa6d3b6006c510c8b14026567af4ac545d2f
7892f5364bc4d35c7a9b42949f0ace4cc54f8b3c

View File

@ -11,6 +11,7 @@ default = ["freetype-lib"]
freetype-lib = ["freetype/servo-freetype-sys"]
profiler = ["thread_profiler/thread_profiler"]
debugger = ["ws", "serde_json", "serde", "serde_derive"]
query = []
[dependencies]
app_units = "0.5.6"
@ -25,7 +26,7 @@ num-traits = "0.1.32"
time = "0.1"
rayon = "0.8"
webrender_api = {path = "../webrender_api"}
bitflags = "0.9"
bitflags = "1.0"
thread_profiler = "0.1.1"
plane-split = "0.6"
ws = { optional = true, version = "0.7.3" }

View File

@ -187,7 +187,7 @@ pub fn main_wrapper(example: &mut Example, options: Option<webrender::RendererOp
Some(glutin::VirtualKeyCode::P),
) => {
let mut flags = renderer.get_debug_flags();
flags.toggle(webrender::PROFILER_DBG);
flags.toggle(webrender::DebugFlags::PROFILER_DBG);
renderer.set_debug_flags(flags);
}
glutin::Event::KeyboardInput(
@ -196,7 +196,7 @@ pub fn main_wrapper(example: &mut Example, options: Option<webrender::RendererOp
Some(glutin::VirtualKeyCode::O),
) => {
let mut flags = renderer.get_debug_flags();
flags.toggle(webrender::RENDER_TARGET_DBG);
flags.toggle(webrender::DebugFlags::RENDER_TARGET_DBG);
renderer.set_debug_flags(flags);
}
glutin::Event::KeyboardInput(
@ -205,7 +205,7 @@ pub fn main_wrapper(example: &mut Example, options: Option<webrender::RendererOp
Some(glutin::VirtualKeyCode::I),
) => {
let mut flags = renderer.get_debug_flags();
flags.toggle(webrender::TEXTURE_CACHE_DBG);
flags.toggle(webrender::DebugFlags::TEXTURE_CACHE_DBG);
renderer.set_debug_flags(flags);
}
glutin::Event::KeyboardInput(
@ -214,7 +214,7 @@ pub fn main_wrapper(example: &mut Example, options: Option<webrender::RendererOp
Some(glutin::VirtualKeyCode::B),
) => {
let mut flags = renderer.get_debug_flags();
flags.toggle(webrender::ALPHA_PRIM_DBG);
flags.toggle(webrender::DebugFlags::ALPHA_PRIM_DBG);
renderer.set_debug_flags(flags);
}
glutin::Event::KeyboardInput(

View File

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

View File

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

View File

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

View File

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

View File

@ -155,8 +155,7 @@ void main(void) {
clip_relative_pos);
// Get AA widths based on zoom / scale etc.
vec2 fw = fwidth(local_pos);
float afwidth = length(fw);
float aa_range = compute_aa_range(local_pos);
// SDF subtract edges for dash clip
float dash_distance = max(d0, -d1);
@ -167,8 +166,8 @@ void main(void) {
// Select between dot/dash clip based on mode.
float d = mix(dash_distance, dot_distance, vAlphaMask.x);
// Apply AA over half a device pixel for the clip.
d = 1.0 - smoothstep(0.0, 0.5 * afwidth, d);
// Apply AA.
d = distance_aa(aa_range, d);
// Completely mask out clip if zero'ing out the rect.
d = d * vAlphaMask.y;

View File

@ -81,76 +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 afwidth) {
float ellipse_distance = distance_to_ellipse(pos - ellipse_center_radius.xy,
ellipse_center_radius.zw);
return mix(current_distance,
ellipse_distance + afwidth,
all(lessThan(sign_modifier * pos, sign_modifier * ellipse_center_radius.xy)));
}
float rounded_rect(vec2 pos) {
float current_distance = 0.0;
// Apply AA
float afwidth = 0.5 * length(fwidth(pos));
// Clip against each ellipse.
current_distance = clip_against_ellipse_if_needed(pos,
current_distance,
vClipCenter_Radius_TL,
vec2(1.0),
afwidth);
current_distance = clip_against_ellipse_if_needed(pos,
current_distance,
vClipCenter_Radius_TR,
vec2(-1.0, 1.0),
afwidth);
current_distance = clip_against_ellipse_if_needed(pos,
current_distance,
vClipCenter_Radius_BR,
vec2(-1.0),
afwidth);
current_distance = clip_against_ellipse_if_needed(pos,
current_distance,
vClipCenter_Radius_BL,
vec2(1.0, -1.0),
afwidth);
return smoothstep(0.0, afwidth, 1.0 - 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);

View File

@ -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);
}

View File

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

View File

@ -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;
@ -719,6 +702,35 @@ void write_clip(vec2 global_pos, ClipArea area) {
#ifdef WR_FRAGMENT_SHADER
/// Find the appropriate half range to apply the AA smoothstep over.
/// This range represents a coefficient to go from one CSS pixel to half a device pixel.
float compute_aa_range(vec2 position) {
// The constant factor is chosen to compensate for the fact that length(fw) is equal
// to sqrt(2) times the device pixel ratio in the typical case. 0.5/sqrt(2) = 0.35355.
//
// This coefficient is chosen to ensure that any sample 0.5 pixels or more inside of
// the shape has no anti-aliasing applied to it (since pixels are sampled at their center,
// such a pixel (axis aligned) is fully inside the border). We need this so that antialiased
// curves properly connect with non-antialiased vertical or horizontal lines, among other things.
//
// Using larger aa steps is quite common when rendering shapes with distance fields.
// It gives a smoother (although blurrier look) by extending the range that is smoothed
// to produce the anti aliasing. In our case, however, extending the range inside of
// the shape causes noticeable artifacts at the junction between an antialiased corner
// and a straight edge.
// We may want to adjust this constant in specific scenarios (for example keep the principled
// value for straight edges where we want pixel-perfect equivalence with non antialiased lines
// when axis aligned, while selecting a larger and smoother aa range on curves).
return 0.35355 * length(fwidth(position));
}
/// Return the blending coefficient to for distance antialiasing.
///
/// 0.0 means inside the shape, 1.0 means outside.
float distance_aa(float aa_range, float signed_distance) {
return 1.0 - smoothstep(-aa_range, aa_range, signed_distance);
}
#ifdef WR_FEATURE_TRANSFORM
float signed_distance_rect(vec2 pos, vec2 p0, vec2 p1) {
vec2 d = max(p0 - pos, pos - p1);
@ -733,10 +745,10 @@ vec2 init_transform_fs(vec3 local_pos, out float fragment_alpha) {
float d = signed_distance_rect(pos, vLocalBounds.xy, vLocalBounds.zw);
// Find the appropriate distance to apply the AA smoothstep over.
float afwidth = 0.5 * length(fwidth(pos.xy));
float aa_range = compute_aa_range(pos.xy);
// Only apply AA to fragments outside the signed distance field.
fragment_alpha = 1.0 - smoothstep(0.0, afwidth, d);
fragment_alpha = distance_aa(aa_range, d);
return pos;
}

View File

@ -324,11 +324,7 @@ void main(void) {
alpha = min(alpha, do_clip());
// Find the appropriate distance to apply the AA smoothstep over.
// Using 0.7 instead of 0.5 for the step compensates for the fact that smoothstep
// is smooth at its endpoints and has a steeper maximum slope than a linear ramp.
vec2 fw = fwidth(local_pos);
float aa_step = 0.7 * length(fw);
float aa_range = compute_aa_range(local_pos);
float distance_for_color;
float color_mix_factor;
@ -349,29 +345,26 @@ void main(void) {
// 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).
// A half device pixel in css pixels (using the average of width and height in case
// there is any kind of transform applied).
float half_px = 0.25 * (fw.x + fw.y);
// Get signed distance from the inner/outer clips.
float d0 = distance_to_ellipse(p, vRadii0.xy) + half_px;
float d1 = distance_to_ellipse(p, vRadii0.zw) + half_px;
float d2 = distance_to_ellipse(p, vRadii1.xy) + half_px;
float d3 = distance_to_ellipse(p, vRadii1.zw) + half_px;
float d0 = distance_to_ellipse(p, vRadii0.xy);
float d1 = distance_to_ellipse(p, vRadii0.zw);
float d2 = distance_to_ellipse(p, vRadii1.xy);
float d3 = distance_to_ellipse(p, vRadii1.zw);
// SDF subtract main radii
float d_main = max(d0, aa_step - d1);
float d_main = max(d0, -d1);
// SDF subtract inner radii (double style borders)
float d_inner = max(d2 - aa_step, -d3);
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, 1.0 - smoothstep(0.0, aa_step, d));
alpha = min(alpha, distance_aa(aa_range, d));
// Get the groove/ridge mix factor.
color_mix_factor = smoothstep(-aa_step, aa_step, -d2);
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
@ -403,7 +396,7 @@ void main(void) {
// 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, local_pos);
float m = smoothstep(-aa_step, aa_step, ld);
float m = distance_aa(aa_range, -ld);
vec4 color = mix(color0, color1, m);
oFragColor = color * vec4(1.0, 1.0, 1.0, alpha);

View File

@ -253,8 +253,7 @@ void main(void) {
alpha = min(alpha, do_clip());
// Find the appropriate distance to apply the step over.
vec2 fw = fwidth(local_pos);
float afwidth = length(fw);
float aa_range = compute_aa_range(local_pos);
// Applies the math necessary to draw a style: double
// border. In the case of a solid border, the vertex
@ -291,9 +290,7 @@ void main(void) {
// 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 = 1.0 - smoothstep(-0.5 * afwidth,
0.5 * afwidth,
dot_distance);
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));

View File

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

View File

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

View File

@ -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);
@ -190,8 +190,7 @@ void main(void) {
#endif
// Find the appropriate distance to apply the step over.
vec2 fw = fwidth(local_pos);
float afwidth = length(fw);
float aa_range = compute_aa_range(local_pos);
// Select the x/y coord, depending on which axis this edge is.
vec2 pos = mix(local_pos.xy, local_pos.yx, vAxisSelect);
@ -215,9 +214,7 @@ void main(void) {
// Get the dot alpha
vec2 dot_relative_pos = vec2(x, pos.y) - vParams.yz;
float dot_distance = length(dot_relative_pos) - vParams.y;
alpha = min(alpha, 1.0 - smoothstep(-0.5 * afwidth,
0.5 * afwidth,
dot_distance));
alpha = min(alpha, distance_aa(aa_range, dot_distance));
break;
}
case LINE_STYLE_WAVY: {
@ -251,9 +248,7 @@ void main(void) {
float d = min(d1, d2);
// Apply AA based on the thickness of the wave.
alpha = 1.0 - smoothstep(vParams.x - 0.5 * afwidth,
vParams.x + 0.5 * afwidth,
d);
alpha = distance_aa(aa_range, d - vParams.x);
break;
}
}

View File

@ -89,10 +89,10 @@ void main(void) {
modulate_color = alpha * vColor;
break;
case MODE_SUBPX_PASS0:
modulate_color = vec4(alpha);
modulate_color = vec4(alpha) * vColor.a;
break;
case MODE_SUBPX_PASS1:
modulate_color = vColor;
modulate_color = alpha * vColor;
break;
}

View File

@ -2,16 +2,20 @@
* 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::{ClipId, LayerPixel, LayerPoint, LayerRect, LayerSize};
use api::{ClipId, DeviceIntRect, LayerPixel, LayerPoint, LayerRect, LayerSize};
use api::{LayerToScrollTransform, LayerToWorldTransform, LayerVector2D, PipelineId};
use api::{ScrollClamping, ScrollEventPhase, ScrollLocation, ScrollSensitivity, StickyFrameInfo};
use api::WorldPoint;
use clip::{ClipRegion, ClipSources, ClipSourcesHandle, ClipStore};
use clip_scroll_tree::TransformUpdateState;
use clip_scroll_tree::{CoordinateSystemId, TransformUpdateState};
use geometry::ray_intersects_rect;
use spring::{Spring, DAMPING, STIFFNESS};
use tiling::PackedLayerIndex;
use util::MatrixHelpers;
use gpu_cache::GpuCache;
use render_task::{ClipChain, ClipChainNode, ClipWorkItem};
use resource_cache::ResourceCache;
use spring::{DAMPING, STIFFNESS, Spring};
use std::rc::Rc;
use tiling::{PackedLayer, PackedLayerIndex};
use util::{MatrixHelpers, MaxRect};
#[cfg(target_os = "macos")]
const CAN_OVERSCROLL: bool = true;
@ -27,6 +31,9 @@ pub struct ClipInfo {
/// The packed layer index for this node, which is used to render a clip mask
/// for it, if necessary.
pub packed_layer_index: PackedLayerIndex,
/// Whether or not this clip node automatically creates a mask.
pub is_masking: bool,
}
impl ClipInfo {
@ -35,9 +42,13 @@ impl ClipInfo {
packed_layer_index: PackedLayerIndex,
clip_store: &mut ClipStore,
) -> ClipInfo {
let clip_sources = ClipSources::from(clip_region);
let is_masking = clip_sources.is_masking();
ClipInfo {
clip_sources: clip_store.insert(ClipSources::from(clip_region)),
clip_sources: clip_store.insert(clip_sources),
packed_layer_index,
is_masking,
}
}
}
@ -102,9 +113,43 @@ pub struct ClipScrollNode {
/// Whether or not this node is a reference frame.
pub node_type: NodeType,
/// The node in the chain of clips that are necessary to clip display items
/// that have this ClipScrollNode as their clip parent. This will be used to
/// generate clip tasks.
pub clip_chain_node: ClipChain,
/// The intersected outer bounds of the clips for this node.
pub combined_clip_outer_bounds: DeviceIntRect,
/// The axis-aligned coordinate system id of this node.
pub coordinate_system_id: CoordinateSystemId,
}
impl ClipScrollNode {
fn new(
pipeline_id: PipelineId,
parent_id: Option<ClipId>,
rect: &LayerRect,
node_type: NodeType
) -> ClipScrollNode {
ClipScrollNode {
local_viewport_rect: *rect,
local_clip_rect: *rect,
combined_local_viewport_rect: LayerRect::zero(),
world_viewport_transform: LayerToWorldTransform::identity(),
world_content_transform: LayerToWorldTransform::identity(),
reference_frame_relative_scroll_offset: LayerVector2D::zero(),
parent: parent_id,
children: Vec::new(),
pipeline_id,
node_type: node_type,
clip_chain_node: None,
combined_clip_outer_bounds: DeviceIntRect::max_rect(),
coordinate_system_id: CoordinateSystemId(0),
}
}
pub fn new_scroll_frame(
pipeline_id: PipelineId,
parent_id: ClipId,
@ -112,24 +157,15 @@ impl ClipScrollNode {
content_size: &LayerSize,
scroll_sensitivity: ScrollSensitivity,
) -> ClipScrollNode {
ClipScrollNode {
local_viewport_rect: *frame_rect,
local_clip_rect: *frame_rect,
combined_local_viewport_rect: LayerRect::zero(),
world_viewport_transform: LayerToWorldTransform::identity(),
world_content_transform: LayerToWorldTransform::identity(),
reference_frame_relative_scroll_offset: LayerVector2D::zero(),
parent: Some(parent_id),
children: Vec::new(),
pipeline_id,
node_type: NodeType::ScrollFrame(ScrollingState::new(
scroll_sensitivity,
LayerSize::new(
(content_size.width - frame_rect.size.width).max(0.0),
(content_size.height - frame_rect.size.height).max(0.0)
)
)),
}
let node_type = NodeType::ScrollFrame(ScrollingState::new(
scroll_sensitivity,
LayerSize::new(
(content_size.width - frame_rect.size.width).max(0.0),
(content_size.height - frame_rect.size.height).max(0.0)
)
));
Self::new(pipeline_id, Some(parent_id), frame_rect, node_type)
}
pub fn new_clip_node(
@ -138,23 +174,12 @@ impl ClipScrollNode {
clip_info: ClipInfo,
clip_rect: LayerRect,
) -> ClipScrollNode {
ClipScrollNode {
local_viewport_rect: clip_rect,
local_clip_rect: clip_rect,
combined_local_viewport_rect: LayerRect::zero(),
world_viewport_transform: LayerToWorldTransform::identity(),
world_content_transform: LayerToWorldTransform::identity(),
reference_frame_relative_scroll_offset: LayerVector2D::zero(),
parent: Some(parent_id),
children: Vec::new(),
pipeline_id,
node_type: NodeType::Clip(clip_info),
}
Self::new(pipeline_id, Some(parent_id), &clip_rect, NodeType::Clip(clip_info))
}
pub fn new_reference_frame(
parent_id: Option<ClipId>,
local_viewport_rect: &LayerRect,
frame_rect: &LayerRect,
transform: &LayerToScrollTransform,
origin_in_parent_reference_frame: LayerVector2D,
pipeline_id: PipelineId,
@ -163,19 +188,7 @@ impl ClipScrollNode {
transform: *transform,
origin_in_parent_reference_frame,
};
ClipScrollNode {
local_viewport_rect: *local_viewport_rect,
local_clip_rect: *local_viewport_rect,
combined_local_viewport_rect: LayerRect::zero(),
world_viewport_transform: LayerToWorldTransform::identity(),
world_content_transform: LayerToWorldTransform::identity(),
reference_frame_relative_scroll_offset: LayerVector2D::zero(),
parent: parent_id,
children: Vec::new(),
pipeline_id,
node_type: NodeType::ReferenceFrame(info),
}
Self::new(pipeline_id, parent_id, frame_rect, NodeType::ReferenceFrame(info))
}
pub fn new_sticky_frame(
@ -184,18 +197,8 @@ impl ClipScrollNode {
sticky_frame_info: StickyFrameInfo,
pipeline_id: PipelineId,
) -> ClipScrollNode {
ClipScrollNode {
local_viewport_rect: frame_rect,
local_clip_rect: frame_rect,
combined_local_viewport_rect: LayerRect::zero(),
world_viewport_transform: LayerToWorldTransform::identity(),
world_content_transform: LayerToWorldTransform::identity(),
reference_frame_relative_scroll_offset: LayerVector2D::zero(),
parent: Some(parent_id),
children: Vec::new(),
pipeline_id,
node_type: NodeType::StickyFrame(sticky_frame_info, LayerVector2D::zero()),
}
let node_type = NodeType::StickyFrame(sticky_frame_info, LayerVector2D::zero());
Self::new(pipeline_id, Some(parent_id), &frame_rect, node_type)
}
@ -255,7 +258,79 @@ impl ClipScrollNode {
true
}
pub fn update_transform(&mut self, state: &TransformUpdateState) {
pub fn update_clip_work_item(
&mut self,
state: &mut TransformUpdateState,
screen_rect: &DeviceIntRect,
device_pixel_ratio: f32,
packed_layers: &mut Vec<PackedLayer>,
clip_store: &mut ClipStore,
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
) {
self.coordinate_system_id = state.current_coordinate_system_id;
let current_clip_chain = state.parent_clip_chain.clone();
let clip_info = match self.node_type {
NodeType::Clip(ref mut info) if info.is_masking => info,
_ => {
self.clip_chain_node = current_clip_chain;
self.combined_clip_outer_bounds = state.combined_outer_clip_bounds;
return;
}
};
// The coordinates of the mask are relative to the origin of the node itself,
// so we need to account for that origin in the transformation we assign to
// the packed layer.
let transform = self.world_viewport_transform
.pre_translate(self.local_viewport_rect.origin.to_vector().to_3d());
let packed_layer = &mut packed_layers[clip_info.packed_layer_index.0];
if packed_layer.set_transform(transform) {
// Meanwhile, the combined viewport rect is relative to the reference frame, so
// we move it into the local coordinate system of the node.
let local_viewport_rect = self.combined_local_viewport_rect
.translate(&-self.local_viewport_rect.origin.to_vector());
packed_layer.set_rect(
&local_viewport_rect,
screen_rect,
device_pixel_ratio,
);
}
let clip_sources = clip_store.get_mut(&clip_info.clip_sources);
clip_sources.update(
&transform,
gpu_cache,
resource_cache,
device_pixel_ratio,
);
let outer_bounds = clip_sources.bounds.outer.as_ref().map_or_else(
DeviceIntRect::zero,
|rect| rect.device_rect
);
self.combined_clip_outer_bounds = outer_bounds.intersection(
&state.combined_outer_clip_bounds).unwrap_or_else(DeviceIntRect::zero);
// TODO: Combine rectangles in the same axis-aligned clip space here?
self.clip_chain_node = Some(Rc::new(ClipChainNode {
work_item: ClipWorkItem {
layer_index: clip_info.packed_layer_index,
clip_sources: clip_info.clip_sources.weak(),
coordinate_system_id: state.current_coordinate_system_id,
},
prev: current_clip_chain,
}));
state.combined_outer_clip_bounds = self.combined_clip_outer_bounds;
state.parent_clip_chain = self.clip_chain_node.clone();
}
pub fn update_transform(&mut self, state: &mut TransformUpdateState) {
// We calculate this here to avoid a double-borrow later.
let sticky_offset = self.calculate_sticky_offset(
&state.nearest_scrolling_ancestor_offset,
@ -315,6 +390,45 @@ impl ClipScrollNode {
let scroll_offset = self.scroll_offset();
self.world_content_transform = self.world_viewport_transform
.pre_translate(scroll_offset.to_3d());
// The transformation we are passing is the transformation of the parent
// reference frame and the offset is the accumulated offset of all the nodes
// between us and the parent reference frame. If we are a reference frame,
// we need to reset both these values.
match self.node_type {
NodeType::ReferenceFrame(ref info) => {
state.parent_reference_frame_transform = self.world_viewport_transform;
state.parent_combined_viewport_rect = self.combined_local_viewport_rect;
state.parent_accumulated_scroll_offset = LayerVector2D::zero();
state.nearest_scrolling_ancestor_viewport =
state.nearest_scrolling_ancestor_viewport
.translate(&info.origin_in_parent_reference_frame);
if !info.transform.preserves_2d_axis_alignment() {
state.current_coordinate_system_id = state.next_coordinate_system_id;
state.next_coordinate_system_id = state.next_coordinate_system_id.next();
}
},
NodeType::Clip(..) => {
state.parent_combined_viewport_rect = self.combined_local_viewport_rect;
},
NodeType::ScrollFrame(ref scrolling) => {
state.parent_combined_viewport_rect =
self.combined_local_viewport_rect.translate(&-scrolling.offset);
state.parent_accumulated_scroll_offset =
scrolling.offset + state.parent_accumulated_scroll_offset;
state.nearest_scrolling_ancestor_offset = scrolling.offset;
state.nearest_scrolling_ancestor_viewport = self.local_viewport_rect;
}
NodeType::StickyFrame(_, sticky_offset) => {
// We don't translate the combined rect by the sticky offset, because sticky
// offsets actually adjust the node position itself, whereas scroll offsets
// only apply to contents inside the node.
state.parent_combined_viewport_rect = self.combined_local_viewport_rect;
state.parent_accumulated_scroll_offset =
sticky_offset + state.parent_accumulated_scroll_offset;
}
}
}
fn calculate_sticky_offset(

View File

@ -2,16 +2,35 @@
* 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::{ClipId, LayerPoint, LayerRect, LayerToScrollTransform, LayerToWorldTransform};
use api::{LayerVector2D, PipelineId, ScrollClamping, ScrollEventPhase, ScrollLayerState};
use api::{ScrollLocation, StickyFrameInfo, WorldPoint};
use api::{ClipId, DeviceIntRect, LayerPoint, LayerRect};
use api::{LayerToScrollTransform, LayerToWorldTransform, LayerVector2D, PipelineId};
use api::{ScrollClamping, ScrollEventPhase, ScrollLayerState, ScrollLocation, StickyFrameInfo};
use api::WorldPoint;
use clip::ClipStore;
use clip_scroll_node::{ClipScrollNode, NodeType, ScrollingState};
use gpu_cache::GpuCache;
use internal_types::{FastHashMap, FastHashSet};
use print_tree::{PrintTree, PrintTreePrinter};
use render_task::ClipChain;
use resource_cache::ResourceCache;
use tiling::PackedLayer;
pub type ScrollStates = FastHashMap<ClipId, ScrollingState>;
/// An id that identifies coordinate systems in the ClipScrollTree. Each
/// coordinate system has an id and those ids will be shared when the coordinates
/// system are the same or are in the same axis-aligned space. This allows
/// for optimizing mask generation.
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct CoordinateSystemId(pub u32);
impl CoordinateSystemId {
pub fn next(&self) -> CoordinateSystemId {
let CoordinateSystemId(id) = *self;
CoordinateSystemId(id + 1)
}
}
pub struct ClipScrollTree {
pub nodes: FastHashMap<ClipId, ClipScrollNode>,
pub pending_scroll_offsets: FastHashMap<ClipId, (LayerPoint, ScrollClamping)>,
@ -38,12 +57,22 @@ pub struct ClipScrollTree {
pub pipelines_to_discard: FastHashSet<PipelineId>,
}
#[derive(Clone)]
pub struct TransformUpdateState {
pub parent_reference_frame_transform: LayerToWorldTransform,
pub parent_combined_viewport_rect: LayerRect,
pub parent_accumulated_scroll_offset: LayerVector2D,
pub nearest_scrolling_ancestor_offset: LayerVector2D,
pub nearest_scrolling_ancestor_viewport: LayerRect,
pub parent_clip_chain: ClipChain,
pub combined_outer_clip_bounds: DeviceIntRect,
/// An id for keeping track of the axis-aligned space of this node. This is used in
/// order to to track what kinds of clip optimizations can be done for a particular
/// display list item, since optimizations can usually only be done among
/// coordinate systems which are relatively axis aligned.
pub current_coordinate_system_id: CoordinateSystemId,
pub next_coordinate_system_id: CoordinateSystemId,
}
impl ClipScrollTree {
@ -297,14 +326,24 @@ impl ClipScrollTree {
.scroll(scroll_location, phase)
}
pub fn update_all_node_transforms(&mut self, pan: LayerPoint) {
pub fn update_all_node_transforms(
&mut self,
screen_rect: &DeviceIntRect,
device_pixel_ratio: f32,
packed_layers: &mut Vec<PackedLayer>,
clip_store: &mut ClipStore,
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
pan: LayerPoint,
) {
if self.nodes.is_empty() {
return;
}
let root_reference_frame_id = self.root_reference_frame_id();
let root_viewport = self.nodes[&root_reference_frame_id].local_clip_rect;
let state = TransformUpdateState {
let mut state = TransformUpdateState {
parent_reference_frame_transform: LayerToWorldTransform::create_translation(
pan.x,
pan.y,
@ -314,63 +353,68 @@ impl ClipScrollTree {
parent_accumulated_scroll_offset: LayerVector2D::zero(),
nearest_scrolling_ancestor_offset: LayerVector2D::zero(),
nearest_scrolling_ancestor_viewport: LayerRect::zero(),
parent_clip_chain: None,
combined_outer_clip_bounds: *screen_rect,
current_coordinate_system_id: CoordinateSystemId(0),
next_coordinate_system_id: CoordinateSystemId(0).next(),
};
self.update_node_transform(root_reference_frame_id, &state);
self.update_node_transform(
root_reference_frame_id,
&mut state,
&screen_rect,
device_pixel_ratio,
packed_layers,
clip_store,
resource_cache,
gpu_cache,
);
}
fn update_node_transform(&mut self, layer_id: ClipId, state: &TransformUpdateState) {
fn update_node_transform(
&mut self,
layer_id: ClipId,
state: &mut TransformUpdateState,
screen_rect: &DeviceIntRect,
device_pixel_ratio: f32,
packed_layers: &mut Vec<PackedLayer>,
clip_store: &mut ClipStore,
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
) {
// TODO(gw): This is an ugly borrow check workaround to clone these.
// Restructure this to avoid the clones!
let (state, node_children) = {
let mut state = state.clone();
let node_children = {
let node = match self.nodes.get_mut(&layer_id) {
Some(node) => node,
None => return,
};
node.update_transform(&state);
// The transformation we are passing is the transformation of the parent
// reference frame and the offset is the accumulated offset of all the nodes
// between us and the parent reference frame. If we are a reference frame,
// we need to reset both these values.
let state = match node.node_type {
NodeType::ReferenceFrame(ref info) => TransformUpdateState {
parent_reference_frame_transform: node.world_viewport_transform,
parent_combined_viewport_rect: node.combined_local_viewport_rect,
parent_accumulated_scroll_offset: LayerVector2D::zero(),
nearest_scrolling_ancestor_viewport: state
.nearest_scrolling_ancestor_viewport
.translate(&info.origin_in_parent_reference_frame),
..*state
},
NodeType::Clip(..) => TransformUpdateState {
parent_combined_viewport_rect: node.combined_local_viewport_rect,
..*state
},
NodeType::ScrollFrame(ref scrolling) => TransformUpdateState {
parent_combined_viewport_rect:
node.combined_local_viewport_rect.translate(&-scrolling.offset),
parent_accumulated_scroll_offset: scrolling.offset +
state.parent_accumulated_scroll_offset,
nearest_scrolling_ancestor_offset: scrolling.offset,
nearest_scrolling_ancestor_viewport: node.local_viewport_rect,
..*state
},
NodeType::StickyFrame(_, sticky_offset) => TransformUpdateState {
// We don't translate the combined rect by the sticky offset, because sticky
// offsets actually adjust the node position itself, whereas scroll offsets
// only apply to contents inside the node.
parent_combined_viewport_rect: node.combined_local_viewport_rect,
parent_accumulated_scroll_offset:
sticky_offset + state.parent_accumulated_scroll_offset,
..*state
}
};
node.update_transform(&mut state);
node.update_clip_work_item(
&mut state,
screen_rect,
device_pixel_ratio,
packed_layers,
clip_store,
resource_cache,
gpu_cache,
);
(state, node.children.clone())
node.children.clone()
};
for child_layer_id in node_children {
self.update_node_transform(child_layer_id, &state);
self.update_node_transform(
child_layer_id,
&mut state,
screen_rect,
device_pixel_ratio,
packed_layers,
clip_store,
resource_cache,
gpu_cache,
);
}
}

View File

@ -485,8 +485,7 @@ pub struct VBOId(gl::GLuint);
#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
struct IBOId(gl::GLuint);
const MAX_TIMERS_PER_FRAME: usize = 256;
const MAX_SAMPLERS_PER_FRAME: usize = 16;
#[cfg(feature = "query")]
const MAX_PROFILE_FRAMES: usize = 4;
pub trait NamedTag {
@ -505,12 +504,14 @@ pub struct GpuSampler<T> {
pub count: u64,
}
#[cfg(feature = "query")]
pub struct QuerySet<T> {
set: Vec<gl::GLuint>,
data: Vec<T>,
pending: gl::GLuint,
}
#[cfg(feature = "query")]
impl<T> QuerySet<T> {
fn new(set: Vec<gl::GLuint>) -> Self {
QuerySet {
@ -543,6 +544,7 @@ impl<T> QuerySet<T> {
}
}
#[cfg(feature = "query")]
pub struct GpuFrameProfile<T> {
gl: Rc<gl::Gl>,
timers: QuerySet<GpuTimer<T>>,
@ -551,15 +553,19 @@ pub struct GpuFrameProfile<T> {
inside_frame: bool,
}
#[cfg(feature = "query")]
impl<T> GpuFrameProfile<T> {
const MAX_TIMERS_PER_FRAME: usize = 256;
// disable samplers on OSX due to driver bugs
#[cfg(target_os = "macos")]
const MAX_SAMPLERS_PER_FRAME: usize = 0;
#[cfg(not(target_os = "macos"))]
const MAX_SAMPLERS_PER_FRAME: usize = 16;
fn new(gl: Rc<gl::Gl>) -> Self {
let (time_queries, sample_queries) = match gl.get_type() {
gl::GlType::Gl => (
gl.gen_queries(MAX_TIMERS_PER_FRAME as gl::GLint),
gl.gen_queries(MAX_SAMPLERS_PER_FRAME as gl::GLint),
),
gl::GlType::Gles => (Vec::new(), Vec::new()),
};
assert_eq!(gl.get_type(), gl::GlType::Gl);
let time_queries = gl.gen_queries(Self::MAX_TIMERS_PER_FRAME as _);
let sample_queries = gl.gen_queries(Self::MAX_SAMPLERS_PER_FRAME as _);
GpuFrameProfile {
gl,
@ -607,26 +613,22 @@ impl<T> GpuFrameProfile<T> {
}
fn done_sampler(&mut self) {
/* FIXME: samplers crash on MacOS
debug_assert!(self.inside_frame);
if self.samplers.pending != 0 {
self.gl.end_query(gl::SAMPLES_PASSED);
self.samplers.pending = 0;
}
*/
}
fn add_sampler(&mut self, _tag: T)
fn add_sampler(&mut self, tag: T)
where
T: NamedTag,
{
/* FIXME: samplers crash on MacOS
self.done_sampler();
if let Some(query) = self.samplers.add(GpuSampler { tag, count: 0 }) {
self.gl.begin_query(gl::SAMPLES_PASSED, query);
}
*/
}
fn is_valid(&self) -> bool {
@ -648,25 +650,27 @@ impl<T> GpuFrameProfile<T> {
}
}
#[cfg(feature = "query")]
impl<T> Drop for GpuFrameProfile<T> {
fn drop(&mut self) {
match self.gl.get_type() {
gl::GlType::Gl => {
self.gl.delete_queries(&self.timers.set);
self.gl.delete_queries(&self.samplers.set);
}
gl::GlType::Gles => {}
if !self.timers.set.is_empty() {
self.gl.delete_queries(&self.timers.set);
}
if !self.samplers.set.is_empty() {
self.gl.delete_queries(&self.samplers.set);
}
}
}
#[cfg(feature = "query")]
pub struct GpuProfiler<T> {
frames: [GpuFrameProfile<T>; MAX_PROFILE_FRAMES],
next_frame: usize,
}
#[cfg(feature = "query")]
impl<T> GpuProfiler<T> {
pub fn new(gl: &Rc<gl::Gl>) -> GpuProfiler<T> {
pub fn new(gl: &Rc<gl::Gl>) -> Self {
GpuProfiler {
next_frame: 0,
frames: [
@ -718,44 +722,70 @@ impl<T> GpuProfiler<T> {
}
}
#[cfg(not(feature = "query"))]
pub struct GpuProfiler<T>(Option<T>);
#[cfg(not(feature = "query"))]
impl<T> GpuProfiler<T> {
pub fn new(_: &Rc<gl::Gl>) -> Self {
GpuProfiler(None)
}
pub fn build_samples(&mut self) -> Option<(FrameId, Vec<GpuTimer<T>>, Vec<GpuSampler<T>>)> {
None
}
pub fn begin_frame(&mut self, _: FrameId) {}
pub fn end_frame(&mut self) {}
pub fn add_marker(&mut self, _: T) -> GpuMarker {
GpuMarker {}
}
pub fn add_sampler(&mut self, _: T) {}
pub fn done_sampler(&mut self) {}
}
#[must_use]
pub struct GpuMarker {
#[cfg(feature = "query")]
gl: Rc<gl::Gl>,
}
#[cfg(feature = "query")]
impl GpuMarker {
pub fn new(gl: &Rc<gl::Gl>, message: &str) -> GpuMarker {
match gl.get_type() {
gl::GlType::Gl => {
gl.push_group_marker_ext(message);
GpuMarker { gl: Rc::clone(gl) }
}
gl::GlType::Gles => GpuMarker { gl: Rc::clone(gl) },
}
pub fn new(gl: &Rc<gl::Gl>, message: &str) -> Self {
debug_assert_eq!(gl.get_type(), gl::GlType::Gl);
gl.push_group_marker_ext(message);
GpuMarker { gl: Rc::clone(gl) }
}
pub fn fire(gl: &gl::Gl, message: &str) {
match gl.get_type() {
gl::GlType::Gl => {
gl.insert_event_marker_ext(message);
}
gl::GlType::Gles => {}
}
debug_assert_eq!(gl.get_type(), gl::GlType::Gl);
gl.insert_event_marker_ext(message);
}
}
#[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
#[cfg(feature = "query")]
impl Drop for GpuMarker {
fn drop(&mut self) {
match self.gl.get_type() {
gl::GlType::Gl => {
self.gl.pop_group_marker_ext();
}
gl::GlType::Gles => {}
}
self.gl.pop_group_marker_ext();
}
}
#[cfg(not(feature = "query"))]
impl GpuMarker {
#[inline]
pub fn new(_: &Rc<gl::Gl>, _: &str) -> Self {
GpuMarker{}
}
#[inline]
pub fn fire(_: &gl::Gl, _: &str) {}
}
#[derive(Debug, Copy, Clone)]
pub enum VertexUsageHint {

View File

@ -222,6 +222,10 @@ impl Frame {
.finalize_and_apply_pending_scroll_offsets(old_scrolling_states);
}
pub fn update_epoch(&mut self, pipeline_id: PipelineId, epoch: Epoch) {
self.pipeline_epoch_map.insert(pipeline_id, epoch);
}
fn flatten_clip<'a>(
&mut self,
context: &mut FlattenContext,
@ -1095,7 +1099,7 @@ impl Frame {
}
}
pub fn build(
pub fn build_renderer_frame(
&mut self,
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
@ -1105,29 +1109,6 @@ impl Frame {
output_pipelines: &FastHashSet<PipelineId>,
texture_cache_profile: &mut TextureCacheProfileCounters,
gpu_cache_profile: &mut GpuCacheProfileCounters,
) -> RendererFrame {
self.clip_scroll_tree.update_all_node_transforms(pan);
let frame = self.build_frame(
resource_cache,
gpu_cache,
pipelines,
device_pixel_ratio,
output_pipelines,
texture_cache_profile,
gpu_cache_profile,
);
frame
}
fn build_frame(
&mut self,
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
pipelines: &FastHashMap<PipelineId, ScenePipeline>,
device_pixel_ratio: f32,
output_pipelines: &FastHashSet<PipelineId>,
texture_cache_profile: &mut TextureCacheProfileCounters,
gpu_cache_profile: &mut GpuCacheProfileCounters,
) -> RendererFrame {
let mut frame_builder = self.frame_builder.take();
let frame = frame_builder.as_mut().map(|builder| {
@ -1138,6 +1119,7 @@ impl Frame {
&mut self.clip_scroll_tree,
pipelines,
device_pixel_ratio,
pan,
output_pipelines,
texture_cache_profile,
gpu_cache_profile,

View File

@ -3,43 +3,42 @@
* 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, FIND_ALL, FilterOp, FontInstance, FontRenderMode};
use api::{ExtendMode, FilterOp, FontInstance, FontRenderMode};
use api::{GlyphInstance, GlyphOptions, GradientStop, HitTestFlags, HitTestItem, HitTestResult};
use api::{ImageKey, ImageRendering, ItemRange, ItemTag, LayerPoint, LayerPrimitiveInfo, LayerRect};
use api::{LayerPixel, LayerSize, LayerToScrollTransform, LayerVector2D, LayoutVector2D, LineOrientation};
use api::{LineStyle, LocalClip, POINT_RELATIVE_TO_PIPELINE_VIEWPORT, PipelineId, RepeatMode};
use api::{LineStyle, LocalClip, PipelineId, RepeatMode};
use api::{ScrollSensitivity, Shadow, TileOffset, TransformStyle};
use api::{WorldPixel, WorldPoint, YuvColorSpace, YuvData, device_length};
use app_units::Au;
use border::ImageBorderSegment;
use clip::{ClipMode, ClipRegion, ClipSource, ClipSources, ClipStore, Contains};
use clip_scroll_node::{ClipInfo, ClipScrollNode, NodeType};
use clip_scroll_tree::ClipScrollTree;
use euclid::{SideOffsets2D, vec2, vec3};
use clip_scroll_tree::{ClipScrollTree, CoordinateSystemId};
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};
use prim_store::{RectanglePrimitive, TextRunPrimitiveCpu};
use profiler::{FrameProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters};
use render_task::{AlphaRenderItem, ClipWorkItem, RenderTask};
use render_task::{RenderTaskId, RenderTaskLocation, RenderTaskTree};
use render_task::{AlphaRenderItem, ClipChain, RenderTask, RenderTaskId, RenderTaskLocation};
use render_task::RenderTaskTree;
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, pack_as_float, recycle_vec, subtract_rect};
use util::{MatrixHelpers, RectHelpers};
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
@ -48,7 +47,7 @@ fn make_polygon(
stacking_context: &StackingContext,
node: &ClipScrollNode,
anchor: usize,
) -> Polygon<f32, WorldPixel> {
) -> Polygon<f64, WorldPixel> {
//TODO: only work with `isolated_items_bounds.size` worth of space
// This can be achieved by moving the `origin` shift
// from the primitive local coordinates into the layer transformation.
@ -56,7 +55,24 @@ fn make_polygon(
// upon rendering, possibly not limited to `write_*_vertex` implementations.
let size = stacking_context.isolated_items_bounds.bottom_right();
let bounds = LayerRect::new(LayerPoint::zero(), LayerSize::new(size.x, size.y));
Polygon::from_transformed_rect(bounds, node.world_content_transform, anchor)
let mat = TypedTransform3D::row_major(
node.world_content_transform.m11 as f64,
node.world_content_transform.m12 as f64,
node.world_content_transform.m13 as f64,
node.world_content_transform.m14 as f64,
node.world_content_transform.m21 as f64,
node.world_content_transform.m22 as f64,
node.world_content_transform.m23 as f64,
node.world_content_transform.m24 as f64,
node.world_content_transform.m31 as f64,
node.world_content_transform.m32 as f64,
node.world_content_transform.m33 as f64,
node.world_content_transform.m34 as f64,
node.world_content_transform.m41 as f64,
node.world_content_transform.m42 as f64,
node.world_content_transform.m43 as f64,
node.world_content_transform.m44 as f64);
Polygon::from_transformed_rect(bounds.cast().unwrap(), mat, anchor)
}
#[derive(Clone, Copy)]
@ -126,15 +142,10 @@ pub struct PrimitiveContext<'a> {
pub packed_layer_index: PackedLayerIndex,
pub packed_layer: &'a PackedLayer,
pub device_pixel_ratio: f32,
// Clip items that apply for this primitive run.
// In the future, we'll build these once at the
// start of the frame when updating the
// clip-scroll tree.
pub current_clip_stack: Vec<ClipWorkItem>,
pub clip_chain: ClipChain,
pub clip_bounds: DeviceIntRect,
pub clip_id: ClipId,
pub coordinate_system_id: CoordinateSystemId,
pub display_list: &'a BuiltDisplayList,
}
@ -143,68 +154,22 @@ impl<'a> PrimitiveContext<'a> {
packed_layer_index: PackedLayerIndex,
packed_layer: &'a PackedLayer,
clip_id: ClipId,
screen_rect: &DeviceIntRect,
clip_scroll_tree: &ClipScrollTree,
clip_store: &ClipStore,
clip_chain: ClipChain,
clip_bounds: DeviceIntRect,
coordinate_system_id: CoordinateSystemId,
device_pixel_ratio: f32,
display_list: &'a BuiltDisplayList,
) -> Option<Self> {
let mut current_clip_stack = Vec::new();
let mut clip_bounds = *screen_rect;
let mut current_id = Some(clip_id);
// Indicates if the next non-reference-frame that we encounter needs to have its
// local combined clip rectangle backed into the clip mask.
let mut next_node_needs_region_mask = false;
while let Some(id) = current_id {
let node = &clip_scroll_tree.nodes.get(&id).unwrap();
current_id = node.parent;
let clip = match node.node_type {
NodeType::ReferenceFrame(ref info) => {
// if the transform is non-aligned, bake the next LCCR into the clip mask
next_node_needs_region_mask |= !info.transform.preserves_2d_axis_alignment();
continue;
}
NodeType::Clip(ref clip) => clip,
NodeType::StickyFrame(..) | NodeType::ScrollFrame(..) => {
continue;
}
};
let clip_sources = clip_store.get(&clip.clip_sources);
if !clip_sources.is_masking() {
continue;
}
// apply the outer device bounds of the clip stack
if let Some(ref outer) = clip_sources.bounds.outer {
clip_bounds = match clip_bounds.intersection(&outer.device_rect) {
Some(rect) => rect,
None => return None,
}
}
//TODO-LCCR: bake a single LCCR instead of all aligned rects?
current_clip_stack.push(ClipWorkItem {
layer_index: clip.packed_layer_index,
clip_sources: clip.clip_sources.weak(),
apply_rectangles: next_node_needs_region_mask,
});
next_node_needs_region_mask = false;
}
current_clip_stack.reverse();
Some(PrimitiveContext {
) -> Self {
PrimitiveContext {
packed_layer_index,
packed_layer,
current_clip_stack,
clip_chain,
clip_bounds,
coordinate_system_id,
device_pixel_ratio,
clip_id,
display_list,
})
}
}
}
@ -371,6 +336,7 @@ impl FrameBuilder {
clip_node_id: info.clip_node_id(),
packed_layer_index,
screen_bounding_rect: None,
coordinate_system_id: CoordinateSystemId(0),
});
group_id
@ -591,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
@ -1196,7 +1162,7 @@ impl FrameBuilder {
font.variations.clone(),
font.synthetic_italics,
);
let mut prim = TextRunPrimitiveCpu {
let prim = TextRunPrimitiveCpu {
font: prim_font,
glyph_range,
glyph_count,
@ -1221,12 +1187,6 @@ impl FrameBuilder {
if shadow.blur_radius == 0.0 {
let mut text_prim = prim.clone();
text_prim.font.color = shadow.color.into();
// If we have translucent text, we need to ensure it won't go
// through the subpixel blend mode, which doesn't work with
// traditional alpha blending.
if shadow.color.a != 1.0 {
text_prim.font.render_mode = text_prim.font.render_mode.limit_by(FontRenderMode::Alpha);
}
text_prim.offset += shadow.offset;
fast_shadow_prims.push((idx, text_prim));
}
@ -1245,12 +1205,6 @@ impl FrameBuilder {
self.shadow_prim_stack[idx].1.push((prim_index, clip_and_scroll));
}
// We defer this until after fast-shadows so that shadows of transparent text
// get subpixel-aa
if color.a != 1.0 {
prim.font.render_mode = FontRenderMode::Alpha;
}
// Create (and add to primitive store) the primitive that will be
// used for both the visual element and also the shadow(s).
let prim_index = self.create_primitive(
@ -1296,47 +1250,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,
@ -1348,162 +1265,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 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;
}
if blur_radius == 0.0 && border_radius != 0.0 {
self.fill_box_shadow_rect(
clip_and_scroll,
info,
bs_rect,
color,
border_radius,
clip_mode,
);
return;
}
// 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<LayerRect>), // Can be drawn via simple rectangles only
Shadow(Vec<LayerRect>), // Requires the full box-shadow code path
}
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)
}
let spread_amount = match clip_mode {
BoxShadowClipMode::Outset => {
spread_radius
}
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)
}
-spread_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(
clip_and_scroll,
info,
bs_rect,
color,
border_radius,
clip_mode,
);
}
// 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);
let inverted = match clip_mode {
BoxShadowClipMode::Outset | BoxShadowClipMode::None => 0.0,
BoxShadowClipMode::Inset => 1.0,
};
if blur_radius == 0.0 {
let mut clips = Vec::new();
// 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 mut extra_clips = Vec::new();
if border_radius >= 0.0 {
extra_clips.push(ClipSource::RoundedRectangle(
info.rect,
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),
extra_clip_mode,
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
));
let prim_cpu = BoxShadowPrimitiveCpu {
src_rect: info.rect,
bs_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,
&fast_info,
clips,
PrimitiveContainer::Rectangle(RectanglePrimitive {
color: *color,
blur_radius,
border_radius,
edge_size,
inverted,
rects,
render_task_id: None,
};
}),
);
} else {
let shadow = Shadow {
blur_radius,
color: *color,
offset: LayerVector2D::zero(),
};
let mut info = info.clone();
info.rect = outer_rect;
self.add_primitive(
clip_and_scroll,
&info,
extra_clips,
PrimitiveContainer::BoxShadow(prim_cpu),
);
}
let blur_offset = 2.0 * blur_radius;
let mut extra_clips = vec![];
let mut pic_prim = PicturePrimitive::new_shadow(shadow, RenderTargetKind::Alpha);
let pic_info = match clip_mode {
BoxShadowClipMode::Outset => {
let brush_prim = BrushPrimitive {
clip_mode: ClipMode::Clip,
radius: shadow_radius,
};
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,
&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 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;
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);
extra_clips.push(ClipSource::RoundedRectangle(
prim_info.rect,
BorderRadius::uniform(border_radius),
ClipMode::Clip,
));
let pic_rect = prim_info.rect.inflate(blur_offset, blur_offset);
LayerPrimitiveInfo::with_clip_rect(pic_rect, prim_info.rect)
}
};
self.add_primitive(
clip_and_scroll,
&pic_info,
extra_clips,
PrimitiveContainer::Picture(pic_prim),
);
}
}
@ -1609,7 +1516,7 @@ impl FrameBuilder {
point: WorldPoint,
flags: HitTestFlags
) -> HitTestResult {
let point = if flags.contains(POINT_RELATIVE_TO_PIPELINE_VIEWPORT) {
let point = if flags.contains(HitTestFlags::POINT_RELATIVE_TO_PIPELINE_VIEWPORT) {
let point = LayerPoint::new(point.x, point.y);
clip_scroll_tree.make_node_relative_point_absolute(pipeline_id, &point)
} else {
@ -1659,8 +1566,9 @@ impl FrameBuilder {
pipeline: clip_and_scroll.clip_node_id().pipeline_id(),
tag: item.tag,
point_in_viewport,
point_relative_to_item: point_in_layer - item.rect.origin.to_vector(),
});
if !flags.contains(FIND_ALL) {
if !flags.contains(HitTestFlags::FIND_ALL) {
return result;
}
}
@ -1681,7 +1589,6 @@ impl FrameBuilder {
resource_cache: &mut ResourceCache,
pipelines: &FastHashMap<PipelineId, ScenePipeline>,
clip_scroll_tree: &ClipScrollTree,
screen_rect: &DeviceIntRect,
device_pixel_ratio: f32,
profile_counters: &mut FrameProfileCounters,
) -> bool {
@ -1695,9 +1602,27 @@ impl FrameBuilder {
}
};
let (clip_chain, clip_bounds, coordinate_system_id) =
match clip_scroll_tree.nodes.get(&clip_and_scroll.clip_node_id()) {
Some(node) if node.combined_clip_outer_bounds != DeviceIntRect::zero() => {
let group_id = self.clip_scroll_group_indices[&clip_and_scroll];
(
node.clip_chain_node.clone(),
node.combined_clip_outer_bounds,
self.clip_scroll_group_store[group_id].coordinate_system_id,
)
}
_ => {
let group_id = self.clip_scroll_group_indices[&clip_and_scroll];
self.clip_scroll_group_store[group_id].screen_bounding_rect = None;
debug!("{:?} of clipped out {:?}", base_prim_index, stacking_context_index);
return false;
}
};
let stacking_context = &mut self.stacking_context_store[stacking_context_index.0];
let pipeline_id = {
let stacking_context =
&mut self.stacking_context_store[stacking_context_index.0];
if !stacking_context.can_contribute_to_scene() {
return false;
}
@ -1715,8 +1640,6 @@ impl FrameBuilder {
packed_layer_index
);
let stacking_context =
&mut self.stacking_context_store[stacking_context_index.0];
let packed_layer = &self.packed_layers[packed_layer_index.0];
let display_list = &pipelines
.get(&pipeline_id)
@ -1731,22 +1654,13 @@ impl FrameBuilder {
packed_layer_index,
packed_layer,
clip_and_scroll.clip_node_id(),
screen_rect,
clip_scroll_tree,
&self.clip_store,
clip_chain,
clip_bounds,
coordinate_system_id,
device_pixel_ratio,
display_list,
);
let prim_context = match prim_context {
Some(prim_context) => prim_context,
None => {
let group_id = self.clip_scroll_group_indices[&clip_and_scroll];
self.clip_scroll_group_store[group_id].screen_bounding_rect = None;
return false
},
};
debug!(
"\tclip_bounds {:?}, layer_local_clip {:?}",
prim_context.clip_bounds,
@ -1836,52 +1750,6 @@ impl FrameBuilder {
}
}
fn recalculate_clip_scroll_nodes(
&mut self,
clip_scroll_tree: &mut ClipScrollTree,
gpu_cache: &mut GpuCache,
resource_cache: &mut ResourceCache,
screen_rect: &DeviceIntRect,
device_pixel_ratio: f32
) {
for (_, ref mut node) in clip_scroll_tree.nodes.iter_mut() {
let node_clip_info = match node.node_type {
NodeType::Clip(ref mut clip_info) => clip_info,
_ => continue,
};
let packed_layer_index = node_clip_info.packed_layer_index;
let packed_layer = &mut self.packed_layers[packed_layer_index.0];
// The coordinates of the mask are relative to the origin of the node itself,
// so we need to account for that origin in the transformation we assign to
// the packed layer.
let transform = node.world_viewport_transform
.pre_translate(node.local_viewport_rect.origin.to_vector().to_3d());
if packed_layer.set_transform(transform) {
// Meanwhile, the combined viewport rect is relative to the reference frame, so
// we move it into the local coordinate system of the node.
let local_viewport_rect = node.combined_local_viewport_rect
.translate(&-node.local_viewport_rect.origin.to_vector());
packed_layer.set_rect(
&local_viewport_rect,
screen_rect,
device_pixel_ratio,
);
}
let clip_sources = self.clip_store.get_mut(&node_clip_info.clip_sources);
clip_sources.update(
&transform,
gpu_cache,
resource_cache,
device_pixel_ratio,
);
}
}
fn recalculate_clip_scroll_groups(
&mut self,
clip_scroll_tree: &ClipScrollTree,
@ -1920,6 +1788,8 @@ impl FrameBuilder {
device_pixel_ratio,
);
group.coordinate_system_id = scroll_node.coordinate_system_id;
debug!(
"\t\tlocal viewport {:?} screen bound {:?}",
local_viewport_rect,
@ -1943,13 +1813,6 @@ impl FrameBuilder {
) {
profile_scope!("cull");
self.recalculate_clip_scroll_nodes(
clip_scroll_tree,
gpu_cache,
resource_cache,
screen_rect,
device_pixel_ratio
);
self.recalculate_clip_scroll_groups(
clip_scroll_tree,
screen_rect,
@ -1973,7 +1836,6 @@ impl FrameBuilder {
resource_cache,
pipelines,
clip_scroll_tree,
screen_rect,
device_pixel_ratio,
profile_counters,
);
@ -2176,10 +2038,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(
@ -2263,9 +2128,9 @@ impl FrameBuilder {
debug!("\t\tproduce {:?} -> {:?} for {:?}", sc_index, poly, task_id);
let pp = &poly.points;
let gpu_blocks = [
[pp[0].x, pp[0].y, pp[0].z, pp[1].x].into(),
[pp[1].y, pp[1].z, pp[2].x, pp[2].y].into(),
[pp[2].z, pp[3].x, pp[3].y, pp[3].z].into(),
[pp[0].x as f32, pp[0].y as f32, pp[0].z as f32, pp[1].x as f32].into(),
[pp[1].y as f32, pp[1].z as f32, pp[2].x as f32, pp[2].y as f32].into(),
[pp[2].z as f32, pp[3].x as f32, pp[3].y as f32, pp[3].z as f32].into(),
];
let handle = gpu_cache.push_per_frame_blocks(&gpu_blocks);
let item =
@ -2343,6 +2208,7 @@ impl FrameBuilder {
clip_scroll_tree: &mut ClipScrollTree,
pipelines: &FastHashMap<PipelineId, ScenePipeline>,
device_pixel_ratio: f32,
pan: LayerPoint,
output_pipelines: &FastHashSet<PipelineId>,
texture_cache_profile: &mut TextureCacheProfileCounters,
gpu_cache_profile: &mut GpuCacheProfileCounters,
@ -2365,6 +2231,16 @@ impl FrameBuilder {
),
);
clip_scroll_tree.update_all_node_transforms(
&screen_rect,
device_pixel_ratio,
&mut self.packed_layers,
&mut self.clip_store,
resource_cache,
gpu_cache,
pan
);
self.update_scroll_bars(clip_scroll_tree, gpu_cache);
let mut render_tasks = RenderTaskTree::new();

View File

@ -188,7 +188,7 @@ impl GlyphRasterizer {
for key in glyph_keys {
match glyph_key_cache.entry(key.clone()) {
Entry::Occupied(mut entry) => {
if let Some(ref mut glyph_info) = *entry.get_mut() {
if let Ok(Some(ref mut glyph_info)) = *entry.get_mut() {
if texture_cache.request(&mut glyph_info.texture_cache_handle, gpu_cache) {
// This case gets hit when we have already rasterized
// the glyph and stored it in CPU memory, the the glyph
@ -352,7 +352,7 @@ impl GlyphRasterizer {
let glyph_key_cache = glyph_cache.get_glyph_key_cache_for_font_mut(job.request.font);
glyph_key_cache.insert(job.request.key, glyph_info);
glyph_key_cache.insert(job.request.key, Ok(glyph_info));
}
// Now that we are done with the critical path (rendering the glyphs),

View File

@ -17,14 +17,6 @@ impl From<PackedLayerIndex> 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<CompositePrimitiveInstance> 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<BrushInstance> 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,
]
}
}
}

View File

@ -150,7 +150,6 @@ extern crate gamma_lut;
#[doc(hidden)]
pub use device::build_shader_strings;
pub use renderer::{ALPHA_PRIM_DBG, PROFILER_DBG, RENDER_TARGET_DBG, TEXTURE_CACHE_DBG};
pub use renderer::{CpuProfile, DebugFlags, GpuProfile, OutputImageHandler, RendererKind};
pub use renderer::{ExternalImage, ExternalImageHandler, ExternalImageSource};
pub use renderer::{GraphicsApi, GraphicsApiInfo, ReadPixelsFormat, Renderer, RendererOptions};

View File

@ -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<PrimitiveRun>,
pub composite_op: CompositeOp,
pub render_task_id: Option<RenderTaskId>,
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,
}
}

View File

@ -278,12 +278,6 @@ impl FontContext {
) -> Option<GlyphDimensions> {
let metrics = unsafe { &(*slot).metrics };
// If there's no advance, no need to consider this glyph
// for layout.
if metrics.horiAdvance == 0 {
return None
}
let advance = metrics.horiAdvance as f32 / 64.0;
match unsafe { (*slot).format } {
FT_Glyph_Format::FT_GLYPH_FORMAT_BITMAP => {

View File

@ -7,18 +7,17 @@ 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};
use picture::PicturePrimitive;
use render_task::{ClipWorkItem, RenderTask, RenderTaskId, RenderTaskTree};
use render_task::{ClipWorkItem, ClipChainNode, RenderTask, RenderTaskId, RenderTaskTree};
use renderer::MAX_VERTEX_TEXTURE_WIDTH;
use resource_cache::{ImageProperties, ResourceCache};
use std::{mem, usize};
use std::rc::Rc;
use util::{MatrixHelpers, pack_as_float, recycle_vec, TransformedRect};
#[derive(Debug, Copy, Clone)]
@ -110,9 +109,9 @@ pub enum PrimitiveKind {
AlignedGradient,
AngleGradient,
RadialGradient,
BoxShadow,
Line,
Picture,
Brush,
}
impl GpuCacheHandle {
@ -165,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 {
@ -231,46 +247,6 @@ impl ToGpuBlocks for BorderPrimitiveCpu {
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct BoxShadowPrimitiveCacheKey {
pub shadow_rect_size: Size2D<Au>,
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<LayerRect>,
pub render_task_id: Option<RenderTaskId>,
}
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<GradientStop>,
@ -804,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<RectanglePrimitive>,
pub cpu_brushes: Vec<BrushPrimitive>,
pub cpu_text_runs: Vec<TextRunPrimitiveCpu>,
pub cpu_pictures: Vec<PicturePrimitive>,
pub cpu_images: Vec<ImagePrimitiveCpu>,
@ -820,7 +797,6 @@ pub struct PrimitiveStore {
pub cpu_radial_gradients: Vec<RadialGradientPrimitiveCpu>,
pub cpu_metadata: Vec<PrimitiveMetadata>,
pub cpu_borders: Vec<BorderPrimitiveCpu>,
pub cpu_box_shadows: Vec<BoxShadowPrimitiveCpu>,
pub cpu_lines: Vec<LinePrimitive>,
}
@ -829,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(),
@ -836,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(),
}
}
@ -845,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),
@ -852,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),
}
}
@ -896,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(),
@ -997,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);
@ -1029,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
@ -1045,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 {
@ -1057,7 +1031,6 @@ impl PrimitiveStore {
}
}
/// Returns true if the bounding box needs to be updated.
fn prepare_prim_for_render_inner(
&mut self,
prim_index: PrimitiveIndex,
@ -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);
}
}
}
}
@ -1283,22 +1226,26 @@ impl PrimitiveStore {
_ => prim_screen_rect,
};
let extra = ClipWorkItem {
layer_index: prim_context.packed_layer_index,
clip_sources: metadata.clip_sources.weak(),
apply_rectangles: false,
};
let extra_clip = Some(Rc::new(ClipChainNode {
work_item: ClipWorkItem {
layer_index: prim_context.packed_layer_index,
clip_sources: metadata.clip_sources.weak(),
coordinate_system_id: prim_context.coordinate_system_id,
},
prev: None,
}));
RenderTask::new_mask(
None,
mask_rect,
&prim_context.current_clip_stack,
Some(extra),
prim_context.clip_chain.clone(),
extra_clip,
prim_screen_rect,
clip_store,
is_axis_aligned,
prim_context.coordinate_system_id,
)
} else if !prim_context.current_clip_stack.is_empty() {
} else if prim_context.clip_chain.is_some() {
// If the primitive doesn't have a specific clip, key the task ID off the
// stacking context. This means that two primitives which are only clipped
// by the stacking context stack can share clip masks during render task
@ -1306,11 +1253,12 @@ impl PrimitiveStore {
RenderTask::new_mask(
Some(prim_context.clip_id),
prim_context.clip_bounds,
&prim_context.current_clip_stack,
prim_context.clip_chain.clone(),
None,
prim_screen_rect,
clip_store,
is_axis_aligned,
prim_context.coordinate_system_id,
)
} else {
None
@ -1320,7 +1268,6 @@ impl PrimitiveStore {
true
}
/// Returns true if the bounding box needs to be updated.
pub fn prepare_prim_for_render(
&mut self,
prim_index: PrimitiveIndex,

View File

@ -103,7 +103,7 @@ impl Document {
self.pan.x as f32 / accumulated_scale_factor,
self.pan.y as f32 / accumulated_scale_factor,
);
self.frame.build(
self.frame.build_renderer_frame(
resource_cache,
gpu_cache,
&self.scene.pipelines,
@ -297,6 +297,17 @@ impl RenderBackend {
DocumentOp::Built
}
DocumentMsg::UpdatePipelineResources { resources, pipeline_id, epoch } => {
profile_scope!("UpdateResources");
self.resource_cache
.update_resources(resources, &mut profile_counters.resources);
doc.scene.update_epoch(pipeline_id, epoch);
doc.frame.update_epoch(pipeline_id, epoch);
DocumentOp::Nop
}
DocumentMsg::SetRootPipeline(pipeline_id) => {
profile_scope!("SetRootPipeline");

View File

@ -6,10 +6,12 @@ use api::{ClipId, DeviceIntLength, DeviceIntPoint, DeviceIntRect, DeviceIntSize}
use api::{FilterOp, MixBlendMode};
use api::PipelineId;
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};
use tiling::{RenderTargetKind, StackingContextIndex};
@ -28,6 +30,31 @@ pub struct RenderTaskTree {
pub task_data: Vec<RenderTaskData>,
}
pub type ClipChain = Option<Rc<ClipChainNode>>;
#[derive(Debug)]
pub struct ClipChainNode {
pub work_item: ClipWorkItem,
pub prev: ClipChain,
}
struct ClipChainNodeIter {
current: ClipChain,
}
impl Iterator for ClipChainNodeIter {
type Item = Rc<ClipChainNode>;
fn next(&mut self) -> ClipChain {
let previous = self.current.clone();
self.current = match self.current {
Some(ref item) => item.prev.clone(),
None => return None,
};
previous
}
}
impl RenderTaskTree {
pub fn new() -> RenderTaskTree {
RenderTaskTree {
@ -112,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),
}
@ -177,11 +202,15 @@ pub enum MaskGeometryKind {
pub struct ClipWorkItem {
pub layer_index: PackedLayerIndex,
pub clip_sources: ClipSourcesWeakHandle,
pub apply_rectangles: bool,
pub coordinate_system_id: CoordinateSystemId,
}
impl ClipWorkItem {
fn get_geometry_kind(&self, clip_store: &ClipStore) -> MaskGeometryKind {
fn get_geometry_kind(
&self,
clip_store: &ClipStore,
prim_coordinate_system_id: CoordinateSystemId
) -> MaskGeometryKind {
let clips = clip_store
.get_opt(&self.clip_sources)
.expect("bug: clip handle should be valid")
@ -190,8 +219,10 @@ impl ClipWorkItem {
for &(ref clip, _) in clips {
match *clip {
ClipSource::Rectangle(..) => if self.apply_rectangles {
return MaskGeometryKind::Default;
ClipSource::Rectangle(..) => {
if self.has_compatible_coordinate_system(prim_coordinate_system_id) {
return MaskGeometryKind::Default;
}
},
ClipSource::RoundedRectangle(..) => {
rounded_rect_count += 1;
@ -208,6 +239,10 @@ impl ClipWorkItem {
MaskGeometryKind::Default
}
}
fn has_compatible_coordinate_system(&self, other_id: CoordinateSystemId) -> bool {
self.coordinate_system_id == other_id
}
}
#[derive(Debug)]
@ -216,6 +251,19 @@ pub struct CacheMaskTask {
inner_rect: DeviceIntRect,
pub clips: Vec<ClipWorkItem>,
pub geometry_kind: MaskGeometryKind,
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)]
@ -226,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),
}
@ -269,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 {
pub fn new_picture(
size: DeviceIntSize,
prim_index: PrimitiveIndex,
target_kind: RenderTargetKind,
) -> 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,
size: DeviceIntSize,
prim_index: PrimitiveIndex,
) -> RenderTask {
RenderTask {
cache_key: Some(RenderTaskKey::BoxShadow(key)),
children: Vec::new(),
location: RenderTaskLocation::Dynamic(None, size),
kind: RenderTaskKind::BoxShadow(prim_index),
kind: RenderTaskKind::Picture(PictureTask {
prim_index,
target_kind,
}),
}
}
@ -303,46 +344,46 @@ impl RenderTask {
pub fn new_mask(
key: Option<ClipId>,
task_rect: DeviceIntRect,
raw_clips: &[ClipWorkItem],
extra_clip: Option<ClipWorkItem>,
raw_clips: ClipChain,
extra_clip: ClipChain,
prim_rect: DeviceIntRect,
clip_store: &ClipStore,
is_axis_aligned: bool,
prim_coordinate_system_id: CoordinateSystemId,
) -> Option<RenderTask> {
// Filter out all the clip instances that don't contribute to the result
let mut current_coordinate_system_id = prim_coordinate_system_id;
let mut inner_rect = Some(task_rect);
let clips: Vec<_> = raw_clips
.iter()
.chain(extra_clip.iter())
.filter(|work_item| {
let clip_info = clip_store
.get_opt(&work_item.clip_sources)
.expect("bug: clip item should exist");
let clips: Vec<_> = ClipChainNodeIter { current: raw_clips }
.chain(ClipChainNodeIter { current: extra_clip })
.filter_map(|node| {
let work_item = node.work_item.clone();
// If this clip does not contribute to a mask, then ensure
// it gets filtered out here. Otherwise, if a mask is
// created (by a different clip in the list), the allocated
// rectangle for the mask could end up being much bigger
// than is actually required.
if !clip_info.is_masking() {
return false;
// FIXME(1828): This is a workaround until we can fix the inconsistency between
// the shader and the CPU code around how inner_rects are handled.
if !node.work_item.has_compatible_coordinate_system(current_coordinate_system_id) {
current_coordinate_system_id = node.work_item.coordinate_system_id;
inner_rect = None;
return Some(work_item)
}
let clip_info = clip_store
.get_opt(&node.work_item.clip_sources)
.expect("bug: clip item should exist");
debug_assert!(clip_info.is_masking());
match clip_info.bounds.inner {
// Inner rects aren't valid if the item is not axis-aligned, which can
// be determined by the apply_rectangles field. This is mostly a band-aid
// until we have better handling of inner rectangles for transformed clips.
Some(ref inner) if !work_item.apply_rectangles && !inner.device_rect.is_empty() => {
Some(ref inner) if !inner.device_rect.is_empty() => {
inner_rect = inner_rect.and_then(|r| r.intersection(&inner.device_rect));
!inner.device_rect.contains_rect(&task_rect)
}
_ => {
inner_rect = None;
true
if inner.device_rect.contains_rect(&task_rect) {
return None;
}
}
_ => inner_rect = None,
}
Some(work_item)
})
.cloned()
.collect();
// Nothing to do, all clips are irrelevant for this case
@ -350,6 +391,7 @@ impl RenderTask {
return None;
}
// TODO(gw): This optimization is very conservative for now.
// For now, only draw optimized geometry if it is
// a single aligned rect mask with rounded corners.
@ -363,7 +405,7 @@ impl RenderTask {
return None;
}
if is_axis_aligned && clips.len() == 1 {
geometry_kind = clips[0].get_geometry_kind(clip_store);
geometry_kind = clips[0].get_geometry_kind(clip_store, prim_coordinate_system_id);
}
}
@ -376,6 +418,7 @@ impl RenderTask {
inner_rect: inner_rect.unwrap_or(DeviceIntRect::zero()),
clips,
geometry_kind,
coordinate_system_id: prim_coordinate_system_id,
}),
})
}
@ -399,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);
@ -417,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
@ -427,7 +475,6 @@ impl RenderTask {
match self.kind {
RenderTaskKind::Alpha(ref mut task) => task,
RenderTaskKind::Picture(..) |
RenderTaskKind::BoxShadow(..) |
RenderTaskKind::CacheMask(..) |
RenderTaskKind::VerticalBlur(..) |
RenderTaskKind::Readback(..) |
@ -440,7 +487,6 @@ impl RenderTask {
match self.kind {
RenderTaskKind::Alpha(ref task) => task,
RenderTaskKind::Picture(..) |
RenderTaskKind::BoxShadow(..) |
RenderTaskKind::CacheMask(..) |
RenderTaskKind::VerticalBlur(..) |
RenderTaskKind::Readback(..) |
@ -481,7 +527,7 @@ impl RenderTask {
],
}
}
RenderTaskKind::Picture(..) | RenderTaskKind::BoxShadow(..) => {
RenderTaskKind::Picture(..) => {
let (target_rect, target_index) = self.get_target_rect();
RenderTaskData {
data: [
@ -519,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: [
@ -529,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,
@ -562,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(),
@ -583,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");
}
@ -612,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");

View File

@ -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(),
);
debug_target.add(
debug_server::BatchKind::Cache,
"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_target.add(
debug_server::BatchKind::Cache,
"Box Shadow",
target.box_shadow_cache_prims.len(),
);
debug_pass.add(debug_target);
}
@ -2011,24 +1998,24 @@ impl Renderer {
fn handle_debug_command(&mut self, command: DebugCommand) {
match command {
DebugCommand::EnableProfiler(enable) => if enable {
self.debug_flags.insert(PROFILER_DBG);
self.debug_flags.insert(DebugFlags::PROFILER_DBG);
} else {
self.debug_flags.remove(PROFILER_DBG);
self.debug_flags.remove(DebugFlags::PROFILER_DBG);
},
DebugCommand::EnableTextureCacheDebug(enable) => if enable {
self.debug_flags.insert(TEXTURE_CACHE_DBG);
self.debug_flags.insert(DebugFlags::TEXTURE_CACHE_DBG);
} else {
self.debug_flags.remove(TEXTURE_CACHE_DBG);
self.debug_flags.remove(DebugFlags::TEXTURE_CACHE_DBG);
},
DebugCommand::EnableRenderTargetDebug(enable) => if enable {
self.debug_flags.insert(RENDER_TARGET_DBG);
self.debug_flags.insert(DebugFlags::RENDER_TARGET_DBG);
} else {
self.debug_flags.remove(RENDER_TARGET_DBG);
self.debug_flags.remove(DebugFlags::RENDER_TARGET_DBG);
},
DebugCommand::EnableAlphaRectsDebug(enable) => if enable {
self.debug_flags.insert(ALPHA_PRIM_DBG);
self.debug_flags.insert(DebugFlags::ALPHA_PRIM_DBG);
} else {
self.debug_flags.remove(ALPHA_PRIM_DBG);
self.debug_flags.remove(DebugFlags::ALPHA_PRIM_DBG);
},
DebugCommand::FetchDocuments => {}
DebugCommand::FetchClipScrollTree => {}
@ -2133,7 +2120,7 @@ impl Renderer {
self.cpu_profiles.push_back(cpu_profile);
}
if self.debug_flags.contains(PROFILER_DBG) {
if self.debug_flags.contains(DebugFlags::PROFILER_DBG) {
let screen_fraction = 1.0 / //TODO: take device/pixel ratio into equation?
(framebuffer_size.width as f32 * framebuffer_size.height as f32);
self.profiler.draw_profile(
@ -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() {
@ -2734,7 +2723,7 @@ impl Renderer {
self.gpu_profile.add_sampler(GPU_SAMPLER_TAG_TRANSPARENT);
for batch in &target.alpha_batcher.batch_list.alpha_batch_list.batches {
if self.debug_flags.contains(ALPHA_PRIM_DBG) {
if self.debug_flags.contains(DebugFlags::ALPHA_PRIM_DBG) {
let color = match batch.key.blend_mode {
BlendMode::None => ColorF::new(0.3, 0.3, 0.3, 1.0),
BlendMode::Alpha => ColorF::new(0.0, 0.9, 0.1, 1.0),
@ -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);
let _gm = self.gpu_profile.add_marker(GPU_TAG_CACHE_BOX_SHADOW);
self.cs_box_shadow
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_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(),
);
}
@ -3330,7 +3349,7 @@ impl Renderer {
}
fn draw_render_target_debug(&mut self, framebuffer_size: DeviceUintSize) {
if !self.debug_flags.contains(RENDER_TARGET_DBG) {
if !self.debug_flags.contains(DebugFlags::RENDER_TARGET_DBG) {
return;
}
@ -3370,7 +3389,7 @@ impl Renderer {
}
fn draw_texture_cache_debug(&mut self, framebuffer_size: DeviceUintSize) {
if !self.debug_flags.contains(TEXTURE_CACHE_DBG) {
if !self.debug_flags.contains(DebugFlags::TEXTURE_CACHE_DBG) {
return;
}
@ -3391,7 +3410,7 @@ impl Renderer {
let mut i = 0;
for texture in &self.texture_resolver.cache_texture_map {
let y = spacing + if self.debug_flags.contains(RENDER_TARGET_DBG) {
let y = spacing + if self.debug_flags.contains(DebugFlags::RENDER_TARGET_DBG) {
528
} else {
0
@ -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);

View File

@ -22,6 +22,7 @@ use internal_types::{FastHashMap, FastHashSet, SourceTexture, TextureUpdateList}
use profiler::{ResourceProfileCounters, TextureCacheProfileCounters};
use rayon::ThreadPool;
use std::collections::hash_map::Entry::{self, Occupied, Vacant};
use std::cmp;
use std::fmt::Debug;
use std::hash::Hash;
use std::mem;
@ -113,8 +114,15 @@ struct CachedImageInfo {
epoch: Epoch,
}
#[derive(Debug)]
pub enum ResourceClassCacheError {
OverLimitSize,
}
pub type ResourceCacheResult<V> = Result<V, ResourceClassCacheError>;
pub struct ResourceClassCache<K, V> {
resources: FastHashMap<K, V>,
resources: FastHashMap<K, ResourceCacheResult<V>>,
}
impl<K, V> ResourceClassCache<K, V>
@ -127,21 +135,21 @@ where
}
}
fn get(&self, key: &K) -> &V {
self.resources
.get(key)
fn get(&self, key: &K) -> &ResourceCacheResult<V> {
self.resources.get(key)
.expect("Didn't find a cached resource with that ID!")
}
pub fn insert(&mut self, key: K, value: V) {
pub fn insert(&mut self, key: K, value: ResourceCacheResult<V>) {
self.resources.insert(key, value);
}
pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {
pub fn get_mut(&mut self, key: &K) -> &mut ResourceCacheResult<V> {
self.resources.get_mut(key)
.expect("Didn't find a cached resource with that ID!")
}
pub fn entry(&mut self, key: K) -> Entry<K, V> {
pub fn entry(&mut self, key: K) -> Entry<K, ResourceCacheResult<V>> {
self.resources.entry(key)
}
@ -159,7 +167,7 @@ where
.cloned()
.collect::<Vec<_>>();
for key in resources_to_destroy {
self.resources.remove(&key).unwrap();
let _ = self.resources.remove(&key).unwrap();
}
}
}
@ -483,25 +491,38 @@ impl ResourceCache {
return;
}
let side_size =
template.tiling.map_or(cmp::max(template.descriptor.width, template.descriptor.height),
|tile_size| tile_size as u32);
if side_size > self.texture_cache.max_texture_size() {
// The image or tiling size is too big for hardware texture size.
warn!("Dropping image, image:(w:{},h:{}, tile:{}) is too big for hardware!",
template.descriptor.width, template.descriptor.height, template.tiling.unwrap_or(0));
self.cached_images.insert(request, Err(ResourceClassCacheError::OverLimitSize));
return;
}
// If this image exists in the texture cache, *and* the epoch
// in the cache matches that of the template, then it is
// valid to use as-is.
let (entry, needs_update) = match self.cached_images.entry(request) {
Occupied(entry) => {
let needs_update = entry.get().epoch != template.epoch;
let needs_update = entry.get().as_ref().unwrap().epoch != template.epoch;
(entry.into_mut(), needs_update)
}
Vacant(entry) => (
entry.insert(CachedImageInfo {
epoch: template.epoch,
texture_cache_handle: TextureCacheHandle::new(),
}),
entry.insert(Ok(
CachedImageInfo {
epoch: template.epoch,
texture_cache_handle: TextureCacheHandle::new(),
}
)),
true,
),
};
let needs_upload = self.texture_cache
.request(&mut entry.texture_cache_handle, gpu_cache);
.request(&mut entry.as_mut().unwrap().texture_cache_handle, gpu_cache);
if !needs_upload && !needs_update {
return;
@ -600,7 +621,7 @@ impl ResourceCache {
debug_assert!(fetch_buffer.is_empty());
for (loop_index, key) in glyph_keys.iter().enumerate() {
if let Some(ref glyph) = *glyph_key_cache.get(key) {
if let Ok(Some(ref glyph)) = *glyph_key_cache.get(key) {
let cache_item = self.texture_cache.get(&glyph.texture_cache_handle);
if current_texture_id != cache_item.texture_id {
if !fetch_buffer.is_empty() {
@ -648,15 +669,24 @@ impl ResourceCache {
image_key: ImageKey,
image_rendering: ImageRendering,
tile: Option<TileOffset>,
) -> CacheItem {
) -> Result<CacheItem, ()> {
debug_assert_eq!(self.state, State::QueryResources);
let key = ImageRequest {
key: image_key,
rendering: image_rendering,
tile,
};
let image_info = &self.cached_images.get(&key);
self.texture_cache.get(&image_info.texture_cache_handle)
// TODO(Jerry): add a debug option to visualize the corresponding area for
// the Err() case of CacheItem.
match *self.cached_images.get(&key) {
Ok(ref image_info) => {
Ok(self.texture_cache.get(&image_info.texture_cache_handle))
}
Err(_) => {
Err(())
}
}
}
pub fn get_image_properties(&self, image_key: ImageKey) -> Option<ImageProperties> {
@ -816,7 +846,7 @@ impl ResourceCache {
image_template.descriptor.clone()
};
let entry = self.cached_images.get_mut(&request).unwrap();
let entry = self.cached_images.get_mut(&request).as_mut().unwrap();
self.texture_cache.update(
&mut entry.texture_cache_handle,
descriptor,

View File

@ -133,6 +133,12 @@ impl Scene {
}
self.pipelines.remove(&pipeline_id);
}
pub fn update_epoch(&mut self, pipeline_id: PipelineId, epoch: Epoch) {
if let Some(pipeline) = self.pipelines.get_mut(&pipeline_id) {
pipeline.epoch = epoch;
}
}
}
pub trait FilterOpHelpers {

View File

@ -9,9 +9,10 @@ use api::{LayerToWorldTransform, MixBlendMode, PipelineId, PropertyBinding, Tran
use api::{LayerVector2D, TileOffset, WorldToLayerTransform, YuvColorSpace, YuvFormat};
use border::{BorderCornerInstance, BorderCornerSide};
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;
@ -400,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];
@ -481,6 +485,7 @@ impl AlphaRenderItem {
);
if color_texture_id == SourceTexture::Invalid {
warn!("Warnings: skip a PrimitiveKind::Image at {:?}.\n", item_bounding_rect);
return;
}
@ -539,6 +544,8 @@ impl AlphaRenderItem {
glyph_fetch_buffer,
gpu_cache,
|texture_id, glyphs| {
debug_assert_ne!(texture_id, SourceTexture::Invalid);
let textures = BatchTextures {
colors: [
texture_id,
@ -573,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);
@ -632,6 +639,7 @@ impl AlphaRenderItem {
);
if texture == SourceTexture::Invalid {
warn!("Warnings: skip a PrimitiveKind::YuvImage at {:?}.\n", item_bounding_rect);
return;
}
@ -691,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) => {
@ -812,11 +800,13 @@ impl ClipBatcher {
&mut self,
task_address: RenderTaskAddress,
clips: &[ClipWorkItem],
coordinate_system_id: CoordinateSystemId,
resource_cache: &ResourceCache,
gpu_cache: &GpuCache,
geometry_kind: MaskGeometryKind,
clip_store: &ClipStore,
) {
let mut coordinate_system_id = coordinate_system_id;
for work_item in clips.iter() {
let instance = ClipMaskInstance {
render_task_address: task_address,
@ -834,23 +824,29 @@ impl ClipBatcher {
match *source {
ClipSource::Image(ref mask) => {
let cache_item =
resource_cache.get_cached_image(mask.image, ImageRendering::Auto, None);
self.images
.entry(cache_item.texture_id)
.or_insert(Vec::new())
.push(ClipMaskInstance {
if let Ok(cache_item) = resource_cache.get_cached_image(mask.image, ImageRendering::Auto, None) {
self.images
.entry(cache_item.texture_id)
.or_insert(Vec::new())
.push(ClipMaskInstance {
clip_data_address: gpu_address,
resource_address: gpu_cache.get_address(&cache_item.uv_rect_handle),
..instance
});
} else {
warn!("Warnings: skip a image mask. Key:{:?} Rect::{:?}.\n", mask.image, mask.rect);
continue;
}
}
ClipSource::Rectangle(..) => {
if work_item.coordinate_system_id != coordinate_system_id {
self.rectangles.push(ClipMaskInstance {
clip_data_address: gpu_address,
resource_address: gpu_cache.get_address(&cache_item.uv_rect_handle),
segment: MaskSegment::All as i32,
..instance
});
}
ClipSource::Rectangle(..) => if work_item.apply_rectangles {
self.rectangles.push(ClipMaskInstance {
clip_data_address: gpu_address,
segment: MaskSegment::All as i32,
..instance
});
coordinate_system_id = work_item.coordinate_system_id;
}
},
ClipSource::RoundedRectangle(..) => match geometry_kind {
MaskGeometryKind::Default => {
@ -971,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
@ -1159,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 {
@ -1232,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) => {
@ -1244,7 +1240,10 @@ impl RenderTarget for ColorRenderTarget {
pub struct AlphaRenderTarget {
pub clip_batcher: ClipBatcher,
pub box_shadow_cache_prims: Vec<BoxShadowCacheInstance>,
pub rect_cache_prims: Vec<PrimitiveInstance>,
// List of blur operations to apply for this render target.
pub vertical_blurs: Vec<BlurInstance>,
pub horizontal_blurs: Vec<BlurInstance>,
allocator: TextureAllocator,
}
@ -1256,7 +1255,9 @@ impl RenderTarget for AlphaRenderTarget {
fn new(size: Option<DeviceUintSize>) -> 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")),
}
}
@ -1279,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!()
}
}
}
@ -1305,6 +1341,7 @@ impl RenderTarget for AlphaRenderTarget {
self.clip_batcher.add(
task_address,
&task_info.clips,
task_info.coordinate_system_id,
&ctx.resource_cache,
gpu_cache,
task_info.geometry_kind,
@ -1488,8 +1525,7 @@ pub enum TransformBatchKind {
AlignedGradient,
AngleGradient,
RadialGradient,
BoxShadow,
CacheImage,
CacheImage(RenderTargetKind),
BorderCorner,
BorderEdge,
Line,
@ -1686,6 +1722,7 @@ pub struct ClipScrollGroup {
pub clip_node_id: ClipId,
pub packed_layer_index: PackedLayerIndex,
pub screen_bounding_rect: Option<(TransformedRectKind, DeviceIntRect)>,
pub coordinate_system_id: CoordinateSystemId,
}
impl ClipScrollGroup {
@ -1824,10 +1861,12 @@ fn resolve_image(
(SourceTexture::External(external_image), cache_handle)
}
None => {
let cache_item =
resource_cache.get_cached_image(image_key, image_rendering, tile_offset);
(cache_item.texture_id, cache_item.uv_rect_handle)
if let Ok(cache_item) = resource_cache.get_cached_image(image_key, image_rendering, tile_offset) {
(cache_item.texture_id, cache_item.uv_rect_handle)
} else {
// There is no usable texture entry for the image key. Just return an invalid texture here.
(SourceTexture::Invalid, GpuCacheHandle::new())
}
}
}
}

View File

@ -2,12 +2,14 @@
* 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, ComplexClipRegion, DeviceIntRect, DevicePoint, DeviceRect, DeviceSize};
use api::{LayerRect, LayerToWorldTransform, LayoutRect, WorldPoint3D};
use api::{BorderRadius, ComplexClipRegion, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
use api::{DevicePoint, DeviceRect, DeviceSize, LayerRect, LayerToWorldTransform, LayoutRect};
use api::WorldPoint3D;
use euclid::{Point2D, Rect, Size2D, TypedPoint2D, TypedRect, TypedSize2D, TypedTransform2D};
use euclid::TypedTransform3D;
use num_traits::Zero;
use std::f32::consts::FRAC_1_SQRT_2;
use std::i32;
// Matches the definition of SK_ScalarNearlyZero in Skia.
const NEARLY_ZERO: f32 = 1.0 / 4096.0;
@ -207,13 +209,6 @@ pub struct TransformedRect {
pub kind: TransformedRectKind,
}
// Having an unlimited bounding box is fine up until we try
// to cast it to `i32`, where we get `-2147483648` for any
// values larger than or equal to 2^31.
//Note: clamping to i32::MIN and i32::MAX is not a solution,
// with explanation left as an exercise for the reader.
const MAX_COORD: f32 = 1.0e9;
impl TransformedRect {
pub fn new(
rect: &LayerRect,
@ -249,10 +244,7 @@ impl TransformedRect {
let inner_min_dp = (DevicePoint::new(xs[1], ys[1]) * device_pixel_ratio).ceil();
let inner_max_dp = (DevicePoint::new(xs[2], ys[2]) * device_pixel_ratio).floor();
let max_rect = DeviceRect::new(
DevicePoint::new(-MAX_COORD, -MAX_COORD),
DeviceSize::new(2.0 * MAX_COORD, 2.0 * MAX_COORD),
);
let max_rect = DeviceRect::max_rect();
let bounding_rect = DeviceRect::new(outer_min_dp, (outer_max_dp - outer_min_dp).to_size())
.intersection(&max_rect)
.unwrap_or(max_rect)
@ -362,3 +354,33 @@ pub mod test {
assert_eq!(m1.inverse_project(&p0), Some(Point2D::new(2.0, 2.0)));
}
}
pub trait MaxRect {
fn max_rect() -> Self;
}
impl MaxRect for DeviceIntRect {
fn max_rect() -> Self {
DeviceIntRect::new(
DeviceIntPoint::new(i32::MIN / 2, i32::MIN / 2),
DeviceIntSize::new(i32::MAX, i32::MAX),
)
}
}
impl MaxRect for DeviceRect {
fn max_rect() -> Self {
// Having an unlimited bounding box is fine up until we try
// to cast it to `i32`, where we get `-2147483648` for any
// values larger than or equal to 2^31.
//
// Note: clamping to i32::MIN and i32::MAX is not a solution,
// with explanation left as an exercise for the reader.
const MAX_COORD: f32 = 1.0e9;
DeviceRect::new(
DevicePoint::new(-MAX_COORD, -MAX_COORD),
DeviceSize::new(2.0 * MAX_COORD, 2.0 * MAX_COORD),
)
}
}

View File

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

View File

@ -11,8 +11,8 @@ ipc = ["ipc-channel"]
[dependencies]
app_units = "0.5.6"
bitflags = "1.0"
bincode = "0.9"
bitflags = "0.9"
byteorder = "1.0"
euclid = "0.15"
heapsize = ">= 0.3.6, < 0.5"

View File

@ -154,6 +154,10 @@ pub struct HitTestItem {
/// viewport is the scroll node formed by the root reference frame of the display item's
/// pipeline.
pub point_in_viewport: LayoutPoint,
/// The coordinates of the original hit test point relative to the origin of this item.
/// This is useful for calculating things like text offsets in the client.
pub point_relative_to_item: LayoutPoint,
}
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
@ -192,6 +196,11 @@ pub enum DocumentMsg {
preserve_frame_state: bool,
resources: ResourceUpdates,
},
UpdatePipelineResources {
resources: ResourceUpdates,
pipeline_id: PipelineId,
epoch: Epoch,
},
SetPageZoom(ZoomFactor),
SetPinchZoom(ZoomFactor),
SetPan(DeviceIntPoint),
@ -214,6 +223,7 @@ impl fmt::Debug for DocumentMsg {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match *self {
DocumentMsg::SetDisplayList { .. } => "DocumentMsg::SetDisplayList",
DocumentMsg::UpdatePipelineResources { .. } => "DocumentMsg::UpdatePipelineResources",
DocumentMsg::HitTest(..) => "DocumentMsg::HitTest",
DocumentMsg::SetPageZoom(..) => "DocumentMsg::SetPageZoom",
DocumentMsg::SetPinchZoom(..) => "DocumentMsg::SetPinchZoom",
@ -461,7 +471,7 @@ impl RenderApi {
ImageKey::new(self.namespace_id, new_id)
}
/// Adds an image identified by the `ImageKey`.
/// Add/remove/update resources such as images and fonts.
pub fn update_resources(&self, resources: ResourceUpdates) {
if resources.updates.is_empty() {
return;
@ -471,6 +481,24 @@ impl RenderApi {
.unwrap();
}
/// Add/remove/update resources such as images and fonts.
///
/// This is similar to update_resources with the addition that it allows updating
/// a pipeline's epoch.
pub fn update_pipeline_resources(
&self,
resources: ResourceUpdates,
document_id: DocumentId,
pipeline_id: PipelineId,
epoch: Epoch,
) {
self.send(document_id, DocumentMsg::UpdatePipelineResources {
resources,
pipeline_id,
epoch,
});
}
pub fn send_external_event(&self, evt: ExternalEvent) {
let msg = ApiMsg::ExternalEvent(evt);
self.api_sender.send(msg).unwrap();

View File

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

View File

@ -399,7 +399,7 @@ impl<'a, T: for<'de> Deserialize<'de>> Iterator for AuxIter<'a, T> {
impl<'a, T: for<'de> Deserialize<'de>> ::std::iter::ExactSizeIterator for AuxIter<'a, T> {}
// This is purely for the JSON writer in wrench
// This is purely for the JSON/RON writers in wrench
impl Serialize for BuiltDisplayList {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut seq = serializer.serialize_seq(None)?;