--- a/gfx/doc/README.webrender
+++ b/gfx/doc/README.webrender
@@ -74,9 +74,9 @@ there is another crate in m-c called moz
the same folder to store its rust dependencies. If one of the libraries that is
required by both mozjs_sys and webrender is updated without updating the other
project's Cargo.lock file, that results in build bustage.
This means that any time you do this sort of manual update of packages, you need
to make sure that mozjs_sys also has its Cargo.lock file updated if needed, hence
the need to run the cargo update command in js/src as well. Hopefully this will
be resolved soon.
-Latest Commit: b9f7e2926731f0ed831108456c219cae30a7e4c8
+Latest Commit: 8516d6c04235e684d9bf9c783ba4fc99dab3bf02
--- a/gfx/webrender/Cargo.toml
+++ b/gfx/webrender/Cargo.toml
@@ -12,29 +12,30 @@ freetype-lib = ["freetype/servo-freetype
profiler = ["thread_profiler/thread_profiler"]
webgl = ["offscreen_gl_context", "webrender_traits/webgl"]
[dependencies]
app_units = "0.4"
bincode = "1.0.0-alpha6"
bit-set = "0.4"
byteorder = "1.0"
-euclid = "0.11"
+euclid = "0.11.2"
fnv = "1.0"
gleam = "0.4.3"
lazy_static = "0.2"
log = "0.3"
num-traits = "0.1.32"
offscreen_gl_context = {version = "0.8.0", features = ["serde", "osmesa"], optional = true}
time = "0.1"
threadpool = "1.3.2"
webrender_traits = {path = "../webrender_traits"}
bitflags = "0.7"
gamma-lut = "0.1"
thread_profiler = "0.1.1"
+plane-split = "0.2.1"
[dev-dependencies]
angle = {git = "https://github.com/servo/angle", branch = "servo"}
servo-glutin = "0.10.1" # for the example apps
[target.'cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))'.dependencies]
freetype = { version = "0.2", default-features = false }
--- a/gfx/webrender/res/clip_shared.glsl
+++ b/gfx/webrender/res/clip_shared.glsl
@@ -83,12 +83,14 @@ ClipVertexInfo write_clip_tile_vertex(Re
vec4 layer_pos = get_layer_pos(actual_pos / uDevicePixelRatio, layer);
// compute the point position in side the layer, in CSS space
vec2 vertex_pos = actual_pos + area.task_bounds.xy - area.screen_origin_target_index.xy;
gl_Position = uTransform * vec4(vertex_pos, 0.0, 1);
+ vLocalBounds = vec4(clipped_local_rect.p0, clipped_local_rect.p0 + clipped_local_rect.size);
+
return ClipVertexInfo(layer_pos.xyw, actual_pos, clipped_local_rect);
}
#endif //WR_VERTEX_SHADER
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/cs_clip_border.fs.glsl
@@ -0,0 +1,30 @@
+#line 1
+/* 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/. */
+
+void main(void) {
+ vec2 local_pos = vPos.xy / vPos.z;
+
+ // Get local space position relative to the clip center.
+ vec2 clip_relative_pos = local_pos - vClipCenter;
+
+ // Get the signed distances to the two clip lines.
+ float d0 = distance_to_line(vPoint_Tangent0.xy,
+ vPoint_Tangent0.zw,
+ clip_relative_pos);
+ float d1 = distance_to_line(vPoint_Tangent1.xy,
+ vPoint_Tangent1.zw,
+ clip_relative_pos);
+
+ // Get AA widths based on zoom / scale etc.
+ vec2 fw = fwidth(local_pos);
+ float afwidth = length(fw);
+
+ // Apply AA over half a device pixel for the clip.
+ float d = smoothstep(-0.5 * afwidth,
+ 0.5 * afwidth,
+ max(d0, -d1));
+
+ oFragColor = vec4(d, 0.0, 0.0, 1.0);
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/cs_clip_border.glsl
@@ -0,0 +1,12 @@
+#line 1
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+varying vec3 vPos;
+
+flat varying vec2 vClipCenter;
+
+flat varying vec4 vPoint_Tangent0;
+flat varying vec4 vPoint_Tangent1;
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/cs_clip_border.vs.glsl
@@ -0,0 +1,68 @@
+#line 1
+/* 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/. */
+
+// Header for a border corner clip.
+struct BorderCorner {
+ RectWithSize rect;
+ vec2 clip_center;
+ vec2 sign_modifier;
+};
+
+BorderCorner fetch_border_corner(int index) {
+ vec4 data[2] = fetch_data_2(index);
+ return BorderCorner(RectWithSize(data[0].xy, data[0].zw),
+ data[1].xy,
+ data[1].zw);
+}
+
+// Per-dash clip information.
+// TODO: Expand this to handle dots in the future!
+struct BorderClip {
+ vec4 point_tangent_0;
+ vec4 point_tangent_1;
+};
+
+BorderClip fetch_border_clip(int index) {
+ vec4 data[2] = fetch_data_2(index);
+ return BorderClip(data[0], data[1]);
+}
+
+void main(void) {
+ CacheClipInstance cci = fetch_clip_item(gl_InstanceID);
+ ClipArea area = fetch_clip_area(cci.render_task_index);
+ Layer layer = fetch_layer(cci.layer_index);
+
+ // Fetch the header information for this corner clip.
+ BorderCorner corner = fetch_border_corner(cci.data_index);
+ vClipCenter = corner.clip_center;
+
+ // Fetch the information about this particular dash.
+ BorderClip clip = fetch_border_clip(cci.data_index + cci.segment_index + 1);
+ vPoint_Tangent0 = clip.point_tangent_0 * corner.sign_modifier.xyxy;
+ vPoint_Tangent1 = clip.point_tangent_1 * corner.sign_modifier.xyxy;
+
+ // Get local vertex position for the corner rect.
+ // TODO(gw): We could reduce the number of pixels written here
+ // by calculating a tight fitting bounding box of the dash itself.
+ vec2 pos = corner.rect.p0 + aPosition.xy * corner.rect.size;
+
+ // Transform to world pos
+ vec4 world_pos = layer.transform * vec4(pos, 0.0, 1.0);
+ world_pos.xyz /= world_pos.w;
+
+ // Scale into device pixels.
+ vec2 device_pos = world_pos.xy * uDevicePixelRatio;
+
+ // Position vertex within the render task area.
+ vec2 final_pos = device_pos -
+ area.screen_origin_target_index.xy +
+ area.task_bounds.xy;
+
+ // Calculate the local space position for this vertex.
+ vec4 layer_pos = get_layer_pos(world_pos.xy, layer);
+ vPos = layer_pos.xyw;
+
+ gl_Position = uTransform * vec4(final_pos, 0.0, 1.0);
+}
--- a/gfx/webrender/res/cs_clip_image.fs.glsl
+++ b/gfx/webrender/res/cs_clip_image.fs.glsl
@@ -1,15 +1,15 @@
/* 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/. */
void main(void) {
float alpha = 1.f;
- vec2 local_pos = init_transform_fs(vPos, vLocalRect, alpha);
+ vec2 local_pos = init_transform_fs(vPos, alpha);
bool repeat_mask = false; //TODO
vec2 clamped_mask_uv = repeat_mask ? fract(vClipMaskUv.xy) :
clamp(vClipMaskUv.xy, vec2(0.0, 0.0), vec2(1.0, 1.0));
vec2 source_uv = clamp(clamped_mask_uv * vClipMaskUvRect.zw + vClipMaskUvRect.xy,
vClipMaskUvInnerRect.xy, vClipMaskUvInnerRect.zw);
float clip_alpha = texture(sColor0, source_uv).r; //careful: texture has type A8
--- a/gfx/webrender/res/cs_clip_image.glsl
+++ b/gfx/webrender/res/cs_clip_image.glsl
@@ -1,10 +1,9 @@
#line 1
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
varying vec3 vPos;
-flat varying RectWithSize vLocalRect;
flat varying vec4 vClipMaskUvRect;
flat varying vec4 vClipMaskUvInnerRect;
--- a/gfx/webrender/res/cs_clip_image.vs.glsl
+++ b/gfx/webrender/res/cs_clip_image.vs.glsl
@@ -21,17 +21,16 @@ void main(void) {
ImageMaskData mask = fetch_mask_data(cci.data_index);
RectWithSize local_rect = mask.local_rect;
ClipVertexInfo vi = write_clip_tile_vertex(local_rect,
layer,
area,
cci.segment_index);
- vLocalRect = vi.clipped_local_rect;
vPos = vi.local_pos;
vClipMaskUv = vec3((vPos.xy / vPos.z - local_rect.p0) / local_rect.size, 0.0);
vec2 texture_size = vec2(textureSize(sColor0, 0));
vClipMaskUvRect = vec4(mask.uv_rect.p0, mask.uv_rect.size) / texture_size.xyxy;
// applying a half-texel offset to the UV boundaries to prevent linear samples from the outside
vec4 inner_rect = vec4(mask.uv_rect.p0, mask.uv_rect.p0 + mask.uv_rect.size);
vClipMaskUvInnerRect = (inner_rect + vec4(0.5, 0.5, -0.5, -0.5)) / texture_size.xyxy;
--- a/gfx/webrender/res/cs_clip_rectangle.fs.glsl
+++ b/gfx/webrender/res/cs_clip_rectangle.fs.glsl
@@ -31,17 +31,17 @@ float rounded_rect(vec2 pos) {
//distance_from_border -= 0.5;
return 1.0 - smoothstep(0.0, 1.0, distance_from_border);
}
void main(void) {
float alpha = 1.f;
- vec2 local_pos = init_transform_fs(vPos, vLocalRect, alpha);
+ vec2 local_pos = init_transform_fs(vPos, alpha);
float clip_alpha = rounded_rect(local_pos);
float combined_alpha = min(alpha, clip_alpha);
// Select alpha or inverse alpha depending on clip in/out.
float final_alpha = mix(combined_alpha, 1.0 - combined_alpha, vClipMode);
--- a/gfx/webrender/res/cs_clip_rectangle.glsl
+++ b/gfx/webrender/res/cs_clip_rectangle.glsl
@@ -1,11 +1,10 @@
#line 1
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
varying vec3 vPos;
-flat varying RectWithSize vLocalRect;
flat varying vec4 vClipRect;
flat varying vec4 vClipRadius;
flat varying float vClipMode;
--- a/gfx/webrender/res/cs_clip_rectangle.vs.glsl
+++ b/gfx/webrender/res/cs_clip_rectangle.vs.glsl
@@ -49,17 +49,16 @@ void main(void) {
Layer layer = fetch_layer(cci.layer_index);
ClipData clip = fetch_clip(cci.data_index);
RectWithSize local_rect = clip.rect.rect;
ClipVertexInfo vi = write_clip_tile_vertex(local_rect,
layer,
area,
cci.segment_index);
- vLocalRect = vi.clipped_local_rect;
vPos = vi.local_pos;
vClipMode = clip.rect.mode.x;
vClipRect = vec4(local_rect.p0, local_rect.p0 + local_rect.size);
vClipRadius = vec4(clip.top_left.outer_inner_radius.x,
clip.top_right.outer_inner_radius.x,
clip.bottom_right.outer_inner_radius.x,
clip.bottom_left.outer_inner_radius.x);
--- a/gfx/webrender/res/prim_shared.glsl
+++ b/gfx/webrender/res/prim_shared.glsl
@@ -43,16 +43,18 @@
#define UV_PIXEL uint(1)
#define EXTEND_MODE_CLAMP 0
#define EXTEND_MODE_REPEAT 1
uniform sampler2DArray sCacheA8;
uniform sampler2DArray sCacheRGBA8;
+uniform sampler2D sGradients;
+
struct RectWithSize {
vec2 p0;
vec2 size;
};
struct RectWithEndpoint {
vec2 p0;
vec2 p1;
@@ -96,26 +98,34 @@ RectWithSize intersect_rect(RectWithSize
return RectWithSize(p.xy, max(vec2(0.0), p.zw - p.xy));
}
RectWithEndpoint intersect_rect(RectWithEndpoint a, RectWithEndpoint b) {
vec4 p = clamp_rect(vec4(a.p0, a.p1), b);
return RectWithEndpoint(p.xy, max(p.xy, p.zw));
}
+float distance_to_line(vec2 p0, vec2 perp_dir, vec2 p) {
+ vec2 dir_to_p0 = p0 - p;
+ return dot(normalize(perp_dir), dir_to_p0);
+}
// TODO: convert back to RectWithEndPoint if driver issues are resolved, if ever.
flat varying vec4 vClipMaskUvBounds;
varying vec3 vClipMaskUv;
+#ifdef WR_FEATURE_TRANSFORM
+ flat varying vec4 vLocalBounds;
+#endif
#ifdef WR_VERTEX_SHADER
#define VECS_PER_LAYER 13
#define VECS_PER_RENDER_TASK 3
#define VECS_PER_PRIM_GEOM 2
+#define VECS_PER_SPLIT_GEOM 3
uniform sampler2D sLayers;
uniform sampler2D sRenderTasks;
uniform sampler2D sPrimGeometry;
uniform sampler2D sData16;
uniform sampler2D sData32;
uniform sampler2D sData64;
@@ -693,16 +703,18 @@ TransformVertexInfo write_transform_vert
// Apply offsets for the render task to get correct screen location.
vec2 final_pos = device_pos -
snap_delta -
task.screen_space_origin +
task.render_target_origin;
gl_Position = uTransform * vec4(final_pos, z, 1.0);
+ vLocalBounds = vec4(local_rect.p0, local_rect.p1);
+
vec4 layer_pos = get_layer_pos(device_pos / uDevicePixelRatio, layer);
return TransformVertexInfo(layer_pos.xyw, device_pos);
}
#endif //WR_FEATURE_TRANSFORM
struct ResourceRect {
@@ -772,48 +784,49 @@ void write_clip(vec2 global_pos, ClipAre
vec2 texture_size = vec2(textureSize(sCacheA8, 0).xy);
vec2 uv = global_pos + area.task_bounds.xy - area.screen_origin_target_index.xy;
vClipMaskUvBounds = area.task_bounds / texture_size.xyxy;
vClipMaskUv = vec3(uv / texture_size, area.screen_origin_target_index.z);
}
#endif //WR_VERTEX_SHADER
#ifdef WR_FRAGMENT_SHADER
+
+#ifdef WR_FEATURE_TRANSFORM
float signed_distance_rect(vec2 pos, vec2 p0, vec2 p1) {
vec2 d = max(p0 - pos, pos - p1);
return length(max(vec2(0.0), d)) + min(0.0, max(d.x, d.y));
}
-vec2 init_transform_fs(vec3 local_pos, RectWithSize local_rect, out float fragment_alpha) {
+vec2 init_transform_fs(vec3 local_pos, out float fragment_alpha) {
fragment_alpha = 1.0;
vec2 pos = local_pos.xy / local_pos.z;
// Because the local rect is placed on whole coordinates, but the interpolation
// occurs at pixel centers, we need to offset the signed distance by that amount.
// In the simple case of no zoom, and no transform, this is 0.5. However, we
// need to scale this by the amount that the local rect is changing by per
// fragment, based on the current zoom and transform.
vec2 fw = fwidth(pos.xy);
vec2 dxdy = 0.5 * fw;
// Now get the actual signed distance. Inset the local rect by the offset amount
// above to get correct distance values. This ensures that we only apply
// anti-aliasing when the fragment has partial coverage.
- float d = signed_distance_rect(pos,
- local_rect.p0 + dxdy,
- local_rect.p0 + local_rect.size - dxdy);
+ float d = signed_distance_rect(pos, vLocalBounds.xy + dxdy, vLocalBounds.zw - dxdy);
// Find the appropriate distance to apply the AA smoothstep over.
float afwidth = 0.5 / length(fw);
// Only apply AA to fragments outside the signed distance field.
fragment_alpha = 1.0 - smoothstep(0.0, afwidth, d);
return pos;
}
+#endif //WR_FEATURE_TRANSFORM
float do_clip() {
// anything outside of the mask is considered transparent
bvec4 inside = lessThanEqual(
vec4(vClipMaskUvBounds.xy, vClipMaskUv.xy),
vec4(vClipMaskUv.xy, vClipMaskUvBounds.zw));
// check for the dummy bounds, which are given to the opaque objects
return vClipMaskUvBounds.xy == vClipMaskUvBounds.zw ? 1.0:
@@ -829,11 +842,33 @@ vec4 dither(vec4 color) {
float noise = (noise_normalized - 0.5) / 256.0; // scale down to the unit length
return color + vec4(noise, noise, noise, 0);
}
#else
vec4 dither(vec4 color) {
return color;
}
-#endif
+#endif //WR_FEATURE_DITHERING
+
+vec4 sample_gradient(float offset, float gradient_repeat, float gradient_index, vec2 gradient_size) {
+ // Either saturate or modulo the offset depending on repeat mode
+ float x = mix(clamp(offset, 0.0, 1.0), fract(offset), gradient_repeat);
+
+ // Scale to the number of gradient color entries (texture width / 2).
+ x = x * 0.5 * gradient_size.x;
+
+ // Calculate the texel to index into the gradient color entries:
+ // floor(x) is the gradient color entry index
+ // fract(x) is the linear filtering factor between start and end
+ // so, 2 * floor(x) + 0.5 is the center of the start color
+ // finally, add floor(x) to interpolate to end
+ x = 2.0 * floor(x) + 0.5 + fract(x);
+
+ // Gradient color entries are encoded with high bits in one row and low bits in the next
+ // So use linear filtering to mix (gradient_index + 1) with (gradient_index)
+ float y = gradient_index * 2.0 + 0.5 + 1.0 / 256.0;
+
+ // Finally sample and apply dithering
+ return dither(texture(sGradients, vec2(x, y) / gradient_size));
+}
#endif //WR_FRAGMENT_SHADER
--- a/gfx/webrender/res/ps_angle_gradient.fs.glsl
+++ b/gfx/webrender/res/ps_angle_gradient.fs.glsl
@@ -1,30 +1,19 @@
/* 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/. */
-uniform sampler2D sGradients;
-
void main(void) {
vec2 pos = mod(vPos, vTileRepeat);
if (pos.x >= vTileSize.x ||
pos.y >= vTileSize.y) {
discard;
}
- // Normalized offset of this vertex within the gradient, before clamp/repeat.
float offset = dot(pos - vStartPoint, vScaledDir);
- vec2 texture_size = vec2(textureSize(sGradients, 0));
-
- // Either saturate or modulo the offset depending on repeat mode, then scale to number of
- // gradient color entries (texture width / 2).
- float x = mix(clamp(offset, 0.0, 1.0), fract(offset), vGradientRepeat) * 0.5 * texture_size.x;
-
- x = 2.0 * floor(x) + 0.5 + fract(x);
-
- // Use linear filtering to mix in the low bits (vGradientIndex + 1) with the high
- // bits (vGradientIndex)
- float y = vGradientIndex * 2.0 + 0.5 + 1.0 / 256.0;
- oFragColor = dither(texture(sGradients, vec2(x, y) / texture_size));
+ oFragColor = sample_gradient(offset,
+ vGradientRepeat,
+ vGradientIndex,
+ vGradientTextureSize);
}
--- a/gfx/webrender/res/ps_angle_gradient.glsl
+++ b/gfx/webrender/res/ps_angle_gradient.glsl
@@ -1,11 +1,15 @@
/* 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/. */
+flat varying float vGradientIndex;
+flat varying vec2 vGradientTextureSize;
+flat varying float vGradientRepeat;
+
flat varying vec2 vScaledDir;
flat varying vec2 vStartPoint;
-flat varying float vGradientIndex;
-flat varying float vGradientRepeat;
+
flat varying vec2 vTileSize;
flat varying vec2 vTileRepeat;
+
varying vec2 vPos;
--- a/gfx/webrender/res/ps_angle_gradient.vs.glsl
+++ b/gfx/webrender/res/ps_angle_gradient.vs.glsl
@@ -24,11 +24,14 @@ void main(void) {
vScaledDir = dir / dot(dir, dir);
vTileSize = gradient.tile_size_repeat.xy;
vTileRepeat = gradient.tile_size_repeat.zw;
// V coordinate of gradient row in lookup texture.
vGradientIndex = float(prim.sub_index);
+ // The texture size of the lookup texture
+ vGradientTextureSize = vec2(textureSize(sGradients, 0));
+
// Whether to repeat the gradient instead of clamping.
vGradientRepeat = float(int(gradient.extend_mode.x) == EXTEND_MODE_REPEAT);
}
--- a/gfx/webrender/res/ps_blend.fs.glsl
+++ b/gfx/webrender/res/ps_blend.fs.glsl
@@ -94,17 +94,18 @@ vec4 Brightness(vec4 Cs, float amount) {
return vec4(Cs.rgb * amount, Cs.a);
}
vec4 Opacity(vec4 Cs, float amount) {
return vec4(Cs.rgb, Cs.a * amount);
}
void main(void) {
- vec4 Cs = texture(sCacheRGBA8, vUv);
+ vec2 uv = clamp(vUv.xy, vUvBounds.xy, vUvBounds.zw);
+ vec4 Cs = textureLod(sCacheRGBA8, vec3(uv, vUv.z), 0.0);
if (Cs.a == 0.0) {
discard;
}
switch (vOp) {
case 0:
// Gaussian blur is specially handled:
--- a/gfx/webrender/res/ps_blend.glsl
+++ b/gfx/webrender/res/ps_blend.glsl
@@ -1,7 +1,8 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
varying vec3 vUv;
+flat varying vec4 vUvBounds;
flat varying float vAmount;
flat varying int vOp;
--- a/gfx/webrender/res/ps_blend.vs.glsl
+++ b/gfx/webrender/res/ps_blend.vs.glsl
@@ -15,14 +15,15 @@ void main(void) {
vec2 local_pos = mix(dest_origin,
dest_origin + src_task.size,
aPosition.xy);
vec2 texture_size = vec2(textureSize(sCacheRGBA8, 0));
vec2 st0 = src_task.render_target_origin / texture_size;
vec2 st1 = (src_task.render_target_origin + src_task.size) / texture_size;
vUv = vec3(mix(st0, st1, aPosition.xy), src_task.render_target_layer_index);
+ vUvBounds = vec4(st0 + 0.5 / texture_size, st1 - 0.5 / texture_size);
vOp = pi.sub_index;
vAmount = float(pi.user_data.y) / 65535.0;
gl_Position = uTransform * vec4(local_pos, pi.z, 1.0);
}
--- a/gfx/webrender/res/ps_border.fs.glsl
+++ b/gfx/webrender/res/ps_border.fs.glsl
@@ -405,30 +405,30 @@ vec4 draw_complete_border(vec2 local_pos
return vec4(0.0);
}
// TODO: Investigate performance of this shader and see
// if it's worthwhile splitting it / removing branches etc.
void main(void) {
#ifdef WR_FEATURE_TRANSFORM
float alpha = 0.0;
- vec2 local_pos = init_transform_fs(vLocalPos, vLocalRect, alpha);
+ vec2 local_pos = init_transform_fs(vLocalPos, alpha);
#else
float alpha = 1.0;
vec2 local_pos = vLocalPos;
#endif
#ifdef WR_FEATURE_TRANSFORM
// TODO(gw): Support other border styles for transformed elements.
float distance_from_mix_line = (local_pos.x - vPieceRect.x) * vPieceRect.w -
(local_pos.y - vPieceRect.y) * vPieceRect.z;
distance_from_mix_line /= vPieceRectHypotenuseLength;
- float distance_from_middle = (local_pos.x - vBorderRect.p0.x) +
- (local_pos.y - vBorderRect.p0.y) -
- 0.5 * (vBorderRect.size.x + vBorderRect.size.y);
+ float distance_from_middle = (local_pos.x - vBorderRect.x) +
+ (local_pos.y - vBorderRect.y) -
+ 0.5 * (vBorderRect.z + vBorderRect.w);
#else
float distance_from_mix_line = vDistanceFromMixLine;
float distance_from_middle = vDistanceFromMiddle;
#endif
oFragColor = draw_complete_border(local_pos, distance_from_mix_line, distance_from_middle);
oFragColor.a *= min(alpha, do_clip());
}
--- a/gfx/webrender/res/ps_border.glsl
+++ b/gfx/webrender/res/ps_border.glsl
@@ -3,30 +3,29 @@
/* 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/. */
// These are not changing.
flat varying vec4 vVerticalColor; // The vertical color, e.g. top/bottom
flat varying vec4 vHorizontalColor; // The horizontal color e.g. left/right
flat varying vec4 vRadii; // The border radius from CSS border-radius
-flat varying RectWithSize vBorderRect; // The rect of the border in local space.
+flat varying vec4 vBorderRect; // The rect of the border in local space.
// for corners, this is the beginning of the corner.
// For the lines, this is the top left of the line.
flat varying vec2 vRefPoint;
flat varying int vBorderStyle;
flat varying int vBorderPart; // Which part of the border we're drawing.
flat varying vec4 vPieceRect;
// These are in device space
#ifdef WR_FEATURE_TRANSFORM
varying vec3 vLocalPos; // The clamped position in local space.
-flat varying RectWithSize vLocalRect;
flat varying float vPieceRectHypotenuseLength;
#else
varying vec2 vLocalPos; // The clamped position in local space.
// These two are interpolated
varying float vDistanceFromMixLine; // This is the distance from the line where two colors
// meet in border corners.
varying float vDistanceFromMiddle; // This is the distance from the line between the top
--- a/gfx/webrender/res/ps_border.vs.glsl
+++ b/gfx/webrender/res/ps_border.vs.glsl
@@ -2,34 +2,34 @@
/* 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/. */
void main(void) {
Primitive prim = load_primitive();
Border border = fetch_border(prim.prim_index);
int sub_part = prim.sub_index;
- vBorderRect = prim.local_rect;
+ vBorderRect = vec4(prim.local_rect.p0, prim.local_rect.size);
- vec2 tl_outer = vBorderRect.p0;
+ vec2 tl_outer = vBorderRect.xy;
vec2 tl_inner = tl_outer + vec2(max(border.radii[0].x, border.widths.x),
max(border.radii[0].y, border.widths.y));
- vec2 tr_outer = vec2(vBorderRect.p0.x + vBorderRect.size.x,
- vBorderRect.p0.y);
+ vec2 tr_outer = vec2(vBorderRect.x + vBorderRect.z,
+ vBorderRect.y);
vec2 tr_inner = tr_outer + vec2(-max(border.radii[0].z, border.widths.z),
max(border.radii[0].w, border.widths.y));
- vec2 br_outer = vec2(vBorderRect.p0.x + vBorderRect.size.x,
- vBorderRect.p0.y + vBorderRect.size.y);
+ vec2 br_outer = vec2(vBorderRect.x + vBorderRect.z,
+ vBorderRect.y + vBorderRect.w);
vec2 br_inner = br_outer - vec2(max(border.radii[1].x, border.widths.z),
max(border.radii[1].y, border.widths.w));
- vec2 bl_outer = vec2(vBorderRect.p0.x,
- vBorderRect.p0.y + vBorderRect.size.y);
+ vec2 bl_outer = vec2(vBorderRect.x,
+ vBorderRect.y + vBorderRect.w);
vec2 bl_inner = bl_outer + vec2(max(border.radii[1].z, border.widths.x),
-max(border.radii[1].w, border.widths.w));
RectWithSize segment_rect;
switch (sub_part) {
case PST_TOP_LEFT:
segment_rect.p0 = tl_outer;
segment_rect.size = tl_inner - tl_outer;
@@ -102,28 +102,26 @@ void main(void) {
#ifdef WR_FEATURE_TRANSFORM
TransformVertexInfo vi = write_transform_vertex(segment_rect,
prim.local_clip_rect,
prim.z,
prim.layer,
prim.task,
prim.local_rect.p0);
- vLocalPos = vi.local_pos;
- vLocalRect = segment_rect;
#else
VertexInfo vi = write_vertex(segment_rect,
prim.local_clip_rect,
prim.z,
prim.layer,
prim.task,
prim.local_rect.p0);
- vLocalPos = vi.local_pos.xy;
#endif
+ vLocalPos = vi.local_pos;
write_clip(vi.screen_pos, prim.clip_area);
float x0, y0, x1, y1;
switch (sub_part) {
// These are the layer tile part PrimitivePart as uploaded by the tiling.rs
case PST_TOP_LEFT:
x0 = segment_rect.p0.x;
y0 = segment_rect.p0.y;
@@ -180,13 +178,13 @@ void main(void) {
// to properly mix border colors. For transformed borders, we calculate this distance
// in the fragment shader itself. For non-transformed borders, we can use the
// interpolator.
#ifdef WR_FEATURE_TRANSFORM
vPieceRectHypotenuseLength = sqrt(pow(width, 2.0) + pow(height, 2.0));
#else
vDistanceFromMixLine = (vi.local_pos.x - x0) * height -
(vi.local_pos.y - y0) * width;
- vDistanceFromMiddle = (vi.local_pos.x - vBorderRect.p0.x) +
- (vi.local_pos.y - vBorderRect.p0.y) -
- 0.5 * (vBorderRect.size.x + vBorderRect.size.y);
+ vDistanceFromMiddle = (vi.local_pos.x - vBorderRect.x) +
+ (vi.local_pos.y - vBorderRect.y) -
+ 0.5 * (vBorderRect.z + vBorderRect.w);
#endif
}
--- a/gfx/webrender/res/ps_border_corner.fs.glsl
+++ b/gfx/webrender/res/ps_border_corner.fs.glsl
@@ -49,37 +49,32 @@ float sdEllipse( vec2 p, in vec2 ab ) {
float si = sqrt( 1.0 - co*co );
vec2 r = vec2( ab.x*co, ab.y*si );
return length(r - p ) * sign(p.y-r.y);
}
-float distance_to_line(vec2 p0, vec2 perp_dir, vec2 p) {
- vec2 dir_to_p0 = p0 - p;
- return dot(normalize(perp_dir), dir_to_p0);
-}
-
float distance_to_ellipse(vec2 p, vec2 radii) {
// sdEllipse fails on exact circles, so handle equal
// radii here. The branch coherency should make this
// a performance win for the circle case too.
if (radii.x == radii.y) {
return length(p) - radii.x;
} else {
return sdEllipse(p, radii);
}
}
void main(void) {
float alpha = 1.0;
#ifdef WR_FEATURE_TRANSFORM
alpha = 0.0;
- vec2 local_pos = init_transform_fs(vLocalPos, vLocalRect, alpha);
+ vec2 local_pos = init_transform_fs(vLocalPos, alpha);
#else
vec2 local_pos = vLocalPos;
#endif
alpha = min(alpha, do_clip());
// Find the appropriate distance to apply the AA smoothstep over.
vec2 fw = fwidth(local_pos);
--- a/gfx/webrender/res/ps_border_corner.glsl
+++ b/gfx/webrender/res/ps_border_corner.glsl
@@ -17,13 +17,12 @@ flat varying vec4 vRadii1;
flat varying vec2 vClipSign;
flat varying vec4 vEdgeDistance;
flat varying float vSDFSelect;
// Border style
flat varying float vAlphaSelect;
#ifdef WR_FEATURE_TRANSFORM
-flat varying RectWithSize vLocalRect;
varying vec3 vLocalPos;
#else
varying vec2 vLocalPos;
#endif
--- a/gfx/webrender/res/ps_border_corner.vs.glsl
+++ b/gfx/webrender/res/ps_border_corner.vs.glsl
@@ -205,22 +205,20 @@ void main(void) {
#ifdef WR_FEATURE_TRANSFORM
TransformVertexInfo vi = write_transform_vertex(segment_rect,
prim.local_clip_rect,
prim.z,
prim.layer,
prim.task,
prim.local_rect.p0);
- vLocalPos = vi.local_pos;
- vLocalRect = segment_rect;
#else
VertexInfo vi = write_vertex(segment_rect,
prim.local_clip_rect,
prim.z,
prim.layer,
prim.task,
prim.local_rect.p0);
- vLocalPos = vi.local_pos.xy;
#endif
+ vLocalPos = vi.local_pos;
write_clip(vi.screen_pos, prim.clip_area);
}
--- a/gfx/webrender/res/ps_border_edge.fs.glsl
+++ b/gfx/webrender/res/ps_border_edge.fs.glsl
@@ -3,45 +3,47 @@
/* 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/. */
void main(void) {
float alpha = 1.0;
#ifdef WR_FEATURE_TRANSFORM
alpha = 0.0;
- vec2 local_pos = init_transform_fs(vLocalPos, vLocalRect, alpha);
+ vec2 local_pos = init_transform_fs(vLocalPos, alpha);
#else
vec2 local_pos = vLocalPos;
#endif
alpha = min(alpha, do_clip());
// Find the appropriate distance to apply the step over.
vec2 fw = fwidth(local_pos);
// Applies the math necessary to draw a style: double
// border. In the case of a solid border, the vertex
// shader sets interpolator values that make this have
// no effect.
// Select the x/y coord, depending on which axis this edge is.
- float pos = mix(local_pos.x, local_pos.y, vAxisSelect);
+ vec2 pos = mix(local_pos.xy, local_pos.yx, vAxisSelect);
// Get signed distance from each of the inner edges.
- float d0 = pos - vEdgeDistance.x;
- float d1 = vEdgeDistance.y - pos;
+ float d0 = pos.x - vEdgeDistance.x;
+ float d1 = vEdgeDistance.y - pos.x;
// SDF union to select both outer edges.
float d = min(d0, d1);
// Select fragment on/off based on signed distance.
// No AA here, since we know we're on a straight edge
// and the width is rounded to a whole CSS pixel.
alpha = min(alpha, mix(vAlphaSelect, 1.0, d < 0.0));
// Mix color based on first distance.
// TODO(gw): Support AA for groove/ridge border edge with transforms.
vec4 color = mix(vColor0, vColor1, bvec4(d0 * vEdgeDistance.y > 0.0));
- //oFragColor = vec4(d0 * vEdgeDistance.y, -d0 * vEdgeDistance.y, 0, 1.0);
+ // Apply dashing parameters.
+ alpha = min(alpha, step(mod(pos.y - vDashParams.x, vDashParams.y), vDashParams.z));
+
oFragColor = color * vec4(1.0, 1.0, 1.0, alpha);
}
--- a/gfx/webrender/res/ps_border_edge.glsl
+++ b/gfx/webrender/res/ps_border_edge.glsl
@@ -2,15 +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/. */
flat varying vec4 vColor0;
flat varying vec4 vColor1;
flat varying vec2 vEdgeDistance;
flat varying float vAxisSelect;
flat varying float vAlphaSelect;
+flat varying vec3 vDashParams;
#ifdef WR_FEATURE_TRANSFORM
varying vec3 vLocalPos;
-flat varying RectWithSize vLocalRect;
#else
varying vec2 vLocalPos;
#endif
--- a/gfx/webrender/res/ps_border_edge.vs.glsl
+++ b/gfx/webrender/res/ps_border_edge.vs.glsl
@@ -51,69 +51,95 @@ void write_color(vec4 color, float style
modulate = vec2(1.0);
break;
}
vColor0 = vec4(color.rgb * modulate.x, color.a);
vColor1 = vec4(color.rgb * modulate.y, color.a);
}
+void write_dash_params(float style,
+ float border_width,
+ float edge_length,
+ float edge_offset) {
+ // x = offset
+ // y = dash on + off length
+ // z = dash length
+ switch (int(style)) {
+ case BORDER_STYLE_DASHED: {
+ float desired_dash_length = border_width * 3.0;
+ // Consider half total length since there is an equal on/off for each dash.
+ float dash_count = ceil(0.5 * edge_length / desired_dash_length);
+ float dash_length = 0.5 * edge_length / dash_count;
+ vDashParams = vec3(edge_offset - 0.5 * dash_length,
+ 2.0 * dash_length,
+ dash_length);
+ break;
+ }
+ default:
+ vDashParams = vec3(1.0);
+ break;
+ }
+}
+
void main(void) {
Primitive prim = load_primitive();
Border border = fetch_border(prim.prim_index);
int sub_part = prim.sub_index;
BorderCorners corners = get_border_corners(border, prim.local_rect);
vec4 adjusted_widths = get_effective_border_widths(border);
vec4 color = border.colors[sub_part];
RectWithSize segment_rect;
switch (sub_part) {
case 0:
segment_rect.p0 = vec2(corners.tl_outer.x, corners.tl_inner.y);
segment_rect.size = vec2(border.widths.x, corners.bl_inner.y - corners.tl_inner.y);
write_edge_distance(segment_rect.p0.x, border.widths.x, adjusted_widths.x, border.style.x, 0.0, 1.0);
write_alpha_select(border.style.x);
write_color(color, border.style.x, false);
+ write_dash_params(border.style.x, border.widths.x, segment_rect.size.y, segment_rect.p0.y);
break;
case 1:
segment_rect.p0 = vec2(corners.tl_inner.x, corners.tl_outer.y);
segment_rect.size = vec2(corners.tr_inner.x - corners.tl_inner.x, border.widths.y);
write_edge_distance(segment_rect.p0.y, border.widths.y, adjusted_widths.y, border.style.y, 1.0, 1.0);
write_alpha_select(border.style.y);
write_color(color, border.style.y, false);
+ write_dash_params(border.style.y, border.widths.y, segment_rect.size.x, segment_rect.p0.x);
break;
case 2:
segment_rect.p0 = vec2(corners.tr_outer.x - border.widths.z, corners.tr_inner.y);
segment_rect.size = vec2(border.widths.z, corners.br_inner.y - corners.tr_inner.y);
write_edge_distance(segment_rect.p0.x, border.widths.z, adjusted_widths.z, border.style.z, 0.0, -1.0);
write_alpha_select(border.style.z);
write_color(color, border.style.z, true);
+ write_dash_params(border.style.z, border.widths.z, segment_rect.size.y, segment_rect.p0.y);
break;
case 3:
segment_rect.p0 = vec2(corners.bl_inner.x, corners.bl_outer.y - border.widths.w);
segment_rect.size = vec2(corners.br_inner.x - corners.bl_inner.x, border.widths.w);
write_edge_distance(segment_rect.p0.y, border.widths.w, adjusted_widths.w, border.style.w, 1.0, -1.0);
write_alpha_select(border.style.w);
write_color(color, border.style.w, true);
+ write_dash_params(border.style.w, border.widths.w, segment_rect.size.x, segment_rect.p0.x);
break;
}
#ifdef WR_FEATURE_TRANSFORM
TransformVertexInfo vi = write_transform_vertex(segment_rect,
prim.local_clip_rect,
prim.z,
prim.layer,
prim.task,
prim.local_rect.p0);
- vLocalPos = vi.local_pos;
- vLocalRect = segment_rect;
#else
VertexInfo vi = write_vertex(segment_rect,
prim.local_clip_rect,
prim.z,
prim.layer,
prim.task,
prim.local_rect.p0);
- vLocalPos = vi.local_pos;
#endif
+ vLocalPos = vi.local_pos;
write_clip(vi.screen_pos, prim.clip_area);
}
--- a/gfx/webrender/res/ps_gradient.fs.glsl
+++ b/gfx/webrender/res/ps_gradient.fs.glsl
@@ -1,16 +1,16 @@
/* 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/. */
void main(void) {
#ifdef WR_FEATURE_TRANSFORM
float alpha = 0.0;
- vec2 local_pos = init_transform_fs(vLocalPos, vLocalRect, alpha);
+ vec2 local_pos = init_transform_fs(vLocalPos, alpha);
#else
float alpha = 1.0;
vec2 local_pos = vPos;
#endif
alpha = min(alpha, do_clip());
oFragColor = dither(vColor * vec4(1.0, 1.0, 1.0, alpha));
}
--- a/gfx/webrender/res/ps_gradient.glsl
+++ b/gfx/webrender/res/ps_gradient.glsl
@@ -1,12 +1,11 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
varying vec4 vColor;
#ifdef WR_FEATURE_TRANSFORM
varying vec3 vLocalPos;
-flat varying RectWithSize vLocalRect;
#else
varying vec2 vPos;
#endif
--- a/gfx/webrender/res/ps_gradient.vs.glsl
+++ b/gfx/webrender/res/ps_gradient.vs.glsl
@@ -58,17 +58,16 @@ void main(void) {
#ifdef WR_FEATURE_TRANSFORM
TransformVertexInfo vi = write_transform_vertex(segment_rect,
prim.local_clip_rect,
prim.z,
prim.layer,
prim.task,
prim.local_rect.p0);
- vLocalRect = segment_rect;
vLocalPos = vi.local_pos;
vec2 f = (vi.local_pos.xy - prim.local_rect.p0) / prim.local_rect.size;
#else
VertexInfo vi = write_vertex(segment_rect,
prim.local_clip_rect,
prim.z,
prim.layer,
prim.task,
--- a/gfx/webrender/res/ps_image.fs.glsl
+++ b/gfx/webrender/res/ps_image.fs.glsl
@@ -2,21 +2,21 @@
/* 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/. */
void main(void) {
#ifdef WR_FEATURE_TRANSFORM
float alpha = 0.0;
- vec2 pos = init_transform_fs(vLocalPos, vLocalRect, alpha);
+ vec2 pos = init_transform_fs(vLocalPos, alpha);
// We clamp the texture coordinate calculation here to the local rectangle boundaries,
// which makes the edge of the texture stretch instead of repeat.
- vec2 relative_pos_in_rect = clamp_rect(pos, vLocalRect) - vLocalRect.p0;
+ vec2 relative_pos_in_rect = clamp(pos, vLocalBounds.xy, vLocalBounds.zw) - vLocalBounds.xy;
#else
float alpha = 1.0;
vec2 relative_pos_in_rect = vLocalPos;
#endif
alpha = min(alpha, do_clip());
// We calculate the particular tile this fragment belongs to, taking into
--- a/gfx/webrender/res/ps_image.glsl
+++ b/gfx/webrender/res/ps_image.glsl
@@ -7,14 +7,12 @@
// check GL_TEXTURE_RECTANGLE.
flat varying vec2 vTextureOffset; // Offset of this image into the texture atlas.
flat varying vec2 vTextureSize; // Size of the image in the texture atlas.
flat varying vec2 vTileSpacing; // Amount of space between tiled instances of this image.
flat varying vec4 vStRect; // Rectangle of valid texture rect.
#ifdef WR_FEATURE_TRANSFORM
varying vec3 vLocalPos;
-flat varying RectWithSize vLocalRect;
-flat varying vec2 vStretchSize;
#else
varying vec2 vLocalPos;
+#endif
flat varying vec2 vStretchSize;
-#endif
--- a/gfx/webrender/res/ps_image.vs.glsl
+++ b/gfx/webrender/res/ps_image.vs.glsl
@@ -10,17 +10,16 @@ void main(void) {
#ifdef WR_FEATURE_TRANSFORM
TransformVertexInfo vi = write_transform_vertex(prim.local_rect,
prim.local_clip_rect,
prim.z,
prim.layer,
prim.task,
prim.local_rect.p0);
- vLocalRect = prim.local_rect;
vLocalPos = vi.local_pos;
#else
VertexInfo vi = write_vertex(prim.local_rect,
prim.local_clip_rect,
prim.z,
prim.layer,
prim.task,
prim.local_rect.p0);
--- a/gfx/webrender/res/ps_radial_gradient.fs.glsl
+++ b/gfx/webrender/res/ps_radial_gradient.fs.glsl
@@ -1,14 +1,12 @@
/* 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/. */
-uniform sampler2D sGradients;
-
void main(void) {
vec2 pos = mod(vPos, vTileRepeat);
if (pos.x >= vTileSize.x ||
pos.y >= vTileSize.y) {
discard;
}
@@ -17,50 +15,42 @@ void main(void) {
float rd = vEndRadius - vStartRadius;
// Solve for t in length(t * cd - pd) = vStartRadius + t * rd
// using a quadratic equation in form of At^2 - 2Bt + C = 0
float A = dot(cd, cd) - rd * rd;
float B = dot(pd, cd) + vStartRadius * rd;
float C = dot(pd, pd) - vStartRadius * vStartRadius;
- float x;
+ float offset;
if (A == 0.0) {
// Since A is 0, just solve for -2Bt + C = 0
if (B == 0.0) {
discard;
}
float t = 0.5 * C / B;
if (vStartRadius + rd * t >= 0.0) {
- x = t;
+ offset = t;
} else {
discard;
}
} else {
float discr = B * B - A * C;
if (discr < 0.0) {
discard;
}
discr = sqrt(discr);
float t0 = (B + discr) / A;
float t1 = (B - discr) / A;
if (vStartRadius + rd * t0 >= 0.0) {
- x = t0;
+ offset = t0;
} else if (vStartRadius + rd * t1 >= 0.0) {
- x = t1;
+ offset = t1;
} else {
discard;
}
}
- vec2 texture_size = vec2(textureSize(sGradients, 0));
-
- // Either saturate or modulo the offset depending on repeat mode, then scale to number of
- // gradient color entries (texture width / 2).
- x = mix(clamp(x, 0.0, 1.0), fract(x), vGradientRepeat) * 0.5 * texture_size.x;
-
- x = 2.0 * floor(x) + 0.5 + fract(x);
-
- // Use linear filtering to mix in the low bits (vGradientIndex + 1) with the high
- // bits (vGradientIndex)
- float y = vGradientIndex * 2.0 + 0.5 + 1.0 / 256.0;
- oFragColor = dither(texture(sGradients, vec2(x, y) / texture_size));
+ oFragColor = sample_gradient(offset,
+ vGradientRepeat,
+ vGradientIndex,
+ vGradientTextureSize);
}
--- a/gfx/webrender/res/ps_radial_gradient.glsl
+++ b/gfx/webrender/res/ps_radial_gradient.glsl
@@ -1,13 +1,17 @@
/* 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/. */
flat varying float vGradientIndex;
+flat varying vec2 vGradientTextureSize;
flat varying float vGradientRepeat;
+
flat varying vec2 vStartCenter;
flat varying vec2 vEndCenter;
flat varying float vStartRadius;
flat varying float vEndRadius;
+
flat varying vec2 vTileSize;
flat varying vec2 vTileRepeat;
+
varying vec2 vPos;
--- a/gfx/webrender/res/ps_radial_gradient.vs.glsl
+++ b/gfx/webrender/res/ps_radial_gradient.vs.glsl
@@ -32,11 +32,14 @@ void main(void) {
vStartCenter.y *= ratio_xy;
vEndCenter.y *= ratio_xy;
vTileSize.y *= ratio_xy;
vTileRepeat.y *= ratio_xy;
// V coordinate of gradient row in lookup texture.
vGradientIndex = float(prim.sub_index);
+ // The texture size of the lookup texture
+ vGradientTextureSize = vec2(textureSize(sGradients, 0));
+
// Whether to repeat the gradient instead of clamping.
vGradientRepeat = float(int(gradient.start_end_radius_ratio_xy_extend_mode.w) == EXTEND_MODE_REPEAT);
}
--- a/gfx/webrender/res/ps_rectangle.fs.glsl
+++ b/gfx/webrender/res/ps_rectangle.fs.glsl
@@ -1,16 +1,16 @@
/* 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/. */
void main(void) {
float alpha = 1.0;
#ifdef WR_FEATURE_TRANSFORM
alpha = 0.0;
- init_transform_fs(vLocalPos, vLocalRect, alpha);
+ init_transform_fs(vLocalPos, alpha);
#endif
#ifdef WR_FEATURE_CLIP
alpha = min(alpha, do_clip());
#endif
oFragColor = vColor * vec4(1.0, 1.0, 1.0, alpha);
}
--- a/gfx/webrender/res/ps_rectangle.glsl
+++ b/gfx/webrender/res/ps_rectangle.glsl
@@ -1,10 +1,9 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
varying vec4 vColor;
#ifdef WR_FEATURE_TRANSFORM
varying vec3 vLocalPos;
-flat varying RectWithSize vLocalRect;
#endif
--- a/gfx/webrender/res/ps_rectangle.vs.glsl
+++ b/gfx/webrender/res/ps_rectangle.vs.glsl
@@ -9,17 +9,16 @@ void main(void) {
vColor = rect.color;
#ifdef WR_FEATURE_TRANSFORM
TransformVertexInfo vi = write_transform_vertex(prim.local_rect,
prim.local_clip_rect,
prim.z,
prim.layer,
prim.task,
prim.local_rect.p0);
- vLocalRect = prim.local_rect;
vLocalPos = vi.local_pos;
#else
VertexInfo vi = write_vertex(prim.local_rect,
prim.local_clip_rect,
prim.z,
prim.layer,
prim.task,
prim.local_rect.p0);
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_split_composite.fs.glsl
@@ -0,0 +1,9 @@
+#line 1
+/* 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/. */
+
+void main(void) {
+ vec2 uv = clamp(vUv.xy, vUvBounds.xy, vUvBounds.zw);
+ oFragColor = textureLod(sCacheRGBA8, vec3(uv, vUv.z), 0.0);
+}
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_split_composite.glsl
@@ -0,0 +1,7 @@
+#line 1
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+varying vec3 vUv;
+flat varying vec4 vUvBounds;
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/ps_split_composite.vs.glsl
@@ -0,0 +1,48 @@
+#line 1
+/* 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/. */
+
+uniform sampler2D sSplitGeometry;
+
+struct SplitGeometry {
+ vec3 points[4];
+};
+
+SplitGeometry fetch_split_geometry(int index) {
+ ivec2 uv = get_fetch_uv(index, VECS_PER_SPLIT_GEOM);
+
+ vec4 data0 = texelFetchOffset(sSplitGeometry, uv, 0, ivec2(0, 0));
+ vec4 data1 = texelFetchOffset(sSplitGeometry, uv, 0, ivec2(1, 0));
+ vec4 data2 = texelFetchOffset(sSplitGeometry, uv, 0, ivec2(2, 0));
+
+ return SplitGeometry(vec3[4](
+ data0.xyz, vec3(data0.w, data1.xy),
+ vec3(data1.zw, data2.x), data2.yzw
+ ));
+}
+
+vec3 bilerp(vec3 a, vec3 b, vec3 c, vec3 d, float s, float t) {
+ vec3 x = mix(a, b, t);
+ vec3 y = mix(c, d, t);
+ return mix(x, y, s);
+}
+
+void main(void) {
+ PrimitiveInstance pi = fetch_prim_instance();
+ SplitGeometry geometry = fetch_split_geometry(pi.specific_prim_index);
+ AlphaBatchTask src_task = fetch_alpha_batch_task(pi.user_data.x);
+
+ vec3 world_pos = bilerp(geometry.points[0], geometry.points[1],
+ geometry.points[3], geometry.points[2],
+ aPosition.y, aPosition.x);
+ vec4 final_pos = vec4(world_pos.xy * uDevicePixelRatio, pi.z, 1.0);
+
+ gl_Position = uTransform * final_pos;
+
+ vec2 uv_origin = src_task.render_target_origin - src_task.screen_space_origin;
+ vec2 uv_pos = uv_origin + world_pos.xy;
+ vec2 texture_size = vec2(textureSize(sCacheRGBA8, 0));
+ vUv = vec3(uv_pos / texture_size, src_task.render_target_layer_index);
+ vUvBounds = vec4((uv_origin + 0.5) / texture_size, (uv_origin + src_task.size - 0.5) / texture_size);
+}
--- a/gfx/webrender/res/ps_text_run.fs.glsl
+++ b/gfx/webrender/res/ps_text_run.fs.glsl
@@ -6,16 +6,16 @@ void main(void) {
vec2 tc = clamp(vUv, vUvBorder.xy, vUvBorder.zw);
#ifdef WR_FEATURE_SUBPIXEL_AA
//note: the blend mode is not compatible with clipping
oFragColor = texture(sColor0, tc);
#else
float alpha = texture(sColor0, tc).a;
#ifdef WR_FEATURE_TRANSFORM
float a = 0.0;
- init_transform_fs(vLocalPos, vLocalRect, a);
+ init_transform_fs(vLocalPos, a);
alpha *= a;
#endif
vec4 color = vColor;
alpha = min(alpha, do_clip());
oFragColor = vec4(vColor.rgb, vColor.a * alpha);
#endif
}
--- a/gfx/webrender/res/ps_text_run.glsl
+++ b/gfx/webrender/res/ps_text_run.glsl
@@ -3,10 +3,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
flat varying vec4 vColor;
varying vec2 vUv;
flat varying vec4 vUvBorder;
#ifdef WR_FEATURE_TRANSFORM
varying vec3 vLocalPos;
-flat varying RectWithSize vLocalRect;
#endif
--- a/gfx/webrender/res/ps_text_run.vs.glsl
+++ b/gfx/webrender/res/ps_text_run.vs.glsl
@@ -14,17 +14,16 @@ void main(void) {
#ifdef WR_FEATURE_TRANSFORM
TransformVertexInfo vi = write_transform_vertex(local_rect,
prim.local_clip_rect,
prim.z,
prim.layer,
prim.task,
local_rect.p0);
- vLocalRect = local_rect;
vLocalPos = vi.local_pos;
vec2 f = (vi.local_pos.xy / vi.local_pos.z - local_rect.p0) / local_rect.size;
#else
VertexInfo vi = write_vertex(local_rect,
prim.local_clip_rect,
prim.z,
prim.layer,
prim.task,
--- a/gfx/webrender/res/ps_yuv_image.fs.glsl
+++ b/gfx/webrender/res/ps_yuv_image.fs.glsl
@@ -32,21 +32,21 @@ const mat3 YuvColorMatrix = mat3(
1.16438, -0.21325, -0.53291,
1.16438, 2.11240, 0.0
);
#endif
void main(void) {
#ifdef WR_FEATURE_TRANSFORM
float alpha = 0.0;
- vec2 pos = init_transform_fs(vLocalPos, vLocalRect, alpha);
+ vec2 pos = init_transform_fs(vLocalPos, alpha);
// We clamp the texture coordinate calculation here to the local rectangle boundaries,
// which makes the edge of the texture stretch instead of repeat.
- vec2 relative_pos_in_rect = clamp_rect(pos, vLocalRect) - vLocalRect.p0;
+ vec2 relative_pos_in_rect = clamp(pos, vLocalBounds.xy, vLocalBounds.zw) - vLocalBounds.xy;
#else
float alpha = 1.0;;
vec2 relative_pos_in_rect = vLocalPos;
#endif
alpha = min(alpha, do_clip());
// We clamp the texture coordinates to the half-pixel offset from the borders
--- a/gfx/webrender/res/ps_yuv_image.glsl
+++ b/gfx/webrender/res/ps_yuv_image.glsl
@@ -11,12 +11,11 @@ flat varying vec2 vTextureOffsetV; // Of
flat varying vec2 vTextureSizeY; // Size of the y plane in the texture atlas.
flat varying vec2 vTextureSizeUv; // Size of the u and v planes in the texture atlas.
flat varying vec2 vStretchSize;
flat varying vec2 vHalfTexelY; // Normalized length of the half of a Y texel.
flat varying vec2 vHalfTexelUv; // Normalized length of the half of u and v texels.
#ifdef WR_FEATURE_TRANSFORM
varying vec3 vLocalPos;
-flat varying RectWithSize vLocalRect;
#else
varying vec2 vLocalPos;
#endif
--- a/gfx/webrender/res/ps_yuv_image.vs.glsl
+++ b/gfx/webrender/res/ps_yuv_image.vs.glsl
@@ -7,17 +7,16 @@ void main(void) {
Primitive prim = load_primitive();
#ifdef WR_FEATURE_TRANSFORM
TransformVertexInfo vi = write_transform_vertex(prim.local_rect,
prim.local_clip_rect,
prim.z,
prim.layer,
prim.task,
prim.local_rect.p0);
- vLocalRect = prim.local_rect;
vLocalPos = vi.local_pos;
#else
VertexInfo vi = write_vertex(prim.local_rect,
prim.local_clip_rect,
prim.z,
prim.layer,
prim.task,
prim.local_rect.p0);
--- a/gfx/webrender/src/border.rs
+++ b/gfx/webrender/src/border.rs
@@ -1,55 +1,69 @@
/* 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 ellipse::Ellipse;
use frame_builder::FrameBuilder;
-use prim_store::{BorderPrimitiveCpu, BorderPrimitiveGpu, PrimitiveContainer};
+use mask_cache::{ClipSource};
+use prim_store::{BorderPrimitiveCpu, BorderPrimitiveGpu, GpuBlock32, PrimitiveContainer};
use tiling::PrimitiveFlags;
use util::pack_as_float;
use webrender_traits::{BorderSide, BorderStyle, BorderWidths, ClipAndScrollInfo, ClipRegion};
use webrender_traits::{ColorF, LayerPoint, LayerRect, LayerSize, NormalBorder};
-#[derive(Copy, Clone, Debug, PartialEq)]
+enum BorderCorner {
+ TopLeft,
+ TopRight,
+ BottomLeft,
+ BottomRight,
+}
+
+#[derive(Clone, Debug, PartialEq)]
pub enum BorderCornerKind {
None,
Solid,
Clip,
+ Mask(BorderCornerClipData, LayerSize, LayerSize),
Unhandled,
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum BorderEdgeKind {
None,
Solid,
Clip,
Unhandled,
}
-pub trait NormalBorderHelpers {
+trait NormalBorderHelpers {
fn get_corner(&self,
edge0: &BorderSide,
width0: f32,
edge1: &BorderSide,
width1: f32,
- radius: &LayerSize) -> BorderCornerKind;
+ radius: &LayerSize,
+ corner: BorderCorner,
+ border_rect: &LayerRect) -> BorderCornerKind;
fn get_edge(&self,
edge: &BorderSide,
width: f32) -> (BorderEdgeKind, f32);
}
impl NormalBorderHelpers for NormalBorder {
fn get_corner(&self,
edge0: &BorderSide,
width0: f32,
edge1: &BorderSide,
width1: f32,
- radius: &LayerSize) -> BorderCornerKind {
+ radius: &LayerSize,
+ corner: BorderCorner,
+ border_rect: &LayerRect) -> BorderCornerKind {
// If either width is zero, a corner isn't formed.
if width0 == 0.0 || width1 == 0.0 {
return BorderCornerKind::None;
}
// If both edges are transparent, no corner is formed.
if edge0.color.a == 0.0 && edge1.color.a == 0.0 {
return BorderCornerKind::None;
@@ -73,16 +87,55 @@ impl NormalBorderHelpers for NormalBorde
// Inset / outset borders just modtify the color of edges, so can be
// drawn with the normal border corner shader.
(BorderStyle::Outset, BorderStyle::Outset) |
(BorderStyle::Inset, BorderStyle::Inset) |
(BorderStyle::Double, BorderStyle::Double) |
(BorderStyle::Groove, BorderStyle::Groove) |
(BorderStyle::Ridge, BorderStyle::Ridge) => BorderCornerKind::Clip,
+ // Dashed border corners get drawn into a clip mask.
+ (BorderStyle::Dashed, BorderStyle::Dashed) => {
+ let size = LayerSize::new(width0.max(radius.width), width1.max(radius.height));
+ let (origin, clip_center, sign_modifier) = match corner {
+ BorderCorner::TopLeft => {
+ let origin = border_rect.origin;
+ let clip_center = origin + size;
+ (origin, clip_center, LayerPoint::new(-1.0, -1.0))
+ }
+ BorderCorner::TopRight => {
+ let origin = LayerPoint::new(border_rect.origin.x +
+ border_rect.size.width -
+ size.width,
+ border_rect.origin.y);
+ let clip_center = origin + LayerSize::new(0.0, size.height);
+ (origin, clip_center, LayerPoint::new(1.0, -1.0))
+ }
+ BorderCorner::BottomRight => {
+ let origin = border_rect.origin + (border_rect.size - size);
+ let clip_center = origin;
+ (origin, clip_center, LayerPoint::new(1.0, 1.0))
+ }
+ BorderCorner::BottomLeft => {
+ let origin = LayerPoint::new(border_rect.origin.x,
+ border_rect.origin.y +
+ border_rect.size.height -
+ size.height);
+ let clip_center = origin + LayerSize::new(size.width, 0.0);
+ (origin, clip_center, LayerPoint::new(-1.0, 1.0))
+ }
+ };
+ let clip_data = BorderCornerClipData {
+ corner_rect: LayerRect::new(origin, size),
+ clip_center: clip_center,
+ sign_modifier: sign_modifier,
+ };
+ BorderCornerKind::Mask(clip_data, *radius, LayerSize::new(width0, width1))
+ }
+
// Assume complex for these cases.
// TODO(gw): There are some cases in here that can be handled with a fast path.
// For example, with inset/outset borders, two of the four corners are solid.
(BorderStyle::Dotted, _) | (_, BorderStyle::Dotted) => BorderCornerKind::Unhandled,
(BorderStyle::Dashed, _) | (_, BorderStyle::Dashed) => BorderCornerKind::Unhandled,
(BorderStyle::Double, _) | (_, BorderStyle::Double) => BorderCornerKind::Unhandled,
(BorderStyle::Groove, _) | (_, BorderStyle::Groove) => BorderCornerKind::Unhandled,
(BorderStyle::Ridge, _) | (_, BorderStyle::Ridge) => BorderCornerKind::Unhandled,
@@ -103,32 +156,33 @@ impl NormalBorderHelpers for NormalBorde
BorderStyle::Hidden => (BorderEdgeKind::None, 0.0),
BorderStyle::Solid |
BorderStyle::Inset |
BorderStyle::Outset => (BorderEdgeKind::Solid, width),
BorderStyle::Double |
BorderStyle::Groove |
- BorderStyle::Ridge => (BorderEdgeKind::Clip, width),
+ BorderStyle::Ridge |
+ BorderStyle::Dashed => (BorderEdgeKind::Clip, width),
- BorderStyle::Dotted |
- BorderStyle::Dashed => (BorderEdgeKind::Unhandled, width),
+ BorderStyle::Dotted => (BorderEdgeKind::Unhandled, width),
}
}
}
impl FrameBuilder {
fn add_normal_border_primitive(&mut self,
rect: &LayerRect,
border: &NormalBorder,
widths: &BorderWidths,
clip_and_scroll: ClipAndScrollInfo,
clip_region: &ClipRegion,
- use_new_border_path: bool) {
+ use_new_border_path: bool,
+ extra_clips: &[ClipSource]) {
let radius = &border.radius;
let left = &border.left;
let right = &border.right;
let top = &border.top;
let bottom = &border.bottom;
// These colors are used during inset/outset scaling.
let left_color = left.border_color(1.0, 2.0/3.0, 0.3, 0.7);
@@ -158,17 +212,17 @@ impl FrameBuilder {
radius.bottom_right,
radius.bottom_left,
],
};
self.add_primitive(clip_and_scroll,
&rect,
clip_region,
- &[],
+ extra_clips,
PrimitiveContainer::Border(prim_cpu, prim_gpu));
}
// TODO(gw): This allows us to move border types over to the
// simplified shader model one at a time. Once all borders
// are converted, this can be removed, along with the complex
// border code path.
pub fn add_normal_border(&mut self,
@@ -188,30 +242,55 @@ impl FrameBuilder {
let radius = &border.radius;
let left = &border.left;
let right = &border.right;
let top = &border.top;
let bottom = &border.bottom;
let corners = [
- border.get_corner(left, widths.left, top, widths.top, &radius.top_left),
- border.get_corner(top, widths.top, right, widths.right, &radius.top_right),
- border.get_corner(right, widths.right, bottom, widths.bottom, &radius.bottom_right),
- border.get_corner(bottom, widths.bottom, left, widths.left, &radius.bottom_left),
+ border.get_corner(left,
+ widths.left,
+ top,
+ widths.top,
+ &radius.top_left,
+ BorderCorner::TopLeft,
+ rect),
+ border.get_corner(right,
+ widths.right,
+ top,
+ widths.top,
+ &radius.top_right,
+ BorderCorner::TopRight,
+ rect),
+ border.get_corner(right,
+ widths.right,
+ bottom,
+ widths.bottom,
+ &radius.bottom_right,
+ BorderCorner::BottomRight,
+ rect),
+ border.get_corner(left,
+ widths.left,
+ bottom,
+ widths.bottom,
+ &radius.bottom_left,
+ BorderCorner::BottomLeft,
+ rect),
];
// If any of the corners are unhandled, fall back to slow path for now.
if corners.iter().any(|c| *c == BorderCornerKind::Unhandled) {
self.add_normal_border_primitive(rect,
border,
widths,
clip_and_scroll,
clip_region,
- false);
+ false,
+ &[]);
return;
}
let (left_edge, left_len) = border.get_edge(left, widths.left);
let (top_edge, top_len) = border.get_edge(top, widths.top);
let (right_edge, right_len) = border.get_edge(right, widths.right);
let (bottom_edge, bottom_len) = border.get_edge(bottom, widths.bottom);
@@ -224,17 +303,18 @@ impl FrameBuilder {
// If any of the edges are unhandled, fall back to slow path for now.
if edges.iter().any(|e| *e == BorderEdgeKind::Unhandled) {
self.add_normal_border_primitive(rect,
border,
widths,
clip_and_scroll,
clip_region,
- false);
+ false,
+ &[]);
return;
}
// Use a simple rectangle case when all edges and corners are either
// solid or none.
let all_corners_simple = corners.iter().all(|c| {
*c == BorderCornerKind::Solid || *c == BorderCornerKind::None
});
@@ -279,22 +359,35 @@ impl FrameBuilder {
self.add_solid_rectangle(clip_and_scroll,
&LayerRect::new(LayerPoint::new(p0.x, p1.y - bottom_len),
LayerSize::new(rect_width, bottom_len)),
clip_region,
&border.bottom.color,
PrimitiveFlags::None);
}
} else {
+ // Create clip masks for border corners, if required.
+ let mut extra_clips = Vec::new();
+
+ for corner in corners.iter() {
+ if let &BorderCornerKind::Mask(corner_data, corner_radius, widths) = corner {
+ let clip_source = BorderCornerClipSource::new(corner_data,
+ corner_radius,
+ widths);
+ extra_clips.push(ClipSource::BorderCorner(clip_source));
+ }
+ }
+
self.add_normal_border_primitive(rect,
border,
widths,
clip_and_scroll,
clip_region,
- true);
+ true,
+ &extra_clips);
}
}
}
pub trait BorderSideHelpers {
fn border_color(&self,
scale_factor_0: f32,
scale_factor_1: f32,
@@ -322,8 +415,132 @@ impl BorderSideHelpers for BorderSide {
} else {
ColorF::new(black_color_1, black_color_1, black_color_1, self.color.a)
}
}
_ => self.color,
}
}
}
+
+/// The source data for a border corner clip mask.
+#[derive(Debug, Clone)]
+pub struct BorderCornerClipSource {
+ pub corner_data: BorderCornerClipData,
+ pub dash_count: usize,
+ dash_arc_length: f32,
+ ellipse: Ellipse,
+}
+
+impl BorderCornerClipSource {
+ pub fn new(corner_data: BorderCornerClipData,
+ corner_radius: LayerSize,
+ widths: LayerSize) -> BorderCornerClipSource {
+ let ellipse = Ellipse::new(corner_radius);
+
+ // Work out a dash length (and therefore dash count)
+ // based on the width of the border edges. The "correct"
+ // dash length is not mentioned in the CSS borders
+ // spec. The calculation below is similar, but not exactly
+ // the same as what Gecko uses.
+ // TODO(gw): Iterate on this to get it closer to what Gecko
+ // uses for dash length.
+
+ // Approximate the total arc length of the quarter ellipse.
+ let total_arc_length = ellipse.get_quarter_arc_length();
+
+ // The desired dash length is ~3x the border width.
+ let average_border_width = 0.5 * (widths.width + widths.height);
+ let desired_dash_arc_length = average_border_width * 3.0;
+
+ // Get the ideal number of dashes for that arc length.
+ // This is scaled by 0.5 since there is an on/off length
+ // for each dash.
+ let desired_count = 0.5 * total_arc_length / desired_dash_arc_length;
+
+ // Round that up to the nearest integer, so that the dash length
+ // doesn't exceed the ratio above.
+ let actual_count = desired_count.ceil();
+
+ // Get the correct dash arc length.
+ let dash_arc_length = 0.5 * total_arc_length / actual_count;
+
+ // Get the number of dashes we'll need to fit.
+ let dash_count = actual_count as usize;
+
+ BorderCornerClipSource {
+ corner_data: corner_data,
+ dash_count: dash_count,
+ ellipse: ellipse,
+ dash_arc_length: dash_arc_length,
+ }
+ }
+
+ pub fn populate_gpu_data(&self, slice: &mut [GpuBlock32]) {
+ let (header, dashes) = slice.split_first_mut().unwrap();
+ *header = self.corner_data.into();
+
+ let mut current_arc_length = self.dash_arc_length * 0.5;
+ for dash_index in 0..self.dash_count {
+ let arc_length0 = current_arc_length;
+ current_arc_length += self.dash_arc_length;
+
+ let arc_length1 = current_arc_length;
+ current_arc_length += self.dash_arc_length;
+
+ let dash_data = BorderCornerDashClipData::new(arc_length0,
+ arc_length1,
+ &self.ellipse);
+ dashes[dash_index] = dash_data.into();
+ }
+ }
+}
+
+/// Represents the common GPU data for writing a
+/// clip mask for a border corner.
+#[derive(Debug, Copy, Clone, PartialEq)]
+#[repr(C)]
+pub struct BorderCornerClipData {
+ /// Local space rect of the border corner.
+ corner_rect: LayerRect,
+ /// Local space point that is the center of the
+ /// circle or ellipse that we are clipping against.
+ clip_center: LayerPoint,
+ /// A constant that flips the local space points
+ /// and tangents of the ellipse for this specific
+ /// corner. This is used since the ellipse points
+ /// and tangents are always generated for a single
+ /// quadrant only.
+ sign_modifier: LayerPoint,
+}
+
+/// Represents the GPU data for drawing a single dash
+/// to a clip mask. A dash clip is defined by two lines.
+/// We store a point on the ellipse curve, and a tangent
+/// to that point, which allows for efficient line-distance
+/// calculations in the fragment shader.
+#[derive(Debug, Clone)]
+#[repr(C)]
+pub struct BorderCornerDashClipData {
+ pub point0: LayerPoint,
+ pub tangent0: LayerPoint,
+ pub point1: LayerPoint,
+ pub tangent1: LayerPoint,
+}
+
+impl BorderCornerDashClipData {
+ pub fn new(arc_length0: f32,
+ arc_length1: f32,
+ ellipse: &Ellipse) -> BorderCornerDashClipData {
+ let alpha = ellipse.find_angle_for_arc_length(arc_length0);
+ let beta = ellipse.find_angle_for_arc_length(arc_length1);
+
+ let (p0, t0) = ellipse.get_point_and_tangent(alpha);
+ let (p1, t1) = ellipse.get_point_and_tangent(beta);
+
+ BorderCornerDashClipData {
+ point0: p0,
+ tangent0: t0,
+ point1: p1,
+ tangent1: t1,
+ }
+ }
+}
--- a/gfx/webrender/src/clip_scroll_node.rs
+++ b/gfx/webrender/src/clip_scroll_node.rs
@@ -266,16 +266,21 @@ impl ClipScrollNode {
self.combined_local_viewport_rect = match self.node_type {
NodeType::Clip(_) => {
parent_combined_viewport_in_local_space.intersection(&self.local_clip_rect)
.unwrap_or(LayerRect::zero())
}
NodeType::ReferenceFrame(_) => parent_combined_viewport_in_local_space,
};
+ // HACK: prevent the code above for non-AA transforms, it's incorrect.
+ if (local_transform.m13, local_transform.m23) != (0.0, 0.0) {
+ self.combined_local_viewport_rect = self.local_clip_rect;
+ }
+
// The transformation for this viewport in world coordinates is the transformation for
// our parent reference frame, plus any accumulated scrolling offsets from nodes
// between our reference frame and this node. For reference frames, we also include
// whatever local transformation this reference frame provides. This can be combined
// with the local_viewport_rect to get its position in world space.
self.world_viewport_transform =
parent_reference_frame_transform
.pre_translated(parent_accumulated_scroll_offset.x,
--- a/gfx/webrender/src/device.rs
+++ b/gfx/webrender/src/device.rs
@@ -1608,16 +1608,21 @@ impl Device {
self.gl.uniform_1i(u_resource_rects, TextureSampler::ResourceRects as i32);
}
let u_gradients = self.gl.get_uniform_location(program.id, "sGradients");
if u_gradients != -1 {
self.gl.uniform_1i(u_gradients, TextureSampler::Gradients as i32);
}
+ let u_split_geometry = self.gl.get_uniform_location(program.id, "sSplitGeometry");
+ if u_split_geometry != -1 {
+ self.gl.uniform_1i(u_split_geometry, TextureSampler::SplitGeometry as i32);
+ }
+
Ok(())
}
/*
pub fn refresh_shader(&mut self, path: PathBuf) {
let mut vs_preamble_path = self.resource_path.clone();
vs_preamble_path.push(VERTEX_SHADER_PREAMBLE);
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/src/ellipse.rs
@@ -0,0 +1,94 @@
+/* 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 webrender_traits::{LayerPoint, LayerSize};
+use std::f32::consts::FRAC_PI_2;
+
+/// Number of steps to integrate arc length over.
+const STEP_COUNT: usize = 20;
+
+/// Represents an ellipse centred at a local space origin.
+#[derive(Debug, Clone)]
+pub struct Ellipse {
+ pub radius: LayerSize,
+}
+
+impl Ellipse {
+ pub fn new(radius: LayerSize) -> Ellipse {
+ Ellipse {
+ radius: radius,
+ }
+ }
+
+ /// Use Simpsons rule to approximate the arc length of
+ /// part of an ellipse. Note that this only works over
+ /// the range of [0, pi/2].
+ // TODO(gw): This is a simplistic way to estimate the
+ // arc length of an ellipse segment. We can probably use
+ // a faster / more accurate method!
+ fn get_simpson_length(&self, theta: f32) -> f32 {
+ let df = theta / STEP_COUNT as f32;
+ let mut sum = 0.0;
+
+ for i in 0..(STEP_COUNT+1) {
+ let (sin_theta, cos_theta) = (i as f32 * df).sin_cos();
+ let a = self.radius.width * sin_theta;
+ let b = self.radius.height * cos_theta;
+ let y = (a*a + b*b).sqrt();
+ let q = if i == 0 || i == STEP_COUNT {
+ 1.0
+ } else if i % 2 == 0 {
+ 2.0
+ } else {
+ 4.0
+ };
+
+ sum += q * y;
+ }
+
+ (df / 3.0) * sum
+ }
+
+ /// Binary search to estimate the angle of an ellipse
+ /// for a given arc length. This only searches over the
+ /// first quadrant of an ellipse.
+ pub fn find_angle_for_arc_length(&self, arc_length: f32) -> f32 {
+ let epsilon = 0.01;
+ let mut low = 0.0;
+ let mut high = FRAC_PI_2;
+ let mut theta = 0.0;
+
+ while low <= high {
+ theta = 0.5 * (low + high);
+ let length = self.get_simpson_length(theta);
+
+ if (length - arc_length).abs() < epsilon {
+ break;
+ } else if length < arc_length {
+ low = theta;
+ } else {
+ high = theta;
+ }
+ }
+
+ theta
+ }
+
+ /// Approximate the total length of the first quadrant of
+ /// this ellipse.
+ pub fn get_quarter_arc_length(&self) -> f32 {
+ self.get_simpson_length(FRAC_PI_2)
+ }
+
+ /// Get a point and tangent on this ellipse from a given angle.
+ /// This only works for the first quadrant of the ellipse.
+ pub fn get_point_and_tangent(&self, theta: f32) -> (LayerPoint, LayerPoint) {
+ let (sin_theta, cos_theta) = theta.sin_cos();
+ let point = LayerPoint::new(self.radius.width * cos_theta,
+ self.radius.height * sin_theta);
+ let tangent = LayerPoint::new(-self.radius.width * sin_theta,
+ self.radius.height * cos_theta);
+ (point, tangent)
+ }
+}
--- a/gfx/webrender/src/frame.rs
+++ b/gfx/webrender/src/frame.rs
@@ -56,17 +56,16 @@ impl<'a> FlattenContext<'a> {
}
}
}
// TODO: doc
pub struct Frame {
pub clip_scroll_tree: ClipScrollTree,
pub pipeline_epoch_map: HashMap<PipelineId, Epoch, BuildHasherDefault<FnvHasher>>,
- pub pipeline_auxiliary_lists: AuxiliaryListsMap,
id: FrameId,
frame_builder_config: FrameBuilderConfig,
frame_builder: Option<FrameBuilder>,
}
trait DisplayListHelpers {
fn starting_stacking_context<'a>(&'a self) -> Option<(&'a StackingContext, &'a LayoutRect)>;
}
@@ -221,17 +220,16 @@ fn clip_intersection(original_rect: &Lay
})
})
}
impl Frame {
pub fn new(config: FrameBuilderConfig) -> Frame {
Frame {
pipeline_epoch_map: HashMap::default(),
- pipeline_auxiliary_lists: HashMap::default(),
clip_scroll_tree: ClipScrollTree::new(),
id: FrameId(0),
frame_builder: None,
frame_builder_config: config,
}
}
pub fn reset(&mut self) -> ScrollStates {
@@ -291,17 +289,16 @@ impl Frame {
None => return,
};
if window_size.width == 0 || window_size.height == 0 {
error!("ERROR: Invalid window dimensions! Please call api.set_window_size()");
}
let old_scrolling_states = self.reset();
- self.pipeline_auxiliary_lists = scene.pipeline_auxiliary_lists.clone();
self.pipeline_epoch_map.insert(root_pipeline_id, root_pipeline.epoch);
let (root_stacking_context, root_bounds) = match display_list.starting_stacking_context() {
Some(some) => some,
None => {
warn!("Pipeline display list does not start with a stacking context.");
return;
@@ -376,19 +373,19 @@ impl Frame {
stacking_context: &StackingContext) {
// Avoid doing unnecessary work for empty stacking contexts.
if traversal.current_stacking_context_empty() {
traversal.skip_current_stacking_context();
return;
}
let composition_operations = {
- let auxiliary_lists = self.pipeline_auxiliary_lists
- .get(&pipeline_id)
- .expect("No auxiliary lists?!");
+ let auxiliary_lists = context.scene.pipeline_auxiliary_lists
+ .get(&pipeline_id)
+ .expect("No auxiliary lists?!");
CompositeOps::new(
stacking_context.filter_ops_for_compositing(auxiliary_lists, &context.scene.properties),
stacking_context.mix_blend_mode_for_compositing())
};
if composition_operations.will_make_invisible() {
traversal.skip_current_stacking_context();
return;
@@ -432,16 +429,17 @@ impl Frame {
reference_frame_relative_offset.y + bounds.origin.y);
}
// TODO(gw): Int with overflow etc
context.builder.push_stacking_context(&reference_frame_relative_offset,
pipeline_id,
level == 0,
composition_operations,
+ *bounds,
stacking_context.transform_style);
// For the root pipeline, there's no need to add a full screen rectangle
// here, as it's handled by the framebuffer clear.
if level == 0 && context.scene.root_pipeline_id.unwrap() != pipeline_id {
if let Some(pipeline) = context.scene.pipeline_map.get(&pipeline_id) {
if let Some(bg_color) = pipeline.background_color {
// Note: we don't use the original clip region here,
@@ -604,19 +602,19 @@ impl Frame {
text_info.font_key,
text_info.size,
text_info.blur_radius,
&text_info.color,
text_info.glyphs,
text_info.glyph_options);
}
SpecificDisplayItem::Rectangle(ref info) => {
- let auxiliary_lists = self.pipeline_auxiliary_lists
- .get(&pipeline_id)
- .expect("No auxiliary lists?!");
+ let auxiliary_lists = context.scene.pipeline_auxiliary_lists
+ .get(&pipeline_id)
+ .expect("No auxiliary lists?!");
// Try to extract the opaque inner rectangle out of the clipped primitive.
if let Some(opaque_rect) = clip_intersection(&item.rect, &item.clip, auxiliary_lists) {
let mut results = Vec::new();
subtract_rect(&item.rect, &opaque_rect, &mut results);
// The inner rectangle is considered opaque within this layer.
// It may still inherit some masking from the clip stack.
context.builder.add_solid_rectangle(clip_and_scroll,
&opaque_rect,
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -2,41 +2,42 @@
* 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 app_units::Au;
use frame::FrameId;
use gpu_store::GpuStoreAddress;
use internal_types::{HardwareCompositeOp, SourceTexture};
use mask_cache::{ClipMode, ClipSource, MaskCacheInfo, RegionMode};
+use plane_split::{BspSplitter, Polygon, Splitter};
use prim_store::{GradientPrimitiveCpu, GradientPrimitiveGpu, ImagePrimitiveCpu, ImagePrimitiveGpu};
use prim_store::{ImagePrimitiveKind, PrimitiveContainer, PrimitiveGeometry, PrimitiveIndex};
use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu, RadialGradientPrimitiveGpu};
-use prim_store::{RectanglePrimitive, TextRunPrimitiveCpu, TextRunPrimitiveGpu};
+use prim_store::{RectanglePrimitive, SplitGeometry, TextRunPrimitiveCpu, TextRunPrimitiveGpu};
use prim_store::{BoxShadowPrimitiveGpu, TexelRect, YuvImagePrimitiveCpu, YuvImagePrimitiveGpu};
use profiler::{FrameProfileCounters, TextureCacheProfileCounters};
use render_task::{AlphaRenderItem, MaskCacheKey, MaskResult, RenderTask, RenderTaskIndex};
use render_task::RenderTaskLocation;
use resource_cache::ResourceCache;
use clip_scroll_node::{ClipInfo, ClipScrollNode, NodeType};
use clip_scroll_tree::ClipScrollTree;
use std::{cmp, f32, i32, mem, usize};
-use euclid::SideOffsets2D;
-use tiling::StackingContextIndex;
+use euclid::{SideOffsets2D, TypedPoint3D};
+use tiling::{ContextIsolation, StackingContextIndex};
use tiling::{AuxiliaryListsMap, ClipScrollGroup, ClipScrollGroupIndex, CompositeOps, Frame};
use tiling::{PackedLayer, PackedLayerIndex, PrimitiveFlags, PrimitiveRunCmd, RenderPass};
use tiling::{RenderTargetContext, RenderTaskCollection, ScrollbarPrimitive, StackingContext};
use util::{self, pack_as_float, subtract_rect, recycle_vec};
use util::RectHelpers;
use webrender_traits::{BorderDetails, BorderDisplayItem, BoxShadowClipMode, ClipAndScrollInfo};
use webrender_traits::{ClipId, ClipRegion, ColorF, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
use webrender_traits::{DeviceUintRect, DeviceUintSize, ExtendMode, FontKey, FontRenderMode};
use webrender_traits::{GlyphOptions, ImageKey, ImageRendering, ItemRange, LayerPoint, LayerRect};
use webrender_traits::{LayerSize, LayerToScrollTransform, PipelineId, RepeatMode, TileOffset};
-use webrender_traits::{TransformStyle, WebGLContextId, YuvColorSpace, YuvData};
+use webrender_traits::{TransformStyle, WebGLContextId, WorldPixel, YuvColorSpace, YuvData};
#[derive(Debug, Clone)]
struct ImageBorderSegment {
geom_rect: LayerRect,
sub_rect: TexelRect,
stretch_size: LayerSize,
tile_spacing: LayerSize,
}
@@ -76,16 +77,26 @@ impl ImageBorderSegment {
geom_rect: rect,
sub_rect: sub_rect,
stretch_size: LayerSize::new(stretch_size_x, stretch_size_y),
tile_spacing: tile_spacing,
}
}
}
+/// Construct a polygon from stacking context boundaries.
+/// `anchor` here is an index that's going to be preserved in all the
+/// splits of the polygon.
+fn make_polygon(sc: &StackingContext, node: &ClipScrollNode, anchor: usize)
+ -> Polygon<f32, WorldPixel> {
+ let mut bounds = sc.local_bounds;
+ bounds.origin = bounds.origin + sc.reference_frame_offset;
+ Polygon::from_transformed_rect(bounds, node.world_content_transform, anchor)
+}
+
#[derive(Clone, Copy)]
pub struct FrameBuilderConfig {
pub enable_scrollbars: bool,
pub enable_subpixel_aa: bool,
pub debug: bool,
}
impl FrameBuilderConfig {
@@ -238,32 +249,40 @@ impl FrameBuilder {
ClipScrollGroupIndex(self.clip_scroll_group_store.len() - 1, info)
}
pub fn push_stacking_context(&mut self,
reference_frame_offset: &LayerPoint,
pipeline_id: PipelineId,
is_page_root: bool,
composite_ops: CompositeOps,
+ local_bounds: LayerRect,
transform_style: TransformStyle) {
if let Some(parent_index) = self.stacking_context_stack.last() {
let parent_is_root = self.stacking_context_store[parent_index.0].is_page_root;
if composite_ops.mix_blend_mode.is_some() && !parent_is_root {
// the parent stacking context of a stacking context with mix-blend-mode
// must be drawn with a transparent background, unless the parent stacking context
// is the root of the page
- self.stacking_context_store[parent_index.0].should_isolate = true;
+ let isolation = &mut self.stacking_context_store[parent_index.0].isolation;
+ if *isolation != ContextIsolation::None {
+ error!("Isolation conflict detected on {:?}: {:?}", parent_index, *isolation);
+ }
+ *isolation = ContextIsolation::Full;
}
}
let stacking_context_index = StackingContextIndex(self.stacking_context_store.len());
+ let reference_frame_id = self.current_reference_frame_id();
self.stacking_context_store.push(StackingContext::new(pipeline_id,
*reference_frame_offset,
is_page_root,
+ reference_frame_id,
+ local_bounds,
transform_style,
composite_ops));
self.cmds.push(PrimitiveRunCmd::PushStackingContext(stacking_context_index));
self.stacking_context_stack.push(stacking_context_index);
}
pub fn pop_stacking_context(&mut self) {
self.cmds.push(PrimitiveRunCmd::PopStackingContext);
@@ -1099,73 +1118,131 @@ impl FrameBuilder {
} else {
None
};
self.prim_store.set_clip_source(scrollbar_prim.prim_index, clip_source);
*self.prim_store.gpu_geometry.get_mut(GpuStoreAddress(scrollbar_prim.prim_index.0 as i32)) = geom;
}
}
- fn build_render_task(&self) -> (RenderTask, usize) {
+ fn build_render_task(&mut self, clip_scroll_tree: &ClipScrollTree)
+ -> (RenderTask, usize) {
profile_scope!("build_render_task");
let mut next_z = 0;
let mut next_task_index = RenderTaskIndex(0);
let mut sc_stack = Vec::new();
let mut current_task = RenderTask::new_alpha_batch(next_task_index,
DeviceIntPoint::zero(),
RenderTaskLocation::Fixed);
next_task_index.0 += 1;
+ // A stack of the alpha batcher tasks. We create them on the way down,
+ // and then actually populate with items and dependencies on the way up.
let mut alpha_task_stack = Vec::new();
+ // The stack of "preserve-3d" contexts. We are baking these into render targets
+ // and onlu compositing once we are out of "preserve-3d" hierarchy.
+ // That is why we stack those contexts.
+ let mut preserve_3d_stack = Vec::new();
+ // The plane splitter, using a simple BSP tree.
+ let mut splitter = BspSplitter::new();
+
+ self.prim_store.gpu_split_geometry.clear();
for cmd in &self.cmds {
match *cmd {
PrimitiveRunCmd::PushStackingContext(stacking_context_index) => {
let stacking_context = &self.stacking_context_store[stacking_context_index.0];
sc_stack.push(stacking_context_index);
if !stacking_context.is_visible {
continue;
}
- let stacking_context_rect = &stacking_context.bounding_rect;
+ let stacking_context_rect = &stacking_context.screen_bounds;
let composite_count = stacking_context.composite_ops.count();
- if composite_count == 0 && stacking_context.should_isolate {
- let location = RenderTaskLocation::Dynamic(None, stacking_context_rect.size);
- let new_task = RenderTask::new_alpha_batch(next_task_index,
- stacking_context_rect.origin,
- location);
+ let new_task = match stacking_context.isolation {
+ ContextIsolation::Items => Some(
+ RenderTask::new_dynamic_alpha_batch(next_task_index, stacking_context_rect)
+ ),
+ ContextIsolation::Full if composite_count == 0 => Some(
+ RenderTask::new_dynamic_alpha_batch(next_task_index, stacking_context_rect)
+ ),
+ ContextIsolation::Full | ContextIsolation::None => None,
+ };
+
+ if let Some(task) = new_task {
next_task_index.0 += 1;
- let prev_task = mem::replace(&mut current_task, new_task);
+ let prev_task = mem::replace(&mut current_task, task);
alpha_task_stack.push(prev_task);
}
for _ in 0..composite_count {
- let location = RenderTaskLocation::Dynamic(None, stacking_context_rect.size);
- let new_task = RenderTask::new_alpha_batch(next_task_index,
- stacking_context_rect.origin,
- location);
+ let new_task = RenderTask::new_dynamic_alpha_batch(next_task_index,
+ stacking_context_rect);
next_task_index.0 += 1;
let prev_task = mem::replace(&mut current_task, new_task);
alpha_task_stack.push(prev_task);
}
}
PrimitiveRunCmd::PopStackingContext => {
let stacking_context_index = sc_stack.pop().unwrap();
let stacking_context = &self.stacking_context_store[stacking_context_index.0];
if !stacking_context.is_visible {
continue;
}
+ // Handle the `Item` isolation type first. Once we are out of an isolated
+ // sub-tree of stacking contexts, we do plane splitting and compositing.
+ match stacking_context.isolation {
+ ContextIsolation::Items => {
+ let prev_task = alpha_task_stack.pop().unwrap();
+ let mut old_current = mem::replace(&mut current_task, prev_task);
+ // If there are any items or non-preserve-3d sub-contexts, we have the
+ // contents to bake and plane split. Note: if the `old_current` is also preserve-3d,
+ // means we are inside the `ContextIsolation::Items` sub-tree, which only
+ // gets added as task dependencies on exit (see the next match arm).
+ if !old_current.as_alpha_batch().items.is_empty() || !old_current.children.is_empty() {
+ let stacking_context = &self.stacking_context_store[stacking_context_index.0];
+ let scroll_node = clip_scroll_tree.nodes.get(&stacking_context.reference_frame_id).unwrap();
+ let sc_polygon = make_polygon(stacking_context, scroll_node, preserve_3d_stack.len());
+ splitter.add(sc_polygon);
+ preserve_3d_stack.push((stacking_context_index, old_current));
+ } else if !old_current.children.is_empty() {
+ current_task.children.push(old_current);
+ }
+ },
+ ContextIsolation::None | ContextIsolation::Full => {
+ // We are back from a "preserve-3d" sub-domain.
+ // Time to split those stacking context planes.
+ current_task.children.extend(preserve_3d_stack.iter().map(|&(_, ref task)| task.clone()));
+ for poly in splitter.sort(TypedPoint3D::new(0.0, 0.0, -1.0)) {
+ let (sc_index, ref task) = preserve_3d_stack[poly.anchor];
+ let pp = &poly.points;
+ let split_geo = SplitGeometry {
+ data: [pp[0].x, pp[0].y, pp[0].z,
+ pp[1].x, pp[1].y, pp[1].z,
+ pp[2].x, pp[2].y, pp[2].z,
+ pp[3].x, pp[3].y, pp[3].z],
+ };
+ let gpu_index = self.prim_store.gpu_split_geometry.push(split_geo);
+ let item = AlphaRenderItem::SplitComposite(sc_index, task.id, gpu_index, next_z);
+ current_task.as_alpha_batch().items.push(item);
+ }
+ splitter.reset();
+ preserve_3d_stack.clear();
+ next_z += 1;
+ },
+ }
+
let composite_count = stacking_context.composite_ops.count();
- if composite_count == 0 && stacking_context.should_isolate {
+ if composite_count == 0 && stacking_context.isolation == ContextIsolation::Full {
let mut prev_task = alpha_task_stack.pop().unwrap();
let item = AlphaRenderItem::HardwareComposite(stacking_context_index,
current_task.id,
HardwareCompositeOp::PremultipliedAlpha,
next_z);
next_z += 1;
prev_task.as_alpha_batch().items.push(item);
prev_task.children.push(current_task);
@@ -1178,20 +1255,21 @@ impl FrameBuilder {
current_task.id,
*filter,
next_z);
next_z += 1;
prev_task.as_alpha_batch().items.push(item);
prev_task.children.push(current_task);
current_task = prev_task;
}
+
if let Some(mix_blend_mode) = stacking_context.composite_ops.mix_blend_mode {
let readback_task =
RenderTask::new_readback(stacking_context_index,
- stacking_context.bounding_rect);
+ stacking_context.screen_bounds);
let mut prev_task = alpha_task_stack.pop().unwrap();
let item = AlphaRenderItem::Composite(stacking_context_index,
readback_task.id,
current_task.id,
mix_blend_mode,
next_z);
next_z += 1;
@@ -1225,26 +1303,27 @@ impl FrameBuilder {
// Add any dynamic render tasks needed to render this primitive
if let Some(ref render_task) = prim_metadata.render_task {
current_task.children.push(render_task.clone());
}
if let Some(ref clip_task) = prim_metadata.clip_task {
current_task.children.push(clip_task.clone());
}
- let item = AlphaRenderItem::Primitive(group_index, prim_index, next_z);
+ let item = AlphaRenderItem::Primitive(Some(group_index), prim_index, next_z);
current_task.as_alpha_batch().items.push(item);
next_z += 1;
}
}
}
}
}
debug_assert!(alpha_task_stack.is_empty());
+ debug_assert!(preserve_3d_stack.is_empty());
(current_task, next_task_index.0)
}
pub fn build(&mut self,
resource_cache: &mut ResourceCache,
frame_id: FrameId,
clip_scroll_tree: &mut ClipScrollTree,
auxiliary_lists_map: &AuxiliaryListsMap,
@@ -1274,17 +1353,17 @@ impl FrameBuilder {
self.build_layer_screen_rects_and_cull_layers(&screen_rect,
clip_scroll_tree,
auxiliary_lists_map,
resource_cache,
&mut profile_counters,
device_pixel_ratio);
- let (main_render_task, static_render_task_count) = self.build_render_task();
+ let (main_render_task, static_render_task_count) = self.build_render_task(clip_scroll_tree);
let mut render_tasks = RenderTaskCollection::new(static_render_task_count);
let mut required_pass_count = 0;
main_render_task.max_depth(0, &mut required_pass_count);
resource_cache.block_until_all_resources_added(texture_cache_profile);
for node in clip_scroll_tree.nodes.values() {
@@ -1337,16 +1416,17 @@ impl FrameBuilder {
layer_texture_data: self.packed_layers.clone(),
render_task_data: render_tasks.render_task_data,
gpu_data16: self.prim_store.gpu_data16.build(),
gpu_data32: self.prim_store.gpu_data32.build(),
gpu_data64: self.prim_store.gpu_data64.build(),
gpu_data128: self.prim_store.gpu_data128.build(),
gpu_geometry: self.prim_store.gpu_geometry.build(),
gpu_gradient_data: self.prim_store.gpu_gradient_data.build(),
+ gpu_split_geometry: self.prim_store.gpu_split_geometry.build(),
gpu_resource_rects: self.prim_store.gpu_resource_rects.build(),
deferred_resolves: deferred_resolves,
}
}
}
struct LayerRectCalculationAndCullingPass<'a> {
@@ -1515,25 +1595,25 @@ impl<'a> LayerRectCalculationAndCullingP
}
fn handle_pop_stacking_context(&mut self) {
let stacking_context_index = self.stacking_context_stack.pop().unwrap();
let (bounding_rect, is_visible) = {
let stacking_context =
&mut self.frame_builder.stacking_context_store[stacking_context_index.0];
- stacking_context.bounding_rect = stacking_context.bounding_rect
+ stacking_context.screen_bounds = stacking_context.screen_bounds
.intersection(self.screen_rect)
.unwrap_or(DeviceIntRect::zero());
- (stacking_context.bounding_rect.clone(), stacking_context.is_visible)
+ (stacking_context.screen_bounds.clone(), stacking_context.is_visible)
};
if let Some(ref mut parent_index) = self.stacking_context_stack.last_mut() {
let parent = &mut self.frame_builder.stacking_context_store[parent_index.0];
- parent.bounding_rect = parent.bounding_rect.union(&bounding_rect);
+ parent.screen_bounds = parent.screen_bounds.union(&bounding_rect);
// The previous compute_stacking_context_visibility pass did not take into
// account visibility of children, so we do that now.
parent.is_visible = parent.is_visible || is_visible;
}
}
fn handle_push_stacking_context(&mut self, stacking_context_index: StackingContextIndex) {
@@ -1541,17 +1621,17 @@ impl<'a> LayerRectCalculationAndCullingP
// Reset bounding rect to zero. We will calculate it as we collect primitives
// from various scroll layers. In handle_pop_stacking_context , we use this to
// calculate the device bounding rect. In the future, we could cache this during
// the initial adding of items for the common case (where there is only a single
// scroll layer for items in a stacking context).
let stacking_context = &mut self.frame_builder
.stacking_context_store[stacking_context_index.0];
- stacking_context.bounding_rect = DeviceIntRect::zero();
+ stacking_context.screen_bounds = DeviceIntRect::zero();
}
fn rebuild_clip_info_stack_if_necessary(&mut self, id: ClipId) -> Option<DeviceIntRect> {
if let Some((current_scroll_id, bounding_rect)) = self.current_clip_info {
if current_scroll_id == id {
return bounding_rect;
}
}
@@ -1641,18 +1721,18 @@ impl<'a> LayerRectCalculationAndCullingP
Some(rect) => rect,
_ => continue,
};
let prim_metadata = &mut self.frame_builder.prim_store.cpu_metadata[prim_index.0];
let prim_clip_info = prim_metadata.clip_cache_info.as_ref();
let mut visible = true;
- stacking_context.bounding_rect =
- stacking_context.bounding_rect.union(&prim_bounding_rect);
+ stacking_context.screen_bounds =
+ stacking_context.screen_bounds.union(&prim_bounding_rect);
if let Some(info) = prim_clip_info {
self.current_clip_stack.push((packed_layer_index, info.clone()));
}
// Try to create a mask if we may need to.
if !self.current_clip_stack.is_empty() {
// If the primitive doesn't have a specific clip, key the task ID off the
--- a/gfx/webrender/src/gpu_store.rs
+++ b/gfx/webrender/src/gpu_store.rs
@@ -134,16 +134,20 @@ impl<T: Clone + Default, L: GpuStoreLayo
pub fn get_slice_mut(&mut self,
address: GpuStoreAddress,
count: usize) -> &mut [T] {
let offset = address.0 as usize;
&mut self.data[offset..offset + count]
}
+ pub fn clear(&mut self) {
+ self.data.clear()
+ }
+
// TODO(gw): Implement incremental updates of
// GPU backed data, and support freelist for removing
// dynamic items.
/*
pub fn free(&mut self, address: GpuStoreAddress) {
}
--- a/gfx/webrender/src/internal_types.rs
+++ b/gfx/webrender/src/internal_types.rs
@@ -71,16 +71,17 @@ pub enum TextureSampler {
Data32,
Data64,
Data128,
Layers,
RenderTasks,
Geometry,
ResourceRects,
Gradients,
+ SplitGeometry,
Dither,
}
impl TextureSampler {
pub fn color(n: usize) -> TextureSampler {
match n {
0 => TextureSampler::Color0,
1 => TextureSampler::Color1,
--- a/gfx/webrender/src/lib.rs
+++ b/gfx/webrender/src/lib.rs
@@ -47,16 +47,17 @@ extern crate thread_profiler;
mod border;
mod clip_scroll_node;
mod clip_scroll_tree;
mod debug_colors;
mod debug_font_data;
mod debug_render;
mod device;
+mod ellipse;
mod frame;
mod frame_builder;
mod freelist;
mod geometry;
mod gpu_store;
mod internal_types;
mod mask_cache;
mod prim_store;
@@ -127,14 +128,15 @@ extern crate gleam;
extern crate num_traits;
//extern crate notify;
extern crate time;
extern crate webrender_traits;
#[cfg(feature = "webgl")]
extern crate offscreen_gl_context;
extern crate byteorder;
extern crate threadpool;
+extern crate plane_split;
#[cfg(any(target_os="macos", target_os="windows"))]
extern crate gamma_lut;
pub use renderer::{ExternalImage, ExternalImageSource, ExternalImageHandler};
pub use renderer::{Renderer, RendererOptions};
--- a/gfx/webrender/src/mask_cache.rs
+++ b/gfx/webrender/src/mask_cache.rs
@@ -1,12 +1,13 @@
/* 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 border::BorderCornerClipSource;
use gpu_store::GpuStoreAddress;
use prim_store::{ClipData, GpuBlock32, PrimitiveStore};
use prim_store::{CLIP_DATA_GPU_SIZE, MASK_DATA_GPU_SIZE};
use renderer::VertexDataStore;
use util::{ComplexClipRegionHelpers, MatrixHelpers, TransformedRect};
use webrender_traits::{AuxiliaryLists, BorderRadius, ClipRegion, ComplexClipRegion, ImageMask};
use webrender_traits::{DeviceIntRect, LayerToWorldTransform};
use webrender_traits::{LayerRect, LayerPoint, LayerSize};
@@ -42,22 +43,29 @@ pub enum RegionMode {
#[derive(Clone, Debug)]
pub enum ClipSource {
Complex(LayerRect, f32, ClipMode),
// The RegionMode here specifies whether to consider the rect
// from the clip region as part of the mask. This is true
// for clip/scroll nodes, but false for primitives, where
// the clip rect is handled in local space.
Region(ClipRegion, RegionMode),
+
+ // TODO(gw): This currently only handles dashed style
+ // clips, where the border style is dashed for both
+ // adjacent border edges. Expand to handle dotted style
+ // and different styles per edge.
+ BorderCorner(BorderCornerClipSource),
}
impl ClipSource {
pub fn image_mask(&self) -> Option<ImageMask> {
match *self {
- ClipSource::Complex(..) => None,
+ ClipSource::Complex(..) |
+ ClipSource::BorderCorner{..} => None,
ClipSource::Region(ref region, _) => region.image_mask,
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct ClipAddressRange {
pub start: GpuStoreAddress,
@@ -107,70 +115,79 @@ pub enum MaskBounds {
/// We can't determine the bounds - draw mask over entire rect.
/// This is currently used for clip-out operations on
/// box shadows.
None,
}
#[derive(Clone, Debug)]
pub struct MaskCacheInfo {
- pub clip_range: ClipAddressRange,
- pub effective_clip_count: usize,
+ pub complex_clip_range: ClipAddressRange,
+ pub effective_complex_clip_count: usize,
pub image: Option<(ImageMask, GpuStoreAddress)>,
+ pub border_corners: Vec<(BorderCornerClipSource, GpuStoreAddress)>,
pub bounds: Option<MaskBounds>,
pub is_aligned: bool,
}
impl MaskCacheInfo {
/// Create a new mask cache info. It allocates the GPU store data but leaves
/// it unitialized for the following `update()` call to deal with.
pub fn new(clips: &[ClipSource],
clip_store: &mut VertexDataStore<GpuBlock32>)
-> Option<MaskCacheInfo> {
if clips.is_empty() {
return None;
}
let mut image = None;
- let mut clip_count = 0;
+ let mut border_corners = Vec::new();
+ let mut complex_clip_count = 0;
// Work out how much clip data space we need to allocate
// and if we have an image mask.
for clip in clips {
match *clip {
ClipSource::Complex(..) => {
- clip_count += 1;
- },
+ complex_clip_count += 1;
+ }
ClipSource::Region(ref region, region_mode) => {
if let Some(info) = region.image_mask {
debug_assert!(image.is_none()); // TODO(gw): Support >1 image mask!
image = Some((info, clip_store.alloc(MASK_DATA_GPU_SIZE)));
}
- clip_count += region.complex.length;
+ complex_clip_count += region.complex.length;
if region_mode == RegionMode::IncludeRect {
- clip_count += 1;
+ complex_clip_count += 1;
}
- },
+ }
+ ClipSource::BorderCorner(ref source) => {
+ // One block for the corner header, plus one
+ // block per dash to clip out.
+ let gpu_address = clip_store.alloc(1 + source.dash_count);
+ border_corners.push((source.clone(), gpu_address));
+ }
}
}
- let clip_range = ClipAddressRange {
- start: if clip_count > 0 {
- clip_store.alloc(CLIP_DATA_GPU_SIZE * clip_count)
+ let complex_clip_range = ClipAddressRange {
+ start: if complex_clip_count > 0 {
+ clip_store.alloc(CLIP_DATA_GPU_SIZE * complex_clip_count)
} else {
GpuStoreAddress(0)
},
- item_count: clip_count,
+ item_count: complex_clip_count,
};
Some(MaskCacheInfo {
- clip_range: clip_range,
- effective_clip_count: clip_range.item_count,
+ complex_clip_range: complex_clip_range,
+ effective_complex_clip_count: complex_clip_range.item_count,
image: image,
+ border_corners: border_corners,
bounds: None,
is_aligned: true,
})
}
pub fn update(&mut self,
sources: &[ClipSource],
transform: &LayerToWorldTransform,
@@ -181,31 +198,32 @@ impl MaskCacheInfo {
// If we haven't cached this info, or if the transform type has changed
// we need to re-calculate the number of clips.
if self.bounds.is_none() || self.is_aligned != is_aligned {
let mut local_rect = Some(LayerRect::new(LayerPoint::new(-MAX_CLIP, -MAX_CLIP),
LayerSize::new(2.0 * MAX_CLIP, 2.0 * MAX_CLIP)));
let mut local_inner: Option<LayerRect> = None;
let mut has_clip_out = false;
+ let mut has_border_clip = false;
- self.effective_clip_count = 0;
+ self.effective_complex_clip_count = 0;
self.is_aligned = is_aligned;
for source in sources {
match *source {
ClipSource::Complex(rect, radius, mode) => {
// Once we encounter a clip-out, we just assume the worst
// case clip mask size, for now.
if mode == ClipMode::ClipOut {
has_clip_out = true;
}
- debug_assert!(self.effective_clip_count < self.clip_range.item_count);
- let address = self.clip_range.start + self.effective_clip_count * CLIP_DATA_GPU_SIZE;
- self.effective_clip_count += 1;
+ debug_assert!(self.effective_complex_clip_count < self.complex_clip_range.item_count);
+ let address = self.complex_clip_range.start + self.effective_complex_clip_count * CLIP_DATA_GPU_SIZE;
+ self.effective_complex_clip_count += 1;
let slice = clip_store.get_slice_mut(address, CLIP_DATA_GPU_SIZE);
let data = ClipData::uniform(rect, radius, mode);
PrimitiveStore::populate_clip_data(slice, data);
local_rect = local_rect.and_then(|r| r.intersection(&rect));
local_inner = ComplexClipRegion::new(rect, BorderRadius::uniform(radius))
.get_inner_rect_safe();
}
@@ -218,43 +236,51 @@ impl MaskCacheInfo {
},
Some(_) => None,
None => local_rect,
};
let clips = aux_lists.complex_clip_regions(®ion.complex);
if !self.is_aligned && region_mode == RegionMode::IncludeRect {
// we have an extra clip rect coming from the transformed layer
- debug_assert!(self.effective_clip_count < self.clip_range.item_count);
- let address = self.clip_range.start + self.effective_clip_count * CLIP_DATA_GPU_SIZE;
- self.effective_clip_count += 1;
+ debug_assert!(self.effective_complex_clip_count < self.complex_clip_range.item_count);
+ let address = self.complex_clip_range.start + self.effective_complex_clip_count * CLIP_DATA_GPU_SIZE;
+ self.effective_complex_clip_count += 1;
let slice = clip_store.get_slice_mut(address, CLIP_DATA_GPU_SIZE);
PrimitiveStore::populate_clip_data(slice, ClipData::uniform(region.main, 0.0, ClipMode::Clip));
}
- debug_assert!(self.effective_clip_count + clips.len() <= self.clip_range.item_count);
- let address = self.clip_range.start + self.effective_clip_count * CLIP_DATA_GPU_SIZE;
- self.effective_clip_count += clips.len();
+ debug_assert!(self.effective_complex_clip_count + clips.len() <= self.complex_clip_range.item_count);
+ let address = self.complex_clip_range.start + self.effective_complex_clip_count * CLIP_DATA_GPU_SIZE;
+ self.effective_complex_clip_count += clips.len();
let slice = clip_store.get_slice_mut(address, CLIP_DATA_GPU_SIZE * clips.len());
for (clip, chunk) in clips.iter().zip(slice.chunks_mut(CLIP_DATA_GPU_SIZE)) {
let data = ClipData::from_clip_region(clip);
PrimitiveStore::populate_clip_data(chunk, data);
local_rect = local_rect.and_then(|r| r.intersection(&clip.rect));
local_inner = local_inner.and_then(|r| clip.get_inner_rect_safe()
.and_then(|ref inner| r.intersection(inner)));
}
}
+ ClipSource::BorderCorner{..} => {}
}
}
+ for &(ref source, gpu_address) in &self.border_corners {
+ has_border_clip = true;
+ let slice = clip_store.get_slice_mut(gpu_address,
+ 1 + source.dash_count);
+ source.populate_gpu_data(slice);
+ }
+
// Work out the type of mask geometry we have, based on the
// list of clip sources above.
- if has_clip_out {
+ if has_clip_out || has_border_clip {
// For clip-out, the mask rect is not known.
self.bounds = Some(MaskBounds::None);
} else {
// TODO(gw): local inner is only valid if there's a single clip (for now).
// This can be improved in the future, with some proper
// rectangle region handling.
if sources.len() > 1 {
local_inner = None;
@@ -283,15 +309,17 @@ impl MaskCacheInfo {
}
&mut MaskBounds::OuterInner(ref mut outer, ref mut inner) => {
outer.update(transform, device_pixel_ratio);
inner.update(transform, device_pixel_ratio);
}
}
}
- /// Check if this `MaskCacheInfo` actually carries any masks. `effective_clip_count`
+ /// Check if this `MaskCacheInfo` actually carries any masks. `effective_complex_clip_count`
/// can change during the `update` call depending on the transformation, so the mask may
/// appear to be empty.
pub fn is_masking(&self) -> bool {
- self.image.is_some() || self.effective_clip_count != 0
+ self.image.is_some() ||
+ self.effective_complex_clip_count != 0 ||
+ !self.border_corners.is_empty()
}
}
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -1,18 +1,19 @@
/* 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 app_units::Au;
+use border::{BorderCornerClipData, BorderCornerDashClipData};
use euclid::{Size2D};
use gpu_store::GpuStoreAddress;
use internal_types::{SourceTexture, PackedTexel};
use mask_cache::{ClipMode, ClipSource, MaskCacheInfo};
-use renderer::{VertexDataStore, GradientDataStore};
+use renderer::{VertexDataStore, GradientDataStore, SplitGeometryStore};
use render_task::{RenderTask, RenderTaskLocation};
use resource_cache::{CacheItem, ImageProperties, ResourceCache};
use std::mem;
use std::usize;
use util::{TransformedRect, recycle_vec};
use webrender_traits::{AuxiliaryLists, ColorF, ImageKey, ImageRendering, YuvColorSpace, YuvFormat};
use webrender_traits::{ClipRegion, ComplexClipRegion, ItemRange, GlyphKey};
use webrender_traits::{FontKey, FontRenderMode, WebGLContextId};
@@ -94,16 +95,25 @@ pub enum PrimitiveKind {
/// Geometry description for simple rectangular primitives, uploaded to the GPU.
#[derive(Debug, Clone)]
pub struct PrimitiveGeometry {
pub local_rect: LayerRect,
pub local_clip_rect: LayerRect,
}
+impl Default for PrimitiveGeometry {
+ fn default() -> PrimitiveGeometry {
+ PrimitiveGeometry {
+ local_rect: unsafe { mem::uninitialized() },
+ local_clip_rect: unsafe { mem::uninitialized() },
+ }
+ }
+}
+
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum PrimitiveCacheKey {
BoxShadow(BoxShadowPrimitiveCacheKey),
TextShadow(PrimitiveIndex),
}
// TODO(gw): Pack the fields here better!
#[derive(Debug)]
@@ -130,16 +140,30 @@ pub struct PrimitiveMetadata {
impl PrimitiveMetadata {
pub fn needs_clipping(&self) -> bool {
self.clip_task.is_some()
}
}
#[derive(Debug, Clone)]
+pub struct SplitGeometry {
+ pub data: [f32; 12],
+}
+
+impl Default for SplitGeometry {
+ fn default() -> SplitGeometry {
+ SplitGeometry {
+ data: unsafe { mem::uninitialized() },
+ }
+ }
+}
+
+
+#[derive(Debug, Clone)]
#[repr(C)]
pub struct RectanglePrimitive {
pub color: ColorF,
}
#[derive(Debug)]
pub enum ImagePrimitiveKind {
Image(ImageKey, ImageRendering, Option<TileOffset>, LayerSize),
@@ -313,21 +337,17 @@ impl Clone for GradientData {
colors_high: self.colors_high,
colors_low: self.colors_low,
}
}
}
impl GradientData {
// Generate a color ramp between the start and end indexes from a start color to an end color.
- fn fill_colors(&mut self, start_idx: usize, end_idx: usize, start_color: &ColorF, end_color: &ColorF) -> usize {
- if start_idx >= end_idx {
- return start_idx;
- }
-
+ fn fill_colors(&mut self, start_idx: usize, end_idx: usize, start_color: &ColorF, end_color: &ColorF) {
// Calculate the color difference for individual steps in the ramp.
let inv_steps = 1.0 / (end_idx - start_idx) as f32;
let step_r = (end_color.r - start_color.r) * inv_steps;
let step_g = (end_color.g - start_color.g) * inv_steps;
let step_b = (end_color.b - start_color.b) * inv_steps;
let step_a = (end_color.a - start_color.a) * inv_steps;
let mut cur_color = *start_color;
@@ -345,53 +365,66 @@ impl GradientData {
cur_color.g += step_g;
cur_color.b += step_b;
cur_color.a += step_a;
cur_color_high = PackedTexel::high_bytes(&cur_color);
cur_color_low = PackedTexel::low_bytes(&cur_color);
high_byte_entry.end_color = cur_color_high;
low_byte_entry.end_color = cur_color_low;
}
-
- end_idx
}
// Compute an entry index based on a gradient stop offset.
#[inline]
fn get_index(offset: f32) -> usize {
(offset.max(0.0).min(1.0) * GRADIENT_DATA_RESOLUTION as f32).round() as usize
}
// Build the gradient data from the supplied stops, reversing them if necessary.
fn build(&mut self, src_stops: &[GradientStop], reverse_stops: bool) {
- let mut cur_idx = 0usize;
- let mut cur_color = if let Some(src) = src_stops.first() {
- src.color
- } else {
- ColorF::new(0.0, 0.0, 0.0, 0.0)
- };
+
+ const MAX_IDX: usize = GRADIENT_DATA_RESOLUTION;
+ const MIN_IDX: usize = 0;
+
+ // Preconditions (should be ensured by DisplayListBuilder):
+ // * we have at least two stops
+ // * first stop has offset 0.0
+ // * last stop has offset 1.0
+
+ let mut src_stops = src_stops.into_iter();
+ let first = src_stops.next().unwrap();
+ let mut cur_color = first.color;
+ debug_assert_eq!(first.offset, 0.0);
if reverse_stops {
- // If the gradient is reversed, then ensure the stops are processed in reverse order
- // and that the offsets are inverted.
- for src in src_stops.iter().rev() {
- cur_idx = self.fill_colors(cur_idx, Self::get_index(1.0 - src.offset),
- &cur_color, &src.color);
- cur_color = src.color;
+ // If the gradient is reversed, then we invert offsets and draw right-to-left
+ let mut cur_idx = MAX_IDX;
+ for next in src_stops {
+ let next_idx = Self::get_index(1.0 - next.offset);
+ if next_idx < cur_idx {
+ self.fill_colors(next_idx, cur_idx,
+ &next.color, &cur_color);
+ cur_idx = next_idx;
+ }
+ cur_color = next.color;
}
+ debug_assert_eq!(cur_idx, MIN_IDX);
} else {
- for src in src_stops {
- cur_idx = self.fill_colors(cur_idx, Self::get_index(src.offset),
- &cur_color, &src.color);
- cur_color = src.color;
+ let mut cur_idx = MIN_IDX;
+ for next in src_stops {
+ let next_idx = Self::get_index(next.offset);
+ if next_idx > cur_idx {
+ self.fill_colors(cur_idx, next_idx,
+ &cur_color, &next.color);
+ cur_idx = next_idx;
+ }
+ cur_color = next.color;
}
+ debug_assert_eq!(cur_idx, MAX_IDX);
}
-
- // Fill out any remaining entries in the gradient.
- self.fill_colors(cur_idx, GRADIENT_DATA_RESOLUTION, &cur_color, &cur_color);
}
}
#[derive(Debug, Clone)]
#[repr(C)]
struct InstanceRect {
rect: LayerRect,
}
@@ -560,38 +593,41 @@ pub enum PrimitiveContainer {
Border(BorderPrimitiveCpu, BorderPrimitiveGpu),
AlignedGradient(GradientPrimitiveCpu, GradientPrimitiveGpu),
AngleGradient(GradientPrimitiveCpu, GradientPrimitiveGpu),
RadialGradient(RadialGradientPrimitiveCpu, RadialGradientPrimitiveGpu),
BoxShadow(BoxShadowPrimitiveGpu, Vec<LayerRect>),
}
pub struct PrimitiveStore {
- // CPU side information only
+ /// CPU side information only.
pub cpu_bounding_rects: Vec<Option<DeviceIntRect>>,
pub cpu_text_runs: Vec<TextRunPrimitiveCpu>,
pub cpu_images: Vec<ImagePrimitiveCpu>,
pub cpu_yuv_images: Vec<YuvImagePrimitiveCpu>,
pub cpu_gradients: Vec<GradientPrimitiveCpu>,
pub cpu_radial_gradients: Vec<RadialGradientPrimitiveCpu>,
pub cpu_metadata: Vec<PrimitiveMetadata>,
pub cpu_borders: Vec<BorderPrimitiveCpu>,
- // Gets uploaded directly to GPU via vertex texture
+ /// Gets uploaded directly to GPU via vertex texture.
pub gpu_geometry: VertexDataStore<PrimitiveGeometry>,
pub gpu_data16: VertexDataStore<GpuBlock16>,
pub gpu_data32: VertexDataStore<GpuBlock32>,
pub gpu_data64: VertexDataStore<GpuBlock64>,
pub gpu_data128: VertexDataStore<GpuBlock128>,
pub gpu_gradient_data: GradientDataStore,
- // Resolved resource rects.
+ /// Geometry generated by plane splitting.
+ pub gpu_split_geometry: SplitGeometryStore,
+
+ /// Resolved resource rects.
pub gpu_resource_rects: VertexDataStore<TexelRect>,
- // General
+ /// General
prims_to_resolve: Vec<PrimitiveIndex>,
}
impl PrimitiveStore {
pub fn new() -> PrimitiveStore {
PrimitiveStore {
cpu_metadata: Vec::new(),
cpu_bounding_rects: Vec::new(),
@@ -603,16 +639,17 @@ impl PrimitiveStore {
cpu_borders: Vec::new(),
prims_to_resolve: Vec::new(),
gpu_geometry: VertexDataStore::new(),
gpu_data16: VertexDataStore::new(),
gpu_data32: VertexDataStore::new(),
gpu_data64: VertexDataStore::new(),
gpu_data128: VertexDataStore::new(),
gpu_gradient_data: GradientDataStore::new(),
+ gpu_split_geometry: SplitGeometryStore::new(),
gpu_resource_rects: VertexDataStore::new(),
}
}
pub fn recycle(self) -> Self {
PrimitiveStore {
cpu_metadata: recycle_vec(self.cpu_metadata),
cpu_bounding_rects: recycle_vec(self.cpu_bounding_rects),
@@ -624,16 +661,17 @@ impl PrimitiveStore {
cpu_borders: recycle_vec(self.cpu_borders),
prims_to_resolve: recycle_vec(self.prims_to_resolve),
gpu_geometry: self.gpu_geometry.recycle(),
gpu_data16: self.gpu_data16.recycle(),
gpu_data32: self.gpu_data32.recycle(),
gpu_data64: self.gpu_data64.recycle(),
gpu_data128: self.gpu_data128.recycle(),
gpu_gradient_data: self.gpu_gradient_data.recycle(),
+ gpu_split_geometry: self.gpu_split_geometry.recycle(),
gpu_resource_rects: self.gpu_resource_rects.recycle(),
}
}
pub fn populate_clip_data(data: &mut [GpuBlock32], clip: ClipData) {
data[0] = GpuBlock32::from(clip.rect);
data[1] = GpuBlock32::from(clip.top_left);
data[2] = GpuBlock32::from(clip.top_right);
@@ -1037,16 +1075,17 @@ impl PrimitiveStore {
pub fn set_clip_source(&mut self, index: PrimitiveIndex, source: Option<ClipSource>) {
let metadata = &mut self.cpu_metadata[index.0];
metadata.clips = match source {
Some(source) => {
let (rect, is_complex) = match source {
ClipSource::Complex(rect, radius, _) => (rect, radius > 0.0),
ClipSource::Region(ref region, _) => (region.main, region.is_complex()),
+ ClipSource::BorderCorner{..} => panic!("Not supported!"),
};
self.gpu_geometry.get_mut(GpuStoreAddress(index.0 as i32))
.local_clip_rect = rect;
if is_complex {
metadata.clip_cache_info = None; //CLIP TODO: re-use the existing GPU allocation
}
vec![source]
}
@@ -1335,17 +1374,18 @@ macro_rules! define_gpu_block {
)
}
define_gpu_block!(GpuBlock16: [f32; 4] =
RectanglePrimitive, InstanceRect, GlyphPrimitive,
TextRunPrimitiveGpu, ImagePrimitiveGpu, YuvImagePrimitiveGpu
);
define_gpu_block!(GpuBlock32: [f32; 8] =
- GradientStopGpu, ClipCorner, ClipRect, ImageMaskData
+ GradientStopGpu, ClipCorner, ClipRect, ImageMaskData,
+ BorderCornerClipData, BorderCornerDashClipData
);
define_gpu_block!(GpuBlock64: [f32; 16] =
GradientPrimitiveGpu, RadialGradientPrimitiveGpu, BoxShadowPrimitiveGpu
);
define_gpu_block!(GpuBlock128: [f32; 32] =
BorderPrimitiveGpu
);
--- a/gfx/webrender/src/render_task.rs
+++ b/gfx/webrender/src/render_task.rs
@@ -1,20 +1,21 @@
/* 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 gpu_store::GpuStoreAddress;
use internal_types::{HardwareCompositeOp, LowLevelFilterOp};
use mask_cache::{MaskBounds, MaskCacheInfo};
use prim_store::{PrimitiveCacheKey, PrimitiveIndex};
use std::{cmp, f32, i32, mem, usize};
use tiling::{ClipScrollGroupIndex, PackedLayerIndex, RenderPass, RenderTargetIndex};
use tiling::{RenderTargetKind, StackingContextIndex};
use webrender_traits::{ClipId, DeviceIntLength, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
-use webrender_traits::MixBlendMode;
+use webrender_traits::{MixBlendMode};
const FLOATS_PER_RENDER_TASK_INFO: usize = 12;
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
pub struct RenderTaskIndex(pub usize);
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum RenderTaskKey {
@@ -46,19 +47,20 @@ pub enum RenderTaskId {
#[derive(Debug, Clone)]
pub enum RenderTaskLocation {
Fixed,
Dynamic(Option<(DeviceIntPoint, RenderTargetIndex)>, DeviceIntSize),
}
#[derive(Debug, Clone)]
pub enum AlphaRenderItem {
- Primitive(ClipScrollGroupIndex, PrimitiveIndex, i32),
+ Primitive(Option<ClipScrollGroupIndex>, PrimitiveIndex, i32),
Blend(StackingContextIndex, RenderTaskId, LowLevelFilterOp, i32),
Composite(StackingContextIndex, RenderTaskId, RenderTaskId, MixBlendMode, i32),
+ SplitComposite(StackingContextIndex, RenderTaskId, GpuStoreAddress, i32),
HardwareComposite(StackingContextIndex, RenderTaskId, HardwareCompositeOp, i32),
}
#[derive(Debug, Clone)]
pub struct AlphaRenderTask {
screen_origin: DeviceIntPoint,
pub items: Vec<AlphaRenderItem>,
}
@@ -150,16 +152,22 @@ impl RenderTask {
location: location,
kind: RenderTaskKind::Alpha(AlphaRenderTask {
screen_origin: screen_origin,
items: Vec::new(),
}),
}
}
+ pub fn new_dynamic_alpha_batch(task_index: RenderTaskIndex,
+ rect: &DeviceIntRect) -> RenderTask {
+ let location = RenderTaskLocation::Dynamic(None, rect.size);
+ Self::new_alpha_batch(task_index, rect.origin, location)
+ }
+
pub fn new_prim_cache(key: PrimitiveCacheKey,
size: DeviceIntSize,
prim_index: PrimitiveIndex) -> RenderTask {
RenderTask {
id: RenderTaskId::Dynamic(RenderTaskKey::CachePrimitive(key)),
children: Vec::new(),
location: RenderTaskLocation::Dynamic(None, size),
kind: RenderTaskKind::CachePrimitive(prim_index),
@@ -231,17 +239,17 @@ impl RenderTask {
// For now, only draw optimized geometry if it is
// a single aligned rect mask with rounded corners.
// In the future, we'll expand this to handle the
// more complex types of clip mask geometry.
let mut geometry_kind = MaskGeometryKind::Default;
if inner_rect.is_some() && clips.len() == 1 {
let (_, ref clip_info) = clips[0];
if clip_info.image.is_none() &&
- clip_info.effective_clip_count == 1 &&
+ clip_info.effective_complex_clip_count == 1 &&
clip_info.is_aligned {
geometry_kind = MaskGeometryKind::CornersOnly;
}
}
let inner_rect = inner_rect.unwrap_or(DeviceIntRect::zero());
MaskResult::Inside(RenderTask {
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -17,17 +17,17 @@ use euclid::Matrix4D;
use fnv::FnvHasher;
use frame_builder::FrameBuilderConfig;
use gleam::gl;
use gpu_store::{GpuStore, GpuStoreLayout};
use internal_types::{CacheTextureId, RendererFrame, ResultMsg, TextureUpdateOp};
use internal_types::{TextureUpdateList, PackedVertex, RenderTargetMode};
use internal_types::{ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE, SourceTexture};
use internal_types::{BatchTextures, TextureSampler};
-use prim_store::GradientData;
+use prim_store::{GradientData, SplitGeometry};
use profiler::{Profiler, BackendProfileCounters};
use profiler::{GpuProfileTag, RendererProfileTimers, RendererProfileCounters};
use record::ApiRecordingReceiver;
use render_backend::RenderBackend;
use render_task::RenderTaskData;
use std;
use std::cmp;
use std::collections::{HashMap, VecDeque};
@@ -65,16 +65,17 @@ const GPU_TAG_CACHE_CLIP: GpuProfileTag
const GPU_TAG_CACHE_TEXT_RUN: GpuProfileTag = GpuProfileTag { label: "C_TextRun", color: debug_colors::MISTYROSE };
const GPU_TAG_INIT: GpuProfileTag = GpuProfileTag { label: "Init", color: debug_colors::WHITE };
const GPU_TAG_SETUP_TARGET: GpuProfileTag = GpuProfileTag { label: "Target", color: debug_colors::SLATEGREY };
const GPU_TAG_PRIM_RECT: GpuProfileTag = GpuProfileTag { label: "Rect", color: debug_colors::RED };
const GPU_TAG_PRIM_IMAGE: GpuProfileTag = GpuProfileTag { label: "Image", color: debug_colors::GREEN };
const GPU_TAG_PRIM_YUV_IMAGE: GpuProfileTag = GpuProfileTag { label: "YuvImage", color: debug_colors::DARKGREEN };
const GPU_TAG_PRIM_BLEND: GpuProfileTag = GpuProfileTag { label: "Blend", color: debug_colors::LIGHTBLUE };
const GPU_TAG_PRIM_HW_COMPOSITE: GpuProfileTag = GpuProfileTag { label: "HwComposite", color: debug_colors::DODGERBLUE };
+const GPU_TAG_PRIM_SPLIT_COMPOSITE: GpuProfileTag = GpuProfileTag { label: "SplitComposite", color: debug_colors::DARKBLUE };
const GPU_TAG_PRIM_COMPOSITE: GpuProfileTag = GpuProfileTag { label: "Composite", color: debug_colors::MAGENTA };
const GPU_TAG_PRIM_TEXT_RUN: GpuProfileTag = GpuProfileTag { label: "TextRun", color: debug_colors::BLUE };
const GPU_TAG_PRIM_GRADIENT: GpuProfileTag = GpuProfileTag { label: "Gradient", color: debug_colors::YELLOW };
const GPU_TAG_PRIM_ANGLE_GRADIENT: GpuProfileTag = GpuProfileTag { label: "AngleGradient", color: debug_colors::POWDERBLUE };
const GPU_TAG_PRIM_RADIAL_GRADIENT: GpuProfileTag = GpuProfileTag { label: "RadialGradient", color: debug_colors::LIGHTPINK };
const GPU_TAG_PRIM_BOX_SHADOW: GpuProfileTag = GpuProfileTag { label: "BoxShadow", color: debug_colors::CYAN };
const GPU_TAG_PRIM_BORDER: GpuProfileTag = GpuProfileTag { label: "Border", color: debug_colors::ORANGE };
const GPU_TAG_PRIM_BORDER_CORNER: GpuProfileTag = GpuProfileTag { label: "BorderCorner", color: debug_colors::DARKSLATEGREY };
@@ -241,17 +242,17 @@ impl GpuStoreLayout for VertexDataTextur
fn texture_filter() -> TextureFilter {
TextureFilter::Nearest
}
}
type VertexDataTexture = GpuDataTexture<VertexDataTextureLayout>;
pub type VertexDataStore<T> = GpuStore<T, VertexDataTextureLayout>;
-pub struct GradientDataTextureLayout {}
+pub struct GradientDataTextureLayout;
impl GpuStoreLayout for GradientDataTextureLayout {
fn image_format() -> ImageFormat {
ImageFormat::RGBA8
}
fn texture_width<T>() -> usize {
mem::size_of::<GradientData>() / Self::texel_size() / 2
@@ -260,16 +261,36 @@ impl GpuStoreLayout for GradientDataText
fn texture_filter() -> TextureFilter {
TextureFilter::Linear
}
}
type GradientDataTexture = GpuDataTexture<GradientDataTextureLayout>;
pub type GradientDataStore = GpuStore<GradientData, GradientDataTextureLayout>;
+pub struct SplitGeometryTextureLayout;
+
+impl GpuStoreLayout for SplitGeometryTextureLayout {
+ fn image_format() -> ImageFormat {
+ //TODO: use normalized integers
+ ImageFormat::RGBAF32
+ }
+
+ fn texture_width<T>() -> usize {
+ MAX_VERTEX_TEXTURE_WIDTH - (MAX_VERTEX_TEXTURE_WIDTH % Self::texels_per_item::<T>())
+ }
+
+ fn texture_filter() -> TextureFilter {
+ TextureFilter::Nearest
+ }
+}
+
+type SplitGeometryTexture = GpuDataTexture<SplitGeometryTextureLayout>;
+pub type SplitGeometryStore = GpuStore<SplitGeometry, SplitGeometryTextureLayout>;
+
const TRANSFORM_FEATURE: &'static str = "TRANSFORM";
const SUBPIXEL_AA_FEATURE: &'static str = "SUBPIXEL_AA";
const CLIP_FEATURE: &'static str = "CLIP";
enum ShaderKind {
Primitive,
Cache(VertexFormat),
ClipCache,
@@ -432,53 +453,57 @@ struct GpuDataTextures {
render_task_texture: VertexDataTexture,
prim_geom_texture: VertexDataTexture,
data16_texture: VertexDataTexture,
data32_texture: VertexDataTexture,
data64_texture: VertexDataTexture,
data128_texture: VertexDataTexture,
resource_rects_texture: VertexDataTexture,
gradient_data_texture: GradientDataTexture,
+ split_geometry_texture: SplitGeometryTexture,
}
impl GpuDataTextures {
fn new(device: &mut Device) -> GpuDataTextures {
GpuDataTextures {
layer_texture: VertexDataTexture::new(device),
render_task_texture: VertexDataTexture::new(device),
prim_geom_texture: VertexDataTexture::new(device),
data16_texture: VertexDataTexture::new(device),
data32_texture: VertexDataTexture::new(device),
data64_texture: VertexDataTexture::new(device),
data128_texture: VertexDataTexture::new(device),
resource_rects_texture: VertexDataTexture::new(device),
gradient_data_texture: GradientDataTexture::new(device),
+ split_geometry_texture: SplitGeometryTexture::new(device),
}
}
fn init_frame(&mut self, device: &mut Device, frame: &mut Frame) {
self.data16_texture.init(device, &mut frame.gpu_data16);
self.data32_texture.init(device, &mut frame.gpu_data32);
self.data64_texture.init(device, &mut frame.gpu_data64);
self.data128_texture.init(device, &mut frame.gpu_data128);
self.prim_geom_texture.init(device, &mut frame.gpu_geometry);
self.resource_rects_texture.init(device, &mut frame.gpu_resource_rects);
self.layer_texture.init(device, &mut frame.layer_texture_data);
self.render_task_texture.init(device, &mut frame.render_task_data);
self.gradient_data_texture.init(device, &mut frame.gpu_gradient_data);
+ self.split_geometry_texture.init(device, &mut frame.gpu_split_geometry);
device.bind_texture(TextureSampler::Layers, self.layer_texture.id);
device.bind_texture(TextureSampler::RenderTasks, self.render_task_texture.id);
device.bind_texture(TextureSampler::Geometry, self.prim_geom_texture.id);
device.bind_texture(TextureSampler::Data16, self.data16_texture.id);
device.bind_texture(TextureSampler::Data32, self.data32_texture.id);
device.bind_texture(TextureSampler::Data64, self.data64_texture.id);
device.bind_texture(TextureSampler::Data128, self.data128_texture.id);
device.bind_texture(TextureSampler::ResourceRects, self.resource_rects_texture.id);
device.bind_texture(TextureSampler::Gradients, self.gradient_data_texture.id);
+ device.bind_texture(TextureSampler::SplitGeometry, self.split_geometry_texture.id);
}
}
/// The renderer is responsible for submitting to the GPU the work prepared by the
/// RenderBackend.
pub struct Renderer {
result_rx: Receiver<ResultMsg>,
device: Device,
@@ -492,16 +517,17 @@ pub struct Renderer {
cs_box_shadow: LazilyCompiledShader,
cs_text_run: LazilyCompiledShader,
cs_blur: LazilyCompiledShader,
/// These are "cache clip shaders". These shaders are used to
/// draw clip instances into the cached clip mask. The results
/// of these shaders are also used by the primitive shaders.
cs_clip_rectangle: LazilyCompiledShader,
cs_clip_image: LazilyCompiledShader,
+ cs_clip_border: LazilyCompiledShader,
// The are "primitive shaders". These shaders draw and blend
// final results on screen. They are aware of tile boundaries.
// Most draw directly to the framebuffer, but some use inputs
// from the cache shaders to draw. Specifically, the box
// shadow primitive shader stretches the box shadow cache
// output, and the cache_image shader blits the results of
// a cache shader (e.g. blur) to the screen.
@@ -517,26 +543,28 @@ pub struct Renderer {
ps_gradient: PrimitiveShader,
ps_angle_gradient: PrimitiveShader,
ps_radial_gradient: PrimitiveShader,
ps_box_shadow: PrimitiveShader,
ps_cache_image: PrimitiveShader,
ps_blend: LazilyCompiledShader,
ps_hw_composite: LazilyCompiledShader,
+ ps_split_composite: LazilyCompiledShader,
ps_composite: LazilyCompiledShader,
notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
enable_profiler: bool,
max_recorded_profiles: usize,
clear_framebuffer: bool,
clear_color: ColorF,
debug: DebugRenderer,
render_target_debug: bool,
+ enable_batcher: bool,
backend_profile_counters: BackendProfileCounters,
profile_counters: RendererProfileCounters,
profiler: Profiler,
last_time: u64,
color_render_targets: Vec<TextureId>,
alpha_render_targets: Vec<TextureId>,
@@ -678,16 +706,24 @@ impl Renderer {
let cs_clip_image = try!{
LazilyCompiledShader::new(ShaderKind::ClipCache,
"cs_clip_image",
&[],
&mut device,
options.precache_shaders)
};
+ let cs_clip_border = try!{
+ LazilyCompiledShader::new(ShaderKind::ClipCache,
+ "cs_clip_border",
+ &[],
+ &mut device,
+ options.precache_shaders)
+ };
+
let ps_rectangle = try!{
PrimitiveShader::new("ps_rectangle",
&mut device,
&[],
options.precache_shaders)
};
let ps_rectangle_clip = try!{
@@ -867,16 +903,24 @@ impl Renderer {
let ps_hw_composite = try!{
LazilyCompiledShader::new(ShaderKind::Primitive,
"ps_hardware_composite",
&[],
&mut device,
options.precache_shaders)
};
+ let ps_split_composite = try!{
+ LazilyCompiledShader::new(ShaderKind::Primitive,
+ "ps_split_composite",
+ &[],
+ &mut device,
+ options.precache_shaders)
+ };
+
let device_max_size = device.max_texture_size();
let max_texture_size = cmp::min(device_max_size, options.max_texture_size.unwrap_or(device_max_size));
let mut texture_cache = TextureCache::new(max_texture_size);
let mut backend_profile_counters = BackendProfileCounters::new();
let white_pixels: Vec<u8> = vec![
0xff, 0xff, 0xff, 0xff,
@@ -1035,16 +1079,17 @@ impl Renderer {
device: device,
current_frame: None,
pending_texture_updates: Vec::new(),
pending_shader_updates: Vec::new(),
cs_box_shadow: cs_box_shadow,
cs_text_run: cs_text_run,
cs_blur: cs_blur,
cs_clip_rectangle: cs_clip_rectangle,
+ cs_clip_border: cs_clip_border,
cs_clip_image: cs_clip_image,
ps_rectangle: ps_rectangle,
ps_rectangle_clip: ps_rectangle_clip,
ps_text_run: ps_text_run,
ps_text_run_subpixel: ps_text_run_subpixel,
ps_image: ps_image,
ps_yuv_image: ps_yuv_image,
ps_border: ps_border,
@@ -1052,20 +1097,22 @@ impl Renderer {
ps_border_edge: ps_border_edge,
ps_box_shadow: ps_box_shadow,
ps_gradient: ps_gradient,
ps_angle_gradient: ps_angle_gradient,
ps_radial_gradient: ps_radial_gradient,
ps_cache_image: ps_cache_image,
ps_blend: ps_blend,
ps_hw_composite: ps_hw_composite,
+ ps_split_composite: ps_split_composite,
ps_composite: ps_composite,
notifier: notifier,
debug: debug_renderer,
render_target_debug: render_target_debug,
+ enable_batcher: options.enable_batcher,
backend_profile_counters: BackendProfileCounters::new(),
profile_counters: RendererProfileCounters::new(),
profiler: Profiler::new(),
enable_profiler: options.enable_profiler,
max_recorded_profiles: options.max_recorded_profiles,
clear_framebuffer: options.clear_framebuffer,
clear_color: options.clear_color,
last_time: 0,
@@ -1432,20 +1479,29 @@ impl Renderer {
self.device.bind_texture(TextureSampler::color(i), texture_id);
}
// TODO: this probably isn't the best place for this.
if let Some(id) = self.dither_matrix_texture_id {
self.device.bind_texture(TextureSampler::Dither, id);
}
- self.device.update_vao_instances(vao, data, VertexUsageHint::Stream);
- self.device.draw_indexed_triangles_instanced_u16(6, data.len() as i32);
+ if self.enable_batcher {
+ self.device.update_vao_instances(vao, data, VertexUsageHint::Stream);
+ self.device.draw_indexed_triangles_instanced_u16(6, data.len() as i32);
+ self.profile_counters.draw_calls.inc();
+ } else {
+ for i in 0 .. data.len() {
+ self.device.update_vao_instances(vao, &data[i..i+1], VertexUsageHint::Stream);
+ self.device.draw_triangles_u16(0, 6);
+ self.profile_counters.draw_calls.inc();
+ }
+ }
+
self.profile_counters.vertices.add(6 * data.len());
- self.profile_counters.draw_calls.inc();
}
fn submit_batch(&mut self,
batch: &PrimitiveBatch,
projection: &Matrix4D<f32>,
render_task_data: &[RenderTaskData],
cache_texture: TextureId,
render_target: Option<(TextureId, i32)>,
@@ -1460,16 +1516,20 @@ impl Renderer {
AlphaBatchKind::Composite => {
let shader = self.ps_composite.get(&mut self.device);
(GPU_TAG_PRIM_COMPOSITE, shader)
}
AlphaBatchKind::HardwareComposite => {
let shader = self.ps_hw_composite.get(&mut self.device);
(GPU_TAG_PRIM_HW_COMPOSITE, shader)
}
+ AlphaBatchKind::SplitComposite => {
+ let shader = self.ps_split_composite.get(&mut self.device);
+ (GPU_TAG_PRIM_SPLIT_COMPOSITE, shader)
+ }
AlphaBatchKind::Blend => {
let shader = self.ps_blend.get(&mut self.device);
(GPU_TAG_PRIM_BLEND, shader)
}
AlphaBatchKind::Rectangle => {
let shader = if needs_clipping {
self.ps_rectangle_clip.get(&mut self.device, transform_kind)
} else {
@@ -1796,16 +1856,26 @@ impl Renderer {
};
let shader = self.cs_clip_image.get(&mut self.device).unwrap();
self.draw_instanced_batch(items,
vao,
shader,
&textures,
&projection);
}
+ // draw special border clips
+ if !target.clip_batcher.borders.is_empty() {
+ let _gm2 = GpuMarker::new(self.device.rc_gl(), "clip borders");
+ let shader = self.cs_clip_border.get(&mut self.device).unwrap();
+ self.draw_instanced_batch(&target.clip_batcher.borders,
+ vao,
+ shader,
+ &BatchTextures::no_texture(),
+ &projection);
+ }
}
}
fn update_deferred_resolves(&mut self, frame: &mut Frame) {
// The first thing we do is run through any pending deferred
// resolves, and use a callback to get the UV rect for this
// custom item. Then we patch the resource_rects structure
// here before it's uploaded to the GPU.
@@ -2115,16 +2185,17 @@ pub struct RendererOptions {
pub max_recorded_profiles: usize,
pub debug: bool,
pub enable_scrollbars: bool,
pub precache_shaders: bool,
pub renderer_kind: RendererKind,
pub enable_subpixel_aa: bool,
pub clear_framebuffer: bool,
pub clear_color: ColorF,
+ pub enable_batcher: bool,
pub render_target_debug: bool,
pub max_texture_size: Option<u32>,
pub workers: Option<Arc<Mutex<ThreadPool>>>,
pub blob_image_renderer: Option<Box<BlobImageRenderer>>,
pub recorder: Option<Box<ApiRecordingReceiver>>,
}
impl Default for RendererOptions {
@@ -2138,16 +2209,17 @@ impl Default for RendererOptions {
max_recorded_profiles: 0,
debug: false,
enable_scrollbars: false,
precache_shaders: false,
renderer_kind: RendererKind::Native,
enable_subpixel_aa: false,
clear_framebuffer: true,
clear_color: ColorF::new(1.0, 1.0, 1.0, 1.0),
+ enable_batcher: true,
render_target_debug: false,
max_texture_size: None,
workers: None,
blob_image_renderer: None,
recorder: None,
}
}
}
--- a/gfx/webrender/src/tiling.rs
+++ b/gfx/webrender/src/tiling.rs
@@ -5,18 +5,18 @@
use app_units::Au;
use device::TextureId;
use fnv::FnvHasher;
use gpu_store::GpuStoreAddress;
use internal_types::{ANGLE_FLOAT_TO_FIXED, BatchTextures, CacheTextureId, LowLevelFilterOp};
use internal_types::SourceTexture;
use mask_cache::MaskCacheInfo;
use prim_store::{CLIP_DATA_GPU_SIZE, DeferredResolve, GpuBlock128, GpuBlock16, GpuBlock32};
-use prim_store::{GpuBlock64, GradientData, PrimitiveCacheKey, PrimitiveGeometry, PrimitiveIndex};
-use prim_store::{PrimitiveKind, PrimitiveMetadata, PrimitiveStore, TexelRect};
+use prim_store::{GpuBlock64, GradientData, SplitGeometry, PrimitiveCacheKey, PrimitiveGeometry};
+use prim_store::{PrimitiveIndex, PrimitiveKind, PrimitiveMetadata, PrimitiveStore, TexelRect};
use profiler::FrameProfileCounters;
use render_task::{AlphaRenderItem, MaskGeometryKind, MaskSegment, RenderTask, RenderTaskData};
use render_task::{RenderTaskId, RenderTaskIndex, RenderTaskKey, RenderTaskKind};
use render_task::RenderTaskLocation;
use renderer::BlendMode;
use renderer::ImageBufferKind;
use resource_cache::ResourceCache;
use std::{f32, i32, mem, usize};
@@ -193,25 +193,16 @@ impl RenderTaskCollection {
&RenderTaskId::Static(index) => index,
&RenderTaskId::Dynamic(key) => {
self.dynamic_tasks[&(key, pass_index)].index
}
}
}
}
-impl Default for PrimitiveGeometry {
- fn default() -> PrimitiveGeometry {
- PrimitiveGeometry {
- local_rect: unsafe { mem::uninitialized() },
- local_clip_rect: unsafe { mem::uninitialized() },
- }
- }
-}
-
struct AlphaBatchTask {
task_id: RenderTaskId,
items: Vec<AlphaRenderItem>,
}
pub struct BatchList {
pub alpha_batches: Vec<PrimitiveBatch>,
pub opaque_batches: Vec<PrimitiveBatch>,
@@ -315,17 +306,17 @@ impl AlphaRenderItem {
LowLevelFilterOp::Invert(amount) => (4, amount.to_f32_px()),
LowLevelFilterOp::Saturate(amount) => (5, amount.to_f32_px()),
LowLevelFilterOp::Sepia(amount) => (6, amount.to_f32_px()),
LowLevelFilterOp::Brightness(amount) => (7, amount.to_f32_px()),
LowLevelFilterOp::Opacity(amount) => (8, amount.to_f32_px()),
};
let amount = (amount * 65535.0).round() as i32;
- let batch = batch_list.get_suitable_batch(&key, &stacking_context.bounding_rect);
+ let batch = batch_list.get_suitable_batch(&key, &stacking_context.screen_bounds);
batch.add_instance(PrimitiveInstance {
global_prim_id: -1,
prim_address: GpuStoreAddress(0),
task_index: task_index.0 as i32,
clip_task_index: -1,
layer_index: -1,
sub_index: filter_mode,
@@ -335,17 +326,17 @@ impl AlphaRenderItem {
}
AlphaRenderItem::HardwareComposite(stacking_context_index, src_id, composite_op, z) => {
let stacking_context = &ctx.stacking_context_store[stacking_context_index.0];
let src_task_index = render_tasks.get_static_task_index(&src_id);
let key = AlphaBatchKey::new(AlphaBatchKind::HardwareComposite,
AlphaBatchKeyFlags::empty(),
composite_op.to_blend_mode(),
BatchTextures::no_texture());
- let batch = batch_list.get_suitable_batch(&key, &stacking_context.bounding_rect);
+ let batch = batch_list.get_suitable_batch(&key, &stacking_context.screen_bounds);
batch.add_instance(PrimitiveInstance {
global_prim_id: -1,
prim_address: GpuStoreAddress(0),
task_index: task_index.0 as i32,
clip_task_index: -1,
layer_index: -1,
sub_index: -1,
user_data: [src_task_index.0 as i32, 0],
@@ -357,35 +348,41 @@ impl AlphaRenderItem {
src_id,
mode,
z) => {
let stacking_context = &ctx.stacking_context_store[stacking_context_index.0];
let key = AlphaBatchKey::new(AlphaBatchKind::Composite,
AlphaBatchKeyFlags::empty(),
BlendMode::Alpha,
BatchTextures::no_texture());
- let batch = batch_list.get_suitable_batch(&key, &stacking_context.bounding_rect);
+ let batch = batch_list.get_suitable_batch(&key, &stacking_context.screen_bounds);
let backdrop_task = render_tasks.get_task_index(&backdrop_id, child_pass_index);
let src_task_index = render_tasks.get_static_task_index(&src_id);
batch.add_instance(PrimitiveInstance {
global_prim_id: -1,
prim_address: GpuStoreAddress(0),
task_index: task_index.0 as i32,
clip_task_index: -1,
layer_index: -1,
sub_index: mode as u32 as i32,
user_data: [ backdrop_task.0 as i32,
src_task_index.0 as i32 ],
z_sort_index: z,
});
}
- AlphaRenderItem::Primitive(clip_scroll_group_index, prim_index, z) => {
- let group = &ctx.clip_scroll_group_store[clip_scroll_group_index.0];
+ AlphaRenderItem::Primitive(clip_scroll_group_index_opt, prim_index, z) => {
let prim_metadata = ctx.prim_store.get_metadata(prim_index);
- let transform_kind = group.screen_bounding_rect.as_ref().unwrap().0;
+ let (transform_kind, packed_layer_index) = match clip_scroll_group_index_opt {
+ Some(group_index) => {
+ let group = &ctx.clip_scroll_group_store[group_index.0];
+ let bounding_rect = group.screen_bounding_rect.as_ref().unwrap();
+ (bounding_rect.0, group.packed_layer_index.0 as i32)
+ },
+ None => (TransformedRectKind::AxisAligned, 0),
+ };
let needs_clipping = prim_metadata.needs_clipping();
let mut flags = AlphaBatchKeyFlags::empty();
if needs_clipping {
flags |= NEEDS_CLIPPING;
}
if transform_kind == TransformedRectKind::AxisAligned {
flags |= AXIS_ALIGNED;
}
@@ -396,18 +393,16 @@ impl AlphaRenderItem {
let clip_task_index = match prim_metadata.clip_task {
Some(ref clip_task) => {
render_tasks.get_task_index(&clip_task.id, child_pass_index)
}
None => {
OPAQUE_TASK_INDEX
}
}.0 as i32;
- let packed_layer_index = ctx.clip_scroll_group_store[clip_scroll_group_index.0]
- .packed_layer_index.0 as i32;
let global_prim_id = prim_index.0 as i32;
let prim_address = prim_metadata.gpu_prim_index;
let task_index = task_index.0 as i32;
let needs_blending = !prim_metadata.is_opaque ||
needs_clipping ||
transform_kind == TransformedRectKind::Complex;
let blend_mode = ctx.prim_store.get_blend_mode(needs_blending, prim_metadata);
let base_instance = PrimitiveInstance {
@@ -581,19 +576,37 @@ impl AlphaRenderItem {
let batch = batch_list.get_suitable_batch(&key, item_bounding_rect);
for rect_index in 0..prim_metadata.gpu_data_count {
batch.add_instance(base_instance.build(prim_metadata.gpu_data_address.0 + rect_index,
cache_task_index.0 as i32,
0));
}
}
-
}
}
+ AlphaRenderItem::SplitComposite(sc_index, task_id, gpu_address, z) => {
+ let key = AlphaBatchKey::new(AlphaBatchKind::SplitComposite,
+ AlphaBatchKeyFlags::empty(),
+ BlendMode::PremultipliedAlpha,
+ BatchTextures::no_texture());
+ let stacking_context = &ctx.stacking_context_store[sc_index.0];
+ let batch = batch_list.get_suitable_batch(&key, &stacking_context.screen_bounds);
+ let source_task = render_tasks.get_task_index(&task_id, child_pass_index);
+ batch.add_instance(PrimitiveInstance {
+ global_prim_id: -1,
+ prim_address: gpu_address,
+ task_index: task_index.0 as i32,
+ clip_task_index: -1,
+ layer_index: -1, // not be used
+ sub_index: 0,
+ user_data: [ source_task.0 as i32, 0 ],
+ z_sort_index: z,
+ });
+ }
}
}
}
impl AlphaBatcher {
fn new() -> AlphaBatcher {
AlphaBatcher {
tasks: Vec::new(),
@@ -625,23 +638,25 @@ impl AlphaBatcher {
/// Batcher managing draw calls into the clip mask (in the RT cache).
#[derive(Debug)]
pub struct ClipBatcher {
/// Rectangle draws fill up the rectangles with rounded corners.
pub rectangles: Vec<CacheClipInstance>,
/// Image draws apply the image masking.
pub images: HashMap<SourceTexture, Vec<CacheClipInstance>>,
+ pub borders: Vec<CacheClipInstance>,
}
impl ClipBatcher {
fn new() -> ClipBatcher {
ClipBatcher {
rectangles: Vec::new(),
images: HashMap::new(),
+ borders: Vec::new(),
}
}
fn add<'a>(&mut self,
task_index: RenderTaskIndex,
clips: &[(PackedLayerIndex, MaskCacheInfo)],
resource_cache: &ResourceCache,
geometry_kind: MaskGeometryKind) {
@@ -649,18 +664,18 @@ impl ClipBatcher {
for &(packed_layer_index, ref info) in clips.iter() {
let instance = CacheClipInstance {
task_id: task_index.0 as i32,
layer_index: packed_layer_index.0 as i32,
address: GpuStoreAddress(0),
segment: 0,
};
- for clip_index in 0..info.effective_clip_count as usize {
- let offset = info.clip_range.start.0 + ((CLIP_DATA_GPU_SIZE * clip_index) as i32);
+ for clip_index in 0..info.effective_complex_clip_count as usize {
+ let offset = info.complex_clip_range.start.0 + ((CLIP_DATA_GPU_SIZE * clip_index) as i32);
match geometry_kind {
MaskGeometryKind::Default => {
self.rectangles.push(CacheClipInstance {
address: GpuStoreAddress(offset),
segment: MaskSegment::All as i32,
..instance
});
}
@@ -695,16 +710,26 @@ impl ClipBatcher {
let cache_item = resource_cache.get_cached_image(mask.image, ImageRendering::Auto, None);
self.images.entry(cache_item.texture_id)
.or_insert(Vec::new())
.push(CacheClipInstance {
address: address,
..instance
})
}
+
+ for &(ref source, gpu_address) in &info.border_corners {
+ for dash_index in 0..source.dash_count {
+ self.borders.push(CacheClipInstance {
+ address: gpu_address,
+ segment: dash_index as i32,
+ ..instance
+ })
+ }
+ }
}
}
}
pub struct RenderTargetContext<'a> {
pub stacking_context_store: &'a [StackingContext],
pub clip_scroll_group_store: &'a [ClipScrollGroup],
pub prim_store: &'a PrimitiveStore,
@@ -1144,16 +1169,17 @@ impl RenderPass {
self.alpha_targets.build(ctx, render_tasks, self.pass_index);
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum AlphaBatchKind {
Composite,
HardwareComposite,
+ SplitComposite,
Blend,
Rectangle,
TextRun,
Image(ImageBufferKind),
YuvImage(ImageBufferKind, YuvFormat, YuvColorSpace),
Border,
AlignedGradient,
AngleGradient,
@@ -1295,59 +1321,82 @@ impl PrimitiveBatch {
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct PackedLayerIndex(pub usize);
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct StackingContextIndex(pub usize);
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
+pub enum ContextIsolation {
+ /// No isolation - the content is mixed up with everything else.
+ None,
+ /// Items are isolated and drawn into a separate render target.
+ /// Child contexts are exposed.
+ Items,
+ /// All the content inside is isolated and drawn into a separate target.
+ Full,
+}
+
#[derive(Debug)]
pub struct StackingContext {
pub pipeline_id: PipelineId,
- // Offset in the parent reference frame to the origin of this stacking
- // context's coordinate system.
+ /// Offset in the parent reference frame to the origin of this stacking
+ /// context's coordinate system.
pub reference_frame_offset: LayerPoint,
- // Bounding rectangle for this stacking context calculated based on the size
- // and position of all its children.
- pub bounding_rect: DeviceIntRect,
+ /// The `ClipId` of the owning reference frame.
+ pub reference_frame_id: ClipId,
+
+ /// Local bounding rectangle for this stacking context.
+ pub local_bounds: LayerRect,
+
+ /// Screen space bounding rectangle for this stacking context,
+ /// calculated based on the size and position of all its children.
+ pub screen_bounds: DeviceIntRect,
pub composite_ops: CompositeOps,
pub clip_scroll_groups: Vec<ClipScrollGroupIndex>,
- // Signifies that this stacking context should be drawn in a separate render pass
- // with a transparent background and then composited back to its parent. Used to
- // support mix-blend-mode in certain cases.
- pub should_isolate: bool,
+ /// Type of the isolation of the content.
+ pub isolation: ContextIsolation,
- // Set for the root stacking context of a display list or an iframe. Used for determining
- // when to isolate a mix-blend-mode composite.
+ /// Set for the root stacking context of a display list or an iframe. Used for determining
+ /// when to isolate a mix-blend-mode composite.
pub is_page_root: bool,
- // Wehther or not this stacking context has any visible components, calculated
- // based on the size and position of all children and how they are clipped.
+ /// Whether or not this stacking context has any visible components, calculated
+ /// based on the size and position of all children and how they are clipped.
pub is_visible: bool,
}
impl StackingContext {
pub fn new(pipeline_id: PipelineId,
reference_frame_offset: LayerPoint,
is_page_root: bool,
+ reference_frame_id: ClipId,
+ local_bounds: LayerRect,
transform_style: TransformStyle,
composite_ops: CompositeOps)
-> StackingContext {
+ let isolation = match transform_style {
+ TransformStyle::Flat => ContextIsolation::None,
+ TransformStyle::Preserve3D => ContextIsolation::Items,
+ };
StackingContext {
pipeline_id: pipeline_id,
reference_frame_offset: reference_frame_offset,
- bounding_rect: DeviceIntRect::zero(),
+ reference_frame_id: reference_frame_id,
+ local_bounds: local_bounds,
+ screen_bounds: DeviceIntRect::zero(),
composite_ops: composite_ops,
clip_scroll_groups: Vec::new(),
- should_isolate: transform_style == TransformStyle::Preserve3D, //TODO
+ isolation: isolation,
is_page_root: is_page_root,
is_visible: false,
}
}
pub fn clip_scroll_group(&self, clip_and_scroll: ClipAndScrollInfo) -> ClipScrollGroupIndex {
// Currently there is only one scrolled stacking context per context,
// but eventually this will be selected from the vector based on the
@@ -1476,16 +1525,17 @@ pub struct Frame {
pub layer_texture_data: Vec<PackedLayer>,
pub render_task_data: Vec<RenderTaskData>,
pub gpu_data16: Vec<GpuBlock16>,
pub gpu_data32: Vec<GpuBlock32>,
pub gpu_data64: Vec<GpuBlock64>,
pub gpu_data128: Vec<GpuBlock128>,
pub gpu_geometry: Vec<PrimitiveGeometry>,
pub gpu_gradient_data: Vec<GradientData>,
+ pub gpu_split_geometry: Vec<SplitGeometry>,
pub gpu_resource_rects: Vec<TexelRect>,
// List of textures that we don't know about yet
// from the backend thread. The render thread
// will use a callback to resolve these and
// patch the data structures.
pub deferred_resolves: Vec<DeferredResolve>,
}
--- a/gfx/webrender_traits/src/display_item.rs
+++ b/gfx/webrender_traits/src/display_item.rs
@@ -582,16 +582,22 @@ impl ClipId {
ClipId::Clip(0, pipeline_id)
}
pub fn root_reference_frame(pipeline_id: PipelineId) -> ClipId {
ClipId::ReferenceFrame(0, pipeline_id)
}
pub fn new(id: u64, pipeline_id: PipelineId) -> ClipId {
+ // We do this because it is very easy to create accidentally create something that
+ // seems like a root scroll node, but isn't one.
+ if id == 0 {
+ return ClipId::root_scroll_node(pipeline_id);
+ }
+
ClipId::ClipExternalId(id, pipeline_id)
}
pub fn pipeline_id(&self) -> PipelineId {
match *self {
ClipId::Clip(_, pipeline_id) |
ClipId::ClipExternalId(_, pipeline_id) |
ClipId::ReferenceFrame(_, pipeline_id) => pipeline_id,
--- a/gfx/webrender_traits/src/units.rs
+++ b/gfx/webrender_traits/src/units.rs
@@ -7,17 +7,18 @@
//! Physical pixels take into account the device pixel ratio and their dimensions tend
//! to correspond to the allocated size of resources in memory, while logical pixels
//! don't have the device pixel ratio applied which means they are agnostic to the usage
//! of hidpi screens and the like.
//!
//! The terms "layer" and "stacking context" can be used interchangeably
//! in the context of coordinate systems.
-use euclid::{TypedMatrix4D, TypedRect, TypedPoint2D, TypedSize2D, TypedPoint4D, Length};
+use euclid::{Length, TypedMatrix4D, TypedRect, TypedSize2D};
+use euclid::{TypedPoint2D, TypedPoint3D, TypedPoint4D};
/// Geometry in the coordinate system of the render target (screen or intermediate
/// surface) in physical pixels.
#[derive(Hash, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct DevicePixel;
pub type DeviceIntRect = TypedRect<i32, DevicePixel>;
pub type DeviceIntPoint = TypedPoint2D<i32, DevicePixel>;
@@ -66,16 +67,17 @@ pub type ScrollLayerSize = TypedSize2D<f
/// Geometry in the document's coordinate space (logical pixels).
#[derive(Hash, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct WorldPixel;
pub type WorldRect = TypedRect<f32, WorldPixel>;
pub type WorldPoint = TypedPoint2D<f32, WorldPixel>;
pub type WorldSize = TypedSize2D<f32, WorldPixel>;
+pub type WorldPoint3D = TypedPoint3D<f32, WorldPixel>;
pub type WorldPoint4D = TypedPoint4D<f32, WorldPixel>;
/// Offset in number of tiles.
#[derive(Hash, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct Tiles;
pub type TileOffset = TypedPoint2D<u16, Tiles>;
pub type LayoutTransform = TypedMatrix4D<f32, LayoutPixel, LayoutPixel>;