tor-browser

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

image.rs (28505B)


      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, ExternalImageType,
      7    ImageKey as ApiImageKey, ImageBufferKind, ImageRendering, PremultipliedColorF,
      8    RasterSpace, Shadow, YuvColorSpace, ColorRange, YuvFormat,
      9 };
     10 use api::units::*;
     11 use euclid::point2;
     12 use crate::composite::CompositorSurfaceKind;
     13 use crate::gpu_types::{ImageBrushPrimitiveData, YuvPrimitive};
     14 use crate::renderer::{GpuBufferBuilderF, GpuBufferWriterF};
     15 use crate::scene_building::{CreateShadow, IsVisible};
     16 use crate::frame_builder::{FrameBuildingContext, FrameBuildingState};
     17 use crate::intern::{Internable, InternDebug, Handle as InternHandle};
     18 use crate::internal_types::LayoutPrimitiveInfo;
     19 use crate::prim_store::{
     20    EdgeAaSegmentMask, PrimitiveInstanceKind,
     21    PrimitiveOpacity, PrimKey,
     22    PrimTemplate, PrimTemplateCommonData, PrimitiveStore, SegmentInstanceIndex,
     23    SizeKey, InternablePrimitive,
     24 };
     25 use crate::render_target::RenderTargetKind;
     26 use crate::render_task_graph::RenderTaskId;
     27 use crate::render_task::RenderTask;
     28 use crate::render_task_cache::{
     29    RenderTaskCacheKey, RenderTaskCacheKeyKind, RenderTaskParent
     30 };
     31 use crate::resource_cache::{ImageRequest, ImageProperties, ResourceCache};
     32 use crate::visibility::{PrimitiveVisibility, compute_conservative_visible_rect};
     33 use crate::spatial_tree::SpatialNodeIndex;
     34 use crate::image_tiling;
     35 
     36 #[derive(Debug)]
     37 #[cfg_attr(feature = "capture", derive(Serialize))]
     38 #[cfg_attr(feature = "replay", derive(Deserialize))]
     39 pub 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))]
     51 pub 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))]
     70 pub 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)]
     82 pub 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 
     91 pub type ImageKey = PrimKey<Image>;
     92 
     93 impl 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 
    105 impl InternDebug for ImageKey {}
    106 
    107 #[cfg_attr(feature = "capture", derive(Serialize))]
    108 #[cfg_attr(feature = "replay", derive(Deserialize))]
    109 #[derive(Debug, MallocSizeOf)]
    110 pub 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 
    119 impl 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 
    132 impl 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                    &mut frame_state.frame_gpu_data.f32,
    196                );
    197 
    198                let mut task_id = frame_state.rg_builder.add().init(
    199                    RenderTask::new_image(size, request, false)
    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                        &mut frame_state.frame_gpu_data.f32,
    278                        frame_state.rg_builder,
    279                        &mut frame_state.surface_builder,
    280                        &mut |rg_builder, _| {
    281                            // Create a task to blit from the texture cache to
    282                            // a normal transient render task surface.
    283                            // TODO: figure out if/when we can do a blit instead.
    284                            let cache_to_target_task_id = RenderTask::new_scaling_with_padding(
    285                                task_id,
    286                                rg_builder,
    287                                target_kind,
    288                                size,
    289                                padding,
    290                            );
    291 
    292                            // Create a task to blit the rect from the child render
    293                            // task above back into the right spot in the persistent
    294                            // render target cache.
    295                            RenderTask::new_blit(
    296                                size,
    297                                cache_to_target_task_id,
    298                                size.into(),
    299                                rg_builder,
    300                            )
    301                        }
    302                    );
    303 
    304                    image_instance.src_color = Some(cached_task_handle);
    305                }
    306            }
    307            // Tiled image path.
    308            Some(ImageProperties { tiling: Some(tile_size), visible_rect, .. }) => {
    309                // we'll  have a source handle per visible tile instead.
    310                image_instance.src_color = None;
    311 
    312                image_instance.visible_tiles.clear();
    313                // TODO: rename the blob's visible_rect into something that doesn't conflict
    314                // with the terminology we use during culling since it's not really the same
    315                // thing.
    316                let active_rect = visible_rect;
    317 
    318                let visible_rect = compute_conservative_visible_rect(
    319                    &visibility.clip_chain,
    320                    frame_state.current_dirty_region().combined,
    321                    frame_state.current_dirty_region().visibility_spatial_node,
    322                    prim_spatial_node_index,
    323                    frame_context.spatial_tree,
    324                );
    325 
    326                let base_edge_flags = edge_flags_for_tile_spacing(&self.tile_spacing);
    327 
    328                let stride = self.stretch_size + self.tile_spacing;
    329 
    330                // We are performing the decomposition on the CPU here, no need to
    331                // have it in the shader.
    332                common.may_need_repetition = false;
    333 
    334                let repetitions = image_tiling::repetitions(
    335                    &common.prim_rect,
    336                    &visible_rect,
    337                    stride,
    338                );
    339 
    340                for image_tiling::Repetition { origin, edge_flags } in repetitions {
    341                    let edge_flags = base_edge_flags | edge_flags;
    342 
    343                    let layout_image_rect = LayoutRect::from_origin_and_size(
    344                        origin,
    345                        self.stretch_size,
    346                    );
    347 
    348                    let tiles = image_tiling::tiles(
    349                        &layout_image_rect,
    350                        &visible_rect,
    351                        &active_rect,
    352                        tile_size as i32,
    353                    );
    354 
    355                    for tile in tiles {
    356                        let request = request.with_tile(tile.offset);
    357                        let size = frame_state.resource_cache.request_image(
    358                            request,
    359                            &mut frame_state.frame_gpu_data.f32,
    360                        );
    361 
    362                        let task_id = frame_state.rg_builder.add().init(
    363                            RenderTask::new_image(size, request, false)
    364                        );
    365 
    366                        image_instance.visible_tiles.push(VisibleImageTile {
    367                            src_color: task_id,
    368                            edge_flags: tile.edge_flags & edge_flags,
    369                            local_rect: tile.rect,
    370                            local_clip_rect: tight_clip_rect,
    371                        });
    372                    }
    373                }
    374 
    375                if image_instance.visible_tiles.is_empty() {
    376                    // Mark as invisible
    377                    visibility.reset();
    378                }
    379            }
    380            None => {
    381                image_instance.src_color = None;
    382            }
    383        }
    384 
    385        if let Some(task_id) = frame_state.image_dependencies.get(&self.key) {
    386            frame_state.surface_builder.add_child_render_task(
    387                *task_id,
    388                frame_state.rg_builder
    389            );
    390        }
    391 
    392        let mut writer = frame_state.frame_gpu_data.f32.write_blocks(3);
    393        self.write_prim_gpu_blocks(&image_instance.adjustment, &mut writer);
    394        common.gpu_buffer_address = writer.finish();
    395    }
    396 
    397    pub fn write_prim_gpu_blocks(&self, adjustment: &AdjustedImageSource, writer: &mut GpuBufferWriterF) {
    398        let stretch_size = adjustment.map_stretch_size(self.stretch_size)
    399             + self.tile_spacing;
    400 
    401        writer.push(&ImageBrushPrimitiveData {
    402            color: self.color.premultiplied(),
    403            background_color: PremultipliedColorF::WHITE,
    404            stretch_size,
    405        });
    406    }
    407 }
    408 
    409 fn edge_flags_for_tile_spacing(tile_spacing: &LayoutSize) -> EdgeAaSegmentMask {
    410    let mut flags = EdgeAaSegmentMask::empty();
    411 
    412    if tile_spacing.width > 0.0 {
    413        flags |= EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::RIGHT;
    414    }
    415    if tile_spacing.height > 0.0 {
    416        flags |= EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::BOTTOM;
    417    }
    418 
    419    flags
    420 }
    421 
    422 pub type ImageTemplate = PrimTemplate<ImageData>;
    423 
    424 impl From<ImageKey> for ImageTemplate {
    425    fn from(image: ImageKey) -> Self {
    426        let common = PrimTemplateCommonData::with_key_common(image.common);
    427 
    428        ImageTemplate {
    429            common,
    430            kind: image.kind.into(),
    431        }
    432    }
    433 }
    434 
    435 pub type ImageDataHandle = InternHandle<Image>;
    436 
    437 impl Internable for Image {
    438    type Key = ImageKey;
    439    type StoreData = ImageTemplate;
    440    type InternData = ();
    441    const PROFILE_COUNTER: usize = crate::profiler::INTERNED_IMAGES;
    442 }
    443 
    444 impl InternablePrimitive for Image {
    445    fn into_key(
    446        self,
    447        info: &LayoutPrimitiveInfo,
    448    ) -> ImageKey {
    449        ImageKey::new(info, self)
    450    }
    451 
    452    fn make_instance_kind(
    453        _key: ImageKey,
    454        data_handle: ImageDataHandle,
    455        prim_store: &mut PrimitiveStore,
    456    ) -> PrimitiveInstanceKind {
    457        // TODO(gw): Refactor this to not need a separate image
    458        //           instance (see ImageInstance struct).
    459        let image_instance_index = prim_store.images.push(ImageInstance {
    460            segment_instance_index: SegmentInstanceIndex::INVALID,
    461            tight_local_clip_rect: LayoutRect::zero(),
    462            visible_tiles: Vec::new(),
    463            src_color: None,
    464            normalized_uvs: false,
    465            adjustment: AdjustedImageSource::new(),
    466        });
    467 
    468        PrimitiveInstanceKind::Image {
    469            data_handle,
    470            image_instance_index,
    471            compositor_surface_kind: CompositorSurfaceKind::Blit,
    472        }
    473    }
    474 }
    475 
    476 impl CreateShadow for Image {
    477    fn create_shadow(
    478        &self,
    479        shadow: &Shadow,
    480        _: bool,
    481        _: RasterSpace,
    482    ) -> Self {
    483        Image {
    484            tile_spacing: self.tile_spacing,
    485            stretch_size: self.stretch_size,
    486            key: self.key,
    487            image_rendering: self.image_rendering,
    488            alpha_type: self.alpha_type,
    489            color: shadow.color.into(),
    490        }
    491    }
    492 }
    493 
    494 impl IsVisible for Image {
    495    fn is_visible(&self) -> bool {
    496        true
    497    }
    498 }
    499 
    500 /// Represents an adjustment to apply to an image primitive.
    501 /// This can be used to compensate for a difference between the bounds of
    502 /// the images expected by the primitive and the bounds that were actually
    503 /// drawn in the texture cache.
    504 ///
    505 /// This happens when rendering snapshot images: A picture is marked so that
    506 /// a specific reference area in layout space can be rendered as an image.
    507 /// However, the bounds of the rasterized area of the picture typically differ
    508 /// from that reference area.
    509 ///
    510 /// The adjustment is stored as 4 floats (x0, y0, x1, y1) that represent a
    511 /// transformation of the primitve's local rect such that:
    512 ///
    513 /// ```ignore
    514 /// adjusted_rect.min = prim_rect.min + prim_rect.size() * (x0, y0);
    515 /// adjusted_rect.max = prim_rect.max + prim_rect.size() * (x1, y1);
    516 /// ```
    517 #[derive(Copy, Clone, Debug)]
    518 #[cfg_attr(feature = "capture", derive(Serialize))]
    519 #[cfg_attr(feature = "replay", derive(Deserialize))]
    520 pub struct AdjustedImageSource {
    521    x0: f32,
    522    y0: f32,
    523    x1: f32,
    524    y1: f32,
    525 }
    526 
    527 impl AdjustedImageSource {
    528    /// The "identity" adjustment.
    529    pub fn new() -> Self {
    530        AdjustedImageSource {
    531            x0: 0.0,
    532            y0: 0.0,
    533            x1: 0.0,
    534            y1: 0.0,
    535        }
    536    }
    537 
    538    /// An adjustment to render an image item defined in function of the `reference`
    539    /// rect whereas the `actual` rect was cached instead.
    540    pub fn from_rects(reference: &LayoutRect, actual: &LayoutRect) -> Self {
    541        let ref_size = reference.size();
    542        let min_offset = reference.min.to_vector();
    543        let max_offset = reference.max.to_vector();
    544        AdjustedImageSource {
    545            x0: (actual.min.x - min_offset.x) / ref_size.width,
    546            y0: (actual.min.y - min_offset.y) / ref_size.height,
    547            x1: (actual.max.x - max_offset.x) / ref_size.width,
    548            y1: (actual.max.y - max_offset.y) / ref_size.height,
    549        }
    550    }
    551 
    552    /// Adjust the primitive's local rect.
    553    pub fn map_local_rect(&self, rect: &LayoutRect) -> LayoutRect {
    554        let w = rect.width();
    555        let h = rect.height();
    556        LayoutRect {
    557            min: point2(
    558                rect.min.x + w * self.x0,
    559                rect.min.y + h * self.y0,
    560            ),
    561            max: point2(
    562                rect.max.x + w * self.x1,
    563                rect.max.y + h * self.y1,
    564            ),
    565        }
    566    }
    567 
    568    /// The stretch size has to be adjusted as well because it is defined
    569    /// using the snapshot area as reference but will stretch the rasterized
    570    /// area instead.
    571    ///
    572    /// It has to be scaled by a factor of (adjusted.size() / prim_rect.size()).
    573    /// We derive the formula in function of the adjustment factors:
    574    ///
    575    /// ```ignore
    576    /// factor = (adjusted.max - adjusted.min) / (w, h)
    577    ///        = (rect.max + (w, h) * (x1, y1) - (rect.min + (w, h) * (x0, y0))) / (w, h)
    578    ///        = ((w, h) + (w, h) * (x1, y1) - (w, h) * (x0, y0)) / (w, h)
    579    ///        = (1.0, 1.0) + (x1, y1) - (x0, y0)
    580    /// ```
    581    pub fn map_stretch_size(&self, size: LayoutSize) -> LayoutSize {
    582        LayoutSize::new(
    583            size.width * (1.0 + self.x1 - self.x0),
    584            size.height * (1.0 + self.y1 - self.y0),
    585        )
    586    }
    587 }
    588 
    589 ////////////////////////////////////////////////////////////////////////////////
    590 
    591 #[cfg_attr(feature = "capture", derive(Serialize))]
    592 #[cfg_attr(feature = "replay", derive(Deserialize))]
    593 #[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash)]
    594 pub struct YuvImage {
    595    pub color_depth: ColorDepth,
    596    pub yuv_key: [ApiImageKey; 3],
    597    pub format: YuvFormat,
    598    pub color_space: YuvColorSpace,
    599    pub color_range: ColorRange,
    600    pub image_rendering: ImageRendering,
    601 }
    602 
    603 pub type YuvImageKey = PrimKey<YuvImage>;
    604 
    605 impl YuvImageKey {
    606    pub fn new(
    607        info: &LayoutPrimitiveInfo,
    608        yuv_image: YuvImage,
    609    ) -> Self {
    610        YuvImageKey {
    611            common: info.into(),
    612            kind: yuv_image,
    613        }
    614    }
    615 }
    616 
    617 impl InternDebug for YuvImageKey {}
    618 
    619 #[cfg_attr(feature = "capture", derive(Serialize))]
    620 #[cfg_attr(feature = "replay", derive(Deserialize))]
    621 #[derive(MallocSizeOf)]
    622 pub struct YuvImageData {
    623    pub color_depth: ColorDepth,
    624    pub yuv_key: [ApiImageKey; 3],
    625    pub src_yuv: [Option<RenderTaskId>; 3],
    626    pub format: YuvFormat,
    627    pub color_space: YuvColorSpace,
    628    pub color_range: ColorRange,
    629    pub image_rendering: ImageRendering,
    630 }
    631 
    632 impl From<YuvImage> for YuvImageData {
    633    fn from(image: YuvImage) -> Self {
    634        YuvImageData {
    635            color_depth: image.color_depth,
    636            yuv_key: image.yuv_key,
    637            src_yuv: [None, None, None],
    638            format: image.format,
    639            color_space: image.color_space,
    640            color_range: image.color_range,
    641            image_rendering: image.image_rendering,
    642        }
    643    }
    644 }
    645 
    646 impl YuvImageData {
    647    /// Update the GPU cache for a given primitive template. This may be called multiple
    648    /// times per frame, by each primitive reference that refers to this interned
    649    /// template. The initial request call to the GPU cache ensures that work is only
    650    /// done if the cache entry is invalid (due to first use or eviction).
    651    pub fn update(
    652        &mut self,
    653        common: &mut PrimTemplateCommonData,
    654        is_composited: bool,
    655        frame_state: &mut FrameBuildingState,
    656    ) {
    657 
    658        self.src_yuv = [ None, None, None ];
    659 
    660        let channel_num = self.format.get_plane_num();
    661        debug_assert!(channel_num <= 3);
    662        for channel in 0 .. channel_num {
    663            let request = ImageRequest {
    664                key: self.yuv_key[channel],
    665                rendering: self.image_rendering,
    666                tile: None,
    667            };
    668 
    669            let size = frame_state.resource_cache.request_image(
    670                request,
    671                &mut frame_state.frame_gpu_data.f32,
    672            );
    673 
    674            let task_id = frame_state.rg_builder.add().init(
    675                RenderTask::new_image(
    676                    size,
    677                    request,
    678                    is_composited,
    679                )
    680            );
    681 
    682            self.src_yuv[channel] = Some(task_id);
    683        }
    684 
    685        let mut writer = frame_state.frame_gpu_data.f32.write_blocks(1);
    686        self.write_prim_gpu_blocks(&mut writer);
    687        common.gpu_buffer_address = writer.finish();
    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_buffer: &mut GpuBufferBuilderF,
    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_buffer,
    708            );
    709        }
    710    }
    711 
    712    pub fn write_prim_gpu_blocks(&self, writer: &mut GpuBufferWriterF) {
    713        writer.push(&YuvPrimitive {
    714            channel_bit_depth: self.color_depth.bit_depth(),
    715            color_space: self.color_space.with_range(self.color_range),
    716            yuv_format: self.format,
    717        });
    718    }
    719 }
    720 
    721 pub type YuvImageTemplate = PrimTemplate<YuvImageData>;
    722 
    723 impl From<YuvImageKey> for YuvImageTemplate {
    724    fn from(image: YuvImageKey) -> Self {
    725        let common = PrimTemplateCommonData::with_key_common(image.common);
    726 
    727        YuvImageTemplate {
    728            common,
    729            kind: image.kind.into(),
    730        }
    731    }
    732 }
    733 
    734 pub type YuvImageDataHandle = InternHandle<YuvImage>;
    735 
    736 impl Internable for YuvImage {
    737    type Key = YuvImageKey;
    738    type StoreData = YuvImageTemplate;
    739    type InternData = ();
    740    const PROFILE_COUNTER: usize = crate::profiler::INTERNED_YUV_IMAGES;
    741 }
    742 
    743 impl InternablePrimitive for YuvImage {
    744    fn into_key(
    745        self,
    746        info: &LayoutPrimitiveInfo,
    747    ) -> YuvImageKey {
    748        YuvImageKey::new(info, self)
    749    }
    750 
    751    fn make_instance_kind(
    752        _key: YuvImageKey,
    753        data_handle: YuvImageDataHandle,
    754        _prim_store: &mut PrimitiveStore,
    755    ) -> PrimitiveInstanceKind {
    756        PrimitiveInstanceKind::YuvImage {
    757            data_handle,
    758            segment_instance_index: SegmentInstanceIndex::INVALID,
    759            compositor_surface_kind: CompositorSurfaceKind::Blit,
    760        }
    761    }
    762 }
    763 
    764 impl IsVisible for YuvImage {
    765    fn is_visible(&self) -> bool {
    766        true
    767    }
    768 }
    769 
    770 #[test]
    771 #[cfg(target_pointer_width = "64")]
    772 fn test_struct_sizes() {
    773    use std::mem;
    774    // The sizes of these structures are critical for performance on a number of
    775    // talos stress tests. If you get a failure here on CI, there's two possibilities:
    776    // (a) You made a structure smaller than it currently is. Great work! Update the
    777    //     test expectations and move on.
    778    // (b) You made a structure larger. This is not necessarily a problem, but should only
    779    //     be done with care, and after checking if talos performance regresses badly.
    780    assert_eq!(mem::size_of::<Image>(), 32, "Image size changed");
    781    assert_eq!(mem::size_of::<ImageTemplate>(), 68, "ImageTemplate size changed");
    782    assert_eq!(mem::size_of::<ImageKey>(), 52, "ImageKey size changed");
    783    assert_eq!(mem::size_of::<YuvImage>(), 32, "YuvImage size changed");
    784    assert_eq!(mem::size_of::<YuvImageTemplate>(), 80, "YuvImageTemplate size changed");
    785    assert_eq!(mem::size_of::<YuvImageKey>(), 52, "YuvImageKey size changed");
    786 }