Move Servo IPC off UI thread
Root cause of the post-tab lag: the GPUI 16 ms timer was calling `WebSurfaceRuntime::ensure_tab` and `tick` on the UI thread, and each call did a synchronous `serde_json` write plus `read_line` against the Servo sidecar over stdin/stdout. With even one visible tab, every frame stalled on cross-process IPC. Introduce `web_surface_worker.rs` — a per-profile worker thread that owns the `ServoLiveClient`, drains a coalescing request queue (latest Ensure/Poll per tab wins, no unbounded growth), and ships results back through a `std::sync::mpsc` channel. `WebSurfaceRuntime` now submits work non-blockingly and drains responses in `tick`; the UI thread never blocks on the sidecar. Adjacent in-flight cleanup riding along: hardware IOSurface rendering-context completion (sidecar `live_protocol`, `hardware_rendering_context`, GPUI BGRA surface shader), CSS viewport size + device pixel ratio plumbing into `ServoLiveFrame`, and the Send opt-ins for `CVPixelBuffer`-bearing types so frames can cross the thread boundary.
This commit is contained in:
+17
-4
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
App, Bounds, Element, ElementId, GlobalElementId, InspectorElementId, IntoElement, LayoutId,
|
||||
ObjectFit, Pixels, Style, StyleRefinement, Styled, Window,
|
||||
App, Bounds, Corners, Element, ElementId, GlobalElementId, InspectorElementId, IntoElement,
|
||||
LayoutId, ObjectFit, Pixels, Style, StyleRefinement, Styled, Window,
|
||||
};
|
||||
#[cfg(target_os = "macos")]
|
||||
use core_video::pixel_buffer::CVPixelBuffer;
|
||||
@@ -25,6 +25,7 @@ impl From<CVPixelBuffer> for SurfaceSource {
|
||||
pub struct Surface {
|
||||
source: SurfaceSource,
|
||||
object_fit: ObjectFit,
|
||||
corner_radii: Option<Corners<Pixels>>,
|
||||
style: StyleRefinement,
|
||||
}
|
||||
|
||||
@@ -33,6 +34,7 @@ pub fn surface(source: impl Into<SurfaceSource>) -> Surface {
|
||||
Surface {
|
||||
source: source.into(),
|
||||
object_fit: ObjectFit::Contain,
|
||||
corner_radii: None,
|
||||
style: Default::default(),
|
||||
}
|
||||
}
|
||||
@@ -43,6 +45,12 @@ impl Surface {
|
||||
self.object_fit = object_fit;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set rounded clipping for the rendered surface.
|
||||
pub fn corner_radii(mut self, corner_radii: impl Into<Corners<Pixels>>) -> Self {
|
||||
self.corner_radii = Some(corner_radii.into());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Element for Surface {
|
||||
@@ -96,8 +104,13 @@ impl Element for Surface {
|
||||
SurfaceSource::Surface(surface) => {
|
||||
let size = crate::size(surface.get_width().into(), surface.get_height().into());
|
||||
let new_bounds = self.object_fit.get_bounds(bounds, size);
|
||||
// TODO: Add support for corner_radii
|
||||
window.paint_surface(new_bounds, surface.clone());
|
||||
let mut style = Style::default();
|
||||
style.refine(&self.style);
|
||||
let corner_radii = self
|
||||
.corner_radii
|
||||
.unwrap_or_else(|| style.corner_radii.to_pixels(window.rem_size()))
|
||||
.clamp_radii_for_quad_size(new_bounds.size);
|
||||
window.paint_surface(new_bounds, corner_radii, surface.clone());
|
||||
}
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => {}
|
||||
|
||||
+10
-3
@@ -1,8 +1,8 @@
|
||||
use super::metal_atlas::MetalAtlas;
|
||||
use crate::{
|
||||
AtlasTextureId, Background, Bounds, ContentMask, DevicePixels, MonochromeSprite, PaintSurface,
|
||||
Path, Point, PolychromeSprite, PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow, Size,
|
||||
Surface, Underline, point, size,
|
||||
AtlasTextureId, Background, Bounds, ContentMask, Corners, DevicePixels, MonochromeSprite,
|
||||
PaintSurface, Path, Point, PolychromeSprite, PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow,
|
||||
Size, Surface, Underline, point, size,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use block::ConcreteBlock;
|
||||
@@ -1112,6 +1112,11 @@ impl MetalRenderer {
|
||||
Some(&instance_buffer.metal_buffer),
|
||||
*instance_offset as u64,
|
||||
);
|
||||
command_encoder.set_fragment_buffer(
|
||||
SurfaceInputIndex::Surfaces as u64,
|
||||
Some(&instance_buffer.metal_buffer),
|
||||
*instance_offset as u64,
|
||||
);
|
||||
command_encoder.set_vertex_bytes(
|
||||
SurfaceInputIndex::TextureSize as u64,
|
||||
mem::size_of_val(&texture_size) as u64,
|
||||
@@ -1180,6 +1185,7 @@ impl MetalRenderer {
|
||||
SurfaceBounds {
|
||||
bounds: surface.bounds,
|
||||
content_mask: surface.content_mask.clone(),
|
||||
corner_radii: surface.corner_radii,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -1387,4 +1393,5 @@ pub struct PathSprite {
|
||||
pub struct SurfaceBounds {
|
||||
pub bounds: Bounds<ScaledPixels>,
|
||||
pub content_mask: ContentMask<ScaledPixels>,
|
||||
pub corner_radii: Corners<ScaledPixels>,
|
||||
}
|
||||
|
||||
+36
-2
@@ -835,12 +835,14 @@ fragment float4 path_sprite_fragment(
|
||||
}
|
||||
|
||||
struct SurfaceVertexOutput {
|
||||
uint surface_id [[flat]];
|
||||
float4 position [[position]];
|
||||
float2 texture_position;
|
||||
float clip_distance [[clip_distance]][4];
|
||||
};
|
||||
|
||||
struct SurfaceFragmentInput {
|
||||
uint surface_id [[flat]];
|
||||
float4 position [[position]];
|
||||
float2 texture_position;
|
||||
};
|
||||
@@ -863,12 +865,28 @@ vertex SurfaceVertexOutput surface_vertex(
|
||||
// to the current vertex of the unit triangle.
|
||||
float2 texture_position = unit_vertex;
|
||||
return SurfaceVertexOutput{
|
||||
surface_id,
|
||||
device_position,
|
||||
texture_position,
|
||||
{clip_distance.x, clip_distance.y, clip_distance.z, clip_distance.w}};
|
||||
}
|
||||
|
||||
float surface_corner_alpha(float2 position, SurfaceBounds surface) {
|
||||
bool unrounded = surface.corner_radii.top_left == 0.0 &&
|
||||
surface.corner_radii.bottom_left == 0.0 &&
|
||||
surface.corner_radii.top_right == 0.0 &&
|
||||
surface.corner_radii.bottom_right == 0.0;
|
||||
if (unrounded) {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
float distance = quad_sdf(position, surface.bounds, surface.corner_radii);
|
||||
return 1.0 - smoothstep(-0.5, 0.5, distance);
|
||||
}
|
||||
|
||||
fragment float4 surface_fragment(SurfaceFragmentInput input [[stage_in]],
|
||||
constant SurfaceBounds *surfaces
|
||||
[[buffer(SurfaceInputIndex_Surfaces)]],
|
||||
texture2d<float> y_texture
|
||||
[[texture(SurfaceInputIndex_YTexture)]],
|
||||
texture2d<float> cb_cr_texture
|
||||
@@ -883,10 +901,19 @@ fragment float4 surface_fragment(SurfaceFragmentInput input [[stage_in]],
|
||||
y_texture.sample(texture_sampler, input.texture_position).r,
|
||||
cb_cr_texture.sample(texture_sampler, input.texture_position).rg, 1.0);
|
||||
|
||||
return ycbcrToRGBTransform * ycbcr;
|
||||
SurfaceBounds surface = surfaces[input.surface_id];
|
||||
float4 color = ycbcrToRGBTransform * ycbcr;
|
||||
float alpha = surface_corner_alpha(input.position.xy, surface);
|
||||
if (alpha <= 0.0) {
|
||||
discard_fragment();
|
||||
}
|
||||
color.a *= alpha;
|
||||
return color;
|
||||
}
|
||||
|
||||
fragment float4 surface_bgra_fragment(SurfaceFragmentInput input [[stage_in]],
|
||||
constant SurfaceBounds *surfaces
|
||||
[[buffer(SurfaceInputIndex_Surfaces)]],
|
||||
texture2d<float> bgra_texture
|
||||
[[texture(SurfaceInputIndex_YTexture)]]) {
|
||||
constexpr sampler texture_sampler(mag_filter::linear, min_filter::linear);
|
||||
@@ -894,7 +921,14 @@ fragment float4 surface_bgra_fragment(SurfaceFragmentInput input [[stage_in]],
|
||||
// from GPUI's surface quad, matching the software readback path.
|
||||
float2 texture_position =
|
||||
float2(input.texture_position.x, 1.0 - input.texture_position.y);
|
||||
return bgra_texture.sample(texture_sampler, texture_position);
|
||||
SurfaceBounds surface = surfaces[input.surface_id];
|
||||
float4 color = bgra_texture.sample(texture_sampler, texture_position);
|
||||
float alpha = surface_corner_alpha(input.position.xy, surface);
|
||||
if (alpha <= 0.0) {
|
||||
discard_fragment();
|
||||
}
|
||||
color.a *= alpha;
|
||||
return color;
|
||||
}
|
||||
|
||||
float4 hsla_to_rgba(Hsla hsla) {
|
||||
|
||||
Vendored
+1
@@ -658,6 +658,7 @@ pub(crate) struct PaintSurface {
|
||||
pub order: DrawOrder,
|
||||
pub bounds: Bounds<ScaledPixels>,
|
||||
pub content_mask: ContentMask<ScaledPixels>,
|
||||
pub corner_radii: Corners<ScaledPixels>,
|
||||
#[cfg(target_os = "macos")]
|
||||
pub image_buffer: core_video::pixel_buffer::CVPixelBuffer,
|
||||
}
|
||||
|
||||
Vendored
+8
-1
@@ -3178,18 +3178,25 @@ impl Window {
|
||||
///
|
||||
/// This method should only be called as part of the paint phase of element drawing.
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn paint_surface(&mut self, bounds: Bounds<Pixels>, image_buffer: CVPixelBuffer) {
|
||||
pub fn paint_surface(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
corner_radii: Corners<Pixels>,
|
||||
image_buffer: CVPixelBuffer,
|
||||
) {
|
||||
use crate::PaintSurface;
|
||||
|
||||
self.invalidator.debug_assert_paint();
|
||||
|
||||
let scale_factor = self.scale_factor();
|
||||
let bounds = bounds.scale(scale_factor);
|
||||
let corner_radii = corner_radii.scale(scale_factor);
|
||||
let content_mask = self.content_mask().scale(scale_factor);
|
||||
self.next_frame.scene.insert_primitive(PaintSurface {
|
||||
order: 0,
|
||||
bounds,
|
||||
content_mask,
|
||||
corner_radii,
|
||||
image_buffer,
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user