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:
2026-05-15 16:41:40 -04:00
parent f4c650c4d8
commit 90c029eddb
29 changed files with 2113 additions and 496 deletions
+36 -2
View File
@@ -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) {