tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

tile_pool.rs (5961B)


      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 std::sync::Arc;
      6 
      7 const NUM_TILE_BUCKETS: usize = 6;
      8 
      9 /// A pool of blob tile buffers to mitigate the overhead of
     10 /// allocating and deallocating blob tiles.
     11 ///
     12 /// The pool keeps a strong reference to each allocated buffers and
     13 /// reuses the ones with a strong count of 1.
     14 pub struct BlobTilePool {
     15    largest_size_class: usize,
     16    buckets: [Vec<Arc<Vec<u8>>>; NUM_TILE_BUCKETS],
     17 }
     18 
     19 impl BlobTilePool {
     20    pub fn new() -> Self {
     21        // The default max tile size is actually 256, using 512 here
     22        // so that this still works when experimenting with larger
     23        // tile sizes. If we ever make larger adjustments, the buckets
     24        // should be changed accordingly.
     25        let max_tile_size = 512;
     26        BlobTilePool {
     27            largest_size_class: max_tile_size * max_tile_size * 4,
     28            buckets: [
     29                Vec::with_capacity(32),
     30                Vec::with_capacity(32),
     31                Vec::with_capacity(32),
     32                Vec::with_capacity(32),
     33                Vec::with_capacity(32),
     34                Vec::with_capacity(32),
     35            ],
     36        }
     37    }
     38 
     39    /// Get or allocate a tile buffer of the requested size.
     40    ///
     41    /// The returned buffer is zero-inizitalized.
     42    /// The length of the returned buffer is equal to the requested size,
     43    /// however the buffer may be allocated with a larger capacity to
     44    /// conform to the pool's corresponding bucket tile size.
     45    pub fn get_buffer(&mut self, requested_size: usize) -> MutableTileBuffer {
     46        if requested_size > self.largest_size_class {
     47            // If the requested size is larger than the largest size class,
     48            // simply return a MutableBuffer that isn't tracked/recycled by
     49            // the pool.
     50            // In Firefox this should only happen in pathological cases
     51            // where the blob visible area ends up so large that the tile
     52            // size is increased to avoid producing too many tiles.
     53            // See wr_resource_updates_add_blob_image.
     54            let mut buf = vec![0; requested_size];
     55            return MutableTileBuffer {
     56                ptr: buf.as_mut_ptr(),
     57                strong_ref: Arc::new(buf),
     58            };
     59        }
     60 
     61        let (bucket_idx, cap) = self.bucket_and_size(requested_size);
     62        let bucket = &mut self.buckets[bucket_idx];
     63        let mut selected_idx = None;
     64        for (buf_idx, buffer) in bucket.iter().enumerate() {
     65            if Arc::strong_count(buffer) == 1 {
     66                selected_idx = Some(buf_idx);
     67                break;
     68            }
     69        }
     70 
     71        let ptr;
     72        let strong_ref;
     73        if let Some(idx) = selected_idx {
     74            {
     75                // This works because we just ensured the pool has the only strong
     76                // ref to the buffer.
     77                let buffer = Arc::get_mut(&mut bucket[idx]).unwrap();
     78                debug_assert!(buffer.capacity() >= requested_size);
     79                // Ensure the length is equal to the requested size. It's not
     80                // strictly necessay for the tile pool but the texture upload
     81                // code relies on it.
     82                unsafe { buffer.set_len(requested_size); }
     83 
     84                // zero-initialize
     85                buffer.fill(0);
     86 
     87                ptr = buffer.as_mut_ptr();
     88            }
     89            strong_ref = Arc::clone(&bucket[idx]);
     90        } else {
     91            // Allocate a buffer with the adequate capacity for the requested
     92            // size's bucket.
     93            let mut buf = vec![0; cap];
     94            // Force the length to be the requested size.
     95            unsafe { buf.set_len(requested_size) };
     96 
     97            ptr = buf.as_mut_ptr();
     98            strong_ref = Arc::new(buf);
     99            // Track the new buffer.
    100            bucket.push(Arc::clone(&strong_ref));
    101        };
    102 
    103        MutableTileBuffer {
    104            ptr,
    105            strong_ref,
    106        }
    107    }
    108 
    109    fn bucket_and_size(&self, size: usize) -> (usize, usize) {
    110        let mut next_size_class = self.largest_size_class / 4;
    111        let mut idx = 0;
    112        while size < next_size_class && idx < NUM_TILE_BUCKETS - 1 {
    113            next_size_class /= 4;
    114            idx += 1;
    115        }
    116 
    117        (idx, next_size_class * 4)
    118    }
    119 
    120    /// Go over all allocated tile buffers. For each bucket, deallocate some buffers
    121    /// until the number of unused buffer is more than half of the buffers for that
    122    /// bucket.
    123    ///
    124    /// In practice, if called regularly, this gradually lets go of blob tiles when
    125    /// they are not used.
    126    pub fn cleanup(&mut self) {
    127        for bucket in &mut self.buckets {
    128            let threshold = bucket.len() / 2;
    129            let mut num_available = 0;
    130            bucket.retain(&mut |buffer: &Arc<Vec<u8>>| {
    131                if Arc::strong_count(buffer) > 1 {
    132                    return true;
    133                }
    134 
    135                num_available += 1;
    136                num_available < threshold
    137            });
    138        }
    139    }
    140 }
    141 
    142 
    143 // The role of tile buffer is to encapsulate an Arc to the underlying buffer
    144 // with a reference count of at most 2 and a way to view the buffer's content
    145 // as a mutable slice, even though the reference count may be more than 1.
    146 // The safety of this relies on the other strong reference being held by the
    147 // tile pool which never accesses the buffer's content, so the only reference
    148 // that can access it is the `TileBuffer` itself.
    149 pub struct MutableTileBuffer {
    150    strong_ref: Arc<Vec<u8>>,
    151    ptr: *mut u8,
    152 }
    153 
    154 impl MutableTileBuffer {
    155    pub fn as_mut_slice(&mut self) -> &mut[u8] {
    156        unsafe { std::slice::from_raw_parts_mut(self.ptr, self.strong_ref.len()) }
    157    }
    158 
    159    pub fn into_arc(self) -> Arc<Vec<u8>> {
    160        self.strong_ref
    161    }
    162 }
    163 
    164 unsafe impl Send for MutableTileBuffer {}