diff options
Diffstat (limited to 'components/shared/compositing/lib.rs')
-rw-r--r-- | components/shared/compositing/lib.rs | 482 |
1 files changed, 481 insertions, 1 deletions
diff --git a/components/shared/compositing/lib.rs b/components/shared/compositing/lib.rs index 9c13afb8dc3..c231960ef50 100644 --- a/components/shared/compositing/lib.rs +++ b/components/shared/compositing/lib.rs @@ -18,7 +18,27 @@ use pixels::Image; use strum_macros::IntoStaticStr; use style_traits::CSSPixel; use webrender_api::DocumentId; -use webrender_traits::{CrossProcessCompositorApi, CrossProcessCompositorMessage}; + +pub mod display_list; +pub mod rendering_context; + +use core::fmt; +use std::collections::HashMap; +use std::sync::{Arc, Mutex}; + +use display_list::CompositorDisplayListInfo; +use embedder_traits::{CompositorHitTestResult, ScreenGeometry}; +use euclid::default::Size2D as UntypedSize2D; +use ipc_channel::ipc::{self, IpcSharedMemory}; +use serde::{Deserialize, Serialize}; +use servo_geometry::{DeviceIndependentIntRect, DeviceIndependentIntSize}; +use webrender_api::units::{DevicePoint, LayoutPoint, TexelRect}; +use webrender_api::{ + BuiltDisplayList, BuiltDisplayListDescriptor, ExternalImage, ExternalImageData, + ExternalImageHandler, ExternalImageId, ExternalImageSource, ExternalScrollId, + FontInstanceFlags, FontInstanceKey, FontKey, HitTestFlags, ImageData, ImageDescriptor, + ImageKey, NativeFontHandle, PipelineId as WebRenderPipelineId, +}; /// Sends messages to the compositor. #[derive(Clone)] @@ -111,3 +131,463 @@ impl Debug for CompositorMsg { write!(formatter, "{string}") } } + +#[derive(Deserialize, Serialize)] +pub enum CrossProcessCompositorMessage { + /// Inform WebRender of the existence of this pipeline. + SendInitialTransaction(WebRenderPipelineId), + /// Perform a scroll operation. + SendScrollNode( + WebViewId, + WebRenderPipelineId, + LayoutPoint, + ExternalScrollId, + ), + /// Inform WebRender of a new display list for the given pipeline. + SendDisplayList { + /// The [`WebViewId`] that this display list belongs to. + webview_id: WebViewId, + /// The [CompositorDisplayListInfo] that describes the display list being sent. + display_list_info: Box<CompositorDisplayListInfo>, + /// A descriptor of this display list used to construct this display list from raw data. + display_list_descriptor: BuiltDisplayListDescriptor, + /// An [ipc::IpcBytesReceiver] used to send the raw data of the display list. + display_list_receiver: ipc::IpcBytesReceiver, + }, + /// Perform a hit test operation. The result will be returned via + /// the provided channel sender. + HitTest( + Option<WebRenderPipelineId>, + DevicePoint, + HitTestFlags, + IpcSender<Vec<CompositorHitTestResult>>, + ), + /// Create a new image key. The result will be returned via the + /// provided channel sender. + GenerateImageKey(IpcSender<ImageKey>), + /// Add an image with the given data and `ImageKey`. + AddImage(ImageKey, ImageDescriptor, SerializableImageData), + /// Perform a resource update operation. + UpdateImages(Vec<ImageUpdate>), + + /// Generate a new batch of font keys which can be used to allocate + /// keys asynchronously. + GenerateFontKeys( + usize, + usize, + IpcSender<(Vec<FontKey>, Vec<FontInstanceKey>)>, + ), + /// Add a font with the given data and font key. + AddFont(FontKey, Arc<IpcSharedMemory>, u32), + /// Add a system font with the given font key and handle. + AddSystemFont(FontKey, NativeFontHandle), + /// Add an instance of a font with the given instance key. + AddFontInstance(FontInstanceKey, FontKey, f32, FontInstanceFlags), + /// Remove the given font resources from our WebRender instance. + RemoveFonts(Vec<FontKey>, Vec<FontInstanceKey>), + + /// Get the client window size and position. + GetClientWindowRect(WebViewId, IpcSender<DeviceIndependentIntRect>), + /// Get the size of the screen that the client window inhabits. + GetScreenSize(WebViewId, IpcSender<DeviceIndependentIntSize>), + /// Get the available screen size (without toolbars and docks) for the screen + /// the client window inhabits. + GetAvailableScreenSize(WebViewId, IpcSender<DeviceIndependentIntSize>), +} + +impl fmt::Debug for CrossProcessCompositorMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::AddImage(..) => f.write_str("AddImage"), + Self::GenerateFontKeys(..) => f.write_str("GenerateFontKeys"), + Self::AddSystemFont(..) => f.write_str("AddSystemFont"), + Self::SendInitialTransaction(..) => f.write_str("SendInitialTransaction"), + Self::SendScrollNode(..) => f.write_str("SendScrollNode"), + Self::SendDisplayList { .. } => f.write_str("SendDisplayList"), + Self::HitTest(..) => f.write_str("HitTest"), + Self::GenerateImageKey(..) => f.write_str("GenerateImageKey"), + Self::UpdateImages(..) => f.write_str("UpdateImages"), + Self::RemoveFonts(..) => f.write_str("RemoveFonts"), + Self::AddFontInstance(..) => f.write_str("AddFontInstance"), + Self::AddFont(..) => f.write_str("AddFont"), + Self::GetClientWindowRect(..) => f.write_str("GetClientWindowRect"), + Self::GetScreenSize(..) => f.write_str("GetScreenSize"), + Self::GetAvailableScreenSize(..) => f.write_str("GetAvailableScreenSize"), + } + } +} + +/// A mechanism to send messages from ScriptThread to the parent process' WebRender instance. +#[derive(Clone, Deserialize, Serialize)] +pub struct CrossProcessCompositorApi(pub IpcSender<CrossProcessCompositorMessage>); + +impl CrossProcessCompositorApi { + /// Create a new [`CrossProcessCompositorApi`] struct that does not have a listener on the other + /// end to use for unit testing. + pub fn dummy() -> Self { + let (sender, _) = ipc::channel().unwrap(); + Self(sender) + } + + /// Get the sender for this proxy. + pub fn sender(&self) -> &IpcSender<CrossProcessCompositorMessage> { + &self.0 + } + + /// Inform WebRender of the existence of this pipeline. + pub fn send_initial_transaction(&self, pipeline: WebRenderPipelineId) { + if let Err(e) = self + .0 + .send(CrossProcessCompositorMessage::SendInitialTransaction( + pipeline, + )) + { + warn!("Error sending initial transaction: {}", e); + } + } + + /// Perform a scroll operation. + pub fn send_scroll_node( + &self, + webview_id: WebViewId, + pipeline_id: WebRenderPipelineId, + point: LayoutPoint, + scroll_id: ExternalScrollId, + ) { + if let Err(e) = self.0.send(CrossProcessCompositorMessage::SendScrollNode( + webview_id, + pipeline_id, + point, + scroll_id, + )) { + warn!("Error sending scroll node: {}", e); + } + } + + /// Inform WebRender of a new display list for the given pipeline. + pub fn send_display_list( + &self, + webview_id: WebViewId, + display_list_info: CompositorDisplayListInfo, + list: BuiltDisplayList, + ) { + let (display_list_data, display_list_descriptor) = list.into_data(); + let (display_list_sender, display_list_receiver) = ipc::bytes_channel().unwrap(); + if let Err(e) = self.0.send(CrossProcessCompositorMessage::SendDisplayList { + webview_id, + display_list_info: Box::new(display_list_info), + display_list_descriptor, + display_list_receiver, + }) { + warn!("Error sending display list: {}", e); + } + + if let Err(error) = display_list_sender.send(&display_list_data.items_data) { + warn!("Error sending display list items: {}", error); + } + if let Err(error) = display_list_sender.send(&display_list_data.cache_data) { + warn!("Error sending display list cache data: {}", error); + } + if let Err(error) = display_list_sender.send(&display_list_data.spatial_tree) { + warn!("Error sending display spatial tree: {}", error); + } + } + + /// Perform a hit test operation. Blocks until the operation is complete and + /// and a result is available. + pub fn hit_test( + &self, + pipeline: Option<WebRenderPipelineId>, + point: DevicePoint, + flags: HitTestFlags, + ) -> Vec<CompositorHitTestResult> { + let (sender, receiver) = ipc::channel().unwrap(); + self.0 + .send(CrossProcessCompositorMessage::HitTest( + pipeline, point, flags, sender, + )) + .expect("error sending hit test"); + receiver.recv().expect("error receiving hit test result") + } + + /// Create a new image key. Blocks until the key is available. + pub fn generate_image_key(&self) -> Option<ImageKey> { + let (sender, receiver) = ipc::channel().unwrap(); + self.0 + .send(CrossProcessCompositorMessage::GenerateImageKey(sender)) + .ok()?; + receiver.recv().ok() + } + + pub fn add_image( + &self, + key: ImageKey, + descriptor: ImageDescriptor, + data: SerializableImageData, + ) { + if let Err(e) = self.0.send(CrossProcessCompositorMessage::AddImage( + key, descriptor, data, + )) { + warn!("Error sending image update: {}", e); + } + } + + /// Perform an image resource update operation. + pub fn update_images(&self, updates: Vec<ImageUpdate>) { + if let Err(e) = self + .0 + .send(CrossProcessCompositorMessage::UpdateImages(updates)) + { + warn!("error sending image updates: {}", e); + } + } + + pub fn remove_unused_font_resources( + &self, + keys: Vec<FontKey>, + instance_keys: Vec<FontInstanceKey>, + ) { + if keys.is_empty() && instance_keys.is_empty() { + return; + } + let _ = self.0.send(CrossProcessCompositorMessage::RemoveFonts( + keys, + instance_keys, + )); + } + + pub fn add_font_instance( + &self, + font_instance_key: FontInstanceKey, + font_key: FontKey, + size: f32, + flags: FontInstanceFlags, + ) { + let _x = self.0.send(CrossProcessCompositorMessage::AddFontInstance( + font_instance_key, + font_key, + size, + flags, + )); + } + + pub fn add_font(&self, font_key: FontKey, data: Arc<IpcSharedMemory>, index: u32) { + let _ = self.0.send(CrossProcessCompositorMessage::AddFont( + font_key, data, index, + )); + } + + pub fn add_system_font(&self, font_key: FontKey, handle: NativeFontHandle) { + let _ = self.0.send(CrossProcessCompositorMessage::AddSystemFont( + font_key, handle, + )); + } + + pub fn fetch_font_keys( + &self, + number_of_font_keys: usize, + number_of_font_instance_keys: usize, + ) -> (Vec<FontKey>, Vec<FontInstanceKey>) { + let (sender, receiver) = ipc_channel::ipc::channel().expect("Could not create IPC channel"); + let _ = self.0.send(CrossProcessCompositorMessage::GenerateFontKeys( + number_of_font_keys, + number_of_font_instance_keys, + sender, + )); + receiver.recv().unwrap() + } +} + +/// This trait is used as a bridge between the different GL clients +/// in Servo that handles WebRender ExternalImages and the WebRender +/// ExternalImageHandler API. +// +/// This trait is used to notify lock/unlock messages and get the +/// required info that WR needs. +pub trait WebrenderExternalImageApi { + fn lock(&mut self, id: u64) -> (WebrenderImageSource, UntypedSize2D<i32>); + fn unlock(&mut self, id: u64); +} + +pub enum WebrenderImageSource<'a> { + TextureHandle(u32), + Raw(&'a [u8]), +} + +/// Type of Webrender External Image Handler. +pub enum WebrenderImageHandlerType { + WebGL, + Media, + WebGPU, +} + +/// List of Webrender external images to be shared among all external image +/// consumers (WebGL, Media, WebGPU). +/// It ensures that external image identifiers are unique. +#[derive(Default)] +pub struct WebrenderExternalImageRegistry { + /// Map of all generated external images. + external_images: HashMap<ExternalImageId, WebrenderImageHandlerType>, + /// Id generator for the next external image identifier. + next_image_id: u64, +} + +impl WebrenderExternalImageRegistry { + pub fn next_id(&mut self, handler_type: WebrenderImageHandlerType) -> ExternalImageId { + self.next_image_id += 1; + let key = ExternalImageId(self.next_image_id); + self.external_images.insert(key, handler_type); + key + } + + pub fn remove(&mut self, key: &ExternalImageId) { + self.external_images.remove(key); + } + + pub fn get(&self, key: &ExternalImageId) -> Option<&WebrenderImageHandlerType> { + self.external_images.get(key) + } +} + +/// WebRender External Image Handler implementation. +pub struct WebrenderExternalImageHandlers { + /// WebGL handler. + webgl_handler: Option<Box<dyn WebrenderExternalImageApi>>, + /// Media player handler. + media_handler: Option<Box<dyn WebrenderExternalImageApi>>, + /// WebGPU handler. + webgpu_handler: Option<Box<dyn WebrenderExternalImageApi>>, + /// Webrender external images. + external_images: Arc<Mutex<WebrenderExternalImageRegistry>>, +} + +impl WebrenderExternalImageHandlers { + pub fn new() -> (Self, Arc<Mutex<WebrenderExternalImageRegistry>>) { + let external_images = Arc::new(Mutex::new(WebrenderExternalImageRegistry::default())); + ( + Self { + webgl_handler: None, + media_handler: None, + webgpu_handler: None, + external_images: external_images.clone(), + }, + external_images, + ) + } + + pub fn set_handler( + &mut self, + handler: Box<dyn WebrenderExternalImageApi>, + handler_type: WebrenderImageHandlerType, + ) { + match handler_type { + WebrenderImageHandlerType::WebGL => self.webgl_handler = Some(handler), + WebrenderImageHandlerType::Media => self.media_handler = Some(handler), + WebrenderImageHandlerType::WebGPU => self.webgpu_handler = Some(handler), + } + } +} + +impl ExternalImageHandler for WebrenderExternalImageHandlers { + /// Lock the external image. Then, WR could start to read the + /// image content. + /// The WR client should not change the image content until the + /// unlock() call. + fn lock(&mut self, key: ExternalImageId, _channel_index: u8) -> ExternalImage { + let external_images = self.external_images.lock().unwrap(); + let handler_type = external_images + .get(&key) + .expect("Tried to get unknown external image"); + match handler_type { + WebrenderImageHandlerType::WebGL => { + let (source, size) = self.webgl_handler.as_mut().unwrap().lock(key.0); + let texture_id = match source { + WebrenderImageSource::TextureHandle(b) => b, + _ => panic!("Wrong type"), + }; + ExternalImage { + uv: TexelRect::new(0.0, size.height as f32, size.width as f32, 0.0), + source: ExternalImageSource::NativeTexture(texture_id), + } + }, + WebrenderImageHandlerType::Media => { + let (source, size) = self.media_handler.as_mut().unwrap().lock(key.0); + let texture_id = match source { + WebrenderImageSource::TextureHandle(b) => b, + _ => panic!("Wrong type"), + }; + ExternalImage { + uv: TexelRect::new(0.0, size.height as f32, size.width as f32, 0.0), + source: ExternalImageSource::NativeTexture(texture_id), + } + }, + WebrenderImageHandlerType::WebGPU => { + let (source, size) = self.webgpu_handler.as_mut().unwrap().lock(key.0); + let buffer = match source { + WebrenderImageSource::Raw(b) => b, + _ => panic!("Wrong type"), + }; + ExternalImage { + uv: TexelRect::new(0.0, size.height as f32, size.width as f32, 0.0), + source: ExternalImageSource::RawData(buffer), + } + }, + } + } + + /// Unlock the external image. The WR should not read the image + /// content after this call. + fn unlock(&mut self, key: ExternalImageId, _channel_index: u8) { + let external_images = self.external_images.lock().unwrap(); + let handler_type = external_images + .get(&key) + .expect("Tried to get unknown external image"); + match handler_type { + WebrenderImageHandlerType::WebGL => self.webgl_handler.as_mut().unwrap().unlock(key.0), + WebrenderImageHandlerType::Media => self.media_handler.as_mut().unwrap().unlock(key.0), + WebrenderImageHandlerType::WebGPU => { + self.webgpu_handler.as_mut().unwrap().unlock(key.0) + }, + }; + } +} + +#[derive(Deserialize, Serialize)] +/// Serializable image updates that must be performed by WebRender. +pub enum ImageUpdate { + /// Register a new image. + AddImage(ImageKey, ImageDescriptor, SerializableImageData), + /// Delete a previously registered image registration. + DeleteImage(ImageKey), + /// Update an existing image registration. + UpdateImage(ImageKey, ImageDescriptor, SerializableImageData), +} + +#[derive(Debug, Deserialize, Serialize)] +/// Serialized `ImageData`. It contains IPC byte channel receiver to prevent from loading bytes too +/// slow. +pub enum SerializableImageData { + /// A simple series of bytes, provided by the embedding and owned by WebRender. + /// The format is stored out-of-band, currently in ImageDescriptor. + Raw(IpcSharedMemory), + /// An image owned by the embedding, and referenced by WebRender. This may + /// take the form of a texture or a heap-allocated buffer. + External(ExternalImageData), +} + +impl From<SerializableImageData> for ImageData { + fn from(value: SerializableImageData) -> Self { + match value { + SerializableImageData::Raw(shared_memory) => ImageData::new(shared_memory.to_vec()), + SerializableImageData::External(image) => ImageData::External(image), + } + } +} + +/// A trait that exposes the embedding layer's `WebView` to the Servo renderer. +/// This is to prevent a dependency cycle between the renderer and the embedding +/// layer. +pub trait RendererWebView { + fn id(&self) -> WebViewId; + fn screen_geometry(&self) -> Option<ScreenGeometry>; +} |