From a1a42c067abde742b886da82afef33bb4c270f69 Mon Sep 17 00:00:00 2001 From: msiglreith Date: Sat, 4 Jul 2020 15:21:39 +0200 Subject: [PATCH] Add interoperability functions for raw-window-handle (#308) * Add ash-window as workspace member * ash-window: fix repository link * ash-window: Address CI fmt&clippy issues * ash-window: Try fix SDL2 CI issues * ash-window: Remove beryllium example --- Cargo.toml | 1 + ash-window/Cargo.toml | 24 +++++ ash-window/Changelog.md | 32 +++++++ ash-window/README.md | 64 +++++++++++++ ash-window/examples/winit.rs | 51 ++++++++++ ash-window/src/lib.rs | 177 +++++++++++++++++++++++++++++++++++ 6 files changed, 349 insertions(+) create mode 100644 ash-window/Cargo.toml create mode 100644 ash-window/Changelog.md create mode 100644 ash-window/README.md create mode 100644 ash-window/examples/winit.rs create mode 100644 ash-window/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 4441300..7c0b5b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,5 +2,6 @@ members = [ "examples", "ash", + "ash-window", "generator", ] diff --git a/ash-window/Cargo.toml b/ash-window/Cargo.toml new file mode 100644 index 0000000..bac2a9f --- /dev/null +++ b/ash-window/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "ash-window" +version = "0.4.1" +authors = ["msiglreith "] +edition = "2018" +license = "MIT OR Apache-2.0" +description = "Interop library between ash and raw-window-handle" +documentation = "https://docs.rs/ash-window" +repository = "https://github.com/MaikKlein/ash" +readme = "README.md" +keywords = ["window", "ash", "graphics"] +categories = ["game-engines", "graphics"] +exclude = [".github/*"] +workspace = ".." + +[dependencies] +ash = { path = "../ash" } +raw-window-handle = "0.3" + +[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies] +raw-window-metal = "0.1" + +[dev-dependencies] +winit = "0.19.4" diff --git a/ash-window/Changelog.md b/ash-window/Changelog.md new file mode 100644 index 0000000..bef8ab6 --- /dev/null +++ b/ash-window/Changelog.md @@ -0,0 +1,32 @@ +### Changelog + +## [Unreleased] + +### Changed +- `impl HasRawWindowHandle` to `dyn HasRawWindowHandle` + +## Version 0.4.1 + +### Changed +- Use `raw-window-metal` to automatically allocate a `CAMetalLayer` if there is none + +## Version 0.4.0 + +### Changed +- Update `ash` version to 0.31 + +## Version 0.3.0 + +### Changed +- Update `ash` version to 0.30 + +## Version 0.2.0 + +### Changed +- `enumerate_required_extension` renamed to `enumerate_required_extensions` +- `enumerate_required_extensions` will return an error if the window handle is not supported instead of panic. +- `enumerate_required_extensions` may return multiple extension names. Includes all dependent extensions. +- `create_surface` will return an error if the window handle is not supported instead of panic. + +## Version 0.1.0 +Initial release for `raw-window-handle = "0.3"` with Windows, Linux, Android, MacOS/iOS support. \ No newline at end of file diff --git a/ash-window/README.md b/ash-window/README.md new file mode 100644 index 0000000..adc1e02 --- /dev/null +++ b/ash-window/README.md @@ -0,0 +1,64 @@ + +

ash-window

+

+ + crates.io + + + docs + + + ci + +
+ + License - MIT + + + License - Apache2 + +

+ +Interoperability between [`ash`](https://github.com/MaikKlein/ash) and [`raw-window-handle`](https://github.com/rust-windowing/raw-window-handle) for surface creation. + +```toml +ash-window = "0.4" +``` + +## Usage + +The library exposes two functions: + +- `enumerate_required_extensions` returns the required instance extensions needed for surface creation from a specific window handle. + +- `create_surface` allows to create a surface from a type implementing `HasRawWindowHandle` + +```rust +ash_window::create_surface(&entry, &instance, &window, None)?; +``` + +## Versions +```toml +ash = "0.31" +raw-window-handle = "0.3" +``` + +## Support + +- [x] Windows (`VK_KHR_win32_surface`) +- [x] Unix (`VK_KHR_xlib_surface`/`VK_KHR_xcb_surface`/`VK_KHR_wayland_surface`) +- [x] MacOS/IOS (`VK_EXT_metal_surface`) +- [x] Android (`VK_KHR_android_surface`) + +## License + +Licensed under either of + +* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) +* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +## Contribution + +Unless you explicitly state otherwise, any Contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. diff --git a/ash-window/examples/winit.rs b/ash-window/examples/winit.rs new file mode 100644 index 0000000..9934757 --- /dev/null +++ b/ash-window/examples/winit.rs @@ -0,0 +1,51 @@ +//! Demonstrate interop with beryllium/SDL windows. +//! +//! Sample creates a surface from a window through the +//! platform agnostic window handle trait. +//! +//! On instance extensions platform specific extensions need to be enabled. + +use ash::{version::EntryV1_0, vk}; +use std::error::Error; + +fn main() -> Result<(), Box> { + let mut events_loop = winit::EventsLoop::new(); + let window = winit::WindowBuilder::new() + .with_dimensions((800, 600).into()) + .build(&events_loop)?; + + unsafe { + let entry = ash::Entry::new()?; + let surface_extensions = ash_window::enumerate_required_extensions(&window)?; + let instance_extensions = surface_extensions + .iter() + .map(|ext| ext.as_ptr()) + .collect::>(); + let app_desc = vk::ApplicationInfo::builder().api_version(vk::make_version(1, 0, 0)); + let instance_desc = vk::InstanceCreateInfo::builder() + .application_info(&app_desc) + .enabled_extension_names(&instance_extensions); + + let instance = entry.create_instance(&instance_desc, None)?; + + // Create a surface from winit window. + let surface = ash_window::create_surface(&entry, &instance, &window, None)?; + let surface_fn = ash::extensions::khr::Surface::new(&entry, &instance); + println!("surface: {:?}", surface); + + let mut running = true; + while running { + events_loop.poll_events(|event| match event { + winit::Event::WindowEvent { + event: winit::WindowEvent::CloseRequested, + .. + } => running = false, + _ => (), + }); + } + + surface_fn.destroy_surface(surface, None); + } + + Ok(()) +} diff --git a/ash-window/src/lib.rs b/ash-window/src/lib.rs new file mode 100644 index 0000000..66d6a57 --- /dev/null +++ b/ash-window/src/lib.rs @@ -0,0 +1,177 @@ +use ash::{ + extensions::khr, + prelude::*, + version::{EntryV1_0, InstanceV1_0}, + vk, +}; +use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; +use std::ffi::CStr; + +#[cfg(any(target_os = "macos", target_os = "ios"))] +use ash::extensions::ext; // portability extensions + +/// Create a surface from a raw surface handle. +/// +/// `instance` must have created with platform specific surface extensions enabled. +/// +/// # Safety +/// +/// In order for the created `SurfaceKHR` to be valid for the duration of its +/// usage, the `Instance` this was called on must be dropped later than the +/// resulting `SurfaceKHR`. +pub unsafe fn create_surface( + entry: &E, + instance: &I, + window_handle: &dyn HasRawWindowHandle, + allocation_callbacks: Option<&vk::AllocationCallbacks>, +) -> VkResult +where + E: EntryV1_0, + I: InstanceV1_0, +{ + match window_handle.raw_window_handle() { + #[cfg(target_os = "windows")] + RawWindowHandle::Windows(handle) => { + let surface_desc = vk::Win32SurfaceCreateInfoKHR::builder() + .hinstance(handle.hinstance) + .hwnd(handle.hwnd); + let surface_fn = khr::Win32Surface::new(entry, instance); + surface_fn.create_win32_surface(&surface_desc, allocation_callbacks) + } + + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + RawWindowHandle::Wayland(handle) => { + let surface_desc = vk::WaylandSurfaceCreateInfoKHR::builder() + .display(handle.display) + .surface(handle.surface); + let surface_fn = khr::WaylandSurface::new(entry, instance); + surface_fn.create_wayland_surface(&surface_desc, allocation_callbacks) + } + + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + RawWindowHandle::Xlib(handle) => { + let surface_desc = vk::XlibSurfaceCreateInfoKHR::builder() + .dpy(handle.display as *mut _) + .window(handle.window); + let surface_fn = khr::XlibSurface::new(entry, instance); + surface_fn.create_xlib_surface(&surface_desc, allocation_callbacks) + } + + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + RawWindowHandle::Xcb(handle) => { + let surface_desc = vk::XcbSurfaceCreateInfoKHR::builder() + .connection(handle.connection as *mut _) + .window(handle.window); + let surface_fn = khr::XcbSurface::new(entry, instance); + surface_fn.create_xcb_surface(&surface_desc, allocation_callbacks) + } + + #[cfg(any(target_os = "android"))] + RawWindowHandle::Android(handle) => { + let surface_desc = + vk::AndroidSurfaceCreateInfoKHR::builder().window(handle.a_native_window as _); + let surface_fn = khr::AndroidSurface::new(entry, instance); + surface_fn.create_android_surface(&surface_desc, allocation_callbacks) + } + + #[cfg(any(target_os = "macos"))] + RawWindowHandle::MacOS(handle) => { + use raw_window_metal::{macos, Layer}; + + let layer = match macos::metal_layer_from_handle(handle) { + Layer::Existing(layer) | Layer::Allocated(layer) => layer as *mut _, + Layer::None => return Err(vk::Result::ERROR_INITIALIZATION_FAILED), + }; + + let surface_desc = vk::MetalSurfaceCreateInfoEXT::builder().layer(&*layer); + let surface_fn = ext::MetalSurface::new(entry, instance); + surface_fn.create_metal_surface(&surface_desc, allocation_callbacks) + } + + #[cfg(any(target_os = "ios"))] + RawWindowHandle::IOS(handle) => { + use raw_window_metal::{ios, Layer}; + + let layer = match ios::metal_layer_from_handle(handle) { + Layer::Existing(layer) | Layer::Allocated(layer) => layer as *mut _, + Layer::None => return Err(vk::Result::ERROR_INITIALIZATION_FAILED), + }; + + let surface_desc = vk::MetalSurfaceCreateInfoEXT::builder().layer(&*layer); + let surface_fn = ext::MetalSurface::new(entry, instance); + surface_fn.create_metal_surface(&surface_desc, allocation_callbacks) + } + + _ => Err(vk::Result::ERROR_EXTENSION_NOT_PRESENT), // not supported + } +} + +/// Query the required instance extensions for creating a surface from a window handle. +/// +/// The returned extensions will include all extension dependencies. +pub fn enumerate_required_extensions( + window_handle: &dyn HasRawWindowHandle, +) -> VkResult> { + let extensions = match window_handle.raw_window_handle() { + #[cfg(target_os = "windows")] + RawWindowHandle::Windows(_) => vec![khr::Surface::name(), khr::Win32Surface::name()], + + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + RawWindowHandle::Wayland(_) => vec![khr::Surface::name(), khr::WaylandSurface::name()], + + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + RawWindowHandle::Xlib(_) => vec![khr::Surface::name(), khr::XlibSurface::name()], + + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + RawWindowHandle::Xcb(_) => vec![khr::Surface::name(), khr::XcbSurface::name()], + + #[cfg(any(target_os = "android"))] + RawWindowHandle::Android(_) => vec![khr::Surface::name(), khr::AndroidSurface::name()], + + #[cfg(any(target_os = "macos"))] + RawWindowHandle::MacOS(_) => vec![khr::Surface::name(), ext::MetalSurface::name()], + + #[cfg(any(target_os = "ios"))] + RawWindowHandle::IOS(_) => vec![khr::Surface::name(), ext::MetalSurface::name()], + + _ => return Err(vk::Result::ERROR_EXTENSION_NOT_PRESENT), + }; + + Ok(extensions) +}