diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:47:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:47:29 +0000 |
commit | 0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d (patch) | |
tree | a31f07c9bcca9d56ce61e9a1ffd30ef350d513aa /gfx/wr/webrender/src/api_resources.rs | |
parent | Initial commit. (diff) | |
download | firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.tar.xz firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.zip |
Adding upstream version 115.8.0esr.upstream/115.8.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/wr/webrender/src/api_resources.rs')
-rw-r--r-- | gfx/wr/webrender/src/api_resources.rs | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/gfx/wr/webrender/src/api_resources.rs b/gfx/wr/webrender/src/api_resources.rs new file mode 100644 index 0000000000..d0cece19f5 --- /dev/null +++ b/gfx/wr/webrender/src/api_resources.rs @@ -0,0 +1,305 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use crate::api::{BlobImageKey, ImageDescriptor, DirtyRect, TileSize, DebugFlags}; +use crate::api::{BlobImageHandler, AsyncBlobImageRasterizer, BlobImageData, BlobImageParams}; +use crate::api::{BlobImageRequest, BlobImageDescriptor, FontTemplate}; +use crate::api::units::*; +use glyph_rasterizer::{SharedFontResources, BaseFontInstance}; +use crate::render_api::{ResourceUpdate, TransactionMsg, AddFont}; +use crate::image_tiling::*; +use crate::profiler; + +use std::collections::HashMap; +use std::mem; +use std::sync::Arc; + +/// We use this to generate the async blob rendering requests. +struct BlobImageTemplate { + descriptor: ImageDescriptor, + tile_size: TileSize, + dirty_rect: BlobDirtyRect, + /// See ImageResource::visible_rect. + visible_rect: DeviceIntRect, + // If the active rect of the blob changes, this represents the + // range of tiles that remain valid. This must be taken into + // account in addition to the valid rect when submitting blob + // rasterization requests. + // `None` means the bounds have not changed (tiles are still valid). + // `Some(TileRange::zero())` means all of the tiles are invalid. + valid_tiles_after_bounds_change: Option<TileRange>, +} + +pub struct ApiResources { + blob_image_templates: HashMap<BlobImageKey, BlobImageTemplate>, + pub blob_image_handler: Option<Box<dyn BlobImageHandler>>, + fonts: SharedFontResources, + // This should only be true for CI or debugging purposes. If true, + // we'll restrict the size of blob images as a result effectively + // rendering them incorrectly. + debug_restrict_blob_size: bool, +} + +impl ApiResources { + pub fn new( + blob_image_handler: Option<Box<dyn BlobImageHandler>>, + fonts: SharedFontResources, + ) -> Self { + ApiResources { + blob_image_templates: HashMap::new(), + blob_image_handler, + fonts, + debug_restrict_blob_size: false, + } + } + + pub fn get_fonts(&self) -> SharedFontResources { + self.fonts.clone() + } + + pub fn set_debug_flags(&mut self, flags: DebugFlags) { + self.debug_restrict_blob_size = flags.contains(DebugFlags::RESTRICT_BLOB_SIZE); + } + + pub fn adjust_blob_visible_rect(&self, rect: &mut DeviceIntRect, size: Option<&mut DeviceIntSize>) { + if self.debug_restrict_blob_size { + rect.max.x = rect.max.x.min(rect.min.x + 2048); + rect.max.y = rect.max.y.min(rect.min.y + 2048); + if let Some(size) = size { + size.width = size.width.min(2048); + size.height = size.height.min(2048); + } + } + } + + pub fn update(&mut self, transaction: &mut TransactionMsg) { + let mut blobs_to_rasterize = Vec::new(); + for update in &mut transaction.resource_updates { + match *update { + ResourceUpdate::AddBlobImage(ref mut img) => { + self.adjust_blob_visible_rect(&mut img.visible_rect, Some(&mut img.descriptor.size)); + self.blob_image_handler + .as_mut() + .expect("no blob image handler") + .add(img.key, Arc::clone(&img.data), &img.visible_rect, img.tile_size); + + self.blob_image_templates.insert( + img.key, + BlobImageTemplate { + descriptor: img.descriptor, + tile_size: img.tile_size, + dirty_rect: DirtyRect::All, + valid_tiles_after_bounds_change: None, + visible_rect: img.visible_rect, + }, + ); + blobs_to_rasterize.push(img.key); + } + ResourceUpdate::UpdateBlobImage(ref mut img) => { + debug_assert_eq!(img.visible_rect.size(), img.descriptor.size); + self.adjust_blob_visible_rect(&mut img.visible_rect, Some(&mut img.descriptor.size)); + self.update_blob_image( + img.key, + Some(&img.descriptor), + Some(&img.dirty_rect), + Some(Arc::clone(&img.data)), + &img.visible_rect, + ); + blobs_to_rasterize.push(img.key); + } + ResourceUpdate::DeleteBlobImage(key) => { + transaction.use_scene_builder_thread = true; + self.blob_image_templates.remove(&key); + if let Some(ref mut handler) = self.blob_image_handler { + handler.delete(key); + } + } + ResourceUpdate::SetBlobImageVisibleArea(ref key, ref mut area) => { + self.adjust_blob_visible_rect(area, None); + self.update_blob_image(*key, None, None, None, &area); + blobs_to_rasterize.push(*key); + } + ResourceUpdate::AddFont(ref font) => { + let (key, template) = match font { + AddFont::Raw(key, bytes, index) => { + (key, FontTemplate::Raw(Arc::clone(bytes), *index)) + } + AddFont::Native(key, native_font_handle) => { + (key, FontTemplate::Native(native_font_handle.clone())) + } + }; + if let Some(shared_key) = self.fonts.font_keys.add_key(key, &template) { + self.fonts.templates.add_font(shared_key, template); + } + } + ResourceUpdate::AddFontInstance(ref mut instance) => { + let shared_font_key = self.fonts.font_keys.map_key(&instance.font_key); + assert!(self.fonts.templates.has_font(&shared_font_key)); + // AddFontInstance will only be processed here, not in the resource cache, so it + // is safe to take the options rather than clone them. + let base = BaseFontInstance::new( + instance.key, + shared_font_key, + instance.glyph_size, + mem::take(&mut instance.options), + mem::take(&mut instance.platform_options), + mem::take(&mut instance.variations), + ); + if let Some(shared_instance) = self.fonts.instance_keys.add_key(base) { + self.fonts.instances.add_font_instance(shared_instance); + } + } + ResourceUpdate::DeleteFont(_key) => { + transaction.use_scene_builder_thread = true; + } + ResourceUpdate::DeleteFontInstance(_key) => { + transaction.use_scene_builder_thread = true; + // We will delete from the shared font instance map in the resource cache + // after scene swap. + } + ResourceUpdate::DeleteImage(..) => { + transaction.use_scene_builder_thread = true; + } + _ => {} + } + } + + let (rasterizer, requests) = self.create_blob_scene_builder_requests(&blobs_to_rasterize); + transaction.profile.set(profiler::RASTERIZED_BLOBS, blobs_to_rasterize.len()); + transaction.profile.set(profiler::RASTERIZED_BLOB_TILES, requests.len()); + transaction.use_scene_builder_thread |= !requests.is_empty(); + transaction.use_scene_builder_thread |= !transaction.scene_ops.is_empty(); + transaction.blob_rasterizer = rasterizer; + transaction.blob_requests = requests; + } + + pub fn enable_multithreading(&mut self, enable: bool) { + if let Some(ref mut handler) = self.blob_image_handler { + handler.enable_multithreading(enable); + } + } + + fn update_blob_image( + &mut self, + key: BlobImageKey, + descriptor: Option<&ImageDescriptor>, + dirty_rect: Option<&BlobDirtyRect>, + data: Option<Arc<BlobImageData>>, + visible_rect: &DeviceIntRect, + ) { + if let Some(data) = data { + let dirty_rect = dirty_rect.expect("no dirty rect"); + self.blob_image_handler + .as_mut() + .expect("no blob image handler") + .update(key, data, visible_rect, dirty_rect); + } + + let image = self.blob_image_templates + .get_mut(&key) + .expect("Attempt to update non-existent blob image"); + + let mut valid_tiles_after_bounds_change = compute_valid_tiles_if_bounds_change( + &image.visible_rect, + visible_rect, + image.tile_size, + ); + + match (image.valid_tiles_after_bounds_change, valid_tiles_after_bounds_change) { + (Some(old), Some(ref mut new)) => { + *new = new.intersection(&old).unwrap_or_else(TileRange::zero); + } + (Some(old), None) => { + valid_tiles_after_bounds_change = Some(old); + } + _ => {} + } + + let blob_size = visible_rect.size(); + + if let Some(descriptor) = descriptor { + image.descriptor = *descriptor; + } else { + // make sure the descriptor size matches the visible rect. + // This might not be necessary but let's stay on the safe side. + image.descriptor.size = blob_size; + } + + if let Some(dirty_rect) = dirty_rect { + image.dirty_rect = image.dirty_rect.union(dirty_rect); + } + + image.valid_tiles_after_bounds_change = valid_tiles_after_bounds_change; + image.visible_rect = *visible_rect; + } + + pub fn create_blob_scene_builder_requests( + &mut self, + keys: &[BlobImageKey] + ) -> (Option<Box<dyn AsyncBlobImageRasterizer>>, Vec<BlobImageParams>) { + if self.blob_image_handler.is_none() || keys.is_empty() { + return (None, Vec::new()); + } + + let mut blob_request_params = Vec::new(); + for key in keys { + let template = self.blob_image_templates.get_mut(key) + .expect("no blob image template"); + + // If we know that only a portion of the blob image is in the viewport, + // only request these visible tiles since blob images can be huge. + let tiles = compute_tile_range( + &template.visible_rect, + template.tile_size, + ); + + // Don't request tiles that weren't invalidated. + let dirty_tiles = match template.dirty_rect { + DirtyRect::Partial(dirty_rect) => { + compute_tile_range( + &dirty_rect.cast_unit(), + template.tile_size, + ) + } + DirtyRect::All => tiles, + }; + + for_each_tile_in_range(&tiles, |tile| { + let still_valid = template.valid_tiles_after_bounds_change + .map(|valid_tiles| valid_tiles.contains(tile)) + .unwrap_or(true); + + if still_valid && !dirty_tiles.contains(tile) { + return; + } + + let descriptor = BlobImageDescriptor { + rect: compute_tile_rect( + &template.visible_rect, + template.tile_size, + tile, + ).cast_unit(), + format: template.descriptor.format, + }; + + assert!(descriptor.rect.width() > 0 && descriptor.rect.height() > 0); + blob_request_params.push( + BlobImageParams { + request: BlobImageRequest { key: *key, tile }, + descriptor, + dirty_rect: DirtyRect::All, + } + ); + }); + + template.dirty_rect = DirtyRect::empty(); + template.valid_tiles_after_bounds_change = None; + } + + let handler = self.blob_image_handler.as_mut() + .expect("no blob image handler"); + handler.prepare_resources(&self.fonts, &blob_request_params); + (Some(handler.create_blob_rasterizer()), blob_request_params) + } +} |