Bug 1410893 - Update webrender to commit 4b8493d6bdc64d2d83202ac15b06b0d4b14c6e76. r=jrmuizel

MozReview-Commit-ID: DoUZXZtRyDY

--HG--
extra : rebase_source : c6b6fac4767e641ac7207bc03dbf206417f7a655
This commit is contained in:
Kartikaya Gupta 2017-10-27 08:51:39 -04:00
parent 05d1c44747
commit d639eb0193
48 changed files with 1784 additions and 1515 deletions

View File

@ -175,4 +175,4 @@ Troubleshooting tips:
-------------------------------------------------------------------------------
The version of WebRender currently in the tree is:
d741f472dd3d6c3441646f7bf4e714c71bea39b7
4b8493d6bdc64d2d83202ac15b06b0d4b14c6e76

View File

@ -1,6 +1,6 @@
[package]
name = "webrender"
version = "0.53.0"
version = "0.53.1"
authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
license = "MPL-2.0"
repository = "https://github.com/servo/webrender"

View File

@ -2,14 +2,28 @@
* 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
void brush_vs(
int prim_address,
vec2 local_pos,
RectWithSize local_rect,
ivec2 user_data
);
// Whether this brush is being drawn on a Picture
// task (new) or an alpha batch task (legacy).
// Can be removed once everything uses pictures.
#define BRUSH_FLAG_USES_PICTURE (1 << 0)
struct BrushInstance {
int picture_address;
int prim_address;
int layer_address;
int clip_address;
int z;
int flags;
ivec2 user_data;
};
BrushInstance load_brush() {
@ -17,63 +31,95 @@ BrushInstance load_brush() {
bi.picture_address = aData0.x;
bi.prim_address = aData0.y;
bi.layer_address = aData0.z;
bi.clip_address = aData0.w;
bi.z = aData1.x;
bi.flags = aData1.y;
bi.user_data = aData1.zw;
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);
vec2 device_pos, local_pos;
RectWithSize local_rect = geom.local_rect;
// 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;
if ((brush.flags & BRUSH_FLAG_USES_PICTURE) != 0) {
// Fetch the dynamic picture that we are drawing on.
PictureTask pic_task = fetch_picture_task(brush.picture_address);
// Right now - pictures only support local positions. In the future, this
// will be expanded to support transform picture types (the common kind).
device_pos = pic_task.target_rect.p0 + aPosition.xy * pic_task.target_rect.size;
local_pos = aPosition.xy * pic_task.target_rect.size / uDevicePixelRatio;
// Write the final position transformed by the orthographic device-pixel projection.
gl_Position = uTransform * vec4(device_pos, 0.0, 1.0);
} else {
AlphaBatchTask alpha_task = fetch_alpha_batch_task(brush.picture_address);
Layer layer = fetch_layer(brush.layer_address);
ClipArea clip_area = fetch_clip_area(brush.clip_address);
// Write the normal vertex information out.
// TODO(gw): Support transform types in brushes. For now,
// the old cache image shader didn't support
// them yet anyway, so we're not losing any
// existing functionality.
VertexInfo vi = write_vertex(
geom.local_rect,
geom.local_clip_rect,
float(brush.z),
layer,
alpha_task,
geom.local_rect
);
local_pos = vi.local_pos;
// For brush instances in the alpha pass, always write
// out clip information.
// TODO(gw): It's possible that we might want alpha
// shaders that don't clip in the future,
// but it's reasonable to assume that one
// implies the other, for now.
#ifdef WR_FEATURE_ALPHA_PASS
write_clip(
vi.screen_pos,
clip_area
);
#endif
}
// 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);
brush_vs(
brush.prim_address + VECS_PER_PRIM_HEADER,
local_pos,
local_rect,
brush.user_data
);
}
#endif
#ifdef WR_FRAGMENT_SHADER
vec4 brush_fs();
void main(void) {
// Run the specific brush FS code to output the color.
vec4 color = brush_fs(vLocalPos, vLocalRect);
vec4 color = brush_fs();
#ifdef WR_FEATURE_ALPHA_PASS
// Apply the clip mask
color *= do_clip();
#endif
// TODO(gw): Handle pre-multiply common code here as required.
oFragColor = color;

View File

@ -0,0 +1,62 @@
/* 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,brush
varying vec3 vUv;
flat varying vec4 vUvBounds;
#if defined WR_FEATURE_ALPHA_TARGET
flat varying vec4 vColor;
#endif
#ifdef WR_VERTEX_SHADER
void brush_vs(
int prim_address,
vec2 local_pos,
RectWithSize local_rect,
ivec2 user_data
) {
// TODO(gw): For now, this brush_image shader is only
// being used to draw items from the intermediate
// surface cache (render tasks). In the future
// we can expand this to support items from
// the normal texture cache and unify this
// with the normal image shader.
BlurTask task = fetch_blur_task(user_data.x);
vUv.z = task.render_target_layer_index;
#if defined WR_FEATURE_COLOR_TARGET
vec2 texture_size = vec2(textureSize(sColor0, 0).xy);
#else
vec2 texture_size = vec2(textureSize(sColor1, 0).xy);
vColor = task.color;
#endif
vec2 uv0 = task.target_rect.p0;
vec2 uv1 = (task.target_rect.p0 + task.target_rect.size);
vec2 f = (local_pos - local_rect.p0) / local_rect.size;
vUv.xy = mix(uv0 / texture_size,
uv1 / texture_size,
f);
vUvBounds = vec4(uv0 + vec2(0.5), uv1 - vec2(0.5)) / texture_size.xyxy;
}
#endif
#ifdef WR_FRAGMENT_SHADER
vec4 brush_fs() {
vec2 uv = clamp(vUv.xy, vUvBounds.xy, vUvBounds.zw);
#if defined WR_FEATURE_COLOR_TARGET
vec4 color = texture(sColor0, vec3(uv, vUv.z));
#else
vec4 color = vColor * texture(sColor1, vec3(uv, vUv.z)).r;
#endif
return color;
}
#endif

View File

@ -2,13 +2,15 @@
* 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
#include shared,prim_shared,ellipse,brush
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;
flat varying vec4 vLocalRect;
varying vec2 vLocalPos;
#ifdef WR_VERTEX_SHADER
@ -25,31 +27,45 @@ BrushPrimitive fetch_brush_primitive(int address) {
return BrushPrimitive(data[0].x, data[1].xy, data[1].zw, data[2].xy, data[2].zw);
}
void brush_vs(int prim_address, vec4 prim_rect) {
void brush_vs(
int prim_address,
vec2 local_pos,
RectWithSize local_rect,
ivec2 user_data
) {
// Load the specific primitive.
BrushPrimitive prim = fetch_brush_primitive(prim_address + 2);
BrushPrimitive prim = fetch_brush_primitive(prim_address);
// Write clip parameters
vClipMode = prim.clip_mode;
// TODO(gw): In the future, when brush primitives may be segment rects
// we need to account for that here, and differentiate between
// the segment rect (geometry) amd the primitive rect (which
// defines where the clip radii are relative to).
vec4 prim_rect = vec4(local_rect.p0, local_rect.p0 + local_rect.size);
vClipCenter_Radius_TL = vec4(prim_rect.xy + prim.radius_tl, prim.radius_tl);
vClipCenter_Radius_TR = vec4(prim_rect.zy + vec2(-prim.radius_tr.x, prim.radius_tr.y), prim.radius_tr);
vClipCenter_Radius_BR = vec4(prim_rect.zw - prim.radius_br, prim.radius_br);
vClipCenter_Radius_BL = vec4(prim_rect.xw + vec2(prim.radius_bl.x, -prim.radius_bl.y), prim.radius_bl);
vLocalRect = prim_rect;
vLocalPos = local_pos;
}
#endif
#ifdef WR_FRAGMENT_SHADER
vec4 brush_fs(vec2 local_pos, vec4 local_rect) {
vec4 brush_fs() {
// 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) {
if (vLocalPos.x >= vLocalRect.x && vLocalPos.x < vLocalRect.z &&
vLocalPos.y >= vLocalRect.y && vLocalPos.y < vLocalRect.w) {
// Apply ellipse clip on each corner.
d = rounded_rect(local_pos,
d = rounded_rect(vLocalPos,
vClipCenter_Radius_TL,
vClipCenter_Radius_TR,
vClipCenter_Radius_BR,
@ -59,5 +75,3 @@ vec4 brush_fs(vec2 local_pos, vec4 local_rect) {
return vec4(mix(d, 1.0 - d, vClipMode));
}
#endif
#include brush

View File

@ -29,13 +29,13 @@ void main(void) {
vec4 src_rect = src_task.data0;
vec4 target_rect = task.data0;
#if defined WR_FEATURE_COLOR
#if defined WR_FEATURE_COLOR_TARGET
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);
vBlurRadius = int(3.0 * task.data1.y);
vSigma = task.data1.y;
switch (aBlurDirection) {
@ -69,7 +69,7 @@ void main(void) {
#ifdef WR_FRAGMENT_SHADER
#if defined WR_FEATURE_COLOR
#if defined WR_FEATURE_COLOR_TARGET
#define SAMPLE_TYPE vec4
#define SAMPLE_TEXTURE(uv) texture(sCacheRGBA8, uv)
#else
@ -106,7 +106,7 @@ void main(void) {
gauss_coefficient_sum += gauss_coefficient.x;
gauss_coefficient.xy *= gauss_coefficient.yz;
for (int i=1 ; i <= vBlurRadius/2 ; ++i) {
for (int i=1 ; i <= vBlurRadius ; ++i) {
vec2 offset = vOffsetScale * float(i);
vec2 st0 = clamp(vUv.xy - offset, vUvRect.xy, vUvRect.zw);

View File

@ -56,6 +56,6 @@ void main(void) {
vClipMaskUvInnerRect.xy, vClipMaskUvInnerRect.zw);
float clip_alpha = texture(sColor0, vec3(source_uv, vLayer)).r; //careful: texture has type A8
oFragColor = vec4(min(alpha, clip_alpha), 1.0, 1.0, 1.0);
oFragColor = vec4(alpha * clip_alpha, 1.0, 1.0, 1.0);
}
#endif

View File

@ -27,7 +27,10 @@ struct ClipCorner {
vec4 outer_inner_radius;
};
ClipCorner fetch_clip_corner(ivec2 address) {
// index is of type float instead of int because using an int led to shader
// miscompilations with a macOS 10.12 Intel driver.
ClipCorner fetch_clip_corner(ivec2 address, float index) {
address += ivec2(2 + 2 * int(index), 0);
vec4 data[2] = fetch_from_resource_cache_2_direct(address);
return ClipCorner(RectWithSize(data[0].xy, data[0].zw), data[1]);
}
@ -44,22 +47,10 @@ ClipData fetch_clip(ivec2 address) {
ClipData clip;
clip.rect = fetch_clip_rect(address);
// Read the corners in groups of two texels, and adjust the read address
// before every read.
// The address adjustment is done inside this function, and not by passing
// the corner index to fetch_clip_corner and computing the correct address
// there, because doing so was hitting a driver bug on certain Intel macOS
// drivers which creates wrong results when doing arithmetic with integer
// variables (under certain, unknown, circumstances).
address.x += 2;
clip.top_left = fetch_clip_corner(address);
address.x += 2;
clip.top_right = fetch_clip_corner(address);
address.x += 2;
clip.bottom_left = fetch_clip_corner(address);
address.x += 2;
clip.bottom_right = fetch_clip_corner(address);
clip.top_left = fetch_clip_corner(address, 0.0);
clip.top_right = fetch_clip_corner(address, 1.0);
clip.bottom_left = fetch_clip_corner(address, 2.0);
clip.bottom_right = fetch_clip_corner(address, 3.0);
return clip;
}
@ -111,7 +102,7 @@ void main(void) {
vClipCenter_Radius_BR,
vClipCenter_Radius_BL);
float combined_alpha = min(alpha, clip_alpha);
float combined_alpha = alpha * clip_alpha;
// Select alpha or inverse alpha depending on clip in/out.
float final_alpha = mix(combined_alpha, 1.0 - combined_alpha, vClipMode);

View File

@ -2,6 +2,8 @@
* 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/. */
#define PRIMITIVE_HAS_PICTURE_TASK
#include shared,prim_shared
varying vec3 vUv;
@ -18,16 +20,6 @@ void main(void) {
int glyph_index = prim.user_data0;
int resource_address = prim.user_data1;
int picture_address = prim.user_data2;
// 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(picture_address);
Picture pic = fetch_picture(picture_address + VECS_PER_PRIM_HEADER);
Glyph glyph = fetch_glyph(prim.specific_prim_address,
glyph_index,
@ -40,8 +32,8 @@ void main(void) {
// the glyph offset, relative to its primitive bounding rect.
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 + pic.offset - shadow_geom.local_rect.p0);
vec2 origin = prim.task.target_rect.p0 +
uDevicePixelRatio * (local_pos - prim.task.content_origin);
vec4 local_rect = vec4(origin, size);
vec2 texture_size = vec2(textureSize(sColor0, 0));
@ -53,7 +45,7 @@ void main(void) {
aPosition.xy);
vUv = vec3(mix(st0, st1, aPosition.xy), res.layer);
vColor = pic.color;
vColor = prim.task.color;
gl_Position = uTransform * vec4(pos, 0.0, 1.0);
}

View File

@ -194,6 +194,47 @@ RenderTaskData fetch_render_task(int index) {
return task;
}
/*
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;
float render_target_layer_index;
vec2 content_origin;
vec4 color;
};
PictureTask fetch_picture_task(int address) {
RenderTaskData task_data = fetch_render_task(address);
return PictureTask(
RectWithSize(task_data.data0.xy, task_data.data0.zw),
task_data.data1.x,
task_data.data1.yz,
task_data.data2
);
}
struct BlurTask {
RectWithSize target_rect;
float render_target_layer_index;
float blur_radius;
vec4 color;
};
BlurTask fetch_blur_task(int address) {
RenderTaskData task_data = fetch_render_task(address);
return BlurTask(
RectWithSize(task_data.data0.xy, task_data.data0.zw),
task_data.data1.x,
task_data.data1.y,
task_data.data2
);
}
struct AlphaBatchTask {
vec2 screen_space_origin;
vec2 render_target_origin;
@ -359,7 +400,11 @@ CompositeInstance fetch_composite_instance() {
struct Primitive {
Layer layer;
ClipArea clip_area;
#ifdef PRIMITIVE_HAS_PICTURE_TASK
PictureTask task;
#else
AlphaBatchTask task;
#endif
RectWithSize local_rect;
RectWithSize local_clip_rect;
int specific_prim_address;
@ -387,7 +432,11 @@ Primitive load_primitive() {
prim.layer = fetch_layer(pi.layer_index);
prim.clip_area = fetch_clip_area(pi.clip_task_index);
#ifdef PRIMITIVE_HAS_PICTURE_TASK
prim.task = fetch_picture_task(pi.render_task_index);
#else
prim.task = fetch_alpha_batch_task(pi.render_task_index);
#endif
PrimitiveGeometry geom = fetch_primitive_geometry(pi.prim_address);
prim.local_rect = geom.local_rect;
@ -659,17 +708,6 @@ Rectangle fetch_rectangle(int address) {
return Rectangle(data);
}
struct Picture {
vec4 color;
vec2 offset;
float blur_radius;
};
Picture fetch_picture(int address) {
vec4 data[2] = fetch_from_resource_cache_2(address);
return Picture(data[0], data[1].xy, data[1].z);
}
struct TextRun {
vec4 color;
vec2 offset;

View File

@ -63,17 +63,6 @@ void main(void) {
offset,
vGradientRepeat);
// 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;
oFragColor = color * do_clip();
}
#endif

View File

@ -110,10 +110,10 @@ void write_color(vec4 color0, vec4 color1, int style, vec2 delta, int instance_k
break;
}
vColor00 = vec4(clamp(color0.rgb * modulate.x, vec3(0.0), vec3(1.0)), color0.a);
vColor01 = vec4(clamp(color0.rgb * modulate.y, vec3(0.0), vec3(1.0)), color0.a);
vColor10 = vec4(clamp(color1.rgb * modulate.z, vec3(0.0), vec3(1.0)), color1.a);
vColor11 = vec4(clamp(color1.rgb * modulate.w, vec3(0.0), vec3(1.0)), color1.a);
vColor00 = vec4(clamp(color0.rgb * modulate.x, vec3(0.0), vec3(color0.a)), color0.a);
vColor01 = vec4(clamp(color0.rgb * modulate.y, vec3(0.0), vec3(color0.a)), color0.a);
vColor10 = vec4(clamp(color1.rgb * modulate.z, vec3(0.0), vec3(color1.a)), color1.a);
vColor11 = vec4(clamp(color1.rgb * modulate.w, vec3(0.0), vec3(color1.a)), color1.a);
}
int select_style(int color_select, vec2 fstyle) {
@ -322,7 +322,7 @@ void main(void) {
vec2 local_pos = vLocalPos;
#endif
alpha = min(alpha, do_clip());
alpha *= do_clip();
float aa_range = compute_aa_range(local_pos);
@ -399,6 +399,6 @@ void main(void) {
float m = distance_aa(aa_range, -ld);
vec4 color = mix(color0, color1, m);
oFragColor = color * vec4(1.0, 1.0, 1.0, alpha);
oFragColor = color * alpha;
}
#endif

View File

@ -75,7 +75,7 @@ void write_color0(vec4 color, float style, bool flip) {
break;
}
vColor0 = vec4(color.rgb * modulate.x, color.a);
vColor0 = vec4(min(color.rgb * modulate.x, vec3(color.a)), color.a);
}
void write_color1(vec4 color, float style, bool flip) {
@ -97,7 +97,7 @@ void write_color1(vec4 color, float style, bool flip) {
break;
}
vColor1 = vec4(color.rgb * modulate.y, color.a);
vColor1 = vec4(min(color.rgb * modulate.y, vec3(color.a)), color.a);
}
void write_clip_params(float style,
@ -250,7 +250,7 @@ void main(void) {
vec2 local_pos = vLocalPos;
#endif
alpha = min(alpha, do_clip());
alpha *= do_clip();
// Find the appropriate distance to apply the step over.
float aa_range = compute_aa_range(local_pos);
@ -295,6 +295,6 @@ void main(void) {
// Select between dot/dash alpha based on clip mode.
alpha = min(alpha, mix(dash_alpha, dot_alpha, vClipSelect));
oFragColor = color * vec4(1.0, 1.0, 1.0, alpha);
oFragColor = color * alpha;
}
#endif

View File

@ -1,76 +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 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.
void main(void) {
Primitive prim = load_primitive();
VertexInfo vi = write_vertex(prim.local_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;
#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);
vec2 f = (vi.local_pos - prim.local_rect.p0) / prim.local_rect.size;
vUv.xy = mix(uv0 / texture_size,
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);
#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

@ -107,7 +107,7 @@ void main(void) {
vec2 local_pos = vPos;
#endif
alpha = min(alpha, do_clip());
alpha *= do_clip();
oFragColor = dither(vColor * alpha);
}
#endif

View File

@ -99,7 +99,7 @@ void main(void) {
vec2 upper_bound_mask = vec2(0.0);
#endif
alpha = min(alpha, do_clip());
alpha *= do_clip();
// We calculate the particular tile this fragment belongs to, taking into
// account the spacing in between tiles. We only paint if our fragment does

View File

@ -2,6 +2,10 @@
* 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/. */
#ifdef WR_FEATURE_CACHE
#define PRIMITIVE_HAS_PICTURE_TASK
#endif
#include shared,prim_shared
varying vec4 vColor;
@ -22,13 +26,14 @@ varying vec2 vLocalPos;
struct Line {
vec4 color;
float wavyLineThickness;
float style;
float orientation;
};
Line fetch_line(int address) {
vec4 data[2] = fetch_from_resource_cache_2(address);
return Line(data[0], data[1].x, data[1].y);
return Line(data[0], data[1].x, data[1].y, data[1].z);
}
void main(void) {
@ -58,59 +63,51 @@ void main(void) {
break;
}
case LINE_STYLE_DASHED: {
// y = dash on + off length
// z = dash length
// w = center line of edge cross-axis (for dots only)
float desired_dash_length = size.y * 3.0;
// Consider half total length since there is an equal on/off for each dash.
float dash_count = 1.0 + ceil(size.x / desired_dash_length);
float dash_length = size.x / dash_count;
vParams = vec4(2.0 * dash_length,
dash_length,
float dash_length = size.y * 3.0;
vParams = vec4(2.0 * dash_length, // period
dash_length, // dash length
0.0,
0.0);
break;
}
case LINE_STYLE_DOTTED: {
float diameter = size.y;
float radius = 0.5 * diameter;
float dot_count = ceil(0.5 * size.x / diameter);
float empty_space = size.x - dot_count * diameter;
float distance_between_centers = diameter + empty_space / dot_count;
float period = diameter * 2.0;
float center_line = pos.y + 0.5 * size.y;
vParams = vec4(distance_between_centers,
radius,
float max_x = floor(size.x / period) * period;
vParams = vec4(period,
diameter / 2.0, // radius
center_line,
0.0);
max_x);
break;
}
case LINE_STYLE_WAVY: {
// Choose some arbitrary values to scale thickness,
// wave period etc.
// TODO(gw): Tune these to get closer to what Gecko uses.
float thickness = 0.15 * size.y;
vParams = vec4(thickness,
size.y * 0.5,
size.y * 0.75,
size.y * 0.5);
// This logic copied from gecko to get the same results
float line_thickness = max(line.wavyLineThickness, 1.0);
// Difference in height between peaks and troughs
// (and since slopes are 45 degrees, the length of each slope)
float slope_length = size.y - line_thickness;
// Length of flat runs
float flat_length = max((line_thickness - 1.0) * 2.0, 1.0);
vParams = vec4(line_thickness / 2.0,
slope_length,
flat_length,
size.y);
break;
}
}
#ifdef WR_FEATURE_CACHE
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 + pic.offset - picture_geom.local_rect.p0);
vec2 device_origin = prim.task.target_rect.p0 +
uDevicePixelRatio * (prim.local_rect.p0 - prim.task.content_origin);
vec2 device_size = uDevicePixelRatio * prim.local_rect.size;
vec2 device_pos = mix(device_origin,
device_origin + device_size,
aPosition.xy);
vColor = pic.color;
vColor = prim.task.color;
vLocalPos = mix(prim.local_rect.p0,
prim.local_rect.p0 + prim.local_rect.size,
aPosition.xy);
@ -142,6 +139,9 @@ void main(void) {
#endif
#ifdef WR_FRAGMENT_SHADER
#define MAGIC_WAVY_LINE_AA_SNAP 0.7
float det(vec2 a, vec2 b) {
return a.x * b.y - b.x * a.y;
}
@ -215,40 +215,51 @@ void main(void) {
vec2 dot_relative_pos = vec2(x, pos.y) - vParams.yz;
float dot_distance = length(dot_relative_pos) - vParams.y;
alpha = min(alpha, distance_aa(aa_range, dot_distance));
// Clip off partial dots
alpha *= step(pos.x - vLocalOrigin.x, vParams.w);
break;
}
case LINE_STYLE_WAVY: {
vec2 normalized_local_pos = pos - vLocalOrigin.xy;
float y0 = vParams.y;
float dy = vParams.z;
float dx = vParams.w;
float half_line_thickness = vParams.x;
float slope_length = vParams.y;
float flat_length = vParams.z;
float vertical_bounds = vParams.w;
// Our pattern is just two slopes and two flats
float half_period = slope_length + flat_length;
// Flip the position of the bezier center points each
// wave period.
dy *= step(mod(normalized_local_pos.x, 4.0 * dx), 2.0 * dx) * 2.0 - 1.0;
float mid_height = vertical_bounds / 2.0;
float peak_offset = mid_height - half_line_thickness;
// Flip the wave every half period
float flip = -2.0 * (step(mod(normalized_local_pos.x, 2.0 * half_period), half_period) - 0.5);
// float flip = -1.0;
peak_offset *= flip;
float peak_height = mid_height + peak_offset;
// Convert pos to a local position within one wave period.
normalized_local_pos.x = dx + mod(normalized_local_pos.x, 2.0 * dx);
// Convert pos to a local position within one half period
normalized_local_pos.x = mod(normalized_local_pos.x, half_period);
// Evaluate SDF to the first bezier.
vec2 b0_0 = vec2(0.0 * dx, y0);
vec2 b1_0 = vec2(1.0 * dx, y0 - dy);
vec2 b2_0 = vec2(2.0 * dx, y0);
float d1 = approx_distance(normalized_local_pos, b0_0, b1_0, b2_0);
// Compute signed distance to the 3 lines that make up an arc
float dist1 = distance_to_line(vec2(0.0, peak_height),
vec2(1.0, -flip),
normalized_local_pos);
float dist2 = distance_to_line(vec2(0.0, peak_height),
vec2(0, -flip),
normalized_local_pos);
float dist3 = distance_to_line(vec2(flat_length, peak_height),
vec2(-1.0, -flip),
normalized_local_pos);
float dist = abs(max(max(dist1, dist2), dist3));
// Evaluate SDF to the second bezier.
vec2 b0_1 = vec2(2.0 * dx, y0);
vec2 b1_1 = vec2(3.0 * dx, y0 + dy);
vec2 b2_1 = vec2(4.0 * dx, y0);
float d2 = approx_distance(normalized_local_pos, b0_1, b1_1, b2_1);
// Apply AA based on the thickness of the wave
alpha = distance_aa(aa_range, dist - half_line_thickness);
// SDF union - this is needed to avoid artifacts where the
// bezier curves join.
float d = min(d1, d2);
// Disable AA for thin lines
if (half_line_thickness <= 1.0) {
alpha = 1.0 - step(alpha, MAGIC_WAVY_LINE_AA_SNAP);
}
// Apply AA based on the thickness of the wave.
alpha = distance_aa(aa_range, d - vParams.x);
break;
}
}

View File

@ -110,17 +110,6 @@ void main(void) {
offset,
vGradientRepeat);
// 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;
oFragColor = color * do_clip();
}
#endif

View File

@ -47,8 +47,8 @@ void main(void) {
#endif
#ifdef WR_FEATURE_CLIP
alpha = min(alpha, do_clip());
alpha *= do_clip();
#endif
oFragColor = vColor * vec4(1.0, 1.0, 1.0, alpha);
oFragColor = vColor * alpha;
}
#endif

View File

@ -13,6 +13,12 @@ varying vec3 vLocalPos;
#endif
#ifdef WR_VERTEX_SHADER
#define MODE_ALPHA 0
#define MODE_SUBPX_PASS0 1
#define MODE_SUBPX_PASS1 2
#define MODE_COLOR_BITMAP 3
void main(void) {
Primitive prim = load_primitive();
TextRun text = fetch_text_run(prim.specific_prim_address);
@ -53,22 +59,27 @@ void main(void) {
write_clip(vi.screen_pos, prim.clip_area);
switch (uMode) {
case MODE_ALPHA:
case MODE_SUBPX_PASS1:
vColor = text.color;
break;
case MODE_SUBPX_PASS0:
case MODE_COLOR_BITMAP:
vColor = vec4(text.color.a);
break;
}
vec2 texture_size = vec2(textureSize(sColor0, 0));
vec2 st0 = res.uv_rect.xy / texture_size;
vec2 st1 = res.uv_rect.zw / texture_size;
vColor = vec4(text.color.rgb * text.color.a, text.color.a);
vUv = vec3(mix(st0, st1, f), res.layer);
vUvBorder = (res.uv_rect + vec4(0.5, 0.5, -0.5, -0.5)) / texture_size.xyxy;
}
#endif
#ifdef WR_FRAGMENT_SHADER
#define MODE_ALPHA 0
#define MODE_SUBPX_PASS0 1
#define MODE_SUBPX_PASS1 2
void main(void) {
vec3 tc = vec3(clamp(vUv.xy, vUvBorder.xy, vUvBorder.zw), vUv.z);
vec4 color = texture(sColor0, tc);
@ -79,23 +90,6 @@ void main(void) {
#endif
alpha *= do_clip();
// TODO(gw): It would be worth profiling this and seeing
// if we should instead handle the mode via
// a combination of mix() etc. Branching on
// a uniform is probably fast in most GPUs now though?
vec4 modulate_color = vec4(0.0);
switch (uMode) {
case MODE_ALPHA:
modulate_color = alpha * vColor;
break;
case MODE_SUBPX_PASS0:
modulate_color = vec4(alpha) * vColor.a;
break;
case MODE_SUBPX_PASS1:
modulate_color = alpha * vColor;
break;
}
oFragColor = color * modulate_color;
oFragColor = color * vColor * alpha;
}
#endif

View File

@ -162,7 +162,7 @@ void main(void) {
vec2 relative_pos_in_rect = vLocalPos;
#endif
alpha = min(alpha, do_clip());
alpha *= do_clip();
// We clamp the texture coordinates to the half-pixel offset from the borders
// in order to avoid sampling outside of the texture area.

View File

@ -27,6 +27,10 @@
// Vertex shader attributes and uniforms
//======================================================================================
#ifdef WR_VERTEX_SHADER
// A generic uniform that shaders can optionally use to configure
// an operation mode for this batch.
uniform int uMode;
// Uniform inputs
uniform mat4 uTransform; // Orthographic projection
uniform float uDevicePixelRatio;
@ -39,10 +43,6 @@
// Fragment shader attributes and uniforms
//======================================================================================
#ifdef WR_FRAGMENT_SHADER
// A generic uniform that shaders can optionally use to configure
// an operation mode for this batch.
uniform int uMode;
// Uniform inputs
// Fragment shader outputs

View File

@ -243,10 +243,10 @@ impl FrameBuilder {
let bottom = &border.bottom;
// These colors are used during inset/outset scaling.
let left_color = left.border_color(1.0, 2.0 / 3.0, 0.3, 0.7);
let top_color = top.border_color(1.0, 2.0 / 3.0, 0.3, 0.7);
let right_color = right.border_color(2.0 / 3.0, 1.0, 0.7, 0.3);
let bottom_color = bottom.border_color(2.0 / 3.0, 1.0, 0.7, 0.3);
let left_color = left.border_color(1.0, 2.0 / 3.0, 0.3, 0.7).premultiplied();
let top_color = top.border_color(1.0, 2.0 / 3.0, 0.3, 0.7).premultiplied();
let right_color = right.border_color(2.0 / 3.0, 1.0, 0.7, 0.3).premultiplied();
let bottom_color = bottom.border_color(2.0 / 3.0, 1.0, 0.7, 0.3).premultiplied();
let prim_cpu = BorderPrimitiveCpu {
corner_instances,

View File

@ -0,0 +1,307 @@
/* 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/. */
use api::{ColorF, LayerPoint, LayerRect, LayerSize, LayerVector2D};
use api::{BorderRadius, BoxShadowClipMode, LayoutSize, LayerPrimitiveInfo};
use api::{ClipMode, ComplexClipRegion, LocalClip, ClipAndScrollInfo};
use clip::ClipSource;
use frame_builder::FrameBuilder;
use prim_store::{PrimitiveContainer, RectanglePrimitive, BrushPrimitive};
use picture::PicturePrimitive;
use util::RectHelpers;
// The blur shader samples BLUR_SAMPLE_SCALE * blur_radius surrounding texels.
pub const BLUR_SAMPLE_SCALE: f32 = 3.0;
impl FrameBuilder {
pub fn add_box_shadow(
&mut self,
clip_and_scroll: ClipAndScrollInfo,
prim_info: &LayerPrimitiveInfo,
box_offset: &LayerVector2D,
color: &ColorF,
blur_radius: f32,
spread_radius: f32,
border_radius: BorderRadius,
clip_mode: BoxShadowClipMode,
) {
if color.a == 0.0 {
return;
}
let spread_amount = match clip_mode {
BoxShadowClipMode::Outset => {
spread_radius
}
BoxShadowClipMode::Inset => {
-spread_radius
}
};
let shadow_radius = adjust_border_radius_for_box_shadow(
border_radius,
spread_amount,
);
let shadow_rect = prim_info.rect
.translate(box_offset)
.inflate(spread_amount, spread_amount);
if blur_radius == 0.0 {
let mut clips = Vec::new();
let fast_info = match clip_mode {
BoxShadowClipMode::Outset => {
// TODO(gw): Add a fast path for ClipOut + zero border radius!
clips.push(ClipSource::RoundedRectangle(
prim_info.rect,
border_radius,
ClipMode::ClipOut
));
LayerPrimitiveInfo::with_clip(
shadow_rect,
LocalClip::RoundedRect(
shadow_rect,
ComplexClipRegion::new(
shadow_rect,
shadow_radius,
ClipMode::Clip,
),
),
)
}
BoxShadowClipMode::Inset => {
clips.push(ClipSource::RoundedRectangle(
shadow_rect,
shadow_radius,
ClipMode::ClipOut
));
LayerPrimitiveInfo::with_clip(
prim_info.rect,
LocalClip::RoundedRect(
prim_info.rect,
ComplexClipRegion::new(
prim_info.rect,
border_radius,
ClipMode::Clip
),
),
)
}
};
self.add_primitive(
clip_and_scroll,
&fast_info,
clips,
PrimitiveContainer::Rectangle(RectanglePrimitive {
color: *color,
}),
);
} else {
let blur_offset = 2.0 * blur_radius;
let mut extra_clips = vec![];
let mut blur_regions = vec![];
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),
);
let pic_rect = shadow_rect.inflate(blur_offset, blur_offset);
let blur_range = BLUR_SAMPLE_SCALE * blur_radius;
let size = pic_rect.size;
let tl = LayerSize::new(
blur_radius.max(border_radius.top_left.width),
blur_radius.max(border_radius.top_left.height)
) * BLUR_SAMPLE_SCALE;
let tr = LayerSize::new(
blur_radius.max(border_radius.top_right.width),
blur_radius.max(border_radius.top_right.height)
) * BLUR_SAMPLE_SCALE;
let br = LayerSize::new(
blur_radius.max(border_radius.bottom_right.width),
blur_radius.max(border_radius.bottom_right.height)
) * BLUR_SAMPLE_SCALE;
let bl = LayerSize::new(
blur_radius.max(border_radius.bottom_left.width),
blur_radius.max(border_radius.bottom_left.height)
) * BLUR_SAMPLE_SCALE;
let max_width = tl.width.max(tr.width.max(bl.width.max(br.width)));
let max_height = tl.height.max(tr.height.max(bl.height.max(br.height)));
// Apply a conservative test that if any of the blur regions below
// will overlap, we won't bother applying the region optimization
// and will just blur the entire thing. This should only happen
// in rare cases, where either the blur radius or border radius
// is very large, in which case there's no real point in trying
// to only blur a small region anyway.
if max_width < 0.5 * size.width && max_height < 0.5 * size.height {
blur_regions.push(LayerRect::from_floats(0.0, 0.0, tl.width, tl.height));
blur_regions.push(LayerRect::from_floats(size.width - tr.width, 0.0, size.width, tr.height));
blur_regions.push(LayerRect::from_floats(size.width - br.width, size.height - br.height, size.width, size.height));
blur_regions.push(LayerRect::from_floats(0.0, size.height - bl.height, bl.width, size.height));
blur_regions.push(LayerRect::from_floats(0.0, tl.height, blur_range, size.height - bl.height));
blur_regions.push(LayerRect::from_floats(size.width - blur_range, tr.height, size.width, size.height - br.height));
blur_regions.push(LayerRect::from_floats(tl.width, 0.0, size.width - tr.width, blur_range));
blur_regions.push(LayerRect::from_floats(bl.width, size.height - blur_range, size.width - br.width, size.height));
}
let mut pic_prim = PicturePrimitive::new_box_shadow(
blur_radius,
*color,
blur_regions,
BoxShadowClipMode::Outset,
);
pic_prim.add_primitive(
brush_prim_index,
&brush_rect,
clip_and_scroll
);
pic_prim.build();
extra_clips.push(ClipSource::RoundedRectangle(
prim_info.rect,
border_radius,
ClipMode::ClipOut,
));
let pic_info = LayerPrimitiveInfo::new(pic_rect);
self.add_primitive(
clip_and_scroll,
&pic_info,
extra_clips,
PrimitiveContainer::Picture(pic_prim),
);
}
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),
);
let pic_rect = prim_info.rect.inflate(blur_offset, blur_offset);
// TODO(gw): Apply minimal blur regions for inset box shadows.
let mut pic_prim = PicturePrimitive::new_box_shadow(
blur_radius,
*color,
blur_regions,
BoxShadowClipMode::Inset,
);
pic_prim.add_primitive(
brush_prim_index,
&prim_info.rect,
clip_and_scroll
);
pic_prim.build();
extra_clips.push(ClipSource::RoundedRectangle(
prim_info.rect,
border_radius,
ClipMode::Clip,
));
let pic_info = LayerPrimitiveInfo::with_clip_rect(pic_rect, prim_info.rect);
self.add_primitive(
clip_and_scroll,
&pic_info,
extra_clips,
PrimitiveContainer::Picture(pic_prim),
);
}
}
}
}
}
fn adjust_border_radius_for_box_shadow(
radius: BorderRadius,
spread_amount: f32,
) -> BorderRadius {
BorderRadius {
top_left: adjust_corner_for_box_shadow(
radius.top_left,
spread_amount,
),
top_right: adjust_corner_for_box_shadow(
radius.top_right,
spread_amount,
),
bottom_right: adjust_corner_for_box_shadow(
radius.bottom_right,
spread_amount,
),
bottom_left: adjust_corner_for_box_shadow(
radius.bottom_left,
spread_amount,
),
}
}
fn adjust_corner_for_box_shadow(
corner: LayoutSize,
spread_amount: f32,
) -> LayoutSize {
LayoutSize::new(
adjust_radius_for_box_shadow(
corner.width,
spread_amount
),
adjust_radius_for_box_shadow(
corner.height,
spread_amount
),
)
}
fn adjust_radius_for_box_shadow(
border_radius: f32,
spread_amount: f32,
) -> f32 {
if border_radius > 0.0 {
(border_radius + spread_amount).max(0.0)
} else {
0.0
}
}

View File

@ -513,6 +513,7 @@ impl ClipScrollTree {
NodeType::Clip(ref info) => {
pt.new_level("Clip".to_owned());
pt.add_item(format!("id: {:?}", id));
let clips = clip_store.get(&info.clip_sources).clips();
pt.new_level(format!("Clip Sources [{}]", clips.len()));
for source in clips {
@ -522,14 +523,17 @@ impl ClipScrollTree {
}
NodeType::ReferenceFrame(ref info) => {
pt.new_level(format!("ReferenceFrame {:?}", info.transform));
pt.add_item(format!("id: {:?}", id));
}
NodeType::ScrollFrame(scrolling_info) => {
pt.new_level(format!("ScrollFrame"));
pt.add_item(format!("id: {:?}", id));
pt.add_item(format!("scrollable_size: {:?}", scrolling_info.scrollable_size));
pt.add_item(format!("scroll.offset: {:?}", scrolling_info.offset));
}
NodeType::StickyFrame(sticky_frame_info, sticky_offset) => {
pt.new_level(format!("StickyFrame"));
pt.add_item(format!("id: {:?}", id));
pt.add_item(format!("sticky info: {:?}", sticky_frame_info));
pt.add_item(format!("sticky offset: {:?}", sticky_offset));
}

File diff suppressed because it is too large Load Diff

View File

@ -2,8 +2,8 @@
* 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::{BorderDetails, BorderDisplayItem, BorderRadius, BoxShadowClipMode, BuiltDisplayList};
use api::{ClipMode, ComplexClipRegion, ClipAndScrollInfo, ClipId, ColorF, LayoutSize};
use api::{BorderDetails, BorderDisplayItem, BuiltDisplayList};
use api::{ClipAndScrollInfo, ClipId, ColorF};
use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintRect, DeviceUintSize};
use api::{ExtendMode, FilterOp, FontInstance, FontRenderMode};
use api::{GlyphInstance, GlyphOptions, GradientStop, HitTestFlags, HitTestItem, HitTestResult};
@ -23,7 +23,7 @@ use gpu_cache::GpuCache;
use internal_types::{FastHashMap, FastHashSet, HardwareCompositeOp};
use picture::{PicturePrimitive};
use plane_split::{BspSplitter, Polygon, Splitter};
use prim_store::{BrushPrimitive, TexelRect, YuvImagePrimitiveCpu};
use prim_store::{TexelRect, YuvImagePrimitiveCpu};
use prim_store::{GradientPrimitiveCpu, ImagePrimitiveCpu, LinePrimitive, PrimitiveKind};
use prim_store::{PrimitiveContainer, PrimitiveIndex};
use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu};
@ -39,9 +39,7 @@ use tiling::{ContextIsolation, RenderTargetKind, StackingContextIndex};
use tiling::{PackedLayer, PackedLayerIndex, PrimitiveFlags, PrimitiveRunCmd, RenderPass};
use tiling::{RenderTargetContext, ScrollbarPrimitive, StackingContext};
use util::{self, pack_as_float, RectHelpers, recycle_vec};
// The blur shader samples BLUR_SAMPLE_SCALE * blur_radius surrounding texels.
const BLUR_SAMPLE_SCALE: f32 = 3.0;
use box_shadow::BLUR_SAMPLE_SCALE;
/// Construct a polygon from stacking context boundaries.
/// `anchor` here is an index that's going to be preserved in all the
@ -104,6 +102,7 @@ impl HitTestingItem {
pub struct HitTestingRun(Vec<HitTestingItem>, ClipAndScrollInfo);
/// A builder structure for `RendererFrame`
pub struct FrameBuilder {
screen_size: DeviceUintSize,
background_color: Option<ColorF>,
@ -111,7 +110,7 @@ pub struct FrameBuilder {
pub clip_store: ClipStore,
cmds: Vec<PrimitiveRunCmd>,
hit_testing_runs: Vec<HitTestingRun>,
config: FrameBuilderConfig,
pub config: FrameBuilderConfig,
stacking_context_store: Vec<StackingContext>,
clip_scroll_group_store: Vec<ClipScrollGroup>,
@ -178,11 +177,11 @@ impl<'a> PrimitiveContext<'a> {
impl FrameBuilder {
pub fn new(
previous: Option<FrameBuilder>,
previous: Option<Self>,
screen_size: DeviceUintSize,
background_color: Option<ColorF>,
config: FrameBuilderConfig,
) -> FrameBuilder {
) -> Self {
match previous {
Some(prev) => FrameBuilder {
stacking_context_store: recycle_vec(prev.stacking_context_store),
@ -228,7 +227,7 @@ impl FrameBuilder {
/// Create a primitive and add it to the prim store. This method doesn't
/// add the primitive to the draw list, so can be used for creating
/// sub-primitives.
fn create_primitive(
pub fn create_primitive(
&mut self,
clip_and_scroll: ClipAndScrollInfo,
info: &LayerPrimitiveInfo,
@ -589,10 +588,8 @@ impl FrameBuilder {
// safe to offset the local rect by the offset of the shadow, which
// is then used when blitting the shadow to the final location.
let metadata = &mut self.prim_store.cpu_metadata[prim_index.0];
let prim = &self.prim_store.cpu_pictures[metadata.cpu_prim_index.0];
let shadow = prim.as_text_shadow();
metadata.local_rect = metadata.local_rect.translate(&shadow.offset);
let prim = &mut self.prim_store.cpu_pictures[metadata.cpu_prim_index.0];
metadata.local_rect = prim.build();
}
// Push any fast-path shadows now
@ -650,26 +647,13 @@ impl FrameBuilder {
&mut self,
clip_and_scroll: ClipAndScrollInfo,
info: &LayerPrimitiveInfo,
baseline: f32,
start: f32,
end: f32,
wavy_line_thickness: f32,
orientation: LineOrientation,
width: f32,
color: &ColorF,
style: LineStyle,
) {
let new_rect = match orientation {
LineOrientation::Horizontal => LayerRect::new(
LayerPoint::new(start, baseline),
LayerSize::new(end - start, width),
),
LineOrientation::Vertical => LayerRect::new(
LayerPoint::new(baseline, start),
LayerSize::new(width, end - start),
),
};
let line = LinePrimitive {
wavy_line_thickness,
color: *color,
style: style,
orientation: orientation,
@ -689,7 +673,7 @@ impl FrameBuilder {
let mut line = line.clone();
line.color = shadow.color;
let mut info = info.clone();
info.rect = new_rect.translate(&shadow.offset);
info.rect = info.rect.translate(&shadow.offset);
let prim_index = self.create_primitive(
clip_and_scroll,
&info,
@ -699,8 +683,6 @@ impl FrameBuilder {
self.shadow_prim_stack[idx].1.push((prim_index, clip_and_scroll));
}
let mut info = info.clone();
info.rect = new_rect;
let prim_index = self.create_primitive(
clip_and_scroll,
&info,
@ -713,7 +695,7 @@ impl FrameBuilder {
self.add_primitive_to_hit_testing_list(&info, clip_and_scroll);
self.add_primitive_to_draw_list(prim_index, clip_and_scroll);
} else {
self.pending_shadow_contents.push((prim_index, clip_and_scroll, info));
self.pending_shadow_contents.push((prim_index, clip_and_scroll, *info));
}
}
@ -726,12 +708,11 @@ impl FrameBuilder {
// Only run real blurs here (fast path zero blurs are handled above).
if blur_radius > 0.0 {
let shadow_rect = new_rect.inflate(
blur_radius,
blur_radius,
picture.add_primitive(
prim_index,
&info.rect,
clip_and_scroll,
);
shadow_metadata.local_rect = shadow_metadata.local_rect.union(&shadow_rect);
picture.add_primitive(prim_index, clip_and_scroll);
}
}
}
@ -1243,240 +1224,11 @@ impl FrameBuilder {
// Only run real blurs here (fast path zero blurs are handled above).
let blur_radius = picture_prim.as_text_shadow().blur_radius;
if blur_radius > 0.0 {
let shadow_rect = rect.inflate(
blur_radius,
blur_radius,
picture_prim.add_primitive(
prim_index,
&rect,
clip_and_scroll,
);
shadow_metadata.local_rect = shadow_metadata.local_rect.union(&shadow_rect);
picture_prim.add_primitive(prim_index, clip_and_scroll);
}
}
}
pub fn add_box_shadow(
&mut self,
clip_and_scroll: ClipAndScrollInfo,
prim_info: &LayerPrimitiveInfo,
box_offset: &LayerVector2D,
color: &ColorF,
blur_radius: f32,
spread_radius: f32,
border_radius: BorderRadius,
clip_mode: BoxShadowClipMode,
) {
if color.a == 0.0 {
return;
}
let spread_amount = match clip_mode {
BoxShadowClipMode::Outset => {
spread_radius
}
BoxShadowClipMode::Inset => {
-spread_radius
}
};
let shadow_radius = adjust_border_radius_for_box_shadow(
border_radius,
spread_amount,
spread_radius
);
let shadow_rect = prim_info.rect
.translate(box_offset)
.inflate(spread_amount, spread_amount);
if blur_radius == 0.0 {
let mut clips = Vec::new();
let fast_info = match clip_mode {
BoxShadowClipMode::Outset => {
// TODO(gw): Add a fast path for ClipOut + zero border radius!
clips.push(ClipSource::RoundedRectangle(
prim_info.rect,
border_radius,
ClipMode::ClipOut
));
LayerPrimitiveInfo::with_clip(
shadow_rect,
LocalClip::RoundedRect(
shadow_rect,
ComplexClipRegion::new(
shadow_rect,
shadow_radius,
ClipMode::Clip,
),
),
)
}
BoxShadowClipMode::Inset => {
clips.push(ClipSource::RoundedRectangle(
shadow_rect,
shadow_radius,
ClipMode::ClipOut
));
LayerPrimitiveInfo::with_clip(
prim_info.rect,
LocalClip::RoundedRect(
prim_info.rect,
ComplexClipRegion::new(
prim_info.rect,
border_radius,
ClipMode::Clip
),
),
)
}
};
self.add_primitive(
clip_and_scroll,
&fast_info,
clips,
PrimitiveContainer::Rectangle(RectanglePrimitive {
color: *color,
}),
);
} else {
let blur_offset = 2.0 * blur_radius;
let mut extra_clips = vec![];
let mut blur_regions = vec![];
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),
);
let pic_rect = shadow_rect.inflate(blur_offset, blur_offset);
let blur_range = BLUR_SAMPLE_SCALE * blur_radius;
let size = pic_rect.size;
let tl = LayerSize::new(
blur_radius.max(border_radius.top_left.width),
blur_radius.max(border_radius.top_left.height)
) * BLUR_SAMPLE_SCALE;
let tr = LayerSize::new(
blur_radius.max(border_radius.top_right.width),
blur_radius.max(border_radius.top_right.height)
) * BLUR_SAMPLE_SCALE;
let br = LayerSize::new(
blur_radius.max(border_radius.bottom_right.width),
blur_radius.max(border_radius.bottom_right.height)
) * BLUR_SAMPLE_SCALE;
let bl = LayerSize::new(
blur_radius.max(border_radius.bottom_left.width),
blur_radius.max(border_radius.bottom_left.height)
) * BLUR_SAMPLE_SCALE;
let max_width = tl.width.max(tr.width.max(bl.width.max(br.width)));
let max_height = tl.height.max(tr.height.max(bl.height.max(br.height)));
// Apply a conservative test that if any of the blur regions below
// will overlap, we won't bother applying the region optimization
// and will just blur the entire thing. This should only happen
// in rare cases, where either the blur radius or border radius
// is very large, in which case there's no real point in trying
// to only blur a small region anyway.
if max_width < 0.5 * size.width && max_height < 0.5 * size.height {
blur_regions.push(LayerRect::from_floats(0.0, 0.0, tl.width, tl.height));
blur_regions.push(LayerRect::from_floats(size.width - tr.width, 0.0, size.width, tr.height));
blur_regions.push(LayerRect::from_floats(size.width - br.width, size.height - br.height, size.width, size.height));
blur_regions.push(LayerRect::from_floats(0.0, size.height - bl.height, bl.width, size.height));
blur_regions.push(LayerRect::from_floats(0.0, tl.height, blur_range, size.height - bl.height));
blur_regions.push(LayerRect::from_floats(size.width - blur_range, tr.height, size.width, size.height - br.height));
blur_regions.push(LayerRect::from_floats(tl.width, 0.0, size.width - tr.width, blur_range));
blur_regions.push(LayerRect::from_floats(bl.width, size.height - blur_range, size.width - br.width, size.height));
}
let mut pic_prim = PicturePrimitive::new_box_shadow(
blur_radius,
*color,
blur_regions,
BoxShadowClipMode::Outset,
);
pic_prim.add_primitive(brush_prim_index, clip_and_scroll);
extra_clips.push(ClipSource::RoundedRectangle(
prim_info.rect,
border_radius,
ClipMode::ClipOut,
));
let pic_info = LayerPrimitiveInfo::new(pic_rect);
self.add_primitive(
clip_and_scroll,
&pic_info,
extra_clips,
PrimitiveContainer::Picture(pic_prim),
);
}
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),
);
let pic_rect = prim_info.rect.inflate(blur_offset, blur_offset);
// TODO(gw): Apply minimal blur regions for inset box shadows.
let mut pic_prim = PicturePrimitive::new_box_shadow(
blur_radius,
*color,
blur_regions,
BoxShadowClipMode::Inset,
);
pic_prim.add_primitive(brush_prim_index, clip_and_scroll);
extra_clips.push(ClipSource::RoundedRectangle(
prim_info.rect,
border_radius,
ClipMode::Clip,
));
let pic_info = LayerPrimitiveInfo::with_clip_rect(pic_rect, prim_info.rect);
self.add_primitive(
clip_and_scroll,
&pic_info,
extra_clips,
PrimitiveContainer::Picture(pic_prim),
);
}
}
}
}
@ -2106,15 +1858,18 @@ impl FrameBuilder {
match *filter {
FilterOp::Blur(blur_radius) => {
let blur_radius = device_length(blur_radius, device_pixel_ratio);
let blur_std_deviation = blur_radius.0 as f32;
let inflate_size = blur_std_deviation * BLUR_SAMPLE_SCALE;
render_tasks.get_mut(current_task_id)
.inflate(blur_radius.0);
.inflate(inflate_size as i32);
let blur_render_task = RenderTask::new_blur(
blur_radius,
blur_std_deviation,
current_task_id,
render_tasks,
RenderTargetKind::Color,
&[],
ClearMode::Transparent,
ColorF::new(0.0, 0.0, 0.0, 0.0),
);
let blur_render_task_id = render_tasks.add(blur_render_task);
let item = AlphaRenderItem::HardwareComposite(
@ -2122,8 +1877,8 @@ impl FrameBuilder {
blur_render_task_id,
HardwareCompositeOp::PremultipliedAlpha,
DeviceIntPoint::new(
screen_origin.x - blur_radius.0,
screen_origin.y - blur_radius.0,
screen_origin.x - inflate_size as i32,
screen_origin.y - inflate_size as i32,
),
next_z,
);
@ -2396,67 +2151,3 @@ impl FrameBuilder {
}
}
}
fn adjust_border_radius_for_box_shadow(
radius: BorderRadius,
spread_amount: f32,
spread_radius: f32,
) -> BorderRadius {
BorderRadius {
top_left: adjust_corner_for_box_shadow(
radius.top_left,
spread_radius,
spread_amount,
),
top_right: adjust_corner_for_box_shadow(
radius.top_right,
spread_radius,
spread_amount,
),
bottom_right: adjust_corner_for_box_shadow(
radius.bottom_right,
spread_radius,
spread_amount,
),
bottom_left: adjust_corner_for_box_shadow(
radius.bottom_left,
spread_radius,
spread_amount,
),
}
}
fn adjust_corner_for_box_shadow(
corner: LayoutSize,
spread_amount: f32,
spread_radius: f32,
) -> LayoutSize {
LayoutSize::new(
adjust_radius_for_box_shadow(
corner.width,
spread_radius,
spread_amount
),
adjust_radius_for_box_shadow(
corner.height,
spread_radius,
spread_amount
),
)
}
fn adjust_radius_for_box_shadow(
border_radius: f32,
spread_amount: f32,
spread_radius: f32,
) -> f32 {
// 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
};
(border_radius + spread_amount * sharpness_scale).max(0.0)
}

View File

@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{DevicePoint, DeviceUintSize, FontInstance, GlyphKey};
use glyph_rasterizer::GlyphFormat;
use internal_types::FastHashMap;
use resource_cache::ResourceClassCache;
use std::sync::Arc;
@ -14,6 +15,7 @@ pub struct CachedGlyphInfo {
pub size: DeviceUintSize,
pub offset: DevicePoint,
pub scale: f32,
pub format: GlyphFormat,
}
pub type GlyphKeyCache = ResourceClassCache<GlyphKey, Option<CachedGlyphInfo>>;

View File

@ -3,10 +3,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#[cfg(test)]
use api::{ColorF, IdNamespace, LayoutPoint};
use api::{DevicePoint, DeviceUintSize, FontInstance};
use api::{FontKey, FontTemplate, FontRenderMode, ColorU};
use api::{GlyphDimensions, GlyphKey, SubpixelDirection};
use api::{ColorF, IdNamespace, LayoutPoint, SubpixelDirection};
use api::{DevicePoint, DeviceUintSize, FontInstance, FontRenderMode};
use api::{FontKey, FontTemplate, GlyphDimensions, GlyphKey};
use api::{ImageData, ImageDescriptor, ImageFormat};
#[cfg(test)]
use app_units::Au;
@ -14,7 +13,7 @@ use device::TextureFilter;
use glyph_cache::{CachedGlyphInfo, GlyphCache};
use gpu_cache::GpuCache;
use internal_types::FastHashSet;
use platform::font::{FontContext, RasterizedGlyph};
use platform::font::FontContext;
use profiler::TextureCacheProfileCounters;
use rayon::ThreadPool;
use rayon::prelude::*;
@ -24,6 +23,35 @@ use std::sync::{Arc, Mutex, MutexGuard};
use std::sync::mpsc::{channel, Receiver, Sender};
use texture_cache::{TextureCache, TextureCacheHandle};
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum GlyphFormat {
Mono,
Alpha,
Subpixel,
ColorBitmap,
}
impl From<FontRenderMode> for GlyphFormat {
fn from(render_mode: FontRenderMode) -> GlyphFormat {
match render_mode {
FontRenderMode::Mono => GlyphFormat::Mono,
FontRenderMode::Alpha => GlyphFormat::Alpha,
FontRenderMode::Subpixel => GlyphFormat::Subpixel,
FontRenderMode::Bitmap => GlyphFormat::ColorBitmap,
}
}
}
pub struct RasterizedGlyph {
pub top: f32,
pub left: f32,
pub width: u32,
pub height: u32,
pub scale: f32,
pub format: GlyphFormat,
pub bytes: Vec<u8>,
}
pub struct FontContexts {
// These worker are mostly accessed from their corresponding worker threads.
// The goal is that there should be no noticeable contention on the muteces.
@ -145,26 +173,7 @@ impl GlyphRasterizer {
}
pub fn prepare_font(&self, font: &mut FontInstance) {
// In alpha/mono mode, the color of the font is irrelevant.
// Forcing it to black in those cases saves rasterizing glyphs
// of different colors when not needed.
match font.render_mode {
FontRenderMode::Mono | FontRenderMode::Bitmap => {
font.color = ColorU::new(255, 255, 255, 255);
// Subpixel positioning is disabled in mono and bitmap modes.
font.subpx_dir = SubpixelDirection::None;
}
FontRenderMode::Alpha => {
font.color = ColorU::new(255, 255, 255, 255);
}
FontRenderMode::Subpixel => {
// In subpixel mode, we only actually need the color if preblending
// is used in the font backend.
if !FontContext::has_gamma_correct_subpixel_aa() {
font.color = ColorU::new(255, 255, 255, 255);
}
}
}
FontContext::prepare_font(font);
}
pub fn request_glyphs(
@ -345,6 +354,7 @@ impl GlyphRasterizer {
size: DeviceUintSize::new(glyph.width, glyph.height),
offset: DevicePoint::new(glyph.left, glyph.top),
scale: glyph.scale,
format: glyph.format,
})
} else {
None

View File

@ -140,22 +140,28 @@ impl From<CompositePrimitiveInstance> for PrimitiveInstance {
}
}
// Whether this brush is being drawn on a Picture
// task (new) or an alpha batch task (legacy).
// Can be removed once everything uses pictures.
pub const BRUSH_FLAG_USES_PICTURE: i32 = (1 << 0);
// TODO(gw): While we are comverting things over, we
// need to have the instance be the same
// size as an old PrimitiveInstance. In the
// future, we can compress this vertex
// format a lot - e.g. z, render task
// addresses etc can reasonably become
// a u16 type.
#[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,
}
}
pub picture_address: RenderTaskAddress,
pub prim_address: GpuCacheAddress,
pub layer_address: PackedLayerAddress,
pub clip_task_address: RenderTaskAddress,
pub z: i32,
pub flags: i32,
pub user_data0: i32,
pub user_data1: i32,
}
impl From<BrushInstance> for PrimitiveInstance {
@ -164,12 +170,12 @@ impl From<BrushInstance> for PrimitiveInstance {
data: [
instance.picture_address.0 as i32,
instance.prim_address.as_int(),
0,
0,
0,
0,
0,
0,
instance.layer_address.0,
instance.clip_task_address.0 as i32,
instance.z,
instance.flags,
instance.user_data0,
instance.user_data1,
]
}
}

View File

@ -157,7 +157,7 @@ impl RendererFrame {
pipeline_epoch_map: FastHashMap<PipelineId, Epoch>,
layers_bouncing_back: FastHashSet<ClipId>,
frame: Option<tiling::Frame>,
) -> RendererFrame {
) -> Self {
RendererFrame {
pipeline_epoch_map,
layers_bouncing_back,

View File

@ -50,6 +50,7 @@ extern crate log;
extern crate thread_profiler;
mod border;
mod box_shadow;
mod clip;
mod clip_scroll_node;
mod clip_scroll_tree;

View File

@ -4,9 +4,10 @@
use api::{ColorF, ClipAndScrollInfo, device_length, DeviceIntSize};
use api::{BoxShadowClipMode, LayerRect, Shadow};
use box_shadow::BLUR_SAMPLE_SCALE;
use frame_builder::PrimitiveContext;
use gpu_cache::GpuDataRequest;
use prim_store::{PrimitiveIndex, PrimitiveMetadata};
use prim_store::PrimitiveIndex;
use render_task::{ClearMode, RenderTask, RenderTaskId, RenderTaskTree};
use tiling::RenderTargetKind;
@ -45,6 +46,7 @@ pub struct PicturePrimitive {
pub prim_runs: Vec<PrimitiveRun>,
pub render_task_id: Option<RenderTaskId>,
pub kind: PictureKind,
pub content_rect: LayerRect,
// TODO(gw): Add a mode that specifies if this
// picture should be rasterized in
@ -56,6 +58,7 @@ impl PicturePrimitive {
PicturePrimitive {
prim_runs: Vec::new(),
render_task_id: None,
content_rect: LayerRect::zero(),
kind: PictureKind::TextShadow {
shadow,
},
@ -71,9 +74,10 @@ impl PicturePrimitive {
PicturePrimitive {
prim_runs: Vec::new(),
render_task_id: None,
content_rect: LayerRect::zero(),
kind: PictureKind::BoxShadow {
blur_radius,
color,
color: color.premultiplied(),
blur_regions,
clip_mode,
},
@ -90,8 +94,19 @@ impl PicturePrimitive {
pub fn add_primitive(
&mut self,
prim_index: PrimitiveIndex,
local_rect: &LayerRect,
clip_and_scroll: ClipAndScrollInfo
) {
// TODO(gw): Accumulating the primitive local rect
// into the content rect here is fine, for now.
// The only way pictures are currently used,
// all the items added to a picture are known
// to be in the same local space. Once we start
// using pictures for other uses, we will need
// to consider the space of a primitive in order
// to build a correct contect rect!
self.content_rect = self.content_rect.union(local_rect);
if let Some(ref mut run) = self.prim_runs.last_mut() {
if run.clip_and_scroll == clip_and_scroll &&
run.prim_index.0 + run.count == prim_index.0 {
@ -107,10 +122,38 @@ impl PicturePrimitive {
});
}
pub fn build(&mut self) -> LayerRect {
match self.kind {
PictureKind::TextShadow { ref shadow } => {
let blur_offset = shadow.blur_radius * BLUR_SAMPLE_SCALE;
self.content_rect = self.content_rect.inflate(
blur_offset,
blur_offset,
);
self.content_rect.translate(&shadow.offset)
}
PictureKind::BoxShadow { blur_radius, .. } => {
// TODO(gw): The 2.0 here should actually be BLUR_SAMPLE_SCALE.
// I'm leaving it as is for now, to avoid having to
// change the code in box_shadow.rs. As I work on
// the box shadow optimizations, I'll fix this up.
let blur_offset = blur_radius * 2.0;
self.content_rect = self.content_rect.inflate(
blur_offset,
blur_offset,
);
self.content_rect
}
}
}
pub fn prepare_for_render(
&mut self,
prim_index: PrimitiveIndex,
prim_metadata: &PrimitiveMetadata,
prim_context: &PrimitiveContext,
render_tasks: &mut RenderTaskTree,
) {
@ -119,64 +162,63 @@ impl PicturePrimitive {
// blur to that text run in order to build the actual primitive
// which will be blitted to the framebuffer.
let cache_width =
(prim_metadata.local_rect.size.width * prim_context.device_pixel_ratio).ceil() as i32;
(self.content_rect.size.width * prim_context.device_pixel_ratio).ceil() as i32;
let cache_height =
(prim_metadata.local_rect.size.height * prim_context.device_pixel_ratio).ceil() as i32;
(self.content_rect.size.height * prim_context.device_pixel_ratio).ceil() as i32;
let cache_size = DeviceIntSize::new(cache_width, cache_height);
let (blur_radius, target_kind, blur_regions, clear_mode) = match self.kind {
let (blur_radius, target_kind, blur_regions, clear_mode, color) = match self.kind {
PictureKind::TextShadow { ref shadow } => {
let dummy: &[LayerRect] = &[];
(shadow.blur_radius, RenderTargetKind::Color, dummy, ClearMode::Transparent)
(shadow.blur_radius,
RenderTargetKind::Color,
dummy,
ClearMode::Transparent,
shadow.color)
}
PictureKind::BoxShadow { blur_radius, clip_mode, ref blur_regions, .. } => {
PictureKind::BoxShadow { blur_radius, clip_mode, ref blur_regions, color, .. } => {
let clear_mode = match clip_mode {
BoxShadowClipMode::Outset => ClearMode::One,
BoxShadowClipMode::Inset => ClearMode::Zero,
};
(blur_radius, RenderTargetKind::Alpha, blur_regions.as_slice(), clear_mode)
(blur_radius,
RenderTargetKind::Alpha,
blur_regions.as_slice(),
clear_mode,
color)
}
};
let blur_radius = device_length(blur_radius, prim_context.device_pixel_ratio);
// Quote from https://drafts.csswg.org/css-backgrounds-3/#shadow-blur
// "the image that would be generated by applying to the shadow a
// Gaussian blur with a standard deviation equal to half the blur radius."
let blur_std_deviation = blur_radius.0 as f32 * 0.5;
let picture_task = RenderTask::new_picture(
cache_size,
prim_index,
target_kind,
self.content_rect.origin,
color,
);
let picture_task_id = render_tasks.add(picture_task);
let render_task = RenderTask::new_blur(
blur_radius,
blur_std_deviation,
picture_task_id,
render_tasks,
target_kind,
blur_regions,
clear_mode,
color,
);
self.render_task_id = Some(render_tasks.add(render_task));
}
pub fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
match self.kind {
PictureKind::TextShadow { ref shadow } => {
request.push(shadow.color);
request.push([
shadow.offset.x,
shadow.offset.y,
shadow.blur_radius,
0.0,
]);
}
PictureKind::BoxShadow { blur_radius, color, .. } => {
request.push(color);
request.push([
0.0,
0.0,
blur_radius,
0.0,
]);
}
}
pub fn write_gpu_blocks(&self, mut _request: GpuDataRequest) {
// TODO(gw): We'll need to write the GPU blocks
// here specific to a brush primitive
// once we start drawing pictures as brushes!
}
pub fn target_kind(&self) -> RenderTargetKind {

View File

@ -4,7 +4,7 @@
use api::{ColorU, FontKey, FontRenderMode, GlyphDimensions};
use api::{FontInstance, FontVariation, NativeFontHandle};
use api::GlyphKey;
use api::{GlyphKey, SubpixelDirection};
use app_units::Au;
use core_foundation::array::{CFArray, CFArrayRef};
use core_foundation::base::TCFType;
@ -22,6 +22,7 @@ use core_text;
use core_text::font::{CTFont, CTFontRef};
use core_text::font_descriptor::{kCTFontDefaultOrientation, kCTFontColorGlyphsTrait};
use gamma_lut::{Color as ColorLut, GammaLut};
use glyph_rasterizer::{GlyphFormat, RasterizedGlyph};
use internal_types::FastHashMap;
use std::collections::hash_map::Entry;
use std::ptr;
@ -37,28 +38,6 @@ pub struct FontContext {
// all hidden inside their font context.
unsafe impl Send for FontContext {}
pub struct RasterizedGlyph {
pub top: f32,
pub left: f32,
pub width: u32,
pub height: u32,
pub scale: f32,
pub bytes: Vec<u8>,
}
impl RasterizedGlyph {
pub fn blank() -> RasterizedGlyph {
RasterizedGlyph {
top: 0.0,
left: 0.0,
width: 0,
height: 0,
scale: 1.0,
bytes: vec![],
}
}
}
struct GlyphMetrics {
rasterized_left: i32,
rasterized_descent: i32,
@ -94,6 +73,14 @@ fn supports_subpixel_aa() -> bool {
data[0] != data[1] || data[1] != data[2]
}
fn should_use_white_on_black(color: ColorU) -> bool {
let r = color.r as f32 / 255.0;
let g = color.g as f32 / 255.0;
let b = color.b as f32 / 255.0;
// These thresholds were determined on 10.12 by observing what CG does.
r >= 0.333 && g >= 0.333 && b >= 0.333 && r + g + b >= 2.0
}
fn get_glyph_metrics(
ct_font: &CTFont,
glyph: CGGlyph,
@ -434,8 +421,24 @@ impl FontContext {
}
}
pub fn has_gamma_correct_subpixel_aa() -> bool {
true
pub fn prepare_font(font: &mut FontInstance) {
match font.render_mode {
FontRenderMode::Mono | FontRenderMode::Bitmap => {
// In mono/bitmap modes the color of the font is irrelevant.
font.color = ColorU::new(255, 255, 255, 255);
// Subpixel positioning is disabled in mono and bitmap modes.
font.subpx_dir = SubpixelDirection::None;
}
FontRenderMode::Alpha => {
font.color = if font.platform_options.unwrap_or_default().font_smoothing &&
should_use_white_on_black(font.color) {
ColorU::new(255, 255, 255, 255)
} else {
ColorU::new(0, 0, 0, 255)
};
}
FontRenderMode::Subpixel => {}
}
}
pub fn rasterize_glyph(
@ -445,14 +448,14 @@ impl FontContext {
) -> Option<RasterizedGlyph> {
let ct_font = match self.get_ct_font(font.font_key, font.size, &font.variations) {
Some(font) => font,
None => return Some(RasterizedGlyph::blank()),
None => return None,
};
let glyph = key.index as CGGlyph;
let (x_offset, y_offset) = font.get_subpx_offset(key);
let metrics = get_glyph_metrics(&ct_font, glyph, x_offset, y_offset);
if metrics.rasterized_width == 0 || metrics.rasterized_height == 0 {
return Some(RasterizedGlyph::blank());
return None;
}
let context_flags = match font.render_mode {
@ -497,13 +500,29 @@ impl FontContext {
// At the end of all this, WR expects individual RGB channels and ignores alpha
// for subpixel AA.
// For alpha/mono, WR ignores all channels other than alpha.
// Also note that WR expects text to be black bg with white text, so invert
// when we draw the glyphs.
let (antialias, smooth, bg_color) = match font.render_mode {
FontRenderMode::Subpixel => (true, true, 1.0),
FontRenderMode::Alpha => (true, false, 1.0),
FontRenderMode::Bitmap => (true, false, 0.0),
FontRenderMode::Mono => (false, false, 1.0),
// Also note that WR expects text to be white text on black bg, so invert
// when we draw the glyphs as black on white.
//
// Unless platform_options.font_smoothing is false, the grayscale AA'd version
// of the glyph will actually be rasterized with subpixel AA. The color channels
// will be then converted to luminance in gamma_correct_pixels to produce the
// final grayscale AA. This ensures that the dilation of the glyph from grayscale
// AA more closely resembles the dilation from subpixel AA in the general case.
let use_white_on_black = should_use_white_on_black(font.color);
let use_font_smoothing = font.platform_options.unwrap_or_default().font_smoothing;
let (antialias, smooth, text_color, bg_color, bg_alpha, invert) = match font.render_mode {
FontRenderMode::Subpixel => if use_white_on_black {
(true, true, 1.0, 0.0, 1.0, false)
} else {
(true, true, 0.0, 1.0, 1.0, true)
},
FontRenderMode::Alpha => if use_font_smoothing && use_white_on_black {
(true, use_font_smoothing, 1.0, 0.0, 1.0, false)
} else {
(true, use_font_smoothing, 0.0, 1.0, 1.0, true)
},
FontRenderMode::Bitmap => (true, false, 0.0, 0.0, 0.0, false),
FontRenderMode::Mono => (false, false, 0.0, 1.0, 1.0, true),
};
// These are always true in Gecko, even for non-AA fonts
@ -527,7 +546,7 @@ impl FontContext {
// Always draw black text on a white background
// Fill the background
cg_context.set_rgb_fill_color(bg_color, bg_color, bg_color, bg_color);
cg_context.set_rgb_fill_color(bg_color, bg_color, bg_color, bg_alpha);
let rect = CGRect {
origin: CGPoint { x: 0.0, y: 0.0 },
size: CGSize {
@ -538,7 +557,7 @@ impl FontContext {
cg_context.fill_rect(rect);
// Set the text color
cg_context.set_rgb_fill_color(0.0, 0.0, 0.0, 1.0);
cg_context.set_rgb_fill_color(text_color, text_color, text_color, 1.0);
cg_context.set_text_drawing_mode(CGTextDrawingMode::CGTextFill);
ct_font.draw_glyphs(&[glyph], &[rasterization_origin], cg_context.clone());
@ -547,7 +566,7 @@ impl FontContext {
if font.render_mode != FontRenderMode::Bitmap {
// Convert to linear space for subpixel AA.
// We explicitly do not do this for grayscale AA
if font.render_mode == FontRenderMode::Subpixel {
if smooth {
self.gamma_lut.coregraphics_convert_to_linear_bgra(
&mut rasterized_pixels,
metrics.rasterized_width as usize,
@ -555,16 +574,16 @@ impl FontContext {
);
}
// We need to invert the pixels back since right now
// transparent pixels are actually opaque white.
for i in 0 .. metrics.rasterized_height {
let current_height = (i * metrics.rasterized_width * 4) as usize;
let end_row = current_height + (metrics.rasterized_width as usize * 4);
for pixel in rasterized_pixels[current_height .. end_row].chunks_mut(4) {
pixel[0] = 255 - pixel[0];
pixel[1] = 255 - pixel[1];
pixel[2] = 255 - pixel[2];
if invert {
pixel[0] = 255 - pixel[0];
pixel[1] = 255 - pixel[1];
pixel[2] = 255 - pixel[2];
}
pixel[3] = match font.render_mode {
FontRenderMode::Subpixel => 255,
@ -575,13 +594,15 @@ impl FontContext {
} // end row
} // end height
self.gamma_correct_pixels(
&mut rasterized_pixels,
metrics.rasterized_width as usize,
metrics.rasterized_height as usize,
font.render_mode,
font.color,
);
if smooth {
self.gamma_correct_pixels(
&mut rasterized_pixels,
metrics.rasterized_width as usize,
metrics.rasterized_height as usize,
font.render_mode,
font.color,
);
}
}
Some(RasterizedGlyph {
@ -590,6 +611,7 @@ impl FontContext {
width: metrics.rasterized_width,
height: metrics.rasterized_height,
scale: 1.0,
format: GlyphFormat::from(font.render_mode),
bytes: rasterized_pixels,
})
}

View File

@ -4,7 +4,7 @@
use api::{FontInstance, FontKey, FontRenderMode, GlyphDimensions};
use api::{FontInstancePlatformOptions, FontLCDFilter, FontHinting};
use api::{NativeFontHandle, SubpixelDirection, GlyphKey};
use api::{NativeFontHandle, SubpixelDirection, GlyphKey, ColorU};
use api::{FONT_FORCE_AUTOHINT, FONT_NO_AUTOHINT, FONT_EMBEDDED_BITMAP};
use api::{FONT_EMBOLDEN, FONT_VERTICAL_LAYOUT, FONT_SUBPIXEL_BGR};
use freetype::freetype::{FT_BBox, FT_Outline_Translate, FT_Pixel_Mode, FT_Render_Mode};
@ -18,6 +18,7 @@ use freetype::freetype::{FT_LOAD_COLOR, FT_LOAD_DEFAULT, FT_LOAD_FORCE_AUTOHINT}
use freetype::freetype::{FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH, FT_LOAD_NO_AUTOHINT};
use freetype::freetype::{FT_LOAD_NO_BITMAP, FT_LOAD_NO_HINTING, FT_LOAD_VERTICAL_LAYOUT};
use freetype::freetype::{FT_FACE_FLAG_SCALABLE, FT_FACE_FLAG_FIXED_SIZES, FT_Err_Cannot_Render_Glyph};
use glyph_rasterizer::{GlyphFormat, RasterizedGlyph};
use internal_types::FastHashMap;
use std::{cmp, mem, ptr, slice};
use std::sync::Arc;
@ -49,15 +50,6 @@ pub struct FontContext {
// a given FontContext so it is safe to move the latter between threads.
unsafe impl Send for FontContext {}
pub struct RasterizedGlyph {
pub top: f32,
pub left: f32,
pub width: u32,
pub height: u32,
pub scale: f32,
pub bytes: Vec<u8>,
}
extern "C" {
fn FT_GlyphSlot_Embolden(slot: FT_GlyphSlot);
fn FT_GlyphSlot_Oblique(slot: FT_GlyphSlot);
@ -378,9 +370,19 @@ impl FontContext {
unsafe { FT_Select_Size(face, best_size) }
}
pub fn has_gamma_correct_subpixel_aa() -> bool {
// We don't do any preblending with FreeType currently, so the color is not used.
false
pub fn prepare_font(font: &mut FontInstance) {
match font.render_mode {
FontRenderMode::Mono | FontRenderMode::Bitmap => {
// In mono/bitmap modes the color of the font is irrelevant.
font.color = ColorU::new(255, 255, 255, 255);
// Subpixel positioning is disabled in mono and bitmap modes.
font.subpx_dir = SubpixelDirection::None;
}
FontRenderMode::Alpha | FontRenderMode::Subpixel => {
// We don't do any preblending with FreeType currently, so the color is not used.
font.color = ColorU::new(255, 255, 255, 255);
}
}
}
fn rasterize_glyph_outline(
@ -489,17 +491,23 @@ impl FontContext {
dimensions
);
let (actual_width, actual_height) = match pixel_mode {
let (format, actual_width, actual_height) = match pixel_mode {
FT_Pixel_Mode::FT_PIXEL_MODE_LCD => {
assert!(bitmap.width % 3 == 0);
((bitmap.width / 3) as i32, bitmap.rows as i32)
(GlyphFormat::Subpixel, (bitmap.width / 3) as i32, bitmap.rows as i32)
}
FT_Pixel_Mode::FT_PIXEL_MODE_LCD_V => {
assert!(bitmap.rows % 3 == 0);
(bitmap.width as i32, (bitmap.rows / 3) as i32)
(GlyphFormat::Subpixel, bitmap.width as i32, (bitmap.rows / 3) as i32)
}
FT_Pixel_Mode::FT_PIXEL_MODE_MONO | FT_Pixel_Mode::FT_PIXEL_MODE_GRAY | FT_Pixel_Mode::FT_PIXEL_MODE_BGRA => {
(bitmap.width as i32, bitmap.rows as i32)
FT_Pixel_Mode::FT_PIXEL_MODE_MONO => {
(GlyphFormat::Mono, bitmap.width as i32, bitmap.rows as i32)
}
FT_Pixel_Mode::FT_PIXEL_MODE_GRAY => {
(GlyphFormat::Alpha, bitmap.width as i32, bitmap.rows as i32)
}
FT_Pixel_Mode::FT_PIXEL_MODE_BGRA => {
(GlyphFormat::ColorBitmap, bitmap.width as i32, bitmap.rows as i32)
}
_ => panic!("Unsupported {:?}", pixel_mode),
};
@ -605,6 +613,7 @@ impl FontContext {
width: actual_width as u32,
height: actual_height as u32,
scale,
format,
bytes: final_buffer,
})
}

View File

@ -3,9 +3,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{FontInstance, FontInstancePlatformOptions, FontKey, FontRenderMode};
use api::{GlyphDimensions, GlyphKey};
use api::{ColorU, GlyphDimensions, GlyphKey, SubpixelDirection};
use dwrote;
use gamma_lut::{Color as ColorLut, GammaLut};
use glyph_rasterizer::{GlyphFormat, RasterizedGlyph};
use internal_types::FastHashMap;
use std::sync::Arc;
@ -28,15 +29,6 @@ pub struct FontContext {
// all hidden inside their font context.
unsafe impl Send for FontContext {}
pub struct RasterizedGlyph {
pub top: f32,
pub left: f32,
pub width: u32,
pub height: u32,
pub scale: f32,
pub bytes: Vec<u8>,
}
fn dwrite_texture_type(render_mode: FontRenderMode) -> dwrote::DWRITE_TEXTURE_TYPE {
match render_mode {
FontRenderMode::Mono | FontRenderMode::Bitmap => dwrote::DWRITE_TEXTURE_ALIASED_1x1,
@ -310,8 +302,20 @@ impl FontContext {
false
}
pub fn has_gamma_correct_subpixel_aa() -> bool {
true
pub fn prepare_font(font: &mut FontInstance) {
match font.render_mode {
FontRenderMode::Mono | FontRenderMode::Bitmap => {
// In mono/bitmap modes the color of the font is irrelevant.
font.color = ColorU::new(255, 255, 255, 255);
// Subpixel positioning is disabled in mono and bitmap modes.
font.subpx_dir = SubpixelDirection::None;
}
FontRenderMode::Alpha => {
// In alpha mode the color of the font is irrelevant.
font.color = ColorU::new(255, 255, 255, 255);
}
FontRenderMode::Subpixel => {}
}
}
pub fn rasterize_glyph(
@ -363,6 +367,7 @@ impl FontContext {
width: width as u32,
height: height as u32,
scale: 1.0,
format: GlyphFormat::from(font.render_mode),
bytes: rgba_pixels,
})
}

View File

@ -160,7 +160,7 @@ pub struct RectanglePrimitive {
impl ToGpuBlocks for RectanglePrimitive {
fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
request.push(self.color);
request.push(self.color.premultiplied());
}
}
@ -197,6 +197,7 @@ impl ToGpuBlocks for BrushPrimitive {
#[repr(C)]
pub struct LinePrimitive {
pub color: ColorF,
pub wavy_line_thickness: f32,
pub style: LineStyle,
pub orientation: LineOrientation,
}
@ -205,10 +206,10 @@ impl ToGpuBlocks for LinePrimitive {
fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
request.push(self.color);
request.push([
self.wavy_line_thickness,
pack_as_float(self.style as u32),
pack_as_float(self.orientation as u32),
0.0,
0.0,
]);
}
}
@ -586,7 +587,7 @@ impl TextRunPrimitiveCpu {
}
fn write_gpu_blocks(&self, request: &mut GpuDataRequest) {
request.push(ColorF::from(self.font.color));
request.push(ColorF::from(self.font.color).premultiplied());
request.push([
self.offset.x,
self.offset.y,
@ -1059,7 +1060,6 @@ impl PrimitiveStore {
self.cpu_pictures[metadata.cpu_prim_index.0]
.prepare_for_render(
prim_index,
metadata,
prim_context,
render_tasks
);

View File

@ -6,13 +6,13 @@ use api::{ApiMsg, BlobImageRenderer, BuiltDisplayList, DebugCommand, DeviceIntPo
#[cfg(feature = "debugger")]
use api::{BuiltDisplayListIter, SpecificDisplayItem};
use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, DocumentId, DocumentMsg};
use api::{IdNamespace, LayerPoint, PipelineId, RenderNotifier};
use api::{HitTestResult, IdNamespace, LayerPoint, PipelineId, RenderNotifier};
use api::channel::{MsgReceiver, PayloadReceiver, PayloadReceiverHelperMethods};
use api::channel::{PayloadSender, PayloadSenderHelperMethods};
#[cfg(feature = "debugger")]
use debug_server;
use frame::Frame;
use frame_builder::FrameBuilderConfig;
use frame::FrameContext;
use frame_builder::{FrameBuilder, FrameBuilderConfig};
use gpu_cache::GpuCache;
use internal_types::{DebugOutput, FastHashMap, FastHashSet, RendererFrame, ResultMsg};
use profiler::{BackendProfileCounters, ResourceProfileCounters};
@ -32,7 +32,8 @@ use time::precise_time_ns;
struct Document {
scene: Scene,
frame: Frame,
frame_ctx: FrameContext,
frame_builder: Option<FrameBuilder>,
window_size: DeviceUintSize,
inner_rect: DeviceUintRect,
pan: DeviceIntPoint,
@ -64,7 +65,8 @@ impl Document {
};
Document {
scene: Scene::new(),
frame: Frame::new(config),
frame_ctx: FrameContext::new(config),
frame_builder: None,
window_size: initial_size,
inner_rect: DeviceUintRect::new(DeviceUintPoint::zero(), initial_size),
pan: DeviceIntPoint::zero(),
@ -84,7 +86,8 @@ impl Document {
fn build_scene(&mut self, resource_cache: &mut ResourceCache) {
let accumulated_scale_factor = self.accumulated_scale_factor();
self.frame.create(
self.frame_builder = self.frame_ctx.create(
self.frame_builder.take(),
&self.scene,
resource_cache,
self.window_size,
@ -104,16 +107,24 @@ impl Document {
self.pan.x as f32 / accumulated_scale_factor,
self.pan.y as f32 / accumulated_scale_factor,
);
self.frame.build_renderer_frame(
resource_cache,
gpu_cache,
&self.scene.pipelines,
accumulated_scale_factor,
pan,
&self.output_pipelines,
&mut resource_profile.texture_cache,
&mut resource_profile.gpu_cache,
)
match self.frame_builder {
Some(ref mut builder) => {
self.frame_ctx.build_renderer_frame(
builder,
resource_cache,
gpu_cache,
&self.scene.pipelines,
accumulated_scale_factor,
pan,
&self.output_pipelines,
&mut resource_profile.texture_cache,
&mut resource_profile.gpu_cache,
)
}
None => {
self.frame_ctx.get_renderer_frame()
}
}
}
}
@ -261,7 +272,7 @@ impl RenderBackend {
BuiltDisplayList::from_data(data.display_list_data, list_descriptor);
if !preserve_frame_state {
doc.frame.discard_frame_state_for_pipeline(pipeline_id);
doc.frame_ctx.discard_frame_state_for_pipeline(pipeline_id);
}
let display_list_len = built_display_list.data().len();
@ -309,7 +320,7 @@ impl RenderBackend {
.update_resources(resources, &mut profile_counters.resources);
doc.scene.update_epoch(pipeline_id, epoch);
doc.frame.update_epoch(pipeline_id, epoch);
doc.frame_ctx.update_epoch(pipeline_id, epoch);
DocumentOp::Nop
}
@ -335,7 +346,7 @@ impl RenderBackend {
profile_scope!("Scroll");
let _timer = profile_counters.total_time.timer();
if doc.frame.scroll(delta, cursor, move_phase) && doc.render_on_scroll == Some(true)
if doc.frame_ctx.scroll(delta, cursor, move_phase) && doc.render_on_scroll == Some(true)
{
let frame = doc.render(
&mut self.resource_cache,
@ -349,7 +360,13 @@ impl RenderBackend {
}
DocumentMsg::HitTest(pipeline_id, point, flags, tx) => {
profile_scope!("HitTest");
let result = doc.frame.hit_test(pipeline_id, point, flags);
let result = match doc.frame_builder {
Some(ref builder) => {
let cst = doc.frame_ctx.get_clip_scroll_tree();
builder.hit_test(cst, pipeline_id, point, flags)
},
None => HitTestResult::default(),
};
tx.send(result).unwrap();
DocumentOp::Nop
}
@ -357,7 +374,7 @@ impl RenderBackend {
profile_scope!("ScrollNodeWithScrollId");
let _timer = profile_counters.total_time.timer();
if doc.frame.scroll_node(origin, id, clamp) && doc.render_on_scroll == Some(true) {
if doc.frame_ctx.scroll_node(origin, id, clamp) && doc.render_on_scroll == Some(true) {
let frame = doc.render(
&mut self.resource_cache,
&mut self.gpu_cache,
@ -372,7 +389,7 @@ impl RenderBackend {
profile_scope!("TickScrollingBounce");
let _timer = profile_counters.total_time.timer();
doc.frame.tick_scrolling_bounce_animations();
doc.frame_ctx.tick_scrolling_bounce_animations();
if doc.render_on_scroll == Some(true) {
let frame = doc.render(
&mut self.resource_cache,
@ -386,7 +403,7 @@ impl RenderBackend {
}
DocumentMsg::GetScrollNodeState(tx) => {
profile_scope!("GetScrollNodeState");
tx.send(doc.frame.get_scroll_node_state()).unwrap();
tx.send(doc.frame_ctx.get_scroll_node_state()).unwrap();
DocumentOp::Nop
}
DocumentMsg::GenerateFrame(property_bindings) => {
@ -664,12 +681,14 @@ impl RenderBackend {
for (_, doc) in &self.documents {
let debug_node = debug_server::TreeNode::new("document clip_scroll tree");
let mut builder = debug_server::TreeNodeBuilder::new(debug_node);
// TODO(gw): Restructure the storage of clip-scroll tree, clip store
// etc so this isn't so untidy.
let clip_store = &doc.frame.frame_builder.as_ref().unwrap().clip_store;
doc.frame
.clip_scroll_tree
.print_with(clip_store, &mut builder);
if let Some(ref frame_builder) = doc.frame_builder {
doc.frame_ctx
.get_clip_scroll_tree()
.print_with(&frame_builder.clip_store, &mut builder);
}
debug_root.add(builder.build());
}

View File

@ -2,8 +2,8 @@
* 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, DeviceIntLength, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
use api::{FilterOp, MixBlendMode};
use api::{ClipId, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
use api::{ColorF, FilterOp, LayerPoint, MixBlendMode};
use api::{LayerRect, PipelineId};
use clip::{ClipSource, ClipSourcesWeakHandle, ClipStore};
use clip_scroll_tree::CoordinateSystemId;
@ -258,13 +258,16 @@ pub struct CacheMaskTask {
pub struct PictureTask {
pub prim_index: PrimitiveIndex,
pub target_kind: RenderTargetKind,
pub content_origin: LayerPoint,
pub color: ColorF,
}
#[derive(Debug)]
pub struct BlurTask {
pub blur_radius: DeviceIntLength,
pub blur_std_deviation: f32,
pub target_kind: RenderTargetKind,
pub regions: Vec<LayerRect>,
pub color: ColorF,
}
#[derive(Debug)]
@ -333,6 +336,8 @@ impl RenderTask {
size: DeviceIntSize,
prim_index: PrimitiveIndex,
target_kind: RenderTargetKind,
content_origin: LayerPoint,
color: ColorF,
) -> RenderTask {
let clear_mode = match target_kind {
RenderTargetKind::Color => ClearMode::Transparent,
@ -346,6 +351,8 @@ impl RenderTask {
kind: RenderTaskKind::Picture(PictureTask {
prim_index,
target_kind,
content_origin,
color,
}),
clear_mode,
}
@ -460,12 +467,13 @@ impl RenderTask {
// +---- This is stored as the input task to the primitive shader.
//
pub fn new_blur(
blur_radius: DeviceIntLength,
blur_std_deviation: f32,
src_task_id: RenderTaskId,
render_tasks: &mut RenderTaskTree,
target_kind: RenderTargetKind,
regions: &[LayerRect],
clear_mode: ClearMode,
color: ColorF,
) -> RenderTask {
let blur_target_size = render_tasks.get(src_task_id).get_dynamic_size();
@ -474,9 +482,10 @@ impl RenderTask {
children: vec![src_task_id],
location: RenderTaskLocation::Dynamic(None, blur_target_size),
kind: RenderTaskKind::VerticalBlur(BlurTask {
blur_radius,
blur_std_deviation,
target_kind,
regions: regions.to_vec(),
color,
}),
clear_mode,
};
@ -488,9 +497,10 @@ impl RenderTask {
children: vec![blur_task_v_id],
location: RenderTaskLocation::Dynamic(None, blur_target_size),
kind: RenderTaskKind::HorizontalBlur(BlurTask {
blur_radius,
blur_std_deviation,
target_kind,
regions: regions.to_vec(),
color,
}),
clear_mode,
};
@ -554,7 +564,7 @@ impl RenderTask {
],
}
}
RenderTaskKind::Picture(..) => {
RenderTaskKind::Picture(ref task) => {
let (target_rect, target_index) = self.get_target_rect();
RenderTaskData {
data: [
@ -563,13 +573,13 @@ impl RenderTask {
target_rect.size.width as f32,
target_rect.size.height as f32,
target_index.0 as f32,
task.content_origin.x,
task.content_origin.y,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
task.color.r,
task.color.g,
task.color.b,
task.color.a,
],
}
}
@ -602,13 +612,13 @@ impl RenderTask {
target_rect.size.width as f32,
target_rect.size.height as f32,
target_index.0 as f32,
task_info.blur_radius.0 as f32,
0.0,
0.0,
0.0,
0.0,
task_info.blur_std_deviation,
0.0,
0.0,
task_info.color.r,
task_info.color.g,
task_info.color.b,
task_info.color.a,
],
}
}

View File

@ -33,6 +33,7 @@ use device::{FileWatcherHandler, GpuTimer, ShaderError, TextureFilter, TextureTa
use euclid::{rect, Transform3D};
use frame_builder::FrameBuilderConfig;
use gleam::gl;
use glyph_rasterizer::GlyphFormat;
use gpu_cache::{GpuBlockData, GpuCacheUpdate, GpuCacheUpdateList};
use gpu_types::PrimitiveInstance;
use internal_types::{BatchTextures, SourceTexture, ORTHO_FAR_PLANE, ORTHO_NEAR_PLANE};
@ -61,7 +62,7 @@ use std::thread;
use texture_cache::TextureCache;
use thread_profiler::{register_thread_with_profiler, write_profile};
use tiling::{AlphaRenderTarget, ColorRenderTarget, RenderTargetKind};
use tiling::{BatchKey, BatchKind, Frame, RenderTarget, TransformBatchKind};
use tiling::{BatchKey, BatchKind, BrushBatchKind, Frame, RenderTarget, TransformBatchKind};
use time::precise_time_ns;
use util::TransformedRectKind;
@ -71,6 +72,10 @@ const GPU_TAG_BRUSH_MASK: GpuProfileTag = GpuProfileTag {
label: "B_Mask",
color: debug_colors::BLACK,
};
const GPU_TAG_BRUSH_IMAGE: GpuProfileTag = GpuProfileTag {
label: "B_Image",
color: debug_colors::SILVER,
};
const GPU_TAG_CACHE_CLIP: GpuProfileTag = GpuProfileTag {
label: "C_Clip",
color: debug_colors::PURPLE,
@ -147,10 +152,6 @@ const GPU_TAG_PRIM_BORDER_EDGE: GpuProfileTag = GpuProfileTag {
label: "BorderEdge",
color: debug_colors::LAVENDER,
};
const GPU_TAG_PRIM_CACHE_IMAGE: GpuProfileTag = GpuProfileTag {
label: "CacheImage",
color: debug_colors::SILVER,
};
const GPU_TAG_BLUR: GpuProfileTag = GpuProfileTag {
label: "Blur",
color: debug_colors::VIOLET,
@ -177,9 +178,14 @@ impl BatchKind {
BatchKind::HardwareComposite => "HardwareComposite",
BatchKind::SplitComposite => "SplitComposite",
BatchKind::Blend => "Blend",
BatchKind::Brush(kind) => {
match kind {
BrushBatchKind::Image(..) => "Brush (Image)",
}
}
BatchKind::Transformable(_, kind) => match kind {
TransformBatchKind::Rectangle(..) => "Rectangle",
TransformBatchKind::TextRun => "TextRun",
TransformBatchKind::TextRun(..) => "TextRun",
TransformBatchKind::Image(image_buffer_kind, ..) => match image_buffer_kind {
ImageBufferKind::Texture2D => "Image (2D)",
ImageBufferKind::TextureRect => "Image (Rect)",
@ -190,7 +196,6 @@ impl BatchKind {
TransformBatchKind::AlignedGradient => "AlignedGradient",
TransformBatchKind::AngleGradient => "AngleGradient",
TransformBatchKind::RadialGradient => "RadialGradient",
TransformBatchKind::CacheImage(..) => "CacheImage",
TransformBatchKind::BorderCorner => "BorderCorner",
TransformBatchKind::BorderEdge => "BorderEdge",
TransformBatchKind::Line => "Line",
@ -218,6 +223,7 @@ enum TextShaderMode {
Alpha = 0,
SubpixelPass0 = 1,
SubpixelPass1 = 2,
ColorBitmap = 3,
}
impl Into<ShaderMode> for TextShaderMode {
@ -226,6 +232,18 @@ impl Into<ShaderMode> for TextShaderMode {
}
}
impl From<GlyphFormat> for TextShaderMode {
fn from(format: GlyphFormat) -> TextShaderMode {
match format {
GlyphFormat::Mono | GlyphFormat::Alpha => TextShaderMode::Alpha,
GlyphFormat::Subpixel => {
panic!("Subpixel glyph format must be handled separately.");
}
GlyphFormat::ColorBitmap => TextShaderMode::ColorBitmap,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum TextureSampler {
Color0,
@ -835,6 +853,7 @@ impl VertexDataTexture {
const TRANSFORM_FEATURE: &str = "TRANSFORM";
const CLIP_FEATURE: &str = "CLIP";
const ALPHA_FEATURE: &str = "ALPHA_PASS";
enum ShaderKind {
Primitive,
@ -929,6 +948,77 @@ struct PrimitiveShader {
transform: LazilyCompiledShader,
}
// A brush shader supports two modes:
// opaque:
// Used for completely opaque primitives,
// or inside segments of partially
// opaque primitives. Assumes no need
// for clip masks, AA etc.
// alpha:
// Used for brush primitives in the alpha
// pass. Assumes that AA should be applied
// along the primitive edge, and also that
// clip mask is present.
struct BrushShader {
opaque: LazilyCompiledShader,
alpha: LazilyCompiledShader,
}
impl BrushShader {
fn new(
name: &'static str,
device: &mut Device,
features: &[&'static str],
precache: bool,
) -> Result<BrushShader, ShaderError> {
let opaque = try!{
LazilyCompiledShader::new(ShaderKind::Brush,
name,
features,
device,
precache)
};
let mut alpha_features = features.to_vec();
alpha_features.push(ALPHA_FEATURE);
let alpha = try!{
LazilyCompiledShader::new(ShaderKind::Brush,
name,
&alpha_features,
device,
precache)
};
Ok(BrushShader { opaque, alpha })
}
fn bind<M>(
&mut self,
device: &mut Device,
blend_mode: BlendMode,
projection: &Transform3D<f32>,
mode: M,
renderer_errors: &mut Vec<RendererError>,
) where M: Into<ShaderMode> {
match blend_mode {
BlendMode::None => {
self.opaque.bind(device, projection, mode, renderer_errors)
}
BlendMode::Alpha |
BlendMode::PremultipliedAlpha |
BlendMode::Subpixel => {
self.alpha.bind(device, projection, mode, renderer_errors)
}
}
}
fn deinit(self, device: &mut Device) {
self.opaque.deinit(device);
self.alpha.deinit(device);
}
}
struct FileWatcher {
notifier: Box<RenderNotifier>,
result_tx: Sender<ResultMsg>,
@ -1096,7 +1186,11 @@ pub struct Renderer {
cs_line: LazilyCompiledShader,
cs_blur_a8: LazilyCompiledShader,
cs_blur_rgba8: LazilyCompiledShader,
// Brush shaders
brush_mask: LazilyCompiledShader,
brush_image_rgba8: BrushShader,
brush_image_a8: BrushShader,
/// These are "cache clip shaders". These shaders are used to
/// draw clip instances into the cached clip mask. The results
@ -1122,8 +1216,6 @@ pub struct Renderer {
ps_gradient: PrimitiveShader,
ps_angle_gradient: PrimitiveShader,
ps_radial_gradient: PrimitiveShader,
ps_cache_image_rgba8: PrimitiveShader,
ps_cache_image_a8: PrimitiveShader,
ps_line: PrimitiveShader,
ps_blend: LazilyCompiledShader,
@ -1296,10 +1388,24 @@ impl Renderer {
options.precache_shaders)
};
let brush_image_a8 = try!{
BrushShader::new("brush_image",
&mut device,
&["ALPHA_TARGET"],
options.precache_shaders)
};
let brush_image_rgba8 = try!{
BrushShader::new("brush_image",
&mut device,
&["COLOR_TARGET"],
options.precache_shaders)
};
let cs_blur_a8 = try!{
LazilyCompiledShader::new(ShaderKind::Cache(VertexArrayKind::Blur),
"cs_blur",
&["ALPHA"],
&["ALPHA_TARGET"],
&mut device,
options.precache_shaders)
};
@ -1307,7 +1413,7 @@ impl Renderer {
let cs_blur_rgba8 = try!{
LazilyCompiledShader::new(ShaderKind::Cache(VertexArrayKind::Blur),
"cs_blur",
&["COLOR"],
&["COLOR_TARGET"],
&mut device,
options.precache_shaders)
};
@ -1481,20 +1587,6 @@ impl Renderer {
options.precache_shaders)
};
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)
};
let ps_blend = try!{
LazilyCompiledShader::new(ShaderKind::Primitive,
"ps_blend",
@ -1712,6 +1804,8 @@ impl Renderer {
cs_blur_a8,
cs_blur_rgba8,
brush_mask,
brush_image_rgba8,
brush_image_a8,
cs_clip_rectangle,
cs_clip_border,
cs_clip_image,
@ -1725,8 +1819,6 @@ impl Renderer {
ps_gradient,
ps_angle_gradient,
ps_radial_gradient,
ps_cache_image_rgba8,
ps_cache_image_a8,
ps_blend,
ps_hw_composite,
ps_split_composite,
@ -2344,6 +2436,24 @@ impl Renderer {
.bind(&mut self.device, projection, 0, &mut self.renderer_errors);
GPU_TAG_PRIM_BLEND
}
BatchKind::Brush(brush_kind) => {
match brush_kind {
BrushBatchKind::Image(target_kind) => {
let shader = match target_kind {
RenderTargetKind::Alpha => &mut self.brush_image_a8,
RenderTargetKind::Color => &mut self.brush_image_rgba8,
};
shader.bind(
&mut self.device,
key.blend_mode,
projection,
0,
&mut self.renderer_errors,
);
GPU_TAG_BRUSH_IMAGE
}
}
}
BatchKind::Transformable(transform_kind, batch_kind) => match batch_kind {
TransformBatchKind::Rectangle(needs_clipping) => {
debug_assert!(
@ -2384,7 +2494,7 @@ impl Renderer {
);
GPU_TAG_PRIM_LINE
}
TransformBatchKind::TextRun => {
TransformBatchKind::TextRun(..) => {
unreachable!("bug: text batches are special cased");
}
TransformBatchKind::Image(image_buffer_kind) => {
@ -2465,29 +2575,6 @@ impl Renderer {
);
GPU_TAG_PRIM_RADIAL_GRADIENT
}
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
}
},
};
@ -2723,7 +2810,7 @@ impl Renderer {
}
match batch.key.kind {
BatchKind::Transformable(transform_kind, TransformBatchKind::TextRun) => {
BatchKind::Transformable(transform_kind, TransformBatchKind::TextRun(glyph_format)) => {
// Text run batches are handled by this special case branch.
// In the case of subpixel text, we draw it as a two pass
// effect, to ensure we can apply clip masks correctly.
@ -2743,7 +2830,7 @@ impl Renderer {
&mut self.device,
transform_kind,
projection,
TextShaderMode::Alpha,
TextShaderMode::from(glyph_format),
&mut self.renderer_errors,
);
@ -3353,21 +3440,22 @@ impl Renderer {
let mut spacing = 16;
let mut size = 512;
let fb_width = framebuffer_size.width as i32;
let num_textures = self.color_render_targets
let num_layers: i32 = self.color_render_targets
.iter()
.chain(self.alpha_render_targets.iter())
.count() as i32;
.map(|texture| texture.get_render_target_layer_count() as i32)
.sum();
if num_textures * (size + spacing) > fb_width {
let factor = fb_width as f32 / (num_textures * (size + spacing)) as f32;
if num_layers * (size + spacing) > fb_width {
let factor = fb_width as f32 / (num_layers * (size + spacing)) as f32;
size = (size as f32 * factor) as i32;
spacing = (spacing as f32 * factor) as i32;
}
for (i, texture) in self.color_render_targets
let mut target_index = 0;
for texture in self.color_render_targets
.iter()
.chain(self.alpha_render_targets.iter())
.enumerate()
{
let dimensions = texture.get_dimensions();
let src_rect = DeviceIntRect::new(DeviceIntPoint::zero(), dimensions.to_i32());
@ -3376,11 +3464,12 @@ impl Renderer {
for layer_index in 0 .. layer_count {
self.device
.bind_read_target(Some((texture, layer_index as i32)));
let x = fb_width - (spacing + size) * (i as i32 + 1);
let x = fb_width - (spacing + size) * (target_index + 1);
let y = spacing;
let dest_rect = rect(x, y, size, size);
self.device.blit_render_target(src_rect, dest_rect);
target_index += 1;
}
}
}
@ -3495,6 +3584,8 @@ impl Renderer {
self.cs_blur_a8.deinit(&mut self.device);
self.cs_blur_rgba8.deinit(&mut self.device);
self.brush_mask.deinit(&mut self.device);
self.brush_image_rgba8.deinit(&mut self.device);
self.brush_image_a8.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);
@ -3519,8 +3610,6 @@ 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_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

@ -16,7 +16,7 @@ use app_units::Au;
use device::TextureFilter;
use frame::FrameId;
use glyph_cache::GlyphCache;
use glyph_rasterizer::{GlyphRasterizer, GlyphRequest};
use glyph_rasterizer::{GlyphFormat, GlyphRasterizer, GlyphRequest};
use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
use internal_types::{FastHashMap, FastHashSet, SourceTexture, TextureUpdateList};
use profiler::{ResourceProfileCounters, TextureCacheProfileCounters};
@ -26,7 +26,7 @@ use std::cmp;
use std::fmt::Debug;
use std::hash::Hash;
use std::mem;
use std::sync::Arc;
use std::sync::{Arc, RwLock};
use texture_cache::{TextureCache, TextureCacheHandle};
const DEFAULT_TILE_SIZE: TileSize = 512;
@ -73,7 +73,7 @@ struct ImageResource {
dirty_rect: Option<DeviceUintRect>,
}
#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct ImageTiling {
pub image_size: DeviceUintSize,
pub tile_size: TileSize,
@ -189,9 +189,12 @@ impl Into<BlobImageRequest> for ImageRequest {
}
}
type ImageCache = ResourceClassCache<ImageRequest, CachedImageInfo>;
pub type FontInstanceMap = Arc<RwLock<FastHashMap<FontInstanceKey, FontInstance>>>;
struct Resources {
font_templates: FastHashMap<FontKey, FontTemplate>,
font_instances: FastHashMap<FontInstanceKey, FontInstance>,
font_instances: FontInstanceMap,
image_templates: ImageTemplates,
}
@ -208,7 +211,7 @@ impl BlobImageResources for Resources {
pub struct ResourceCache {
cached_glyphs: GlyphCache,
cached_images: ResourceClassCache<ImageRequest, CachedImageInfo>,
cached_images: ImageCache,
resources: Resources,
state: State,
@ -239,7 +242,7 @@ impl ResourceCache {
cached_images: ResourceClassCache::new(),
resources: Resources {
font_templates: FastHashMap::default(),
font_instances: FastHashMap::default(),
font_instances: Arc::new(RwLock::new(FastHashMap::default())),
image_templates: ImageTemplates::new(),
},
cached_glyph_dimensions: FastHashMap::default(),
@ -256,8 +259,7 @@ impl ResourceCache {
self.texture_cache.max_texture_size()
}
fn should_tile(&self, descriptor: &ImageDescriptor, data: &ImageData) -> bool {
let limit = self.max_texture_size();
fn should_tile(limit: u32, descriptor: &ImageDescriptor, data: &ImageData) -> bool {
let size_check = descriptor.width > limit || descriptor.height > limit;
match *data {
ImageData::Raw(_) | ImageData::Blob(_) => size_check,
@ -365,18 +367,24 @@ impl ResourceCache {
if self.glyph_rasterizer.is_bitmap_font(&instance) {
instance.render_mode = instance.render_mode.limit_by(FontRenderMode::Bitmap);
}
self.resources.font_instances.insert(instance_key, instance);
self.resources.font_instances
.write()
.unwrap()
.insert(instance_key, instance);
}
pub fn delete_font_instance(&mut self, instance_key: FontInstanceKey) {
self.resources.font_instances.remove(&instance_key);
self.resources.font_instances
.write()
.unwrap()
.remove(&instance_key);
if let Some(ref mut r) = self.blob_image_renderer {
r.delete_font_instance(instance_key);
}
}
pub fn get_font_instance(&self, instance_key: FontInstanceKey) -> Option<&FontInstance> {
self.resources.font_instances.get(&instance_key)
pub fn get_font_instances(&self) -> FontInstanceMap {
self.resources.font_instances.clone()
}
pub fn add_image_template(
@ -386,7 +394,7 @@ impl ResourceCache {
mut data: ImageData,
mut tiling: Option<TileSize>,
) {
if tiling.is_none() && self.should_tile(&descriptor, &data) {
if tiling.is_none() && Self::should_tile(self.max_texture_size(), &descriptor, &data) {
// We aren't going to be able to upload a texture this big, so tile it, even
// if tiling was not requested.
tiling = Some(DEFAULT_TILE_SIZE);
@ -418,40 +426,38 @@ impl ResourceCache {
mut data: ImageData,
dirty_rect: Option<DeviceUintRect>,
) {
let resource = if let Some(image) = self.resources.image_templates.get(image_key) {
let next_epoch = Epoch(image.epoch.0 + 1);
let mut tiling = image.tiling;
if tiling.is_none() && self.should_tile(&descriptor, &data) {
tiling = Some(DEFAULT_TILE_SIZE);
}
if let ImageData::Blob(ref mut blob) = data {
self.blob_image_renderer
.as_mut()
.unwrap()
.update(image_key, mem::replace(blob, BlobImageData::new()), dirty_rect);
}
ImageResource {
descriptor,
data,
epoch: next_epoch,
tiling,
dirty_rect: match (dirty_rect, image.dirty_rect) {
(Some(rect), Some(prev_rect)) => Some(rect.union(&prev_rect)),
(Some(rect), None) => Some(rect),
_ => None,
},
}
} else {
panic!(
"Attempt to update non-existant image (key {:?}).",
let max_texture_size = self.max_texture_size();
let image = match self.resources.image_templates.get_mut(image_key) {
Some(res) => res,
None => panic!(
"Attempt to update non-existent image (key {:?}).",
image_key
);
),
};
self.resources.image_templates.insert(image_key, resource);
let mut tiling = image.tiling;
if tiling.is_none() && Self::should_tile(max_texture_size, &descriptor, &data) {
tiling = Some(DEFAULT_TILE_SIZE);
}
if let ImageData::Blob(ref mut blob) = data {
self.blob_image_renderer
.as_mut()
.unwrap()
.update(image_key, mem::replace(blob, BlobImageData::new()), dirty_rect);
}
*image = ImageResource {
descriptor,
data,
epoch: Epoch(image.epoch.0 + 1),
tiling,
dirty_rect: match (dirty_rect, image.dirty_rect) {
(Some(rect), Some(prev_rect)) => Some(rect.union(&prev_rect)),
(Some(rect), None) => Some(rect),
(None, _) => None,
},
};
}
pub fn delete_image_template(&mut self, image_key: ImageKey) {
@ -484,98 +490,99 @@ impl ResourceCache {
tile,
};
match self.resources.image_templates.get(key) {
Some(template) => {
// Images that don't use the texture cache can early out.
if !template.data.uses_texture_cache() {
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().as_ref().unwrap().epoch != template.epoch;
(entry.into_mut(), needs_update)
}
Vacant(entry) => (
entry.insert(Ok(
CachedImageInfo {
epoch: template.epoch,
texture_cache_handle: TextureCacheHandle::new(),
}
)),
true,
),
};
let needs_upload = self.texture_cache
.request(&mut entry.as_mut().unwrap().texture_cache_handle, gpu_cache);
if !needs_upload && !needs_update {
return;
}
// We can start a worker thread rasterizing right now, if:
// - The image is a blob.
// - The blob hasn't already been requested this frame.
if self.pending_image_requests.insert(request) {
if template.data.is_blob() {
if let Some(ref mut renderer) = self.blob_image_renderer {
let (offset, w, h) = match template.tiling {
Some(tile_size) => {
let tile_offset = request.tile.unwrap();
let (w, h) = compute_tile_size(
&template.descriptor,
tile_size,
tile_offset,
);
let offset = DevicePoint::new(
tile_offset.x as f32 * tile_size as f32,
tile_offset.y as f32 * tile_size as f32,
);
(offset, w, h)
}
None => (
DevicePoint::zero(),
template.descriptor.width,
template.descriptor.height,
),
};
renderer.request(
&self.resources,
request.into(),
&BlobImageDescriptor {
width: w,
height: h,
offset,
format: template.descriptor.format,
},
template.dirty_rect,
);
}
}
}
}
let template = match self.resources.image_templates.get(key) {
Some(template) => template,
None => {
warn!(
"ERROR: Trying to render deleted / non-existent key {:?}",
key
);
return
}
};
// Images that don't use the texture cache can early out.
if !template.data.uses_texture_cache() {
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().as_ref().unwrap().epoch != template.epoch;
(entry.into_mut(), needs_update)
}
Vacant(entry) => (
entry.insert(Ok(
CachedImageInfo {
epoch: template.epoch,
texture_cache_handle: TextureCacheHandle::new(),
}
)),
true,
),
};
let needs_upload = self.texture_cache
.request(&mut entry.as_mut().unwrap().texture_cache_handle, gpu_cache);
if !needs_upload && !needs_update {
return;
}
// We can start a worker thread rasterizing right now, if:
// - The image is a blob.
// - The blob hasn't already been requested this frame.
if self.pending_image_requests.insert(request) {
if template.data.is_blob() {
if let Some(ref mut renderer) = self.blob_image_renderer {
let (offset, w, h) = match template.tiling {
Some(tile_size) => {
let tile_offset = request.tile.unwrap();
let (w, h) = compute_tile_size(
&template.descriptor,
tile_size,
tile_offset,
);
let offset = DevicePoint::new(
tile_offset.x as f32 * tile_size as f32,
tile_offset.y as f32 * tile_size as f32,
);
(offset, w, h)
}
None => (
DevicePoint::zero(),
template.descriptor.width,
template.descriptor.height,
),
};
renderer.request(
&self.resources,
request.into(),
&BlobImageDescriptor {
width: w,
height: h,
offset,
format: template.descriptor.format,
},
template.dirty_rect,
);
}
}
}
}
@ -610,7 +617,7 @@ impl ResourceCache {
gpu_cache: &GpuCache,
mut f: F,
) where
F: FnMut(SourceTexture, &[GlyphFetchResult]),
F: FnMut(SourceTexture, GlyphFormat, &[GlyphFetchResult]),
{
debug_assert_eq!(self.state, State::QueryResources);
@ -618,17 +625,20 @@ impl ResourceCache {
let glyph_key_cache = self.cached_glyphs.get_glyph_key_cache_for_font(&font);
let mut current_texture_id = SourceTexture::Invalid;
let mut current_glyph_format = GlyphFormat::Subpixel;
debug_assert!(fetch_buffer.is_empty());
for (loop_index, key) in glyph_keys.iter().enumerate() {
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 current_texture_id != cache_item.texture_id ||
current_glyph_format != glyph.format {
if !fetch_buffer.is_empty() {
f(current_texture_id, fetch_buffer);
f(current_texture_id, current_glyph_format, fetch_buffer);
fetch_buffer.clear();
}
current_texture_id = cache_item.texture_id;
current_glyph_format = glyph.format;
}
fetch_buffer.push(GlyphFetchResult {
index_in_text_run: loop_index as i32,
@ -638,7 +648,7 @@ impl ResourceCache {
}
if !fetch_buffer.is_empty() {
f(current_texture_id, fetch_buffer);
f(current_texture_id, current_glyph_format, fetch_buffer);
fetch_buffer.clear();
}
}
@ -879,27 +889,14 @@ impl ResourceCache {
}
pub fn clear_namespace(&mut self, namespace: IdNamespace) {
//TODO: use `retain` when we are on Rust-1.18
let image_keys: Vec<_> = self.resources
self.resources
.image_templates
.images
.keys()
.filter(|&key| key.0 == namespace)
.cloned()
.collect();
for key in &image_keys {
self.resources.image_templates.images.remove(key);
}
.retain(|key, _| key.0 != namespace);
let font_keys: Vec<_> = self.resources
self.resources
.font_templates
.keys()
.filter(|&key| key.0 == namespace)
.cloned()
.collect();
for key in &font_keys {
self.resources.font_templates.remove(key);
}
.retain(|key, _| key.0 != namespace);
self.cached_images
.clear_keys(|request| request.key.0 == namespace);

View File

@ -11,9 +11,11 @@ use border::{BorderCornerInstance, BorderCornerSide};
use clip::{ClipSource, ClipStore};
use clip_scroll_tree::CoordinateSystemId;
use device::Texture;
use glyph_rasterizer::GlyphFormat;
use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle, GpuCacheUpdateList};
use gpu_types::{BlurDirection, BlurInstance, BrushInstance, ClipMaskInstance};
use gpu_types::{CompositePrimitiveInstance, PrimitiveInstance, SimplePrimitiveInstance};
use gpu_types::{BRUSH_FLAG_USES_PICTURE};
use internal_types::{FastHashMap, SourceTexture};
use internal_types::BatchTextures;
use prim_store::{PrimitiveIndex, PrimitiveKind, PrimitiveMetadata, PrimitiveStore};
@ -62,6 +64,8 @@ impl AlphaBatchHelpers for PrimitiveStore {
FontRenderMode::Bitmap => BlendMode::PremultipliedAlpha,
}
}
PrimitiveKind::Rectangle |
PrimitiveKind::Border |
PrimitiveKind::Image |
PrimitiveKind::AlignedGradient |
PrimitiveKind::AngleGradient |
@ -71,7 +75,9 @@ impl AlphaBatchHelpers for PrimitiveStore {
} else {
BlendMode::None
},
_ => if needs_blending {
PrimitiveKind::YuvImage |
PrimitiveKind::Line |
PrimitiveKind::Brush => if needs_blending {
BlendMode::Alpha
} else {
BlendMode::None
@ -137,7 +143,7 @@ impl AlphaBatchList {
// the input to the next composite. Perhaps we can
// optimize this in the future.
}
BatchKind::Transformable(_, TransformBatchKind::TextRun) => {
BatchKind::Transformable(_, TransformBatchKind::TextRun(_)) => {
'outer_text: for (batch_index, batch) in self.batches.iter().enumerate().rev().take(10) {
// Subpixel text is drawn in two passes. Because of this, we need
// to check for overlaps with every batch (which is a bit different
@ -544,7 +550,7 @@ impl AlphaRenderItem {
&text_cpu.glyph_keys,
glyph_fetch_buffer,
gpu_cache,
|texture_id, glyphs| {
|texture_id, glyph_format, glyphs| {
debug_assert_ne!(texture_id, SourceTexture::Invalid);
let textures = BatchTextures {
@ -557,7 +563,7 @@ impl AlphaRenderItem {
let kind = BatchKind::Transformable(
transform_kind,
TransformBatchKind::TextRun,
TransformBatchKind::TextRun(glyph_format),
);
let key = BatchKey::new(kind, blend_mode, textures);
@ -579,13 +585,22 @@ impl AlphaRenderItem {
let cache_task_id = picture.render_task_id.expect("no render task!");
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::CacheImage(picture.target_kind()),
let kind = BatchKind::Brush(
BrushBatchKind::Image(picture.target_kind()),
);
let key = BatchKey::new(kind, blend_mode, textures);
let batch = batch_list.get_suitable_batch(key, item_bounding_rect);
batch.push(base_instance.build(0, cache_task_address.0 as i32, 0));
let instance = BrushInstance {
picture_address: task_address,
prim_address: prim_cache_address,
layer_address: packed_layer_index.into(),
clip_task_address,
z,
flags: 0,
user_data0: cache_task_address.0 as i32,
user_data1: 0,
};
batch.push(PrimitiveInstance::from(instance));
}
PrimitiveKind::AlignedGradient => {
let gradient_cpu =
@ -1158,8 +1173,6 @@ impl RenderTarget for ColorRenderTarget {
}
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 {
PrimitiveKind::Picture => {
let prim = &ctx.prim_store.cpu_pictures[prim_metadata.cpu_prim_index.0];
@ -1197,7 +1210,7 @@ impl RenderTarget for ColorRenderTarget {
&text.glyph_keys,
&mut self.glyph_fetch_buffer,
gpu_cache,
|texture_id, glyphs| {
|texture_id, _glyph_format, glyphs| {
let batch = text_run_cache_prims
.entry(texture_id)
.or_insert(Vec::new());
@ -1206,7 +1219,7 @@ impl RenderTarget for ColorRenderTarget {
batch.push(instance.build(
glyph.index_in_text_run,
glyph.uv_rect_address.as_int(),
prim_address,
0
));
}
},
@ -1214,7 +1227,7 @@ impl RenderTarget for ColorRenderTarget {
}
PrimitiveKind::Line => {
self.line_cache_prims
.push(instance.build(prim_address, 0, 0));
.push(instance.build(0, 0, 0));
}
_ => {
unreachable!("Unexpected sub primitive type");
@ -1334,7 +1347,21 @@ impl RenderTarget for AlphaRenderTarget {
match sub_metadata.prim_kind {
PrimitiveKind::Brush => {
let instance = BrushInstance::new(task_index, sub_prim_address);
let instance = BrushInstance {
picture_address: task_index,
prim_address: sub_prim_address,
// TODO(gw): In the future, when brush
// primitives on picture backed
// tasks support clip masks and
// transform primitives, these
// will need to be filled out!
layer_address: PackedLayerIndex(0).into(),
clip_task_address: RenderTaskAddress(0),
z: 0,
flags: BRUSH_FLAG_USES_PICTURE,
user_data0: 0,
user_data1: 0,
};
self.rect_cache_prims.push(PrimitiveInstance::from(instance));
}
_ => {
@ -1533,18 +1560,22 @@ impl RenderPass {
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum TransformBatchKind {
Rectangle(bool),
TextRun,
TextRun(GlyphFormat),
Image(ImageBufferKind),
YuvImage(ImageBufferKind, YuvFormat, YuvColorSpace),
AlignedGradient,
AngleGradient,
RadialGradient,
CacheImage(RenderTargetKind),
BorderCorner,
BorderEdge,
Line,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum BrushBatchKind {
Image(RenderTargetKind)
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum BatchKind {
Composite {
@ -1556,6 +1587,7 @@ pub enum BatchKind {
SplitComposite,
Blend,
Transformable(TransformedRectKind, TransformBatchKind),
Brush(BrushBatchKind),
}
#[derive(Copy, Clone, Debug)]

View File

@ -66,10 +66,6 @@ const SHADERS: &[Shader] = &[
name: "ps_radial_gradient",
features: PRIM_FEATURES,
},
Shader {
name: "ps_cache_image",
features: &["COLOR", "ALPHA"],
},
Shader {
name: "ps_blend",
features: PRIM_FEATURES,
@ -107,6 +103,10 @@ const SHADERS: &[Shader] = &[
name: "brush_mask",
features: &[],
},
Shader {
name: "brush_image",
features: &["COLOR_TARGET", "ALPHA_TARGET"],
},
];
const VERSION_STRING: &str = "#version 300 es\n";

View File

@ -1,6 +1,6 @@
[package]
name = "webrender_api"
version = "0.53.0"
version = "0.53.1"
authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
license = "MPL-2.0"
repository = "https://github.com/servo/webrender"

View File

@ -148,11 +148,8 @@ pub struct RectangleDisplayItem {
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub struct LineDisplayItem {
pub baseline: f32, // LayerPixel
pub start: f32,
pub end: f32,
pub orientation: LineOrientation, // toggles whether above values are interpreted as x/y values
pub width: f32,
pub wavy_line_thickness: f32,
pub color: ColorF,
pub style: LineStyle,
}

View File

@ -491,6 +491,7 @@ impl<'a, 'b> Serialize for DisplayItemRef<'a, 'b> {
struct UnsafeVecWriter(*mut u8);
impl Write for UnsafeVecWriter {
#[inline(always)]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
unsafe {
ptr::copy_nonoverlapping(buf.as_ptr(), self.0, buf.len());
@ -498,16 +499,36 @@ impl Write for UnsafeVecWriter {
}
Ok(buf.len())
}
#[inline(always)]
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
unsafe {
ptr::copy_nonoverlapping(buf.as_ptr(), self.0, buf.len());
self.0 = self.0.offset(buf.len() as isize);
}
Ok(())
}
#[inline(always)]
fn flush(&mut self) -> io::Result<()> { Ok(()) }
}
struct SizeCounter(usize);
impl<'a> Write for SizeCounter {
#[inline(always)]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0 += buf.len();
Ok(buf.len())
}
#[inline(always)]
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
self.0 += buf.len();
Ok(())
}
#[inline(always)]
fn flush(&mut self) -> io::Result<()> { Ok(()) }
}
@ -773,21 +794,15 @@ impl DisplayListBuilder {
pub fn push_line(
&mut self,
info: &LayoutPrimitiveInfo,
baseline: f32,
start: f32,
end: f32,
wavy_line_thickness: f32,
orientation: LineOrientation,
width: f32,
color: ColorF,
color: &ColorF,
style: LineStyle,
) {
let item = SpecificDisplayItem::Line(LineDisplayItem {
baseline,
start,
end,
wavy_line_thickness,
orientation,
width,
color,
color: *color,
style,
});

View File

@ -243,14 +243,14 @@ impl Default for FontInstancePlatformOptions {
#[repr(C)]
#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)]
pub struct FontInstancePlatformOptions {
pub unused: u32,
pub font_smoothing: bool,
}
#[cfg(target_os = "macos")]
impl Default for FontInstancePlatformOptions {
fn default() -> FontInstancePlatformOptions {
FontInstancePlatformOptions {
unused: 0,
font_smoothing: true,
}
}
}

View File

@ -5,7 +5,7 @@ authors = ["The Mozilla Project Developers"]
license = "MPL-2.0"
[dependencies]
webrender_api = {path = "../webrender_api", version = "0.53.0"}
webrender_api = {path = "../webrender_api", version = "0.53.1"}
bincode = "0.8"
rayon = "0.8"
thread_profiler = "0.1.1"
@ -15,5 +15,5 @@ gleam = "0.4"
[dependencies.webrender]
path = "../webrender"
version = "0.53.0"
version = "0.53.1"
default-features = false