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 {}