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}