1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 use api::{ColorF, DeviceUintPoint, DeviceUintRect, DeviceUintSize};
6 use api::{ExternalImageType, ImageData, ImageFormat, PremultipliedColorF};
7 use api::ImageDescriptor;
8 use device::TextureFilter;
9 use freelist::{FreeList, FreeListHandle, UpsertResult, WeakFreeListHandle};
10 use gpu_cache::{GpuCache, GpuCacheHandle};
11 use gpu_types::ImageSource;
12 use internal_types::{CacheTextureId, FastHashMap, TextureUpdateList, TextureUpdateSource};
13 use internal_types::{RenderTargetInfo, SourceTexture, TextureUpdate, TextureUpdateOp};
14 use profiler::{ResourceProfileCounter, TextureCacheProfileCounters};
15 use render_backend::FrameId;
16 use resource_cache::CacheItem;
17 use std::cmp;
18 use std::mem;
19 
20 // The fixed number of layers for the shared texture cache.
21 // There is one array texture per image format, allocated lazily.
22 const TEXTURE_ARRAY_LAYERS_LINEAR: usize = 4;
23 const TEXTURE_ARRAY_LAYERS_NEAREST: usize = 1;
24 
25 // The dimensions of each layer in the texture cache.
26 const TEXTURE_LAYER_DIMENSIONS: u32 = 2048;
27 
28 // The size of each region (page) in a texture layer.
29 const TEXTURE_REGION_DIMENSIONS: u32 = 512;
30 
31 // Maintains a simple freelist of texture IDs that are mapped
32 // to real API-specific texture IDs in the renderer.
33 #[cfg_attr(feature = "capture", derive(Serialize))]
34 #[cfg_attr(feature = "replay", derive(Deserialize))]
35 struct CacheTextureIdList {
36     free_lists: FastHashMap<ImageFormat, Vec<CacheTextureId>>,
37     next_id: usize,
38 }
39 
40 impl CacheTextureIdList {
new() -> Self41     fn new() -> Self {
42         CacheTextureIdList {
43             next_id: 0,
44             free_lists: FastHashMap::default(),
45         }
46     }
47 
allocate(&mut self, format: ImageFormat) -> CacheTextureId48     fn allocate(&mut self, format: ImageFormat) -> CacheTextureId {
49         // If nothing on the free list of texture IDs,
50         // allocate a new one.
51         self.free_lists.get_mut(&format)
52             .and_then(|fl| fl.pop())
53             .unwrap_or_else(|| {
54                 self.next_id += 1;
55                 CacheTextureId(self.next_id - 1)
56             })
57     }
58 
free(&mut self, id: CacheTextureId, format: ImageFormat)59     fn free(&mut self, id: CacheTextureId, format: ImageFormat) {
60         self.free_lists
61             .entry(format)
62             .or_insert(Vec::new())
63             .push(id);
64     }
65 }
66 
67 // Items in the texture cache can either be standalone textures,
68 // or a sub-rect inside the shared cache.
69 #[derive(Debug)]
70 #[cfg_attr(feature = "capture", derive(Serialize))]
71 #[cfg_attr(feature = "replay", derive(Deserialize))]
72 enum EntryKind {
73     Standalone,
74     Cache {
75         // Origin within the texture layer where this item exists.
76         origin: DeviceUintPoint,
77         // The layer index of the texture array.
78         layer_index: u16,
79         // The region that this entry belongs to in the layer.
80         region_index: u16,
81     },
82 }
83 
84 // Stores information related to a single entry in the texture
85 // cache. This is stored for each item whether it's in the shared
86 // cache or a standalone texture.
87 #[derive(Debug)]
88 #[cfg_attr(feature = "capture", derive(Serialize))]
89 #[cfg_attr(feature = "replay", derive(Deserialize))]
90 struct CacheEntry {
91     // Size the requested item, in device pixels.
92     size: DeviceUintSize,
93     // Details specific to standalone or shared items.
94     kind: EntryKind,
95     // Arbitrary user data associated with this item.
96     user_data: [f32; 3],
97     // The last frame this item was requested for rendering.
98     last_access: FrameId,
99     // Handle to the resource rect in the GPU cache.
100     uv_rect_handle: GpuCacheHandle,
101     // Image format of the item.
102     format: ImageFormat,
103     filter: TextureFilter,
104     // The actual device texture ID this is part of.
105     texture_id: CacheTextureId,
106     // Color to modulate this cache item by.
107     color: PremultipliedColorF,
108 }
109 
110 impl CacheEntry {
111     // Create a new entry for a standalone texture.
new_standalone( texture_id: CacheTextureId, size: DeviceUintSize, format: ImageFormat, filter: TextureFilter, user_data: [f32; 3], last_access: FrameId, ) -> Self112     fn new_standalone(
113         texture_id: CacheTextureId,
114         size: DeviceUintSize,
115         format: ImageFormat,
116         filter: TextureFilter,
117         user_data: [f32; 3],
118         last_access: FrameId,
119     ) -> Self {
120         CacheEntry {
121             size,
122             user_data,
123             last_access,
124             kind: EntryKind::Standalone,
125             texture_id,
126             format,
127             filter,
128             uv_rect_handle: GpuCacheHandle::new(),
129             color: ColorF::new(1.0, 1.0, 1.0, 1.0).premultiplied(),
130         }
131     }
132 
133     // Update the GPU cache for this texture cache entry.
134     // This ensures that the UV rect, and texture layer index
135     // are up to date in the GPU cache for vertex shaders
136     // to fetch from.
update_gpu_cache(&mut self, gpu_cache: &mut GpuCache)137     fn update_gpu_cache(&mut self, gpu_cache: &mut GpuCache) {
138         if let Some(mut request) = gpu_cache.request(&mut self.uv_rect_handle) {
139             let (origin, layer_index) = match self.kind {
140                 EntryKind::Standalone { .. } => (DeviceUintPoint::zero(), 0.0),
141                 EntryKind::Cache {
142                     origin,
143                     layer_index,
144                     ..
145                 } => (origin, layer_index as f32),
146             };
147             let image_source = ImageSource {
148                 p0: origin.to_f32(),
149                 p1: (origin + self.size).to_f32(),
150                 color: self.color,
151                 texture_layer: layer_index,
152                 user_data: self.user_data,
153             };
154             image_source.write_gpu_blocks(&mut request);
155         }
156     }
157 }
158 
159 type WeakCacheEntryHandle = WeakFreeListHandle<CacheEntry>;
160 
161 // A texture cache handle is a weak reference to a cache entry.
162 // If the handle has not been inserted into the cache yet, the
163 // value will be None. Even when the value is Some(), the location
164 // may not actually be valid if it has been evicted by the cache.
165 // In this case, the cache handle needs to re-upload this item
166 // to the texture cache (see request() below).
167 #[derive(Debug)]
168 #[cfg_attr(feature = "capture", derive(Clone, Serialize))]
169 #[cfg_attr(feature = "replay", derive(Deserialize))]
170 pub struct TextureCacheHandle {
171     entry: Option<WeakCacheEntryHandle>,
172 }
173 
174 impl TextureCacheHandle {
new() -> Self175     pub fn new() -> Self {
176         TextureCacheHandle { entry: None }
177     }
178 }
179 
180 #[cfg_attr(feature = "capture", derive(Serialize))]
181 #[cfg_attr(feature = "replay", derive(Deserialize))]
182 pub struct TextureCache {
183     // A lazily allocated, fixed size, texture array for
184     // each format the texture cache supports.
185     array_rgba8_nearest: TextureArray,
186     array_a8_linear: TextureArray,
187     array_rgba8_linear: TextureArray,
188 
189     // Maximum texture size supported by hardware.
190     max_texture_size: u32,
191 
192     // A list of texture IDs that represent native
193     // texture handles. This indirection allows the texture
194     // cache to create / destroy / reuse texture handles
195     // without knowing anything about the device code.
196     cache_textures: CacheTextureIdList,
197 
198     // A list of updates that need to be applied to the
199     // texture cache in the rendering thread this frame.
200     #[cfg_attr(feature = "serde", serde(skip))]
201     pending_updates: TextureUpdateList,
202 
203     // The current frame ID. Used for cache eviction policies.
204     frame_id: FrameId,
205 
206     // Maintains the list of all current items in
207     // the texture cache.
208     entries: FreeList<CacheEntry>,
209 
210     // A list of the strong handles of items that were
211     // allocated in the standalone texture pool. Used
212     // for evicting old standalone textures.
213     standalone_entry_handles: Vec<FreeListHandle<CacheEntry>>,
214 
215     // A list of the strong handles of items that were
216     // allocated in the shared texture cache. Used
217     // for evicting old cache items.
218     shared_entry_handles: Vec<FreeListHandle<CacheEntry>>,
219 }
220 
221 impl TextureCache {
new(max_texture_size: u32) -> Self222     pub fn new(max_texture_size: u32) -> Self {
223         TextureCache {
224             max_texture_size,
225             array_a8_linear: TextureArray::new(
226                 ImageFormat::R8,
227                 TextureFilter::Linear,
228                 TEXTURE_ARRAY_LAYERS_LINEAR,
229             ),
230             array_rgba8_linear: TextureArray::new(
231                 ImageFormat::BGRA8,
232                 TextureFilter::Linear,
233                 TEXTURE_ARRAY_LAYERS_LINEAR,
234             ),
235             array_rgba8_nearest: TextureArray::new(
236                 ImageFormat::BGRA8,
237                 TextureFilter::Nearest,
238                 TEXTURE_ARRAY_LAYERS_NEAREST,
239             ),
240             cache_textures: CacheTextureIdList::new(),
241             pending_updates: TextureUpdateList::new(),
242             frame_id: FrameId(0),
243             entries: FreeList::new(),
244             standalone_entry_handles: Vec::new(),
245             shared_entry_handles: Vec::new(),
246         }
247     }
248 
begin_frame(&mut self, frame_id: FrameId)249     pub fn begin_frame(&mut self, frame_id: FrameId) {
250         self.frame_id = frame_id;
251     }
252 
end_frame(&mut self, texture_cache_profile: &mut TextureCacheProfileCounters)253     pub fn end_frame(&mut self, texture_cache_profile: &mut TextureCacheProfileCounters) {
254         self.expire_old_standalone_entries();
255 
256         self.array_a8_linear
257             .update_profile(&mut texture_cache_profile.pages_a8_linear);
258         self.array_rgba8_linear
259             .update_profile(&mut texture_cache_profile.pages_rgba8_linear);
260         self.array_rgba8_nearest
261             .update_profile(&mut texture_cache_profile.pages_rgba8_nearest);
262     }
263 
264     // Request an item in the texture cache. All images that will
265     // be used on a frame *must* have request() called on their
266     // handle, to update the last used timestamp and ensure
267     // that resources are not flushed from the cache too early.
268     //
269     // Returns true if the image needs to be uploaded to the
270     // texture cache (either never uploaded, or has been
271     // evicted on a previous frame).
request(&mut self, handle: &TextureCacheHandle, gpu_cache: &mut GpuCache) -> bool272     pub fn request(&mut self, handle: &TextureCacheHandle, gpu_cache: &mut GpuCache) -> bool {
273         match handle.entry {
274             Some(ref handle) => {
275                 match self.entries.get_opt_mut(handle) {
276                     // If an image is requested that is already in the cache,
277                     // refresh the GPU cache data associated with this item.
278                     Some(entry) => {
279                         entry.last_access = self.frame_id;
280                         entry.update_gpu_cache(gpu_cache);
281                         false
282                     }
283                     None => true,
284                 }
285             }
286             None => true,
287         }
288     }
289 
max_texture_size(&self) -> u32290     pub fn max_texture_size(&self) -> u32 {
291         self.max_texture_size
292     }
293 
pending_updates(&mut self) -> TextureUpdateList294     pub fn pending_updates(&mut self) -> TextureUpdateList {
295         mem::replace(&mut self.pending_updates, TextureUpdateList::new())
296     }
297 
298     // Update the data stored by a given texture cache handle.
update( &mut self, handle: &mut TextureCacheHandle, descriptor: ImageDescriptor, filter: TextureFilter, data: Option<ImageData>, user_data: [f32; 3], mut dirty_rect: Option<DeviceUintRect>, gpu_cache: &mut GpuCache, )299     pub fn update(
300         &mut self,
301         handle: &mut TextureCacheHandle,
302         descriptor: ImageDescriptor,
303         filter: TextureFilter,
304         data: Option<ImageData>,
305         user_data: [f32; 3],
306         mut dirty_rect: Option<DeviceUintRect>,
307         gpu_cache: &mut GpuCache,
308     ) {
309         // Determine if we need to allocate texture cache memory
310         // for this item. We need to reallocate if any of the following
311         // is true:
312         // - Never been in the cache
313         // - Has been in the cache but was evicted.
314         // - Exists in the cache but dimensions / format have changed.
315         let realloc = match handle.entry {
316             Some(ref handle) => {
317                 match self.entries.get_opt(handle) {
318                     Some(entry) => {
319                         entry.size.width != descriptor.width ||
320                             entry.size.height != descriptor.height ||
321                             entry.format != descriptor.format
322                     }
323                     None => {
324                         // Was previously allocated but has been evicted.
325                         true
326                     }
327                 }
328             }
329             None => {
330                 // This handle has not been allocated yet.
331                 true
332             }
333         };
334 
335         if realloc {
336             self.allocate(handle, descriptor, filter, user_data);
337 
338             // If we reallocated, we need to upload the whole item again.
339             dirty_rect = None;
340         }
341 
342         let entry = self.entries
343             .get_opt_mut(handle.entry.as_ref().unwrap())
344             .expect("BUG: handle must be valid now");
345 
346         // Invalidate the contents of the resource rect in the GPU cache.
347         // This ensures that the update_gpu_cache below will add
348         // the new information to the GPU cache.
349         gpu_cache.invalidate(&entry.uv_rect_handle);
350 
351         // Upload the resource rect and texture array layer.
352         entry.update_gpu_cache(gpu_cache);
353 
354         // Create an update command, which the render thread processes
355         // to upload the new image data into the correct location
356         // in GPU memory.
357         if let Some(data) = data {
358             let (layer_index, origin) = match entry.kind {
359                 EntryKind::Standalone { .. } => (0, DeviceUintPoint::zero()),
360                 EntryKind::Cache {
361                     layer_index,
362                     origin,
363                     ..
364                 } => (layer_index, origin),
365             };
366 
367             let op = TextureUpdate::new_update(
368                 data,
369                 &descriptor,
370                 origin,
371                 entry.size,
372                 entry.texture_id,
373                 layer_index as i32,
374                 dirty_rect,
375             );
376             self.pending_updates.push(op);
377         }
378     }
379 
380     // Get a specific region by index from a shared texture array.
get_region_mut(&mut self, format: ImageFormat, filter: TextureFilter, region_index: u16 ) -> &mut TextureRegion381     fn get_region_mut(&mut self,
382         format: ImageFormat,
383         filter: TextureFilter,
384         region_index: u16
385     ) -> &mut TextureRegion {
386         let texture_array = match (format, filter) {
387             (ImageFormat::R8, TextureFilter::Linear) => &mut self.array_a8_linear,
388             (ImageFormat::BGRA8, TextureFilter::Linear) => &mut self.array_rgba8_linear,
389             (ImageFormat::BGRA8, TextureFilter::Nearest) => &mut self.array_rgba8_nearest,
390             (ImageFormat::RGBAF32, _) |
391             (ImageFormat::RG8, _) |
392             (ImageFormat::R8, TextureFilter::Nearest) |
393             (ImageFormat::R8, TextureFilter::Trilinear) |
394             (ImageFormat::BGRA8, TextureFilter::Trilinear) => unreachable!(),
395         };
396 
397         &mut texture_array.regions[region_index as usize]
398     }
399 
400     // Check if a given texture handle has a valid allocation
401     // in the texture cache.
is_allocated(&self, handle: &TextureCacheHandle) -> bool402     pub fn is_allocated(&self, handle: &TextureCacheHandle) -> bool {
403         handle.entry.as_ref().map_or(false, |handle| {
404             self.entries.get_opt(handle).is_some()
405         })
406     }
407 
408     // Retrieve the details of an item in the cache. This is used
409     // during batch creation to provide the resource rect address
410     // to the shaders and texture ID to the batching logic.
411     // This function will asssert in debug modes if the caller
412     // tries to get a handle that was not requested this frame.
get(&self, handle: &TextureCacheHandle) -> CacheItem413     pub fn get(&self, handle: &TextureCacheHandle) -> CacheItem {
414         match handle.entry {
415             Some(ref handle) => {
416                 let entry = self.entries
417                     .get_opt(handle)
418                     .expect("BUG: was dropped from cache or not updated!");
419                 debug_assert_eq!(entry.last_access, self.frame_id);
420                 let (layer_index, origin) = match entry.kind {
421                     EntryKind::Standalone { .. } => {
422                         (0, DeviceUintPoint::zero())
423                     }
424                     EntryKind::Cache {
425                         layer_index,
426                         origin,
427                         ..
428                     } => (layer_index, origin),
429                 };
430                 CacheItem {
431                     uv_rect_handle: entry.uv_rect_handle,
432                     texture_id: SourceTexture::TextureCache(entry.texture_id),
433                     uv_rect: DeviceUintRect::new(origin, entry.size),
434                     texture_layer: layer_index as i32,
435                 }
436             }
437             None => panic!("BUG: handle not requested earlier in frame"),
438         }
439     }
440 
441     // A more detailed version of get(). This allows access to the actual
442     // device rect of the cache allocation.
get_cache_location( &self, handle: &TextureCacheHandle, ) -> (SourceTexture, i32, DeviceUintRect)443     pub fn get_cache_location(
444         &self,
445         handle: &TextureCacheHandle,
446     ) -> (SourceTexture, i32, DeviceUintRect) {
447         let handle = handle
448             .entry
449             .as_ref()
450             .expect("BUG: handle not requested earlier in frame");
451 
452         let entry = self.entries
453             .get_opt(handle)
454             .expect("BUG: was dropped from cache or not updated!");
455         debug_assert_eq!(entry.last_access, self.frame_id);
456         let (layer_index, origin) = match entry.kind {
457             EntryKind::Standalone { .. } => {
458                 (0, DeviceUintPoint::zero())
459             }
460             EntryKind::Cache {
461                 layer_index,
462                 origin,
463                 ..
464             } => (layer_index, origin),
465         };
466         (SourceTexture::TextureCache(entry.texture_id),
467          layer_index as i32,
468          DeviceUintRect::new(origin, entry.size))
469     }
470 
471     // Expire old standalone textures.
expire_old_standalone_entries(&mut self)472     fn expire_old_standalone_entries(&mut self) {
473         let mut eviction_candidates = Vec::new();
474         let mut retained_entries = Vec::new();
475 
476         // Build a list of eviction candidates (which are
477         // anything not used this frame).
478         for handle in self.standalone_entry_handles.drain(..) {
479             let entry = self.entries.get(&handle);
480             if entry.last_access == self.frame_id {
481                 retained_entries.push(handle);
482             } else {
483                 eviction_candidates.push(handle);
484             }
485         }
486 
487         // Sort by access time so we remove the oldest ones first.
488         eviction_candidates.sort_by_key(|handle| {
489             let entry = self.entries.get(handle);
490             entry.last_access
491         });
492 
493         // We only allow an arbitrary number of unused
494         // standalone textures to remain in GPU memory.
495         // TODO(gw): We should make this a better heuristic,
496         //           for example based on total memory size.
497         if eviction_candidates.len() > 32 {
498             let entries_to_keep = eviction_candidates.split_off(32);
499             retained_entries.extend(entries_to_keep);
500         }
501 
502         // Free the selected items
503         for handle in eviction_candidates {
504             let entry = self.entries.free(handle);
505             self.free(entry);
506         }
507 
508         // Keep a record of the remaining handles for next frame.
509         self.standalone_entry_handles = retained_entries;
510     }
511 
512     // Expire old shared items. Pass in the allocation size
513     // that is being requested, so we know when we've evicted
514     // enough items to guarantee we can fit this allocation in
515     // the cache.
expire_old_shared_entries(&mut self, required_alloc: &ImageDescriptor)516     fn expire_old_shared_entries(&mut self, required_alloc: &ImageDescriptor) {
517         let mut eviction_candidates = Vec::new();
518         let mut retained_entries = Vec::new();
519 
520         // Build a list of eviction candidates (which are
521         // anything not used this frame).
522         for handle in self.shared_entry_handles.drain(..) {
523             let entry = self.entries.get(&handle);
524             if entry.last_access == self.frame_id {
525                 retained_entries.push(handle);
526             } else {
527                 eviction_candidates.push(handle);
528             }
529         }
530 
531         // Sort by access time so we remove the oldest ones first.
532         eviction_candidates.sort_by_key(|handle| {
533             let entry = self.entries.get(handle);
534             entry.last_access
535         });
536 
537         // Doing an eviction is quite expensive, so we don't want to
538         // do it all the time. To avoid this, try and evict a
539         // significant number of items each cycle. However, we don't
540         // want to evict everything we can, since that will result in
541         // more items being uploaded than necessary.
542         // Instead, we say we will keep evicting until both of these
543         // conditions are met:
544         // - We have evicted some arbitrary number of items (512 currently).
545         //   AND
546         // - We have freed an item that will definitely allow us to
547         //   fit the currently requested allocation.
548         let needed_slab_size =
549             SlabSize::new(required_alloc.width, required_alloc.height).get_size();
550         let mut found_matching_slab = false;
551         let mut freed_complete_page = false;
552         let mut evicted_items = 0;
553 
554         for handle in eviction_candidates {
555             if evicted_items > 512 && (found_matching_slab || freed_complete_page) {
556                 retained_entries.push(handle);
557             } else {
558                 let entry = self.entries.free(handle);
559                 if let Some(region) = self.free(entry) {
560                     found_matching_slab |= region.slab_size == needed_slab_size;
561                     freed_complete_page |= region.is_empty();
562                 }
563                 evicted_items += 1;
564             }
565         }
566 
567         // Keep a record of the remaining handles for next eviction cycle.
568         self.shared_entry_handles = retained_entries;
569     }
570 
571     // Free a cache entry from the standalone list or shared cache.
free(&mut self, entry: CacheEntry) -> Option<&TextureRegion>572     fn free(&mut self, entry: CacheEntry) -> Option<&TextureRegion> {
573         match entry.kind {
574             EntryKind::Standalone { .. } => {
575                 // This is a standalone texture allocation. Just push it back onto the free
576                 // list.
577                 self.pending_updates.push(TextureUpdate {
578                     id: entry.texture_id,
579                     op: TextureUpdateOp::Free,
580                 });
581                 self.cache_textures.free(entry.texture_id, entry.format);
582                 None
583             }
584             EntryKind::Cache {
585                 origin,
586                 region_index,
587                 ..
588             } => {
589                 // Free the block in the given region.
590                 let region = self.get_region_mut(
591                     entry.format,
592                     entry.filter,
593                     region_index
594                 );
595                 region.free(origin);
596                 Some(region)
597             }
598         }
599     }
600 
601     // Attempt to allocate a block from the shared cache.
allocate_from_shared_cache( &mut self, descriptor: &ImageDescriptor, filter: TextureFilter, user_data: [f32; 3], ) -> Option<CacheEntry>602     fn allocate_from_shared_cache(
603         &mut self,
604         descriptor: &ImageDescriptor,
605         filter: TextureFilter,
606         user_data: [f32; 3],
607     ) -> Option<CacheEntry> {
608         // Work out which cache it goes in, based on format.
609         let texture_array = match (descriptor.format, filter) {
610             (ImageFormat::R8, TextureFilter::Linear) => &mut self.array_a8_linear,
611             (ImageFormat::BGRA8, TextureFilter::Linear) => &mut self.array_rgba8_linear,
612             (ImageFormat::BGRA8, TextureFilter::Nearest) => &mut self.array_rgba8_nearest,
613             (ImageFormat::RGBAF32, _) |
614             (ImageFormat::R8, TextureFilter::Nearest) |
615             (ImageFormat::R8, TextureFilter::Trilinear) |
616             (ImageFormat::BGRA8, TextureFilter::Trilinear) |
617             (ImageFormat::RG8, _) => unreachable!(),
618         };
619 
620         // Lazy initialize this texture array if required.
621         if texture_array.texture_id.is_none() {
622             let texture_id = self.cache_textures.allocate(descriptor.format);
623 
624             let update_op = TextureUpdate {
625                 id: texture_id,
626                 op: TextureUpdateOp::Create {
627                     width: TEXTURE_LAYER_DIMENSIONS,
628                     height: TEXTURE_LAYER_DIMENSIONS,
629                     format: descriptor.format,
630                     filter: texture_array.filter,
631                     // TODO(gw): Creating a render target here is only used
632                     //           for the texture cache debugger display. In
633                     //           the future, we should change the debug
634                     //           display to use a shader that blits the
635                     //           texture, and then we can remove this
636                     //           memory allocation (same for the other
637                     //           standalone texture below).
638                     render_target: Some(RenderTargetInfo { has_depth: false }),
639                     layer_count: texture_array.layer_count as i32,
640                 },
641             };
642             self.pending_updates.push(update_op);
643 
644             texture_array.texture_id = Some(texture_id);
645         }
646 
647         // Do the allocation. This can fail and return None
648         // if there are no free slots or regions available.
649         texture_array.alloc(
650             descriptor.width,
651             descriptor.height,
652             user_data,
653             self.frame_id,
654         )
655     }
656 
657     // Returns true if the given image descriptor *may* be
658     // placed in the shared texture cache.
is_allowed_in_shared_cache( &self, filter: TextureFilter, descriptor: &ImageDescriptor, ) -> bool659     pub fn is_allowed_in_shared_cache(
660         &self,
661         filter: TextureFilter,
662         descriptor: &ImageDescriptor,
663     ) -> bool {
664         let mut allowed_in_shared_cache = true;
665 
666         // TODO(gw): For now, anything that requests nearest filtering and isn't BGRA8
667         //           just fails to allocate in a texture page, and gets a standalone
668         //           texture. This is probably rare enough that it can be fixed up later.
669         if filter == TextureFilter::Nearest &&
670            descriptor.format != ImageFormat::BGRA8 {
671             allowed_in_shared_cache = false;
672         }
673 
674         // Anything larger than 512 goes in a standalone texture.
675         // TODO(gw): If we find pages that suffer from batch breaks in this
676         //           case, add support for storing these in a standalone
677         //           texture array.
678         if descriptor.width > 512 || descriptor.height > 512 {
679             allowed_in_shared_cache = false;
680         }
681 
682         allowed_in_shared_cache
683     }
684 
685     // Allocate storage for a given image. This attempts to allocate
686     // from the shared cache, but falls back to standalone texture
687     // if the image is too large, or the cache is full.
allocate( &mut self, handle: &mut TextureCacheHandle, descriptor: ImageDescriptor, filter: TextureFilter, user_data: [f32; 3], )688     fn allocate(
689         &mut self,
690         handle: &mut TextureCacheHandle,
691         descriptor: ImageDescriptor,
692         filter: TextureFilter,
693         user_data: [f32; 3],
694     ) {
695         assert!(descriptor.width > 0 && descriptor.height > 0);
696 
697         // Work out if this image qualifies to go in the shared (batching) cache.
698         let allowed_in_shared_cache = self.is_allowed_in_shared_cache(
699             filter,
700             &descriptor,
701         );
702         let mut allocated_in_shared_cache = true;
703         let mut new_cache_entry = None;
704         let size = DeviceUintSize::new(descriptor.width, descriptor.height);
705         let frame_id = self.frame_id;
706 
707         // If it's allowed in the cache, see if there is a spot for it.
708         if allowed_in_shared_cache {
709             new_cache_entry = self.allocate_from_shared_cache(
710                 &descriptor,
711                 filter,
712                 user_data
713             );
714 
715             // If we failed to allocate in the shared cache, run an
716             // eviction cycle, and then try to allocate again.
717             if new_cache_entry.is_none() {
718                 self.expire_old_shared_entries(&descriptor);
719 
720                 new_cache_entry = self.allocate_from_shared_cache(
721                     &descriptor,
722                     filter,
723                     user_data
724                 );
725             }
726         }
727 
728         // If not allowed in the cache, or if the shared cache is full, then it
729         // will just have to be in a unique texture. This hurts batching but should
730         // only occur on a small number of images (or pathological test cases!).
731         if new_cache_entry.is_none() {
732             let texture_id = self.cache_textures.allocate(descriptor.format);
733 
734             // Create an update operation to allocate device storage
735             // of the right size / format.
736             let update_op = TextureUpdate {
737                 id: texture_id,
738                 op: TextureUpdateOp::Create {
739                     width: descriptor.width,
740                     height: descriptor.height,
741                     format: descriptor.format,
742                     filter,
743                     render_target: Some(RenderTargetInfo { has_depth: false }),
744                     layer_count: 1,
745                 },
746             };
747             self.pending_updates.push(update_op);
748 
749             new_cache_entry = Some(CacheEntry::new_standalone(
750                 texture_id,
751                 size,
752                 descriptor.format,
753                 filter,
754                 user_data,
755                 frame_id,
756             ));
757 
758             allocated_in_shared_cache = false;
759         }
760 
761         let new_cache_entry = new_cache_entry.expect("BUG: must have allocated by now");
762 
763         // We need to update the texture cache handle now, so that it
764         // points to the correct location.
765         let new_entry_handle = match handle.entry {
766             Some(ref existing_entry) => {
767                 // If the handle already exists, there's two possibilities:
768                 // 1) It points to a valid entry in the freelist.
769                 // 2) It points to a stale entry in the freelist (i.e. has been evicted).
770                 //
771                 // For (1) we want to replace the cache entry with our
772                 // newly updated location. We also need to ensure that
773                 // the storage (region or standalone) associated with the
774                 // previous entry here gets freed.
775                 //
776                 // For (2) we need to add the data to a new location
777                 // in the freelist.
778                 //
779                 // This is managed with a database style upsert operation.
780                 match self.entries.upsert(existing_entry, new_cache_entry) {
781                     UpsertResult::Updated(old_entry) => {
782                         self.free(old_entry);
783                         None
784                     }
785                     UpsertResult::Inserted(new_handle) => Some(new_handle),
786                 }
787             }
788             None => {
789                 // This handle has never been allocated, so just
790                 // insert a new cache entry.
791                 Some(self.entries.insert(new_cache_entry))
792             }
793         };
794 
795         // If the cache entry is new, update it in the cache handle.
796         if let Some(new_entry_handle) = new_entry_handle {
797             handle.entry = Some(new_entry_handle.weak());
798             // Store the strong handle in the list that we scan for
799             // cache evictions.
800             if allocated_in_shared_cache {
801                 self.shared_entry_handles.push(new_entry_handle);
802             } else {
803                 self.standalone_entry_handles.push(new_entry_handle);
804             }
805         }
806     }
807 }
808 
809 // A list of the block sizes that a region can be initialized with.
810 #[derive(Copy, Clone, PartialEq)]
811 enum SlabSize {
812     Size16x16,
813     Size32x32,
814     Size64x64,
815     Size128x128,
816     Size256x256,
817     Size512x512,
818 }
819 
820 impl SlabSize {
new(width: u32, height: u32) -> SlabSize821     fn new(width: u32, height: u32) -> SlabSize {
822         // TODO(gw): Consider supporting non-square
823         //           allocator sizes here.
824         let max_dim = cmp::max(width, height);
825 
826         match max_dim {
827             0 => unreachable!(),
828             1...16 => SlabSize::Size16x16,
829             17...32 => SlabSize::Size32x32,
830             33...64 => SlabSize::Size64x64,
831             65...128 => SlabSize::Size128x128,
832             129...256 => SlabSize::Size256x256,
833             257...512 => SlabSize::Size512x512,
834             _ => panic!("Invalid dimensions for cache!"),
835         }
836     }
837 
get_size(&self) -> u32838     fn get_size(&self) -> u32 {
839         match *self {
840             SlabSize::Size16x16 => 16,
841             SlabSize::Size32x32 => 32,
842             SlabSize::Size64x64 => 64,
843             SlabSize::Size128x128 => 128,
844             SlabSize::Size256x256 => 256,
845             SlabSize::Size512x512 => 512,
846         }
847     }
848 }
849 
850 // The x/y location within a texture region of an allocation.
851 #[cfg_attr(feature = "capture", derive(Serialize))]
852 #[cfg_attr(feature = "replay", derive(Deserialize))]
853 struct TextureLocation(u8, u8);
854 
855 impl TextureLocation {
new(x: u32, y: u32) -> Self856     fn new(x: u32, y: u32) -> Self {
857         debug_assert!(x < 0x100 && y < 0x100);
858         TextureLocation(x as u8, y as u8)
859     }
860 }
861 
862 // A region is a sub-rect of a texture array layer.
863 // All allocations within a region are of the same size.
864 #[cfg_attr(feature = "capture", derive(Serialize))]
865 #[cfg_attr(feature = "replay", derive(Deserialize))]
866 struct TextureRegion {
867     layer_index: i32,
868     region_size: u32,
869     slab_size: u32,
870     free_slots: Vec<TextureLocation>,
871     slots_per_axis: u32,
872     total_slot_count: usize,
873     origin: DeviceUintPoint,
874 }
875 
876 impl TextureRegion {
new(region_size: u32, layer_index: i32, origin: DeviceUintPoint) -> Self877     fn new(region_size: u32, layer_index: i32, origin: DeviceUintPoint) -> Self {
878         TextureRegion {
879             layer_index,
880             region_size,
881             slab_size: 0,
882             free_slots: Vec::new(),
883             slots_per_axis: 0,
884             total_slot_count: 0,
885             origin,
886         }
887     }
888 
889     // Initialize a region to be an allocator for a specific slab size.
init(&mut self, slab_size: SlabSize)890     fn init(&mut self, slab_size: SlabSize) {
891         debug_assert!(self.slab_size == 0);
892         debug_assert!(self.free_slots.is_empty());
893 
894         self.slab_size = slab_size.get_size();
895         self.slots_per_axis = self.region_size / self.slab_size;
896 
897         // Add each block to a freelist.
898         for y in 0 .. self.slots_per_axis {
899             for x in 0 .. self.slots_per_axis {
900                 self.free_slots.push(TextureLocation::new(x, y));
901             }
902         }
903 
904         self.total_slot_count = self.free_slots.len();
905     }
906 
907     // Deinit a region, allowing it to become a region with
908     // a different allocator size.
deinit(&mut self)909     fn deinit(&mut self) {
910         self.slab_size = 0;
911         self.free_slots.clear();
912         self.slots_per_axis = 0;
913         self.total_slot_count = 0;
914     }
915 
is_empty(&self) -> bool916     fn is_empty(&self) -> bool {
917         self.slab_size == 0
918     }
919 
920     // Attempt to allocate a fixed size block from this region.
alloc(&mut self) -> Option<DeviceUintPoint>921     fn alloc(&mut self) -> Option<DeviceUintPoint> {
922         self.free_slots.pop().map(|location| {
923             DeviceUintPoint::new(
924                 self.origin.x + self.slab_size * location.0 as u32,
925                 self.origin.y + self.slab_size * location.1 as u32,
926             )
927         })
928     }
929 
930     // Free a block in this region.
free(&mut self, point: DeviceUintPoint)931     fn free(&mut self, point: DeviceUintPoint) {
932         let x = (point.x - self.origin.x) / self.slab_size;
933         let y = (point.y - self.origin.y) / self.slab_size;
934         self.free_slots.push(TextureLocation::new(x, y));
935 
936         // If this region is completely unused, deinit it
937         // so that it can become a different slab size
938         // as required.
939         if self.free_slots.len() == self.total_slot_count {
940             self.deinit();
941         }
942     }
943 }
944 
945 // A texture array contains a number of texture layers, where
946 // each layer contains one or more regions that can act
947 // as slab allocators.
948 #[cfg_attr(feature = "capture", derive(Serialize))]
949 #[cfg_attr(feature = "replay", derive(Deserialize))]
950 struct TextureArray {
951     filter: TextureFilter,
952     layer_count: usize,
953     format: ImageFormat,
954     is_allocated: bool,
955     regions: Vec<TextureRegion>,
956     texture_id: Option<CacheTextureId>,
957 }
958 
959 impl TextureArray {
new( format: ImageFormat, filter: TextureFilter, layer_count: usize ) -> Self960     fn new(
961         format: ImageFormat,
962         filter: TextureFilter,
963         layer_count: usize
964     ) -> Self {
965         TextureArray {
966             format,
967             filter,
968             layer_count,
969             is_allocated: false,
970             regions: Vec::new(),
971             texture_id: None,
972         }
973     }
974 
update_profile(&self, counter: &mut ResourceProfileCounter)975     fn update_profile(&self, counter: &mut ResourceProfileCounter) {
976         if self.is_allocated {
977             let size = self.layer_count as u32 * TEXTURE_LAYER_DIMENSIONS *
978                 TEXTURE_LAYER_DIMENSIONS * self.format.bytes_per_pixel();
979             counter.set(self.layer_count as usize, size as usize);
980         } else {
981             counter.set(0, 0);
982         }
983     }
984 
985     // Allocate space in this texture array.
alloc( &mut self, width: u32, height: u32, user_data: [f32; 3], frame_id: FrameId, ) -> Option<CacheEntry>986     fn alloc(
987         &mut self,
988         width: u32,
989         height: u32,
990         user_data: [f32; 3],
991         frame_id: FrameId,
992     ) -> Option<CacheEntry> {
993         // Lazily allocate the regions if not already created.
994         // This means that very rarely used image formats can be
995         // added but won't allocate a cache if never used.
996         if !self.is_allocated {
997             debug_assert!(TEXTURE_LAYER_DIMENSIONS % TEXTURE_REGION_DIMENSIONS == 0);
998             let regions_per_axis = TEXTURE_LAYER_DIMENSIONS / TEXTURE_REGION_DIMENSIONS;
999             for layer_index in 0 .. self.layer_count {
1000                 for y in 0 .. regions_per_axis {
1001                     for x in 0 .. regions_per_axis {
1002                         let origin = DeviceUintPoint::new(
1003                             x * TEXTURE_REGION_DIMENSIONS,
1004                             y * TEXTURE_REGION_DIMENSIONS,
1005                         );
1006                         let region = TextureRegion::new(
1007                             TEXTURE_REGION_DIMENSIONS,
1008                             layer_index as i32,
1009                             origin
1010                         );
1011                         self.regions.push(region);
1012                     }
1013                 }
1014             }
1015             self.is_allocated = true;
1016         }
1017 
1018         // Quantize the size of the allocation to select a region to
1019         // allocate from.
1020         let slab_size = SlabSize::new(width, height);
1021         let slab_size_dim = slab_size.get_size();
1022 
1023         // TODO(gw): For simplicity, the initial implementation just
1024         //           has a single vec<> of regions. We could easily
1025         //           make this more efficient by storing a list of
1026         //           regions for each slab size specifically...
1027 
1028         // Keep track of the location of an empty region,
1029         // in case we need to select a new empty region
1030         // after the loop.
1031         let mut empty_region_index = None;
1032         let mut entry_kind = None;
1033 
1034         // Run through the existing regions of this size, and see if
1035         // we can find a free block in any of them.
1036         for (i, region) in self.regions.iter_mut().enumerate() {
1037             if region.slab_size == 0 {
1038                 empty_region_index = Some(i);
1039             } else if region.slab_size == slab_size_dim {
1040                 if let Some(location) = region.alloc() {
1041                     entry_kind = Some(EntryKind::Cache {
1042                         layer_index: region.layer_index as u16,
1043                         region_index: i as u16,
1044                         origin: location,
1045                     });
1046                     break;
1047                 }
1048             }
1049         }
1050 
1051         // Find a region of the right size and try to allocate from it.
1052         if entry_kind.is_none() {
1053             if let Some(empty_region_index) = empty_region_index {
1054                 let region = &mut self.regions[empty_region_index];
1055                 region.init(slab_size);
1056                 entry_kind = region.alloc().map(|location| {
1057                     EntryKind::Cache {
1058                         layer_index: region.layer_index as u16,
1059                         region_index: empty_region_index as u16,
1060                         origin: location,
1061                     }
1062                 });
1063             }
1064         }
1065 
1066         entry_kind.map(|kind| {
1067             CacheEntry {
1068                 size: DeviceUintSize::new(width, height),
1069                 user_data,
1070                 last_access: frame_id,
1071                 kind,
1072                 uv_rect_handle: GpuCacheHandle::new(),
1073                 format: self.format,
1074                 filter: self.filter,
1075                 texture_id: self.texture_id.unwrap(),
1076                 color: ColorF::new(1.0, 1.0, 1.0, 1.0).premultiplied(),
1077             }
1078         })
1079     }
1080 }
1081 
1082 impl TextureUpdate {
1083     // Constructs a TextureUpdate operation to be passed to the
1084     // rendering thread in order to do an upload to the right
1085     // location in the texture cache.
new_update( data: ImageData, descriptor: &ImageDescriptor, origin: DeviceUintPoint, size: DeviceUintSize, texture_id: CacheTextureId, layer_index: i32, dirty_rect: Option<DeviceUintRect>, ) -> TextureUpdate1086     fn new_update(
1087         data: ImageData,
1088         descriptor: &ImageDescriptor,
1089         origin: DeviceUintPoint,
1090         size: DeviceUintSize,
1091         texture_id: CacheTextureId,
1092         layer_index: i32,
1093         dirty_rect: Option<DeviceUintRect>,
1094     ) -> TextureUpdate {
1095         let data_src = match data {
1096             ImageData::Blob(..) => {
1097                 panic!("The vector image should have been rasterized.");
1098             }
1099             ImageData::External(ext_image) => match ext_image.image_type {
1100                 ExternalImageType::TextureHandle(_) => {
1101                     panic!("External texture handle should not go through texture_cache.");
1102                 }
1103                 ExternalImageType::Buffer => TextureUpdateSource::External {
1104                     id: ext_image.id,
1105                     channel_index: ext_image.channel_index,
1106                 },
1107             },
1108             ImageData::Raw(bytes) => {
1109                 let finish = descriptor.offset +
1110                     descriptor.width * descriptor.format.bytes_per_pixel() +
1111                     (descriptor.height - 1) * descriptor.compute_stride();
1112                 assert!(bytes.len() >= finish as usize);
1113 
1114                 TextureUpdateSource::Bytes { data: bytes }
1115             }
1116         };
1117 
1118         let update_op = match dirty_rect {
1119             Some(dirty) => {
1120                 let stride = descriptor.compute_stride();
1121                 let offset = descriptor.offset + dirty.origin.y * stride + dirty.origin.x * descriptor.format.bytes_per_pixel();
1122                 let origin =
1123                     DeviceUintPoint::new(origin.x + dirty.origin.x, origin.y + dirty.origin.y);
1124                 TextureUpdateOp::Update {
1125                     rect: DeviceUintRect::new(origin, dirty.size),
1126                     source: data_src,
1127                     stride: Some(stride),
1128                     offset,
1129                     layer_index,
1130                 }
1131             }
1132             None => TextureUpdateOp::Update {
1133                 rect: DeviceUintRect::new(origin, size),
1134                 source: data_src,
1135                 stride: descriptor.stride,
1136                 offset: descriptor.offset,
1137                 layer_index,
1138             },
1139         };
1140 
1141         TextureUpdate {
1142             id: texture_id,
1143             op: update_op,
1144         }
1145     }
1146 }
1147