tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

line_dec.rs (8927B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 use api::{
      6    ColorF, ColorU, RasterSpace,
      7    LineOrientation, LineStyle, PremultipliedColorF, Shadow,
      8 };
      9 use api::units::*;
     10 use crate::gpu_types::ImageBrushPrimitiveData;
     11 use crate::renderer::GpuBufferWriterF;
     12 use crate::scene_building::{CreateShadow, IsVisible};
     13 use crate::frame_builder::FrameBuildingState;
     14 use crate::intern;
     15 use crate::internal_types::LayoutPrimitiveInfo;
     16 use crate::prim_store::{
     17    PrimKey, PrimTemplate, PrimTemplateCommonData,
     18    InternablePrimitive, PrimitiveStore,
     19 };
     20 use crate::prim_store::PrimitiveInstanceKind;
     21 
     22 /// Maximum resolution in device pixels at which line decorations are rasterized.
     23 pub const MAX_LINE_DECORATION_RESOLUTION: u32 = 4096;
     24 
     25 #[derive(Clone, Debug, Hash, MallocSizeOf, PartialEq, Eq)]
     26 #[cfg_attr(feature = "capture", derive(Serialize))]
     27 #[cfg_attr(feature = "replay", derive(Deserialize))]
     28 pub struct LineDecorationCacheKey {
     29    pub style: LineStyle,
     30    pub orientation: LineOrientation,
     31    pub wavy_line_thickness: Au,
     32    pub size: LayoutSizeAu,
     33 }
     34 
     35 /// Identifying key for a line decoration.
     36 #[derive(Clone, Debug, Hash, MallocSizeOf, PartialEq, Eq)]
     37 #[cfg_attr(feature = "capture", derive(Serialize))]
     38 #[cfg_attr(feature = "replay", derive(Deserialize))]
     39 pub struct LineDecoration {
     40    // If the cache_key is Some(..) it is a line decoration
     41    // that relies on a render task (e.g. wavy). If the
     42    // cache key is None, it uses a fast path to draw the
     43    // line decoration as a solid rect.
     44    pub cache_key: Option<LineDecorationCacheKey>,
     45    pub color: ColorU,
     46 }
     47 
     48 pub type LineDecorationKey = PrimKey<LineDecoration>;
     49 
     50 impl LineDecorationKey {
     51    pub fn new(
     52        info: &LayoutPrimitiveInfo,
     53        line_dec: LineDecoration,
     54    ) -> Self {
     55        LineDecorationKey {
     56            common: info.into(),
     57            kind: line_dec,
     58        }
     59    }
     60 }
     61 
     62 impl intern::InternDebug for LineDecorationKey {}
     63 
     64 #[cfg_attr(feature = "capture", derive(Serialize))]
     65 #[cfg_attr(feature = "replay", derive(Deserialize))]
     66 #[derive(MallocSizeOf)]
     67 pub struct LineDecorationData {
     68    pub cache_key: Option<LineDecorationCacheKey>,
     69    pub color: ColorF,
     70 }
     71 
     72 impl LineDecorationData {
     73    /// Update the GPU cache for a given primitive template. This may be called multiple
     74    /// times per frame, by each primitive reference that refers to this interned
     75    /// template. The initial request call to the GPU cache ensures that work is only
     76    /// done if the cache entry is invalid (due to first use or eviction).
     77    pub fn update(
     78        &mut self,
     79        common: &mut PrimTemplateCommonData,
     80        frame_state: &mut FrameBuildingState,
     81    ) {
     82        let mut writer = frame_state.frame_gpu_data.f32.write_blocks(3);
     83        self.write_prim_gpu_blocks(&mut writer);
     84        common.gpu_buffer_address = writer.finish();
     85    }
     86 
     87    fn write_prim_gpu_blocks(
     88        &self,
     89        writer: &mut GpuBufferWriterF
     90    ) {
     91        match self.cache_key.as_ref() {
     92            Some(cache_key) => {
     93                writer.push(&ImageBrushPrimitiveData {
     94                    color: self.color.premultiplied(),
     95                    background_color: PremultipliedColorF::WHITE,
     96                    stretch_size: LayoutSize::new(
     97                        cache_key.size.width.to_f32_px(),
     98                        cache_key.size.height.to_f32_px(),
     99                    ),
    100                });
    101            }
    102            None => {
    103                writer.push_one(self.color.premultiplied());
    104            }
    105        }
    106    }
    107 }
    108 
    109 pub type LineDecorationTemplate = PrimTemplate<LineDecorationData>;
    110 
    111 impl From<LineDecorationKey> for LineDecorationTemplate {
    112    fn from(line_dec: LineDecorationKey) -> Self {
    113        let common = PrimTemplateCommonData::with_key_common(line_dec.common);
    114        LineDecorationTemplate {
    115            common,
    116            kind: LineDecorationData {
    117                cache_key: line_dec.kind.cache_key,
    118                color: line_dec.kind.color.into(),
    119            }
    120        }
    121    }
    122 }
    123 
    124 pub type LineDecorationDataHandle = intern::Handle<LineDecoration>;
    125 
    126 impl intern::Internable for LineDecoration {
    127    type Key = LineDecorationKey;
    128    type StoreData = LineDecorationTemplate;
    129    type InternData = ();
    130    const PROFILE_COUNTER: usize = crate::profiler::INTERNED_LINE_DECORATIONS;
    131 }
    132 
    133 impl InternablePrimitive for LineDecoration {
    134    fn into_key(
    135        self,
    136        info: &LayoutPrimitiveInfo,
    137    ) -> LineDecorationKey {
    138        LineDecorationKey::new(
    139            info,
    140            self,
    141        )
    142    }
    143 
    144    fn make_instance_kind(
    145        _key: LineDecorationKey,
    146        data_handle: LineDecorationDataHandle,
    147        _: &mut PrimitiveStore,
    148    ) -> PrimitiveInstanceKind {
    149        PrimitiveInstanceKind::LineDecoration {
    150            data_handle,
    151            render_task: None,
    152        }
    153    }
    154 }
    155 
    156 impl CreateShadow for LineDecoration {
    157    fn create_shadow(
    158        &self,
    159        shadow: &Shadow,
    160        _: bool,
    161        _: RasterSpace,
    162    ) -> Self {
    163        LineDecoration {
    164            color: shadow.color.into(),
    165            cache_key: self.cache_key.clone(),
    166        }
    167    }
    168 }
    169 
    170 impl IsVisible for LineDecoration {
    171    fn is_visible(&self) -> bool {
    172        self.color.a > 0
    173    }
    174 }
    175 
    176 /// Choose the decoration mask tile size for a given line.
    177 ///
    178 /// Given a line with overall size `rect_size` and the given `orientation`,
    179 /// return the dimensions of a single mask tile for the decoration pattern
    180 /// described by `style` and `wavy_line_thickness`.
    181 ///
    182 /// If `style` is `Solid`, no mask tile is necessary; return `None`. The other
    183 /// styles each have their own characteristic periods of repetition, so for each
    184 /// one, this function returns a `LayoutSize` with the right aspect ratio and
    185 /// whose specific size is convenient for the `cs_line_decoration.glsl` fragment
    186 /// shader to work with. The shader uses a local coordinate space in which the
    187 /// tile fills a rectangle with one corner at the origin, and with the size this
    188 /// function returns.
    189 ///
    190 /// The returned size is not necessarily in pixels; device scaling and other
    191 /// concerns can still affect the actual task size.
    192 ///
    193 /// Regardless of whether `orientation` is `Vertical` or `Horizontal`, the
    194 /// `width` and `height` of the returned size are always horizontal and
    195 /// vertical, respectively.
    196 pub fn get_line_decoration_size(
    197    rect_size: &LayoutSize,
    198    orientation: LineOrientation,
    199    style: LineStyle,
    200    wavy_line_thickness: f32,
    201 ) -> Option<LayoutSize> {
    202    let h = match orientation {
    203        LineOrientation::Horizontal => rect_size.height,
    204        LineOrientation::Vertical => rect_size.width,
    205    };
    206 
    207    // TODO(gw): The formulae below are based on the existing gecko and line
    208    //           shader code. They give reasonable results for most inputs,
    209    //           but could definitely do with a detailed pass to get better
    210    //           quality on a wider range of inputs!
    211    //           See nsCSSRendering::PaintDecorationLine in Gecko.
    212 
    213    let (parallel, perpendicular) = match style {
    214        LineStyle::Solid => {
    215            return None;
    216        }
    217        LineStyle::Dashed => {
    218            let dash_length = (3.0 * h).min(64.0).max(1.0);
    219 
    220            (2.0 * dash_length, 4.0)
    221        }
    222        LineStyle::Dotted => {
    223            let diameter = h.min(64.0).max(1.0);
    224            let period = 2.0 * diameter;
    225 
    226            (period, diameter)
    227        }
    228        LineStyle::Wavy => {
    229            let line_thickness = wavy_line_thickness.max(1.0);
    230            let slope_length = h - line_thickness;
    231            let flat_length = ((line_thickness - 1.0) * 2.0).max(1.0);
    232            let approx_period = 2.0 * (slope_length + flat_length);
    233 
    234            (approx_period, h)
    235        }
    236    };
    237 
    238    Some(match orientation {
    239        LineOrientation::Horizontal => LayoutSize::new(parallel, perpendicular),
    240        LineOrientation::Vertical => LayoutSize::new(perpendicular, parallel),
    241    })
    242 }
    243 
    244 #[test]
    245 #[cfg(target_pointer_width = "64")]
    246 fn test_struct_sizes() {
    247    use std::mem;
    248    // The sizes of these structures are critical for performance on a number of
    249    // talos stress tests. If you get a failure here on CI, there's two possibilities:
    250    // (a) You made a structure smaller than it currently is. Great work! Update the
    251    //     test expectations and move on.
    252    // (b) You made a structure larger. This is not necessarily a problem, but should only
    253    //     be done with care, and after checking if talos performance regresses badly.
    254    assert_eq!(mem::size_of::<LineDecoration>(), 20, "LineDecoration size changed");
    255    assert_eq!(mem::size_of::<LineDecorationTemplate>(), 56, "LineDecorationTemplate size changed");
    256    assert_eq!(mem::size_of::<LineDecorationKey>(), 40, "LineDecorationKey size changed");
    257 }