diff --git a/gfx/webrender_bindings/revision.txt b/gfx/webrender_bindings/revision.txt index ed91691bb8fc..37180c4984f7 100644 --- a/gfx/webrender_bindings/revision.txt +++ b/gfx/webrender_bindings/revision.txt @@ -1 +1 @@ -f450af9277e2474e2a2a2c1358689ca9486e2a09 +e2e52b1145ad959191c0612edd41b0b189cf6b59 diff --git a/gfx/wr/webrender/src/batch.rs b/gfx/wr/webrender/src/batch.rs index 0bcbd7768a0c..68cd33c19c44 100644 --- a/gfx/wr/webrender/src/batch.rs +++ b/gfx/wr/webrender/src/batch.rs @@ -17,7 +17,7 @@ use internal_types::{FastHashMap, SavedTargetIndex, TextureSource}; use picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive, PictureSurface}; use prim_store::{BrushKind, BrushPrimitive, DeferredResolve, PrimitiveTemplateKind}; use prim_store::{EdgeAaSegmentMask, ImageSource, PrimitiveInstanceKind, PrimitiveStore}; -use prim_store::{VisibleGradientTile, PrimitiveInstance, PrimitiveOpacity}; +use prim_store::{VisibleGradientTile, PrimitiveInstance, PrimitiveOpacity, SegmentInstanceIndex}; use prim_store::{BrushSegment, ClipMaskKind, ClipTaskIndex, PrimitiveDetails}; use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskTree}; use renderer::{BlendMode, ImageBufferKind, ShaderColorMode}; @@ -905,6 +905,7 @@ impl AlphaBatchBuilder { PrimitiveInstanceKind::LegacyPrimitive { .. } | PrimitiveInstanceKind::NormalBorder { .. } | PrimitiveInstanceKind::ImageBorder { .. } | + PrimitiveInstanceKind::Rectangle { .. } | PrimitiveInstanceKind::Clear => { unreachable!(); } @@ -1617,6 +1618,71 @@ impl AlphaBatchBuilder { ctx, ); } + ( + PrimitiveInstanceKind::Rectangle { segment_instance_index, opacity_binding_index, .. }, + PrimitiveTemplateKind::Rectangle { .. } + ) => { + let specified_blend_mode = BlendMode::PremultipliedAlpha; + let opacity_binding = ctx.prim_store.get_opacity_binding(*opacity_binding_index); + + let opacity = PrimitiveOpacity::from_alpha(opacity_binding); + let opacity = opacity.combine(prim_data.opacity); + + let non_segmented_blend_mode = if !opacity.is_opaque || + prim_instance.clip_task_index != ClipTaskIndex::INVALID || + transform_kind == TransformedRectKind::Complex + { + specified_blend_mode + } else { + BlendMode::None + }; + + let batch_params = BrushBatchParameters::shared( + BrushBatchKind::Solid, + BatchTextures::no_texture(), + [get_shader_opacity(opacity_binding), 0, 0], + 0, + ); + + let (prim_cache_address, segments) = if *segment_instance_index == SegmentInstanceIndex::UNUSED { + (gpu_cache.get_address(&prim_data.gpu_cache_handle), None) + } else { + let segment_instance = &ctx.scratch.segment_instances[*segment_instance_index]; + let segments = Some(&ctx.scratch.segments[segment_instance.segments_range]); + (gpu_cache.get_address(&segment_instance.gpu_cache_handle), segments) + }; + + let prim_header = PrimitiveHeader { + local_rect: prim_data.prim_rect, + local_clip_rect: prim_instance.combined_local_clip_rect, + task_address, + specific_prim_address: prim_cache_address, + clip_task_address, + transform_id, + }; + + let prim_header_index = prim_headers.push( + &prim_header, + z_id, + batch_params.prim_user_data, + ); + + self.add_segmented_prim_to_batch( + segments, + opacity, + &batch_params, + specified_blend_mode, + non_segmented_blend_mode, + prim_header_index, + clip_task_address, + bounding_rect, + transform_kind, + render_tasks, + z_id, + prim_instance.clip_task_index, + ctx, + ); + } _ => { unreachable!(); } @@ -1794,7 +1860,7 @@ impl AlphaBatchBuilder { textures: segment_data.textures, }; let instance = PrimitiveInstanceData::from(BrushInstance { - segment_index: 0, + segment_index: INVALID_SEGMENT_INDEX, edge_flags: EdgeAaSegmentMask::all(), clip_task_address, brush_flags: BrushFlags::PERSPECTIVE_INTERPOLATION, @@ -1999,15 +2065,6 @@ impl BrushPrimitive { )) } } - BrushKind::Solid { opacity_binding_index, .. } => { - let opacity_binding = prim_store.get_opacity_binding(opacity_binding_index); - Some(BrushBatchParameters::shared( - BrushBatchKind::Solid, - BatchTextures::no_texture(), - [get_shader_opacity(opacity_binding), 0, 0], - 0, - )) - } BrushKind::RadialGradient { ref stops_handle, .. } => { Some(BrushBatchParameters::shared( BrushBatchKind::RadialGradient, @@ -2106,7 +2163,6 @@ impl PrimitiveInstance { AlphaType::Alpha => BlendMode::Alpha, } } - BrushKind::Solid { .. } | BrushKind::YuvImage { .. } | BrushKind::RadialGradient { .. } | BrushKind::LinearGradient { .. } => { diff --git a/gfx/wr/webrender/src/box_shadow.rs b/gfx/wr/webrender/src/box_shadow.rs index b3f6ac70cc50..7e9170bb9a21 100644 --- a/gfx/wr/webrender/src/box_shadow.rs +++ b/gfx/wr/webrender/src/box_shadow.rs @@ -8,7 +8,7 @@ use clip::ClipItemKey; use display_list_flattener::DisplayListFlattener; use gpu_cache::GpuCacheHandle; use gpu_types::BoxShadowStretchMode; -use prim_store::{BrushKind, BrushPrimitive, PrimitiveContainer}; +use prim_store::PrimitiveContainer; use prim_store::ScrollNodeAndClipChain; use render_task::RenderTaskCacheEntryHandle; use util::RectHelpers; @@ -149,7 +149,9 @@ impl<'a> DisplayListFlattener<'a> { clip_and_scroll, &LayoutPrimitiveInfo::with_clip_rect(final_prim_rect, prim_info.clip_rect), clips, - PrimitiveContainer::Brush(BrushPrimitive::new(BrushKind::new_solid(*color), None)), + PrimitiveContainer::Rectangle { + color: *color, + }, ); } else { // Normal path for box-shadows with a valid blur radius. @@ -170,7 +172,9 @@ impl<'a> DisplayListFlattener<'a> { // Draw the box-shadow as a solid rect, using a box-shadow // clip mask item. - let prim = BrushPrimitive::new(BrushKind::new_solid(*color), None); + let prim = PrimitiveContainer::Rectangle { + color: *color, + }; // Create the box-shadow clip item. let shadow_clip_source = ClipItemKey::box_shadow( @@ -221,7 +225,7 @@ impl<'a> DisplayListFlattener<'a> { clip_and_scroll, &prim_info, extra_clips, - PrimitiveContainer::Brush(prim), + prim, ); } } diff --git a/gfx/wr/webrender/src/display_list_flattener.rs b/gfx/wr/webrender/src/display_list_flattener.rs index 657b73d78009..9c1412d1ca3a 100644 --- a/gfx/wr/webrender/src/display_list_flattener.rs +++ b/gfx/wr/webrender/src/display_list_flattener.rs @@ -840,7 +840,9 @@ impl<'a> DisplayListFlattener<'a> { // style PrimitiveDetails structure from the // source primitive container. let mut info = info.clone(); - let (prim_key_kind, prim_details) = container.build(&mut info); + let (prim_key_kind, prim_details) = container.build( + &mut info, + ); let prim_key = PrimitiveKey::new( info.is_backface_visible, @@ -1679,16 +1681,13 @@ impl<'a> DisplayListFlattener<'a> { return; } - let prim = BrushPrimitive::new( - BrushKind::new_solid(color), - None, - ); - self.add_primitive( clip_and_scroll, info, Vec::new(), - PrimitiveContainer::Brush(prim), + PrimitiveContainer::Rectangle { + color, + }, ); } diff --git a/gfx/wr/webrender/src/frame_builder.rs b/gfx/wr/webrender/src/frame_builder.rs index 2882aff86c44..f19ae7a103d0 100644 --- a/gfx/wr/webrender/src/frame_builder.rs +++ b/gfx/wr/webrender/src/frame_builder.rs @@ -85,7 +85,6 @@ pub struct FrameBuildingState<'a> { pub transforms: &'a mut TransformPalette, pub segment_builder: SegmentBuilder, pub surfaces: &'a mut Vec, - pub scratch: &'a mut PrimitiveScratchBuffer, } /// Immutable context of a picture when processing children. @@ -255,7 +254,6 @@ impl FrameBuilder { transforms: transform_palette, segment_builder: SegmentBuilder::new(), surfaces: pic_update_state.surfaces, - scratch, }; let (pic_context, mut pic_state, mut prim_list) = self @@ -279,6 +277,7 @@ impl FrameBuilder { &frame_context, &mut frame_state, resources, + scratch, ); let pic = &mut self.prim_store.pictures[self.root_pic_index.0]; diff --git a/gfx/wr/webrender/src/picture.rs b/gfx/wr/webrender/src/picture.rs index 46f6aeaed56a..d0cd30731e7e 100644 --- a/gfx/wr/webrender/src/picture.rs +++ b/gfx/wr/webrender/src/picture.rs @@ -414,14 +414,6 @@ impl TileCache { // TODO(gw): In future, we might be able to completely remove // opacity collapsing support. It's of limited use // once we have full picture caching. - BrushKind::Solid { opacity_binding_index, .. } => { - let opacity_binding = &opacity_binding_store[opacity_binding_index]; - for binding in &opacity_binding.bindings { - if let PropertyBinding::Binding(key, default) = binding { - opacity_bindings.push((key.id, *default)); - } - } - } BrushKind::Image { opacity_binding_index, ref request, .. } => { let opacity_binding = &opacity_binding_store[opacity_binding_index]; for binding in &opacity_binding.bindings { @@ -442,6 +434,14 @@ impl TileCache { } } } + PrimitiveInstanceKind::Rectangle { opacity_binding_index, .. } => { + let opacity_binding = &opacity_binding_store[opacity_binding_index]; + for binding in &opacity_binding.bindings { + if let PropertyBinding::Binding(key, default) = binding { + opacity_bindings.push((key.id, *default)); + } + } + } PrimitiveInstanceKind::TextRun { .. } | PrimitiveInstanceKind::LineDecoration { .. } | PrimitiveInstanceKind::Clear | diff --git a/gfx/wr/webrender/src/prim_store.rs b/gfx/wr/webrender/src/prim_store.rs index 482a625c3b0f..239800c724c2 100644 --- a/gfx/wr/webrender/src/prim_store.rs +++ b/gfx/wr/webrender/src/prim_store.rs @@ -12,7 +12,7 @@ use api::{PicturePixel, RasterPixel, ColorDepth, LineStyle, LineOrientation, Lay use app_units::Au; use border::{get_max_scale_for_border, build_border_instances, create_border_segments}; use border::{create_nine_patch_segments, BorderSegmentCacheKey, NormalBorderAu}; -use clip::ClipStore; +use clip::{ClipStore}; use clip_scroll_tree::{ClipScrollTree, SpatialNodeIndex}; use clip::{ClipDataStore, ClipNodeFlags, ClipChainId, ClipChainInstance, ClipItem, ClipNodeCollector}; use euclid::{SideOffsets2D, TypedTransform3D, TypedRect, TypedScale}; @@ -33,6 +33,7 @@ use render_task::{RenderTaskCacheKeyKind, RenderTaskId, RenderTaskCacheEntryHand use renderer::{MAX_VERTEX_TEXTURE_WIDTH}; use resource_cache::{ImageProperties, ImageRequest, ResourceCache}; use scene::SceneProperties; +use segment::SegmentBuilder; use std::{cmp, fmt, mem, ops, u32, usize}; #[cfg(debug_assertions)] use std::sync::atomic::{AtomicUsize, Ordering}; @@ -100,6 +101,12 @@ impl PrimitiveOpacity { is_opaque: alpha >= 1.0, } } + + pub fn combine(&self, other: PrimitiveOpacity) -> PrimitiveOpacity { + PrimitiveOpacity{ + is_opaque: self.is_opaque && other.is_opaque + } + } } #[derive(Debug, Copy, Clone)] @@ -377,6 +384,9 @@ pub enum PrimitiveKeyKind { repeat_vertical: RepeatMode, outset: SideOffsets2D, }, + Rectangle { + color: ColorU, + } } #[cfg_attr(feature = "capture", derive(Serialize))] @@ -439,6 +449,12 @@ impl PrimitiveKey { PrimitiveInstanceKind::ImageBorder { } } + PrimitiveKeyKind::Rectangle { .. } => { + PrimitiveInstanceKind::Rectangle { + opacity_binding_index: OpacityBindingIndex::INVALID, + segment_instance_index: SegmentInstanceIndex::INVALID, + } + } PrimitiveKeyKind::Unused => { // Should never be hit as this method should not be // called for old style primitives. @@ -478,6 +494,9 @@ pub enum PrimitiveTemplateKind { request: ImageRequest, brush_segments: Vec, }, + Rectangle { + color: ColorF, + }, Clear, Unused, } @@ -486,7 +505,10 @@ pub enum PrimitiveTemplateKind { /// is invoked when a primitive key is created and the interner /// doesn't currently contain a primitive with this key. impl PrimitiveKeyKind { - fn into_template(self, rect: &LayoutRect) -> PrimitiveTemplateKind { + fn into_template( + self, + rect: &LayoutRect, + ) -> PrimitiveTemplateKind { match self { PrimitiveKeyKind::Unused => PrimitiveTemplateKind::Unused, PrimitiveKeyKind::TextRun { glyphs, font, offset, .. } => { @@ -562,6 +584,11 @@ impl PrimitiveKeyKind { brush_segments, } } + PrimitiveKeyKind::Rectangle { color, .. } => { + PrimitiveTemplateKind::Rectangle { + color: color.into(), + } + } PrimitiveKeyKind::LineDecoration { cache_key, color } => { PrimitiveTemplateKind::LineDecoration { cache_key, @@ -627,6 +654,13 @@ impl PrimitiveTemplate { PrimitiveOpacity::translucent() } + PrimitiveTemplateKind::Rectangle { ref color, .. } => { + if let Some(mut request) = frame_state.gpu_cache.request(&mut self.gpu_cache_handle) { + request.push(color.premultiplied()); + } + + PrimitiveOpacity::from_alpha(color.a) + } PrimitiveTemplateKind::NormalBorder { ref template, .. } => { if let Some(mut request) = frame_state.gpu_cache.request(&mut self.gpu_cache_handle) { // Border primitives currently used for @@ -856,10 +890,6 @@ pub struct BorderSegmentInfo { } pub enum BrushKind { - Solid { - color: ColorF, - opacity_binding_index: OpacityBindingIndex, - }, Image { request: ImageRequest, alpha_type: AlphaType, @@ -915,21 +945,12 @@ impl BrushKind { .is_none() } - BrushKind::Solid { .. } | BrushKind::YuvImage { .. } | BrushKind::RadialGradient { .. } | BrushKind::LinearGradient { .. } => true, } } - // Construct a brush that is a solid color rectangle. - pub fn new_solid(color: ColorF) -> BrushKind { - BrushKind::Solid { - color, - opacity_binding_index: OpacityBindingIndex::INVALID, - } - } - // Construct a brush that is an image wisth `stretch_size` dimensions and // `color`. pub fn new_image( @@ -1117,10 +1138,6 @@ impl BrushPrimitive { 0.0, ]); } - // Solid rects also support opacity collapsing. - BrushKind::Solid { ref color, .. } => { - request.push(color.premultiplied()); - } BrushKind::LinearGradient { stretch_size, start_point, end_point, extend_mode, .. } => { request.push([ start_point.x, @@ -1725,6 +1742,9 @@ pub enum PrimitiveContainer { repeat_vertical: RepeatMode, outset: SideOffsets2D, }, + Rectangle { + color: ColorF, + }, } impl PrimitiveContainer { @@ -1742,9 +1762,6 @@ impl PrimitiveContainer { } PrimitiveContainer::Brush(ref brush) => { match brush.kind { - BrushKind::Solid { ref color, .. } => { - color.a > 0.0 - } BrushKind::Image { .. } | BrushKind::YuvImage { .. } | BrushKind::RadialGradient { .. } | @@ -1758,6 +1775,7 @@ impl PrimitiveContainer { PrimitiveContainer::Clear => { true } + PrimitiveContainer::Rectangle { ref color, .. } | PrimitiveContainer::LineDecoration { ref color, .. } => { color.a > 0.0 } @@ -1784,6 +1802,13 @@ impl PrimitiveContainer { PrimitiveContainer::Clear => { (PrimitiveKeyKind::Clear, None) } + PrimitiveContainer::Rectangle { color, .. } => { + let key = PrimitiveKeyKind::Rectangle { + color: color.into(), + }; + + (key, None) + } PrimitiveContainer::ImageBorder { request, widths, @@ -1920,6 +1945,11 @@ impl PrimitiveContainer { wavy_line_thickness, } } + PrimitiveContainer::Rectangle { .. } => { + PrimitiveContainer::Rectangle { + color: shadow.color, + } + } PrimitiveContainer::NormalBorder { border, widths, .. } => { let border = border.with_color(shadow.color); PrimitiveContainer::NormalBorder { @@ -1929,12 +1959,6 @@ impl PrimitiveContainer { } PrimitiveContainer::Brush(ref brush) => { match brush.kind { - BrushKind::Solid { .. } => { - PrimitiveContainer::Brush(BrushPrimitive::new( - BrushKind::new_solid(shadow.color), - None, - )) - } BrushKind::Image { request, stretch_size, .. } => { PrimitiveContainer::Brush(BrushPrimitive::new( BrushKind::new_image(request.clone(), @@ -2008,6 +2032,10 @@ pub enum PrimitiveInstanceKind { }, ImageBorder { }, + Rectangle { + opacity_binding_index: OpacityBindingIndex, + segment_instance_index: SegmentInstanceIndex, + }, /// Clear out a rect, used for special effects. Clear, } @@ -2091,12 +2119,22 @@ impl PrimitiveInstance { } } +#[derive(Debug)] +pub struct SegmentedInstance { + pub gpu_cache_handle: GpuCacheHandle, + pub segments_range: SegmentsRange, +} + pub type GlyphKeyStorage = storage::Storage; pub type TextRunIndex = storage::Index; pub type TextRunStorage = storage::Storage; pub type OpacityBindingIndex = storage::Index; pub type OpacityBindingStorage = storage::Storage; pub type BorderHandleStorage = storage::Storage; +pub type SegmentStorage = storage::Storage; +pub type SegmentsRange = storage::Range; +pub type SegmentInstanceStorage = storage::Storage; +pub type SegmentInstanceIndex = storage::Index; /// Contains various vecs of data that is used only during frame building, /// where we want to recycle the memory each new display list, to avoid constantly @@ -2114,6 +2152,14 @@ pub struct PrimitiveScratchBuffer { /// List of render task handles for border segment instances /// that have been added this frame. pub border_cache_handles: BorderHandleStorage, + + /// A list of brush segments that have been built for this scene. + pub segments: SegmentStorage, + + /// A list of segment ranges and GPU cache handles for prim instances + /// that have opted into segment building. In future, this should be + /// removed in favor of segment building during primitive interning. + pub segment_instances: SegmentInstanceStorage, } impl PrimitiveScratchBuffer { @@ -2122,6 +2168,8 @@ impl PrimitiveScratchBuffer { clip_mask_instances: Vec::new(), glyph_keys: GlyphKeyStorage::new(0), border_cache_handles: BorderHandleStorage::new(0), + segments: SegmentStorage::new(0), + segment_instances: SegmentInstanceStorage::new(0), } } @@ -2129,6 +2177,8 @@ impl PrimitiveScratchBuffer { recycle_vec(&mut self.clip_mask_instances); self.glyph_keys.recycle(); self.border_cache_handles.recycle(); + self.segments.recycle(); + self.segment_instances.recycle(); } pub fn begin_frame(&mut self) { @@ -2282,7 +2332,7 @@ impl PrimitiveStore { fn get_opacity_collapse_prim( &self, pic_index: PictureIndex, - ) -> Option { + ) -> Option { let pic = &self.pictures[pic_index.0]; // We can only collapse opacity if there is a single primitive, otherwise @@ -2298,6 +2348,9 @@ impl PrimitiveStore { // handled by this optimization. In the future, we can easily extend // this to other primitives, such as text runs and gradients. match prim_instance.kind { + PrimitiveInstanceKind::Rectangle { .. } => { + return Some(pic_index); + } PrimitiveInstanceKind::Clear | PrimitiveInstanceKind::TextRun { .. } | PrimitiveInstanceKind::NormalBorder { .. } | @@ -2324,9 +2377,8 @@ impl PrimitiveStore { match brush.kind { // If we find a single rect or image, we can use that // as the primitive to collapse the opacity into. - BrushKind::Solid { .. } | BrushKind::Image { .. } => { - return Some(prim_index) + return Some(pic_index) } BrushKind::YuvImage { .. } | BrushKind::LinearGradient { .. } | @@ -2361,28 +2413,43 @@ impl PrimitiveStore { // See if this picture contains a single primitive that supports // opacity collapse. match self.get_opacity_collapse_prim(pic_index) { - Some(prim_index) => { - let prim = &mut self.primitives[prim_index.0]; - match prim.details { - PrimitiveDetails::Brush(ref mut brush) => { - // By this point, we know we should only have found a primitive - // that supports opacity collapse. - match brush.kind { - BrushKind::Solid { ref mut opacity_binding_index, .. } | - BrushKind::Image { ref mut opacity_binding_index, .. } => { - if *opacity_binding_index == OpacityBindingIndex::INVALID { - *opacity_binding_index = self.opacity_bindings.push(OpacityBinding::new()); + Some(pic_index) => { + let pic = &mut self.pictures[pic_index.0]; + let prim_instance = &mut pic.prim_list.prim_instances[0]; + match prim_instance.kind { + PrimitiveInstanceKind::Rectangle { ref mut opacity_binding_index, .. } => { + if *opacity_binding_index == OpacityBindingIndex::INVALID { + *opacity_binding_index = self.opacity_bindings.push(OpacityBinding::new()); + } + let opacity_binding = &mut self.opacity_bindings[*opacity_binding_index]; + opacity_binding.push(binding); + } + PrimitiveInstanceKind::LegacyPrimitive { prim_index } => { + let prim = &mut self.primitives[prim_index.0]; + match prim.details { + PrimitiveDetails::Brush(ref mut brush) => { + // By this point, we know we should only have found a primitive + // that supports opacity collapse. + match brush.kind { + BrushKind::Image { ref mut opacity_binding_index, .. } => { + if *opacity_binding_index == OpacityBindingIndex::INVALID { + *opacity_binding_index = self.opacity_bindings.push(OpacityBinding::new()); + } + let opacity_binding = &mut self.opacity_bindings[*opacity_binding_index]; + opacity_binding.push(binding); + } + BrushKind::YuvImage { .. } | + BrushKind::LinearGradient { .. } | + BrushKind::RadialGradient { .. } => { + unreachable!("bug: invalid prim type for opacity collapse"); + } } - let opacity_binding = &mut self.opacity_bindings[*opacity_binding_index]; - opacity_binding.push(binding); - } - BrushKind::YuvImage { .. } | - BrushKind::LinearGradient { .. } | - BrushKind::RadialGradient { .. } => { - unreachable!("bug: invalid prim type for opacity collapse"); } } } + _ => { + unreachable!(); + } } } None => { @@ -2413,6 +2480,7 @@ impl PrimitiveStore { display_list: &BuiltDisplayList, plane_split_anchor: usize, resources: &mut FrameResources, + scratch: &mut PrimitiveScratchBuffer, ) -> bool { // If we have dependencies, we need to prepare them first, in order // to know the actual rect of this primitive. @@ -2444,6 +2512,7 @@ impl PrimitiveStore { } } PrimitiveInstanceKind::TextRun { .. } | + PrimitiveInstanceKind::Rectangle { .. } | PrimitiveInstanceKind::LineDecoration { .. } | PrimitiveInstanceKind::LegacyPrimitive { .. } | PrimitiveInstanceKind::NormalBorder { .. } | @@ -2466,6 +2535,7 @@ impl PrimitiveStore { frame_context, frame_state, resources, + scratch, ); if !pic_state_for_children.is_cacheable { @@ -2498,6 +2568,7 @@ impl PrimitiveStore { PrimitiveInstanceKind::Clear | PrimitiveInstanceKind::NormalBorder { .. } | PrimitiveInstanceKind::ImageBorder { .. } | + PrimitiveInstanceKind::Rectangle { .. } | PrimitiveInstanceKind::LineDecoration { .. } => { let prim_data = &resources .prim_data_store[prim_instance.prim_data_handle]; @@ -2633,6 +2704,7 @@ impl PrimitiveStore { &clip_node_collector, &mut self.primitives, resources, + scratch, ); if prim_instance.is_chased() { @@ -2688,6 +2760,7 @@ impl PrimitiveStore { } PrimitiveInstanceKind::TextRun { .. } | PrimitiveInstanceKind::Clear | + PrimitiveInstanceKind::Rectangle { .. } | PrimitiveInstanceKind::NormalBorder { .. } | PrimitiveInstanceKind::ImageBorder { .. } | PrimitiveInstanceKind::LineDecoration { .. } => { @@ -2698,6 +2771,7 @@ impl PrimitiveStore { frame_context, frame_state, resources, + scratch, ); } PrimitiveInstanceKind::LegacyPrimitive { prim_index } => { @@ -2728,6 +2802,7 @@ impl PrimitiveStore { frame_context: &FrameBuildingContext, frame_state: &mut FrameBuildingState, resources: &mut FrameResources, + scratch: &mut PrimitiveScratchBuffer, ) { let display_list = &frame_context .pipelines @@ -2808,6 +2883,7 @@ impl PrimitiveStore { display_list, plane_split_anchor, resources, + scratch, ) { frame_state.profile_counters.visible_primitives.inc(); } @@ -2825,6 +2901,7 @@ impl PrimitiveStore { frame_context: &FrameBuildingContext, frame_state: &mut FrameBuildingState, resources: &mut FrameResources, + scratch: &mut PrimitiveScratchBuffer, ) { let prim_data = &mut resources .prim_data_store[prim_instance.prim_data_handle]; @@ -2907,7 +2984,7 @@ impl PrimitiveStore { frame_state.gpu_cache, frame_state.render_tasks, frame_state.special_render_passes, - frame_state.scratch, + scratch, ); } ( @@ -2969,8 +3046,7 @@ impl PrimitiveStore { )); } - *cache_handles = frame_state - .scratch + *cache_handles = scratch .border_cache_handles .extend(handles); } @@ -2979,6 +3055,33 @@ impl PrimitiveStore { PrimitiveTemplateKind::ImageBorder { .. } ) => { } + ( + PrimitiveInstanceKind::Rectangle { segment_instance_index, opacity_binding_index, .. }, + PrimitiveTemplateKind::Rectangle { ref color, .. } + ) => { + if *segment_instance_index != SegmentInstanceIndex::UNUSED { + let segment_instance = &mut scratch.segment_instances[*segment_instance_index]; + + if let Some(mut request) = frame_state.gpu_cache.request(&mut segment_instance.gpu_cache_handle) { + let segments = &scratch.segments[segment_instance.segments_range]; + + request.push(color.premultiplied()); + + for segment in segments { + request.write_segment( + segment.local_rect, + [0.0; 4], + ); + } + } + } + + update_opacity_binding( + &mut self.opacity_bindings, + *opacity_binding_index, + frame_context.scene_properties, + ); + } _ => { unreachable!(); } @@ -3103,29 +3206,14 @@ impl<'a> GpuDataRequest<'a> { } } -impl BrushPrimitive { fn write_brush_segment_description( - &mut self, prim_local_rect: LayoutRect, prim_local_clip_rect: LayoutRect, clip_chain: &ClipChainInstance, - frame_state: &mut FrameBuildingState, + segment_builder: &mut SegmentBuilder, + clip_store: &ClipStore, resources: &FrameResources, - ) { - match self.segment_desc { - Some(..) => { - // If we already have a segment descriptor, skip segment build. - return; - } - None => { - // If no segment descriptor built yet, see if it is a brush - // type that wants to be segmented. - if !self.kind.supports_segments(frame_state.resource_cache) { - return; - } - } - } - + ) -> bool { // If the brush is small, we generally want to skip building segments // and just draw it as a single primitive with clip mask. However, // if the clips are purely rectangles that have no per-fragment @@ -3138,7 +3226,6 @@ impl BrushPrimitive { // the clip sources here. let mut rect_clips_only = true; - let segment_builder = &mut frame_state.segment_builder; segment_builder.initialize( prim_local_rect, None, @@ -3148,8 +3235,7 @@ impl BrushPrimitive { // Segment the primitive on all the local-space clip sources that we can. let mut local_clip_count = 0; for i in 0 .. clip_chain.clips_range.count { - let clip_instance = frame_state - .clip_store + let clip_instance = clip_store .get_instance_from_range(&clip_chain.clips_range, i); let clip_node = &resources.clip_data_store[clip_instance.handle]; @@ -3241,35 +3327,11 @@ impl BrushPrimitive { } } - match self.segment_desc { - Some(..) => panic!("bug: should not already have descriptor"), - None => { - // TODO(gw): We can probably make the allocation - // patterns of this and the segment - // builder significantly better, by - // retaining it across primitives. - let mut segments = BrushSegmentVec::new(); - - segment_builder.build(|segment| { - segments.push( - BrushSegment::new( - segment.rect, - segment.has_mask, - segment.edge_flags, - [0.0; 4], - BrushFlags::empty(), - ), - ); - }); - - self.segment_desc = Some(BrushSegmentDescriptor { - segments, - }); - } - } + return true } + + false } -} impl PrimitiveInstance { fn build_segments_if_needed( @@ -3280,20 +3342,100 @@ impl PrimitiveInstance { frame_state: &mut FrameBuildingState, primitives: &mut [Primitive], resources: &FrameResources, + scratch: &mut PrimitiveScratchBuffer, ) { - if let PrimitiveInstanceKind::LegacyPrimitive { prim_index } = self.kind { - let prim = &mut primitives[prim_index.0]; - match prim.details { - PrimitiveDetails::Brush(ref mut brush) => { - brush.write_brush_segment_description( + match self.kind { + PrimitiveInstanceKind::Rectangle { ref mut segment_instance_index, .. } => { + if *segment_instance_index == SegmentInstanceIndex::INVALID { + let mut segments: SmallVec<[BrushSegment; 8]> = SmallVec::new(); + + if write_brush_segment_description( prim_local_rect, prim_local_clip_rect, prim_clip_chain, - frame_state, + &mut frame_state.segment_builder, + frame_state.clip_store, resources, - ); + ) { + frame_state.segment_builder.build(|segment| { + segments.push( + BrushSegment::new( + segment.rect, + segment.has_mask, + segment.edge_flags, + [0.0; 4], + BrushFlags::empty(), + ), + ); + }); + } + + if segments.is_empty() { + *segment_instance_index = SegmentInstanceIndex::UNUSED; + } else { + let segments_range = scratch + .segments + .extend(segments); + + let instance = SegmentedInstance { + segments_range, + gpu_cache_handle: GpuCacheHandle::new(), + }; + + *segment_instance_index = scratch + .segment_instances + .push(instance); + }; } } + PrimitiveInstanceKind::LegacyPrimitive { prim_index } => { + let prim = &mut primitives[prim_index.0]; + match prim.details { + PrimitiveDetails::Brush(ref mut brush) => { + match brush.segment_desc { + Some(..) => { + // If we already have a segment descriptor, skip segment build. + return; + } + None => { + // If no segment descriptor built yet, see if it is a brush + // type that wants to be segmented. + if brush.kind.supports_segments(frame_state.resource_cache) { + let mut segments = BrushSegmentVec::new(); + + if write_brush_segment_description( + prim_local_rect, + prim_local_clip_rect, + prim_clip_chain, + &mut frame_state.segment_builder, + frame_state.clip_store, + resources, + ) { + frame_state.segment_builder.build(|segment| { + segments.push( + BrushSegment::new( + segment.rect, + segment.has_mask, + segment.edge_flags, + [0.0; 4], + BrushFlags::empty(), + ), + ); + }); + } + + if !segments.is_empty() { + brush.segment_desc = Some(BrushSegmentDescriptor { + segments, + }); + } + } + } + } + } + } + } + _ => {} } } @@ -3311,6 +3453,7 @@ impl PrimitiveInstance { clip_node_collector: &Option, primitives: &[Primitive], resources: &mut FrameResources, + scratch: &mut PrimitiveScratchBuffer, ) -> bool { let segments = match self.kind { PrimitiveInstanceKind::Picture { .. } | @@ -3319,6 +3462,17 @@ impl PrimitiveInstance { PrimitiveInstanceKind::LineDecoration { .. } => { return false; } + PrimitiveInstanceKind::Rectangle { segment_instance_index, .. } => { + debug_assert!(segment_instance_index != SegmentInstanceIndex::INVALID); + + if segment_instance_index == SegmentInstanceIndex::UNUSED { + return false; + } + + let segment_instance = &scratch.segment_instances[segment_instance_index]; + + &mut scratch.segments[segment_instance.segments_range] + } PrimitiveInstanceKind::ImageBorder { .. } => { let prim_data = &resources.prim_data_store[self.prim_data_handle]; @@ -3326,7 +3480,7 @@ impl PrimitiveInstance { // can change this to be a tuple match on (instance, template) match prim_data.kind { PrimitiveTemplateKind::ImageBorder { ref brush_segments, .. } => { - brush_segments + brush_segments.as_slice() } _ => { unreachable!(); @@ -3373,7 +3527,7 @@ impl PrimitiveInstance { // Set where in the clip mask instances array the clip mask info // can be found for this primitive. Each segment will push the // clip mask information for itself in update_clip_task below. - self.clip_task_index = ClipTaskIndex(frame_state.scratch.clip_mask_instances.len() as _); + self.clip_task_index = ClipTaskIndex(scratch.clip_mask_instances.len() as _); // If we only built 1 segment, there is no point in re-running // the clip chain builder. Instead, just use the clip chain @@ -3390,7 +3544,7 @@ impl PrimitiveInstance { frame_state, &mut resources.clip_data_store, ); - frame_state.scratch.clip_mask_instances.push(clip_mask_kind); + scratch.clip_mask_instances.push(clip_mask_kind); } else { for segment in segments { // Build a clip chain for the smaller segment rect. This will @@ -3424,7 +3578,7 @@ impl PrimitiveInstance { frame_state, &mut resources.clip_data_store, ); - frame_state.scratch.clip_mask_instances.push(clip_mask_kind); + scratch.clip_mask_instances.push(clip_mask_kind); } } @@ -3802,14 +3956,6 @@ impl PrimitiveInstance { PrimitiveOpacity::translucent() } } - BrushKind::Solid { ref color, opacity_binding_index, .. } => { - let current_opacity = update_opacity_binding( - opacity_bindings, - opacity_binding_index, - frame_context.scene_properties, - ); - PrimitiveOpacity::from_alpha(current_opacity * color.a) - } }; } } @@ -3845,6 +3991,7 @@ impl PrimitiveInstance { clip_node_collector: &Option, primitives: &mut [Primitive], resources: &mut FrameResources, + scratch: &mut PrimitiveScratchBuffer, ) { if self.is_chased() { println!("\tupdating clip task with pic rect {:?}", clip_chain.pic_clip_rect); @@ -3860,6 +4007,7 @@ impl PrimitiveInstance { frame_state, primitives, resources, + scratch, ); // First try to render this primitive's mask using optimized brush rendering. @@ -3876,6 +4024,7 @@ impl PrimitiveInstance { clip_node_collector, primitives, resources, + scratch, ) { if self.is_chased() { println!("\tsegment tasks have been created for clipping"); @@ -3908,8 +4057,8 @@ impl PrimitiveInstance { clip_task_id, device_rect); } // Set the global clip mask instance for this primitive. - let clip_task_index = ClipTaskIndex(frame_state.scratch.clip_mask_instances.len() as _); - frame_state.scratch.clip_mask_instances.push(ClipMaskKind::Mask(clip_task_id)); + let clip_task_index = ClipTaskIndex(scratch.clip_mask_instances.len() as _); + scratch.clip_mask_instances.push(ClipMaskKind::Mask(clip_task_id)); self.clip_task_index = clip_task_index; frame_state.surfaces[pic_context.surface_index.0].tasks.push(clip_task_id); } diff --git a/gfx/wr/webrender/src/storage.rs b/gfx/wr/webrender/src/storage.rs index 7a069122119f..ae00ad251d1a 100644 --- a/gfx/wr/webrender/src/storage.rs +++ b/gfx/wr/webrender/src/storage.rs @@ -31,6 +31,7 @@ impl Index { } pub const INVALID: Index = Index(u32::MAX, PhantomData); + pub const UNUSED: Index = Index(u32::MAX-1, PhantomData); } #[derive(Debug)] diff --git a/gfx/wr/webrender/src/surface.rs b/gfx/wr/webrender/src/surface.rs index cfbd1f3d1074..9e90cac738e3 100644 --- a/gfx/wr/webrender/src/surface.rs +++ b/gfx/wr/webrender/src/surface.rs @@ -243,6 +243,7 @@ impl SurfaceDescriptor { PrimitiveInstanceKind::LineDecoration { .. } | PrimitiveInstanceKind::TextRun { .. } | PrimitiveInstanceKind::NormalBorder { .. } | + PrimitiveInstanceKind::Rectangle { .. } | PrimitiveInstanceKind::ImageBorder { .. } | PrimitiveInstanceKind::Clear => {} } diff --git a/gfx/wr/wrench/reftests/boxshadow/inset-alpha.png b/gfx/wr/wrench/reftests/boxshadow/inset-alpha.png index f413da144e92..6b5299f386e4 100644 Binary files a/gfx/wr/wrench/reftests/boxshadow/inset-alpha.png and b/gfx/wr/wrench/reftests/boxshadow/inset-alpha.png differ