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}