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 }