diff options
Diffstat (limited to 'gfx/wr/webrender/src/prim_store/line_dec.rs')
-rw-r--r-- | gfx/wr/webrender/src/prim_store/line_dec.rs | 257 |
1 files changed, 257 insertions, 0 deletions
diff --git a/gfx/wr/webrender/src/prim_store/line_dec.rs b/gfx/wr/webrender/src/prim_store/line_dec.rs new file mode 100644 index 0000000000..496bab7569 --- /dev/null +++ b/gfx/wr/webrender/src/prim_store/line_dec.rs @@ -0,0 +1,257 @@ +/* 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, RasterSpace, + LineOrientation, LineStyle, PremultipliedColorF, Shadow, +}; +use api::units::*; +use crate::scene_building::{CreateShadow, IsVisible}; +use crate::frame_builder::{FrameBuildingState}; +use crate::gpu_cache::GpuDataRequest; +use crate::intern; +use crate::internal_types::LayoutPrimitiveInfo; +use crate::prim_store::{ + PrimKey, PrimTemplate, PrimTemplateCommonData, + InternablePrimitive, PrimitiveStore, +}; +use crate::prim_store::PrimitiveInstanceKind; + +/// Maximum resolution in device pixels at which line decorations are rasterized. +pub const MAX_LINE_DECORATION_RESOLUTION: u32 = 4096; + +#[derive(Clone, Debug, Hash, MallocSizeOf, PartialEq, Eq)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct LineDecorationCacheKey { + pub style: LineStyle, + pub orientation: LineOrientation, + pub wavy_line_thickness: Au, + pub size: LayoutSizeAu, +} + +/// Identifying key for a line decoration. +#[derive(Clone, Debug, Hash, MallocSizeOf, PartialEq, Eq)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct LineDecoration { + // If the cache_key is Some(..) it is a line decoration + // that relies on a render task (e.g. wavy). If the + // cache key is None, it uses a fast path to draw the + // line decoration as a solid rect. + pub cache_key: Option<LineDecorationCacheKey>, + pub color: ColorU, +} + +pub type LineDecorationKey = PrimKey<LineDecoration>; + +impl LineDecorationKey { + pub fn new( + info: &LayoutPrimitiveInfo, + line_dec: LineDecoration, + ) -> Self { + LineDecorationKey { + common: info.into(), + kind: line_dec, + } + } +} + +impl intern::InternDebug for LineDecorationKey {} + +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(MallocSizeOf)] +pub struct LineDecorationData { + pub cache_key: Option<LineDecorationCacheKey>, + pub color: ColorF, +} + +impl LineDecorationData { + /// 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, + common: &mut PrimTemplateCommonData, + frame_state: &mut FrameBuildingState, + ) { + if let Some(ref mut request) = frame_state.gpu_cache.request(&mut common.gpu_cache_handle) { + self.write_prim_gpu_blocks(request); + } + } + + fn write_prim_gpu_blocks( + &self, + request: &mut GpuDataRequest + ) { + match self.cache_key.as_ref() { + Some(cache_key) => { + request.push(self.color.premultiplied()); + request.push(PremultipliedColorF::WHITE); + request.push([ + cache_key.size.width.to_f32_px(), + cache_key.size.height.to_f32_px(), + 0.0, + 0.0, + ]); + } + None => { + request.push(self.color.premultiplied()); + } + } + } +} + +pub type LineDecorationTemplate = PrimTemplate<LineDecorationData>; + +impl From<LineDecorationKey> for LineDecorationTemplate { + fn from(line_dec: LineDecorationKey) -> Self { + let common = PrimTemplateCommonData::with_key_common(line_dec.common); + LineDecorationTemplate { + common, + kind: LineDecorationData { + cache_key: line_dec.kind.cache_key, + color: line_dec.kind.color.into(), + } + } + } +} + +pub type LineDecorationDataHandle = intern::Handle<LineDecoration>; + +impl intern::Internable for LineDecoration { + type Key = LineDecorationKey; + type StoreData = LineDecorationTemplate; + type InternData = (); + const PROFILE_COUNTER: usize = crate::profiler::INTERNED_LINE_DECORATIONS; +} + +impl InternablePrimitive for LineDecoration { + fn into_key( + self, + info: &LayoutPrimitiveInfo, + ) -> LineDecorationKey { + LineDecorationKey::new( + info, + self, + ) + } + + fn make_instance_kind( + _key: LineDecorationKey, + data_handle: LineDecorationDataHandle, + _: &mut PrimitiveStore, + _reference_frame_relative_offset: LayoutVector2D, + ) -> PrimitiveInstanceKind { + PrimitiveInstanceKind::LineDecoration { + data_handle, + render_task: None, + } + } +} + +impl CreateShadow for LineDecoration { + fn create_shadow( + &self, + shadow: &Shadow, + _: bool, + _: RasterSpace, + ) -> Self { + LineDecoration { + color: shadow.color.into(), + cache_key: self.cache_key.clone(), + } + } +} + +impl IsVisible for LineDecoration { + fn is_visible(&self) -> bool { + self.color.a > 0 + } +} + +/// Choose the decoration mask tile size for a given line. +/// +/// Given a line with overall size `rect_size` and the given `orientation`, +/// return the dimensions of a single mask tile for the decoration pattern +/// described by `style` and `wavy_line_thickness`. +/// +/// If `style` is `Solid`, no mask tile is necessary; return `None`. The other +/// styles each have their own characteristic periods of repetition, so for each +/// one, this function returns a `LayoutSize` with the right aspect ratio and +/// whose specific size is convenient for the `cs_line_decoration.glsl` fragment +/// shader to work with. The shader uses a local coordinate space in which the +/// tile fills a rectangle with one corner at the origin, and with the size this +/// function returns. +/// +/// The returned size is not necessarily in pixels; device scaling and other +/// concerns can still affect the actual task size. +/// +/// Regardless of whether `orientation` is `Vertical` or `Horizontal`, the +/// `width` and `height` of the returned size are always horizontal and +/// vertical, respectively. +pub fn get_line_decoration_size( + rect_size: &LayoutSize, + orientation: LineOrientation, + style: LineStyle, + wavy_line_thickness: f32, +) -> Option<LayoutSize> { + let h = match orientation { + LineOrientation::Horizontal => rect_size.height, + LineOrientation::Vertical => rect_size.width, + }; + + // TODO(gw): The formulae below are based on the existing gecko and line + // shader code. They give reasonable results for most inputs, + // but could definitely do with a detailed pass to get better + // quality on a wider range of inputs! + // See nsCSSRendering::PaintDecorationLine in Gecko. + + let (parallel, perpendicular) = match style { + LineStyle::Solid => { + return None; + } + LineStyle::Dashed => { + let dash_length = (3.0 * h).min(64.0).max(1.0); + + (2.0 * dash_length, 4.0) + } + LineStyle::Dotted => { + let diameter = h.min(64.0).max(1.0); + let period = 2.0 * diameter; + + (period, diameter) + } + LineStyle::Wavy => { + let line_thickness = wavy_line_thickness.max(1.0); + let slope_length = h - line_thickness; + let flat_length = ((line_thickness - 1.0) * 2.0).max(1.0); + let approx_period = 2.0 * (slope_length + flat_length); + + (approx_period, h) + } + }; + + Some(match orientation { + LineOrientation::Horizontal => LayoutSize::new(parallel, perpendicular), + LineOrientation::Vertical => LayoutSize::new(perpendicular, parallel), + }) +} + +#[test] +#[cfg(target_pointer_width = "64")] +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::<LineDecoration>(), 20, "LineDecoration size changed"); + assert_eq!(mem::size_of::<LineDecorationTemplate>(), 60, "LineDecorationTemplate size changed"); + assert_eq!(mem::size_of::<LineDecorationKey>(), 40, "LineDecorationKey size changed"); +} |