diff --git a/Cargo.lock b/Cargo.lock index cb4f6fc..542d07f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1145,14 +1145,15 @@ name = "imaging_skia" version = "0.0.1" dependencies = [ "ash", - "foreign-types-shared", "imaging", "imaging_wgpu", "kurbo", "oaty", + "objc2", + "objc2-metal", "peniko", "skia-safe", - "wgpu 28.0.0", + "wgpu 29.0.3", "windows 0.62.2", ] diff --git a/imaging_skia/Cargo.toml b/imaging_skia/Cargo.toml index 8faad66..fe88963 100644 --- a/imaging_skia/Cargo.toml +++ b/imaging_skia/Cargo.toml @@ -14,10 +14,11 @@ categories = ["graphics", "rendering"] default = [] gpu = [ "dep:imaging_wgpu", - "imaging_wgpu/wgpu-28", - "dep:foreign-types-shared", + "imaging_wgpu/wgpu-29", "dep:wgpu", "dep:ash", + "dep:objc2", + "dep:objc2-metal", "dep:windows", ] @@ -27,10 +28,15 @@ imaging_wgpu = { default-features = false, optional = true, workspace = true } kurbo = { default-features = true, workspace = true } peniko = { default-features = true, workspace = true } skia-safe = { version = "0.97.0" } -wgpu = { optional = true, version = "28.0.0" } +wgpu = { optional = true, version = "29.0.3" } [target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies] -foreign-types-shared = { version = "0.3", optional = true } +objc2 = { version = "0.6", optional = true } +objc2-metal = { version = "0.3", default-features = false, features = [ + "MTLCommandQueue", + "MTLDevice", + "MTLTexture", +], optional = true } oaty = "0.1" skia-safe = { version = "0.97.0", features = ["metal"] } diff --git a/imaging_skia/src/ganesh.rs b/imaging_skia/src/ganesh.rs index 30888f9..43fd932 100644 --- a/imaging_skia/src/ganesh.rs +++ b/imaging_skia/src/ganesh.rs @@ -138,8 +138,20 @@ impl GaneshBackend { } pub(crate) fn flush_surface(&mut self, surface: &mut sk::Surface) { - self.direct_context() - .flush_and_submit_surface(surface, sk::gpu::SyncCpu::No); + match self { + #[cfg(any(target_os = "macos", target_os = "ios"))] + Self::Metal(backend) => backend + .direct_context() + .flush_and_submit_surface(surface, sk::gpu::SyncCpu::Yes), + #[cfg(windows)] + Self::Dx12(backend) => backend + .direct_context() + .flush_and_submit_surface(surface, sk::gpu::SyncCpu::No), + #[cfg(not(any(target_os = "macos", target_os = "ios")))] + Self::Vulkan(backend) => backend + .direct_context() + .flush_and_submit_surface(surface, sk::gpu::SyncCpu::No), + } } pub(crate) fn flush_surface_for_readback(&mut self, surface: &mut sk::Surface) { diff --git a/imaging_skia/src/lib.rs b/imaging_skia/src/lib.rs index 1c2f2a4..f118909 100644 --- a/imaging_skia/src/lib.rs +++ b/imaging_skia/src/lib.rs @@ -165,8 +165,6 @@ mod sinks; #[cfg(all(feature = "gpu", not(any(target_os = "macos", target_os = "ios"))))] mod vulkan; -#[cfg(all(feature = "gpu", any(target_os = "macos", target_os = "ios")))] -use foreign_types_shared as _; use imaging::{ Filter, GeometryRef, GlyphRunRef, RgbaImage, record::{Scene, ValidateError, replay}, @@ -194,7 +192,7 @@ use crate::gpu_readback::{ ReadbackError, ScratchTexture, read_texture_into, read_texture_into_target, }; #[cfg(feature = "gpu")] -use imaging_wgpu::v28::TextureRenderer; +use imaging_wgpu::v29::TextureRenderer; #[cfg(feature = "gpu")] use imaging_wgpu::{TextureRendererError, TextureTargetError}; use sinks::MaskCache; @@ -851,7 +849,7 @@ impl SkiaGpuRendererState { "Skia GPU renderer requires RENDER_ATTACHMENT texture usage", )); } - initialize_texture_for_wgpu(&self.device, &self.queue, texture); + initialize_texture_for_wgpu(&self.device, &self.queue, texture)?; Ok(()) } @@ -1162,7 +1160,8 @@ impl ImageRenderer for SkiaRenderer { ) .map_err(map_image_renderer_error)?; let texture = self.scratch_texture_for_format(target.width, target.height, texture_format); - initialize_texture_for_wgpu(&self.state.device, &self.state.queue, &texture); + initialize_texture_for_wgpu(&self.state.device, &self.state.queue, &texture) + .map_err(map_image_renderer_error)?; let mut surface = self .state .backend @@ -1281,7 +1280,7 @@ fn initialize_texture_for_wgpu( device: &wgpu::Device, queue: &wgpu::Queue, texture: &wgpu::Texture, -) { +) -> Result<(), Error> { let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("imaging_skia texture init"), @@ -1304,6 +1303,10 @@ fn initialize_texture_for_wgpu( }); drop(_pass); queue.submit([encoder.finish()]); + device + .poll(wgpu::PollType::wait_indefinitely()) + .map_err(|_| Error::Internal("wgpu texture initialization poll failed"))?; + Ok(()) } #[allow( diff --git a/imaging_skia/src/metal.rs b/imaging_skia/src/metal.rs index 9104c70..f52a9e7 100644 --- a/imaging_skia/src/metal.rs +++ b/imaging_skia/src/metal.rs @@ -3,7 +3,10 @@ #![allow(unsafe_code, reason = "Metal interop requires raw handle bridging")] -use foreign_types_shared::ForeignType; +use core::ptr::NonNull; +use objc2::rc::Retained; +use objc2::runtime::ProtocolObject; +use objc2_metal::{MTLCommandQueue, MTLDevice as _}; use skia_safe as sk; use crate::{Error, color_space_for_wgpu_texture_format, color_type_for_wgpu_texture_format}; @@ -11,31 +14,37 @@ use crate::{Error, color_space_for_wgpu_texture_format, color_type_for_wgpu_text #[derive(Debug)] pub(crate) struct MetalBackend { context: sk::gpu::DirectContext, + _command_queue: Retained>, } impl MetalBackend { - pub(crate) fn from_wgpu(device: &wgpu::Device, queue: &wgpu::Queue) -> Result { + pub(crate) fn from_wgpu(device: &wgpu::Device, _queue: &wgpu::Queue) -> Result { let device = unsafe { device .as_hal::() .ok_or(Error::CreateGpuContext("missing Metal device"))? }; - let queue = unsafe { - queue - .as_hal::() - .ok_or(Error::CreateGpuContext("missing Metal queue"))? - }; + let command_queue = + device + .raw_device() + .newCommandQueue() + .ok_or(Error::CreateGpuContext( + "unable to create Metal command queue", + ))?; let backend = unsafe { sk::gpu::mtl::BackendContext::new( - device.raw_device().as_ptr() as sk::gpu::mtl::Handle, - queue.as_raw().lock().as_ptr() as sk::gpu::mtl::Handle, + Retained::as_ptr(device.raw_device()) as sk::gpu::mtl::Handle, + Retained::as_ptr(&command_queue) as sk::gpu::mtl::Handle, ) }; let context = sk::gpu::direct_contexts::make_metal(&backend, None).ok_or( Error::CreateGpuContext("unable to create Skia Metal context"), )?; - Ok(Self { context }) + Ok(Self { + context, + _command_queue: command_queue, + }) } pub(crate) fn direct_context(&mut self) -> &mut sk::gpu::DirectContext { @@ -53,8 +62,8 @@ impl MetalBackend { .as_hal::() .ok_or(Error::CreateGpuSurface)? }; - let texture_info = - unsafe { sk::gpu::mtl::TextureInfo::new(hal_texture.raw_handle().as_ptr() as _) }; + let texture_handle = NonNull::from(hal_texture.raw_handle()).as_ptr(); + let texture_info = unsafe { sk::gpu::mtl::TextureInfo::new(texture_handle as _) }; let backend_texture = unsafe { sk::gpu::backend_textures::make_mtl( (width, height),