fix(servo): stop forcing paint+present on every ensure
Servo's reference embedder (\`examples/winit_minimal.rs\`) is reactive:
- \`user_event\` only calls \`servo.spin_event_loop()\` — never paints
- \`notify_new_frame_ready\` is what flips the embedder into the
redraw path (calls \`window.request_redraw()\`)
- \`RedrawRequested\` is the **single** site that runs
\`webview.paint() + rendering_context.present()\`
Painting is therefore at-most-once-per-real-frame: a paint happens
only when Servo has actually composited new content.
Our \`ServoLiveClient::ensure\` was forcing
\`paint_without_readback_with_completion\` after every setup pass.
\`ensure\` runs on every \`ensure_surface\` invocation — viewport
debounce settle, URL redirect (google.com → / → /?zx=…), permission
update, native-surface re-attach, page-zoom change. Each invocation
called Servo's painter while it had no new composited frame ready:
WebRender's \`clear_background\` wiped the surface to the configured
shell background colour, the empty scene rendered, \`present\` swapped
that blank surface to the NSView's CALayer — and the user saw a
white flash. A chain of redirects produced a chain of flashes.
Drop the unconditional \`paint\` from \`ensure\`. Keep the
\`host.tick()\` (Servo's spin) so the navigate / viewport / input
messages reach the constellation in this turn, but defer painting to
\`poll\`, which already gates \`paint_without_readback_with_completion\`
on \`snapshot.has_pending_frame()\` — exactly mirroring the
reference embedder's reactive model. \`ServoLiveFrame\` returned from
\`ensure\` only carries snapshot metadata, so callers that depend on
the frame value (worker queue, UI state) are unaffected.
This commit is contained in:
@@ -53,11 +53,22 @@ impl ServoLiveClient {
|
|||||||
self.apply_permissions(&request, &webview_id, &profile_id)?;
|
self.apply_permissions(&request, &webview_id, &profile_id)?;
|
||||||
self.apply_navigation(&request, &webview_id, tab_id, requested_url)?;
|
self.apply_navigation(&request, &webview_id, tab_id, requested_url)?;
|
||||||
self.apply_input(&request, &webview_id)?;
|
self.apply_input(&request, &webview_id)?;
|
||||||
|
// Match Servo's `examples/winit_minimal.rs`: spin the event loop on
|
||||||
|
// the embedder-side hot path, never paint. Painting is reactive in
|
||||||
|
// Servo — `notify_new_frame_ready` on the delegate flags the
|
||||||
|
// session, and the next `poll` (gated on `has_pending_frame`)
|
||||||
|
// performs the single paint + present for that frame. Forcing a
|
||||||
|
// paint here would clear the surface to the WebRender background
|
||||||
|
// and present it *before* Servo has composited the navigated
|
||||||
|
// page, which is what produced the per-redirect white-flash on
|
||||||
|
// sites that perform a chain of redirects (google.com → /
|
||||||
|
// → /?zx=…). The `ServoLiveFrame` we return only carries the
|
||||||
|
// snapshot metadata; the on-screen surface is owned by Servo via
|
||||||
|
// the native NSView and updated through `poll`.
|
||||||
self.host.tick();
|
self.host.tick();
|
||||||
if !self.session_uses_native_surface(&request.tab_id) {
|
if !self.session_uses_native_surface(&request.tab_id) {
|
||||||
return Err(ServoLiveError::NativeSurfaceUnavailable);
|
return Err(ServoLiveError::NativeSurfaceUnavailable);
|
||||||
}
|
}
|
||||||
self.host.paint_without_readback_with_completion(&webview_id, false)?;
|
|
||||||
|
|
||||||
let frame = self.frame_from_session(&request.tab_id, &webview_id)?;
|
let frame = self.frame_from_session(&request.tab_id, &webview_id)?;
|
||||||
Ok(Some(frame))
|
Ok(Some(frame))
|
||||||
|
|||||||
Reference in New Issue
Block a user