webrender/
resource_cache.rs

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