diff options
author | Martin Robinson <mrobinson@igalia.com> | 2024-10-09 10:30:24 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-10-09 17:30:24 +0000 |
commit | 9195344b75b4e5fd1cd86671b10df46a956f0fea (patch) | |
tree | 20ef541e886032195be6ae57403e4c05ff438c26 /components/shared/webrender/lib.rs | |
parent | 30cbf01280dc3aa03caeaba11f55808f7ba59cf3 (diff) | |
download | servo-9195344b75b4e5fd1cd86671b10df46a956f0fea.tar.gz servo-9195344b75b4e5fd1cd86671b10df46a956f0fea.zip |
compositor: Create a single cross-process compositor API (#33619) (#33660)
Instead of exposing many different kinds of messages to the compositor
that are routed through the constellation, expose a single message type
which can be sent across IPC channels. In addition, this IPC channel and
the route to the crossbeam channel with the compositor is created along
with the `CompositorProxy`, simplifying what needs to be passed around
during pipeline initialization.
Previously, some image updates (from video) were sent over IPC with a
special serialization routine and some were sent via crossbeam channels
(canvas). Now all updates go over the IPC channel `IpcSharedMemory` is
used to avoid serialization penalties. This should improve performance
and reduce copies for video, but add a memory copy overhead for canvas.
This will improve in the future when canvas renders directly into a
texture.
All-in-all this is a simplification which opens the path toward having a
standard compositor API and reduces the number of duplicate messages and
proxying that had to happen in libservo.
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Diffstat (limited to 'components/shared/webrender/lib.rs')
-rw-r--r-- | components/shared/webrender/lib.rs | 546 |
1 files changed, 266 insertions, 280 deletions
diff --git a/components/shared/webrender/lib.rs b/components/shared/webrender/lib.rs index 3e51e6bc6ea..807c8909d61 100644 --- a/components/shared/webrender/lib.rs +++ b/components/shared/webrender/lib.rs @@ -7,11 +7,11 @@ pub mod display_list; pub mod rendering_context; +use core::fmt; use std::collections::HashMap; use std::sync::{Arc, Mutex}; use base::id::PipelineId; -use crossbeam_channel::Sender; use display_list::{CompositorDisplayListInfo, ScrollTreeNodeId}; use embedder_traits::Cursor; use euclid::default::Size2D; @@ -19,7 +19,7 @@ use ipc_channel::ipc::{self, IpcSender, IpcSharedMemory}; use libc::c_void; use log::warn; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use webrender_api::units::{DevicePoint, LayoutPoint, TexelRect}; +use webrender_api::units::{DeviceIntRect, DeviceIntSize, DevicePoint, LayoutPoint, TexelRect}; use webrender_api::{ BuiltDisplayList, BuiltDisplayListDescriptor, ExternalImage, ExternalImageData, ExternalImageHandler, ExternalImageId, ExternalImageSource, ExternalScrollId, @@ -29,6 +29,261 @@ use webrender_api::{ pub use crate::rendering_context::RenderingContext; +#[derive(Deserialize, Serialize)] +pub enum CrossProcessCompositorMessage { + /// Inform WebRender of the existence of this pipeline. + SendInitialTransaction(WebRenderPipelineId), + /// Perform a scroll operation. + SendScrollNode(WebRenderPipelineId, LayoutPoint, ExternalScrollId), + /// Inform WebRender of a new display list for the given pipeline. + SendDisplayList { + /// 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(IpcSender<DeviceIntRect>), + /// Get the size of the screen that the client window inhabits. + GetScreenSize(IpcSender<DeviceIntSize>), + /// Get the available screen size (without toolbars and docks) for the screen + /// the client window inhabits. + GetAvailableScreenSize(IpcSender<DeviceIntSize>), +} + +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, + pipeline_id: WebRenderPipelineId, + point: LayoutPoint, + scroll_id: ExternalScrollId, + ) { + if let Err(e) = self.0.send(CrossProcessCompositorMessage::SendScrollNode( + 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, + 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 { + 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. @@ -183,303 +438,34 @@ impl ExternalImageHandler for WebrenderExternalImageHandlers { } } -pub trait WebRenderFontApi { - fn add_font_instance( - &self, - font_instance_key: FontInstanceKey, - font_key: FontKey, - size: f32, - flags: FontInstanceFlags, - ); - fn add_font(&self, font_key: FontKey, data: Arc<IpcSharedMemory>, index: u32); - fn add_system_font(&self, font_key: FontKey, handle: NativeFontHandle); - fn fetch_font_keys( - &self, - number_of_font_keys: usize, - number_of_font_instance_keys: usize, - ) -> (Vec<FontKey>, Vec<FontInstanceKey>); -} - -pub enum CanvasToCompositorMsg { - GenerateKey(Sender<ImageKey>), - UpdateImages(Vec<ImageUpdate>), -} - -pub enum FontToCompositorMsg { - GenerateKeys(usize, usize, Sender<(Vec<FontKey>, Vec<FontInstanceKey>)>), - AddFontInstance(FontInstanceKey, FontKey, f32, FontInstanceFlags), - AddFont(FontKey, u32, Arc<IpcSharedMemory>), - AddSystemFont(FontKey, NativeFontHandle), -} - -#[derive(Deserialize, Serialize)] -pub enum NetToCompositorMsg { - AddImage(ImageKey, ImageDescriptor, ImageData), - GenerateImageKey(IpcSender<ImageKey>), -} - -/// The set of WebRender operations that can be initiated by the content process. -#[derive(Deserialize, Serialize)] -pub enum ScriptToCompositorMsg { - /// Inform WebRender of the existence of this pipeline. - SendInitialTransaction(WebRenderPipelineId), - /// Perform a scroll operation. - SendScrollNode(WebRenderPipelineId, LayoutPoint, ExternalScrollId), - /// Inform WebRender of a new display list for the given pipeline. - SendDisplayList { - /// 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>), - /// Perform a resource update operation. - UpdateImages(Vec<SerializedImageUpdate>), - /// Remove the given font resources from our WebRender instance. - RemoveFonts(Vec<FontKey>, Vec<FontInstanceKey>), - AddFontInstance(FontInstanceKey, FontKey, f32, FontInstanceFlags), - AddFont(FontKey, Arc<IpcSharedMemory>, u32), -} - -/// A mechanism to send messages from networking to the WebRender instance. -#[derive(Clone, Deserialize, Serialize)] -pub struct WebRenderNetApi(IpcSender<NetToCompositorMsg>); - -impl WebRenderNetApi { - pub fn new(sender: IpcSender<NetToCompositorMsg>) -> Self { - Self(sender) - } - - pub fn generate_image_key(&self) -> ImageKey { - let (sender, receiver) = ipc::channel().unwrap(); - self.0 - .send(NetToCompositorMsg::GenerateImageKey(sender)) - .expect("error sending image key generation"); - receiver.recv().expect("error receiving image key result") - } - - pub fn add_image(&self, key: ImageKey, descriptor: ImageDescriptor, data: ImageData) { - if let Err(e) = self - .0 - .send(NetToCompositorMsg::AddImage(key, descriptor, data)) - { - warn!("Error sending image update: {}", e); - } - } -} - -/// A mechanism to send messages from ScriptThread to the parent process' WebRender instance. -#[derive(Clone, Deserialize, Serialize)] -pub struct WebRenderScriptApi(IpcSender<ScriptToCompositorMsg>); - -impl WebRenderScriptApi { - /// Create a new [`WebRenderScriptApi`] object that wraps the provided channel sender. - pub fn new(sender: IpcSender<ScriptToCompositorMsg>) -> Self { - Self(sender) - } - - /// Create a new [`WebRenderScriptApi`] object that does not have a listener on the - /// other end. - pub fn dummy() -> Self { - let (sender, _) = ipc::channel().unwrap(); - Self::new(sender) - } - - /// Get the sender for this proxy. - pub fn sender(&self) -> &IpcSender<ScriptToCompositorMsg> { - &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(ScriptToCompositorMsg::SendInitialTransaction(pipeline)) - { - warn!("Error sending initial transaction: {}", e); - } - } - - /// Perform a scroll operation. - pub fn send_scroll_node( - &self, - pipeline_id: WebRenderPipelineId, - point: LayoutPoint, - scroll_id: ExternalScrollId, - ) { - if let Err(e) = self.0.send(ScriptToCompositorMsg::SendScrollNode( - 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, - 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(ScriptToCompositorMsg::SendDisplayList { - 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(ScriptToCompositorMsg::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(ScriptToCompositorMsg::GenerateImageKey(sender)) - .ok()?; - receiver.recv().ok() - } - - /// Perform a resource update operation. - pub fn update_images(&self, updates: Vec<ImageUpdate>) { - let mut senders = Vec::new(); - // Convert `ImageUpdate` to `SerializedImageUpdate` because `ImageData` may contain large - // byes. With this conversion, we send `IpcBytesReceiver` instead and use it to send the - // actual bytes. - let updates = updates - .into_iter() - .map(|update| match update { - ImageUpdate::AddImage(k, d, data) => { - let data = match data { - ImageData::Raw(r) => { - let (sender, receiver) = ipc::bytes_channel().unwrap(); - senders.push((sender, r)); - SerializedImageData::Raw(receiver) - }, - ImageData::External(e) => SerializedImageData::External(e), - }; - SerializedImageUpdate::AddImage(k, d, data) - }, - ImageUpdate::DeleteImage(k) => SerializedImageUpdate::DeleteImage(k), - ImageUpdate::UpdateImage(k, d, data) => { - let data = match data { - ImageData::Raw(r) => { - let (sender, receiver) = ipc::bytes_channel().unwrap(); - senders.push((sender, r)); - SerializedImageData::Raw(receiver) - }, - ImageData::External(e) => SerializedImageData::External(e), - }; - SerializedImageUpdate::UpdateImage(k, d, data) - }, - }) - .collect(); - - if let Err(e) = self.0.send(ScriptToCompositorMsg::UpdateImages(updates)) { - warn!("error sending image updates: {}", e); - } - - senders.into_iter().for_each(|(tx, data)| { - if let Err(e) = tx.send(&data) { - warn!("error sending image data: {}", 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(ScriptToCompositorMsg::RemoveFonts(keys, instance_keys)); - } -} - #[derive(Deserialize, Serialize)] /// Serializable image updates that must be performed by WebRender. pub enum ImageUpdate { /// Register a new image. - AddImage(ImageKey, ImageDescriptor, ImageData), + AddImage(ImageKey, ImageDescriptor, SerializableImageData), /// Delete a previously registered image registration. DeleteImage(ImageKey), /// Update an existing image registration. - UpdateImage(ImageKey, ImageDescriptor, ImageData), -} - -#[derive(Deserialize, Serialize)] -/// Serialized `ImageUpdate`. -pub enum SerializedImageUpdate { - /// Register a new image. - AddImage(ImageKey, ImageDescriptor, SerializedImageData), - /// Delete a previously registered image registration. - DeleteImage(ImageKey), - /// Update an existing image registration. - UpdateImage(ImageKey, ImageDescriptor, SerializedImageData), + 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 SerializedImageData { +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(ipc::IpcBytesReceiver), + 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 SerializedImageData { - /// Convert to ``ImageData`. - pub fn to_image_data(&self) -> Result<ImageData, ipc::IpcError> { - match self { - SerializedImageData::Raw(rx) => rx.recv().map(ImageData::new), - SerializedImageData::External(image) => Ok(ImageData::External(*image)), +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), } } } |