resource_cache.rs (100000B)
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::{BlobImageRequest, ImageDescriptorFlags, ImageFormat, RasterizedBlobImage}; 6 use api::{DebugFlags, FontInstanceKey, FontKey, FontTemplate, GlyphIndex}; 7 use api::{ExternalImageData, ExternalImageType, ExternalImageId, BlobImageResult}; 8 use api::{DirtyRect, GlyphDimensions, IdNamespace, DEFAULT_TILE_SIZE}; 9 use api::{ColorF, ImageData, ImageDescriptor, ImageKey, ImageRendering, TileSize}; 10 use api::{BlobImageHandler, BlobImageKey, VoidPtrToSizeFn}; 11 use api::units::*; 12 use euclid::size2; 13 use crate::render_target::RenderTargetKind; 14 use crate::render_task::{RenderTaskLocation, StaticRenderTaskSurface}; 15 use crate::{render_api::{ClearCache, AddFont, ResourceUpdate, MemoryReport}, util::WeakTable}; 16 use crate::prim_store::image::AdjustedImageSource; 17 use crate::image_tiling::{compute_tile_size, compute_tile_range}; 18 #[cfg(feature = "capture")] 19 use crate::capture::ExternalCaptureImage; 20 #[cfg(feature = "replay")] 21 use crate::capture::PlainExternalImage; 22 #[cfg(any(feature = "replay", feature = "png", feature="capture"))] 23 use crate::capture::CaptureConfig; 24 use crate::composite::{NativeSurfaceId, NativeSurfaceOperation, NativeTileId, NativeSurfaceOperationDetails}; 25 use crate::device::TextureFilter; 26 use crate::glyph_cache::{GlyphCache, CachedGlyphInfo}; 27 use crate::glyph_cache::GlyphCacheEntry; 28 use glyph_rasterizer::{GLYPH_FLASHING, FontInstance, GlyphFormat, GlyphKey, GlyphRasterizer, GlyphRasterJob}; 29 use glyph_rasterizer::{SharedFontResources, BaseFontInstance}; 30 use crate::gpu_types::UvRectKind; 31 use crate::internal_types::{ 32 CacheTextureId, FastHashMap, FastHashSet, TextureSource, ResourceUpdateList, 33 FrameId, FrameStamp, 34 }; 35 use crate::profiler::{self, TransactionProfile, bytes_to_mb}; 36 use crate::render_task_graph::{RenderTaskId, RenderTaskGraphBuilder}; 37 use crate::render_task_cache::{RenderTaskCache, RenderTaskCacheKey, RenderTaskParent}; 38 use crate::render_task_cache::{RenderTaskCacheEntry, RenderTaskCacheEntryHandle}; 39 use crate::renderer::{GpuBufferAddress, GpuBufferBuilder, GpuBufferBuilderF, GpuBufferHandle}; 40 use crate::surface::SurfaceBuilder; 41 use euclid::point2; 42 use smallvec::SmallVec; 43 use std::collections::hash_map::Entry::{self, Occupied, Vacant}; 44 use std::collections::hash_map::{Iter, IterMut}; 45 use std::collections::VecDeque; 46 use std::{cmp, mem}; 47 use std::fmt::Debug; 48 use std::hash::Hash; 49 use std::os::raw::c_void; 50 #[cfg(any(feature = "capture", feature = "replay"))] 51 use std::path::PathBuf; 52 use std::sync::Arc; 53 use std::sync::atomic::{AtomicUsize, Ordering}; 54 use std::u32; 55 use crate::texture_cache::{TextureCache, TextureCacheHandle, Eviction, TargetShader}; 56 use crate::picture_textures::PictureTextures; 57 use peek_poke::PeekPoke; 58 59 // Counter for generating unique native surface ids 60 static NEXT_NATIVE_SURFACE_ID: AtomicUsize = AtomicUsize::new(0); 61 62 #[cfg_attr(feature = "capture", derive(Serialize))] 63 #[cfg_attr(feature = "replay", derive(Deserialize))] 64 pub struct GlyphFetchResult { 65 pub index_in_text_run: i32, 66 pub uv_rect_address: GpuBufferAddress, 67 pub offset: DevicePoint, 68 pub size: DeviceIntSize, 69 pub scale: f32, 70 pub subpx_offset_x: u8, 71 pub subpx_offset_y: u8, 72 pub is_packed_glyph: bool, 73 } 74 75 // These coordinates are always in texels. 76 // They are converted to normalized ST 77 // values in the vertex shader. The reason 78 // for this is that the texture may change 79 // dimensions (e.g. the pages in a texture 80 // atlas can grow). When this happens, by 81 // storing the coordinates as texel values 82 // we don't need to go through and update 83 // various CPU-side structures. 84 #[derive(Debug, Clone)] 85 #[cfg_attr(feature = "capture", derive(Serialize))] 86 #[cfg_attr(feature = "replay", derive(Deserialize))] 87 pub struct CacheItem { 88 pub texture_id: TextureSource, 89 pub uv_rect_handle: GpuBufferHandle, 90 pub uv_rect: DeviceIntRect, 91 pub user_data: [f32; 4], 92 } 93 94 impl CacheItem { 95 pub fn invalid() -> Self { 96 CacheItem { 97 texture_id: TextureSource::Invalid, 98 uv_rect_handle: GpuBufferHandle::INVALID, 99 uv_rect: DeviceIntRect::zero(), 100 user_data: [0.0; 4], 101 } 102 } 103 104 pub fn is_valid(&self) -> bool { 105 self.texture_id != TextureSource::Invalid 106 } 107 } 108 109 /// Represents the backing store of an image in the cache. 110 /// This storage can take several forms. 111 #[derive(Clone, Debug)] 112 pub enum CachedImageData { 113 /// A simple series of bytes, provided by the embedding and owned by WebRender. 114 /// The format is stored out-of-band, currently in ImageDescriptor. 115 Raw(Arc<Vec<u8>>), 116 /// An series of commands that can be rasterized into an image via an 117 /// embedding-provided callback. 118 /// 119 /// The commands are stored elsewhere and this variant is used as a placeholder. 120 Blob, 121 /// A stacking context for which a snapshot has been requested. 122 /// 123 /// The snapshot is grabbed from GPU-side rasterized pixels so there is no 124 /// CPU-side data to store here. 125 Snapshot, 126 /// An image owned by the embedding, and referenced by WebRender. This may 127 /// take the form of a texture or a heap-allocated buffer. 128 External(ExternalImageData), 129 } 130 131 impl From<ImageData> for CachedImageData { 132 fn from(img_data: ImageData) -> Self { 133 match img_data { 134 ImageData::Raw(data) => CachedImageData::Raw(data), 135 ImageData::External(data) => CachedImageData::External(data), 136 } 137 } 138 } 139 140 impl CachedImageData { 141 /// Returns true if this represents a blob. 142 #[inline] 143 pub fn is_blob(&self) -> bool { 144 match *self { 145 CachedImageData::Blob => true, 146 _ => false, 147 } 148 } 149 150 #[inline] 151 pub fn is_snapshot(&self) -> bool { 152 match *self { 153 CachedImageData::Snapshot => true, 154 _ => false, 155 } 156 } 157 158 /// Returns true if this variant of CachedImageData should go through the texture 159 /// cache. 160 #[inline] 161 pub fn uses_texture_cache(&self) -> bool { 162 match *self { 163 CachedImageData::External(ref ext_data) => match ext_data.image_type { 164 ExternalImageType::TextureHandle(_) => false, 165 ExternalImageType::Buffer => true, 166 }, 167 CachedImageData::Blob => true, 168 CachedImageData::Raw(_) => true, 169 CachedImageData::Snapshot => true, 170 } 171 } 172 } 173 174 #[derive(Debug)] 175 #[cfg_attr(feature = "capture", derive(Serialize))] 176 #[cfg_attr(feature = "replay", derive(Deserialize))] 177 pub struct ImageProperties { 178 pub descriptor: ImageDescriptor, 179 pub external_image: Option<ExternalImageData>, 180 pub tiling: Option<TileSize>, 181 // Potentially a subset of the image's total rectangle. This rectangle is what 182 // we map to the (layout space) display item bounds. 183 pub visible_rect: DeviceIntRect, 184 pub adjustment: AdjustedImageSource, 185 } 186 187 #[derive(Debug, Copy, Clone, PartialEq)] 188 enum State { 189 Idle, 190 AddResources, 191 QueryResources, 192 } 193 194 /// Post scene building state. 195 type RasterizedBlob = FastHashMap<TileOffset, RasterizedBlobImage>; 196 197 #[cfg_attr(feature = "capture", derive(Serialize))] 198 #[cfg_attr(feature = "replay", derive(Deserialize))] 199 #[derive(Debug, Copy, Clone, PartialEq, PeekPoke, Default)] 200 pub struct ImageGeneration(pub u32); 201 202 impl ImageGeneration { 203 pub const INVALID: ImageGeneration = ImageGeneration(u32::MAX); 204 } 205 206 struct ImageResource { 207 data: CachedImageData, 208 descriptor: ImageDescriptor, 209 tiling: Option<TileSize>, 210 /// This is used to express images that are virtually very large 211 /// but with only a visible sub-set that is valid at a given time. 212 visible_rect: DeviceIntRect, 213 adjustment: AdjustedImageSource, 214 generation: ImageGeneration, 215 } 216 217 #[derive(Default)] 218 struct ImageTemplates { 219 images: FastHashMap<ImageKey, ImageResource>, 220 } 221 222 impl ImageTemplates { 223 fn insert(&mut self, key: ImageKey, resource: ImageResource) { 224 self.images.insert(key, resource); 225 } 226 227 fn remove(&mut self, key: ImageKey) -> Option<ImageResource> { 228 self.images.remove(&key) 229 } 230 231 fn get(&self, key: ImageKey) -> Option<&ImageResource> { 232 self.images.get(&key) 233 } 234 235 fn get_mut(&mut self, key: ImageKey) -> Option<&mut ImageResource> { 236 self.images.get_mut(&key) 237 } 238 } 239 240 #[cfg_attr(feature = "capture", derive(Serialize))] 241 #[cfg_attr(feature = "replay", derive(Deserialize))] 242 struct CachedImageInfo { 243 texture_cache_handle: TextureCacheHandle, 244 dirty_rect: ImageDirtyRect, 245 manual_eviction: bool, 246 } 247 248 impl CachedImageInfo { 249 fn mark_unused(&mut self, texture_cache: &mut TextureCache) { 250 texture_cache.evict_handle(&self.texture_cache_handle); 251 self.manual_eviction = false; 252 } 253 } 254 255 #[cfg(debug_assertions)] 256 impl Drop for CachedImageInfo { 257 fn drop(&mut self) { 258 debug_assert!(!self.manual_eviction, "Manual eviction requires cleanup"); 259 } 260 } 261 262 #[cfg_attr(feature = "capture", derive(Serialize))] 263 #[cfg_attr(feature = "replay", derive(Deserialize))] 264 pub struct ResourceClassCache<K: Hash + Eq, V, U: Default> { 265 resources: FastHashMap<K, V>, 266 pub user_data: U, 267 } 268 269 impl<K, V, U> ResourceClassCache<K, V, U> 270 where 271 K: Clone + Hash + Eq + Debug, 272 U: Default, 273 { 274 pub fn new() -> Self { 275 ResourceClassCache { 276 resources: FastHashMap::default(), 277 user_data: Default::default(), 278 } 279 } 280 281 pub fn get(&self, key: &K) -> &V { 282 self.resources.get(key) 283 .expect("Didn't find a cached resource with that ID!") 284 } 285 286 pub fn try_get(&self, key: &K) -> Option<&V> { 287 self.resources.get(key) 288 } 289 290 pub fn insert(&mut self, key: K, value: V) { 291 self.resources.insert(key, value); 292 } 293 294 pub fn remove(&mut self, key: &K) -> Option<V> { 295 self.resources.remove(key) 296 } 297 298 pub fn get_mut(&mut self, key: &K) -> &mut V { 299 self.resources.get_mut(key) 300 .expect("Didn't find a cached resource with that ID!") 301 } 302 303 pub fn try_get_mut(&mut self, key: &K) -> Option<&mut V> { 304 self.resources.get_mut(key) 305 } 306 307 pub fn entry(&mut self, key: K) -> Entry<K, V> { 308 self.resources.entry(key) 309 } 310 311 pub fn iter(&self) -> Iter<K, V> { 312 self.resources.iter() 313 } 314 315 pub fn iter_mut(&mut self) -> IterMut<K, V> { 316 self.resources.iter_mut() 317 } 318 319 pub fn is_empty(&mut self) -> bool { 320 self.resources.is_empty() 321 } 322 323 pub fn clear(&mut self) { 324 self.resources.clear(); 325 } 326 327 pub fn retain<F>(&mut self, f: F) 328 where 329 F: FnMut(&K, &mut V) -> bool, 330 { 331 self.resources.retain(f); 332 } 333 } 334 335 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] 336 #[cfg_attr(feature = "capture", derive(Serialize))] 337 #[cfg_attr(feature = "replay", derive(Deserialize))] 338 struct CachedImageKey { 339 pub rendering: ImageRendering, 340 pub tile: Option<TileOffset>, 341 } 342 343 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] 344 #[cfg_attr(feature = "capture", derive(Serialize))] 345 #[cfg_attr(feature = "replay", derive(Deserialize))] 346 pub struct ImageRequest { 347 pub key: ImageKey, 348 pub rendering: ImageRendering, 349 pub tile: Option<TileOffset>, 350 } 351 352 impl ImageRequest { 353 pub fn with_tile(&self, offset: TileOffset) -> Self { 354 ImageRequest { 355 key: self.key, 356 rendering: self.rendering, 357 tile: Some(offset), 358 } 359 } 360 361 pub fn is_untiled_auto(&self) -> bool { 362 self.tile.is_none() && self.rendering == ImageRendering::Auto 363 } 364 } 365 366 impl Into<BlobImageRequest> for ImageRequest { 367 fn into(self) -> BlobImageRequest { 368 BlobImageRequest { 369 key: BlobImageKey(self.key), 370 tile: self.tile.unwrap(), 371 } 372 } 373 } 374 375 impl Into<CachedImageKey> for ImageRequest { 376 fn into(self) -> CachedImageKey { 377 CachedImageKey { 378 rendering: self.rendering, 379 tile: self.tile, 380 } 381 } 382 } 383 384 #[derive(Debug)] 385 #[cfg_attr(feature = "capture", derive(Clone, Serialize))] 386 #[cfg_attr(feature = "replay", derive(Deserialize))] 387 pub enum ImageCacheError { 388 OverLimitSize, 389 } 390 391 #[cfg_attr(feature = "capture", derive(Serialize))] 392 #[cfg_attr(feature = "replay", derive(Deserialize))] 393 enum ImageResult { 394 UntiledAuto(CachedImageInfo), 395 Multi(ResourceClassCache<CachedImageKey, CachedImageInfo, ()>), 396 Err(ImageCacheError), 397 } 398 399 impl ImageResult { 400 /// Releases any texture cache entries held alive by this ImageResult. 401 fn drop_from_cache(&mut self, texture_cache: &mut TextureCache) { 402 match *self { 403 ImageResult::UntiledAuto(ref mut entry) => { 404 entry.mark_unused(texture_cache); 405 }, 406 ImageResult::Multi(ref mut entries) => { 407 for entry in entries.resources.values_mut() { 408 entry.mark_unused(texture_cache); 409 } 410 }, 411 ImageResult::Err(_) => {}, 412 } 413 } 414 } 415 416 type ImageCache = ResourceClassCache<ImageKey, ImageResult, ()>; 417 418 struct Resources { 419 fonts: SharedFontResources, 420 image_templates: ImageTemplates, 421 // We keep a set of Weak references to the fonts so that we're able to include them in memory 422 // reports even if only the OS is holding on to the Vec<u8>. PtrWeakHashSet will periodically 423 // drop any references that have gone dead. 424 weak_fonts: WeakTable 425 } 426 427 // We only use this to report glyph dimensions to the user of the API, so using 428 // the font instance key should be enough. If we start using it to cache dimensions 429 // for internal font instances we should change the hash key accordingly. 430 pub type GlyphDimensionsCache = FastHashMap<(FontInstanceKey, GlyphIndex), Option<GlyphDimensions>>; 431 432 /// Internal information about allocated render targets in the pool 433 struct RenderTarget { 434 size: DeviceIntSize, 435 format: ImageFormat, 436 texture_id: CacheTextureId, 437 /// If true, this is currently leant out, and not available to other passes 438 is_active: bool, 439 last_frame_used: FrameId, 440 } 441 442 impl RenderTarget { 443 fn size_in_bytes(&self) -> usize { 444 let bpp = self.format.bytes_per_pixel() as usize; 445 (self.size.width * self.size.height) as usize * bpp 446 } 447 448 /// Returns true if this texture was used within `threshold` frames of 449 /// the current frame. 450 pub fn used_recently(&self, current_frame_id: FrameId, threshold: u64) -> bool { 451 self.last_frame_used + threshold >= current_frame_id 452 } 453 } 454 455 /// High-level container for resources managed by the `RenderBackend`. 456 /// 457 /// This includes a variety of things, including images, fonts, and glyphs, 458 /// which may be stored as memory buffers, GPU textures, or handles to resources 459 /// managed by the OS or other parts of WebRender. 460 pub struct ResourceCache { 461 cached_glyphs: GlyphCache, 462 cached_images: ImageCache, 463 cached_render_tasks: RenderTaskCache, 464 465 resources: Resources, 466 state: State, 467 current_frame_id: FrameId, 468 469 #[cfg(feature = "capture")] 470 /// Used for capture sequences. If the resource cache is updated, then we 471 /// mark it as dirty. When the next frame is captured in the sequence, we 472 /// dump the state of the resource cache. 473 capture_dirty: bool, 474 475 pub texture_cache: TextureCache, 476 pub picture_textures: PictureTextures, 477 478 /// TODO(gw): We should expire (parts of) this cache semi-regularly! 479 cached_glyph_dimensions: GlyphDimensionsCache, 480 glyph_rasterizer: GlyphRasterizer, 481 482 /// The set of images that aren't present or valid in the texture cache, 483 /// and need to be rasterized and/or uploaded this frame. This includes 484 /// both blobs and regular images. 485 pending_image_requests: FastHashSet<ImageRequest>, 486 487 rasterized_blob_images: FastHashMap<BlobImageKey, RasterizedBlob>, 488 489 /// A log of the last three frames worth of deleted image keys kept 490 /// for debugging purposes. 491 deleted_blob_keys: VecDeque<Vec<BlobImageKey>>, 492 493 /// We keep one around to be able to call clear_namespace 494 /// after the api object is deleted. For most purposes the 495 /// api object's blob handler should be used instead. 496 blob_image_handler: Option<Box<dyn BlobImageHandler>>, 497 498 /// A list of queued compositor surface updates to apply next frame. 499 pending_native_surface_updates: Vec<NativeSurfaceOperation>, 500 501 image_templates_memory: usize, 502 font_templates_memory: usize, 503 504 /// A pool of render targets for use by the render task graph 505 render_target_pool: Vec<RenderTarget>, 506 507 /// An empty (1x1 transparent) image used when a stacking context snapshot 508 /// is missing. 509 /// 510 /// For now it acts as a catch-all solution for cases where WebRender fails 511 /// to produce a texture cache item for a snapshotted tacking context. 512 /// These cases include: 513 /// - Empty stacking contexts. 514 /// - Stacking contexts that are more aggressively culled out than they 515 /// should, for example when they are in a perspective transform that 516 /// cannot be projected to screen space. 517 /// - Likely other cases we have not found yet. 518 /// Over time it would be better to handle each of these cases explicitly 519 /// and make it a hard error to fail to snapshot a stacking context. 520 fallback_handle: TextureCacheHandle, 521 debug_fallback_panic: bool, 522 debug_fallback_pink: bool, 523 } 524 525 impl ResourceCache { 526 pub fn new( 527 texture_cache: TextureCache, 528 picture_textures: PictureTextures, 529 glyph_rasterizer: GlyphRasterizer, 530 cached_glyphs: GlyphCache, 531 fonts: SharedFontResources, 532 blob_image_handler: Option<Box<dyn BlobImageHandler>>, 533 ) -> Self { 534 ResourceCache { 535 cached_glyphs, 536 cached_images: ResourceClassCache::new(), 537 cached_render_tasks: RenderTaskCache::new(), 538 resources: Resources { 539 fonts, 540 image_templates: ImageTemplates::default(), 541 weak_fonts: WeakTable::new(), 542 }, 543 cached_glyph_dimensions: FastHashMap::default(), 544 texture_cache, 545 picture_textures, 546 state: State::Idle, 547 current_frame_id: FrameId::INVALID, 548 pending_image_requests: FastHashSet::default(), 549 glyph_rasterizer, 550 rasterized_blob_images: FastHashMap::default(), 551 // We want to keep three frames worth of delete blob keys 552 deleted_blob_keys: vec![Vec::new(), Vec::new(), Vec::new()].into(), 553 blob_image_handler, 554 pending_native_surface_updates: Vec::new(), 555 #[cfg(feature = "capture")] 556 capture_dirty: true, 557 image_templates_memory: 0, 558 font_templates_memory: 0, 559 render_target_pool: Vec::new(), 560 fallback_handle: TextureCacheHandle::invalid(), 561 debug_fallback_panic: false, 562 debug_fallback_pink: false, 563 } 564 } 565 566 /// Construct a resource cache for use in unit tests. 567 #[cfg(test)] 568 pub fn new_for_testing() -> Self { 569 use rayon::ThreadPoolBuilder; 570 571 let texture_cache = TextureCache::new_for_testing( 572 4096, 573 ImageFormat::RGBA8, 574 ); 575 let workers = Arc::new(ThreadPoolBuilder::new().build().unwrap()); 576 let glyph_rasterizer = GlyphRasterizer::new(workers, None, true); 577 let cached_glyphs = GlyphCache::new(); 578 let fonts = SharedFontResources::new(IdNamespace(0)); 579 let picture_textures = PictureTextures::new( 580 crate::tile_cache::TILE_SIZE_DEFAULT, 581 TextureFilter::Nearest, 582 ); 583 584 ResourceCache::new( 585 texture_cache, 586 picture_textures, 587 glyph_rasterizer, 588 cached_glyphs, 589 fonts, 590 None, 591 ) 592 } 593 594 pub fn max_texture_size(&self) -> i32 { 595 self.texture_cache.max_texture_size() 596 } 597 598 /// Maximum texture size before we consider it preferrable to break the texture 599 /// into tiles. 600 pub fn tiling_threshold(&self) -> i32 { 601 self.texture_cache.tiling_threshold() 602 } 603 604 pub fn enable_multithreading(&mut self, enable: bool) { 605 self.glyph_rasterizer.enable_multithreading(enable); 606 } 607 608 fn should_tile(limit: i32, descriptor: &ImageDescriptor, data: &CachedImageData) -> bool { 609 let size_check = descriptor.size.width > limit || descriptor.size.height > limit; 610 match *data { 611 CachedImageData::Raw(_) | CachedImageData::Blob => size_check, 612 CachedImageData::External(info) => { 613 // External handles already represent existing textures so it does 614 // not make sense to tile them into smaller ones. 615 info.image_type == ExternalImageType::Buffer && size_check 616 } 617 CachedImageData::Snapshot => false, 618 } 619 } 620 621 /// Request an optionally cacheable render task. 622 /// 623 /// If the render task cache key is None, the render task is 624 /// not cached. 625 /// Otherwise, if the item is already cached, the texture cache 626 /// handle will be returned. Otherwise, the user supplied 627 /// closure will be invoked to generate the render task 628 /// chain that is required to draw this task. 629 /// 630 /// This function takes care of adding the render task as a 631 /// dependency to its parent task or surface. 632 pub fn request_render_task( 633 &mut self, 634 key: Option<RenderTaskCacheKey>, 635 is_opaque: bool, 636 parent: RenderTaskParent, 637 gpu_buffer_builder: &mut GpuBufferBuilderF, 638 rg_builder: &mut RenderTaskGraphBuilder, 639 surface_builder: &mut SurfaceBuilder, 640 f: &mut dyn FnMut(&mut RenderTaskGraphBuilder, &mut GpuBufferBuilderF) -> RenderTaskId, 641 ) -> RenderTaskId { 642 self.cached_render_tasks.request_render_task( 643 key.clone(), 644 &mut self.texture_cache, 645 is_opaque, 646 parent, 647 gpu_buffer_builder, 648 rg_builder, 649 surface_builder, 650 f 651 ) 652 } 653 654 pub fn render_as_image( 655 &mut self, 656 image_key: ImageKey, 657 size: DeviceIntSize, 658 rg_builder: &mut RenderTaskGraphBuilder, 659 gpu_buffer_builder: &mut GpuBufferBuilderF, 660 is_opaque: bool, 661 adjustment: &AdjustedImageSource, 662 f: &mut dyn FnMut(&mut RenderTaskGraphBuilder, &mut GpuBufferBuilderF) -> RenderTaskId, 663 ) -> RenderTaskId { 664 665 let task_id = f(rg_builder, gpu_buffer_builder); 666 667 let render_task = rg_builder.get_task_mut(task_id); 668 669 // Note: We are defaulting to `ImageRendering::Auto` and only support 670 // this mode here, because the desired rendering mode is known later 671 // when an image display item will read the produced snapshot. In theory, 672 // multiple image items with different rendering modes could refer to 673 // the snapshot's image key, or they could first appear in a later frame 674 // So delaying snapshotting logic until the we know about the rendering 675 // mode would, in addition to adding complexity, only work in certain 676 // cases. 677 // If supporting more rendering modes is important for snapshots, we could 678 // consider specifying it in the stacking context's snapshot params so 679 // that we have the information early enough. 680 // Here and in other parts of the code, this restriction manifests itself 681 // in the expectation that we are dealing with `ImageResult::UntiledAuto` 682 // which implicitly specifies the rendering mode. 683 684 // Make sure to update the existing image info and texture cache handle 685 // instead of overwriting them if they already exist for this key. 686 let image_result = self.cached_images.entry(image_key).or_insert_with(|| { 687 ImageResult::UntiledAuto(CachedImageInfo { 688 texture_cache_handle: TextureCacheHandle::invalid(), 689 dirty_rect: ImageDirtyRect::All, 690 manual_eviction: true, 691 }) 692 }); 693 694 let ImageResult::UntiledAuto(ref mut info) = *image_result else { 695 unreachable!("Expected untiled image with auto filter for snapshot"); 696 }; 697 698 let flags = if is_opaque { 699 ImageDescriptorFlags::IS_OPAQUE 700 } else { 701 ImageDescriptorFlags::empty() 702 }; 703 704 let descriptor = ImageDescriptor::new( 705 size.width, 706 size.height, 707 self.texture_cache.shared_color_expected_format(), 708 flags, 709 ); 710 711 // TODO(bug 1975123) We currently do not have a way to ensure that an 712 // atlas texture used as a destination for the snapshot will not be 713 // also used as an input by a primitive of the snapshot. 714 // We can't both read and write the same texture in a draw call 715 // so we work around it by preventing the snapshot from being placed 716 // in a texture atlas. 717 let force_standalone_texture = true; 718 719 // Allocate space in the texture cache, but don't supply 720 // and CPU-side data to be uploaded. 721 let user_data = [0.0; 4]; 722 self.texture_cache.update( 723 &mut info.texture_cache_handle, 724 descriptor, 725 TextureFilter::Linear, 726 None, 727 user_data, 728 DirtyRect::All, 729 gpu_buffer_builder, 730 None, 731 render_task.uv_rect_kind(), 732 Eviction::Manual, 733 TargetShader::Default, 734 force_standalone_texture, 735 ); 736 737 // Get the allocation details in the texture cache, and store 738 // this in the render task. The renderer will draw this task 739 // into the appropriate rect of the texture cache on this frame. 740 let (texture_id, uv_rect, _, _, _) = 741 self.texture_cache.get_cache_location(&info.texture_cache_handle); 742 743 render_task.location = RenderTaskLocation::Static { 744 surface: StaticRenderTaskSurface::TextureCache { 745 texture: texture_id, 746 target_kind: RenderTargetKind::Color, 747 }, 748 rect: uv_rect.to_i32(), 749 }; 750 751 self.resources.image_templates 752 .get_mut(image_key) 753 .unwrap() 754 .adjustment = *adjustment; 755 756 task_id 757 } 758 759 pub fn post_scene_building_update( 760 &mut self, 761 updates: Vec<ResourceUpdate>, 762 profile: &mut TransactionProfile, 763 ) { 764 // TODO, there is potential for optimization here, by processing updates in 765 // bulk rather than one by one (for example by sorting allocations by size or 766 // in a way that reduces fragmentation in the atlas). 767 #[cfg(feature = "capture")] 768 match updates.is_empty() { 769 false => self.capture_dirty = true, 770 _ => {}, 771 } 772 773 for update in updates { 774 match update { 775 ResourceUpdate::AddImage(img) => { 776 if let ImageData::Raw(ref bytes) = img.data { 777 self.image_templates_memory += bytes.len(); 778 profile.set(profiler::IMAGE_TEMPLATES_MEM, bytes_to_mb(self.image_templates_memory)); 779 } 780 self.add_image_template( 781 img.key, 782 img.descriptor, 783 img.data.into(), 784 &img.descriptor.size.into(), 785 img.tiling, 786 ); 787 profile.set(profiler::IMAGE_TEMPLATES, self.resources.image_templates.images.len()); 788 } 789 ResourceUpdate::UpdateImage(img) => { 790 self.update_image_template(img.key, img.descriptor, img.data.into(), &img.dirty_rect); 791 } 792 ResourceUpdate::AddBlobImage(img) => { 793 self.add_image_template( 794 img.key.as_image(), 795 img.descriptor, 796 CachedImageData::Blob, 797 &img.visible_rect, 798 Some(img.tile_size), 799 ); 800 } 801 ResourceUpdate::UpdateBlobImage(img) => { 802 self.update_image_template( 803 img.key.as_image(), 804 img.descriptor, 805 CachedImageData::Blob, 806 &to_image_dirty_rect( 807 &img.dirty_rect 808 ), 809 ); 810 self.discard_tiles_outside_visible_area(img.key, &img.visible_rect); // TODO: remove? 811 self.set_image_visible_rect(img.key.as_image(), &img.visible_rect); 812 } 813 ResourceUpdate::DeleteImage(img) => { 814 self.delete_image_template(img); 815 profile.set(profiler::IMAGE_TEMPLATES, self.resources.image_templates.images.len()); 816 profile.set(profiler::IMAGE_TEMPLATES_MEM, bytes_to_mb(self.image_templates_memory)); 817 } 818 ResourceUpdate::DeleteBlobImage(img) => { 819 self.delete_image_template(img.as_image()); 820 } 821 ResourceUpdate::AddSnapshotImage(img) => { 822 let format = self.texture_cache.shared_color_expected_format(); 823 self.add_image_template( 824 img.key.as_image(), 825 ImageDescriptor { 826 format, 827 // We'll know about the size when creating the render task. 828 size: DeviceIntSize::zero(), 829 stride: None, 830 offset: 0, 831 flags: ImageDescriptorFlags::empty(), 832 }, 833 CachedImageData::Snapshot, 834 &DeviceIntRect::zero(), 835 None, 836 ); 837 } 838 ResourceUpdate::DeleteSnapshotImage(img) => { 839 self.delete_image_template(img.as_image()); 840 } 841 ResourceUpdate::DeleteFont(font) => { 842 if let Some(shared_key) = self.resources.fonts.font_keys.delete_key(&font) { 843 self.delete_font_template(shared_key); 844 if let Some(ref mut handler) = &mut self.blob_image_handler { 845 handler.delete_font(shared_key); 846 } 847 profile.set(profiler::FONT_TEMPLATES, self.resources.fonts.templates.len()); 848 profile.set(profiler::FONT_TEMPLATES_MEM, bytes_to_mb(self.font_templates_memory)); 849 } 850 } 851 ResourceUpdate::DeleteFontInstance(font) => { 852 if let Some(shared_key) = self.resources.fonts.instance_keys.delete_key(&font) { 853 self.delete_font_instance(shared_key); 854 } 855 if let Some(ref mut handler) = &mut self.blob_image_handler { 856 handler.delete_font_instance(font); 857 } 858 } 859 ResourceUpdate::SetBlobImageVisibleArea(key, area) => { 860 self.discard_tiles_outside_visible_area(key, &area); 861 self.set_image_visible_rect(key.as_image(), &area); 862 } 863 ResourceUpdate::AddFont(font) => { 864 // The shared key was already added in ApiResources, but the first time it is 865 // seen on the backend we still need to do some extra initialization here. 866 let (key, template) = match font { 867 AddFont::Raw(key, bytes, index) => { 868 (key, FontTemplate::Raw(bytes, index)) 869 } 870 AddFont::Native(key, native_font_handle) => { 871 (key, FontTemplate::Native(native_font_handle)) 872 } 873 }; 874 let shared_key = self.resources.fonts.font_keys.map_key(&key); 875 if !self.glyph_rasterizer.has_font(shared_key) { 876 self.add_font_template(shared_key, template); 877 profile.set(profiler::FONT_TEMPLATES, self.resources.fonts.templates.len()); 878 profile.set(profiler::FONT_TEMPLATES_MEM, bytes_to_mb(self.font_templates_memory)); 879 } 880 } 881 ResourceUpdate::AddFontInstance(..) => { 882 // Already added in ApiResources. 883 } 884 } 885 } 886 } 887 888 pub fn add_rasterized_blob_images( 889 &mut self, 890 images: Vec<(BlobImageRequest, BlobImageResult)>, 891 profile: &mut TransactionProfile, 892 ) { 893 for (request, result) in images { 894 let data = match result { 895 Ok(data) => data, 896 Err(..) => { 897 warn!("Failed to rasterize a blob image"); 898 continue; 899 } 900 }; 901 902 profile.add(profiler::RASTERIZED_BLOBS_PX, data.rasterized_rect.area()); 903 904 // First make sure we have an entry for this key (using a placeholder 905 // if need be). 906 let tiles = self.rasterized_blob_images.entry(request.key).or_insert_with( 907 || { RasterizedBlob::default() } 908 ); 909 910 tiles.insert(request.tile, data); 911 912 match self.cached_images.try_get_mut(&request.key.as_image()) { 913 Some(&mut ImageResult::Multi(ref mut entries)) => { 914 let cached_key = CachedImageKey { 915 rendering: ImageRendering::Auto, // TODO(nical) 916 tile: Some(request.tile), 917 }; 918 if let Some(entry) = entries.try_get_mut(&cached_key) { 919 entry.dirty_rect = DirtyRect::All; 920 } 921 } 922 _ => {} 923 } 924 } 925 } 926 927 pub fn add_font_template(&mut self, font_key: FontKey, template: FontTemplate) { 928 // Push the new font to the font renderer, and also store 929 // it locally for glyph metric requests. 930 if let FontTemplate::Raw(ref data, _) = template { 931 self.resources.weak_fonts.insert(Arc::downgrade(data)); 932 self.font_templates_memory += data.len(); 933 } 934 self.glyph_rasterizer.add_font(font_key, template.clone()); 935 self.resources.fonts.templates.add_font(font_key, template); 936 } 937 938 pub fn delete_font_template(&mut self, font_key: FontKey) { 939 self.glyph_rasterizer.delete_font(font_key); 940 if let Some(FontTemplate::Raw(data, _)) = self.resources.fonts.templates.delete_font(&font_key) { 941 self.font_templates_memory -= data.len(); 942 } 943 self.cached_glyphs.delete_fonts(&[font_key]); 944 } 945 946 pub fn delete_font_instance(&mut self, instance_key: FontInstanceKey) { 947 self.resources.fonts.instances.delete_font_instance(instance_key); 948 } 949 950 pub fn get_font_instance(&self, instance_key: FontInstanceKey) -> Option<Arc<BaseFontInstance>> { 951 self.resources.fonts.instances.get_font_instance(instance_key) 952 } 953 954 pub fn get_fonts(&self) -> SharedFontResources { 955 self.resources.fonts.clone() 956 } 957 958 pub fn add_image_template( 959 &mut self, 960 image_key: ImageKey, 961 descriptor: ImageDescriptor, 962 data: CachedImageData, 963 visible_rect: &DeviceIntRect, 964 mut tiling: Option<TileSize>, 965 ) { 966 if let Some(ref mut tile_size) = tiling { 967 // Sanitize the value since it can be set by a pref. 968 *tile_size = (*tile_size).max(16).min(2048); 969 } 970 971 if tiling.is_none() && Self::should_tile(self.tiling_threshold(), &descriptor, &data) { 972 // We aren't going to be able to upload a texture this big, so tile it, even 973 // if tiling was not requested. 974 tiling = Some(DEFAULT_TILE_SIZE); 975 } 976 977 let resource = ImageResource { 978 descriptor, 979 data, 980 tiling, 981 visible_rect: *visible_rect, 982 adjustment: AdjustedImageSource::new(), 983 generation: ImageGeneration(0), 984 }; 985 986 self.resources.image_templates.insert(image_key, resource); 987 } 988 989 pub fn update_image_template( 990 &mut self, 991 image_key: ImageKey, 992 descriptor: ImageDescriptor, 993 data: CachedImageData, 994 dirty_rect: &ImageDirtyRect, 995 ) { 996 let tiling_threshold = self.tiling_threshold(); 997 let image = match self.resources.image_templates.get_mut(image_key) { 998 Some(res) => res, 999 None => panic!("Attempt to update non-existent image"), 1000 }; 1001 1002 let mut tiling = image.tiling; 1003 if tiling.is_none() && Self::should_tile(tiling_threshold, &descriptor, &data) { 1004 tiling = Some(DEFAULT_TILE_SIZE); 1005 } 1006 1007 // Each cache entry stores its own copy of the image's dirty rect. This allows them to be 1008 // updated independently. 1009 match self.cached_images.try_get_mut(&image_key) { 1010 Some(&mut ImageResult::UntiledAuto(ref mut entry)) => { 1011 entry.dirty_rect = entry.dirty_rect.union(dirty_rect); 1012 } 1013 Some(&mut ImageResult::Multi(ref mut entries)) => { 1014 for (key, entry) in entries.iter_mut() { 1015 // We want the dirty rect relative to the tile and not the whole image. 1016 let local_dirty_rect = match (tiling, key.tile) { 1017 (Some(tile_size), Some(tile)) => { 1018 dirty_rect.map(|mut rect|{ 1019 let tile_offset = DeviceIntPoint::new( 1020 tile.x as i32, 1021 tile.y as i32, 1022 ) * tile_size as i32; 1023 rect = rect.translate(-tile_offset.to_vector()); 1024 1025 let tile_rect = compute_tile_size( 1026 &descriptor.size.into(), 1027 tile_size, 1028 tile, 1029 ).into(); 1030 1031 rect.intersection(&tile_rect).unwrap_or_else(DeviceIntRect::zero) 1032 }) 1033 } 1034 (None, Some(..)) => DirtyRect::All, 1035 _ => *dirty_rect, 1036 }; 1037 entry.dirty_rect = entry.dirty_rect.union(&local_dirty_rect); 1038 } 1039 } 1040 _ => {} 1041 } 1042 1043 if image.descriptor.format != descriptor.format { 1044 // could be a stronger warning/error? 1045 trace!("Format change {:?} -> {:?}", image.descriptor.format, descriptor.format); 1046 } 1047 *image = ImageResource { 1048 descriptor, 1049 data, 1050 tiling, 1051 visible_rect: descriptor.size.into(), 1052 adjustment: AdjustedImageSource::new(), 1053 generation: ImageGeneration(image.generation.0 + 1), 1054 }; 1055 } 1056 1057 pub fn increment_image_generation(&mut self, key: ImageKey) { 1058 if let Some(image) = self.resources.image_templates.get_mut(key) { 1059 image.generation.0 += 1; 1060 } 1061 } 1062 1063 pub fn delete_image_template(&mut self, image_key: ImageKey) { 1064 // Remove the template. 1065 let value = self.resources.image_templates.remove(image_key); 1066 1067 // Release the corresponding texture cache entry, if any. 1068 if let Some(mut cached) = self.cached_images.remove(&image_key) { 1069 cached.drop_from_cache(&mut self.texture_cache); 1070 } 1071 1072 match value { 1073 Some(image) => if image.data.is_blob() { 1074 if let CachedImageData::Raw(data) = image.data { 1075 self.image_templates_memory -= data.len(); 1076 } 1077 1078 let blob_key = BlobImageKey(image_key); 1079 self.deleted_blob_keys.back_mut().unwrap().push(blob_key); 1080 self.rasterized_blob_images.remove(&blob_key); 1081 }, 1082 None => { 1083 warn!("Delete the non-exist key"); 1084 debug!("key={:?}", image_key); 1085 } 1086 } 1087 } 1088 1089 /// Return the current generation of an image template 1090 pub fn get_image_generation(&self, key: ImageKey) -> ImageGeneration { 1091 self.resources 1092 .image_templates 1093 .get(key) 1094 .map_or(ImageGeneration::INVALID, |template| template.generation) 1095 } 1096 1097 /// Requests an image to ensure that it will be in the texture cache this frame. 1098 /// 1099 /// returns the size in device pixel of the image or tile. 1100 pub fn request_image( 1101 &mut self, 1102 mut request: ImageRequest, 1103 gpu_buffer: &mut GpuBufferBuilderF, 1104 ) -> DeviceIntSize { 1105 debug_assert_eq!(self.state, State::AddResources); 1106 1107 let template = match self.resources.image_templates.get(request.key) { 1108 Some(template) => template, 1109 None => { 1110 warn!("ERROR: Trying to render deleted / non-existent key"); 1111 debug!("key={:?}", request.key); 1112 return DeviceIntSize::zero(); 1113 } 1114 }; 1115 1116 let size = match request.tile { 1117 Some(tile) => compute_tile_size(&template.visible_rect, template.tiling.unwrap(), tile), 1118 None => template.descriptor.size, 1119 }; 1120 1121 // Images that don't use the texture cache can early out. 1122 if !template.data.uses_texture_cache() { 1123 return size; 1124 } 1125 1126 if template.data.is_snapshot() { 1127 // We only Support `Auto` for snapshots. This is because we have 1128 // to make the decision about the filtering mode earlier when 1129 // producing the snapshot. 1130 // See the comment at the top of `render_as_image`. 1131 request.rendering = ImageRendering::Auto; 1132 } 1133 1134 let side_size = 1135 template.tiling.map_or(cmp::max(template.descriptor.size.width, template.descriptor.size.height), 1136 |tile_size| tile_size as i32); 1137 if side_size > self.texture_cache.max_texture_size() { 1138 // The image or tiling size is too big for hardware texture size. 1139 warn!("Dropping image, image:(w:{},h:{}, tile:{}) is too big for hardware!", 1140 template.descriptor.size.width, template.descriptor.size.height, template.tiling.unwrap_or(0)); 1141 self.cached_images.insert(request.key, ImageResult::Err(ImageCacheError::OverLimitSize)); 1142 return DeviceIntSize::zero(); 1143 } 1144 1145 let storage = match self.cached_images.entry(request.key) { 1146 Occupied(e) => { 1147 // We might have an existing untiled entry, and need to insert 1148 // a second entry. In such cases we need to move the old entry 1149 // out first, replacing it with a dummy entry, and then creating 1150 // the tiled/multi-entry variant. 1151 let entry = e.into_mut(); 1152 if !request.is_untiled_auto() { 1153 let untiled_entry = match entry { 1154 &mut ImageResult::UntiledAuto(ref mut entry) => { 1155 Some(mem::replace(entry, CachedImageInfo { 1156 texture_cache_handle: TextureCacheHandle::invalid(), 1157 dirty_rect: DirtyRect::All, 1158 manual_eviction: false, 1159 })) 1160 } 1161 _ => None 1162 }; 1163 1164 if let Some(untiled_entry) = untiled_entry { 1165 let mut entries = ResourceClassCache::new(); 1166 let untiled_key = CachedImageKey { 1167 rendering: ImageRendering::Auto, 1168 tile: None, 1169 }; 1170 entries.insert(untiled_key, untiled_entry); 1171 *entry = ImageResult::Multi(entries); 1172 } 1173 } 1174 entry 1175 } 1176 Vacant(entry) => { 1177 entry.insert(if request.is_untiled_auto() { 1178 ImageResult::UntiledAuto(CachedImageInfo { 1179 texture_cache_handle: TextureCacheHandle::invalid(), 1180 dirty_rect: DirtyRect::All, 1181 manual_eviction: false, 1182 }) 1183 } else { 1184 ImageResult::Multi(ResourceClassCache::new()) 1185 }) 1186 } 1187 }; 1188 1189 // If this image exists in the texture cache, *and* the dirty rect 1190 // in the cache is empty, then it is valid to use as-is. 1191 let entry = match *storage { 1192 ImageResult::UntiledAuto(ref mut entry) => entry, 1193 ImageResult::Multi(ref mut entries) => { 1194 entries.entry(request.into()) 1195 .or_insert(CachedImageInfo { 1196 texture_cache_handle: TextureCacheHandle::invalid(), 1197 dirty_rect: DirtyRect::All, 1198 manual_eviction: false, 1199 }) 1200 }, 1201 ImageResult::Err(_) => panic!("Errors should already have been handled"), 1202 }; 1203 1204 let needs_upload = self.texture_cache.request(&entry.texture_cache_handle, gpu_buffer); 1205 1206 if !needs_upload && entry.dirty_rect.is_empty() { 1207 return size; 1208 } 1209 1210 if !self.pending_image_requests.insert(request) { 1211 return size; 1212 } 1213 1214 if template.data.is_blob() { 1215 let request: BlobImageRequest = request.into(); 1216 let missing = match self.rasterized_blob_images.get(&request.key) { 1217 Some(tiles) => !tiles.contains_key(&request.tile), 1218 _ => true, 1219 }; 1220 1221 assert!(!missing); 1222 } 1223 1224 size 1225 } 1226 1227 fn discard_tiles_outside_visible_area( 1228 &mut self, 1229 key: BlobImageKey, 1230 area: &DeviceIntRect 1231 ) { 1232 let tile_size = match self.resources.image_templates.get(key.as_image()) { 1233 Some(template) => template.tiling.unwrap(), 1234 None => { 1235 //debug!("Missing image template (key={:?})!", key); 1236 return; 1237 } 1238 }; 1239 1240 let tiles = match self.rasterized_blob_images.get_mut(&key) { 1241 Some(tiles) => tiles, 1242 _ => { return; } 1243 }; 1244 1245 let tile_range = compute_tile_range( 1246 &area, 1247 tile_size, 1248 ); 1249 1250 tiles.retain(|tile, _| { tile_range.contains(*tile) }); 1251 1252 let texture_cache = &mut self.texture_cache; 1253 match self.cached_images.try_get_mut(&key.as_image()) { 1254 Some(&mut ImageResult::Multi(ref mut entries)) => { 1255 entries.retain(|key, entry| { 1256 if key.tile.is_none() || tile_range.contains(key.tile.unwrap()) { 1257 return true; 1258 } 1259 entry.mark_unused(texture_cache); 1260 return false; 1261 }); 1262 } 1263 _ => {} 1264 } 1265 } 1266 1267 fn set_image_visible_rect(&mut self, key: ImageKey, rect: &DeviceIntRect) { 1268 if let Some(image) = self.resources.image_templates.get_mut(key) { 1269 image.visible_rect = *rect; 1270 image.descriptor.size = rect.size(); 1271 } 1272 } 1273 1274 pub fn request_glyphs( 1275 &mut self, 1276 mut font: FontInstance, 1277 glyph_keys: &[GlyphKey], 1278 gpu_buffer: &mut GpuBufferBuilderF, 1279 ) { 1280 debug_assert_eq!(self.state, State::AddResources); 1281 1282 self.glyph_rasterizer.prepare_font(&mut font); 1283 let glyph_key_cache = self.cached_glyphs.insert_glyph_key_cache_for_font(&font); 1284 let texture_cache = &mut self.texture_cache; 1285 self.glyph_rasterizer.request_glyphs( 1286 font, 1287 glyph_keys, 1288 |key| { 1289 let cache_key = key.cache_key(); 1290 if let Some(entry) = glyph_key_cache.try_get(&cache_key) { 1291 match entry { 1292 GlyphCacheEntry::Cached(ref glyph) => { 1293 if !texture_cache.request(&glyph.texture_cache_handle, gpu_buffer) { 1294 return false; 1295 } 1296 // This case gets hit when we already rasterized the glyph, but the 1297 // glyph has been evicted from the texture cache. Just force it to 1298 // pending so it gets rematerialized. 1299 } 1300 // Otherwise, skip the entry if it is blank or pending. 1301 GlyphCacheEntry::Blank | GlyphCacheEntry::Pending => return false, 1302 } 1303 }; 1304 1305 glyph_key_cache.add_glyph(cache_key, GlyphCacheEntry::Pending); 1306 1307 true 1308 } 1309 ); 1310 } 1311 1312 pub fn pending_updates(&mut self) -> ResourceUpdateList { 1313 ResourceUpdateList { 1314 texture_updates: self.texture_cache.pending_updates(), 1315 native_surface_updates: mem::replace(&mut self.pending_native_surface_updates, Vec::new()), 1316 } 1317 } 1318 1319 pub fn fetch_glyphs<F>( 1320 &self, 1321 mut font: FontInstance, 1322 glyph_keys: &[GlyphKey], 1323 gpu_buffer: &GpuBufferBuilderF, 1324 fetch_buffer: &mut Vec<GlyphFetchResult>, 1325 mut f: F, 1326 ) where 1327 F: FnMut(TextureSource, GlyphFormat, &[GlyphFetchResult]), 1328 { 1329 debug_assert_eq!(self.state, State::QueryResources); 1330 1331 self.glyph_rasterizer.prepare_font(&mut font); 1332 let glyph_key_cache = self.cached_glyphs.get_glyph_key_cache_for_font(&font); 1333 1334 let mut current_texture_id = TextureSource::Invalid; 1335 let mut current_glyph_format = GlyphFormat::Subpixel; 1336 debug_assert!(fetch_buffer.is_empty()); 1337 1338 for (loop_index, key) in glyph_keys.iter().enumerate() { 1339 let cache_key = key.cache_key(); 1340 let (cache_item, glyph_format, is_packed_glyph) = match *glyph_key_cache.get(&cache_key) { 1341 GlyphCacheEntry::Cached(ref glyph) => { 1342 (self.texture_cache.get(&glyph.texture_cache_handle), glyph.format, glyph.is_packed_glyph) 1343 } 1344 GlyphCacheEntry::Blank | GlyphCacheEntry::Pending => continue, 1345 }; 1346 if current_texture_id != cache_item.texture_id || 1347 current_glyph_format != glyph_format { 1348 if !fetch_buffer.is_empty() { 1349 f(current_texture_id, current_glyph_format, fetch_buffer); 1350 fetch_buffer.clear(); 1351 } 1352 current_texture_id = cache_item.texture_id; 1353 current_glyph_format = glyph_format; 1354 } 1355 let (subpx_offset_x, subpx_offset_y) = key.subpixel_offset(); 1356 fetch_buffer.push(GlyphFetchResult { 1357 index_in_text_run: loop_index as i32, 1358 uv_rect_address: gpu_buffer.resolve_handle(cache_item.uv_rect_handle), 1359 offset: DevicePoint::new(cache_item.user_data[0], cache_item.user_data[1]), 1360 size: cache_item.uv_rect.size(), 1361 scale: cache_item.user_data[2], 1362 subpx_offset_x: subpx_offset_x as u8, 1363 subpx_offset_y: subpx_offset_y as u8, 1364 is_packed_glyph, 1365 }); 1366 } 1367 1368 if !fetch_buffer.is_empty() { 1369 f(current_texture_id, current_glyph_format, fetch_buffer); 1370 fetch_buffer.clear(); 1371 } 1372 } 1373 1374 pub fn map_font_key(&self, key: FontKey) -> FontKey { 1375 self.resources.fonts.font_keys.map_key(&key) 1376 } 1377 1378 pub fn map_font_instance_key(&self, key: FontInstanceKey) -> FontInstanceKey { 1379 self.resources.fonts.instance_keys.map_key(&key) 1380 } 1381 1382 pub fn get_glyph_dimensions( 1383 &mut self, 1384 font: &FontInstance, 1385 glyph_index: GlyphIndex, 1386 ) -> Option<GlyphDimensions> { 1387 match self.cached_glyph_dimensions.entry((font.instance_key, glyph_index)) { 1388 Occupied(entry) => *entry.get(), 1389 Vacant(entry) => *entry.insert( 1390 self.glyph_rasterizer 1391 .get_glyph_dimensions(font, glyph_index), 1392 ), 1393 } 1394 } 1395 1396 pub fn get_glyph_index(&mut self, font_key: FontKey, ch: char) -> Option<u32> { 1397 self.glyph_rasterizer.get_glyph_index(font_key, ch) 1398 } 1399 1400 #[inline] 1401 pub fn get_cached_image(&self, request: ImageRequest) -> Result<CacheItem, ()> { 1402 debug_assert_eq!(self.state, State::QueryResources); 1403 let image_info = self.get_image_info(request)?; 1404 1405 if let Ok(item) = self.get_texture_cache_item(&image_info.texture_cache_handle) { 1406 // Common path. 1407 return Ok(item); 1408 } 1409 1410 if self.resources.image_templates 1411 .get(request.key) 1412 .map_or(false, |img| img.data.is_snapshot()) { 1413 if self.debug_fallback_panic { 1414 panic!("Missing snapshot image"); 1415 } 1416 return self.get_texture_cache_item(&self.fallback_handle); 1417 } 1418 1419 panic!("Requested image missing from the texture cache"); 1420 } 1421 1422 pub fn get_cached_render_task( 1423 &self, 1424 handle: &RenderTaskCacheEntryHandle, 1425 ) -> &RenderTaskCacheEntry { 1426 self.cached_render_tasks.get_cache_entry(handle) 1427 } 1428 1429 #[inline] 1430 fn get_image_info(&self, request: ImageRequest) -> Result<&CachedImageInfo, ()> { 1431 // TODO(Jerry): add a debug option to visualize the corresponding area for 1432 // the Err() case of CacheItem. 1433 match *self.cached_images.get(&request.key) { 1434 ImageResult::UntiledAuto(ref image_info) => Ok(image_info), 1435 ImageResult::Multi(ref entries) => Ok(entries.get(&request.into())), 1436 ImageResult::Err(_) => Err(()), 1437 } 1438 } 1439 1440 #[inline] 1441 pub fn get_texture_cache_item(&self, handle: &TextureCacheHandle) -> Result<CacheItem, ()> { 1442 if let Some(item) = self.texture_cache.try_get(handle) { 1443 return Ok(item); 1444 } 1445 1446 Err(()) 1447 } 1448 1449 pub fn get_image_properties(&self, image_key: ImageKey) -> Option<ImageProperties> { 1450 let image_template = &self.resources.image_templates.get(image_key); 1451 1452 image_template.map(|image_template| { 1453 let external_image = match image_template.data { 1454 CachedImageData::External(ext_image) => match ext_image.image_type { 1455 ExternalImageType::TextureHandle(_) => Some(ext_image), 1456 // external buffer uses resource_cache. 1457 ExternalImageType::Buffer => None, 1458 }, 1459 // raw and blob image are all using resource_cache. 1460 CachedImageData::Raw(..) 1461 | CachedImageData::Blob 1462 | CachedImageData::Snapshot 1463 => None, 1464 }; 1465 1466 ImageProperties { 1467 descriptor: image_template.descriptor, 1468 external_image, 1469 tiling: image_template.tiling, 1470 visible_rect: image_template.visible_rect, 1471 adjustment: image_template.adjustment, 1472 } 1473 }) 1474 } 1475 1476 pub fn begin_frame(&mut self, stamp: FrameStamp, profile: &mut TransactionProfile) { 1477 profile_scope!("begin_frame"); 1478 debug_assert_eq!(self.state, State::Idle); 1479 self.state = State::AddResources; 1480 self.texture_cache.begin_frame(stamp, profile); 1481 self.picture_textures.begin_frame(stamp, &mut self.texture_cache.pending_updates); 1482 1483 self.cached_glyphs.begin_frame( 1484 stamp, 1485 &mut self.texture_cache, 1486 &mut self.glyph_rasterizer, 1487 ); 1488 self.cached_render_tasks.begin_frame(&mut self.texture_cache); 1489 self.current_frame_id = stamp.frame_id(); 1490 1491 // Pop the old frame and push a new one. 1492 // Recycle the allocation if any. 1493 let mut v = self.deleted_blob_keys.pop_front().unwrap_or_else(Vec::new); 1494 v.clear(); 1495 self.deleted_blob_keys.push_back(v); 1496 1497 self.texture_cache.run_compaction(); 1498 } 1499 1500 pub fn block_until_all_resources_added( 1501 &mut self, 1502 gpu_buffer: &mut GpuBufferBuilder, 1503 profile: &mut TransactionProfile, 1504 ) { 1505 profile_scope!("block_until_all_resources_added"); 1506 1507 debug_assert_eq!(self.state, State::AddResources); 1508 self.state = State::QueryResources; 1509 1510 let cached_glyphs = &mut self.cached_glyphs; 1511 let texture_cache = &mut self.texture_cache; 1512 1513 self.glyph_rasterizer.resolve_glyphs( 1514 |job, can_use_r8_format| { 1515 let GlyphRasterJob { font, key, result } = job; 1516 let cache_key = key.cache_key(); 1517 let glyph_key_cache = cached_glyphs.get_glyph_key_cache_for_font_mut(&*font); 1518 let glyph_info = match result { 1519 Err(_) => GlyphCacheEntry::Blank, 1520 Ok(ref glyph) if glyph.width == 0 || glyph.height == 0 => { 1521 GlyphCacheEntry::Blank 1522 } 1523 Ok(glyph) => { 1524 let mut texture_cache_handle = TextureCacheHandle::invalid(); 1525 texture_cache.request(&texture_cache_handle, &mut gpu_buffer.f32); 1526 texture_cache.update( 1527 &mut texture_cache_handle, 1528 ImageDescriptor { 1529 size: size2(glyph.width, glyph.height), 1530 stride: None, 1531 format: glyph.format.image_format(can_use_r8_format), 1532 flags: ImageDescriptorFlags::empty(), 1533 offset: 0, 1534 }, 1535 TextureFilter::Linear, 1536 Some(CachedImageData::Raw(Arc::new(glyph.bytes))), 1537 [glyph.left, -glyph.top, glyph.scale, 0.0], 1538 DirtyRect::All, 1539 &mut gpu_buffer.f32, 1540 Some(glyph_key_cache.eviction_notice()), 1541 UvRectKind::Rect, 1542 Eviction::Auto, 1543 TargetShader::Text, 1544 false, 1545 ); 1546 GlyphCacheEntry::Cached(CachedGlyphInfo { 1547 texture_cache_handle, 1548 format: glyph.format, 1549 is_packed_glyph: glyph.is_packed_glyph, 1550 }) 1551 } 1552 }; 1553 glyph_key_cache.insert(cache_key, glyph_info); 1554 }, 1555 profile, 1556 ); 1557 1558 // Apply any updates of new / updated images (incl. blobs) to the texture cache. 1559 self.update_texture_cache(gpu_buffer); 1560 } 1561 1562 fn update_texture_cache(&mut self, gpu_buffer: &mut GpuBufferBuilder) { 1563 profile_scope!("update_texture_cache"); 1564 1565 if self.fallback_handle == TextureCacheHandle::invalid() { 1566 let fallback_color = if self.debug_fallback_pink { 1567 vec![255, 0, 255, 255] 1568 } else { 1569 vec![0, 0, 0, 0] 1570 }; 1571 self.texture_cache.update( 1572 &mut self.fallback_handle, 1573 ImageDescriptor { 1574 size: size2(1, 1), 1575 stride: None, 1576 format: ImageFormat::BGRA8, 1577 flags: ImageDescriptorFlags::empty(), 1578 offset: 0, 1579 }, 1580 TextureFilter::Linear, 1581 Some(CachedImageData::Raw(Arc::new(fallback_color))), 1582 [0.0; 4], 1583 DirtyRect::All, 1584 &mut gpu_buffer.f32, 1585 None, 1586 UvRectKind::Rect, 1587 Eviction::Manual, 1588 TargetShader::Default, 1589 false, 1590 ); 1591 } 1592 1593 for request in self.pending_image_requests.drain() { 1594 let image_template = self.resources.image_templates.get_mut(request.key).unwrap(); 1595 debug_assert!(image_template.data.uses_texture_cache()); 1596 1597 let mut updates: SmallVec<[(CachedImageData, Option<DeviceIntRect>); 1]> = SmallVec::new(); 1598 1599 match image_template.data { 1600 CachedImageData::Snapshot => { 1601 // The update is done in ResourceCache::render_as_image. 1602 } 1603 CachedImageData::Raw(..) 1604 | CachedImageData::External(..) => { 1605 // Safe to clone here since the Raw image data is an 1606 // Arc, and the external image data is small. 1607 updates.push((image_template.data.clone(), None)); 1608 } 1609 CachedImageData::Blob => { 1610 let blob_image = self.rasterized_blob_images.get_mut(&BlobImageKey(request.key)).unwrap(); 1611 let img = &blob_image[&request.tile.unwrap()]; 1612 updates.push(( 1613 CachedImageData::Raw(Arc::clone(&img.data)), 1614 Some(img.rasterized_rect) 1615 )); 1616 } 1617 }; 1618 1619 for (image_data, blob_rasterized_rect) in updates { 1620 let entry = match *self.cached_images.get_mut(&request.key) { 1621 ImageResult::UntiledAuto(ref mut entry) => entry, 1622 ImageResult::Multi(ref mut entries) => entries.get_mut(&request.into()), 1623 ImageResult::Err(_) => panic!("Update requested for invalid entry") 1624 }; 1625 1626 let mut descriptor = image_template.descriptor.clone(); 1627 let mut dirty_rect = entry.dirty_rect.replace_with_empty(); 1628 1629 if let Some(tile) = request.tile { 1630 let tile_size = image_template.tiling.unwrap(); 1631 let clipped_tile_size = compute_tile_size(&image_template.visible_rect, tile_size, tile); 1632 // The tiled image could be stored on the CPU as one large image or be 1633 // already broken up into tiles. This affects the way we compute the stride 1634 // and offset. 1635 let tiled_on_cpu = image_template.data.is_blob(); 1636 if !tiled_on_cpu { 1637 // we don't expect to have partial tiles at the top and left of non-blob 1638 // images. 1639 debug_assert_eq!(image_template.visible_rect.min, point2(0, 0)); 1640 let bpp = descriptor.format.bytes_per_pixel(); 1641 let stride = descriptor.compute_stride(); 1642 descriptor.stride = Some(stride); 1643 descriptor.offset += 1644 tile.y as i32 * tile_size as i32 * stride + 1645 tile.x as i32 * tile_size as i32 * bpp; 1646 } 1647 1648 descriptor.size = clipped_tile_size; 1649 } 1650 1651 // If we are uploading the dirty region of a blob image we might have several 1652 // rects to upload so we use each of these rasterized rects rather than the 1653 // overall dirty rect of the image. 1654 if let Some(rect) = blob_rasterized_rect { 1655 dirty_rect = DirtyRect::Partial(rect); 1656 } 1657 1658 let filter = match request.rendering { 1659 ImageRendering::Pixelated => { 1660 TextureFilter::Nearest 1661 } 1662 ImageRendering::Auto | ImageRendering::CrispEdges => { 1663 // If the texture uses linear filtering, enable mipmaps and 1664 // trilinear filtering, for better image quality. We only 1665 // support this for now on textures that are not placed 1666 // into the shared cache. This accounts for any image 1667 // that is > 512 in either dimension, so it should cover 1668 // the most important use cases. We may want to support 1669 // mip-maps on shared cache items in the future. 1670 if descriptor.allow_mipmaps() && 1671 descriptor.size.width > 512 && 1672 descriptor.size.height > 512 && 1673 !self.texture_cache.is_allowed_in_shared_cache( 1674 TextureFilter::Linear, 1675 &descriptor, 1676 ) { 1677 TextureFilter::Trilinear 1678 } else { 1679 TextureFilter::Linear 1680 } 1681 } 1682 }; 1683 1684 let eviction = match &image_template.data { 1685 CachedImageData::Blob | CachedImageData::Snapshot => { 1686 entry.manual_eviction = true; 1687 Eviction::Manual 1688 } 1689 _ => { 1690 Eviction::Auto 1691 } 1692 }; 1693 1694 //Note: at this point, the dirty rectangle is local to the descriptor space 1695 self.texture_cache.update( 1696 &mut entry.texture_cache_handle, 1697 descriptor, 1698 filter, 1699 Some(image_data), 1700 [0.0; 4], 1701 dirty_rect, 1702 &mut gpu_buffer.f32, 1703 None, 1704 UvRectKind::Rect, 1705 eviction, 1706 TargetShader::Default, 1707 false, 1708 ); 1709 } 1710 } 1711 } 1712 1713 pub fn create_compositor_backdrop_surface( 1714 &mut self, 1715 color: ColorF 1716 ) -> NativeSurfaceId { 1717 let id = NativeSurfaceId(NEXT_NATIVE_SURFACE_ID.fetch_add(1, Ordering::Relaxed) as u64); 1718 1719 self.pending_native_surface_updates.push( 1720 NativeSurfaceOperation { 1721 details: NativeSurfaceOperationDetails::CreateBackdropSurface { 1722 id, 1723 color, 1724 }, 1725 } 1726 ); 1727 1728 id 1729 } 1730 1731 /// Queue up allocation of a new OS native compositor surface with the 1732 /// specified tile size. 1733 pub fn create_compositor_surface( 1734 &mut self, 1735 virtual_offset: DeviceIntPoint, 1736 tile_size: DeviceIntSize, 1737 is_opaque: bool, 1738 ) -> NativeSurfaceId { 1739 let id = NativeSurfaceId(NEXT_NATIVE_SURFACE_ID.fetch_add(1, Ordering::Relaxed) as u64); 1740 1741 self.pending_native_surface_updates.push( 1742 NativeSurfaceOperation { 1743 details: NativeSurfaceOperationDetails::CreateSurface { 1744 id, 1745 virtual_offset, 1746 tile_size, 1747 is_opaque, 1748 }, 1749 } 1750 ); 1751 1752 id 1753 } 1754 1755 pub fn create_compositor_external_surface( 1756 &mut self, 1757 is_opaque: bool, 1758 ) -> NativeSurfaceId { 1759 let id = NativeSurfaceId(NEXT_NATIVE_SURFACE_ID.fetch_add(1, Ordering::Relaxed) as u64); 1760 1761 self.pending_native_surface_updates.push( 1762 NativeSurfaceOperation { 1763 details: NativeSurfaceOperationDetails::CreateExternalSurface { 1764 id, 1765 is_opaque, 1766 }, 1767 } 1768 ); 1769 1770 id 1771 } 1772 1773 /// Queue up destruction of an existing native OS surface. This is used when 1774 /// a picture cache surface is dropped or resized. 1775 pub fn destroy_compositor_surface( 1776 &mut self, 1777 id: NativeSurfaceId, 1778 ) { 1779 self.pending_native_surface_updates.push( 1780 NativeSurfaceOperation { 1781 details: NativeSurfaceOperationDetails::DestroySurface { 1782 id, 1783 } 1784 } 1785 ); 1786 } 1787 1788 /// Queue construction of a native compositor tile on a given surface. 1789 pub fn create_compositor_tile( 1790 &mut self, 1791 id: NativeTileId, 1792 ) { 1793 self.pending_native_surface_updates.push( 1794 NativeSurfaceOperation { 1795 details: NativeSurfaceOperationDetails::CreateTile { 1796 id, 1797 }, 1798 } 1799 ); 1800 } 1801 1802 /// Queue destruction of a native compositor tile. 1803 pub fn destroy_compositor_tile( 1804 &mut self, 1805 id: NativeTileId, 1806 ) { 1807 self.pending_native_surface_updates.push( 1808 NativeSurfaceOperation { 1809 details: NativeSurfaceOperationDetails::DestroyTile { 1810 id, 1811 }, 1812 } 1813 ); 1814 } 1815 1816 pub fn attach_compositor_external_image( 1817 &mut self, 1818 id: NativeSurfaceId, 1819 external_image: ExternalImageId, 1820 ) { 1821 self.pending_native_surface_updates.push( 1822 NativeSurfaceOperation { 1823 details: NativeSurfaceOperationDetails::AttachExternalImage { 1824 id, 1825 external_image, 1826 }, 1827 } 1828 ); 1829 } 1830 1831 1832 pub fn end_frame(&mut self, profile: &mut TransactionProfile) { 1833 debug_assert_eq!(self.state, State::QueryResources); 1834 profile_scope!("end_frame"); 1835 self.state = State::Idle; 1836 1837 // GC the render target pool, if it's currently > 64 MB in size. 1838 // 1839 // We use a simple scheme whereby we drop any texture that hasn't been used 1840 // in the last 60 frames, until we are below the size threshold. This should 1841 // generally prevent any sustained build-up of unused textures, unless we don't 1842 // generate frames for a long period. This can happen when the window is 1843 // minimized, and we probably want to flush all the WebRender caches in that case [1]. 1844 // There is also a second "red line" memory threshold which prevents 1845 // memory exhaustion if many render targets are allocated within a small 1846 // number of frames. For now this is set at 320 MB (10x the normal memory threshold). 1847 // 1848 // [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1494099 1849 self.gc_render_targets( 1850 64 * 1024 * 1024, 1851 32 * 1024 * 1024 * 10, 1852 60, 1853 ); 1854 1855 self.texture_cache.end_frame(profile); 1856 self.picture_textures.gc( 1857 &mut self.texture_cache.pending_updates, 1858 ); 1859 1860 self.picture_textures.update_profile(profile); 1861 } 1862 1863 pub fn set_debug_flags(&mut self, flags: DebugFlags) { 1864 GLYPH_FLASHING.store(flags.contains(DebugFlags::GLYPH_FLASHING), std::sync::atomic::Ordering::Relaxed); 1865 self.texture_cache.set_debug_flags(flags); 1866 self.picture_textures.set_debug_flags(flags); 1867 self.debug_fallback_panic = flags.contains(DebugFlags::MISSING_SNAPSHOT_PANIC); 1868 let fallback_pink = flags.contains(DebugFlags::MISSING_SNAPSHOT_PINK); 1869 1870 if fallback_pink != self.debug_fallback_pink && self.fallback_handle != TextureCacheHandle::invalid() { 1871 self.texture_cache.evict_handle(&self.fallback_handle); 1872 } 1873 self.debug_fallback_pink = fallback_pink; 1874 } 1875 1876 pub fn clear(&mut self, what: ClearCache) { 1877 if what.contains(ClearCache::IMAGES) { 1878 for (_key, mut cached) in self.cached_images.resources.drain() { 1879 cached.drop_from_cache(&mut self.texture_cache); 1880 } 1881 } 1882 if what.contains(ClearCache::GLYPHS) { 1883 self.cached_glyphs.clear(); 1884 } 1885 if what.contains(ClearCache::GLYPH_DIMENSIONS) { 1886 self.cached_glyph_dimensions.clear(); 1887 } 1888 if what.contains(ClearCache::RENDER_TASKS) { 1889 self.cached_render_tasks.clear(); 1890 } 1891 if what.contains(ClearCache::TEXTURE_CACHE) { 1892 self.texture_cache.clear_all(); 1893 self.picture_textures.clear(&mut self.texture_cache.pending_updates); 1894 } 1895 if what.contains(ClearCache::RENDER_TARGETS) { 1896 self.clear_render_target_pool(); 1897 } 1898 } 1899 1900 pub fn clear_namespace(&mut self, namespace: IdNamespace) { 1901 self.clear_images(|k| k.0 == namespace); 1902 1903 // First clear out any non-shared resources associated with the namespace. 1904 self.resources.fonts.instances.clear_namespace(namespace); 1905 let deleted_keys = self.resources.fonts.templates.clear_namespace(namespace); 1906 self.glyph_rasterizer.delete_fonts(&deleted_keys); 1907 self.cached_glyphs.clear_namespace(namespace); 1908 if let Some(handler) = &mut self.blob_image_handler { 1909 handler.clear_namespace(namespace); 1910 } 1911 1912 // Check for any shared instance keys that were remapped from the namespace. 1913 let shared_instance_keys = self.resources.fonts.instance_keys.clear_namespace(namespace); 1914 if !shared_instance_keys.is_empty() { 1915 self.resources.fonts.instances.delete_font_instances(&shared_instance_keys); 1916 self.cached_glyphs.delete_font_instances(&shared_instance_keys, &mut self.glyph_rasterizer); 1917 // Blob font instances are not shared across namespaces, so there is no 1918 // need to call the handler for them individually. 1919 } 1920 1921 // Finally check for any shared font keys that were remapped from the namespace. 1922 let shared_keys = self.resources.fonts.font_keys.clear_namespace(namespace); 1923 if !shared_keys.is_empty() { 1924 self.glyph_rasterizer.delete_fonts(&shared_keys); 1925 self.resources.fonts.templates.delete_fonts(&shared_keys); 1926 self.cached_glyphs.delete_fonts(&shared_keys); 1927 if let Some(handler) = &mut self.blob_image_handler { 1928 for &key in &shared_keys { 1929 handler.delete_font(key); 1930 } 1931 } 1932 } 1933 } 1934 1935 /// Reports the CPU heap usage of this ResourceCache. 1936 /// 1937 /// NB: It would be much better to use the derive(MallocSizeOf) machinery 1938 /// here, but the Arcs complicate things. The two ways to handle that would 1939 /// be to either (a) Implement MallocSizeOf manually for the things that own 1940 /// them and manually avoid double-counting, or (b) Use the "seen this pointer 1941 /// yet" machinery from the proper malloc_size_of crate. We can do this if/when 1942 /// more accurate memory reporting on these resources becomes a priority. 1943 pub fn report_memory(&self, op: VoidPtrToSizeFn) -> MemoryReport { 1944 let mut report = MemoryReport::default(); 1945 1946 let mut seen_fonts = std::collections::HashSet::new(); 1947 // Measure fonts. We only need the templates here, because the instances 1948 // don't have big buffers. 1949 for (_, font) in self.resources.fonts.templates.lock().iter() { 1950 if let FontTemplate::Raw(ref raw, _) = font { 1951 report.fonts += unsafe { op(raw.as_ptr() as *const c_void) }; 1952 seen_fonts.insert(raw.as_ptr()); 1953 } 1954 } 1955 1956 for font in self.resources.weak_fonts.iter() { 1957 if !seen_fonts.contains(&font.as_ptr()) { 1958 report.weak_fonts += unsafe { op(font.as_ptr() as *const c_void) }; 1959 } 1960 } 1961 1962 // Measure images. 1963 for (_, image) in self.resources.image_templates.images.iter() { 1964 report.images += match image.data { 1965 CachedImageData::Raw(ref v) => unsafe { op(v.as_ptr() as *const c_void) }, 1966 CachedImageData::Blob 1967 | CachedImageData::External(..) 1968 | CachedImageData::Snapshot => 0, 1969 } 1970 } 1971 1972 // Mesure rasterized blobs. 1973 // TODO(gw): Temporarily disabled while we roll back a crash. We can re-enable 1974 // these when that crash is fixed. 1975 /* 1976 for (_, image) in self.rasterized_blob_images.iter() { 1977 let mut accumulate = |b: &RasterizedBlobImage| { 1978 report.rasterized_blobs += unsafe { op(b.data.as_ptr() as *const c_void) }; 1979 }; 1980 match image { 1981 RasterizedBlob::Tiled(map) => map.values().for_each(&mut accumulate), 1982 RasterizedBlob::NonTiled(vec) => vec.iter().for_each(&mut accumulate), 1983 }; 1984 } 1985 */ 1986 1987 report 1988 } 1989 1990 /// Properly deletes all images matching the predicate. 1991 fn clear_images<F: Fn(&ImageKey) -> bool>(&mut self, f: F) { 1992 let keys = self.resources.image_templates.images.keys().filter(|k| f(*k)) 1993 .cloned().collect::<SmallVec<[ImageKey; 16]>>(); 1994 1995 for key in keys { 1996 self.delete_image_template(key); 1997 } 1998 1999 #[cfg(feature="leak_checks")] 2000 let check_leaks = true; 2001 #[cfg(not(feature="leak_checks"))] 2002 let check_leaks = false; 2003 2004 if check_leaks { 2005 let blob_f = |key: &BlobImageKey| { f(&key.as_image()) }; 2006 assert!(!self.resources.image_templates.images.keys().any(&f)); 2007 assert!(!self.cached_images.resources.keys().any(&f)); 2008 assert!(!self.rasterized_blob_images.keys().any(&blob_f)); 2009 } 2010 } 2011 2012 /// Get a render target from the pool, or allocate a new one if none are 2013 /// currently available that match the requested parameters. 2014 pub fn get_or_create_render_target_from_pool( 2015 &mut self, 2016 size: DeviceIntSize, 2017 format: ImageFormat, 2018 ) -> CacheTextureId { 2019 for target in &mut self.render_target_pool { 2020 if target.size == size && 2021 target.format == format && 2022 !target.is_active { 2023 // Found a target that's not currently in use which matches. Update 2024 // the last_frame_used for GC purposes. 2025 target.is_active = true; 2026 target.last_frame_used = self.current_frame_id; 2027 return target.texture_id; 2028 } 2029 } 2030 2031 // Need to create a new render target and add it to the pool 2032 2033 let texture_id = self.texture_cache.alloc_render_target( 2034 size, 2035 format, 2036 ); 2037 2038 self.render_target_pool.push(RenderTarget { 2039 size, 2040 format, 2041 texture_id, 2042 is_active: true, 2043 last_frame_used: self.current_frame_id, 2044 }); 2045 2046 texture_id 2047 } 2048 2049 /// Return a render target to the pool. 2050 pub fn return_render_target_to_pool( 2051 &mut self, 2052 id: CacheTextureId, 2053 ) { 2054 let target = self.render_target_pool 2055 .iter_mut() 2056 .find(|t| t.texture_id == id) 2057 .expect("bug: invalid render target id"); 2058 2059 assert!(target.is_active); 2060 target.is_active = false; 2061 } 2062 2063 /// Clear all current render targets (e.g. on memory pressure) 2064 fn clear_render_target_pool( 2065 &mut self, 2066 ) { 2067 for target in self.render_target_pool.drain(..) { 2068 debug_assert!(!target.is_active); 2069 self.texture_cache.free_render_target(target.texture_id); 2070 } 2071 } 2072 2073 /// Garbage collect and remove old render targets from the pool that haven't 2074 /// been used for some time. 2075 fn gc_render_targets( 2076 &mut self, 2077 total_bytes_threshold: usize, 2078 total_bytes_red_line_threshold: usize, 2079 frames_threshold: u64, 2080 ) { 2081 // Get the total GPU memory size used by the current render target pool 2082 let mut rt_pool_size_in_bytes: usize = self.render_target_pool 2083 .iter() 2084 .map(|t| t.size_in_bytes()) 2085 .sum(); 2086 2087 // If the total size of the pool is less than the threshold, don't bother 2088 // trying to GC any targets 2089 if rt_pool_size_in_bytes <= total_bytes_threshold { 2090 return; 2091 } 2092 2093 // Sort the current pool by age, so that we remove oldest textures first 2094 self.render_target_pool.sort_by_key(|t| t.last_frame_used); 2095 2096 // We can't just use retain() because `RenderTarget` requires manual cleanup. 2097 let mut retained_targets = SmallVec::<[RenderTarget; 8]>::new(); 2098 2099 for target in self.render_target_pool.drain(..) { 2100 assert!(!target.is_active); 2101 2102 // Drop oldest textures until we are under the allowed size threshold. 2103 // However, if it's been used in very recently, it is always kept around, 2104 // which ensures we don't thrash texture allocations on pages that do 2105 // require a very large render target pool and are regularly changing. 2106 let above_red_line = rt_pool_size_in_bytes > total_bytes_red_line_threshold; 2107 let above_threshold = rt_pool_size_in_bytes > total_bytes_threshold; 2108 let used_recently = target.used_recently(self.current_frame_id, frames_threshold); 2109 let used_this_frame = target.last_frame_used == self.current_frame_id; 2110 2111 if !used_this_frame && (above_red_line || (above_threshold && !used_recently)) { 2112 rt_pool_size_in_bytes -= target.size_in_bytes(); 2113 self.texture_cache.free_render_target(target.texture_id); 2114 } else { 2115 retained_targets.push(target); 2116 } 2117 } 2118 2119 self.render_target_pool.extend(retained_targets); 2120 } 2121 2122 #[cfg(test)] 2123 pub fn validate_surfaces( 2124 &self, 2125 expected_surfaces: &[(i32, i32, ImageFormat)], 2126 ) { 2127 assert_eq!(expected_surfaces.len(), self.render_target_pool.len()); 2128 2129 for (expected, surface) in expected_surfaces.iter().zip(self.render_target_pool.iter()) { 2130 assert_eq!(DeviceIntSize::new(expected.0, expected.1), surface.size); 2131 assert_eq!(expected.2, surface.format); 2132 } 2133 } 2134 } 2135 2136 impl Drop for ResourceCache { 2137 fn drop(&mut self) { 2138 self.clear_images(|_| true); 2139 } 2140 } 2141 2142 #[cfg(any(feature = "capture", feature = "replay"))] 2143 #[cfg_attr(feature = "capture", derive(Serialize))] 2144 #[cfg_attr(feature = "replay", derive(Deserialize))] 2145 struct PlainFontTemplate { 2146 data: String, 2147 index: u32, 2148 } 2149 2150 #[cfg(any(feature = "capture", feature = "replay"))] 2151 #[cfg_attr(feature = "capture", derive(Serialize))] 2152 #[cfg_attr(feature = "replay", derive(Deserialize))] 2153 struct PlainImageTemplate { 2154 data: String, 2155 descriptor: ImageDescriptor, 2156 tiling: Option<TileSize>, 2157 generation: ImageGeneration, 2158 } 2159 2160 #[cfg(any(feature = "capture", feature = "replay"))] 2161 #[cfg_attr(feature = "capture", derive(Serialize))] 2162 #[cfg_attr(feature = "replay", derive(Deserialize))] 2163 pub struct PlainResources { 2164 font_templates: FastHashMap<FontKey, PlainFontTemplate>, 2165 font_instances: Vec<BaseFontInstance>, 2166 image_templates: FastHashMap<ImageKey, PlainImageTemplate>, 2167 } 2168 2169 #[cfg(feature = "capture")] 2170 #[derive(Serialize)] 2171 pub struct PlainCacheRef<'a> { 2172 current_frame_id: FrameId, 2173 glyphs: &'a GlyphCache, 2174 glyph_dimensions: &'a GlyphDimensionsCache, 2175 images: &'a ImageCache, 2176 render_tasks: &'a RenderTaskCache, 2177 textures: &'a TextureCache, 2178 picture_textures: &'a PictureTextures, 2179 } 2180 2181 #[cfg(feature = "replay")] 2182 #[derive(Deserialize)] 2183 pub struct PlainCacheOwn { 2184 current_frame_id: FrameId, 2185 glyphs: GlyphCache, 2186 glyph_dimensions: GlyphDimensionsCache, 2187 images: ImageCache, 2188 render_tasks: RenderTaskCache, 2189 textures: TextureCache, 2190 picture_textures: PictureTextures, 2191 } 2192 2193 #[cfg(feature = "replay")] 2194 const NATIVE_FONT: &'static [u8] = include_bytes!("../res/Proggy.ttf"); 2195 2196 // This currently only casts the unit but will soon apply an offset 2197 fn to_image_dirty_rect(blob_dirty_rect: &BlobDirtyRect) -> ImageDirtyRect { 2198 match *blob_dirty_rect { 2199 DirtyRect::Partial(rect) => DirtyRect::Partial(rect.cast_unit()), 2200 DirtyRect::All => DirtyRect::All, 2201 } 2202 } 2203 2204 impl ResourceCache { 2205 #[cfg(feature = "capture")] 2206 pub fn save_capture( 2207 &mut self, root: &PathBuf 2208 ) -> (PlainResources, Vec<ExternalCaptureImage>) { 2209 use std::fs; 2210 use std::io::Write; 2211 2212 info!("saving resource cache"); 2213 let res = &self.resources; 2214 let path_fonts = root.join("fonts"); 2215 if !path_fonts.is_dir() { 2216 fs::create_dir(&path_fonts).unwrap(); 2217 } 2218 let path_images = root.join("images"); 2219 if !path_images.is_dir() { 2220 fs::create_dir(&path_images).unwrap(); 2221 } 2222 let path_blobs = root.join("blobs"); 2223 if !path_blobs.is_dir() { 2224 fs::create_dir(&path_blobs).unwrap(); 2225 } 2226 let path_externals = root.join("externals"); 2227 if !path_externals.is_dir() { 2228 fs::create_dir(&path_externals).unwrap(); 2229 } 2230 2231 info!("\tfont templates"); 2232 let mut font_paths = FastHashMap::default(); 2233 for template in res.fonts.templates.lock().values() { 2234 let data: &[u8] = match *template { 2235 FontTemplate::Raw(ref arc, _) => arc, 2236 FontTemplate::Native(_) => continue, 2237 }; 2238 let font_id = res.fonts.templates.len() + 1; 2239 let entry = match font_paths.entry(data.as_ptr()) { 2240 Entry::Occupied(_) => continue, 2241 Entry::Vacant(e) => e, 2242 }; 2243 let file_name = format!("{}.raw", font_id); 2244 let short_path = format!("fonts/{}", file_name); 2245 fs::File::create(path_fonts.join(file_name)) 2246 .expect(&format!("Unable to create {}", short_path)) 2247 .write_all(data) 2248 .unwrap(); 2249 entry.insert(short_path); 2250 } 2251 2252 info!("\timage templates"); 2253 let mut image_paths = FastHashMap::default(); 2254 let mut other_paths = FastHashMap::default(); 2255 let mut num_blobs = 0; 2256 let mut external_images = Vec::new(); 2257 for (&key, template) in res.image_templates.images.iter() { 2258 let desc = &template.descriptor; 2259 match template.data { 2260 CachedImageData::Raw(ref arc) => { 2261 let image_id = image_paths.len() + 1; 2262 let entry = match image_paths.entry(arc.as_ptr()) { 2263 Entry::Occupied(_) => continue, 2264 Entry::Vacant(e) => e, 2265 }; 2266 2267 #[cfg(feature = "png")] 2268 CaptureConfig::save_png( 2269 root.join(format!("images/{}.png", image_id)), 2270 desc.size, 2271 desc.format, 2272 desc.stride, 2273 &arc, 2274 ); 2275 let file_name = format!("{}.raw", image_id); 2276 let short_path = format!("images/{}", file_name); 2277 fs::File::create(path_images.join(file_name)) 2278 .expect(&format!("Unable to create {}", short_path)) 2279 .write_all(&*arc) 2280 .unwrap(); 2281 entry.insert(short_path); 2282 } 2283 CachedImageData::Blob => { 2284 warn!("Tiled blob images aren't supported yet"); 2285 let result = RasterizedBlobImage { 2286 rasterized_rect: desc.size.into(), 2287 data: Arc::new(vec![0; desc.compute_total_size() as usize]) 2288 }; 2289 2290 assert_eq!(result.rasterized_rect.size(), desc.size); 2291 assert_eq!(result.data.len(), desc.compute_total_size() as usize); 2292 2293 num_blobs += 1; 2294 #[cfg(feature = "png")] 2295 CaptureConfig::save_png( 2296 root.join(format!("blobs/{}.png", num_blobs)), 2297 desc.size, 2298 desc.format, 2299 desc.stride, 2300 &result.data, 2301 ); 2302 let file_name = format!("{}.raw", num_blobs); 2303 let short_path = format!("blobs/{}", file_name); 2304 let full_path = path_blobs.clone().join(&file_name); 2305 fs::File::create(full_path) 2306 .expect(&format!("Unable to create {}", short_path)) 2307 .write_all(&result.data) 2308 .unwrap(); 2309 other_paths.insert(key, short_path); 2310 } 2311 CachedImageData::Snapshot => { 2312 let short_path = format!("snapshots/{}", external_images.len() + 1); 2313 other_paths.insert(key, short_path.clone()); 2314 } 2315 CachedImageData::External(ref ext) => { 2316 let short_path = format!("externals/{}", external_images.len() + 1); 2317 other_paths.insert(key, short_path.clone()); 2318 external_images.push(ExternalCaptureImage { 2319 short_path, 2320 descriptor: desc.clone(), 2321 external: ext.clone(), 2322 }); 2323 } 2324 } 2325 } 2326 2327 let mut font_templates = FastHashMap::default(); 2328 let mut font_remap = FastHashMap::default(); 2329 // Generate a map from duplicate font keys to their template. 2330 for key in res.fonts.font_keys.keys() { 2331 let shared_key = res.fonts.font_keys.map_key(&key); 2332 let template = match res.fonts.templates.get_font(&shared_key) { 2333 Some(template) => template, 2334 None => { 2335 debug!("Failed serializing font template {:?}", key); 2336 continue; 2337 } 2338 }; 2339 let plain_font = match template { 2340 FontTemplate::Raw(arc, index) => { 2341 PlainFontTemplate { 2342 data: font_paths[&arc.as_ptr()].clone(), 2343 index, 2344 } 2345 } 2346 #[cfg(not(any(target_os = "macos", target_os = "ios")))] 2347 FontTemplate::Native(native) => { 2348 PlainFontTemplate { 2349 data: native.path.to_string_lossy().to_string(), 2350 index: native.index, 2351 } 2352 } 2353 #[cfg(any(target_os = "macos", target_os = "ios"))] 2354 FontTemplate::Native(native) => { 2355 PlainFontTemplate { 2356 data: native.name, 2357 index: 0, 2358 } 2359 } 2360 }; 2361 font_templates.insert(key, plain_font); 2362 // Generate a reverse map from a shared key to a representive key. 2363 font_remap.insert(shared_key, key); 2364 } 2365 let mut font_instances = Vec::new(); 2366 // Build a list of duplicate instance keys. 2367 for instance_key in res.fonts.instance_keys.keys() { 2368 let shared_key = res.fonts.instance_keys.map_key(&instance_key); 2369 let instance = match res.fonts.instances.get_font_instance(shared_key) { 2370 Some(instance) => instance, 2371 None => { 2372 debug!("Failed serializing font instance {:?}", instance_key); 2373 continue; 2374 } 2375 }; 2376 // Target the instance towards a representive duplicate font key. The font key will be 2377 // de-duplicated on load to an appropriate shared key. 2378 font_instances.push(BaseFontInstance { 2379 font_key: font_remap.get(&instance.font_key).cloned().unwrap_or(instance.font_key), 2380 instance_key, 2381 ..(*instance).clone() 2382 }); 2383 } 2384 let resources = PlainResources { 2385 font_templates, 2386 font_instances, 2387 image_templates: res.image_templates.images 2388 .iter() 2389 .map(|(key, template)| { 2390 (*key, PlainImageTemplate { 2391 data: match template.data { 2392 CachedImageData::Raw(ref arc) => image_paths[&arc.as_ptr()].clone(), 2393 _ => other_paths[key].clone(), 2394 }, 2395 descriptor: template.descriptor.clone(), 2396 tiling: template.tiling, 2397 generation: template.generation, 2398 }) 2399 }) 2400 .collect(), 2401 }; 2402 2403 (resources, external_images) 2404 } 2405 2406 #[cfg(feature = "capture")] 2407 pub fn save_caches(&self, _root: &PathBuf) -> PlainCacheRef { 2408 PlainCacheRef { 2409 current_frame_id: self.current_frame_id, 2410 glyphs: &self.cached_glyphs, 2411 glyph_dimensions: &self.cached_glyph_dimensions, 2412 images: &self.cached_images, 2413 render_tasks: &self.cached_render_tasks, 2414 textures: &self.texture_cache, 2415 picture_textures: &self.picture_textures, 2416 } 2417 } 2418 2419 #[cfg(feature = "replay")] 2420 pub fn load_capture( 2421 &mut self, 2422 resources: PlainResources, 2423 caches: Option<PlainCacheOwn>, 2424 config: &CaptureConfig, 2425 ) -> Vec<PlainExternalImage> { 2426 use std::{fs, path::Path}; 2427 use crate::texture_cache::TextureCacheConfig; 2428 2429 info!("loading resource cache"); 2430 //TODO: instead of filling the local path to Arc<data> map as we process 2431 // each of the resource types, we could go through all of the local paths 2432 // and fill out the map as the first step. 2433 let mut raw_map = FastHashMap::<String, Arc<Vec<u8>>>::default(); 2434 2435 self.clear(ClearCache::all()); 2436 self.clear_images(|_| true); 2437 2438 match caches { 2439 Some(cached) => { 2440 self.current_frame_id = cached.current_frame_id; 2441 self.cached_glyphs = cached.glyphs; 2442 self.cached_glyph_dimensions = cached.glyph_dimensions; 2443 self.cached_images = cached.images; 2444 self.cached_render_tasks = cached.render_tasks; 2445 self.texture_cache = cached.textures; 2446 self.picture_textures = cached.picture_textures; 2447 } 2448 None => { 2449 self.current_frame_id = FrameId::INVALID; 2450 self.texture_cache = TextureCache::new( 2451 self.texture_cache.max_texture_size(), 2452 self.texture_cache.tiling_threshold(), 2453 self.texture_cache.color_formats(), 2454 self.texture_cache.swizzle_settings(), 2455 &TextureCacheConfig::DEFAULT, 2456 ); 2457 self.picture_textures = PictureTextures::new( 2458 self.picture_textures.default_tile_size(), 2459 self.picture_textures.filter(), 2460 ); 2461 } 2462 } 2463 2464 self.glyph_rasterizer.reset(); 2465 let res = &mut self.resources; 2466 res.fonts.templates.clear(); 2467 res.fonts.instances.clear(); 2468 res.image_templates.images.clear(); 2469 2470 info!("\tfont templates..."); 2471 let root = config.resource_root(); 2472 let native_font_replacement = Arc::new(NATIVE_FONT.to_vec()); 2473 for (key, plain_template) in resources.font_templates { 2474 let arc = match raw_map.entry(plain_template.data) { 2475 Entry::Occupied(e) => { 2476 e.get().clone() 2477 } 2478 Entry::Vacant(e) => { 2479 let file_path = if Path::new(e.key()).is_absolute() { 2480 PathBuf::from(e.key()) 2481 } else { 2482 root.join(e.key()) 2483 }; 2484 let arc = match fs::read(file_path) { 2485 Ok(buffer) => Arc::new(buffer), 2486 Err(err) => { 2487 error!("Unable to open font template {:?}: {:?}", e.key(), err); 2488 Arc::clone(&native_font_replacement) 2489 } 2490 }; 2491 e.insert(arc).clone() 2492 } 2493 }; 2494 2495 let template = FontTemplate::Raw(arc, plain_template.index); 2496 // Only add the template if this is the first time it has been seen. 2497 if let Some(shared_key) = res.fonts.font_keys.add_key(&key, &template) { 2498 self.glyph_rasterizer.add_font(shared_key, template.clone()); 2499 res.fonts.templates.add_font(shared_key, template); 2500 } 2501 } 2502 2503 info!("\tfont instances..."); 2504 for instance in resources.font_instances { 2505 // Target the instance to a shared font key. 2506 let base = BaseFontInstance { 2507 font_key: res.fonts.font_keys.map_key(&instance.font_key), 2508 ..instance 2509 }; 2510 if let Some(shared_instance) = res.fonts.instance_keys.add_key(base) { 2511 res.fonts.instances.add_font_instance(shared_instance); 2512 } 2513 } 2514 2515 info!("\timage templates..."); 2516 let mut external_images = Vec::new(); 2517 for (key, template) in resources.image_templates { 2518 let data = if template.data.starts_with("snapshots/") { 2519 // TODO(nical): If a snapshot was captured in a previous frame, 2520 // we have to serialize/deserialize the image itself. 2521 CachedImageData::Snapshot 2522 } else { 2523 match config.deserialize_for_resource::<PlainExternalImage, _>(&template.data) { 2524 Some(plain) => { 2525 let ext_data = plain.external; 2526 external_images.push(plain); 2527 CachedImageData::External(ext_data) 2528 } 2529 None => { 2530 let arc = match raw_map.entry(template.data) { 2531 Entry::Occupied(e) => e.get().clone(), 2532 Entry::Vacant(e) => { 2533 match fs::read(root.join(e.key())) { 2534 Ok(buffer) => { 2535 e.insert(Arc::new(buffer)).clone() 2536 } 2537 Err(err) => { 2538 log::warn!("Unable to open {}: {err:?}", e.key()); 2539 continue; 2540 } 2541 } 2542 } 2543 }; 2544 CachedImageData::Raw(arc) 2545 } 2546 } 2547 }; 2548 2549 res.image_templates.images.insert(key, ImageResource { 2550 data, 2551 descriptor: template.descriptor, 2552 tiling: template.tiling, 2553 visible_rect: template.descriptor.size.into(), 2554 adjustment: AdjustedImageSource::new(), // TODO(nical) 2555 generation: template.generation, 2556 }); 2557 } 2558 2559 external_images 2560 } 2561 2562 #[cfg(feature = "capture")] 2563 pub fn save_capture_sequence(&mut self, config: &mut CaptureConfig) -> Vec<ExternalCaptureImage> { 2564 if self.capture_dirty { 2565 self.capture_dirty = false; 2566 config.prepare_resource(); 2567 let (resources, deferred) = self.save_capture(&config.resource_root()); 2568 config.serialize_for_resource(&resources, "plain-resources.ron"); 2569 deferred 2570 } else { 2571 Vec::new() 2572 } 2573 } 2574 }