webrender/prim_store/
image.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::{
6    AlphaType, ColorDepth, ColorF, ColorU, ExternalImageType,
7    ImageKey as ApiImageKey, ImageBufferKind, ImageRendering, PremultipliedColorF,
8    RasterSpace, Shadow, YuvColorSpace, ColorRange, YuvFormat,
9};
10use api::units::*;
11use euclid::point2;
12use crate::composite::CompositorSurfaceKind;
13use crate::scene_building::{CreateShadow, IsVisible};
14use crate::frame_builder::{FrameBuildingContext, FrameBuildingState};
15use crate::gpu_cache::{GpuCache, GpuDataRequest};
16use crate::intern::{Internable, InternDebug, Handle as InternHandle};
17use crate::internal_types::LayoutPrimitiveInfo;
18use crate::prim_store::{
19    EdgeAaSegmentMask, PrimitiveInstanceKind,
20    PrimitiveOpacity, PrimKey,
21    PrimTemplate, PrimTemplateCommonData, PrimitiveStore, SegmentInstanceIndex,
22    SizeKey, InternablePrimitive,
23};
24use crate::render_target::RenderTargetKind;
25use crate::render_task_graph::RenderTaskId;
26use crate::render_task::RenderTask;
27use crate::render_task_cache::{
28    RenderTaskCacheKey, RenderTaskCacheKeyKind, RenderTaskParent
29};
30use crate::resource_cache::{ImageRequest, ImageProperties, ResourceCache};
31use crate::util::pack_as_float;
32use crate::visibility::{PrimitiveVisibility, compute_conservative_visible_rect};
33use crate::spatial_tree::SpatialNodeIndex;
34use crate::image_tiling;
35
36#[derive(Debug)]
37#[cfg_attr(feature = "capture", derive(Serialize))]
38#[cfg_attr(feature = "replay", derive(Deserialize))]
39pub struct VisibleImageTile {
40    pub src_color: RenderTaskId,
41    pub edge_flags: EdgeAaSegmentMask,
42    pub local_rect: LayoutRect,
43    pub local_clip_rect: LayoutRect,
44}
45
46// Key that identifies a unique (partial) image that is being
47// stored in the render task cache.
48#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
49#[cfg_attr(feature = "capture", derive(Serialize))]
50#[cfg_attr(feature = "replay", derive(Deserialize))]
51pub struct ImageCacheKey {
52    pub request: ImageRequest,
53    pub texel_rect: Option<DeviceIntRect>,
54}
55
56/// Instance specific fields for an image primitive. These are
57/// currently stored in a separate array to avoid bloating the
58/// size of PrimitiveInstance. In the future, we should be able
59/// to remove this and store the information inline, by:
60/// (a) Removing opacity collapse / binding support completely.
61///     Once we have general picture caching, we don't need this.
62/// (b) Change visible_tiles to use Storage in the primitive
63///     scratch buffer. This will reduce the size of the
64///     visible_tiles field here, and save memory allocation
65///     when image tiling is used. I've left it as a Vec for
66///     now to reduce the number of changes, and because image
67///     tiling is very rare on real pages.
68#[derive(Debug)]
69#[cfg_attr(feature = "capture", derive(Serialize))]
70pub struct ImageInstance {
71    pub segment_instance_index: SegmentInstanceIndex,
72    pub tight_local_clip_rect: LayoutRect,
73    pub visible_tiles: Vec<VisibleImageTile>,
74    pub src_color: Option<RenderTaskId>,
75    pub normalized_uvs: bool,
76    pub adjustment: AdjustedImageSource,
77}
78
79#[cfg_attr(feature = "capture", derive(Serialize))]
80#[cfg_attr(feature = "replay", derive(Deserialize))]
81#[derive(Debug, Clone, Eq, PartialEq, MallocSizeOf, Hash)]
82pub struct Image {
83    pub key: ApiImageKey,
84    pub stretch_size: SizeKey,
85    pub tile_spacing: SizeKey,
86    pub color: ColorU,
87    pub image_rendering: ImageRendering,
88    pub alpha_type: AlphaType,
89}
90
91pub type ImageKey = PrimKey<Image>;
92
93impl ImageKey {
94    pub fn new(
95        info: &LayoutPrimitiveInfo,
96        image: Image,
97    ) -> Self {
98        ImageKey {
99            common: info.into(),
100            kind: image,
101        }
102    }
103}
104
105impl InternDebug for ImageKey {}
106
107#[cfg_attr(feature = "capture", derive(Serialize))]
108#[cfg_attr(feature = "replay", derive(Deserialize))]
109#[derive(Debug, MallocSizeOf)]
110pub struct ImageData {
111    pub key: ApiImageKey,
112    pub stretch_size: LayoutSize,
113    pub tile_spacing: LayoutSize,
114    pub color: ColorF,
115    pub image_rendering: ImageRendering,
116    pub alpha_type: AlphaType,
117}
118
119impl From<Image> for ImageData {
120    fn from(image: Image) -> Self {
121        ImageData {
122            key: image.key,
123            color: image.color.into(),
124            stretch_size: image.stretch_size.into(),
125            tile_spacing: image.tile_spacing.into(),
126            image_rendering: image.image_rendering,
127            alpha_type: image.alpha_type,
128        }
129    }
130}
131
132impl ImageData {
133    /// Update the GPU cache for a given primitive template. This may be called multiple
134    /// times per frame, by each primitive reference that refers to this interned
135    /// template. The initial request call to the GPU cache ensures that work is only
136    /// done if the cache entry is invalid (due to first use or eviction).
137    pub fn update(
138        &mut self,
139        common: &mut PrimTemplateCommonData,
140        image_instance: &mut ImageInstance,
141        prim_spatial_node_index: SpatialNodeIndex,
142        frame_state: &mut FrameBuildingState,
143        frame_context: &FrameBuildingContext,
144        visibility: &mut PrimitiveVisibility,
145    ) {
146
147        let image_properties = frame_state
148            .resource_cache
149            .get_image_properties(self.key);
150
151        common.opacity = match &image_properties {
152            Some(properties) => {
153                if properties.descriptor.is_opaque() {
154                    PrimitiveOpacity::from_alpha(self.color.a)
155                } else {
156                    PrimitiveOpacity::translucent()
157                }
158            }
159            None => PrimitiveOpacity::opaque(),
160        };
161
162        if self.stretch_size.width >= common.prim_rect.width() &&
163            self.stretch_size.height >= common.prim_rect.height() {
164
165            common.may_need_repetition = false;
166        }
167
168        let request = ImageRequest {
169            key: self.key,
170            rendering: self.image_rendering,
171            tile: None,
172        };
173
174        // Tighten the clip rect because decomposing the repeated image can
175        // produce primitives that are partially covering the original image
176        // rect and we want to clip these extra parts out.
177        // We also rely on having a tight clip rect in some cases other than
178        // tiled/repeated images, for example when rendering a snapshot image
179        // where the snapshot area is tighter than the rasterized area.
180        let tight_clip_rect = visibility
181            .clip_chain
182            .local_clip_rect
183            .intersection(&common.prim_rect).unwrap();
184        image_instance.tight_local_clip_rect = tight_clip_rect;
185
186        image_instance.adjustment = AdjustedImageSource::new();
187
188        match image_properties {
189            // Non-tiled (most common) path.
190            Some(ImageProperties { tiling: None, ref descriptor, ref external_image, adjustment, .. }) => {
191                image_instance.adjustment = adjustment;
192
193                let mut size = frame_state.resource_cache.request_image(
194                    request,
195                    frame_state.gpu_cache,
196                );
197
198                let mut task_id = frame_state.rg_builder.add().init(
199                    RenderTask::new_image(size, request)
200                );
201
202                if let Some(external_image) = external_image {
203                    // On some devices we cannot render from an ImageBufferKind::TextureExternal
204                    // source using most shaders, so must peform a copy to a regular texture first.
205                    let requires_copy = frame_context.fb_config.external_images_require_copy &&
206                        external_image.image_type ==
207                            ExternalImageType::TextureHandle(ImageBufferKind::TextureExternal);
208
209                    if requires_copy {
210                        let target_kind = if descriptor.format.bytes_per_pixel() == 1 {
211                            RenderTargetKind::Alpha
212                        } else {
213                            RenderTargetKind::Color
214                        };
215
216                        task_id = RenderTask::new_scaling(
217                            task_id,
218                            frame_state.rg_builder,
219                            target_kind,
220                            size
221                        );
222
223                        frame_state.surface_builder.add_child_render_task(
224                            task_id,
225                            frame_state.rg_builder,
226                        );
227                    }
228
229                    // Ensure the instance is rendered using normalized_uvs if the external image
230                    // requires so. If we inserted a scale above this is not required as the
231                    // instance is rendered from a render task rather than the external image.
232                    if !requires_copy {
233                        image_instance.normalized_uvs = external_image.normalized_uvs;
234                    }
235                }
236
237                // Every frame, for cached items, we need to request the render
238                // task cache item. The closure will be invoked on the first
239                // time through, and any time the render task output has been
240                // evicted from the texture cache.
241                if self.tile_spacing == LayoutSize::zero() {
242                    // Most common case.
243                    image_instance.src_color = Some(task_id);
244                } else {
245                    let padding = DeviceIntSideOffsets::new(
246                        0,
247                        (self.tile_spacing.width * size.width as f32 / self.stretch_size.width) as i32,
248                        (self.tile_spacing.height * size.height as f32 / self.stretch_size.height) as i32,
249                        0,
250                    );
251
252                    size.width += padding.horizontal();
253                    size.height += padding.vertical();
254
255                    if padding != DeviceIntSideOffsets::zero() {
256                        common.opacity = PrimitiveOpacity::translucent();
257                    }
258
259                    let image_cache_key = ImageCacheKey {
260                        request,
261                        texel_rect: None,
262                    };
263                    let target_kind = if descriptor.format.bytes_per_pixel() == 1 {
264                        RenderTargetKind::Alpha
265                    } else {
266                        RenderTargetKind::Color
267                    };
268
269                    // Request a pre-rendered image task.
270                    let cached_task_handle = frame_state.resource_cache.request_render_task(
271                        Some(RenderTaskCacheKey {
272                            size,
273                            kind: RenderTaskCacheKeyKind::Image(image_cache_key),
274                        }),
275                        descriptor.is_opaque(),
276                        RenderTaskParent::Surface,
277                        frame_state.gpu_cache,
278                        &mut frame_state.frame_gpu_data.f32,
279                        frame_state.rg_builder,
280                        &mut frame_state.surface_builder,
281                        &mut |rg_builder, _, _| {
282                            // Create a task to blit from the texture cache to
283                            // a normal transient render task surface.
284                            // TODO: figure out if/when we can do a blit instead.
285                            let cache_to_target_task_id = RenderTask::new_scaling_with_padding(
286                                task_id,
287                                rg_builder,
288                                target_kind,
289                                size,
290                                padding,
291                            );
292
293                            // Create a task to blit the rect from the child render
294                            // task above back into the right spot in the persistent
295                            // render target cache.
296                            RenderTask::new_blit(
297                                size,
298                                cache_to_target_task_id,
299                                size.into(),
300                                rg_builder,
301                            )
302                        }
303                    );
304
305                    image_instance.src_color = Some(cached_task_handle);
306                }
307            }
308            // Tiled image path.
309            Some(ImageProperties { tiling: Some(tile_size), visible_rect, .. }) => {
310                // we'll  have a source handle per visible tile instead.
311                image_instance.src_color = None;
312
313                image_instance.visible_tiles.clear();
314                // TODO: rename the blob's visible_rect into something that doesn't conflict
315                // with the terminology we use during culling since it's not really the same
316                // thing.
317                let active_rect = visible_rect;
318
319                let visible_rect = compute_conservative_visible_rect(
320                    &visibility.clip_chain,
321                    frame_state.current_dirty_region().combined,
322                    frame_state.current_dirty_region().visibility_spatial_node,
323                    prim_spatial_node_index,
324                    frame_context.spatial_tree,
325                );
326
327                let base_edge_flags = edge_flags_for_tile_spacing(&self.tile_spacing);
328
329                let stride = self.stretch_size + self.tile_spacing;
330
331                // We are performing the decomposition on the CPU here, no need to
332                // have it in the shader.
333                common.may_need_repetition = false;
334
335                let repetitions = image_tiling::repetitions(
336                    &common.prim_rect,
337                    &visible_rect,
338                    stride,
339                );
340
341                for image_tiling::Repetition { origin, edge_flags } in repetitions {
342                    let edge_flags = base_edge_flags | edge_flags;
343
344                    let layout_image_rect = LayoutRect::from_origin_and_size(
345                        origin,
346                        self.stretch_size,
347                    );
348
349                    let tiles = image_tiling::tiles(
350                        &layout_image_rect,
351                        &visible_rect,
352                        &active_rect,
353                        tile_size as i32,
354                    );
355
356                    for tile in tiles {
357                        let request = request.with_tile(tile.offset);
358                        let size = frame_state.resource_cache.request_image(
359                            request,
360                            frame_state.gpu_cache,
361                        );
362
363                        let task_id = frame_state.rg_builder.add().init(
364                            RenderTask::new_image(size, request)
365                        );
366
367                        image_instance.visible_tiles.push(VisibleImageTile {
368                            src_color: task_id,
369                            edge_flags: tile.edge_flags & edge_flags,
370                            local_rect: tile.rect,
371                            local_clip_rect: tight_clip_rect,
372                        });
373                    }
374                }
375
376                if image_instance.visible_tiles.is_empty() {
377                    // Mark as invisible
378                    visibility.reset();
379                }
380            }
381            None => {
382                image_instance.src_color = None;
383            }
384        }
385
386        if let Some(task_id) = frame_state.image_dependencies.get(&self.key) {
387            frame_state.surface_builder.add_child_render_task(
388                *task_id,
389                frame_state.rg_builder
390            );
391        }
392
393        if let Some(mut request) = frame_state.gpu_cache.request(&mut common.gpu_cache_handle) {
394            self.write_prim_gpu_blocks(&image_instance.adjustment, &mut request);
395        }
396    }
397
398    pub fn write_prim_gpu_blocks(&self, adjustment: &AdjustedImageSource, request: &mut GpuDataRequest) {
399        let stretch_size = adjustment.map_stretch_size(self.stretch_size);
400        // Images are drawn as a white color, modulated by the total
401        // opacity coming from any collapsed property bindings.
402        // Size has to match `VECS_PER_SPECIFIC_BRUSH` from `brush_image.glsl` exactly.
403        request.push(self.color.premultiplied());
404        request.push(PremultipliedColorF::WHITE);
405        request.push([
406            stretch_size.width + self.tile_spacing.width,
407            stretch_size.height + self.tile_spacing.height,
408            0.0,
409            0.0,
410        ]);
411    }
412}
413
414fn edge_flags_for_tile_spacing(tile_spacing: &LayoutSize) -> EdgeAaSegmentMask {
415    let mut flags = EdgeAaSegmentMask::empty();
416
417    if tile_spacing.width > 0.0 {
418        flags |= EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::RIGHT;
419    }
420    if tile_spacing.height > 0.0 {
421        flags |= EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::BOTTOM;
422    }
423
424    flags
425}
426
427pub type ImageTemplate = PrimTemplate<ImageData>;
428
429impl From<ImageKey> for ImageTemplate {
430    fn from(image: ImageKey) -> Self {
431        let common = PrimTemplateCommonData::with_key_common(image.common);
432
433        ImageTemplate {
434            common,
435            kind: image.kind.into(),
436        }
437    }
438}
439
440pub type ImageDataHandle = InternHandle<Image>;
441
442impl Internable for Image {
443    type Key = ImageKey;
444    type StoreData = ImageTemplate;
445    type InternData = ();
446    const PROFILE_COUNTER: usize = crate::profiler::INTERNED_IMAGES;
447}
448
449impl InternablePrimitive for Image {
450    fn into_key(
451        self,
452        info: &LayoutPrimitiveInfo,
453    ) -> ImageKey {
454        ImageKey::new(info, self)
455    }
456
457    fn make_instance_kind(
458        _key: ImageKey,
459        data_handle: ImageDataHandle,
460        prim_store: &mut PrimitiveStore,
461    ) -> PrimitiveInstanceKind {
462        // TODO(gw): Refactor this to not need a separate image
463        //           instance (see ImageInstance struct).
464        let image_instance_index = prim_store.images.push(ImageInstance {
465            segment_instance_index: SegmentInstanceIndex::INVALID,
466            tight_local_clip_rect: LayoutRect::zero(),
467            visible_tiles: Vec::new(),
468            src_color: None,
469            normalized_uvs: false,
470            adjustment: AdjustedImageSource::new(),
471        });
472
473        PrimitiveInstanceKind::Image {
474            data_handle,
475            image_instance_index,
476            compositor_surface_kind: CompositorSurfaceKind::Blit,
477        }
478    }
479}
480
481impl CreateShadow for Image {
482    fn create_shadow(
483        &self,
484        shadow: &Shadow,
485        _: bool,
486        _: RasterSpace,
487    ) -> Self {
488        Image {
489            tile_spacing: self.tile_spacing,
490            stretch_size: self.stretch_size,
491            key: self.key,
492            image_rendering: self.image_rendering,
493            alpha_type: self.alpha_type,
494            color: shadow.color.into(),
495        }
496    }
497}
498
499impl IsVisible for Image {
500    fn is_visible(&self) -> bool {
501        true
502    }
503}
504
505/// Represents an adjustment to apply to an image primitive.
506/// This can be used to compensate for a difference between the bounds of
507/// the images expected by the primitive and the bounds that were actually
508/// drawn in the texture cache.
509///
510/// This happens when rendering snapshot images: A picture is marked so that
511/// a specific reference area in layout space can be rendered as an image.
512/// However, the bounds of the rasterized area of the picture typically differ
513/// from that reference area.
514///
515/// The adjustment is stored as 4 floats (x0, y0, x1, y1) that represent a
516/// transformation of the primitve's local rect such that:
517///
518/// ```ignore
519/// adjusted_rect.min = prim_rect.min + prim_rect.size() * (x0, y0);
520/// adjusted_rect.max = prim_rect.max + prim_rect.size() * (x1, y1);
521/// ```
522#[derive(Copy, Clone, Debug)]
523#[cfg_attr(feature = "capture", derive(Serialize))]
524#[cfg_attr(feature = "replay", derive(Deserialize))]
525pub struct AdjustedImageSource {
526    x0: f32,
527    y0: f32,
528    x1: f32,
529    y1: f32,
530}
531
532impl AdjustedImageSource {
533    /// The "identity" adjustment.
534    pub fn new() -> Self {
535        AdjustedImageSource {
536            x0: 0.0,
537            y0: 0.0,
538            x1: 0.0,
539            y1: 0.0,
540        }
541    }
542
543    /// An adjustment to render an image item defined in function of the `reference`
544    /// rect whereas the `actual` rect was cached instead.
545    pub fn from_rects(reference: &LayoutRect, actual: &LayoutRect) -> Self {
546        let ref_size = reference.size();
547        let min_offset = reference.min.to_vector();
548        let max_offset = reference.max.to_vector();
549        AdjustedImageSource {
550            x0: (actual.min.x - min_offset.x) / ref_size.width,
551            y0: (actual.min.y - min_offset.y) / ref_size.height,
552            x1: (actual.max.x - max_offset.x) / ref_size.width,
553            y1: (actual.max.y - max_offset.y) / ref_size.height,
554        }
555    }
556
557    /// Adjust the primitive's local rect.
558    pub fn map_local_rect(&self, rect: &LayoutRect) -> LayoutRect {
559        let w = rect.width();
560        let h = rect.height();
561        LayoutRect {
562            min: point2(
563                rect.min.x + w * self.x0,
564                rect.min.y + h * self.y0,
565            ),
566            max: point2(
567                rect.max.x + w * self.x1,
568                rect.max.y + h * self.y1,
569            ),
570        }
571    }
572
573    /// The stretch size has to be adjusted as well because it is defined
574    /// using the snapshot area as reference but will stretch the rasterized
575    /// area instead.
576    ///
577    /// It has to be scaled by a factor of (adjusted.size() / prim_rect.size()).
578    /// We derive the formula in function of the adjustment factors:
579    ///
580    /// ```ignore
581    /// factor = (adjusted.max - adjusted.min) / (w, h)
582    ///        = (rect.max + (w, h) * (x1, y1) - (rect.min + (w, h) * (x0, y0))) / (w, h)
583    ///        = ((w, h) + (w, h) * (x1, y1) - (w, h) * (x0, y0)) / (w, h)
584    ///        = (1.0, 1.0) + (x1, y1) - (x0, y0)
585    /// ```
586    pub fn map_stretch_size(&self, size: LayoutSize) -> LayoutSize {
587        LayoutSize::new(
588            size.width * (1.0 + self.x1 - self.x0),
589            size.height * (1.0 + self.y1 - self.y0),
590        )
591    }
592}
593
594////////////////////////////////////////////////////////////////////////////////
595
596#[cfg_attr(feature = "capture", derive(Serialize))]
597#[cfg_attr(feature = "replay", derive(Deserialize))]
598#[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash)]
599pub struct YuvImage {
600    pub color_depth: ColorDepth,
601    pub yuv_key: [ApiImageKey; 3],
602    pub format: YuvFormat,
603    pub color_space: YuvColorSpace,
604    pub color_range: ColorRange,
605    pub image_rendering: ImageRendering,
606}
607
608pub type YuvImageKey = PrimKey<YuvImage>;
609
610impl YuvImageKey {
611    pub fn new(
612        info: &LayoutPrimitiveInfo,
613        yuv_image: YuvImage,
614    ) -> Self {
615        YuvImageKey {
616            common: info.into(),
617            kind: yuv_image,
618        }
619    }
620}
621
622impl InternDebug for YuvImageKey {}
623
624#[cfg_attr(feature = "capture", derive(Serialize))]
625#[cfg_attr(feature = "replay", derive(Deserialize))]
626#[derive(MallocSizeOf)]
627pub struct YuvImageData {
628    pub color_depth: ColorDepth,
629    pub yuv_key: [ApiImageKey; 3],
630    pub src_yuv: [Option<RenderTaskId>; 3],
631    pub format: YuvFormat,
632    pub color_space: YuvColorSpace,
633    pub color_range: ColorRange,
634    pub image_rendering: ImageRendering,
635}
636
637impl From<YuvImage> for YuvImageData {
638    fn from(image: YuvImage) -> Self {
639        YuvImageData {
640            color_depth: image.color_depth,
641            yuv_key: image.yuv_key,
642            src_yuv: [None, None, None],
643            format: image.format,
644            color_space: image.color_space,
645            color_range: image.color_range,
646            image_rendering: image.image_rendering,
647        }
648    }
649}
650
651impl YuvImageData {
652    /// Update the GPU cache for a given primitive template. This may be called multiple
653    /// times per frame, by each primitive reference that refers to this interned
654    /// template. The initial request call to the GPU cache ensures that work is only
655    /// done if the cache entry is invalid (due to first use or eviction).
656    pub fn update(
657        &mut self,
658        common: &mut PrimTemplateCommonData,
659        frame_state: &mut FrameBuildingState,
660    ) {
661
662        self.src_yuv = [ None, None, None ];
663
664        let channel_num = self.format.get_plane_num();
665        debug_assert!(channel_num <= 3);
666        for channel in 0 .. channel_num {
667            let request = ImageRequest {
668                key: self.yuv_key[channel],
669                rendering: self.image_rendering,
670                tile: None,
671            };
672
673            let size = frame_state.resource_cache.request_image(
674                request,
675                frame_state.gpu_cache,
676            );
677
678            let task_id = frame_state.rg_builder.add().init(
679                RenderTask::new_image(size, request)
680            );
681
682            self.src_yuv[channel] = Some(task_id);
683        }
684
685        if let Some(mut request) = frame_state.gpu_cache.request(&mut common.gpu_cache_handle) {
686            self.write_prim_gpu_blocks(&mut request);
687        };
688
689        // YUV images never have transparency
690        common.opacity = PrimitiveOpacity::opaque();
691    }
692
693    pub fn request_resources(
694        &mut self,
695        resource_cache: &mut ResourceCache,
696        gpu_cache: &mut GpuCache,
697    ) {
698        let channel_num = self.format.get_plane_num();
699        debug_assert!(channel_num <= 3);
700        for channel in 0 .. channel_num {
701            resource_cache.request_image(
702                ImageRequest {
703                    key: self.yuv_key[channel],
704                    rendering: self.image_rendering,
705                    tile: None,
706                },
707                gpu_cache,
708            );
709        }
710    }
711
712    pub fn write_prim_gpu_blocks(&self, request: &mut GpuDataRequest) {
713        let ranged_color_space = self.color_space.with_range(self.color_range);
714        request.push([
715            pack_as_float(self.color_depth.bit_depth()),
716            pack_as_float(ranged_color_space as u32),
717            pack_as_float(self.format as u32),
718            0.0
719        ]);
720    }
721}
722
723pub type YuvImageTemplate = PrimTemplate<YuvImageData>;
724
725impl From<YuvImageKey> for YuvImageTemplate {
726    fn from(image: YuvImageKey) -> Self {
727        let common = PrimTemplateCommonData::with_key_common(image.common);
728
729        YuvImageTemplate {
730            common,
731            kind: image.kind.into(),
732        }
733    }
734}
735
736pub type YuvImageDataHandle = InternHandle<YuvImage>;
737
738impl Internable for YuvImage {
739    type Key = YuvImageKey;
740    type StoreData = YuvImageTemplate;
741    type InternData = ();
742    const PROFILE_COUNTER: usize = crate::profiler::INTERNED_YUV_IMAGES;
743}
744
745impl InternablePrimitive for YuvImage {
746    fn into_key(
747        self,
748        info: &LayoutPrimitiveInfo,
749    ) -> YuvImageKey {
750        YuvImageKey::new(info, self)
751    }
752
753    fn make_instance_kind(
754        _key: YuvImageKey,
755        data_handle: YuvImageDataHandle,
756        _prim_store: &mut PrimitiveStore,
757    ) -> PrimitiveInstanceKind {
758        PrimitiveInstanceKind::YuvImage {
759            data_handle,
760            segment_instance_index: SegmentInstanceIndex::INVALID,
761            compositor_surface_kind: CompositorSurfaceKind::Blit,
762        }
763    }
764}
765
766impl IsVisible for YuvImage {
767    fn is_visible(&self) -> bool {
768        true
769    }
770}
771
772#[test]
773#[cfg(target_pointer_width = "64")]
774fn test_struct_sizes() {
775    use std::mem;
776    // The sizes of these structures are critical for performance on a number of
777    // talos stress tests. If you get a failure here on CI, there's two possibilities:
778    // (a) You made a structure smaller than it currently is. Great work! Update the
779    //     test expectations and move on.
780    // (b) You made a structure larger. This is not necessarily a problem, but should only
781    //     be done with care, and after checking if talos performance regresses badly.
782    assert_eq!(mem::size_of::<Image>(), 32, "Image size changed");
783    assert_eq!(mem::size_of::<ImageTemplate>(), 72, "ImageTemplate size changed");
784    assert_eq!(mem::size_of::<ImageKey>(), 52, "ImageKey size changed");
785    assert_eq!(mem::size_of::<YuvImage>(), 32, "YuvImage size changed");
786    assert_eq!(mem::size_of::<YuvImageTemplate>(), 84, "YuvImageTemplate size changed");
787    assert_eq!(mem::size_of::<YuvImageKey>(), 52, "YuvImageKey size changed");
788}