mirror of
https://github.com/vulkano-rs/vulkano.git
synced 2024-11-23 07:15:31 +00:00
Use raw-window-metal to get a CAMetalLayer from raw-window-handle (#2561)
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.
This commit is contained in:
parent
db4657d0f0
commit
0af3fb3cb3
86
Cargo.lock
generated
86
Cargo.lock
generated
@ -196,7 +196,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15b55663a85f33501257357e6421bb33e769d5c9ffb5ba0921c975a123e35e68"
|
||||
dependencies = [
|
||||
"block-sys",
|
||||
"objc2",
|
||||
"objc2 0.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block2"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f"
|
||||
dependencies = [
|
||||
"objc2 0.5.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -928,9 +937,9 @@ version = "0.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99d3aaff8a54577104bafdf686ff18565c3b6903ca5782a2026ef06e2c7aa319"
|
||||
dependencies = [
|
||||
"block2",
|
||||
"block2 0.3.0",
|
||||
"dispatch",
|
||||
"objc2",
|
||||
"objc2 0.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1437,7 +1446,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "559c5a40fdd30eb5e344fbceacf7595a81e242529fb4e21cf5f43fb4f11ff98d"
|
||||
dependencies = [
|
||||
"objc-sys",
|
||||
"objc2-encode",
|
||||
"objc2-encode 3.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804"
|
||||
dependencies = [
|
||||
"objc-sys",
|
||||
"objc2-encode 4.0.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1446,6 +1465,49 @@ version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d079845b37af429bfe5dfa76e6d087d788031045b25cfc6fd898486fd9847666"
|
||||
|
||||
[[package]]
|
||||
name = "objc2-encode"
|
||||
version = "4.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7891e71393cd1f227313c9379a26a584ff3d7e6e7159e988851f0934c993f0f8"
|
||||
|
||||
[[package]]
|
||||
name = "objc2-foundation"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"block2 0.5.1",
|
||||
"libc",
|
||||
"objc2 0.5.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-metal"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"block2 0.5.1",
|
||||
"objc2 0.5.2",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-quartz-core"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"block2 0.5.1",
|
||||
"objc2 0.5.2",
|
||||
"objc2-foundation",
|
||||
"objc2-metal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.35.0"
|
||||
@ -1706,6 +1768,17 @@ version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539"
|
||||
|
||||
[[package]]
|
||||
name = "raw-window-metal"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2000e45d7daa9b6d946e88dfa1d7ae330424a81918a6545741821c989eb80a9"
|
||||
dependencies = [
|
||||
"objc2 0.5.2",
|
||||
"objc2-foundation",
|
||||
"objc2-quartz-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.3.5"
|
||||
@ -2353,7 +2426,6 @@ dependencies = [
|
||||
"ahash",
|
||||
"ash",
|
||||
"bytemuck",
|
||||
"core-graphics-types",
|
||||
"crossbeam-queue",
|
||||
"half",
|
||||
"heck",
|
||||
@ -2361,12 +2433,12 @@ dependencies = [
|
||||
"libc",
|
||||
"libloading 0.8.3",
|
||||
"nom",
|
||||
"objc",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"raw-window-handle 0.6.2",
|
||||
"raw-window-metal",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"slabbin",
|
||||
@ -3052,7 +3124,7 @@ dependencies = [
|
||||
"memmap2 0.9.4",
|
||||
"ndk 0.8.0",
|
||||
"ndk-sys 0.5.0+25.2.9519653",
|
||||
"objc2",
|
||||
"objc2 0.4.1",
|
||||
"once_cell",
|
||||
"orbclient",
|
||||
"percent-encoding",
|
||||
|
@ -47,14 +47,12 @@ 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"
|
||||
@ -62,6 +60,7 @@ 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"
|
||||
|
13
README.md
13
README.md
@ -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`:
|
||||
@ -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
|
||||
|
||||
|
@ -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?",
|
||||
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,10 @@
|
||||
name = "vulkano-win"
|
||||
version = "0.34.0"
|
||||
edition = "2021"
|
||||
authors = ["Pierre Krieger <pierre.krieger1708@gmail.com>", "The vulkano contributors"]
|
||||
authors = [
|
||||
"Pierre Krieger <pierre.krieger1708@gmail.com>",
|
||||
"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"
|
||||
@ -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.
|
||||
@ -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
|
||||
|
@ -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,
|
||||
};
|
||||
@ -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() {
|
||||
@ -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() {
|
||||
|
@ -22,8 +22,7 @@ pub fn required_extensions(library: &VulkanLibrary) -> InstanceExtensions {
|
||||
khr_wayland_surface: true,
|
||||
khr_android_surface: true,
|
||||
khr_win32_surface: true,
|
||||
mvk_ios_surface: true,
|
||||
mvk_macos_surface: true,
|
||||
ext_metal_surface: true,
|
||||
khr_get_physical_device_properties2: true,
|
||||
khr_get_surface_capabilities2: true,
|
||||
..InstanceExtensions::empty()
|
||||
@ -119,12 +118,7 @@ unsafe fn winit_to_surface(
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
unix,
|
||||
not(target_os = "android"),
|
||||
not(target_os = "macos"),
|
||||
not(target_os = "ios")
|
||||
))]
|
||||
#[cfg(all(unix, not(target_os = "android"), target_vendor = "apple",))]
|
||||
unsafe fn winit_to_surface(
|
||||
instance: Arc<Instance>,
|
||||
window: Arc<Window>,
|
||||
@ -157,68 +151,15 @@ unsafe fn winit_to_surface(
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
use objc::{class, msg_send, runtime::Object, sel, sel_impl};
|
||||
|
||||
/// Get (and set) `CAMetalLayer` to ns_view.
|
||||
/// This is necessary to be able to render on Mac.
|
||||
#[cfg(target_os = "macos")]
|
||||
pub(crate) unsafe fn get_metal_layer_macos(view: *mut std::ffi::c_void) -> *mut Object {
|
||||
use core_graphics_types::base::CGFloat;
|
||||
use objc::runtime::YES;
|
||||
use objc::runtime::{BOOL, NO};
|
||||
|
||||
let view: *mut Object = std::mem::transmute(view);
|
||||
let main_layer: *mut Object = msg_send![view, layer];
|
||||
let class = class!(CAMetalLayer);
|
||||
let is_valid_layer: BOOL = msg_send![main_layer, isKindOfClass: class];
|
||||
if is_valid_layer == NO {
|
||||
let new_layer: *mut Object = msg_send![class, new];
|
||||
let () = msg_send![new_layer, setEdgeAntialiasingMask: 0];
|
||||
let () = msg_send![new_layer, setPresentsWithTransaction: false];
|
||||
let () = msg_send![new_layer, removeAllAnimations];
|
||||
let () = msg_send![view, setLayer: new_layer];
|
||||
let () = msg_send![view, setWantsLayer: YES];
|
||||
let window: *mut Object = msg_send![view, window];
|
||||
if !window.is_null() {
|
||||
let scale_factor: CGFloat = msg_send![window, backingScaleFactor];
|
||||
let () = msg_send![new_layer, setContentsScale: scale_factor];
|
||||
}
|
||||
new_layer
|
||||
} else {
|
||||
main_layer
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
unsafe fn winit_to_surface(
|
||||
instance: Arc<Instance>,
|
||||
window: Arc<Window>,
|
||||
) -> Result<Arc<Surface>, Validated<VulkanError>> {
|
||||
use winit::platform::macos::WindowExtMacOS;
|
||||
let metal_layer = get_metal_layer_macos(window.ns_view());
|
||||
Surface::from_mac_os(instance, metal_layer as _, Some(window))
|
||||
}
|
||||
|
||||
#[cfg(target_os = "ios")]
|
||||
use vulkano::swapchain::IOSMetalLayer;
|
||||
|
||||
/// Get sublayer from iOS main view (ui_view). The sublayer is created as CAMetalLayer
|
||||
#[cfg(target_os = "ios")]
|
||||
pub(crate) unsafe fn get_metal_layer_ios(view: *mut std::ffi::c_void) -> IOSMetalLayer {
|
||||
use core_graphics_types::{base::CGFloat, geometry::CGRect};
|
||||
|
||||
let view: *mut Object = std::mem::transmute(view);
|
||||
let main_layer: *mut Object = msg_send![view, layer];
|
||||
let class = class!(CAMetalLayer);
|
||||
let new_layer: *mut Object = msg_send![class, new];
|
||||
let frame: CGRect = msg_send![main_layer, bounds];
|
||||
let () = msg_send![new_layer, setFrame: frame];
|
||||
let () = msg_send![main_layer, addSublayer: new_layer];
|
||||
let screen: *mut Object = msg_send![class!(UIScreen), mainScreen];
|
||||
let scale_factor: CGFloat = msg_send![screen, nativeScale];
|
||||
let () = msg_send![view, setContentScaleFactor: scale_factor];
|
||||
IOSMetalLayer::new(view, new_layer)
|
||||
let view = std::ptr::NonNull::new(window.ns_view()).unwrap();
|
||||
let layer = raw_window_metal::Layer::from_ns_view(view);
|
||||
Surface::from_metal(instance, layer.as_ptr(), Some(window))
|
||||
}
|
||||
|
||||
#[cfg(target_os = "ios")]
|
||||
@ -227,8 +168,9 @@ unsafe fn winit_to_surface(
|
||||
window: Arc<Window>,
|
||||
) -> Result<Arc<Surface>, Validated<VulkanError>> {
|
||||
use winit::platform::ios::WindowExtIOS;
|
||||
let metal_layer = get_metal_layer_ios(window.ui_view());
|
||||
Surface::from_ios(instance, metal_layer.render_layer.0 as _, Some(window))
|
||||
let view = std::ptr::NonNull::new(window.ui_view()).unwrap();
|
||||
let layer = raw_window_metal::Layer::from_ui_view(view);
|
||||
Surface::from_metal(instance, layer.as_ptr(), Some(window))
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
|
@ -30,9 +30,8 @@ smallvec = { workspace = true }
|
||||
thread_local = { workspace = true }
|
||||
vulkano-macros = { workspace = true, optional = true }
|
||||
|
||||
[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies]
|
||||
objc = { workspace = true }
|
||||
core-graphics-types = { workspace = true }
|
||||
[target.'cfg(target_vendor = "apple")'.dependencies]
|
||||
raw-window-metal = { workspace = true }
|
||||
|
||||
[target.'cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "hurd", target_os = "illumos", target_os = "linux", target_os = "netbsd", target_os = "openbsd", target_os = "solaris"))'.dependencies]
|
||||
x11-dl = { workspace = true, optional = true }
|
||||
|
@ -4,14 +4,16 @@ mod autogen;
|
||||
|
||||
fn main() {
|
||||
let target = env::var("TARGET").unwrap();
|
||||
if target.contains("apple-ios") {
|
||||
if target.contains("apple-ios") || target.contains("apple-tvos") {
|
||||
println!("cargo:rustc-link-search=framework=/Library/Frameworks/");
|
||||
println!("cargo:rustc-link-lib=c++");
|
||||
println!("cargo:rustc-link-lib=framework=MoltenVK");
|
||||
println!("cargo:rustc-link-lib=framework=Metal");
|
||||
println!("cargo:rustc-link-lib=framework=IOSurface");
|
||||
println!("cargo:rustc-link-lib=framework=CoreGraphics");
|
||||
println!("cargo:rustc-link-lib=framework=QuartzCore");
|
||||
println!("cargo:rustc-link-lib=framework=UIKit");
|
||||
println!("cargo:rustc-link-lib=framework=IOKit");
|
||||
println!("cargo:rustc-link-lib=framework=Foundation");
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,7 @@ pub struct VulkanLibrary {
|
||||
impl VulkanLibrary {
|
||||
/// Loads the default Vulkan library for this system.
|
||||
pub fn new() -> Result<Arc<Self>, LoadingError> {
|
||||
#[cfg(target_os = "ios")]
|
||||
#[cfg(any(target_os = "ios", target_os = "tvos"))]
|
||||
#[allow(non_snake_case)]
|
||||
fn def_loader_impl() -> Result<Box<dyn Loader>, LoadingError> {
|
||||
let loader = crate::statically_linked_vulkan_loader!();
|
||||
@ -48,7 +48,7 @@ impl VulkanLibrary {
|
||||
Ok(Box::new(loader))
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "ios"))]
|
||||
#[cfg(not(any(target_os = "ios", target_os = "tvos")))]
|
||||
fn def_loader_impl() -> Result<Box<dyn Loader>, LoadingError> {
|
||||
#[cfg(windows)]
|
||||
fn get_paths() -> [&'static Path; 1] {
|
||||
|
@ -318,8 +318,6 @@
|
||||
//! ```
|
||||
|
||||
pub use self::{acquire_present::*, surface::*};
|
||||
#[cfg(target_os = "ios")]
|
||||
pub use surface::IOSMetalLayer;
|
||||
|
||||
mod acquire_present;
|
||||
mod surface;
|
||||
|
@ -10,8 +10,6 @@ use crate::{
|
||||
DebugWrapper, Requires, RequiresAllOf, RequiresOneOf, Validated, ValidationError, VulkanError,
|
||||
VulkanObject,
|
||||
};
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
use objc::{class, msg_send, runtime::Object, sel, sel_impl};
|
||||
use raw_window_handle::{
|
||||
HandleError, HasDisplayHandle, HasWindowHandle, RawDisplayHandle, RawWindowHandle,
|
||||
};
|
||||
@ -36,10 +34,6 @@ pub struct Surface {
|
||||
id: NonZeroU64,
|
||||
api: SurfaceApi,
|
||||
object: Option<Arc<dyn Any + Send + Sync>>,
|
||||
// FIXME: This field is never set.
|
||||
#[cfg(target_os = "ios")]
|
||||
metal_layer: IOSMetalLayer,
|
||||
|
||||
// Data queried by the user at runtime, cached for faster lookups.
|
||||
// This is stored here rather than on `PhysicalDevice` to ensure that it's freed when the
|
||||
// `Surface` is destroyed.
|
||||
@ -62,9 +56,8 @@ impl Surface {
|
||||
};
|
||||
match event_loop.display_handle()?.as_raw() {
|
||||
RawDisplayHandle::Android(_) => extensions.khr_android_surface = true,
|
||||
// FIXME: `mvk_macos_surface` and `mvk_ios_surface` are deprecated.
|
||||
RawDisplayHandle::AppKit(_) => extensions.mvk_macos_surface = true,
|
||||
RawDisplayHandle::UiKit(_) => extensions.mvk_ios_surface = true,
|
||||
RawDisplayHandle::AppKit(_) => extensions.ext_metal_surface = true,
|
||||
RawDisplayHandle::UiKit(_) => extensions.ext_metal_surface = true,
|
||||
RawDisplayHandle::Windows(_) => extensions.khr_win32_surface = true,
|
||||
RawDisplayHandle::Wayland(_) => extensions.khr_wayland_surface = true,
|
||||
RawDisplayHandle::Xcb(_) => extensions.khr_xcb_surface = true,
|
||||
@ -107,19 +100,19 @@ impl Surface {
|
||||
(RawWindowHandle::AndroidNdk(window), RawDisplayHandle::Android(_display)) => {
|
||||
Self::from_android(instance, window.a_native_window.as_ptr().cast(), None)
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
(RawWindowHandle::AppKit(window), RawDisplayHandle::AppKit(_display)) => {
|
||||
// Ensure the layer is `CAMetalLayer`.
|
||||
let metal_layer = get_metal_layer_macos(window.ns_view.as_ptr().cast());
|
||||
#[cfg(target_vendor = "apple")]
|
||||
(RawWindowHandle::AppKit(handle), _) => {
|
||||
let layer = raw_window_metal::Layer::from_ns_view(handle.ns_view);
|
||||
|
||||
Self::from_mac_os(instance, metal_layer.cast(), None)
|
||||
// Vulkan retains the CAMetalLayer, so no need to retain it past this invocation
|
||||
Self::from_metal(instance, layer.as_ptr().as_ptr(), None)
|
||||
}
|
||||
#[cfg(target_os = "ios")]
|
||||
(RawWindowHandle::UiKit(window), RawDisplayHandle::UiKit(_display)) => {
|
||||
// Ensure the layer is `CAMetalLayer`.
|
||||
let metal_layer = get_metal_layer_ios(window.ui_view.as_ptr().cast());
|
||||
#[cfg(target_vendor = "apple")]
|
||||
(RawWindowHandle::UiKit(handle), _) => {
|
||||
let layer = raw_window_metal::Layer::from_ui_view(handle.ui_view);
|
||||
|
||||
Self::from_ios(instance, metal_layer.render_layer.0.cast(), None)
|
||||
// Vulkan retains the CAMetalLayer, so no need to retain it past this invocation
|
||||
Self::from_metal(instance, layer.as_ptr().as_ptr(), None)
|
||||
}
|
||||
(RawWindowHandle::Wayland(window), RawDisplayHandle::Wayland(display)) => {
|
||||
Self::from_wayland(
|
||||
@ -177,8 +170,6 @@ impl Surface {
|
||||
id: Self::next_id(),
|
||||
api,
|
||||
object,
|
||||
#[cfg(target_os = "ios")]
|
||||
metal_layer: IOSMetalLayer::new(std::ptr::null_mut(), std::ptr::null_mut()),
|
||||
surface_formats: OnceCache::new(),
|
||||
surface_present_modes: OnceCache::new(),
|
||||
surface_support: OnceCache::new(),
|
||||
@ -874,13 +865,20 @@ impl Surface {
|
||||
)))
|
||||
}
|
||||
|
||||
/// Creates a `Surface` from a Metal `CAMetalLayer`.
|
||||
/// Create a `Surface` from a [`CAMetalLayer`].
|
||||
///
|
||||
/// If you want to create this from a `NSView` or `UIView`, it is
|
||||
/// recommended that you use the `raw-window-metal` crate to get access to
|
||||
/// a layer without overwriting the view's layer.
|
||||
///
|
||||
/// [`CAMetalLayer`]: https://developer.apple.com/documentation/quartzcore/cametallayer
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - `layer` must be a valid Metal `CAMetalLayer` handle.
|
||||
/// - The object referred to by `layer` must outlive the created `Surface`. The `object`
|
||||
/// parameter can be used to ensure this.
|
||||
/// `layer` must point to a valid, initialized, non-NULL `CAMetalLayer`.
|
||||
///
|
||||
/// The surface will retain the layer internally, so you do not need to
|
||||
/// keep the source from where this came from alive.
|
||||
pub unsafe fn from_metal(
|
||||
instance: Arc<Instance>,
|
||||
layer: *const ash::vk::CAMetalLayer,
|
||||
@ -893,7 +891,7 @@ impl Surface {
|
||||
|
||||
fn validate_from_metal(
|
||||
instance: &Instance,
|
||||
_layer: *const ash::vk::CAMetalLayer,
|
||||
layer: *const ash::vk::CAMetalLayer,
|
||||
) -> Result<(), Box<ValidationError>> {
|
||||
if !instance.enabled_extensions().ext_metal_surface {
|
||||
return Err(Box::new(ValidationError {
|
||||
@ -904,6 +902,8 @@ impl Surface {
|
||||
}));
|
||||
}
|
||||
|
||||
assert!(!layer.is_null(), "CAMetalLayer must not be NULL");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -1437,24 +1437,6 @@ impl Surface {
|
||||
pub fn object(&self) -> Option<&Arc<dyn Any + Send + Sync>> {
|
||||
self.object.as_ref()
|
||||
}
|
||||
|
||||
/// Resizes the sublayer bounds on iOS.
|
||||
/// It may not be necessary if original window size matches device's, but often it does not.
|
||||
/// Thus this should be called after a resize has occurred and swapchain has been recreated.
|
||||
///
|
||||
/// On iOS, we've created CAMetalLayer as a sublayer. However, when the view changes size,
|
||||
/// its sublayers are not automatically resized, and we must resize
|
||||
/// it here.
|
||||
#[cfg(target_os = "ios")]
|
||||
#[inline]
|
||||
pub unsafe fn update_ios_sublayer_on_resize(&self) {
|
||||
use core_graphics_types::geometry::CGRect;
|
||||
let class = class!(CAMetalLayer);
|
||||
let main_layer: *mut Object = self.metal_layer.main_layer.0;
|
||||
let bounds: CGRect = msg_send![main_layer, bounds];
|
||||
let render_layer: *mut Object = self.metal_layer.render_layer.0;
|
||||
let () = msg_send![render_layer, setFrame: bounds];
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Surface {
|
||||
@ -1509,52 +1491,6 @@ impl Debug for Surface {
|
||||
|
||||
impl_id_counter!(Surface);
|
||||
|
||||
/// Get sublayer from iOS main view (ui_view). The sublayer is created as `CAMetalLayer`.
|
||||
#[cfg(target_os = "ios")]
|
||||
unsafe fn get_metal_layer_ios(ui_view: *mut c_void) -> IOSMetalLayer {
|
||||
use core_graphics_types::{base::CGFloat, geometry::CGRect};
|
||||
|
||||
let view: *mut Object = ui_view.cast();
|
||||
let main_layer: *mut Object = msg_send![view, layer];
|
||||
let class = class!(CAMetalLayer);
|
||||
let new_layer: *mut Object = msg_send![class, new];
|
||||
let frame: CGRect = msg_send![main_layer, bounds];
|
||||
let () = msg_send![new_layer, setFrame: frame];
|
||||
let () = msg_send![main_layer, addSublayer: new_layer];
|
||||
let screen: *mut Object = msg_send![class!(UIScreen), mainScreen];
|
||||
let scale_factor: CGFloat = msg_send![screen, nativeScale];
|
||||
let () = msg_send![view, setContentScaleFactor: scale_factor];
|
||||
IOSMetalLayer::new(view, new_layer)
|
||||
}
|
||||
|
||||
/// Get (and set) `CAMetalLayer` to `ns_view`. This is necessary to be able to render on Mac.
|
||||
#[cfg(target_os = "macos")]
|
||||
unsafe fn get_metal_layer_macos(ns_view: *mut c_void) -> *mut Object {
|
||||
use core_graphics_types::base::CGFloat;
|
||||
use objc::runtime::{BOOL, NO, YES};
|
||||
|
||||
let view: *mut Object = ns_view.cast();
|
||||
let main_layer: *mut Object = msg_send![view, layer];
|
||||
let class = class!(CAMetalLayer);
|
||||
let is_valid_layer: BOOL = msg_send![main_layer, isKindOfClass: class];
|
||||
if is_valid_layer == NO {
|
||||
let new_layer: *mut Object = msg_send![class, new];
|
||||
let () = msg_send![new_layer, setEdgeAntialiasingMask: 0];
|
||||
let () = msg_send![new_layer, setPresentsWithTransaction: false];
|
||||
let () = msg_send![new_layer, removeAllAnimations];
|
||||
let () = msg_send![view, setLayer: new_layer];
|
||||
let () = msg_send![view, setWantsLayer: YES];
|
||||
let window: *mut Object = msg_send![view, window];
|
||||
if !window.is_null() {
|
||||
let scale_factor: CGFloat = msg_send![window, backingScaleFactor];
|
||||
let () = msg_send![new_layer, setContentsScale: scale_factor];
|
||||
}
|
||||
new_layer
|
||||
} else {
|
||||
main_layer
|
||||
}
|
||||
}
|
||||
|
||||
/// Parameters to create a surface from a display mode and plane.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DisplaySurfaceCreateInfo {
|
||||
@ -1766,8 +1702,8 @@ impl TryFrom<RawWindowHandle> for SurfaceApi {
|
||||
|
||||
fn try_from(handle: RawWindowHandle) -> Result<Self, Self::Error> {
|
||||
match handle {
|
||||
RawWindowHandle::UiKit(_) => Ok(SurfaceApi::Ios),
|
||||
RawWindowHandle::AppKit(_) => Ok(SurfaceApi::MacOs),
|
||||
RawWindowHandle::UiKit(_) => Ok(SurfaceApi::Metal),
|
||||
RawWindowHandle::AppKit(_) => Ok(SurfaceApi::Metal),
|
||||
RawWindowHandle::Orbital(_) => Err(()),
|
||||
RawWindowHandle::Xlib(_) => Ok(SurfaceApi::Xlib),
|
||||
RawWindowHandle::Xcb(_) => Ok(SurfaceApi::Xcb),
|
||||
@ -2222,39 +2158,6 @@ impl SurfaceInfo {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "ios")]
|
||||
struct LayerHandle(*mut Object);
|
||||
|
||||
#[cfg(target_os = "ios")]
|
||||
unsafe impl Send for LayerHandle {}
|
||||
|
||||
#[cfg(target_os = "ios")]
|
||||
unsafe impl Sync for LayerHandle {}
|
||||
|
||||
/// Represents the metal layer for IOS
|
||||
#[cfg(target_os = "ios")]
|
||||
pub struct IOSMetalLayer {
|
||||
main_layer: LayerHandle,
|
||||
render_layer: LayerHandle,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "ios")]
|
||||
impl IOSMetalLayer {
|
||||
#[inline]
|
||||
pub fn new(main_layer: *mut Object, render_layer: *mut Object) -> Self {
|
||||
Self {
|
||||
main_layer: LayerHandle(main_layer),
|
||||
render_layer: LayerHandle(render_layer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "ios")]
|
||||
unsafe impl Send for IOSMetalLayer {}
|
||||
|
||||
#[cfg(target_os = "ios")]
|
||||
unsafe impl Sync for IOSMetalLayer {}
|
||||
|
||||
/// The capabilities of a surface when used by a physical device.
|
||||
///
|
||||
/// You have to match these capabilities when you create a swapchain.
|
||||
|
Loading…
Reference in New Issue
Block a user