diff --git a/webrender/res/brush.glsl b/webrender/res/brush.glsl index 1d3bc3f9ec..81e90736e9 100644 --- a/webrender/res/brush.glsl +++ b/webrender/res/brush.glsl @@ -14,6 +14,22 @@ void brush_vs( #define RASTERIZATION_MODE_LOCAL_SPACE 0.0 #define RASTERIZATION_MODE_SCREEN_SPACE 1.0 +#define SEGMENT_ALL 0 +#define SEGMENT_TOP_LEFT 1 +#define SEGMENT_TOP_RIGHT 2 +#define SEGMENT_BOTTOM_RIGHT 3 +#define SEGMENT_BOTTOM_LEFT 4 +#define SEGMENT_TOP_MID 5 +#define SEGMENT_MID_RIGHT 6 +#define SEGMENT_BOTTOM_MID 7 +#define SEGMENT_MID_LEFT 8 +#define SEGMENT_CENTER 9 + +#define AA_KIND_DEFAULT 0 +#define AA_KIND_SEGMENT 1 + +#define VECS_PER_BRUSH_PRIM 4 + struct BrushInstance { int picture_address; int prim_address; @@ -21,7 +37,7 @@ struct BrushInstance { int scroll_node_id; int clip_address; int z; - int flags; + int segment_kind; ivec2 user_data; }; @@ -34,12 +50,32 @@ BrushInstance load_brush() { bi.scroll_node_id = aData0.z % 65536; bi.clip_address = aData0.w; bi.z = aData1.x; - bi.flags = aData1.y; + bi.segment_kind = aData1.y; bi.user_data = aData1.zw; return bi; } +struct BrushPrimitive { + RectWithSize local_rect; + RectWithSize local_clip_rect; + vec4 offsets; + int aa_kind; +}; + +BrushPrimitive fetch_brush_primitive(int address) { + vec4 data[4] = fetch_from_resource_cache_4(address); + + BrushPrimitive prim = BrushPrimitive( + RectWithSize(data[0].xy, data[0].zw), + RectWithSize(data[1].xy, data[1].zw), + data[2], + int(data[3].x) + ); + + return prim; +} + void main(void) { // Load the brush instance from vertex attributes. BrushInstance brush = load_brush(); @@ -47,17 +83,78 @@ void main(void) { // Load the geometry for this brush. For now, this is simply the // local rect of the primitive. In the future, this will support // loading segment rects, and other rect formats (glyphs). - PrimitiveGeometry geom = fetch_primitive_geometry(brush.prim_address); + BrushPrimitive brush_prim = fetch_brush_primitive(brush.prim_address); + + // Fetch the segment of this brush primitive we are drawing. + RectWithSize local_segment_rect; + vec4 edge_aa_segment_mask; + + // p0 = origin of outer rect + // p1 = origin of inner rect + // p2 = bottom right corner of inner rect + // p3 = bottom right corner of outer rect + vec2 p0 = brush_prim.local_rect.p0; + vec2 p1 = brush_prim.local_rect.p0 + brush_prim.offsets.xy; + vec2 p2 = brush_prim.local_rect.p0 + brush_prim.local_rect.size - brush_prim.offsets.zw; + vec2 p3 = brush_prim.local_rect.p0 + brush_prim.local_rect.size; + + switch (brush.segment_kind) { + case SEGMENT_ALL: + local_segment_rect = brush_prim.local_rect; + break; + + case SEGMENT_TOP_LEFT: + local_segment_rect = RectWithSize(p0, p1 - p0); + break; + case SEGMENT_TOP_RIGHT: + local_segment_rect = RectWithSize(vec2(p2.x, p0.y), vec2(p3.x - p2.x, p1.y - p0.y)); + break; + case SEGMENT_BOTTOM_RIGHT: + local_segment_rect = RectWithSize(vec2(p2.x, p2.y), vec2(p3.x - p2.x, p3.y - p2.y)); + break; + case SEGMENT_BOTTOM_LEFT: + local_segment_rect = RectWithSize(vec2(p0.x, p2.y), vec2(p1.x - p0.x, p3.y - p2.y)); + break; + + case SEGMENT_TOP_MID: + local_segment_rect = RectWithSize(vec2(p1.x, p0.y), vec2(p2.x - p1.x, p1.y - p0.y)); + break; + case SEGMENT_MID_RIGHT: + local_segment_rect = RectWithSize(vec2(p2.x, p1.y), vec2(p3.x - p2.x, p2.y - p1.y)); + break; + case SEGMENT_BOTTOM_MID: + local_segment_rect = RectWithSize(vec2(p1.x, p2.y), vec2(p2.x - p1.x, p3.y - p2.y)); + break; + case SEGMENT_MID_LEFT: + local_segment_rect = RectWithSize(vec2(p0.x, p1.y), vec2(p1.x - p0.x, p2.y - p1.y)); + break; + + case SEGMENT_CENTER: + local_segment_rect = RectWithSize(p1, p2 - p1); + break; + + default: + local_segment_rect = RectWithSize(vec2(0.0), vec2(0.0)); + break; + } + + switch (brush_prim.aa_kind) { + case AA_KIND_SEGMENT: + // TODO: select these correctly based on the segment kind. + edge_aa_segment_mask = vec4(1.0); + break; + case AA_KIND_DEFAULT: + edge_aa_segment_mask = vec4(1.0); + break; + } vec2 device_pos, local_pos; - RectWithSize local_rect = geom.local_rect; // Fetch the dynamic picture that we are drawing on. PictureTask pic_task = fetch_picture_task(brush.picture_address); if (pic_task.rasterization_mode == RASTERIZATION_MODE_LOCAL_SPACE) { - - local_pos = local_rect.p0 + aPosition.xy * local_rect.size; + local_pos = local_segment_rect.p0 + aPosition.xy * local_segment_rect.size; // Right now - pictures only support local positions. In the future, this // will be expanded to support transform picture types (the common kind). @@ -74,12 +171,12 @@ void main(void) { // Write the normal vertex information out. if (layer.is_axis_aligned) { vi = write_vertex( - geom.local_rect, - geom.local_clip_rect, + local_segment_rect, + brush_prim.local_clip_rect, float(brush.z), layer, pic_task, - geom.local_rect + brush_prim.local_rect ); // TODO(gw): vLocalBounds may be referenced by @@ -88,15 +185,15 @@ void main(void) { // items. For now, just ensure it has no // effect. We can tidy this up as we move // more items to be brush shaders. - vLocalBounds = vec4( - geom.local_clip_rect.p0, - geom.local_clip_rect.p0 + geom.local_clip_rect.size - ); +#ifdef WR_FEATURE_ALPHA_PASS + vLocalBounds = vec4(vec2(-1000000.0), vec2(1000000.0)); +#endif } else { - vi = write_transform_vertex(geom.local_rect, - geom.local_rect, - geom.local_clip_rect, - vec4(1.0), + vi = write_transform_vertex( + local_segment_rect, + brush_prim.local_rect, + brush_prim.local_clip_rect, + edge_aa_segment_mask, float(brush.z), layer, pic_task @@ -121,9 +218,9 @@ void main(void) { // Run the specific brush VS code to write interpolators. brush_vs( - brush.prim_address + VECS_PER_PRIM_HEADER, + brush.prim_address + VECS_PER_BRUSH_PRIM, local_pos, - local_rect, + brush_prim.local_rect, brush.user_data ); } diff --git a/webrender/res/brush_mask_rounded_rect.glsl b/webrender/res/brush_mask_rounded_rect.glsl index fd490d240c..327e7d8f23 100644 --- a/webrender/res/brush_mask_rounded_rect.glsl +++ b/webrender/res/brush_mask_rounded_rect.glsl @@ -14,7 +14,7 @@ varying vec2 vLocalPos; #ifdef WR_VERTEX_SHADER -struct BrushPrimitive { +struct RoundedRectPrimitive { float clip_mode; vec4 rect; vec2 radius_tl; @@ -23,9 +23,9 @@ struct BrushPrimitive { vec2 radius_bl; }; -BrushPrimitive fetch_brush_primitive(int address) { +RoundedRectPrimitive fetch_rounded_rect_primitive(int address) { vec4 data[4] = fetch_from_resource_cache_4(address); - return BrushPrimitive( + return RoundedRectPrimitive( data[0].x, data[1], data[2].xy, @@ -42,7 +42,7 @@ void brush_vs( ivec2 user_data ) { // Load the specific primitive. - BrushPrimitive prim = fetch_brush_primitive(prim_address); + RoundedRectPrimitive prim = fetch_rounded_rect_primitive(prim_address); // Write clip parameters vClipMode = prim.clip_mode; diff --git a/webrender/res/brush_solid.glsl b/webrender/res/brush_solid.glsl new file mode 100644 index 0000000000..7976707223 --- /dev/null +++ b/webrender/res/brush_solid.glsl @@ -0,0 +1,47 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include shared,prim_shared,brush + +flat varying vec4 vColor; + +#ifdef WR_FEATURE_ALPHA_PASS +varying vec2 vLocalPos; +#endif + +#ifdef WR_VERTEX_SHADER + +struct SolidBrush { + vec4 color; +}; + +SolidBrush fetch_solid_primitive(int address) { + vec4 data = fetch_from_resource_cache_1(address); + return SolidBrush(data); +} + +void brush_vs( + int prim_address, + vec2 local_pos, + RectWithSize local_rect, + ivec2 user_data +) { + SolidBrush prim = fetch_solid_primitive(prim_address); + vColor = prim.color; + +#ifdef WR_FEATURE_ALPHA_PASS + vLocalPos = local_pos; +#endif +} +#endif + +#ifdef WR_FRAGMENT_SHADER +vec4 brush_fs() { + vec4 color = vColor; +#ifdef WR_FEATURE_ALPHA_PASS + color *= init_transform_fs(vLocalPos); +#endif + return color; +} +#endif diff --git a/webrender/res/clip_shared.glsl b/webrender/res/clip_shared.glsl index 2e437bc270..90da9e7799 100644 --- a/webrender/res/clip_shared.glsl +++ b/webrender/res/clip_shared.glsl @@ -50,38 +50,8 @@ RectWithSize intersect_rect(RectWithSize a, RectWithSize b) { // which is the intersection of all clip instances of a given primitive ClipVertexInfo write_clip_tile_vertex(RectWithSize local_clip_rect, Layer layer, - ClipArea area, - int segment) { - vec2 outer_p0 = area.screen_origin; - vec2 outer_p1 = outer_p0 + area.common_data.task_rect.size; - vec2 inner_p0 = area.inner_rect.xy; - vec2 inner_p1 = area.inner_rect.zw; - - vec2 p0, p1; - switch (segment) { - case SEGMENT_ALL: - p0 = outer_p0; - p1 = outer_p1; - break; - case SEGMENT_CORNER_TL: - p0 = outer_p0; - p1 = inner_p0; - break; - case SEGMENT_CORNER_BL: - p0 = vec2(outer_p0.x, outer_p1.y); - p1 = vec2(inner_p0.x, inner_p1.y); - break; - case SEGMENT_CORNER_TR: - p0 = vec2(outer_p1.x, outer_p1.y); - p1 = vec2(inner_p1.x, inner_p1.y); - break; - case SEGMENT_CORNER_BR: - p0 = vec2(outer_p1.x, outer_p0.y); - p1 = vec2(inner_p1.x, inner_p0.y); - break; - } - - vec2 actual_pos = mix(p0, p1, aPosition.xy); + ClipArea area) { + vec2 actual_pos = area.screen_origin + aPosition.xy * area.common_data.task_rect.size; vec4 layer_pos = get_layer_pos(actual_pos / uDevicePixelRatio, layer); diff --git a/webrender/res/cs_clip_image.glsl b/webrender/res/cs_clip_image.glsl index 188c5e2e03..009896e17c 100644 --- a/webrender/res/cs_clip_image.glsl +++ b/webrender/res/cs_clip_image.glsl @@ -31,8 +31,7 @@ void main(void) { ClipVertexInfo vi = write_clip_tile_vertex(local_rect, layer, - area, - cmi.segment); + area); vPos = vi.local_pos; vLayer = res.layer; diff --git a/webrender/res/cs_clip_rectangle.glsl b/webrender/res/cs_clip_rectangle.glsl index d6518df517..c1a985ce1f 100644 --- a/webrender/res/cs_clip_rectangle.glsl +++ b/webrender/res/cs_clip_rectangle.glsl @@ -64,8 +64,7 @@ void main(void) { ClipVertexInfo vi = write_clip_tile_vertex(local_rect, layer, - area, - cmi.segment); + area); vPos = vi.local_pos; vClipMode = clip.rect.mode.x; diff --git a/webrender/res/prim_shared.glsl b/webrender/res/prim_shared.glsl index ef8bb0c803..6f68882033 100644 --- a/webrender/res/prim_shared.glsl +++ b/webrender/res/prim_shared.glsl @@ -305,7 +305,6 @@ BlurTask fetch_blur_task(int address) { struct ClipArea { RenderTaskCommonData common_data; vec2 screen_origin; - vec4 inner_rect; }; ClipArea fetch_clip_area(int index) { @@ -317,13 +316,11 @@ ClipArea fetch_clip_area(int index) { 0.0 ); area.screen_origin = vec2(0.0); - area.inner_rect = vec4(0.0); } else { RenderTaskData task_data = fetch_render_task_data(index); area.common_data = task_data.common_data; area.screen_origin = task_data.data1.xy; - area.inner_rect = task_data.data2; } return area; @@ -732,17 +729,6 @@ ImageResource fetch_image_resource_direct(ivec2 address) { return ImageResource(data[0], data[1].x); } -struct Rectangle { - vec4 color; - vec4 edge_aa_segment_mask; -}; - -Rectangle fetch_rectangle(int address) { - vec4 data[2] = fetch_from_resource_cache_2(address); - vec4 mask = vec4((int(data[1].x) & ivec4(1,2,4,8)) != ivec4(0)); - return Rectangle(data[0], mask); -} - struct TextRun { vec4 color; vec4 bg_color; diff --git a/webrender/res/ps_rectangle.glsl b/webrender/res/ps_rectangle.glsl deleted file mode 100644 index bf17f91b43..0000000000 --- a/webrender/res/ps_rectangle.glsl +++ /dev/null @@ -1,54 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include shared,prim_shared - -varying vec4 vColor; - -#ifdef WR_FEATURE_TRANSFORM -varying vec2 vLocalPos; -#endif - -#ifdef WR_VERTEX_SHADER -void main(void) { - Primitive prim = load_primitive(); - Rectangle rect = fetch_rectangle(prim.specific_prim_address); - vColor = rect.color; -#ifdef WR_FEATURE_TRANSFORM - VertexInfo vi = write_transform_vertex(prim.local_rect, - prim.local_rect, - prim.local_clip_rect, - rect.edge_aa_segment_mask, - prim.z, - prim.layer, - prim.task); - vLocalPos = vi.local_pos; -#else - VertexInfo vi = write_vertex(prim.local_rect, - prim.local_clip_rect, - prim.z, - prim.layer, - prim.task, - prim.local_rect); -#endif - -#ifdef WR_FEATURE_CLIP - write_clip(vi.screen_pos, prim.clip_area); -#endif -} -#endif - -#ifdef WR_FRAGMENT_SHADER -void main(void) { - float alpha = 1.0; -#ifdef WR_FEATURE_TRANSFORM - alpha = init_transform_fs(vLocalPos); -#endif - -#ifdef WR_FEATURE_CLIP - alpha *= do_clip(); -#endif - oFragColor = vColor * alpha; -} -#endif diff --git a/webrender/src/border.rs b/webrender/src/border.rs index ed1ecd50a1..a0e627a060 100644 --- a/webrender/src/border.rs +++ b/webrender/src/border.rs @@ -9,7 +9,7 @@ use clip::ClipSource; use ellipse::Ellipse; use frame_builder::FrameBuilder; use gpu_cache::GpuDataRequest; -use internal_types::EdgeAaSegmentMask; +use prim_store::{BrushAntiAliasMode, BrushSegmentDescriptor, BrushSegmentKind}; use prim_store::{BorderPrimitiveCpu, PrimitiveContainer, TexelRect}; use util::{lerp, pack_as_float}; @@ -374,56 +374,84 @@ impl FrameBuilder { let has_no_curve = radius.is_zero(); if has_no_curve && all_corners_simple && all_edges_simple { - let p0 = info.rect.origin; - let p1 = info.rect.bottom_right(); - let rect_width = info.rect.size.width; - let rect_height = info.rect.size.height; - let mut info = info.clone(); + let inner_rect = LayerRect::new( + LayerPoint::new( + info.rect.origin.x + left_len, + info.rect.origin.y + top_len, + ), + LayerSize::new( + info.rect.size.width - left_len - right_len, + info.rect.size.height - top_len - bottom_len, + ), + ); // Add a solid rectangle for each visible edge/corner combination. if top_edge == BorderEdgeKind::Solid { - info.rect = LayerRect::new(p0, LayerSize::new(rect_width, top_len)); + let descriptor = BrushSegmentDescriptor::new( + &info.rect, + &inner_rect, + Some(&[ + BrushSegmentKind::TopLeft, + BrushSegmentKind::TopMid, + BrushSegmentKind::TopRight + ]), + ); self.add_solid_rectangle( clip_and_scroll, &info, border.top.color, - EdgeAaSegmentMask::BOTTOM, + Some(Box::new(descriptor)), + BrushAntiAliasMode::Segment, ); } if left_edge == BorderEdgeKind::Solid { - info.rect = LayerRect::new( - LayerPoint::new(p0.x, p0.y + top_len), - LayerSize::new(left_len, rect_height - top_len - bottom_len), + let descriptor = BrushSegmentDescriptor::new( + &info.rect, + &inner_rect, + Some(&[ + BrushSegmentKind::MidLeft, + ]), ); self.add_solid_rectangle( clip_and_scroll, &info, border.left.color, - EdgeAaSegmentMask::RIGHT, + Some(Box::new(descriptor)), + BrushAntiAliasMode::Segment, ); } if right_edge == BorderEdgeKind::Solid { - info.rect = LayerRect::new( - LayerPoint::new(p1.x - right_len, p0.y + top_len), - LayerSize::new(right_len, rect_height - top_len - bottom_len), + let descriptor = BrushSegmentDescriptor::new( + &info.rect, + &inner_rect, + Some(&[ + BrushSegmentKind::MidRight, + ]), ); self.add_solid_rectangle( clip_and_scroll, &info, border.right.color, - EdgeAaSegmentMask::LEFT, + Some(Box::new(descriptor)), + BrushAntiAliasMode::Segment, ); } if bottom_edge == BorderEdgeKind::Solid { - info.rect = LayerRect::new( - LayerPoint::new(p0.x, p1.y - bottom_len), - LayerSize::new(rect_width, bottom_len), + let descriptor = BrushSegmentDescriptor::new( + &info.rect, + &inner_rect, + Some(&[ + BrushSegmentKind::BottomLeft, + BrushSegmentKind::BottomMid, + BrushSegmentKind::BottomRight + ]), ); self.add_solid_rectangle( clip_and_scroll, &info, border.bottom.color, - EdgeAaSegmentMask::TOP, + Some(Box::new(descriptor)), + BrushAntiAliasMode::Segment, ); } } else { diff --git a/webrender/src/box_shadow.rs b/webrender/src/box_shadow.rs index d0a253b9dc..836130c2a8 100644 --- a/webrender/src/box_shadow.rs +++ b/webrender/src/box_shadow.rs @@ -9,8 +9,7 @@ use api::{PipelineId}; use app_units::Au; use clip::ClipSource; use frame_builder::FrameBuilder; -use internal_types::EdgeAaSegmentMask; -use prim_store::{PrimitiveContainer, RectangleContent, RectanglePrimitive}; +use prim_store::{BrushAntiAliasMode, PrimitiveContainer}; use prim_store::{BrushMaskKind, BrushKind, BrushPrimitive}; use picture::PicturePrimitive; use util::RectHelpers; @@ -131,10 +130,14 @@ impl FrameBuilder { clip_and_scroll, &fast_info, clips, - PrimitiveContainer::Rectangle(RectanglePrimitive { - content: RectangleContent::Fill(*color), - edge_aa_segment_mask: EdgeAaSegmentMask::empty(), - }), + PrimitiveContainer::Brush( + BrushPrimitive::new(BrushKind::Solid { + color: *color, + }, + None, + BrushAntiAliasMode::Primitive, + ) + ), ); } else { let blur_offset = BLUR_SAMPLE_SCALE * blur_radius; @@ -178,12 +181,14 @@ impl FrameBuilder { width = MASK_CORNER_PADDING + corner_size.width.max(BLUR_SAMPLE_SCALE * blur_radius); height = MASK_CORNER_PADDING + corner_size.height.max(BLUR_SAMPLE_SCALE * blur_radius); - brush_prim = BrushPrimitive { - kind: BrushKind::Mask { + brush_prim = BrushPrimitive::new( + BrushKind::Mask { clip_mode: brush_clip_mode, kind: BrushMaskKind::Corner(corner_size), - } - }; + }, + None, + BrushAntiAliasMode::Primitive, + ); } else { // Create a minimal size primitive mask to blur. In this // case, we ensure the size of each corner is the same, @@ -205,12 +210,14 @@ impl FrameBuilder { let clip_rect = LayerRect::new(LayerPoint::zero(), LayerSize::new(width, height)); - brush_prim = BrushPrimitive { - kind: BrushKind::Mask { + brush_prim = BrushPrimitive::new( + BrushKind::Mask { clip_mode: brush_clip_mode, kind: BrushMaskKind::RoundedRect(clip_rect, shadow_radius), - } - }; + }, + None, + BrushAntiAliasMode::Primitive, + ); }; // Construct a mask primitive to add to the picture. @@ -288,12 +295,14 @@ impl FrameBuilder { } let brush_rect = brush_rect.inflate(inflate_size, inflate_size); - let brush_prim = BrushPrimitive { - kind: BrushKind::Mask { + let brush_prim = BrushPrimitive::new( + BrushKind::Mask { clip_mode: brush_clip_mode, kind: BrushMaskKind::RoundedRect(clip_rect, shadow_radius), - } - }; + }, + None, + BrushAntiAliasMode::Primitive, + ); let brush_info = LayerPrimitiveInfo::new(brush_rect); let brush_prim_index = self.create_primitive( &brush_info, diff --git a/webrender/src/clip_scroll_node.rs b/webrender/src/clip_scroll_node.rs index 593d891cef..dd90675b9a 100644 --- a/webrender/src/clip_scroll_node.rs +++ b/webrender/src/clip_scroll_node.rs @@ -520,7 +520,8 @@ impl ClipScrollNode { state.nearest_scrolling_ancestor_viewport .translate(&info.origin_in_parent_reference_frame); - if !info.resolved_transform.preserves_2d_axis_alignment() { + if !info.resolved_transform.preserves_2d_axis_alignment() || + info.resolved_transform.has_perspective_component() { state.current_coordinate_system_id = state.next_coordinate_system_id; state.next_coordinate_system_id = state.next_coordinate_system_id.next(); } diff --git a/webrender/src/frame.rs b/webrender/src/frame.rs index 4b476a62dc..71e158fbc7 100644 --- a/webrender/src/frame.rs +++ b/webrender/src/frame.rs @@ -6,23 +6,22 @@ use api::{BuiltDisplayListIter, ClipAndScrollInfo, ClipId, ColorF, ComplexClipRegion}; use api::{DeviceUintRect, DeviceUintSize, DisplayItemRef, DocumentLayer, Epoch, FilterOp}; use api::{ImageDisplayItem, ItemRange, LayerPoint, LayerPrimitiveInfo, LayerRect}; -use api::{LayerSize, LayerVector2D}; -use api::{LayoutRect, LayoutSize}; +use api::{LayerSize, LayerVector2D, LayoutSize}; use api::{LocalClip, PipelineId, ScrollClamping, ScrollEventPhase, ScrollLayerState}; use api::{ScrollLocation, ScrollPolicy, ScrollSensitivity, SpecificDisplayItem, StackingContext}; -use api::{ClipMode, TileOffset, TransformStyle, WorldPoint}; +use api::{TileOffset, TransformStyle, WorldPoint}; use clip::ClipRegion; use clip_scroll_node::StickyFrameInfo; use clip_scroll_tree::{ClipScrollTree, ScrollStates}; use euclid::rect; use frame_builder::{FrameBuilder, FrameBuilderConfig, ScrollbarInfo}; use gpu_cache::GpuCache; -use internal_types::{EdgeAaSegmentMask, FastHashMap, FastHashSet, RenderedDocument}; +use internal_types::{FastHashMap, FastHashSet, RenderedDocument}; +use prim_store::{BrushAntiAliasMode}; use profiler::{GpuCacheProfileCounters, TextureCacheProfileCounters}; use resource_cache::{FontInstanceMap,ResourceCache, TiledImageMap}; use scene::{Scene, StackingContextHelpers, ScenePipeline, SceneProperties}; use tiling::CompositeOps; -use util::ComplexClipRegionHelpers; #[derive(Copy, Clone, PartialEq, PartialOrd, Debug, Eq, Ord)] pub struct FrameId(pub u32); @@ -42,11 +41,6 @@ struct FlattenContext<'a> { tiled_image_map: TiledImageMap, pipeline_epochs: Vec<(PipelineId, Epoch)>, replacements: Vec<(ClipId, ClipId)>, - /// Opaque rectangle vector, stored here in order to - /// avoid re-allocation on each use. - opaque_parts: Vec, - /// Same for the transparent rectangles. - transparent_parts: Vec, output_pipelines: &'a FastHashSet, } @@ -111,7 +105,8 @@ impl<'a> FlattenContext<'a> { ClipAndScrollInfo::simple(root_reference_frame_id), &info, bg_color, - EdgeAaSegmentMask::empty(), + None, + BrushAntiAliasMode::Primitive, ); } } @@ -448,18 +443,13 @@ impl<'a> FlattenContext<'a> { } } SpecificDisplayItem::Rectangle(ref info) => { - if !self.try_to_add_rectangle_splitting_on_clip( + self.builder.add_solid_rectangle( + clip_and_scroll, &prim_info, info.color, - &clip_and_scroll, - ) { - self.builder.add_solid_rectangle( - clip_and_scroll, - &prim_info, - info.color, - EdgeAaSegmentMask::empty(), - ); - } + None, + BrushAntiAliasMode::Primitive, + ); } SpecificDisplayItem::ClearRectangle => { self.builder.add_clear_rectangle( @@ -631,86 +621,6 @@ impl<'a> FlattenContext<'a> { None } - /// Try to optimize the rendering of a solid rectangle that is clipped by a single - /// rounded rectangle, by only masking the parts of the rectangle that intersect - /// the rounded parts of the clip. This is pretty simple now, so has a lot of - /// potential for further optimizations. - fn try_to_add_rectangle_splitting_on_clip( - &mut self, - info: &LayerPrimitiveInfo, - color: ColorF, - clip_and_scroll: &ClipAndScrollInfo, - ) -> bool { - if info.rect.size.area() < 200.0 { // arbitrary threshold - // too few pixels, don't bother adding instances - return false; - } - // If this rectangle is not opaque, splitting the rectangle up - // into an inner opaque region just ends up hurting batching and - // doing more work than necessary. - if color.a != 1.0 { - return false; - } - - self.opaque_parts.clear(); - self.transparent_parts.clear(); - - match info.local_clip { - LocalClip::Rect(_) => return false, - LocalClip::RoundedRect(_, ref region) => { - if region.mode == ClipMode::ClipOut { - return false; - } - region.split_rectangles( - &mut self.opaque_parts, - &mut self.transparent_parts, - ); - } - }; - - let local_clip = LocalClip::from(*info.local_clip.clip_rect()); - let mut has_opaque = false; - - for opaque in &self.opaque_parts { - let prim_info = LayerPrimitiveInfo { - rect: match opaque.intersection(&info.rect) { - Some(rect) => rect, - None => continue, - }, - local_clip, - .. info.clone() - }; - self.builder.add_solid_rectangle( - *clip_and_scroll, - &prim_info, - color, - EdgeAaSegmentMask::empty(), - ); - has_opaque = true; - } - - if !has_opaque { - return false - } - - for transparent in &self.transparent_parts { - let prim_info = LayerPrimitiveInfo { - rect: match transparent.intersection(&info.rect) { - Some(rect) => rect, - None => continue, - }, - .. info.clone() - }; - self.builder.add_solid_rectangle( - *clip_and_scroll, - &prim_info, - color, - EdgeAaSegmentMask::empty(), - ); - } - true - } - /// Decomposes an image display item that is repeated into an image per individual repetition. /// We need to do this when we are unable to perform the repetition in the shader, /// for example if the image is tiled. @@ -1122,8 +1032,6 @@ impl FrameContext { tiled_image_map: resource_cache.get_tiled_image_map(), pipeline_epochs: Vec::new(), replacements: Vec::new(), - opaque_parts: Vec::new(), - transparent_parts: Vec::new(), output_pipelines, }; diff --git a/webrender/src/frame_builder.rs b/webrender/src/frame_builder.rs index 85abdc0421..f0fd8fdf50 100644 --- a/webrender/src/frame_builder.rs +++ b/webrender/src/frame_builder.rs @@ -21,13 +21,14 @@ use euclid::{SideOffsets2D, vec2}; use frame::FrameId; use glyph_rasterizer::FontInstance; use gpu_cache::GpuCache; -use internal_types::{EdgeAaSegmentMask, FastHashMap, FastHashSet}; +use gpu_types::ClipScrollNodeData; +use internal_types::{FastHashMap, FastHashSet}; use picture::{PictureCompositeMode, PictureKind, PicturePrimitive, RasterizationSpace}; -use prim_store::{TexelRect, YuvImagePrimitiveCpu}; +use prim_store::{BrushAntiAliasMode, BrushKind, BrushPrimitive, TexelRect, YuvImagePrimitiveCpu}; use prim_store::{GradientPrimitiveCpu, ImagePrimitiveCpu, LinePrimitive, PrimitiveKind}; use prim_store::{PrimitiveContainer, PrimitiveIndex, SpecificPrimitiveIndex}; use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu}; -use prim_store::{RectangleContent, RectanglePrimitive, TextRunPrimitiveCpu}; +use prim_store::{BrushSegmentDescriptor, TextRunPrimitiveCpu}; use profiler::{FrameProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters}; use render_task::{ClearMode, RenderTask, RenderTaskId, RenderTaskTree}; use resource_cache::ResourceCache; @@ -756,7 +757,8 @@ impl FrameBuilder { clip_and_scroll: ClipAndScrollInfo, info: &LayerPrimitiveInfo, color: ColorF, - edge_aa_segment_mask: EdgeAaSegmentMask, + segments: Option>, + aa_mode: BrushAntiAliasMode, ) { if color.a == 0.0 { // Don't add transparent rectangles to the draw list, but do consider them for hit @@ -765,16 +767,19 @@ impl FrameBuilder { return; } - let prim = RectanglePrimitive { - content: RectangleContent::Fill(color), - edge_aa_segment_mask, - }; + let prim = BrushPrimitive::new( + BrushKind::Solid { + color, + }, + segments, + aa_mode, + ); self.add_primitive( clip_and_scroll, info, Vec::new(), - PrimitiveContainer::Rectangle(prim), + PrimitiveContainer::Brush(prim), ); } @@ -783,16 +788,17 @@ impl FrameBuilder { clip_and_scroll: ClipAndScrollInfo, info: &LayerPrimitiveInfo, ) { - let prim = RectanglePrimitive { - content: RectangleContent::Clear, - edge_aa_segment_mask: EdgeAaSegmentMask::empty(), - }; + let prim = BrushPrimitive::new( + BrushKind::Clear, + None, + BrushAntiAliasMode::Primitive, + ); self.add_primitive( clip_and_scroll, info, Vec::new(), - PrimitiveContainer::Rectangle(prim), + PrimitiveContainer::Brush(prim), ); } @@ -807,16 +813,19 @@ impl FrameBuilder { return; } - let prim = RectanglePrimitive { - content: RectangleContent::Fill(color), - edge_aa_segment_mask: EdgeAaSegmentMask::empty(), - }; + let prim = BrushPrimitive::new( + BrushKind::Solid { + color, + }, + None, + BrushAntiAliasMode::Primitive, + ); let prim_index = self.add_primitive( clip_and_scroll, info, Vec::new(), - PrimitiveContainer::Rectangle(prim), + PrimitiveContainer::Brush(prim), ); self.scrollbar_prims.push(ScrollbarPrimitive { @@ -1576,6 +1585,7 @@ impl FrameBuilder { profile_counters: &mut FrameProfileCounters, device_pixel_ratio: f32, scene_properties: &SceneProperties, + node_data: &[ClipScrollNodeData], ) -> Option { profile_scope!("cull"); @@ -1618,6 +1628,7 @@ impl FrameBuilder { scene_properties, SpecificPrimitiveIndex(0), &self.screen_rect.to_i32(), + node_data, ); let pic = &mut self.prim_store.cpu_pictures[0]; @@ -1723,6 +1734,7 @@ impl FrameBuilder { &mut profile_counters, device_pixel_ratio, scene_properties, + &node_data, ); let mut passes = Vec::new(); diff --git a/webrender/src/gpu_types.rs b/webrender/src/gpu_types.rs index 52b70c0f47..61e36ecc56 100644 --- a/webrender/src/gpu_types.rs +++ b/webrender/src/gpu_types.rs @@ -154,7 +154,7 @@ pub struct BrushInstance { pub scroll_id: ClipScrollNodeIndex, pub clip_task_address: RenderTaskAddress, pub z: i32, - pub flags: i32, + pub segment_kind: i32, pub user_data0: i32, pub user_data1: i32, } @@ -168,7 +168,7 @@ impl From for PrimitiveInstance { ((instance.clip_id.0 as i32) << 16) | instance.scroll_id.0 as i32, instance.clip_task_address.0 as i32, instance.z, - instance.flags, + instance.segment_kind, instance.user_data0, instance.user_data1, ] @@ -186,7 +186,7 @@ pub enum BrushImageKind { Mirror = 2, // A top left corner only (mirror across x/y axes) } -#[derive(Copy, Debug, Clone)] +#[derive(Copy, Debug, Clone, PartialEq)] #[repr(C)] pub struct ClipScrollNodeIndex(pub u32); diff --git a/webrender/src/internal_types.rs b/webrender/src/internal_types.rs index a5c3a92410..f80bde7765 100644 --- a/webrender/src/internal_types.rs +++ b/webrender/src/internal_types.rs @@ -190,18 +190,3 @@ pub struct UvRect { pub uv0: DevicePoint, pub uv1: DevicePoint, } - -bitflags! { - /// Each bit of the edge AA mask is: - /// 0, when the edge of the primitive needs to be considered for AA - /// 1, when the edge of the segment needs to be considered for AA - /// - /// *Note*: the bit values have to match the shader logic in - /// `write_transform_vertex()` function. - pub struct EdgeAaSegmentMask: u8 { - const LEFT = 0x1; - const TOP = 0x2; - const RIGHT = 0x4; - const BOTTOM = 0x8; - } -} diff --git a/webrender/src/picture.rs b/webrender/src/picture.rs index 0bd529e4be..b707f377a8 100644 --- a/webrender/src/picture.rs +++ b/webrender/src/picture.rs @@ -525,7 +525,7 @@ impl PicturePrimitive { } } - pub fn write_gpu_blocks(&self, mut _request: GpuDataRequest) { + pub fn write_gpu_blocks(&self, _request: &mut GpuDataRequest) { // TODO(gw): We'll need to write the GPU blocks // here specific to a brush primitive // once we start drawing pictures as brushes! diff --git a/webrender/src/prim_store.rs b/webrender/src/prim_store.rs index 79e0afcfd0..1e9dedd501 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -6,16 +6,17 @@ use api::{BorderRadius, BuiltDisplayList, ColorF, ComplexClipRegion, DeviceIntRe use api::{DevicePoint, ExtendMode, FontRenderMode, GlyphInstance, GlyphKey}; use api::{GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag, LayerPoint, LayerRect}; use api::{ClipMode, LayerSize, LayerVector2D, LayerToWorldTransform, LineOrientation, LineStyle}; -use api::{ClipAndScrollInfo, PremultipliedColorF, TileOffset}; +use api::{ClipAndScrollInfo, PremultipliedColorF, TileOffset, WorldToLayerTransform}; use api::{ClipId, LayerTransform, PipelineId, YuvColorSpace, YuvFormat}; use border::BorderCornerInstance; use clip_scroll_tree::{CoordinateSystemId, ClipScrollTree}; -use clip::{ClipSourcesHandle, ClipStore}; +use clip::{ClipSource, ClipSourcesHandle, ClipStore}; use frame_builder::PrimitiveContext; use glyph_rasterizer::{FontInstance, FontTransform}; -use internal_types::{EdgeAaSegmentMask, FastHashMap}; +use internal_types::{FastHashMap}; use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest, ToGpuBlocks}; +use gpu_types::ClipScrollNodeData; use picture::{PictureKind, PicturePrimitive, RasterizationSpace}; use profiler::FrameProfileCounters; use render_task::{ClipChainNode, ClipChainNodeIter, ClipWorkItem, RenderTask, RenderTaskId}; @@ -23,9 +24,12 @@ use render_task::RenderTaskTree; use renderer::MAX_VERTEX_TEXTURE_WIDTH; use resource_cache::{ImageProperties, ResourceCache}; use scene::{ScenePipeline, SceneProperties}; -use std::{mem, usize}; +use std::{mem, u16, usize}; use std::rc::Rc; -use util::{pack_as_float, recycle_vec, MatrixHelpers, TransformedRect, TransformedRectKind}; +use util::{extract_inner_rect_safe, pack_as_float, recycle_vec}; +use util::{MatrixHelpers, TransformedRect}; + +const MIN_BRUSH_SPLIT_AREA: f32 = 128.0 * 128.0; #[derive(Debug)] pub struct PrimitiveRun { @@ -136,7 +140,6 @@ pub struct PrimitiveIndex(pub usize); #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum PrimitiveKind { - Rectangle, TextRun, Image, YuvImage, @@ -187,31 +190,6 @@ pub struct PrimitiveMetadata { pub tag: Option, } -#[derive(Debug,Clone,Copy)] -pub enum RectangleContent { - Fill(ColorF), - Clear, -} - -#[derive(Debug)] -pub struct RectanglePrimitive { - pub content: RectangleContent, - pub edge_aa_segment_mask: EdgeAaSegmentMask, -} - -impl ToGpuBlocks for RectanglePrimitive { - fn write_gpu_blocks(&self, mut request: GpuDataRequest) { - request.push(match self.content { - RectangleContent::Fill(color) => color.premultiplied(), - // Opaque black with operator dest out - RectangleContent::Clear => PremultipliedColorF::BLACK, - }); - request.extend_from_slice(&[GpuBlockData { - data: [self.edge_aa_segment_mask.bits() as f32, 0.0, 0.0, 0.0], - }]); - } -} - #[derive(Debug)] pub enum BrushMaskKind { //Rect, // TODO(gw): Optimization opportunity for masks with 0 border radii. @@ -224,17 +202,182 @@ pub enum BrushKind { Mask { clip_mode: ClipMode, kind: BrushMaskKind, + }, + Solid { + color: ColorF, + }, + Clear, +} + +#[derive(Debug, Copy, Clone)] +#[repr(u32)] +pub enum BrushAntiAliasMode { + Primitive = 0, + Segment = 1, +} + +#[allow(dead_code)] +#[derive(Debug, Copy, Clone)] +#[repr(C)] +pub enum BrushSegmentKind { + TopLeft = 0, + TopRight, + BottomRight, + BottomLeft, + + TopMid, + MidRight, + BottomMid, + MidLeft, + + Center, +} + +#[derive(Debug)] +pub struct BrushSegment { + pub local_rect: LayerRect, + pub clip_task_id: Option, +} + +impl BrushSegment { + fn new( + origin: LayerPoint, + size: LayerSize, + ) -> BrushSegment { + BrushSegment { + local_rect: LayerRect::new(origin, size), + clip_task_id: None, + } + } +} + +#[derive(Debug)] +pub struct BrushSegmentDescriptor { + pub top_left_offset: LayerVector2D, + pub bottom_right_offset: LayerVector2D, + pub segments: [BrushSegment; 9], + pub enabled_segments: u16, + pub can_optimize_clip_mask: bool, +} + +impl BrushSegmentDescriptor { + pub fn new( + outer_rect: &LayerRect, + inner_rect: &LayerRect, + valid_segments: Option<&[BrushSegmentKind]>, + ) -> BrushSegmentDescriptor { + let p0 = outer_rect.origin; + let p1 = inner_rect.origin; + let p2 = inner_rect.bottom_right(); + let p3 = outer_rect.bottom_right(); + + let enabled_segments = match valid_segments { + Some(valid_segments) => { + valid_segments.iter().fold( + 0, + |acc, segment| acc | 1 << *segment as u32 + ) + } + None => u16::MAX, + }; + + BrushSegmentDescriptor { + enabled_segments, + can_optimize_clip_mask: false, + top_left_offset: p1 - p0, + bottom_right_offset: p3 - p2, + segments: [ + BrushSegment::new( + LayerPoint::new(p0.x, p0.y), + LayerSize::new(p1.x - p0.x, p1.y - p0.y), + ), + BrushSegment::new( + LayerPoint::new(p2.x, p0.y), + LayerSize::new(p3.x - p2.x, p1.y - p0.y), + ), + BrushSegment::new( + LayerPoint::new(p2.x, p2.y), + LayerSize::new(p3.x - p2.x, p3.y - p2.y), + ), + BrushSegment::new( + LayerPoint::new(p0.x, p2.y), + LayerSize::new(p1.x - p0.x, p3.y - p2.y), + ), + BrushSegment::new( + LayerPoint::new(p1.x, p0.y), + LayerSize::new(p2.x - p1.x, p1.y - p0.y), + ), + BrushSegment::new( + LayerPoint::new(p2.x, p1.y), + LayerSize::new(p3.x - p2.x, p2.y - p1.y), + ), + BrushSegment::new( + LayerPoint::new(p1.x, p2.y), + LayerSize::new(p2.x - p1.x, p3.y - p2.y), + ), + BrushSegment::new( + LayerPoint::new(p0.x, p1.y), + LayerSize::new(p1.x - p0.x, p2.y - p1.y), + ), + BrushSegment::new( + LayerPoint::new(p1.x, p1.y), + LayerSize::new(p2.x - p1.x, p2.y - p1.y), + ), + ], + } } } #[derive(Debug)] pub struct BrushPrimitive { pub kind: BrushKind, + pub segment_desc: Option>, + pub aa_mode: BrushAntiAliasMode, +} + +impl BrushPrimitive { + pub fn new( + kind: BrushKind, + segment_desc: Option>, + aa_mode: BrushAntiAliasMode, + ) -> BrushPrimitive { + BrushPrimitive { + kind, + segment_desc, + aa_mode, + } + } } impl ToGpuBlocks for BrushPrimitive { fn write_gpu_blocks(&self, mut request: GpuDataRequest) { + match self.segment_desc { + Some(ref segment_desc) => { + request.push([ + segment_desc.top_left_offset.x, + segment_desc.top_left_offset.y, + segment_desc.bottom_right_offset.x, + segment_desc.bottom_right_offset.y, + ]); + } + None => { + request.push([0.0; 4]); + } + } + request.push([ + self.aa_mode as u32 as f32, + 0.0, + 0.0, + 0.0, + ]); match self.kind { + BrushKind::Solid { color } => { + request.push(color.premultiplied()); + } + BrushKind::Clear => { + // Opaque black with operator dest out + request.push(PremultipliedColorF::BLACK); + } BrushKind::Mask { clip_mode, kind: BrushMaskKind::Corner(radius) } => { request.push([ radius.width, @@ -863,7 +1006,6 @@ impl ClipData { #[derive(Debug)] pub enum PrimitiveContainer { - Rectangle(RectanglePrimitive), TextRun(TextRunPrimitiveCpu), Image(ImagePrimitiveCpu), YuvImage(YuvImagePrimitiveCpu), @@ -878,7 +1020,6 @@ pub enum PrimitiveContainer { pub struct PrimitiveStore { /// CPU side information only. - pub cpu_rectangles: Vec, pub cpu_brushes: Vec, pub cpu_text_runs: Vec, pub cpu_pictures: Vec, @@ -895,7 +1036,6 @@ impl PrimitiveStore { pub fn new() -> PrimitiveStore { PrimitiveStore { cpu_metadata: Vec::new(), - cpu_rectangles: Vec::new(), cpu_brushes: Vec::new(), cpu_text_runs: Vec::new(), cpu_pictures: Vec::new(), @@ -911,7 +1051,6 @@ impl PrimitiveStore { pub fn recycle(self) -> Self { PrimitiveStore { cpu_metadata: recycle_vec(self.cpu_metadata), - cpu_rectangles: recycle_vec(self.cpu_rectangles), cpu_brushes: recycle_vec(self.cpu_brushes), cpu_text_runs: recycle_vec(self.cpu_text_runs), cpu_pictures: recycle_vec(self.cpu_pictures), @@ -945,32 +1084,20 @@ impl PrimitiveStore { screen_rect: None, tag, opacity: PrimitiveOpacity::translucent(), - prim_kind: PrimitiveKind::Rectangle, + prim_kind: PrimitiveKind::Brush, cpu_prim_index: SpecificPrimitiveIndex(0), }; let metadata = match container { - PrimitiveContainer::Rectangle(rect) => { - let opacity = match &rect.content { - &RectangleContent::Fill(ref color) => { - PrimitiveOpacity::from_alpha(color.a) - }, - &RectangleContent::Clear => PrimitiveOpacity::opaque() - }; - let metadata = PrimitiveMetadata { - opacity, - prim_kind: PrimitiveKind::Rectangle, - cpu_prim_index: SpecificPrimitiveIndex(self.cpu_rectangles.len()), - ..base_metadata + PrimitiveContainer::Brush(brush) => { + let opacity = match brush.kind { + BrushKind::Clear => PrimitiveOpacity::translucent(), + BrushKind::Solid { ref color } => PrimitiveOpacity::from_alpha(color.a), + BrushKind::Mask { .. } => PrimitiveOpacity::translucent(), }; - self.cpu_rectangles.push(rect); - - metadata - } - PrimitiveContainer::Brush(brush) => { let metadata = PrimitiveMetadata { - opacity: PrimitiveOpacity::translucent(), + opacity, prim_kind: PrimitiveKind::Brush, cpu_prim_index: SpecificPrimitiveIndex(self.cpu_brushes.len()), ..base_metadata @@ -1109,7 +1236,7 @@ impl PrimitiveStore { ) { let metadata = &mut self.cpu_metadata[prim_index.0]; match metadata.prim_kind { - PrimitiveKind::Rectangle | PrimitiveKind::Border | PrimitiveKind::Line => {} + PrimitiveKind::Border | PrimitiveKind::Line => {} PrimitiveKind::Picture => { self.cpu_pictures[metadata.cpu_prim_index.0] .prepare_for_render( @@ -1169,10 +1296,10 @@ impl PrimitiveStore { ); } } + PrimitiveKind::Brush | PrimitiveKind::AlignedGradient | PrimitiveKind::AngleGradient | - PrimitiveKind::RadialGradient | - PrimitiveKind::Brush => {} + PrimitiveKind::RadialGradient => {} } // Mark this GPU resource as required for this frame. @@ -1181,10 +1308,6 @@ impl PrimitiveStore { request.push(metadata.local_clip_rect); match metadata.prim_kind { - PrimitiveKind::Rectangle => { - let rect = &self.cpu_rectangles[metadata.cpu_prim_index.0]; - rect.write_gpu_blocks(request); - } PrimitiveKind::Line => { let line = &self.cpu_lines[metadata.cpu_prim_index.0]; line.write_gpu_blocks(request); @@ -1218,8 +1341,23 @@ impl PrimitiveStore { text.write_gpu_blocks(&mut request); } PrimitiveKind::Picture => { + // TODO(gw): This is a bit of a hack. The Picture type + // is drawn by the brush_image shader, so the + // layout here needs to conform to the same + // BrushPrimitive layout. We should tidy this + // up in the future so it's enforced that these + // types use a shared function to write out the + // GPU blocks... + request.push([0.0; 4]); + request.push([ + BrushAntiAliasMode::Primitive as u32 as f32, + 0.0, + 0.0, + 0.0, + ]); + self.cpu_pictures[metadata.cpu_prim_index.0] - .write_gpu_blocks(request); + .write_gpu_blocks(&mut request); } PrimitiveKind::Brush => { let brush = &self.cpu_brushes[metadata.cpu_prim_index.0]; @@ -1240,6 +1378,7 @@ impl PrimitiveStore { render_tasks: &mut RenderTaskTree, clip_store: &mut ClipStore, tasks: &mut Vec, + node_data: &[ClipScrollNodeData], ) -> bool { let metadata = &mut self.cpu_metadata[prim_index.0]; metadata.clip_task_id = None; @@ -1344,17 +1483,112 @@ impl PrimitiveStore { return true; } - let clip_task = RenderTask::new_mask( - None, - combined_outer_rect, - combined_inner_rect, - clips, - clip_store, - transform.transform_kind() == TransformedRectKind::AxisAligned, - prim_coordinate_system_id, - ); + let mut needs_prim_clip_task = true; + + if metadata.prim_kind == PrimitiveKind::Brush { + let brush = &mut self.cpu_brushes[metadata.cpu_prim_index.0]; + if brush.segment_desc.is_none() && metadata.local_rect.size.area() > MIN_BRUSH_SPLIT_AREA { + if let BrushKind::Solid { .. } = brush.kind { + if clips.len() == 1 { + let clip_item = clips.first().unwrap(); + if clip_item.coordinate_system_id == prim_coordinate_system_id { + let local_clips = clip_store.get_opt(&clip_item.clip_sources).expect("bug"); + let mut selected_clip = None; + for &(ref clip, _) in &local_clips.clips { + match *clip { + ClipSource::RoundedRectangle(rect, radii, ClipMode::Clip) => { + if selected_clip.is_some() { + selected_clip = None; + break; + } + selected_clip = Some((rect, radii, clip_item.scroll_node_data_index)); + } + ClipSource::Rectangle(..) => {} + ClipSource::RoundedRectangle(_, _, ClipMode::ClipOut) | + ClipSource::BorderCorner(..) | + ClipSource::Image(..) => { + selected_clip = None; + break; + } + } + } + if let Some((rect, radii, clip_scroll_node_data_index)) = selected_clip { + // If the scroll node transforms are different between the clip + // node and the primitive, we need to get the clip rect in the + // local space of the primitive, in order to generate correct + // local segments. + let local_clip_rect = if clip_scroll_node_data_index == prim_context.scroll_node.node_data_index { + rect + } else { + let clip_transform_data = &node_data[clip_scroll_node_data_index.0 as usize]; + let prim_transform = &prim_context.scroll_node.world_content_transform; + + let relative_transform = prim_transform + .inverse() + .unwrap_or(WorldToLayerTransform::identity()) + .pre_mul(&clip_transform_data.transform); + + relative_transform.transform_rect(&rect) + }; + brush.segment_desc = create_nine_patch( + &metadata.local_rect, + &local_clip_rect, + &radii + ); + } + } + } + } + } + + if let Some(ref mut segment_desc) = brush.segment_desc { + let enabled_segments = segment_desc.enabled_segments; + let can_optimize_clip_mask = segment_desc.can_optimize_clip_mask; + + for (i, segment) in segment_desc.segments.iter_mut().enumerate() { + // We only build clips for the corners. The ordering of the + // BrushSegmentKind enum is such that corners come first, then + // edges, then inner. + let segment_enabled = ((1 << i) & enabled_segments) != 0; + let create_clip_task = segment_enabled && + (!can_optimize_clip_mask || i <= BrushSegmentKind::BottomLeft as usize); + segment.clip_task_id = if create_clip_task { + let segment_rect = TransformedRect::new( + &segment.local_rect, + &prim_context.scroll_node.world_content_transform, + prim_context.device_pixel_ratio + ); + + combined_outer_rect.intersection(&segment_rect.bounding_rect).map(|bounds| { + let clip_task = RenderTask::new_mask( + None, + bounds, + clips.clone(), + prim_coordinate_system_id, + ); + + let clip_task_id = render_tasks.add(clip_task); + tasks.push(clip_task_id); + + clip_task_id + }) + } else { + None + }; + } + + needs_prim_clip_task = false; + } + } + + if needs_prim_clip_task { + let clip_task = RenderTask::new_mask( + None, + combined_outer_rect, + clips, + prim_coordinate_system_id, + ); - if let Some(clip_task) = clip_task { let clip_task_id = render_tasks.add(clip_task); metadata.clip_task_id = Some(clip_task_id); tasks.push(clip_task_id); @@ -1379,6 +1613,7 @@ impl PrimitiveStore { profile_counters: &mut FrameProfileCounters, pic_index: SpecificPrimitiveIndex, screen_rect: &DeviceIntRect, + node_data: &[ClipScrollNodeData], ) -> Option { // Reset the visibility of this primitive. // Do some basic checks first, that can early out @@ -1445,6 +1680,7 @@ impl PrimitiveStore { scene_properties, cpu_prim_index, screen_rect, + node_data, ); let metadata = &mut self.cpu_metadata[prim_index.0]; @@ -1500,6 +1736,7 @@ impl PrimitiveStore { render_tasks, clip_store, parent_tasks, + node_data, ) { return None; } @@ -1544,6 +1781,7 @@ impl PrimitiveStore { scene_properties: &SceneProperties, pic_index: SpecificPrimitiveIndex, screen_rect: &DeviceIntRect, + node_data: &[ClipScrollNodeData], ) -> PrimitiveRunLocalRect { let mut result = PrimitiveRunLocalRect { local_rect_in_actual_parent_space: LayerRect::zero(), @@ -1611,6 +1849,7 @@ impl PrimitiveStore { profile_counters, pic_index, screen_rect, + node_data, ) { profile_counters.visible_primitives.inc(); @@ -1683,3 +1922,20 @@ fn get_local_bounding_rect( LayerRect::new(LayerPoint::new(x0, y0), LayerSize::new(x1 - x0, y1 - y0)) } + +fn create_nine_patch( + local_rect: &LayerRect, + local_clip_rect: &LayerRect, + radii: &BorderRadius +) -> Option> { + extract_inner_rect_safe(local_clip_rect, radii).map(|inner| { + let mut desc = BrushSegmentDescriptor::new( + local_rect, + &inner, + None, + ); + desc.can_optimize_clip_mask = true; + + Box::new(desc) + }) +} diff --git a/webrender/src/render_task.rs b/webrender/src/render_task.rs index 760c2df3ae..df3d624373 100644 --- a/webrender/src/render_task.rs +++ b/webrender/src/render_task.rs @@ -5,7 +5,7 @@ use api::{ClipId, DeviceIntPoint, DeviceIntRect, DeviceIntSize, DevicePixel}; use api::{LayerPoint, LayerRect, PremultipliedColorF}; use box_shadow::BoxShadowCacheKey; -use clip::{ClipSource, ClipSourcesWeakHandle, ClipStore}; +use clip::{ClipSourcesWeakHandle}; use clip_scroll_tree::CoordinateSystemId; use euclid::TypedSize2D; use gpu_types::{ClipScrollNodeIndex}; @@ -164,25 +164,6 @@ pub enum RenderTaskLocation { Dynamic(Option<(DeviceIntPoint, RenderTargetIndex)>, DeviceIntSize), } -#[derive(Debug, Copy, Clone)] -#[repr(C)] -pub enum MaskSegment { - // This must match the SEGMENT_ values in clip_shared.glsl! - All = 0, - TopLeftCorner, - TopRightCorner, - BottomLeftCorner, - BottomRightCorner, -} - -#[derive(Debug, Copy, Clone)] -#[repr(C)] -pub enum MaskGeometryKind { - Default, // Draw the entire rect - CornersOnly, // Draw the corners (simple axis aligned mask) - // TODO(gw): Add more types here (e.g. 4 rectangles outside the inner rect) -} - #[derive(Debug, Clone)] pub struct ClipWorkItem { pub scroll_node_data_index: ClipScrollNodeIndex, @@ -190,52 +171,10 @@ pub struct ClipWorkItem { pub coordinate_system_id: CoordinateSystemId, } -impl ClipWorkItem { - fn get_geometry_kind( - &self, - clip_store: &ClipStore, - prim_coordinate_system_id: CoordinateSystemId - ) -> MaskGeometryKind { - let clips = clip_store - .get_opt(&self.clip_sources) - .expect("bug: clip handle should be valid") - .clips(); - let mut rounded_rect_count = 0; - - for &(ref clip, _) in clips { - match *clip { - ClipSource::Rectangle(..) => { - if !self.has_compatible_coordinate_system(prim_coordinate_system_id) { - return MaskGeometryKind::Default; - } - }, - ClipSource::RoundedRectangle(..) => { - rounded_rect_count += 1; - } - ClipSource::Image(..) | ClipSource::BorderCorner(..) => { - return MaskGeometryKind::Default; - } - } - } - - if rounded_rect_count == 1 { - MaskGeometryKind::CornersOnly - } else { - MaskGeometryKind::Default - } - } - - fn has_compatible_coordinate_system(&self, other_id: CoordinateSystemId) -> bool { - self.coordinate_system_id == other_id - } -} - #[derive(Debug)] pub struct CacheMaskTask { actual_rect: DeviceIntRect, - inner_rect: DeviceIntRect, pub clips: Vec, - pub geometry_kind: MaskGeometryKind, pub coordinate_system_id: CoordinateSystemId, } @@ -353,38 +292,20 @@ impl RenderTask { pub fn new_mask( key: Option, outer_rect: DeviceIntRect, - inner_rect: DeviceIntRect, clips: Vec, - clip_store: &ClipStore, - is_axis_aligned: bool, prim_coordinate_system_id: CoordinateSystemId, - ) -> Option { - // TODO(gw): This optimization is very conservative for now. - // For now, only draw optimized geometry if it is - // a single aligned rect mask with rounded corners. - // In the future, we'll expand this to handle the - // more complex types of clip mask geometry. - let geometry_kind = if is_axis_aligned && - clips.len() == 1 && - inner_rect.size != DeviceIntSize::zero() { - clips[0].get_geometry_kind(clip_store, prim_coordinate_system_id) - } else { - MaskGeometryKind::Default - }; - - Some(RenderTask { + ) -> RenderTask { + RenderTask { cache_key: key.map(RenderTaskKey::CacheMask), children: Vec::new(), location: RenderTaskLocation::Dynamic(None, outer_rect.size), kind: RenderTaskKind::CacheMask(CacheMaskTask { actual_rect: outer_rect, - inner_rect: inner_rect, clips, - geometry_kind, coordinate_system_id: prim_coordinate_system_id, }), clear_mode: ClearMode::One, - }) + } } // Construct a render task to apply a blur to a primitive. @@ -529,12 +450,7 @@ impl RenderTask { task.actual_rect.origin.y as f32, 0.0, ], - [ - task.inner_rect.origin.x as f32, - task.inner_rect.origin.y as f32, - (task.inner_rect.origin.x + task.inner_rect.size.width) as f32, - (task.inner_rect.origin.y + task.inner_rect.size.height) as f32, - ], + [0.0; 4], ) } RenderTaskKind::VerticalBlur(ref task) | @@ -673,7 +589,6 @@ impl RenderTask { RenderTaskKind::CacheMask(ref task) => { pt.new_level(format!("CacheMask with {} clips", task.clips.len())); pt.add_item(format!("rect: {:?}", task.actual_rect)); - pt.add_item(format!("geometry: {:?}", task.geometry_kind)); } RenderTaskKind::VerticalBlur(ref task) => { pt.new_level("VerticalBlur".to_owned()); diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index 27f24d5fc0..a64e739a5a 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -72,6 +72,10 @@ use util::TransformedRectKind; pub const MAX_VERTEX_TEXTURE_WIDTH: usize = 1024; +const GPU_TAG_BRUSH_SOLID: GpuProfileTag = GpuProfileTag { + label: "B_Solid", + color: debug_colors::RED, +}; const GPU_TAG_BRUSH_MASK: GpuProfileTag = GpuProfileTag { label: "B_Mask", color: debug_colors::BLACK, @@ -100,10 +104,6 @@ const GPU_TAG_SETUP_DATA: GpuProfileTag = GpuProfileTag { label: "data init", color: debug_colors::LIGHTGREY, }; -const GPU_TAG_PRIM_RECT: GpuProfileTag = GpuProfileTag { - label: "Rect", - color: debug_colors::RED, -}; const GPU_TAG_PRIM_LINE: GpuProfileTag = GpuProfileTag { label: "Line", color: debug_colors::DARKRED, @@ -178,7 +178,6 @@ impl TransformBatchKind { #[cfg(feature = "debugger")] fn debug_name(&self) -> &'static str { match *self { - TransformBatchKind::Rectangle(..) => "Rectangle", TransformBatchKind::TextRun(..) => "TextRun", TransformBatchKind::Image(image_buffer_kind, ..) => match image_buffer_kind { ImageBufferKind::Texture2D => "Image (2D)", @@ -198,7 +197,6 @@ impl TransformBatchKind { fn gpu_sampler_tag(&self) -> GpuProfileTag { match *self { - TransformBatchKind::Rectangle(_) => GPU_TAG_PRIM_RECT, TransformBatchKind::Line => GPU_TAG_PRIM_LINE, TransformBatchKind::TextRun(..) => GPU_TAG_PRIM_TEXT_RUN, TransformBatchKind::Image(..) => GPU_TAG_PRIM_IMAGE, @@ -220,7 +218,12 @@ impl BatchKind { BatchKind::HardwareComposite => "HardwareComposite", BatchKind::SplitComposite => "SplitComposite", BatchKind::Blend => "Blend", - BatchKind::Brush(BrushBatchKind::Image(..)) => "Brush (Image)", + BatchKind::Brush(kind) => { + match kind { + BrushBatchKind::Image(..) => "Brush (Image)", + BrushBatchKind::Solid => "Brush (Solid)", + } + } BatchKind::Transformable(_, batch_kind) => batch_kind.debug_name(), } } @@ -231,7 +234,12 @@ impl BatchKind { BatchKind::HardwareComposite => GPU_TAG_PRIM_HW_COMPOSITE, BatchKind::SplitComposite => GPU_TAG_PRIM_SPLIT_COMPOSITE, BatchKind::Blend => GPU_TAG_PRIM_BLEND, - BatchKind::Brush(BrushBatchKind::Image(_)) => GPU_TAG_BRUSH_IMAGE, + BatchKind::Brush(kind) => { + match kind { + BrushBatchKind::Image(..) => GPU_TAG_BRUSH_IMAGE, + BrushBatchKind::Solid => GPU_TAG_BRUSH_SOLID, + } + } BatchKind::Transformable(_, batch_kind) => batch_kind.gpu_sampler_tag(), } } @@ -912,7 +920,6 @@ impl VertexDataTexture { } const TRANSFORM_FEATURE: &str = "TRANSFORM"; -const CLIP_FEATURE: &str = "CLIP"; const ALPHA_FEATURE: &str = "ALPHA_PASS"; enum ShaderKind { @@ -1355,6 +1362,7 @@ pub struct Renderer { brush_mask_rounded_rect: LazilyCompiledShader, brush_image_rgba8: BrushShader, brush_image_a8: BrushShader, + brush_solid: BrushShader, /// These are "cache clip shaders". These shaders are used to /// draw clip instances into the cached clip mask. The results @@ -1370,8 +1378,6 @@ pub struct Renderer { // shadow primitive shader stretches the box shadow cache // output, and the cache_image shader blits the results of // a cache shader (e.g. blur) to the screen. - ps_rectangle: PrimitiveShader, - ps_rectangle_clip: PrimitiveShader, ps_text_run: TextShader, ps_text_run_subpx_bg_pass1: TextShader, ps_image: Vec>, @@ -1558,6 +1564,13 @@ impl Renderer { options.precache_shaders) }; + let brush_solid = try!{ + BrushShader::new("brush_solid", + &mut device, + &[], + options.precache_shaders) + }; + let brush_image_a8 = try!{ BrushShader::new("brush_image", &mut device, @@ -1612,20 +1625,6 @@ impl Renderer { options.precache_shaders) }; - let ps_rectangle = try!{ - PrimitiveShader::new("ps_rectangle", - &mut device, - &[], - options.precache_shaders) - }; - - let ps_rectangle_clip = try!{ - PrimitiveShader::new("ps_rectangle", - &mut device, - &[ CLIP_FEATURE ], - options.precache_shaders) - }; - let ps_line = try!{ PrimitiveShader::new("ps_line", &mut device, @@ -2011,11 +2010,10 @@ impl Renderer { brush_mask_rounded_rect, brush_image_rgba8, brush_image_a8, + brush_solid, cs_clip_rectangle, cs_clip_border, cs_clip_image, - ps_rectangle, - ps_rectangle_clip, ps_text_run, ps_text_run_subpx_bg_pass1, ps_image, @@ -2787,6 +2785,15 @@ impl Renderer { } BatchKind::Brush(brush_kind) => { match brush_kind { + BrushBatchKind::Solid => { + self.brush_solid.bind( + &mut self.device, + key.blend_mode, + projection, + 0, + &mut self.renderer_errors, + ); + } BrushBatchKind::Image(target_kind) => { let shader = match target_kind { RenderTargetKind::Alpha => &mut self.brush_image_a8, @@ -2803,36 +2810,6 @@ impl Renderer { } } BatchKind::Transformable(transform_kind, batch_kind) => match batch_kind { - TransformBatchKind::Rectangle(needs_clipping) => { - debug_assert!( - !needs_clipping || match key.blend_mode { - BlendMode::PremultipliedAlpha | - BlendMode::PremultipliedDestOut | - BlendMode::SubpixelConstantTextColor(..) | - BlendMode::SubpixelVariableTextColor | - BlendMode::SubpixelWithBgColor => true, - BlendMode::None => false, - } - ); - - if needs_clipping { - self.ps_rectangle_clip.bind( - &mut self.device, - transform_kind, - projection, - 0, - &mut self.renderer_errors, - ); - } else { - self.ps_rectangle.bind( - &mut self.device, - transform_kind, - projection, - 0, - &mut self.renderer_errors, - ); - } - } TransformBatchKind::Line => { self.ps_line.bind( &mut self.device, @@ -4179,11 +4156,10 @@ impl Renderer { self.brush_mask_corner.deinit(&mut self.device); self.brush_image_rgba8.deinit(&mut self.device); self.brush_image_a8.deinit(&mut self.device); + self.brush_solid.deinit(&mut self.device); self.cs_clip_rectangle.deinit(&mut self.device); self.cs_clip_image.deinit(&mut self.device); self.cs_clip_border.deinit(&mut self.device); - self.ps_rectangle.deinit(&mut self.device); - self.ps_rectangle_clip.deinit(&mut self.device); self.ps_text_run.deinit(&mut self.device); self.ps_text_run_subpx_bg_pass1.deinit(&mut self.device); for shader in self.ps_image { diff --git a/webrender/src/tiling.rs b/webrender/src/tiling.rs index 9670af6618..9120164dd3 100644 --- a/webrender/src/tiling.rs +++ b/webrender/src/tiling.rs @@ -20,13 +20,13 @@ use gpu_types::{BlurDirection, BlurInstance, BrushInstance, BrushImageKind, Clip use gpu_types::{CompositePrimitiveInstance, PrimitiveInstance, SimplePrimitiveInstance}; use gpu_types::{ClipScrollNodeIndex, ClipScrollNodeData}; use internal_types::{FastHashMap, SourceTexture}; -use internal_types::BatchTextures; +use internal_types::{BatchTextures}; use picture::{PictureCompositeMode, PictureKind, PicturePrimitive, RasterizationSpace}; use plane_split::{BspSplitter, Polygon, Splitter}; use prim_store::{PrimitiveIndex, PrimitiveKind, PrimitiveMetadata, PrimitiveStore}; -use prim_store::{BrushMaskKind, BrushKind, DeferredResolve, PrimitiveRun, RectangleContent}; +use prim_store::{BrushPrimitive, BrushMaskKind, BrushKind, BrushSegmentKind, DeferredResolve, PrimitiveRun}; use profiler::FrameProfileCounters; -use render_task::{ClipWorkItem, MaskGeometryKind, MaskSegment}; +use render_task::{ClipWorkItem}; use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskKey, RenderTaskKind}; use render_task::{BlurTask, ClearMode, RenderTaskLocation, RenderTaskTree}; use renderer::BlendMode; @@ -121,23 +121,6 @@ impl AlphaBatchHelpers for PrimitiveStore { FontRenderMode::Bitmap => BlendMode::PremultipliedAlpha, } }, - PrimitiveKind::Rectangle => { - let rectangle_cpu = &self.cpu_rectangles[metadata.cpu_prim_index.0]; - match rectangle_cpu.content { - RectangleContent::Fill(..) => if needs_blending { - BlendMode::PremultipliedAlpha - } else { - BlendMode::None - }, - RectangleContent::Clear => { - // TODO: If needs_blending == false, we could use BlendMode::None - // to clear the rectangle, but then we'd need to draw the rectangle - // with alpha == 0.0 instead of alpha == 1.0, and the RectanglePrimitive - // would need to know about that. - BlendMode::PremultipliedDestOut - }, - } - }, PrimitiveKind::Border | PrimitiveKind::Image | PrimitiveKind::YuvImage | @@ -410,7 +393,64 @@ fn add_to_batch( match prim_metadata.prim_kind { PrimitiveKind::Brush => { - panic!("BUG: brush type not expected in an alpha task (yet)"); + let brush = &ctx.prim_store.cpu_brushes[prim_metadata.cpu_prim_index.0]; + let base_instance = BrushInstance { + picture_address: task_address, + prim_address: prim_cache_address, + clip_id, + scroll_id, + clip_task_address, + z, + segment_kind: 0, + user_data0: 0, + user_data1: 0, + }; + + match brush.segment_desc { + Some(ref segment_desc) => { + let opaque_batch = batch_list.opaque_batch_list.get_suitable_batch( + brush.get_batch_key( + BlendMode::None + ), + item_bounding_rect + ); + let alpha_batch = batch_list.alpha_batch_list.get_suitable_batch( + brush.get_batch_key( + BlendMode::PremultipliedAlpha + ), + item_bounding_rect + ); + + for (i, segment) in segment_desc.segments.iter().enumerate() { + if ((1 << i) & segment_desc.enabled_segments) != 0 { + let is_inner = i == BrushSegmentKind::Center as usize; + let needs_blending = !prim_metadata.opacity.is_opaque || + segment.clip_task_id.is_some() || + (!is_inner && transform_kind == TransformedRectKind::Complex); + + let clip_task_address = segment + .clip_task_id + .map_or(OPAQUE_TASK_ADDRESS, |id| render_tasks.get_task_address(id)); + + let instance = PrimitiveInstance::from(BrushInstance { + segment_kind: 1 + i as i32, + clip_task_address, + ..base_instance + }); + + if needs_blending { + alpha_batch.push(instance); + } else { + opaque_batch.push(instance); + } + } + } + } + None => { + let batch = batch_list.get_suitable_batch(brush.get_batch_key(blend_mode), item_bounding_rect); + batch.push(PrimitiveInstance::from(base_instance)); + } + } } PrimitiveKind::Border => { let border_cpu = @@ -464,16 +504,6 @@ fn add_to_batch( batch.push(base_instance.build(border_segment, 0, 0)); } } - PrimitiveKind::Rectangle => { - let needs_clipping = prim_metadata.clip_task_id.is_some(); - let kind = BatchKind::Transformable( - transform_kind, - TransformBatchKind::Rectangle(needs_clipping), - ); - let key = BatchKey::new(kind, blend_mode, no_textures); - let batch = batch_list.get_suitable_batch(key, item_bounding_rect); - batch.push(base_instance.build(0, 0, 0)); - } PrimitiveKind::Line => { let kind = BatchKind::Transformable(transform_kind, TransformBatchKind::Line); @@ -609,7 +639,7 @@ fn add_to_batch( scroll_id, clip_task_address, z, - flags: 0, + segment_kind: 0, user_data0: cache_task_address.0 as i32, user_data1: BrushImageKind::Simple as i32, }; @@ -638,7 +668,7 @@ fn add_to_batch( scroll_id, clip_task_address, z, - flags: 0, + segment_kind: 0, user_data0: cache_task_address.0 as i32, user_data1: image_kind as i32, }; @@ -1083,7 +1113,6 @@ impl ClipBatcher { coordinate_system_id: CoordinateSystemId, resource_cache: &ResourceCache, gpu_cache: &GpuCache, - geometry_kind: MaskGeometryKind, clip_store: &ClipStore, ) { let mut coordinate_system_id = coordinate_system_id; @@ -1122,43 +1151,17 @@ impl ClipBatcher { if work_item.coordinate_system_id != coordinate_system_id { self.rectangles.push(ClipMaskInstance { clip_data_address: gpu_address, - segment: MaskSegment::All as i32, ..instance }); coordinate_system_id = work_item.coordinate_system_id; } - }, - ClipSource::RoundedRectangle(..) => match geometry_kind { - MaskGeometryKind::Default => { - self.rectangles.push(ClipMaskInstance { - clip_data_address: gpu_address, - segment: MaskSegment::All as i32, - ..instance - }); - } - MaskGeometryKind::CornersOnly => { - self.rectangles.push(ClipMaskInstance { - clip_data_address: gpu_address, - segment: MaskSegment::TopLeftCorner as i32, - ..instance - }); - self.rectangles.push(ClipMaskInstance { - clip_data_address: gpu_address, - segment: MaskSegment::TopRightCorner as i32, - ..instance - }); - self.rectangles.push(ClipMaskInstance { - clip_data_address: gpu_address, - segment: MaskSegment::BottomLeftCorner as i32, - ..instance - }); - self.rectangles.push(ClipMaskInstance { - clip_data_address: gpu_address, - segment: MaskSegment::BottomRightCorner as i32, - ..instance - }); - } - }, + } + ClipSource::RoundedRectangle(..) => { + self.rectangles.push(ClipMaskInstance { + clip_data_address: gpu_address, + ..instance + }); + } ClipSource::BorderCorner(ref source) => { self.border_clears.push(ClipMaskInstance { clip_data_address: gpu_address, @@ -1666,12 +1669,16 @@ impl RenderTarget for AlphaRenderTarget { scroll_id: ClipScrollNodeIndex(0), clip_task_address: RenderTaskAddress(0), z: 0, - flags: 0, + segment_kind: 0, user_data0: 0, user_data1: 0, }; let brush = &ctx.prim_store.cpu_brushes[sub_metadata.cpu_prim_index.0]; let batch = match brush.kind { + BrushKind::Solid { .. } | + BrushKind::Clear => { + unreachable!("bug: unexpected brush here"); + } BrushKind::Mask { ref kind, .. } => { match *kind { BrushMaskKind::Corner(..) => &mut self.brush_mask_corners, @@ -1702,7 +1709,6 @@ impl RenderTarget for AlphaRenderTarget { task_info.coordinate_system_id, &ctx.resource_cache, gpu_cache, - task_info.geometry_kind, clip_store, ); } @@ -1863,7 +1869,6 @@ impl RenderPass { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub enum TransformBatchKind { - Rectangle(bool), TextRun(GlyphFormat), Image(ImageBufferKind), YuvImage(ImageBufferKind, YuvFormat, YuvColorSpace), @@ -1877,7 +1882,8 @@ pub enum TransformBatchKind { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub enum BrushBatchKind { - Image(RenderTargetKind) + Image(RenderTargetKind), + Solid, } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] @@ -2097,3 +2103,27 @@ fn make_polygon( transform.m44 as f64); Polygon::from_transformed_rect(rect.cast().unwrap(), mat, anchor) } + +impl BrushPrimitive { + fn get_batch_key(&self, blend_mode: BlendMode) -> BatchKey { + match self.kind { + BrushKind::Solid { .. } => { + BatchKey::new( + BatchKind::Brush(BrushBatchKind::Solid), + blend_mode, + BatchTextures::no_texture(), + ) + } + BrushKind::Clear => { + BatchKey::new( + BatchKind::Brush(BrushBatchKind::Solid), + BlendMode::PremultipliedDestOut, + BatchTextures::no_texture(), + ) + } + BrushKind::Mask { .. } => { + unreachable!("bug: mask brushes not expected in normal alpha pass"); + } + } + } +} diff --git a/webrender/src/util.rs b/webrender/src/util.rs index 624a0e4130..1441f2acf3 100644 --- a/webrender/src/util.rs +++ b/webrender/src/util.rs @@ -4,7 +4,7 @@ use api::{BorderRadius, ComplexClipRegion, DeviceIntPoint, DeviceIntRect, DeviceIntSize}; use api::{DevicePoint, DeviceRect, DeviceSize, LayerPoint, LayerRect, LayerSize}; -use api::{LayerToWorldTransform, LayoutPoint, LayoutRect, LayoutSize, WorldPoint3D}; +use api::{LayerToWorldTransform, LayoutRect, WorldPoint3D}; use euclid::{Point2D, Rect, Size2D, TypedPoint2D, TypedRect, TypedSize2D, TypedTransform2D}; use euclid::TypedTransform3D; use num_traits::Zero; @@ -290,14 +290,6 @@ pub trait ComplexClipRegionHelpers { /// Return the approximately largest aligned rectangle that is fully inside /// the provided clip region. fn get_inner_rect_full(&self) -> Option; - /// Split the clip region into 2 sets of rectangles: opaque and transparent. - /// Guarantees no T-junctions in the produced split. - /// Attempts to cover more space in opaque, where it reasonably makes sense. - fn split_rectangles( - &self, - opaque: &mut Vec, - transparent: &mut Vec, - ); } impl ComplexClipRegionHelpers for ComplexClipRegion { @@ -306,63 +298,6 @@ impl ComplexClipRegionHelpers for ComplexClipRegion { let k = 1.0 - 0.5 * FRAC_1_SQRT_2; // could be nicely approximated to `0.3` extract_inner_rect_impl(&self.rect, &self.radii, k) } - - fn split_rectangles( - &self, - opaque: &mut Vec, - transparent: &mut Vec, - ) { - fn rect(p0: LayoutPoint, p1: LayoutPoint) -> Option { - if p0.x != p1.x && p0.y != p1.y { - Some(LayerRect::new(p0.min(p1), (p1 - p0).abs().to_size())) - } else { - None - } - } - - let inner = match extract_inner_rect_impl(&self.rect, &self.radii, 1.0) { - Some(rect) => rect, - None => { - transparent.push(self.rect); - return - }, - }; - let left_top = inner.origin - self.rect.origin; - let right_bot = self.rect.bottom_right() - inner.bottom_right(); - - // fill in the opaque parts - opaque.push(inner); - if left_top.x > 0.0 { - opaque.push(LayerRect::new( - LayoutPoint::new(self.rect.origin.x, inner.origin.y), - LayoutSize::new(left_top.x, inner.size.height), - )); - } - if right_bot.y > 0.0 { - opaque.push(LayerRect::new( - LayoutPoint::new(inner.origin.x, inner.origin.y + inner.size.height), - LayoutSize::new(inner.size.width, right_bot.y), - )); - } - if right_bot.x > 0.0 { - opaque.push(LayerRect::new( - LayoutPoint::new(inner.origin.x + inner.size.width, inner.origin.y), - LayoutSize::new(right_bot.x, inner.size.height), - )); - } - if left_top.y > 0.0 { - opaque.push(LayerRect::new( - LayoutPoint::new(inner.origin.x, self.rect.origin.y), - LayoutSize::new(inner.size.width, left_top.y), - )); - } - - // fill in the transparent parts - transparent.extend(rect(self.rect.origin, inner.origin)); - transparent.extend(rect(self.rect.bottom_left(), inner.bottom_left())); - transparent.extend(rect(self.rect.bottom_right(), inner.bottom_right())); - transparent.extend(rect(self.rect.top_right(), inner.top_right())); - } } #[inline] diff --git a/webrender/tests/angle_shader_validation.rs b/webrender/tests/angle_shader_validation.rs index a1459e0e4c..274b50cb54 100644 --- a/webrender/tests/angle_shader_validation.rs +++ b/webrender/tests/angle_shader_validation.rs @@ -94,15 +94,15 @@ const SHADERS: &[Shader] = &[ name: "ps_text_run", features: PRIM_FEATURES, }, - Shader { - name: "ps_rectangle", - features: &["", "TRANSFORM", "CLIP_FEATURE", "TRANSFORM,CLIP_FEATURE"], - }, // Brush shaders Shader { name: "brush_mask", features: &[], }, + Shader { + name: "brush_solid", + features: &[], + }, Shader { name: "brush_image", features: &["COLOR_TARGET", "ALPHA_TARGET"], diff --git a/wrench/reftests/transforms/reftest.list b/wrench/reftests/transforms/reftest.list index 176db325d0..a986305e49 100644 --- a/wrench/reftests/transforms/reftest.list +++ b/wrench/reftests/transforms/reftest.list @@ -7,4 +7,4 @@ fuzzy(1,2) == rotated-image.yaml rotated-image.png == singular.yaml singular-ref.yaml fuzzy(1,41) == perspective.yaml perspective.png == prim-suite.yaml prim-suite.png - +== segments-bug.yaml segments-bug-ref.yaml diff --git a/wrench/reftests/transforms/rotated-image.png b/wrench/reftests/transforms/rotated-image.png index fc0958fd80..5aede3284d 100644 Binary files a/wrench/reftests/transforms/rotated-image.png and b/wrench/reftests/transforms/rotated-image.png differ diff --git a/wrench/reftests/transforms/segments-bug-ref.yaml b/wrench/reftests/transforms/segments-bug-ref.yaml new file mode 100644 index 0000000000..c5bc92096c --- /dev/null +++ b/wrench/reftests/transforms/segments-bug-ref.yaml @@ -0,0 +1,24 @@ +--- +root: + items: + - + type: "stacking-context" + items: + - + bounds: [12, 12, 130, 130] + type: clip + id: 4 + complex: + - + rect: [12, 12, 130, 130] + radius: 20 + "clip-mode": clip + - + "clip-and-scroll": 4 + type: "stacking-context" + items: + - + bounds: [12, 12, 130, 130] + "clip-and-scroll": 4 + type: rect + color: 0 128 0 1.0000 diff --git a/wrench/reftests/transforms/segments-bug.yaml b/wrench/reftests/transforms/segments-bug.yaml new file mode 100644 index 0000000000..abf2ff25cf --- /dev/null +++ b/wrench/reftests/transforms/segments-bug.yaml @@ -0,0 +1,28 @@ +# Test that opaque/alpha segments are correctly calculated +# when the clip is in a different (but compatible) coordinate +# space from the primitive. +--- +root: + items: + - + type: "stacking-context" + items: + - + bounds: [12, 12, 130, 130] + type: clip + id: 4 + complex: + - + rect: [12, 12, 130, 130] + radius: 20 + "clip-mode": clip + - + "clip-and-scroll": 4 + type: "stacking-context" + transform: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 12, 12, 0, 1] + items: + - + bounds: [0, 0, 130, 130] + "clip-and-scroll": 4 + type: rect + color: 0 128 0 1.0000