dacite_winit/
lib.rs

1// Copyright (c) 2017, Dennis Hamester <dennis.hamester@startmail.com>
2//
3// Permission to use, copy, modify, and/or distribute this software for any
4// purpose with or without fee is hereby granted, provided that the above
5// copyright notice and this permission notice appear in all copies.
6//
7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
8// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
9// FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
10// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
11// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
12// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
13// PERFORMANCE OF THIS SOFTWARE.
14
15//! This is a small interoperability library for [dacite] and [winit], which allows the creation of
16//! Vulkan surfaces in an easy and platform-independent manner.
17//!
18//! The main entry point to this crate is the `WindowExt` trait, through which Vulkan surfaces can
19//! be created for winit `Window`s.
20//!
21//! This crate deals only with Vulkan surfaces. `Window` and `EventsLoop` must be created and
22//! managed manually.
23//!
24//! ```rust,no_run
25//! extern crate dacite;
26//! extern crate dacite_winit;
27//! extern crate winit;
28//!
29//! // Import the `WindowExt` trait:
30//! use dacite_winit::WindowExt;
31//!
32//! // Create an `EventsLoop` and a `Window`:
33//! let events_loop = winit::EventsLoop::new();
34//! let window = winit::Window::new(&events_loop).unwrap();
35//!
36//! // Determine required extensions before a Vulkan instance is created:
37//! let required_extensions = match window.get_required_extensions() {
38//!     Ok(required_extensions) => required_extensions,
39//!     Err(error) => {
40//!         // This error can mean either, that the windowing system is not supported, or that
41//!         // a Vulkan error occurred.
42//!
43//!         // Other functions from the `WindowExt` trait can also return errors, which should be
44//!         // handled appropriately.
45//!     }
46//! };
47//!
48//! // Create a Vulkan instance and enable at least the extensions required for the window:
49//! let create_info = dacite::core::InstanceCreateInfo {
50//!     // ...
51//!     enabled_extensions: required_extensions.to_extensions(),
52//!     // ...
53//! };
54//!
55//! let instance = dacite::core::Instance::create(&create_info, None).unwrap();
56//!
57//! // While searching for a suitable physical device, use
58//! // WindowExt::is_presentation_supported() to determine if the physical device has a queue
59//! // family, that can present to the window.
60//! let physical_device = // ...
61//!
62//! // And finally, create a `Surface` from the window:
63//! let surface = window.create_surface(&instance,
64//!                                     dacite_winit::SurfaceCreateFlags::empty(),
65//!                                     None).unwrap();
66//! ```
67//!
68//! [dacite]: https://gitlab.com/dennis-hamester/dacite/tree/master/dacite
69//! [winit]: https://github.com/tomaka/winit
70
71#[macro_use]
72extern crate bitflags;
73
74extern crate dacite;
75extern crate winit;
76
77use dacite::core;
78use dacite::khr_surface;
79use dacite::khr_wayland_surface;
80use dacite::khr_win32_surface;
81use dacite::khr_xlib_surface;
82use dacite::wayland_types;
83use dacite::win32_types;
84use dacite::xlib_types;
85use std::error;
86use std::fmt;
87use winit::Window;
88
89/// Extension trait for Vulkan surface creation.
90pub trait WindowExt {
91    /// Test whether presentation is supported on a physical device.
92    ///
93    /// This function first determines the correct Vulkan WSI extension for this window and then calls one of the
94    /// `get_*_presentation_support_*` family of functions on the `PhysicalDevice`.
95    fn is_presentation_supported(&self, physical_device: &core::PhysicalDevice, queue_family_indices: u32) -> Result<bool, Error>;
96
97    /// Determine required Vulkan instance extensions.
98    ///
99    /// This will always include [`VK_KHR_surface`]. One of the platform-dependent WSI extensions,
100    /// that corresponds to this window, will also be added.
101    ///
102    /// Please note, that the device extension [`VK_KHR_swapchain`] is also required for
103    /// presentation.
104    ///
105    /// [`VK_KHR_surface`]: https://www.khronos.org/registry/vulkan/specs/1.0-extensions/html/vkspec.html#VK_KHR_surface
106    /// [`VK_KHR_swapchain`]: https://www.khronos.org/registry/vulkan/specs/1.0-extensions/html/vkspec.html#VK_KHR_swapchain
107    fn get_required_extensions(&self) -> Result<core::InstanceExtensionsProperties, Error>;
108
109    /// Create a surface for this window.
110    ///
111    /// `Instance` must have been created with required extensions, as determined by
112    /// `get_required_extensions()`. The `flags` parameter is currently just a place holder. You
113    /// should specify `SurfaceCreateFlags::empty()` here.
114    fn create_surface(&self, instance: &core::Instance, flags: SurfaceCreateFlags, allocator: Option<Box<core::Allocator>>) -> Result<khr_surface::SurfaceKhr, Error>;
115}
116
117impl WindowExt for Window {
118    fn is_presentation_supported(&self, physical_device: &core::PhysicalDevice, queue_family_indices: u32) -> Result<bool, Error> {
119        let backend = get_backend(self)?;
120
121        match backend {
122            Backend::Xlib { .. } => Ok(true), // FIXME: This needs a VisualID, which winit does not expose
123            Backend::Wayland { display, .. } => Ok(physical_device.get_wayland_presentation_support_khr(queue_family_indices, display)),
124            Backend::Win32 { .. } => Ok(physical_device.get_win32_presentation_support_khr(queue_family_indices)),
125        }
126    }
127
128    fn get_required_extensions(&self) -> Result<core::InstanceExtensionsProperties, Error> {
129        let backend = get_backend(self)?;
130
131        let mut extensions = core::InstanceExtensionsProperties::new();
132        extensions.add_khr_surface(25);
133
134        match backend {
135            Backend::Xlib { .. } => extensions.add_khr_xlib_surface(6),
136            Backend::Wayland { .. } => extensions.add_khr_wayland_surface(5),
137            Backend::Win32 { .. } => extensions.add_khr_win32_surface(5),
138        };
139
140        Ok(extensions)
141    }
142
143    fn create_surface(&self, instance: &core::Instance, _: SurfaceCreateFlags, allocator: Option<Box<core::Allocator>>) -> Result<khr_surface::SurfaceKhr, Error> {
144        let backend = get_backend(self)?;
145
146        match backend {
147            Backend::Xlib { display, window } => {
148                let create_info = khr_xlib_surface::XlibSurfaceCreateInfoKhr {
149                    flags: khr_xlib_surface::XlibSurfaceCreateFlagsKhr::empty(),
150                    dpy: display,
151                    window: window,
152                    chain: None,
153                };
154
155                Ok(instance.create_xlib_surface_khr(&create_info, allocator)?)
156            }
157
158            Backend::Wayland { display, surface } => {
159                let create_info = khr_wayland_surface::WaylandSurfaceCreateInfoKhr {
160                    flags: khr_wayland_surface::WaylandSurfaceCreateFlagsKhr::empty(),
161                    display: display,
162                    surface: surface,
163                    chain: None,
164                };
165
166                Ok(instance.create_wayland_surface_khr(&create_info, allocator)?)
167            }
168
169            Backend::Win32 { hinstance, hwnd } => {
170                let create_info = khr_win32_surface::Win32SurfaceCreateInfoKhr {
171                    flags: khr_win32_surface::Win32SurfaceCreateFlagsKhr::empty(),
172                    hinstance: hinstance,
173                    hwnd: hwnd,
174                    chain: None,
175                };
176
177                Ok(instance.create_win32_surface_khr(&create_info, allocator)?)
178            }
179        }
180    }
181}
182
183/// Error type used throughout this crate.
184#[derive(Debug)]
185pub enum Error {
186    /// The windowing system is not supported by either dacite-winit or dacite.
187    Unsupported,
188
189    /// A Vulkan error occurred.
190    VulkanError(core::Error),
191}
192
193impl fmt::Display for Error {
194    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
195        match *self {
196            Error::Unsupported => write!(f, "Unsupported"),
197            Error::VulkanError(e) => write!(f, "VulkanError({})", e),
198        }
199    }
200}
201
202impl error::Error for Error {
203    fn description(&self) -> &str {
204        match *self {
205            Error::Unsupported => "The windowing system is not supported",
206            Error::VulkanError(ref e) => e.description(),
207        }
208    }
209}
210
211impl From<core::Error> for Error {
212    fn from(e: core::Error) -> Self {
213        Error::VulkanError(e)
214    }
215}
216
217bitflags! {
218    /// Flags used for surface creation.
219    ///
220    /// This is currently a placeholder, with no valid flags. Use `SurfaceCreateFlags::empty()`.
221    pub struct SurfaceCreateFlags: u32 {
222        /// Dummy flag
223        ///
224        /// This flag exists just to satisfy the bitflags! macro, which doesn't support empty
225        /// flags. Use `SurfaceCreateFlags::empty()` instead.
226        const SURFACE_CREATE_DUMMY = 0;
227    }
228}
229
230#[allow(dead_code)]
231enum Backend {
232    Xlib {
233        display: *mut xlib_types::Display,
234        window: xlib_types::Window,
235    },
236
237    Wayland {
238        display: *mut wayland_types::wl_display,
239        surface: *mut wayland_types::wl_surface,
240    },
241
242    Win32 {
243        hinstance: win32_types::HINSTANCE,
244        hwnd: win32_types::HWND,
245    },
246}
247
248#[allow(unused_variables)]
249#[allow(unreachable_code)]
250fn get_backend(window: &Window) -> Result<Backend, Error> {
251    #[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
252    {
253        use winit::os::unix::WindowExt;
254
255        if let (Some(display), Some(window)) = (window.get_xlib_display(), window.get_xlib_window()) {
256            return Ok(Backend::Xlib {
257                display: display as _,
258                window: xlib_types::Window(window as _),
259            });
260        }
261
262        if let (Some(display), Some(surface)) = (window.get_wayland_display(), window.get_wayland_surface()) {
263            return Ok(Backend::Wayland {
264                display: display as _,
265                surface: surface as _,
266            });
267        }
268    }
269
270    #[cfg(target_os = "windows")]
271    {
272        use winit::os::windows::WindowExt;
273
274        return Ok(Backend::Win32 {
275            hinstance: ::std::ptr::null_mut(), // FIXME: Need HINSTANCE of the correct module
276            hwnd: window.get_hwnd() as _,
277        });
278    }
279
280    Err(Error::Unsupported)
281}