feat(servo): isolate profiles with hardware sidecars

This commit is contained in:
2026-07-09 20:27:22 -04:00
parent 78dc86b18e
commit c28ec2bee8
92 changed files with 10306 additions and 1485 deletions
@@ -0,0 +1,144 @@
#![cfg(feature = "hardware-render")]
use dpi::PhysicalSize;
use ely_servo_host::{HardwareOffscreenContext, IOSurfaceHandle, IOSurfaceIdentity};
use servo::RenderingContext;
#[cfg(target_os = "macos")]
const HARDWARE_HOST_CHILD_ENV: &str = "ELY_SERVO_HARDWARE_HOST_CHILD";
#[test]
fn identity_uses_handle_dimensions() {
let handle = IOSurfaceHandle { mach_port_name: 7, surface_id: 11, width: 640, height: 480 };
assert_eq!(
IOSurfaceIdentity::from_handle(handle),
IOSurfaceIdentity { surface_id: 11, width: 640, height: 480 }
);
}
#[cfg(not(target_os = "macos"))]
#[test]
fn hardware_context_constructs() -> Result<(), String> {
let context = HardwareOffscreenContext::new(PhysicalSize::new(64, 64))
.map_err(|error| format!("hardware context creation failed: {error:?}"))?;
assert_eq!(context.size(), PhysicalSize::new(64, 64));
Ok(())
}
#[cfg(target_os = "macos")]
#[test]
fn presented_surface_exposes_iosurface_identity_and_mach_port() -> Result<(), String> {
let size = PhysicalSize::new(256, 192);
let context = HardwareOffscreenContext::new(size)
.map_err(|error| format!("hardware context creation failed: {error:?}"))?;
assert_eq!(
context
.peek_iosurface_identity()
.map_err(|error| format!("identity probe failed: {error:?}"))?,
None
);
context.make_current().map_err(|error| format!("make current failed: {error:?}"))?;
context.prepare_for_rendering();
context.present();
let identity = context
.peek_iosurface_identity()
.map_err(|error| format!("identity probe failed: {error:?}"))?
.ok_or_else(|| "present did not expose an IOSurface".to_string())?;
let handle = context
.current_iosurface_mach_port()
.map_err(|error| format!("Mach port creation failed: {error:?}"))?;
assert_ne!(handle.mach_port_name, 0);
assert_eq!(identity, IOSurfaceIdentity::from_handle(handle));
assert_eq!(identity.width, size.width);
assert_eq!(identity.height, size.height);
assert_eq!(deallocate_mach_port(handle.mach_port_name), mach2::kern_return::KERN_SUCCESS);
Ok(())
}
#[cfg(target_os = "macos")]
#[test]
fn hardware_host_paints_and_presents_an_iosurface() -> Result<(), Box<dyn std::error::Error>> {
use std::env;
use std::process::{Command, Stdio};
if env::var_os(HARDWARE_HOST_CHILD_ENV).is_some() {
return exercise_hardware_host();
}
let output = Command::new(env::current_exe()?)
.arg("--exact")
.arg("hardware_host_paints_and_presents_an_iosurface")
.env(HARDWARE_HOST_CHILD_ENV, "1")
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.output()?;
if output.status.success() {
return Ok(());
}
Err(format!(
"hardware host child failed\nstatus: {}\nstdout: {}\nstderr: {}",
output.status,
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
)
.into())
}
#[cfg(target_os = "macos")]
fn exercise_hardware_host() -> Result<(), Box<dyn std::error::Error>> {
use std::thread;
use std::time::Duration;
use ely_domain::{ProfileId, TabId, UrlText};
use ely_servo_host::{
NavigationRequest, RenderingContextKind, ServoHost, ServoSurfaceSize, SoftwareServoHost,
};
let size = ServoSurfaceSize::new(320, 240);
let mut host = SoftwareServoHost::new_with_config_dir_and_kind(
size,
None,
RenderingContextKind::Hardware,
)?;
let tab_id = TabId::new();
let webview_id = host.create_webview(tab_id.clone(), ProfileId::new())?;
host.navigate(NavigationRequest {
webview_id: webview_id.clone(),
tab_id,
url: UrlText::parse("data:text/html,%3Cbody%20style%3D%27background%3A%230369a1%27%3E")?,
})?;
for _ in 0..5_000 {
host.tick();
if host.snapshot(&webview_id)?.has_pending_frame() {
host.paint_without_readback(&webview_id)?;
if host.peek_iosurface_identity(&webview_id)?.is_some() {
let handle = host
.current_iosurface_handle(&webview_id)?
.ok_or("hardware webview did not expose an IOSurface handle")?;
assert_eq!(handle.width, 320);
assert_eq!(handle.height, 240);
assert_eq!(
deallocate_mach_port(handle.mach_port_name),
mach2::kern_return::KERN_SUCCESS
);
return Ok(());
}
}
thread::sleep(Duration::from_millis(2));
}
Err("timed out waiting for a hardware IOSurface frame".into())
}
#[cfg(target_os = "macos")]
#[expect(unsafe_code)]
fn deallocate_mach_port(name: u32) -> i32 {
// `name` is a live send right minted by IOSurfaceCreateMachPort in this task.
unsafe { mach2::mach_port::mach_port_deallocate(mach2::traps::mach_task_self(), name) }
}