Skip to content

Commit

Permalink
Use raw-window-metal to get a CAMetalLayer from raw-window-handle (#2561
Browse files Browse the repository at this point in the history
)

The way `raw-window-metal` works is by creating a layer, and inserting
that as a sublayer, just like we did on iOS before. The bounds are then
kept in-sync with an observer, ensuring smooth resizing.

This also fixes compilation errors on iOS, and adds preliminary support
for tvOS.

The implementation now solely uses `VK_EXT_metal_surface`, which was
added in 2018, instead of `VK_MVK_ios_surface` / `VK_MVK_macos_surface`,
which are deprecated, and only available a year and a half earlier
anyhow.

Note that apart from the above, there is a slight behavioral change on
macOS: we no longer set `edgeAntialiasingMask` on the layer, as it's not
really required, and allows us to avoid depending on `objc2` directly.
It was introduced without motivation in 40e0b24, so I doubt anyone uses
it, and if they do, they can change it on the layer themselves.
  • Loading branch information
madsmtm committed Sep 11, 2024
1 parent db4657d commit 0af3fb3
Show file tree
Hide file tree
Showing 13 changed files with 167 additions and 276 deletions.
86 changes: 79 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,21 +47,20 @@ ahash = "0.8"
ash = "0.38.0"
bytemuck = "1.9"
concurrent-slotmap = { git = "https://github.com/vulkano-rs/concurrent-slotmap", rev = "fa906d916d8d126d3cc3a2b4ab9a29fa27bee62d" }
core-graphics-types = "0.1"
crossbeam-queue = "0.3"
half = "2.0"
heck = "0.4"
indexmap = "2.0"
libloading = "0.8"
nom = "7.1"
objc = "0.2.5"
once_cell = "1.17"
parking_lot = "0.12"
proc-macro2 = "1.0"
proc-macro-crate = "2.0"
quote = "1.0"
rangemap = "1.5"
raw-window-handle = "0.6"
raw-window-metal = "1.0"
serde = "1.0"
serde_json = "1.0"
shaderc = "0.8.3"
Expand Down
13 changes: 6 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ Vulkano uses [shaderc-rs](https://github.com/google/shaderc-rs) for shader compi
Note that in general vulkano does **not** require you to install the official Vulkan SDK. This is
not something specific to vulkano (you don't need the SDK to write programs that use Vulkan, even
without vulkano), but many people are unaware of that and install the SDK thinking that it is
required. However, macOS and iOS platforms do require a little more Vulkan setup since it is not
required. However, macOS, iOS and tvOS platforms do require a little more Vulkan setup since it is not
natively supported. See below for more details.

Unless you provide libshaderc, in order to build libshaderc with the shaderc-sys crate, the following tools must be installed and available on `PATH`:
Expand Down Expand Up @@ -203,17 +203,16 @@ On arch based system
sudo pacman -Sy base-devel git python cmake vulkan-devel --noconfirm
```

### macOS and iOS Specific Setup
### macOS, iOS and tvOS Specific Setup

Vulkan is not natively supported by macOS and iOS. However, there exists [MoltenVK](https://github.com/KhronosGroup/MoltenVK)
an open-source Vulkan implementation on top of Apple's Metal API. This allows vulkano to build and run on macOS
and iOS platforms.
Vulkan is not natively supported by macOS, iOS and tvOS. However, there exists [MoltenVK](https://github.com/KhronosGroup/MoltenVK)
an open-source Vulkan implementation on top of Apple's Metal API. This allows vulkano to build and run on macOS, iOS and tvOS platforms.

The easiest way to get vulkano up and running with MoltenVK is to install the
[Vulkan SDK for macOS](https://vulkan.lunarg.com/sdk/home). There are [installation instructions](https://vulkan.lunarg.com/doc/sdk/latest/mac/getting_started.html) on the LunarG website.

On iOS, vulkano links directly to the MoltenVK framework. There is nothing else to do besides
installing it. Note that the Vulkan SDK for macOS also comes with the iOS framework.
On iOS and tvOS, vulkano links directly to the MoltenVK framework. There is nothing else to do besides
installing it. Note that the Vulkan SDK for macOS also comes with the framework for iOS and tvOS.

## License

Expand Down
5 changes: 2 additions & 3 deletions vulkano-util/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ impl VulkanoContext {
pub fn new(mut config: VulkanoConfig) -> Self {
let library = match VulkanLibrary::new() {
Ok(x) => x,
#[cfg(target_os = "macos")]
#[cfg(target_vendor = "apple")]
Err(vulkano::library::LoadingError::LibraryLoadFailure(err)) => panic!(
"failed to load Vulkan library: {err}; did you install VulkanSDK from \
https://vulkan.lunarg.com/sdk/home?",
Expand All @@ -140,8 +140,7 @@ impl VulkanoContext {
khr_wayland_surface: true,
khr_android_surface: true,
khr_win32_surface: true,
mvk_ios_surface: true,
mvk_macos_surface: true,
ext_metal_surface: true,
..InstanceExtensions::empty()
})
.union(&config.instance_create_info.enabled_extensions);
Expand Down
4 changes: 0 additions & 4 deletions vulkano-util/src/renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -373,10 +373,6 @@ impl VulkanoWindowRenderer {
self.remove_additional_image_view(i);
self.add_additional_image_view(i, format, usage);
}
#[cfg(target_os = "ios")]
unsafe {
self.surface.update_ios_sublayer_on_resize();
}
self.recreate_swapchain = false;
}
}
14 changes: 8 additions & 6 deletions vulkano-win/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
name = "vulkano-win"
version = "0.34.0"
edition = "2021"
authors = ["Pierre Krieger <[email protected]>", "The vulkano contributors"]
authors = [
"Pierre Krieger <[email protected]>",
"The vulkano contributors",
]
repository = "https://github.com/vulkano-rs/vulkano/tree/master/vulkano-win"
description = "Link between vulkano and winit"
license = "MIT OR Apache-2.0"
Expand All @@ -16,8 +19,8 @@ readme = "../README.md"
default = ["winit", "raw-window-handle"]
raw-window-handle = ["dep:raw-window-handle"]
raw-window-handle_ = ["dep:raw-window-handle"]
winit = ["dep:winit", "dep:objc", "dep:core-graphics-types"]
winit_ = ["dep:winit", "dep:objc", "dep:core-graphics-types"]
winit = ["dep:winit"]
winit_ = ["dep:winit"]

# NOTE(Marc): The dependencies here are not workspace dependencies because vulkano-win is
# deprecated and won't be receiving updates.
Expand All @@ -27,6 +30,5 @@ raw-window-handle = { version = "0.5", optional = true }
vulkano = { workspace = true }
winit = { version = "0.28", optional = true }

[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies]
objc = { version = "0.2.5", optional = true }
core-graphics-types = { version = "0.1", optional = true }
[target.'cfg(target_vendor = "apple")'.dependencies]
raw-window-metal.workspace = true
76 changes: 28 additions & 48 deletions vulkano-win/src/raw_window_handle.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
#[cfg(target_os = "ios")]
use crate::get_metal_layer_ios;
#[cfg(target_os = "macos")]
use crate::get_metal_layer_macos;
use raw_window_handle::{
HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle,
};
Expand All @@ -19,29 +15,21 @@ pub fn create_surface_from_handle(
RawWindowHandle::AndroidNdk(h) => {
Surface::from_android(instance, h.a_native_window, Some(window))
}
RawWindowHandle::UiKit(_h) => {
#[cfg(target_os = "ios")]
{
// Ensure the layer is CAMetalLayer
let metal_layer = get_metal_layer_ios(_h.ui_view);
Surface::from_ios(instance, metal_layer.render_layer.0 as _, Some(window))
}
#[cfg(not(target_os = "ios"))]
{
panic!("UiKit handle should only be used when target_os == 'ios'");
}
#[cfg(target_vendor = "apple")]
RawWindowHandle::AppKit(handle) => {
let view = std::ptr::NonNull::new(handle.ns_view).unwrap();
let layer = raw_window_metal::Layer::from_ns_view(view);

// Vulkan retains the CAMetalLayer, so no need to retain it past this invocation
Surface::from_metal(instance, layer.as_ptr(), Some(window))
}
RawWindowHandle::AppKit(_h) => {
#[cfg(target_os = "macos")]
{
// Ensure the layer is CAMetalLayer
let metal_layer = get_metal_layer_macos(_h.ns_view);
Surface::from_mac_os(instance, metal_layer as _, Some(window))
}
#[cfg(not(target_os = "macos"))]
{
panic!("AppKit handle should only be used when target_os == 'macos'");
}
#[cfg(target_vendor = "apple")]
RawWindowHandle::UiKit(handle) => {
let view = std::ptr::NonNull::new(handle.ui_view).unwrap();
let layer = raw_window_metal::Layer::from_ui_view(view);

// Vulkan retains the CAMetalLayer, so no need to retain it past this invocation
Surface::from_metal(instance, layer.as_ptr(), Some(window))
}
RawWindowHandle::Wayland(h) => {
let d = match window.raw_display_handle() {
Expand Down Expand Up @@ -88,29 +76,21 @@ pub unsafe fn create_surface_from_handle_ref(
RawWindowHandle::AndroidNdk(h) => {
Surface::from_android(instance, h.a_native_window, None)
}
RawWindowHandle::UiKit(_h) => {
#[cfg(target_os = "ios")]
{
// Ensure the layer is CAMetalLayer
let metal_layer = get_metal_layer_ios(_h.ui_view);
Surface::from_ios(instance, metal_layer.render_layer.0 as _, None)
}
#[cfg(not(target_os = "ios"))]
{
panic!("UiKit handle should only be used when target_os == 'ios'");
}
#[cfg(target_vendor = "apple")]
(RawWindowHandle::AppKit(handle), _) => {
let view = std::ptr::NonNull::new(handle.ns_view).unwrap();
let layer = raw_window_metal::Layer::from_ns_view(view);

// Vulkan retains the CAMetalLayer, so no need to retain it past this invocation
Surface::from_metal(instance, layer.as_ptr(), None)
}
RawWindowHandle::AppKit(_h) => {
#[cfg(target_os = "macos")]
{
// Ensure the layer is CAMetalLayer
let metal_layer = get_metal_layer_macos(_h.ns_view);
Surface::from_mac_os(instance, metal_layer as _, None)
}
#[cfg(not(target_os = "macos"))]
{
panic!("AppKit handle should only be used when target_os == 'macos'");
}
#[cfg(target_vendor = "apple")]
(RawWindowHandle::UiKit(handle), _) => {
let view = std::ptr::NonNull::new(handle.ui_view).unwrap();
let layer = raw_window_metal::Layer::from_ui_view(view);

// Vulkan retains the CAMetalLayer, so no need to retain it past this invocation
Surface::from_metal(instance, layer.as_ptr(), None)
}
RawWindowHandle::Wayland(h) => {
let d = match window.raw_display_handle() {
Expand Down
Loading

0 comments on commit 0af3fb3

Please sign in to comment.