T10.2: extract IOSurface mach port from the hardware surface on macOS
This commit is contained in:
Generated
+2
@@ -2302,6 +2302,7 @@ dependencies = [
|
|||||||
"glow",
|
"glow",
|
||||||
"image",
|
"image",
|
||||||
"log",
|
"log",
|
||||||
|
"objc2-io-surface",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"servo",
|
"servo",
|
||||||
@@ -6131,6 +6132,7 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"objc2",
|
"objc2",
|
||||||
"objc2-core-foundation",
|
"objc2-core-foundation",
|
||||||
|
"objc2-foundation",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ hardware-render = [
|
|||||||
"dep:image",
|
"dep:image",
|
||||||
"dep:log",
|
"dep:log",
|
||||||
"dep:surfman",
|
"dep:surfman",
|
||||||
|
"dep:objc2-io-surface",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
@@ -38,5 +39,8 @@ surfman = { version = "0.11", optional = true }
|
|||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
url = { workspace = true, optional = true }
|
url = { workspace = true, optional = true }
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
|
objc2-io-surface = { version = "0.3.2", optional = true }
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|||||||
@@ -147,6 +147,56 @@ impl RenderingContext for HardwareOffscreenContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Cross-process handle to a hardware surface: the receiving process
|
||||||
|
/// can rebuild an `IOSurfaceRef` from `mach_port_name` and import it as
|
||||||
|
/// a Metal texture without ever copying the pixels.
|
||||||
|
///
|
||||||
|
/// `width`/`height` are reported in surface pixels (post-DPR), matching
|
||||||
|
/// what surfman handed out at construction time. The receiver should
|
||||||
|
/// scale layout coordinates by its own backing scale factor.
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub struct IOSurfaceHandle {
|
||||||
|
pub mach_port_name: u32,
|
||||||
|
pub width: u32,
|
||||||
|
pub height: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
impl HardwareOffscreenContext {
|
||||||
|
/// Snapshot the IOSurface currently bound to the context and
|
||||||
|
/// return its mach port name plus dimensions. Increments the
|
||||||
|
/// IOSurface's mach-port use count; the receiving process holds it
|
||||||
|
/// via `IOSurfaceLookupFromMachPort` and is responsible for
|
||||||
|
/// `mach_port_deallocate` once the import is finished.
|
||||||
|
///
|
||||||
|
/// Implementation note: surfman's CGL backend keeps the bound
|
||||||
|
/// surface inside the GL context. To inspect it we temporarily
|
||||||
|
/// `unbind_surface_from_context`, call `device.native_surface()`
|
||||||
|
/// (which retains the `IOSurfaceRef`), then `bind_surface_to_context`
|
||||||
|
/// again. The unbind path calls `glFlush` so the IOSurface contents
|
||||||
|
/// are consistent for any reader importing it after this returns.
|
||||||
|
pub fn current_iosurface_mach_port(&self) -> Result<IOSurfaceHandle, SurfmanError> {
|
||||||
|
let device = &mut self.inner.device.borrow_mut();
|
||||||
|
let context = &mut self.inner.context.borrow_mut();
|
||||||
|
// `new` always binds a surface and `current_iosurface_mach_port`
|
||||||
|
// is the only method that unbinds; the `None` branch only fires
|
||||||
|
// if the invariant has been broken from outside.
|
||||||
|
let surface =
|
||||||
|
device.unbind_surface_from_context(context)?.ok_or(SurfmanError::Failed)?;
|
||||||
|
let native = device.native_surface(&surface);
|
||||||
|
let mach_port = native.0.create_mach_port();
|
||||||
|
let info = device.surface_info(&surface);
|
||||||
|
let handle = IOSurfaceHandle {
|
||||||
|
mach_port_name: mach_port,
|
||||||
|
width: u32::try_from(info.size.width).unwrap_or(0),
|
||||||
|
height: u32::try_from(info.size.height).unwrap_or(0),
|
||||||
|
};
|
||||||
|
device.bind_surface_to_context(context, surface).map_err(|(error, _)| error)?;
|
||||||
|
Ok(handle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Trimmed mirror of `paint_api::rendering_context::SurfmanRenderingContext`.
|
/// Trimmed mirror of `paint_api::rendering_context::SurfmanRenderingContext`.
|
||||||
///
|
///
|
||||||
/// Only the methods the public type above actually uses are kept; the
|
/// Only the methods the public type above actually uses are kept; the
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ mod runtime_webview;
|
|||||||
pub use error::ServoHostError;
|
pub use error::ServoHostError;
|
||||||
#[cfg(feature = "hardware-render")]
|
#[cfg(feature = "hardware-render")]
|
||||||
pub use hardware_rendering_context::HardwareOffscreenContext;
|
pub use hardware_rendering_context::HardwareOffscreenContext;
|
||||||
|
#[cfg(all(feature = "hardware-render", target_os = "macos"))]
|
||||||
|
pub use hardware_rendering_context::IOSurfaceHandle;
|
||||||
pub use host::{
|
pub use host::{
|
||||||
KeyboardTextRequest, MouseClickRequest, MouseDragRequest, MouseHoverRequest,
|
KeyboardTextRequest, MouseClickRequest, MouseDragRequest, MouseHoverRequest,
|
||||||
NavigationRequest, PageZoomRequest, PermissionDecision, PermissionRequest, RenderedFrame,
|
NavigationRequest, PageZoomRequest, PermissionDecision, PermissionRequest, RenderedFrame,
|
||||||
|
|||||||
@@ -35,3 +35,40 @@ fn constructs_or_explains_why_not() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
#[test]
|
||||||
|
fn extracts_iosurface_mach_port_from_current_surface() {
|
||||||
|
let width = 256;
|
||||||
|
let height = 192;
|
||||||
|
let context = match HardwareOffscreenContext::new(PhysicalSize::new(width, height)) {
|
||||||
|
Ok(context) => context,
|
||||||
|
Err(error) => {
|
||||||
|
eprintln!(
|
||||||
|
"hardware GL adapter not available on this host \
|
||||||
|
(acceptable in headless / no-GPU environments): {error:?}"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let first = context
|
||||||
|
.current_iosurface_mach_port()
|
||||||
|
.expect("first IOSurface mach port extraction must succeed");
|
||||||
|
assert!(
|
||||||
|
first.mach_port_name != 0,
|
||||||
|
"IOSurfaceCreateMachPort must return a non-null mach_port_t (got 0)"
|
||||||
|
);
|
||||||
|
assert_eq!(first.width, width, "reported width must match surface width");
|
||||||
|
assert_eq!(first.height, height, "reported height must match surface height");
|
||||||
|
|
||||||
|
// The unbind/rebind cycle must leave the context usable: a second
|
||||||
|
// call should still produce a valid mach port without panicking on
|
||||||
|
// a stale `Framebuffer::None`.
|
||||||
|
let second = context
|
||||||
|
.current_iosurface_mach_port()
|
||||||
|
.expect("repeated mach port extraction must succeed after rebind");
|
||||||
|
assert!(second.mach_port_name != 0);
|
||||||
|
assert_eq!(second.width, width);
|
||||||
|
assert_eq!(second.height, height);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user