From 2ca99dddf2840ef9d58c0523809163d0752945fe Mon Sep 17 00:00:00 2001 From: Dan Glastonbury Date: Wed, 5 Dec 2018 10:18:09 +1000 Subject: [PATCH] Extract Linear and Radial Gradients. --- webrender/src/batch.rs | 32 +- webrender/src/display_list_flattener.rs | 100 ++-- webrender/src/picture.rs | 10 +- webrender/src/prim_store/gradient.rs | 729 ++++++++++++++++++++++++ webrender/src/prim_store/mod.rs | 603 ++------------------ webrender/src/profiler.rs | 6 + webrender/src/render_backend.rs | 21 +- webrender/src/scene_builder.rs | 46 ++ 8 files changed, 934 insertions(+), 613 deletions(-) create mode 100644 webrender/src/prim_store/gradient.rs diff --git a/webrender/src/batch.rs b/webrender/src/batch.rs index 69742d65ab..8a29fcdd7e 100644 --- a/webrender/src/batch.rs +++ b/webrender/src/batch.rs @@ -1790,13 +1790,7 @@ impl AlphaBatchBuilder { } } PrimitiveInstanceKind::LinearGradient { data_handle, ref visible_tiles_range, .. } => { - let prim_data = &ctx.resources.prim_data_store[data_handle]; - let (ref stops_handle, brush_segments) = match prim_data.kind { - PrimitiveTemplateKind::LinearGradient { stops_handle, ref brush_segments, .. } => { - (stops_handle, brush_segments) - } - _ => unreachable!() - }; + let prim_data = &ctx.resources.linear_grad_data_store[data_handle]; let specified_blend_mode = BlendMode::PremultipliedAlpha; let mut prim_header = PrimitiveHeader { @@ -1822,7 +1816,7 @@ impl AlphaBatchBuilder { BrushBatchKind::LinearGradient, BatchTextures::no_texture(), [ - stops_handle.as_int(gpu_cache), + prim_data.stops_handle.as_int(gpu_cache), 0, 0, ], @@ -1837,10 +1831,10 @@ impl AlphaBatchBuilder { batch_params.prim_user_data, ); - let segments = if brush_segments.is_empty() { + let segments = if prim_data.brush_segments.is_empty() { None } else { - Some(brush_segments.as_slice()) + Some(prim_data.brush_segments.as_slice()) }; self.add_segmented_prim_to_batch( @@ -1863,7 +1857,7 @@ impl AlphaBatchBuilder { add_gradient_tiles( visible_tiles, - stops_handle, + &prim_data.stops_handle, BrushBatchKind::LinearGradient, specified_blend_mode, bounding_rect, @@ -1877,13 +1871,7 @@ impl AlphaBatchBuilder { } } PrimitiveInstanceKind::RadialGradient { data_handle, ref visible_tiles_range, .. } => { - let prim_data = &ctx.resources.prim_data_store[data_handle]; - let (stops_handle, brush_segments) = match prim_data.kind { - PrimitiveTemplateKind::RadialGradient { ref stops_handle, ref brush_segments, .. } => { - (stops_handle, brush_segments) - } - _ => unreachable!() - }; + let prim_data = &ctx.resources.radial_grad_data_store[data_handle]; let specified_blend_mode = BlendMode::PremultipliedAlpha; let mut prim_header = PrimitiveHeader { @@ -1909,7 +1897,7 @@ impl AlphaBatchBuilder { BrushBatchKind::RadialGradient, BatchTextures::no_texture(), [ - stops_handle.as_int(gpu_cache), + prim_data.stops_handle.as_int(gpu_cache), 0, 0, ], @@ -1924,10 +1912,10 @@ impl AlphaBatchBuilder { batch_params.prim_user_data, ); - let segments = if brush_segments.is_empty() { + let segments = if prim_data.brush_segments.is_empty() { None } else { - Some(brush_segments.as_slice()) + Some(prim_data.brush_segments.as_slice()) }; self.add_segmented_prim_to_batch( @@ -1950,7 +1938,7 @@ impl AlphaBatchBuilder { add_gradient_tiles( visible_tiles, - stops_handle, + &prim_data.stops_handle, BrushBatchKind::RadialGradient, specified_blend_mode, bounding_rect, diff --git a/webrender/src/display_list_flattener.rs b/webrender/src/display_list_flattener.rs index d886551d94..2b361b432c 100644 --- a/webrender/src/display_list_flattener.rs +++ b/webrender/src/display_list_flattener.rs @@ -22,10 +22,11 @@ use image::simplify_repeated_primitive; use intern::{Handle, Internable}; use internal_types::{FastHashMap, FastHashSet}; use picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive, PrimitiveList}; -use prim_store::{PrimitiveInstance, PrimitiveKeyKind, RadialGradientParams}; -use prim_store::{PrimitiveKey, PrimitiveSceneData, PrimitiveInstanceKind, GradientStopKey, NinePatchDescriptor}; +use prim_store::{PrimitiveInstance, PrimitiveKeyKind}; +use prim_store::{PrimitiveKey, PrimitiveSceneData, PrimitiveInstanceKind, NinePatchDescriptor}; use prim_store::{PrimitiveDataHandle, PrimitiveStore, PrimitiveStoreStats, LineDecorationCacheKey}; use prim_store::{ScrollNodeAndClipChain, PictureIndex, register_prim_chase_id, get_line_decoration_sizes}; +use prim_store::gradient::{GradientStopKey, LinearGradient, RadialGradient, RadialGradientParams}; use prim_store::text_run::TextRun; use render_backend::{DocumentView}; use resource_cache::{FontInstanceMap, ImageRequest}; @@ -781,7 +782,7 @@ impl<'a> DisplayListFlattener<'a> { pipeline_id, None, ) { - self.add_primitive( + self.add_nonshadowable_primitive( clip_and_scroll, &prim_info, Vec::new(), @@ -803,7 +804,7 @@ impl<'a> DisplayListFlattener<'a> { pipeline_id, None, ); - self.add_primitive( + self.add_nonshadowable_primitive( clip_and_scroll, &prim_info, Vec::new(), @@ -1113,6 +1114,33 @@ impl<'a> DisplayListFlattener<'a> { /// Convenience interface that creates a primitive entry and adds it /// to the draw list. + fn add_nonshadowable_primitive

( + &mut self, + clip_and_scroll: ScrollNodeAndClipChain, + info: &LayoutPrimitiveInfo, + clip_items: Vec<(LayoutPoint, ClipItemKey)>, + prim: P, + ) + where + P: Internable + IsVisible, + P::Source: AsInstanceKind>, + DocumentResources: InternerMut

, + { + if prim.is_visible() { + let clip_chain_id = self.build_clip_chain( + clip_items, + clip_and_scroll.spatial_node_index, + clip_and_scroll.clip_chain_id, + ); + self.add_prim_to_draw_list( + info, + clip_chain_id, + clip_and_scroll, + prim + ); + } + } + pub fn add_primitive

( &mut self, clip_and_scroll: ScrollNodeAndClipChain, @@ -1129,19 +1157,7 @@ impl<'a> DisplayListFlattener<'a> { // If a shadow context is not active, then add the primitive // directly to the parent picture. if self.pending_shadow_items.is_empty() { - if prim.is_visible() { - let clip_chain_id = self.build_clip_chain( - clip_items, - clip_and_scroll.spatial_node_index, - clip_and_scroll.clip_chain_id, - ); - self.add_prim_to_draw_list( - info, - clip_chain_id, - clip_and_scroll, - prim - ); - } + self.add_nonshadowable_primitive(clip_and_scroll, info, clip_items, prim); } else { debug_assert!(clip_items.is_empty(), "No per-prim clips expected for shadowed primitives"); @@ -2075,19 +2091,26 @@ impl<'a> DisplayListFlattener<'a> { widths: border_item.widths.into(), }; - let prim = match border.source { + match border.source { NinePatchBorderSource::Image(image_key) => { - PrimitiveKeyKind::ImageBorder { + let prim = PrimitiveKeyKind::ImageBorder { request: ImageRequest { key: image_key, rendering: ImageRendering::Auto, tile: None, }, nine_patch, - } + }; + + self.add_primitive( + clip_and_scroll, + info, + Vec::new(), + prim, + ); } NinePatchBorderSource::Gradient(gradient) => { - match self.create_linear_gradient_prim( + let prim = match self.create_linear_gradient_prim( &info, gradient.start_point, gradient.end_point, @@ -2100,10 +2123,17 @@ impl<'a> DisplayListFlattener<'a> { ) { Some(prim) => prim, None => return, - } + }; + + self.add_nonshadowable_primitive( + clip_and_scroll, + info, + Vec::new(), + prim, + ); } NinePatchBorderSource::RadialGradient(gradient) => { - self.create_radial_gradient_prim( + let prim = self.create_radial_gradient_prim( &info, gradient.center, gradient.start_offset * gradient.radius.width, @@ -2115,16 +2145,16 @@ impl<'a> DisplayListFlattener<'a> { LayoutSize::zero(), pipeline_id, Some(Box::new(nine_patch)), - ) + ); + + self.add_nonshadowable_primitive( + clip_and_scroll, + info, + Vec::new(), + prim, + ); } }; - - self.add_primitive( - clip_and_scroll, - info, - Vec::new(), - prim, - ); } BorderDetails::Normal(ref border) => { self.add_normal_border( @@ -2148,7 +2178,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); @@ -2190,7 +2220,7 @@ impl<'a> DisplayListFlattener<'a> { (start_point, end_point) }; - Some(PrimitiveKeyKind::LinearGradient { + Some(LinearGradient { extend_mode, start_point: sp.into(), end_point: ep.into(), @@ -2215,7 +2245,7 @@ impl<'a> DisplayListFlattener<'a> { mut tile_spacing: LayoutSize, pipeline_id: PipelineId, nine_patch: Option>, - ) -> PrimitiveKeyKind { + ) -> RadialGradient { let mut prim_rect = info.rect; simplify_repeated_primitive(&stretch_size, &mut tile_spacing, &mut prim_rect); @@ -2237,7 +2267,7 @@ impl<'a> DisplayListFlattener<'a> { } }).collect(); - PrimitiveKeyKind::RadialGradient { + RadialGradient { extend_mode, center: center.into(), params, diff --git a/webrender/src/picture.rs b/webrender/src/picture.rs index 8f66ff28ef..c62d1e7760 100644 --- a/webrender/src/picture.rs +++ b/webrender/src/picture.rs @@ -1301,11 +1301,15 @@ impl PrimitiveList { PrimitiveInstanceKind::Rectangle { data_handle, .. } | PrimitiveInstanceKind::YuvImage { data_handle, .. } | PrimitiveInstanceKind::Image { data_handle, .. } | - PrimitiveInstanceKind::LinearGradient { data_handle, .. } | - PrimitiveInstanceKind::RadialGradient { data_handle, ..} | - PrimitiveInstanceKind::Clear { data_handle, .. } => { + PrimitiveInstanceKind::Clear { data_handle, .. } => { &resources.prim_interner[data_handle] } + PrimitiveInstanceKind::LinearGradient { data_handle, .. } => { + &resources.linear_grad_interner[data_handle] + } + PrimitiveInstanceKind::RadialGradient { data_handle, ..} => { + &resources.radial_grad_interner[data_handle] + } PrimitiveInstanceKind::TextRun { data_handle, .. } => { &resources.text_run_interner[data_handle] } diff --git a/webrender/src/prim_store/gradient.rs b/webrender/src/prim_store/gradient.rs new file mode 100644 index 0000000000..081f7d1f90 --- /dev/null +++ b/webrender/src/prim_store/gradient.rs @@ -0,0 +1,729 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use api::{ + ColorF, ColorU,ExtendMode, GradientStop, LayoutPoint, LayoutSize, + LayoutPrimitiveInfo, LayoutRect, PremultipliedColorF +}; +use display_list_flattener::{AsInstanceKind, IsVisible}; +use frame_builder::FrameBuildingState; +use gpu_cache::{GpuCacheHandle, GpuDataRequest}; +use intern::{DataStore, Handle, Internable, Interner, UpdateList}; +use prim_store::{BrushSegment, GradientTileRange}; +use prim_store::{PrimitiveInstanceKind, PrimitiveOpacity, PrimitiveSceneData}; +use prim_store::{PrimKeyCommonData, PrimTemplateCommonData, PrimitiveStore}; +use prim_store::{NinePatchDescriptor, PointKey, SizeKey}; +use std::{hash, ops::{Deref, DerefMut}, mem}; +use util::pack_as_float; + +/// A hashable gradient stop that can be used in primitive keys. +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(Debug, Clone, PartialEq)] +pub struct GradientStopKey { + pub offset: f32, + pub color: ColorU, +} + +impl Eq for GradientStopKey {} + +impl hash::Hash for GradientStopKey { + fn hash(&self, state: &mut H) { + self.offset.to_bits().hash(state); + self.color.hash(state); + } +} + +/// Identifying key for a line decoration. +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct LinearGradientKey { + pub common: PrimKeyCommonData, + pub extend_mode: ExtendMode, + pub start_point: PointKey, + pub end_point: PointKey, + pub stretch_size: SizeKey, + pub tile_spacing: SizeKey, + pub stops: Vec, + pub reverse_stops: bool, + pub nine_patch: Option>, +} + +impl LinearGradientKey { + pub fn new( + is_backface_visible: bool, + prim_size: LayoutSize, + prim_relative_clip_rect: LayoutRect, + linear_grad: LinearGradient, + ) -> Self { + LinearGradientKey { + common: PrimKeyCommonData { + is_backface_visible, + prim_size: prim_size.into(), + prim_relative_clip_rect: prim_relative_clip_rect.into(), + }, + extend_mode: linear_grad.extend_mode, + start_point: linear_grad.start_point, + end_point: linear_grad.end_point, + stretch_size: linear_grad.stretch_size, + tile_spacing: linear_grad.tile_spacing, + stops: linear_grad.stops, + reverse_stops: linear_grad.reverse_stops, + nine_patch: linear_grad.nine_patch, + } + } +} + +impl AsInstanceKind for LinearGradientKey { + /// Construct a primitive instance that matches the type + /// of primitive key. + fn as_instance_kind( + &self, + data_handle: LinearGradientDataHandle, + _prim_store: &mut PrimitiveStore, + ) -> PrimitiveInstanceKind { + PrimitiveInstanceKind::LinearGradient { + data_handle, + visible_tiles_range: GradientTileRange::empty(), + } + } +} + +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct LinearGradientTemplate { + pub common: PrimTemplateCommonData, + pub extend_mode: ExtendMode, + pub start_point: LayoutPoint, + pub end_point: LayoutPoint, + pub stretch_size: LayoutSize, + pub tile_spacing: LayoutSize, + pub stops_opacity: PrimitiveOpacity, + pub stops: Vec, + pub brush_segments: Vec, + pub reverse_stops: bool, + pub stops_handle: GpuCacheHandle, +} + +impl Deref for LinearGradientTemplate { + type Target = PrimTemplateCommonData; + fn deref(&self) -> &Self::Target { + &self.common + } +} + +impl DerefMut for LinearGradientTemplate { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.common + } +} + +impl From for LinearGradientTemplate { + fn from(item: LinearGradientKey) -> Self { + let common = PrimTemplateCommonData::with_key_common(item.common); + let mut min_alpha: f32 = 1.0; + + // Convert the stops to more convenient representation + // for the current gradient builder. + let stops = item.stops.iter().map(|stop| { + let color: ColorF = stop.color.into(); + min_alpha = min_alpha.min(color.a); + + GradientStop { + offset: stop.offset, + color, + } + }).collect(); + + let mut brush_segments = Vec::new(); + + if let Some(ref nine_patch) = item.nine_patch { + brush_segments = nine_patch.create_segments(common.prim_size); + } + + // Save opacity of the stops for use in + // selecting which pass this gradient + // should be drawn in. + let stops_opacity = PrimitiveOpacity::from_alpha(min_alpha); + + LinearGradientTemplate { + common, + extend_mode: item.extend_mode, + start_point: item.start_point.into(), + end_point: item.end_point.into(), + stretch_size: item.stretch_size.into(), + tile_spacing: item.tile_spacing.into(), + stops_opacity, + stops, + brush_segments, + reverse_stops: item.reverse_stops, + stops_handle: GpuCacheHandle::new(), + } + } +} + +impl LinearGradientTemplate { + /// Update the GPU cache for a given primitive template. This may be called multiple + /// times per frame, by each primitive reference that refers to this interned + /// template. The initial request call to the GPU cache ensures that work is only + /// done if the cache entry is invalid (due to first use or eviction). + pub fn update( + &mut self, + frame_state: &mut FrameBuildingState, + ) { + if let Some(mut request) = + frame_state.gpu_cache.request(&mut self.common.gpu_cache_handle) { + // write_prim_gpu_blocks + request.push([ + self.start_point.x, + self.start_point.y, + self.end_point.x, + self.end_point.y, + ]); + request.push([ + pack_as_float(self.extend_mode as u32), + self.stretch_size.width, + self.stretch_size.height, + 0.0, + ]); + + // write_segment_gpu_blocks + for segment in &self.brush_segments { + // has to match VECS_PER_SEGMENT + request.write_segment( + segment.local_rect, + segment.extra_data, + ); + } + } + + if let Some(mut request) = frame_state.gpu_cache.request(&mut self.stops_handle) { + GradientGpuBlockBuilder::build( + self.reverse_stops, + &mut request, + &self.stops, + ); + } + + self.opacity = { + // If the coverage of the gradient extends to or beyond + // the primitive rect, then the opacity can be determined + // by the colors of the stops. If we have tiling / spacing + // then we just assume the gradient is translucent for now. + // (In the future we could consider segmenting in some cases). + let stride = self.stretch_size + self.tile_spacing; + if stride.width >= self.common.prim_size.width && + stride.height >= self.common.prim_size.height { + self.stops_opacity + } else { + PrimitiveOpacity::translucent() + } + } + } +} + +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] +pub struct LinearGradientDataMarker; + +pub type LinearGradientDataStore = DataStore; +pub type LinearGradientDataHandle = Handle; +pub type LinearGradientDataUpdateList = UpdateList; +pub type LinearGradientDataInterner = Interner; + +pub struct LinearGradient { + pub extend_mode: ExtendMode, + pub start_point: PointKey, + pub end_point: PointKey, + pub stretch_size: SizeKey, + pub tile_spacing: SizeKey, + pub stops: Vec, + pub reverse_stops: bool, + pub nine_patch: Option>, +} + +impl Internable for LinearGradient { + type Marker = LinearGradientDataMarker; + type Source = LinearGradientKey; + type StoreData = LinearGradientTemplate; + type InternData = PrimitiveSceneData; + + /// Build a new key from self with `info`. + fn build_key( + self, + info: &LayoutPrimitiveInfo, + prim_relative_clip_rect: LayoutRect + ) -> LinearGradientKey { + LinearGradientKey::new( + info.is_backface_visible, + info.rect.size, + prim_relative_clip_rect, + self + ) + } +} + +impl IsVisible for LinearGradient { + fn is_visible(&self) -> bool { + true + } +} + +//////////////////////////////////////////////////////////////////////////////// + +/// 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); + } +} + +/// Identifying key for a line decoration. +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct RadialGradientKey { + pub common: PrimKeyCommonData, + pub extend_mode: ExtendMode, + pub center: PointKey, + pub params: RadialGradientParams, + pub stretch_size: SizeKey, + pub stops: Vec, + pub tile_spacing: SizeKey, + pub nine_patch: Option>, +} + +impl RadialGradientKey { + pub fn new( + is_backface_visible: bool, + prim_size: LayoutSize, + prim_relative_clip_rect: LayoutRect, + radial_grad: RadialGradient, + ) -> Self { + RadialGradientKey { + common: PrimKeyCommonData { + is_backface_visible, + prim_size: prim_size.into(), + prim_relative_clip_rect: prim_relative_clip_rect.into(), + }, + extend_mode: radial_grad.extend_mode, + center: radial_grad.center, + params: radial_grad.params, + stretch_size: radial_grad.stretch_size, + stops: radial_grad.stops, + tile_spacing: radial_grad.tile_spacing, + nine_patch: radial_grad.nine_patch, + } + } +} + +impl AsInstanceKind for RadialGradientKey { + /// Construct a primitive instance that matches the type + /// of primitive key. + fn as_instance_kind( + &self, + data_handle: RadialGradientDataHandle, + _prim_store: &mut PrimitiveStore, + ) -> PrimitiveInstanceKind { + PrimitiveInstanceKind::RadialGradient { + data_handle, + visible_tiles_range: GradientTileRange::empty(), + } + } +} + +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct RadialGradientTemplate { + pub common: PrimTemplateCommonData, + pub extend_mode: ExtendMode, + pub center: LayoutPoint, + pub params: RadialGradientParams, + pub stretch_size: LayoutSize, + pub tile_spacing: LayoutSize, + pub brush_segments: Vec, + pub stops: Vec, + pub stops_handle: GpuCacheHandle, +} + +impl Deref for RadialGradientTemplate { + type Target = PrimTemplateCommonData; + fn deref(&self) -> &Self::Target { + &self.common + } +} + +impl DerefMut for RadialGradientTemplate { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.common + } +} + +impl From for RadialGradientTemplate { + fn from(item: RadialGradientKey) -> Self { + let common = PrimTemplateCommonData::with_key_common(item.common); + let mut brush_segments = Vec::new(); + + if let Some(ref nine_patch) = item.nine_patch { + brush_segments = nine_patch.create_segments(common.prim_size); + } + + let stops = item.stops.iter().map(|stop| { + GradientStop { + offset: stop.offset, + color: stop.color.into(), + } + }).collect(); + + RadialGradientTemplate { + common, + center: item.center.into(), + extend_mode: item.extend_mode, + params: item.params, + stretch_size: item.stretch_size.into(), + tile_spacing: item.tile_spacing.into(), + brush_segments: brush_segments, + stops, + stops_handle: GpuCacheHandle::new(), + } + } +} + +impl RadialGradientTemplate { + /// Update the GPU cache for a given primitive template. This may be called multiple + /// times per frame, by each primitive reference that refers to this interned + /// template. The initial request call to the GPU cache ensures that work is only + /// done if the cache entry is invalid (due to first use or eviction). + pub fn update( + &mut self, + frame_state: &mut FrameBuildingState, + ) { + if let Some(mut request) = + frame_state.gpu_cache.request(&mut self.common.gpu_cache_handle) { + // write_prim_gpu_blocks + request.push([ + self.center.x, + self.center.y, + self.params.start_radius, + self.params.end_radius, + ]); + request.push([ + self.params.ratio_xy, + pack_as_float(self.extend_mode as u32), + self.stretch_size.width, + self.stretch_size.height, + ]); + + // write_segment_gpu_blocks + for segment in &self.brush_segments { + // has to match VECS_PER_SEGMENT + request.write_segment( + segment.local_rect, + segment.extra_data, + ); + } + } + + if let Some(mut request) = frame_state.gpu_cache.request(&mut self.stops_handle) { + GradientGpuBlockBuilder::build( + false, + &mut request, + &self.stops, + ); + } + + self.opacity = PrimitiveOpacity::translucent(); + } +} + +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] +pub struct RadialGradientDataMarker; + +pub type RadialGradientDataStore = DataStore; +pub type RadialGradientDataHandle = Handle; +pub type RadialGradientDataUpdateList = UpdateList; +pub type RadialGradientDataInterner = Interner; + +pub struct RadialGradient { + pub extend_mode: ExtendMode, + pub center: PointKey, + pub params: RadialGradientParams, + pub stretch_size: SizeKey, + pub stops: Vec, + pub tile_spacing: SizeKey, + pub nine_patch: Option>, +} + +impl Internable for RadialGradient { + type Marker = RadialGradientDataMarker; + type Source = RadialGradientKey; + type StoreData = RadialGradientTemplate; + type InternData = PrimitiveSceneData; + + /// Build a new key from self with `info`. + fn build_key( + self, + info: &LayoutPrimitiveInfo, + prim_relative_clip_rect: LayoutRect, + ) -> RadialGradientKey { + RadialGradientKey::new( + info.is_backface_visible, + info.rect.size, + prim_relative_clip_rect, + self, + ) + } +} + +impl IsVisible for RadialGradient { + fn is_visible(&self) -> bool { + true + } +} + +//////////////////////////////////////////////////////////////////////////////// + +// The gradient entry index for the first color stop +pub const GRADIENT_DATA_FIRST_STOP: usize = 0; +// The gradient entry index for the last color stop +pub const GRADIENT_DATA_LAST_STOP: usize = GRADIENT_DATA_SIZE - 1; + +// The start of the gradient data table +pub const GRADIENT_DATA_TABLE_BEGIN: usize = GRADIENT_DATA_FIRST_STOP + 1; +// The exclusive bound of the gradient data table +pub const GRADIENT_DATA_TABLE_END: usize = GRADIENT_DATA_LAST_STOP; +// The number of entries in the gradient data table. +pub const GRADIENT_DATA_TABLE_SIZE: usize = 128; + +// The number of entries in a gradient data: GRADIENT_DATA_TABLE_SIZE + first stop entry + last stop entry +pub const GRADIENT_DATA_SIZE: usize = GRADIENT_DATA_TABLE_SIZE + 2; + +#[derive(Debug)] +#[repr(C)] +// An entry in a gradient data table representing a segment of the gradient color space. +pub struct GradientDataEntry { + pub start_color: PremultipliedColorF, + pub end_color: PremultipliedColorF, +} + +// TODO(gw): Tidy this up to be a free function / module? +struct GradientGpuBlockBuilder {} + +impl GradientGpuBlockBuilder { + /// Generate a color ramp filling the indices in [start_idx, end_idx) and interpolating + /// from start_color to end_color. + fn fill_colors( + start_idx: usize, + end_idx: usize, + start_color: &PremultipliedColorF, + end_color: &PremultipliedColorF, + entries: &mut [GradientDataEntry; GRADIENT_DATA_SIZE], + ) { + // Calculate the color difference for individual steps in the ramp. + let inv_steps = 1.0 / (end_idx - start_idx) as f32; + let step_r = (end_color.r - start_color.r) * inv_steps; + let step_g = (end_color.g - start_color.g) * inv_steps; + let step_b = (end_color.b - start_color.b) * inv_steps; + let step_a = (end_color.a - start_color.a) * inv_steps; + + let mut cur_color = *start_color; + + // Walk the ramp writing start and end colors for each entry. + for index in start_idx .. end_idx { + let entry = &mut entries[index]; + entry.start_color = cur_color; + cur_color.r += step_r; + cur_color.g += step_g; + cur_color.b += step_b; + cur_color.a += step_a; + entry.end_color = cur_color; + } + } + + /// Compute an index into the gradient entry table based on a gradient stop offset. This + /// function maps offsets from [0, 1] to indices in [GRADIENT_DATA_TABLE_BEGIN, GRADIENT_DATA_TABLE_END]. + #[inline] + fn get_index(offset: f32) -> usize { + (offset.max(0.0).min(1.0) * GRADIENT_DATA_TABLE_SIZE as f32 + + GRADIENT_DATA_TABLE_BEGIN as f32) + .round() as usize + } + + // Build the gradient data from the supplied stops, reversing them if necessary. + fn build( + reverse_stops: bool, + request: &mut GpuDataRequest, + src_stops: &[GradientStop], + ) { + // Preconditions (should be ensured by DisplayListBuilder): + // * we have at least two stops + // * first stop has offset 0.0 + // * last stop has offset 1.0 + let mut src_stops = src_stops.into_iter(); + let mut cur_color = match src_stops.next() { + Some(stop) => { + debug_assert_eq!(stop.offset, 0.0); + stop.color.premultiplied() + } + None => { + error!("Zero gradient stops found!"); + PremultipliedColorF::BLACK + } + }; + + // A table of gradient entries, with two colors per entry, that specify the start and end color + // within the segment of the gradient space represented by that entry. To lookup a gradient result, + // first the entry index is calculated to determine which two colors to interpolate between, then + // the offset within that entry bucket is used to interpolate between the two colors in that entry. + // This layout preserves hard stops, as the end color for a given entry can differ from the start + // color for the following entry, despite them being adjacent. Colors are stored within in BGRA8 + // format for texture upload. This table requires the gradient color stops to be normalized to the + // range [0, 1]. The first and last entries hold the first and last color stop colors respectively, + // while the entries in between hold the interpolated color stop values for the range [0, 1]. + let mut entries: [GradientDataEntry; GRADIENT_DATA_SIZE] = unsafe { mem::uninitialized() }; + + if reverse_stops { + // Fill in the first entry (for reversed stops) with the first color stop + GradientGpuBlockBuilder::fill_colors( + GRADIENT_DATA_LAST_STOP, + GRADIENT_DATA_LAST_STOP + 1, + &cur_color, + &cur_color, + &mut entries, + ); + + // Fill in the center of the gradient table, generating a color ramp between each consecutive pair + // of gradient stops. Each iteration of a loop will fill the indices in [next_idx, cur_idx). The + // loop will then fill indices in [GRADIENT_DATA_TABLE_BEGIN, GRADIENT_DATA_TABLE_END). + let mut cur_idx = GRADIENT_DATA_TABLE_END; + for next in src_stops { + let next_color = next.color.premultiplied(); + let next_idx = Self::get_index(1.0 - next.offset); + + if next_idx < cur_idx { + GradientGpuBlockBuilder::fill_colors( + next_idx, + cur_idx, + &next_color, + &cur_color, + &mut entries, + ); + cur_idx = next_idx; + } + + cur_color = next_color; + } + if cur_idx != GRADIENT_DATA_TABLE_BEGIN { + error!("Gradient stops abruptly at {}, auto-completing to white", cur_idx); + GradientGpuBlockBuilder::fill_colors( + GRADIENT_DATA_TABLE_BEGIN, + cur_idx, + &PremultipliedColorF::WHITE, + &cur_color, + &mut entries, + ); + } + + // Fill in the last entry (for reversed stops) with the last color stop + GradientGpuBlockBuilder::fill_colors( + GRADIENT_DATA_FIRST_STOP, + GRADIENT_DATA_FIRST_STOP + 1, + &cur_color, + &cur_color, + &mut entries, + ); + } else { + // Fill in the first entry with the first color stop + GradientGpuBlockBuilder::fill_colors( + GRADIENT_DATA_FIRST_STOP, + GRADIENT_DATA_FIRST_STOP + 1, + &cur_color, + &cur_color, + &mut entries, + ); + + // Fill in the center of the gradient table, generating a color ramp between each consecutive pair + // of gradient stops. Each iteration of a loop will fill the indices in [cur_idx, next_idx). The + // loop will then fill indices in [GRADIENT_DATA_TABLE_BEGIN, GRADIENT_DATA_TABLE_END). + let mut cur_idx = GRADIENT_DATA_TABLE_BEGIN; + for next in src_stops { + let next_color = next.color.premultiplied(); + let next_idx = Self::get_index(next.offset); + + if next_idx > cur_idx { + GradientGpuBlockBuilder::fill_colors( + cur_idx, + next_idx, + &cur_color, + &next_color, + &mut entries, + ); + cur_idx = next_idx; + } + + cur_color = next_color; + } + if cur_idx != GRADIENT_DATA_TABLE_END { + error!("Gradient stops abruptly at {}, auto-completing to white", cur_idx); + GradientGpuBlockBuilder::fill_colors( + cur_idx, + GRADIENT_DATA_TABLE_END, + &PremultipliedColorF::WHITE, + &cur_color, + &mut entries, + ); + } + + // Fill in the last entry with the last color stop + GradientGpuBlockBuilder::fill_colors( + GRADIENT_DATA_LAST_STOP, + GRADIENT_DATA_LAST_STOP + 1, + &cur_color, + &cur_color, + &mut entries, + ); + } + + for entry in entries.iter() { + request.push(entry.start_color); + request.push(entry.end_color); + } + } +} + +#[test] +#[cfg(target_os = "linux")] +fn test_struct_sizes() { + use std::mem; + // The sizes of these structures are critical for performance on a number of + // talos stress tests. If you get a failure here on CI, there's two possibilities: + // (a) You made a structure smaller than it currently is. Great work! Update the + // 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::(), 72, "LinearGradient size changed"); + assert_eq!(mem::size_of::(), 168, "LinearGradientTemplate size changed"); + assert_eq!(mem::size_of::(), 96, "LinearGradientKey size changed"); + + assert_eq!(mem::size_of::(), 72, "RadialGradient size changed"); + assert_eq!(mem::size_of::(), 168, "RadialGradientTemplate size changed"); + assert_eq!(mem::size_of::(), 104, "RadialGradientKey size changed"); +} diff --git a/webrender/src/prim_store/mod.rs b/webrender/src/prim_store/mod.rs index 1d4c352414..a9d0116a42 100644 --- a/webrender/src/prim_store/mod.rs +++ b/webrender/src/prim_store/mod.rs @@ -3,8 +3,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{AlphaType, BorderRadius, ClipMode, ColorF, PictureRect, ColorU, LayoutVector2D}; -use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, ExtendMode, DeviceRect, LayoutSideOffsetsAu}; -use api::{FilterOp, GradientStop, ImageKey, ImageRendering, TileOffset, RepeatMode}; +use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, DeviceRect, LayoutSideOffsetsAu}; +use api::{FilterOp, ImageKey, ImageRendering, TileOffset, RepeatMode}; use api::{LayoutPoint, LayoutRect, LayoutSideOffsets, LayoutSize}; use api::{PremultipliedColorF, PropertyBinding, Shadow, YuvColorSpace, YuvFormat}; use api::{DeviceIntSideOffsets, WorldPixel, BoxShadowClipMode, NormalBorder, WorldRect, LayoutToWorldScale}; @@ -28,6 +28,7 @@ use intern; use internal_types::FastHashMap; use picture::{PictureCompositeMode, PicturePrimitive, PictureUpdateState}; use picture::{ClusterRange, PrimitiveList, SurfaceIndex, TileDescriptor}; +use prim_store::gradient::{LinearGradientDataHandle, RadialGradientDataHandle}; use prim_store::text_run::{TextRunDataHandle, TextRunPrimitive}; #[cfg(debug_assertions)] use render_backend::{FrameId}; @@ -38,7 +39,7 @@ use renderer::{MAX_VERTEX_TEXTURE_WIDTH}; use resource_cache::{ImageProperties, ImageRequest, ResourceCache}; use scene::SceneProperties; use segment::SegmentBuilder; -use std::{cmp, fmt, hash, mem, ops, u32, usize}; +use std::{cmp, fmt, hash, ops, u32, usize}; #[cfg(debug_assertions)] use std::sync::atomic::{AtomicUsize, Ordering}; use storage; @@ -47,6 +48,7 @@ use util::{ScaleOffset, MatrixHelpers, MaxRect, recycle_vec}; use util::{pack_as_float, project_rect, raster_rect_to_device_pixels}; use smallvec::SmallVec; +pub mod gradient; pub mod text_run; /// Counter for unique primitive IDs for debug tracing. @@ -391,43 +393,6 @@ pub enum PrimitiveKeyKind { image_rendering: ImageRendering, alpha_type: AlphaType, }, - LinearGradient { - extend_mode: ExtendMode, - start_point: PointKey, - end_point: PointKey, - stretch_size: SizeKey, - tile_spacing: SizeKey, - stops: Vec, - 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. -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -#[derive(Debug, Clone, PartialEq)] -pub struct GradientStopKey { - pub offset: f32, - pub color: ColorU, -} - -impl Eq for GradientStopKey {} - -impl hash::Hash for GradientStopKey { - fn hash(&self, state: &mut H) { - self.offset.to_bits().hash(state); - self.color.hash(state); - } } #[cfg_attr(feature = "capture", derive(Serialize))] @@ -568,26 +533,6 @@ impl 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))] @@ -727,18 +672,6 @@ impl AsInstanceKind for PrimitiveKey { image_instance_index, } } - PrimitiveKeyKind::LinearGradient { .. } => { - PrimitiveInstanceKind::LinearGradient { - data_handle, - visible_tiles_range: GradientTileRange::empty(), - } - } - PrimitiveKeyKind::RadialGradient { .. } => { - PrimitiveInstanceKind::RadialGradient { - data_handle, - visible_tiles_range: GradientTileRange::empty(), - } - } PrimitiveKeyKind::Unused => { // Should never be hit as this method should not be // called for old style primitives. @@ -793,28 +726,6 @@ pub enum PrimitiveTemplateKind { sub_rect: Option, alpha_type: AlphaType, }, - LinearGradient { - extend_mode: ExtendMode, - start_point: LayoutPoint, - end_point: LayoutPoint, - stretch_size: LayoutSize, - tile_spacing: LayoutSize, - stops_opacity: PrimitiveOpacity, - stops: Vec, - brush_segments: Vec, - 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, } @@ -903,89 +814,6 @@ impl PrimitiveKeyKind { color: color.into(), } } - PrimitiveKeyKind::LinearGradient { - extend_mode, - tile_spacing, - start_point, - end_point, - stretch_size, - stops, - reverse_stops, - nine_patch, - .. - } => { - let mut min_alpha: f32 = 1.0; - - // Convert the stops to more convenient representation - // for the current gradient builder. - let stops = stops.iter().map(|stop| { - let color: ColorF = stop.color.into(); - min_alpha = min_alpha.min(color.a); - - GradientStop { - offset: stop.offset, - color, - } - }).collect(); - - let mut brush_segments = Vec::new(); - - if let Some(ref nine_patch) = nine_patch { - brush_segments = nine_patch.create_segments(size); - } - - // Save opacity of the stops for use in - // selecting which pass this gradient - // should be drawn in. - let stops_opacity = PrimitiveOpacity::from_alpha(min_alpha); - - PrimitiveTemplateKind::LinearGradient { - extend_mode, - start_point: start_point.into(), - end_point: end_point.into(), - stretch_size: stretch_size.into(), - tile_spacing: tile_spacing.into(), - stops_opacity, - stops, - brush_segments, - reverse_stops, - 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 = nine_patch.create_segments(size); - } - - 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, - } - } } } } @@ -1123,40 +951,6 @@ impl PrimitiveTemplateKind { 0.0, ]); } - PrimitiveTemplateKind::LinearGradient { stretch_size, start_point, end_point, extend_mode, .. } => { - request.push([ - start_point.x, - start_point.y, - end_point.x, - end_point.y, - ]); - request.push([ - pack_as_float(extend_mode as u32), - stretch_size.width, - stretch_size.height, - 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 => {} } } @@ -1184,16 +978,6 @@ impl PrimitiveTemplateKind { ); } } - PrimitiveTemplateKind::LinearGradient { ref brush_segments, .. } | - PrimitiveTemplateKind::RadialGradient { ref brush_segments, .. } => { - for segment in brush_segments { - // has to match VECS_PER_SEGMENT - request.write_segment( - segment.local_rect, - segment.extra_data, - ); - } - } PrimitiveTemplateKind::Clear | PrimitiveTemplateKind::LineDecoration { .. } | PrimitiveTemplateKind::Image { .. } | @@ -1238,48 +1022,6 @@ impl PrimitiveTemplate { // Shouldn't matter, since the segment opacity is used instead PrimitiveOpacity::translucent() } - PrimitiveTemplateKind::LinearGradient { - stretch_size, - tile_spacing, - stops_opacity, - ref mut stops_handle, - reverse_stops, - ref stops, - .. - } => { - if let Some(mut request) = frame_state.gpu_cache.request(stops_handle) { - GradientGpuBlockBuilder::build( - reverse_stops, - &mut request, - stops, - ); - } - - // If the coverage of the gradient extends to or beyond - // the primitive rect, then the opacity can be determined - // by the colors of the stops. If we have tiling / spacing - // then we just assume the gradient is translucent for now. - // (In the future we could consider segmenting in some cases). - let stride = stretch_size + tile_spacing; - if stride.width >= self.common.prim_size.width && - stride.height >= self.common.prim_size.height { - stops_opacity - } else { - 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 @@ -1708,214 +1450,6 @@ pub enum ImageSource { }, } -// The gradient entry index for the first color stop -pub const GRADIENT_DATA_FIRST_STOP: usize = 0; -// The gradient entry index for the last color stop -pub const GRADIENT_DATA_LAST_STOP: usize = GRADIENT_DATA_SIZE - 1; - -// The start of the gradient data table -pub const GRADIENT_DATA_TABLE_BEGIN: usize = GRADIENT_DATA_FIRST_STOP + 1; -// The exclusive bound of the gradient data table -pub const GRADIENT_DATA_TABLE_END: usize = GRADIENT_DATA_LAST_STOP; -// The number of entries in the gradient data table. -pub const GRADIENT_DATA_TABLE_SIZE: usize = 128; - -// The number of entries in a gradient data: GRADIENT_DATA_TABLE_SIZE + first stop entry + last stop entry -pub const GRADIENT_DATA_SIZE: usize = GRADIENT_DATA_TABLE_SIZE + 2; - -#[derive(Debug)] -#[repr(C)] -// An entry in a gradient data table representing a segment of the gradient color space. -pub struct GradientDataEntry { - pub start_color: PremultipliedColorF, - pub end_color: PremultipliedColorF, -} - -// TODO(gw): Tidy this up to be a free function / module? -struct GradientGpuBlockBuilder {} - -impl GradientGpuBlockBuilder { - /// Generate a color ramp filling the indices in [start_idx, end_idx) and interpolating - /// from start_color to end_color. - fn fill_colors( - start_idx: usize, - end_idx: usize, - start_color: &PremultipliedColorF, - end_color: &PremultipliedColorF, - entries: &mut [GradientDataEntry; GRADIENT_DATA_SIZE], - ) { - // Calculate the color difference for individual steps in the ramp. - let inv_steps = 1.0 / (end_idx - start_idx) as f32; - let step_r = (end_color.r - start_color.r) * inv_steps; - let step_g = (end_color.g - start_color.g) * inv_steps; - let step_b = (end_color.b - start_color.b) * inv_steps; - let step_a = (end_color.a - start_color.a) * inv_steps; - - let mut cur_color = *start_color; - - // Walk the ramp writing start and end colors for each entry. - for index in start_idx .. end_idx { - let entry = &mut entries[index]; - entry.start_color = cur_color; - cur_color.r += step_r; - cur_color.g += step_g; - cur_color.b += step_b; - cur_color.a += step_a; - entry.end_color = cur_color; - } - } - - /// Compute an index into the gradient entry table based on a gradient stop offset. This - /// function maps offsets from [0, 1] to indices in [GRADIENT_DATA_TABLE_BEGIN, GRADIENT_DATA_TABLE_END]. - #[inline] - fn get_index(offset: f32) -> usize { - (offset.max(0.0).min(1.0) * GRADIENT_DATA_TABLE_SIZE as f32 + - GRADIENT_DATA_TABLE_BEGIN as f32) - .round() as usize - } - - // Build the gradient data from the supplied stops, reversing them if necessary. - fn build( - reverse_stops: bool, - request: &mut GpuDataRequest, - src_stops: &[GradientStop], - ) { - // Preconditions (should be ensured by DisplayListBuilder): - // * we have at least two stops - // * first stop has offset 0.0 - // * last stop has offset 1.0 - let mut src_stops = src_stops.into_iter(); - let mut cur_color = match src_stops.next() { - Some(stop) => { - debug_assert_eq!(stop.offset, 0.0); - stop.color.premultiplied() - } - None => { - error!("Zero gradient stops found!"); - PremultipliedColorF::BLACK - } - }; - - // A table of gradient entries, with two colors per entry, that specify the start and end color - // within the segment of the gradient space represented by that entry. To lookup a gradient result, - // first the entry index is calculated to determine which two colors to interpolate between, then - // the offset within that entry bucket is used to interpolate between the two colors in that entry. - // This layout preserves hard stops, as the end color for a given entry can differ from the start - // color for the following entry, despite them being adjacent. Colors are stored within in BGRA8 - // format for texture upload. This table requires the gradient color stops to be normalized to the - // range [0, 1]. The first and last entries hold the first and last color stop colors respectively, - // while the entries in between hold the interpolated color stop values for the range [0, 1]. - let mut entries: [GradientDataEntry; GRADIENT_DATA_SIZE] = unsafe { mem::uninitialized() }; - - if reverse_stops { - // Fill in the first entry (for reversed stops) with the first color stop - GradientGpuBlockBuilder::fill_colors( - GRADIENT_DATA_LAST_STOP, - GRADIENT_DATA_LAST_STOP + 1, - &cur_color, - &cur_color, - &mut entries, - ); - - // Fill in the center of the gradient table, generating a color ramp between each consecutive pair - // of gradient stops. Each iteration of a loop will fill the indices in [next_idx, cur_idx). The - // loop will then fill indices in [GRADIENT_DATA_TABLE_BEGIN, GRADIENT_DATA_TABLE_END). - let mut cur_idx = GRADIENT_DATA_TABLE_END; - for next in src_stops { - let next_color = next.color.premultiplied(); - let next_idx = Self::get_index(1.0 - next.offset); - - if next_idx < cur_idx { - GradientGpuBlockBuilder::fill_colors( - next_idx, - cur_idx, - &next_color, - &cur_color, - &mut entries, - ); - cur_idx = next_idx; - } - - cur_color = next_color; - } - if cur_idx != GRADIENT_DATA_TABLE_BEGIN { - error!("Gradient stops abruptly at {}, auto-completing to white", cur_idx); - GradientGpuBlockBuilder::fill_colors( - GRADIENT_DATA_TABLE_BEGIN, - cur_idx, - &PremultipliedColorF::WHITE, - &cur_color, - &mut entries, - ); - } - - // Fill in the last entry (for reversed stops) with the last color stop - GradientGpuBlockBuilder::fill_colors( - GRADIENT_DATA_FIRST_STOP, - GRADIENT_DATA_FIRST_STOP + 1, - &cur_color, - &cur_color, - &mut entries, - ); - } else { - // Fill in the first entry with the first color stop - GradientGpuBlockBuilder::fill_colors( - GRADIENT_DATA_FIRST_STOP, - GRADIENT_DATA_FIRST_STOP + 1, - &cur_color, - &cur_color, - &mut entries, - ); - - // Fill in the center of the gradient table, generating a color ramp between each consecutive pair - // of gradient stops. Each iteration of a loop will fill the indices in [cur_idx, next_idx). The - // loop will then fill indices in [GRADIENT_DATA_TABLE_BEGIN, GRADIENT_DATA_TABLE_END). - let mut cur_idx = GRADIENT_DATA_TABLE_BEGIN; - for next in src_stops { - let next_color = next.color.premultiplied(); - let next_idx = Self::get_index(next.offset); - - if next_idx > cur_idx { - GradientGpuBlockBuilder::fill_colors( - cur_idx, - next_idx, - &cur_color, - &next_color, - &mut entries, - ); - cur_idx = next_idx; - } - - cur_color = next_color; - } - if cur_idx != GRADIENT_DATA_TABLE_END { - error!("Gradient stops abruptly at {}, auto-completing to white", cur_idx); - GradientGpuBlockBuilder::fill_colors( - cur_idx, - GRADIENT_DATA_TABLE_END, - &PremultipliedColorF::WHITE, - &cur_color, - &mut entries, - ); - } - - // Fill in the last entry with the last color stop - GradientGpuBlockBuilder::fill_colors( - GRADIENT_DATA_LAST_STOP, - GRADIENT_DATA_LAST_STOP + 1, - &cur_color, - &cur_color, - &mut entries, - ); - } - - for entry in entries.iter() { - request.push(entry.start_color); - request.push(entry.end_color); - } - } -} - #[derive(Debug)] #[repr(C)] struct ClipRect { @@ -2153,8 +1687,6 @@ impl IsVisible for PrimitiveKeyKind { PrimitiveKeyKind::ImageBorder { .. } | PrimitiveKeyKind::YuvImage { .. } | PrimitiveKeyKind::Image { .. } | - PrimitiveKeyKind::LinearGradient { .. } | - PrimitiveKeyKind::RadialGradient { .. } | PrimitiveKeyKind::Clear | PrimitiveKeyKind::Unused => { true @@ -2207,8 +1739,6 @@ impl CreateShadow for PrimitiveKeyKind { } PrimitiveKeyKind::ImageBorder { .. } | PrimitiveKeyKind::YuvImage { .. } | - PrimitiveKeyKind::LinearGradient { .. } | - PrimitiveKeyKind::RadialGradient { .. } | PrimitiveKeyKind::Unused | PrimitiveKeyKind::Clear => { panic!("bug: this prim is not supported in shadow contexts"); @@ -2298,12 +1828,12 @@ pub enum PrimitiveInstanceKind { }, LinearGradient { /// Handle to the common interned data for this primitive. - data_handle: PrimitiveDataHandle, + data_handle: LinearGradientDataHandle, visible_tiles_range: GradientTileRange, }, RadialGradient { /// Handle to the common interned data for this primitive. - data_handle: PrimitiveDataHandle, + data_handle: RadialGradientDataHandle, visible_tiles_range: GradientTileRange, }, /// Clear out a rect, used for special effects. @@ -2401,8 +1931,12 @@ impl PrimitiveInstance { PrimitiveInstanceKind::ImageBorder { data_handle, .. } | PrimitiveInstanceKind::Rectangle { data_handle, .. } | PrimitiveInstanceKind::YuvImage { data_handle, .. } | - PrimitiveInstanceKind::Image { data_handle, .. } | - PrimitiveInstanceKind::LinearGradient { data_handle, .. } | + PrimitiveInstanceKind::Image { data_handle, .. } => { + data_handle.uid() + } + PrimitiveInstanceKind::LinearGradient { data_handle, .. } => { + data_handle.uid() + } PrimitiveInstanceKind::RadialGradient { data_handle, .. } => { data_handle.uid() } @@ -3496,43 +3030,33 @@ impl PrimitiveStore { write_segment(prim_data, image_instance.segment_instance_index, frame_state, scratch); } PrimitiveInstanceKind::LinearGradient { data_handle, ref mut visible_tiles_range, .. } => { - let prim_data = &mut resources.prim_data_store[*data_handle]; + let prim_data = &mut resources.linear_grad_data_store[*data_handle]; // Update the template this instane references, which may refresh the GPU // cache with any shared template data. - prim_data.update( - pic_context.surface_index, - frame_state, - ); - - let (extend_mode, stretch_size, start_point, end_point, tile_spacing) = match prim_data.kind { - PrimitiveTemplateKind::LinearGradient { ref extend_mode, ref stretch_size, ref start_point, ref end_point, ref tile_spacing, .. } => { - (extend_mode, stretch_size, start_point, end_point, tile_spacing) - } - _ => unreachable!() - }; + prim_data.update(frame_state); - if *tile_spacing != LayoutSize::zero() { + if prim_data.tile_spacing != LayoutSize::zero() { *visible_tiles_range = decompose_repeated_primitive( &prim_instance.combined_local_clip_rect, &prim_local_rect, - &stretch_size, - &tile_spacing, + &prim_data.stretch_size, + &prim_data.tile_spacing, prim_context, frame_state, &pic_context.dirty_world_rect, &mut scratch.gradient_tiles, &mut |_, mut request| { request.push([ - start_point.x, - start_point.y, - end_point.x, - end_point.y, + prim_data.start_point.x, + prim_data.start_point.y, + prim_data.end_point.x, + prim_data.end_point.y, ]); request.push([ - pack_as_float(*extend_mode as u32), - stretch_size.width, - stretch_size.height, + pack_as_float(prim_data.extend_mode as u32), + prim_data.stretch_size.width, + prim_data.stretch_size.height, 0.0, ]); } @@ -3547,44 +3071,34 @@ impl PrimitiveStore { // for gradient primitives. } PrimitiveInstanceKind::RadialGradient { data_handle, ref mut visible_tiles_range, .. } => { - let prim_data = &mut resources.prim_data_store[*data_handle]; + let prim_data = &mut resources.radial_grad_data_store[*data_handle]; // Update the template this instane references, which may refresh the GPU // cache with any shared template data. - prim_data.update( - pic_context.surface_index, - frame_state, - ); - - let (params, extend_mode, stretch_size, tile_spacing, center) = match prim_data.kind { - PrimitiveTemplateKind::RadialGradient { ref params, ref extend_mode, stretch_size, ref tile_spacing, center, .. } => { - (params, extend_mode, stretch_size, tile_spacing, center) - } - _ => unreachable!() - }; + prim_data.update(frame_state); - if *tile_spacing != LayoutSize::zero() { + if prim_data.tile_spacing != LayoutSize::zero() { *visible_tiles_range = decompose_repeated_primitive( &prim_instance.combined_local_clip_rect, &prim_local_rect, - &stretch_size, - &tile_spacing, + &prim_data.stretch_size, + &prim_data.tile_spacing, prim_context, frame_state, &pic_context.dirty_world_rect, &mut scratch.gradient_tiles, &mut |_, mut request| { request.push([ - center.x, - center.y, - params.start_radius, - params.end_radius, + prim_data.center.x, + prim_data.center.y, + prim_data.params.start_radius, + prim_data.params.end_radius, ]); request.push([ - params.ratio_xy, - pack_as_float(*extend_mode as u32), - stretch_size.width, - stretch_size.height, + prim_data.params.ratio_xy, + pack_as_float(prim_data.extend_mode as u32), + prim_data.stretch_size.width, + prim_data.stretch_size.height, ]); }, ); @@ -4030,40 +3544,26 @@ impl PrimitiveInstance { } } PrimitiveInstanceKind::LinearGradient { data_handle, .. } => { - let prim_data = &resources.prim_data_store[data_handle]; + let prim_data = &resources.linear_grad_data_store[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::LinearGradient { ref brush_segments, .. } => { - if brush_segments.is_empty() { - return false; - } - - brush_segments.as_slice() - } - _ => { - unreachable!(); - } + if prim_data.brush_segments.is_empty() { + return false; } + + prim_data.brush_segments.as_slice() } PrimitiveInstanceKind::RadialGradient { data_handle, .. } => { - let prim_data = &resources.prim_data_store[data_handle]; + let prim_data = &resources.radial_grad_data_store[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!(); - } + if prim_data.brush_segments.is_empty() { + return false; } + + prim_data.brush_segments.as_slice() } }; @@ -4317,6 +3817,7 @@ fn update_opacity_binding( #[test] #[cfg(target_os = "linux")] fn test_struct_sizes() { + use std::mem; // The sizes of these structures are critical for performance on a number of // talos stress tests. If you get a failure here on CI, there's two possibilities: // (a) You made a structure smaller than it currently is. Great work! Update the @@ -4325,8 +3826,8 @@ fn test_struct_sizes() { // be done with care, and after checking if talos performance regresses badly. assert_eq!(mem::size_of::(), 128, "PrimitiveInstance size changed"); assert_eq!(mem::size_of::(), 40, "PrimitiveInstanceKind size changed"); - assert_eq!(mem::size_of::(), 168, "PrimitiveTemplate size changed"); - assert_eq!(mem::size_of::(), 112, "PrimitiveTemplateKind size changed"); - assert_eq!(mem::size_of::(), 128, "PrimitiveKey size changed"); + assert_eq!(mem::size_of::(), 144, "PrimitiveTemplate size changed"); + assert_eq!(mem::size_of::(), 88, "PrimitiveTemplateKind size changed"); + assert_eq!(mem::size_of::(), 124, "PrimitiveKey size changed"); assert_eq!(mem::size_of::(), 96, "PrimitiveKeyKind size changed"); } diff --git a/webrender/src/profiler.rs b/webrender/src/profiler.rs index 9148c2e10f..e8ad4e2cbd 100644 --- a/webrender/src/profiler.rs +++ b/webrender/src/profiler.rs @@ -404,6 +404,8 @@ pub struct IpcProfileCounters { #[derive(Clone)] pub struct InternProfileCounters { pub prims: ResourceProfileCounter, + pub linear_gradients: ResourceProfileCounter, + pub radial_gradients: ResourceProfileCounter, pub text_runs: ResourceProfileCounter, pub clips: ResourceProfileCounter, } @@ -448,6 +450,8 @@ impl BackendProfileCounters { }, intern: InternProfileCounters { prims: ResourceProfileCounter::new("Interned primitives"), + linear_gradients: ResourceProfileCounter::new("Interned linear gradients"), + radial_gradients: ResourceProfileCounter::new("Interned radial gradients"), text_runs: ResourceProfileCounter::new("Interned text runs"), clips: ResourceProfileCounter::new("Interned clips"), }, @@ -1099,6 +1103,8 @@ impl Profiler { &[ &backend_profile.intern.clips, &backend_profile.intern.prims, + &backend_profile.intern.linear_gradients, + &backend_profile.intern.radial_gradients, &backend_profile.intern.text_runs, ], debug_renderer, diff --git a/webrender/src/render_backend.rs b/webrender/src/render_backend.rs index 179b44181b..1e1a2645a9 100644 --- a/webrender/src/render_backend.rs +++ b/webrender/src/render_backend.rs @@ -32,6 +32,7 @@ use hit_test::{HitTest, HitTester}; use internal_types::{DebugOutput, FastHashMap, FastHashSet, RenderedDocument, ResultMsg}; use prim_store::{PrimitiveDataStore, PrimitiveScratchBuffer, PrimitiveInstance}; use prim_store::{PrimitiveInstanceKind, PrimTemplateCommonData}; +use prim_store::gradient::{LinearGradientDataStore, RadialGradientDataStore}; use prim_store::text_run::TextRunDataStore; use profiler::{BackendProfileCounters, IpcProfileCounters, ResourceProfileCounters}; use record::ApiRecordingReceiver; @@ -203,6 +204,8 @@ pub struct FrameResources { /// Currently active / available primitives. Kept in sync with the /// primitive interner in the scene builder, per document. pub prim_data_store: PrimitiveDataStore, + pub linear_grad_data_store: LinearGradientDataStore, + pub radial_grad_data_store: RadialGradientDataStore, pub text_run_data_store: TextRunDataStore, } @@ -219,12 +222,18 @@ impl FrameResources { PrimitiveInstanceKind::Rectangle { data_handle, .. } | PrimitiveInstanceKind::YuvImage { data_handle, .. } | PrimitiveInstanceKind::Image { data_handle, .. } | - PrimitiveInstanceKind::LinearGradient { data_handle, .. } | - PrimitiveInstanceKind::RadialGradient { data_handle, .. } | PrimitiveInstanceKind::Clear { data_handle, .. } => { let prim_data = &self.prim_data_store[data_handle]; &prim_data.common } + PrimitiveInstanceKind::LinearGradient { data_handle, .. } => { + let prim_data = &self.linear_grad_data_store[data_handle]; + &prim_data.common + } + PrimitiveInstanceKind::RadialGradient { data_handle, .. } =>{ + let prim_data = &self.radial_grad_data_store[data_handle]; + &prim_data.common + } PrimitiveInstanceKind::TextRun { data_handle, .. } => { let prim_data = &self.text_run_data_store[data_handle]; &prim_data.common @@ -1211,6 +1220,14 @@ impl RenderBackend { updates.prim_updates, &mut profile_counters.intern.prims, ); + doc.resources.linear_grad_data_store.apply_updates( + updates.linear_grad_updates, + &mut profile_counters.intern.linear_gradients, + ); + doc.resources.radial_grad_data_store.apply_updates( + updates.radial_grad_updates, + &mut profile_counters.intern.radial_gradients, + ); doc.resources.text_run_data_store.apply_updates( updates.text_run_updates, &mut profile_counters.intern.text_runs, diff --git a/webrender/src/scene_builder.rs b/webrender/src/scene_builder.rs index c75ffa88e5..8cb77b3a1e 100644 --- a/webrender/src/scene_builder.rs +++ b/webrender/src/scene_builder.rs @@ -16,6 +16,10 @@ use intern::{Internable, Interner}; use internal_types::{FastHashMap, FastHashSet}; use prim_store::{PrimitiveDataInterner, PrimitiveDataUpdateList, PrimitiveKeyKind}; use prim_store::PrimitiveStoreStats; +use prim_store::gradient::{ + LinearGradient, LinearGradientDataInterner, LinearGradientDataUpdateList, + RadialGradient, RadialGradientDataInterner, RadialGradientDataUpdateList +}; use prim_store::text_run::{TextRunDataInterner, TextRun, TextRunDataUpdateList}; use resource_cache::FontInstanceMap; use render_backend::DocumentView; @@ -31,6 +35,8 @@ use std::time::Duration; pub struct DocumentResourceUpdates { pub clip_updates: ClipDataUpdateList, pub prim_updates: PrimitiveDataUpdateList, + pub linear_grad_updates: LinearGradientDataUpdateList, + pub radial_grad_updates: RadialGradientDataUpdateList, pub text_run_updates: TextRunDataUpdateList, } @@ -167,6 +173,8 @@ pub enum SceneSwapResult { pub struct DocumentResources { pub clip_interner: ClipDataInterner, pub prim_interner: PrimitiveDataInterner, + pub linear_grad_interner: LinearGradientDataInterner, + pub radial_grad_interner: RadialGradientDataInterner, pub text_run_interner: TextRunDataInterner, } @@ -182,6 +190,18 @@ impl InternerMut for DocumentResources { } } +impl InternerMut for DocumentResources { + fn interner_mut(&mut self) -> &mut LinearGradientDataInterner { + &mut self.linear_grad_interner + } +} + +impl InternerMut for DocumentResources { + fn interner_mut(&mut self) -> &mut RadialGradientDataInterner { + &mut self.radial_grad_interner + } +} + impl InternerMut for DocumentResources { fn interner_mut(&mut self) -> &mut TextRunDataInterner { &mut self.text_run_interner @@ -347,6 +367,8 @@ impl SceneBuilder { &PrimitiveStoreStats::empty(), ); + // TODO(djg): Can we do better than this? Use a #[derive] to + // write the code for us, or unify updates into one enum/list? let clip_updates = item .doc_resources .clip_interner @@ -357,6 +379,16 @@ impl SceneBuilder { .prim_interner .end_frame_and_get_pending_updates(); + let linear_grad_updates = item + .doc_resources + .linear_grad_interner + .end_frame_and_get_pending_updates(); + + let radial_grad_updates = item + .doc_resources + .radial_grad_interner + .end_frame_and_get_pending_updates(); + let text_run_updates = item .doc_resources .text_run_interner @@ -366,6 +398,8 @@ impl SceneBuilder { DocumentResourceUpdates { clip_updates, prim_updates, + linear_grad_updates, + radial_grad_updates, text_run_updates, } ); @@ -475,6 +509,16 @@ impl SceneBuilder { .prim_interner .end_frame_and_get_pending_updates(); + let linear_grad_updates = doc + .resources + .linear_grad_interner + .end_frame_and_get_pending_updates(); + + let radial_grad_updates = doc + .resources + .radial_grad_interner + .end_frame_and_get_pending_updates(); + let text_run_updates = doc .resources .text_run_interner @@ -484,6 +528,8 @@ impl SceneBuilder { DocumentResourceUpdates { clip_updates, prim_updates, + linear_grad_updates, + radial_grad_updates, text_run_updates, } );