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
This commit is contained in:
parent
ac4d046d4b
commit
a1a42c067a
|
@ -2,5 +2,6 @@
|
|||
members = [
|
||||
"examples",
|
||||
"ash",
|
||||
"ash-window",
|
||||
"generator",
|
||||
]
|
||||
|
|
24
ash-window/Cargo.toml
Normal file
24
ash-window/Cargo.toml
Normal file
|
@ -0,0 +1,24 @@
|
|||
[package]
|
||||
name = "ash-window"
|
||||
version = "0.4.1"
|
||||
authors = ["msiglreith <m.siglreith@gmail.com>"]
|
||||
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"
|
32
ash-window/Changelog.md
Normal file
32
ash-window/Changelog.md
Normal file
|
@ -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.
|
64
ash-window/README.md
Normal file
64
ash-window/README.md
Normal file
|
@ -0,0 +1,64 @@
|
|||
|
||||
<h1 align="center">ash-window</h1>
|
||||
<p align="center">
|
||||
<a href="https://crates.io/crates/ash-window">
|
||||
<img src="https://img.shields.io/crates/v/ash-window?style=flat-square" alt = "crates.io">
|
||||
</a>
|
||||
<a href="https://docs.rs/ash-window">
|
||||
<img src="https://docs.rs/ash-window/badge.svg?style=flat-square" alt="docs">
|
||||
</a>
|
||||
<a href="https://github.com/MaikKlein/ash/actions">
|
||||
<img src="https://github.com/MaikKlein/ash/workflows/CI/badge.svg?style=flat" alt="ci">
|
||||
</a>
|
||||
<br>
|
||||
<a href="LICENSE-MIT">
|
||||
<img src="https://img.shields.io/badge/license-MIT-green.svg?style=flat-square" alt="License - MIT">
|
||||
</a>
|
||||
<a href="LICENSE-APACHE">
|
||||
<img src="https://img.shields.io/badge/license-APACHE2-green.svg?style=flat-square" alt="License - Apache2">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
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.
|
51
ash-window/examples/winit.rs
Normal file
51
ash-window/examples/winit.rs
Normal file
|
@ -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<dyn Error>> {
|
||||
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::<Vec<_>>();
|
||||
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(())
|
||||
}
|
177
ash-window/src/lib.rs
Normal file
177
ash-window/src/lib.rs
Normal file
|
@ -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<E, I>(
|
||||
entry: &E,
|
||||
instance: &I,
|
||||
window_handle: &dyn HasRawWindowHandle,
|
||||
allocation_callbacks: Option<&vk::AllocationCallbacks>,
|
||||
) -> VkResult<vk::SurfaceKHR>
|
||||
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<Vec<&'static CStr>> {
|
||||
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)
|
||||
}
|
Loading…
Reference in a new issue