T10.5: render IOSurface via gpui::surface(CVPixelBuffer)

This commit is contained in:
2026-05-10 23:37:00 -04:00
parent c4a6ea3c57
commit a447d52262
6 changed files with 177 additions and 177 deletions
+43 -39
View File
@@ -23,18 +23,19 @@ use thiserror::Error;
use super::servo_sidecar_command::{SidecarCommandError, default_sidecar_command};
#[cfg(target_os = "macos")]
use super::iosurface_metal::MetalSurfaceImporter;
use super::iosurface_metal::IOSurfaceCache;
#[cfg(target_os = "macos")]
use core_video::pixel_buffer::CVPixelBuffer;
pub(crate) struct ServoLiveClient {
child: Child,
stdin: ChildStdin,
stdout: BufReader<ChildStdout>,
/// Cache of imported Metal textures keyed by surface_id. Built
/// Cache of imported `CVPixelBuffer`s keyed by surface_id. Built
/// lazily on the first `surface_handle` the sidecar publishes —
/// software-path tabs never trigger construction, so machines
/// without a Metal device aren't penalised.
/// software-path tabs never trigger construction.
#[cfg(target_os = "macos")]
metal_importer: Option<MetalSurfaceImporter>,
iosurface_cache: IOSurfaceCache,
}
impl ServoLiveClient {
@@ -64,10 +65,11 @@ impl ServoLiveClient {
stdin,
stdout: BufReader::new(stdout),
#[cfg(target_os = "macos")]
metal_importer: None,
iosurface_cache: IOSurfaceCache::new(),
})
}
pub fn ensure(
&mut self,
request: ServoLiveEnsureRequest,
@@ -154,7 +156,14 @@ impl ServoLiveClient {
.read_exact(&mut rgba_bytes)
.map_err(ServoLiveError::FrameRead)?;
Ok(Some(ServoLiveFrame::from_parts(report, rgba_bytes)))
let mut frame = ServoLiveFrame::from_parts(report, rgba_bytes);
#[cfg(target_os = "macos")]
if let Some(surface_id) = response.current_surface_id {
frame.pixel_buffer = self.iosurface_cache.pixel_buffer_for(surface_id);
}
Ok(Some(frame))
}
}
@@ -167,49 +176,25 @@ impl Drop for ServoLiveClient {
#[cfg(target_os = "macos")]
impl ServoLiveClient {
/// Run a freshly-arrived `surface_handle` through the Metal
/// importer. Lazily constructs the importer on first call so
/// software-only sessions never touch the GPU. Failures are
/// logged but don't error the request — T10.5 will fall back to
/// the existing software RGBA path if `texture_for` returns
/// `None`, so the user still sees a frame.
/// Convert the sidecar's `surface_handle` into a `CVPixelBuffer`
/// in the local cache. Failures are logged but don't error the
/// request — the renderer falls back to the existing software
/// `Arc<RenderImage>` path when no pixel buffer is available, so
/// the user always sees a frame.
fn import_iosurface_handle(&mut self, handle: &LiveSurfaceHandle) {
if self.metal_importer.is_none() {
match MetalSurfaceImporter::new() {
Ok(importer) => {
self.metal_importer = Some(importer);
}
Err(error) => {
tracing::warn!(
target: "ely::servo::iosurface",
error = %error,
"no Metal device for IOSurface import; staying on software path",
);
return;
}
}
}
let Some(importer) = self.metal_importer.as_mut() else {
return;
};
match importer.import(
handle.mach_port_name,
handle.surface_id,
handle.width,
handle.height,
) {
match self.iosurface_cache.import(handle.mach_port_name, handle.surface_id) {
Ok(()) => tracing::info!(
target: "ely::servo::iosurface",
surface_id = handle.surface_id,
width = handle.width,
height = handle.height,
"imported IOSurface into Metal texture cache",
"imported IOSurface into CVPixelBuffer cache",
),
Err(error) => tracing::warn!(
target: "ely::servo::iosurface",
error = %error,
surface_id = handle.surface_id,
"IOSurface→MTLTexture import failed; subsequent samples will miss",
"IOSurface→CVPixelBuffer import failed; subsequent samples will miss",
),
}
}
@@ -262,6 +247,12 @@ pub(crate) struct ServoLiveFrame {
#[cfg(all(test, feature = "live-site-smoke"))]
sample_hash: u64,
rgba_bytes: Vec<u8>,
/// Hardware-path companion: when present, the renderer can hand
/// the underlying IOSurface straight to GPUI's Metal pipeline via
/// `gpui::surface(...)` and skip the RGBA upload entirely. Always
/// `None` on the software path and on non-macOS hosts.
#[cfg(target_os = "macos")]
pixel_buffer: Option<CVPixelBuffer>,
}
impl ServoLiveFrame {
@@ -279,9 +270,20 @@ impl ServoLiveFrame {
#[cfg(all(test, feature = "live-site-smoke"))]
sample_hash: report.sample_hash,
rgba_bytes,
#[cfg(target_os = "macos")]
pixel_buffer: None,
}
}
/// Returns the imported `CVPixelBuffer` matching the frame's
/// current hardware surface, if any. The renderer hands this to
/// `gpui::surface(...)` to skip the RGBA→texture upload path.
#[cfg(target_os = "macos")]
#[must_use]
pub fn pixel_buffer(&self) -> Option<&CVPixelBuffer> {
self.pixel_buffer.as_ref()
}
#[must_use]
pub fn loaded_url(&self) -> Option<&str> {
self.loaded_url.as_deref()
@@ -345,6 +347,8 @@ impl ServoLiveFrame {
#[cfg(all(test, feature = "live-site-smoke"))]
sample_hash: 0,
rgba_bytes,
#[cfg(target_os = "macos")]
pixel_buffer: None,
}
}
}