webrender/prim_store/
borders.rs

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
5use api::{NormalBorder, PremultipliedColorF, Shadow, RasterSpace};
6use api::units::*;
7use crate::border::create_border_segments;
8use crate::border::NormalBorderAu;
9use crate::scene_building::{CreateShadow, IsVisible};
10use crate::frame_builder::FrameBuildingState;
11use crate::gpu_cache::GpuDataRequest;
12use crate::intern;
13use crate::internal_types::{LayoutPrimitiveInfo, FrameId};
14use crate::prim_store::{
15    BorderSegmentInfo, BrushSegment, NinePatchDescriptor, PrimKey,
16    PrimTemplate, PrimTemplateCommonData,
17    PrimitiveInstanceKind, PrimitiveOpacity,
18    PrimitiveStore, InternablePrimitive,
19};
20use crate::resource_cache::ImageRequest;
21use crate::render_task::RenderTask;
22use crate::render_task_graph::RenderTaskId;
23
24use super::storage;
25
26#[cfg_attr(feature = "capture", derive(Serialize))]
27#[cfg_attr(feature = "replay", derive(Deserialize))]
28#[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash)]
29pub struct NormalBorderPrim {
30    pub border: NormalBorderAu,
31    pub widths: LayoutSideOffsetsAu,
32}
33
34pub type NormalBorderKey = PrimKey<NormalBorderPrim>;
35
36impl NormalBorderKey {
37    pub fn new(
38        info: &LayoutPrimitiveInfo,
39        normal_border: NormalBorderPrim,
40    ) -> Self {
41        NormalBorderKey {
42            common: info.into(),
43            kind: normal_border,
44        }
45    }
46}
47
48impl intern::InternDebug for NormalBorderKey {}
49
50#[cfg_attr(feature = "capture", derive(Serialize))]
51#[cfg_attr(feature = "replay", derive(Deserialize))]
52#[derive(MallocSizeOf)]
53pub struct NormalBorderData {
54    pub brush_segments: Vec<BrushSegment>,
55    pub border_segments: Vec<BorderSegmentInfo>,
56    pub border: NormalBorder,
57    pub widths: LayoutSideOffsets,
58}
59
60impl NormalBorderData {
61    /// Update the GPU cache for a given primitive template. This may be called multiple
62    /// times per frame, by each primitive reference that refers to this interned
63    /// template. The initial request call to the GPU cache ensures that work is only
64    /// done if the cache entry is invalid (due to first use or eviction).
65    pub fn update(
66        &mut self,
67        common: &mut PrimTemplateCommonData,
68        frame_state: &mut FrameBuildingState,
69    ) {
70        if let Some(ref mut request) = frame_state.gpu_cache.request(&mut common.gpu_cache_handle) {
71            self.write_prim_gpu_blocks(request, common.prim_rect.size());
72            self.write_segment_gpu_blocks(request);
73        }
74
75        common.opacity = PrimitiveOpacity::translucent();
76    }
77
78    fn write_prim_gpu_blocks(
79        &self,
80        request: &mut GpuDataRequest,
81        prim_size: LayoutSize
82    ) {
83        // Border primitives currently used for
84        // image borders, and run through the
85        // normal brush_image shader.
86        request.push(PremultipliedColorF::WHITE);
87        request.push(PremultipliedColorF::WHITE);
88        request.push([
89            prim_size.width,
90            prim_size.height,
91            0.0,
92            0.0,
93        ]);
94    }
95
96    fn write_segment_gpu_blocks(
97        &self,
98        request: &mut GpuDataRequest,
99    ) {
100        for segment in &self.brush_segments {
101            // has to match VECS_PER_SEGMENT
102            request.write_segment(
103                segment.local_rect,
104                segment.extra_data,
105            );
106        }
107    }
108}
109
110pub type NormalBorderTemplate = PrimTemplate<NormalBorderData>;
111
112impl From<NormalBorderKey> for NormalBorderTemplate {
113    fn from(key: NormalBorderKey) -> Self {
114        let common = PrimTemplateCommonData::with_key_common(key.common);
115
116        let mut border: NormalBorder = key.kind.border.into();
117        let widths = LayoutSideOffsets::from_au(key.kind.widths);
118
119        // FIXME(emilio): Is this the best place to do this?
120        border.normalize(&widths);
121
122        let mut brush_segments = Vec::new();
123        let mut border_segments = Vec::new();
124
125        create_border_segments(
126            common.prim_rect.size(),
127            &border,
128            &widths,
129            &mut border_segments,
130            &mut brush_segments,
131        );
132
133        NormalBorderTemplate {
134            common,
135            kind: NormalBorderData {
136                brush_segments,
137                border_segments,
138                border,
139                widths,
140            }
141        }
142    }
143}
144
145pub type NormalBorderDataHandle = intern::Handle<NormalBorderPrim>;
146
147impl intern::Internable for NormalBorderPrim {
148    type Key = NormalBorderKey;
149    type StoreData = NormalBorderTemplate;
150    type InternData = ();
151    const PROFILE_COUNTER: usize = crate::profiler::INTERNED_NORMAL_BORDERS;
152}
153
154impl InternablePrimitive for NormalBorderPrim {
155    fn into_key(
156        self,
157        info: &LayoutPrimitiveInfo,
158    ) -> NormalBorderKey {
159        NormalBorderKey::new(
160            info,
161            self,
162        )
163    }
164
165    fn make_instance_kind(
166        _key: NormalBorderKey,
167        data_handle: NormalBorderDataHandle,
168        _: &mut PrimitiveStore,
169    ) -> PrimitiveInstanceKind {
170        PrimitiveInstanceKind::NormalBorder {
171            data_handle,
172            render_task_ids: storage::Range::empty(),
173        }
174    }
175}
176
177impl CreateShadow for NormalBorderPrim {
178    fn create_shadow(
179        &self,
180        shadow: &Shadow,
181        _: bool,
182        _: RasterSpace,
183    ) -> Self {
184        let border = self.border.with_color(shadow.color.into());
185        NormalBorderPrim {
186            border,
187            widths: self.widths,
188        }
189    }
190}
191
192impl IsVisible for NormalBorderPrim {
193    fn is_visible(&self) -> bool {
194        true
195    }
196}
197
198////////////////////////////////////////////////////////////////////////////////
199
200#[cfg_attr(feature = "capture", derive(Serialize))]
201#[cfg_attr(feature = "replay", derive(Deserialize))]
202#[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash)]
203pub struct ImageBorder {
204    #[ignore_malloc_size_of = "Arc"]
205    pub request: ImageRequest,
206    pub nine_patch: NinePatchDescriptor,
207}
208
209pub type ImageBorderKey = PrimKey<ImageBorder>;
210
211impl ImageBorderKey {
212    pub fn new(
213        info: &LayoutPrimitiveInfo,
214        image_border: ImageBorder,
215    ) -> Self {
216        ImageBorderKey {
217            common: info.into(),
218            kind: image_border,
219        }
220    }
221}
222
223impl intern::InternDebug for ImageBorderKey {}
224
225
226#[cfg_attr(feature = "capture", derive(Serialize))]
227#[cfg_attr(feature = "replay", derive(Deserialize))]
228#[derive(MallocSizeOf)]
229pub struct ImageBorderData {
230    #[ignore_malloc_size_of = "Arc"]
231    pub request: ImageRequest,
232    pub brush_segments: Vec<BrushSegment>,
233    pub src_color: Option<RenderTaskId>,
234    pub frame_id: FrameId,
235    pub is_opaque: bool,
236}
237
238impl ImageBorderData {
239    /// Update the GPU cache for a given primitive template. This may be called multiple
240    /// times per frame, by each primitive reference that refers to this interned
241    /// template. The initial request call to the GPU cache ensures that work is only
242    /// done if the cache entry is invalid (due to first use or eviction).
243    pub fn update(
244        &mut self,
245        common: &mut PrimTemplateCommonData,
246        frame_state: &mut FrameBuildingState,
247    ) {
248        if let Some(ref mut request) = frame_state.gpu_cache.request(&mut common.gpu_cache_handle) {
249            self.write_prim_gpu_blocks(request, &common.prim_rect.size());
250            self.write_segment_gpu_blocks(request);
251        }
252
253        let frame_id = frame_state.rg_builder.frame_id();
254        if self.frame_id != frame_id {
255            self.frame_id = frame_id;
256
257            let size = frame_state.resource_cache.request_image(
258                self.request,
259                frame_state.gpu_cache,
260            );
261
262            let task_id = frame_state.rg_builder.add().init(
263                RenderTask::new_image(size, self.request)
264            );
265
266            self.src_color = Some(task_id);
267
268            let image_properties = frame_state
269                .resource_cache
270                .get_image_properties(self.request.key);
271
272            self.is_opaque = image_properties
273                .map(|properties| properties.descriptor.is_opaque())
274                .unwrap_or(true);
275        }
276
277        common.opacity = PrimitiveOpacity { is_opaque: self.is_opaque };
278    }
279
280    fn write_prim_gpu_blocks(
281        &self,
282        request: &mut GpuDataRequest,
283        prim_size: &LayoutSize,
284    ) {
285        // Border primitives currently used for
286        // image borders, and run through the
287        // normal brush_image shader.
288        request.push(PremultipliedColorF::WHITE);
289        request.push(PremultipliedColorF::WHITE);
290        request.push([
291            prim_size.width,
292            prim_size.height,
293            0.0,
294            0.0,
295        ]);
296    }
297
298    fn write_segment_gpu_blocks(
299        &self,
300        request: &mut GpuDataRequest,
301    ) {
302        for segment in &self.brush_segments {
303            // has to match VECS_PER_SEGMENT
304            request.write_segment(
305                segment.local_rect,
306                segment.extra_data,
307            );
308        }
309    }
310}
311
312pub type ImageBorderTemplate = PrimTemplate<ImageBorderData>;
313
314impl From<ImageBorderKey> for ImageBorderTemplate {
315    fn from(key: ImageBorderKey) -> Self {
316        let common = PrimTemplateCommonData::with_key_common(key.common);
317
318        let brush_segments = key.kind.nine_patch.create_segments(common.prim_rect.size());
319        ImageBorderTemplate {
320            common,
321            kind: ImageBorderData {
322                request: key.kind.request,
323                brush_segments,
324                src_color: None,
325                frame_id: FrameId::INVALID,
326                is_opaque: false,
327            }
328        }
329    }
330}
331
332pub type ImageBorderDataHandle = intern::Handle<ImageBorder>;
333
334impl intern::Internable for ImageBorder {
335    type Key = ImageBorderKey;
336    type StoreData = ImageBorderTemplate;
337    type InternData = ();
338    const PROFILE_COUNTER: usize = crate::profiler::INTERNED_IMAGE_BORDERS;
339}
340
341impl InternablePrimitive for ImageBorder {
342    fn into_key(
343        self,
344        info: &LayoutPrimitiveInfo,
345    ) -> ImageBorderKey {
346        ImageBorderKey::new(
347            info,
348            self,
349        )
350    }
351
352    fn make_instance_kind(
353        _key: ImageBorderKey,
354        data_handle: ImageBorderDataHandle,
355        _: &mut PrimitiveStore,
356    ) -> PrimitiveInstanceKind {
357        PrimitiveInstanceKind::ImageBorder {
358            data_handle
359        }
360    }
361}
362
363impl IsVisible for ImageBorder {
364    fn is_visible(&self) -> bool {
365        true
366    }
367}
368
369#[test]
370#[cfg(target_pointer_width = "64")]
371fn test_struct_sizes() {
372    use std::mem;
373    // The sizes of these structures are critical for performance on a number of
374    // talos stress tests. If you get a failure here on CI, there's two possibilities:
375    // (a) You made a structure smaller than it currently is. Great work! Update the
376    //     test expectations and move on.
377    // (b) You made a structure larger. This is not necessarily a problem, but should only
378    //     be done with care, and after checking if talos performance regresses badly.
379    assert_eq!(mem::size_of::<NormalBorderPrim>(), 84, "NormalBorderPrim size changed");
380    assert_eq!(mem::size_of::<NormalBorderTemplate>(), 216, "NormalBorderTemplate size changed");
381    assert_eq!(mem::size_of::<NormalBorderKey>(), 104, "NormalBorderKey size changed");
382    assert_eq!(mem::size_of::<ImageBorder>(), 68, "ImageBorder size changed");
383    assert_eq!(mem::size_of::<ImageBorderTemplate>(), 104, "ImageBorderTemplate size changed");
384    assert_eq!(mem::size_of::<ImageBorderKey>(), 88, "ImageBorderKey size changed");
385}