compositor: Allow canvas to upload rendered contents asynchronously (#37776)

Adds epoch to each WR image op command that is sent to compositor. The
renderer now has a `FrameDelayer` data structure that is responsible for
tracking when a frame is ready to be displayed. When asking canvases to
update their rendering, they are given an optional `Epoch` which denotes
the `Document`'s canvas epoch. When all image updates for that `Epoch`
are seen in the renderer, the frame can be displayed.

Testing: Existing WPT tests
Fixes: #35733

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
Sam 2025-08-29 12:04:41 +02:00 committed by GitHub
parent 4700149fcb
commit 8beef6c21f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
36 changed files with 452 additions and 100 deletions

View file

@ -6,6 +6,7 @@
use std::fmt::{Debug, Error, Formatter};
use base::Epoch;
use base::id::{PipelineId, WebViewId};
use crossbeam_channel::Sender;
use embedder_traits::{AnimationState, EventLoopWaker, TouchEventResult};
@ -131,6 +132,11 @@ pub enum CompositorMsg {
GenerateImageKeysForPipeline(PipelineId),
/// Perform a resource update operation.
UpdateImages(SmallVec<[ImageUpdate; 1]>),
/// Pause all pipeline display list processing for the given pipeline until the
/// following image updates have been received. This is used to ensure that canvas
/// elements have had a chance to update their rendering and send the image update to
/// the renderer before their associated display list is actually displayed.
DelayNewFrameForCanvas(PipelineId, Epoch, Vec<ImageKey>),
/// Generate a new batch of font keys which can be used to allocate
/// keys asynchronously.
@ -222,6 +228,21 @@ impl CrossProcessCompositorApi {
}
}
pub fn delay_new_frame_for_canvas(
&self,
pipeline_id: PipelineId,
canvas_epoch: Epoch,
image_keys: Vec<ImageKey>,
) {
if let Err(error) = self.0.send(CompositorMsg::DelayNewFrameForCanvas(
pipeline_id,
canvas_epoch,
image_keys,
)) {
warn!("Error delaying frames for canvas image updates {error:?}");
}
}
/// Inform WebRender of a new display list for the given pipeline.
pub fn send_display_list(
&self,
@ -296,8 +317,9 @@ impl CrossProcessCompositorApi {
key: ImageKey,
descriptor: ImageDescriptor,
data: SerializableImageData,
epoch: Option<Epoch>,
) {
self.update_images([ImageUpdate::UpdateImage(key, descriptor, data)].into());
self.update_images([ImageUpdate::UpdateImage(key, descriptor, data, epoch)].into());
}
pub fn delete_image(&self, key: ImageKey) {
@ -538,7 +560,31 @@ pub enum ImageUpdate {
/// Delete a previously registered image registration.
DeleteImage(ImageKey),
/// Update an existing image registration.
UpdateImage(ImageKey, ImageDescriptor, SerializableImageData),
UpdateImage(
ImageKey,
ImageDescriptor,
SerializableImageData,
Option<Epoch>,
),
}
impl Debug for ImageUpdate {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::AddImage(image_key, image_desc, _) => f
.debug_tuple("AddImage")
.field(image_key)
.field(image_desc)
.finish(),
Self::DeleteImage(image_key) => f.debug_tuple("DeleteImage").field(image_key).finish(),
Self::UpdateImage(image_key, image_desc, _, epoch) => f
.debug_tuple("UpdateImage")
.field(image_key)
.field(image_desc)
.field(epoch)
.finish(),
}
}
}
#[derive(Debug, Deserialize, Serialize)]