diff --git a/webrender/res/brush.glsl b/webrender/res/brush.glsl index ac1d57d077..ad833d3ee0 100644 --- a/webrender/res/brush.glsl +++ b/webrender/res/brush.glsl @@ -78,7 +78,6 @@ void main(void) { vec4[2] segment_data = fetch_from_resource_cache_2(segment_address); RectWithSize local_segment_rect = RectWithSize(segment_data[0].xy, segment_data[0].zw); - vec4 edge_aa_segment_mask = vec4((int(segment_data[1].x) & ivec4(1,2,4,8)) != ivec4(0)); vec2 device_pos, local_pos; @@ -121,11 +120,12 @@ void main(void) { vLocalBounds = vec4(vec2(-1000000.0), vec2(1000000.0)); #endif } else { + bvec4 edge_mask = notEqual(int(segment_data[1].x) & ivec4(1, 2, 4, 8), ivec4(0)); vi = write_transform_vertex( local_segment_rect, brush_prim.local_rect, brush_prim.local_clip_rect, - edge_aa_segment_mask, + mix(vec4(0.0), vec4(1.0), edge_mask), float(brush.z), scroll_node, pic_task diff --git a/webrender/res/prim_shared.glsl b/webrender/res/prim_shared.glsl index 7753298899..25531288ac 100644 --- a/webrender/res/prim_shared.glsl +++ b/webrender/res/prim_shared.glsl @@ -16,6 +16,11 @@ #define SUBPX_DIR_HORIZONTAL 1 #define SUBPX_DIR_VERTICAL 2 +// Fixed value for the edge extrusion for AA, +// specified in pixels. If not defined, the extrusion +// is computed automatically. +//#define FIXED_EDGE_EXTRUSION (2.0) + uniform sampler2DArray sCacheA8; uniform sampler2DArray sCacheRGBA8; @@ -145,6 +150,9 @@ vec4 fetch_from_resource_cache_1(int address) { struct ClipScrollNode { mat4 transform; +#ifndef FIXED_EDGE_EXTRUSION + mat4 inverse_transform; +#endif bool is_axis_aligned; }; @@ -162,6 +170,9 @@ ClipScrollNode fetch_clip_scroll_node(int index) { 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)); +#ifndef FIXED_EDGE_EXTRUSION + node.inverse_transform = inverse(node.transform); +#endif vec4 misc = TEXEL_FETCH(sClipScrollNodes, uv0, 0, ivec2(4, 0)); node.is_axis_aligned = misc.x == 0.0; @@ -514,11 +525,28 @@ vec4 get_node_pos(vec2 pos, ClipScrollNode node) { vec3 a = ah.xyz / ah.w; // get the normal to the scroll node plane +#ifdef FIXED_EDGE_EXTRUSION mat4 inv_transform = inverse(node.transform); +#else + mat4 inv_transform = node.inverse_transform; +#endif vec3 n = transpose(mat3(inv_transform)) * vec3(0.0, 0.0, 1.0); return untransform(pos, n, a, inv_transform); } +#ifndef FIXED_EDGE_EXTRUSION +// Given a local CSS space position, and an expected offset of its transformed result, +// produce a new local space CSS position in homogeneous coordinates. +vec4 retransform(vec2 pos, vec2 transformed_offset, ClipScrollNode node) { + vec4 ah = node.transform * vec4(0.0, 0.0, 0.0, 1.0); + vec3 a = ah.xyz / ah.w; + vec3 n = transpose(mat3(node.inverse_transform)) * vec3(0.0, 0.0, 1.0); + + vec4 pt = node.transform * vec4(pos, 0.0, 1.0); + return untransform(pt.xy/pt.w + transformed_offset, n, a, node.inverse_transform); +} +#endif //ifndef FIXED_EDGE_EXTRUSION + // 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, @@ -622,20 +650,41 @@ VertexInfo write_transform_vertex(RectWithSize local_segment_rect, prim_rect.p0 = clamp(prim_rect.p0, clip_rect.p0, clip_rect.p1); prim_rect.p1 = clamp(prim_rect.p1, clip_rect.p0, clip_rect.p1); - // As this is a transform shader, extrude by 2 (local space) pixels - // in each direction. This gives enough space around the edge to + // Note: only those sides are extruded that have AA applied. + vec4 extrude_edge_mask = clip_edge_mask * vec4(-1.0, -1.0, 1.0, 1.0); + vec2 local_pos; + +#ifdef FIXED_EDGE_EXTRUSION + // Extrude the edges by a given value (in local space) in each direction. + // Hopefully, this gives enough space around the edge to // apply distance anti-aliasing. Technically, it: // (a) slightly over-estimates the number of required pixels in the simple case. // (b) might not provide enough edge in edge case perspective projections. - // However, it's fast and simple. If / when we ever run into issues, we - // can do some math on the projection matrix to work out a variable - // amount to extrude. - float extrude_distance = 2.0; - local_segment_rect.p0 -= vec2(extrude_distance); - local_segment_rect.size += vec2(2.0 * extrude_distance); + // (c) simple and fast // Select the corner of the local rect that we are processing. - vec2 local_pos = local_segment_rect.p0 + local_segment_rect.size * aPosition.xy; + local_pos = local_segment_rect.p0 + aPosition.xy * local_segment_rect.size + + FIXED_EDGE_EXTRUSION * mix(extrude_edge_mask.xy, extrude_edge_mask.zw, aPosition.xy); +#else + // Dynamic extrusion distance computation based on the inverse transform. + { + extrude_edge_mask *= 0.5; // half a pixel offset. TODO: divide by `uDevicePixelRatio`? + vec4 rect = vec4(local_segment_rect.p0, local_segment_rect.p0 + local_segment_rect.size); + + vec4 p00 = retransform(rect.xy, extrude_edge_mask.xy, scroll_node); + vec4 p10 = retransform(rect.zy, extrude_edge_mask.zy, scroll_node); + vec4 p11 = retransform(rect.zw, extrude_edge_mask.zw, scroll_node); + vec4 p01 = retransform(rect.xw, extrude_edge_mask.xw, scroll_node); + + vec4 diag0 = vec4(p00.xy, p11.xy) / vec4(p00.ww, p11.ww); + vec4 diag1 = vec4(p10.xy, p01.xy) / vec4(p10.ww, p01.ww); + // This should work for affine transformations. + local_pos = mix( + mix(diag0.xy, diag1.xw, aPosition.xy), + mix(diag1.zy, diag0.zw, aPosition.xy), + aPosition.yx); + } +#endif // Transform the current vertex to the world cpace. vec4 world_pos = scroll_node.transform * vec4(local_pos, 0.0, 1.0); @@ -668,7 +717,7 @@ VertexInfo write_transform_vertex_primitive(Primitive prim) { prim.local_rect, prim.local_rect, prim.local_clip_rect, - vec4(0.0), + vec4(1.0), prim.z, prim.scroll_node, prim.task @@ -717,7 +766,7 @@ TextRun fetch_text_run(int address) { struct Image { vec4 stretch_size_and_tile_spacing; // Size of the actual image and amount of space between // tiled instances of this image. - vec4 sub_rect; // If negative, ignored. + vec4 sub_rect; // If negative, ignored. }; Image fetch_image(int address) { diff --git a/webrender/src/border.rs b/webrender/src/border.rs index b9e17506ae..9c4b1b1688 100644 --- a/webrender/src/border.rs +++ b/webrender/src/border.rs @@ -433,20 +433,20 @@ impl FrameBuilder { ); let p3 = info.rect.bottom_right(); - let segment = |x0, y0, x1, y1, mask| BrushSegment::new( + let segment = |x0, y0, x1, y1| BrushSegment::new( LayerPoint::new(x0, y0), LayerSize::new(x1-x0, y1-y0), false, - mask + EdgeAaSegmentMask::all() // Note: this doesn't seem right, needs revision ); // Add a solid rectangle for each visible edge/corner combination. if top_edge == BorderEdgeKind::Solid { let descriptor = BrushSegmentDescriptor { segments: vec![ - segment(p0.x, p0.y, p1.x, p1.y, EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::LEFT), - segment(p2.x, p0.y, p3.x, p1.y, EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::RIGHT), - segment(p1.x, p0.y, p2.x, p1.y, EdgeAaSegmentMask::TOP), + segment(p0.x, p0.y, p1.x, p1.y), + segment(p2.x, p0.y, p3.x, p1.y), + segment(p1.x, p0.y, p2.x, p1.y), ], clip_mask_kind: BrushClipMaskKind::Unknown, }; @@ -462,7 +462,7 @@ impl FrameBuilder { if left_edge == BorderEdgeKind::Solid { let descriptor = BrushSegmentDescriptor { segments: vec![ - segment(p0.x, p1.y, p1.x, p2.y, EdgeAaSegmentMask::LEFT), + segment(p0.x, p1.y, p1.x, p2.y), ], clip_mask_kind: BrushClipMaskKind::Unknown, }; @@ -478,7 +478,7 @@ impl FrameBuilder { if right_edge == BorderEdgeKind::Solid { let descriptor = BrushSegmentDescriptor { segments: vec![ - segment(p2.x, p1.y, p3.x, p2.y, EdgeAaSegmentMask::RIGHT), + segment(p2.x, p1.y, p3.x, p2.y), ], clip_mask_kind: BrushClipMaskKind::Unknown, }; @@ -494,9 +494,9 @@ impl FrameBuilder { if bottom_edge == BorderEdgeKind::Solid { let descriptor = BrushSegmentDescriptor { segments: vec![ - segment(p1.x, p2.y, p2.x, p3.y, EdgeAaSegmentMask::BOTTOM), - segment(p2.x, p2.y, p3.x, p3.y, EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::RIGHT), - segment(p0.x, p2.y, p1.x, p3.y, EdgeAaSegmentMask::BOTTOM | EdgeAaSegmentMask::LEFT), + 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, }; @@ -934,4 +934,4 @@ impl ImageBorderSegment { tile_spacing, } } -} \ No newline at end of file +} diff --git a/webrender/src/prim_store.rs b/webrender/src/prim_store.rs index 2d33c56bdc..59b02a0dac 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -293,6 +293,7 @@ impl BrushPrimitive { } fn write_gpu_blocks(&self, request: &mut GpuDataRequest) { + // has to match VECS_PER_SPECIFIC_BRUSH match self.kind { BrushKind::Solid { color } => { request.push(color.premultiplied()); @@ -1241,6 +1242,7 @@ impl PrimitiveStore { // Mark this GPU resource as required for this frame. if let Some(mut request) = gpu_cache.request(&mut metadata.gpu_location) { + // has to match VECS_PER_BRUSH_PRIM request.push(metadata.local_rect); request.push(metadata.local_clip_rect); @@ -1290,7 +1292,7 @@ impl PrimitiveStore { // GPU blocks... request.push(metadata.local_rect); request.push([ - EdgeAaSegmentMask::empty().bits() as f32, + EdgeAaSegmentMask::all().bits() as f32, 0.0, 0.0, 0.0 @@ -1299,10 +1301,10 @@ impl PrimitiveStore { PrimitiveKind::Brush => { let brush = &self.cpu_brushes[metadata.cpu_prim_index.0]; brush.write_gpu_blocks(&mut request); - match brush.segment_desc { Some(ref segment_desc) => { for segment in &segment_desc.segments { + // has to match VECS_PER_SEGMENT request.push(segment.local_rect); request.push([ segment.edge_flags.bits() as f32, @@ -1315,7 +1317,7 @@ impl PrimitiveStore { None => { request.push(metadata.local_rect); request.push([ - EdgeAaSegmentMask::empty().bits() as f32, + EdgeAaSegmentMask::all().bits() as f32, 0.0, 0.0, 0.0 diff --git a/wrench/reftests/aa/reftest.list b/wrench/reftests/aa/reftest.list index ec09c585ab..55953d46d2 100644 --- a/wrench/reftests/aa/reftest.list +++ b/wrench/reftests/aa/reftest.list @@ -1 +1,2 @@ == rounded-rects.yaml rounded-rects-ref.png +== rotate-y.yaml rotate-y.png diff --git a/wrench/reftests/aa/rotate-y.png b/wrench/reftests/aa/rotate-y.png new file mode 100644 index 0000000000..b2324b8c29 Binary files /dev/null and b/wrench/reftests/aa/rotate-y.png differ diff --git a/wrench/reftests/aa/rotate-y.yaml b/wrench/reftests/aa/rotate-y.yaml new file mode 100644 index 0000000000..841d49f9de --- /dev/null +++ b/wrench/reftests/aa/rotate-y.yaml @@ -0,0 +1,47 @@ +--- +root: + items: + - + type: "stacking-context" + items: + - + type: "stacking-context" + transform: rotate-z(1) rotate-y(0) translate(20, 20, 0) + items: + - + bounds: [0, 0, 200, 200] + image: checkerboard(2, 16, 12) + stretch-size: 200 200 + - + type: "stacking-context" + transform: rotate-z(1) rotate-y(22) translate(240,20,0) + items: + - + bounds: [0, 0, 200, 200] + image: checkerboard(2, 16, 12) + stretch-size: 200 200 + - + type: "stacking-context" + transform: rotate-z(1) rotate-y(44) translate(-240,240,0) + items: + - + bounds: [0, 0, 200, 200] + image: checkerboard(2, 16, 12) + stretch-size: 200 200 + - + type: "stacking-context" + transform: rotate-z(1) rotate-y(66) translate(-320,240,0) + items: + - + bounds: [0, 0, 200, 200] + image: checkerboard(2, 16, 12) + stretch-size: 200 200 + + - + type: "stacking-context" + transform: rotate-z(1) rotate-y(88) translate(-480,240,0) + items: + - + bounds: [0, 0, 200, 200] + image: checkerboard(2, 16, 12) + stretch-size: 200 200 diff --git a/wrench/reftests/transforms/reftest.list b/wrench/reftests/transforms/reftest.list index f580bc0155..9a6942c38d 100644 --- a/wrench/reftests/transforms/reftest.list +++ b/wrench/reftests/transforms/reftest.list @@ -1,5 +1,6 @@ == local-clip.yaml local-clip.png == rotated-clip.yaml rotated-clip.png +== rotated-clip-large.yaml rotated-clip-large.png == image-rotated-clip.yaml image-rotated-clip.png # Something leaks the state: the test passes if only run `reftest reftests/transform` # but fails when all the tests are run diff --git a/wrench/reftests/transforms/rotated-clip-large.png b/wrench/reftests/transforms/rotated-clip-large.png new file mode 100644 index 0000000000..5f9a252f14 Binary files /dev/null and b/wrench/reftests/transforms/rotated-clip-large.png differ diff --git a/wrench/reftests/transforms/rotated-clip-large.yaml b/wrench/reftests/transforms/rotated-clip-large.yaml new file mode 100644 index 0000000000..18aa7e3dd4 --- /dev/null +++ b/wrench/reftests/transforms/rotated-clip-large.yaml @@ -0,0 +1,17 @@ +--- +root: + items: + - + bounds: 100 100 300 300 + items: + - type: clip + bounds: [20, 20, 200, 200] + complex: + - rect: [20, 20, 200, 200] + radius: 32 + items: + - type: rect + bounds: 20 20 200 200 + color: blue + type: stacking-context + transform: rotate(33)