From ec129d915fa05bafd2ce2dc8ca24c71e1f64ea47 Mon Sep 17 00:00:00 2001 From: Glenn Watson Date: Mon, 26 Nov 2018 14:19:11 +1000 Subject: [PATCH 1/3] Port radial gradients to be interned primitives. --- webrender/src/batch.rs | 206 ++++----- webrender/src/display_list_flattener.rs | 67 +-- webrender/src/picture.rs | 1 + webrender/src/prim_store.rs | 394 ++++++++++-------- webrender/src/surface.rs | 1 + .../gradient/premultiplied-radial-2.png | Bin 20155 -> 27094 bytes 6 files changed, 358 insertions(+), 311 deletions(-) diff --git a/webrender/src/batch.rs b/webrender/src/batch.rs index d88ff71ca9..37b10c182b 100644 --- a/webrender/src/batch.rs +++ b/webrender/src/batch.rs @@ -15,10 +15,10 @@ use gpu_types::{PrimitiveInstanceData, RasterizationSpace, GlyphInstance}; use gpu_types::{PrimitiveHeader, PrimitiveHeaderIndex, TransformPaletteId, TransformPalette}; use internal_types::{FastHashMap, SavedTargetIndex, TextureSource}; use picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive, PictureSurface}; -use prim_store::{BrushKind, BrushPrimitive, DeferredResolve, PrimitiveTemplateKind, PrimitiveDataStore}; +use prim_store::{DeferredResolve, PrimitiveTemplateKind, PrimitiveDataStore}; use prim_store::{EdgeAaSegmentMask, ImageSource, PrimitiveInstanceKind}; use prim_store::{VisibleGradientTile, PrimitiveInstance, PrimitiveOpacity, SegmentInstanceIndex}; -use prim_store::{BrushSegment, ClipMaskKind, ClipTaskIndex, PrimitiveDetails}; +use prim_store::{BrushSegment, ClipMaskKind, ClipTaskIndex}; use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskTree}; use renderer::{BlendMode, ImageBufferKind, ShaderColorMode}; use renderer::BLOCKS_PER_UV_RECT; @@ -909,6 +909,7 @@ impl AlphaBatchBuilder { PrimitiveInstanceKind::YuvImage { .. } | PrimitiveInstanceKind::Image { .. } | PrimitiveInstanceKind::LinearGradient { .. } | + PrimitiveInstanceKind::RadialGradient { .. } | PrimitiveInstanceKind::Clear => { unreachable!(); } @@ -1401,102 +1402,9 @@ impl AlphaBatchBuilder { } } ( - PrimitiveInstanceKind::LegacyPrimitive { prim_index }, + PrimitiveInstanceKind::LegacyPrimitive { .. }, PrimitiveTemplateKind::Unused, ) => { - let prim = &ctx.prim_store.primitives[prim_index.0]; - - // If the primitive is internally decomposed into multiple sub-primitives we may not - // use some of the per-primitive data and get it from each sub-primitive instead. - let is_multiple_primitives = match prim.details { - PrimitiveDetails::Brush(ref brush) => { - match brush.kind { - BrushKind::RadialGradient { visible_tiles_range, .. } => !visible_tiles_range.is_empty(), - } - } - }; - - let specified_blend_mode = BlendMode::PremultipliedAlpha; - - match prim.details { - PrimitiveDetails::Brush(ref brush) => { - let non_segmented_blend_mode = if !brush.opacity.is_opaque || - prim_instance.clip_task_index != ClipTaskIndex::INVALID || - transform_kind == TransformedRectKind::Complex - { - specified_blend_mode - } else { - BlendMode::None - }; - - let prim_cache_address = if is_multiple_primitives { - GpuCacheAddress::invalid() - } else { - gpu_cache.get_address(&brush.gpu_location) - }; - - let prim_header = PrimitiveHeader { - local_rect: prim.local_rect, - local_clip_rect: prim_instance.combined_local_clip_rect, - task_address, - specific_prim_address: prim_cache_address, - clip_task_address, - transform_id, - }; - - if prim_instance.is_chased() { - println!("\ttask target {:?}", self.target_rect); - println!("\t{:?}", prim_header); - } - - match brush.kind { - BrushKind::RadialGradient { ref stops_handle, visible_tiles_range, .. } if !visible_tiles_range.is_empty() => { - let visible_tiles = &ctx.scratch.gradient_tiles[visible_tiles_range]; - - add_gradient_tiles( - visible_tiles, - stops_handle, - BrushBatchKind::RadialGradient, - specified_blend_mode, - bounding_rect, - clip_task_address, - gpu_cache, - &mut self.batch_list, - &prim_header, - prim_headers, - z_id, - ); - } - _ => { - if let Some(params) = brush.get_batch_params( - gpu_cache, - ) { - let prim_header_index = prim_headers.push(&prim_header, z_id, params.prim_user_data); - if prim_instance.is_chased() { - println!("\t{:?} {:?}, task relative bounds {:?}", - params.batch_kind, prim_header_index, bounding_rect); - } - - self.add_segmented_prim_to_batch( - brush.segment_desc.as_ref().map(|desc| desc.segments.as_slice()), - brush.opacity, - ¶ms, - 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, - ); - } - } - } - } - } } ( PrimitiveInstanceKind::ImageBorder { .. }, @@ -1964,6 +1872,89 @@ impl AlphaBatchBuilder { ); } } + ( + PrimitiveInstanceKind::RadialGradient { visible_tiles_range, .. }, + PrimitiveTemplateKind::RadialGradient { stops_handle, ref brush_segments, .. } + ) => { + let specified_blend_mode = BlendMode::PremultipliedAlpha; + + let mut prim_header = PrimitiveHeader { + local_rect: prim_data.prim_rect, + local_clip_rect: prim_instance.combined_local_clip_rect, + task_address, + specific_prim_address: GpuCacheAddress::invalid(), + clip_task_address, + transform_id, + }; + + if visible_tiles_range.is_empty() { + let non_segmented_blend_mode = if !prim_data.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::RadialGradient, + BatchTextures::no_texture(), + [ + stops_handle.as_int(gpu_cache), + 0, + 0, + ], + 0, + ); + + prim_header.specific_prim_address = gpu_cache.get_address(&prim_data.gpu_cache_handle); + + let prim_header_index = prim_headers.push( + &prim_header, + z_id, + batch_params.prim_user_data, + ); + + let segments = if brush_segments.is_empty() { + None + } else { + Some(brush_segments.as_slice()) + }; + + self.add_segmented_prim_to_batch( + segments, + prim_data.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, + ); + } else { + let visible_tiles = &ctx.scratch.gradient_tiles[*visible_tiles_range]; + + add_gradient_tiles( + visible_tiles, + stops_handle, + BrushBatchKind::RadialGradient, + specified_blend_mode, + bounding_rect, + clip_task_address, + gpu_cache, + &mut self.batch_list, + &prim_header, + prim_headers, + z_id, + ); + } + } _ => { unreachable!(); } @@ -2295,28 +2286,6 @@ impl BrushBatchParameters { } } -impl BrushPrimitive { - fn get_batch_params( - &self, - gpu_cache: &mut GpuCache, - ) -> Option { - match self.kind { - BrushKind::RadialGradient { ref stops_handle, .. } => { - Some(BrushBatchParameters::shared( - BrushBatchKind::RadialGradient, - BatchTextures::no_texture(), - [ - stops_handle.as_int(gpu_cache), - 0, - 0, - ], - 0, - )) - } - } - } -} - impl PrimitiveInstance { pub fn is_cacheable( &self, @@ -2345,6 +2314,7 @@ impl PrimitiveInstance { PrimitiveInstanceKind::ImageBorder { .. } | PrimitiveInstanceKind::Rectangle { .. } | PrimitiveInstanceKind::LinearGradient { .. } | + PrimitiveInstanceKind::RadialGradient { .. } | PrimitiveInstanceKind::Clear => { return true; } diff --git a/webrender/src/display_list_flattener.rs b/webrender/src/display_list_flattener.rs index 8b7f63cf85..b7defe4a59 100644 --- a/webrender/src/display_list_flattener.rs +++ b/webrender/src/display_list_flattener.rs @@ -17,20 +17,18 @@ use clip::{ClipChainId, ClipRegion, ClipItemKey, ClipStore, ClipItemSceneData}; use clip_scroll_tree::{ROOT_SPATIAL_NODE_INDEX, ClipScrollTree, SpatialNodeIndex}; use frame_builder::{ChasePrimitive, FrameBuilder, FrameBuilderConfig}; use glyph_rasterizer::FontInstance; -use gpu_cache::GpuCacheHandle; use hit_test::{HitTestingItem, HitTestingRun}; use image::simplify_repeated_primitive; use internal_types::{FastHashMap, FastHashSet}; use picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive, PrimitiveList}; -use prim_store::{BrushKind, BrushPrimitive, PrimitiveInstance, PrimitiveDataInterner, PrimitiveKeyKind}; +use prim_store::{PrimitiveInstance, PrimitiveDataInterner, PrimitiveKeyKind, RadialGradientParams}; use prim_store::{PrimitiveKey, PrimitiveSceneData, PrimitiveInstanceKind, GradientStopKey, NinePatchDescriptor}; -use prim_store::{PrimitiveContainer, PrimitiveDataHandle, PrimitiveStore, PrimitiveStoreStats, BrushSegmentDescriptor}; -use prim_store::{ScrollNodeAndClipChain, PictureIndex, register_prim_chase_id, GradientTileRange}; +use prim_store::{PrimitiveContainer, PrimitiveDataHandle, PrimitiveStore, PrimitiveStoreStats}; +use prim_store::{ScrollNodeAndClipChain, PictureIndex, register_prim_chase_id}; use render_backend::{DocumentView}; use resource_cache::{FontInstanceMap, ImageRequest}; use scene::{Scene, ScenePipeline, StackingContextHelpers}; use scene_builder::DocumentResources; -use smallvec::SmallVec; use spatial_node::{StickyFrameInfo}; use std::{f32, mem}; use std::collections::vec_deque::VecDeque; @@ -600,7 +598,7 @@ impl<'a> DisplayListFlattener<'a> { } } SpecificDisplayItem::RadialGradient(ref info) => { - let brush_kind = self.create_brush_kind_for_radial_gradient( + let prim = self.create_radial_gradient_prim( &prim_info, info.gradient.center, info.gradient.start_offset * info.gradient.radius.width, @@ -610,9 +608,15 @@ impl<'a> DisplayListFlattener<'a> { info.gradient.extend_mode, info.tile_size, info.tile_spacing, + pipeline_id, + None, + ); + self.add_primitive( + clip_and_scroll, + &prim_info, + Vec::new(), + prim, ); - let prim = PrimitiveContainer::Brush(BrushPrimitive::new(brush_kind, None)); - self.add_primitive(clip_and_scroll, &prim_info, Vec::new(), prim); } SpecificDisplayItem::BoxShadow(ref box_shadow_info) => { let bounds = box_shadow_info @@ -1781,7 +1785,7 @@ impl<'a> DisplayListFlattener<'a> { } } NinePatchBorderSource::RadialGradient(gradient) => { - let brush_kind = self.create_brush_kind_for_radial_gradient( + self.create_radial_gradient_prim( &info, gradient.center, gradient.start_offset * gradient.radius.width, @@ -1791,16 +1795,8 @@ impl<'a> DisplayListFlattener<'a> { gradient.extend_mode, LayoutSize::new(border.height as f32, border.width as f32), LayoutSize::zero(), - ); - - let segments = nine_patch.create_segments(&info.rect); - - let descriptor = BrushSegmentDescriptor { - segments: SmallVec::from_vec(segments), - }; - - PrimitiveContainer::Brush( - BrushPrimitive::new(brush_kind, Some(descriptor)) + pipeline_id, + Some(Box::new(nine_patch)), ) } }; @@ -1888,7 +1884,7 @@ impl<'a> DisplayListFlattener<'a> { }) } - pub fn create_brush_kind_for_radial_gradient( + pub fn create_radial_gradient_prim( &mut self, info: &LayoutPrimitiveInfo, center: LayoutPoint, @@ -1899,21 +1895,38 @@ impl<'a> DisplayListFlattener<'a> { extend_mode: ExtendMode, stretch_size: LayoutSize, mut tile_spacing: LayoutSize, - ) -> BrushKind { + pipeline_id: PipelineId, + nine_patch: Option>, + ) -> PrimitiveContainer { let mut prim_rect = info.rect; simplify_repeated_primitive(&stretch_size, &mut tile_spacing, &mut prim_rect); - BrushKind::RadialGradient { - stops_range: stops, - extend_mode, - center, + // TODO(gw): It seems like we should be able to look this up once in + // flatten_root() and pass to all children here to avoid + // some hash lookups? + let display_list = self.scene.get_display_list_for_pipeline(pipeline_id); + + let params = RadialGradientParams { start_radius, end_radius, ratio_xy, - stops_handle: GpuCacheHandle::new(), + }; + + let stops = display_list.get(stops).map(|stop| { + GradientStopKey { + offset: stop.offset, + color: stop.color.into(), + } + }).collect(); + + PrimitiveContainer::RadialGradient { + extend_mode, + center, + params, stretch_size, tile_spacing, - visible_tiles_range: GradientTileRange::empty(), + nine_patch, + stops, } } diff --git a/webrender/src/picture.rs b/webrender/src/picture.rs index 93a1037471..198abcaa6d 100644 --- a/webrender/src/picture.rs +++ b/webrender/src/picture.rs @@ -447,6 +447,7 @@ impl TileCache { PrimitiveInstanceKind::Clear | PrimitiveInstanceKind::NormalBorder { .. } | PrimitiveInstanceKind::LinearGradient { .. } | + PrimitiveInstanceKind::RadialGradient { .. } | PrimitiveInstanceKind::ImageBorder { .. } => { // These don't contribute dependencies } diff --git a/webrender/src/prim_store.rs b/webrender/src/prim_store.rs index 61c3da5890..f03f7ea080 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -4,7 +4,7 @@ use api::{AlphaType, BorderRadius, BuiltDisplayList, ClipMode, ColorF, PictureRect, ColorU, LayoutPrimitiveInfo}; use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, ExtendMode, DeviceRect, LayoutSideOffsetsAu}; -use api::{FilterOp, GlyphInstance, GradientStop, ImageKey, ImageRendering, ItemRange, TileOffset, RepeatMode}; +use api::{FilterOp, GlyphInstance, GradientStop, ImageKey, ImageRendering, TileOffset, RepeatMode}; use api::{RasterSpace, LayoutPoint, LayoutRect, LayoutSideOffsets, LayoutSize, LayoutToWorldTransform}; use api::{LayoutVector2D, PremultipliedColorF, PropertyBinding, Shadow, YuvColorSpace, YuvFormat}; use api::{DeviceIntSideOffsets, WorldPixel, BoxShadowClipMode, NormalBorder, WorldRect, LayoutToWorldScale}; @@ -406,6 +406,15 @@ pub enum PrimitiveKeyKind { reverse_stops: bool, nine_patch: Option>, }, + RadialGradient { + extend_mode: ExtendMode, + center: PointKey, + params: RadialGradientParams, + stretch_size: SizeKey, + stops: Vec, + tile_spacing: SizeKey, + nine_patch: Option>, + }, } /// A hashable gradient stop that can be used in primitive keys. @@ -555,6 +564,26 @@ impl From for SizeKey { } } +/// Hashable radial gradient parameters, for use during prim interning. +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(Debug, Clone, PartialEq)] +pub struct RadialGradientParams { + pub start_radius: f32, + pub end_radius: f32, + pub ratio_xy: f32, +} + +impl Eq for RadialGradientParams {} + +impl hash::Hash for RadialGradientParams { + fn hash(&self, state: &mut H) { + self.start_radius.to_bits().hash(state); + self.end_radius.to_bits().hash(state); + self.ratio_xy.to_bits().hash(state); + } +} + /// A hashable point for using as a key during primitive interning. #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] @@ -677,6 +706,11 @@ impl PrimitiveKey { visible_tiles_range: GradientTileRange::empty(), } } + PrimitiveKeyKind::RadialGradient { .. } => { + PrimitiveInstanceKind::RadialGradient { + visible_tiles_range: GradientTileRange::empty(), + } + } PrimitiveKeyKind::Unused => { // Should never be hit as this method should not be // called for old style primitives. @@ -748,6 +782,16 @@ pub enum PrimitiveTemplateKind { reverse_stops: bool, stops_handle: GpuCacheHandle, }, + RadialGradient { + extend_mode: ExtendMode, + center: LayoutPoint, + params: RadialGradientParams, + stretch_size: LayoutSize, + tile_spacing: LayoutSize, + brush_segments: Vec, + stops: Vec, + stops_handle: GpuCacheHandle, + }, Clear, Unused, } @@ -892,6 +936,43 @@ impl PrimitiveKeyKind { stops_handle: GpuCacheHandle::new(), } } + PrimitiveKeyKind::RadialGradient { + extend_mode, + params, + stretch_size, + tile_spacing, + nine_patch, + center, + stops, + .. + } => { + let mut brush_segments = Vec::new(); + + if let Some(ref nine_patch) = nine_patch { + brush_segments = create_nine_patch_segments( + rect, + nine_patch, + ); + } + + let stops = stops.iter().map(|stop| { + GradientStop { + offset: stop.offset, + color: stop.color.into(), + } + }).collect(); + + PrimitiveTemplateKind::RadialGradient { + center: center.into(), + extend_mode, + params, + stretch_size: stretch_size.into(), + tile_spacing: tile_spacing.into(), + brush_segments, + stops_handle: GpuCacheHandle::new(), + stops, + } + } } } } @@ -1054,6 +1135,26 @@ impl PrimitiveTemplateKind { 0.0, ]); } + PrimitiveTemplateKind::RadialGradient { + center, + ref params, + extend_mode, + stretch_size, + .. + } => { + request.push([ + center.x, + center.y, + params.start_radius, + params.end_radius, + ]); + request.push([ + params.ratio_xy, + pack_as_float(extend_mode as u32), + stretch_size.width, + stretch_size.height, + ]); + } PrimitiveTemplateKind::Unused => {} } } @@ -1094,7 +1195,8 @@ impl PrimitiveTemplateKind { [0.0; 4], ); } - PrimitiveTemplateKind::LinearGradient { ref brush_segments, .. } => { + PrimitiveTemplateKind::LinearGradient { ref brush_segments, .. } | + PrimitiveTemplateKind::RadialGradient { ref brush_segments, .. } => { for segment in brush_segments { // has to match VECS_PER_SEGMENT request.write_segment( @@ -1156,7 +1258,7 @@ impl PrimitiveTemplate { GradientGpuBlockBuilder::build( reverse_stops, &mut request, - stops.iter().cloned(), + stops, ); } @@ -1173,6 +1275,18 @@ impl PrimitiveTemplate { PrimitiveOpacity::translucent() } } + PrimitiveTemplateKind::RadialGradient { ref mut stops_handle, ref stops, .. } => { + if let Some(mut request) = frame_state.gpu_cache.request(stops_handle) { + GradientGpuBlockBuilder::build( + false, + &mut request, + stops, + ); + } + + //TODO: can we make it opaque in some cases? + PrimitiveOpacity::translucent() + } PrimitiveTemplateKind::ImageBorder { request, .. } => { let image_properties = frame_state .resource_cache @@ -1440,6 +1554,7 @@ pub struct BorderSegmentInfo { } pub enum BrushKind { + /* RadialGradient { stops_handle: GpuCacheHandle, stops_range: ItemRange, @@ -1452,6 +1567,7 @@ pub enum BrushKind { tile_spacing: LayoutSize, visible_tiles_range: GradientTileRange, }, + */ } bitflags! { @@ -1579,64 +1695,6 @@ pub struct BrushPrimitive { pub gpu_location: GpuCacheHandle, } -impl BrushPrimitive { - pub fn new( - kind: BrushKind, - segment_desc: Option, - ) -> Self { - BrushPrimitive { - kind, - opacity: PrimitiveOpacity::translucent(), - segment_desc, - gpu_location: GpuCacheHandle::new(), - } - } - - fn write_gpu_blocks_if_required( - &mut self, - local_rect: LayoutRect, - gpu_cache: &mut GpuCache, - ) { - if let Some(mut request) = gpu_cache.request(&mut self.gpu_location) { - // has to match VECS_PER_SPECIFIC_BRUSH - match self.kind { - BrushKind::RadialGradient { stretch_size, center, start_radius, end_radius, ratio_xy, extend_mode, .. } => { - request.push([ - center.x, - center.y, - start_radius, - end_radius, - ]); - request.push([ - ratio_xy, - pack_as_float(extend_mode as u32), - stretch_size.width, - stretch_size.height, - ]); - } - } - - match self.segment_desc { - Some(ref segment_desc) => { - for segment in &segment_desc.segments { - // has to match VECS_PER_SEGMENT - request.write_segment( - segment.local_rect, - segment.extra_data, - ); - } - } - None => { - request.write_segment( - local_rect, - [0.0; 4], - ); - } - } - } - } -} - // Key that identifies a unique (partial) image that is being // stored in the render task cache. #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)] @@ -1739,11 +1797,11 @@ impl GradientGpuBlockBuilder { } // Build the gradient data from the supplied stops, reversing them if necessary. - fn build( + fn build( reverse_stops: bool, request: &mut GpuDataRequest, - src_stops: I, - ) where I: IntoIterator { + src_stops: &[GradientStop], + ) { // Preconditions (should be ensured by DisplayListBuilder): // * we have at least two stops // * first stop has offset 0.0 @@ -2198,7 +2256,6 @@ pub enum PrimitiveContainer { shadow: bool, }, Clear, - Brush(BrushPrimitive), LineDecoration { color: ColorF, style: LineStyle, @@ -2242,6 +2299,15 @@ pub enum PrimitiveContainer { reverse_stops: bool, nine_patch: Option>, }, + RadialGradient { + extend_mode: ExtendMode, + center: LayoutPoint, + params: RadialGradientParams, + stretch_size: LayoutSize, + tile_spacing: LayoutSize, + stops: Vec, + nine_patch: Option>, + }, } impl PrimitiveContainer { @@ -2257,18 +2323,12 @@ impl PrimitiveContainer { PrimitiveContainer::TextRun { ref font, .. } => { font.color.a > 0 } - PrimitiveContainer::Brush(ref brush) => { - match brush.kind { - BrushKind::RadialGradient { .. } => { - true - } - } - } PrimitiveContainer::NormalBorder { .. } | PrimitiveContainer::ImageBorder { .. } | PrimitiveContainer::YuvImage { .. } | PrimitiveContainer::Image { .. } | PrimitiveContainer::LinearGradient { .. } | + PrimitiveContainer::RadialGradient { .. } | PrimitiveContainer::Clear => { true } @@ -2320,6 +2380,28 @@ impl PrimitiveContainer { (key, None) } + PrimitiveContainer::RadialGradient { + extend_mode, + center, + params, + stretch_size, + tile_spacing, + nine_patch, + stops, + .. + } => { + let key = PrimitiveKeyKind::RadialGradient { + extend_mode, + center: center.into(), + params, + stretch_size: stretch_size.into(), + tile_spacing: tile_spacing.into(), + nine_patch, + stops, + }; + + (key, None) + } PrimitiveContainer::Clear => { (PrimitiveKeyKind::Clear, None) } @@ -2429,9 +2511,6 @@ impl PrimitiveContainer { (key, None) } - PrimitiveContainer::Brush(prim) => { - (PrimitiveKeyKind::Unused, Some(PrimitiveDetails::Brush(prim))) - } } } @@ -2490,10 +2569,10 @@ impl PrimitiveContainer { color: shadow.color, } } - PrimitiveContainer::Brush(..) | PrimitiveContainer::ImageBorder { .. } | PrimitiveContainer::YuvImage { .. } | PrimitiveContainer::LinearGradient { .. } | + PrimitiveContainer::RadialGradient { .. } | PrimitiveContainer::Clear => { panic!("bug: this prim is not supported in shadow contexts"); } @@ -2581,6 +2660,9 @@ pub enum PrimitiveInstanceKind { LinearGradient { visible_tiles_range: GradientTileRange, }, + RadialGradient { + visible_tiles_range: GradientTileRange, + }, /// Clear out a rect, used for special effects. Clear, } @@ -2931,6 +3013,7 @@ impl PrimitiveStore { PrimitiveInstanceKind::YuvImage { .. } | PrimitiveInstanceKind::LegacyPrimitive { .. } | PrimitiveInstanceKind::LinearGradient { .. } | + PrimitiveInstanceKind::RadialGradient { .. } | PrimitiveInstanceKind::LineDecoration { .. } => { // These prims don't support opacity collapse } @@ -3066,6 +3149,7 @@ impl PrimitiveStore { PrimitiveInstanceKind::YuvImage { .. } | PrimitiveInstanceKind::Image { .. } | PrimitiveInstanceKind::LinearGradient { .. } | + PrimitiveInstanceKind::RadialGradient { .. } | PrimitiveInstanceKind::Clear => { None } @@ -3121,6 +3205,7 @@ impl PrimitiveStore { PrimitiveInstanceKind::YuvImage { .. } | PrimitiveInstanceKind::Image { .. } | PrimitiveInstanceKind::LinearGradient { .. } | + PrimitiveInstanceKind::RadialGradient { .. } | PrimitiveInstanceKind::LineDecoration { .. } => { let prim_data = &resources .prim_data_store[prim_instance.prim_data_handle]; @@ -3323,6 +3408,7 @@ impl PrimitiveStore { PrimitiveInstanceKind::YuvImage { .. } | PrimitiveInstanceKind::Image { .. } | PrimitiveInstanceKind::LinearGradient { .. } | + PrimitiveInstanceKind::RadialGradient { .. } | PrimitiveInstanceKind::LineDecoration { .. } => { self.prepare_interned_prim_for_render( prim_instance, @@ -3783,6 +3869,46 @@ impl PrimitiveStore { // for gradient primitives. SegmentInstanceIndex::UNUSED } + ( + PrimitiveInstanceKind::RadialGradient { ref mut visible_tiles_range, .. }, + PrimitiveTemplateKind::RadialGradient { ref params, extend_mode, stretch_size, tile_spacing, center, .. } + ) => { + if *tile_spacing != LayoutSize::zero() { + *visible_tiles_range = decompose_repeated_primitive( + &prim_instance.combined_local_clip_rect, + &prim_data.prim_rect, + &stretch_size, + &tile_spacing, + prim_context, + frame_state, + &pic_context.dirty_world_rect, + &mut scratch.gradient_tiles, + &mut |rect, mut request| { + request.push([ + center.x, + center.y, + params.start_radius, + params.end_radius, + ]); + request.push([ + params.ratio_xy, + pack_as_float(*extend_mode as u32), + stretch_size.width, + stretch_size.height, + ]); + request.write_segment(*rect, [0.0; 4]); + }, + ); + + if visible_tiles_range.is_empty() { + prim_instance.bounding_rect = None; + } + } + + // TODO(gw): Consider whether it's worth doing segment building + // for gradient primitives. + SegmentInstanceIndex::UNUSED + } _ => { unreachable!(); } @@ -4075,6 +4201,7 @@ impl PrimitiveInstance { PrimitiveInstanceKind::ImageBorder { .. } | PrimitiveInstanceKind::Clear | PrimitiveInstanceKind::LinearGradient { .. } | + PrimitiveInstanceKind::RadialGradient { .. } | PrimitiveInstanceKind::LineDecoration { .. } => { // These primitives don't support / need segments. return; @@ -4265,6 +4392,24 @@ impl PrimitiveInstance { } } } + PrimitiveInstanceKind::RadialGradient { .. } => { + let prim_data = &resources.prim_data_store[self.prim_data_handle]; + + // TODO: This is quite messy - once we remove legacy primitives we + // can change this to be a tuple match on (instance, template) + match prim_data.kind { + PrimitiveTemplateKind::RadialGradient { ref brush_segments, .. } => { + if brush_segments.is_empty() { + return false; + } + + brush_segments.as_slice() + } + _ => { + unreachable!(); + } + } + } PrimitiveInstanceKind::LegacyPrimitive { prim_index } => { let prim = &prim_store.primitives[prim_index.0]; match prim.details { @@ -4351,97 +4496,14 @@ impl PrimitiveInstance { fn prepare_prim_for_render_inner( &mut self, - prim_local_rect: LayoutRect, - prim_details: &mut PrimitiveDetails, - prim_context: &PrimitiveContext, - pic_context: &PictureContext, - frame_state: &mut FrameBuildingState, - display_list: &BuiltDisplayList, - scratch: &mut PrimitiveScratchBuffer, + _prim_local_rect: LayoutRect, + _prim_details: &mut PrimitiveDetails, + _prim_context: &PrimitiveContext, + _pic_context: &PictureContext, + _frame_state: &mut FrameBuildingState, + _display_list: &BuiltDisplayList, + _scratch: &mut PrimitiveScratchBuffer, ) { - let mut is_tiled = false; - - match *prim_details { - PrimitiveDetails::Brush(ref mut brush) => { - brush.opacity = match brush.kind { - BrushKind::RadialGradient { - stops_range, - center, - start_radius, - end_radius, - ratio_xy, - extend_mode, - stretch_size, - tile_spacing, - ref mut stops_handle, - ref mut visible_tiles_range, - .. - } => { - if let Some(mut request) = frame_state.gpu_cache.request(stops_handle) { - let src_stops = display_list.get(stops_range); - - GradientGpuBlockBuilder::build( - false, - &mut request, - src_stops, - ); - } - - if tile_spacing != LayoutSize::zero() { - is_tiled = true; - - *visible_tiles_range = decompose_repeated_primitive( - &self.combined_local_clip_rect, - &prim_local_rect, - &stretch_size, - &tile_spacing, - prim_context, - frame_state, - &pic_context.dirty_world_rect, - &mut scratch.gradient_tiles, - &mut |rect, mut request| { - request.push([ - center.x, - center.y, - start_radius, - end_radius, - ]); - request.push([ - ratio_xy, - pack_as_float(extend_mode as u32), - stretch_size.width, - stretch_size.height, - ]); - request.write_segment(*rect, [0.0; 4]); - }, - ); - - if visible_tiles_range.is_empty() { - self.bounding_rect = None; - } - } - - //TODO: can we make it opaque in some cases? - PrimitiveOpacity::translucent() - } - }; - } - } - - if is_tiled { - // we already requested each tile's gpu data. - return; - } - - // Mark this GPU resource as required for this frame. - match *prim_details { - PrimitiveDetails::Brush(ref mut brush) => { - brush.write_gpu_blocks_if_required( - prim_local_rect, - frame_state.gpu_cache, - ); - } - } } fn update_clip_task( diff --git a/webrender/src/surface.rs b/webrender/src/surface.rs index 97947175fe..60b94c6164 100644 --- a/webrender/src/surface.rs +++ b/webrender/src/surface.rs @@ -244,6 +244,7 @@ impl SurfaceDescriptor { PrimitiveInstanceKind::YuvImage { .. } | PrimitiveInstanceKind::LineDecoration { .. } | PrimitiveInstanceKind::LinearGradient { .. } | + PrimitiveInstanceKind::RadialGradient { .. } | PrimitiveInstanceKind::TextRun { .. } | PrimitiveInstanceKind::NormalBorder { .. } | PrimitiveInstanceKind::Rectangle { .. } | diff --git a/wrench/reftests/gradient/premultiplied-radial-2.png b/wrench/reftests/gradient/premultiplied-radial-2.png index 01a551e6ead1c5d43fa15fc08524eaf853976237..9a4462c7afaf9798ed624b49910ee37dfc2bbe3a 100644 GIT binary patch literal 27094 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

`|s9y-#-z{Z=hMk?Nqh; zXE)_H1agNS(T4FIx zWZhzM6Wj@*|Ds5KoL~6Af9oecl!o@}Bs6z8xDcjSy}f-XZnX#3y8!YMy{%b&?&1+i v#eX-G{C7Lce>c?pcT3Oz!;6Myv1a@Q%1QGNX2N|<^PKFRciywxfAaqTv_9&y literal 20155 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 ALI3~& From f97480cdc200916f8ba070997888ce914bfde124 Mon Sep 17 00:00:00 2001 From: Glenn Watson Date: Mon, 26 Nov 2018 14:54:50 +1000 Subject: [PATCH 2/3] Remove old legacy style primitive code. --- webrender/src/batch.rs | 7 - webrender/src/display_list_flattener.rs | 27 +-- webrender/src/frame_builder.rs | 2 +- webrender/src/picture.rs | 1 - webrender/src/prim_store.rs | 252 +++--------------------- webrender/src/render_backend.rs | 7 - webrender/src/surface.rs | 3 +- webrender_api/src/display_item.rs | 2 +- 8 files changed, 35 insertions(+), 266 deletions(-) diff --git a/webrender/src/batch.rs b/webrender/src/batch.rs index 37b10c182b..00ac0f6350 100644 --- a/webrender/src/batch.rs +++ b/webrender/src/batch.rs @@ -902,7 +902,6 @@ impl AlphaBatchBuilder { PrimitiveInstanceKind::Picture { pic_index } => pic_index, PrimitiveInstanceKind::LineDecoration { .. } | PrimitiveInstanceKind::TextRun { .. } | - PrimitiveInstanceKind::LegacyPrimitive { .. } | PrimitiveInstanceKind::NormalBorder { .. } | PrimitiveInstanceKind::ImageBorder { .. } | PrimitiveInstanceKind::Rectangle { .. } | @@ -1401,11 +1400,6 @@ impl AlphaBatchBuilder { } } } - ( - PrimitiveInstanceKind::LegacyPrimitive { .. }, - PrimitiveTemplateKind::Unused, - ) => { - } ( PrimitiveInstanceKind::ImageBorder { .. }, PrimitiveTemplateKind::ImageBorder { request, brush_segments, .. } @@ -2306,7 +2300,6 @@ impl PrimitiveInstance { _ => unreachable!(), } } - PrimitiveInstanceKind::LegacyPrimitive { .. } | PrimitiveInstanceKind::Picture { .. } | PrimitiveInstanceKind::TextRun { .. } | PrimitiveInstanceKind::LineDecoration { .. } | diff --git a/webrender/src/display_list_flattener.rs b/webrender/src/display_list_flattener.rs index b7defe4a59..ab5a367819 100644 --- a/webrender/src/display_list_flattener.rs +++ b/webrender/src/display_list_flattener.rs @@ -844,13 +844,9 @@ impl<'a> DisplayListFlattener<'a> { spatial_node_index: SpatialNodeIndex, container: PrimitiveContainer, ) -> PrimitiveInstance { - // Build a primitive key, and optionally an old - // style PrimitiveDetails structure from the - // source primitive container. + // Build a primitive key. let mut info = info.clone(); - let (prim_key_kind, prim_details) = container.build( - &mut info, - ); + let prim_key_kind = container.build(&mut info); let prim_key = PrimitiveKey::new( info.is_backface_visible, @@ -875,24 +871,7 @@ impl<'a> DisplayListFlattener<'a> { } }); - // If we are building an old style primitive, add it to - // the prim store, and create a primitive index for it. - // For an interned primitive, use the primitive key to - // create a matching primitive instance kind. - let instance_kind = match prim_details { - Some(prim_details) => { - let prim_index = self.prim_store.add_primitive( - &info.rect, - &info.clip_rect, - prim_details, - ); - - PrimitiveInstanceKind::LegacyPrimitive { prim_index } - } - None => { - prim_key.to_instance_kind(&mut self.prim_store) - } - }; + let instance_kind = prim_key.to_instance_kind(&mut self.prim_store); PrimitiveInstance::new( instance_kind, diff --git a/webrender/src/frame_builder.rs b/webrender/src/frame_builder.rs index f19ae7a103..0b93afe330 100644 --- a/webrender/src/frame_builder.rs +++ b/webrender/src/frame_builder.rs @@ -338,7 +338,7 @@ impl FrameBuilder { let mut profile_counters = FrameProfileCounters::new(); profile_counters .total_primitives - .set(self.prim_store.prim_count()); + .set(self.prim_store.prim_count); resource_cache.begin_frame(stamp); gpu_cache.begin_frame(stamp.frame_id()); diff --git a/webrender/src/picture.rs b/webrender/src/picture.rs index 198abcaa6d..30b74396fe 100644 --- a/webrender/src/picture.rs +++ b/webrender/src/picture.rs @@ -441,7 +441,6 @@ impl TileCache { } } } - PrimitiveInstanceKind::LegacyPrimitive { .. } | PrimitiveInstanceKind::TextRun { .. } | PrimitiveInstanceKind::LineDecoration { .. } | PrimitiveInstanceKind::Clear | diff --git a/webrender/src/prim_store.rs b/webrender/src/prim_store.rs index f03f7ea080..a270f39012 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{AlphaType, BorderRadius, BuiltDisplayList, ClipMode, ColorF, PictureRect, ColorU, LayoutPrimitiveInfo}; +use api::{AlphaType, BorderRadius, ClipMode, ColorF, PictureRect, ColorU, LayoutPrimitiveInfo}; use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, ExtendMode, DeviceRect, LayoutSideOffsetsAu}; use api::{FilterOp, GlyphInstance, GradientStop, ImageKey, ImageRendering, TileOffset, RepeatMode}; use api::{RasterSpace, LayoutPoint, LayoutRect, LayoutSideOffsets, LayoutSize, LayoutToWorldTransform}; @@ -298,11 +298,6 @@ pub struct DeferredResolve { pub rendering: ImageRendering, } -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct PrimitiveIndex(pub usize); - #[derive(Debug, Copy, Clone, PartialEq)] pub struct ClipTaskIndex(pub u32); @@ -1553,23 +1548,6 @@ pub struct BorderSegmentInfo { pub cache_key: BorderSegmentCacheKey, } -pub enum BrushKind { - /* - RadialGradient { - stops_handle: GpuCacheHandle, - stops_range: ItemRange, - extend_mode: ExtendMode, - center: LayoutPoint, - start_radius: f32, - end_radius: f32, - ratio_xy: f32, - stretch_size: LayoutSize, - tile_spacing: LayoutSize, - visible_tiles_range: GradientTileRange, - }, - */ -} - bitflags! { /// Each bit of the edge AA mask is: /// 0, when the edge of the primitive needs to be considered for AA @@ -1681,20 +1659,6 @@ impl BrushSegment { } } -pub type BrushSegmentVec = SmallVec<[BrushSegment; 1]>; - -#[derive(Debug)] -pub struct BrushSegmentDescriptor { - pub segments: BrushSegmentVec, -} - -pub struct BrushPrimitive { - pub kind: BrushKind, - pub opacity: PrimitiveOpacity, - pub segment_desc: Option, - pub gpu_location: GpuCacheHandle, -} - // Key that identifies a unique (partial) image that is being // stored in the render task cache. #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)] @@ -2339,22 +2303,19 @@ impl PrimitiveContainer { } } - /// Convert a source primitive container into a key, and optionally - /// an old style PrimitiveDetails structure. + /// Convert a source primitive container into a key. pub fn build( self, info: &mut LayoutPrimitiveInfo, - ) -> (PrimitiveKeyKind, Option) { + ) -> PrimitiveKeyKind { match self { PrimitiveContainer::TextRun { font, offset, glyphs, shadow, .. } => { - let key = PrimitiveKeyKind::TextRun { + PrimitiveKeyKind::TextRun { font, offset: offset.to_au(), glyphs, shadow, - }; - - (key, None) + } } PrimitiveContainer::LinearGradient { extend_mode, @@ -2367,7 +2328,7 @@ impl PrimitiveContainer { nine_patch, .. } => { - let key = PrimitiveKeyKind::LinearGradient { + PrimitiveKeyKind::LinearGradient { extend_mode, start_point: start_point.into(), end_point: end_point.into(), @@ -2376,9 +2337,7 @@ impl PrimitiveContainer { stops, reverse_stops, nine_patch, - }; - - (key, None) + } } PrimitiveContainer::RadialGradient { extend_mode, @@ -2390,7 +2349,7 @@ impl PrimitiveContainer { stops, .. } => { - let key = PrimitiveKeyKind::RadialGradient { + PrimitiveKeyKind::RadialGradient { extend_mode, center: center.into(), params, @@ -2398,22 +2357,18 @@ impl PrimitiveContainer { tile_spacing: tile_spacing.into(), nine_patch, stops, - }; - - (key, None) + } } PrimitiveContainer::Clear => { - (PrimitiveKeyKind::Clear, None) + PrimitiveKeyKind::Clear } PrimitiveContainer::Rectangle { color, .. } => { - let key = PrimitiveKeyKind::Rectangle { + PrimitiveKeyKind::Rectangle { color: color.into(), - }; - - (key, None) + } } PrimitiveContainer::Image { alpha_type, key, stretch_size, color, tile_spacing, image_rendering, sub_rect, .. } => { - let key = PrimitiveKeyKind::Image { + PrimitiveKeyKind::Image { key, tile_spacing: tile_spacing.into(), stretch_size: stretch_size.into(), @@ -2421,36 +2376,28 @@ impl PrimitiveContainer { sub_rect, image_rendering, alpha_type, - }; - - (key, None) + } } PrimitiveContainer::YuvImage { color_depth, yuv_key, format, color_space, image_rendering, .. } => { - let key = PrimitiveKeyKind::YuvImage { + PrimitiveKeyKind::YuvImage { color_depth, yuv_key, format, color_space, image_rendering, - }; - - (key, None) + } } PrimitiveContainer::ImageBorder { request, nine_patch, .. } => { - let key = PrimitiveKeyKind::ImageBorder { + PrimitiveKeyKind::ImageBorder { request, nine_patch, - }; - - (key, None) + } } PrimitiveContainer::NormalBorder { border, widths, .. } => { - let key = PrimitiveKeyKind::NormalBorder { + PrimitiveKeyKind::NormalBorder { border: border.into(), widths: widths.to_au(), - }; - - (key, None) + } } PrimitiveContainer::LineDecoration { color, style, orientation, wavy_line_thickness } => { // For line decorations, we can construct the render task cache key @@ -2504,12 +2451,10 @@ impl PrimitiveContainer { } }); - let key = PrimitiveKeyKind::LineDecoration { + PrimitiveKeyKind::LineDecoration { cache_key, color: color.into(), - }; - - (key, None) + } } } } @@ -2580,16 +2525,6 @@ impl PrimitiveContainer { } } -pub enum PrimitiveDetails { - Brush(BrushPrimitive), -} - -pub struct Primitive { - pub local_rect: LayoutRect, - pub local_clip_rect: LayoutRect, - pub details: PrimitiveDetails, -} - /// Instance specific fields for an image primitive. These are /// currently stored in a separate array to avoid bloating the /// size of PrimitiveInstance. In the future, we should be able @@ -2620,11 +2555,6 @@ pub enum PrimitiveInstanceKind { Picture { pic_index: PictureIndex, }, - /// An old style, non-interned primitive. Uses prim_index to - /// access the primitive details in the prim_store. - LegacyPrimitive { - prim_index: PrimitiveIndex, - }, /// A run of glyphs, with associated font parameters. TextRun { run_index: TextRunIndex, @@ -2839,7 +2769,6 @@ impl PrimitiveScratchBuffer { #[cfg_attr(feature = "replay", derive(Deserialize))] #[derive(Clone, Debug)] pub struct PrimitiveStoreStats { - primitive_count: usize, picture_count: usize, text_run_count: usize, opacity_binding_count: usize, @@ -2849,7 +2778,6 @@ pub struct PrimitiveStoreStats { impl PrimitiveStoreStats { pub fn empty() -> Self { PrimitiveStoreStats { - primitive_count: 0, picture_count: 0, text_run_count: 0, opacity_binding_count: 0, @@ -2859,7 +2787,6 @@ impl PrimitiveStoreStats { } pub struct PrimitiveStore { - pub primitives: Vec, pub pictures: Vec, pub text_runs: TextRunStorage, @@ -2870,22 +2797,25 @@ pub struct PrimitiveStore { /// List of animated opacity bindings for a primitive. pub opacity_bindings: OpacityBindingStorage, + + /// Total count of primitive instances contained in pictures. + /// This is used for profile counters only. + pub prim_count: usize, } impl PrimitiveStore { pub fn new(stats: &PrimitiveStoreStats) -> PrimitiveStore { PrimitiveStore { - primitives: Vec::with_capacity(stats.primitive_count), pictures: Vec::with_capacity(stats.picture_count), text_runs: TextRunStorage::new(stats.text_run_count), images: ImageInstanceStorage::new(stats.image_count), opacity_bindings: OpacityBindingStorage::new(stats.opacity_binding_count), + prim_count: 0, } } pub fn get_stats(&self) -> PrimitiveStoreStats { PrimitiveStoreStats { - primitive_count: self.primitives.len(), picture_count: self.pictures.len(), text_run_count: self.text_runs.len(), image_count: self.images.len(), @@ -2898,6 +2828,7 @@ impl PrimitiveStore { prim: PicturePrimitive, ) -> PictureIndex { let index = PictureIndex(self.pictures.len()); + self.prim_count += prim.prim_list.prim_instances.len(); self.pictures.push(prim); index } @@ -2949,25 +2880,6 @@ impl PrimitiveStore { } } - pub fn add_primitive( - &mut self, - local_rect: &LayoutRect, - local_clip_rect: &LayoutRect, - details: PrimitiveDetails, - ) -> PrimitiveIndex { - let prim_index = self.primitives.len(); - - let prim = Primitive { - local_rect: *local_rect, - local_clip_rect: *local_clip_rect, - details, - }; - - self.primitives.push(prim); - - PrimitiveIndex(prim_index) - } - pub fn get_opacity_binding( &self, opacity_binding_index: OpacityBindingIndex, @@ -3011,7 +2923,6 @@ impl PrimitiveStore { PrimitiveInstanceKind::NormalBorder { .. } | PrimitiveInstanceKind::ImageBorder { .. } | PrimitiveInstanceKind::YuvImage { .. } | - PrimitiveInstanceKind::LegacyPrimitive { .. } | PrimitiveInstanceKind::LinearGradient { .. } | PrimitiveInstanceKind::RadialGradient { .. } | PrimitiveInstanceKind::LineDecoration { .. } => { @@ -3094,10 +3005,6 @@ impl PrimitiveStore { self.pictures[pic_index.0].requested_composite_mode = None; } - pub fn prim_count(&self) -> usize { - self.primitives.len() - } - pub fn prepare_prim_for_render( &mut self, prim_instance: &mut PrimitiveInstance, @@ -3106,7 +3013,6 @@ impl PrimitiveStore { pic_state: &mut PictureState, frame_context: &FrameBuildingContext, frame_state: &mut FrameBuildingState, - display_list: &BuiltDisplayList, plane_split_anchor: usize, resources: &mut FrameResources, scratch: &mut PrimitiveScratchBuffer, @@ -3143,7 +3049,6 @@ impl PrimitiveStore { PrimitiveInstanceKind::TextRun { .. } | PrimitiveInstanceKind::Rectangle { .. } | PrimitiveInstanceKind::LineDecoration { .. } | - PrimitiveInstanceKind::LegacyPrimitive { .. } | PrimitiveInstanceKind::NormalBorder { .. } | PrimitiveInstanceKind::ImageBorder { .. } | PrimitiveInstanceKind::YuvImage { .. } | @@ -3211,10 +3116,6 @@ impl PrimitiveStore { .prim_data_store[prim_instance.prim_data_handle]; (prim_data.prim_rect, prim_data.clip_rect) } - PrimitiveInstanceKind::LegacyPrimitive { prim_index } => { - let prim = &self.primitives[prim_index.0]; - (prim.local_rect, prim.local_clip_rect) - } }; // TODO(gw): Eventually we can move all the code handling below for @@ -3420,19 +3321,6 @@ impl PrimitiveStore { scratch, ); } - PrimitiveInstanceKind::LegacyPrimitive { prim_index } => { - let prim_details = &mut self.primitives[prim_index.0].details; - - prim_instance.prepare_prim_for_render_inner( - prim_local_rect, - prim_details, - prim_context, - pic_context, - frame_state, - display_list, - scratch, - ); - } } true @@ -3448,12 +3336,6 @@ impl PrimitiveStore { resources: &mut FrameResources, scratch: &mut PrimitiveScratchBuffer, ) { - let display_list = &frame_context - .pipelines - .get(&pic_context.pipeline_id) - .expect("No display list?") - .display_list; - for (plane_split_anchor, prim_instance) in prim_list.prim_instances.iter_mut().enumerate() { prim_instance.bounding_rect = None; @@ -3524,7 +3406,6 @@ impl PrimitiveStore { pic_state, frame_context, frame_state, - display_list, plane_split_anchor, resources, scratch, @@ -4206,53 +4087,6 @@ impl PrimitiveInstance { // These primitives don't support / need segments. return; } - PrimitiveInstanceKind::LegacyPrimitive { prim_index } => { - let prim = &mut prim_store.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. - 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, - }); - } - } - } - } - } - - return; - } }; if *segment_instance_index == SegmentInstanceIndex::INVALID { @@ -4410,21 +4244,6 @@ impl PrimitiveInstance { } } } - PrimitiveInstanceKind::LegacyPrimitive { prim_index } => { - let prim = &prim_store.primitives[prim_index.0]; - match prim.details { - PrimitiveDetails::Brush(ref brush) => { - match brush.segment_desc { - Some(ref description) => { - &description.segments - } - None => { - return false; - } - } - } - } - } }; // If there are no segments, early out to avoid setting a valid @@ -4494,18 +4313,6 @@ impl PrimitiveInstance { true } - fn prepare_prim_for_render_inner( - &mut self, - _prim_local_rect: LayoutRect, - _prim_details: &mut PrimitiveDetails, - _prim_context: &PrimitiveContext, - _pic_context: &PictureContext, - _frame_state: &mut FrameBuildingState, - _display_list: &BuiltDisplayList, - _scratch: &mut PrimitiveScratchBuffer, - ) { - } - fn update_clip_task( &mut self, prim_local_rect: LayoutRect, @@ -4694,12 +4501,11 @@ fn test_struct_sizes() { // test expectations and move on. // (b) You made a structure larger. This is not necessarily a problem, but should only // be done with care, and after checking if talos performance regresses badly. - assert_eq!(mem::size_of::(), 176, "PrimitiveContainer size changed"); + assert_eq!(mem::size_of::(), 136, "PrimitiveContainer size changed"); assert_eq!(mem::size_of::(), 120, "PrimitiveInstance size changed"); assert_eq!(mem::size_of::(), 16, "PrimitiveInstanceKind size changed"); assert_eq!(mem::size_of::(), 176, "PrimitiveTemplate size changed"); assert_eq!(mem::size_of::(), 112, "PrimitiveTemplateKind size changed"); assert_eq!(mem::size_of::(), 152, "PrimitiveKey size changed"); assert_eq!(mem::size_of::(), 112, "PrimitiveKeyKind size changed"); - assert_eq!(mem::size_of::(), 200, "Primitive size changed"); } diff --git a/webrender/src/render_backend.rs b/webrender/src/render_backend.rs index 0fc3123afd..28fa06e9b6 100644 --- a/webrender/src/render_backend.rs +++ b/webrender/src/render_backend.rs @@ -49,7 +49,6 @@ use serde_json; use std::path::PathBuf; use std::sync::atomic::{AtomicUsize, Ordering}; use std::mem::replace; -use std::os::raw::c_void; use std::sync::mpsc::{channel, Sender, Receiver}; use std::time::{UNIX_EPOCH, SystemTime}; use std::u32; @@ -1369,18 +1368,12 @@ impl RenderBackend { serde_json::to_string(&debug_root).unwrap() } - fn size_of(&self, ptr: *const T) -> usize { - let op = self.size_of_op.as_ref().unwrap(); - unsafe { op(ptr as *const c_void) } - } - fn report_memory(&self) -> MemoryReport { let mut report = MemoryReport::default(); let op = self.size_of_op.unwrap(); report.gpu_cache_metadata = self.gpu_cache.malloc_size_of(op); for (_id, doc) in &self.documents { if let Some(ref fb) = doc.frame_builder { - report.primitive_stores += self.size_of(fb.prim_store.primitives.as_ptr()); report.clip_stores += fb.clip_store.malloc_size_of(op); } report.hit_testers += diff --git a/webrender/src/surface.rs b/webrender/src/surface.rs index 60b94c6164..c4f39fb00e 100644 --- a/webrender/src/surface.rs +++ b/webrender/src/surface.rs @@ -236,8 +236,7 @@ impl SurfaceDescriptor { // a legacy primitive or picture, then fail to create a cache // descriptor. match prim_instance.kind { - PrimitiveInstanceKind::Picture { .. } | - PrimitiveInstanceKind::LegacyPrimitive { .. } => { + PrimitiveInstanceKind::Picture { .. } => { return None; } PrimitiveInstanceKind::Image { .. } | diff --git a/webrender_api/src/display_item.rs b/webrender_api/src/display_item.rs index 4700871042..70c8c7ddb1 100644 --- a/webrender_api/src/display_item.rs +++ b/webrender_api/src/display_item.rs @@ -463,7 +463,7 @@ pub struct Shadow { pub blur_radius: f32, } -#[repr(u32)] +#[repr(u8)] #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Ord, PartialOrd)] pub enum ExtendMode { Clamp, From 5f88090b4b769e5273559c88c492aedfce7bbdee Mon Sep 17 00:00:00 2001 From: Glenn Watson Date: Mon, 26 Nov 2018 15:56:24 +1000 Subject: [PATCH 3/3] Remove PrimitiveContainer --- webrender/src/border.rs | 20 +- webrender/src/box_shadow.rs | 13 +- webrender/src/display_list_flattener.rs | 146 +++++++---- webrender/src/prim_store.rs | 312 ++++-------------------- webrender_api/src/display_item.rs | 10 - 5 files changed, 163 insertions(+), 338 deletions(-) diff --git a/webrender/src/border.rs b/webrender/src/border.rs index cc16a826b8..c22a428f6f 100644 --- a/webrender/src/border.rs +++ b/webrender/src/border.rs @@ -11,7 +11,7 @@ use euclid::vec2; use display_list_flattener::DisplayListFlattener; use gpu_types::{BorderInstance, BorderSegment, BrushFlags}; use prim_store::{BorderSegmentInfo, BrushSegment, NinePatchDescriptor}; -use prim_store::{EdgeAaSegmentMask, PrimitiveContainer, ScrollNodeAndClipChain}; +use prim_store::{EdgeAaSegmentMask, ScrollNodeAndClipChain, PrimitiveKeyKind}; use util::{lerp, RectHelpers}; // Using 2048 as the maximum radius in device space before which we @@ -117,6 +117,18 @@ pub struct NormalBorderAu { pub do_aa: bool, } +impl NormalBorderAu { + // Construct a border based upon self with color + pub fn with_color(&self, color: ColorU) -> Self { + let mut b = self.clone(); + b.left.color = color; + b.right.color = color; + b.top.color = color; + b.bottom.color = color; + b + } +} + impl From for NormalBorderAu { fn from(border: NormalBorder) -> Self { NormalBorderAu { @@ -217,9 +229,9 @@ impl<'a> DisplayListFlattener<'a> { clip_and_scroll, info, Vec::new(), - PrimitiveContainer::NormalBorder { - border, - widths, + PrimitiveKeyKind::NormalBorder { + border: border.into(), + widths: widths.to_au(), }, ); } diff --git a/webrender/src/box_shadow.rs b/webrender/src/box_shadow.rs index 7e9170bb9a..eaaa4fd71b 100644 --- a/webrender/src/box_shadow.rs +++ b/webrender/src/box_shadow.rs @@ -8,8 +8,7 @@ use clip::ClipItemKey; use display_list_flattener::DisplayListFlattener; use gpu_cache::GpuCacheHandle; use gpu_types::BoxShadowStretchMode; -use prim_store::PrimitiveContainer; -use prim_store::ScrollNodeAndClipChain; +use prim_store::{ScrollNodeAndClipChain, PrimitiveKeyKind}; use render_task::RenderTaskCacheEntryHandle; use util::RectHelpers; @@ -73,7 +72,7 @@ impl<'a> DisplayListFlattener<'a> { clip_and_scroll: ScrollNodeAndClipChain, prim_info: &LayoutPrimitiveInfo, box_offset: &LayoutVector2D, - color: &ColorF, + color: ColorF, mut blur_radius: f32, spread_radius: f32, border_radius: BorderRadius, @@ -149,8 +148,8 @@ impl<'a> DisplayListFlattener<'a> { clip_and_scroll, &LayoutPrimitiveInfo::with_clip_rect(final_prim_rect, prim_info.clip_rect), clips, - PrimitiveContainer::Rectangle { - color: *color, + PrimitiveKeyKind::Rectangle { + color: color.into(), }, ); } else { @@ -172,8 +171,8 @@ impl<'a> DisplayListFlattener<'a> { // Draw the box-shadow as a solid rect, using a box-shadow // clip mask item. - let prim = PrimitiveContainer::Rectangle { - color: *color, + let prim = PrimitiveKeyKind::Rectangle { + color: color.into(), }; // Create the box-shadow clip item. diff --git a/webrender/src/display_list_flattener.rs b/webrender/src/display_list_flattener.rs index ab5a367819..efa17f972d 100644 --- a/webrender/src/display_list_flattener.rs +++ b/webrender/src/display_list_flattener.rs @@ -5,7 +5,7 @@ use api::{AlphaType, BorderDetails, BorderDisplayItem, BuiltDisplayListIter, ClipAndScrollInfo}; use api::{ClipId, ColorF, ComplexClipRegion, DeviceIntPoint, DeviceIntRect, DeviceIntSize}; -use api::{DisplayItemRef, ExtendMode, ExternalScrollId}; +use api::{DisplayItemRef, ExtendMode, ExternalScrollId, AuHelpers}; use api::{FilterOp, FontInstanceKey, GlyphInstance, GlyphOptions, RasterSpace, GradientStop}; use api::{IframeDisplayItem, ImageKey, ImageRendering, ItemRange, LayoutPoint, ColorDepth}; use api::{LayoutPrimitiveInfo, LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D}; @@ -13,6 +13,7 @@ use api::{LineOrientation, LineStyle, NinePatchBorderSource, PipelineId}; use api::{PropertyBinding, ReferenceFrame, ScrollFrameDisplayItem, ScrollSensitivity}; use api::{Shadow, SpecificDisplayItem, StackingContext, StickyFrameDisplayItem, TexelRect}; use api::{ClipMode, TransformStyle, YuvColorSpace, YuvData}; +use app_units::Au; use clip::{ClipChainId, ClipRegion, ClipItemKey, ClipStore, ClipItemSceneData}; use clip_scroll_tree::{ROOT_SPATIAL_NODE_INDEX, ClipScrollTree, SpatialNodeIndex}; use frame_builder::{ChasePrimitive, FrameBuilder, FrameBuilderConfig}; @@ -23,8 +24,8 @@ use internal_types::{FastHashMap, FastHashSet}; use picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive, PrimitiveList}; use prim_store::{PrimitiveInstance, PrimitiveDataInterner, PrimitiveKeyKind, RadialGradientParams}; use prim_store::{PrimitiveKey, PrimitiveSceneData, PrimitiveInstanceKind, GradientStopKey, NinePatchDescriptor}; -use prim_store::{PrimitiveContainer, PrimitiveDataHandle, PrimitiveStore, PrimitiveStoreStats}; -use prim_store::{ScrollNodeAndClipChain, PictureIndex, register_prim_chase_id}; +use prim_store::{PrimitiveDataHandle, PrimitiveStore, PrimitiveStoreStats, LineDecorationCacheKey}; +use prim_store::{ScrollNodeAndClipChain, PictureIndex, register_prim_chase_id, get_line_decoration_sizes}; use render_backend::{DocumentView}; use resource_cache::{FontInstanceMap, ImageRequest}; use scene::{Scene, ScenePipeline, StackingContextHelpers}; @@ -573,12 +574,12 @@ impl<'a> DisplayListFlattener<'a> { &prim_info, info.wavy_line_thickness, info.orientation, - &info.color, + info.color, info.style, ); } SpecificDisplayItem::Gradient(ref info) => { - if let Some(prim) = self.create_linear_gradient_prim( + if let Some(prim_key_kind) = self.create_linear_gradient_prim( &prim_info, info.gradient.start_point, info.gradient.end_point, @@ -593,12 +594,12 @@ impl<'a> DisplayListFlattener<'a> { clip_and_scroll, &prim_info, Vec::new(), - prim, + prim_key_kind, ); } } SpecificDisplayItem::RadialGradient(ref info) => { - let prim = self.create_radial_gradient_prim( + let prim_key_kind = self.create_radial_gradient_prim( &prim_info, info.gradient.center, info.gradient.start_offset * info.gradient.radius.width, @@ -615,7 +616,7 @@ impl<'a> DisplayListFlattener<'a> { clip_and_scroll, &prim_info, Vec::new(), - prim, + prim_key_kind, ); } SpecificDisplayItem::BoxShadow(ref box_shadow_info) => { @@ -628,7 +629,7 @@ impl<'a> DisplayListFlattener<'a> { clip_and_scroll, &prim_info, &box_shadow_info.offset, - &box_shadow_info.color, + box_shadow_info.color, box_shadow_info.blur_radius, box_shadow_info.spread_radius, box_shadow_info.border_radius, @@ -842,12 +843,9 @@ impl<'a> DisplayListFlattener<'a> { info: &LayoutPrimitiveInfo, clip_chain_id: ClipChainId, spatial_node_index: SpatialNodeIndex, - container: PrimitiveContainer, + prim_key_kind: PrimitiveKeyKind, ) -> PrimitiveInstance { // Build a primitive key. - let mut info = info.clone(); - let prim_key_kind = container.build(&mut info); - let prim_key = PrimitiveKey::new( info.is_backface_visible, info.rect, @@ -924,12 +922,12 @@ impl<'a> DisplayListFlattener<'a> { clip_and_scroll: ScrollNodeAndClipChain, info: &LayoutPrimitiveInfo, clip_items: Vec, - container: PrimitiveContainer, + key_kind: PrimitiveKeyKind, ) { // If a shadow context is not active, then add the primitive // directly to the parent picture. if self.pending_shadow_items.is_empty() { - if container.is_visible() { + if key_kind.is_visible() { let clip_chain_id = self.build_clip_chain( clip_items, clip_and_scroll.spatial_node_index, @@ -939,7 +937,7 @@ impl<'a> DisplayListFlattener<'a> { info, clip_chain_id, clip_and_scroll.spatial_node_index, - container, + key_kind, ); self.register_chase_primitive_by_rect( &info.rect, @@ -956,7 +954,7 @@ impl<'a> DisplayListFlattener<'a> { self.pending_shadow_items.push_back(ShadowItem::Primitive(PendingPrimitive { clip_and_scroll, info: *info, - container, + key_kind, })); } } @@ -1542,7 +1540,7 @@ impl<'a> DisplayListFlattener<'a> { &info, pending_primitive.clip_and_scroll.clip_chain_id, pending_primitive.clip_and_scroll.spatial_node_index, - pending_primitive.container.create_shadow( + pending_primitive.key_kind.create_shadow( &pending_shadow.shadow, ), ); @@ -1613,12 +1611,12 @@ impl<'a> DisplayListFlattener<'a> { ShadowItem::Primitive(pending_primitive) => { // For a normal primitive, if it has alpha > 0, then we add this // as a normal primitive to the parent picture. - if pending_primitive.container.is_visible() { + if pending_primitive.key_kind.is_visible() { let prim_instance = self.create_primitive( &pending_primitive.info, pending_primitive.clip_and_scroll.clip_chain_id, pending_primitive.clip_and_scroll.spatial_node_index, - pending_primitive.container, + pending_primitive.key_kind, ); self.register_chase_primitive_by_rect( &pending_primitive.info.rect, @@ -1672,8 +1670,8 @@ impl<'a> DisplayListFlattener<'a> { clip_and_scroll, info, Vec::new(), - PrimitiveContainer::Rectangle { - color, + PrimitiveKeyKind::Rectangle { + color: color.into(), }, ); } @@ -1687,7 +1685,7 @@ impl<'a> DisplayListFlattener<'a> { clip_and_scroll, info, Vec::new(), - PrimitiveContainer::Clear, + PrimitiveKeyKind::Clear, ); } @@ -1697,21 +1695,69 @@ impl<'a> DisplayListFlattener<'a> { info: &LayoutPrimitiveInfo, wavy_line_thickness: f32, orientation: LineOrientation, - color: &ColorF, + color: ColorF, style: LineStyle, ) { - let container = PrimitiveContainer::LineDecoration { - color: *color, - style, + // For line decorations, we can construct the render task cache key + // here during scene building, since it doesn't depend on device + // pixel ratio or transform. + let mut info = info.clone(); + + let size = get_line_decoration_sizes( + &info.rect.size, orientation, + style, wavy_line_thickness, - }; + ); + + let cache_key = size.map(|(inline_size, block_size)| { + let size = match orientation { + LineOrientation::Horizontal => LayoutSize::new(inline_size, block_size), + LineOrientation::Vertical => LayoutSize::new(block_size, inline_size), + }; + + // If dotted, adjust the clip rect to ensure we don't draw a final + // partial dot. + if style == LineStyle::Dotted { + let clip_size = match orientation { + LineOrientation::Horizontal => { + LayoutSize::new( + inline_size * (info.rect.size.width / inline_size).floor(), + info.rect.size.height, + ) + } + LineOrientation::Vertical => { + LayoutSize::new( + info.rect.size.width, + inline_size * (info.rect.size.height / inline_size).floor(), + ) + } + }; + let clip_rect = LayoutRect::new( + info.rect.origin, + clip_size, + ); + info.clip_rect = clip_rect + .intersection(&info.clip_rect) + .unwrap_or(LayoutRect::zero()); + } + + LineDecorationCacheKey { + style, + orientation, + wavy_line_thickness: Au::from_f32_px(wavy_line_thickness), + size: size.to_au(), + } + }); self.add_primitive( clip_and_scroll, - info, + &info, Vec::new(), - container, + PrimitiveKeyKind::LineDecoration { + cache_key, + color: color.into(), + }, ); } @@ -1738,7 +1784,7 @@ impl<'a> DisplayListFlattener<'a> { let prim = match border.source { NinePatchBorderSource::Image(image_key) => { - PrimitiveContainer::ImageBorder { + PrimitiveKeyKind::ImageBorder { request: ImageRequest { key: image_key, rendering: ImageRendering::Auto, @@ -1809,7 +1855,7 @@ impl<'a> DisplayListFlattener<'a> { mut tile_spacing: LayoutSize, pipeline_id: PipelineId, nine_patch: Option>, - ) -> Option { + ) -> Option { let mut prim_rect = info.rect; simplify_repeated_primitive(&stretch_size, &mut tile_spacing, &mut prim_rect); @@ -1851,12 +1897,12 @@ impl<'a> DisplayListFlattener<'a> { (start_point, end_point) }; - Some(PrimitiveContainer::LinearGradient { + Some(PrimitiveKeyKind::LinearGradient { extend_mode, - start_point: sp, - end_point: ep, - stretch_size, - tile_spacing, + start_point: sp.into(), + end_point: ep.into(), + stretch_size: stretch_size.into(), + tile_spacing: tile_spacing.into(), stops, reverse_stops, nine_patch, @@ -1876,7 +1922,7 @@ impl<'a> DisplayListFlattener<'a> { mut tile_spacing: LayoutSize, pipeline_id: PipelineId, nine_patch: Option>, - ) -> PrimitiveContainer { + ) -> PrimitiveKeyKind { let mut prim_rect = info.rect; simplify_repeated_primitive(&stretch_size, &mut tile_spacing, &mut prim_rect); @@ -1898,12 +1944,12 @@ impl<'a> DisplayListFlattener<'a> { } }).collect(); - PrimitiveContainer::RadialGradient { + PrimitiveKeyKind::RadialGradient { extend_mode, - center, + center: center.into(), params, - stretch_size, - tile_spacing, + stretch_size: stretch_size.into(), + tile_spacing: tile_spacing.into(), nine_patch, stops, } @@ -1969,10 +2015,10 @@ impl<'a> DisplayListFlattener<'a> { // primitive template. let glyphs = display_list.get(glyph_range).collect(); - PrimitiveContainer::TextRun { + PrimitiveKeyKind::TextRun { glyphs, font, - offset, + offset: offset.to_au(), shadow: false, } }; @@ -2021,11 +2067,11 @@ impl<'a> DisplayListFlattener<'a> { clip_and_scroll, &info, Vec::new(), - PrimitiveContainer::Image { + PrimitiveKeyKind::Image { key: image_key, - tile_spacing, - stretch_size, - color, + tile_spacing: tile_spacing.into(), + stretch_size: stretch_size.into(), + color: color.into(), sub_rect, image_rendering, alpha_type, @@ -2053,7 +2099,7 @@ impl<'a> DisplayListFlattener<'a> { clip_and_scroll, info, Vec::new(), - PrimitiveContainer::YuvImage { + PrimitiveKeyKind::YuvImage { color_depth, yuv_key, format, @@ -2192,7 +2238,7 @@ impl FlattenedStackingContext { struct PendingPrimitive { clip_and_scroll: ScrollNodeAndClipChain, info: LayoutPrimitiveInfo, - container: PrimitiveContainer, + key_kind: PrimitiveKeyKind, } /// As shadows are pushed, they are stored as pending diff --git a/webrender/src/prim_store.rs b/webrender/src/prim_store.rs index a270f39012..4b14feabf5 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -2,11 +2,11 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{AlphaType, BorderRadius, ClipMode, ColorF, PictureRect, ColorU, LayoutPrimitiveInfo}; +use api::{AlphaType, BorderRadius, ClipMode, ColorF, PictureRect, ColorU}; use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, ExtendMode, DeviceRect, LayoutSideOffsetsAu}; use api::{FilterOp, GlyphInstance, GradientStop, ImageKey, ImageRendering, TileOffset, RepeatMode}; use api::{RasterSpace, LayoutPoint, LayoutRect, LayoutSideOffsets, LayoutSize, LayoutToWorldTransform}; -use api::{LayoutVector2D, PremultipliedColorF, PropertyBinding, Shadow, YuvColorSpace, YuvFormat}; +use api::{PremultipliedColorF, PropertyBinding, Shadow, YuvColorSpace, YuvFormat}; use api::{DeviceIntSideOffsets, WorldPixel, BoxShadowClipMode, NormalBorder, WorldRect, LayoutToWorldScale}; use api::{PicturePixel, RasterPixel, ColorDepth, LineStyle, LineOrientation, LayoutSizeAu, AuHelpers, LayoutVector2DAu}; use app_units::Au; @@ -529,7 +529,7 @@ impl From> for SideOffsetsKey { /// A hashable size for using as a key during primitive interning. #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] -#[derive(Debug, Clone, PartialEq)] +#[derive(Copy, Debug, Clone, PartialEq)] pub struct SizeKey { w: f32, h: f32, @@ -944,10 +944,7 @@ impl PrimitiveKeyKind { let mut brush_segments = Vec::new(); if let Some(ref nine_patch) = nine_patch { - brush_segments = create_nine_patch_segments( - rect, - nine_patch, - ); + brush_segments = nine_patch.create_segments(rect); } let stops = stops.iter().map(|stop| { @@ -1673,10 +1670,10 @@ pub struct ImageCacheKey { #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct LineDecorationCacheKey { - style: LineStyle, - orientation: LineOrientation, - wavy_line_thickness: Au, - size: LayoutSizeAu, + pub style: LineStyle, + pub orientation: LineOrientation, + pub wavy_line_thickness: Au, + pub size: LayoutSizeAu, } // Where to find the texture data for an image primitive. @@ -2212,69 +2209,7 @@ pub struct NinePatchDescriptor { pub widths: SideOffsetsKey, } -pub enum PrimitiveContainer { - TextRun { - font: FontInstance, - offset: LayoutVector2D, - glyphs: Vec, - shadow: bool, - }, - Clear, - LineDecoration { - color: ColorF, - style: LineStyle, - orientation: LineOrientation, - wavy_line_thickness: f32, - }, - NormalBorder { - border: NormalBorder, - widths: LayoutSideOffsets, - }, - ImageBorder { - request: ImageRequest, - nine_patch: NinePatchDescriptor, - }, - Rectangle { - color: ColorF, - }, - YuvImage { - color_depth: ColorDepth, - yuv_key: [ImageKey; 3], - format: YuvFormat, - color_space: YuvColorSpace, - image_rendering: ImageRendering, - }, - Image { - key: ImageKey, - color: ColorF, - tile_spacing: LayoutSize, - stretch_size: LayoutSize, - sub_rect: Option, - image_rendering: ImageRendering, - alpha_type: AlphaType, - }, - LinearGradient { - extend_mode: ExtendMode, - start_point: LayoutPoint, - end_point: LayoutPoint, - stretch_size: LayoutSize, - tile_spacing: LayoutSize, - stops: Vec, - reverse_stops: bool, - nine_patch: Option>, - }, - RadialGradient { - extend_mode: ExtendMode, - center: LayoutPoint, - params: RadialGradientParams, - stretch_size: LayoutSize, - tile_spacing: LayoutSize, - stops: Vec, - nine_patch: Option>, - }, -} - -impl PrimitiveContainer { +impl PrimitiveKeyKind { // Return true if the primary primitive is visible. // Used to trivially reject non-visible primitives. // TODO(gw): Currently, primitives other than those @@ -2284,177 +2219,22 @@ impl PrimitiveContainer { // primitive types to use this. pub fn is_visible(&self) -> bool { match *self { - PrimitiveContainer::TextRun { ref font, .. } => { + PrimitiveKeyKind::TextRun { ref font, .. } => { font.color.a > 0 } - PrimitiveContainer::NormalBorder { .. } | - PrimitiveContainer::ImageBorder { .. } | - PrimitiveContainer::YuvImage { .. } | - PrimitiveContainer::Image { .. } | - PrimitiveContainer::LinearGradient { .. } | - PrimitiveContainer::RadialGradient { .. } | - PrimitiveContainer::Clear => { + PrimitiveKeyKind::NormalBorder { .. } | + PrimitiveKeyKind::ImageBorder { .. } | + PrimitiveKeyKind::YuvImage { .. } | + PrimitiveKeyKind::Image { .. } | + PrimitiveKeyKind::LinearGradient { .. } | + PrimitiveKeyKind::RadialGradient { .. } | + PrimitiveKeyKind::Clear | + PrimitiveKeyKind::Unused => { true } - PrimitiveContainer::Rectangle { ref color, .. } | - PrimitiveContainer::LineDecoration { ref color, .. } => { - color.a > 0.0 - } - } - } - - /// Convert a source primitive container into a key. - pub fn build( - self, - info: &mut LayoutPrimitiveInfo, - ) -> PrimitiveKeyKind { - match self { - PrimitiveContainer::TextRun { font, offset, glyphs, shadow, .. } => { - PrimitiveKeyKind::TextRun { - font, - offset: offset.to_au(), - glyphs, - shadow, - } - } - PrimitiveContainer::LinearGradient { - extend_mode, - start_point, - end_point, - stretch_size, - tile_spacing, - stops, - reverse_stops, - nine_patch, - .. - } => { - PrimitiveKeyKind::LinearGradient { - extend_mode, - start_point: start_point.into(), - end_point: end_point.into(), - stretch_size: stretch_size.into(), - tile_spacing: tile_spacing.into(), - stops, - reverse_stops, - nine_patch, - } - } - PrimitiveContainer::RadialGradient { - extend_mode, - center, - params, - stretch_size, - tile_spacing, - nine_patch, - stops, - .. - } => { - PrimitiveKeyKind::RadialGradient { - extend_mode, - center: center.into(), - params, - stretch_size: stretch_size.into(), - tile_spacing: tile_spacing.into(), - nine_patch, - stops, - } - } - PrimitiveContainer::Clear => { - PrimitiveKeyKind::Clear - } - PrimitiveContainer::Rectangle { color, .. } => { - PrimitiveKeyKind::Rectangle { - color: color.into(), - } - } - PrimitiveContainer::Image { alpha_type, key, stretch_size, color, tile_spacing, image_rendering, sub_rect, .. } => { - PrimitiveKeyKind::Image { - key, - tile_spacing: tile_spacing.into(), - stretch_size: stretch_size.into(), - color: color.into(), - sub_rect, - image_rendering, - alpha_type, - } - } - PrimitiveContainer::YuvImage { color_depth, yuv_key, format, color_space, image_rendering, .. } => { - PrimitiveKeyKind::YuvImage { - color_depth, - yuv_key, - format, - color_space, - image_rendering, - } - } - PrimitiveContainer::ImageBorder { request, nine_patch, .. } => { - PrimitiveKeyKind::ImageBorder { - request, - nine_patch, - } - } - PrimitiveContainer::NormalBorder { border, widths, .. } => { - PrimitiveKeyKind::NormalBorder { - border: border.into(), - widths: widths.to_au(), - } - } - PrimitiveContainer::LineDecoration { color, style, orientation, wavy_line_thickness } => { - // For line decorations, we can construct the render task cache key - // here during scene building, since it doesn't depend on device - // pixel ratio or transform. - - let size = get_line_decoration_sizes( - &info.rect.size, - orientation, - style, - wavy_line_thickness, - ); - - let cache_key = size.map(|(inline_size, block_size)| { - let size = match orientation { - LineOrientation::Horizontal => LayoutSize::new(inline_size, block_size), - LineOrientation::Vertical => LayoutSize::new(block_size, inline_size), - }; - - // If dotted, adjust the clip rect to ensure we don't draw a final - // partial dot. - if style == LineStyle::Dotted { - let clip_size = match orientation { - LineOrientation::Horizontal => { - LayoutSize::new( - inline_size * (info.rect.size.width / inline_size).floor(), - info.rect.size.height, - ) - } - LineOrientation::Vertical => { - LayoutSize::new( - info.rect.size.width, - inline_size * (info.rect.size.height / inline_size).floor(), - ) - } - }; - let clip_rect = LayoutRect::new( - info.rect.origin, - clip_size, - ); - info.clip_rect = clip_rect - .intersection(&info.clip_rect) - .unwrap_or(LayoutRect::zero()); - } - - LineDecorationCacheKey { - style, - orientation, - wavy_line_thickness: Au::from_f32_px(wavy_line_thickness), - size: size.to_au(), - } - }); - - PrimitiveKeyKind::LineDecoration { - cache_key, - color: color.into(), - } + PrimitiveKeyKind::Rectangle { ref color, .. } | + PrimitiveKeyKind::LineDecoration { ref color, .. } => { + color.a > 0 } } } @@ -2465,9 +2245,9 @@ impl PrimitiveContainer { pub fn create_shadow( &self, shadow: &Shadow, - ) -> PrimitiveContainer { + ) -> PrimitiveKeyKind { match *self { - PrimitiveContainer::TextRun { ref font, offset, ref glyphs, .. } => { + PrimitiveKeyKind::TextRun { ref font, offset, ref glyphs, .. } => { let mut font = FontInstance { color: shadow.color.into(), ..font.clone() @@ -2476,49 +2256,48 @@ impl PrimitiveContainer { font.disable_subpixel_aa(); } - PrimitiveContainer::TextRun { + PrimitiveKeyKind::TextRun { font, glyphs: glyphs.clone(), - offset: offset + shadow.offset, + offset: offset + shadow.offset.to_au(), shadow: true, } } - PrimitiveContainer::LineDecoration { style, orientation, wavy_line_thickness, .. } => { - PrimitiveContainer::LineDecoration { - color: shadow.color, - style, - orientation, - wavy_line_thickness, + PrimitiveKeyKind::LineDecoration { ref cache_key, .. } => { + PrimitiveKeyKind::LineDecoration { + color: shadow.color.into(), + cache_key: cache_key.clone(), } } - PrimitiveContainer::Rectangle { .. } => { - PrimitiveContainer::Rectangle { - color: shadow.color, + PrimitiveKeyKind::Rectangle { .. } => { + PrimitiveKeyKind::Rectangle { + color: shadow.color.into(), } } - PrimitiveContainer::NormalBorder { border, widths, .. } => { - let border = border.with_color(shadow.color); - PrimitiveContainer::NormalBorder { + PrimitiveKeyKind::NormalBorder { ref border, widths, .. } => { + let border = border.with_color(shadow.color.into()); + PrimitiveKeyKind::NormalBorder { border, widths, } } - PrimitiveContainer::Image { alpha_type, image_rendering, tile_spacing, stretch_size, key, sub_rect, .. } => { - PrimitiveContainer::Image { + PrimitiveKeyKind::Image { alpha_type, image_rendering, tile_spacing, stretch_size, key, sub_rect, .. } => { + PrimitiveKeyKind::Image { tile_spacing, stretch_size, key, sub_rect, image_rendering, alpha_type, - color: shadow.color, + color: shadow.color.into(), } } - PrimitiveContainer::ImageBorder { .. } | - PrimitiveContainer::YuvImage { .. } | - PrimitiveContainer::LinearGradient { .. } | - PrimitiveContainer::RadialGradient { .. } | - PrimitiveContainer::Clear => { + PrimitiveKeyKind::ImageBorder { .. } | + PrimitiveKeyKind::YuvImage { .. } | + PrimitiveKeyKind::LinearGradient { .. } | + PrimitiveKeyKind::RadialGradient { .. } | + PrimitiveKeyKind::Unused | + PrimitiveKeyKind::Clear => { panic!("bug: this prim is not supported in shadow contexts"); } } @@ -4435,7 +4214,7 @@ pub fn get_raster_rects( /// Get the inline (horizontal) and block (vertical) sizes /// for a given line decoration. -fn get_line_decoration_sizes( +pub fn get_line_decoration_sizes( rect_size: &LayoutSize, orientation: LineOrientation, style: LineStyle, @@ -4501,7 +4280,6 @@ fn test_struct_sizes() { // test expectations and move on. // (b) You made a structure larger. This is not necessarily a problem, but should only // be done with care, and after checking if talos performance regresses badly. - assert_eq!(mem::size_of::(), 136, "PrimitiveContainer size changed"); assert_eq!(mem::size_of::(), 120, "PrimitiveInstance size changed"); assert_eq!(mem::size_of::(), 16, "PrimitiveInstanceKind size changed"); assert_eq!(mem::size_of::(), 176, "PrimitiveTemplate size changed"); diff --git a/webrender_api/src/display_item.rs b/webrender_api/src/display_item.rs index 70c8c7ddb1..6cadaf3218 100644 --- a/webrender_api/src/display_item.rs +++ b/webrender_api/src/display_item.rs @@ -274,16 +274,6 @@ pub struct NormalBorder { } impl NormalBorder { - // Construct a border based upon self with color - pub fn with_color(&self, color: ColorF) -> Self { - let mut b = *self; - b.left.color = color; - b.right.color = color; - b.top.color = color; - b.bottom.color = color; - b - } - fn can_disable_antialiasing(&self) -> bool { fn is_valid(style: BorderStyle) -> bool { style == BorderStyle::Solid || style == BorderStyle::None