api_resources.rs (12725B)
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 crate::api::{BlobImageKey, ImageDescriptor, DirtyRect, TileSize, DebugFlags}; 6 use crate::api::{BlobImageHandler, AsyncBlobImageRasterizer, BlobImageData, BlobImageParams}; 7 use crate::api::{BlobImageRequest, BlobImageDescriptor, FontTemplate}; 8 use crate::api::units::*; 9 use glyph_rasterizer::{SharedFontResources, BaseFontInstance}; 10 use crate::render_api::{ResourceUpdate, TransactionMsg, AddFont}; 11 use crate::image_tiling::*; 12 use crate::profiler; 13 14 use std::collections::HashMap; 15 use std::mem; 16 use std::sync::Arc; 17 18 /// We use this to generate the async blob rendering requests. 19 struct BlobImageTemplate { 20 descriptor: ImageDescriptor, 21 tile_size: TileSize, 22 dirty_rect: BlobDirtyRect, 23 /// See ImageResource::visible_rect. 24 visible_rect: DeviceIntRect, 25 // If the active rect of the blob changes, this represents the 26 // range of tiles that remain valid. This must be taken into 27 // account in addition to the valid rect when submitting blob 28 // rasterization requests. 29 // `None` means the bounds have not changed (tiles are still valid). 30 // `Some(TileRange::zero())` means all of the tiles are invalid. 31 valid_tiles_after_bounds_change: Option<TileRange>, 32 } 33 34 pub struct ApiResources { 35 blob_image_templates: HashMap<BlobImageKey, BlobImageTemplate>, 36 pub blob_image_handler: Option<Box<dyn BlobImageHandler>>, 37 fonts: SharedFontResources, 38 // This should only be true for CI or debugging purposes. If true, 39 // we'll restrict the size of blob images as a result effectively 40 // rendering them incorrectly. 41 debug_restrict_blob_size: bool, 42 } 43 44 impl ApiResources { 45 pub fn new( 46 blob_image_handler: Option<Box<dyn BlobImageHandler>>, 47 fonts: SharedFontResources, 48 ) -> Self { 49 ApiResources { 50 blob_image_templates: HashMap::new(), 51 blob_image_handler, 52 fonts, 53 debug_restrict_blob_size: false, 54 } 55 } 56 57 pub fn get_fonts(&self) -> SharedFontResources { 58 self.fonts.clone() 59 } 60 61 pub fn set_debug_flags(&mut self, flags: DebugFlags) { 62 self.debug_restrict_blob_size = flags.contains(DebugFlags::RESTRICT_BLOB_SIZE); 63 } 64 65 pub fn adjust_blob_visible_rect(&self, rect: &mut DeviceIntRect, size: Option<&mut DeviceIntSize>) { 66 if self.debug_restrict_blob_size { 67 rect.max.x = rect.max.x.min(rect.min.x + 2048); 68 rect.max.y = rect.max.y.min(rect.min.y + 2048); 69 if let Some(size) = size { 70 size.width = size.width.min(2048); 71 size.height = size.height.min(2048); 72 } 73 } 74 } 75 76 pub fn update(&mut self, transaction: &mut TransactionMsg) { 77 let mut blobs_to_rasterize = Vec::new(); 78 for update in &mut transaction.resource_updates { 79 match *update { 80 ResourceUpdate::AddBlobImage(ref mut img) => { 81 self.adjust_blob_visible_rect(&mut img.visible_rect, Some(&mut img.descriptor.size)); 82 self.blob_image_handler 83 .as_mut() 84 .expect("no blob image handler") 85 .add(img.key, Arc::clone(&img.data), &img.visible_rect, img.tile_size); 86 87 self.blob_image_templates.insert( 88 img.key, 89 BlobImageTemplate { 90 descriptor: img.descriptor, 91 tile_size: img.tile_size, 92 dirty_rect: DirtyRect::All, 93 valid_tiles_after_bounds_change: None, 94 visible_rect: img.visible_rect, 95 }, 96 ); 97 blobs_to_rasterize.push(img.key); 98 } 99 ResourceUpdate::UpdateBlobImage(ref mut img) => { 100 debug_assert_eq!(img.visible_rect.size(), img.descriptor.size); 101 self.adjust_blob_visible_rect(&mut img.visible_rect, Some(&mut img.descriptor.size)); 102 self.update_blob_image( 103 img.key, 104 Some(&img.descriptor), 105 Some(&img.dirty_rect), 106 Some(Arc::clone(&img.data)), 107 &img.visible_rect, 108 ); 109 blobs_to_rasterize.push(img.key); 110 } 111 ResourceUpdate::DeleteBlobImage(key) => { 112 transaction.use_scene_builder_thread = true; 113 self.blob_image_templates.remove(&key); 114 if let Some(ref mut handler) = self.blob_image_handler { 115 handler.delete(key); 116 } 117 } 118 ResourceUpdate::SetBlobImageVisibleArea(ref key, ref mut area) => { 119 self.adjust_blob_visible_rect(area, None); 120 self.update_blob_image(*key, None, None, None, &area); 121 blobs_to_rasterize.push(*key); 122 } 123 ResourceUpdate::AddFont(ref font) => { 124 let (key, template) = match font { 125 AddFont::Raw(key, bytes, index) => { 126 (key, FontTemplate::Raw(Arc::clone(bytes), *index)) 127 } 128 AddFont::Native(key, native_font_handle) => { 129 (key, FontTemplate::Native(native_font_handle.clone())) 130 } 131 }; 132 if let Some(shared_key) = self.fonts.font_keys.add_key(key, &template) { 133 self.fonts.templates.add_font(shared_key, template); 134 } 135 } 136 ResourceUpdate::AddFontInstance(ref mut instance) => { 137 let shared_font_key = self.fonts.font_keys.map_key(&instance.font_key); 138 assert!(self.fonts.templates.has_font(&shared_font_key)); 139 // AddFontInstance will only be processed here, not in the resource cache, so it 140 // is safe to take the options rather than clone them. 141 let base = BaseFontInstance::new( 142 instance.key, 143 shared_font_key, 144 instance.glyph_size, 145 mem::take(&mut instance.options), 146 mem::take(&mut instance.platform_options), 147 mem::take(&mut instance.variations), 148 ); 149 if let Some(shared_instance) = self.fonts.instance_keys.add_key(base) { 150 self.fonts.instances.add_font_instance(shared_instance); 151 } 152 } 153 ResourceUpdate::DeleteFont(_key) => { 154 transaction.use_scene_builder_thread = true; 155 } 156 ResourceUpdate::DeleteFontInstance(_key) => { 157 transaction.use_scene_builder_thread = true; 158 // We will delete from the shared font instance map in the resource cache 159 // after scene swap. 160 } 161 ResourceUpdate::DeleteImage(..) => { 162 transaction.use_scene_builder_thread = true; 163 } 164 _ => {} 165 } 166 } 167 168 let (rasterizer, requests) = self.create_blob_scene_builder_requests(&blobs_to_rasterize); 169 transaction.profile.set(profiler::RASTERIZED_BLOBS, blobs_to_rasterize.len()); 170 transaction.profile.set(profiler::RASTERIZED_BLOB_TILES, requests.len()); 171 transaction.use_scene_builder_thread |= !requests.is_empty(); 172 transaction.use_scene_builder_thread |= !transaction.scene_ops.is_empty(); 173 transaction.blob_rasterizer = rasterizer; 174 transaction.blob_requests = requests; 175 } 176 177 pub fn enable_multithreading(&mut self, enable: bool) { 178 if let Some(ref mut handler) = self.blob_image_handler { 179 handler.enable_multithreading(enable); 180 } 181 } 182 183 fn update_blob_image( 184 &mut self, 185 key: BlobImageKey, 186 descriptor: Option<&ImageDescriptor>, 187 dirty_rect: Option<&BlobDirtyRect>, 188 data: Option<Arc<BlobImageData>>, 189 visible_rect: &DeviceIntRect, 190 ) { 191 if let Some(data) = data { 192 let dirty_rect = dirty_rect.expect("no dirty rect"); 193 self.blob_image_handler 194 .as_mut() 195 .expect("no blob image handler") 196 .update(key, data, visible_rect, dirty_rect); 197 } 198 199 let image = self.blob_image_templates 200 .get_mut(&key) 201 .expect("Attempt to update non-existent blob image"); 202 203 let mut valid_tiles_after_bounds_change = compute_valid_tiles_if_bounds_change( 204 &image.visible_rect, 205 visible_rect, 206 image.tile_size, 207 ); 208 209 match (image.valid_tiles_after_bounds_change, valid_tiles_after_bounds_change) { 210 (Some(old), Some(ref mut new)) => { 211 *new = new.intersection(&old).unwrap_or_else(TileRange::zero); 212 } 213 (Some(old), None) => { 214 valid_tiles_after_bounds_change = Some(old); 215 } 216 _ => {} 217 } 218 219 let blob_size = visible_rect.size(); 220 221 if let Some(descriptor) = descriptor { 222 image.descriptor = *descriptor; 223 } else { 224 // make sure the descriptor size matches the visible rect. 225 // This might not be necessary but let's stay on the safe side. 226 image.descriptor.size = blob_size; 227 } 228 229 if let Some(dirty_rect) = dirty_rect { 230 image.dirty_rect = image.dirty_rect.union(dirty_rect); 231 } 232 233 image.valid_tiles_after_bounds_change = valid_tiles_after_bounds_change; 234 image.visible_rect = *visible_rect; 235 } 236 237 pub fn create_blob_scene_builder_requests( 238 &mut self, 239 keys: &[BlobImageKey] 240 ) -> (Option<Box<dyn AsyncBlobImageRasterizer>>, Vec<BlobImageParams>) { 241 if self.blob_image_handler.is_none() || keys.is_empty() { 242 return (None, Vec::new()); 243 } 244 245 let mut blob_request_params = Vec::new(); 246 for key in keys { 247 let template = self.blob_image_templates.get_mut(key) 248 .expect("no blob image template"); 249 250 // If we know that only a portion of the blob image is in the viewport, 251 // only request these visible tiles since blob images can be huge. 252 let tiles = compute_tile_range( 253 &template.visible_rect, 254 template.tile_size, 255 ); 256 257 // Don't request tiles that weren't invalidated. 258 let dirty_tiles = match template.dirty_rect { 259 DirtyRect::Partial(dirty_rect) => { 260 compute_tile_range( 261 &dirty_rect.cast_unit(), 262 template.tile_size, 263 ) 264 } 265 DirtyRect::All => tiles, 266 }; 267 268 for_each_tile_in_range(&tiles, |tile| { 269 let still_valid = template.valid_tiles_after_bounds_change 270 .map(|valid_tiles| valid_tiles.contains(tile)) 271 .unwrap_or(true); 272 273 if still_valid && !dirty_tiles.contains(tile) { 274 return; 275 } 276 277 let descriptor = BlobImageDescriptor { 278 rect: compute_tile_rect( 279 &template.visible_rect, 280 template.tile_size, 281 tile, 282 ).cast_unit(), 283 format: template.descriptor.format, 284 }; 285 286 assert!(descriptor.rect.width() > 0 && descriptor.rect.height() > 0); 287 blob_request_params.push( 288 BlobImageParams { 289 request: BlobImageRequest { key: *key, tile }, 290 descriptor, 291 dirty_rect: DirtyRect::All, 292 } 293 ); 294 }); 295 296 template.dirty_rect = DirtyRect::empty(); 297 template.valid_tiles_after_bounds_change = None; 298 } 299 300 let handler = self.blob_image_handler.as_mut() 301 .expect("no blob image handler"); 302 handler.prepare_resources(&self.fonts, &blob_request_params); 303 (Some(handler.create_blob_rasterizer()), blob_request_params) 304 } 305 }