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     AlphaType, ColorDepth, ColorF, ColorU,
7     ImageKey as ApiImageKey, ImageRendering,
8     PremultipliedColorF, Shadow, YuvColorSpace, ColorRange, YuvFormat,
9 };
10 use api::units::*;
11 use crate::scene_building::{CreateShadow, IsVisible};
12 use crate::frame_builder::FrameBuildingState;
13 use crate::gpu_cache::{GpuCache, GpuDataRequest};
14 use crate::intern::{Internable, InternDebug, Handle as InternHandle};
15 use crate::internal_types::{LayoutPrimitiveInfo};
16 use crate::prim_store::{
17     EdgeAaSegmentMask, OpacityBindingIndex, PrimitiveInstanceKind,
18     PrimitiveOpacity, PrimKey,
19     PrimTemplate, PrimTemplateCommonData, PrimitiveStore, SegmentInstanceIndex,
20     SizeKey, InternablePrimitive,
21 };
22 use crate::render_target::RenderTargetKind;
23 use crate::render_task::{BlitSource, RenderTask};
24 use crate::render_task_cache::{
25     RenderTaskCacheEntryHandle, RenderTaskCacheKey, RenderTaskCacheKeyKind
26 };
27 use crate::resource_cache::{ImageRequest, ResourceCache};
28 use crate::util::pack_as_float;
29 
30 #[derive(Debug)]
31 #[cfg_attr(feature = "capture", derive(Serialize))]
32 #[cfg_attr(feature = "replay", derive(Deserialize))]
33 pub struct VisibleImageTile {
34     pub tile_offset: TileOffset,
35     pub edge_flags: EdgeAaSegmentMask,
36     pub local_rect: LayoutRect,
37     pub local_clip_rect: LayoutRect,
38 }
39 
40 // Key that identifies a unique (partial) image that is being
41 // stored in the render task cache.
42 #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
43 #[cfg_attr(feature = "capture", derive(Serialize))]
44 #[cfg_attr(feature = "replay", derive(Deserialize))]
45 pub struct ImageCacheKey {
46     pub request: ImageRequest,
47     pub texel_rect: Option<DeviceIntRect>,
48 }
49 
50 /// Instance specific fields for an image primitive. These are
51 /// currently stored in a separate array to avoid bloating the
52 /// size of PrimitiveInstance. In the future, we should be able
53 /// to remove this and store the information inline, by:
54 /// (a) Removing opacity collapse / binding support completely.
55 ///     Once we have general picture caching, we don't need this.
56 /// (b) Change visible_tiles to use Storage in the primitive
57 ///     scratch buffer. This will reduce the size of the
58 ///     visible_tiles field here, and save memory allocation
59 ///     when image tiling is used. I've left it as a Vec for
60 ///     now to reduce the number of changes, and because image
61 ///     tiling is very rare on real pages.
62 #[derive(Debug)]
63 #[cfg_attr(feature = "capture", derive(Serialize))]
64 pub struct ImageInstance {
65     pub opacity_binding_index: OpacityBindingIndex,
66     pub segment_instance_index: SegmentInstanceIndex,
67     pub tight_local_clip_rect: LayoutRect,
68     pub visible_tiles: Vec<VisibleImageTile>,
69 }
70 
71 #[cfg_attr(feature = "capture", derive(Serialize))]
72 #[cfg_attr(feature = "replay", derive(Deserialize))]
73 #[derive(Debug, Clone, Eq, PartialEq, MallocSizeOf, Hash)]
74 pub struct Image {
75     pub key: ApiImageKey,
76     pub stretch_size: SizeKey,
77     pub tile_spacing: SizeKey,
78     pub color: ColorU,
79     pub image_rendering: ImageRendering,
80     pub alpha_type: AlphaType,
81 }
82 
83 pub type ImageKey = PrimKey<Image>;
84 
85 impl ImageKey {
new( info: &LayoutPrimitiveInfo, image: Image, ) -> Self86     pub fn new(
87         info: &LayoutPrimitiveInfo,
88         image: Image,
89     ) -> Self {
90         ImageKey {
91             common: info.into(),
92             kind: image,
93         }
94     }
95 }
96 
97 impl InternDebug for ImageKey {}
98 
99 // Where to find the texture data for an image primitive.
100 #[cfg_attr(feature = "capture", derive(Serialize))]
101 #[cfg_attr(feature = "replay", derive(Deserialize))]
102 #[derive(Debug, MallocSizeOf)]
103 pub enum ImageSource {
104     // A normal image - just reference the texture cache.
105     Default,
106     // An image that is pre-rendered into the texture cache
107     // via a render task.
108     Cache {
109         size: DeviceIntSize,
110         handle: Option<RenderTaskCacheEntryHandle>,
111     },
112 }
113 
114 #[cfg_attr(feature = "capture", derive(Serialize))]
115 #[cfg_attr(feature = "replay", derive(Deserialize))]
116 #[derive(Debug, MallocSizeOf)]
117 pub struct ImageData {
118     pub key: ApiImageKey,
119     pub stretch_size: LayoutSize,
120     pub tile_spacing: LayoutSize,
121     pub color: ColorF,
122     pub source: ImageSource,
123     pub image_rendering: ImageRendering,
124     pub alpha_type: AlphaType,
125 }
126 
127 impl From<Image> for ImageData {
from(image: Image) -> Self128     fn from(image: Image) -> Self {
129         ImageData {
130             key: image.key,
131             color: image.color.into(),
132             stretch_size: image.stretch_size.into(),
133             tile_spacing: image.tile_spacing.into(),
134             source: ImageSource::Default,
135             image_rendering: image.image_rendering,
136             alpha_type: image.alpha_type,
137         }
138     }
139 }
140 
141 impl ImageData {
142     /// Update the GPU cache for a given primitive template. This may be called multiple
143     /// times per frame, by each primitive reference that refers to this interned
144     /// template. The initial request call to the GPU cache ensures that work is only
145     /// done if the cache entry is invalid (due to first use or eviction).
update( &mut self, common: &mut PrimTemplateCommonData, frame_state: &mut FrameBuildingState, )146     pub fn update(
147         &mut self,
148         common: &mut PrimTemplateCommonData,
149         frame_state: &mut FrameBuildingState,
150     ) {
151         if let Some(mut request) = frame_state.gpu_cache.request(&mut common.gpu_cache_handle) {
152             self.write_prim_gpu_blocks(&mut request);
153         }
154 
155         common.opacity = {
156             let image_properties = frame_state
157                 .resource_cache
158                 .get_image_properties(self.key);
159 
160             match image_properties {
161                 Some(image_properties) => {
162                     let is_tiled = image_properties.tiling.is_some();
163 
164                     if self.tile_spacing != LayoutSize::zero() && !is_tiled {
165                         self.source = ImageSource::Cache {
166                             // Size in device-pixels we need to allocate in render task cache.
167                             size: image_properties.descriptor.size.to_i32(),
168                             handle: None,
169                         };
170                     }
171 
172                     let mut is_opaque = image_properties.descriptor.is_opaque();
173                     let request = ImageRequest {
174                         key: self.key,
175                         rendering: self.image_rendering,
176                         tile: None,
177                     };
178 
179                     // Every frame, for cached items, we need to request the render
180                     // task cache item. The closure will be invoked on the first
181                     // time through, and any time the render task output has been
182                     // evicted from the texture cache.
183                     match self.source {
184                         ImageSource::Cache { ref mut size, ref mut handle } => {
185                             let padding = DeviceIntSideOffsets::new(
186                                 0,
187                                 (self.tile_spacing.width * size.width as f32 / self.stretch_size.width) as i32,
188                                 (self.tile_spacing.height * size.height as f32 / self.stretch_size.height) as i32,
189                                 0,
190                             );
191 
192                             size.width += padding.horizontal();
193                             size.height += padding.vertical();
194 
195                             is_opaque &= padding == DeviceIntSideOffsets::zero();
196 
197                             let image_cache_key = ImageCacheKey {
198                                 request,
199                                 texel_rect: None,
200                             };
201                             let target_kind = if image_properties.descriptor.format.bytes_per_pixel() == 1 {
202                                 RenderTargetKind::Alpha
203                             } else {
204                                 RenderTargetKind::Color
205                             };
206 
207                             // Request a pre-rendered image task.
208                             *handle = Some(frame_state.resource_cache.request_render_task(
209                                 RenderTaskCacheKey {
210                                     size: *size,
211                                     kind: RenderTaskCacheKeyKind::Image(image_cache_key),
212                                 },
213                                 frame_state.gpu_cache,
214                                 frame_state.render_tasks,
215                                 None,
216                                 image_properties.descriptor.is_opaque(),
217                                 |render_tasks| {
218                                     // Create a task to blit from the texture cache to
219                                     // a normal transient render task surface. This will
220                                     // copy only the sub-rect, if specified.
221                                     // TODO: figure out if/when we can do a blit instead.
222                                     let cache_to_target_task_id = RenderTask::new_scaling_with_padding(
223                                         BlitSource::Image { key: image_cache_key },
224                                         render_tasks,
225                                         target_kind,
226                                         *size,
227                                         padding,
228                                     );
229 
230                                     // Create a task to blit the rect from the child render
231                                     // task above back into the right spot in the persistent
232                                     // render target cache.
233                                     render_tasks.add().init(RenderTask::new_blit(
234                                         *size,
235                                         BlitSource::RenderTask {
236                                             task_id: cache_to_target_task_id,
237                                         },
238                                     ))
239                                 }
240                             ));
241                         }
242                         ImageSource::Default => {}
243                     }
244 
245                     if is_opaque {
246                         PrimitiveOpacity::from_alpha(self.color.a)
247                     } else {
248                         PrimitiveOpacity::translucent()
249                     }
250                 }
251                 None => {
252                     PrimitiveOpacity::opaque()
253                 }
254             }
255         };
256     }
257 
write_prim_gpu_blocks(&self, request: &mut GpuDataRequest)258     pub fn write_prim_gpu_blocks(&self, request: &mut GpuDataRequest) {
259         // Images are drawn as a white color, modulated by the total
260         // opacity coming from any collapsed property bindings.
261         // Size has to match `VECS_PER_SPECIFIC_BRUSH` from `brush_image.glsl` exactly.
262         request.push(self.color.premultiplied());
263         request.push(PremultipliedColorF::WHITE);
264         request.push([
265             self.stretch_size.width + self.tile_spacing.width,
266             self.stretch_size.height + self.tile_spacing.height,
267             0.0,
268             0.0,
269         ]);
270     }
271 }
272 
273 pub type ImageTemplate = PrimTemplate<ImageData>;
274 
275 impl From<ImageKey> for ImageTemplate {
from(image: ImageKey) -> Self276     fn from(image: ImageKey) -> Self {
277         let common = PrimTemplateCommonData::with_key_common(image.common);
278 
279         ImageTemplate {
280             common,
281             kind: image.kind.into(),
282         }
283     }
284 }
285 
286 pub type ImageDataHandle = InternHandle<Image>;
287 
288 impl Internable for Image {
289     type Key = ImageKey;
290     type StoreData = ImageTemplate;
291     type InternData = ();
292 }
293 
294 impl InternablePrimitive for Image {
into_key( self, info: &LayoutPrimitiveInfo, ) -> ImageKey295     fn into_key(
296         self,
297         info: &LayoutPrimitiveInfo,
298     ) -> ImageKey {
299         ImageKey::new(info, self)
300     }
301 
make_instance_kind( _key: ImageKey, data_handle: ImageDataHandle, prim_store: &mut PrimitiveStore, _reference_frame_relative_offset: LayoutVector2D, ) -> PrimitiveInstanceKind302     fn make_instance_kind(
303         _key: ImageKey,
304         data_handle: ImageDataHandle,
305         prim_store: &mut PrimitiveStore,
306         _reference_frame_relative_offset: LayoutVector2D,
307     ) -> PrimitiveInstanceKind {
308         // TODO(gw): Refactor this to not need a separate image
309         //           instance (see ImageInstance struct).
310         let image_instance_index = prim_store.images.push(ImageInstance {
311             opacity_binding_index: OpacityBindingIndex::INVALID,
312             segment_instance_index: SegmentInstanceIndex::INVALID,
313             tight_local_clip_rect: LayoutRect::zero(),
314             visible_tiles: Vec::new(),
315         });
316 
317         PrimitiveInstanceKind::Image {
318             data_handle,
319             image_instance_index,
320             is_compositor_surface: false,
321         }
322     }
323 }
324 
325 impl CreateShadow for Image {
create_shadow(&self, shadow: &Shadow) -> Self326     fn create_shadow(&self, shadow: &Shadow) -> Self {
327         Image {
328             tile_spacing: self.tile_spacing,
329             stretch_size: self.stretch_size,
330             key: self.key,
331             image_rendering: self.image_rendering,
332             alpha_type: self.alpha_type,
333             color: shadow.color.into(),
334         }
335     }
336 }
337 
338 impl IsVisible for Image {
is_visible(&self) -> bool339     fn is_visible(&self) -> bool {
340         true
341     }
342 }
343 
344 ////////////////////////////////////////////////////////////////////////////////
345 
346 #[cfg_attr(feature = "capture", derive(Serialize))]
347 #[cfg_attr(feature = "replay", derive(Deserialize))]
348 #[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash)]
349 pub struct YuvImage {
350     pub color_depth: ColorDepth,
351     pub yuv_key: [ApiImageKey; 3],
352     pub format: YuvFormat,
353     pub color_space: YuvColorSpace,
354     pub color_range: ColorRange,
355     pub image_rendering: ImageRendering,
356 }
357 
358 pub type YuvImageKey = PrimKey<YuvImage>;
359 
360 impl YuvImageKey {
new( info: &LayoutPrimitiveInfo, yuv_image: YuvImage, ) -> Self361     pub fn new(
362         info: &LayoutPrimitiveInfo,
363         yuv_image: YuvImage,
364     ) -> Self {
365         YuvImageKey {
366             common: info.into(),
367             kind: yuv_image,
368         }
369     }
370 }
371 
372 impl InternDebug for YuvImageKey {}
373 
374 #[cfg_attr(feature = "capture", derive(Serialize))]
375 #[cfg_attr(feature = "replay", derive(Deserialize))]
376 #[derive(MallocSizeOf)]
377 pub struct YuvImageData {
378     pub color_depth: ColorDepth,
379     pub yuv_key: [ApiImageKey; 3],
380     pub format: YuvFormat,
381     pub color_space: YuvColorSpace,
382     pub color_range: ColorRange,
383     pub image_rendering: ImageRendering,
384 }
385 
386 impl From<YuvImage> for YuvImageData {
from(image: YuvImage) -> Self387     fn from(image: YuvImage) -> Self {
388         YuvImageData {
389             color_depth: image.color_depth,
390             yuv_key: image.yuv_key,
391             format: image.format,
392             color_space: image.color_space,
393             color_range: image.color_range,
394             image_rendering: image.image_rendering,
395         }
396     }
397 }
398 
399 impl YuvImageData {
400     /// Update the GPU cache for a given primitive template. This may be called multiple
401     /// times per frame, by each primitive reference that refers to this interned
402     /// template. The initial request call to the GPU cache ensures that work is only
403     /// done if the cache entry is invalid (due to first use or eviction).
update( &mut self, common: &mut PrimTemplateCommonData, frame_state: &mut FrameBuildingState, )404     pub fn update(
405         &mut self,
406         common: &mut PrimTemplateCommonData,
407         frame_state: &mut FrameBuildingState,
408     ) {
409         if let Some(mut request) = frame_state.gpu_cache.request(&mut common.gpu_cache_handle) {
410             self.write_prim_gpu_blocks(&mut request);
411         };
412 
413         // YUV images never have transparency
414         common.opacity = PrimitiveOpacity::opaque();
415     }
416 
request_resources( &mut self, resource_cache: &mut ResourceCache, gpu_cache: &mut GpuCache, )417     pub fn request_resources(
418         &mut self,
419         resource_cache: &mut ResourceCache,
420         gpu_cache: &mut GpuCache,
421     ) {
422         let channel_num = self.format.get_plane_num();
423         debug_assert!(channel_num <= 3);
424         for channel in 0 .. channel_num {
425             resource_cache.request_image(
426                 ImageRequest {
427                     key: self.yuv_key[channel],
428                     rendering: self.image_rendering,
429                     tile: None,
430                 },
431                 gpu_cache,
432             );
433         }
434     }
435 
write_prim_gpu_blocks(&self, request: &mut GpuDataRequest)436     pub fn write_prim_gpu_blocks(&self, request: &mut GpuDataRequest) {
437         request.push([
438             self.color_depth.rescaling_factor(),
439             pack_as_float(self.color_space as u32),
440             pack_as_float(self.format as u32),
441             0.0
442         ]);
443     }
444 }
445 
446 pub type YuvImageTemplate = PrimTemplate<YuvImageData>;
447 
448 impl From<YuvImageKey> for YuvImageTemplate {
from(image: YuvImageKey) -> Self449     fn from(image: YuvImageKey) -> Self {
450         let common = PrimTemplateCommonData::with_key_common(image.common);
451 
452         YuvImageTemplate {
453             common,
454             kind: image.kind.into(),
455         }
456     }
457 }
458 
459 pub type YuvImageDataHandle = InternHandle<YuvImage>;
460 
461 impl Internable for YuvImage {
462     type Key = YuvImageKey;
463     type StoreData = YuvImageTemplate;
464     type InternData = ();
465 }
466 
467 impl InternablePrimitive for YuvImage {
into_key( self, info: &LayoutPrimitiveInfo, ) -> YuvImageKey468     fn into_key(
469         self,
470         info: &LayoutPrimitiveInfo,
471     ) -> YuvImageKey {
472         YuvImageKey::new(info, self)
473     }
474 
make_instance_kind( _key: YuvImageKey, data_handle: YuvImageDataHandle, _prim_store: &mut PrimitiveStore, _reference_frame_relative_offset: LayoutVector2D, ) -> PrimitiveInstanceKind475     fn make_instance_kind(
476         _key: YuvImageKey,
477         data_handle: YuvImageDataHandle,
478         _prim_store: &mut PrimitiveStore,
479         _reference_frame_relative_offset: LayoutVector2D,
480     ) -> PrimitiveInstanceKind {
481         PrimitiveInstanceKind::YuvImage {
482             data_handle,
483             segment_instance_index: SegmentInstanceIndex::INVALID,
484             is_compositor_surface: false,
485         }
486     }
487 }
488 
489 impl IsVisible for YuvImage {
is_visible(&self) -> bool490     fn is_visible(&self) -> bool {
491         true
492     }
493 }
494 
495 #[test]
496 #[cfg(target_pointer_width = "64")]
test_struct_sizes()497 fn test_struct_sizes() {
498     use std::mem;
499     // The sizes of these structures are critical for performance on a number of
500     // talos stress tests. If you get a failure here on CI, there's two possibilities:
501     // (a) You made a structure smaller than it currently is. Great work! Update the
502     //     test expectations and move on.
503     // (b) You made a structure larger. This is not necessarily a problem, but should only
504     //     be done with care, and after checking if talos performance regresses badly.
505     assert_eq!(mem::size_of::<Image>(), 32, "Image size changed");
506     assert_eq!(mem::size_of::<ImageTemplate>(), 92, "ImageTemplate size changed");
507     assert_eq!(mem::size_of::<ImageKey>(), 52, "ImageKey size changed");
508     assert_eq!(mem::size_of::<YuvImage>(), 32, "YuvImage size changed");
509     assert_eq!(mem::size_of::<YuvImageTemplate>(), 60, "YuvImageTemplate size changed");
510     assert_eq!(mem::size_of::<YuvImageKey>(), 52, "YuvImageKey size changed");
511 }
512