Skip to main content

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