1use api::{DirtyRect, ExternalImageType, ImageFormat, ImageBufferKind};
6use api::{DebugFlags, ImageDescriptor};
7use api::units::*;
8#[cfg(test)]
9use api::{DocumentId, IdNamespace};
10use crate::device::{TextureFilter, TextureFormatPair};
11use crate::freelist::{FreeList, FreeListHandle, WeakFreeListHandle};
12use crate::gpu_cache::{GpuCache, GpuCacheHandle};
13use crate::gpu_types::{ImageSource, UvRectKind};
14use crate::internal_types::{
15    CacheTextureId, Swizzle, SwizzleSettings, FrameStamp, FrameId,
16    TextureUpdateList, TextureUpdateSource, TextureSource,
17    TextureCacheAllocInfo, TextureCacheUpdate, TextureCacheCategory,
18};
19use crate::lru_cache::LRUCache;
20use crate::profiler::{self, TransactionProfile};
21use crate::resource_cache::{CacheItem, CachedImageData};
22use crate::texture_pack::{
23    AllocatorList, AllocId, AtlasAllocatorList, ShelfAllocator, ShelfAllocatorOptions,
24};
25use std::cell::Cell;
26use std::mem;
27use std::rc::Rc;
28use euclid::size2;
29use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
30
31#[derive(Copy, Clone, Debug, PartialEq, Eq)]
36#[cfg_attr(feature = "capture", derive(Serialize))]
37#[cfg_attr(feature = "replay", derive(Deserialize))]
38pub enum TargetShader {
39    Default,
40    Text,
41}
42
43pub const TEXTURE_REGION_DIMENSIONS: i32 = 512;
45
46#[derive(Clone, Debug)]
49#[cfg_attr(feature = "capture", derive(Serialize))]
50#[cfg_attr(feature = "replay", derive(Deserialize))]
51pub enum EntryDetails {
52    Standalone {
53        size_in_bytes: usize,
55    },
56    Cache {
57        origin: DeviceIntPoint,
59        alloc_id: AllocId,
61        allocated_size_in_bytes: usize,
63    },
64}
65
66impl EntryDetails {
67    fn describe(&self) -> DeviceIntPoint {
68        match *self {
69            EntryDetails::Standalone { .. }  => DeviceIntPoint::zero(),
70            EntryDetails::Cache { origin, .. } => origin,
71        }
72    }
73}
74
75#[derive(Debug, PartialEq)]
76#[cfg_attr(feature = "capture", derive(Serialize))]
77#[cfg_attr(feature = "replay", derive(Deserialize))]
78pub enum AutoCacheEntryMarker {}
79
80#[derive(Debug, PartialEq)]
81#[cfg_attr(feature = "capture", derive(Serialize))]
82#[cfg_attr(feature = "replay", derive(Deserialize))]
83pub enum ManualCacheEntryMarker {}
84
85#[derive(Debug)]
89#[cfg_attr(feature = "capture", derive(Serialize))]
90#[cfg_attr(feature = "replay", derive(Deserialize))]
91pub struct CacheEntry {
92    pub size: DeviceIntSize,
96    pub details: EntryDetails,
98    pub user_data: [f32; 4],
100    pub last_access: FrameStamp,
105    pub uv_rect_handle: GpuCacheHandle,
107    pub input_format: ImageFormat,
109    pub filter: TextureFilter,
110    pub swizzle: Swizzle,
111    pub texture_id: CacheTextureId,
113    pub eviction_notice: Option<EvictionNotice>,
115    pub uv_rect_kind: UvRectKind,
117
118    pub shader: TargetShader,
119}
120
121malloc_size_of::malloc_size_of_is_0!(
122    CacheEntry,
123    AutoCacheEntryMarker, ManualCacheEntryMarker
124);
125
126impl CacheEntry {
127    fn new_standalone(
129        texture_id: CacheTextureId,
130        last_access: FrameStamp,
131        params: &CacheAllocParams,
132        swizzle: Swizzle,
133        size_in_bytes: usize,
134    ) -> Self {
135        CacheEntry {
136            size: params.descriptor.size,
137            user_data: params.user_data,
138            last_access,
139            details: EntryDetails::Standalone {
140                size_in_bytes,
141            },
142            texture_id,
143            input_format: params.descriptor.format,
144            filter: params.filter,
145            swizzle,
146            uv_rect_handle: GpuCacheHandle::new(),
147            eviction_notice: None,
148            uv_rect_kind: params.uv_rect_kind,
149            shader: TargetShader::Default,
150        }
151    }
152
153    fn update_gpu_cache(&mut self, gpu_cache: &mut GpuCache) {
158        if let Some(mut request) = gpu_cache.request(&mut self.uv_rect_handle) {
159            let origin = self.details.describe();
160            let image_source = ImageSource {
161                p0: origin.to_f32(),
162                p1: (origin + self.size).to_f32(),
163                user_data: self.user_data,
164                uv_rect_kind: self.uv_rect_kind,
165            };
166            image_source.write_gpu_blocks(&mut request);
167        }
168    }
169
170    fn evict(&self) {
171        if let Some(eviction_notice) = self.eviction_notice.as_ref() {
172            eviction_notice.notify();
173        }
174    }
175
176    fn alternative_input_format(&self) -> ImageFormat {
177        match self.input_format {
178            ImageFormat::RGBA8 => ImageFormat::BGRA8,
179            ImageFormat::BGRA8 => ImageFormat::RGBA8,
180            other => other,
181        }
182    }
183}
184
185
186#[derive(MallocSizeOf,Clone,PartialEq,Debug)]
194#[cfg_attr(feature = "capture", derive(Serialize))]
195#[cfg_attr(feature = "replay", derive(Deserialize))]
196pub enum TextureCacheHandle {
197    Empty,
199
200    Auto(WeakFreeListHandle<AutoCacheEntryMarker>),
202
203    Manual(WeakFreeListHandle<ManualCacheEntryMarker>)
205}
206
207impl TextureCacheHandle {
208    pub fn invalid() -> Self {
209        TextureCacheHandle::Empty
210    }
211}
212
213#[derive(Copy, Clone, Debug, PartialEq, Eq)]
215#[cfg_attr(feature = "capture", derive(Serialize))]
216#[cfg_attr(feature = "replay", derive(Deserialize))]
217pub enum Eviction {
218    Auto,
221    Manual,
224}
225
226#[derive(Clone, Debug, Default)]
233#[cfg_attr(feature = "capture", derive(Serialize))]
234#[cfg_attr(feature = "replay", derive(Deserialize))]
235pub struct EvictionNotice {
236    evicted: Rc<Cell<bool>>,
237}
238
239impl EvictionNotice {
240    fn notify(&self) {
241        self.evicted.set(true);
242    }
243
244    pub fn check(&self) -> bool {
245        if self.evicted.get() {
246            self.evicted.set(false);
247            true
248        } else {
249            false
250        }
251    }
252}
253
254#[derive(Copy, Clone, Debug, PartialEq, Eq)]
261#[repr(u8)]
262#[cfg_attr(feature = "capture", derive(Serialize))]
263#[cfg_attr(feature = "replay", derive(Deserialize))]
264enum BudgetType {
265    SharedColor8Linear,
266    SharedColor8Nearest,
267    SharedColor8Glyphs,
268    SharedAlpha8,
269    SharedAlpha8Glyphs,
270    SharedAlpha16,
271    Standalone,
272}
273
274impl BudgetType {
275    pub const COUNT: usize = 7;
276
277    pub const VALUES: [BudgetType; BudgetType::COUNT] = [
278        BudgetType::SharedColor8Linear,
279        BudgetType::SharedColor8Nearest,
280        BudgetType::SharedColor8Glyphs,
281        BudgetType::SharedAlpha8,
282        BudgetType::SharedAlpha8Glyphs,
283        BudgetType::SharedAlpha16,
284        BudgetType::Standalone,
285    ];
286
287    pub const PRESSURE_COUNTERS: [usize; BudgetType::COUNT] = [
288        profiler::ATLAS_COLOR8_LINEAR_PRESSURE,
289        profiler::ATLAS_COLOR8_NEAREST_PRESSURE,
290        profiler::ATLAS_COLOR8_GLYPHS_PRESSURE,
291        profiler::ATLAS_ALPHA8_PRESSURE,
292        profiler::ATLAS_ALPHA8_GLYPHS_PRESSURE,
293        profiler::ATLAS_ALPHA16_PRESSURE,
294        profiler::ATLAS_STANDALONE_PRESSURE,
295    ];
296
297    pub fn iter() -> impl Iterator<Item = BudgetType> {
298        BudgetType::VALUES.iter().cloned()
299    }
300}
301
302#[cfg_attr(feature = "capture", derive(Serialize))]
305#[cfg_attr(feature = "replay", derive(Deserialize))]
306struct SharedTextures {
307    color8_nearest: AllocatorList<ShelfAllocator, TextureParameters>,
308    alpha8_linear: AllocatorList<ShelfAllocator, TextureParameters>,
309    alpha8_glyphs: AllocatorList<ShelfAllocator, TextureParameters>,
310    alpha16_linear: AllocatorList<ShelfAllocator, TextureParameters>,
311    color8_linear: AllocatorList<ShelfAllocator, TextureParameters>,
312    color8_glyphs: AllocatorList<ShelfAllocator, TextureParameters>,
313    bytes_per_texture_of_type: [i32 ; BudgetType::COUNT],
314    next_compaction_idx: usize,
315}
316
317impl SharedTextures {
318    fn new(color_formats: TextureFormatPair<ImageFormat>, config: &TextureCacheConfig) -> Self {
320        let mut bytes_per_texture_of_type = [0 ; BudgetType::COUNT];
321
322        let alpha8_linear = AllocatorList::new(
331            config.alpha8_texture_size,
332            ShelfAllocatorOptions {
333                num_columns: 1,
334                alignment: size2(8, 8),
335                .. ShelfAllocatorOptions::default()
336            },
337            TextureParameters {
338                formats: TextureFormatPair::from(ImageFormat::R8),
339                filter: TextureFilter::Linear,
340            },
341        );
342        bytes_per_texture_of_type[BudgetType::SharedAlpha8 as usize] =
343            config.alpha8_texture_size * config.alpha8_texture_size;
344
345        let alpha8_glyphs = AllocatorList::new(
347            config.alpha8_glyph_texture_size,
348            ShelfAllocatorOptions {
349                num_columns: if config.alpha8_glyph_texture_size >= 1024 { 2 } else { 1 },
350                alignment: size2(4, 8),
351                .. ShelfAllocatorOptions::default()
352            },
353            TextureParameters {
354                formats: TextureFormatPair::from(ImageFormat::R8),
355                filter: TextureFilter::Linear,
356            },
357        );
358        bytes_per_texture_of_type[BudgetType::SharedAlpha8Glyphs as usize] =
359            config.alpha8_glyph_texture_size * config.alpha8_glyph_texture_size;
360
361        let alpha16_linear = AllocatorList::new(
364            config.alpha16_texture_size,
365            ShelfAllocatorOptions {
366                num_columns: if config.alpha16_texture_size >= 1024 { 2 } else { 1 },
367                alignment: size2(8, 8),
368                .. ShelfAllocatorOptions::default()
369            },
370            TextureParameters {
371                formats: TextureFormatPair::from(ImageFormat::R16),
372                filter: TextureFilter::Linear,
373            },
374        );
375        bytes_per_texture_of_type[BudgetType::SharedAlpha16 as usize] =
376            ImageFormat::R16.bytes_per_pixel() *
377            config.alpha16_texture_size * config.alpha16_texture_size;
378
379        let color8_linear = AllocatorList::new(
381            config.color8_linear_texture_size,
382            ShelfAllocatorOptions {
383                num_columns: if config.color8_linear_texture_size >= 1024 { 2 } else { 1 },
384                alignment: size2(16, 16),
385                .. ShelfAllocatorOptions::default()
386            },
387            TextureParameters {
388                formats: color_formats.clone(),
389                filter: TextureFilter::Linear,
390            },
391        );
392        bytes_per_texture_of_type[BudgetType::SharedColor8Linear as usize] =
393            color_formats.internal.bytes_per_pixel() *
394            config.color8_linear_texture_size * config.color8_linear_texture_size;
395
396        let color8_glyphs = AllocatorList::new(
398            config.color8_glyph_texture_size,
399            ShelfAllocatorOptions {
400                num_columns: if config.color8_glyph_texture_size >= 1024 { 2 } else { 1 },
401                alignment: size2(4, 8),
402                .. ShelfAllocatorOptions::default()
403            },
404            TextureParameters {
405                formats: color_formats.clone(),
406                filter: TextureFilter::Linear,
407            },
408        );
409        bytes_per_texture_of_type[BudgetType::SharedColor8Glyphs as usize] =
410            color_formats.internal.bytes_per_pixel() *
411            config.color8_glyph_texture_size * config.color8_glyph_texture_size;
412
413        let color8_nearest = AllocatorList::new(
417            config.color8_nearest_texture_size,
418            ShelfAllocatorOptions::default(),
419            TextureParameters {
420                formats: color_formats.clone(),
421                filter: TextureFilter::Nearest,
422            }
423        );
424        bytes_per_texture_of_type[BudgetType::SharedColor8Nearest as usize] =
425            color_formats.internal.bytes_per_pixel() *
426            config.color8_nearest_texture_size * config.color8_nearest_texture_size;
427
428        Self {
429            alpha8_linear,
430            alpha8_glyphs,
431            alpha16_linear,
432            color8_linear,
433            color8_glyphs,
434            color8_nearest,
435            bytes_per_texture_of_type,
436            next_compaction_idx: 0,
437        }
438    }
439
440    fn clear(&mut self, updates: &mut TextureUpdateList) {
442        let texture_dealloc_cb = &mut |texture_id| {
443            updates.push_free(texture_id);
444        };
445
446        self.alpha8_linear.clear(texture_dealloc_cb);
447        self.alpha8_glyphs.clear(texture_dealloc_cb);
448        self.alpha16_linear.clear(texture_dealloc_cb);
449        self.color8_linear.clear(texture_dealloc_cb);
450        self.color8_nearest.clear(texture_dealloc_cb);
451        self.color8_glyphs.clear(texture_dealloc_cb);
452    }
453
454    fn select(
456        &mut self, external_format: ImageFormat, filter: TextureFilter, shader: TargetShader,
457    ) -> (&mut dyn AtlasAllocatorList<TextureParameters>, BudgetType) {
458        match external_format {
459            ImageFormat::R8 => {
460                assert_eq!(filter, TextureFilter::Linear);
461                match shader {
462                    TargetShader::Text => {
463                        (&mut self.alpha8_glyphs, BudgetType::SharedAlpha8Glyphs)
464                    },
465                    _ => (&mut self.alpha8_linear, BudgetType::SharedAlpha8),
466                }
467            }
468            ImageFormat::R16 => {
469                assert_eq!(filter, TextureFilter::Linear);
470                (&mut self.alpha16_linear, BudgetType::SharedAlpha16)
471            }
472            ImageFormat::RGBA8 |
473            ImageFormat::BGRA8 => {
474                match (filter, shader) {
475                    (TextureFilter::Linear, TargetShader::Text) => {
476                        (&mut self.color8_glyphs, BudgetType::SharedColor8Glyphs)
477                    },
478                    (TextureFilter::Linear, _) => {
479                        (&mut self.color8_linear, BudgetType::SharedColor8Linear)
480                    },
481                    (TextureFilter::Nearest, _) => {
482                        (&mut self.color8_nearest, BudgetType::SharedColor8Nearest)
483                    },
484                    _ => panic!("Unexpected filter {:?}", filter),
485                }
486            }
487            _ => panic!("Unexpected format {:?}", external_format),
488        }
489    }
490
491    fn bytes_per_shared_texture(&self, budget_type: BudgetType) -> usize {
494        self.bytes_per_texture_of_type[budget_type as usize] as usize
495    }
496
497    fn has_multiple_textures(&self, budget_type: BudgetType) -> bool {
498        match budget_type {
499            BudgetType::SharedColor8Linear => self.color8_linear.allocated_textures() > 1,
500            BudgetType::SharedColor8Nearest => self.color8_nearest.allocated_textures() > 1,
501            BudgetType::SharedColor8Glyphs => self.color8_glyphs.allocated_textures() > 1,
502            BudgetType::SharedAlpha8 => self.alpha8_linear.allocated_textures() > 1,
503            BudgetType::SharedAlpha8Glyphs => self.alpha8_glyphs.allocated_textures() > 1,
504            BudgetType::SharedAlpha16 => self.alpha16_linear.allocated_textures() > 1,
505            BudgetType::Standalone => false,
506        }
507    }
508}
509
510struct CacheAllocParams {
512    descriptor: ImageDescriptor,
513    filter: TextureFilter,
514    user_data: [f32; 4],
515    uv_rect_kind: UvRectKind,
516    shader: TargetShader,
517}
518
519#[derive(Clone)]
523pub struct TextureCacheConfig {
524    pub color8_linear_texture_size: i32,
525    pub color8_nearest_texture_size: i32,
526    pub color8_glyph_texture_size: i32,
527    pub alpha8_texture_size: i32,
528    pub alpha8_glyph_texture_size: i32,
529    pub alpha16_texture_size: i32,
530}
531
532impl TextureCacheConfig {
533    pub const DEFAULT: Self = TextureCacheConfig {
534        color8_linear_texture_size: 2048,
535        color8_nearest_texture_size: 512,
536        color8_glyph_texture_size: 2048,
537        alpha8_texture_size: 1024,
538        alpha8_glyph_texture_size: 2048,
539        alpha16_texture_size: 512,
540    };
541}
542
543#[cfg_attr(feature = "capture", derive(Serialize))]
561#[cfg_attr(feature = "replay", derive(Deserialize))]
562pub struct TextureCache {
563    shared_textures: SharedTextures,
565
566    max_texture_size: i32,
568
569    tiling_threshold: i32,
572
573    swizzle: Option<SwizzleSettings>,
575
576    debug_flags: DebugFlags,
578
579    pub next_id: CacheTextureId,
581
582    #[cfg_attr(all(feature = "serde", any(feature = "capture", feature = "replay")), serde(skip))]
585    pub pending_updates: TextureUpdateList,
586
587    now: FrameStamp,
589
590    lru_cache: LRUCache<CacheEntry, AutoCacheEntryMarker>,
593
594    manual_entries: FreeList<CacheEntry, ManualCacheEntryMarker>,
596
597    manual_handles: Vec<FreeListHandle<ManualCacheEntryMarker>>,
599
600    bytes_allocated: [usize ; BudgetType::COUNT],
603}
604
605impl TextureCache {
606    const MAX_EVICTIONS_PER_FRAME: usize = 32;
611
612    pub fn new(
613        max_texture_size: i32,
614        tiling_threshold: i32,
615        color_formats: TextureFormatPair<ImageFormat>,
616        swizzle: Option<SwizzleSettings>,
617        config: &TextureCacheConfig,
618    ) -> Self {
619        let pending_updates = TextureUpdateList::new();
620
621        assert!(color_formats.internal != ImageFormat::BGRA8 ||
625            swizzle.map_or(true, |s| s.bgra8_sampling_swizzle == Swizzle::default())
626        );
627
628        let next_texture_id = CacheTextureId(1);
629
630        TextureCache {
631            shared_textures: SharedTextures::new(color_formats, config),
632            max_texture_size,
633            tiling_threshold,
634            swizzle,
635            debug_flags: DebugFlags::empty(),
636            next_id: next_texture_id,
637            pending_updates,
638            now: FrameStamp::INVALID,
639            lru_cache: LRUCache::new(BudgetType::COUNT),
640            manual_entries: FreeList::new(),
641            manual_handles: Vec::new(),
642            bytes_allocated: [0 ; BudgetType::COUNT],
643        }
644    }
645
646    #[cfg(test)]
650    pub fn new_for_testing(
651        max_texture_size: i32,
652        image_format: ImageFormat,
653    ) -> Self {
654        let mut cache = Self::new(
655            max_texture_size,
656            max_texture_size,
657            TextureFormatPair::from(image_format),
658            None,
659            &TextureCacheConfig::DEFAULT,
660        );
661        let mut now = FrameStamp::first(DocumentId::new(IdNamespace(1), 1));
662        now.advance();
663        cache.begin_frame(now, &mut TransactionProfile::new());
664        cache
665    }
666
667    pub fn set_debug_flags(&mut self, flags: DebugFlags) {
668        self.debug_flags = flags;
669    }
670
671    pub fn clear_all(&mut self) {
674        let manual_handles = mem::replace(
676            &mut self.manual_handles,
677            Vec::new(),
678        );
679        for handle in manual_handles {
680            let entry = self.manual_entries.free(handle);
681            self.evict_impl(entry);
682        }
683
684        for budget_type in BudgetType::iter() {
686            while let Some(entry) = self.lru_cache.pop_oldest(budget_type as u8) {
687                entry.evict();
688                self.free(&entry);
689            }
690        }
691
692        self.shared_textures.clear(&mut self.pending_updates);
694        self.pending_updates.note_clear();
695    }
696
697    pub fn begin_frame(&mut self, stamp: FrameStamp, profile: &mut TransactionProfile) {
699        debug_assert!(!self.now.is_valid());
700        profile_scope!("begin_frame");
701        self.now = stamp;
702
703        self.evict_items_from_cache_if_required(profile);
708    }
709
710    pub fn end_frame(&mut self, profile: &mut TransactionProfile) {
711        debug_assert!(self.now.is_valid());
712
713        let updates = &mut self.pending_updates; let callback = &mut|texture_id| { updates.push_free(texture_id); };
715
716        self.shared_textures.alpha8_linear.release_empty_textures(callback);
720        self.shared_textures.alpha8_glyphs.release_empty_textures(callback);
721        self.shared_textures.alpha16_linear.release_empty_textures(callback);
722        self.shared_textures.color8_linear.release_empty_textures(callback);
723        self.shared_textures.color8_nearest.release_empty_textures(callback);
724        self.shared_textures.color8_glyphs.release_empty_textures(callback);
725
726        for budget in BudgetType::iter() {
727            let threshold = self.get_eviction_threshold(budget);
728            let pressure = self.bytes_allocated[budget as usize] as f32 / threshold as f32;
729            profile.set(BudgetType::PRESSURE_COUNTERS[budget as usize], pressure);
730        }
731
732        profile.set(profiler::ATLAS_A8_PIXELS, self.shared_textures.alpha8_linear.allocated_space());
733        profile.set(profiler::ATLAS_A8_TEXTURES, self.shared_textures.alpha8_linear.allocated_textures());
734        profile.set(profiler::ATLAS_A8_GLYPHS_PIXELS, self.shared_textures.alpha8_glyphs.allocated_space());
735        profile.set(profiler::ATLAS_A8_GLYPHS_TEXTURES, self.shared_textures.alpha8_glyphs.allocated_textures());
736        profile.set(profiler::ATLAS_A16_PIXELS, self.shared_textures.alpha16_linear.allocated_space());
737        profile.set(profiler::ATLAS_A16_TEXTURES, self.shared_textures.alpha16_linear.allocated_textures());
738        profile.set(profiler::ATLAS_RGBA8_LINEAR_PIXELS, self.shared_textures.color8_linear.allocated_space());
739        profile.set(profiler::ATLAS_RGBA8_LINEAR_TEXTURES, self.shared_textures.color8_linear.allocated_textures());
740        profile.set(profiler::ATLAS_RGBA8_NEAREST_PIXELS, self.shared_textures.color8_nearest.allocated_space());
741        profile.set(profiler::ATLAS_RGBA8_NEAREST_TEXTURES, self.shared_textures.color8_nearest.allocated_textures());
742        profile.set(profiler::ATLAS_RGBA8_GLYPHS_PIXELS, self.shared_textures.color8_glyphs.allocated_space());
743        profile.set(profiler::ATLAS_RGBA8_GLYPHS_TEXTURES, self.shared_textures.color8_glyphs.allocated_textures());
744
745        let shared_bytes = [
746            BudgetType::SharedColor8Linear,
747            BudgetType::SharedColor8Nearest,
748            BudgetType::SharedColor8Glyphs,
749            BudgetType::SharedAlpha8,
750            BudgetType::SharedAlpha8Glyphs,
751            BudgetType::SharedAlpha16,
752        ].iter().map(|b| self.bytes_allocated[*b as usize]).sum();
753
754        profile.set(profiler::ATLAS_ITEMS_MEM, profiler::bytes_to_mb(shared_bytes));
755
756        self.now = FrameStamp::INVALID;
757    }
758
759    pub fn run_compaction(&mut self, gpu_cache: &mut GpuCache) {
760        let allocator_lists = [
763            &mut self.shared_textures.color8_linear,
764            &mut self.shared_textures.color8_nearest,
765            &mut self.shared_textures.color8_glyphs,
766            &mut self.shared_textures.alpha8_linear,
767            &mut self.shared_textures.alpha8_glyphs,
768            &mut self.shared_textures.alpha16_linear,
769        ];
770
771        let idx = self.shared_textures.next_compaction_idx;
773
774        let area_threshold = 512*512;
778
779        let mut changes = Vec::new();
780        allocator_lists[idx].try_compaction(area_threshold, &mut changes);
781
782        if changes.is_empty() {
783            self.shared_textures.next_compaction_idx = (self.shared_textures.next_compaction_idx + 1) % allocator_lists.len();
785        }
786
787        for change in changes {
788            let bpp = allocator_lists[idx].texture_parameters().formats.internal.bytes_per_pixel();
789
790            let old_bytes = (change.old_rect.area() * bpp) as usize;
794            let new_bytes = (change.new_rect.area() * bpp) as usize;
795            self.bytes_allocated[idx] -= old_bytes;
796            self.bytes_allocated[idx] += new_bytes;
797
798            let entry = match change.handle {
799                TextureCacheHandle::Auto(handle) => self.lru_cache.get_opt_mut(&handle).unwrap(),
800                TextureCacheHandle::Manual(handle) => self.manual_entries.get_opt_mut(&handle).unwrap(),
801                TextureCacheHandle::Empty => { panic!("invalid handle"); }
802            };
803            entry.texture_id = change.new_tex;
804            entry.details = EntryDetails::Cache {
805                origin: change.new_rect.min,
806                alloc_id: change.new_id,
807                allocated_size_in_bytes: new_bytes,
808            };
809
810            gpu_cache.invalidate(&entry.uv_rect_handle);
811            entry.uv_rect_handle = GpuCacheHandle::new();
812
813            let src_rect = DeviceIntRect::from_origin_and_size(change.old_rect.min, entry.size);
814            let dst_rect = DeviceIntRect::from_origin_and_size(change.new_rect.min, entry.size);
815
816            self.pending_updates.push_copy(change.old_tex, &src_rect, change.new_tex, &dst_rect);
817
818            if self.debug_flags.contains(
819                DebugFlags::TEXTURE_CACHE_DBG |
820                DebugFlags::TEXTURE_CACHE_DBG_CLEAR_EVICTED)
821            {
822                self.pending_updates.push_debug_clear(
823                    change.old_tex,
824                    src_rect.min,
825                    src_rect.width(),
826                    src_rect.height(),
827                );
828            }
829        }
830    }
831
832    pub fn request(&mut self, handle: &TextureCacheHandle, gpu_cache: &mut GpuCache) -> bool {
841        let now = self.now;
842        let entry = match handle {
843            TextureCacheHandle::Empty => None,
844            TextureCacheHandle::Auto(handle) => {
845                self.lru_cache.touch(handle)
848            },
849            TextureCacheHandle::Manual(handle) => {
850                self.manual_entries.get_opt_mut(handle)
851            },
852        };
853        entry.map_or(true, |entry| {
854            entry.last_access = now;
857            entry.update_gpu_cache(gpu_cache);
858            false
859        })
860    }
861
862    fn get_entry_opt(&self, handle: &TextureCacheHandle) -> Option<&CacheEntry> {
863        match handle {
864            TextureCacheHandle::Empty => None,
865            TextureCacheHandle::Auto(handle) => self.lru_cache.get_opt(handle),
866            TextureCacheHandle::Manual(handle) => self.manual_entries.get_opt(handle),
867        }
868    }
869
870    fn get_entry_opt_mut(&mut self, handle: &TextureCacheHandle) -> Option<&mut CacheEntry> {
871        match handle {
872            TextureCacheHandle::Empty => None,
873            TextureCacheHandle::Auto(handle) => self.lru_cache.get_opt_mut(handle),
874            TextureCacheHandle::Manual(handle) => self.manual_entries.get_opt_mut(handle),
875        }
876    }
877
878    pub fn needs_upload(&self, handle: &TextureCacheHandle) -> bool {
882        !self.is_allocated(handle)
883    }
884
885    pub fn max_texture_size(&self) -> i32 {
886        self.max_texture_size
887    }
888
889    pub fn tiling_threshold(&self) -> i32 {
890        self.tiling_threshold
891    }
892
893    #[cfg(feature = "replay")]
894    pub fn color_formats(&self) -> TextureFormatPair<ImageFormat> {
895        self.shared_textures.color8_linear.texture_parameters().formats.clone()
896    }
897
898    #[cfg(feature = "replay")]
899    pub fn swizzle_settings(&self) -> Option<SwizzleSettings> {
900        self.swizzle
901    }
902
903    pub fn pending_updates(&mut self) -> TextureUpdateList {
904        mem::replace(&mut self.pending_updates, TextureUpdateList::new())
905    }
906
907    pub fn update(
909        &mut self,
910        handle: &mut TextureCacheHandle,
911        descriptor: ImageDescriptor,
912        filter: TextureFilter,
913        data: Option<CachedImageData>,
914        user_data: [f32; 4],
915        mut dirty_rect: ImageDirtyRect,
916        gpu_cache: &mut GpuCache,
917        eviction_notice: Option<&EvictionNotice>,
918        uv_rect_kind: UvRectKind,
919        eviction: Eviction,
920        shader: TargetShader,
921        force_standalone_texture: bool,
922    ) {
923        debug_assert!(self.now.is_valid());
924        let realloc = match self.get_entry_opt(handle) {
931            Some(entry) => {
932                entry.size != descriptor.size || (entry.input_format != descriptor.format &&
933                    entry.alternative_input_format() != descriptor.format)
934            }
935            None => {
936                true
938            }
939        };
940
941        if realloc {
942            let params = CacheAllocParams { descriptor, filter, user_data, uv_rect_kind, shader };
943            self.allocate(¶ms, handle, eviction, force_standalone_texture);
944
945            dirty_rect = DirtyRect::All;
947        }
948
949        let entry = self.get_entry_opt_mut(handle)
950            .expect("BUG: There must be an entry at this handle now");
951
952        entry.eviction_notice = eviction_notice.cloned();
954        entry.uv_rect_kind = uv_rect_kind;
955
956        gpu_cache.invalidate(&entry.uv_rect_handle);
961
962        entry.update_gpu_cache(gpu_cache);
964
965        if let Some(data) = data {
969            let origin = entry.details.describe();
973            let texture_id = entry.texture_id;
974            let size = entry.size;
975            let use_upload_format = self.swizzle.is_none();
976            let op = TextureCacheUpdate::new_update(
977                data,
978                &descriptor,
979                origin,
980                size,
981                use_upload_format,
982                &dirty_rect,
983            );
984            self.pending_updates.push_update(texture_id, op);
985        }
986    }
987
988    pub fn is_allocated(&self, handle: &TextureCacheHandle) -> bool {
991        self.get_entry_opt(handle).is_some()
992    }
993
994    pub fn get_allocated_size(&self, handle: &TextureCacheHandle) -> Option<usize> {
997        self.get_entry_opt(handle).map(|entry| {
998            (entry.input_format.bytes_per_pixel() * entry.size.area()) as usize
999        })
1000    }
1001
1002    pub fn get(&self, handle: &TextureCacheHandle) -> CacheItem {
1008        let (texture_id, uv_rect, swizzle, uv_rect_handle, user_data) = self.get_cache_location(handle);
1009        CacheItem {
1010            uv_rect_handle,
1011            texture_id: TextureSource::TextureCache(
1012                texture_id,
1013                swizzle,
1014            ),
1015            uv_rect,
1016            user_data,
1017        }
1018    }
1019
1020    pub fn try_get(&self, handle: &TextureCacheHandle) -> Option<CacheItem> {
1021        let (texture_id, uv_rect, swizzle, uv_rect_handle, user_data) = self.try_get_cache_location(handle)?;
1022        Some(CacheItem {
1023            uv_rect_handle,
1024            texture_id: TextureSource::TextureCache(
1025                texture_id,
1026                swizzle,
1027            ),
1028            uv_rect,
1029            user_data,
1030        })
1031    }
1032
1033    pub fn try_get_cache_location(
1034        &self,
1035        handle: &TextureCacheHandle,
1036    ) -> Option<(CacheTextureId, DeviceIntRect, Swizzle, GpuCacheHandle, [f32; 4])> {
1037        let entry = self.get_entry_opt(handle)?;
1038        let origin = entry.details.describe();
1039        Some((
1040            entry.texture_id,
1041            DeviceIntRect::from_origin_and_size(origin, entry.size),
1042            entry.swizzle,
1043            entry.uv_rect_handle,
1044            entry.user_data,
1045        ))
1046    }
1047
1048    pub fn get_cache_location(
1054        &self,
1055        handle: &TextureCacheHandle,
1056    ) -> (CacheTextureId, DeviceIntRect, Swizzle, GpuCacheHandle, [f32; 4]) {
1057        self.try_get_cache_location(handle).expect("BUG: was dropped from cache or not updated!")
1058    }
1059
1060    fn evict_impl(
1062        &mut self,
1063        entry: CacheEntry,
1064    ) {
1065        entry.evict();
1066        self.free(&entry);
1067    }
1068
1069    pub fn evict_handle(&mut self, handle: &TextureCacheHandle) {
1072        match handle {
1073            TextureCacheHandle::Manual(handle) => {
1074                let index = self.manual_handles.iter().position(|strong_handle| {
1080                    strong_handle.matches(handle)
1081                });
1082                if let Some(index) = index {
1083                    let handle = self.manual_handles.swap_remove(index);
1084                    let entry = self.manual_entries.free(handle);
1085                    self.evict_impl(entry);
1086                }
1087            }
1088            TextureCacheHandle::Auto(handle) => {
1089                if let Some(entry) = self.lru_cache.remove(handle) {
1090                    self.evict_impl(entry);
1091                }
1092            }
1093            _ => {}
1094        }
1095    }
1096
1097    pub fn dump_color8_linear_as_svg(&self, output: &mut dyn std::io::Write) -> std::io::Result<()> {
1098        self.shared_textures.color8_linear.dump_as_svg(output)
1099    }
1100
1101    pub fn dump_color8_glyphs_as_svg(&self, output: &mut dyn std::io::Write) -> std::io::Result<()> {
1102        self.shared_textures.color8_glyphs.dump_as_svg(output)
1103    }
1104
1105    pub fn dump_alpha8_glyphs_as_svg(&self, output: &mut dyn std::io::Write) -> std::io::Result<()> {
1106        self.shared_textures.alpha8_glyphs.dump_as_svg(output)
1107    }
1108
1109    pub fn dump_alpha8_linear_as_svg(&self, output: &mut dyn std::io::Write) -> std::io::Result<()> {
1110        self.shared_textures.alpha8_linear.dump_as_svg(output)
1111    }
1112
1113    fn get_eviction_threshold(&self, budget_type: BudgetType) -> usize {
1115        if budget_type == BudgetType::Standalone {
1116            return 8 * 1024 * 1024;
1121        }
1122
1123        let bytes_per_texture = self.shared_textures.bytes_per_shared_texture(budget_type);
1132
1133        let ideal_utilization = match budget_type {
1151            BudgetType::SharedAlpha8Glyphs | BudgetType::SharedColor8Glyphs => {
1152                bytes_per_texture * 2 / 3
1155            }
1156            _ => {
1157                bytes_per_texture / 3
1161            }
1162        };
1163
1164        ideal_utilization
1165    }
1166
1167    fn should_continue_evicting(
1173        &self,
1174        budget_type: BudgetType,
1175        eviction_count: usize,
1176    ) -> Option<u64> {
1177
1178        let threshold = self.get_eviction_threshold(budget_type);
1179        let bytes_allocated = self.bytes_allocated[budget_type as usize];
1180
1181        let uses_multiple_atlases = self.shared_textures.has_multiple_textures(budget_type);
1182
1183        if bytes_allocated < threshold && !uses_multiple_atlases {
1188            return None;
1189        }
1190
1191        let age_theshold = match bytes_allocated / threshold {
1194            0 => 400,
1195            1 => 200,
1196            2 => 100,
1197            3 => 50,
1198            4 => 25,
1199            5 => 10,
1200            6 => 5,
1201            _ => 1,
1202        };
1203
1204        if bytes_allocated > 4 * threshold {
1206            return Some(age_theshold);
1207        }
1208
1209        if eviction_count < Self::MAX_EVICTIONS_PER_FRAME {
1212            return Some(age_theshold)
1213        }
1214
1215        None
1216    }
1217
1218
1219    fn evict_items_from_cache_if_required(&mut self, profile: &mut TransactionProfile) {
1222        let previous_frame_id = self.now.frame_id() - 1;
1223        let mut eviction_count = 0;
1224        let mut youngest_evicted = FrameId::first();
1225
1226        for budget in BudgetType::iter() {
1227            while let Some(age_threshold) = self.should_continue_evicting(
1228                budget,
1229                eviction_count,
1230            ) {
1231                if let Some(entry) = self.lru_cache.peek_oldest(budget as u8) {
1232                    if entry.last_access.frame_id() + age_threshold > previous_frame_id {
1236                        break;
1239                    }
1240                    if entry.last_access.frame_id() > youngest_evicted {
1241                        youngest_evicted = entry.last_access.frame_id();
1242                    }
1243                    let entry = self.lru_cache.pop_oldest(budget as u8).unwrap();
1244                    entry.evict();
1245                    self.free(&entry);
1246                    eviction_count += 1;
1247                } else {
1248                    break;
1253                }
1254            }
1255        }
1256
1257        if eviction_count > 0 {
1258            profile.set(profiler::TEXTURE_CACHE_EVICTION_COUNT, eviction_count);
1259            profile.set(
1260                profiler::TEXTURE_CACHE_YOUNGEST_EVICTION,
1261                self.now.frame_id().as_u64() - youngest_evicted.as_u64()
1262            );
1263        }
1264    }
1265
1266    fn free(&mut self, entry: &CacheEntry) {
1268        match entry.details {
1269            EntryDetails::Standalone { size_in_bytes, .. } => {
1270                self.bytes_allocated[BudgetType::Standalone as usize] -= size_in_bytes;
1271
1272                self.pending_updates.push_free(entry.texture_id);
1274            }
1275            EntryDetails::Cache { origin, alloc_id, allocated_size_in_bytes } => {
1276                let (allocator_list, budget_type) = self.shared_textures.select(
1277                    entry.input_format,
1278                    entry.filter,
1279                    entry.shader,
1280                );
1281
1282                allocator_list.deallocate(entry.texture_id, alloc_id);
1283
1284                self.bytes_allocated[budget_type as usize] -= allocated_size_in_bytes;
1285
1286                if self.debug_flags.contains(
1287                    DebugFlags::TEXTURE_CACHE_DBG |
1288                    DebugFlags::TEXTURE_CACHE_DBG_CLEAR_EVICTED)
1289                {
1290                    self.pending_updates.push_debug_clear(
1291                        entry.texture_id,
1292                        origin,
1293                        entry.size.width,
1294                        entry.size.height,
1295                    );
1296                }
1297            }
1298        }
1299    }
1300
1301    fn allocate_from_shared_cache(
1303        &mut self,
1304        params: &CacheAllocParams,
1305    ) -> (CacheEntry, BudgetType) {
1306        let (allocator_list, budget_type) = self.shared_textures.select(
1307            params.descriptor.format,
1308            params.filter,
1309            params.shader,
1310        );
1311
1312        let next_id = &mut self.next_id;
1314        let pending_updates = &mut self.pending_updates;
1315
1316        let (texture_id, alloc_id, allocated_rect) = allocator_list.allocate(
1317            params.descriptor.size,
1318            &mut |size, parameters| {
1319                let texture_id = *next_id;
1320                next_id.0 += 1;
1321                pending_updates.push_alloc(
1322                    texture_id,
1323                    TextureCacheAllocInfo {
1324                        target: ImageBufferKind::Texture2D,
1325                        width: size.width,
1326                        height: size.height,
1327                        format: parameters.formats.internal,
1328                        filter: parameters.filter,
1329                        is_shared_cache: true,
1330                        has_depth: false,
1331                        category: TextureCacheCategory::Atlas,
1332                    },
1333                );
1334
1335                texture_id
1336            },
1337        );
1338
1339        let formats = &allocator_list.texture_parameters().formats;
1340
1341        let swizzle = if formats.external == params.descriptor.format {
1342            Swizzle::default()
1343        } else {
1344            match self.swizzle {
1345                Some(_) => Swizzle::Bgra,
1346                None => Swizzle::default(),
1347            }
1348        };
1349
1350        let bpp = formats.internal.bytes_per_pixel();
1351        let allocated_size_in_bytes = (allocated_rect.area() * bpp) as usize;
1352        self.bytes_allocated[budget_type as usize] += allocated_size_in_bytes;
1353
1354        (CacheEntry {
1355            size: params.descriptor.size,
1356            user_data: params.user_data,
1357            last_access: self.now,
1358            details: EntryDetails::Cache {
1359                origin: allocated_rect.min,
1360                alloc_id,
1361                allocated_size_in_bytes,
1362            },
1363            uv_rect_handle: GpuCacheHandle::new(),
1364            input_format: params.descriptor.format,
1365            filter: params.filter,
1366            swizzle,
1367            texture_id,
1368            eviction_notice: None,
1369            uv_rect_kind: params.uv_rect_kind,
1370            shader: params.shader
1371        }, budget_type)
1372    }
1373
1374    pub fn is_allowed_in_shared_cache(
1377        &self,
1378        filter: TextureFilter,
1379        descriptor: &ImageDescriptor,
1380    ) -> bool {
1381        let mut allowed_in_shared_cache = true;
1382
1383        if matches!(descriptor.format, ImageFormat::RGBA8 | ImageFormat::BGRA8)
1384            && filter == TextureFilter::Linear
1385        {
1386            let max = self.shared_textures.color8_linear.size() / 2;
1388            allowed_in_shared_cache = descriptor.size.width.max(descriptor.size.height) <= max;
1389        } else if descriptor.size.width > TEXTURE_REGION_DIMENSIONS {
1390            allowed_in_shared_cache = false;
1391        }
1392
1393        if descriptor.size.height > TEXTURE_REGION_DIMENSIONS {
1394            allowed_in_shared_cache = false;
1395        }
1396
1397        if filter == TextureFilter::Nearest &&
1401           descriptor.format.bytes_per_pixel() <= 2
1402        {
1403            allowed_in_shared_cache = false;
1404        }
1405
1406        allowed_in_shared_cache
1407    }
1408
1409    pub fn alloc_render_target(
1411        &mut self,
1412        size: DeviceIntSize,
1413        format: ImageFormat,
1414    ) -> CacheTextureId {
1415        let texture_id = self.next_id;
1416        self.next_id.0 += 1;
1417
1418        let info = TextureCacheAllocInfo {
1420            target: ImageBufferKind::Texture2D,
1421            width: size.width,
1422            height: size.height,
1423            format,
1424            filter: TextureFilter::Linear,
1425            is_shared_cache: false,
1426            has_depth: false,
1427            category: TextureCacheCategory::RenderTarget,
1428        };
1429
1430        self.pending_updates.push_alloc(texture_id, info);
1431
1432        texture_id
1433    }
1434
1435    pub fn free_render_target(
1437        &mut self,
1438        id: CacheTextureId,
1439    ) {
1440        self.pending_updates.push_free(id);
1441    }
1442
1443    fn allocate_standalone_entry(
1445        &mut self,
1446        params: &CacheAllocParams,
1447    ) -> (CacheEntry, BudgetType) {
1448        let texture_id = self.next_id;
1449        self.next_id.0 += 1;
1450
1451        let info = TextureCacheAllocInfo {
1453            target: ImageBufferKind::Texture2D,
1454            width: params.descriptor.size.width,
1455            height: params.descriptor.size.height,
1456            format: params.descriptor.format,
1457            filter: params.filter,
1458            is_shared_cache: false,
1459            has_depth: false,
1460            category: TextureCacheCategory::Standalone,
1461        };
1462
1463        let size_in_bytes = (info.width * info.height * info.format.bytes_per_pixel()) as usize;
1464        self.bytes_allocated[BudgetType::Standalone as usize] += size_in_bytes;
1465
1466        self.pending_updates.push_alloc(texture_id, info);
1467
1468        let swizzle = if params.descriptor.format == ImageFormat::BGRA8 {
1470            self.swizzle.map(|s| s.bgra8_sampling_swizzle)
1471        } else {
1472            None
1473        };
1474
1475        (CacheEntry::new_standalone(
1476            texture_id,
1477            self.now,
1478            params,
1479            swizzle.unwrap_or_default(),
1480            size_in_bytes,
1481        ), BudgetType::Standalone)
1482    }
1483
1484    fn allocate(
1487        &mut self,
1488        params: &CacheAllocParams,
1489        handle: &mut TextureCacheHandle,
1490        eviction: Eviction,
1491        force_standalone_texture: bool,
1492    ) {
1493        debug_assert!(self.now.is_valid());
1494        assert!(!params.descriptor.size.is_empty());
1495
1496        let use_shared_cache = !force_standalone_texture && self.is_allowed_in_shared_cache(params.filter, ¶ms.descriptor);
1499        let (new_cache_entry, budget_type) = if use_shared_cache {
1500            self.allocate_from_shared_cache(params)
1501        } else {
1502            self.allocate_standalone_entry(params)
1503        };
1504
1505        let details = new_cache_entry.details.clone();
1506        let texture_id = new_cache_entry.texture_id;
1507
1508        let old_entry = match (&mut *handle, eviction) {
1516            (TextureCacheHandle::Auto(handle), Eviction::Auto) => {
1517                self.lru_cache.replace_or_insert(handle, budget_type as u8, new_cache_entry)
1518            },
1519            (TextureCacheHandle::Manual(handle), Eviction::Manual) => {
1520                let entry = self.manual_entries.get_opt_mut(handle)
1521                    .expect("Don't call this after evicting");
1522                Some(mem::replace(entry, new_cache_entry))
1523            },
1524            (TextureCacheHandle::Manual(_), Eviction::Auto) |
1525            (TextureCacheHandle::Auto(_), Eviction::Manual) => {
1526                panic!("Can't change eviction policy after initial allocation");
1527            },
1528            (TextureCacheHandle::Empty, Eviction::Auto) => {
1529                let new_handle = self.lru_cache.push_new(budget_type as u8, new_cache_entry);
1530                *handle = TextureCacheHandle::Auto(new_handle);
1531                None
1532            },
1533            (TextureCacheHandle::Empty, Eviction::Manual) => {
1534                let manual_handle = self.manual_entries.insert(new_cache_entry);
1535                let new_handle = manual_handle.weak();
1536                self.manual_handles.push(manual_handle);
1537                *handle = TextureCacheHandle::Manual(new_handle);
1538                None
1539            },
1540        };
1541        if let Some(old_entry) = old_entry {
1542            old_entry.evict();
1543            self.free(&old_entry);
1544        }
1545
1546        if let EntryDetails::Cache { alloc_id, .. } = details {
1547            let allocator_list = self.shared_textures.select(
1548                params.descriptor.format,
1549                params.filter,
1550                params.shader,
1551            ).0;
1552
1553            allocator_list.set_handle(texture_id, alloc_id, handle);
1554        }
1555    }
1556
1557    pub fn shared_alpha_expected_format(&self) -> ImageFormat {
1558        self.shared_textures.alpha8_linear.texture_parameters().formats.external
1559    }
1560
1561    pub fn shared_color_expected_format(&self) -> ImageFormat {
1562        self.shared_textures.color8_linear.texture_parameters().formats.external
1563    }
1564
1565
1566    #[cfg(test)]
1567    pub fn total_allocated_bytes_for_testing(&self) -> usize {
1568        BudgetType::iter().map(|b| self.bytes_allocated[b as usize]).sum()
1569    }
1570
1571    pub fn report_memory(&self, ops: &mut MallocSizeOfOps) -> usize {
1572        self.lru_cache.size_of(ops)
1573    }
1574}
1575
1576#[cfg_attr(feature = "capture", derive(Serialize))]
1577#[cfg_attr(feature = "replay", derive(Deserialize))]
1578pub struct TextureParameters {
1579    pub formats: TextureFormatPair<ImageFormat>,
1580    pub filter: TextureFilter,
1581}
1582
1583impl TextureCacheUpdate {
1584    fn new_update(
1588        data: CachedImageData,
1589        descriptor: &ImageDescriptor,
1590        origin: DeviceIntPoint,
1591        size: DeviceIntSize,
1592        use_upload_format: bool,
1593        dirty_rect: &ImageDirtyRect,
1594    ) -> TextureCacheUpdate {
1595        let source = match data {
1596            CachedImageData::Snapshot => {
1597                panic!("Snapshots should not do texture uploads");
1598            }
1599            CachedImageData::Blob => {
1600                panic!("The vector image should have been rasterized.");
1601            }
1602            CachedImageData::External(ext_image) => match ext_image.image_type {
1603                ExternalImageType::TextureHandle(_) => {
1604                    panic!("External texture handle should not go through texture_cache.");
1605                }
1606                ExternalImageType::Buffer => TextureUpdateSource::External {
1607                    id: ext_image.id,
1608                    channel_index: ext_image.channel_index,
1609                },
1610            },
1611            CachedImageData::Raw(bytes) => {
1612                let finish = descriptor.offset +
1613                    descriptor.size.width * descriptor.format.bytes_per_pixel() +
1614                    (descriptor.size.height - 1) * descriptor.compute_stride();
1615                assert!(bytes.len() >= finish as usize);
1616
1617                TextureUpdateSource::Bytes { data: bytes }
1618            }
1619        };
1620        let format_override = if use_upload_format {
1621            Some(descriptor.format)
1622        } else {
1623            None
1624        };
1625
1626        match *dirty_rect {
1627            DirtyRect::Partial(dirty) => {
1628                let stride = descriptor.compute_stride();
1630                let offset = descriptor.offset + dirty.min.y * stride + dirty.min.x * descriptor.format.bytes_per_pixel();
1631
1632                TextureCacheUpdate {
1633                    rect: DeviceIntRect::from_origin_and_size(
1634                        DeviceIntPoint::new(origin.x + dirty.min.x, origin.y + dirty.min.y),
1635                        DeviceIntSize::new(
1636                            dirty.width().min(size.width - dirty.min.x),
1637                            dirty.height().min(size.height - dirty.min.y),
1638                        ),
1639                    ),
1640                    source,
1641                    stride: Some(stride),
1642                    offset,
1643                    format_override,
1644                }
1645            }
1646            DirtyRect::All => {
1647                TextureCacheUpdate {
1648                    rect: DeviceIntRect::from_origin_and_size(origin, size),
1649                    source,
1650                    stride: descriptor.stride,
1651                    offset: descriptor.offset,
1652                    format_override,
1653                }
1654            }
1655        }
1656    }
1657}
1658
1659#[cfg(test)]
1660mod test_texture_cache {
1661    #[test]
1662    fn check_allocation_size_balance() {
1663        use crate::texture_cache::{TextureCache, TextureCacheHandle, Eviction, TargetShader};
1668        use crate::gpu_cache::GpuCache;
1669        use crate::device::TextureFilter;
1670        use crate::gpu_types::UvRectKind;
1671        use api::{ImageDescriptor, ImageDescriptorFlags, ImageFormat, DirtyRect};
1672        use api::units::*;
1673        use euclid::size2;
1674        let mut texture_cache = TextureCache::new_for_testing(2048, ImageFormat::BGRA8);
1675        let mut gpu_cache = GpuCache::new_for_testing();
1676
1677        let sizes: &[DeviceIntSize] = &[
1678            size2(23, 27),
1679            size2(15, 22),
1680            size2(11, 5),
1681            size2(20, 25),
1682            size2(38, 41),
1683            size2(11, 19),
1684            size2(13, 21),
1685            size2(37, 40),
1686            size2(13, 15),
1687            size2(14, 16),
1688            size2(10, 9),
1689            size2(25, 28),
1690        ];
1691
1692        let bytes_at_start = texture_cache.total_allocated_bytes_for_testing();
1693
1694        let handles: Vec<TextureCacheHandle> = sizes.iter().map(|size| {
1695            let mut texture_cache_handle = TextureCacheHandle::invalid();
1696            texture_cache.request(&texture_cache_handle, &mut gpu_cache);
1697            texture_cache.update(
1698                &mut texture_cache_handle,
1699                ImageDescriptor {
1700                    size: *size,
1701                    stride: None,
1702                    format: ImageFormat::BGRA8,
1703                    flags: ImageDescriptorFlags::empty(),
1704                    offset: 0,
1705                },
1706                TextureFilter::Linear,
1707                None,
1708                [0.0; 4],
1709                DirtyRect::All,
1710                &mut gpu_cache,
1711                None,
1712                UvRectKind::Rect,
1713                Eviction::Manual,
1714                TargetShader::Text,
1715                false,
1716            );
1717            texture_cache_handle
1718        }).collect();
1719
1720        let bytes_after_allocating = texture_cache.total_allocated_bytes_for_testing();
1721        assert!(bytes_after_allocating > bytes_at_start);
1722
1723        for handle in handles {
1724            texture_cache.evict_handle(&handle);
1725        }
1726
1727        let bytes_at_end = texture_cache.total_allocated_bytes_for_testing();
1728        assert_eq!(bytes_at_end, bytes_at_start);
1729    }
1730}