vulkano_win/
winit.rs

1use std::{
2    error::Error,
3    fmt::{Display, Error as FmtError, Formatter},
4    sync::Arc,
5};
6use vulkano::{
7    instance::{Instance, InstanceExtensions},
8    swapchain::Surface,
9    Validated, VulkanError, VulkanLibrary,
10};
11use winit::{
12    error::OsError as WindowCreationError,
13    event_loop::EventLoopWindowTarget,
14    window::{Window, WindowBuilder},
15};
16
17pub fn required_extensions(library: &VulkanLibrary) -> InstanceExtensions {
18    let ideal = InstanceExtensions {
19        khr_surface: true,
20        khr_xlib_surface: true,
21        khr_xcb_surface: true,
22        khr_wayland_surface: true,
23        khr_android_surface: true,
24        khr_win32_surface: true,
25        mvk_ios_surface: true,
26        mvk_macos_surface: true,
27        khr_get_physical_device_properties2: true,
28        khr_get_surface_capabilities2: true,
29        ..InstanceExtensions::empty()
30    };
31
32    library.supported_extensions().intersection(&ideal)
33}
34
35/// Create a surface from a Winit window or a reference to it. The surface takes `W` to prevent it
36/// from being dropped before the surface.
37pub fn create_surface_from_winit(
38    window: Arc<Window>,
39    instance: Arc<Instance>,
40) -> Result<Arc<Surface>, Validated<VulkanError>> {
41    unsafe { winit_to_surface(instance, window) }
42}
43
44pub trait VkSurfaceBuild<E> {
45    fn build_vk_surface(
46        self,
47        event_loop: &EventLoopWindowTarget<E>,
48        instance: Arc<Instance>,
49    ) -> Result<Arc<Surface>, CreationError>;
50}
51
52impl<E> VkSurfaceBuild<E> for WindowBuilder {
53    fn build_vk_surface(
54        self,
55        event_loop: &EventLoopWindowTarget<E>,
56        instance: Arc<Instance>,
57    ) -> Result<Arc<Surface>, CreationError> {
58        let window = Arc::new(self.build(event_loop)?);
59
60        Ok(create_surface_from_winit(window, instance)?)
61    }
62}
63
64/// Error that can happen when creating a window.
65#[derive(Debug)]
66pub enum CreationError {
67    /// Error when creating the surface.
68    Surface(Validated<VulkanError>),
69
70    /// Error when creating the window.
71    Window(WindowCreationError),
72}
73
74impl Error for CreationError {
75    fn source(&self) -> Option<&(dyn Error + 'static)> {
76        match self {
77            CreationError::Surface(err) => Some(err),
78            CreationError::Window(err) => Some(err),
79        }
80    }
81}
82
83impl Display for CreationError {
84    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
85        write!(
86            f,
87            "{}",
88            match self {
89                CreationError::Surface(_) => "error while creating the surface",
90                CreationError::Window(_) => "error while creating the window",
91            }
92        )
93    }
94}
95
96impl From<Validated<VulkanError>> for CreationError {
97    fn from(err: Validated<VulkanError>) -> CreationError {
98        CreationError::Surface(err)
99    }
100}
101
102impl From<WindowCreationError> for CreationError {
103    fn from(err: WindowCreationError) -> CreationError {
104        CreationError::Window(err)
105    }
106}
107
108#[cfg(target_os = "android")]
109unsafe fn winit_to_surface(
110    instance: Arc<Instance>,
111    window: Arc<Window>,
112) -> Result<Arc<Surface>, Validated<VulkanError>> {
113    use raw_window_handle::HasRawWindowHandle;
114    use raw_window_handle::RawWindowHandle::AndroidNdk;
115    if let AndroidNdk(handle) = window.raw_window_handle() {
116        Surface::from_android(instance, handle.a_native_window, Some(window))
117    } else {
118        unreachable!("This should be unreachable if the target is android");
119    }
120}
121
122#[cfg(all(
123    unix,
124    not(target_os = "android"),
125    not(target_os = "macos"),
126    not(target_os = "ios")
127))]
128unsafe fn winit_to_surface(
129    instance: Arc<Instance>,
130    window: Arc<Window>,
131) -> Result<Arc<Surface>, Validated<VulkanError>> {
132    use winit::platform::{wayland::WindowExtWayland, x11::WindowExtX11};
133
134    match (window.wayland_display(), window.wayland_surface()) {
135        (Some(display), Some(surface)) => {
136            Surface::from_wayland(instance, display, surface, Some(window))
137        }
138        _ => {
139            // No wayland display found, check if we can use xlib.
140            // If not, we use xcb.
141            if instance.enabled_extensions().khr_xlib_surface {
142                Surface::from_xlib(
143                    instance,
144                    window.xlib_display().unwrap(),
145                    window.xlib_window().unwrap() as _,
146                    Some(window),
147                )
148            } else {
149                Surface::from_xcb(
150                    instance,
151                    window.xcb_connection().unwrap(),
152                    window.xlib_window().unwrap() as _,
153                    Some(window),
154                )
155            }
156        }
157    }
158}
159
160#[cfg(any(target_os = "macos", target_os = "ios"))]
161use objc::{class, msg_send, runtime::Object, sel, sel_impl};
162
163/// Get (and set) `CAMetalLayer` to ns_view.
164/// This is necessary to be able to render on Mac.
165#[cfg(target_os = "macos")]
166pub(crate) unsafe fn get_metal_layer_macos(view: *mut std::ffi::c_void) -> *mut Object {
167    use core_graphics_types::base::CGFloat;
168    use objc::runtime::YES;
169    use objc::runtime::{BOOL, NO};
170
171    let view: *mut Object = std::mem::transmute(view);
172    let main_layer: *mut Object = msg_send![view, layer];
173    let class = class!(CAMetalLayer);
174    let is_valid_layer: BOOL = msg_send![main_layer, isKindOfClass: class];
175    if is_valid_layer == NO {
176        let new_layer: *mut Object = msg_send![class, new];
177        let () = msg_send![new_layer, setEdgeAntialiasingMask: 0];
178        let () = msg_send![new_layer, setPresentsWithTransaction: false];
179        let () = msg_send![new_layer, removeAllAnimations];
180        let () = msg_send![view, setLayer: new_layer];
181        let () = msg_send![view, setWantsLayer: YES];
182        let window: *mut Object = msg_send![view, window];
183        if !window.is_null() {
184            let scale_factor: CGFloat = msg_send![window, backingScaleFactor];
185            let () = msg_send![new_layer, setContentsScale: scale_factor];
186        }
187        new_layer
188    } else {
189        main_layer
190    }
191}
192
193#[cfg(target_os = "macos")]
194unsafe fn winit_to_surface(
195    instance: Arc<Instance>,
196    window: Arc<Window>,
197) -> Result<Arc<Surface>, Validated<VulkanError>> {
198    use winit::platform::macos::WindowExtMacOS;
199    let layer = get_metal_layer_macos(window.ns_view());
200    Surface::from_mac_os(instance, layer as *const (), Some(window))
201}
202
203#[cfg(target_os = "ios")]
204use vulkano::swapchain::IOSMetalLayer;
205
206/// Get sublayer from iOS main view (ui_view). The sublayer is created as CAMetalLayer
207#[cfg(target_os = "ios")]
208pub(crate) unsafe fn get_metal_layer_ios(view: *mut std::ffi::c_void) -> IOSMetalLayer {
209    use core_graphics_types::{base::CGFloat, geometry::CGRect};
210
211    let view: *mut Object = std::mem::transmute(view);
212    let main_layer: *mut Object = msg_send![view, layer];
213    let class = class!(CAMetalLayer);
214    let new_layer: *mut Object = msg_send![class, new];
215    let frame: CGRect = msg_send![main_layer, bounds];
216    let () = msg_send![new_layer, setFrame: frame];
217    let () = msg_send![main_layer, addSublayer: new_layer];
218    let screen: *mut Object = msg_send![class!(UIScreen), mainScreen];
219    let scale_factor: CGFloat = msg_send![screen, nativeScale];
220    let () = msg_send![view, setContentScaleFactor: scale_factor];
221    IOSMetalLayer::new(view, new_layer)
222}
223
224#[cfg(target_os = "ios")]
225unsafe fn winit_to_surface(
226    instance: Arc<Instance>,
227    window: Arc<Window>,
228) -> Result<Arc<Surface>, Validated<VulkanError>> {
229    use winit::platform::ios::WindowExtIOS;
230    let layer = get_metal_layer_ios(window.ui_view());
231    Surface::from_ios(instance, layer, Some(window))
232}
233
234#[cfg(target_os = "windows")]
235unsafe fn winit_to_surface(
236    instance: Arc<Instance>,
237    window: Arc<Window>,
238) -> Result<Arc<Surface>, Validated<VulkanError>> {
239    use winit::platform::windows::WindowExtWindows;
240
241    Surface::from_win32(
242        instance,
243        window.hinstance() as *const (),
244        window.hwnd() as *const (),
245        Some(window),
246    )
247}
248
249#[cfg(target_os = "windows")]
250use vulkano::swapchain::Win32Monitor;
251#[cfg(target_os = "windows")]
252use winit::{monitor::MonitorHandle, platform::windows::MonitorHandleExtWindows};
253
254#[cfg(target_os = "windows")]
255/// Creates a `Win32Monitor` from a Winit monitor handle.
256#[inline]
257pub fn create_win32_monitor_from_winit(monitor_handle: &MonitorHandle) -> Win32Monitor {
258    unsafe { Win32Monitor::new(monitor_handle.hmonitor() as *const ()) }
259}