Skip to content

Commit

Permalink
Use raw-window-metal to do layer creation in skia
Browse files Browse the repository at this point in the history
  • Loading branch information
madsmtm authored and tronical committed Sep 10, 2024
1 parent 84fe321 commit 945e65f
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 48 deletions.
5 changes: 3 additions & 2 deletions internal/renderers/skia/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand All @@ -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]
Expand Down
67 changes: 39 additions & 28 deletions internal/renderers/skia/metal_surface.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,29 @@
// Copyright © SixtyFPS GmbH <info@slint.dev>
// 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 {
Expand All @@ -27,36 +38,40 @@ impl super::Surface for MetalSurface {
_display_handle: Rc<dyn raw_window_handle::HasDisplayHandle>,
size: PhysicalWindowSize,
) -> Result<Self, i_slint_core::platform::PlatformError> {
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);
layer.set_presents_with_transaction(false);

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();

Expand Down Expand Up @@ -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,
Expand Down
28 changes: 10 additions & 18 deletions internal/renderers/skia/vulkan_surface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Instance>,
window_handle: raw_window_handle::WindowHandle<'_>,
display_handle: raw_window_handle::DisplayHandle<'_>,
) -> Result<Arc<Surface>, vulkano::Validated<vulkano::VulkanError>> {
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 {
Expand Down

0 comments on commit 945e65f

Please sign in to comment.