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
35pub 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#[derive(Debug)]
66pub enum CreationError {
67 Surface(Validated<VulkanError>),
69
70 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 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#[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#[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#[inline]
257pub fn create_win32_monitor_from_winit(monitor_handle: &MonitorHandle) -> Win32Monitor {
258 unsafe { Win32Monitor::new(monitor_handle.hmonitor() as *const ()) }
259}