mirror of
https://github.com/servo/servo.git
synced 2025-09-30 08:39:16 +01:00
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:
parent
4700149fcb
commit
8beef6c21f
36 changed files with 452 additions and 100 deletions
|
@ -12,10 +12,11 @@ use std::str::FromStr;
|
|||
use std::sync::{LazyLock, Mutex};
|
||||
use std::time::Duration;
|
||||
|
||||
use base::Epoch;
|
||||
use base::cross_process_instant::CrossProcessInstant;
|
||||
use base::id::WebViewId;
|
||||
use canvas_traits::canvas::CanvasId;
|
||||
use canvas_traits::webgl::{self, WebGLContextId, WebGLMsg};
|
||||
use canvas_traits::webgl::{WebGLContextId, WebGLMsg};
|
||||
use chrono::Local;
|
||||
use constellation_traits::{NavigationHistoryBehavior, ScriptToConstellationMessage};
|
||||
use content_security_policy::{CspList, PolicyDisposition};
|
||||
|
@ -556,6 +557,15 @@ pub(crate) struct Document {
|
|||
/// When a `ResizeObserver` starts observing a target, this becomes true, which in turn is a
|
||||
/// signal to the [`ScriptThread`] that a rendering update should happen.
|
||||
resize_observer_started_observing_target: Cell<bool>,
|
||||
/// Whether or not this [`Document`] is waiting on canvas image updates. If it is
|
||||
/// waiting it will not do any new layout until the canvas images are up-to-date in
|
||||
/// the renderer.
|
||||
waiting_on_canvas_image_updates: Cell<bool>,
|
||||
/// The current canvas epoch, which is used to track when canvas images have been
|
||||
/// uploaded to the renderer after a rendering update. Until those images are uploaded
|
||||
/// this `Document` will not perform any more rendering updates.
|
||||
#[no_trace]
|
||||
current_canvas_epoch: RefCell<Epoch>,
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
|
@ -2671,40 +2681,69 @@ impl Document {
|
|||
}
|
||||
|
||||
// All dirty canvases are flushed before updating the rendering.
|
||||
#[cfg(feature = "webgpu")]
|
||||
self.webgpu_contexts
|
||||
.borrow_mut()
|
||||
.iter()
|
||||
.filter_map(|(_, context)| context.root())
|
||||
.filter(|context| context.onscreen())
|
||||
.for_each(|context| context.update_rendering());
|
||||
self.current_canvas_epoch.borrow_mut().next();
|
||||
let canvas_epoch = *self.current_canvas_epoch.borrow();
|
||||
let mut image_keys = Vec::new();
|
||||
|
||||
self.dirty_2d_contexts
|
||||
.borrow_mut()
|
||||
.drain()
|
||||
.filter(|(_, context)| context.onscreen())
|
||||
.for_each(|(_, context)| context.update_rendering());
|
||||
#[cfg(feature = "webgpu")]
|
||||
image_keys.extend(
|
||||
self.webgpu_contexts
|
||||
.borrow_mut()
|
||||
.iter()
|
||||
.filter_map(|(_, context)| context.root())
|
||||
.filter(|context| context.update_rendering(Some(canvas_epoch)))
|
||||
.map(|context| context.image_key()),
|
||||
);
|
||||
|
||||
image_keys.extend(
|
||||
self.dirty_2d_contexts
|
||||
.borrow_mut()
|
||||
.drain()
|
||||
.filter(|(_, context)| context.update_rendering(Some(canvas_epoch)))
|
||||
.map(|(_, context)| context.image_key()),
|
||||
);
|
||||
|
||||
let dirty_webgl_context_ids: Vec<_> = self
|
||||
.dirty_webgl_contexts
|
||||
.borrow_mut()
|
||||
.drain()
|
||||
.filter(|(_, context)| context.onscreen())
|
||||
.map(|(id, _)| id)
|
||||
.map(|(id, context)| {
|
||||
image_keys.push(context.image_key());
|
||||
id
|
||||
})
|
||||
.collect();
|
||||
|
||||
if !dirty_webgl_context_ids.is_empty() {
|
||||
let (sender, receiver) = webgl::webgl_channel().unwrap();
|
||||
self.window
|
||||
.webgl_chan()
|
||||
.expect("Where's the WebGL channel?")
|
||||
.send(WebGLMsg::SwapBuffers(dirty_webgl_context_ids, sender, 0))
|
||||
.send(WebGLMsg::SwapBuffers(
|
||||
dirty_webgl_context_ids,
|
||||
Some(canvas_epoch),
|
||||
0,
|
||||
))
|
||||
.unwrap();
|
||||
receiver.recv().unwrap();
|
||||
}
|
||||
|
||||
// The renderer should wait to display the frame until all canvas images are
|
||||
// uploaded. This allows canvas image uploading to happen asynchronously.
|
||||
if !image_keys.is_empty() {
|
||||
self.waiting_on_canvas_image_updates.set(true);
|
||||
self.window().compositor_api().delay_new_frame_for_canvas(
|
||||
self.window().pipeline_id(),
|
||||
canvas_epoch,
|
||||
image_keys.into_iter().flatten().collect(),
|
||||
);
|
||||
}
|
||||
|
||||
self.window().reflow(ReflowGoal::UpdateTheRendering)
|
||||
}
|
||||
|
||||
pub(crate) fn handle_no_longer_waiting_on_asynchronous_image_updates(&self) {
|
||||
self.waiting_on_canvas_image_updates.set(false);
|
||||
}
|
||||
|
||||
/// From <https://drafts.csswg.org/css-font-loading/#fontfaceset-pending-on-the-environment>:
|
||||
///
|
||||
/// > A FontFaceSet is pending on the environment if any of the following are true:
|
||||
|
@ -3390,6 +3429,8 @@ impl Document {
|
|||
adopted_stylesheets_frozen_types: CachedFrozenArray::new(),
|
||||
pending_scroll_event_targets: Default::default(),
|
||||
resize_observer_started_observing_target: Cell::new(false),
|
||||
waiting_on_canvas_image_updates: Cell::new(false),
|
||||
current_canvas_epoch: RefCell::new(Epoch(0)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue