--- a/gfx/webrender/examples/alpha_perf.rs
+++ b/gfx/webrender/examples/alpha_perf.rs
@@ -28,16 +28,17 @@ impl Example for App {
_pipeline_id: PipelineId,
_document_id: DocumentId,
) {
let bounds = (0, 0).to(1920, 1080);
let info = LayoutPrimitiveInfo::new(bounds);
builder.push_stacking_context(
&info,
+ None,
ScrollPolicy::Scrollable,
None,
TransformStyle::Flat,
None,
MixBlendMode::Normal,
Vec::new(),
);
--- a/gfx/webrender/examples/animation.rs
+++ b/gfx/webrender/examples/animation.rs
@@ -44,16 +44,17 @@ impl Example for App {
let filters = vec![
FilterOp::Opacity(PropertyBinding::Binding(self.opacity_key), self.opacity),
];
let info = LayoutPrimitiveInfo::new(bounds);
builder.push_stacking_context(
&info,
+ None,
ScrollPolicy::Scrollable,
Some(PropertyBinding::Binding(self.property_key)),
TransformStyle::Flat,
None,
MixBlendMode::Normal,
filters,
);
--- a/gfx/webrender/examples/basic.rs
+++ b/gfx/webrender/examples/basic.rs
@@ -186,16 +186,17 @@ impl Example for App {
_: DeviceUintSize,
_pipeline_id: PipelineId,
_document_id: DocumentId,
) {
let bounds = LayoutRect::new(LayoutPoint::zero(), builder.content_size());
let info = LayoutPrimitiveInfo::new(bounds);
builder.push_stacking_context(
&info,
+ None,
ScrollPolicy::Scrollable,
None,
TransformStyle::Flat,
None,
MixBlendMode::Normal,
Vec::new(),
);
--- a/gfx/webrender/examples/blob.rs
+++ b/gfx/webrender/examples/blob.rs
@@ -244,16 +244,17 @@ impl Example for App {
api::ImageData::new_blob_image(serialize_blob(api::ColorU::new(50, 150, 50, 255))),
None,
);
let bounds = api::LayoutRect::new(api::LayoutPoint::zero(), builder.content_size());
let info = api::LayoutPrimitiveInfo::new(bounds);
builder.push_stacking_context(
&info,
+ None,
api::ScrollPolicy::Scrollable,
None,
api::TransformStyle::Flat,
None,
api::MixBlendMode::Normal,
Vec::new(),
);
--- a/gfx/webrender/examples/document.rs
+++ b/gfx/webrender/examples/document.rs
@@ -107,16 +107,17 @@ impl Example for App {
);
let local_rect = LayoutRect::new(
LayoutPoint::zero(),
doc.content_rect.size,
);
builder.push_stacking_context(
&LayoutPrimitiveInfo::new(doc.content_rect),
+ None,
ScrollPolicy::Fixed,
None,
TransformStyle::Flat,
None,
MixBlendMode::Normal,
Vec::new(),
);
builder.push_rect(
--- a/gfx/webrender/examples/frame_output.rs
+++ b/gfx/webrender/examples/frame_output.rs
@@ -95,16 +95,17 @@ impl App {
let info = LayoutPrimitiveInfo::new(document.content_rect);
let mut builder = DisplayListBuilder::new(
document.pipeline_id,
document.content_rect.size,
);
builder.push_stacking_context(
&info,
+ None,
ScrollPolicy::Scrollable,
None,
TransformStyle::Flat,
None,
MixBlendMode::Normal,
Vec::new(),
);
@@ -142,16 +143,17 @@ impl Example for App {
let device_pixel_ratio = framebuffer_size.width as f32 /
builder.content_size().width;
self.init_output_document(api, DeviceUintSize::new(200, 200), device_pixel_ratio);
}
let info = LayoutPrimitiveInfo::new((100, 100).to(200, 200));
builder.push_stacking_context(
&info,
+ None,
ScrollPolicy::Scrollable,
None,
TransformStyle::Flat,
None,
MixBlendMode::Normal,
Vec::new(),
);
--- a/gfx/webrender/examples/iframe.rs
+++ b/gfx/webrender/examples/iframe.rs
@@ -33,16 +33,17 @@ impl Example for App {
let sub_bounds = (0, 0).to(sub_size.width as i32, sub_size.height as i32);
let sub_pipeline_id = PipelineId(pipeline_id.0, 42);
let mut sub_builder = DisplayListBuilder::new(sub_pipeline_id, sub_bounds.size);
let info = LayoutPrimitiveInfo::new(sub_bounds);
sub_builder.push_stacking_context(
&info,
+ None,
ScrollPolicy::Scrollable,
None,
TransformStyle::Flat,
None,
MixBlendMode::Normal,
Vec::new(),
);
@@ -58,16 +59,17 @@ impl Example for App {
sub_builder.finalize(),
true,
);
api.send_transaction(document_id, txn);
// And this is for the root pipeline
builder.push_stacking_context(
&info,
+ None,
ScrollPolicy::Scrollable,
Some(PropertyBinding::Binding(PropertyBindingKey::new(42))),
TransformStyle::Flat,
None,
MixBlendMode::Normal,
Vec::new(),
);
// red rect under the iframe: if this is visible, things have gone wrong
--- a/gfx/webrender/examples/image_resize.rs
+++ b/gfx/webrender/examples/image_resize.rs
@@ -35,16 +35,17 @@ impl Example for App {
image_data,
None,
);
let bounds = (0, 0).to(512, 512);
let info = LayoutPrimitiveInfo::new(bounds);
builder.push_stacking_context(
&info,
+ None,
ScrollPolicy::Scrollable,
None,
TransformStyle::Flat,
None,
MixBlendMode::Normal,
Vec::new(),
);
--- a/gfx/webrender/examples/multiwindow.rs
+++ b/gfx/webrender/examples/multiwindow.rs
@@ -175,16 +175,17 @@ impl Window {
let layout_size = framebuffer_size.to_f32() / euclid::TypedScale::new(device_pixel_ratio);
let mut txn = Transaction::new();
let mut builder = DisplayListBuilder::new(self.pipeline_id, layout_size);
let bounds = LayoutRect::new(LayoutPoint::zero(), builder.content_size());
let info = LayoutPrimitiveInfo::new(bounds);
builder.push_stacking_context(
&info,
+ None,
ScrollPolicy::Scrollable,
None,
TransformStyle::Flat,
None,
MixBlendMode::Normal,
Vec::new(),
);
--- a/gfx/webrender/examples/scrolling.rs
+++ b/gfx/webrender/examples/scrolling.rs
@@ -28,30 +28,32 @@ impl Example for App {
_pipeline_id: PipelineId,
_document_id: DocumentId,
) {
let info = LayoutPrimitiveInfo::new(
LayoutRect::new(LayoutPoint::zero(), builder.content_size())
);
builder.push_stacking_context(
&info,
+ None,
ScrollPolicy::Scrollable,
None,
TransformStyle::Flat,
None,
MixBlendMode::Normal,
Vec::new(),
);
if true {
// scrolling and clips stuff
// let's make a scrollbox
let scrollbox = (0, 0).to(300, 400);
builder.push_stacking_context(
&LayoutPrimitiveInfo::new((10, 10).by(0, 0)),
+ None,
ScrollPolicy::Scrollable,
None,
TransformStyle::Flat,
None,
MixBlendMode::Normal,
Vec::new(),
);
// set the scrolling clip
@@ -158,33 +160,31 @@ impl Example for App {
glutin::VirtualKeyCode::Right => (-10.0, 0.0),
glutin::VirtualKeyCode::Left => (10.0, 0.0),
_ => return false,
};
txn.scroll(
ScrollLocation::Delta(LayoutVector2D::new(offset.0, offset.1)),
self.cursor_position,
- ScrollEventPhase::Start,
);
}
glutin::WindowEvent::CursorMoved { position: (x, y), .. } => {
self.cursor_position = WorldPoint::new(x as f32, y as f32);
}
glutin::WindowEvent::MouseWheel { delta, .. } => {
const LINE_HEIGHT: f32 = 38.0;
let (dx, dy) = match delta {
glutin::MouseScrollDelta::LineDelta(dx, dy) => (dx, dy * LINE_HEIGHT),
glutin::MouseScrollDelta::PixelDelta(dx, dy) => (dx, dy),
};
txn.scroll(
ScrollLocation::Delta(LayoutVector2D::new(dx, dy)),
self.cursor_position,
- ScrollEventPhase::Start,
);
}
glutin::WindowEvent::MouseInput { .. } => {
let results = api.hit_test(
document_id,
None,
self.cursor_position,
HitTestFlags::FIND_ALL
--- a/gfx/webrender/examples/texture_cache_stress.rs
+++ b/gfx/webrender/examples/texture_cache_stress.rs
@@ -87,16 +87,17 @@ impl Example for App {
_framebuffer_size: DeviceUintSize,
_pipeline_id: PipelineId,
_document_id: DocumentId,
) {
let bounds = (0, 0).to(512, 512);
let info = LayoutPrimitiveInfo::new(bounds);
builder.push_stacking_context(
&info,
+ None,
ScrollPolicy::Scrollable,
None,
TransformStyle::Flat,
None,
MixBlendMode::Normal,
Vec::new(),
);
--- a/gfx/webrender/examples/yuv.rs
+++ b/gfx/webrender/examples/yuv.rs
@@ -82,16 +82,17 @@ impl Example for App {
_framebuffer_size: DeviceUintSize,
_pipeline_id: PipelineId,
_document_id: DocumentId,
) {
let bounds = LayoutRect::new(LayoutPoint::zero(), builder.content_size());
let info = LayoutPrimitiveInfo::new(bounds);
builder.push_stacking_context(
&info,
+ None,
ScrollPolicy::Scrollable,
None,
TransformStyle::Flat,
None,
MixBlendMode::Normal,
Vec::new(),
);
--- a/gfx/webrender/res/base.glsl
+++ b/gfx/webrender/res/base.glsl
@@ -28,11 +28,10 @@
#endif
#ifdef WR_VERTEX_SHADER
#define varying out
#endif
#ifdef WR_FRAGMENT_SHADER
precision highp float;
-
#define varying in
#endif
--- a/gfx/webrender/res/brush.glsl
+++ b/gfx/webrender/res/brush.glsl
@@ -98,24 +98,24 @@ void main(void) {
local_segment_rect,
brush_prim.local_clip_rect,
float(brush.z),
scroll_node,
pic_task,
brush_prim.local_rect
);
- // TODO(gw): vLocalBounds may be referenced by
+ // TODO(gw): transform bounds may be referenced by
// the fragment shader when running in
// the alpha pass, even on non-transformed
// items. For now, just ensure it has no
// effect. We can tidy this up as we move
// more items to be brush shaders.
#ifdef WR_FEATURE_ALPHA_PASS
- vLocalBounds = vec4(vec2(-1000000.0), vec2(1000000.0));
+ init_transform_vs(vec4(vec2(-1000000.0), vec2(1000000.0)));
#endif
} else {
bvec4 edge_mask = notEqual(brush.edge_mask & ivec4(1, 2, 4, 8), ivec4(0));
bool do_perspective_interpolation = (brush.flags & BRUSH_FLAG_PERSPECTIVE_INTERPOLATION) != 0;
vi = write_transform_vertex(
local_segment_rect,
brush_prim.local_rect,
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/clip_scroll.glsl
@@ -0,0 +1,88 @@
+/* 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/. */
+
+#ifdef WR_VERTEX_SHADER
+#define VECS_PER_CLIP_SCROLL_NODE 9
+
+uniform HIGHP_SAMPLER_FLOAT sampler2D sClipScrollNodes;
+
+struct ClipScrollNode {
+ mat4 transform;
+ mat4 inv_transform;
+ bool is_axis_aligned;
+};
+
+ClipScrollNode fetch_clip_scroll_node(int index) {
+ ClipScrollNode node;
+
+ // Create a UV base coord for each 8 texels.
+ // This is required because trying to use an offset
+ // of more than 8 texels doesn't work on some versions
+ // of OSX.
+ ivec2 uv = get_fetch_uv(index, VECS_PER_CLIP_SCROLL_NODE);
+ ivec2 uv0 = ivec2(uv.x + 0, uv.y);
+ ivec2 uv1 = ivec2(uv.x + 8, uv.y);
+
+ node.transform[0] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(0, 0));
+ node.transform[1] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(1, 0));
+ node.transform[2] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(2, 0));
+ node.transform[3] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(3, 0));
+
+ node.inv_transform[0] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(4, 0));
+ node.inv_transform[1] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(5, 0));
+ node.inv_transform[2] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(6, 0));
+ node.inv_transform[3] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(7, 0));
+
+ vec4 misc = TEXEL_FETCH(sClipScrollNodes, uv1, 0, ivec2(0, 0));
+ node.is_axis_aligned = misc.x == 0.0;
+
+ return node;
+}
+
+// Return the intersection of the plane (set up by "normal" and "point")
+// with the ray (set up by "ray_origin" and "ray_dir"),
+// writing the resulting scaler into "t".
+bool ray_plane(vec3 normal, vec3 pt, vec3 ray_origin, vec3 ray_dir, out float t)
+{
+ float denom = dot(normal, ray_dir);
+ if (abs(denom) > 1e-6) {
+ vec3 d = pt - ray_origin;
+ t = dot(d, normal) / denom;
+ return t >= 0.0;
+ }
+
+ return false;
+}
+
+// Apply the inverse transform "inv_transform"
+// to the reference point "ref" in CSS space,
+// producing a local point on a ClipScrollNode plane,
+// set by a base point "a" and a normal "n".
+vec4 untransform(vec2 ref, vec3 n, vec3 a, mat4 inv_transform) {
+ vec3 p = vec3(ref, -10000.0);
+ vec3 d = vec3(0, 0, 1.0);
+
+ float t = 0.0;
+ // get an intersection of the ClipScrollNode plane with Z axis vector,
+ // originated from the "ref" point
+ ray_plane(n, a, p, d, t);
+ float z = p.z + d.z * t; // Z of the visible point on the ClipScrollNode
+
+ vec4 r = inv_transform * vec4(ref, z, 1.0);
+ return r;
+}
+
+// Given a CSS space position, transform it back into the ClipScrollNode space.
+vec4 get_node_pos(vec2 pos, ClipScrollNode node) {
+ // get a point on the scroll node plane
+ vec4 ah = node.transform * vec4(0.0, 0.0, 0.0, 1.0);
+ vec3 a = ah.xyz / ah.w;
+
+ // get the normal to the scroll node plane
+ vec3 n = transpose(mat3(node.inv_transform)) * vec3(0.0, 0.0, 1.0);
+ return untransform(pos, n, a, node.inv_transform);
+}
+
+#endif //WR_VERTEX_SHADER
+
--- a/gfx/webrender/res/clip_shared.glsl
+++ b/gfx/webrender/res/clip_shared.glsl
@@ -1,12 +1,14 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include rect,clip_scroll,render_task,resource_cache,snap,transform
+
#ifdef WR_VERTEX_SHADER
#define SEGMENT_ALL 0
#define SEGMENT_CORNER_TL 1
#define SEGMENT_CORNER_TR 2
#define SEGMENT_CORNER_BL 3
#define SEGMENT_CORNER_BR 4
@@ -46,34 +48,62 @@ RectWithSize intersect_rect(RectWithSize
return RectWithSize(p.xy, max(vec2(0.0), p.zw - p.xy));
}
// The transformed vertex function that always covers the whole clip area,
// which is the intersection of all clip instances of a given primitive
ClipVertexInfo write_clip_tile_vertex(RectWithSize local_clip_rect,
ClipScrollNode scroll_node,
ClipArea area) {
- vec2 actual_pos = area.screen_origin + aPosition.xy * area.common_data.task_rect.size;
+ vec2 device_pos = area.screen_origin + aPosition.xy * area.common_data.task_rect.size;
+ vec2 actual_pos = device_pos;
+
+ if (scroll_node.is_axis_aligned) {
+ vec4 snap_positions = compute_snap_positions(
+ scroll_node.transform,
+ local_clip_rect
+ );
+
+ vec2 snap_offsets = compute_snap_offset_impl(
+ device_pos,
+ scroll_node.transform,
+ local_clip_rect,
+ RectWithSize(snap_positions.xy, snap_positions.zw - snap_positions.xy),
+ snap_positions
+ );
+
+ actual_pos -= snap_offsets;
+ }
vec4 node_pos;
// Select the local position, based on whether we are rasterizing this
// clip mask in local- or sccreen-space.
if (area.local_space) {
node_pos = vec4(actual_pos / uDevicePixelRatio, 0.0, 1.0);
} else {
node_pos = get_node_pos(actual_pos / uDevicePixelRatio, scroll_node);
}
// compute the point position inside the scroll node, in CSS space
- vec2 vertex_pos = actual_pos +
+ vec2 vertex_pos = device_pos +
area.common_data.task_rect.p0 -
area.screen_origin;
gl_Position = uTransform * vec4(vertex_pos, 0.0, 1);
- vLocalBounds = vec4(local_clip_rect.p0, local_clip_rect.p0 + local_clip_rect.size);
+ init_transform_vs(vec4(local_clip_rect.p0, local_clip_rect.p0 + local_clip_rect.size));
ClipVertexInfo vi = ClipVertexInfo(node_pos.xyw, actual_pos, local_clip_rect);
return vi;
}
#endif //WR_VERTEX_SHADER
+
+#ifdef WR_FRAGMENT_SHADER
+
+//Note: identical to prim_shared
+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);
+}
+
+#endif //WR_FRAGMENT_SHADER
--- a/gfx/webrender/res/cs_clip_border.glsl
+++ b/gfx/webrender/res/cs_clip_border.glsl
@@ -1,13 +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/. */
-#include shared,prim_shared,clip_shared
+#include shared,clip_shared
varying vec3 vPos;
flat varying vec2 vClipCenter;
flat varying vec4 vPoint_Tangent0;
flat varying vec4 vPoint_Tangent1;
flat varying vec3 vDotParams;
--- a/gfx/webrender/res/cs_clip_box_shadow.glsl
+++ b/gfx/webrender/res/cs_clip_box_shadow.glsl
@@ -1,13 +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/. */
-#include shared,prim_shared,clip_shared
+#include shared,clip_shared
varying vec3 vPos;
varying vec2 vUv;
flat varying vec4 vUvBounds;
flat varying float vLayer;
flat varying vec4 vEdge;
flat varying vec4 vUvBounds_NoClamp;
flat varying float vClipMode;
@@ -98,21 +98,17 @@ void main(void) {
void main(void) {
vec2 local_pos = vPos.xy / vPos.z;
vec2 uv = clamp(vUv.xy, vec2(0.0), vEdge.xy);
uv += max(vec2(0.0), vUv.xy - vEdge.zw);
uv = mix(vUvBounds_NoClamp.xy, vUvBounds_NoClamp.zw, uv);
uv = clamp(uv, vUvBounds.xy, vUvBounds.zw);
- float in_shadow_rect = point_inside_rect(
- local_pos,
- vLocalBounds.xy,
- vLocalBounds.zw
- );
+ float in_shadow_rect = init_transform_rough_fs(local_pos);
float texel = TEX_SAMPLE(sColor0, vec3(uv, vLayer)).r;
float alpha = mix(texel, 1.0 - texel, vClipMode);
oFragColor = vec4(mix(vClipMode, alpha, in_shadow_rect));
}
#endif
--- a/gfx/webrender/res/cs_clip_image.glsl
+++ b/gfx/webrender/res/cs_clip_image.glsl
@@ -1,15 +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/. */
-#include shared,prim_shared,clip_shared
+#include shared,clip_shared
varying vec3 vPos;
+varying vec3 vClipMaskImageUv;
+
flat varying vec4 vClipMaskUvRect;
flat varying vec4 vClipMaskUvInnerRect;
flat varying float vLayer;
#ifdef WR_VERTEX_SHADER
struct ImageMaskData {
RectWithSize local_rect;
};
@@ -31,31 +33,31 @@ void main(void) {
ClipVertexInfo vi = write_clip_tile_vertex(local_rect,
scroll_node,
area);
vPos = vi.local_pos;
vLayer = res.layer;
- vClipMaskUv = vec3((vPos.xy / vPos.z - local_rect.p0) / local_rect.size, 0.0);
+ vClipMaskImageUv = vec3((vPos.xy / vPos.z - local_rect.p0) / local_rect.size, 0.0);
vec2 texture_size = vec2(textureSize(sColor0, 0));
vClipMaskUvRect = vec4(res.uv_rect.p0, res.uv_rect.p1 - res.uv_rect.p0) / texture_size.xyxy;
// applying a half-texel offset to the UV boundaries to prevent linear samples from the outside
vec4 inner_rect = vec4(res.uv_rect.p0, res.uv_rect.p1);
vClipMaskUvInnerRect = (inner_rect + vec4(0.5, 0.5, -0.5, -0.5)) / texture_size.xyxy;
}
#endif
#ifdef WR_FRAGMENT_SHADER
void main(void) {
float alpha = init_transform_fs(vPos.xy / vPos.z);
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 clamped_mask_uv = repeat_mask ? fract(vClipMaskImageUv.xy) :
+ clamp(vClipMaskImageUv.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, vec3(source_uv, vLayer)).r; //careful: texture has type A8
oFragColor = vec4(alpha * clip_alpha, 1.0, 1.0, 1.0);
}
#endif
--- a/gfx/webrender/res/cs_clip_line.glsl
+++ b/gfx/webrender/res/cs_clip_line.glsl
@@ -1,13 +1,18 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#include shared,prim_shared,clip_shared
+#include shared,clip_shared
+
+#define LINE_STYLE_SOLID 0
+#define LINE_STYLE_DOTTED 1
+#define LINE_STYLE_DASHED 2
+#define LINE_STYLE_WAVY 3
varying vec3 vLocalPos;
flat varying int vStyle;
flat varying float vAxisSelect;
flat varying vec4 vParams;
flat varying vec2 vLocalOrigin;
--- a/gfx/webrender/res/cs_clip_rectangle.glsl
+++ b/gfx/webrender/res/cs_clip_rectangle.glsl
@@ -1,43 +1,45 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#include shared,prim_shared,clip_shared,ellipse
+#include shared,clip_shared,ellipse
varying vec3 vPos;
flat varying float vClipMode;
flat varying vec4 vClipCenter_Radius_TL;
flat varying vec4 vClipCenter_Radius_TR;
flat varying vec4 vClipCenter_Radius_BL;
flat varying vec4 vClipCenter_Radius_BR;
#ifdef WR_VERTEX_SHADER
struct ClipRect {
RectWithSize rect;
vec4 mode;
};
ClipRect fetch_clip_rect(ivec2 address) {
vec4 data[2] = fetch_from_resource_cache_2_direct(address);
- return ClipRect(RectWithSize(data[0].xy, data[0].zw), data[1]);
+ ClipRect rect = ClipRect(RectWithSize(data[0].xy, data[0].zw), data[1]);
+ return rect;
}
struct ClipCorner {
RectWithSize rect;
vec4 outer_inner_radius;
};
// index is of type float instead of int because using an int led to shader
// miscompilations with a macOS 10.12 Intel driver.
ClipCorner fetch_clip_corner(ivec2 address, float index) {
address += ivec2(2 + 2 * int(index), 0);
vec4 data[2] = fetch_from_resource_cache_2_direct(address);
- return ClipCorner(RectWithSize(data[0].xy, data[0].zw), data[1]);
+ ClipCorner corner = ClipCorner(RectWithSize(data[0].xy, data[0].zw), data[1]);
+ return corner;
}
struct ClipData {
ClipRect rect;
ClipCorner top_left;
ClipCorner top_right;
ClipCorner bottom_left;
ClipCorner bottom_right;
--- a/gfx/webrender/res/prim_shared.glsl
+++ b/gfx/webrender/res/prim_shared.glsl
@@ -1,31 +1,24 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#include rect
+#include rect,clip_scroll,render_task,resource_cache,snap,transform
#define EXTEND_MODE_CLAMP 0
#define EXTEND_MODE_REPEAT 1
-#define LINE_STYLE_SOLID 0
-#define LINE_STYLE_DOTTED 1
-#define LINE_STYLE_DASHED 2
-#define LINE_STYLE_WAVY 3
-
#define SUBPX_DIR_NONE 0
#define SUBPX_DIR_HORIZONTAL 1
#define SUBPX_DIR_VERTICAL 2
#define RASTER_LOCAL 0
#define RASTER_SCREEN 1
-#define EPSILON 0.0001
-
uniform sampler2DArray sCacheA8;
uniform sampler2DArray sCacheRGBA8;
// An A8 target for standalone tasks that is available to all passes.
uniform sampler2DArray sSharedCacheA8;
uniform sampler2D sGradients;
@@ -36,274 +29,37 @@ vec2 clamp_rect(vec2 pt, RectWithSize re
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;
-flat varying vec4 vLocalBounds;
-// TODO(gw): This is here temporarily while we have
-// both GPU store and cache. When the GPU
-// store code is removed, we can change the
-// PrimitiveInstance instance structure to
-// use 2x unsigned shorts as vertex attributes
-// instead of an int, and encode the UV directly
-// in the vertices.
-ivec2 get_resource_cache_uv(int address) {
- return ivec2(address % WR_MAX_VERTEX_TEXTURE_WIDTH,
- address / WR_MAX_VERTEX_TEXTURE_WIDTH);
-}
-
-uniform HIGHP_SAMPLER_FLOAT sampler2D sResourceCache;
-
-vec4[2] fetch_from_resource_cache_2_direct(ivec2 address) {
- return vec4[2](
- TEXEL_FETCH(sResourceCache, address, 0, ivec2(0, 0)),
- TEXEL_FETCH(sResourceCache, address, 0, ivec2(1, 0))
- );
-}
-
-vec4[2] fetch_from_resource_cache_2(int address) {
- ivec2 uv = get_resource_cache_uv(address);
- return vec4[2](
- TEXEL_FETCH(sResourceCache, uv, 0, ivec2(0, 0)),
- TEXEL_FETCH(sResourceCache, uv, 0, ivec2(1, 0))
- );
-}
#ifdef WR_VERTEX_SHADER
-#define VECS_PER_CLIP_SCROLL_NODE 9
#define VECS_PER_LOCAL_CLIP_RECT 1
-#define VECS_PER_RENDER_TASK 2
#define VECS_PER_PRIM_HEADER 2
#define VECS_PER_TEXT_RUN 3
#define VECS_PER_GRADIENT_STOP 2
-uniform HIGHP_SAMPLER_FLOAT sampler2D sClipScrollNodes;
uniform HIGHP_SAMPLER_FLOAT sampler2D sLocalClipRects;
-uniform HIGHP_SAMPLER_FLOAT sampler2D sRenderTasks;
// Instanced attributes
in ivec4 aData0;
in ivec4 aData1;
-// get_fetch_uv is a macro to work around a macOS Intel driver parsing bug.
-// TODO: convert back to a function once the driver issues are resolved, if ever.
-// https://github.com/servo/webrender/pull/623
-// https://github.com/servo/servo/issues/13953
-// Do the division with unsigned ints because that's more efficient with D3D
-#define get_fetch_uv(i, vpi) ivec2(int(uint(vpi) * (uint(i) % uint(WR_MAX_VERTEX_TEXTURE_WIDTH/vpi))), int(uint(i) / uint(WR_MAX_VERTEX_TEXTURE_WIDTH/vpi)))
-
-
-vec4[8] fetch_from_resource_cache_8(int address) {
- ivec2 uv = get_resource_cache_uv(address);
- return vec4[8](
- TEXEL_FETCH(sResourceCache, uv, 0, ivec2(0, 0)),
- TEXEL_FETCH(sResourceCache, uv, 0, ivec2(1, 0)),
- TEXEL_FETCH(sResourceCache, uv, 0, ivec2(2, 0)),
- TEXEL_FETCH(sResourceCache, uv, 0, ivec2(3, 0)),
- TEXEL_FETCH(sResourceCache, uv, 0, ivec2(4, 0)),
- TEXEL_FETCH(sResourceCache, uv, 0, ivec2(5, 0)),
- TEXEL_FETCH(sResourceCache, uv, 0, ivec2(6, 0)),
- TEXEL_FETCH(sResourceCache, uv, 0, ivec2(7, 0))
- );
-}
-
-vec4[3] fetch_from_resource_cache_3(int address) {
- ivec2 uv = get_resource_cache_uv(address);
- return vec4[3](
- TEXEL_FETCH(sResourceCache, uv, 0, ivec2(0, 0)),
- TEXEL_FETCH(sResourceCache, uv, 0, ivec2(1, 0)),
- TEXEL_FETCH(sResourceCache, uv, 0, ivec2(2, 0))
- );
-}
-
-vec4[3] fetch_from_resource_cache_3_direct(ivec2 address) {
- return vec4[3](
- TEXEL_FETCH(sResourceCache, address, 0, ivec2(0, 0)),
- TEXEL_FETCH(sResourceCache, address, 0, ivec2(1, 0)),
- TEXEL_FETCH(sResourceCache, address, 0, ivec2(2, 0))
- );
-}
-
-vec4[4] fetch_from_resource_cache_4_direct(ivec2 address) {
- return vec4[4](
- TEXEL_FETCH(sResourceCache, address, 0, ivec2(0, 0)),
- TEXEL_FETCH(sResourceCache, address, 0, ivec2(1, 0)),
- TEXEL_FETCH(sResourceCache, address, 0, ivec2(2, 0)),
- TEXEL_FETCH(sResourceCache, address, 0, ivec2(3, 0))
- );
-}
-
-vec4[4] fetch_from_resource_cache_4(int address) {
- ivec2 uv = get_resource_cache_uv(address);
- return vec4[4](
- TEXEL_FETCH(sResourceCache, uv, 0, ivec2(0, 0)),
- TEXEL_FETCH(sResourceCache, uv, 0, ivec2(1, 0)),
- TEXEL_FETCH(sResourceCache, uv, 0, ivec2(2, 0)),
- TEXEL_FETCH(sResourceCache, uv, 0, ivec2(3, 0))
- );
-}
-
-vec4 fetch_from_resource_cache_1_direct(ivec2 address) {
- return texelFetch(sResourceCache, address, 0);
-}
-
-vec4 fetch_from_resource_cache_1(int address) {
- ivec2 uv = get_resource_cache_uv(address);
- return texelFetch(sResourceCache, uv, 0);
-}
-
-struct ClipScrollNode {
- mat4 transform;
- mat4 inv_transform;
- bool is_axis_aligned;
-};
-
-ClipScrollNode fetch_clip_scroll_node(int index) {
- ClipScrollNode node;
-
- // Create a UV base coord for each 8 texels.
- // This is required because trying to use an offset
- // of more than 8 texels doesn't work on some versions
- // of OSX.
- ivec2 uv = get_fetch_uv(index, VECS_PER_CLIP_SCROLL_NODE);
- ivec2 uv0 = ivec2(uv.x + 0, uv.y);
- ivec2 uv1 = ivec2(uv.x + 8, uv.y);
-
- node.transform[0] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(0, 0));
- node.transform[1] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(1, 0));
- node.transform[2] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(2, 0));
- node.transform[3] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(3, 0));
-
- node.inv_transform[0] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(4, 0));
- node.inv_transform[1] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(5, 0));
- node.inv_transform[2] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(6, 0));
- node.inv_transform[3] = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(7, 0));
-
- vec4 misc = TEXEL_FETCH(sClipScrollNodes, uv1, 0, ivec2(0, 0));
- node.is_axis_aligned = misc.x == 0.0;
-
- return node;
-}
-
RectWithSize fetch_clip_chain_rect(int index) {
ivec2 uv = get_fetch_uv(index, VECS_PER_LOCAL_CLIP_RECT);
vec4 rect = TEXEL_FETCH(sLocalClipRects, uv, 0, ivec2(0, 0));
return RectWithSize(rect.xy, rect.zw);
}
-struct RenderTaskCommonData {
- RectWithSize task_rect;
- float texture_layer_index;
-};
-
-struct RenderTaskData {
- RenderTaskCommonData common_data;
- vec3 data1;
-};
-
-RenderTaskData fetch_render_task_data(int index) {
- ivec2 uv = get_fetch_uv(index, VECS_PER_RENDER_TASK);
-
- vec4 texel0 = TEXEL_FETCH(sRenderTasks, uv, 0, ivec2(0, 0));
- vec4 texel1 = TEXEL_FETCH(sRenderTasks, uv, 0, ivec2(1, 0));
-
- RectWithSize task_rect = RectWithSize(
- texel0.xy,
- texel0.zw
- );
-
- RenderTaskCommonData common_data = RenderTaskCommonData(
- task_rect,
- texel1.x
- );
-
- RenderTaskData data = RenderTaskData(
- common_data,
- texel1.yzw
- );
-
- return data;
-}
-
-RenderTaskCommonData fetch_render_task_common_data(int index) {
- ivec2 uv = get_fetch_uv(index, VECS_PER_RENDER_TASK);
-
- vec4 texel0 = TEXEL_FETCH(sRenderTasks, uv, 0, ivec2(0, 0));
- vec4 texel1 = TEXEL_FETCH(sRenderTasks, uv, 0, ivec2(1, 0));
-
- RectWithSize task_rect = RectWithSize(
- texel0.xy,
- texel0.zw
- );
-
- RenderTaskCommonData data = RenderTaskCommonData(
- task_rect,
- texel1.x
- );
-
- return data;
-}
-
-#define PIC_TYPE_IMAGE 1
-#define PIC_TYPE_TEXT_SHADOW 2
-
-/*
- The dynamic picture that this brush exists on. Right now, it
- contains minimal information. In the future, it will describe
- the transform mode of primitives on this picture, among other things.
- */
-struct PictureTask {
- RenderTaskCommonData common_data;
- vec2 content_origin;
-};
-
-PictureTask fetch_picture_task(int address) {
- RenderTaskData task_data = fetch_render_task_data(address);
-
- PictureTask task = PictureTask(
- task_data.common_data,
- task_data.data1.xy
- );
-
- return task;
-}
-
-struct ClipArea {
- RenderTaskCommonData common_data;
- vec2 screen_origin;
- bool local_space;
-};
-
-ClipArea fetch_clip_area(int index) {
- ClipArea area;
-
- if (index == 0x7FFF) { //special sentinel task index
- area.common_data = RenderTaskCommonData(
- RectWithSize(vec2(0.0), vec2(0.0)),
- 0.0
- );
- area.screen_origin = vec2(0.0);
- area.local_space = false;
- } else {
- RenderTaskData task_data = fetch_render_task_data(index);
-
- area.common_data = task_data.common_data;
- area.screen_origin = task_data.data1.xy;
- area.local_space = task_data.data1.z == 0.0;
- }
-
- return area;
-}
-
struct Glyph {
vec2 offset;
};
Glyph fetch_glyph(int specific_prim_address,
int glyph_index,
int subpx_dir) {
// Two glyphs are packed in each texel in the GPU cache.
@@ -437,87 +193,16 @@ Primitive load_primitive() {
prim.user_data0 = pi.user_data0;
prim.user_data1 = pi.user_data1;
prim.user_data2 = pi.user_data2;
prim.z = float(pi.z);
return prim;
}
-// Return the intersection of the plane (set up by "normal" and "point")
-// with the ray (set up by "ray_origin" and "ray_dir"),
-// writing the resulting scaler into "t".
-bool ray_plane(vec3 normal, vec3 pt, vec3 ray_origin, vec3 ray_dir, out float t)
-{
- float denom = dot(normal, ray_dir);
- if (abs(denom) > 1e-6) {
- vec3 d = pt - ray_origin;
- t = dot(d, normal) / denom;
- return t >= 0.0;
- }
-
- return false;
-}
-
-// Apply the inverse transform "inv_transform"
-// to the reference point "ref" in CSS space,
-// producing a local point on a ClipScrollNode plane,
-// set by a base point "a" and a normal "n".
-vec4 untransform(vec2 ref, vec3 n, vec3 a, mat4 inv_transform) {
- vec3 p = vec3(ref, -10000.0);
- vec3 d = vec3(0, 0, 1.0);
-
- float t = 0.0;
- // get an intersection of the ClipScrollNode plane with Z axis vector,
- // originated from the "ref" point
- ray_plane(n, a, p, d, t);
- float z = p.z + d.z * t; // Z of the visible point on the ClipScrollNode
-
- vec4 r = inv_transform * vec4(ref, z, 1.0);
- return r;
-}
-
-// Given a CSS space position, transform it back into the ClipScrollNode space.
-vec4 get_node_pos(vec2 pos, ClipScrollNode node) {
- // get a point on the scroll node plane
- vec4 ah = node.transform * vec4(0.0, 0.0, 0.0, 1.0);
- vec3 a = ah.xyz / ah.w;
-
- // get the normal to the scroll node plane
- vec3 n = transpose(mat3(node.inv_transform)) * vec3(0.0, 0.0, 1.0);
- return untransform(pos, n, a, node.inv_transform);
-}
-
-// Compute a snapping offset in world space (adjusted to pixel ratio),
-// given local position on the scroll_node and a snap rectangle.
-vec2 compute_snap_offset(vec2 local_pos,
- mat4 transform,
- RectWithSize snap_rect) {
- // Ensure that the snap rect is at *least* one device pixel in size.
- // TODO(gw): It's not clear to me that this is "correct". Specifically,
- // how should it interact with sub-pixel snap rects when there
- // is a scroll_node transform with scale present? But it does fix
- // the test cases we have in Servo that are failing without it
- // and seem better than not having this at all.
- snap_rect.size = max(snap_rect.size, vec2(1.0 / uDevicePixelRatio));
-
- // Transform the snap corners to the world space.
- vec4 world_snap_p0 = transform * vec4(snap_rect.p0, 0.0, 1.0);
- vec4 world_snap_p1 = transform * vec4(snap_rect.p0 + snap_rect.size, 0.0, 1.0);
- // Snap bounds in world coordinates, adjusted for pixel ratio. XY = top left, ZW = bottom right
- vec4 world_snap = uDevicePixelRatio * vec4(world_snap_p0.xy, world_snap_p1.xy) /
- vec4(world_snap_p0.ww, world_snap_p1.ww);
- /// World offsets applied to the corners of the snap rectangle.
- vec4 snap_offsets = floor(world_snap + 0.5) - world_snap;
-
- /// Compute the position of this vertex inside the snap rectangle.
- vec2 normalized_snap_pos = (local_pos - snap_rect.p0) / snap_rect.size;
- /// Compute the actual world offset for this vertex needed to make it snap.
- return mix(snap_offsets.xy, snap_offsets.zw, normalized_snap_pos);
-}
struct VertexInfo {
vec2 local_pos;
vec2 screen_pos;
float w;
vec2 snapped_device_pos;
};
@@ -635,21 +320,21 @@ VertexInfo write_transform_vertex(RectWi
// We want the world space coords to be perspective divided by W.
// We also want that to apply to any interpolators. However, we
// want a constant Z across the primitive, since we're using it
// for draw ordering - so scale by the W coord to ensure this.
vec4 final_pos = vec4(device_pos + task_offset, z, 1.0) * world_pos.w;
gl_Position = uTransform * final_pos;
- vLocalBounds = mix(
+ init_transform_vs(mix(
vec4(prim_rect.p0, prim_rect.p1),
vec4(segment_rect.p0, segment_rect.p1),
clip_edge_mask
- );
+ ));
VertexInfo vi = VertexInfo(
local_pos,
device_pos,
world_pos.w,
device_pos
);
@@ -676,35 +361,16 @@ struct GlyphResource {
float scale;
};
GlyphResource fetch_glyph_resource(int address) {
vec4 data[2] = fetch_from_resource_cache_2(address);
return GlyphResource(data[0], data[1].x, data[1].yz, data[1].w);
}
-struct ImageResource {
- RectWithEndpoint uv_rect;
- float layer;
- vec3 user_data;
-};
-
-ImageResource fetch_image_resource(int address) {
- //Note: number of blocks has to match `renderer::BLOCKS_PER_UV_RECT`
- vec4 data[2] = fetch_from_resource_cache_2(address);
- RectWithEndpoint uv_rect = RectWithEndpoint(data[0].xy, data[0].zw);
- return ImageResource(uv_rect, data[1].x, data[1].yzw);
-}
-
-ImageResource fetch_image_resource_direct(ivec2 address) {
- vec4 data[2] = fetch_from_resource_cache_2_direct(address);
- RectWithEndpoint uv_rect = RectWithEndpoint(data[0].xy, data[0].zw);
- return ImageResource(uv_rect, data[1].x, data[1].yzw);
-}
-
struct TextRun {
vec4 color;
vec4 bg_color;
vec2 offset;
};
TextRun fetch_text_run(int address) {
vec4 data[3] = fetch_from_resource_cache_3(address);
@@ -730,84 +396,16 @@ void write_clip(vec2 global_pos, ClipAre
area.common_data.task_rect.p0 + area.common_data.task_rect.size
);
vClipMaskUv = vec3(uv, area.common_data.texture_layer_index);
}
#endif //WR_VERTEX_SHADER
#ifdef WR_FRAGMENT_SHADER
-/// Find the appropriate half range to apply the AA approximation over.
-/// This range represents a coefficient to go from one CSS pixel to half a device pixel.
-float compute_aa_range(vec2 position) {
- // The constant factor is chosen to compensate for the fact that length(fw) is equal
- // to sqrt(2) times the device pixel ratio in the typical case. 0.5/sqrt(2) = 0.35355.
- //
- // This coefficient is chosen to ensure that any sample 0.5 pixels or more inside of
- // the shape has no anti-aliasing applied to it (since pixels are sampled at their center,
- // such a pixel (axis aligned) is fully inside the border). We need this so that antialiased
- // curves properly connect with non-antialiased vertical or horizontal lines, among other things.
- //
- // Lines over a half-pixel away from the pixel center *can* intersect with the pixel square;
- // indeed, unless they are horizontal or vertical, they are guaranteed to. However, choosing
- // a nonzero area for such pixels causes noticeable artifacts at the junction between an anti-
- // aliased corner and a straight edge.
- //
- // We may want to adjust this constant in specific scenarios (for example keep the principled
- // value for straight edges where we want pixel-perfect equivalence with non antialiased lines
- // when axis aligned, while selecting a larger and smoother aa range on curves).
- return 0.35355 * length(fwidth(position));
-}
-
-/// Return the blending coefficient for distance antialiasing.
-///
-/// 0.0 means inside the shape, 1.0 means outside.
-///
-/// This cubic polynomial approximates the area of a 1x1 pixel square under a
-/// line, given the signed Euclidean distance from the center of the square to
-/// that line. Calculating the *exact* area would require taking into account
-/// not only this distance but also the angle of the line. However, in
-/// practice, this complexity is not required, as the area is roughly the same
-/// regardless of the angle.
-///
-/// The coefficients of this polynomial were determined through least-squares
-/// regression and are accurate to within 2.16% of the total area of the pixel
-/// square 95% of the time, with a maximum error of 3.53%.
-///
-/// See the comments in `compute_aa_range()` for more information on the
-/// cutoff values of -0.5 and 0.5.
-float distance_aa(float aa_range, float signed_distance) {
- float dist = 0.5 * signed_distance / aa_range;
- if (dist <= -0.5 + EPSILON)
- return 1.0;
- if (dist >= 0.5 - EPSILON)
- return 0.0;
- return 0.5 + dist * (0.8431027 * dist * dist - 1.14453603);
-}
-
-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));
-}
-
-float init_transform_fs(vec2 local_pos) {
- // Get signed distance from local rect bounds.
- float d = signed_distance_rect(
- local_pos,
- vLocalBounds.xy,
- vLocalBounds.zw
- );
-
- // Find the appropriate distance to apply the AA smoothstep over.
- float aa_range = compute_aa_range(local_pos);
-
- // Only apply AA to fragments outside the signed distance field.
- return distance_aa(aa_range, d);
-}
-
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:
all(inside) ? texelFetch(sCacheA8, ivec3(vClipMaskUv), 0).r : 0.0;
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/render_task.glsl
@@ -0,0 +1,115 @@
+/* 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/. */
+
+
+#ifdef WR_VERTEX_SHADER
+#define VECS_PER_RENDER_TASK 2
+
+uniform HIGHP_SAMPLER_FLOAT sampler2D sRenderTasks;
+
+struct RenderTaskCommonData {
+ RectWithSize task_rect;
+ float texture_layer_index;
+};
+
+struct RenderTaskData {
+ RenderTaskCommonData common_data;
+ vec3 data1;
+};
+
+RenderTaskData fetch_render_task_data(int index) {
+ ivec2 uv = get_fetch_uv(index, VECS_PER_RENDER_TASK);
+
+ vec4 texel0 = TEXEL_FETCH(sRenderTasks, uv, 0, ivec2(0, 0));
+ vec4 texel1 = TEXEL_FETCH(sRenderTasks, uv, 0, ivec2(1, 0));
+
+ RectWithSize task_rect = RectWithSize(
+ texel0.xy,
+ texel0.zw
+ );
+
+ RenderTaskCommonData common_data = RenderTaskCommonData(
+ task_rect,
+ texel1.x
+ );
+
+ RenderTaskData data = RenderTaskData(
+ common_data,
+ texel1.yzw
+ );
+
+ return data;
+}
+
+RenderTaskCommonData fetch_render_task_common_data(int index) {
+ ivec2 uv = get_fetch_uv(index, VECS_PER_RENDER_TASK);
+
+ vec4 texel0 = TEXEL_FETCH(sRenderTasks, uv, 0, ivec2(0, 0));
+ vec4 texel1 = TEXEL_FETCH(sRenderTasks, uv, 0, ivec2(1, 0));
+
+ RectWithSize task_rect = RectWithSize(
+ texel0.xy,
+ texel0.zw
+ );
+
+ RenderTaskCommonData data = RenderTaskCommonData(
+ task_rect,
+ texel1.x
+ );
+
+ return data;
+}
+
+#define PIC_TYPE_IMAGE 1
+#define PIC_TYPE_TEXT_SHADOW 2
+
+/*
+ The dynamic picture that this brush exists on. Right now, it
+ contains minimal information. In the future, it will describe
+ the transform mode of primitives on this picture, among other things.
+ */
+struct PictureTask {
+ RenderTaskCommonData common_data;
+ vec2 content_origin;
+};
+
+PictureTask fetch_picture_task(int address) {
+ RenderTaskData task_data = fetch_render_task_data(address);
+
+ PictureTask task = PictureTask(
+ task_data.common_data,
+ task_data.data1.xy
+ );
+
+ return task;
+}
+
+struct ClipArea {
+ RenderTaskCommonData common_data;
+ vec2 screen_origin;
+ bool local_space;
+};
+
+ClipArea fetch_clip_area(int index) {
+ ClipArea area;
+
+ if (index == 0x7FFF) { //special sentinel task index
+ area.common_data = RenderTaskCommonData(
+ RectWithSize(vec2(0.0), vec2(0.0)),
+ 0.0
+ );
+ area.screen_origin = vec2(0.0);
+ area.local_space = false;
+ } else {
+ RenderTaskData task_data = fetch_render_task_data(index);
+
+ area.common_data = task_data.common_data;
+ area.screen_origin = task_data.data1.xy;
+ area.local_space = task_data.data1.z == 0.0;
+ }
+
+ return area;
+}
+
+#endif //WR_VERTEX_SHADER
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/resource_cache.glsl
@@ -0,0 +1,116 @@
+/* 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 HIGHP_SAMPLER_FLOAT sampler2D sResourceCache;
+
+// TODO(gw): This is here temporarily while we have
+// both GPU store and cache. When the GPU
+// store code is removed, we can change the
+// PrimitiveInstance instance structure to
+// use 2x unsigned shorts as vertex attributes
+// instead of an int, and encode the UV directly
+// in the vertices.
+ivec2 get_resource_cache_uv(int address) {
+ return ivec2(address % WR_MAX_VERTEX_TEXTURE_WIDTH,
+ address / WR_MAX_VERTEX_TEXTURE_WIDTH);
+}
+
+vec4[2] fetch_from_resource_cache_2_direct(ivec2 address) {
+ return vec4[2](
+ TEXEL_FETCH(sResourceCache, address, 0, ivec2(0, 0)),
+ TEXEL_FETCH(sResourceCache, address, 0, ivec2(1, 0))
+ );
+}
+
+vec4[2] fetch_from_resource_cache_2(int address) {
+ ivec2 uv = get_resource_cache_uv(address);
+ return vec4[2](
+ TEXEL_FETCH(sResourceCache, uv, 0, ivec2(0, 0)),
+ TEXEL_FETCH(sResourceCache, uv, 0, ivec2(1, 0))
+ );
+}
+
+#ifdef WR_VERTEX_SHADER
+
+vec4[8] fetch_from_resource_cache_8(int address) {
+ ivec2 uv = get_resource_cache_uv(address);
+ return vec4[8](
+ TEXEL_FETCH(sResourceCache, uv, 0, ivec2(0, 0)),
+ TEXEL_FETCH(sResourceCache, uv, 0, ivec2(1, 0)),
+ TEXEL_FETCH(sResourceCache, uv, 0, ivec2(2, 0)),
+ TEXEL_FETCH(sResourceCache, uv, 0, ivec2(3, 0)),
+ TEXEL_FETCH(sResourceCache, uv, 0, ivec2(4, 0)),
+ TEXEL_FETCH(sResourceCache, uv, 0, ivec2(5, 0)),
+ TEXEL_FETCH(sResourceCache, uv, 0, ivec2(6, 0)),
+ TEXEL_FETCH(sResourceCache, uv, 0, ivec2(7, 0))
+ );
+}
+
+vec4[3] fetch_from_resource_cache_3(int address) {
+ ivec2 uv = get_resource_cache_uv(address);
+ return vec4[3](
+ TEXEL_FETCH(sResourceCache, uv, 0, ivec2(0, 0)),
+ TEXEL_FETCH(sResourceCache, uv, 0, ivec2(1, 0)),
+ TEXEL_FETCH(sResourceCache, uv, 0, ivec2(2, 0))
+ );
+}
+
+vec4[3] fetch_from_resource_cache_3_direct(ivec2 address) {
+ return vec4[3](
+ TEXEL_FETCH(sResourceCache, address, 0, ivec2(0, 0)),
+ TEXEL_FETCH(sResourceCache, address, 0, ivec2(1, 0)),
+ TEXEL_FETCH(sResourceCache, address, 0, ivec2(2, 0))
+ );
+}
+
+vec4[4] fetch_from_resource_cache_4_direct(ivec2 address) {
+ return vec4[4](
+ TEXEL_FETCH(sResourceCache, address, 0, ivec2(0, 0)),
+ TEXEL_FETCH(sResourceCache, address, 0, ivec2(1, 0)),
+ TEXEL_FETCH(sResourceCache, address, 0, ivec2(2, 0)),
+ TEXEL_FETCH(sResourceCache, address, 0, ivec2(3, 0))
+ );
+}
+
+vec4[4] fetch_from_resource_cache_4(int address) {
+ ivec2 uv = get_resource_cache_uv(address);
+ return vec4[4](
+ TEXEL_FETCH(sResourceCache, uv, 0, ivec2(0, 0)),
+ TEXEL_FETCH(sResourceCache, uv, 0, ivec2(1, 0)),
+ TEXEL_FETCH(sResourceCache, uv, 0, ivec2(2, 0)),
+ TEXEL_FETCH(sResourceCache, uv, 0, ivec2(3, 0))
+ );
+}
+
+vec4 fetch_from_resource_cache_1_direct(ivec2 address) {
+ return texelFetch(sResourceCache, address, 0);
+}
+
+vec4 fetch_from_resource_cache_1(int address) {
+ ivec2 uv = get_resource_cache_uv(address);
+ return texelFetch(sResourceCache, uv, 0);
+}
+
+//TODO: image resource is too specific for this module
+
+struct ImageResource {
+ RectWithEndpoint uv_rect;
+ float layer;
+ vec3 user_data;
+};
+
+ImageResource fetch_image_resource(int address) {
+ //Note: number of blocks has to match `renderer::BLOCKS_PER_UV_RECT`
+ vec4 data[2] = fetch_from_resource_cache_2(address);
+ RectWithEndpoint uv_rect = RectWithEndpoint(data[0].xy, data[0].zw);
+ return ImageResource(uv_rect, data[1].x, data[1].yzw);
+}
+
+ImageResource fetch_image_resource_direct(ivec2 address) {
+ vec4 data[2] = fetch_from_resource_cache_2_direct(address);
+ RectWithEndpoint uv_rect = RectWithEndpoint(data[0].xy, data[0].zw);
+ return ImageResource(uv_rect, data[1].x, data[1].yzw);
+}
+
+#endif //WR_VERTEX_SHADER
--- a/gfx/webrender/res/shared.glsl
+++ b/gfx/webrender/res/shared.glsl
@@ -29,16 +29,23 @@
uniform int uMode;
// Uniform inputs
uniform mat4 uTransform; // Orthographic projection
uniform float uDevicePixelRatio;
// Attribute inputs
in vec3 aPosition;
+
+ // get_fetch_uv is a macro to work around a macOS Intel driver parsing bug.
+ // TODO: convert back to a function once the driver issues are resolved, if ever.
+ // https://github.com/servo/webrender/pull/623
+ // https://github.com/servo/servo/issues/13953
+ // Do the division with unsigned ints because that's more efficient with D3D
+ #define get_fetch_uv(i, vpi) ivec2(int(uint(vpi) * (uint(i) % uint(WR_MAX_VERTEX_TEXTURE_WIDTH/vpi))), int(uint(i) / uint(WR_MAX_VERTEX_TEXTURE_WIDTH/vpi)))
#endif
//======================================================================================
// Fragment shader attributes and uniforms
//======================================================================================
#ifdef WR_FRAGMENT_SHADER
// Uniform inputs
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/snap.glsl
@@ -0,0 +1,63 @@
+/* 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/. */
+
+#ifdef WR_VERTEX_SHADER
+
+vec4 compute_snap_positions(mat4 transform, RectWithSize snap_rect) {
+ // Ensure that the snap rect is at *least* one device pixel in size.
+ // TODO(gw): It's not clear to me that this is "correct". Specifically,
+ // how should it interact with sub-pixel snap rects when there
+ // is a scroll_node transform with scale present? But it does fix
+ // the test cases we have in Servo that are failing without it
+ // and seem better than not having this at all.
+ snap_rect.size = max(snap_rect.size, vec2(1.0 / uDevicePixelRatio));
+
+ // Transform the snap corners to the world space.
+ vec4 world_snap_p0 = transform * vec4(snap_rect.p0, 0.0, 1.0);
+ vec4 world_snap_p1 = transform * vec4(snap_rect.p0 + snap_rect.size, 0.0, 1.0);
+ // Snap bounds in world coordinates, adjusted for pixel ratio. XY = top left, ZW = bottom right
+ vec4 world_snap = uDevicePixelRatio * vec4(world_snap_p0.xy, world_snap_p1.xy) /
+ vec4(world_snap_p0.ww, world_snap_p1.ww);
+ return world_snap;
+}
+
+vec2 compute_snap_offset_impl(
+ vec2 reference_pos,
+ mat4 transform,
+ RectWithSize snap_rect,
+ RectWithSize reference_rect,
+ vec4 snap_positions) {
+
+ /// World offsets applied to the corners of the snap rectangle.
+ vec4 snap_offsets = floor(snap_positions + 0.5) - snap_positions;
+
+ /// Compute the position of this vertex inside the snap rectangle.
+ vec2 normalized_snap_pos = (reference_pos - reference_rect.p0) / reference_rect.size;
+
+ /// Compute the actual world offset for this vertex needed to make it snap.
+ return mix(snap_offsets.xy, snap_offsets.zw, normalized_snap_pos);
+}
+
+// Compute a snapping offset in world space (adjusted to pixel ratio),
+// given local position on the scroll_node and a snap rectangle.
+vec2 compute_snap_offset(vec2 local_pos,
+ mat4 transform,
+ RectWithSize snap_rect) {
+ vec4 snap_positions = compute_snap_positions(
+ transform,
+ snap_rect
+ );
+
+ vec2 snap_offsets = compute_snap_offset_impl(
+ local_pos,
+ transform,
+ snap_rect,
+ snap_rect,
+ snap_positions
+ );
+
+ return snap_offsets;
+}
+
+#endif //WR_VERTEX_SHADER
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/res/transform.glsl
@@ -0,0 +1,95 @@
+/* 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/. */
+
+#define EPSILON 0.0001
+
+flat varying vec4 vTransformBounds;
+
+#ifdef WR_VERTEX_SHADER
+
+void init_transform_vs(vec4 local_bounds) {
+ vTransformBounds = local_bounds;
+}
+
+#endif //WR_VERTEX_SHADER
+
+#ifdef WR_FRAGMENT_SHADER
+
+/// Find the appropriate half range to apply the AA approximation over.
+/// This range represents a coefficient to go from one CSS pixel to half a device pixel.
+float compute_aa_range(vec2 position) {
+ // The constant factor is chosen to compensate for the fact that length(fw) is equal
+ // to sqrt(2) times the device pixel ratio in the typical case. 0.5/sqrt(2) = 0.35355.
+ //
+ // This coefficient is chosen to ensure that any sample 0.5 pixels or more inside of
+ // the shape has no anti-aliasing applied to it (since pixels are sampled at their center,
+ // such a pixel (axis aligned) is fully inside the border). We need this so that antialiased
+ // curves properly connect with non-antialiased vertical or horizontal lines, among other things.
+ //
+ // Lines over a half-pixel away from the pixel center *can* intersect with the pixel square;
+ // indeed, unless they are horizontal or vertical, they are guaranteed to. However, choosing
+ // a nonzero area for such pixels causes noticeable artifacts at the junction between an anti-
+ // aliased corner and a straight edge.
+ //
+ // We may want to adjust this constant in specific scenarios (for example keep the principled
+ // value for straight edges where we want pixel-perfect equivalence with non antialiased lines
+ // when axis aligned, while selecting a larger and smoother aa range on curves).
+ return 0.35355 * length(fwidth(position));
+}
+
+/// Return the blending coefficient for distance antialiasing.
+///
+/// 0.0 means inside the shape, 1.0 means outside.
+///
+/// This cubic polynomial approximates the area of a 1x1 pixel square under a
+/// line, given the signed Euclidean distance from the center of the square to
+/// that line. Calculating the *exact* area would require taking into account
+/// not only this distance but also the angle of the line. However, in
+/// practice, this complexity is not required, as the area is roughly the same
+/// regardless of the angle.
+///
+/// The coefficients of this polynomial were determined through least-squares
+/// regression and are accurate to within 2.16% of the total area of the pixel
+/// square 95% of the time, with a maximum error of 3.53%.
+///
+/// See the comments in `compute_aa_range()` for more information on the
+/// cutoff values of -0.5 and 0.5.
+float distance_aa(float aa_range, float signed_distance) {
+ float dist = 0.5 * signed_distance / aa_range;
+ if (dist <= -0.5 + EPSILON)
+ return 1.0;
+ if (dist >= 0.5 - EPSILON)
+ return 0.0;
+ return 0.5 + dist * (0.8431027 * dist * dist - 1.14453603);
+}
+
+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));
+}
+
+float init_transform_fs(vec2 local_pos) {
+ // Get signed distance from local rect bounds.
+ float d = signed_distance_rect(
+ local_pos,
+ vTransformBounds.xy,
+ vTransformBounds.zw
+ );
+
+ // Find the appropriate distance to apply the AA smoothstep over.
+ float aa_range = compute_aa_range(local_pos);
+
+ // Only apply AA to fragments outside the signed distance field.
+ return distance_aa(aa_range, d);
+}
+
+float init_transform_rough_fs(vec2 local_pos) {
+ return point_inside_rect(
+ local_pos,
+ vTransformBounds.xy,
+ vTransformBounds.zw
+ );
+}
+
+#endif //WR_FRAGMENT_SHADER
--- a/gfx/webrender/src/batch.rs
+++ b/gfx/webrender/src/batch.rs
@@ -1,13 +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 api::{AlphaType, DeviceIntRect, DeviceIntSize};
+use api::{AlphaType, ClipMode, DeviceIntRect, DeviceIntSize};
use api::{DeviceUintRect, DeviceUintPoint, DeviceUintSize, ExternalImageType, FilterOp, ImageRendering, LayerRect};
use api::{DeviceIntPoint, SubpixelDirection, YuvColorSpace, YuvFormat};
use api::{LayerToWorldTransform, WorldPixel};
use border::{BorderCornerInstance, BorderCornerSide, BorderEdgeKind};
use clip::{ClipSource, ClipStore, ClipWorkItem};
use clip_scroll_tree::{CoordinateSystemId};
use euclid::{TypedTransform3D, vec3};
use glyph_rasterizer::GlyphFormat;
@@ -235,17 +235,17 @@ impl OpaqueBatchList {
let mut selected_batch_index = None;
let item_area = task_relative_bounding_rect.size.to_f32().area();
// If the area of this primitive is larger than the given threshold,
// then it is large enough to warrant breaking a batch for. In this
// case we just see if it can be added to the existing batch or
// create a new one.
if item_area > self.pixel_area_threshold_for_new_batch {
- if let Some(ref batch) = self.batches.last() {
+ if let Some(batch) = self.batches.last() {
if batch.key.is_compatible_with(&key) {
selected_batch_index = Some(self.batches.len() - 1);
}
}
} else {
// Otherwise, look back through a reasonable number of batches.
for (batch_index, batch) in self.batches.iter().enumerate().rev().take(10) {
if batch.key.is_compatible_with(&key) {
@@ -650,17 +650,17 @@ impl AlphaBatchBuilder {
debug_assert!(picture.surface.is_some());
let real_xf = &ctx.clip_scroll_tree
.nodes[picture.reference_frame_index.0]
.world_content_transform
.into();
let polygon = make_polygon(
picture.real_local_rect,
- &real_xf,
+ real_xf,
prim_index.0,
);
splitter.add(polygon);
return;
}
@@ -879,26 +879,30 @@ impl AlphaBatchBuilder {
source_task_address.0 as i32,
],
};
batch.push(PrimitiveInstance::from(instance));
false
}
Some(PictureCompositeMode::Blit) => {
- let cache_task_id = picture.surface.expect("bug: no surface allocated");
+ let cache_task_id =
+ picture.surface.expect("bug: no surface allocated");
let kind = BatchKind::Brush(
BrushBatchKind::Image(ImageBufferKind::Texture2DArray)
);
let key = BatchKey::new(
kind,
non_segmented_blend_mode,
BatchTextures::render_target_cache(),
);
- let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
+ let batch = self.batch_list.get_suitable_batch(
+ key,
+ &task_relative_bounding_rect
+ );
let uv_rect_address = render_tasks[cache_task_id]
.get_texture_handle()
.as_int(gpu_cache);
let instance = BrushInstance {
picture_address: task_address,
prim_address: prim_cache_address,
@@ -938,17 +942,17 @@ impl AlphaBatchBuilder {
);
}
}
_ => {
if let Some((batch_kind, textures, user_data)) = brush.get_batch_params(
ctx.resource_cache,
gpu_cache,
deferred_resolves,
- &ctx.cached_gradients,
+ ctx.cached_gradients,
) {
self.add_brush_to_batch(
brush,
prim_metadata,
batch_kind,
specified_blend_mode,
non_segmented_blend_mode,
textures,
@@ -1249,23 +1253,31 @@ impl BrushPrimitive {
fn get_batch_params(
&self,
resource_cache: &ResourceCache,
gpu_cache: &mut GpuCache,
deferred_resolves: &mut Vec<DeferredResolve>,
cached_gradients: &[CachedGradient],
) -> Option<(BrushBatchKind, BatchTextures, [i32; 3])> {
match self.kind {
- BrushKind::Image { request, .. } => {
- let cache_item = resolve_image(
- request,
- resource_cache,
- gpu_cache,
- deferred_resolves,
- );
+ BrushKind::Image { request, ref source, .. } => {
+
+ let cache_item = match *source {
+ ImageSource::Default => {
+ resolve_image(
+ request,
+ resource_cache,
+ gpu_cache,
+ deferred_resolves,
+ )
+ }
+ ImageSource::Cache { ref item, .. } => {
+ item.clone()
+ }
+ };
if cache_item.texture_id == SourceTexture::Invalid {
None
} else {
let textures = BatchTextures::color(cache_item.texture_id);
Some((
BrushBatchKind::Image(get_buffer_kind(cache_item.texture_id)),
@@ -1610,18 +1622,19 @@ impl ClipBatcher {
.entry(info.cache_item.texture_id)
.or_insert(Vec::new())
.push(ClipMaskInstance {
clip_data_address: gpu_address,
resource_address: gpu_cache.get_address(&info.cache_item.uv_rect_handle),
..instance
});
}
- ClipSource::Rectangle(..) => {
- if work_item.coordinate_system_id != coordinate_system_id {
+ ClipSource::Rectangle(_, mode) => {
+ if work_item.coordinate_system_id != coordinate_system_id ||
+ mode == ClipMode::ClipOut {
self.rectangles.push(ClipMaskInstance {
clip_data_address: gpu_address,
..instance
});
coordinate_system_id = work_item.coordinate_system_id;
}
}
ClipSource::RoundedRectangle(..) => {
--- a/gfx/webrender/src/border.rs
+++ b/gfx/webrender/src/border.rs
@@ -1,13 +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 api::{BorderRadius, BorderSide, BorderStyle, BorderWidths, ColorF, LayerPoint};
+use api::{BorderRadius, BorderSide, BorderStyle, BorderWidths, ClipMode, ColorF, LayerPoint};
use api::{LayerPrimitiveInfo, LayerRect, LayerSize, NormalBorder, RepeatMode, TexelRect};
use clip::ClipSource;
use ellipse::Ellipse;
use display_list_flattener::DisplayListFlattener;
use gpu_cache::GpuDataRequest;
use prim_store::{BorderPrimitiveCpu, BrushClipMaskKind, BrushSegment, BrushSegmentDescriptor};
use prim_store::{EdgeAaSegmentMask, PrimitiveContainer, ScrollNodeAndClipChain};
use util::{lerp, pack_as_float};
@@ -368,16 +368,73 @@ impl<'a> DisplayListFlattener<'a> {
ensure_no_corner_overlap(&mut border.radius, &info.rect);
let radius = &border.radius;
let left = &border.left;
let right = &border.right;
let top = &border.top;
let bottom = &border.bottom;
+ let constant_color = left.color;
+ let is_simple_border = [left, top, right, bottom].iter().all(|edge| {
+ edge.style == BorderStyle::Solid &&
+ edge.color == constant_color
+ });
+
+ if is_simple_border {
+ let extra_clips = vec![
+ ClipSource::new_rounded_rect(
+ info.rect,
+ border.radius,
+ ClipMode::Clip,
+ ),
+ ClipSource::new_rounded_rect(
+ LayerRect::new(
+ LayerPoint::new(
+ info.rect.origin.x + widths.left,
+ info.rect.origin.y + widths.top,
+ ),
+ LayerSize::new(
+ info.rect.size.width - widths.left - widths.right,
+ info.rect.size.height - widths.top - widths.bottom,
+ ),
+ ),
+ BorderRadius {
+ top_left: LayerSize::new(
+ (border.radius.top_left.width - widths.left).max(0.0),
+ (border.radius.top_left.height - widths.top).max(0.0),
+ ),
+ top_right: LayerSize::new(
+ (border.radius.top_right.width - widths.right).max(0.0),
+ (border.radius.top_right.height - widths.top).max(0.0),
+ ),
+ bottom_left: LayerSize::new(
+ (border.radius.bottom_left.width - widths.left).max(0.0),
+ (border.radius.bottom_left.height - widths.bottom).max(0.0),
+ ),
+ bottom_right: LayerSize::new(
+ (border.radius.bottom_right.width - widths.right).max(0.0),
+ (border.radius.bottom_right.height - widths.bottom).max(0.0),
+ ),
+ },
+ ClipMode::ClipOut,
+ ),
+ ];
+
+ self.add_solid_rectangle(
+ clip_and_scroll,
+ info,
+ border.top.color,
+ None,
+ extra_clips,
+ );
+
+ return;
+ }
+
let corners = [
border.get_corner(
left,
widths.left,
top,
widths.top,
&radius.top_left,
BorderCorner::TopLeft,
@@ -457,69 +514,73 @@ impl<'a> DisplayListFlattener<'a> {
segment(p2.x, p0.y, p3.x, p1.y),
segment(p1.x, p0.y, p2.x, p1.y),
],
clip_mask_kind: BrushClipMaskKind::Unknown,
};
self.add_solid_rectangle(
clip_and_scroll,
- &info,
+ info,
border.top.color,
Some(descriptor),
+ Vec::new(),
);
}
if left_edge == BorderEdgeKind::Solid {
let descriptor = BrushSegmentDescriptor {
segments: vec![
segment(p0.x, p1.y, p1.x, p2.y),
],
clip_mask_kind: BrushClipMaskKind::Unknown,
};
self.add_solid_rectangle(
clip_and_scroll,
- &info,
+ info,
border.left.color,
Some(descriptor),
+ Vec::new(),
);
}
if right_edge == BorderEdgeKind::Solid {
let descriptor = BrushSegmentDescriptor {
segments: vec![
segment(p2.x, p1.y, p3.x, p2.y),
],
clip_mask_kind: BrushClipMaskKind::Unknown,
};
self.add_solid_rectangle(
clip_and_scroll,
- &info,
+ info,
border.right.color,
Some(descriptor),
+ Vec::new(),
);
}
if bottom_edge == BorderEdgeKind::Solid {
let descriptor = BrushSegmentDescriptor {
segments: vec![
segment(p1.x, p2.y, p2.x, p3.y),
segment(p2.x, p2.y, p3.x, p3.y),
segment(p0.x, p2.y, p1.x, p3.y),
],
clip_mask_kind: BrushClipMaskKind::Unknown,
};
self.add_solid_rectangle(
clip_and_scroll,
- &info,
+ info,
border.bottom.color,
Some(descriptor),
+ Vec::new(),
);
}
} else {
// Create clip masks for border corners, if required.
let mut extra_clips = Vec::new();
let mut corner_instances = [BorderCornerInstance::Single; 4];
for (i, corner) in corners.iter().enumerate() {
--- a/gfx/webrender/src/clip.rs
+++ b/gfx/webrender/src/clip.rs
@@ -61,32 +61,32 @@ impl ClipRegion {
complex_clips,
}
}
pub fn create_for_clip_node_with_local_clip(
local_clip: &LocalClip,
reference_frame_relative_offset: &LayoutVector2D
) -> ClipRegion {
- let complex_clips = match local_clip {
- &LocalClip::Rect(_) => Vec::new(),
- &LocalClip::RoundedRect(_, ref region) => vec![region.clone()],
+ let complex_clips = match *local_clip {
+ LocalClip::Rect(_) => Vec::new(),
+ LocalClip::RoundedRect(_, ref region) => vec![region.clone()],
};
ClipRegion::create_for_clip_node(
*local_clip.clip_rect(),
complex_clips,
None,
reference_frame_relative_offset
)
}
}
#[derive(Debug)]
pub enum ClipSource {
- Rectangle(LayerRect),
+ Rectangle(LayerRect, ClipMode),
RoundedRectangle(LayerRect, BorderRadius, ClipMode),
Image(ImageMask),
/// TODO(gw): This currently only handles dashed style
/// clips, where the border style is dashed for both
/// adjacent border edges. Expand to handle dotted style
/// and different styles per edge.
BorderCorner(BorderCornerClipSource),
BoxShadow(BoxShadowClipSource),
@@ -96,17 +96,17 @@ pub enum ClipSource {
impl From<ClipRegion> for ClipSources {
fn from(region: ClipRegion) -> ClipSources {
let mut clips = Vec::new();
if let Some(info) = region.image_mask {
clips.push(ClipSource::Image(info));
}
- clips.push(ClipSource::Rectangle(region.main));
+ clips.push(ClipSource::Rectangle(region.main, ClipMode::Clip));
for complex in region.complex_clips {
clips.push(ClipSource::new_rounded_rect(
complex.rect,
complex.radii,
complex.mode,
));
}
@@ -116,22 +116,26 @@ impl From<ClipRegion> for ClipSources {
}
impl ClipSource {
pub fn new_rounded_rect(
rect: LayerRect,
mut radii: BorderRadius,
clip_mode: ClipMode
) -> ClipSource {
- ensure_no_corner_overlap(&mut radii, &rect);
- ClipSource::RoundedRectangle(
- rect,
- radii,
- clip_mode,
- )
+ if radii.is_zero() {
+ ClipSource::Rectangle(rect, clip_mode)
+ } else {
+ ensure_no_corner_overlap(&mut radii, &rect);
+ ClipSource::RoundedRectangle(
+ rect,
+ radii,
+ clip_mode,
+ )
+ }
}
pub fn new_line_decoration(
rect: LayerRect,
style: LineStyle,
orientation: LineOrientation,
wavy_line_thickness: f32,
) -> ClipSource {
@@ -301,17 +305,24 @@ impl ClipSources {
match *source {
ClipSource::Image(ref mask) => {
if !mask.repeat {
can_calculate_outer_rect = true;
local_outer = local_outer.and_then(|r| r.intersection(&mask.rect));
}
local_inner = None;
}
- ClipSource::Rectangle(rect) => {
+ ClipSource::Rectangle(rect, mode) => {
+ // Once we encounter a clip-out, we just assume the worst
+ // case clip mask size, for now.
+ if mode == ClipMode::ClipOut {
+ can_calculate_inner_rect = false;
+ break;
+ }
+
can_calculate_outer_rect = true;
local_outer = local_outer.and_then(|r| r.intersection(&rect));
local_inner = local_inner.and_then(|r| r.intersection(&rect));
}
ClipSource::RoundedRectangle(ref rect, ref radius, mode) => {
// Once we encounter a clip-out, we just assume the worst
// case clip mask size, for now.
if mode == ClipMode::ClipOut {
@@ -330,24 +341,26 @@ impl ClipSources {
ClipSource::BorderCorner { .. } |
ClipSource::LineDecoration(..) => {
can_calculate_inner_rect = false;
break;
}
}
}
- let outer = match can_calculate_outer_rect {
- true => Some(local_outer.unwrap_or_else(LayerRect::zero)),
- false => None,
+ let outer = if can_calculate_outer_rect {
+ Some(local_outer.unwrap_or_else(LayerRect::zero))
+ } else {
+ None
};
- let inner = match can_calculate_inner_rect {
- true => local_inner.unwrap_or_else(LayerRect::zero),
- false => LayerRect::zero(),
+ let inner = if can_calculate_inner_rect {
+ local_inner.unwrap_or_else(LayerRect::zero)
+ } else {
+ LayerRect::zero()
};
(inner, outer)
}
pub fn update(
&mut self,
gpu_cache: &mut GpuCache,
@@ -371,18 +384,18 @@ impl ClipSources {
request.push([
info.stretch_mode_x as i32 as f32,
info.stretch_mode_y as i32 as f32,
0.0,
0.0,
]);
request.push(info.prim_shadow_rect);
}
- ClipSource::Rectangle(rect) => {
- let data = ClipData::uniform(rect, 0.0, ClipMode::Clip);
+ ClipSource::Rectangle(rect, mode) => {
+ let data = ClipData::uniform(rect, 0.0, mode);
data.write(&mut request);
}
ClipSource::RoundedRectangle(ref rect, ref radius, mode) => {
let data = ClipData::rounded_rect(rect, radius, mode);
data.write(&mut request);
}
ClipSource::BorderCorner(ref mut source) => {
source.write(request);
--- a/gfx/webrender/src/clip_scroll_node.rs
+++ b/gfx/webrender/src/clip_scroll_node.rs
@@ -1,35 +1,28 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{DevicePixelScale, ExternalScrollId, LayerPixel, LayerPoint, LayerRect, LayerSize};
use api::{LayerVector2D, LayoutTransform, LayoutVector2D, PipelineId, PropertyBinding};
-use api::{ScrollClamping, ScrollEventPhase, ScrollLocation, ScrollSensitivity, StickyOffsetBounds};
+use api::{ScrollClamping, ScrollLocation, ScrollSensitivity, StickyOffsetBounds};
use api::WorldPoint;
use clip::{ClipChain, ClipChainNode, ClipSourcesHandle, ClipStore, ClipWorkItem};
use clip_scroll_tree::{ClipChainIndex, ClipScrollNodeIndex, CoordinateSystemId};
use clip_scroll_tree::TransformUpdateState;
use euclid::SideOffsets2D;
use geometry::ray_intersects_rect;
use gpu_cache::GpuCache;
use gpu_types::{ClipScrollNodeIndex as GPUClipScrollNodeIndex, ClipScrollNodeData};
use resource_cache::ResourceCache;
use scene::SceneProperties;
-use spring::{DAMPING, STIFFNESS, Spring};
use util::{LayerToWorldFastTransform, LayerFastTransform, LayoutFastTransform};
use util::{TransformedRectKind};
-#[cfg(target_os = "macos")]
-const CAN_OVERSCROLL: bool = true;
-
-#[cfg(not(target_os = "macos"))]
-const CAN_OVERSCROLL: bool = false;
-
#[derive(Debug)]
pub struct StickyFrameInfo {
pub margins: SideOffsets2D<Option<f32>>,
pub vertical_offset_bounds: StickyOffsetBounds,
pub horizontal_offset_bounds: StickyOffsetBounds,
pub previously_applied_offset: LayoutVector2D,
pub current_offset: LayerVector2D,
}
@@ -264,18 +257,16 @@ impl ClipScrollNode {
ScrollClamping::NoClamping => LayerPoint::zero() - *origin,
};
if new_offset == scrolling.offset {
return false;
}
scrolling.offset = new_offset;
- scrolling.bouncing_back = false;
- scrolling.started_bouncing_back = false;
true
}
pub fn mark_uninvertible(&mut self) {
self.invertible = false;
self.world_content_transform = LayerToWorldFastTransform::identity();
self.world_viewport_transform = LayerToWorldFastTransform::identity();
}
@@ -661,27 +652,23 @@ impl ClipScrollNode {
pub fn scrollable_size(&self) -> LayerSize {
match self.node_type {
NodeType:: ScrollFrame(state) => state.scrollable_size,
_ => LayerSize::zero(),
}
}
- pub fn scroll(&mut self, scroll_location: ScrollLocation, phase: ScrollEventPhase) -> bool {
+ pub fn scroll(&mut self, scroll_location: ScrollLocation) -> bool {
let scrolling = match self.node_type {
NodeType::ScrollFrame(ref mut scrolling) => scrolling,
_ => return false,
};
- if scrolling.started_bouncing_back && phase == ScrollEventPhase::Move(false) {
- return false;
- }
-
- let mut delta = match scroll_location {
+ let delta = match scroll_location {
ScrollLocation::Delta(delta) => delta,
ScrollLocation::Start => {
if scrolling.offset.y.round() >= 0.0 {
// Nothing to do on this layer.
return false;
}
scrolling.offset.y = 0.0;
@@ -694,66 +681,35 @@ impl ClipScrollNode {
return false;
}
scrolling.offset.y = end_pos;
return true;
}
};
- let overscroll_amount = scrolling.overscroll_amount();
- let overscrolling = CAN_OVERSCROLL && (overscroll_amount != LayerVector2D::zero());
- if overscrolling {
- if overscroll_amount.x != 0.0 {
- delta.x /= overscroll_amount.x.abs()
- }
- if overscroll_amount.y != 0.0 {
- delta.y /= overscroll_amount.y.abs()
- }
- }
-
let scrollable_width = scrolling.scrollable_size.width;
let scrollable_height = scrolling.scrollable_size.height;
- let is_unscrollable = scrollable_width <= 0. && scrollable_height <= 0.;
let original_layer_scroll_offset = scrolling.offset;
if scrollable_width > 0. {
- scrolling.offset.x = scrolling.offset.x + delta.x;
- if is_unscrollable || !CAN_OVERSCROLL {
- scrolling.offset.x = scrolling.offset.x.min(0.0).max(-scrollable_width).round();
- }
+ scrolling.offset.x = (scrolling.offset.x + delta.x)
+ .min(0.0)
+ .max(-scrollable_width)
+ .round();
}
if scrollable_height > 0. {
- scrolling.offset.y = scrolling.offset.y + delta.y;
- if is_unscrollable || !CAN_OVERSCROLL {
- scrolling.offset.y = scrolling.offset.y.min(0.0).max(-scrollable_height).round();
- }
+ scrolling.offset.y = (scrolling.offset.y + delta.y)
+ .min(0.0)
+ .max(-scrollable_height)
+ .round();
}
- if phase == ScrollEventPhase::Start || phase == ScrollEventPhase::Move(true) {
- scrolling.started_bouncing_back = false
- } else if overscrolling &&
- ((delta.x < 1.0 && delta.y < 1.0) || phase == ScrollEventPhase::End)
- {
- scrolling.started_bouncing_back = true;
- scrolling.bouncing_back = true
- }
-
- if CAN_OVERSCROLL {
- scrolling.stretch_overscroll_spring(overscroll_amount);
- }
-
- scrolling.offset != original_layer_scroll_offset || scrolling.started_bouncing_back
- }
-
- pub fn tick_scrolling_bounce_animation(&mut self) {
- if let NodeType::ScrollFrame(ref mut scrolling) = self.node_type {
- scrolling.tick_scrolling_bounce_animation();
- }
+ scrolling.offset != original_layer_scroll_offset
}
pub fn ray_intersects_node(&self, cursor: &WorldPoint) -> bool {
let inv = match self.world_viewport_transform.inverse() {
Some(inv) => inv,
None => return false,
};
@@ -776,109 +732,60 @@ impl ClipScrollNode {
pub fn scroll_offset(&self) -> LayerVector2D {
match self.node_type {
NodeType::ScrollFrame(ref scrolling) => scrolling.offset,
_ => LayerVector2D::zero(),
}
}
- pub fn is_overscrolling(&self) -> bool {
- match self.node_type {
- NodeType::ScrollFrame(ref state) => state.overscroll_amount() != LayerVector2D::zero(),
- _ => false,
- }
- }
-
pub fn matches_external_id(&self, external_id: ExternalScrollId) -> bool {
match self.node_type {
NodeType::ScrollFrame(info) if info.external_id == Some(external_id) => true,
_ => false,
}
}
}
#[derive(Copy, Clone, Debug)]
pub struct ScrollFrameInfo {
pub offset: LayerVector2D,
- pub spring: Spring,
- pub started_bouncing_back: bool,
- pub bouncing_back: bool,
- pub should_handoff_scroll: bool,
pub scroll_sensitivity: ScrollSensitivity,
/// Amount that this ScrollFrame can scroll in both directions.
pub scrollable_size: LayerSize,
/// An external id to identify this scroll frame to API clients. This
/// allows setting scroll positions via the API without relying on ClipsIds
/// which may change between frames.
pub external_id: Option<ExternalScrollId>,
}
-/// Manages scrolling offset, overscroll state, etc.
+/// Manages scrolling offset.
impl ScrollFrameInfo {
pub fn new(
scroll_sensitivity: ScrollSensitivity,
scrollable_size: LayerSize,
external_id: Option<ExternalScrollId>,
) -> ScrollFrameInfo {
ScrollFrameInfo {
offset: LayerVector2D::zero(),
- spring: Spring::at(LayerPoint::zero(), STIFFNESS, DAMPING),
- started_bouncing_back: false,
- bouncing_back: false,
- should_handoff_scroll: false,
scroll_sensitivity,
scrollable_size,
external_id,
}
}
pub fn sensitive_to_input_events(&self) -> bool {
match self.scroll_sensitivity {
ScrollSensitivity::ScriptAndInputEvents => true,
ScrollSensitivity::Script => false,
}
}
-
- pub fn stretch_overscroll_spring(&mut self, overscroll_amount: LayerVector2D) {
- let offset = self.offset.to_point();
- self.spring
- .coords(offset, offset, offset + overscroll_amount);
- }
-
- pub fn tick_scrolling_bounce_animation(&mut self) {
- let finished = self.spring.animate();
- self.offset = self.spring.current().to_vector();
- if finished {
- self.bouncing_back = false
- }
- }
-
- pub fn overscroll_amount(&self) -> LayerVector2D {
- let overscroll_x = if self.offset.x > 0.0 {
- -self.offset.x
- } else if self.offset.x < -self.scrollable_size.width {
- -self.scrollable_size.width - self.offset.x
- } else {
- 0.0
- };
-
- let overscroll_y = if self.offset.y > 0.0 {
- -self.offset.y
- } else if self.offset.y < -self.scrollable_size.height {
- -self.scrollable_size.height - self.offset.y
- } else {
- 0.0
- };
-
- LayerVector2D::new(overscroll_x, overscroll_y)
- }
}
/// Contains information about reference frames.
#[derive(Copy, Clone, Debug)]
pub struct ReferenceFrameInfo {
/// The transformation that establishes this reference frame, relative to the parent
/// reference frame. The origin of the reference frame is included in the transformation.
pub resolved_transform: LayerFastTransform,
--- a/gfx/webrender/src/clip_scroll_tree.rs
+++ b/gfx/webrender/src/clip_scroll_tree.rs
@@ -1,14 +1,14 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{DeviceIntRect, DevicePixelScale, ExternalScrollId, LayerPoint, LayerRect, LayerVector2D};
-use api::{PipelineId, ScrollClamping, ScrollEventPhase, ScrollLocation, ScrollNodeState};
+use api::{PipelineId, ScrollClamping, ScrollLocation, ScrollNodeState};
use api::WorldPoint;
use clip::{ClipChain, ClipSourcesHandle, ClipStore};
use clip_scroll_node::{ClipScrollNode, NodeType, ScrollFrameInfo, StickyFrameInfo};
use gpu_cache::GpuCache;
use gpu_types::{ClipScrollNodeIndex as GPUClipScrollNodeIndex, ClipScrollNodeData};
use internal_types::{FastHashMap, FastHashSet};
use print_tree::{PrintTree, PrintTreePrinter};
use resource_cache::ResourceCache;
@@ -65,20 +65,16 @@ pub struct ClipScrollTree {
pub clip_chains_descriptors: Vec<ClipChainDescriptor>,
/// A vector of all ClipChains in this ClipScrollTree including those from
/// ClipChainDescriptors and also those defined by the clipping node hierarchy.
pub clip_chains: Vec<ClipChain>,
pub pending_scroll_offsets: FastHashMap<ExternalScrollId, (LayerPoint, ScrollClamping)>,
- /// The ClipId of the currently scrolling node. Used to allow the same
- /// node to scroll even if a touch operation leaves the boundaries of that node.
- pub currently_scrolling_node_index: Option<ClipScrollNodeIndex>,
-
/// The current frame id, used for giving a unique id to all new dynamically
/// added frames and clips. The ClipScrollTree increments this by one every
/// time a new dynamic frame is created.
current_new_node_item: u64,
/// A set of pipelines which should be discarded the next time this
/// tree is drained.
pub pipelines_to_discard: FastHashSet<PipelineId>,
@@ -111,17 +107,16 @@ pub struct TransformUpdateState {
impl ClipScrollTree {
pub fn new() -> Self {
ClipScrollTree {
nodes: Vec::new(),
clip_chains_descriptors: Vec::new(),
clip_chains: vec![ClipChain::empty(&DeviceIntRect::zero())],
pending_scroll_offsets: FastHashMap::default(),
- currently_scrolling_node_index: None,
current_new_node_item: 1,
pipelines_to_discard: FastHashSet::default(),
}
}
/// The root reference frame, which is the true root of the ClipScrollTree. Initially
/// this ID is not valid, which is indicated by ```nodes``` being empty.
pub fn root_reference_frame_index(&self) -> ClipScrollNodeIndex {
@@ -133,28 +128,16 @@ impl ClipScrollTree {
/// The root scroll node which is the first child of the root reference frame.
/// Initially this ID is not valid, which is indicated by ```nodes``` being empty.
pub fn topmost_scroll_node_index(&self) -> ClipScrollNodeIndex {
// TODO(mrobinson): We should eventually make this impossible to misuse.
debug_assert!(self.nodes.len() >= 1);
TOPMOST_SCROLL_NODE_INDEX
}
- pub fn collect_nodes_bouncing_back(&self) -> FastHashSet<ClipScrollNodeIndex> {
- let mut nodes_bouncing_back = FastHashSet::default();
- for (index, node) in self.nodes.iter().enumerate() {
- if let NodeType::ScrollFrame(ref scrolling) = node.node_type {
- if scrolling.bouncing_back {
- nodes_bouncing_back.insert(ClipScrollNodeIndex(index));
- }
- }
- }
- nodes_bouncing_back
- }
-
fn find_scrolling_node_at_point_in_node(
&self,
cursor: &WorldPoint,
index: ClipScrollNodeIndex,
) -> Option<ClipScrollNodeIndex> {
let node = &self.nodes[index.0];
for child_index in node.children.iter().rev() {
let found_index = self.find_scrolling_node_at_point_in_node(cursor, *child_index);
@@ -230,82 +213,23 @@ impl ClipScrollTree {
self.pending_scroll_offsets.insert(id, (origin, clamp));
false
}
pub fn scroll(
&mut self,
scroll_location: ScrollLocation,
cursor: WorldPoint,
- phase: ScrollEventPhase,
) -> bool {
if self.nodes.is_empty() {
return false;
}
- let node_index = match (
- phase,
- self.find_scrolling_node_at_point(&cursor),
- self.currently_scrolling_node_index,
- ) {
- (ScrollEventPhase::Start, scroll_node_at_point_index, _) => {
- self.currently_scrolling_node_index = Some(scroll_node_at_point_index);
- scroll_node_at_point_index
- }
- (_, scroll_node_at_point_index, Some(cached_node_index)) => {
- let node_index = match self.nodes.get(cached_node_index.0) {
- Some(_) => cached_node_index,
- None => {
- self.currently_scrolling_node_index = Some(scroll_node_at_point_index);
- scroll_node_at_point_index
- }
- };
- node_index
- }
- (_, _, None) => return false,
- };
-
- let topmost_scroll_node_index = self.topmost_scroll_node_index();
- let non_root_overscroll = if node_index != topmost_scroll_node_index {
- self.nodes[node_index.0].is_overscrolling()
- } else {
- false
- };
-
- let mut switch_node = false;
- {
- let node = &mut self.nodes[node_index.0];
- if let NodeType::ScrollFrame(ref mut scrolling) = node.node_type {
- match phase {
- ScrollEventPhase::Start => {
- // if this is a new gesture, we do not switch node,
- // however we do save the state of non_root_overscroll,
- // for use in the subsequent Move phase.
- scrolling.should_handoff_scroll = non_root_overscroll;
- }
- ScrollEventPhase::Move(_) => {
- // Switch node if movement originated in a new gesture,
- // from a non root node in overscroll.
- switch_node = scrolling.should_handoff_scroll && non_root_overscroll
- }
- ScrollEventPhase::End => {
- // clean-up when gesture ends.
- scrolling.should_handoff_scroll = false;
- }
- }
- }
- }
-
- let node_index = if switch_node {
- topmost_scroll_node_index
- } else {
- node_index
- };
-
- self.nodes[node_index.0].scroll(scroll_location, phase)
+ let node_index = self.find_scrolling_node_at_point(&cursor);
+ self.nodes[node_index.0].scroll(scroll_location)
}
pub fn update_tree(
&mut self,
screen_rect: &DeviceIntRect,
device_pixel_scale: DevicePixelScale,
clip_store: &mut ClipStore,
resource_cache: &mut ResourceCache,
@@ -429,22 +353,16 @@ impl ClipScrollTree {
};
}
chain.parent_index = descriptor.parent;
self.clip_chains[descriptor.index.0] = chain;
}
}
- pub fn tick_scrolling_bounce_animations(&mut self) {
- for node in &mut self.nodes {
- node.tick_scrolling_bounce_animation()
- }
- }
-
pub fn finalize_and_apply_pending_scroll_offsets(&mut self, old_states: ScrollStates) {
for node in &mut self.nodes {
let external_id = match node.node_type {
NodeType::ScrollFrame(ScrollFrameInfo { external_id: Some(id), ..} ) => id,
_ => continue,
};
if let Some(scrolling_state) = old_states.get(&external_id) {
@@ -525,37 +443,31 @@ impl ClipScrollTree {
let length_to_extend = self.nodes.len() .. index.0;
self.nodes.extend(length_to_extend.map(|_| ClipScrollNode::empty()));
self.nodes.push(node);
}
pub fn discard_frame_state_for_pipeline(&mut self, pipeline_id: PipelineId) {
self.pipelines_to_discard.insert(pipeline_id);
-
- if let Some(index) = self.currently_scrolling_node_index {
- if self.nodes[index.0].pipeline_id == pipeline_id {
- self.currently_scrolling_node_index = None;
- }
- }
}
fn print_node<T: PrintTreePrinter>(
&self,
index: ClipScrollNodeIndex,
pt: &mut T,
clip_store: &ClipStore
) {
let node = &self.nodes[index.0];
match node.node_type {
NodeType::Clip { ref handle, .. } => {
pt.new_level("Clip".to_owned());
pt.add_item(format!("index: {:?}", index));
- let clips = clip_store.get(&handle).clips();
+ let clips = clip_store.get(handle).clips();
pt.new_level(format!("Clip Sources [{}]", clips.len()));
for source in clips {
pt.add_item(format!("{:?}", source));
}
pt.end_level();
}
NodeType::ReferenceFrame(ref info) => {
pt.new_level(format!("ReferenceFrame {:?}", info.resolved_transform));
--- a/gfx/webrender/src/device.rs
+++ b/gfx/webrender/src/device.rs
@@ -48,17 +48,16 @@ const GL_FORMAT_BGRA_GL: gl::GLuint = gl
const GL_FORMAT_BGRA_GLES: gl::GLuint = gl::BGRA_EXT;
const SHADER_VERSION_GL: &str = "#version 150\n";
const SHADER_VERSION_GLES: &str = "#version 300 es\n";
const SHADER_KIND_VERTEX: &str = "#define WR_VERTEX_SHADER\n";
const SHADER_KIND_FRAGMENT: &str = "#define WR_FRAGMENT_SHADER\n";
const SHADER_IMPORT: &str = "#include ";
-const SHADER_LINE_MARKER: &str = "#line 1\n";
pub struct TextureSlot(pub usize);
// In some places we need to temporarily bind a texture to any slot.
const DEFAULT_TEXTURE: TextureSlot = TextureSlot(0);
#[repr(u32)]
pub enum DepthFunction {
@@ -165,30 +164,26 @@ fn get_shader_source(shader_name: &str,
shader_source::SHADERS
.get(shader_name)
.map(|s| s.to_string())
}
// Parse a shader string for imports. Imports are recursively processed, and
// prepended to the list of outputs.
fn parse_shader_source(source: String, base_path: &Option<PathBuf>, output: &mut String) {
- output.push_str(SHADER_LINE_MARKER);
-
- for (line_num, line) in source.lines().enumerate() {
+ for line in source.lines() {
if line.starts_with(SHADER_IMPORT) {
- let imports = line[SHADER_IMPORT.len() ..].split(",");
+ let imports = line[SHADER_IMPORT.len() ..].split(',');
// For each import, get the source, and recurse.
for import in imports {
if let Some(include) = get_shader_source(import, base_path) {
parse_shader_source(include, base_path, output);
}
}
-
- output.push_str(&format!("#line {}\n", line_num+1));
} else {
output.push_str(line);
output.push_str("\n");
}
}
}
pub fn build_shader_strings(
@@ -344,21 +339,21 @@ impl VertexDescriptor {
for (i, attr) in attributes.iter().enumerate() {
let attr_index = (start_index + i) as gl::GLuint;
attr.bind_to_vao(attr_index, divisor, stride as _, offset, gl);
offset += attr.size_in_bytes();
}
}
fn bind(&self, gl: &gl::Gl, main: VBOId, instance: VBOId) {
- Self::bind_attributes(&self.vertex_attributes, 0, 0, gl, main);
+ Self::bind_attributes(self.vertex_attributes, 0, 0, gl, main);
if !self.instance_attributes.is_empty() {
Self::bind_attributes(
- &self.instance_attributes,
+ self.instance_attributes,
self.vertex_attributes.len(),
1, gl, instance,
);
}
}
}
impl VBOId {
@@ -772,29 +767,50 @@ impl Device {
pub fn reset_state(&mut self) {
self.bound_textures = [0; 16];
self.bound_vao = 0;
self.bound_read_fbo = FBOId(0);
self.bound_draw_fbo = FBOId(0);
}
+ #[cfg(debug_assertions)]
+ fn print_shader_errors(source: &str, log: &str) {
+ // hacky way to extract the offending lines
+ if !log.starts_with("0:") {
+ return;
+ }
+ let end_pos = match log[2..].chars().position(|c| !c.is_digit(10)) {
+ Some(pos) => 2 + pos,
+ None => return,
+ };
+ let base_line_number = match log[2 .. end_pos].parse::<usize>() {
+ Ok(number) if number >= 2 => number - 2,
+ _ => return,
+ };
+ for (line, prefix) in source.lines().skip(base_line_number).zip(&["|",">","|"]) {
+ println!("{}\t{}", prefix, line);
+ }
+ }
+
pub fn compile_shader(
gl: &gl::Gl,
name: &str,
shader_type: gl::GLenum,
source: &String,
) -> Result<gl::GLuint, ShaderError> {
debug!("compile {}", name);
let id = gl.create_shader(shader_type);
gl.shader_source(id, &[source.as_bytes()]);
gl.compile_shader(id);
let log = gl.get_shader_info_log(id);
if gl.get_shader_iv(id, gl::COMPILE_STATUS) == (0 as gl::GLint) {
println!("Failed to compile shader: {}\n{}", name, log);
+ #[cfg(debug_assertions)]
+ Self::print_shader_errors(source, &log);
Err(ShaderError::Compilation(name.to_string(), log))
} else {
if !log.is_empty() {
println!("Warnings detected on shader: {}\n{}", name, log);
}
Ok(id)
}
}
--- a/gfx/webrender/src/display_list_flattener.rs
+++ b/gfx/webrender/src/display_list_flattener.rs
@@ -316,41 +316,45 @@ impl<'a> DisplayListFlattener<'a> {
.collect()
}
fn flatten_root(&mut self, pipeline: &'a ScenePipeline, frame_size: &LayoutSize) {
let pipeline_id = pipeline.pipeline_id;
let reference_frame_info = self.id_to_index_mapper.simple_scroll_and_clip_chain(
&ClipId::root_reference_frame(pipeline_id)
);
+
+ let root_scroll_node = ClipId::root_scroll_node(pipeline_id);
let scroll_frame_info = self.id_to_index_mapper.simple_scroll_and_clip_chain(
- &ClipId::root_scroll_node(pipeline_id)
+ &root_scroll_node,
);
self.push_stacking_context(
pipeline_id,
CompositeOps::default(),
TransformStyle::Flat,
true,
true,
- scroll_frame_info,
+ root_scroll_node,
+ None,
);
// For the root pipeline, there's no need to add a full screen rectangle
// here, as it's handled by the framebuffer clear.
if self.scene.root_pipeline_id != Some(pipeline_id) {
if let Some(pipeline) = self.scene.pipelines.get(&pipeline_id) {
if let Some(bg_color) = pipeline.background_color {
let root_bounds = LayerRect::new(LayerPoint::zero(), *frame_size);
let info = LayerPrimitiveInfo::new(root_bounds);
self.add_solid_rectangle(
reference_frame_info,
&info,
bg_color,
None,
+ Vec::new(),
);
}
}
}
self.flatten_items(&mut pipeline.display_list.iter(), pipeline_id, LayerVector2D::zero());
if self.config.enable_scrollbars {
@@ -402,56 +406,56 @@ impl<'a> DisplayListFlattener<'a> {
fn flatten_sticky_frame(
&mut self,
item: &DisplayItemRef,
info: &StickyFrameDisplayItem,
clip_and_scroll: &ScrollNodeAndClipChain,
parent_id: &ClipId,
reference_frame_relative_offset: &LayerVector2D,
) {
- let frame_rect = item.rect().translate(&reference_frame_relative_offset);
+ let frame_rect = item.rect().translate(reference_frame_relative_offset);
let sticky_frame_info = StickyFrameInfo::new(
info.margins,
info.vertical_offset_bounds,
info.horizontal_offset_bounds,
info.previously_applied_offset,
);
let index = self.id_to_index_mapper.get_node_index(info.id);
self.clip_scroll_tree.add_sticky_frame(
index,
clip_and_scroll.scroll_node_id, /* parent id */
frame_rect,
sticky_frame_info,
info.id.pipeline_id(),
);
- self.id_to_index_mapper.map_to_parent_clip_chain(info.id, &parent_id);
+ self.id_to_index_mapper.map_to_parent_clip_chain(info.id, parent_id);
}
fn flatten_scroll_frame(
&mut self,
item: &DisplayItemRef,
info: &ScrollFrameDisplayItem,
pipeline_id: PipelineId,
clip_and_scroll_ids: &ClipAndScrollInfo,
reference_frame_relative_offset: &LayerVector2D,
) {
let complex_clips = self.get_complex_clips(pipeline_id, item.complex_clip().0);
let clip_region = ClipRegion::create_for_clip_node(
*item.clip_rect(),
complex_clips,
info.image_mask,
- &reference_frame_relative_offset,
+ reference_frame_relative_offset,
);
// Just use clip rectangle as the frame rect for this scroll frame.
// This is useful when calculating scroll extents for the
// ClipScrollNode::scroll(..) API as well as for properly setting sticky
// positioning offsets.
- let frame_rect = item.clip_rect().translate(&reference_frame_relative_offset);
- let content_rect = item.rect().translate(&reference_frame_relative_offset);
+ let frame_rect = item.clip_rect().translate(reference_frame_relative_offset);
+ let content_rect = item.rect().translate(reference_frame_relative_offset);
debug_assert!(info.clip_id != info.scroll_frame_id);
self.add_clip_node(info.clip_id, clip_and_scroll_ids.scroll_node_id, clip_region);
self.add_scroll_frame(
info.scroll_frame_id,
info.clip_id,
@@ -462,22 +466,21 @@ impl<'a> DisplayListFlattener<'a> {
info.scroll_sensitivity,
);
}
fn flatten_stacking_context(
&mut self,
traversal: &mut BuiltDisplayListIter<'a>,
pipeline_id: PipelineId,
+ item: &DisplayItemRef,
+ stacking_context: &StackingContext,
unreplaced_scroll_id: ClipId,
mut scroll_node_id: ClipId,
mut reference_frame_relative_offset: LayerVector2D,
- bounds: &LayerRect,
- stacking_context: &StackingContext,
- filters: ItemRange<FilterOp>,
is_backface_visible: bool,
) {
// Avoid doing unnecessary work for empty stacking contexts.
if traversal.current_stacking_context_empty() {
traversal.skip_current_stacking_context();
return;
}
@@ -485,26 +488,27 @@ impl<'a> DisplayListFlattener<'a> {
// TODO(optimization?): self.traversal.display_list()
let display_list = &self
.scene
.pipelines
.get(&pipeline_id)
.expect("No display list?!")
.display_list;
CompositeOps::new(
- stacking_context.filter_ops_for_compositing(display_list, filters),
+ stacking_context.filter_ops_for_compositing(display_list, item.filters()),
stacking_context.mix_blend_mode_for_compositing(),
)
};
if stacking_context.scroll_policy == ScrollPolicy::Fixed {
scroll_node_id = self.current_reference_frame_id();
self.replacements.push((unreplaced_scroll_id, scroll_node_id));
}
+ let bounds = item.rect();
reference_frame_relative_offset += bounds.origin.to_vector();
// If we have a transformation or a perspective, we should have been assigned a new
// reference frame id. This means this stacking context establishes a new reference frame.
// Descendant fixed position content will be positioned relative to us.
if let Some(reference_frame_id) = stacking_context.reference_frame_id {
debug_assert!(
stacking_context.transform.is_some() ||
@@ -522,26 +526,25 @@ impl<'a> DisplayListFlattener<'a> {
reference_frame_relative_offset,
);
self.replacements.push((unreplaced_scroll_id, reference_frame_id));
reference_frame_relative_offset = LayerVector2D::zero();
}
// We apply the replacements one more time in case we need to set it to a replacement
// that we just pushed above.
- let sc_scroll_node = self.apply_scroll_frame_id_replacement(unreplaced_scroll_id);
- let stacking_context_clip_and_scroll =
- self.id_to_index_mapper.simple_scroll_and_clip_chain(&sc_scroll_node);
+ let final_scroll_node = self.apply_scroll_frame_id_replacement(unreplaced_scroll_id);
self.push_stacking_context(
pipeline_id,
composition_operations,
stacking_context.transform_style,
is_backface_visible,
false,
- stacking_context_clip_and_scroll,
+ final_scroll_node,
+ stacking_context.clip_node_id,
);
self.flatten_items(
traversal,
pipeline_id,
reference_frame_relative_offset,
);
@@ -572,17 +575,17 @@ impl<'a> DisplayListFlattener<'a> {
self.id_to_index_mapper.initialize_for_pipeline(pipeline);
self.add_clip_node(
info.clip_id,
clip_and_scroll_ids.scroll_node_id,
ClipRegion::create_for_clip_node_with_local_clip(
&LocalClip::from(*item.clip_rect()),
- &reference_frame_relative_offset
+ reference_frame_relative_offset
),
);
let epoch = self.scene.pipeline_epochs[&iframe_pipeline_id];
self.pipeline_epochs.push((iframe_pipeline_id, epoch));
let bounds = item.rect();
let iframe_rect = LayerRect::new(LayerPoint::zero(), bounds.size);
@@ -683,26 +686,26 @@ impl<'a> DisplayListFlattener<'a> {
SpecificDisplayItem::Text(ref text_info) => {
self.add_text(
clip_and_scroll,
reference_frame_relative_offset,
&prim_info,
&text_info.font_key,
&text_info.color,
item.glyphs(),
- item.display_list().get(item.glyphs()).count(),
text_info.glyph_options,
);
}
SpecificDisplayItem::Rectangle(ref info) => {
self.add_solid_rectangle(
clip_and_scroll,
&prim_info,
info.color,
None,
+ Vec::new(),
);
}
SpecificDisplayItem::ClearRectangle => {
self.add_clear_rectangle(
clip_and_scroll,
&prim_info,
);
}
@@ -769,22 +772,21 @@ impl<'a> DisplayListFlattener<'a> {
item.display_list().get(item.gradient_stops()).count(),
);
}
SpecificDisplayItem::PushStackingContext(ref info) => {
let mut subtraversal = item.sub_iter();
self.flatten_stacking_context(
&mut subtraversal,
pipeline_id,
+ &item,
+ &info.stacking_context,
unreplaced_scroll_id,
clip_and_scroll_ids.scroll_node_id,
reference_frame_relative_offset,
- &item.rect(),
- &info.stacking_context,
- item.filters(),
prim_info.is_backface_visible,
);
return Some(subtraversal);
}
SpecificDisplayItem::Iframe(ref info) => {
self.flatten_iframe(
&item,
info,
@@ -864,26 +866,24 @@ impl<'a> DisplayListFlattener<'a> {
let stacking_context = self.sc_stack.last().expect("bug: no stacking context!");
let clip_sources = if clip_sources.is_empty() {
None
} else {
Some(self.clip_store.insert(ClipSources::new(clip_sources)))
};
- let prim_index = self.prim_store.add_primitive(
+ self.prim_store.add_primitive(
&info.rect,
&info.clip_rect,
info.is_backface_visible && stacking_context.is_backface_visible,
clip_sources,
info.tag,
container,
- );
-
- prim_index
+ )
}
pub fn add_primitive_to_hit_testing_list(
&mut self,
info: &LayerPrimitiveInfo,
clip_and_scroll: ScrollNodeAndClipChain
) {
let tag = match info.tag {
@@ -964,18 +964,28 @@ impl<'a> DisplayListFlattener<'a> {
pub fn push_stacking_context(
&mut self,
pipeline_id: PipelineId,
composite_ops: CompositeOps,
transform_style: TransformStyle,
is_backface_visible: bool,
is_pipeline_root: bool,
- clip_and_scroll: ScrollNodeAndClipChain,
+ positioning_node: ClipId,
+ clipping_node: Option<ClipId>,
) {
+ let clip_chain_id = match clipping_node {
+ Some(ref clipping_node) => self.id_to_index_mapper.get_clip_chain_index(clipping_node),
+ None => ClipChainIndex(0), // This means no clipping.
+ };
+ let clip_and_scroll = ScrollNodeAndClipChain::new(
+ self.id_to_index_mapper.get_node_index(positioning_node),
+ clip_chain_id
+ );
+
// Construct the necessary set of Picture primitives
// to draw this stacking context.
let current_reference_frame_index = self.current_reference_frame_index();
// An arbitrary large clip rect. For now, we don't
// specify a clip specific to the stacking context.
// However, now that they are represented as Picture
// primitives, we can apply any kind of clip mask
@@ -1069,20 +1079,17 @@ impl<'a> DisplayListFlattener<'a> {
None,
None,
PrimitiveContainer::Brush(prim),
);
let parent_pic_index = *self.picture_stack.last().unwrap();
let pic = &mut self.prim_store.pictures[parent_pic_index.0];
- pic.add_primitive(
- prim_index,
- clip_and_scroll,
- );
+ pic.add_primitive(prim_index, clip_and_scroll);
self.picture_stack.push(container_index);
Some(container_index)
} else {
None
};
@@ -1155,20 +1162,17 @@ impl<'a> DisplayListFlattener<'a> {
if let Some(shadow_prim_index) = shadow_prim_index {
parent_pic.add_primitive(
shadow_prim_index,
clip_and_scroll,
);
}
- parent_pic.add_primitive(
- src_prim_index,
- clip_and_scroll,
- );
+ parent_pic.add_primitive(src_prim_index, clip_and_scroll);
self.picture_stack.push(src_pic_index);
}
// Same for mix-blend-mode.
if let Some(mix_blend_mode) = composite_ops.mix_blend_mode {
let src_pic_index = self.prim_store.add_image_picture(
Some(PictureCompositeMode::MixBlend(mix_blend_mode)),
@@ -1191,37 +1195,38 @@ impl<'a> DisplayListFlattener<'a> {
is_backface_visible,
None,
None,
PrimitiveContainer::Brush(src_prim),
);
let parent_pic = &mut self.prim_store.pictures[parent_pic_index.0];
parent_pic_index = src_pic_index;
- parent_pic.add_primitive(
- src_prim_index,
- clip_and_scroll,
- );
+ parent_pic.add_primitive(src_prim_index, clip_and_scroll);
self.picture_stack.push(src_pic_index);
}
// By default, this picture will be collapsed into
// the owning target.
let mut composite_mode = None;
let mut frame_output_pipeline_id = None;
// If this stacking context if the root of a pipeline, and the caller
// has requested it as an output frame, create a render task to isolate it.
if is_pipeline_root && self.output_pipelines.contains(&pipeline_id) {
composite_mode = Some(PictureCompositeMode::Blit);
frame_output_pipeline_id = Some(pipeline_id);
}
- if participating_in_3d_context {
+ // Force an intermediate surface if the stacking context
+ // has a clip node. In the future, we may decide during
+ // prepare step to skip the intermediate surface if the
+ // clip node doesn't affect the stacking context rect.
+ if participating_in_3d_context || clipping_node.is_some() {
// TODO(gw): For now, as soon as this picture is in
// a 3D context, we draw it to an intermediate
// surface and apply plane splitting. However,
// there is a large optimization opportunity here.
// During culling, we can check if there is actually
// perspective present, and skip the plane splitting
// completely when that is not the case.
composite_mode = Some(PictureCompositeMode::Blit);
@@ -1229,17 +1234,17 @@ impl<'a> DisplayListFlattener<'a> {
// Add picture for this actual stacking context contents to render into.
let pic_index = self.prim_store.add_image_picture(
composite_mode,
participating_in_3d_context,
pipeline_id,
current_reference_frame_index,
frame_output_pipeline_id,
- true,
+ true,
);
// Create a brush primitive that draws this picture.
let sc_prim = BrushPrimitive::new_picture(
pic_index,
BrushImageSourceKind::Color,
LayoutVector2D::zero(),
);
@@ -1503,16 +1508,17 @@ impl<'a> DisplayListFlattener<'a> {
}
pub fn add_solid_rectangle(
&mut self,
clip_and_scroll: ScrollNodeAndClipChain,
info: &LayerPrimitiveInfo,
color: ColorF,
segments: Option<BrushSegmentDescriptor>,
+ extra_clips: Vec<ClipSource>,
) {
if color.a == 0.0 {
// Don't add transparent rectangles to the draw list, but do consider them for hit
// testing. This allows specifying invisible hit testing areas.
self.add_primitive_to_hit_testing_list(info, clip_and_scroll);
return;
}
@@ -1521,17 +1527,17 @@ impl<'a> DisplayListFlattener<'a> {
color,
},
segments,
);
self.add_primitive(
clip_and_scroll,
info,
- Vec::new(),
+ extra_clips,
PrimitiveContainer::Brush(prim),
);
}
pub fn add_clear_rectangle(
&mut self,
clip_and_scroll: ScrollNodeAndClipChain,
info: &LayerPrimitiveInfo,
@@ -2060,22 +2066,21 @@ impl<'a> DisplayListFlattener<'a> {
pub fn add_text(
&mut self,
clip_and_scroll: ScrollNodeAndClipChain,
run_offset: LayoutVector2D,
prim_info: &LayerPrimitiveInfo,
font_instance_key: &FontInstanceKey,
text_color: &ColorF,
glyph_range: ItemRange<GlyphInstance>,
- glyph_count: usize,
glyph_options: Option<GlyphOptions>,
) {
let prim = {
let instance_map = self.font_instances.read().unwrap();
- let font_instance = match instance_map.get(&font_instance_key) {
+ let font_instance = match instance_map.get(font_instance_key) {
Some(instance) => instance,
None => {
warn!("Unknown font instance key");
debug!("key={:?}", font_instance_key);
return;
}
};
@@ -2109,17 +2114,17 @@ impl<'a> DisplayListFlattener<'a> {
// There are some conditions under which we can't use
// subpixel text rendering, even if enabled.
if render_mode == FontRenderMode::Subpixel {
// text on a picture that has filters
// (e.g. opacity) can't use sub-pixel.
// TODO(gw): It's possible we can relax this in
// the future, if we modify the way
// we handle subpixel blending.
- if let Some(ref stacking_context) = self.sc_stack.last() {
+ if let Some(stacking_context) = self.sc_stack.last() {
if !stacking_context.allow_subpixel_aa {
render_mode = FontRenderMode::Alpha;
}
}
}
let prim_font = FontInstance::new(
font_instance.font_key,
@@ -2130,17 +2135,16 @@ impl<'a> DisplayListFlattener<'a> {
font_instance.subpx_dir,
flags,
font_instance.platform_options,
font_instance.variations.clone(),
);
TextRunPrimitiveCpu {
font: prim_font,
glyph_range,
- glyph_count,
glyph_gpu_blocks: Vec::new(),
glyph_keys: Vec::new(),
offset: run_offset,
shadow: false,
}
};
self.add_primitive(
@@ -2172,27 +2176,43 @@ impl<'a> DisplayListFlattener<'a> {
}
let request = ImageRequest {
key: image_key,
rendering: image_rendering,
tile: tile_offset,
};
+ let sub_rect = sub_rect.map(|texel_rect| {
+ DeviceIntRect::new(
+ DeviceIntPoint::new(
+ texel_rect.uv0.x as i32,
+ texel_rect.uv0.y as i32,
+ ),
+ DeviceIntSize::new(
+ (texel_rect.uv1.x - texel_rect.uv0.x) as i32,
+ (texel_rect.uv1.y - texel_rect.uv0.y) as i32,
+ ),
+ )
+ });
+
// See if conditions are met to run through the new
// image brush shader, which supports segments.
if tile_spacing == LayerSize::zero() &&
stretch_size == info.rect.size &&
- sub_rect.is_none() &&
tile_offset.is_none() {
let prim = BrushPrimitive::new(
BrushKind::Image {
request,
current_epoch: Epoch::invalid(),
alpha_type,
+ stretch_size,
+ tile_spacing,
+ source: ImageSource::Default,
+ sub_rect,
},
None,
);
self.add_primitive(
clip_and_scroll,
info,
Vec::new(),
@@ -2202,28 +2222,17 @@ impl<'a> DisplayListFlattener<'a> {
let prim_cpu = ImagePrimitiveCpu {
tile_spacing,
alpha_type,
stretch_size,
current_epoch: Epoch::invalid(),
source: ImageSource::Default,
key: ImageCacheKey {
request,
- texel_rect: sub_rect.map(|texel_rect| {
- DeviceIntRect::new(
- DeviceIntPoint::new(
- texel_rect.uv0.x as i32,
- texel_rect.uv0.y as i32,
- ),
- DeviceIntSize::new(
- (texel_rect.uv1.x - texel_rect.uv0.x) as i32,
- (texel_rect.uv1.y - texel_rect.uv0.y) as i32,
- ),
- )
- }),
+ texel_rect: sub_rect,
},
};
self.add_primitive(
clip_and_scroll,
info,
Vec::new(),
PrimitiveContainer::Image(prim_cpu),
--- a/gfx/webrender/src/ellipse.rs
+++ b/gfx/webrender/src/ellipse.rs
@@ -117,17 +117,17 @@ impl Ellipse {
let ry = (s - u) * (3.0_f32).sqrt();
let rm = (rx * rx + ry * ry).sqrt();
let p = ry / (rm - rx).sqrt();
(p + 2.0 * g / rm - m) / 2.0
};
let si = (1.0 - co * co).sqrt();
let r = LayerVector2D::new(ab.x * co, ab.y * si);
- return (r - p).length() * (p.y - r.y).signum();
+ (r - p).length() * (p.y - r.y).signum()
}
}
/// 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/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -289,17 +289,17 @@ impl FrameBuilder {
.total_primitives
.set(self.prim_store.prim_count());
resource_cache.begin_frame(frame_id);
gpu_cache.begin_frame();
let mut node_data = Vec::with_capacity(clip_scroll_tree.nodes.len());
let total_prim_runs =
- self.prim_store.pictures.iter().fold(1, |count, ref pic| count + pic.runs.len());
+ self.prim_store.pictures.iter().fold(1, |count, pic| count + pic.runs.len());
let mut clip_chain_local_clip_rects = Vec::with_capacity(total_prim_runs);
clip_chain_local_clip_rects.push(LayerRect::max_rect());
clip_scroll_tree.update_tree(
&self.screen_rect.to_i32(),
device_pixel_scale,
&mut self.clip_store,
resource_cache,
@@ -409,14 +409,14 @@ impl FrameBuilder {
has_been_rendered: false,
has_texture_cache_tasks,
}
}
pub fn create_hit_tester(&mut self, clip_scroll_tree: &ClipScrollTree) -> HitTester {
HitTester::new(
&self.hit_testing_runs,
- &clip_scroll_tree,
+ clip_scroll_tree,
&self.clip_store
)
}
}
--- a/gfx/webrender/src/glyph_rasterizer.rs
+++ b/gfx/webrender/src/glyph_rasterizer.rs
@@ -772,17 +772,17 @@ impl GlyphRasterizer {
GlyphRasterResult::LoadFailed => GlyphCacheEntry::Blank,
GlyphRasterResult::Bitmap(ref glyph) if glyph.width == 0 ||
glyph.height == 0 => {
GlyphCacheEntry::Blank
}
GlyphRasterResult::Bitmap(glyph) => {
assert_eq!((glyph.left.fract(), glyph.top.fract()), (0.0, 0.0));
let mut texture_cache_handle = TextureCacheHandle::new();
- texture_cache.request(&mut texture_cache_handle, gpu_cache);
+ texture_cache.request(&texture_cache_handle, gpu_cache);
texture_cache.update(
&mut texture_cache_handle,
ImageDescriptor {
width: glyph.width,
height: glyph.height,
stride: None,
format: ImageFormat::BGRA8,
is_opaque: false,
@@ -840,36 +840,36 @@ impl GlyphRasterizer {
}
trait AddFont {
fn add_font(&mut self, font_key: &FontKey, template: &FontTemplate);
}
impl AddFont for FontContext {
fn add_font(&mut self, font_key: &FontKey, template: &FontTemplate) {
- match template {
- &FontTemplate::Raw(ref bytes, index) => {
- self.add_raw_font(&font_key, bytes.clone(), index);
+ match *template {
+ FontTemplate::Raw(ref bytes, index) => {
+ self.add_raw_font(font_key, bytes.clone(), index);
}
- &FontTemplate::Native(ref native_font_handle) => {
- self.add_native_font(&font_key, (*native_font_handle).clone());
+ FontTemplate::Native(ref native_font_handle) => {
+ self.add_native_font(font_key, (*native_font_handle).clone());
}
}
}
}
#[cfg(feature = "pathfinder")]
impl AddFont for PathfinderFontContext {
fn add_font(&mut self, font_key: &FontKey, template: &FontTemplate) {
- match template {
- &FontTemplate::Raw(ref bytes, index) => {
- drop(self.add_font_from_memory(&font_key, bytes.clone(), index));
+ match *template {
+ FontTemplate::Raw(ref bytes, index) => {
+ drop(self.add_font_from_memory(font_key, bytes.clone(), index));
}
- &FontTemplate::Native(ref native_font_handle) => {
- drop(self.add_native_font(&font_key, (*native_font_handle).clone().0));
+ FontTemplate::Native(ref native_font_handle) => {
+ drop(self.add_native_font(font_key, (*native_font_handle).clone().0));
}
}
}
}
#[derive(Clone, Hash, PartialEq, Eq, Debug, Ord, PartialOrd)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
--- a/gfx/webrender/src/hit_test.rs
+++ b/gfx/webrender/src/hit_test.rs
@@ -70,27 +70,30 @@ impl HitTestingItem {
}
}
}
#[derive(Clone)]
pub struct HitTestingRun(pub Vec<HitTestingItem>, pub ScrollNodeAndClipChain);
enum HitTestRegion {
- Rectangle(LayerRect),
+ Rectangle(LayerRect, ClipMode),
RoundedRectangle(LayerRect, BorderRadius, ClipMode),
}
impl HitTestRegion {
pub fn contains(&self, point: &LayerPoint) -> bool {
- match self {
- &HitTestRegion::Rectangle(ref rectangle) => rectangle.contains(point),
- &HitTestRegion::RoundedRectangle(rect, radii, ClipMode::Clip) =>
+ match *self {
+ HitTestRegion::Rectangle(ref rectangle, ClipMode::Clip) =>
+ rectangle.contains(point),
+ HitTestRegion::Rectangle(ref rectangle, ClipMode::ClipOut) =>
+ !rectangle.contains(point),
+ HitTestRegion::RoundedRectangle(rect, radii, ClipMode::Clip) =>
rounded_rectangle_contains_point(point, &rect, &radii),
- &HitTestRegion::RoundedRectangle(rect, radii, ClipMode::ClipOut) =>
+ HitTestRegion::RoundedRectangle(rect, radii, ClipMode::ClipOut) =>
!rounded_rectangle_contains_point(point, &rect, &radii),
}
}
}
pub struct HitTester {
runs: Vec<HitTestingRun>,
nodes: Vec<HitTestClipScrollNode>,
@@ -300,22 +303,22 @@ fn get_regions_for_clip_scroll_node(
node: &ClipScrollNode,
clip_store: &ClipStore
) -> Vec<HitTestRegion> {
let clips = match node.node_type {
NodeType::Clip{ ref handle, .. } => clip_store.get(handle).clips(),
_ => return Vec::new(),
};
- clips.iter().map(|ref source| {
+ clips.iter().map(|source| {
match source.0 {
- ClipSource::Rectangle(ref rect) => HitTestRegion::Rectangle(*rect),
+ ClipSource::Rectangle(ref rect, mode) => HitTestRegion::Rectangle(*rect, mode),
ClipSource::RoundedRectangle(ref rect, ref radii, ref mode) =>
HitTestRegion::RoundedRectangle(*rect, *radii, *mode),
- ClipSource::Image(ref mask) => HitTestRegion::Rectangle(mask.rect),
+ ClipSource::Image(ref mask) => HitTestRegion::Rectangle(mask.rect, ClipMode::Clip),
ClipSource::BorderCorner(_) |
ClipSource::LineDecoration(_) |
ClipSource::BoxShadow(_) => {
unreachable!("Didn't expect to hit test against BorderCorner / BoxShadow / LineDecoration");
}
}
}).collect()
}
@@ -360,12 +363,12 @@ impl HitTest {
pub fn get_absolute_point(&self, hit_tester: &HitTester) -> WorldPoint {
if !self.flags.contains(HitTestFlags::POINT_RELATIVE_TO_PIPELINE_VIEWPORT) {
return self.point;
}
let point = &LayerPoint::new(self.point.x, self.point.y);
self.pipeline_id.map(|id|
- hit_tester.get_pipeline_root(id).world_viewport_transform.transform_point2d(&point)
+ hit_tester.get_pipeline_root(id).world_viewport_transform.transform_point2d(point)
).unwrap_or_else(|| WorldPoint::new(self.point.x, self.point.y))
}
}
--- a/gfx/webrender/src/image.rs
+++ b/gfx/webrender/src/image.rs
@@ -84,17 +84,17 @@ fn decompose_row(item_rect: &LayerRect,
let num_repetitions = (item_rect.size.width / layout_stride).ceil() as u32;
for i in 0 .. num_repetitions {
let decomposed_rect = rect(
item_rect.origin.x + (i as f32) * layout_stride,
item_rect.origin.y,
info.stretch_size.width,
item_rect.size.height,
- ).intersection(&item_rect);
+ ).intersection(item_rect);
if let Some(decomposed_rect) = decomposed_rect {
decompose_cache_tiles(&decomposed_rect, info, callback);
}
}
}
fn decompose_cache_tiles(
@@ -265,16 +265,16 @@ fn add_device_tile(
}
if shader_repeat_y {
assert_eq!(tile_offset.y, 0);
prim_rect.size.height = item_rect.size.height;
}
// Fix up the primitive's rect if it overflows the original item rect.
- if let Some(rect) = prim_rect.intersection(&item_rect) {
+ if let Some(rect) = prim_rect.intersection(item_rect) {
callback(&DecomposedTile {
tile_offset,
rect,
stretch_size,
});
}
}
--- a/gfx/webrender/src/internal_types.rs
+++ b/gfx/webrender/src/internal_types.rs
@@ -1,15 +1,14 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{DebugCommand, DeviceUintRect, DocumentId, ExternalImageData, ExternalImageId};
use api::ImageFormat;
-use clip_scroll_tree::ClipScrollNodeIndex;
use device::TextureFilter;
use renderer::PipelineInfo;
use gpu_cache::GpuCacheUpdateList;
use fxhash::FxHasher;
use profiler::BackendProfileCounters;
use std::{usize, i32};
use std::collections::{HashMap, HashSet};
use std::f32;
@@ -133,31 +132,27 @@ impl TextureUpdateList {
/// Mostly wraps a tiling::Frame, adding a bit of extra information.
pub struct RenderedDocument {
/// The pipeline info contains:
/// - The last rendered epoch for each pipeline present in the frame.
/// This information is used to know if a certain transformation on the layout has
/// been rendered, which is necessary for reftests.
/// - Pipelines that were removed from the scene.
pub pipeline_info: PipelineInfo,
- /// The layers that are currently affected by the over-scrolling animation.
- pub layers_bouncing_back: FastHashSet<ClipScrollNodeIndex>,
pub frame: tiling::Frame,
}
impl RenderedDocument {
pub fn new(
pipeline_info: PipelineInfo,
- layers_bouncing_back: FastHashSet<ClipScrollNodeIndex>,
frame: tiling::Frame,
) -> Self {
RenderedDocument {
pipeline_info,
- layers_bouncing_back,
frame,
}
}
}
pub enum DebugOutput {
FetchDocuments(String),
FetchClipScrollTree(String),
--- a/gfx/webrender/src/lib.rs
+++ b/gfx/webrender/src/lib.rs
@@ -95,17 +95,16 @@ mod record;
mod render_backend;
mod render_task;
mod renderer;
mod resource_cache;
mod scene;
mod scene_builder;
mod segment;
mod shade;
-mod spring;
mod texture_allocator;
mod texture_cache;
mod tiling;
mod util;
mod shader_source {
include!(concat!(env!("OUT_DIR"), "/shaders.rs"));
}
@@ -180,12 +179,12 @@ extern crate base64;
extern crate png;
pub extern crate webrender_api;
#[doc(hidden)]
pub use device::{build_shader_strings, ProgramCache, ReadPixelsFormat, UploadMethod, VertexUsageHint};
pub use renderer::{CpuProfile, DebugFlags, GpuProfile, OutputImageHandler, RendererKind};
pub use renderer::{ExternalImage, ExternalImageHandler, ExternalImageSource};
-pub use renderer::{GraphicsApi, GraphicsApiInfo, Renderer, RendererOptions};
-pub use renderer::{RendererStats, ThreadListener};
+pub use renderer::{GraphicsApi, GraphicsApiInfo, PipelineInfo, Renderer, RendererOptions};
+pub use renderer::{RendererStats, SceneBuilderHooks, ThreadListener};
pub use renderer::MAX_VERTEX_TEXTURE_WIDTH;
pub use webrender_api as api;
--- a/gfx/webrender/src/picture.rs
+++ b/gfx/webrender/src/picture.rs
@@ -84,18 +84,18 @@ pub struct PicturePrimitive {
// picture.
pub extra_gpu_data_handle: GpuCacheHandle,
}
impl PicturePrimitive {
pub fn resolve_scene_properties(&mut self, properties: &SceneProperties) -> bool {
match self.composite_mode {
Some(PictureCompositeMode::Filter(ref mut filter)) => {
- match filter {
- &mut FilterOp::Opacity(ref binding, ref mut value) => {
+ match *filter {
+ FilterOp::Opacity(ref binding, ref mut value) => {
*value = properties.resolve_float(binding, *value);
}
_ => {}
}
filter.is_visible()
}
_ => true,
@@ -164,86 +164,98 @@ impl PicturePrimitive {
local_content_rect.inflate(inflate_size, inflate_size)
}
_ => {
local_content_rect
}
}
}
- pub fn prepare_for_render(
+ pub fn can_draw_directly_to_parent_surface(&self) -> bool {
+ match self.composite_mode {
+ Some(PictureCompositeMode::Filter(filter)) => {
+ filter.is_noop()
+ }
+ Some(PictureCompositeMode::Blit) |
+ Some(PictureCompositeMode::MixBlend(..)) => {
+ false
+ }
+ None => {
+ true
+ }
+ }
+ }
+
+ pub fn prepare_for_render_inner(
&mut self,
prim_index: PrimitiveIndex,
prim_metadata: &mut PrimitiveMetadata,
pic_state_for_children: PictureState,
pic_state: &mut PictureState,
frame_context: &FrameBuildingContext,
frame_state: &mut FrameBuildingState,
- ) {
+ ) -> Option<DeviceIntRect> {
let prim_screen_rect = prim_metadata
.screen_rect
.as_ref()
.expect("bug: trying to draw an off-screen picture!?");
+ if self.can_draw_directly_to_parent_surface() {
+ pic_state.tasks.extend(pic_state_for_children.tasks);
+ self.surface = None;
+ return None;
+ }
+
// TODO(gw): Almost all of the Picture types below use extra_gpu_cache_data
// to store the same type of data. The exception is the filter
// with a ColorMatrix, which stores the color matrix here. It's
// probably worth tidying this code up to be a bit more consistent.
// Perhaps store the color matrix after the common data, even though
// it's not used by that shader.
- let device_rect = match self.composite_mode {
+ match self.composite_mode {
Some(PictureCompositeMode::Filter(FilterOp::Blur(blur_radius))) => {
- // If blur radius is 0, we can skip drawing this on an
- // intermediate surface.
- if blur_radius == 0.0 {
- pic_state.tasks.extend(pic_state_for_children.tasks);
- self.surface = None;
+ let blur_std_deviation = blur_radius * frame_context.device_pixel_scale.0;
+ let blur_range = (blur_std_deviation * BLUR_SAMPLE_SCALE).ceil() as i32;
- None
- } else {
- let blur_std_deviation = blur_radius * frame_context.device_pixel_scale.0;
- let blur_range = (blur_std_deviation * BLUR_SAMPLE_SCALE).ceil() as i32;
+ // The clipped field is the part of the picture that is visible
+ // on screen. The unclipped field is the screen-space rect of
+ // the complete picture, if no screen / clip-chain was applied
+ // (this includes the extra space for blur region). To ensure
+ // that we draw a large enough part of the picture to get correct
+ // blur results, inflate that clipped area by the blur range, and
+ // then intersect with the total screen rect, to minimize the
+ // allocation size.
+ let device_rect = prim_screen_rect
+ .clipped
+ .inflate(blur_range, blur_range)
+ .intersection(&prim_screen_rect.unclipped)
+ .unwrap();
- // The clipped field is the part of the picture that is visible
- // on screen. The unclipped field is the screen-space rect of
- // the complete picture, if no screen / clip-chain was applied
- // (this includes the extra space for blur region). To ensure
- // that we draw a large enough part of the picture to get correct
- // blur results, inflate that clipped area by the blur range, and
- // then intersect with the total screen rect, to minimize the
- // allocation size.
- let device_rect = prim_screen_rect
- .clipped
- .inflate(blur_range, blur_range)
- .intersection(&prim_screen_rect.unclipped)
- .unwrap();
+ let picture_task = RenderTask::new_picture(
+ RenderTaskLocation::Dynamic(None, device_rect.size),
+ prim_index,
+ device_rect.origin,
+ pic_state_for_children.tasks,
+ );
+
+ let picture_task_id = frame_state.render_tasks.add(picture_task);
- let picture_task = RenderTask::new_picture(
- RenderTaskLocation::Dynamic(None, device_rect.size),
- prim_index,
- device_rect.origin,
- pic_state_for_children.tasks,
- );
-
- let picture_task_id = frame_state.render_tasks.add(picture_task);
+ let blur_render_task = RenderTask::new_blur(
+ blur_std_deviation,
+ picture_task_id,
+ frame_state.render_tasks,
+ RenderTargetKind::Color,
+ ClearMode::Transparent,
+ );
- let blur_render_task = RenderTask::new_blur(
- blur_std_deviation,
- picture_task_id,
- frame_state.render_tasks,
- RenderTargetKind::Color,
- ClearMode::Transparent,
- );
+ let render_task_id = frame_state.render_tasks.add(blur_render_task);
+ pic_state.tasks.push(render_task_id);
+ self.surface = Some(render_task_id);
- let render_task_id = frame_state.render_tasks.add(blur_render_task);
- pic_state.tasks.push(render_task_id);
- self.surface = Some(render_task_id);
-
- Some(device_rect)
- }
+ Some(device_rect)
}
Some(PictureCompositeMode::Filter(FilterOp::DropShadow(_, blur_radius, _))) => {
let blur_std_deviation = blur_radius * frame_context.device_pixel_scale.0;
let blur_range = (blur_std_deviation * BLUR_SAMPLE_SCALE).ceil() as i32;
// The clipped field is the part of the picture that is visible
// on screen. The unclipped field is the screen-space rect of
// the complete picture, if no screen / clip-chain was applied
@@ -301,77 +313,76 @@ impl PicturePrimitive {
let render_task_id = frame_state.render_tasks.add(picture_task);
pic_state.tasks.push(render_task_id);
self.surface = Some(render_task_id);
Some(prim_screen_rect.clipped)
}
Some(PictureCompositeMode::Filter(filter)) => {
- // If this filter is not currently going to affect
- // the picture, just collapse this picture into the
- // current render task. This most commonly occurs
- // when opacity == 1.0, but can also occur on other
- // filters and be a significant performance win.
- if filter.is_noop() {
- pic_state.tasks.extend(pic_state_for_children.tasks);
- self.surface = None;
-
- None
- } else {
- let device_rect = match filter {
- FilterOp::ColorMatrix(m) => {
- if let Some(mut request) = frame_state.gpu_cache.request(&mut self.extra_gpu_data_handle) {
- for i in 0..5 {
- request.push([m[i*4], m[i*4+1], m[i*4+2], m[i*4+3]]);
- }
+ let device_rect = match filter {
+ FilterOp::ColorMatrix(m) => {
+ if let Some(mut request) = frame_state.gpu_cache.request(&mut self.extra_gpu_data_handle) {
+ for i in 0..5 {
+ request.push([m[i*4], m[i*4+1], m[i*4+2], m[i*4+3]]);
}
-
- None
}
- _ => {
- Some(prim_screen_rect.clipped)
- }
- };
+
+ None
+ }
+ _ => Some(prim_screen_rect.clipped),
+ };
- let picture_task = RenderTask::new_picture(
- RenderTaskLocation::Dynamic(None, prim_screen_rect.clipped.size),
- prim_index,
- prim_screen_rect.clipped.origin,
- pic_state_for_children.tasks,
- );
+ let picture_task = RenderTask::new_picture(
+ RenderTaskLocation::Dynamic(None, prim_screen_rect.clipped.size),
+ prim_index,
+ prim_screen_rect.clipped.origin,
+ pic_state_for_children.tasks,
+ );
- let render_task_id = frame_state.render_tasks.add(picture_task);
- pic_state.tasks.push(render_task_id);
- self.surface = Some(render_task_id);
+ let render_task_id = frame_state.render_tasks.add(picture_task);
+ pic_state.tasks.push(render_task_id);
+ self.surface = Some(render_task_id);
- device_rect
- }
+ device_rect
}
- Some(PictureCompositeMode::Blit) => {
+ Some(PictureCompositeMode::Blit) | None => {
let picture_task = RenderTask::new_picture(
RenderTaskLocation::Dynamic(None, prim_screen_rect.clipped.size),
prim_index,
prim_screen_rect.clipped.origin,
pic_state_for_children.tasks,
);
let render_task_id = frame_state.render_tasks.add(picture_task);
pic_state.tasks.push(render_task_id);
self.surface = Some(render_task_id);
Some(prim_screen_rect.clipped)
}
- None => {
- pic_state.tasks.extend(pic_state_for_children.tasks);
- self.surface = None;
+ }
+ }
- None
- }
- };
+ pub fn prepare_for_render(
+ &mut self,
+ prim_index: PrimitiveIndex,
+ prim_metadata: &mut PrimitiveMetadata,
+ pic_state_for_children: PictureState,
+ pic_state: &mut PictureState,
+ frame_context: &FrameBuildingContext,
+ frame_state: &mut FrameBuildingState,
+ ) {
+ let device_rect = self.prepare_for_render_inner(
+ prim_index,
+ prim_metadata,
+ pic_state_for_children,
+ pic_state,
+ frame_context,
+ frame_state,
+ );
// If this picture type uses the common / general GPU data
// format, then write it now.
if let Some(device_rect) = device_rect {
// If scrolling or property animation has resulted in the task
// rect being different than last time, invalidate the GPU
// cache entry for this picture to ensure that the correct
// task rect is provided to the image shader.
--- a/gfx/webrender/src/platform/macos/font.rs
+++ b/gfx/webrender/src/platform/macos/font.rs
@@ -142,26 +142,24 @@ fn get_glyph_metrics(
left -= 1;
bottom -= 1;
right += 1;
top += 1;
let width = right - left;
let height = top - bottom;
- let metrics = GlyphMetrics {
+ GlyphMetrics {
rasterized_left: left,
rasterized_width: width as u32,
rasterized_height: height as u32,
rasterized_ascent: top,
rasterized_descent: -bottom,
advance: advance.width as f32,
- };
-
- metrics
+ }
}
#[link(name = "ApplicationServices", kind = "framework")]
extern {
static kCTFontVariationAxisIdentifierKey: CFStringRef;
static kCTFontVariationAxisNameKey: CFStringRef;
static kCTFontVariationAxisMinimumValueKey: CFStringRef;
static kCTFontVariationAxisMaximumValueKey: CFStringRef;
@@ -448,17 +446,17 @@ impl FontContext {
for pixel in data[current_height .. current_height + (width * 4)].chunks(4) {
let b = pixel[0];
let g = pixel[1];
let r = pixel[2];
let a = pixel[3];
print!("({}, {}, {}, {}) ", r, g, b, a);
}
- println!("");
+ println!();
}
}
pub fn prepare_font(font: &mut FontInstance) {
match font.render_mode {
FontRenderMode::Mono => {
// In mono mode the color of the font is irrelevant.
font.color = ColorU::new(255, 255, 255, 255);
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -208,16 +208,20 @@ pub enum BrushKind {
// A local space offset to apply when drawing
// this picture.
local_offset: LayerVector2D,
},
Image {
request: ImageRequest,
current_epoch: Epoch,
alpha_type: AlphaType,
+ stretch_size: LayerSize,
+ tile_spacing: LayerSize,
+ source: ImageSource,
+ sub_rect: Option<DeviceIntRect>,
},
YuvImage {
yuv_key: [ImageKey; 3],
format: YuvFormat,
color_space: YuvColorSpace,
image_rendering: ImageRendering,
},
RadialGradient {
@@ -628,17 +632,16 @@ impl<'a> GradientGpuBlockBuilder<'a> {
}
}
#[derive(Debug, Clone)]
pub struct TextRunPrimitiveCpu {
pub font: FontInstance,
pub offset: LayerVector2D,
pub glyph_range: ItemRange<GlyphInstance>,
- pub glyph_count: usize,
pub glyph_keys: Vec<GlyphKey>,
pub glyph_gpu_blocks: Vec<GpuBlockData>,
pub shadow: bool,
}
impl TextRunPrimitiveCpu {
pub fn get_font(
&self,
@@ -967,17 +970,16 @@ impl PrimitiveContainer {
PrimitiveContainer::TextRun(TextRunPrimitiveCpu {
font: FontInstance {
color: shadow.color.into(),
render_mode,
..info.font.clone()
},
offset: info.offset + shadow.offset,
glyph_range: info.glyph_range,
- glyph_count: info.glyph_count,
glyph_keys: info.glyph_keys.clone(),
glyph_gpu_blocks: Vec::new(),
shadow: true,
})
}
PrimitiveContainer::Brush(ref brush) => {
match brush.kind {
BrushKind::Solid { .. } => {
@@ -1046,29 +1048,28 @@ impl PrimitiveStore {
&mut self,
composite_mode: Option<PictureCompositeMode>,
is_in_3d_context: bool,
pipeline_id: PipelineId,
reference_frame_index: ClipScrollNodeIndex,
frame_output_pipeline_id: Option<PipelineId>,
apply_local_clip_rect: bool,
) -> PictureIndex {
- let pic = PicturePrimitive::new_image(
+ let picture = PicturePrimitive::new_image(
composite_mode,
is_in_3d_context,
pipeline_id,
reference_frame_index,
frame_output_pipeline_id,
apply_local_clip_rect,
);
- let pic_index = PictureIndex(self.pictures.len());
- self.pictures.push(pic);
-
- pic_index
+ let picture_index = PictureIndex(self.pictures.len());
+ self.pictures.push(picture);
+ picture_index
}
pub fn add_primitive(
&mut self,
local_rect: &LayerRect,
local_clip_rect: &LayerRect,
is_backface_visible: bool,
clip_sources: Option<ClipSourcesHandle>,
@@ -1296,34 +1297,111 @@ impl PrimitiveStore {
);
}
}
}
PrimitiveKind::Brush => {
let brush = &mut self.cpu_brushes[metadata.cpu_prim_index.0];
match brush.kind {
- BrushKind::Image { request, ref mut current_epoch, .. } => {
+ BrushKind::Image { request, sub_rect, ref mut current_epoch, ref mut source, .. } => {
let image_properties = frame_state
.resource_cache
.get_image_properties(request.key);
+ // Set if we need to request the source image from the cache this frame.
if let Some(image_properties) = image_properties {
// See if this image has been updated since we last hit this code path.
// If so, we need to update the opacity.
if image_properties.epoch != *current_epoch {
*current_epoch = image_properties.epoch;
metadata.opacity.is_opaque = image_properties.descriptor.is_opaque;
}
+
+ // Work out whether this image is a normal / simple type, or if
+ // we need to pre-render it to the render task cache.
+ if let Some(rect) = sub_rect {
+ *source = ImageSource::Cache {
+ // Size in device-pixels we need to allocate in render task cache.
+ size: rect.size,
+ item: CacheItem::invalid(),
+ };
+ }
+
+ let mut request_source_image = false;
+
+ // Every frame, for cached items, we need to request the render
+ // task cache item. The closure will be invoked on the first
+ // time through, and any time the render task output has been
+ // evicted from the texture cache.
+ match *source {
+ ImageSource::Cache { size, ref mut item } => {
+ let image_cache_key = ImageCacheKey {
+ request,
+ texel_rect: sub_rect,
+ };
+
+ // Request a pre-rendered image task.
+ *item = frame_state.resource_cache.request_render_task(
+ RenderTaskCacheKey {
+ size,
+ kind: RenderTaskCacheKeyKind::Image(image_cache_key),
+ },
+ frame_state.gpu_cache,
+ frame_state.render_tasks,
+ None,
+ |render_tasks| {
+ // We need to render the image cache this frame,
+ // so will need access to the source texture.
+ request_source_image = true;
+
+ // Create a task to blit from the texture cache to
+ // a normal transient render task surface. This will
+ // copy only the sub-rect, if specified.
+ let cache_to_target_task = RenderTask::new_blit(
+ size,
+ BlitSource::Image { key: image_cache_key },
+ );
+ let cache_to_target_task_id = render_tasks.add(cache_to_target_task);
+
+ // Create a task to blit the rect from the child render
+ // task above back into the right spot in the persistent
+ // render target cache.
+ let target_to_cache_task = RenderTask::new_blit(
+ size,
+ BlitSource::RenderTask {
+ task_id: cache_to_target_task_id,
+ },
+ );
+ let target_to_cache_task_id = render_tasks.add(target_to_cache_task);
+
+ // Hook this into the render task tree at the right spot.
+ pic_state.tasks.push(target_to_cache_task_id);
+
+ // Pass the image opacity, so that the cached render task
+ // item inherits the same opacity properties.
+ (target_to_cache_task_id, image_properties.descriptor.is_opaque)
+ }
+ );
+
+ }
+ ImageSource::Default => {
+ // Normal images just reference the source texture each frame.
+ request_source_image = true;
+ }
+ }
+
+ if request_source_image {
+ frame_state.resource_cache.request_image(
+ request,
+ frame_state.gpu_cache,
+ );
+ }
}
- frame_state.resource_cache.request_image(
- request,
- frame_state.gpu_cache,
- );
}
BrushKind::YuvImage { format, yuv_key, image_rendering, .. } => {
let channel_num = format.get_plane_num();
debug_assert!(channel_num <= 3);
for channel in 0 .. channel_num {
frame_state.resource_cache.request_image(
ImageRequest {
key: yuv_key[channel],
@@ -1459,51 +1537,65 @@ impl PrimitiveStore {
}
}
None => {
// If no segment descriptor built yet, see if it is a brush
// type that wants to be segmented.
if !brush.kind.supports_segments() {
return;
}
- if metadata.local_rect.size.area() <= MIN_BRUSH_SPLIT_AREA {
- return;
- }
}
}
+ // If the brush is small, we generally want to skip building segments
+ // and just draw it as a single primitive with clip mask. However,
+ // if the clips are purely rectangles that have no per-fragment
+ // clip masks, we will segment anyway. This allows us to completely
+ // skip allocating a clip mask in these cases.
+ let is_large = metadata.local_rect.size.area() > MIN_BRUSH_SPLIT_AREA;
+
+ // TODO(gw): We should probably detect and store this on each
+ // ClipSources instance, to avoid having to iterate
+ // the clip sources here.
+ let mut rect_clips_only = true;
+
let mut segment_builder = SegmentBuilder::new(
metadata.local_rect,
None,
metadata.local_clip_rect
);
// If this primitive is clipped by clips from a different coordinate system, then we
// need to apply a clip mask for the entire primitive.
- let mut clip_mask_kind = match has_clips_from_other_coordinate_systems {
- true => BrushClipMaskKind::Global,
- false => BrushClipMaskKind::Individual,
+ let mut clip_mask_kind = if has_clips_from_other_coordinate_systems {
+ BrushClipMaskKind::Global
+ } else {
+ BrushClipMaskKind::Individual
};
// Segment the primitive on all the local-space clip sources that we can.
for clip_item in clips {
if clip_item.coordinate_system_id != prim_run_context.scroll_node.coordinate_system_id {
continue;
}
let local_clips = frame_state.clip_store.get_opt(&clip_item.clip_sources).expect("bug");
for &(ref clip, _) in &local_clips.clips {
let (local_clip_rect, radius, mode) = match *clip {
ClipSource::RoundedRectangle(rect, radii, clip_mode) => {
+ rect_clips_only = false;
+
(rect, Some(radii), clip_mode)
}
- ClipSource::Rectangle(rect) => {
- (rect, None, ClipMode::Clip)
+ ClipSource::Rectangle(rect, mode) => {
+ (rect, None, mode)
}
ClipSource::BoxShadow(ref info) => {
+ rect_clips_only = false;
+
// For inset box shadows, we can clip out any
// pixels that are inside the shadow region
// and are beyond the inner rect, as they can't
// be affected by the blur radius.
let inner_clip_mode = match info.clip_mode {
BoxShadowClipMode::Outset => None,
BoxShadowClipMode::Inset => Some(ClipMode::ClipOut),
};
@@ -1521,16 +1613,18 @@ impl PrimitiveStore {
inner_clip_mode,
);
continue;
}
ClipSource::BorderCorner(..) |
ClipSource::LineDecoration(..) |
ClipSource::Image(..) => {
+ rect_clips_only = false;
+
// TODO(gw): We can easily extend the segment builder
// to support these clip sources in the
// future, but they are rarely used.
clip_mask_kind = BrushClipMaskKind::Global;
continue;
}
};
@@ -1552,42 +1646,44 @@ impl PrimitiveStore {
relative_transform.transform_rect(&local_clip_rect)
};
segment_builder.push_clip_rect(local_clip_rect, radius, mode);
}
}
- match brush.segment_desc {
- Some(ref mut segment_desc) => {
- segment_desc.clip_mask_kind = clip_mask_kind;
- }
- None => {
- // TODO(gw): We can probably make the allocation
- // patterns of this and the segment
- // builder significantly better, by
- // retaining it across primitives.
- let mut segments = Vec::new();
+ if is_large || rect_clips_only {
+ match brush.segment_desc {
+ Some(ref mut segment_desc) => {
+ segment_desc.clip_mask_kind = clip_mask_kind;
+ }
+ None => {
+ // TODO(gw): We can probably make the allocation
+ // patterns of this and the segment
+ // builder significantly better, by
+ // retaining it across primitives.
+ let mut segments = Vec::new();
- segment_builder.build(|segment| {
- segments.push(
- BrushSegment::new(
- segment.rect.origin,
- segment.rect.size,
- segment.has_mask,
- segment.edge_flags,
- ),
- );
- });
+ segment_builder.build(|segment| {
+ segments.push(
+ BrushSegment::new(
+ segment.rect.origin,
+ segment.rect.size,
+ segment.has_mask,
+ segment.edge_flags,
+ ),
+ );
+ });
- brush.segment_desc = Some(BrushSegmentDescriptor {
- segments,
- clip_mask_kind,
- });
+ brush.segment_desc = Some(BrushSegmentDescriptor {
+ segments,
+ clip_mask_kind,
+ });
+ }
}
}
}
fn update_clip_task_for_brush(
&mut self,
prim_run_context: &PrimitiveRunContext,
prim_index: PrimitiveIndex,
@@ -1693,17 +1789,17 @@ impl PrimitiveStore {
let mut combined_outer_rect =
prim_screen_rect.intersection(&prim_run_context.clip_chain.combined_outer_screen_rect);
let clip_chain = prim_run_context.clip_chain.nodes.clone();
let prim_coordinate_system_id = prim_run_context.scroll_node.coordinate_system_id;
let transform = &prim_run_context.scroll_node.world_content_transform;
let extra_clip = {
let metadata = &self.cpu_metadata[prim_index.0];
- metadata.clip_sources.as_ref().map(|ref clip_sources| {
+ metadata.clip_sources.as_ref().map(|clip_sources| {
let prim_clips = frame_state.clip_store.get_mut(clip_sources);
prim_clips.update(
frame_state.gpu_cache,
frame_state.resource_cache,
frame_context.device_pixel_scale,
);
let (screen_inner_rect, screen_outer_rect) =
prim_clips.get_screen_bounds(transform, frame_context.device_pixel_scale);
@@ -2118,17 +2214,17 @@ fn convert_clip_chain_to_clip_vector(
.chain(ClipChainNodeIter { current: clip_chain_nodes })
.filter_map(|node| {
*has_clips_from_other_coordinate_systems |=
prim_coordinate_system != node.work_item.coordinate_system_id;
*combined_inner_rect = if !node.screen_inner_rect.is_empty() {
// If this clip's inner area contains the area of the primitive clipped
// by previous clips, then it's not going to affect rendering in any way.
- if node.screen_inner_rect.contains_rect(&combined_outer_rect) {
+ if node.screen_inner_rect.contains_rect(combined_outer_rect) {
return None;
}
combined_inner_rect.intersection(&node.screen_inner_rect)
.unwrap_or_else(DeviceIntRect::zero)
} else {
DeviceIntRect::zero()
};
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -3,17 +3,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{ApiMsg, BuiltDisplayList, ClearCache, DebugCommand};
#[cfg(feature = "debugger")]
use api::{BuiltDisplayListIter, SpecificDisplayItem};
use api::{DeviceIntPoint, DevicePixelScale, DeviceUintPoint, DeviceUintRect, DeviceUintSize};
use api::{DocumentId, DocumentLayer, ExternalScrollId, FrameMsg, HitTestResult};
use api::{IdNamespace, LayerPoint, PipelineId, RenderNotifier, SceneMsg, ScrollClamping};
-use api::{ScrollEventPhase, ScrollLocation, ScrollNodeState, TransactionMsg, WorldPoint};
+use api::{ScrollLocation, ScrollNodeState, TransactionMsg, WorldPoint};
use api::channel::{MsgReceiver, Payload};
#[cfg(feature = "capture")]
use api::CaptureBits;
#[cfg(feature = "replay")]
use api::CapturedDocument;
use clip_scroll_tree::ClipScrollTree;
#[cfg(feature = "debugger")]
use debug_server;
@@ -259,16 +259,17 @@ impl Document {
};
scene_tx.send(SceneBuilderRequest::Transaction {
scene: scene_request,
resource_updates: transaction_msg.resource_updates,
frame_ops: transaction_msg.frame_ops,
render: transaction_msg.generate_frame,
document_id,
+ current_epochs: self.current.scene.pipeline_epochs.clone(),
}).unwrap();
}
fn render(
&mut self,
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
resource_profile: &mut ResourceProfileCounters,
@@ -295,56 +296,49 @@ impl Document {
self.hit_tester = Some(frame_builder.create_hit_tester(&self.clip_scroll_tree));
frame
};
self.make_rendered_document(frame, removed_pipelines)
}
pub fn make_rendered_document(&mut self, frame: Frame, removed_pipelines: Vec<PipelineId>) -> RenderedDocument {
- let nodes_bouncing_back = self.clip_scroll_tree.collect_nodes_bouncing_back();
RenderedDocument::new(
PipelineInfo {
epochs: self.current.scene.pipeline_epochs.clone(),
removed_pipelines,
},
- nodes_bouncing_back,
frame
)
}
pub fn discard_frame_state_for_pipeline(&mut self, pipeline_id: PipelineId) {
self.clip_scroll_tree
.discard_frame_state_for_pipeline(pipeline_id);
}
/// Returns true if any nodes actually changed position or false otherwise.
pub fn scroll(
&mut self,
scroll_location: ScrollLocation,
cursor: WorldPoint,
- phase: ScrollEventPhase,
) -> bool {
- self.clip_scroll_tree.scroll(scroll_location, cursor, phase)
+ self.clip_scroll_tree.scroll(scroll_location, cursor)
}
/// Returns true if the node actually changed position or false otherwise.
pub fn scroll_node(
&mut self,
origin: LayerPoint,
id: ExternalScrollId,
clamp: ScrollClamping
) -> bool {
self.clip_scroll_tree.scroll_node(origin, id, clamp)
}
- pub fn tick_scrolling_bounce_animations(&mut self) {
- self.clip_scroll_tree.tick_scrolling_bounce_animations();
- }
-
pub fn get_scroll_node_state(&self) -> Vec<ScrollNodeState> {
self.clip_scroll_tree.get_scroll_node_state()
}
pub fn new_async_scene_ready(&mut self, mut built_scene: BuiltScene) {
self.current.scene = built_scene.scene;
self.frame_builder = Some(built_scene.frame_builder);
@@ -616,20 +610,20 @@ impl RenderBackend {
FrameMsg::EnableFrameOutput(pipeline_id, enable) => {
if enable {
doc.output_pipelines.insert(pipeline_id);
} else {
doc.output_pipelines.remove(&pipeline_id);
}
DocumentOps::nop()
}
- FrameMsg::Scroll(delta, cursor, move_phase) => {
+ FrameMsg::Scroll(delta, cursor) => {
profile_scope!("Scroll");
- let should_render = doc.scroll(delta, cursor, move_phase)
+ let should_render = doc.scroll(delta, cursor)
&& doc.render_on_scroll == Some(true);
DocumentOps {
scroll: true,
render: should_render,
composite: should_render,
..DocumentOps::nop()
}
@@ -658,30 +652,16 @@ impl RenderBackend {
DocumentOps {
scroll: true,
render: should_render,
composite: should_render,
..DocumentOps::nop()
}
}
- FrameMsg::TickScrollingBounce => {
- profile_scope!("TickScrollingBounce");
-
- doc.tick_scrolling_bounce_animations();
-
- let should_render = doc.render_on_scroll == Some(true);
-
- DocumentOps {
- scroll: true,
- render: should_render,
- composite: should_render,
- ..DocumentOps::nop()
- }
- }
FrameMsg::GetScrollNodeState(tx) => {
profile_scope!("GetScrollNodeState");
tx.send(doc.get_scroll_node_state()).unwrap();
DocumentOps::nop()
}
FrameMsg::UpdateDynamicProperties(property_bindings) => {
doc.dynamic_properties.set_properties(property_bindings);
DocumentOps::render()
@@ -703,26 +683,29 @@ impl RenderBackend {
while let Ok(msg) = self.scene_rx.try_recv() {
match msg {
SceneBuilderResult::Transaction {
document_id,
mut built_scene,
resource_updates,
frame_ops,
render,
+ result_tx,
} => {
if let Some(doc) = self.documents.get_mut(&document_id) {
if let Some(mut built_scene) = built_scene.take() {
doc.new_async_scene_ready(built_scene);
doc.render_on_hittest = true;
}
+ result_tx.send(SceneSwapResult::Complete).unwrap();
} else {
// The document was removed while we were building it, skip it.
// TODO: we might want to just ensure that removed documents are
// always forwarded to the scene builder thread to avoid this case.
+ result_tx.send(SceneSwapResult::Aborted).unwrap();
continue;
}
let transaction_msg = TransactionMsg {
scene_ops: Vec::new(),
frame_ops,
resource_updates,
generate_frame: render,
@@ -759,16 +742,19 @@ impl RenderBackend {
fn process_api_msg(
&mut self,
msg: ApiMsg,
profile_counters: &mut BackendProfileCounters,
frame_counter: &mut u32,
) -> bool {
match msg {
ApiMsg::WakeUp => {}
+ ApiMsg::WakeSceneBuilder => {
+ self.scene_tx.send(SceneBuilderRequest::WakeUp).unwrap();
+ }
ApiMsg::UpdateResources(updates) => {
self.resource_cache
.update_resources(updates, &mut profile_counters.resources);
}
ApiMsg::GetGlyphDimensions(instance_key, glyph_keys, tx) => {
let mut glyph_dimensions = Vec::with_capacity(glyph_keys.len());
if let Some(font) = self.resource_cache.get_font_instance(instance_key) {
for glyph_key in &glyph_keys {
@@ -924,17 +910,17 @@ impl RenderBackend {
document_id,
scene_msg,
*frame_counter,
&mut profile_counters.ipc,
)
);
}
- if transaction_msg.use_scene_builder_thread && !transaction_msg.is_empty() {
+ if transaction_msg.use_scene_builder_thread {
let doc = self.documents.get_mut(&document_id).unwrap();
doc.forward_transaction_to_scene_builder(
transaction_msg,
&op,
document_id,
&self.resource_cache,
&self.scene_tx,
);
@@ -1176,17 +1162,17 @@ impl RenderBackend {
}
if config.bits.contains(CaptureBits::FRAME) {
let rendered_document = doc.render(
&mut self.resource_cache,
&mut self.gpu_cache,
&mut profile_counters.resources,
);
//TODO: write down full `RenderedDocument`?
- // it has `pipeline_epoch_map` and `layers_bouncing_back`,
+ // it has `pipeline_epoch_map`,
// which may capture necessary details for some cases.
let file_name = format!("frame-{}-{}", (id.0).0, id.1);
config.serialize(&rendered_document.frame, file_name);
}
}
debug!("\tresource cache");
let (resources, deferred) = self.resource_cache.save_capture(&config.root);
--- a/gfx/webrender/src/render_task.rs
+++ b/gfx/webrender/src/render_task.rs
@@ -503,29 +503,27 @@ impl RenderTask {
uv_rect_handle: GpuCacheHandle::new(),
}),
clear_mode,
saved_index: None,
};
let blur_task_v_id = render_tasks.add(blur_task_v);
- let blur_task_h = RenderTask {
+ RenderTask {
children: vec![blur_task_v_id],
location: RenderTaskLocation::Dynamic(None, adjusted_blur_target_size),
kind: RenderTaskKind::HorizontalBlur(BlurTask {
blur_std_deviation: adjusted_blur_std_deviation,
target_kind,
uv_rect_handle: GpuCacheHandle::new(),
}),
clear_mode,
saved_index: None,
- };
-
- blur_task_h
+ }
}
pub fn new_scaling(
target_kind: RenderTargetKind,
src_task_id: RenderTaskId,
target_size: DeviceIntSize,
) -> Self {
RenderTask {
@@ -927,17 +925,17 @@ impl RenderTaskCache {
// or create one.
let cache_entry = self.entries
.entry(key)
.or_insert(RenderTaskCacheEntry {
handle: TextureCacheHandle::new(),
});
// Check if this texture cache handle is valid.
- if texture_cache.request(&mut cache_entry.handle, gpu_cache) {
+ if texture_cache.request(&cache_entry.handle, gpu_cache) {
// Invoke user closure to get render task chain
// to draw this into the texture cache.
let (render_task_id, is_opaque) = try!(f(render_tasks));
let render_task = &mut render_tasks[render_task_id];
// Select the right texture page to allocate from.
let image_format = match render_task.target_kind() {
RenderTargetKind::Color => ImageFormat::BGRA8,
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -1017,18 +1017,18 @@ impl CacheTexture {
}
}
}
fn update(&mut self, device: &mut Device, updates: &GpuCacheUpdateList) {
match self.bus {
CacheBus::PixelBuffer { ref mut rows, ref mut cpu_blocks, .. } => {
for update in &updates.updates {
- match update {
- &GpuCacheUpdate::Copy {
+ match *update {
+ GpuCacheUpdate::Copy {
block_index,
block_count,
address,
} => {
let row = address.v as usize;
// Ensure that the CPU-side shadow copy of the GPU cache data has enough
// rows to apply this patch.
@@ -1061,18 +1061,18 @@ impl CacheTexture {
} => {
//TODO: re-use this heap allocation
// Unused positions will be left as 0xFFFF, which translates to
// (1.0, 1.0) in the vertex output position and gets culled out
let mut position_data = vec![[!0u16; 2]; updates.blocks.len()];
let size = self.texture.get_dimensions().to_usize();
for update in &updates.updates {
- match update {
- &GpuCacheUpdate::Copy {
+ match *update {
+ GpuCacheUpdate::Copy {
block_index,
block_count,
address,
} => {
// Convert the absolute texel position into normalized
let y = ((2*address.v as usize + 1) << 15) / size.height;
for i in 0 .. block_count {
let x = ((2*address.u as usize + 2*i + 1) << 15) / size.width;
@@ -1602,22 +1602,25 @@ impl Renderer {
.build();
Arc::new(worker.unwrap())
});
let enable_render_on_scroll = options.enable_render_on_scroll;
let blob_image_renderer = options.blob_image_renderer.take();
let thread_listener_for_render_backend = thread_listener.clone();
let thread_listener_for_scene_builder = thread_listener.clone();
- let renderer_id_for_render_backend = options.renderer_id.clone();
+ let scene_builder_hooks = options.scene_builder_hooks;
let rb_thread_name = format!("WRRenderBackend#{}", options.renderer_id.unwrap_or(0));
let scene_thread_name = format!("WRSceneBuilder#{}", options.renderer_id.unwrap_or(0));
let glyph_rasterizer = GlyphRasterizer::new(workers)?;
- let (scene_builder, scene_tx, scene_rx) = SceneBuilder::new(config, api_tx.clone());
+ let (scene_builder, scene_tx, scene_rx) = SceneBuilder::new(
+ config,
+ api_tx.clone(),
+ scene_builder_hooks);
thread::Builder::new().name(scene_thread_name.clone()).spawn(move || {
register_thread_with_profiler(scene_thread_name.clone());
if let Some(ref thread_listener) = *thread_listener_for_scene_builder {
thread_listener.thread_started(&scene_thread_name);
}
let mut scene_builder = scene_builder;
scene_builder.run();
@@ -1625,17 +1628,16 @@ impl Renderer {
if let Some(ref thread_listener) = *thread_listener_for_scene_builder {
thread_listener.thread_stopped(&scene_thread_name);
}
})?;
thread::Builder::new().name(rb_thread_name.clone()).spawn(move || {
register_thread_with_profiler(rb_thread_name.clone());
if let Some(ref thread_listener) = *thread_listener_for_render_backend {
- thread_listener.new_render_backend_thread(renderer_id_for_render_backend);
thread_listener.thread_started(&rb_thread_name);
}
let texture_cache = TextureCache::new(max_device_size);
let resource_cache = ResourceCache::new(
texture_cache,
glyph_rasterizer,
blob_image_renderer,
@@ -2314,22 +2316,16 @@ impl Renderer {
if self.renderer_errors.is_empty() {
Ok(stats)
} else {
Err(mem::replace(&mut self.renderer_errors, Vec::new()))
}
}
- pub fn layers_are_bouncing_back(&self) -> bool {
- self.active_documents
- .iter()
- .any(|&(_, ref render_doc)| !render_doc.layers_bouncing_back.is_empty())
- }
-
fn update_gpu_cache(&mut self) {
let _gm = self.gpu_profile.start_marker("gpu cache update");
// For an artificial stress test of GPU cache resizing,
// always pass an extra update list with at least one block in it.
let gpu_cache_height = self.gpu_cache_texture.get_height();
if gpu_cache_height != 0 && GPU_CACHE_RESIZE_TEST {
self.pending_gpu_cache_updates.push(GpuCacheUpdateList {
@@ -2844,17 +2840,17 @@ impl Renderer {
for batch in alpha_batch_container
.opaque_batches
.iter()
.rev()
{
self.submit_batch(
&batch.key,
&batch.instances,
- &projection,
+ projection,
render_tasks,
render_target,
target_size,
stats,
alpha_batch_container.target_rect,
);
}
@@ -3029,17 +3025,17 @@ impl Renderer {
}
}
prev_blend_mode = batch.key.blend_mode;
}
self.submit_batch(
&batch.key,
&batch.instances,
- &projection,
+ projection,
render_tasks,
render_target,
target_size,
stats,
alpha_batch_container.target_rect,
);
}
}
@@ -3991,17 +3987,38 @@ pub trait ExternalImageHandler {
pub trait OutputImageHandler {
fn lock(&mut self, pipeline_id: PipelineId) -> Option<(u32, DeviceIntSize)>;
fn unlock(&mut self, pipeline_id: PipelineId);
}
pub trait ThreadListener {
fn thread_started(&self, thread_name: &str);
fn thread_stopped(&self, thread_name: &str);
- fn new_render_backend_thread(&self, renderer_id: Option<u64>);
+}
+
+/// Allows callers to hook in at certain points of the async scene build. These
+/// functions are all called from the scene builder thread.
+pub trait SceneBuilderHooks {
+ /// This is called exactly once, when the scene builder thread is started
+ /// and before it processes anything.
+ fn register(&self);
+ /// This is called before each scene swap occurs.
+ fn pre_scene_swap(&self);
+ /// This is called after each scene swap occurs. The PipelineInfo contains
+ /// the updated epochs and pipelines removed in the new scene compared to
+ /// the old scene.
+ fn post_scene_swap(&self, info: PipelineInfo);
+ /// This is a generic callback which provides an opportunity to run code
+ /// on the scene builder thread. This is called as part of the main message
+ /// loop of the scene builder thread, but outside of any specific message
+ /// handler.
+ fn poke(&self);
+ /// This is called exactly once, when the scene builder thread is about to
+ /// terminate.
+ fn deregister(&self);
}
pub struct RendererOptions {
pub device_pixel_ratio: f32,
pub resource_override_path: Option<PathBuf>,
pub enable_aa: bool,
pub enable_dithering: bool,
pub max_recorded_profiles: usize,
@@ -4018,16 +4035,17 @@ pub struct RendererOptions {
pub blob_image_renderer: Option<Box<BlobImageRenderer>>,
pub recorder: Option<Box<ApiRecordingReceiver>>,
pub thread_listener: Option<Box<ThreadListener + Send + Sync>>,
pub enable_render_on_scroll: bool,
pub cached_programs: Option<Rc<ProgramCache>>,
pub debug_flags: DebugFlags,
pub renderer_id: Option<u64>,
pub disable_dual_source_blending: bool,
+ pub scene_builder_hooks: Option<Box<SceneBuilderHooks + Send>>,
}
impl Default for RendererOptions {
fn default() -> Self {
RendererOptions {
device_pixel_ratio: 1.0,
resource_override_path: None,
enable_aa: true,
@@ -4049,16 +4067,17 @@ impl Default for RendererOptions {
workers: None,
blob_image_renderer: None,
recorder: None,
thread_listener: None,
enable_render_on_scroll: true,
renderer_id: None,
cached_programs: None,
disable_dual_source_blending: false,
+ scene_builder_hooks: None,
}
}
}
#[cfg(not(feature = "debugger"))]
pub struct DebugServer;
#[cfg(not(feature = "debugger"))]
--- a/gfx/webrender/src/resource_cache.rs
+++ b/gfx/webrender/src/resource_cache.rs
@@ -592,17 +592,17 @@ impl ResourceCache {
texture_cache_handle: TextureCacheHandle::new(),
}
)),
true,
),
};
let needs_upload = self.texture_cache
- .request(&mut entry.as_mut().unwrap().texture_cache_handle, gpu_cache);
+ .request(&entry.as_ref().unwrap().texture_cache_handle, gpu_cache);
if !needs_upload && !needs_update {
return;
}
// We can start a worker thread rasterizing right now, if:
// - The image is a blob.
// - The blob hasn't already been requested this frame.
@@ -853,17 +853,17 @@ impl ResourceCache {
})
.collect()
}
pub fn begin_frame(&mut self, frame_id: FrameId) {
debug_assert_eq!(self.state, State::Idle);
self.state = State::AddResources;
self.texture_cache.begin_frame(frame_id);
- self.cached_glyphs.begin_frame(&mut self.texture_cache, &self.cached_render_tasks);
+ self.cached_glyphs.begin_frame(&self.texture_cache, &self.cached_render_tasks);
self.cached_render_tasks.begin_frame(&mut self.texture_cache);
self.current_frame_id = frame_id;
}
pub fn block_until_all_resources_added(
&mut self,
gpu_cache: &mut GpuCache,
render_tasks: &mut RenderTaskTree,
--- a/gfx/webrender/src/scene_builder.rs
+++ b/gfx/webrender/src/scene_builder.rs
@@ -1,46 +1,58 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-use api::{DocumentId, PipelineId, ApiMsg, FrameMsg, ResourceUpdates};
+use api::{DocumentId, Epoch, PipelineId, ApiMsg, FrameMsg, ResourceUpdates};
use api::channel::MsgSender;
use display_list_flattener::build_scene;
use frame_builder::{FrameBuilderConfig, FrameBuilder};
use clip_scroll_tree::ClipScrollTree;
-use internal_types::FastHashSet;
+use internal_types::{FastHashMap, FastHashSet};
use resource_cache::{FontInstanceMap, TiledImageMap};
use render_backend::DocumentView;
+use renderer::{PipelineInfo, SceneBuilderHooks};
use scene::Scene;
use std::sync::mpsc::{channel, Receiver, Sender};
// Message from render backend to scene builder.
pub enum SceneBuilderRequest {
Transaction {
document_id: DocumentId,
scene: Option<SceneRequest>,
resource_updates: ResourceUpdates,
frame_ops: Vec<FrameMsg>,
render: bool,
+ current_epochs: FastHashMap<PipelineId, Epoch>,
},
+ WakeUp,
Stop
}
// Message from scene builder to render backend.
pub enum SceneBuilderResult {
Transaction {
document_id: DocumentId,
built_scene: Option<BuiltScene>,
resource_updates: ResourceUpdates,
frame_ops: Vec<FrameMsg>,
render: bool,
+ result_tx: Sender<SceneSwapResult>,
},
}
+// Message from render backend to scene builder to indicate the
+// scene swap was completed. We need a separate channel for this
+// so that they don't get mixed with SceneBuilderRequest messages.
+pub enum SceneSwapResult {
+ Complete,
+ Aborted,
+}
+
/// Contains the render backend data needed to build a scene.
pub struct SceneRequest {
pub scene: Scene,
pub view: DocumentView,
pub font_instances: FontInstanceMap,
pub tiled_image_map: TiledImageMap,
pub output_pipelines: FastHashSet<PipelineId>,
pub removed_pipelines: Vec<PipelineId>,
@@ -53,75 +65,114 @@ pub struct BuiltScene {
pub removed_pipelines: Vec<PipelineId>,
}
pub struct SceneBuilder {
rx: Receiver<SceneBuilderRequest>,
tx: Sender<SceneBuilderResult>,
api_tx: MsgSender<ApiMsg>,
config: FrameBuilderConfig,
+ hooks: Option<Box<SceneBuilderHooks + Send>>,
}
impl SceneBuilder {
pub fn new(
config: FrameBuilderConfig,
- api_tx: MsgSender<ApiMsg>
+ api_tx: MsgSender<ApiMsg>,
+ hooks: Option<Box<SceneBuilderHooks + Send>>,
) -> (Self, Sender<SceneBuilderRequest>, Receiver<SceneBuilderResult>) {
let (in_tx, in_rx) = channel();
let (out_tx, out_rx) = channel();
(
SceneBuilder {
rx: in_rx,
tx: out_tx,
api_tx,
config,
+ hooks,
},
in_tx,
out_rx,
)
}
pub fn run(&mut self) {
+ if let Some(ref hooks) = self.hooks {
+ hooks.register();
+ }
+
loop {
match self.rx.recv() {
Ok(msg) => {
if !self.process_message(msg) {
- return;
+ break;
}
}
Err(_) => {
- return;
+ break;
}
}
+
+ if let Some(ref hooks) = self.hooks {
+ hooks.poke();
+ }
+ }
+
+ if let Some(ref hooks) = self.hooks {
+ hooks.deregister();
}
}
fn process_message(&mut self, msg: SceneBuilderRequest) -> bool {
match msg {
+ SceneBuilderRequest::WakeUp => {}
SceneBuilderRequest::Transaction {
document_id,
scene,
resource_updates,
frame_ops,
render,
+ current_epochs,
} => {
let built_scene = scene.map(|request|{
build_scene(&self.config, request)
});
+ let pipeline_info = if let Some(ref built) = built_scene {
+ PipelineInfo {
+ epochs: built.scene.pipeline_epochs.clone(),
+ removed_pipelines: built.removed_pipelines.clone(),
+ }
+ } else {
+ PipelineInfo {
+ epochs: current_epochs,
+ removed_pipelines: vec![],
+ }
+ };
// TODO: pre-rasterization.
+ if let Some(ref hooks) = self.hooks {
+ hooks.pre_scene_swap();
+ }
+ let (result_tx, result_rx) = channel();
self.tx.send(SceneBuilderResult::Transaction {
document_id,
built_scene,
resource_updates,
frame_ops,
render,
+ result_tx,
}).unwrap();
let _ = self.api_tx.send(ApiMsg::WakeUp);
+
+ // Block until the swap is done, then invoke the hook
+ let _ = result_rx.recv();
+ if let Some(ref hooks) = self.hooks {
+ hooks.post_scene_swap(pipeline_info);
+ }
}
SceneBuilderRequest::Stop => { return false; }
}
true
}
}
--- a/gfx/webrender/src/shade.rs
+++ b/gfx/webrender/src/shade.rs
@@ -619,44 +619,43 @@ impl Shaders {
// All yuv_image configuration.
let mut yuv_features = Vec::new();
let yuv_shader_num = IMAGE_BUFFER_KINDS.len() * YUV_FORMATS.len() * YUV_COLOR_SPACES.len();
let mut brush_yuv_image = Vec::new();
// PrimitiveShader is not clonable. Use push() to initialize the vec.
for _ in 0 .. yuv_shader_num {
brush_yuv_image.push(None);
}
- for buffer_kind in 0 .. IMAGE_BUFFER_KINDS.len() {
- if IMAGE_BUFFER_KINDS[buffer_kind].has_platform_support(&gl_type) {
- for format_kind in 0 .. YUV_FORMATS.len() {
- for color_space_kind in 0 .. YUV_COLOR_SPACES.len() {
- let feature_string = IMAGE_BUFFER_KINDS[buffer_kind].get_feature_string();
+ for image_buffer_kind in &IMAGE_BUFFER_KINDS {
+ if image_buffer_kind.has_platform_support(&gl_type) {
+ for format_kind in &YUV_FORMATS {
+ for color_space_kind in &YUV_COLOR_SPACES {
+ let feature_string = image_buffer_kind.get_feature_string();
if feature_string != "" {
yuv_features.push(feature_string);
}
- let feature_string = YUV_FORMATS[format_kind].get_feature_string();
+ let feature_string = format_kind.get_feature_string();
if feature_string != "" {
yuv_features.push(feature_string);
}
- let feature_string =
- YUV_COLOR_SPACES[color_space_kind].get_feature_string();
+ let feature_string = color_space_kind.get_feature_string();
if feature_string != "" {
yuv_features.push(feature_string);
}
let shader = BrushShader::new(
"brush_yuv_image",
device,
&yuv_features,
options.precache_shaders,
)?;
let index = Self::get_yuv_shader_index(
- IMAGE_BUFFER_KINDS[buffer_kind],
- YUV_FORMATS[format_kind],
- YUV_COLOR_SPACES[color_space_kind],
+ *image_buffer_kind,
+ *format_kind,
+ *color_space_kind,
);
brush_yuv_image[index] = Some(shader);
yuv_features.clear();
}
}
}
}
deleted file mode 100644
--- a/gfx/webrender/src/spring.rs
+++ /dev/null
@@ -1,110 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-use api::LayerPoint;
-
-/// Some arbitrarily small positive number used as threshold value.
-pub const EPSILON: f32 = 0.1;
-
-/// The default stiffness factor.
-pub const STIFFNESS: f32 = 0.2;
-
-/// The default damping factor.
-pub const DAMPING: f32 = 1.0;
-
-#[derive(Copy, Clone, Debug)]
-pub struct Spring {
- /// The current position of spring.
- cur: LayerPoint,
- /// The position of spring at previous tick.
- prev: LayerPoint,
- /// The destination of spring.
- dest: LayerPoint,
- /// How hard it springs back.
- stiffness: f32,
- /// Friction. 1.0 means no bounce.
- damping: f32,
-}
-
-impl Spring {
- /// Create a new spring at location.
- pub fn at(pos: LayerPoint, stiffness: f32, damping: f32) -> Spring {
- Spring {
- cur: pos,
- prev: pos,
- dest: pos,
- stiffness,
- damping,
- }
- }
-
- /// Set coords on a spring, mutating spring
- pub fn coords(&mut self, cur: LayerPoint, prev: LayerPoint, dest: LayerPoint) {
- self.cur = cur;
- self.prev = prev;
- self.dest = dest
- }
-
- pub fn current(&self) -> LayerPoint {
- self.cur
- }
-
- /// Run one tick of the spring animation. Return true if the animation is complete.
- pub fn animate(&mut self) -> bool {
- if !is_resting(self.cur.x, self.prev.x, self.dest.x) ||
- !is_resting(self.cur.y, self.prev.y, self.dest.y)
- {
- let next = LayerPoint::new(
- next(
- self.cur.x,
- self.prev.x,
- self.dest.x,
- self.stiffness,
- self.damping,
- ),
- next(
- self.cur.y,
- self.prev.y,
- self.dest.y,
- self.stiffness,
- self.damping,
- ),
- );
- let (cur, dest) = (self.cur, self.dest);
- self.coords(next, cur, dest);
- false
- } else {
- let dest = self.dest;
- self.coords(dest, dest, dest);
- true
- }
- }
-}
-
-/// Given numbers, calculate the next position for a spring.
-fn next(cur: f32, prev: f32, dest: f32, stiffness: f32, damping: f32) -> f32 {
- // Calculate spring force
- let fspring = -stiffness * (cur - dest);
-
- // Calculate velocity
- let vel = cur - prev;
-
- // Calculate damping force.
- let fdamping = -damping * vel;
-
- // Calc acceleration by adjusting spring force to damping force
- let acc = fspring + fdamping;
-
- // Calculate new velocity after adding acceleration. Scale to framerate.
- let nextv = vel + acc;
-
- // Calculate next position by integrating velocity. Scale to framerate.
- let next = cur + nextv;
- next
-}
-
-/// Given numbers, calculate if a spring is at rest.
-fn is_resting(cur: f32, prev: f32, dest: f32) -> bool {
- (cur - prev).abs() < EPSILON && (cur - dest).abs() < EPSILON
-}
--- a/gfx/webrender/src/tiling.rs
+++ b/gfx/webrender/src/tiling.rs
@@ -585,17 +585,17 @@ impl RenderTarget for AlphaRenderTarget
);
}
RenderTaskKind::CacheMask(ref task_info) => {
let task_address = render_tasks.get_task_address(task_id);
self.clip_batcher.add(
task_address,
&task_info.clips,
task_info.coordinate_system_id,
- &ctx.resource_cache,
+ ctx.resource_cache,
gpu_cache,
clip_store,
);
}
RenderTaskKind::ClipRegion(ref task) => {
let task_address = render_tasks.get_task_address(task_id);
self.clip_batcher.add_clip_region(
task_address,
--- a/gfx/webrender/src/util.rs
+++ b/gfx/webrender/src/util.rs
@@ -278,17 +278,17 @@ pub fn recycle_vec<T>(mut old_vec: Vec<T
// Avoid reusing the buffer if it is a lot larger than it needs to be. This prevents
// a frame with exceptionally large allocations to cause subsequent frames to retain
// more memory than they need.
return Vec::with_capacity(old_vec.len());
}
old_vec.clear();
- return old_vec;
+ old_vec
}
#[cfg(test)]
pub mod test {
use super::*;
use euclid::{Point2D, Angle, Transform3D};
use std::f32::consts::PI;
@@ -407,20 +407,20 @@ impl<Src, Dst> FastTransform<Src, Dst> {
let new_transform = self.to_transform().pre_mul(&other.to_transform());
FastTransform::with_transform(new_transform)
}
}
}
#[inline(always)]
pub fn pre_translate(&self, other_offset: &TypedVector2D<f32, Src>) -> Self {
- match self {
- &FastTransform::Offset(ref offset) =>
- return FastTransform::Offset(*offset + *other_offset),
- &FastTransform::Transform { transform, .. } =>
+ match *self {
+ FastTransform::Offset(ref offset) =>
+ FastTransform::Offset(*offset + *other_offset),
+ FastTransform::Transform { transform, .. } =>
FastTransform::with_transform(transform.pre_translate(other_offset.to_3d()))
}
}
#[inline(always)]
pub fn preserves_2d_axis_alignment(&self) -> bool {
match *self {
FastTransform::Offset(..) => true,
@@ -474,17 +474,17 @@ impl<Src, Dst> FastTransform<Src, Dst> {
}
}
pub fn unapply(&self, rect: &TypedRect<f32, Dst>) -> Option<TypedRect<f32, Src>> {
match *self {
FastTransform::Offset(offset) =>
Some(TypedRect::from_untyped(&rect.to_untyped().translate(&-offset.to_untyped()))),
FastTransform::Transform { inverse: Some(ref inverse), is_2d: true, .. } =>
- Some(inverse.transform_rect(&rect)),
+ Some(inverse.transform_rect(rect)),
FastTransform::Transform { ref transform, is_2d: false, .. } =>
Some(transform.inverse_rect_footprint(rect)),
FastTransform::Transform { inverse: None, .. } => None,
}
}
#[inline(always)]
pub fn offset(&self, new_offset: TypedVector2D<f32, Src>) -> Self {
--- a/gfx/webrender_api/src/api.rs
+++ b/gfx/webrender_api/src/api.rs
@@ -283,19 +283,18 @@ impl Transaction {
/// Scrolls the scrolling layer under the `cursor`
///
/// WebRender looks for the layer closest to the user
/// which has `ScrollPolicy::Scrollable` set.
pub fn scroll(
&mut self,
scroll_location: ScrollLocation,
cursor: WorldPoint,
- phase: ScrollEventPhase,
) {
- self.frame_ops.push(FrameMsg::Scroll(scroll_location, cursor, phase));
+ self.frame_ops.push(FrameMsg::Scroll(scroll_location, cursor));
}
pub fn scroll_node_with_id(
&mut self,
origin: LayoutPoint,
id: ExternalScrollId,
clamp: ScrollClamping,
) {
@@ -309,20 +308,16 @@ impl Transaction {
pub fn set_pinch_zoom(&mut self, pinch_zoom: ZoomFactor) {
self.scene_ops.push(SceneMsg::SetPinchZoom(pinch_zoom));
}
pub fn set_pan(&mut self, pan: DeviceIntPoint) {
self.frame_ops.push(FrameMsg::SetPan(pan));
}
- pub fn tick_scrolling_bounce_animations(&mut self) {
- self.frame_ops.push(FrameMsg::TickScrollingBounce);
- }
-
/// Generate a new frame.
pub fn generate_frame(&mut self) {
self.generate_frame = true;
}
/// Supply a list of animated property bindings that should be used to resolve
/// bindings in the current display list.
pub fn update_dynamic_properties(&mut self, properties: DynamicProperties) {
@@ -482,19 +477,18 @@ pub enum SceneMsg {
// Frame messages affect frame generation (applied after building the scene).
#[derive(Clone, Deserialize, Serialize)]
pub enum FrameMsg {
UpdateEpoch(PipelineId, Epoch),
HitTest(Option<PipelineId>, WorldPoint, HitTestFlags, MsgSender<HitTestResult>),
SetPan(DeviceIntPoint),
EnableFrameOutput(PipelineId, bool),
- Scroll(ScrollLocation, WorldPoint, ScrollEventPhase),
+ Scroll(ScrollLocation, WorldPoint),
ScrollNodeWithId(LayoutPoint, ExternalScrollId, ScrollClamping),
- TickScrollingBounce,
GetScrollNodeState(MsgSender<Vec<ScrollNodeState>>),
UpdateDynamicProperties(DynamicProperties),
}
impl fmt::Debug for SceneMsg {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match *self {
SceneMsg::UpdateEpoch(..) => "SceneMsg::UpdateEpoch",
@@ -511,17 +505,16 @@ impl fmt::Debug for SceneMsg {
impl fmt::Debug for FrameMsg {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match *self {
FrameMsg::UpdateEpoch(..) => "FrameMsg::UpdateEpoch",
FrameMsg::HitTest(..) => "FrameMsg::HitTest",
FrameMsg::SetPan(..) => "FrameMsg::SetPan",
FrameMsg::Scroll(..) => "FrameMsg::Scroll",
FrameMsg::ScrollNodeWithId(..) => "FrameMsg::ScrollNodeWithId",
- FrameMsg::TickScrollingBounce => "FrameMsg::TickScrollingBounce",
FrameMsg::GetScrollNodeState(..) => "FrameMsg::GetScrollNodeState",
FrameMsg::EnableFrameOutput(..) => "FrameMsg::EnableFrameOutput",
FrameMsg::UpdateDynamicProperties(..) => "FrameMsg::UpdateDynamicProperties",
})
}
}
bitflags!{
@@ -617,16 +610,17 @@ pub enum ApiMsg {
ClearNamespace(IdNamespace),
/// Flush from the caches anything that isn't necessary, to free some memory.
MemoryPressure,
/// Change debugging options.
DebugCommand(DebugCommand),
/// Wakes the render backend's event loop up. Needed when an event is communicated
/// through another channel.
WakeUp,
+ WakeSceneBuilder,
ShutDown,
}
impl fmt::Debug for ApiMsg {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match *self {
ApiMsg::UpdateResources(..) => "ApiMsg::UpdateResources",
ApiMsg::GetGlyphDimensions(..) => "ApiMsg::GetGlyphDimensions",
@@ -636,16 +630,17 @@ impl fmt::Debug for ApiMsg {
ApiMsg::UpdateDocument(..) => "ApiMsg::UpdateDocument",
ApiMsg::DeleteDocument(..) => "ApiMsg::DeleteDocument",
ApiMsg::ExternalEvent(..) => "ApiMsg::ExternalEvent",
ApiMsg::ClearNamespace(..) => "ApiMsg::ClearNamespace",
ApiMsg::MemoryPressure => "ApiMsg::MemoryPressure",
ApiMsg::DebugCommand(..) => "ApiMsg::DebugCommand",
ApiMsg::ShutDown => "ApiMsg::ShutDown",
ApiMsg::WakeUp => "ApiMsg::WakeUp",
+ ApiMsg::WakeSceneBuilder => "ApiMsg::WakeSceneBuilder",
})
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
pub struct Epoch(pub u32);
@@ -944,16 +939,20 @@ impl RenderApi {
}
pub fn get_scroll_node_state(&self, document_id: DocumentId) -> Vec<ScrollNodeState> {
let (tx, rx) = channel::msg_channel().unwrap();
self.send_frame_msg(document_id, FrameMsg::GetScrollNodeState(tx));
rx.recv().unwrap()
}
+ pub fn wake_scene_builder(&self) {
+ self.send_message(ApiMsg::WakeSceneBuilder);
+ }
+
/// Save a capture of the current frame state for debugging.
pub fn save_capture(&self, path: PathBuf, bits: CaptureBits) {
let msg = ApiMsg::DebugCommand(DebugCommand::SaveCapture(path, bits));
self.send_message(msg);
}
/// Load a capture of the current frame state for debugging.
pub fn load_capture(&self, path: PathBuf) -> Vec<CapturedDocument> {
@@ -976,27 +975,16 @@ impl RenderApi {
impl Drop for RenderApi {
fn drop(&mut self) {
let msg = ApiMsg::ClearNamespace(self.namespace_id);
let _ = self.api_sender.send(msg);
}
}
-#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
-pub enum ScrollEventPhase {
- /// The user started scrolling.
- Start,
- /// The user performed a scroll. The Boolean flag indicates whether the user's fingers are
- /// down, if a touchpad is in use. (If false, the event is a touchpad fling.)
- Move(bool),
- /// The user ended scrolling.
- End,
-}
-
#[derive(Clone, Deserialize, Serialize)]
pub struct ScrollNodeState {
pub id: ExternalScrollId,
pub scroll_offset: LayoutVector2D,
}
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub enum ScrollLocation {
--- a/gfx/webrender_api/src/display_item.rs
+++ b/gfx/webrender_api/src/display_item.rs
@@ -446,16 +446,17 @@ pub struct PushStackingContextDisplayIte
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub struct StackingContext {
pub scroll_policy: ScrollPolicy,
pub transform: Option<PropertyBinding<LayoutTransform>>,
pub transform_style: TransformStyle,
pub perspective: Option<LayoutTransform>,
pub mix_blend_mode: MixBlendMode,
pub reference_frame_id: Option<ClipId>,
+ pub clip_node_id: Option<ClipId>,
} // IMPLICIT: filters: Vec<FilterOp>
#[repr(u32)]
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub enum ScrollPolicy {
Scrollable = 0,
Fixed = 1,
}
--- a/gfx/webrender_api/src/display_list.rs
+++ b/gfx/webrender_api/src/display_list.rs
@@ -1264,16 +1264,17 @@ impl DisplayListBuilder {
});
self.push_item(item, info);
}
pub fn push_stacking_context(
&mut self,
info: &LayoutPrimitiveInfo,
+ clip_node_id: Option<ClipId>,
scroll_policy: ScrollPolicy,
transform: Option<PropertyBinding<LayoutTransform>>,
transform_style: TransformStyle,
perspective: Option<LayoutTransform>,
mix_blend_mode: MixBlendMode,
filters: Vec<FilterOp>,
) {
let reference_frame_id = if transform.is_some() || perspective.is_some() {
@@ -1285,16 +1286,17 @@ impl DisplayListBuilder {
let item = SpecificDisplayItem::PushStackingContext(PushStackingContextDisplayItem {
stacking_context: StackingContext {
scroll_policy,
transform,
transform_style,
perspective,
mix_blend_mode,
reference_frame_id,
+ clip_node_id,
},
});
self.push_item(item, info);
self.push_iter(&filters);
}
pub fn pop_stacking_context(&mut self) {
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-941bf5ac998061689a1bcd18d417f1f315e41ae6
+092ada1154b72fe71d2f227a5df0239586d2323a
--- a/gfx/wrench/src/yaml_frame_reader.rs
+++ b/gfx/wrench/src/yaml_frame_reader.rs
@@ -1509,16 +1509,19 @@ impl YamlFrameReader {
let perspective_origin = yaml["perspective-origin"]
.as_point()
.unwrap_or(default_transform_origin);
let transform = yaml["transform"]
.as_transform(&transform_origin)
.map(|transform| transform.into());
+ let clip_node_id =
+ yaml["clip-node"].as_i64().map(|id| self.to_clip_id(id as u64, dl.pipeline_id));
+
let perspective = match yaml["perspective"].as_f32() {
Some(value) if value != 0.0 => {
Some(make_perspective(perspective_origin, value as f32))
}
Some(_) => None,
_ => yaml["perspective"].as_matrix4d(),
};
@@ -1540,16 +1543,17 @@ impl YamlFrameReader {
}
let filters = yaml["filters"].as_vec_filter_op().unwrap_or(vec![]);
info.rect = bounds;
info.clip_rect = bounds;
dl.push_stacking_context(
&info,
+ clip_node_id,
scroll_policy,
transform.into(),
transform_style,
perspective,
mix_blend_mode,
filters,
);
--- a/gfx/wrench/src/yaml_frame_writer.rs
+++ b/gfx/wrench/src/yaml_frame_writer.rs
@@ -173,23 +173,33 @@ fn maybe_radius_yaml(radius: &BorderRadi
size_node(&mut table, "top-left", &radius.top_left);
size_node(&mut table, "top-right", &radius.top_right);
size_node(&mut table, "bottom-left", &radius.bottom_left);
size_node(&mut table, "bottom-right", &radius.bottom_right);
Some(Yaml::Hash(table))
}
}
-fn write_sc(parent: &mut Table, sc: &StackingContext, properties: &SceneProperties, filter_iter: AuxIter<FilterOp>) {
+fn write_stacking_context(
+ parent: &mut Table,
+ sc: &StackingContext,
+ properties: &SceneProperties,
+ filter_iter: AuxIter<FilterOp>,
+ clip_id_mapper: &ClipIdMapper,
+) {
enum_node(parent, "scroll-policy", sc.scroll_policy);
matrix4d_node(parent, "transform", &properties.resolve_layout_transform(&sc.transform));
enum_node(parent, "transform-style", sc.transform_style);
+ if let Some(clip_node_id) = sc.clip_node_id {
+ yaml_node(parent, "clip-node", Yaml::Integer(clip_id_mapper.map_id(&clip_node_id) as i64));
+ }
+
if let Some(perspective) = sc.perspective {
matrix4d_node(parent, "perspective", &perspective);
}
// mix_blend_mode
if sc.mix_blend_mode != MixBlendMode::Normal {
enum_node(parent, "mix-blend-mode", sc.mix_blend_mode)
}
@@ -994,17 +1004,23 @@ impl YamlFrameWriter {
}
Iframe(item) => {
str_node(&mut v, "type", "iframe");
u32_vec_node(&mut v, "id", &[item.pipeline_id.0, item.pipeline_id.1]);
}
PushStackingContext(item) => {
str_node(&mut v, "type", "stacking-context");
let filters = display_list.get(base.filters());
- write_sc(&mut v, &item.stacking_context, &scene.properties, filters);
+ write_stacking_context(
+ &mut v,
+ &item.stacking_context,
+ &scene.properties,
+ filters,
+ clip_id_mapper,
+ );
let mut sub_iter = base.sub_iter();
self.write_display_list(&mut v, display_list, scene, &mut sub_iter, clip_id_mapper);
continue_traversal = Some(sub_iter);
}
Clip(item) => {
str_node(&mut v, "type", "clip");
usize_node(&mut v, "id", clip_id_mapper.add_id(item.id));