Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Ios fix #1950

Merged
merged 7 commits into from
Aug 11, 2022
Merged

Ios fix #1950

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
-->
- **Breaking** Public dependency updates:
- Winit 0.27
- raw-window-handle 0.5
- **Breaking** Changes to `Instance` and Vulkan initialization:
- `FunctionPointers` is renamed to `VulkanLibrary`, and now resides in a separate `library` module. It is re-exported from the crate root.
- The `Loader` trait is now in the `library` module.
Expand Down Expand Up @@ -36,6 +37,12 @@
- **Breaking** Changes to memory pools:
- Renamed `StdMemoryPool[Alloc]`, `StdHostVisibleMemoryTypePool[Alloc]`, `StdNonHostVisibleMemoryTypePool[Alloc]` to `Standard{...}`.
- Removed `Device::standard_pool` in favor of `Device::standard_memory_pool`, which returns `&Arc<StandardMemoryPool>`.
- **Potentially Breaking** Fix iOS compilation:
- Removed dependency to `cocoa` and `metal`
- Fixed iOS compilation errors
- Added `winit_to_surface` method for iOS, ensuring we can draw to a sub `CAMetalLayer` layer
- Added `Surface::update_ios_sublayer_on_resize` to ensure iOS sublayer is fullscreen if initial window size was not the same as device's
- Ensure both iOS and MacOS have `CAMetalLayer` when using `create_surface_from_handle`
- Bugs fixed:
- [#1896](https://github.com/vulkano-rs/vulkano/issues/1896): Vulkano-shaders generates invalid struct definitions when struct field names are stripped out by the compiler.

Expand Down
4 changes: 4 additions & 0 deletions vulkano-util/src/renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,10 @@ 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;
}
}
11 changes: 5 additions & 6 deletions vulkano-win/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,14 @@ categories = ["rendering::graphics-api"]

[features]
default = ["winit_", "raw-window-handle_"]
winit_ = ["winit", "metal", "cocoa", "objc"]
winit_ = ["winit", "objc", "core-graphics-types"]
raw-window-handle_ = ["raw-window-handle"]

[dependencies]
raw-window-handle = { version = "0.4", optional = true }
raw-window-handle = { version = "0.5", optional = true }
vulkano = { version = "0.30.0", path = "../vulkano" }
winit = { version = "0.27", optional = true }

[target.'cfg(target_os = "macos")'.dependencies]
cocoa = { version = "0.24", optional = true }
metal = { version = "0.23", optional = true }
objc = { version = "0.2", 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 }
61 changes: 51 additions & 10 deletions vulkano-win/src/raw_window_handle.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,75 @@
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
#[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,
};
use std::sync::Arc;
use vulkano::instance::Instance;
use vulkano::swapchain::Surface;
use vulkano::swapchain::SurfaceCreationError;

/// Creates a vulkan surface from a generic window
/// which implements HasRawWindowHandle and thus can reveal the os-dependent handle.
/// - Note that if you wish to use this function with MacOS, you will need to ensure that the
/// `CAMetalLayer` is set to the ns_view. An example of how one might do that can be found in
/// `vulkano_win::set_ca_metal_layer_to_winit`
pub fn create_surface_from_handle<W>(
window: W,
instance: Arc<Instance>,
) -> Result<Arc<Surface<W>>, SurfaceCreationError>
where
W: HasRawWindowHandle,
W: HasRawWindowHandle + HasRawDisplayHandle,
{
unsafe {
match window.raw_window_handle() {
RawWindowHandle::AndroidNdk(h) => {
Surface::from_android(instance, h.a_native_window, window)
}
RawWindowHandle::UiKit(h) => Surface::from_ios(instance, h.ui_view, window),
RawWindowHandle::AppKit(h) => Surface::from_mac_os(instance, h.ns_view, window),
RawWindowHandle::UiKit(_h) => {
#[cfg(target_os = "ios")]
{
// Ensure the layer is CAMetalLayer
let layer = get_metal_layer_ios(_h.ui_view);
Surface::from_ios(instance, layer, window)
}
#[cfg(not(target_os = "ios"))]
{
panic!("UiKit handle should only be used when target_os == 'ios'");
}
}
RawWindowHandle::AppKit(_h) => {
#[cfg(target_os = "macos")]
{
// Ensure the layer is CAMetalLayer
let layer = get_metal_layer_macos(_h.ns_view);
Surface::from_mac_os(instance, layer as *const (), window)
}
#[cfg(not(target_os = "macos"))]
{
panic!("AppKit handle should only be used when target_os == 'macos'");
}
}
RawWindowHandle::Wayland(h) => {
Surface::from_wayland(instance, h.display, h.surface, window)
let d = match window.raw_display_handle() {
RawDisplayHandle::Wayland(d) => d,
_ => panic!("Invalid RawDisplayHandle"),
};
Surface::from_wayland(instance, d.display, h.surface, window)
}
RawWindowHandle::Win32(h) => Surface::from_win32(instance, h.hinstance, h.hwnd, window),
RawWindowHandle::Xcb(h) => Surface::from_xcb(instance, h.connection, h.window, window),
RawWindowHandle::Xlib(h) => Surface::from_xlib(instance, h.display, h.window, window),
RawWindowHandle::Xcb(h) => {
let d = match window.raw_display_handle() {
RawDisplayHandle::Xcb(d) => d,
_ => panic!("Invalid RawDisplayHandle"),
};
Surface::from_xcb(instance, d.connection, h.window, window)
}
RawWindowHandle::Xlib(h) => {
let d = match window.raw_display_handle() {
RawDisplayHandle::Xlib(d) => d,
_ => panic!("Invalid RawDisplayHandle"),
};
Surface::from_xlib(instance, d.display, h.window, window)
}
RawWindowHandle::Web(_) => unimplemented!(),
_ => unimplemented!(),
}
Expand Down
94 changes: 65 additions & 29 deletions vulkano-win/src/winit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,12 @@ unsafe fn winit_to_surface<W: SafeBorrow<Window>>(
}
}

#[cfg(all(unix, not(target_os = "android"), not(target_os = "macos")))]
#[cfg(all(
unix,
not(target_os = "android"),
not(target_os = "macos"),
not(target_os = "ios")
))]
unsafe fn winit_to_surface<W: SafeBorrow<Window>>(
instance: Arc<Instance>,
win: W,
Expand Down Expand Up @@ -157,36 +162,37 @@ unsafe fn winit_to_surface<W: SafeBorrow<Window>>(
}
}

#[cfg(target_os = "macos")]
use cocoa::{
appkit::{NSView, NSWindow},
base::id as cocoa_id,
};
#[cfg(target_os = "macos")]
use metal::MetalLayer;
#[cfg(target_os = "macos")]
use objc::runtime::YES;
#[cfg(target_os = "macos")]
use std::mem;
#[cfg(any(target_os = "macos", target_os = "ios"))]
use objc::{class, msg_send, runtime::Object, sel, sel_impl};

/// Ensure `CAMetalLayer` (native rendering surface on MacOs) is used by the ns_view.
/// Get (and set) `CAMetalLayer` to ns_view.
/// This is necessary to be able to render on Mac.
#[cfg(target_os = "macos")]
unsafe fn set_ca_metal_layer_to_winit<W: SafeBorrow<Window>>(win: W) {
use winit::platform::macos::WindowExtMacOS;
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 wnd: cocoa_id = mem::transmute(win.borrow().ns_window());
let layer = MetalLayer::new();

layer.set_edge_antialiasing_mask(0);
layer.set_presents_with_transaction(false);
layer.remove_all_animations();

let view = wnd.contentView();

layer.set_contents_scale(view.backingScaleFactor());
view.setLayer(mem::transmute(layer.as_ref())); // Bombs here with out of memory
view.setWantsLayer(YES);
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];
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added these just to make sure we're doing the same function calls master version is doing. Though I am unsure if these are necessary.

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")]
Expand All @@ -195,9 +201,39 @@ unsafe fn winit_to_surface<W: SafeBorrow<Window>>(
win: W,
) -> Result<Arc<Surface<W>>, SurfaceCreationError> {
use winit::platform::macos::WindowExtMacOS;
let layer = get_metal_layer_macos(win.borrow().ns_view());
Surface::from_mac_os(instance, layer as *const (), win)
}

#[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)
}

set_ca_metal_layer_to_winit(win.borrow());
Surface::from_mac_os(instance, win.borrow().ns_view() as *const (), win)
#[cfg(target_os = "ios")]
unsafe fn winit_to_surface<W: SafeBorrow<Window>>(
instance: Arc<Instance>,
win: W,
) -> Result<Arc<Surface<W>>, SurfaceCreationError> {
use winit::platform::ios::WindowExtIOS;
let layer = get_metal_layer_ios(win.borrow().ui_view());
Surface::from_ios(instance, layer, win)
}

#[cfg(target_os = "windows")]
Expand Down
4 changes: 4 additions & 0 deletions vulkano/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ once_cell = { version = "1.13", features = ["parking_lot"] }
parking_lot = { version = "0.12", features = ["send_guard"] }
smallvec = "1.8"

[target.'cfg(target_os = "ios")'.dependencies]
objc = "0.2.5"
core-graphics-types = "0.1"

[build-dependencies]
heck = "0.4"
indexmap = "1.8"
Expand Down
11 changes: 5 additions & 6 deletions vulkano/src/library.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ impl VulkanLibrary {
pub fn new() -> Result<Arc<Self>, LoadingError> {
#[cfg(target_os = "ios")]
#[allow(non_snake_case)]
fn def_loader_impl() -> Result<Box<Loader>, LoadingError> {
let loader = statically_linked_vulkan_loader!();
fn def_loader_impl() -> Result<Box<dyn Loader>, LoadingError> {
let loader = crate::statically_linked_vulkan_loader!();
Ok(Box::new(loader))
}

Expand Down Expand Up @@ -84,7 +84,6 @@ impl VulkanLibrary {
.get_instance_proc_addr(ash::vk::Instance::null(), name.as_ptr())
.map_or(ptr::null(), |func| func as _)
});

// Per the Vulkan spec:
// If the vkGetInstanceProcAddr returns NULL for vkEnumerateInstanceVersion, it is a
// Vulkan 1.0 implementation. Otherwise, the application can call vkEnumerateInstanceVersion
Expand Down Expand Up @@ -325,12 +324,12 @@ macro_rules! statically_linked_vulkan_loader {

struct StaticallyLinkedVulkanLoader;
unsafe impl Loader for StaticallyLinkedVulkanLoader {
fn get_instance_proc_addr(
unsafe fn get_instance_proc_addr(
&self,
instance: ash::vk::Instance,
name: *const c_char,
) -> extern "system" fn() -> () {
unsafe { vkGetInstanceProcAddr(instance, name) }
) -> ash::vk::PFN_vkVoidFunction {
vkGetInstanceProcAddr(instance, name)
}
}

Expand Down
3 changes: 3 additions & 0 deletions vulkano/src/swapchain/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,9 @@ pub use self::{
SwapchainAcquireFuture, SwapchainCreateInfo, SwapchainCreationError, Win32Monitor,
},
};
#[cfg(target_os = "ios")]
pub use surface::IOSMetalLayer;

use std::sync::atomic::AtomicBool;

pub mod display;
Expand Down
Loading