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,
16 TextureUpdateList, TextureUpdateSource, TextureSource,
17 TextureCacheAllocInfo, TextureCacheUpdate, TextureCacheCategory,
18};
19use crate::lru_cache::LRUCache;
20use crate::profiler::{self, TransactionProfile};
21use crate::render_backend::{FrameStamp, FrameId};
22use crate::resource_cache::{CacheItem, CachedImageData};
23use crate::texture_pack::{
24 AllocatorList,
25 AllocId,
26 AtlasAllocatorList,
27 ShelfAllocator,
28 ShelfAllocatorOptions,
29 SlabAllocator, SlabAllocatorParameters,
30};
31use smallvec::SmallVec;
32use std::cell::Cell;
33use std::{cmp, mem};
34use std::rc::Rc;
35use euclid::size2;
36use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
37
38#[derive(Copy, Clone, Debug, PartialEq, Eq)]
43#[cfg_attr(feature = "capture", derive(Serialize))]
44#[cfg_attr(feature = "replay", derive(Deserialize))]
45pub enum TargetShader {
46 Default,
47 Text,
48}
49
50pub const TEXTURE_REGION_DIMENSIONS: i32 = 512;
52
53#[derive(Debug)]
56#[cfg_attr(feature = "capture", derive(Serialize))]
57#[cfg_attr(feature = "replay", derive(Deserialize))]
58enum EntryDetails {
59 Standalone {
60 size_in_bytes: usize,
62 },
63 Picture {
64 size: DeviceIntSize,
66 },
67 Cache {
68 origin: DeviceIntPoint,
70 alloc_id: AllocId,
72 allocated_size_in_bytes: usize,
74 },
75}
76
77impl EntryDetails {
78 fn describe(&self) -> DeviceIntPoint {
79 match *self {
80 EntryDetails::Standalone { .. } => DeviceIntPoint::zero(),
81 EntryDetails::Picture { .. } => DeviceIntPoint::zero(),
82 EntryDetails::Cache { origin, .. } => origin,
83 }
84 }
85}
86
87#[derive(Debug, PartialEq)]
88#[cfg_attr(feature = "capture", derive(Serialize))]
89#[cfg_attr(feature = "replay", derive(Deserialize))]
90pub enum PictureCacheEntryMarker {}
91
92#[derive(Debug, PartialEq)]
93#[cfg_attr(feature = "capture", derive(Serialize))]
94#[cfg_attr(feature = "replay", derive(Deserialize))]
95pub enum AutoCacheEntryMarker {}
96
97#[derive(Debug, PartialEq)]
98#[cfg_attr(feature = "capture", derive(Serialize))]
99#[cfg_attr(feature = "replay", derive(Deserialize))]
100pub enum ManualCacheEntryMarker {}
101
102#[derive(Debug)]
106#[cfg_attr(feature = "capture", derive(Serialize))]
107#[cfg_attr(feature = "replay", derive(Deserialize))]
108struct CacheEntry {
109 size: DeviceIntSize,
113 details: EntryDetails,
115 user_data: [f32; 4],
117 last_access: FrameStamp,
122 uv_rect_handle: GpuCacheHandle,
124 input_format: ImageFormat,
126 filter: TextureFilter,
127 swizzle: Swizzle,
128 texture_id: CacheTextureId,
130 eviction_notice: Option<EvictionNotice>,
132 uv_rect_kind: UvRectKind,
134
135 shader: TargetShader,
136}
137
138malloc_size_of::malloc_size_of_is_0!(
139 CacheEntry,
140 AutoCacheEntryMarker, ManualCacheEntryMarker, PictureCacheEntryMarker
141);
142
143impl CacheEntry {
144 fn new_standalone(
146 texture_id: CacheTextureId,
147 last_access: FrameStamp,
148 params: &CacheAllocParams,
149 swizzle: Swizzle,
150 size_in_bytes: usize,
151 ) -> Self {
152 CacheEntry {
153 size: params.descriptor.size,
154 user_data: params.user_data,
155 last_access,
156 details: EntryDetails::Standalone {
157 size_in_bytes,
158 },
159 texture_id,
160 input_format: params.descriptor.format,
161 filter: params.filter,
162 swizzle,
163 uv_rect_handle: GpuCacheHandle::new(),
164 eviction_notice: None,
165 uv_rect_kind: params.uv_rect_kind,
166 shader: TargetShader::Default,
167 }
168 }
169
170 fn update_gpu_cache(&mut self, gpu_cache: &mut GpuCache) {
175 if let Some(mut request) = gpu_cache.request(&mut self.uv_rect_handle) {
176 let origin = self.details.describe();
177 let image_source = ImageSource {
178 p0: origin.to_f32(),
179 p1: (origin + self.size).to_f32(),
180 user_data: self.user_data,
181 uv_rect_kind: self.uv_rect_kind,
182 };
183 image_source.write_gpu_blocks(&mut request);
184 }
185 }
186
187 fn evict(&self) {
188 if let Some(eviction_notice) = self.eviction_notice.as_ref() {
189 eviction_notice.notify();
190 }
191 }
192
193 fn alternative_input_format(&self) -> ImageFormat {
194 match self.input_format {
195 ImageFormat::RGBA8 => ImageFormat::BGRA8,
196 ImageFormat::BGRA8 => ImageFormat::RGBA8,
197 other => other,
198 }
199 }
200}
201
202
203#[derive(MallocSizeOf,Clone,PartialEq,Debug)]
211#[cfg_attr(feature = "capture", derive(Serialize))]
212#[cfg_attr(feature = "replay", derive(Deserialize))]
213pub enum TextureCacheHandle {
214 Empty,
216
217 Picture(WeakFreeListHandle<PictureCacheEntryMarker>),
219
220 Auto(WeakFreeListHandle<AutoCacheEntryMarker>),
222
223 Manual(WeakFreeListHandle<ManualCacheEntryMarker>)
225}
226
227impl TextureCacheHandle {
228 pub fn invalid() -> Self {
229 TextureCacheHandle::Empty
230 }
231}
232
233#[derive(Copy, Clone, Debug, PartialEq, Eq)]
235#[cfg_attr(feature = "capture", derive(Serialize))]
236#[cfg_attr(feature = "replay", derive(Deserialize))]
237pub enum Eviction {
238 Auto,
241 Manual,
244}
245
246#[derive(Clone, Debug, Default)]
253#[cfg_attr(feature = "capture", derive(Serialize))]
254#[cfg_attr(feature = "replay", derive(Deserialize))]
255pub struct EvictionNotice {
256 evicted: Rc<Cell<bool>>,
257}
258
259impl EvictionNotice {
260 fn notify(&self) {
261 self.evicted.set(true);
262 }
263
264 pub fn check(&self) -> bool {
265 if self.evicted.get() {
266 self.evicted.set(false);
267 true
268 } else {
269 false
270 }
271 }
272}
273
274#[derive(Copy, Clone, Debug, PartialEq, Eq)]
281#[repr(u8)]
282#[cfg_attr(feature = "capture", derive(Serialize))]
283#[cfg_attr(feature = "replay", derive(Deserialize))]
284enum BudgetType {
285 SharedColor8Linear,
286 SharedColor8Nearest,
287 SharedColor8Glyphs,
288 SharedAlpha8,
289 SharedAlpha8Glyphs,
290 SharedAlpha16,
291 Standalone,
292}
293
294impl BudgetType {
295 pub const COUNT: usize = 7;
296
297 pub const VALUES: [BudgetType; BudgetType::COUNT] = [
298 BudgetType::SharedColor8Linear,
299 BudgetType::SharedColor8Nearest,
300 BudgetType::SharedColor8Glyphs,
301 BudgetType::SharedAlpha8,
302 BudgetType::SharedAlpha8Glyphs,
303 BudgetType::SharedAlpha16,
304 BudgetType::Standalone,
305 ];
306
307 pub const PRESSURE_COUNTERS: [usize; BudgetType::COUNT] = [
308 profiler::ATLAS_COLOR8_LINEAR_PRESSURE,
309 profiler::ATLAS_COLOR8_NEAREST_PRESSURE,
310 profiler::ATLAS_COLOR8_GLYPHS_PRESSURE,
311 profiler::ATLAS_ALPHA8_PRESSURE,
312 profiler::ATLAS_ALPHA8_GLYPHS_PRESSURE,
313 profiler::ATLAS_ALPHA16_PRESSURE,
314 profiler::ATLAS_STANDALONE_PRESSURE,
315 ];
316
317 pub fn iter() -> impl Iterator<Item = BudgetType> {
318 BudgetType::VALUES.iter().cloned()
319 }
320}
321
322#[cfg_attr(feature = "capture", derive(Serialize))]
325#[cfg_attr(feature = "replay", derive(Deserialize))]
326struct SharedTextures {
327 color8_nearest: AllocatorList<ShelfAllocator, TextureParameters>,
328 alpha8_linear: AllocatorList<ShelfAllocator, TextureParameters>,
329 alpha8_glyphs: AllocatorList<ShelfAllocator, TextureParameters>,
330 alpha16_linear: AllocatorList<SlabAllocator, TextureParameters>,
331 color8_linear: AllocatorList<ShelfAllocator, TextureParameters>,
332 color8_glyphs: AllocatorList<ShelfAllocator, TextureParameters>,
333 bytes_per_texture_of_type: [i32 ; BudgetType::COUNT],
334}
335
336impl SharedTextures {
337 fn new(color_formats: TextureFormatPair<ImageFormat>, config: &TextureCacheConfig) -> Self {
339 let mut bytes_per_texture_of_type = [0 ; BudgetType::COUNT];
340
341 let alpha8_linear = AllocatorList::new(
350 config.alpha8_texture_size,
351 ShelfAllocatorOptions {
352 num_columns: 1,
353 alignment: size2(8, 8),
354 .. ShelfAllocatorOptions::default()
355 },
356 TextureParameters {
357 formats: TextureFormatPair::from(ImageFormat::R8),
358 filter: TextureFilter::Linear,
359 },
360 );
361 bytes_per_texture_of_type[BudgetType::SharedAlpha8 as usize] =
362 config.alpha8_texture_size * config.alpha8_texture_size;
363
364 let alpha8_glyphs = AllocatorList::new(
366 config.alpha8_glyph_texture_size,
367 ShelfAllocatorOptions {
368 num_columns: if config.alpha8_glyph_texture_size >= 1024 { 2 } else { 1 },
369 alignment: size2(4, 8),
370 .. ShelfAllocatorOptions::default()
371 },
372 TextureParameters {
373 formats: TextureFormatPair::from(ImageFormat::R8),
374 filter: TextureFilter::Linear,
375 },
376 );
377 bytes_per_texture_of_type[BudgetType::SharedAlpha8Glyphs as usize] =
378 config.alpha8_glyph_texture_size * config.alpha8_glyph_texture_size;
379
380 let alpha16_linear = AllocatorList::new(
383 config.alpha16_texture_size,
384 SlabAllocatorParameters {
385 region_size: TEXTURE_REGION_DIMENSIONS,
386 },
387 TextureParameters {
388 formats: TextureFormatPair::from(ImageFormat::R16),
389 filter: TextureFilter::Linear,
390 },
391 );
392 bytes_per_texture_of_type[BudgetType::SharedAlpha16 as usize] =
393 ImageFormat::R16.bytes_per_pixel() *
394 config.alpha16_texture_size * config.alpha16_texture_size;
395
396 let color8_linear = AllocatorList::new(
398 config.color8_linear_texture_size,
399 ShelfAllocatorOptions {
400 num_columns: if config.color8_linear_texture_size >= 1024 { 2 } else { 1 },
401 alignment: size2(16, 16),
402 .. ShelfAllocatorOptions::default()
403 },
404 TextureParameters {
405 formats: color_formats.clone(),
406 filter: TextureFilter::Linear,
407 },
408 );
409 bytes_per_texture_of_type[BudgetType::SharedColor8Linear as usize] =
410 color_formats.internal.bytes_per_pixel() *
411 config.color8_linear_texture_size * config.color8_linear_texture_size;
412
413 let color8_glyphs = AllocatorList::new(
415 config.color8_glyph_texture_size,
416 ShelfAllocatorOptions {
417 num_columns: if config.color8_glyph_texture_size >= 1024 { 2 } else { 1 },
418 alignment: size2(4, 8),
419 .. ShelfAllocatorOptions::default()
420 },
421 TextureParameters {
422 formats: color_formats.clone(),
423 filter: TextureFilter::Linear,
424 },
425 );
426 bytes_per_texture_of_type[BudgetType::SharedColor8Glyphs as usize] =
427 color_formats.internal.bytes_per_pixel() *
428 config.color8_glyph_texture_size * config.color8_glyph_texture_size;
429
430 let color8_nearest = AllocatorList::new(
434 config.color8_nearest_texture_size,
435 ShelfAllocatorOptions::default(),
436 TextureParameters {
437 formats: color_formats.clone(),
438 filter: TextureFilter::Nearest,
439 }
440 );
441 bytes_per_texture_of_type[BudgetType::SharedColor8Nearest as usize] =
442 color_formats.internal.bytes_per_pixel() *
443 config.color8_nearest_texture_size * config.color8_nearest_texture_size;
444
445 Self {
446 alpha8_linear,
447 alpha8_glyphs,
448 alpha16_linear,
449 color8_linear,
450 color8_glyphs,
451 color8_nearest,
452 bytes_per_texture_of_type,
453 }
454 }
455
456 fn clear(&mut self, updates: &mut TextureUpdateList) {
458 let texture_dealloc_cb = &mut |texture_id| {
459 updates.push_free(texture_id);
460 };
461
462 self.alpha8_linear.clear(texture_dealloc_cb);
463 self.alpha8_glyphs.clear(texture_dealloc_cb);
464 self.alpha16_linear.clear(texture_dealloc_cb);
465 self.color8_linear.clear(texture_dealloc_cb);
466 self.color8_nearest.clear(texture_dealloc_cb);
467 self.color8_glyphs.clear(texture_dealloc_cb);
468 }
469
470 fn select(
472 &mut self, external_format: ImageFormat, filter: TextureFilter, shader: TargetShader,
473 ) -> (&mut dyn AtlasAllocatorList<TextureParameters>, BudgetType) {
474 match external_format {
475 ImageFormat::R8 => {
476 assert_eq!(filter, TextureFilter::Linear);
477 match shader {
478 TargetShader::Text => {
479 (&mut self.alpha8_glyphs, BudgetType::SharedAlpha8Glyphs)
480 },
481 _ => (&mut self.alpha8_linear, BudgetType::SharedAlpha8),
482 }
483 }
484 ImageFormat::R16 => {
485 assert_eq!(filter, TextureFilter::Linear);
486 (&mut self.alpha16_linear, BudgetType::SharedAlpha16)
487 }
488 ImageFormat::RGBA8 |
489 ImageFormat::BGRA8 => {
490 match (filter, shader) {
491 (TextureFilter::Linear, TargetShader::Text) => {
492 (&mut self.color8_glyphs, BudgetType::SharedColor8Glyphs)
493 },
494 (TextureFilter::Linear, _) => {
495 (&mut self.color8_linear, BudgetType::SharedColor8Linear)
496 },
497 (TextureFilter::Nearest, _) => {
498 (&mut self.color8_nearest, BudgetType::SharedColor8Nearest)
499 },
500 _ => panic!("Unexpected filter {:?}", filter),
501 }
502 }
503 _ => panic!("Unexpected format {:?}", external_format),
504 }
505 }
506
507 fn bytes_per_shared_texture(&self, budget_type: BudgetType) -> usize {
510 self.bytes_per_texture_of_type[budget_type as usize] as usize
511 }
512}
513
514#[cfg_attr(feature = "capture", derive(Serialize))]
516#[cfg_attr(feature = "replay", derive(Deserialize))]
517struct PictureTexture {
518 texture_id: CacheTextureId,
519 size: DeviceIntSize,
520 is_allocated: bool,
521 last_frame_used: FrameId,
522}
523
524#[cfg_attr(feature = "capture", derive(Serialize))]
526#[cfg_attr(feature = "replay", derive(Deserialize))]
527struct PictureTextures {
528 textures: Vec<PictureTexture>,
530 default_tile_size: DeviceIntSize,
532 allocated_texture_count: usize,
534 filter: TextureFilter,
536}
537
538impl PictureTextures {
539 fn new(
540 default_tile_size: DeviceIntSize,
541 filter: TextureFilter,
542 ) -> Self {
543 PictureTextures {
544 textures: Vec::new(),
545 default_tile_size,
546 allocated_texture_count: 0,
547 filter,
548 }
549 }
550
551 fn get_or_allocate_tile(
552 &mut self,
553 tile_size: DeviceIntSize,
554 now: FrameStamp,
555 next_texture_id: &mut CacheTextureId,
556 pending_updates: &mut TextureUpdateList,
557 ) -> CacheEntry {
558 let mut texture_id = None;
559 self.allocated_texture_count += 1;
560
561 for texture in &mut self.textures {
562 if texture.size == tile_size && !texture.is_allocated {
563 texture.is_allocated = true;
566 texture.last_frame_used = FrameId::INVALID;
567 texture_id = Some(texture.texture_id);
568 break;
569 }
570 }
571
572 let texture_id = texture_id.unwrap_or_else(|| {
575 let texture_id = *next_texture_id;
576 next_texture_id.0 += 1;
577
578 let info = TextureCacheAllocInfo {
580 target: ImageBufferKind::Texture2D,
581 width: tile_size.width,
582 height: tile_size.height,
583 format: ImageFormat::RGBA8,
584 filter: self.filter,
585 is_shared_cache: false,
586 has_depth: true,
587 category: TextureCacheCategory::PictureTile,
588 };
589
590 pending_updates.push_alloc(texture_id, info);
591
592 self.textures.push(PictureTexture {
593 texture_id,
594 is_allocated: true,
595 size: tile_size,
596 last_frame_used: FrameId::INVALID,
597 });
598
599 texture_id
600 });
601
602 CacheEntry {
603 size: tile_size,
604 user_data: [0.0; 4],
605 last_access: now,
606 details: EntryDetails::Picture {
607 size: tile_size,
608 },
609 uv_rect_handle: GpuCacheHandle::new(),
610 input_format: ImageFormat::RGBA8,
611 filter: self.filter,
612 swizzle: Swizzle::default(),
613 texture_id,
614 eviction_notice: None,
615 uv_rect_kind: UvRectKind::Rect,
616 shader: TargetShader::Default,
617 }
618 }
619
620 fn free_tile(
621 &mut self,
622 id: CacheTextureId,
623 current_frame_id: FrameId,
624 ) {
625 self.allocated_texture_count -= 1;
626
627 let texture = self.textures
628 .iter_mut()
629 .find(|t| t.texture_id == id)
630 .expect("bug: invalid texture id");
631
632 assert!(texture.is_allocated);
633 texture.is_allocated = false;
634
635 assert_eq!(texture.last_frame_used, FrameId::INVALID);
636 texture.last_frame_used = current_frame_id;
637 }
638
639 fn clear(&mut self, pending_updates: &mut TextureUpdateList) {
640 for texture in self.textures.drain(..) {
641 pending_updates.push_free(texture.texture_id);
642 }
643 }
644
645 fn update_profile(&self, profile: &mut TransactionProfile) {
646 profile.set(profiler::PICTURE_TILES, self.textures.len());
647 }
648
649 fn gc(
651 &mut self,
652 pending_updates: &mut TextureUpdateList,
653 ) {
654 let free_texture_count = self.textures.len() - self.allocated_texture_count;
658 let allowed_retained_count = (self.allocated_texture_count as f32 * 0.25).ceil() as usize;
659 let do_gc = free_texture_count > allowed_retained_count;
660
661 if do_gc {
662 self.textures.sort_unstable_by_key(|t| cmp::Reverse(t.last_frame_used));
664
665 let mut allocated_targets = SmallVec::<[PictureTexture; 32]>::new();
667 let mut retained_targets = SmallVec::<[PictureTexture; 32]>::new();
668
669 for target in self.textures.drain(..) {
670 if target.is_allocated {
671 allocated_targets.push(target);
673 } else if retained_targets.len() < allowed_retained_count {
674 retained_targets.push(target);
676 } else {
677 assert_ne!(target.last_frame_used, FrameId::INVALID);
679 pending_updates.push_free(target.texture_id);
680 }
681 }
682
683 self.textures.extend(retained_targets);
684 self.textures.extend(allocated_targets);
685 }
686 }
687}
688
689struct CacheAllocParams {
691 descriptor: ImageDescriptor,
692 filter: TextureFilter,
693 user_data: [f32; 4],
694 uv_rect_kind: UvRectKind,
695 shader: TargetShader,
696}
697
698#[derive(Clone)]
702pub struct TextureCacheConfig {
703 pub color8_linear_texture_size: i32,
704 pub color8_nearest_texture_size: i32,
705 pub color8_glyph_texture_size: i32,
706 pub alpha8_texture_size: i32,
707 pub alpha8_glyph_texture_size: i32,
708 pub alpha16_texture_size: i32,
709}
710
711impl TextureCacheConfig {
712 pub const DEFAULT: Self = TextureCacheConfig {
713 color8_linear_texture_size: 2048,
714 color8_nearest_texture_size: 512,
715 color8_glyph_texture_size: 2048,
716 alpha8_texture_size: 1024,
717 alpha8_glyph_texture_size: 2048,
718 alpha16_texture_size: 512,
719 };
720}
721
722#[cfg_attr(feature = "capture", derive(Serialize))]
740#[cfg_attr(feature = "replay", derive(Deserialize))]
741pub struct TextureCache {
742 shared_textures: SharedTextures,
744
745 picture_textures: PictureTextures,
747
748 max_texture_size: i32,
750
751 tiling_threshold: i32,
754
755 swizzle: Option<SwizzleSettings>,
757
758 debug_flags: DebugFlags,
760
761 next_id: CacheTextureId,
763
764 #[cfg_attr(all(feature = "serde", any(feature = "capture", feature = "replay")), serde(skip))]
767 pending_updates: TextureUpdateList,
768
769 now: FrameStamp,
771
772 lru_cache: LRUCache<CacheEntry, AutoCacheEntryMarker>,
775
776 picture_cache_entries: FreeList<CacheEntry, PictureCacheEntryMarker>,
778
779 picture_cache_handles: Vec<FreeListHandle<PictureCacheEntryMarker>>,
781
782 manual_entries: FreeList<CacheEntry, ManualCacheEntryMarker>,
784
785 manual_handles: Vec<FreeListHandle<ManualCacheEntryMarker>>,
787
788 bytes_allocated: [usize ; BudgetType::COUNT],
791}
792
793impl TextureCache {
794 const MAX_EVICTIONS_PER_FRAME: usize = 32;
799
800 pub fn new(
801 max_texture_size: i32,
802 tiling_threshold: i32,
803 default_picture_tile_size: DeviceIntSize,
804 color_formats: TextureFormatPair<ImageFormat>,
805 swizzle: Option<SwizzleSettings>,
806 config: &TextureCacheConfig,
807 picture_texture_filter: TextureFilter,
808 ) -> Self {
809 let pending_updates = TextureUpdateList::new();
810
811 assert!(color_formats.internal != ImageFormat::BGRA8 ||
815 swizzle.map_or(true, |s| s.bgra8_sampling_swizzle == Swizzle::default())
816 );
817
818 let next_texture_id = CacheTextureId(1);
819
820 TextureCache {
821 shared_textures: SharedTextures::new(color_formats, config),
822 picture_textures: PictureTextures::new(
823 default_picture_tile_size,
824 picture_texture_filter,
825 ),
826 max_texture_size,
827 tiling_threshold,
828 swizzle,
829 debug_flags: DebugFlags::empty(),
830 next_id: next_texture_id,
831 pending_updates,
832 now: FrameStamp::INVALID,
833 lru_cache: LRUCache::new(BudgetType::COUNT),
834 picture_cache_entries: FreeList::new(),
835 picture_cache_handles: Vec::new(),
836 manual_entries: FreeList::new(),
837 manual_handles: Vec::new(),
838 bytes_allocated: [0 ; BudgetType::COUNT],
839 }
840 }
841
842 #[cfg(test)]
846 pub fn new_for_testing(
847 max_texture_size: i32,
848 image_format: ImageFormat,
849 ) -> Self {
850 let mut cache = Self::new(
851 max_texture_size,
852 max_texture_size,
853 crate::picture::TILE_SIZE_DEFAULT,
854 TextureFormatPair::from(image_format),
855 None,
856 &TextureCacheConfig::DEFAULT,
857 TextureFilter::Nearest,
858 );
859 let mut now = FrameStamp::first(DocumentId::new(IdNamespace(1), 1));
860 now.advance();
861 cache.begin_frame(now, &mut TransactionProfile::new());
862 cache
863 }
864
865 pub fn set_debug_flags(&mut self, flags: DebugFlags) {
866 self.debug_flags = flags;
867 }
868
869 pub fn clear_all(&mut self) {
872 let manual_handles = mem::replace(
874 &mut self.manual_handles,
875 Vec::new(),
876 );
877 for handle in manual_handles {
878 let entry = self.manual_entries.free(handle);
879 self.evict_impl(entry);
880 }
881
882 let picture_handles = mem::replace(
884 &mut self.picture_cache_handles,
885 Vec::new(),
886 );
887 for handle in picture_handles {
888 let entry = self.picture_cache_entries.free(handle);
889 self.evict_impl(entry);
890 }
891
892 for budget_type in BudgetType::iter() {
894 while let Some(entry) = self.lru_cache.pop_oldest(budget_type as u8) {
895 entry.evict();
896 self.free(&entry);
897 }
898 }
899
900 self.picture_textures.clear(&mut self.pending_updates);
902 self.shared_textures.clear(&mut self.pending_updates);
903 self.pending_updates.note_clear();
904 }
905
906 pub fn begin_frame(&mut self, stamp: FrameStamp, profile: &mut TransactionProfile) {
908 debug_assert!(!self.now.is_valid());
909 profile_scope!("begin_frame");
910 self.now = stamp;
911
912 self.evict_items_from_cache_if_required(profile);
917 self.expire_old_picture_cache_tiles();
918 }
919
920 pub fn end_frame(&mut self, profile: &mut TransactionProfile) {
921 debug_assert!(self.now.is_valid());
922 self.picture_textures.gc(
923 &mut self.pending_updates,
924 );
925
926 let updates = &mut self.pending_updates; let callback = &mut|texture_id| { updates.push_free(texture_id); };
928
929 self.shared_textures.alpha8_linear.release_empty_textures(callback);
933 self.shared_textures.alpha8_glyphs.release_empty_textures(callback);
934 self.shared_textures.alpha16_linear.release_empty_textures(callback);
935 self.shared_textures.color8_linear.release_empty_textures(callback);
936 self.shared_textures.color8_nearest.release_empty_textures(callback);
937 self.shared_textures.color8_glyphs.release_empty_textures(callback);
938
939 for budget in BudgetType::iter() {
940 let threshold = self.get_eviction_threshold(budget);
941 let pressure = self.bytes_allocated[budget as usize] as f32 / threshold as f32;
942 profile.set(BudgetType::PRESSURE_COUNTERS[budget as usize], pressure);
943 }
944
945 profile.set(profiler::ATLAS_A8_PIXELS, self.shared_textures.alpha8_linear.allocated_space());
946 profile.set(profiler::ATLAS_A8_TEXTURES, self.shared_textures.alpha8_linear.allocated_textures());
947 profile.set(profiler::ATLAS_A8_GLYPHS_PIXELS, self.shared_textures.alpha8_glyphs.allocated_space());
948 profile.set(profiler::ATLAS_A8_GLYPHS_TEXTURES, self.shared_textures.alpha8_glyphs.allocated_textures());
949 profile.set(profiler::ATLAS_A16_PIXELS, self.shared_textures.alpha16_linear.allocated_space());
950 profile.set(profiler::ATLAS_A16_TEXTURES, self.shared_textures.alpha16_linear.allocated_textures());
951 profile.set(profiler::ATLAS_RGBA8_LINEAR_PIXELS, self.shared_textures.color8_linear.allocated_space());
952 profile.set(profiler::ATLAS_RGBA8_LINEAR_TEXTURES, self.shared_textures.color8_linear.allocated_textures());
953 profile.set(profiler::ATLAS_RGBA8_NEAREST_PIXELS, self.shared_textures.color8_nearest.allocated_space());
954 profile.set(profiler::ATLAS_RGBA8_NEAREST_TEXTURES, self.shared_textures.color8_nearest.allocated_textures());
955 profile.set(profiler::ATLAS_RGBA8_GLYPHS_PIXELS, self.shared_textures.color8_glyphs.allocated_space());
956 profile.set(profiler::ATLAS_RGBA8_GLYPHS_TEXTURES, self.shared_textures.color8_glyphs.allocated_textures());
957
958 self.picture_textures.update_profile(profile);
959
960 let shared_bytes = [
961 BudgetType::SharedColor8Linear,
962 BudgetType::SharedColor8Nearest,
963 BudgetType::SharedColor8Glyphs,
964 BudgetType::SharedAlpha8,
965 BudgetType::SharedAlpha8Glyphs,
966 BudgetType::SharedAlpha16,
967 ].iter().map(|b| self.bytes_allocated[*b as usize]).sum();
968
969 profile.set(profiler::ATLAS_ITEMS_MEM, profiler::bytes_to_mb(shared_bytes));
970
971 self.now = FrameStamp::INVALID;
972 }
973
974 pub fn request(&mut self, handle: &TextureCacheHandle, gpu_cache: &mut GpuCache) -> bool {
983 let now = self.now;
984 let entry = match handle {
985 TextureCacheHandle::Empty => None,
986 TextureCacheHandle::Picture(handle) => {
987 self.picture_cache_entries.get_opt_mut(handle)
988 },
989 TextureCacheHandle::Auto(handle) => {
990 self.lru_cache.touch(handle)
993 },
994 TextureCacheHandle::Manual(handle) => {
995 self.manual_entries.get_opt_mut(handle)
996 },
997 };
998 entry.map_or(true, |entry| {
999 entry.last_access = now;
1002 entry.update_gpu_cache(gpu_cache);
1003 false
1004 })
1005 }
1006
1007 fn get_entry_opt(&self, handle: &TextureCacheHandle) -> Option<&CacheEntry> {
1008 match handle {
1009 TextureCacheHandle::Empty => None,
1010 TextureCacheHandle::Picture(handle) => self.picture_cache_entries.get_opt(handle),
1011 TextureCacheHandle::Auto(handle) => self.lru_cache.get_opt(handle),
1012 TextureCacheHandle::Manual(handle) => self.manual_entries.get_opt(handle),
1013 }
1014 }
1015
1016 fn get_entry_opt_mut(&mut self, handle: &TextureCacheHandle) -> Option<&mut CacheEntry> {
1017 match handle {
1018 TextureCacheHandle::Empty => None,
1019 TextureCacheHandle::Picture(handle) => self.picture_cache_entries.get_opt_mut(handle),
1020 TextureCacheHandle::Auto(handle) => self.lru_cache.get_opt_mut(handle),
1021 TextureCacheHandle::Manual(handle) => self.manual_entries.get_opt_mut(handle),
1022 }
1023 }
1024
1025 pub fn needs_upload(&self, handle: &TextureCacheHandle) -> bool {
1029 !self.is_allocated(handle)
1030 }
1031
1032 pub fn max_texture_size(&self) -> i32 {
1033 self.max_texture_size
1034 }
1035
1036 pub fn tiling_threshold(&self) -> i32 {
1037 self.tiling_threshold
1038 }
1039
1040 #[cfg(feature = "replay")]
1041 pub fn color_formats(&self) -> TextureFormatPair<ImageFormat> {
1042 self.shared_textures.color8_linear.texture_parameters().formats.clone()
1043 }
1044
1045 #[cfg(feature = "replay")]
1046 pub fn swizzle_settings(&self) -> Option<SwizzleSettings> {
1047 self.swizzle
1048 }
1049
1050 #[cfg(feature = "replay")]
1051 pub fn picture_texture_filter(&self) -> TextureFilter {
1052 self.picture_textures.filter
1053 }
1054
1055 pub fn pending_updates(&mut self) -> TextureUpdateList {
1056 mem::replace(&mut self.pending_updates, TextureUpdateList::new())
1057 }
1058
1059 pub fn update(
1061 &mut self,
1062 handle: &mut TextureCacheHandle,
1063 descriptor: ImageDescriptor,
1064 filter: TextureFilter,
1065 data: Option<CachedImageData>,
1066 user_data: [f32; 4],
1067 mut dirty_rect: ImageDirtyRect,
1068 gpu_cache: &mut GpuCache,
1069 eviction_notice: Option<&EvictionNotice>,
1070 uv_rect_kind: UvRectKind,
1071 eviction: Eviction,
1072 shader: TargetShader,
1073 ) {
1074 debug_assert!(self.now.is_valid());
1075 let realloc = match self.get_entry_opt(handle) {
1082 Some(entry) => {
1083 entry.size != descriptor.size || (entry.input_format != descriptor.format &&
1084 entry.alternative_input_format() != descriptor.format)
1085 }
1086 None => {
1087 true
1089 }
1090 };
1091
1092 if realloc {
1093 let params = CacheAllocParams { descriptor, filter, user_data, uv_rect_kind, shader };
1094 self.allocate(¶ms, handle, eviction);
1095
1096 dirty_rect = DirtyRect::All;
1098 }
1099
1100 let entry = self.get_entry_opt_mut(handle)
1101 .expect("BUG: There must be an entry at this handle now");
1102
1103 entry.eviction_notice = eviction_notice.cloned();
1105 entry.uv_rect_kind = uv_rect_kind;
1106
1107 gpu_cache.invalidate(&entry.uv_rect_handle);
1112
1113 entry.update_gpu_cache(gpu_cache);
1115
1116 if let Some(data) = data {
1120 let origin = entry.details.describe();
1124 let texture_id = entry.texture_id;
1125 let size = entry.size;
1126 let use_upload_format = self.swizzle.is_none();
1127 let op = TextureCacheUpdate::new_update(
1128 data,
1129 &descriptor,
1130 origin,
1131 size,
1132 use_upload_format,
1133 &dirty_rect,
1134 );
1135 self.pending_updates.push_update(texture_id, op);
1136 }
1137 }
1138
1139 pub fn is_allocated(&self, handle: &TextureCacheHandle) -> bool {
1142 self.get_entry_opt(handle).is_some()
1143 }
1144
1145 pub fn is_recently_used(&self, handle: &TextureCacheHandle, margin: usize) -> bool {
1148 self.get_entry_opt(handle).map_or(false, |entry| {
1149 entry.last_access.frame_id() + margin >= self.now.frame_id()
1150 })
1151 }
1152
1153 pub fn get_allocated_size(&self, handle: &TextureCacheHandle) -> Option<usize> {
1156 self.get_entry_opt(handle).map(|entry| {
1157 (entry.input_format.bytes_per_pixel() * entry.size.area()) as usize
1158 })
1159 }
1160
1161 pub fn get(&self, handle: &TextureCacheHandle) -> CacheItem {
1167 let (texture_id, uv_rect, swizzle, uv_rect_handle, user_data) = self.get_cache_location(handle);
1168 CacheItem {
1169 uv_rect_handle,
1170 texture_id: TextureSource::TextureCache(
1171 texture_id,
1172 swizzle,
1173 ),
1174 uv_rect,
1175 user_data,
1176 }
1177 }
1178
1179 pub fn get_cache_location(
1185 &self,
1186 handle: &TextureCacheHandle,
1187 ) -> (CacheTextureId, DeviceIntRect, Swizzle, GpuCacheHandle, [f32; 4]) {
1188 let entry = self
1189 .get_entry_opt(handle)
1190 .expect("BUG: was dropped from cache or not updated!");
1191 debug_assert_eq!(entry.last_access, self.now);
1192 let origin = entry.details.describe();
1193 (
1194 entry.texture_id,
1195 DeviceIntRect::from_origin_and_size(origin, entry.size),
1196 entry.swizzle,
1197 entry.uv_rect_handle,
1198 entry.user_data,
1199 )
1200 }
1201
1202 fn evict_impl(
1204 &mut self,
1205 entry: CacheEntry,
1206 ) {
1207 entry.evict();
1208 self.free(&entry);
1209 }
1210
1211 pub fn evict_handle(&mut self, handle: &TextureCacheHandle) {
1214 match handle {
1215 TextureCacheHandle::Manual(handle) => {
1216 let index = self.manual_handles.iter().position(|strong_handle| {
1222 strong_handle.matches(handle)
1223 });
1224 if let Some(index) = index {
1225 let handle = self.manual_handles.swap_remove(index);
1226 let entry = self.manual_entries.free(handle);
1227 self.evict_impl(entry);
1228 }
1229 }
1230 TextureCacheHandle::Auto(handle) => {
1231 if let Some(entry) = self.lru_cache.remove(handle) {
1232 self.evict_impl(entry);
1233 }
1234 }
1235 _ => {}
1236 }
1237 }
1238
1239 pub fn dump_color8_linear_as_svg(&self, output: &mut dyn std::io::Write) -> std::io::Result<()> {
1240 self.shared_textures.color8_linear.dump_as_svg(output)
1241 }
1242
1243 pub fn dump_color8_glyphs_as_svg(&self, output: &mut dyn std::io::Write) -> std::io::Result<()> {
1244 self.shared_textures.color8_glyphs.dump_as_svg(output)
1245 }
1246
1247 pub fn dump_alpha8_glyphs_as_svg(&self, output: &mut dyn std::io::Write) -> std::io::Result<()> {
1248 self.shared_textures.alpha8_glyphs.dump_as_svg(output)
1249 }
1250
1251 pub fn dump_alpha8_linear_as_svg(&self, output: &mut dyn std::io::Write) -> std::io::Result<()> {
1252 self.shared_textures.alpha8_linear.dump_as_svg(output)
1253 }
1254
1255 fn expire_old_picture_cache_tiles(&mut self) {
1259 for i in (0 .. self.picture_cache_handles.len()).rev() {
1260 let evict = {
1261 let entry = self.picture_cache_entries.get(
1262 &self.picture_cache_handles[i]
1263 );
1264
1265 entry.last_access.frame_id() < self.now.frame_id() - 1
1270 };
1271
1272 if evict {
1273 let handle = self.picture_cache_handles.swap_remove(i);
1274 let entry = self.picture_cache_entries.free(handle);
1275 self.evict_impl(entry);
1276 }
1277 }
1278 }
1279
1280 fn get_eviction_threshold(&self, budget_type: BudgetType) -> usize {
1282 if budget_type == BudgetType::Standalone {
1283 return 16 * 1024 * 1024;
1288 }
1289
1290 let expected_texture_count = match budget_type {
1297 BudgetType::SharedColor8Nearest | BudgetType::SharedAlpha16 => {
1298 1
1301 },
1302
1303 _ => {
1304 2
1306 },
1307 };
1308
1309 let average_used_bytes_per_texture_when_full =
1319 self.shared_textures.bytes_per_shared_texture(budget_type) / 2;
1320
1321 expected_texture_count * average_used_bytes_per_texture_when_full
1329 }
1330
1331 fn evict_items_from_cache_if_required(&mut self, profile: &mut TransactionProfile) {
1334 let previous_frame_id = self.now.frame_id() - 1;
1335 let mut eviction_count = 0;
1336 let mut youngest_evicted = FrameId::first();
1337
1338 for budget in BudgetType::iter() {
1339 let threshold = self.get_eviction_threshold(budget);
1340 while self.should_continue_evicting(
1341 self.bytes_allocated[budget as usize],
1342 threshold,
1343 eviction_count,
1344 ) {
1345 if let Some(entry) = self.lru_cache.peek_oldest(budget as u8) {
1346 if entry.last_access.frame_id() >= previous_frame_id {
1350 break;
1353 }
1354 if entry.last_access.frame_id() > youngest_evicted {
1355 youngest_evicted = entry.last_access.frame_id();
1356 }
1357 let entry = self.lru_cache.pop_oldest(budget as u8).unwrap();
1358 entry.evict();
1359 self.free(&entry);
1360 eviction_count += 1;
1361 } else {
1362 break;
1367 }
1368 }
1369 }
1370
1371 if eviction_count > 0 {
1372 profile.set(profiler::TEXTURE_CACHE_EVICTION_COUNT, eviction_count);
1373 profile.set(
1374 profiler::TEXTURE_CACHE_YOUNGEST_EVICTION,
1375 self.now.frame_id().as_usize() - youngest_evicted.as_usize()
1376 );
1377 }
1378 }
1379
1380 fn should_continue_evicting(
1382 &self,
1383 bytes_allocated: usize,
1384 threshold: usize,
1385 eviction_count: usize,
1386 ) -> bool {
1387 if bytes_allocated < threshold {
1389 return false;
1390 }
1391
1392 if bytes_allocated > 4 * threshold {
1394 return true;
1395 }
1396
1397 eviction_count < Self::MAX_EVICTIONS_PER_FRAME
1400 }
1401
1402 fn free(&mut self, entry: &CacheEntry) {
1404 match entry.details {
1405 EntryDetails::Picture { size } => {
1406 self.picture_textures.free_tile(entry.texture_id, self.now.frame_id());
1407 if self.debug_flags.contains(
1408 DebugFlags::TEXTURE_CACHE_DBG |
1409 DebugFlags::TEXTURE_CACHE_DBG_CLEAR_EVICTED)
1410 {
1411 self.pending_updates.push_debug_clear(
1412 entry.texture_id,
1413 DeviceIntPoint::zero(),
1414 size.width,
1415 size.height,
1416 );
1417 }
1418 }
1419 EntryDetails::Standalone { size_in_bytes, .. } => {
1420 self.bytes_allocated[BudgetType::Standalone as usize] -= size_in_bytes;
1421
1422 self.pending_updates.push_free(entry.texture_id);
1424 }
1425 EntryDetails::Cache { origin, alloc_id, allocated_size_in_bytes } => {
1426 let (allocator_list, budget_type) = self.shared_textures.select(
1427 entry.input_format,
1428 entry.filter,
1429 entry.shader,
1430 );
1431
1432 allocator_list.deallocate(entry.texture_id, alloc_id);
1433
1434 self.bytes_allocated[budget_type as usize] -= allocated_size_in_bytes;
1435
1436 if self.debug_flags.contains(
1437 DebugFlags::TEXTURE_CACHE_DBG |
1438 DebugFlags::TEXTURE_CACHE_DBG_CLEAR_EVICTED)
1439 {
1440 self.pending_updates.push_debug_clear(
1441 entry.texture_id,
1442 origin,
1443 entry.size.width,
1444 entry.size.height,
1445 );
1446 }
1447 }
1448 }
1449 }
1450
1451 fn allocate_from_shared_cache(
1453 &mut self,
1454 params: &CacheAllocParams,
1455 ) -> (CacheEntry, BudgetType) {
1456 let (allocator_list, budget_type) = self.shared_textures.select(
1457 params.descriptor.format,
1458 params.filter,
1459 params.shader,
1460 );
1461
1462 let next_id = &mut self.next_id;
1464 let pending_updates = &mut self.pending_updates;
1465
1466 let (texture_id, alloc_id, allocated_rect) = allocator_list.allocate(
1467 params.descriptor.size,
1468 &mut |size, parameters| {
1469 let texture_id = *next_id;
1470 next_id.0 += 1;
1471 pending_updates.push_alloc(
1472 texture_id,
1473 TextureCacheAllocInfo {
1474 target: ImageBufferKind::Texture2D,
1475 width: size.width,
1476 height: size.height,
1477 format: parameters.formats.internal,
1478 filter: parameters.filter,
1479 is_shared_cache: true,
1480 has_depth: false,
1481 category: TextureCacheCategory::Atlas,
1482 },
1483 );
1484
1485 texture_id
1486 },
1487 );
1488
1489 let formats = &allocator_list.texture_parameters().formats;
1490
1491 let swizzle = if formats.external == params.descriptor.format {
1492 Swizzle::default()
1493 } else {
1494 match self.swizzle {
1495 Some(_) => Swizzle::Bgra,
1496 None => Swizzle::default(),
1497 }
1498 };
1499
1500 let bpp = formats.internal.bytes_per_pixel();
1501 let allocated_size_in_bytes = (allocated_rect.area() * bpp) as usize;
1502 self.bytes_allocated[budget_type as usize] += allocated_size_in_bytes;
1503
1504 (CacheEntry {
1505 size: params.descriptor.size,
1506 user_data: params.user_data,
1507 last_access: self.now,
1508 details: EntryDetails::Cache {
1509 origin: allocated_rect.min,
1510 alloc_id,
1511 allocated_size_in_bytes,
1512 },
1513 uv_rect_handle: GpuCacheHandle::new(),
1514 input_format: params.descriptor.format,
1515 filter: params.filter,
1516 swizzle,
1517 texture_id,
1518 eviction_notice: None,
1519 uv_rect_kind: params.uv_rect_kind,
1520 shader: params.shader
1521 }, budget_type)
1522 }
1523
1524 pub fn is_allowed_in_shared_cache(
1527 &self,
1528 filter: TextureFilter,
1529 descriptor: &ImageDescriptor,
1530 ) -> bool {
1531 let mut allowed_in_shared_cache = true;
1532
1533 if descriptor.size.width > TEXTURE_REGION_DIMENSIONS ||
1538 descriptor.size.height > TEXTURE_REGION_DIMENSIONS
1539 {
1540 allowed_in_shared_cache = false;
1541 }
1542
1543 if filter == TextureFilter::Nearest &&
1547 descriptor.format.bytes_per_pixel() <= 2
1548 {
1549 allowed_in_shared_cache = false;
1550 }
1551
1552 allowed_in_shared_cache
1553 }
1554
1555 pub fn alloc_render_target(
1557 &mut self,
1558 size: DeviceIntSize,
1559 format: ImageFormat,
1560 ) -> CacheTextureId {
1561 let texture_id = self.next_id;
1562 self.next_id.0 += 1;
1563
1564 let info = TextureCacheAllocInfo {
1566 target: ImageBufferKind::Texture2D,
1567 width: size.width,
1568 height: size.height,
1569 format,
1570 filter: TextureFilter::Linear,
1571 is_shared_cache: false,
1572 has_depth: false,
1573 category: TextureCacheCategory::RenderTarget,
1574 };
1575
1576 self.pending_updates.push_alloc(texture_id, info);
1577
1578 texture_id
1579 }
1580
1581 pub fn free_render_target(
1583 &mut self,
1584 id: CacheTextureId,
1585 ) {
1586 self.pending_updates.push_free(id);
1587 }
1588
1589 fn allocate_standalone_entry(
1591 &mut self,
1592 params: &CacheAllocParams,
1593 ) -> (CacheEntry, BudgetType) {
1594 let texture_id = self.next_id;
1595 self.next_id.0 += 1;
1596
1597 let info = TextureCacheAllocInfo {
1599 target: ImageBufferKind::Texture2D,
1600 width: params.descriptor.size.width,
1601 height: params.descriptor.size.height,
1602 format: params.descriptor.format,
1603 filter: params.filter,
1604 is_shared_cache: false,
1605 has_depth: false,
1606 category: TextureCacheCategory::Standalone,
1607 };
1608
1609 let size_in_bytes = (info.width * info.height * info.format.bytes_per_pixel()) as usize;
1610 self.bytes_allocated[BudgetType::Standalone as usize] += size_in_bytes;
1611
1612 self.pending_updates.push_alloc(texture_id, info);
1613
1614 let swizzle = if params.descriptor.format == ImageFormat::BGRA8 {
1616 self.swizzle.map(|s| s.bgra8_sampling_swizzle)
1617 } else {
1618 None
1619 };
1620
1621 (CacheEntry::new_standalone(
1622 texture_id,
1623 self.now,
1624 params,
1625 swizzle.unwrap_or_default(),
1626 size_in_bytes,
1627 ), BudgetType::Standalone)
1628 }
1629
1630 fn allocate_cache_entry(
1636 &mut self,
1637 params: &CacheAllocParams,
1638 ) -> (CacheEntry, BudgetType) {
1639 assert!(!params.descriptor.size.is_empty());
1640
1641 if self.is_allowed_in_shared_cache(params.filter, ¶ms.descriptor) {
1644 self.allocate_from_shared_cache(params)
1645 } else {
1646 self.allocate_standalone_entry(params)
1647 }
1648 }
1649
1650 fn allocate(
1653 &mut self,
1654 params: &CacheAllocParams,
1655 handle: &mut TextureCacheHandle,
1656 eviction: Eviction,
1657 ) {
1658 debug_assert!(self.now.is_valid());
1659 let (new_cache_entry, budget_type) = self.allocate_cache_entry(params);
1660
1661 let old_entry = match (&mut *handle, eviction) {
1669 (TextureCacheHandle::Auto(handle), Eviction::Auto) => {
1670 self.lru_cache.replace_or_insert(handle, budget_type as u8, new_cache_entry)
1671 },
1672 (TextureCacheHandle::Manual(handle), Eviction::Manual) => {
1673 let entry = self.manual_entries.get_opt_mut(handle)
1674 .expect("Don't call this after evicting");
1675 Some(mem::replace(entry, new_cache_entry))
1676 },
1677 (TextureCacheHandle::Manual(_), Eviction::Auto) |
1678 (TextureCacheHandle::Auto(_), Eviction::Manual) => {
1679 panic!("Can't change eviction policy after initial allocation");
1680 },
1681 (TextureCacheHandle::Empty, Eviction::Auto) => {
1682 let new_handle = self.lru_cache.push_new(budget_type as u8, new_cache_entry);
1683 *handle = TextureCacheHandle::Auto(new_handle);
1684 None
1685 },
1686 (TextureCacheHandle::Empty, Eviction::Manual) => {
1687 let manual_handle = self.manual_entries.insert(new_cache_entry);
1688 let new_handle = manual_handle.weak();
1689 self.manual_handles.push(manual_handle);
1690 *handle = TextureCacheHandle::Manual(new_handle);
1691 None
1692 },
1693 (TextureCacheHandle::Picture(_), _) => {
1694 panic!("Picture cache entries are managed separately and shouldn't appear in this function");
1695 },
1696 };
1697 if let Some(old_entry) = old_entry {
1698 old_entry.evict();
1699 self.free(&old_entry);
1700 }
1701 }
1702
1703 pub fn update_picture_cache(
1705 &mut self,
1706 tile_size: DeviceIntSize,
1707 handle: &mut TextureCacheHandle,
1708 gpu_cache: &mut GpuCache,
1709 ) {
1710 debug_assert!(self.now.is_valid());
1711 debug_assert!(tile_size.width > 0 && tile_size.height > 0);
1712
1713 let need_alloc = match handle {
1714 TextureCacheHandle::Empty => true,
1715 TextureCacheHandle::Picture(handle) => {
1716 self.picture_cache_entries.get_opt(handle).is_none()
1718 },
1719 TextureCacheHandle::Auto(_) | TextureCacheHandle::Manual(_) => {
1720 panic!("Unexpected handle type in update_picture_cache");
1721 }
1722 };
1723
1724 if need_alloc {
1725 let cache_entry = self.picture_textures.get_or_allocate_tile(
1726 tile_size,
1727 self.now,
1728 &mut self.next_id,
1729 &mut self.pending_updates,
1730 );
1731
1732 let strong_handle = self.picture_cache_entries.insert(cache_entry);
1734 let new_handle = strong_handle.weak();
1735
1736 self.picture_cache_handles.push(strong_handle);
1737
1738 *handle = TextureCacheHandle::Picture(new_handle);
1739 }
1740
1741 if let TextureCacheHandle::Picture(handle) = handle {
1742 self.picture_cache_entries
1744 .get_opt_mut(handle)
1745 .expect("BUG: handle must be valid now")
1746 .update_gpu_cache(gpu_cache);
1747 } else {
1748 panic!("The handle should be valid picture cache handle now")
1749 }
1750 }
1751
1752 pub fn shared_alpha_expected_format(&self) -> ImageFormat {
1753 self.shared_textures.alpha8_linear.texture_parameters().formats.external
1754 }
1755
1756 pub fn shared_color_expected_format(&self) -> ImageFormat {
1757 self.shared_textures.color8_linear.texture_parameters().formats.external
1758 }
1759
1760
1761 pub fn default_picture_tile_size(&self) -> DeviceIntSize {
1762 self.picture_textures.default_tile_size
1763 }
1764
1765 #[cfg(test)]
1766 pub fn total_allocated_bytes_for_testing(&self) -> usize {
1767 BudgetType::iter().map(|b| self.bytes_allocated[b as usize]).sum()
1768 }
1769
1770 pub fn report_memory(&self, ops: &mut MallocSizeOfOps) -> usize {
1771 self.lru_cache.size_of(ops)
1772 }
1773}
1774
1775#[cfg_attr(feature = "capture", derive(Serialize))]
1776#[cfg_attr(feature = "replay", derive(Deserialize))]
1777pub struct TextureParameters {
1778 pub formats: TextureFormatPair<ImageFormat>,
1779 pub filter: TextureFilter,
1780}
1781
1782impl TextureCacheUpdate {
1783 fn new_update(
1787 data: CachedImageData,
1788 descriptor: &ImageDescriptor,
1789 origin: DeviceIntPoint,
1790 size: DeviceIntSize,
1791 use_upload_format: bool,
1792 dirty_rect: &ImageDirtyRect,
1793 ) -> TextureCacheUpdate {
1794 let source = match data {
1795 CachedImageData::Blob => {
1796 panic!("The vector image should have been rasterized.");
1797 }
1798 CachedImageData::External(ext_image) => match ext_image.image_type {
1799 ExternalImageType::TextureHandle(_) => {
1800 panic!("External texture handle should not go through texture_cache.");
1801 }
1802 ExternalImageType::Buffer => TextureUpdateSource::External {
1803 id: ext_image.id,
1804 channel_index: ext_image.channel_index,
1805 },
1806 },
1807 CachedImageData::Raw(bytes) => {
1808 let finish = descriptor.offset +
1809 descriptor.size.width * descriptor.format.bytes_per_pixel() +
1810 (descriptor.size.height - 1) * descriptor.compute_stride();
1811 assert!(bytes.len() >= finish as usize);
1812
1813 TextureUpdateSource::Bytes { data: bytes }
1814 }
1815 };
1816 let format_override = if use_upload_format {
1817 Some(descriptor.format)
1818 } else {
1819 None
1820 };
1821
1822 match *dirty_rect {
1823 DirtyRect::Partial(dirty) => {
1824 let stride = descriptor.compute_stride();
1826 let offset = descriptor.offset + dirty.min.y * stride + dirty.min.x * descriptor.format.bytes_per_pixel();
1827
1828 TextureCacheUpdate {
1829 rect: DeviceIntRect::from_origin_and_size(
1830 DeviceIntPoint::new(origin.x + dirty.min.x, origin.y + dirty.min.y),
1831 DeviceIntSize::new(
1832 dirty.width().min(size.width - dirty.min.x),
1833 dirty.height().min(size.height - dirty.min.y),
1834 ),
1835 ),
1836 source,
1837 stride: Some(stride),
1838 offset,
1839 format_override,
1840 }
1841 }
1842 DirtyRect::All => {
1843 TextureCacheUpdate {
1844 rect: DeviceIntRect::from_origin_and_size(origin, size),
1845 source,
1846 stride: descriptor.stride,
1847 offset: descriptor.offset,
1848 format_override,
1849 }
1850 }
1851 }
1852 }
1853}
1854
1855#[cfg(test)]
1856mod test_texture_cache {
1857 #[test]
1858 fn check_allocation_size_balance() {
1859 use crate::texture_cache::{TextureCache, TextureCacheHandle, Eviction, TargetShader};
1864 use crate::gpu_cache::GpuCache;
1865 use crate::device::TextureFilter;
1866 use crate::gpu_types::UvRectKind;
1867 use api::{ImageDescriptor, ImageDescriptorFlags, ImageFormat, DirtyRect};
1868 use api::units::*;
1869 use euclid::size2;
1870 let mut gpu_cache = GpuCache::new_for_testing();
1871 let mut texture_cache = TextureCache::new_for_testing(2048, ImageFormat::BGRA8);
1872
1873 let sizes: &[DeviceIntSize] = &[
1874 size2(23, 27),
1875 size2(15, 22),
1876 size2(11, 5),
1877 size2(20, 25),
1878 size2(38, 41),
1879 size2(11, 19),
1880 size2(13, 21),
1881 size2(37, 40),
1882 size2(13, 15),
1883 size2(14, 16),
1884 size2(10, 9),
1885 size2(25, 28),
1886 ];
1887
1888 let bytes_at_start = texture_cache.total_allocated_bytes_for_testing();
1889
1890 let handles: Vec<TextureCacheHandle> = sizes.iter().map(|size| {
1891 let mut texture_cache_handle = TextureCacheHandle::invalid();
1892 texture_cache.request(&texture_cache_handle, &mut gpu_cache);
1893 texture_cache.update(
1894 &mut texture_cache_handle,
1895 ImageDescriptor {
1896 size: *size,
1897 stride: None,
1898 format: ImageFormat::BGRA8,
1899 flags: ImageDescriptorFlags::empty(),
1900 offset: 0,
1901 },
1902 TextureFilter::Linear,
1903 None,
1904 [0.0; 4],
1905 DirtyRect::All,
1906 &mut gpu_cache,
1907 None,
1908 UvRectKind::Rect,
1909 Eviction::Manual,
1910 TargetShader::Text,
1911 );
1912 texture_cache_handle
1913 }).collect();
1914
1915 let bytes_after_allocating = texture_cache.total_allocated_bytes_for_testing();
1916 assert!(bytes_after_allocating > bytes_at_start);
1917
1918 for handle in handles {
1919 texture_cache.evict_handle(&handle);
1920 }
1921
1922 let bytes_at_end = texture_cache.total_allocated_bytes_for_testing();
1923 assert_eq!(bytes_at_end, bytes_at_start);
1924 }
1925}