perf(sidecar): skip redundant paint barriers
This commit is contained in:
@@ -201,12 +201,13 @@ fn poll_frame(
|
|||||||
) -> Result<LiveOutcome, LiveSidecarError> {
|
) -> Result<LiveOutcome, LiveSidecarError> {
|
||||||
host.tick();
|
host.tick();
|
||||||
let snapshot = host.snapshot(&session.webview_id)?;
|
let snapshot = host.snapshot(&session.webview_id)?;
|
||||||
if !should_paint_live_frame(snapshot.has_pending_frame(), session.awaiting_visible_frame) {
|
let has_pending_frame = snapshot.has_pending_frame();
|
||||||
|
if !should_paint_live_frame(has_pending_frame, session.awaiting_visible_frame) {
|
||||||
return Ok(LiveOutcome::empty());
|
return Ok(LiveOutcome::empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
let (outcome, has_visible_content) =
|
let (outcome, has_visible_content) =
|
||||||
paint_pending_frame(host, session, rendering_context_kind)?;
|
paint_pending_frame(host, session, rendering_context_kind, has_pending_frame)?;
|
||||||
if has_visible_content {
|
if has_visible_content {
|
||||||
session.awaiting_visible_frame = false;
|
session.awaiting_visible_frame = false;
|
||||||
session.ever_visible_frame = true;
|
session.ever_visible_frame = true;
|
||||||
@@ -227,22 +228,26 @@ fn paint_pending_frame(
|
|||||||
host: &mut SoftwareServoHost,
|
host: &mut SoftwareServoHost,
|
||||||
session: &mut LiveSession,
|
session: &mut LiveSession,
|
||||||
rendering_context_kind: RenderingContextKind,
|
rendering_context_kind: RenderingContextKind,
|
||||||
|
has_pending_frame: bool,
|
||||||
) -> Result<(LiveOutcome, bool), LiveSidecarError> {
|
) -> Result<(LiveOutcome, bool), LiveSidecarError> {
|
||||||
match rendering_context_kind {
|
match rendering_context_kind {
|
||||||
RenderingContextKind::Software => paint_readback_frame(host, session),
|
RenderingContextKind::Software => paint_readback_frame(host, session, !has_pending_frame),
|
||||||
#[cfg(all(feature = "hardware-render", target_os = "macos"))]
|
#[cfg(all(feature = "hardware-render", target_os = "macos"))]
|
||||||
RenderingContextKind::Hardware => paint_hardware_surface_frame(host, session),
|
RenderingContextKind::Hardware => {
|
||||||
|
paint_hardware_surface_frame(host, session, has_pending_frame)
|
||||||
|
}
|
||||||
#[cfg(not(all(feature = "hardware-render", target_os = "macos")))]
|
#[cfg(not(all(feature = "hardware-render", target_os = "macos")))]
|
||||||
RenderingContextKind::Hardware => paint_readback_frame(host, session),
|
RenderingContextKind::Hardware => paint_readback_frame(host, session, !has_pending_frame),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint_readback_frame(
|
fn paint_readback_frame(
|
||||||
host: &mut SoftwareServoHost,
|
host: &mut SoftwareServoHost,
|
||||||
session: &LiveSession,
|
session: &LiveSession,
|
||||||
|
wait_for_completion: bool,
|
||||||
) -> Result<(LiveOutcome, bool), LiveSidecarError> {
|
) -> Result<(LiveOutcome, bool), LiveSidecarError> {
|
||||||
let paint_started_at = Instant::now();
|
let paint_started_at = Instant::now();
|
||||||
host.paint(&session.webview_id)?;
|
host.paint_with_readback(&session.webview_id, wait_for_completion)?;
|
||||||
let snapshot = host.snapshot(&session.webview_id)?;
|
let snapshot = host.snapshot(&session.webview_id)?;
|
||||||
let frame = host.last_rendered_frame()?;
|
let frame = host.last_rendered_frame()?;
|
||||||
let paint_ns = elapsed_ns(paint_started_at);
|
let paint_ns = elapsed_ns(paint_started_at);
|
||||||
@@ -259,24 +264,26 @@ fn paint_readback_frame(
|
|||||||
fn paint_hardware_surface_frame(
|
fn paint_hardware_surface_frame(
|
||||||
host: &mut SoftwareServoHost,
|
host: &mut SoftwareServoHost,
|
||||||
session: &LiveSession,
|
session: &LiveSession,
|
||||||
|
has_pending_frame: bool,
|
||||||
) -> Result<(LiveOutcome, bool), LiveSidecarError> {
|
) -> Result<(LiveOutcome, bool), LiveSidecarError> {
|
||||||
if !session.ever_visible_frame {
|
if !session.ever_visible_frame {
|
||||||
return paint_initial_hardware_surface_frame(host, session);
|
return paint_initial_hardware_surface_frame(host, session, !has_pending_frame);
|
||||||
}
|
}
|
||||||
// Cross-process IOSurface lookup can block the app-side worker for
|
// Cross-process IOSurface lookup can block the app-side worker for
|
||||||
// seconds on macOS. Live app frames use readback so scroll/click
|
// seconds on macOS. Live app frames use readback so scroll/click
|
||||||
// input stays bounded by the paint barrier instead of the surface
|
// input stays bounded by the paint barrier instead of the surface
|
||||||
// import path.
|
// import path.
|
||||||
paint_readback_frame(host, session)
|
paint_readback_frame(host, session, !has_pending_frame)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "hardware-render", target_os = "macos"))]
|
#[cfg(all(feature = "hardware-render", target_os = "macos"))]
|
||||||
fn paint_initial_hardware_surface_frame(
|
fn paint_initial_hardware_surface_frame(
|
||||||
host: &mut SoftwareServoHost,
|
host: &mut SoftwareServoHost,
|
||||||
session: &LiveSession,
|
session: &LiveSession,
|
||||||
|
wait_for_completion: bool,
|
||||||
) -> Result<(LiveOutcome, bool), LiveSidecarError> {
|
) -> Result<(LiveOutcome, bool), LiveSidecarError> {
|
||||||
let paint_started_at = Instant::now();
|
let paint_started_at = Instant::now();
|
||||||
host.paint(&session.webview_id)?;
|
host.paint_with_readback(&session.webview_id, wait_for_completion)?;
|
||||||
let snapshot = host.snapshot(&session.webview_id)?;
|
let snapshot = host.snapshot(&session.webview_id)?;
|
||||||
let frame = host.last_rendered_frame()?;
|
let frame = host.last_rendered_frame()?;
|
||||||
let paint_ns = elapsed_ns(paint_started_at);
|
let paint_ns = elapsed_ns(paint_started_at);
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ impl SoftwareServoHost {
|
|||||||
/// bytes. The live hardware path exports the just-presented
|
/// bytes. The live hardware path exports the just-presented
|
||||||
/// IOSurface from the rendering context.
|
/// IOSurface from the rendering context.
|
||||||
pub fn paint_without_readback(&mut self, webview_id: &WebViewId) -> Result<(), ServoHostError> {
|
pub fn paint_without_readback(&mut self, webview_id: &WebViewId) -> Result<(), ServoHostError> {
|
||||||
self.paint_webview(webview_id, false).map(|_| ())
|
self.paint_webview(webview_id, false, true).map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close_webview(&mut self, webview_id: &WebViewId) -> bool {
|
pub fn close_webview(&mut self, webview_id: &WebViewId) -> bool {
|
||||||
@@ -146,6 +146,7 @@ impl SoftwareServoHost {
|
|||||||
&mut self,
|
&mut self,
|
||||||
webview_id: &WebViewId,
|
webview_id: &WebViewId,
|
||||||
capture_frame: bool,
|
capture_frame: bool,
|
||||||
|
wait_for_completion: bool,
|
||||||
) -> Result<Option<RenderedFrame>, ServoHostError> {
|
) -> Result<Option<RenderedFrame>, ServoHostError> {
|
||||||
let rendering_context = self.webview(webview_id)?.rendering_context.clone();
|
let rendering_context = self.webview(webview_id)?.rendering_context.clone();
|
||||||
rendering_context.make_current().map_err(|_| ServoHostError::RenderingContextNotCurrent)?;
|
rendering_context.make_current().map_err(|_| ServoHostError::RenderingContextNotCurrent)?;
|
||||||
@@ -154,18 +155,20 @@ impl SoftwareServoHost {
|
|||||||
// thread — it does NOT block until the framebuffer is consistent.
|
// thread — it does NOT block until the framebuffer is consistent.
|
||||||
// Without a barrier, `read_rendered_frame` below races the paint
|
// Without a barrier, `read_rendered_frame` below races the paint
|
||||||
// thread and reliably reads the cleared-white state on data: URLs.
|
// thread and reliably reads the cleared-white state on data: URLs.
|
||||||
// Clear the pending-frame flag first so we can detect the *next*
|
// Clear the pending-frame flag first so barrier callers can
|
||||||
// `notify_new_frame_ready` (the one our `paint()` triggers), then
|
// detect the *next* `notify_new_frame_ready` (the one our
|
||||||
// pump the event loop until Servo reports the new frame is ready
|
// `paint()` triggers), then pump the event loop until Servo
|
||||||
// or `paint_barrier_budget()` elapses. On timeout we fall through
|
// reports the new frame is ready or `paint_barrier_budget()`
|
||||||
// and read anyway, preserving the pre-T15 fast path for callers
|
// elapses. When the caller already observed a pending frame,
|
||||||
// that explicitly disable the barrier with `ELY_PAINT_BARRIER_MS=0`.
|
// readback can use that ready frame and skip the extra wait.
|
||||||
{
|
{
|
||||||
let webview = self.webview(webview_id)?;
|
let webview = self.webview(webview_id)?;
|
||||||
webview.delegate.mark_frame_presented();
|
webview.delegate.mark_frame_presented();
|
||||||
webview.webview.paint();
|
webview.webview.paint();
|
||||||
}
|
}
|
||||||
self.wait_for_paint_completion(webview_id);
|
if wait_for_completion {
|
||||||
|
self.wait_for_paint_completion(webview_id);
|
||||||
|
}
|
||||||
let rendered_frame = if capture_frame {
|
let rendered_frame = if capture_frame {
|
||||||
Some(Self::read_rendered_frame(rendering_context.as_ref())?)
|
Some(Self::read_rendered_frame(rendering_context.as_ref())?)
|
||||||
} else {
|
} else {
|
||||||
@@ -175,6 +178,19 @@ impl SoftwareServoHost {
|
|||||||
self.webview(webview_id)?.delegate.mark_frame_presented();
|
self.webview(webview_id)?.delegate.mark_frame_presented();
|
||||||
Ok(rendered_frame)
|
Ok(rendered_frame)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn paint_with_readback(
|
||||||
|
&mut self,
|
||||||
|
webview_id: &WebViewId,
|
||||||
|
wait_for_completion: bool,
|
||||||
|
) -> Result<(), ServoHostError> {
|
||||||
|
let Some(rendered_frame) = self.paint_webview(webview_id, true, wait_for_completion)?
|
||||||
|
else {
|
||||||
|
return Err(ServoHostError::RenderedFrameUnavailable);
|
||||||
|
};
|
||||||
|
self.last_rendered_frame = Some(rendered_frame);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServoHost for SoftwareServoHost {
|
impl ServoHost for SoftwareServoHost {
|
||||||
@@ -383,11 +399,7 @@ impl ServoHost for SoftwareServoHost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn paint(&mut self, webview_id: &WebViewId) -> Result<(), ServoHostError> {
|
fn paint(&mut self, webview_id: &WebViewId) -> Result<(), ServoHostError> {
|
||||||
let Some(rendered_frame) = self.paint_webview(webview_id, true)? else {
|
self.paint_with_readback(webview_id, true)
|
||||||
return Err(ServoHostError::RenderedFrameUnavailable);
|
|
||||||
};
|
|
||||||
self.last_rendered_frame = Some(rendered_frame);
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn last_rendered_frame(&self) -> Result<RenderedFrame, ServoHostError> {
|
fn last_rendered_frame(&self) -> Result<RenderedFrame, ServoHostError> {
|
||||||
|
|||||||
Reference in New Issue
Block a user