--- a/gfx/webrender/examples/alpha_perf.rs
+++ b/gfx/webrender/examples/alpha_perf.rs
@@ -30,19 +30,17 @@ impl Example for App {
_document_id: DocumentId,
) {
let bounds = (0, 0).to(1920, 1080);
let info = LayoutPrimitiveInfo::new(bounds);
builder.push_stacking_context(
&info,
None,
- None,
TransformStyle::Flat,
- None,
MixBlendMode::Normal,
Vec::new(),
GlyphRasterSpace::Screen,
);
for _ in 0 .. self.rect_count {
builder.push_rect(&info, ColorF::new(1.0, 1.0, 1.0, 0.05));
}
--- a/gfx/webrender/examples/animation.rs
+++ b/gfx/webrender/examples/animation.rs
@@ -43,22 +43,29 @@ impl Example for App {
// Create a 200x200 stacking context with an animated transform property.
let bounds = (0, 0).to(200, 200);
let filters = vec![
FilterOp::Opacity(PropertyBinding::Binding(self.opacity_key, self.opacity), self.opacity),
];
let info = LayoutPrimitiveInfo::new(bounds);
+ let reference_frame_id = builder.push_reference_frame(
+ &info,
+ Some(PropertyBinding::Binding(self.property_key, LayoutTransform::identity())),
+ None,
+ );
+
+ builder.push_clip_id(reference_frame_id);
+
+ let info = LayoutPrimitiveInfo::new(bounds);
builder.push_stacking_context(
&info,
None,
- Some(PropertyBinding::Binding(self.property_key, LayoutTransform::identity())),
TransformStyle::Flat,
- None,
MixBlendMode::Normal,
filters,
GlyphRasterSpace::Screen,
);
let complex_clip = ComplexClipRegion {
rect: bounds,
radii: BorderRadius::uniform(50.0),
@@ -68,16 +75,19 @@ impl Example for App {
builder.push_clip_id(clip_id);
// Fill it with a white rect
builder.push_rect(&info, ColorF::new(0.0, 1.0, 0.0, 1.0));
builder.pop_clip_id();
builder.pop_stacking_context();
+
+ builder.pop_clip_id();
+ builder.pop_reference_frame();
}
fn on_event(&mut self, win_event: winit::WindowEvent, api: &RenderApi, document_id: DocumentId) -> bool {
match win_event {
winit::WindowEvent::KeyboardInput {
input: winit::KeyboardInput {
state: winit::ElementState::Pressed,
virtual_keycode: Some(key),
--- a/gfx/webrender/examples/basic.rs
+++ b/gfx/webrender/examples/basic.rs
@@ -188,19 +188,17 @@ impl Example for App {
_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,
- None,
TransformStyle::Flat,
- None,
MixBlendMode::Normal,
Vec::new(),
GlyphRasterSpace::Screen,
);
let image_mask_key = api.generate_image_key();
txn.add_image(
image_mask_key,
--- a/gfx/webrender/examples/blob.rs
+++ b/gfx/webrender/examples/blob.rs
@@ -246,19 +246,17 @@ impl Example for App {
None,
);
let bounds = api::LayoutRect::new(api::LayoutPoint::zero(), builder.content_size());
let info = api::LayoutPrimitiveInfo::new(bounds);
builder.push_stacking_context(
&info,
None,
- None,
api::TransformStyle::Flat,
- None,
api::MixBlendMode::Normal,
Vec::new(),
api::GlyphRasterSpace::Screen,
);
let info = api::LayoutPrimitiveInfo::new((30, 30).by(500, 500));
builder.push_image(
&info,
--- a/gfx/webrender/examples/document.rs
+++ b/gfx/webrender/examples/document.rs
@@ -64,17 +64,17 @@ impl App {
];
for (pipeline_id, layer, color, offset) in init_data {
let size = DeviceUintSize::new(250, 250);
let bounds = DeviceUintRect::new(offset, size);
let document_id = api.add_document(size, layer);
let mut txn = Transaction::new();
- txn.set_window_parameters(framebuffer_size, bounds, 1.0);
+ txn.set_window_parameters(framebuffer_size, bounds, device_pixel_ratio);
txn.set_root_pipeline(pipeline_id);
api.send_transaction(document_id, txn);
self.documents.push(Document {
id: document_id,
pipeline_id,
content_rect: bounds.to_f32() / TypedScale::new(device_pixel_ratio),
color,
@@ -109,19 +109,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,
- None,
TransformStyle::Flat,
- None,
MixBlendMode::Normal,
Vec::new(),
GlyphRasterSpace::Screen,
);
builder.push_rect(
&LayoutPrimitiveInfo::new(local_rect),
doc.color,
);
--- a/gfx/webrender/examples/frame_output.rs
+++ b/gfx/webrender/examples/frame_output.rs
@@ -97,19 +97,17 @@ impl App {
let mut builder = DisplayListBuilder::new(
document.pipeline_id,
document.content_rect.size,
);
builder.push_stacking_context(
&info,
None,
- None,
TransformStyle::Flat,
- None,
MixBlendMode::Normal,
Vec::new(),
GlyphRasterSpace::Screen,
);
builder.push_rect(&info, ColorF::new(1.0, 1.0, 0.0, 1.0));
builder.pop_stacking_context();
@@ -143,19 +141,17 @@ impl Example for App {
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,
- None,
TransformStyle::Flat,
- None,
MixBlendMode::Normal,
Vec::new(),
GlyphRasterSpace::Screen,
);
builder.push_image(
&info,
info.rect.size,
--- a/gfx/webrender/examples/iframe.rs
+++ b/gfx/webrender/examples/iframe.rs
@@ -35,19 +35,17 @@ impl Example for App {
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,
- None,
TransformStyle::Flat,
- None,
MixBlendMode::Normal,
Vec::new(),
GlyphRasterSpace::Screen,
);
// green rect visible == success
sub_builder.push_rect(&info, ColorF::new(0.0, 1.0, 0.0, 1.0));
sub_builder.pop_stacking_context();
@@ -57,30 +55,40 @@ impl Example for App {
Epoch(0),
None,
sub_bounds.size,
sub_builder.finalize(),
true,
);
api.send_transaction(document_id, txn);
+ let info = LayoutPrimitiveInfo::new(sub_bounds);
+ let reference_frame_id = builder.push_reference_frame(
+ &info,
+ Some(PropertyBinding::Binding(PropertyBindingKey::new(42), LayoutTransform::identity())),
+ None,
+ );
+ builder.push_clip_id(reference_frame_id);
+
+
// And this is for the root pipeline
builder.push_stacking_context(
&info,
None,
- Some(PropertyBinding::Binding(PropertyBindingKey::new(42), LayoutTransform::identity())),
TransformStyle::Flat,
- None,
MixBlendMode::Normal,
Vec::new(),
GlyphRasterSpace::Screen,
);
// red rect under the iframe: if this is visible, things have gone wrong
builder.push_rect(&info, ColorF::new(1.0, 0.0, 0.0, 1.0));
builder.push_iframe(&info, sub_pipeline_id, false);
builder.pop_stacking_context();
+
+ builder.pop_clip_id();
+ builder.pop_reference_frame();
}
}
fn main() {
let mut app = App {};
boilerplate::main_wrapper(&mut app, None);
}
--- a/gfx/webrender/examples/image_resize.rs
+++ b/gfx/webrender/examples/image_resize.rs
@@ -37,19 +37,17 @@ impl Example for App {
None,
);
let bounds = (0, 0).to(512, 512);
let info = LayoutPrimitiveInfo::new(bounds);
builder.push_stacking_context(
&info,
None,
- None,
TransformStyle::Flat,
- None,
MixBlendMode::Normal,
Vec::new(),
GlyphRasterSpace::Screen,
);
let image_size = LayoutSize::new(100.0, 100.0);
let info = LayoutPrimitiveInfo::with_clip_rect(
--- a/gfx/webrender/examples/multiwindow.rs
+++ b/gfx/webrender/examples/multiwindow.rs
@@ -175,19 +175,17 @@ impl Window {
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,
- None,
TransformStyle::Flat,
- None,
MixBlendMode::Normal,
Vec::new(),
GlyphRasterSpace::Screen,
);
let info = LayoutPrimitiveInfo::new(LayoutRect::new(
LayoutPoint::new(100.0, 100.0),
LayoutSize::new(100.0, 200.0)
--- a/gfx/webrender/examples/scrolling.rs
+++ b/gfx/webrender/examples/scrolling.rs
@@ -30,34 +30,30 @@ impl Example for App {
_document_id: DocumentId,
) {
let info = LayoutPrimitiveInfo::new(
LayoutRect::new(LayoutPoint::zero(), builder.content_size())
);
builder.push_stacking_context(
&info,
None,
- None,
TransformStyle::Flat,
- None,
MixBlendMode::Normal,
Vec::new(),
GlyphRasterSpace::Screen,
);
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,
- None,
TransformStyle::Flat,
- None,
MixBlendMode::Normal,
Vec::new(),
GlyphRasterSpace::Screen,
);
// set the scrolling clip
let clip_id = builder.define_scroll_frame(
None,
(0, 0).by(1000, 1000),
--- a/gfx/webrender/examples/texture_cache_stress.rs
+++ b/gfx/webrender/examples/texture_cache_stress.rs
@@ -89,19 +89,17 @@ impl Example for App {
_pipeline_id: PipelineId,
_document_id: DocumentId,
) {
let bounds = (0, 0).to(512, 512);
let info = LayoutPrimitiveInfo::new(bounds);
builder.push_stacking_context(
&info,
None,
- None,
TransformStyle::Flat,
- None,
MixBlendMode::Normal,
Vec::new(),
GlyphRasterSpace::Screen,
);
let x0 = 50.0;
let y0 = 50.0;
let image_size = LayoutSize::new(4.0, 4.0);
--- a/gfx/webrender/examples/yuv.rs
+++ b/gfx/webrender/examples/yuv.rs
@@ -84,19 +84,17 @@ impl Example for App {
_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,
- None,
TransformStyle::Flat,
- None,
MixBlendMode::Normal,
Vec::new(),
GlyphRasterSpace::Screen,
);
let yuv_chanel1 = api.generate_image_key();
let yuv_chanel2 = api.generate_image_key();
let yuv_chanel2_1 = api.generate_image_key();
--- a/gfx/webrender/res/clip_shared.glsl
+++ b/gfx/webrender/res/clip_shared.glsl
@@ -93,18 +93,8 @@ ClipVertexInfo write_clip_tile_vertex(Re
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_border_segment.glsl
+++ b/gfx/webrender/res/cs_border_segment.glsl
@@ -1,46 +1,75 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#include shared,prim_shared,ellipse,shared_border
+#include shared,ellipse
-flat varying vec4 vColor0;
-flat varying vec4 vColor1;
+// For edges, the colors are the same. For corners, these
+// are the colors of each edge making up the corner.
+flat varying vec4 vColor0[2];
+flat varying vec4 vColor1[2];
+
+// A point + tangent defining the line where the edge
+// transition occurs. Used for corners only.
flat varying vec4 vColorLine;
-flat varying int vFeatures;
-flat varying vec2 vClipCenter;
+
+// x = segment, y = styles, z = edge axes
+flat varying ivec3 vConfig;
+
+// xy = Local space position of the clip center.
+// zw = Scale the rect origin by this to get the outer
+// corner from the segment rectangle.
+flat varying vec4 vClipCenter_Sign;
+
+// An outer and inner elliptical radii for border
+// corner clipping.
flat varying vec4 vClipRadii;
-flat varying vec2 vClipSign;
+
+// Reference point for determine edge clip lines.
+flat varying vec4 vEdgeReference;
+// Stores widths/2 and widths/3 to save doing this in FS.
+flat varying vec4 vPartialWidths;
+
+// Local space position
varying vec2 vPos;
-#define CLIP_RADII 1
-#define MIX_COLOR 2
+#define SEGMENT_TOP_LEFT 0
+#define SEGMENT_TOP_RIGHT 1
+#define SEGMENT_BOTTOM_RIGHT 2
+#define SEGMENT_BOTTOM_LEFT 3
+#define SEGMENT_LEFT 4
+#define SEGMENT_TOP 5
+#define SEGMENT_RIGHT 6
+#define SEGMENT_BOTTOM 7
+
+// Border styles as defined in webrender_api/types.rs
+#define BORDER_STYLE_NONE 0
+#define BORDER_STYLE_SOLID 1
+#define BORDER_STYLE_DOUBLE 2
+#define BORDER_STYLE_DOTTED 3
+#define BORDER_STYLE_DASHED 4
+#define BORDER_STYLE_HIDDEN 5
+#define BORDER_STYLE_GROOVE 6
+#define BORDER_STYLE_RIDGE 7
+#define BORDER_STYLE_INSET 8
+#define BORDER_STYLE_OUTSET 9
#ifdef WR_VERTEX_SHADER
in vec2 aTaskOrigin;
in vec4 aRect;
in vec4 aColor0;
in vec4 aColor1;
in int aFlags;
in vec2 aWidths;
in vec2 aRadii;
-#define SEGMENT_TOP_LEFT 0
-#define SEGMENT_TOP_RIGHT 1
-#define SEGMENT_BOTTOM_RIGHT 2
-#define SEGMENT_BOTTOM_LEFT 3
-#define SEGMENT_LEFT 4
-#define SEGMENT_TOP 5
-#define SEGMENT_RIGHT 6
-#define SEGMENT_BOTTOM 7
-
vec2 get_outer_corner_scale(int segment) {
vec2 p;
switch (segment) {
case SEGMENT_TOP_LEFT:
p = vec2(0.0, 0.0);
break;
case SEGMENT_TOP_RIGHT:
@@ -56,72 +85,257 @@ vec2 get_outer_corner_scale(int segment)
// Should never get hit
p = vec2(0.0);
break;
}
return p;
}
-void main(void) {
- vec2 pos = aRect.xy + aRect.zw * aPosition.xy;
+vec4 mod_color(vec4 color, float f) {
+ return vec4(clamp(color.rgb * f, vec3(0.0), vec3(color.a)), color.a);
+}
+
+vec4[2] get_colors_for_side(vec4 color, int style) {
+ vec4 result[2];
+ const vec2 f = vec2(1.3, 0.7);
+ switch (style) {
+ case BORDER_STYLE_GROOVE:
+ result[0] = mod_color(color, f.x);
+ result[1] = mod_color(color, f.y);
+ break;
+ case BORDER_STYLE_RIDGE:
+ result[0] = mod_color(color, f.y);
+ result[1] = mod_color(color, f.x);
+ break;
+ default:
+ result[0] = color;
+ result[1] = color;
+ break;
+ }
+
+ return result;
+}
+
+void main(void) {
int segment = aFlags & 0xff;
- int style = (aFlags >> 8) & 0xff;
+ int style0 = (aFlags >> 8) & 0xff;
+ int style1 = (aFlags >> 16) & 0xff;
vec2 outer_scale = get_outer_corner_scale(segment);
- vec2 outer = aRect.xy + outer_scale * aRect.zw;
+ vec2 outer = outer_scale * aRect.zw;
vec2 clip_sign = 1.0 - 2.0 * outer_scale;
- vColor0 = aColor0;
- vColor1 = aColor1;
- vPos = pos;
-
- vFeatures = 0;
- vClipSign = clip_sign;
- vClipCenter = outer + clip_sign * aRadii;
- vClipRadii = vec4(aRadii, aRadii - aWidths);
- vColorLine = vec4(0.0);
-
+ // Set some flags used by the FS to determine the
+ // orientation of the two edges in this corner.
+ ivec2 edge_axis;
+ // Derive the positions for the edge clips, which must be handled
+ // differently between corners and edges.
+ vec2 edge_reference;
switch (segment) {
case SEGMENT_TOP_LEFT:
+ edge_axis = ivec2(0, 1);
+ edge_reference = outer;
+ break;
case SEGMENT_TOP_RIGHT:
+ edge_axis = ivec2(1, 0);
+ edge_reference = vec2(outer.x - aWidths.x, outer.y);
+ break;
case SEGMENT_BOTTOM_RIGHT:
+ edge_axis = ivec2(0, 1);
+ edge_reference = outer - aWidths;
+ break;
case SEGMENT_BOTTOM_LEFT:
- vFeatures |= (CLIP_RADII | MIX_COLOR);
- vColorLine = vec4(outer, aWidths.y * -clip_sign.y, aWidths.x * clip_sign.x);
+ edge_axis = ivec2(1, 0);
+ edge_reference = vec2(outer.x, outer.y - aWidths.y);
+ break;
+ case SEGMENT_TOP:
+ case SEGMENT_BOTTOM:
+ edge_axis = ivec2(1, 1);
+ edge_reference = vec2(0.0);
+ break;
+ case SEGMENT_LEFT:
+ case SEGMENT_RIGHT:
+ default:
+ edge_axis = ivec2(0, 0);
+ edge_reference = vec2(0.0);
break;
+ }
+
+ vConfig = ivec3(
+ segment,
+ style0 | (style1 << 16),
+ edge_axis.x | (edge_axis.y << 16)
+ );
+ vPartialWidths = vec4(aWidths / 3.0, aWidths / 2.0);
+ vPos = aRect.zw * aPosition.xy;
+
+ vColor0 = get_colors_for_side(aColor0, style0);
+ vColor1 = get_colors_for_side(aColor1, style1);
+ vClipCenter_Sign = vec4(outer + clip_sign * aRadii, clip_sign);
+ vClipRadii = vec4(aRadii, max(aRadii - aWidths, 0.0));
+ vColorLine = vec4(outer, aWidths.y * -clip_sign.y, aWidths.x * clip_sign.x);
+ vEdgeReference = vec4(edge_reference, edge_reference + aWidths);
+
+ gl_Position = uTransform * vec4(aTaskOrigin + aRect.xy + vPos, 0.0, 1.0);
+}
+#endif
+
+#ifdef WR_FRAGMENT_SHADER
+vec4 evaluate_color_for_style_in_corner(
+ vec2 clip_relative_pos,
+ int style,
+ vec4 color[2],
+ vec4 clip_radii,
+ float mix_factor,
+ int segment,
+ float aa_range
+) {
+ switch (style) {
+ case BORDER_STYLE_DOUBLE: {
+ // Get the distances from 0.33 of the radii, and
+ // also 0.67 of the radii. Use these to form a
+ // SDF subtraction which will clip out the inside
+ // third of the rounded edge.
+ float d_radii_a = distance_to_ellipse(
+ clip_relative_pos,
+ clip_radii.xy - vPartialWidths.xy,
+ aa_range
+ );
+ float d_radii_b = distance_to_ellipse(
+ clip_relative_pos,
+ clip_radii.xy - 2.0 * vPartialWidths.xy,
+ aa_range
+ );
+ float d = min(-d_radii_a, d_radii_b);
+ float alpha = distance_aa(aa_range, d);
+ return alpha * color[0];
+ }
+ case BORDER_STYLE_GROOVE:
+ case BORDER_STYLE_RIDGE: {
+ float d = distance_to_ellipse(
+ clip_relative_pos,
+ clip_radii.xy - vPartialWidths.zw,
+ aa_range
+ );
+ float alpha = distance_aa(aa_range, d);
+ float swizzled_factor;
+ switch (segment) {
+ case SEGMENT_TOP_LEFT: swizzled_factor = 0.0; break;
+ case SEGMENT_TOP_RIGHT: swizzled_factor = mix_factor; break;
+ case SEGMENT_BOTTOM_RIGHT: swizzled_factor = 1.0; break;
+ case SEGMENT_BOTTOM_LEFT: swizzled_factor = 1.0 - mix_factor; break;
+ default: swizzled_factor = 0.0; break;
+ };
+ vec4 c0 = mix(color[1], color[0], swizzled_factor);
+ vec4 c1 = mix(color[0], color[1], swizzled_factor);
+ return mix(c0, c1, alpha);
+ }
default:
break;
}
- gl_Position = uTransform * vec4(aTaskOrigin + pos, 0.0, 1.0);
+ return color[0];
}
-#endif
-#ifdef WR_FRAGMENT_SHADER
+vec4 evaluate_color_for_style_in_edge(
+ vec2 pos,
+ int style,
+ vec4 color[2],
+ float aa_range,
+ int edge_axis
+) {
+ switch (style) {
+ case BORDER_STYLE_DOUBLE: {
+ float d0 = -1.0;
+ float d1 = -1.0;
+ if (vPartialWidths[edge_axis] > 1.0) {
+ vec2 ref = vec2(
+ vEdgeReference[edge_axis] + vPartialWidths[edge_axis],
+ vEdgeReference[edge_axis+2] - vPartialWidths[edge_axis]
+ );
+ d0 = pos[edge_axis] - ref.x;
+ d1 = ref.y - pos[edge_axis];
+ }
+ float d = min(d0, d1);
+ float alpha = distance_aa(aa_range, d);
+ return alpha * color[0];
+ }
+ case BORDER_STYLE_GROOVE:
+ case BORDER_STYLE_RIDGE: {
+ float ref = vEdgeReference[edge_axis] + vPartialWidths[edge_axis+2];
+ float d = pos[edge_axis] - ref;
+ float alpha = distance_aa(aa_range, d);
+ return mix(color[0], color[1], alpha);
+ }
+ default:
+ break;
+ }
+
+ return color[0];
+}
+
void main(void) {
float aa_range = compute_aa_range(vPos);
float d = -1.0;
+ vec4 color0, color1;
- // Apply color mix
+ int segment = vConfig.x;
+ ivec2 style = ivec2(vConfig.y & 0xffff, vConfig.y >> 16);
+ ivec2 edge_axis = ivec2(vConfig.z & 0xffff, vConfig.z >> 16);
+
float mix_factor = 0.0;
- if ((vFeatures & MIX_COLOR) != 0) {
+ if (edge_axis.x != edge_axis.y) {
float d_line = distance_to_line(vColorLine.xy, vColorLine.zw, vPos);
mix_factor = distance_aa(aa_range, -d_line);
}
- // Apply clip radii
- if ((vFeatures & CLIP_RADII) != 0) {
- vec2 p = vPos - vClipCenter;
- if (vClipSign.x * p.x < 0.0 && vClipSign.y * p.y < 0.0) {
- float d_radii_a = distance_to_ellipse(p, vClipRadii.xy, aa_range);
- float d_radii_b = distance_to_ellipse(p, vClipRadii.zw, aa_range);
- float d_radii = max(d_radii_a, -d_radii_b);
- d = max(d, d_radii);
- }
+ // Check if inside corner clip-region
+ vec2 clip_relative_pos = vPos - vClipCenter_Sign.xy;
+ bool in_clip_region = all(lessThan(vClipCenter_Sign.zw * clip_relative_pos, vec2(0.0)));
+
+ if (in_clip_region) {
+ float d_radii_a = distance_to_ellipse(clip_relative_pos, vClipRadii.xy, aa_range);
+ float d_radii_b = distance_to_ellipse(clip_relative_pos, vClipRadii.zw, aa_range);
+ float d_radii = max(d_radii_a, -d_radii_b);
+ d = max(d, d_radii);
+
+ color0 = evaluate_color_for_style_in_corner(
+ clip_relative_pos,
+ style.x,
+ vColor0,
+ vClipRadii,
+ mix_factor,
+ segment,
+ aa_range
+ );
+ color1 = evaluate_color_for_style_in_corner(
+ clip_relative_pos,
+ style.y,
+ vColor1,
+ vClipRadii,
+ mix_factor,
+ segment,
+ aa_range
+ );
+ } else {
+ color0 = evaluate_color_for_style_in_edge(
+ vPos,
+ style.x,
+ vColor0,
+ aa_range,
+ edge_axis.x
+ );
+ color1 = evaluate_color_for_style_in_edge(
+ vPos,
+ style.y,
+ vColor1,
+ aa_range,
+ edge_axis.y
+ );
}
float alpha = distance_aa(aa_range, d);
- vec4 color = mix(vColor0, vColor1, mix_factor);
+ vec4 color = mix(color0, color1, mix_factor);
oFragColor = color * alpha;
}
#endif
--- a/gfx/webrender/res/prim_shared.glsl
+++ b/gfx/webrender/res/prim_shared.glsl
@@ -20,21 +20,16 @@ uniform sampler2DArray sCacheRGBA8;
// An A8 target for standalone tasks that is available to all passes.
uniform sampler2DArray sSharedCacheA8;
vec2 clamp_rect(vec2 pt, RectWithSize rect) {
return clamp(pt, rect.p0, rect.p0 + rect.size);
}
-float distance_to_line(vec2 p0, vec2 perp_dir, vec2 p) {
- vec2 dir_to_p0 = p0 - p;
- return dot(normalize(perp_dir), dir_to_p0);
-}
-
// TODO: convert back to RectWithEndPoint if driver issues are resolved, if ever.
flat varying vec4 vClipMaskUvBounds;
varying vec3 vClipMaskUv;
#ifdef WR_VERTEX_SHADER
#define VECS_PER_LOCAL_CLIP_RECT 1
--- a/gfx/webrender/res/shared.glsl
+++ b/gfx/webrender/res/shared.glsl
@@ -51,16 +51,71 @@
// Fragment shader outputs
#ifdef WR_FEATURE_DUAL_SOURCE_BLENDING
layout(location = 0, index = 0) out vec4 oFragColor;
layout(location = 0, index = 1) out vec4 oFragBlend;
#else
out vec4 oFragColor;
#endif
+
+ #define EPSILON 0.0001
+
+ 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);
+ }
+
+ /// 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);
+ }
#endif
//======================================================================================
// Shared shader uniforms
//======================================================================================
#ifdef WR_FEATURE_TEXTURE_2D
uniform sampler2D sColor0;
uniform sampler2D sColor1;
--- a/gfx/webrender/res/shared_border.glsl
+++ b/gfx/webrender/res/shared_border.glsl
@@ -1,26 +1,26 @@
/* 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
-
// Border styles as defined in webrender_api/types.rs
#define BORDER_STYLE_NONE 0
#define BORDER_STYLE_SOLID 1
#define BORDER_STYLE_DOUBLE 2
#define BORDER_STYLE_DOTTED 3
#define BORDER_STYLE_DASHED 4
#define BORDER_STYLE_HIDDEN 5
#define BORDER_STYLE_GROOVE 6
#define BORDER_STYLE_RIDGE 7
#define BORDER_STYLE_INSET 8
#define BORDER_STYLE_OUTSET 9
+#ifdef WR_VERTEX_SHADER
+
struct Border {
vec4 style;
vec4 widths;
vec4 colors[4];
vec4 radii[2];
};
struct BorderCorners {
--- a/gfx/webrender/res/transform.glsl
+++ b/gfx/webrender/res/transform.glsl
@@ -1,74 +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/. */
-#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(
--- a/gfx/webrender/src/border.rs
+++ b/gfx/webrender/src/border.rs
@@ -466,26 +466,26 @@ impl<'a> DisplayListFlattener<'a> {
let top = &border.top;
let bottom = &border.bottom;
let brush_border_supported = [left, top, right, bottom].iter().all(|edge| {
match edge.style {
BorderStyle::Solid |
BorderStyle::Hidden |
BorderStyle::None |
+ BorderStyle::Double |
BorderStyle::Inset |
+ BorderStyle::Groove |
+ BorderStyle::Ridge |
BorderStyle::Outset => {
true
}
- BorderStyle::Double |
BorderStyle::Dotted |
- BorderStyle::Dashed |
- BorderStyle::Groove |
- BorderStyle::Ridge => {
+ BorderStyle::Dashed => {
false
}
}
});
if brush_border_supported {
let prim = BrushPrimitive::new(
BrushKind::Border {
@@ -1011,31 +1011,38 @@ struct DotInfo {
impl DotInfo {
fn new(arc_pos: f32, diameter: f32) -> DotInfo {
DotInfo { arc_pos, diameter }
}
}
#[derive(Debug)]
+pub struct BorderSegmentInfo {
+ task_rect: DeviceRect,
+ segment: BorderSegment,
+ radius: DeviceSize,
+ widths: DeviceSize,
+}
+
+#[derive(Debug)]
pub struct BorderRenderTaskInfo {
- pub instances: Vec<BorderInstance>,
- pub segments: Vec<BrushSegment>,
+ pub border_segments: Vec<BorderSegmentInfo>,
pub size: DeviceIntSize,
}
impl BorderRenderTaskInfo {
pub fn new(
rect: &LayoutRect,
border: &NormalBorder,
widths: &BorderWidths,
scale: LayoutToDeviceScale,
+ brush_segments: &mut Vec<BrushSegment>,
) -> Self {
- let mut instances = Vec::new();
- let mut segments = Vec::new();
+ let mut border_segments = Vec::new();
let dp_width_top = (widths.top * scale.0).ceil();
let dp_width_bottom = (widths.bottom * scale.0).ceil();
let dp_width_left = (widths.left * scale.0).ceil();
let dp_width_right = (widths.right * scale.0).ceil();
let dp_corner_tl = (border.radius.top_left * scale).ceil();
let dp_corner_tr = (border.radius.top_right * scale).ceil();
@@ -1083,219 +1090,258 @@ impl BorderRenderTaskInfo {
let width_inner = 16.0;
let height_inner = 16.0;
let size = DeviceSize::new(
dp_size_tl.width.max(dp_size_bl.width) + width_inner + dp_size_tr.width.max(dp_size_br.width),
dp_size_tl.height.max(dp_size_tr.height) + height_inner + dp_size_bl.height.max(dp_size_br.height),
);
- // These modulate colors are not part of the specification. They
- // are derived from the Gecko source code and experimentation, and
- // used to modulate the colors in order to generate colors for
- // the inset/outset and groove/ridge border styles.
- let left_color = border.left.border_color(1.0, 2.0 / 3.0, 0.3, 0.7);
- let top_color = border.top.border_color(1.0, 2.0 / 3.0, 0.3, 0.7);
- let right_color = border.right.border_color(2.0 / 3.0, 1.0, 0.7, 0.3);
- let bottom_color = border.bottom.border_color(2.0 / 3.0, 1.0, 0.7, 0.3);
-
add_edge_segment(
LayoutRect::from_floats(
rect.origin.x,
rect.origin.y + local_size_tl.height,
rect.origin.x + widths.left,
rect.origin.y + rect.size.height - local_size_bl.height,
),
DeviceRect::from_floats(
0.0,
dp_size_tl.height,
dp_width_left,
size.height - dp_size_bl.height,
),
- border.left.style,
- left_color,
+ &border.left,
BorderSegment::Left,
EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::RIGHT,
- &mut instances,
+ &mut border_segments,
BrushFlags::SEGMENT_RELATIVE | BrushFlags::SEGMENT_REPEAT_Y,
- &mut segments,
+ brush_segments,
);
add_edge_segment(
LayoutRect::from_floats(
rect.origin.x + local_size_tl.width,
rect.origin.y,
rect.origin.x + rect.size.width - local_size_tr.width,
rect.origin.y + widths.top,
),
DeviceRect::from_floats(
dp_size_tl.width,
0.0,
size.width - dp_size_tr.width,
dp_width_top,
),
- border.top.style,
- top_color,
+ &border.top,
BorderSegment::Top,
EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::BOTTOM,
- &mut instances,
+ &mut border_segments,
BrushFlags::SEGMENT_RELATIVE | BrushFlags::SEGMENT_REPEAT_X,
- &mut segments,
+ brush_segments,
);
add_edge_segment(
LayoutRect::from_floats(
rect.origin.x + rect.size.width - widths.right,
rect.origin.y + local_size_tr.height,
rect.origin.x + rect.size.width,
rect.origin.y + rect.size.height - local_size_br.height,
),
DeviceRect::from_floats(
size.width - dp_width_right,
dp_size_tr.height,
size.width,
size.height - dp_size_br.height,
),
- border.right.style,
- right_color,
+ &border.right,
BorderSegment::Right,
EdgeAaSegmentMask::RIGHT | EdgeAaSegmentMask::LEFT,
- &mut instances,
+ &mut border_segments,
BrushFlags::SEGMENT_RELATIVE | BrushFlags::SEGMENT_REPEAT_Y,
- &mut segments,
+ brush_segments,
);
add_edge_segment(
LayoutRect::from_floats(
rect.origin.x + local_size_bl.width,
rect.origin.y + rect.size.height - widths.bottom,
rect.origin.x + rect.size.width - local_size_br.width,
rect.origin.y + rect.size.height,
),
DeviceRect::from_floats(
dp_size_bl.width,
size.height - dp_width_bottom,
size.width - dp_size_br.width,
size.height,
),
- border.bottom.style,
- bottom_color,
+ &border.bottom,
BorderSegment::Bottom,
EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::TOP,
- &mut instances,
+ &mut border_segments,
BrushFlags::SEGMENT_RELATIVE | BrushFlags::SEGMENT_REPEAT_X,
- &mut segments,
+ brush_segments,
);
add_corner_segment(
LayoutRect::from_floats(
rect.origin.x,
rect.origin.y,
rect.origin.x + local_size_tl.width,
rect.origin.y + local_size_tl.height,
),
DeviceRect::from_floats(
0.0,
0.0,
dp_size_tl.width,
dp_size_tl.height,
),
- border.left.style,
- left_color,
- border.top.style,
- top_color,
+ &border.left,
+ &border.top,
DeviceSize::new(dp_width_left, dp_width_top),
dp_corner_tl,
BorderSegment::TopLeft,
EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::LEFT,
- &mut instances,
- &mut segments,
+ &mut border_segments,
+ brush_segments,
);
add_corner_segment(
LayoutRect::from_floats(
rect.origin.x + rect.size.width - local_size_tr.width,
rect.origin.y,
rect.origin.x + rect.size.width,
rect.origin.y + local_size_tr.height,
),
DeviceRect::from_floats(
size.width - dp_size_tr.width,
0.0,
size.width,
dp_size_tr.height,
),
- border.top.style,
- top_color,
- border.right.style,
- right_color,
+ &border.top,
+ &border.right,
DeviceSize::new(dp_width_right, dp_width_top),
dp_corner_tr,
BorderSegment::TopRight,
EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::RIGHT,
- &mut instances,
- &mut segments,
+ &mut border_segments,
+ brush_segments,
);
add_corner_segment(
LayoutRect::from_floats(
rect.origin.x + rect.size.width - local_size_br.width,
rect.origin.y + rect.size.height - local_size_br.height,
rect.origin.x + rect.size.width,
rect.origin.y + rect.size.height,
),
DeviceRect::from_floats(
size.width - dp_size_br.width,
size.height - dp_size_br.height,
size.width,
size.height,
),
- border.right.style,
- right_color,
- border.bottom.style,
- bottom_color,
+ &border.right,
+ &border.bottom,
DeviceSize::new(dp_width_right, dp_width_bottom),
dp_corner_br,
BorderSegment::BottomRight,
EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::RIGHT,
- &mut instances,
- &mut segments,
+ &mut border_segments,
+ brush_segments,
);
add_corner_segment(
LayoutRect::from_floats(
rect.origin.x,
rect.origin.y + rect.size.height - local_size_bl.height,
rect.origin.x + local_size_bl.width,
rect.origin.y + rect.size.height,
),
DeviceRect::from_floats(
0.0,
size.height - dp_size_bl.height,
dp_size_bl.width,
size.height,
),
- border.bottom.style,
- bottom_color,
- border.left.style,
- left_color,
+ &border.bottom,
+ &border.left,
DeviceSize::new(dp_width_left, dp_width_bottom),
dp_corner_bl,
BorderSegment::BottomLeft,
EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::LEFT,
- &mut instances,
- &mut segments,
+ &mut border_segments,
+ brush_segments,
);
BorderRenderTaskInfo {
- segments,
- instances,
+ border_segments,
size: size.to_i32(),
}
}
+
+ pub fn build_instances(
+ &self,
+ border: &NormalBorder,
+ ) -> Vec<BorderInstance> {
+ let mut instances = Vec::new();
+
+ for info in &self.border_segments {
+ let (side0, side1, flip0, flip1) = match info.segment {
+ BorderSegment::Left => (&border.left, &border.left, false, false),
+ BorderSegment::Top => (&border.top, &border.top, false, false),
+ BorderSegment::Right => (&border.right, &border.right, true, true),
+ BorderSegment::Bottom => (&border.bottom, &border.bottom, true, true),
+ BorderSegment::TopLeft => (&border.left, &border.top, false, false),
+ BorderSegment::TopRight => (&border.top, &border.right, false, true),
+ BorderSegment::BottomRight => (&border.right, &border.bottom, true, true),
+ BorderSegment::BottomLeft => (&border.bottom, &border.left, true, false),
+ };
+
+ let style0 = if side0.style.is_hidden() {
+ side1.style
+ } else {
+ side0.style
+ };
+ let style1 = if side1.style.is_hidden() {
+ side0.style
+ } else {
+ side1.style
+ };
+
+ // These modulate colors are not part of the specification. They
+ // are derived from the Gecko source code and experimentation, and
+ // used to modulate the colors in order to generate colors for
+ // the inset/outset and groove/ridge border styles.
+ let color0 = if flip0 {
+ side0.border_color(2.0 / 3.0, 1.0, 0.7, 0.3)
+ } else {
+ side0.border_color(1.0, 2.0 / 3.0, 0.3, 0.7)
+ };
+
+ let color1 = if flip1 {
+ side1.border_color(2.0 / 3.0, 1.0, 0.7, 0.3)
+ } else {
+ side1.border_color(1.0, 2.0 / 3.0, 0.3, 0.7)
+ };
+
+ add_segment(
+ info.task_rect,
+ style0,
+ style1,
+ color0,
+ color1,
+ info.segment,
+ &mut instances,
+ info.widths,
+ info.radius,
+ );
+ }
+
+ instances
+ }
}
fn add_brush_segment(
image_rect: LayoutRect,
task_rect: DeviceRect,
brush_flags: BrushFlags,
edge_flags: EdgeAaSegmentMask,
brush_segments: &mut Vec<BrushSegment>,
@@ -1342,105 +1388,77 @@ fn add_segment(
};
instances.push(base_instance);
}
fn add_corner_segment(
image_rect: LayoutRect,
task_rect: DeviceRect,
- mut style0: BorderStyle,
- color0: ColorF,
- mut style1: BorderStyle,
- color1: ColorF,
+ side0: &BorderSide,
+ side1: &BorderSide,
widths: DeviceSize,
radius: DeviceSize,
segment: BorderSegment,
edge_flags: EdgeAaSegmentMask,
- instances: &mut Vec<BorderInstance>,
+ border_segments: &mut Vec<BorderSegmentInfo>,
brush_segments: &mut Vec<BrushSegment>,
) {
- // TODO(gw): This will need to be a bit more involved when
- // we support other border types here. For example,
- // groove / ridge borders will always need to
- // use two instances.
-
- if color0.a <= 0.0 && color1.a <= 0.0 {
+ if side0.color.a <= 0.0 && side1.color.a <= 0.0 {
return;
}
if widths.width <= 0.0 && widths.height <= 0.0 {
return;
}
- let style0_hidden = style0 == BorderStyle::Hidden || style0 == BorderStyle::None;
- let style1_hidden = style1 == BorderStyle::Hidden || style1 == BorderStyle::None;
-
- if style0_hidden && style1_hidden {
+ if side0.style.is_hidden() && side1.style.is_hidden() {
return;
}
- if style0_hidden {
- style0 = style1;
- }
- if style1_hidden {
- style1 = style0;
- }
-
- add_segment(
+ border_segments.push(BorderSegmentInfo {
task_rect,
- style0,
- style1,
- color0,
- color1,
segment,
- instances,
+ radius,
widths,
- radius,
- );
+ });
add_brush_segment(
image_rect,
task_rect,
BrushFlags::SEGMENT_RELATIVE,
edge_flags,
brush_segments,
);
}
fn add_edge_segment(
image_rect: LayoutRect,
task_rect: DeviceRect,
- style: BorderStyle,
- color: ColorF,
+ side: &BorderSide,
segment: BorderSegment,
edge_flags: EdgeAaSegmentMask,
- instances: &mut Vec<BorderInstance>,
+ border_segments: &mut Vec<BorderSegmentInfo>,
brush_flags: BrushFlags,
brush_segments: &mut Vec<BrushSegment>,
) {
- if color.a <= 0.0 {
+ if side.color.a <= 0.0 {
return;
}
- if style == BorderStyle::Hidden || style == BorderStyle::None {
+ if side.style.is_hidden() {
return;
}
- add_segment(
+ border_segments.push(BorderSegmentInfo {
task_rect,
- style,
- style,
- color,
- color,
segment,
- instances,
- DeviceSize::zero(),
- DeviceSize::zero(),
- );
+ radius: DeviceSize::zero(),
+ widths: task_rect.size,
+ });
add_brush_segment(
image_rect,
task_rect,
brush_flags,
edge_flags,
brush_segments,
);
--- a/gfx/webrender/src/display_list_flattener.rs
+++ b/gfx/webrender/src/display_list_flattener.rs
@@ -3,20 +3,20 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{AlphaType, BorderDetails, BorderDisplayItem, BuiltDisplayListIter, ClipAndScrollInfo};
use api::{ClipId, ColorF, ComplexClipRegion, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
use api::{DevicePixelScale, DeviceUintRect, DisplayItemRef, Epoch, ExtendMode, ExternalScrollId};
use api::{FilterOp, FontInstanceKey, GlyphInstance, GlyphOptions, GlyphRasterSpace, GradientStop};
use api::{IframeDisplayItem, ImageKey, ImageRendering, ItemRange, LayoutPoint};
-use api::{LayoutPrimitiveInfo, LayoutRect, LayoutVector2D, LayoutSize, LayoutTransform};
+use api::{LayoutPrimitiveInfo, LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D};
use api::{LineOrientation, LineStyle, LocalClip, NinePatchBorderSource, PipelineId};
-use api::{PropertyBinding, RepeatMode, ScrollFrameDisplayItem, ScrollSensitivity, Shadow};
-use api::{SpecificDisplayItem, StackingContext, StickyFrameDisplayItem, TexelRect};
+use api::{PropertyBinding, ReferenceFrame, RepeatMode, ScrollFrameDisplayItem, ScrollSensitivity};
+use api::{Shadow, SpecificDisplayItem, StackingContext, StickyFrameDisplayItem, TexelRect};
use api::{TransformStyle, YuvColorSpace, YuvData};
use app_units::Au;
use clip::{ClipRegion, ClipSource, ClipSources, ClipStore};
use clip_scroll_node::{ClipScrollNode, NodeType, StickyFrameInfo};
use clip_scroll_tree::{ClipChainIndex, ClipScrollNodeIndex, ClipScrollTree};
use euclid::{SideOffsets2D, vec2};
use frame_builder::{FrameBuilder, FrameBuilderConfig};
use glyph_rasterizer::FontInstance;
@@ -152,20 +152,16 @@ pub struct DisplayListFlattener<'a> {
/// Used to track the latest flattened epoch for each pipeline.
pipeline_epochs: Vec<(PipelineId, Epoch)>,
/// A set of pipelines that the caller has requested be made available as
/// output textures.
output_pipelines: &'a FastHashSet<PipelineId>,
- /// A list of replacements to make in order to properly handle fixed position
- /// content as well as stacking contexts that create reference frames.
- replacements: Vec<(ClipId, ClipId)>,
-
/// The data structure that converting between ClipId and the various index
/// types that the ClipScrollTree uses.
id_to_index_mapper: ClipIdToIndexMapper,
/// A stack of scroll nodes used during display list processing to properly
/// parent new scroll nodes.
reference_frame_stack: Vec<(ClipId, ClipScrollNodeIndex)>,
@@ -220,17 +216,16 @@ impl<'a> DisplayListFlattener<'a> {
.and_then(|color| if color.a > 0.0 { Some(color) } else { None });
let mut flattener = DisplayListFlattener {
scene,
clip_scroll_tree,
font_instances,
config: *frame_builder_config,
pipeline_epochs: Vec::new(),
- replacements: Vec::new(),
output_pipelines,
id_to_index_mapper: ClipIdToIndexMapper::default(),
hit_testing_runs: recycle_vec(old_builder.hit_testing_runs),
cached_gradients: recycle_vec(old_builder.cached_gradients),
scrollbar_prims: recycle_vec(old_builder.scrollbar_prims),
reference_frame_stack: Vec::new(),
picture_stack: Vec::new(),
shadow_stack: Vec::new(),
@@ -258,61 +253,36 @@ impl<'a> DisplayListFlattener<'a> {
FrameBuilder::with_display_list_flattener(
view.inner_rect,
background_color,
view.window_size,
flattener
)
}
- /// Since WebRender still handles fixed position and reference frame content internally
- /// we need to apply this table of id replacements only to the id that affects the
- /// position of a node. We can eventually remove this when clients start handling
- /// reference frames themselves. This method applies these replacements.
- fn apply_scroll_frame_id_replacement(&self, index: ClipId) -> ClipId {
- match self.replacements.last() {
- Some(&(to_replace, replacement)) if to_replace == index => replacement,
- _ => index,
- }
- }
-
fn get_complex_clips(
&self,
pipeline_id: PipelineId,
complex_clips: ItemRange<ComplexClipRegion>,
) -> Vec<ComplexClipRegion> {
if complex_clips.is_empty() {
return vec![];
}
-
- self.scene
- .pipelines
- .get(&pipeline_id)
- .expect("No display list?")
- .display_list
- .get(complex_clips)
- .collect()
+ self.scene.get_display_list_for_pipeline(pipeline_id).get(complex_clips).collect()
}
fn get_clip_chain_items(
&self,
pipeline_id: PipelineId,
items: ItemRange<ClipId>,
) -> Vec<ClipId> {
if items.is_empty() {
return vec![];
}
-
- self.scene
- .pipelines
- .get(&pipeline_id)
- .expect("No display list?")
- .display_list
- .get(items)
- .collect()
+ self.scene.get_display_list_for_pipeline(pipeline_id).get(items).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)
);
@@ -374,16 +344,20 @@ impl<'a> DisplayListFlattener<'a> {
) {
loop {
let subtraversal = {
let item = match traversal.next() {
Some(item) => item,
None => break,
};
+ if SpecificDisplayItem::PopReferenceFrame == *item.item() {
+ return;
+ }
+
if SpecificDisplayItem::PopStackingContext == *item.item() {
return;
}
self.flatten_item(
item,
pipeline_id,
reference_frame_relative_offset,
@@ -457,96 +431,81 @@ impl<'a> DisplayListFlattener<'a> {
info.external_id,
pipeline_id,
&frame_rect,
&content_rect.size,
info.scroll_sensitivity,
);
}
+ fn flatten_reference_frame(
+ &mut self,
+ traversal: &mut BuiltDisplayListIter<'a>,
+ pipeline_id: PipelineId,
+ item: &DisplayItemRef,
+ reference_frame: &ReferenceFrame,
+ scroll_node_id: ClipId,
+ reference_frame_relative_offset: LayoutVector2D,
+ ) {
+ self.push_reference_frame(
+ reference_frame.id,
+ Some(scroll_node_id),
+ pipeline_id,
+ reference_frame.transform,
+ reference_frame.perspective,
+ reference_frame_relative_offset + item.rect().origin.to_vector(),
+ );
+
+ self.flatten_items(traversal, pipeline_id, LayoutVector2D::zero());
+
+ self.pop_reference_frame();
+ }
+
fn flatten_stacking_context(
&mut self,
traversal: &mut BuiltDisplayListIter<'a>,
pipeline_id: PipelineId,
item: &DisplayItemRef,
stacking_context: &StackingContext,
- unreplaced_scroll_id: ClipId,
scroll_node_id: ClipId,
- mut reference_frame_relative_offset: LayoutVector2D,
+ reference_frame_relative_offset: LayoutVector2D,
is_backface_visible: bool,
) {
// Avoid doing unnecessary work for empty stacking contexts.
if traversal.current_stacking_context_empty() {
traversal.skip_current_stacking_context();
return;
}
let composition_operations = {
// TODO(optimization?): self.traversal.display_list()
- let display_list = &self
- .scene
- .pipelines
- .get(&pipeline_id)
- .expect("No display list?!")
- .display_list;
+ let display_list = self.scene.get_display_list_for_pipeline(pipeline_id);
CompositeOps::new(
stacking_context.filter_ops_for_compositing(display_list, item.filters()),
stacking_context.mix_blend_mode_for_compositing(),
)
};
- 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() ||
- stacking_context.perspective.is_some()
- );
-
- self.push_reference_frame(
- reference_frame_id,
- Some(scroll_node_id),
- pipeline_id,
- stacking_context.transform,
- stacking_context.perspective,
- reference_frame_relative_offset,
- );
- self.replacements.push((unreplaced_scroll_id, reference_frame_id));
- reference_frame_relative_offset = LayoutVector2D::zero();
- }
-
- // We apply the replacements one more time in case we need to set it to a replacement
- // that we just pushed above.
- 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,
- final_scroll_node,
+ scroll_node_id,
stacking_context.clip_node_id,
stacking_context.glyph_raster_space,
);
self.flatten_items(
traversal,
pipeline_id,
- reference_frame_relative_offset,
+ reference_frame_relative_offset + item.rect().origin.to_vector(),
);
- if stacking_context.reference_frame_id.is_some() {
- self.replacements.pop();
- self.pop_reference_frame();
- }
-
self.pop_stacking_context();
}
fn flatten_iframe(
&mut self,
item: &DisplayItemRef,
info: &IframeDisplayItem,
clip_and_scroll_ids: &ClipAndScrollInfo,
@@ -603,20 +562,17 @@ impl<'a> DisplayListFlattener<'a> {
}
fn flatten_item<'b>(
&'b mut self,
item: DisplayItemRef<'a, 'b>,
pipeline_id: PipelineId,
reference_frame_relative_offset: LayoutVector2D,
) -> Option<BuiltDisplayListIter<'a>> {
- let mut clip_and_scroll_ids = item.clip_and_scroll();
- let unreplaced_scroll_id = clip_and_scroll_ids.scroll_node_id;
- clip_and_scroll_ids.scroll_node_id =
- self.apply_scroll_frame_id_replacement(clip_and_scroll_ids.scroll_node_id);
+ let clip_and_scroll_ids = item.clip_and_scroll();
let clip_and_scroll = self.id_to_index_mapper.map_clip_and_scroll(&clip_and_scroll_ids);
let prim_info = item.get_layout_primitive_info(&reference_frame_relative_offset);
match *item.item() {
SpecificDisplayItem::Image(ref info) => {
self.add_image(
clip_and_scroll,
&prim_info,
@@ -728,23 +684,35 @@ impl<'a> DisplayListFlattener<'a> {
}
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,
prim_info.is_backface_visible,
);
return Some(subtraversal);
}
+ SpecificDisplayItem::PushReferenceFrame(ref info) => {
+ let mut subtraversal = item.sub_iter();
+ self.flatten_reference_frame(
+ &mut subtraversal,
+ pipeline_id,
+ &item,
+ &info.reference_frame,
+ clip_and_scroll_ids.scroll_node_id,
+ reference_frame_relative_offset,
+ );
+ return Some(subtraversal);
+
+ }
SpecificDisplayItem::Iframe(ref info) => {
self.flatten_iframe(
&item,
info,
&clip_and_scroll_ids,
&reference_frame_relative_offset
);
}
@@ -787,17 +755,17 @@ impl<'a> DisplayListFlattener<'a> {
&clip_and_scroll_ids.scroll_node_id,
&reference_frame_relative_offset
);
}
// Do nothing; these are dummy items for the display list parser
SpecificDisplayItem::SetGradientStops => {}
- SpecificDisplayItem::PopStackingContext => {
+ SpecificDisplayItem::PopStackingContext | SpecificDisplayItem::PopReferenceFrame => {
unreachable!("Should have returned in parent method.")
}
SpecificDisplayItem::PushShadow(shadow) => {
let mut prim_info = prim_info.clone();
prim_info.rect = LayoutRect::zero();
self
.push_shadow(shadow, clip_and_scroll, &prim_info);
}
--- a/gfx/webrender/src/glyph_rasterizer/pathfinder.rs
+++ b/gfx/webrender/src/glyph_rasterizer/pathfinder.rs
@@ -76,17 +76,16 @@ impl Deref for ThreadSafePathfinderFontC
/// PathfinderFontContext can contain a *mut IDWriteFactory.
/// However, since we know that it is wrapped in a Mutex, it is safe
/// to assume that this struct is thread-safe
unsafe impl Send for ThreadSafePathfinderFontContext {}
unsafe impl Sync for ThreadSafePathfinderFontContext { }
impl GlyphRasterizer {
-
pub(in super) fn add_font_to_pathfinder(&mut self, font_key: &FontKey, template: &FontTemplate) {
let font_contexts = Arc::clone(&self.font_contexts);
debug!("add_font_to_pathfinder({:?})", font_key);
font_contexts.lock_pathfinder_context().add_font(&font_key, &template);
}
pub fn get_cache_item_for_glyph(
&self,
@@ -175,66 +174,65 @@ impl GlyphRasterizer {
cached_glyph_info = Some(glyph_info.clone())
}
GlyphCacheEntry::Blank | GlyphCacheEntry::Pending => {}
}
}
Entry::Vacant(_) => {}
}
- let cached_glyph_info = match cached_glyph_info {
- Some(cached_glyph_info) => cached_glyph_info,
- None => {
- let mut pathfinder_font_context = self.font_contexts.lock_pathfinder_context();
+ if cached_glyph_info.is_none() {
+ let mut pathfinder_font_context = self.font_contexts.lock_pathfinder_context();
- let pathfinder_font_instance = pathfinder_font_renderer::FontInstance {
- font_key: font.font_key.clone(),
- size: font.size,
- };
+ let pathfinder_font_instance = pathfinder_font_renderer::FontInstance {
+ font_key: font.font_key.clone(),
+ size: font.size,
+ };
- // TODO: pathfinder will need to support 2D subpixel offset
- let pathfinder_subpixel_offset =
- pathfinder_font_renderer::SubpixelOffset(glyph_key.subpixel_offset.0 as u8);
- let pathfinder_glyph_key =
- pathfinder_font_renderer::GlyphKey::new(glyph_key.index,
- pathfinder_subpixel_offset);
- let glyph_dimensions =
- match pathfinder_font_context.glyph_dimensions(&pathfinder_font_instance,
- &pathfinder_glyph_key,
- false) {
- Ok(glyph_dimensions) => glyph_dimensions,
- Err(_) => continue,
- };
+ // TODO: pathfinder will need to support 2D subpixel offset
+ let pathfinder_subpixel_offset =
+ pathfinder_font_renderer::SubpixelOffset(glyph_key.subpixel_offset.0 as u8);
+ let pathfinder_glyph_key =
+ pathfinder_font_renderer::GlyphKey::new(glyph_key.index,
+ pathfinder_subpixel_offset);
- let cached_glyph_info = CachedGlyphInfo {
- render_task_cache_key: RenderTaskCacheKey {
- size: TypedSize2D::from_untyped(&glyph_dimensions.size.to_i32()),
- kind: RenderTaskCacheKeyKind::Glyph(self.next_gpu_glyph_cache_key),
- },
+ if let Ok(glyph_dimensions) =
+ pathfinder_font_context.glyph_dimensions(&pathfinder_font_instance,
+ &pathfinder_glyph_key,
+ false) {
+ let render_task_cache_key = RenderTaskCacheKey {
+ size: TypedSize2D::from_untyped(&glyph_dimensions.size.to_i32()),
+ kind: RenderTaskCacheKeyKind::Glyph(self.next_gpu_glyph_cache_key),
+ };
+ cached_glyph_info = Some(CachedGlyphInfo {
+ render_task_cache_key,
format: font.get_glyph_format(),
origin: DeviceIntPoint::new(glyph_dimensions.origin.x as i32,
-glyph_dimensions.origin.y as i32),
- };
+ });
self.next_gpu_glyph_cache_key.0 += 1;
- cached_glyph_info
}
- };
+ }
- let handle =
- match self.request_glyph_from_pathfinder_if_necessary(glyph_key,
- &font,
- cached_glyph_info.clone(),
- texture_cache,
- gpu_cache,
- render_task_cache,
- render_task_tree,
- render_passes) {
- Ok(_) => GlyphCacheEntry::Cached(cached_glyph_info),
- Err(_) => GlyphCacheEntry::Blank,
- };
+ let handle = match cached_glyph_info {
+ Some(glyph_info) => {
+ match self.request_glyph_from_pathfinder_if_necessary(glyph_key,
+ &font,
+ glyph_info.clone(),
+ texture_cache,
+ gpu_cache,
+ render_task_cache,
+ render_task_tree,
+ render_passes) {
+ Ok(_) => GlyphCacheEntry::Cached(glyph_info),
+ Err(_) => GlyphCacheEntry::Blank,
+ }
+ }
+ None => GlyphCacheEntry::Blank,
+ };
glyph_key_cache.insert(glyph_key.clone(), handle);
}
}
pub fn resolve_glyphs(
&mut self,
_: &mut GlyphCache,
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -1464,25 +1464,27 @@ impl PrimitiveStore {
} = *source {
// TODO(gw): When drawing in screen raster mode, we should also incorporate a
// scale factor from the world transform to get an appropriately
// sized border task.
let world_scale = LayoutToWorldScale::new(1.0);
let scale = world_scale * frame_context.device_pixel_scale;
let scale_au = Au::from_f32_px(scale.0);
let needs_update = scale_au != cache_key.scale;
+ let mut new_segments = Vec::new();
if needs_update {
cache_key.scale = scale_au;
*task_info = Some(BorderRenderTaskInfo::new(
&metadata.local_rect,
border,
widths,
scale,
+ &mut new_segments,
));
}
let task_info = task_info.as_ref().unwrap();
*handle = Some(frame_state.resource_cache.request_render_task(
RenderTaskCacheKey {
size: DeviceIntSize::zero(),
@@ -1490,30 +1492,30 @@ impl PrimitiveStore {
},
frame_state.gpu_cache,
frame_state.render_tasks,
None,
false, // todo
|render_tasks| {
let task = RenderTask::new_border(
task_info.size,
- task_info.instances.clone(),
+ task_info.build_instances(border),
);
let task_id = render_tasks.add(task);
pic_state.tasks.push(task_id);
task_id
}
));
if needs_update {
brush.segment_desc = Some(BrushSegmentDescriptor {
- segments: task_info.segments.clone(),
+ segments: new_segments,
clip_mask_kind: BrushClipMaskKind::Unknown,
});
}
}
}
}
fn prepare_prim_for_render_inner(
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -1185,36 +1185,38 @@ impl RenderBackend {
trait ToDebugString {
fn debug_string(&self) -> String;
}
#[cfg(feature = "debugger")]
impl ToDebugString for SpecificDisplayItem {
fn debug_string(&self) -> String {
match *self {
- SpecificDisplayItem::Image(..) => String::from("image"),
- SpecificDisplayItem::YuvImage(..) => String::from("yuv_image"),
- SpecificDisplayItem::Text(..) => String::from("text"),
- SpecificDisplayItem::Rectangle(..) => String::from("rectangle"),
+ SpecificDisplayItem::Border(..) => String::from("border"),
+ SpecificDisplayItem::BoxShadow(..) => String::from("box_shadow"),
SpecificDisplayItem::ClearRectangle => String::from("clear_rectangle"),
- SpecificDisplayItem::Line(..) => String::from("line"),
- SpecificDisplayItem::Gradient(..) => String::from("gradient"),
- SpecificDisplayItem::RadialGradient(..) => String::from("radial_gradient"),
- SpecificDisplayItem::BoxShadow(..) => String::from("box_shadow"),
- SpecificDisplayItem::Border(..) => String::from("border"),
- SpecificDisplayItem::PushStackingContext(..) => String::from("push_stacking_context"),
- SpecificDisplayItem::Iframe(..) => String::from("iframe"),
SpecificDisplayItem::Clip(..) => String::from("clip"),
SpecificDisplayItem::ClipChain(..) => String::from("clip_chain"),
+ SpecificDisplayItem::Gradient(..) => String::from("gradient"),
+ SpecificDisplayItem::Iframe(..) => String::from("iframe"),
+ SpecificDisplayItem::Image(..) => String::from("image"),
+ SpecificDisplayItem::Line(..) => String::from("line"),
+ SpecificDisplayItem::PopAllShadows => String::from("pop_all_shadows"),
+ SpecificDisplayItem::PopReferenceFrame => String::from("pop_reference_frame"),
+ SpecificDisplayItem::PopStackingContext => String::from("pop_stacking_context"),
+ SpecificDisplayItem::PushReferenceFrame(..) => String::from("push_reference_frame"),
+ SpecificDisplayItem::PushShadow(..) => String::from("push_shadow"),
+ SpecificDisplayItem::PushStackingContext(..) => String::from("push_stacking_context"),
+ SpecificDisplayItem::RadialGradient(..) => String::from("radial_gradient"),
+ SpecificDisplayItem::Rectangle(..) => String::from("rectangle"),
SpecificDisplayItem::ScrollFrame(..) => String::from("scroll_frame"),
- SpecificDisplayItem::StickyFrame(..) => String::from("sticky_frame"),
SpecificDisplayItem::SetGradientStops => String::from("set_gradient_stops"),
- SpecificDisplayItem::PopStackingContext => String::from("pop_stacking_context"),
- SpecificDisplayItem::PushShadow(..) => String::from("push_shadow"),
- SpecificDisplayItem::PopAllShadows => String::from("pop_all_shadows"),
+ SpecificDisplayItem::StickyFrame(..) => String::from("sticky_frame"),
+ SpecificDisplayItem::Text(..) => String::from("text"),
+ SpecificDisplayItem::YuvImage(..) => String::from("yuv_image"),
}
}
}
impl RenderBackend {
#[cfg(feature = "capture")]
// Note: the mutable `self` is only needed here for resolving blob images
fn save_capture(
--- a/gfx/webrender/src/render_task.rs
+++ b/gfx/webrender/src/render_task.rs
@@ -696,17 +696,23 @@ impl RenderTask {
RenderTaskKind::Readback(..) |
RenderTaskKind::Scaling(..) |
RenderTaskKind::Border(..) |
RenderTaskKind::Blit(..) => {
[0.0; 3]
}
};
- let (target_rect, target_index) = self.get_target_rect();
+ let (mut target_rect, target_index) = self.get_target_rect();
+ // The primitives inside a fixed-location render task
+ // are already placed to their corresponding positions,
+ // so the shader doesn't need to shift by the origin.
+ if let RenderTaskLocation::Fixed(_) = self.location {
+ target_rect.origin = DeviceIntPoint::origin();
+ };
RenderTaskData {
data: [
target_rect.origin.x as f32,
target_rect.origin.y as f32,
target_rect.size.width as f32,
target_rect.size.height as f32,
target_index.0 as f32,
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -2882,18 +2882,28 @@ impl Renderer {
self.device.set_blend(false);
//Note: depth equality is needed for split planes
self.device.set_depth_func(DepthFunction::LessEqual);
self.device.enable_depth();
self.device.enable_depth_write();
for alpha_batch_container in &target.alpha_batch_containers {
if let Some(target_rect) = alpha_batch_container.target_rect {
+ // Note: `framebuffer_target_rect` needs a Y-flip before going to GL
+ let rect = if render_target.is_none() {
+ let mut rect = target_rect
+ .intersection(&framebuffer_target_rect.to_i32())
+ .unwrap_or(DeviceIntRect::zero());
+ rect.origin.y = target_size.height as i32 - rect.origin.y - rect.size.height;
+ rect
+ } else {
+ target_rect
+ };
self.device.enable_scissor();
- self.device.set_scissor_rect(target_rect);
+ self.device.set_scissor_rect(rect);
}
// Draw opaque batches front-to-back for maximum
// z-buffer efficiency!
for batch in alpha_batch_container
.opaque_batches
.iter()
.rev()
@@ -2925,18 +2935,28 @@ impl Renderer {
let _gl = self.gpu_profile.start_marker("alpha batches");
let transparent_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_TRANSPARENT);
self.device.set_blend(true);
let mut prev_blend_mode = BlendMode::None;
for alpha_batch_container in &target.alpha_batch_containers {
if let Some(target_rect) = alpha_batch_container.target_rect {
+ // Note: `framebuffer_target_rect` needs a Y-flip before going to GL
+ let rect = if render_target.is_none() {
+ let mut rect = target_rect
+ .intersection(&framebuffer_target_rect.to_i32())
+ .unwrap_or(DeviceIntRect::zero());
+ rect.origin.y = target_size.height as i32 - rect.origin.y - rect.size.height;
+ rect
+ } else {
+ target_rect
+ };
self.device.enable_scissor();
- self.device.set_scissor_rect(target_rect);
+ self.device.set_scissor_rect(rect);
}
for batch in &alpha_batch_container.alpha_batches {
self.shaders
.get(&batch.key)
.bind(
&mut self.device, projection,
&mut self.renderer_errors,
--- a/gfx/webrender/src/resource_cache.rs
+++ b/gfx/webrender/src/resource_cache.rs
@@ -657,16 +657,17 @@ impl ResourceCache {
let offset = DevicePoint::new(
tile_offset.x as f32 * tile_size as f32,
tile_offset.y as f32 * tile_size as f32,
);
if let Some(dirty) = dirty_rect {
if intersect_for_tile(dirty, actual_size, tile_size, tile_offset).is_none() {
// don't bother requesting unchanged tiles
+ self.pending_image_requests.remove(&request);
return
}
}
(offset, actual_size)
}
None => (DevicePoint::zero(), template.descriptor.size),
};
--- a/gfx/webrender/src/scene.rs
+++ b/gfx/webrender/src/scene.rs
@@ -110,16 +110,23 @@ impl Scene {
pipeline_epochs: FastHashMap::default(),
}
}
pub fn set_root_pipeline_id(&mut self, pipeline_id: PipelineId) {
self.root_pipeline_id = Some(pipeline_id);
}
+ pub fn get_display_list_for_pipeline(&self, pipeline_id: PipelineId) -> &BuiltDisplayList {
+ &self.pipelines
+ .get(&pipeline_id)
+ .expect("Expected to find display list for pipeline")
+ .display_list
+ }
+
pub fn set_display_list(
&mut self,
pipeline_id: PipelineId,
epoch: Epoch,
display_list: BuiltDisplayList,
background_color: Option<ColorF>,
viewport_size: LayoutSize,
content_size: LayoutSize,
--- a/gfx/webrender_api/src/display_item.rs
+++ b/gfx/webrender_api/src/display_item.rs
@@ -104,16 +104,18 @@ pub enum SpecificDisplayItem {
Border(BorderDisplayItem),
BoxShadow(BoxShadowDisplayItem),
Gradient(GradientDisplayItem),
RadialGradient(RadialGradientDisplayItem),
ClipChain(ClipChainItem),
Iframe(IframeDisplayItem),
PushStackingContext(PushStackingContextDisplayItem),
PopStackingContext,
+ PushReferenceFrame(PushReferenceFrameDisplayListItem),
+ PopReferenceFrame,
SetGradientStops,
PushShadow(Shadow),
PopAllShadows,
}
/// This is a "complete" version of the DI specifics,
/// containing the auxiliary data within the corresponding
/// enumeration variants, to be used for debug serialization.
@@ -133,16 +135,18 @@ pub enum CompletelySpecificDisplayItem {
YuvImage(YuvImageDisplayItem),
Border(BorderDisplayItem),
BoxShadow(BoxShadowDisplayItem),
Gradient(GradientDisplayItem),
RadialGradient(RadialGradientDisplayItem),
Iframe(IframeDisplayItem),
PushStackingContext(PushStackingContextDisplayItem, Vec<FilterOp>),
PopStackingContext,
+ PushReferenceFrame(PushReferenceFrameDisplayListItem),
+ PopReferenceFrame,
SetGradientStops(Vec<GradientStop>),
PushShadow(Shadow),
PopAllShadows,
}
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub struct ClipDisplayItem {
pub id: ClipId,
@@ -381,16 +385,22 @@ pub enum BorderStyle {
Dashed = 4,
Hidden = 5,
Groove = 6,
Ridge = 7,
Inset = 8,
Outset = 9,
}
+impl BorderStyle {
+ pub fn is_hidden(&self) -> bool {
+ *self == BorderStyle::Hidden || *self == BorderStyle::None
+ }
+}
+
#[repr(u32)]
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub enum BoxShadowClipMode {
Outset = 0,
Inset = 1,
}
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
@@ -458,27 +468,36 @@ pub struct ClipChainItem {
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub struct RadialGradientDisplayItem {
pub gradient: RadialGradient,
pub tile_size: LayoutSize,
pub tile_spacing: LayoutSize,
}
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct PushReferenceFrameDisplayListItem {
+ pub reference_frame: ReferenceFrame,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
+pub struct ReferenceFrame {
+ pub transform: Option<PropertyBinding<LayoutTransform>>,
+ pub perspective: Option<LayoutTransform>,
+ pub id: ClipId,
+}
+
+#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub struct PushStackingContextDisplayItem {
pub stacking_context: StackingContext,
}
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub struct StackingContext {
- 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>,
pub glyph_raster_space: GlyphRasterSpace,
} // IMPLICIT: filters: Vec<FilterOp>
#[repr(u32)]
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub enum TransformStyle {
--- a/gfx/webrender_api/src/display_list.rs
+++ b/gfx/webrender_api/src/display_list.rs
@@ -15,20 +15,21 @@ use std::{io, mem, ptr, slice};
use time::precise_time_ns;
use {AlphaType, BorderDetails, BorderDisplayItem, BorderRadius, BorderWidths, BoxShadowClipMode};
use {BoxShadowDisplayItem, ClipAndScrollInfo, ClipChainId, ClipChainItem, ClipDisplayItem, ClipId};
use {ColorF, ComplexClipRegion, DisplayItem, ExtendMode, ExternalScrollId, FilterOp};
use {FontInstanceKey, GlyphInstance, GlyphOptions, GlyphRasterSpace, Gradient};
use {GradientDisplayItem, GradientStop, IframeDisplayItem, ImageDisplayItem, ImageKey, ImageMask};
use {ImageRendering, LayoutPoint, LayoutPrimitiveInfo, LayoutRect, LayoutSize, LayoutTransform};
use {LayoutVector2D, LineDisplayItem, LineOrientation, LineStyle, MixBlendMode, PipelineId};
-use {PropertyBinding, PushStackingContextDisplayItem, RadialGradient, RadialGradientDisplayItem};
-use {RectangleDisplayItem, ScrollFrameDisplayItem, ScrollSensitivity, Shadow, SpecificDisplayItem};
-use {StackingContext, StickyFrameDisplayItem, StickyOffsetBounds, TextDisplayItem, TransformStyle};
-use {YuvColorSpace, YuvData, YuvImageDisplayItem};
+use {PropertyBinding, PushReferenceFrameDisplayListItem, PushStackingContextDisplayItem};
+use {RadialGradient, RadialGradientDisplayItem, RectangleDisplayItem, ReferenceFrame};
+use {ScrollFrameDisplayItem, ScrollSensitivity, Shadow, SpecificDisplayItem, StackingContext};
+use {StickyFrameDisplayItem, StickyOffsetBounds, TextDisplayItem, TransformStyle, YuvColorSpace};
+use {YuvData, YuvImageDisplayItem};
// We don't want to push a long text-run. If a text-run is too long, split it into several parts.
// This needs to be set to (renderer::MAX_VERTEX_TEXTURE_WIDTH - VECS_PER_PRIM_HEADER - VECS_PER_TEXT_RUN) * 2
pub const MAX_TEXT_RUN_LENGTH: usize = 2038;
// We start at 2, because the root reference is always 0 and the root scroll node is always 1.
const FIRST_CLIP_ID: usize = 2;
@@ -476,16 +477,18 @@ impl Serialize for BuiltDisplayList {
SpecificDisplayItem::Gradient(v) => Gradient(v),
SpecificDisplayItem::RadialGradient(v) => RadialGradient(v),
SpecificDisplayItem::Iframe(v) => Iframe(v),
SpecificDisplayItem::PushStackingContext(v) => PushStackingContext(
v,
item.iter.list.get(item.iter.cur_filters).collect()
),
SpecificDisplayItem::PopStackingContext => PopStackingContext,
+ SpecificDisplayItem::PushReferenceFrame(v) => PushReferenceFrame(v),
+ SpecificDisplayItem::PopReferenceFrame => PopReferenceFrame,
SpecificDisplayItem::SetGradientStops => SetGradientStops(
item.iter.list.get(item.iter.cur_stops).collect()
),
SpecificDisplayItem::PushShadow(v) => PushShadow(v),
SpecificDisplayItem::PopAllShadows => PopAllShadows,
},
clip_and_scroll: display_item.clip_and_scroll,
info: display_item.info,
@@ -550,23 +553,25 @@ impl<'de> Deserialize<'de> for BuiltDisp
Gradient(specific_item) => SpecificDisplayItem::Gradient(specific_item),
RadialGradient(specific_item) =>
SpecificDisplayItem::RadialGradient(specific_item),
Iframe(specific_item) => {
total_clip_ids += 1;
SpecificDisplayItem::Iframe(specific_item)
}
PushStackingContext(specific_item, filters) => {
- if specific_item.stacking_context.reference_frame_id.is_some() {
- total_clip_ids += 1;
- }
DisplayListBuilder::push_iter_impl(&mut temp, filters);
SpecificDisplayItem::PushStackingContext(specific_item)
},
PopStackingContext => SpecificDisplayItem::PopStackingContext,
+ PushReferenceFrame(specific_item) => {
+ total_clip_ids += 1;
+ SpecificDisplayItem::PushReferenceFrame(specific_item)
+ }
+ PopReferenceFrame => SpecificDisplayItem::PopReferenceFrame,
SetGradientStops(stops) => {
DisplayListBuilder::push_iter_impl(&mut temp, stops);
SpecificDisplayItem::SetGradientStops
},
PushShadow(specific_item) => SpecificDisplayItem::PushShadow(specific_item),
PopAllShadows => SpecificDisplayItem::PopAllShadows,
},
clip_and_scroll: complete.clip_and_scroll,
@@ -1272,49 +1277,58 @@ impl DisplayListBuilder {
gradient,
tile_size,
tile_spacing,
});
self.push_item(item, info);
}
+ pub fn push_reference_frame(
+ &mut self,
+ info: &LayoutPrimitiveInfo,
+ transform: Option<PropertyBinding<LayoutTransform>>,
+ perspective: Option<LayoutTransform>,
+ ) -> ClipId {
+ let id = self.generate_clip_id();
+ let item = SpecificDisplayItem::PushReferenceFrame(PushReferenceFrameDisplayListItem {
+ reference_frame: ReferenceFrame {
+ transform,
+ perspective,
+ id,
+ },
+ });
+ self.push_item(item, info);
+ id
+ }
+
+ pub fn pop_reference_frame(&mut self) {
+ self.push_new_empty_item(SpecificDisplayItem::PopReferenceFrame);
+ }
+
pub fn push_stacking_context(
&mut self,
info: &LayoutPrimitiveInfo,
clip_node_id: Option<ClipId>,
- transform: Option<PropertyBinding<LayoutTransform>>,
transform_style: TransformStyle,
- perspective: Option<LayoutTransform>,
mix_blend_mode: MixBlendMode,
filters: Vec<FilterOp>,
glyph_raster_space: GlyphRasterSpace,
- ) -> Option<ClipId> {
- let reference_frame_id = if transform.is_some() || perspective.is_some() {
- Some(self.generate_clip_id())
- } else {
- None
- };
-
+ ) {
let item = SpecificDisplayItem::PushStackingContext(PushStackingContextDisplayItem {
stacking_context: StackingContext {
- transform,
transform_style,
- perspective,
mix_blend_mode,
- reference_frame_id,
clip_node_id,
glyph_raster_space,
},
});
self.push_item(item, info);
self.push_iter(&filters);
-
- reference_frame_id
}
pub fn pop_stacking_context(&mut self) {
self.push_new_empty_item(SpecificDisplayItem::PopStackingContext);
}
pub fn push_stops(&mut self, stops: &[GradientStop]) {
if stops.is_empty() {
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-3829687ffbe8d55885d71a3d5e5e79216251548f
+8e697f8cb1f1aab2e5f6b9b903eb7191340b10c5
--- a/gfx/wrench/src/rawtest.rs
+++ b/gfx/wrench/src/rawtest.rs
@@ -149,16 +149,23 @@ impl<'a> RawtestHarness<'a> {
let blob_img = self.wrench.api.generate_image_key();
txn.add_image(
blob_img,
ImageDescriptor::new(1510, 111256, ImageFormat::BGRA8, false, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))),
Some(31),
);
+ let called = Arc::new(AtomicIsize::new(0));
+ let called_inner = Arc::clone(&called);
+
+ self.wrench.callbacks.lock().unwrap().request = Box::new(move |_| {
+ called_inner.fetch_add(1, Ordering::SeqCst);
+ });
+
let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
let info = LayoutPrimitiveInfo::new(rect(0., -9600.0, 1510.000031, 111256.));
let image_size = size(1510., 111256.);
let clip_id = builder.define_clip(rect(40., 41., 200., 201.), vec![], None);
@@ -173,23 +180,16 @@ impl<'a> RawtestHarness<'a> {
blob_img,
);
builder.pop_clip_id();
let mut epoch = Epoch(0);
self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
- let called = Arc::new(AtomicIsize::new(0));
- let called_inner = Arc::clone(&called);
-
- self.wrench.callbacks.lock().unwrap().request = Box::new(move |_| {
- called_inner.fetch_add(1, Ordering::SeqCst);
- });
-
let pixels = self.render_and_get_pixels(window_rect);
// make sure we didn't request too many blobs
assert_eq!(called.load(Ordering::SeqCst), 16);
// make sure things are in the right spot
assert!(
pixels[(148 +
@@ -220,16 +220,18 @@ impl<'a> RawtestHarness<'a> {
window_rect.size.width as usize) * 4 + 3] == 255
);
// Leaving a tiled blob image in the resource cache
// confuses the `test_capture`. TODO: remove this
txn = Transaction::new();
txn.delete_image(blob_img);
self.wrench.api.update_resources(txn.resource_updates);
+
+ *self.wrench.callbacks.lock().unwrap() = blob::BlobCallbacks::new();
}
fn test_offscreen_blob(&mut self) {
println!("\toffscreen blob update.");
assert_eq!(self.wrench.device_pixel_ratio, 1.);
let window_size = self.window.get_inner_size();
@@ -357,16 +359,23 @@ impl<'a> RawtestHarness<'a> {
txn.add_image(
blob_img,
ImageDescriptor::new(500, 500, ImageFormat::BGRA8, false, false),
ImageData::new_blob_image(blob::serialize_blob(ColorU::new(50, 50, 150, 255))),
None,
);
}
+ let called = Arc::new(AtomicIsize::new(0));
+ let called_inner = Arc::clone(&called);
+
+ self.wrench.callbacks.lock().unwrap().request = Box::new(move |_| {
+ assert_eq!(0, called_inner.fetch_add(1, Ordering::SeqCst));
+ });
+
// draw the blob the first time
let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
let info = LayoutPrimitiveInfo::new(rect(0.0, 60.0, 200.0, 200.0));
builder.push_image(
&info,
size(200.0, 200.0),
size(0.0, 0.0),
@@ -374,26 +383,19 @@ impl<'a> RawtestHarness<'a> {
AlphaType::PremultipliedAlpha,
blob_img,
);
let mut epoch = Epoch(0);
self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
- let called = Arc::new(AtomicIsize::new(0));
- let called_inner = Arc::clone(&called);
-
- self.wrench.callbacks.lock().unwrap().request = Box::new(move |_| {
- called_inner.fetch_add(1, Ordering::SeqCst);
- });
-
let pixels_first = self.render_and_get_pixels(window_rect);
- assert!(called.load(Ordering::SeqCst) == 1);
+ assert_eq!(1, called.load(Ordering::SeqCst));
// draw the blob image a second time at a different location
// make a new display list that refers to the first image
let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
let info = LayoutPrimitiveInfo::new(rect(1.0, 60.0, 200.0, 200.0));
builder.push_image(
&info,
@@ -406,23 +408,26 @@ impl<'a> RawtestHarness<'a> {
txn.resource_updates.clear();
self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
let pixels_second = self.render_and_get_pixels(window_rect);
// make sure we only requested once
- assert!(called.load(Ordering::SeqCst) == 1);
+ assert_eq!(1, called.load(Ordering::SeqCst));
// use png;
// png::save_flipped("out1.png", &pixels_first, window_rect.size);
// png::save_flipped("out2.png", &pixels_second, window_rect.size);
assert!(pixels_first != pixels_second);
+
+ // cleanup
+ *self.wrench.callbacks.lock().unwrap() = blob::BlobCallbacks::new();
}
fn test_blob_update_epoch_test(&mut self) {
println!("\tblob update epoch test...");
let (blob_img, blob_img2);
let window_size = self.window.get_inner_size();
let test_size = DeviceUintSize::new(400, 400);
@@ -534,16 +539,19 @@ impl<'a> RawtestHarness<'a> {
push_images(&mut builder);
self.submit_dl(&mut epoch, layout_size, builder, &txn.resource_updates);
let _pixels_third = self.render_and_get_pixels(window_rect);
// the first image should be requested 3 times
assert_eq!(img1_requested.load(Ordering::SeqCst), 3);
// the second image should've been requested twice
assert_eq!(img2_requested.load(Ordering::SeqCst), 2);
+
+ // cleanup
+ *self.wrench.callbacks.lock().unwrap() = blob::BlobCallbacks::new();
}
fn test_blob_update_test(&mut self) {
println!("\tblob update test...");
let window_size = self.window.get_inner_size();
let test_size = DeviceUintSize::new(400, 400);
--- a/gfx/wrench/src/yaml_frame_reader.rs
+++ b/gfx/wrench/src/yaml_frame_reader.rs
@@ -1249,56 +1249,57 @@ impl YamlFrameReader {
pub fn get_complex_clip_for_item(&mut self, yaml: &Yaml) -> Option<ComplexClipRegion> {
let complex_clip = &yaml["complex-clip"];
if complex_clip.is_badvalue() {
return None;
}
Some(self.to_complex_clip_region(complex_clip))
}
+ pub fn get_item_type_from_yaml(item: &Yaml) -> &str {
+ let shorthands = [
+ "rect",
+ "image",
+ "text",
+ "glyphs",
+ "box-shadow", // Note: box_shadow shorthand check has to come before border.
+ "border",
+ "gradient",
+ "radial-gradient",
+ ];
+
+ for shorthand in shorthands.iter() {
+ if !item[*shorthand].is_badvalue() {
+ return shorthand;
+ }
+ }
+ item["type"].as_str().unwrap_or("unknown")
+ }
+
pub fn add_display_list_items_from_yaml(
&mut self,
dl: &mut DisplayListBuilder,
wrench: &mut Wrench,
yaml: &Yaml,
) {
// A very large number (but safely far away from finite limits of f32)
let big_number = 1.0e30;
// A rect that should in practical terms serve as a no-op for clipping
let full_clip = LayoutRect::new(
LayoutPoint::new(-big_number / 2.0, -big_number / 2.0),
LayoutSize::new(big_number, big_number));
for item in yaml.as_vec().unwrap() {
- // an explicit type can be skipped with some shorthand
- let item_type = if !item["rect"].is_badvalue() {
- "rect"
- } else if !item["image"].is_badvalue() {
- "image"
- } else if !item["text"].is_badvalue() {
- "text"
- } else if !item["glyphs"].is_badvalue() {
- "glyphs"
- } else if !item["box-shadow"].is_badvalue() {
- // Note: box_shadow shorthand check has to come before border.
- "box-shadow"
- } else if !item["border"].is_badvalue() {
- "border"
- } else if !item["gradient"].is_badvalue() {
- "gradient"
- } else if !item["radial-gradient"].is_badvalue() {
- "radial-gradient"
- } else {
- item["type"].as_str().unwrap_or("unknown")
- };
+ let item_type = Self::get_item_type_from_yaml(item);
- // We never skip stacking contexts because they are structural elements
- // of the display list.
- if item_type != "stacking-context" && self.include_only.contains(&item_type.to_owned())
- {
+ // We never skip stacking contexts and reference frames because
+ // they are structural elements of the display list.
+ if item_type != "stacking-context" &&
+ item_type != "reference-frame" &&
+ self.include_only.contains(&item_type.to_owned()) {
continue;
}
let clip_scroll_info = self.to_clip_and_scroll_info(
&item["clip-and-scroll"],
dl.pipeline_id
);
if let Some(clip_scroll_info) = clip_scroll_info {
@@ -1338,16 +1339,17 @@ impl YamlFrameReader {
"border" => self.handle_border(dl, wrench, item, &mut info),
"gradient" => self.handle_gradient(dl, item, &mut info),
"radial-gradient" => self.handle_radial_gradient(dl, item, &mut info),
"box-shadow" => self.handle_box_shadow(dl, item, &mut info),
"iframe" => self.handle_iframe(dl, item, &mut info),
"stacking-context" => {
self.add_stacking_context_from_yaml(dl, wrench, item, false, &mut info)
}
+ "reference-frame" => self.handle_reference_frame(dl, wrench, item, &mut info),
"shadow" => self.handle_push_shadow(dl, item, &mut info),
"pop-all-shadows" => self.handle_pop_all_shadows(dl),
_ => println!("Skipping unknown item type: {:?}", item),
}
if pushed_clip {
dl.pop_clip_id();
@@ -1498,54 +1500,104 @@ impl YamlFrameReader {
pub fn get_root_size_from_yaml(&mut self, wrench: &mut Wrench, yaml: &Yaml) -> LayoutSize {
yaml["bounds"]
.as_rect()
.map(|rect| rect.size)
.unwrap_or(wrench.window_size_f32())
}
- pub fn add_stacking_context_from_yaml(
+ pub fn push_reference_frame(
&mut self,
dl: &mut DisplayListBuilder,
wrench: &mut Wrench,
yaml: &Yaml,
- is_root: bool,
info: &mut LayoutPrimitiveInfo,
- ) {
+ ) -> ClipId {
let default_bounds = LayoutRect::new(LayoutPoint::zero(), wrench.window_size_f32());
let bounds = yaml["bounds"].as_rect().unwrap_or(default_bounds);
-
let default_transform_origin = LayoutPoint::new(
bounds.origin.x + bounds.size.width * 0.5,
bounds.origin.y + bounds.size.height * 0.5,
);
+ info.rect = bounds;
+
let transform_origin = yaml["transform-origin"]
.as_point()
.unwrap_or(default_transform_origin);
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 = self.to_clip_id(&yaml["clip-node"], 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(),
};
+ let reference_frame_id = dl.push_reference_frame(info, transform.into(), perspective);
+
+ let numeric_id = yaml["reference-frame-id"].as_i64();
+ if let Some(numeric_id) = numeric_id {
+ self.add_clip_id_mapping(numeric_id as u64, reference_frame_id);
+ }
+
+ reference_frame_id
+ }
+
+ pub fn handle_reference_frame(
+ &mut self,
+ dl: &mut DisplayListBuilder,
+ wrench: &mut Wrench,
+ yaml: &Yaml,
+ info: &mut LayoutPrimitiveInfo,
+ ) {
+ let reference_frame_id = self.push_reference_frame(dl, wrench, yaml, info);
+
+ if !yaml["items"].is_badvalue() {
+ dl.push_clip_id(reference_frame_id);
+ self.add_display_list_items_from_yaml(dl, wrench, &yaml["items"]);
+ dl.pop_clip_id();
+ }
+
+ dl.pop_reference_frame();
+ }
+
+ pub fn add_stacking_context_from_yaml(
+ &mut self,
+ dl: &mut DisplayListBuilder,
+ wrench: &mut Wrench,
+ yaml: &Yaml,
+ is_root: bool,
+ info: &mut LayoutPrimitiveInfo,
+ ) {
+ let default_bounds = LayoutRect::new(LayoutPoint::zero(), wrench.window_size_f32());
+ let bounds = yaml["bounds"].as_rect().unwrap_or(default_bounds);
+ info.rect = bounds;
+ info.clip_rect = bounds;
+
+ let reference_frame_id = if !yaml["transform"].is_badvalue() ||
+ !yaml["perspective"].is_badvalue() {
+ let reference_frame_id = self.push_reference_frame(dl, wrench, yaml, info);
+ info.rect.origin = LayoutPoint::zero();
+ info.clip_rect.origin = LayoutPoint::zero();
+ Some(reference_frame_id)
+ } else {
+ None
+ };
+
+ let clip_node_id = self.to_clip_id(&yaml["clip-node"], dl.pipeline_id);
let transform_style = yaml["transform-style"]
.as_transform_style()
.unwrap_or(TransformStyle::Flat);
let mix_blend_mode = yaml["mix-blend-mode"]
.as_mix_blend_mode()
.unwrap_or(MixBlendMode::Normal);
let glyph_raster_space = yaml["glyph-raster-space"]
.as_glyph_raster_space()
@@ -1554,43 +1606,43 @@ impl YamlFrameReader {
if is_root {
if let Some(size) = yaml["scroll-offset"].as_point() {
let external_id = ExternalScrollId(0, dl.pipeline_id);
self.scroll_offsets.insert(external_id, LayoutPoint::new(size.x, size.y));
}
}
let filters = yaml["filters"].as_vec_filter_op().unwrap_or(vec![]);
- info.rect = bounds;
- info.clip_rect = bounds;
- let reference_frame_id = dl.push_stacking_context(
+ if let Some(reference_frame_id) = reference_frame_id {
+ dl.push_clip_id(reference_frame_id);
+ }
+
+ dl.push_stacking_context(
&info,
clip_node_id,
- transform.into(),
transform_style,
- perspective,
mix_blend_mode,
filters,
glyph_raster_space,
);
- let numeric_id = yaml["reference-frame-id"].as_i64();
- match (numeric_id, reference_frame_id) {
- (Some(numeric_id), Some(reference_frame_id)) => {
- self.add_clip_id_mapping(numeric_id as u64, reference_frame_id);
- }
- _ => {},
- }
-
if !yaml["items"].is_badvalue() {
self.add_display_list_items_from_yaml(dl, wrench, &yaml["items"]);
}
dl.pop_stacking_context();
+
+ if reference_frame_id.is_some() {
+ dl.pop_clip_id();
+ }
+
+ if reference_frame_id.is_some() {
+ dl.pop_reference_frame();
+ }
}
}
impl WrenchThing for YamlFrameReader {
fn do_frame(&mut self, wrench: &mut Wrench) -> u32 {
if !self.frame_built || self.watch_source {
self.build(wrench);
self.frame_built = false;
--- a/gfx/wrench/src/yaml_frame_writer.rs
+++ b/gfx/wrench/src/yaml_frame_writer.rs
@@ -173,45 +173,58 @@ 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_reference_frame(
+ parent: &mut Table,
+ reference_frame: &ReferenceFrame,
+ properties: &SceneProperties,
+ clip_id_mapper: &mut ClipIdMapper,
+) {
+ matrix4d_node(
+ parent,
+ "transform",
+ &properties.resolve_layout_transform(&reference_frame.transform)
+ );
+
+ if let Some(perspective) = reference_frame.perspective {
+ matrix4d_node(parent, "perspective", &perspective);
+ }
+
+ usize_node(parent, "id", clip_id_mapper.add_id(reference_frame.id));
+}
+
fn write_stacking_context(
parent: &mut Table,
sc: &StackingContext,
properties: &SceneProperties,
filter_iter: AuxIter<FilterOp>,
clip_id_mapper: &ClipIdMapper,
) {
- matrix4d_node(parent, "transform", &properties.resolve_layout_transform(&sc.transform));
-
enum_node(parent, "transform-style", sc.transform_style);
let glyph_raster_space = match sc.glyph_raster_space {
GlyphRasterSpace::Local(scale) => {
format!("local({})", scale)
}
GlyphRasterSpace::Screen => {
"screen".to_owned()
}
};
str_node(parent, "glyph-raster-space", &glyph_raster_space);
if let Some(clip_node_id) = sc.clip_node_id {
yaml_node(parent, "clip-node", clip_id_mapper.map_id(&clip_node_id));
}
- 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)
}
// filters
let mut filters = vec![];
for filter in filter_iter {
match filter {
@@ -1026,16 +1039,29 @@ impl YamlFrameWriter {
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);
}
+ PushReferenceFrame(item) => {
+ str_node(&mut v, "type", "reference-frame");
+ write_reference_frame(
+ &mut v,
+ &item.reference_frame,
+ &scene.properties,
+ 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));
let (complex_clips, complex_clip_count) = base.complex_clip();
if let Some(complex) = self.make_complex_clips_node(
complex_clip_count,
complex_clips,
@@ -1116,16 +1142,17 @@ impl YamlFrameWriter {
let applied = vec![
Yaml::Real(item.previously_applied_offset.x.to_string()),
Yaml::Real(item.previously_applied_offset.y.to_string()),
];
yaml_node(&mut v, "previously-applied-offset", Yaml::Array(applied));
}
PopStackingContext => return,
+ PopReferenceFrame => return,
SetGradientStops => panic!("dummy item yielded?"),
PushShadow(shadow) => {
str_node(&mut v, "type", "shadow");
vector_node(&mut v, "offset", &shadow.offset);
color_node(&mut v, "color", shadow.color);
f32_node(&mut v, "blur-radius", shadow.blur_radius);
}
PopAllShadows => {