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>
This commit is contained in:
Martin Robinson 2024-10-09 10:30:24 -07:00 committed by GitHub
parent 30cbf01280
commit 9195344b75
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 547 additions and 800 deletions

View file

@ -21,15 +21,18 @@ use script_traits::{
AnimationState, ConstellationControlMsg, EventResult, MouseButton, MouseEventType,
};
use style_traits::CSSPixel;
use webrender_api::units::{DeviceIntPoint, DeviceIntSize, DeviceRect};
use webrender_api::units::DeviceRect;
use webrender_api::DocumentId;
use webrender_traits::{
CanvasToCompositorMsg, FontToCompositorMsg, NetToCompositorMsg, ScriptToCompositorMsg,
};
use webrender_traits::{CrossProcessCompositorApi, CrossProcessCompositorMessage};
/// Sends messages to the compositor.
#[derive(Clone)]
pub struct CompositorProxy {
pub sender: Sender<CompositorMsg>,
/// Access to [`Self::sender`] that is possible to send across an IPC
/// channel. These messages are routed via the router thread to
/// [`Self::sender`].
pub cross_process_compositor_api: CrossProcessCompositorApi,
pub event_loop_waker: Box<dyn EventLoopWaker>,
}
@ -42,15 +45,6 @@ impl CompositorProxy {
}
}
impl Clone for CompositorProxy {
fn clone(&self) -> CompositorProxy {
CompositorProxy {
sender: self.sender.clone(),
event_loop_waker: self.event_loop_waker.clone(),
}
}
}
/// The port that the compositor receives messages on.
pub struct CompositorReceiver {
pub receiver: Receiver<CompositorMsg>,
@ -114,16 +108,9 @@ pub enum CompositorMsg {
/// WebDriver mouse move event
WebDriverMouseMoveEvent(f32, f32),
/// Get Window Informations size and position.
GetClientWindow(IpcSender<(DeviceIntSize, DeviceIntPoint)>),
/// Get screen size.
GetScreenSize(IpcSender<DeviceIntSize>),
/// Get screen available size.
GetScreenAvailSize(IpcSender<DeviceIntSize>),
/// Messages forwarded to the compositor by the constellation from other crates. These
/// messages are mainly passed on from the compositor to WebRender.
Forwarded(ForwardedToCompositorMsg),
CrossProcess(CrossProcessCompositorMessage),
}
pub struct SendableFrameTree {
@ -139,27 +126,6 @@ pub struct CompositionPipeline {
pub script_chan: IpcSender<ConstellationControlMsg>,
}
/// Messages forwarded by the Constellation to the Compositor.
pub enum ForwardedToCompositorMsg {
Layout(ScriptToCompositorMsg),
Net(NetToCompositorMsg),
SystemFontService(FontToCompositorMsg),
Canvas(CanvasToCompositorMsg),
}
impl Debug for ForwardedToCompositorMsg {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
match self {
ForwardedToCompositorMsg::Layout(_) => write!(f, "Layout(ScriptToCompositorMsg)"),
ForwardedToCompositorMsg::Net(_) => write!(f, "Net(NetToCompositorMsg)"),
ForwardedToCompositorMsg::SystemFontService(_) => {
write!(f, "SystemFontService(FontToCompositorMsg)")
},
ForwardedToCompositorMsg::Canvas(_) => write!(f, "Canvas(CanvasToCompositorMsg)"),
}
}
}
impl Debug for CompositorMsg {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
match *self {
@ -183,10 +149,7 @@ impl Debug for CompositorMsg {
CompositorMsg::LoadComplete(..) => write!(f, "LoadComplete"),
CompositorMsg::WebDriverMouseButtonEvent(..) => write!(f, "WebDriverMouseButtonEvent"),
CompositorMsg::WebDriverMouseMoveEvent(..) => write!(f, "WebDriverMouseMoveEvent"),
CompositorMsg::GetClientWindow(..) => write!(f, "GetClientWindow"),
CompositorMsg::GetScreenSize(..) => write!(f, "GetScreenSize"),
CompositorMsg::GetScreenAvailSize(..) => write!(f, "GetScreenAvailSize"),
CompositorMsg::Forwarded(..) => write!(f, "Webrender"),
CompositorMsg::CrossProcess(..) => write!(f, "CrossProcess"),
}
}
}

View file

@ -10,7 +10,7 @@ use malloc_size_of_derive::MallocSizeOf;
use pixels::{Image, ImageMetadata};
use serde::{Deserialize, Serialize};
use servo_url::{ImmutableOrigin, ServoUrl};
use webrender_traits::WebRenderNetApi;
use webrender_traits::CrossProcessCompositorApi;
use crate::request::CorsSettings;
use crate::FetchResponseMsg;
@ -99,7 +99,7 @@ pub enum ImageCacheResult {
}
pub trait ImageCache: Sync + Send {
fn new(webrender_api: WebRenderNetApi) -> Self
fn new(compositor_api: CrossProcessCompositorApi) -> Self
where
Self: Sized;

View file

@ -58,7 +58,9 @@ use style_traits::{CSSPixel, SpeculativePainter};
use webgpu::WebGPUMsg;
use webrender_api::units::{DeviceIntSize, DevicePixel, LayoutPixel};
use webrender_api::{DocumentId, ExternalScrollId, ImageKey};
use webrender_traits::{UntrustedNodeAddress as WebRenderUntrustedNodeAddress, WebRenderScriptApi};
use webrender_traits::{
CrossProcessCompositorApi, UntrustedNodeAddress as WebRenderUntrustedNodeAddress,
};
pub use crate::script_msg::{
DOMMessage, EventResult, HistoryEntryReplacement, IFrameSizeMsg, Job, JobError, JobResult,
@ -659,8 +661,8 @@ pub struct InitialScriptState {
pub webxr_registry: webxr_api::Registry,
/// The Webrender document ID associated with this thread.
pub webrender_document: DocumentId,
/// FIXME(victor): The Webrender API sender in this constellation's pipeline
pub webrender_api_sender: WebRenderScriptApi,
/// Access to the compositor across a process boundary.
pub compositor_api: CrossProcessCompositorApi,
/// Application window's GL Context for Media player
pub player_context: WindowGLContext,
}

View file

@ -24,7 +24,6 @@ use serde::{Deserialize, Serialize};
use servo_url::{ImmutableOrigin, ServoUrl};
use style_traits::CSSPixel;
use webgpu::{wgc, WebGPU, WebGPUResponse};
use webrender_api::units::{DeviceIntPoint, DeviceIntSize};
use crate::{
AnimationState, AuxiliaryBrowsingContextLoadInfo, BroadcastMsg, DocumentState,
@ -245,12 +244,6 @@ pub enum ScriptMsg {
ForwardDOMMessage(DOMMessage, ServoUrl),
/// <https://w3c.github.io/ServiceWorker/#schedule-job-algorithm>
ScheduleJob(Job),
/// Get Window Informations size and position
GetClientWindow(IpcSender<(DeviceIntSize, DeviceIntPoint)>),
/// Get the screen size (pixel)
GetScreenSize(IpcSender<DeviceIntSize>),
/// Get the available screen size (pixel)
GetScreenAvailSize(IpcSender<DeviceIntSize>),
/// Notifies the constellation about media session events
/// (i.e. when there is metadata for the active media session, playback state changes...).
MediaSessionEvent(PipelineId, MediaSessionEvent),
@ -318,9 +311,6 @@ impl fmt::Debug for ScriptMsg {
PipelineExited => "PipelineExited",
ForwardDOMMessage(..) => "ForwardDOMMessage",
ScheduleJob(..) => "ScheduleJob",
GetClientWindow(..) => "GetClientWindow",
GetScreenSize(..) => "GetScreenSize",
GetScreenAvailSize(..) => "GetScreenAvailSize",
MediaSessionEvent(..) => "MediaSessionEvent",
RequestAdapter(..) => "RequestAdapter",
GetWebGPUChan(..) => "GetWebGPUChan",

View file

@ -53,7 +53,7 @@ use style::stylesheets::Stylesheet;
use style::Atom;
use style_traits::CSSPixel;
use webrender_api::ImageKey;
use webrender_traits::WebRenderScriptApi;
use webrender_traits::CrossProcessCompositorApi;
pub type GenericLayoutData = dyn Any + Send + Sync;
@ -177,7 +177,7 @@ pub struct LayoutConfig {
pub resource_threads: ResourceThreads,
pub system_font_service: Arc<SystemFontServiceProxy>,
pub time_profiler_chan: time::ProfilerChan,
pub webrender_api_sender: WebRenderScriptApi,
pub compositor_api: CrossProcessCompositorApi,
pub paint_time_metrics: PaintTimeMetrics,
pub window_size: WindowSizeData,
}

View file

@ -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),
}
}
}