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

@ -16,8 +16,7 @@ use base::cross_process_instant::CrossProcessInstant;
use base::id::{PipelineId, TopLevelBrowsingContextId, WebViewId};
use base::{Epoch, WebRenderEpochToU16};
use compositing_traits::{
CompositionPipeline, CompositorMsg, CompositorReceiver, ConstellationMsg,
ForwardedToCompositorMsg, SendableFrameTree,
CompositionPipeline, CompositorMsg, CompositorReceiver, ConstellationMsg, SendableFrameTree,
};
use crossbeam_channel::Sender;
use embedder_traits::Cursor;
@ -51,8 +50,7 @@ use webrender_api::{
};
use webrender_traits::display_list::{HitTestInfo, ScrollTree};
use webrender_traits::{
CanvasToCompositorMsg, CompositorHitTestResult, FontToCompositorMsg, ImageUpdate,
NetToCompositorMsg, RenderingContext, ScriptToCompositorMsg, SerializedImageUpdate,
CompositorHitTestResult, CrossProcessCompositorMessage, ImageUpdate, RenderingContext,
UntrustedNodeAddress,
};
@ -653,29 +651,8 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
self.pending_paint_metrics.insert(pipeline_id, epoch);
},
CompositorMsg::GetClientWindow(req) => {
if let Err(e) = req.send(self.embedder_coordinates.window) {
warn!("Sending response to get client window failed ({:?}).", e);
}
},
CompositorMsg::GetScreenSize(req) => {
if let Err(e) = req.send(self.embedder_coordinates.screen) {
warn!("Sending response to get screen size failed ({:?}).", e);
}
},
CompositorMsg::GetScreenAvailSize(req) => {
if let Err(e) = req.send(self.embedder_coordinates.screen_avail) {
warn!(
"Sending response to get screen avail size failed ({:?}).",
e
);
}
},
CompositorMsg::Forwarded(msg) => {
self.handle_webrender_message(msg);
CompositorMsg::CrossProcess(cross_proces_message) => {
self.handle_cross_process_message(cross_proces_message);
},
}
@ -685,11 +662,9 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
/// Accept messages from content processes that need to be relayed to the WebRender
/// instance in the parent process.
#[instrument(skip_all, fields(servo_profiling = true))]
fn handle_webrender_message(&mut self, msg: ForwardedToCompositorMsg) {
fn handle_cross_process_message(&mut self, msg: CrossProcessCompositorMessage) {
match msg {
ForwardedToCompositorMsg::Layout(ScriptToCompositorMsg::SendInitialTransaction(
pipeline,
)) => {
CrossProcessCompositorMessage::SendInitialTransaction(pipeline) => {
let mut txn = Transaction::new();
txn.set_display_list(WebRenderEpoch(0), (pipeline, Default::default()));
self.generate_frame(&mut txn, RenderReasons::SCENE);
@ -697,11 +672,11 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
.send_transaction(self.webrender_document, txn);
},
ForwardedToCompositorMsg::Layout(ScriptToCompositorMsg::SendScrollNode(
CrossProcessCompositorMessage::SendScrollNode(
pipeline_id,
point,
external_scroll_id,
)) => {
) => {
let pipeline_id = pipeline_id.into();
let pipeline_details = match self.pipeline_details.get_mut(&pipeline_id) {
Some(details) => details,
@ -733,11 +708,11 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
.send_transaction(self.webrender_document, txn);
},
ForwardedToCompositorMsg::Layout(ScriptToCompositorMsg::SendDisplayList {
CrossProcessCompositorMessage::SendDisplayList {
display_list_info,
display_list_descriptor,
display_list_receiver,
}) => {
} => {
// This must match the order from the sender, currently in `shared/script/lib.rs`.
let items_data = match display_list_receiver.recv() {
Ok(display_list_data) => display_list_data,
@ -793,12 +768,7 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
.send_transaction(self.webrender_document, transaction);
},
ForwardedToCompositorMsg::Layout(ScriptToCompositorMsg::HitTest(
pipeline,
point,
flags,
sender,
)) => {
CrossProcessCompositorMessage::HitTest(pipeline, point, flags, sender) => {
// When a display list is sent to WebRender, it starts scene building in a
// separate thread and then that display list is available for hit testing.
// Without flushing scene building, any hit test we do might be done against
@ -815,27 +785,20 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
let _ = sender.send(result);
},
ForwardedToCompositorMsg::Layout(ScriptToCompositorMsg::GenerateImageKey(sender)) |
ForwardedToCompositorMsg::Net(NetToCompositorMsg::GenerateImageKey(sender)) => {
CrossProcessCompositorMessage::GenerateImageKey(sender) => {
let _ = sender.send(self.webrender_api.generate_image_key());
},
ForwardedToCompositorMsg::Layout(ScriptToCompositorMsg::UpdateImages(updates)) => {
CrossProcessCompositorMessage::UpdateImages(updates) => {
let mut txn = Transaction::new();
for update in updates {
match update {
SerializedImageUpdate::AddImage(key, desc, data) => {
match data.to_image_data() {
Ok(data) => txn.add_image(key, desc, data, None),
Err(e) => warn!("error when sending image data: {:?}", e),
}
ImageUpdate::AddImage(key, desc, data) => {
txn.add_image(key, desc, data.into(), None)
},
SerializedImageUpdate::DeleteImage(key) => txn.delete_image(key),
SerializedImageUpdate::UpdateImage(key, desc, data) => {
match data.to_image_data() {
Ok(data) => txn.update_image(key, desc, data, &DirtyRect::All),
Err(e) => warn!("error when sending image data: {:?}", e),
}
ImageUpdate::DeleteImage(key) => txn.delete_image(key),
ImageUpdate::UpdateImage(key, desc, data) => {
txn.update_image(key, desc, data.into(), &DirtyRect::All)
},
}
}
@ -843,27 +806,27 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
.send_transaction(self.webrender_document, txn);
},
ForwardedToCompositorMsg::Layout(ScriptToCompositorMsg::AddFont(
font_key,
data,
index,
)) => {
CrossProcessCompositorMessage::AddFont(font_key, data, index) => {
self.add_font(font_key, index, data);
},
ForwardedToCompositorMsg::Layout(ScriptToCompositorMsg::AddFontInstance(
CrossProcessCompositorMessage::AddSystemFont(font_key, native_handle) => {
let mut transaction = Transaction::new();
transaction.add_native_font(font_key, native_handle);
self.webrender_api
.send_transaction(self.webrender_document, transaction);
},
CrossProcessCompositorMessage::AddFontInstance(
font_instance_key,
font_key,
size,
flags,
)) => {
) => {
self.add_font_instance(font_instance_key, font_key, size, flags);
},
ForwardedToCompositorMsg::Layout(ScriptToCompositorMsg::RemoveFonts(
keys,
instance_keys,
)) => {
CrossProcessCompositorMessage::RemoveFonts(keys, instance_keys) => {
let mut transaction = Transaction::new();
for instance in instance_keys.into_iter() {
@ -877,18 +840,18 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
.send_transaction(self.webrender_document, transaction);
},
ForwardedToCompositorMsg::Net(NetToCompositorMsg::AddImage(key, desc, data)) => {
CrossProcessCompositorMessage::AddImage(key, desc, data) => {
let mut txn = Transaction::new();
txn.add_image(key, desc, data, None);
txn.add_image(key, desc, data.into(), None);
self.webrender_api
.send_transaction(self.webrender_document, txn);
},
ForwardedToCompositorMsg::SystemFontService(FontToCompositorMsg::GenerateKeys(
CrossProcessCompositorMessage::GenerateFontKeys(
number_of_font_keys,
number_of_font_instance_keys,
result_sender,
)) => {
) => {
let font_keys = (0..number_of_font_keys)
.map(|_| self.webrender_api.generate_font_key())
.collect();
@ -897,53 +860,23 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
.collect();
let _ = result_sender.send((font_keys, font_instance_keys));
},
ForwardedToCompositorMsg::SystemFontService(FontToCompositorMsg::AddFontInstance(
font_instance_key,
font_key,
size,
flags,
)) => {
self.add_font_instance(font_instance_key, font_key, size, flags);
},
ForwardedToCompositorMsg::SystemFontService(FontToCompositorMsg::AddFont(
font_key,
index,
data,
)) => {
self.add_font(font_key, index, data);
},
ForwardedToCompositorMsg::SystemFontService(FontToCompositorMsg::AddSystemFont(
font_key,
native_handle,
)) => {
let mut transaction = Transaction::new();
transaction.add_native_font(font_key, native_handle);
self.webrender_api
.send_transaction(self.webrender_document, transaction);
},
ForwardedToCompositorMsg::Canvas(CanvasToCompositorMsg::GenerateKey(sender)) => {
let _ = sender.send(self.webrender_api.generate_image_key());
},
ForwardedToCompositorMsg::Canvas(CanvasToCompositorMsg::UpdateImages(updates)) => {
let mut txn = Transaction::new();
for update in updates {
match update {
ImageUpdate::AddImage(key, descriptor, data) => {
txn.add_image(key, descriptor, data, None)
},
ImageUpdate::UpdateImage(key, descriptor, data) => {
txn.update_image(key, descriptor, data, &DirtyRect::All)
},
ImageUpdate::DeleteImage(key) => txn.delete_image(key),
}
CrossProcessCompositorMessage::GetClientWindowRect(req) => {
if let Err(e) = req.send(self.embedder_coordinates.window_rect) {
warn!("Sending response to get client window failed ({:?}).", e);
}
},
CrossProcessCompositorMessage::GetScreenSize(req) => {
if let Err(e) = req.send(self.embedder_coordinates.screen_size) {
warn!("Sending response to get screen size failed ({:?}).", e);
}
},
CrossProcessCompositorMessage::GetAvailableScreenSize(req) => {
if let Err(e) = req.send(self.embedder_coordinates.available_screen_size) {
warn!(
"Sending response to get screen avail size failed ({:?}).",
e
);
}
self.webrender_api
.send_transaction(self.webrender_document, txn);
},
}
}
@ -968,23 +901,40 @@ impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
self.remove_pipeline_root_layer(pipeline_id);
let _ = sender.send(());
},
CompositorMsg::Forwarded(ForwardedToCompositorMsg::Canvas(
CanvasToCompositorMsg::GenerateKey(sender),
CompositorMsg::CrossProcess(CrossProcessCompositorMessage::GenerateImageKey(
sender,
)) => {
let _ = sender.send(self.webrender_api.generate_image_key());
},
CompositorMsg::GetClientWindow(sender) => {
if let Err(e) = sender.send(self.embedder_coordinates.window) {
CompositorMsg::CrossProcess(CrossProcessCompositorMessage::GenerateFontKeys(
number_of_font_keys,
number_of_font_instance_keys,
result_sender,
)) => {
let font_keys = (0..number_of_font_keys)
.map(|_| self.webrender_api.generate_font_key())
.collect();
let font_instance_keys = (0..number_of_font_instance_keys)
.map(|_| self.webrender_api.generate_font_instance_key())
.collect();
let _ = result_sender.send((font_keys, font_instance_keys));
},
CompositorMsg::CrossProcess(CrossProcessCompositorMessage::GetClientWindowRect(
req,
)) => {
if let Err(e) = req.send(self.embedder_coordinates.window_rect) {
warn!("Sending response to get client window failed ({:?}).", e);
}
},
CompositorMsg::GetScreenSize(sender) => {
if let Err(e) = sender.send(self.embedder_coordinates.screen) {
CompositorMsg::CrossProcess(CrossProcessCompositorMessage::GetScreenSize(req)) => {
if let Err(e) = req.send(self.embedder_coordinates.screen_size) {
warn!("Sending response to get screen size failed ({:?}).", e);
}
},
CompositorMsg::GetScreenAvailSize(sender) => {
if let Err(e) = sender.send(self.embedder_coordinates.screen_avail) {
CompositorMsg::CrossProcess(CrossProcessCompositorMessage::GetAvailableScreenSize(
req,
)) => {
if let Err(e) = req.send(self.embedder_coordinates.available_screen_size) {
warn!(
"Sending response to get screen avail size failed ({:?}).",
e