From 12b221a5d783209be3d67d5ee3e1c3812fa24d25 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Mon, 9 Sep 2024 23:00:01 +0200 Subject: [PATCH] Use `raw-window-metal` to do layer creation in skia --- internal/renderers/skia/Cargo.toml | 5 +- internal/renderers/skia/metal_surface.rs | 67 +++++++++++++---------- internal/renderers/skia/vulkan_surface.rs | 28 ++++------ 3 files changed, 52 insertions(+), 48 deletions(-) diff --git a/internal/renderers/skia/Cargo.toml b/internal/renderers/skia/Cargo.toml index 943dac4ab5a..6269084eec7 100644 --- a/internal/renderers/skia/Cargo.toml +++ b/internal/renderers/skia/Cargo.toml @@ -60,7 +60,7 @@ bytemuck = { workspace = true } windows = { version = "0.56.0", features = ["Win32", "Win32_System_Com", "Win32_Graphics", "Win32_Graphics_Dxgi", "Win32_Graphics_Direct3D12", "Win32_Graphics_Direct3D", "Win32_Foundation", "Win32_Graphics_Dxgi_Common", "Win32_System_Threading", "Win32_Security"] } skia-safe = { version = "0.75.0", features = ["d3d"] } -[target.'cfg(target_os = "macos")'.dependencies] +[target.'cfg(target_vendor = "apple")'.dependencies] cocoa = { version = "0.25.0" } core-foundation = { version = "0.9.1" } metal = { version = "0.27.0" } @@ -69,8 +69,9 @@ foreign-types = { version = "0.5.0" } objc = { version = "0.2.7" } core-graphics-types = { version = "0.1.1" } skia-safe = { version = "0.75.0", features = ["metal"] } +raw-window-metal = "1.0" -[target.'cfg(not(any(target_os = "macos", target_family = "windows")))'.dependencies] +[target.'cfg(not(any(target_vendor = "apple", target_family = "windows")))'.dependencies] skia-safe = { version = "0.75.0", features = ["gl"] } [build-dependencies] diff --git a/internal/renderers/skia/metal_surface.rs b/internal/renderers/skia/metal_surface.rs index 56857138410..fdc94656bf1 100644 --- a/internal/renderers/skia/metal_surface.rs +++ b/internal/renderers/skia/metal_surface.rs @@ -1,18 +1,29 @@ // Copyright © SixtyFPS GmbH // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 -use cocoa::{appkit::NSView, base::id as cocoa_id}; use core_graphics_types::geometry::CGSize; -use foreign_types::ForeignTypeRef; +use foreign_types::{ForeignType, ForeignTypeRef}; use i_slint_core::api::PhysicalSize as PhysicalWindowSize; use metal::MTLPixelFormat; -use objc::{rc::autoreleasepool, runtime::YES}; +use objc::{msg_send, sel, sel_impl}; +use objc::{ + rc::autoreleasepool, + runtime::{Object, BOOL, NO}, +}; use skia_safe::gpu::mtl; use std::cell::RefCell; use std::rc::Rc; +#[link(name = "QuartzCore", kind = "framework")] +extern "C" { + #[allow(non_upper_case_globals)] + static kCAGravityTopLeft: *mut Object; + #[allow(non_upper_case_globals)] + static kCAGravityBottomLeft: *mut Object; +} + /// This surface renders into the given window using Metal. The provided display argument /// is ignored, as it has no meaning on macOS. pub struct MetalSurface { @@ -27,10 +38,26 @@ impl super::Surface for MetalSurface { _display_handle: Rc, size: PhysicalWindowSize, ) -> Result { + let layer = match window_handle + .window_handle() + .map_err(|e| format!("Error obtaining window handle for skia metal renderer: {e}"))? + .as_raw() + { + raw_window_handle::RawWindowHandle::AppKit(handle) => unsafe { + raw_window_metal::Layer::from_ns_view(handle.ns_view) + }, + raw_window_handle::RawWindowHandle::UiKit(handle) => unsafe { + raw_window_metal::Layer::from_ui_view(handle.ui_view) + }, + _ => return Err("Skia Renderer: Metal surface is only supported with AppKit".into()), + }; + + // SAFETY: The layer is an initialized instance of `CAMetalLayer`, and + // we transfer the retain count to `MetalLayer` using `into_raw`. + let layer = unsafe { metal::MetalLayer::from_ptr(layer.into_raw().cast().as_ptr()) }; + let device = metal::Device::system_default() .ok_or_else(|| format!("Skia Renderer: No metal device found"))?; - - let layer = metal::MetalLayer::new(); layer.set_device(&device); layer.set_pixel_format(MTLPixelFormat::BGRA8Unorm); layer.set_opaque(false); @@ -38,25 +65,13 @@ impl super::Surface for MetalSurface { layer.set_drawable_size(CGSize::new(size.width as f64, size.height as f64)); - unsafe { - let view = match window_handle - .window_handle() - .map_err(|e| format!("Error obtaining window handle for skia metal renderer: {e}"))? - .as_raw() - { - raw_window_handle::RawWindowHandle::AppKit( - raw_window_handle::AppKitWindowHandle { ns_view, .. }, - ) => ns_view.as_ptr(), - _ => { - return Err("Skia Renderer: Metal surface is only supported with AppKit".into()) - } - } as cocoa_id; - view.setWantsLayer(YES); - view.setLayer(layer.as_ref() as *const _ as _); - view.setLayerContentsPlacement( - cocoa::appkit::NSViewLayerContentsPlacement::NSViewLayerContentsPlacementTopLeft, - ); - } + let flipped: BOOL = unsafe { msg_send![layer.as_ptr(), contentsAreFlipped] }; + let gravity = if flipped == NO { + unsafe { kCAGravityTopLeft } + } else { + unsafe { kCAGravityBottomLeft } + }; + let _: () = unsafe { msg_send![layer.as_ptr(), setContentsGravity: gravity] }; let command_queue = device.new_command_queue(); @@ -85,10 +100,6 @@ impl super::Surface for MetalSurface { Ok(()) } - fn set_scale_factor(&self, scale_factor: f32) { - self.layer.set_contents_scale(scale_factor.into()); - } - fn render( &self, _size: PhysicalWindowSize, diff --git a/internal/renderers/skia/vulkan_surface.rs b/internal/renderers/skia/vulkan_surface.rs index 7e55708e3bf..2631b2c8271 100644 --- a/internal/renderers/skia/vulkan_surface.rs +++ b/internal/renderers/skia/vulkan_surface.rs @@ -395,30 +395,22 @@ impl super::Surface for VulkanSurface { } } +// FIXME(madsmtm): Why are we doing this instead of using `Surface::from_window`? fn create_surface( instance: &Arc, window_handle: raw_window_handle::WindowHandle<'_>, display_handle: raw_window_handle::DisplayHandle<'_>, ) -> Result, vulkano::Validated> { match (window_handle.as_raw(), display_handle.as_raw()) { - #[cfg(target_os = "macos")] - ( - raw_window_handle::RawWindowHandle::AppKit(raw_window_handle::AppKitWindowHandle { - ns_view, - .. - }), - _, - ) => unsafe { - use cocoa::{appkit::NSView, base::id as cocoa_id}; - use objc::runtime::YES; - - let layer = metal::MetalLayer::new(); - layer.set_opaque(false); - layer.set_presents_with_transaction(false); - let view = ns_view.as_ptr() as cocoa_id; - view.setWantsLayer(YES); - view.setLayer(layer.as_ref() as *const _ as _); - Surface::from_metal(instance.clone(), layer.as_ref(), None) + #[cfg(target_vendor = "apple")] + (raw_window_handle::RawWindowHandle::AppKit(handle), _) => unsafe { + let layer = raw_window_metal::Layer::from_ns_view(handle.ns_view); + Surface::from_metal(instance.clone(), layer.as_ptr().as_ptr(), None) + }, + #[cfg(target_vendor = "apple")] + (raw_window_handle::RawWindowHandle::UiKit(handle), _) => unsafe { + let layer = raw_window_metal::Layer::from_ui_view(handle.ui_view); + Surface::from_metal(instance.clone(), layer.as_ptr().as_ptr(), None) }, ( raw_window_handle::RawWindowHandle::Xlib(raw_window_handle::XlibWindowHandle {