metrics: Simplify ProgressiveWebMetrics (#35985)

Simply how `ProgressiveWebMetrics` works:

1. Keep only a single struct instead of one in layout and one script
   that both implement the `ProgressiveWebMetrics` trait. Since layout
   and script are the same thread these can now just be a single
   `ProgressiveWebMetrics` struct stored in script.
2. Have the compositor be responsible for informing the Constellation
   (which informs the ScripThread) about paint metrics. This makes
   communication flow one way and removes one dependency between the
   compositor and script (of two).
3. All units tests are moved into the `metrics` crate itself since there
   is only one struct there now.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
Martin Robinson 2025-03-21 15:55:00 +01:00 committed by GitHub
parent 1f232eb17c
commit 5424479768
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 416 additions and 787 deletions

View file

@ -109,7 +109,8 @@ use canvas_traits::ConstellationCanvasMsg;
use canvas_traits::canvas::{CanvasId, CanvasMsg};
use canvas_traits::webgl::WebGLThreads;
use compositing_traits::{
CompositorMsg, CompositorProxy, ConstellationMsg as FromCompositorMsg, SendableFrameTree,
CompositorMsg, CompositorProxy, ConstellationMsg as FromCompositorMsg, PaintMetricEvent,
SendableFrameTree,
};
use crossbeam_channel::{Receiver, Sender, select, unbounded};
use devtools_traits::{
@ -141,8 +142,8 @@ use script_traits::{
AnimationState, AnimationTickType, AuxiliaryWebViewCreationRequest,
AuxiliaryWebViewCreationResponse, BroadcastMsg, ConstellationInputEvent,
DiscardBrowsingContext, DocumentActivity, DocumentState, IFrameLoadInfo,
IFrameLoadInfoWithData, IFrameSandboxState, IFrameSizeMsg, Job, LayoutMsg as FromLayoutMsg,
LoadData, LoadOrigin, LogEntry, MessagePortMsg, NavigationHistoryBehavior, PortMessageTask,
IFrameLoadInfoWithData, IFrameSandboxState, IFrameSizeMsg, Job, LoadData, LoadOrigin, LogEntry,
MessagePortMsg, NavigationHistoryBehavior, PortMessageTask, ProgressiveWebMetricType,
SWManagerMsg, SWManagerSenders, ScriptMsg as FromScriptMsg, ScriptThreadMessage,
ScriptToConstellationChan, ServiceWorkerManagerFactory, ServiceWorkerMsg,
StructuredSerializedData, UpdatePipelineIdReason, WindowSizeData, WindowSizeType,
@ -309,14 +310,6 @@ pub struct Constellation<STF, SWF> {
/// dependency between script and layout.
layout_factory: Arc<dyn LayoutFactory>,
/// An IPC channel for layout to send messages to the constellation.
/// This is the layout's view of `layout_receiver`.
layout_sender: IpcSender<FromLayoutMsg>,
/// A channel for the constellation to receive messages from layout.
/// This is the constellation's view of `layout_sender`.
layout_receiver: Receiver<Result<FromLayoutMsg, IpcError>>,
/// A channel for the constellation to receive messages from the compositor thread.
compositor_receiver: Receiver<FromCompositorMsg>,
@ -661,13 +654,6 @@ where
)
};
let (layout_ipc_sender, layout_ipc_receiver) =
ipc::channel().expect("ipc channel failure");
let layout_receiver =
route_ipc_receiver_to_new_crossbeam_receiver_preserving_errors(
layout_ipc_receiver,
);
let swmanager_receiver =
route_ipc_receiver_to_new_crossbeam_receiver_preserving_errors(
swmanager_ipc_receiver,
@ -693,11 +679,9 @@ where
background_hang_monitor_receiver,
background_monitor_register,
background_monitor_control_senders: background_hang_monitor_control_ipc_senders,
layout_sender: layout_ipc_sender,
script_receiver,
compositor_receiver,
layout_factory,
layout_receiver,
embedder_proxy: state.embedder_proxy,
compositor_proxy: state.compositor_proxy,
webviews: WebViewManager::default(),
@ -967,7 +951,6 @@ where
background_hang_monitor_to_constellation_chan: self
.background_hang_monitor_sender
.clone(),
layout_to_constellation_chan: self.layout_sender.clone(),
layout_factory: self.layout_factory.clone(),
compositor_proxy: self.compositor_proxy.clone(),
devtools_sender: self.devtools_sender.clone(),
@ -1132,7 +1115,6 @@ where
Script((PipelineId, FromScriptMsg)),
BackgroundHangMonitor(HangMonitorAlert),
Compositor(FromCompositorMsg),
Layout(FromLayoutMsg),
FromSWManager(SWManagerMsg),
}
// Get one incoming request.
@ -1163,9 +1145,6 @@ where
recv(self.compositor_receiver) -> msg => {
Ok(Request::Compositor(msg.expect("Unexpected compositor channel panic in constellation")))
}
recv(self.layout_receiver) -> msg => {
msg.expect("Unexpected layout channel panic in constellation").map(Request::Layout)
}
recv(self.swmanager_receiver) -> msg => {
msg.expect("Unexpected SW channel panic in constellation").map(Request::FromSWManager)
}
@ -1188,9 +1167,6 @@ where
Request::BackgroundHangMonitor(message) => {
self.handle_request_from_background_hang_monitor(message);
},
Request::Layout(message) => {
self.handle_request_from_layout(message);
},
Request::FromSWManager(message) => {
self.handle_request_from_swmanager(message);
},
@ -1409,6 +1385,9 @@ where
FromCompositorMsg::SetScrollStates(pipeline_id, scroll_states) => {
self.handle_set_scroll_states(pipeline_id, scroll_states)
},
FromCompositorMsg::PaintMetric(pipeline_id, paint_metric_event) => {
self.handle_paint_metric(pipeline_id, paint_metric_event);
},
}
}
@ -1980,19 +1959,6 @@ where
}
}
#[cfg_attr(
feature = "tracing",
tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace")
)]
fn handle_request_from_layout(&mut self, message: FromLayoutMsg) {
trace_layout_msg!(message, "{message:?}");
match message {
FromLayoutMsg::PendingPaintMetric(webview_id, pipeline_id, epoch) => {
self.handle_pending_paint_metric(webview_id, pipeline_id, epoch);
},
}
}
#[cfg_attr(
feature = "tracing",
tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace")
@ -3401,24 +3367,6 @@ where
});
}
#[cfg_attr(
feature = "tracing",
tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace")
)]
fn handle_pending_paint_metric(
&self,
webview_id: WebViewId,
pipeline_id: PipelineId,
epoch: Epoch,
) {
self.compositor_proxy
.send(CompositorMsg::PendingPaintMetric(
webview_id,
pipeline_id,
epoch,
))
}
#[cfg_attr(
feature = "tracing",
tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace")
@ -5585,4 +5533,35 @@ where
warn!("Could not send scroll offsets to pipeline: {pipeline_id:?}: {error:?}");
}
}
#[cfg_attr(
feature = "tracing",
tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace")
)]
fn handle_paint_metric(&mut self, pipeline_id: PipelineId, event: PaintMetricEvent) {
let Some(pipeline) = self.pipelines.get(&pipeline_id) else {
warn!("Discarding paint metric event for unknown pipeline");
return;
};
let (metric_type, metric_value, first_reflow) = match event {
PaintMetricEvent::FirstPaint(metric_value, first_reflow) => (
ProgressiveWebMetricType::FirstPaint,
metric_value,
first_reflow,
),
PaintMetricEvent::FirstContentfulPaint(metric_value, first_reflow) => (
ProgressiveWebMetricType::FirstContentfulPaint,
metric_value,
first_reflow,
),
};
if let Err(error) = pipeline.event_loop.send(ScriptThreadMessage::PaintMetric(
pipeline_id,
metric_type,
metric_value,
first_reflow,
)) {
warn!("Could not sent paint metric event to pipeline: {pipeline_id:?}: {error:?}");
}
}
}