From 0be08efbe7572fd4f2cfff3970a41e0cdbbb72ed Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Wed, 11 Oct 2023 18:51:11 +0200 Subject: [PATCH] ash-window: Upgrade to raw-window-handle 0.6.0 This is the successor to #795. While it doesn't tackle things like backwards-compatibility nor use of the new lifetimed handles (if we _can_ even come up with a safe abstraction for them - it's very likely out of scope!), it includes the following improvements: - Upgrade `raw-window-metal` via [#8] (Mac/iOS wasn't tested in our CI); - Fix Windows platform types (in the `ash` crate) to be `isize` instead of `*const c_void`, matching the usptream definition; - Update example code (impossible until `winit` with `raw-window-handle 0.6` becomes available). [#8]: https://github.com/norse-rs/raw-window-metal/pull/8 --- ash-examples/Cargo.toml | 3 +- ash-examples/src/bin/texture.rs | 9 ++- ash-examples/src/bin/triangle.rs | 9 ++- ash-examples/src/lib.rs | 99 +++++++++++++++----------------- ash-window/Cargo.toml | 6 +- ash-window/Changelog.md | 1 + ash-window/examples/winit.rs | 30 +++++----- ash-window/src/lib.rs | 93 ++++++++++++++++++++++++++---- 8 files changed, 161 insertions(+), 89 deletions(-) diff --git a/ash-examples/Cargo.toml b/ash-examples/Cargo.toml index 853d7ab5e..b74e1d9ad 100644 --- a/ash-examples/Cargo.toml +++ b/ash-examples/Cargo.toml @@ -7,8 +7,7 @@ publish = false [dependencies] image = "0.24" -raw-window-handle = "0.5" -winit = "0.28.0" +winit = { version = "0.29", features = ["rwh_06"] } # The examples require the validation layers, which means the SDK or # equivalent development packages should be present, so we can link # directly and benefit from the infallible `Entry` constructor. diff --git a/ash-examples/src/bin/texture.rs b/ash-examples/src/bin/texture.rs index 45b1803f4..981dde757 100644 --- a/ash-examples/src/bin/texture.rs +++ b/ash-examples/src/bin/texture.rs @@ -1,6 +1,7 @@ #![warn(unused_qualifications)] use std::default::Default; +use std::error::Error; use std::ffi::CStr; use std::io::Cursor; use std::mem; @@ -24,9 +25,9 @@ pub struct Vector3 { pub _pad: f32, } -fn main() { +fn main() -> Result<(), Box> { unsafe { - let base = ExampleBase::new(1920, 1080); + let base = ExampleBase::new(1920, 1080)?; let renderpass_attachments = [ vk::AttachmentDescription { @@ -686,7 +687,7 @@ fn main() { let graphic_pipeline = graphics_pipelines[0]; - base.render_loop(|| { + let _ = base.render_loop(|| { let (present_index, _) = base .swapchain_loader .acquire_next_image( @@ -813,5 +814,7 @@ fn main() { base.device.destroy_framebuffer(framebuffer, None); } base.device.destroy_render_pass(renderpass, None); + + Ok(()) } } diff --git a/ash-examples/src/bin/triangle.rs b/ash-examples/src/bin/triangle.rs index 318a6cf4c..b1ee237e2 100644 --- a/ash-examples/src/bin/triangle.rs +++ b/ash-examples/src/bin/triangle.rs @@ -1,6 +1,7 @@ #![warn(unused_qualifications)] use std::default::Default; +use std::error::Error; use std::ffi::CStr; use std::io::Cursor; use std::mem; @@ -15,9 +16,9 @@ struct Vertex { color: [f32; 4], } -fn main() { +fn main() -> Result<(), Box> { unsafe { - let base = ExampleBase::new(1920, 1080); + let base = ExampleBase::new(1920, 1080)?; let renderpass_attachments = [ vk::AttachmentDescription { format: base.surface_format.format, @@ -348,7 +349,7 @@ fn main() { let graphic_pipeline = graphics_pipelines[0]; - base.render_loop(|| { + let _ = base.render_loop(|| { let (present_index, _) = base .swapchain_loader .acquire_next_image( @@ -455,4 +456,6 @@ fn main() { } base.device.destroy_render_pass(renderpass, None); } + + Ok(()) } diff --git a/ash-examples/src/lib.rs b/ash-examples/src/lib.rs index 5715ad457..5ad9dac43 100644 --- a/ash-examples/src/lib.rs +++ b/ash-examples/src/lib.rs @@ -7,29 +7,24 @@ unused_qualifications )] -use ash::extensions::{ - ext::DebugUtils, - khr::{Surface, Swapchain}, -}; -use ash::{vk, Entry}; -pub use ash::{Device, Instance}; -use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle}; -use std::borrow::Cow; -use std::cell::RefCell; -use std::default::Default; -use std::ffi::CStr; -use std::ops::Drop; -use std::os::raw::c_char; - -#[cfg(any(target_os = "macos", target_os = "ios"))] -use ash::vk::{ - KhrGetPhysicalDeviceProperties2Fn, KhrPortabilityEnumerationFn, KhrPortabilitySubsetFn, +use std::{ + borrow::Cow, cell::RefCell, default::Default, error::Error, ffi::CStr, ops::Drop, + os::raw::c_char, }; +use ash::{ + extensions::{ + ext::DebugUtils, + khr::{Surface, Swapchain}, + }, + vk, Device, Entry, Instance, +}; use winit::{ - event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}, + event::{ElementState, Event, KeyEvent, WindowEvent}, event_loop::{ControlFlow, EventLoop}, - platform::run_return::EventLoopExtRunReturn, + keyboard::{Key, NamedKey}, + platform::run_on_demand::EventLoopExtRunOnDemand, + raw_window_handle::{HasDisplayHandle, HasWindowHandle}, window::WindowBuilder, }; @@ -182,35 +177,35 @@ pub struct ExampleBase { } impl ExampleBase { - pub fn render_loop(&self, f: F) { - self.event_loop - .borrow_mut() - .run_return(|event, _, control_flow| { - *control_flow = ControlFlow::Poll; - match event { - Event::WindowEvent { - event: - WindowEvent::CloseRequested - | WindowEvent::KeyboardInput { - input: - KeyboardInput { - state: ElementState::Pressed, - virtual_keycode: Some(VirtualKeyCode::Escape), - .. - }, - .. - }, - .. - } => *control_flow = ControlFlow::Exit, - Event::MainEventsCleared => f(), - _ => (), + pub fn render_loop(&self, f: F) -> Result<(), impl Error> { + self.event_loop.borrow_mut().run_on_demand(|event, elwp| { + elwp.set_control_flow(ControlFlow::Poll); + match event { + Event::WindowEvent { + event: + WindowEvent::CloseRequested + | WindowEvent::KeyboardInput { + event: + KeyEvent { + state: ElementState::Pressed, + logical_key: Key::Named(NamedKey::Escape), + .. + }, + .. + }, + .. + } => { + elwp.exit(); } - }); + Event::AboutToWait => f(), + _ => (), + } + }) } - pub fn new(window_width: u32, window_height: u32) -> Self { + pub fn new(window_width: u32, window_height: u32) -> Result> { unsafe { - let event_loop = EventLoop::new(); + let event_loop = EventLoop::new()?; let window = WindowBuilder::new() .with_title("Ash - Example") .with_inner_size(winit::dpi::LogicalSize::new( @@ -231,16 +226,16 @@ impl ExampleBase { .collect(); let mut extension_names = - ash_window::enumerate_required_extensions(window.raw_display_handle()) + ash_window::enumerate_required_extensions(window.display_handle()?) .unwrap() .to_vec(); extension_names.push(DebugUtils::NAME.as_ptr()); #[cfg(any(target_os = "macos", target_os = "ios"))] { - extension_names.push(KhrPortabilityEnumerationFn::NAME.as_ptr()); + extension_names.push(vk::KhrPortabilityEnumerationFn::NAME.as_ptr()); // Enabling this extension is a requirement when using `VK_KHR_portability_subset` - extension_names.push(KhrGetPhysicalDeviceProperties2Fn::NAME.as_ptr()); + extension_names.push(vk::KhrGetPhysicalDeviceProperties2Fn::NAME.as_ptr()); } let appinfo = vk::ApplicationInfo::default() @@ -286,8 +281,8 @@ impl ExampleBase { let surface = ash_window::create_surface( &entry, &instance, - window.raw_display_handle(), - window.raw_window_handle(), + window.display_handle()?, + window.window_handle()?, None, ) .unwrap(); @@ -324,7 +319,7 @@ impl ExampleBase { let device_extension_names_raw = [ Swapchain::NAME.as_ptr(), #[cfg(any(target_os = "macos", target_os = "ios"))] - KhrPortabilitySubsetFn::NAME.as_ptr(), + vk::KhrPortabilitySubsetFn::NAME.as_ptr(), ]; let features = vk::PhysicalDeviceFeatures { shader_clip_distance: 1, @@ -547,7 +542,7 @@ impl ExampleBase { .create_semaphore(&semaphore_create_info, None) .unwrap(); - Self { + Ok(Self { event_loop: RefCell::new(event_loop), entry, instance, @@ -577,7 +572,7 @@ impl ExampleBase { debug_call_back, debug_utils_loader, depth_image_memory, - } + }) } } } diff --git a/ash-window/Cargo.toml b/ash-window/Cargo.toml index 254c8d05d..81ccd3f60 100644 --- a/ash-window/Cargo.toml +++ b/ash-window/Cargo.toml @@ -20,13 +20,13 @@ rust-version = "1.69.0" [dependencies] ash = { path = "../ash", version = "0.37", default-features = false } -raw-window-handle = "0.5" +raw-window-handle = "0.6" [target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies] -raw-window-metal = "0.3" +raw-window-metal = "0.4" [dev-dependencies] -winit = "0.28.0" +winit = { version = "0.29", features = ["rwh_06"] } ash = { path = "../ash", version = "0.37", default-features = false, features = ["linked"] } [[example]] diff --git a/ash-window/Changelog.md b/ash-window/Changelog.md index 557300eee..8eb012e48 100644 --- a/ash-window/Changelog.md +++ b/ash-window/Changelog.md @@ -3,6 +3,7 @@ ## [Unreleased] - ReleaseDate - Bumped MSRV from 1.59 to 1.69 for `winit 0.28` and `raw-window-handle 0.5.1`, and `CStr::from_bytes_until_nul`. (#709, #716, #746) +- Bumped `raw-window-handle` to `0.6.0` (#799) ## [0.12.0] - 2022-09-23 diff --git a/ash-window/examples/winit.rs b/ash-window/examples/winit.rs index b730e94a8..58b80ba9f 100644 --- a/ash-window/examples/winit.rs +++ b/ash-window/examples/winit.rs @@ -6,22 +6,23 @@ //! On instance extensions platform specific extensions need to be enabled. use ash::vk; -use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle}; use std::error::Error; use winit::{ dpi::PhysicalSize, - event::{Event, VirtualKeyCode, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, + event::{Event, KeyEvent, WindowEvent}, + event_loop::EventLoop, + keyboard::{Key, NamedKey}, + raw_window_handle::{HasDisplayHandle, HasWindowHandle}, window::WindowBuilder, }; fn main() -> Result<(), Box> { - let event_loop = EventLoop::new(); + let event_loop = EventLoop::new()?; unsafe { let entry = ash::Entry::linked(); let surface_extensions = - ash_window::enumerate_required_extensions(event_loop.raw_display_handle())?; + ash_window::enumerate_required_extensions(event_loop.display_handle()?)?; let app_desc = vk::ApplicationInfo::default().api_version(vk::make_api_version(0, 1, 0, 0)); let instance_desc = vk::InstanceCreateInfo::default() .application_info(&app_desc) @@ -37,33 +38,34 @@ fn main() -> Result<(), Box> { let surface = ash_window::create_surface( &entry, &instance, - window.raw_display_handle(), - window.raw_window_handle(), + window.display_handle()?, + window.window_handle()?, None, )?; let surface_fn = ash::extensions::khr::Surface::new(&entry, &instance); println!("surface: {surface:?}"); - event_loop.run(move |event, _, control_flow| match event { + let _ = event_loop.run(move |event, elwp| match event { winit::event::Event::WindowEvent { event: WindowEvent::CloseRequested | WindowEvent::KeyboardInput { - input: - winit::event::KeyboardInput { - virtual_keycode: Some(VirtualKeyCode::Escape), + event: + KeyEvent { + logical_key: Key::Named(NamedKey::Escape), .. }, .. }, window_id: _, } => { - *control_flow = ControlFlow::Exit; + elwp.exit(); } - Event::LoopDestroyed => { + Event::LoopExiting => { surface_fn.destroy_surface(surface, None); } _ => {} - }) + }); + Ok(()) } } diff --git a/ash-window/src/lib.rs b/ash-window/src/lib.rs index 7b97fb752..d1564b58f 100644 --- a/ash-window/src/lib.rs +++ b/ash-window/src/lib.rs @@ -7,9 +7,9 @@ use ash::{ prelude::*, vk, Entry, Instance, }; -use raw_window_handle::{RawDisplayHandle, RawWindowHandle}; +use raw_window_handle::{DisplayHandle, RawDisplayHandle, RawWindowHandle, WindowHandle}; -/// Create a surface from a raw surface handle. +/// Create a surface from display and window handle. /// /// `instance` must have created with platform specific surface extensions enabled, acquired /// through [`enumerate_required_extensions()`]. @@ -33,6 +33,47 @@ use raw_window_handle::{RawDisplayHandle, RawWindowHandle}; /// /// [parent/child relation]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#fundamentals-objectmodel-lifetime pub unsafe fn create_surface( + entry: &Entry, + instance: &Instance, + display_handle: DisplayHandle<'_>, + window_handle: WindowHandle<'_>, + allocation_callbacks: Option<&vk::AllocationCallbacks>, +) -> VkResult { + #[allow(deprecated)] + create_surface_raw( + entry, + instance, + display_handle.as_raw(), + window_handle.as_raw(), + allocation_callbacks, + ) +} + +/// Create a surface from a raw surface handle. +/// +/// `instance` must have created with platform specific surface extensions enabled, acquired +/// through [`enumerate_required_extensions()`]. +/// +/// # Safety +/// +/// There is a [parent/child relation] between [`Instance`] and [`Entry`], and the resulting +/// [`vk::SurfaceKHR`]. The application must not [destroy][Instance::destroy_instance()] these +/// parent objects before first [destroying][khr::Surface::destroy_surface()] the returned +/// [`vk::SurfaceKHR`] child object. [`vk::SurfaceKHR`] does _not_ implement [drop][drop()] +/// semantics and can only be destroyed via [`destroy_surface()`][khr::Surface::destroy_surface()]. +/// +/// See the [`Entry::create_instance()`] documentation for more destruction ordering rules on +/// [`Instance`]. +/// +/// The window represented by `window_handle` must be associated with the display connection +/// in `display_handle`. +/// +/// `window_handle` and `display_handle` must be associated with a valid window and display +/// connection, which must not be destroyed for the lifetime of the returned [`vk::SurfaceKHR`]. +/// +/// [parent/child relation]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#fundamentals-objectmodel-lifetime +#[deprecated = "Direct use of raw window handles is deprecated"] +pub unsafe fn create_surface_raw( entry: &Entry, instance: &Instance, display_handle: RawDisplayHandle, @@ -42,23 +83,33 @@ pub unsafe fn create_surface( match (display_handle, window_handle) { (RawDisplayHandle::Windows(_), RawWindowHandle::Win32(window)) => { let surface_desc = vk::Win32SurfaceCreateInfoKHR::default() - .hinstance(window.hinstance as isize) - .hwnd(window.hwnd as isize); + .hwnd(window.hwnd.get()) + .hinstance( + window + .hinstance + .ok_or(vk::Result::ERROR_INITIALIZATION_FAILED)? + .get(), + ); let surface_fn = khr::Win32Surface::new(entry, instance); surface_fn.create_win32_surface(&surface_desc, allocation_callbacks) } (RawDisplayHandle::Wayland(display), RawWindowHandle::Wayland(window)) => { let surface_desc = vk::WaylandSurfaceCreateInfoKHR::default() - .display(display.display) - .surface(window.surface); + .display(display.display.as_ptr()) + .surface(window.surface.as_ptr()); let surface_fn = khr::WaylandSurface::new(entry, instance); surface_fn.create_wayland_surface(&surface_desc, allocation_callbacks) } (RawDisplayHandle::Xlib(display), RawWindowHandle::Xlib(window)) => { let surface_desc = vk::XlibSurfaceCreateInfoKHR::default() - .dpy(display.display.cast()) + .dpy( + display + .display + .ok_or(vk::Result::ERROR_INITIALIZATION_FAILED)? + .as_ptr(), + ) .window(window.window); let surface_fn = khr::XlibSurface::new(entry, instance); surface_fn.create_xlib_surface(&surface_desc, allocation_callbacks) @@ -66,15 +117,20 @@ pub unsafe fn create_surface( (RawDisplayHandle::Xcb(display), RawWindowHandle::Xcb(window)) => { let surface_desc = vk::XcbSurfaceCreateInfoKHR::default() - .connection(display.connection) - .window(window.window); + .connection( + display + .connection + .ok_or(vk::Result::ERROR_INITIALIZATION_FAILED)? + .as_ptr(), + ) + .window(window.window.get()); let surface_fn = khr::XcbSurface::new(entry, instance); surface_fn.create_xcb_surface(&surface_desc, allocation_callbacks) } (RawDisplayHandle::Android(_), RawWindowHandle::AndroidNdk(window)) => { let surface_desc = - vk::AndroidSurfaceCreateInfoKHR::default().window(window.a_native_window); + vk::AndroidSurfaceCreateInfoKHR::default().window(window.a_native_window.as_ptr()); let surface_fn = khr::AndroidSurface::new(entry, instance); surface_fn.create_android_surface(&surface_desc, allocation_callbacks) } @@ -85,7 +141,6 @@ pub unsafe fn create_surface( let layer = match appkit::metal_layer_from_handle(window) { Layer::Existing(layer) | Layer::Allocated(layer) => layer.cast(), - Layer::None => return Err(vk::Result::ERROR_INITIALIZATION_FAILED), }; let surface_desc = vk::MetalSurfaceCreateInfoEXT::default().layer(&*layer); @@ -99,7 +154,6 @@ pub unsafe fn create_surface( let layer = match uikit::metal_layer_from_handle(window) { Layer::Existing(layer) | Layer::Allocated(layer) => layer.cast(), - Layer::None => return Err(vk::Result::ERROR_INITIALIZATION_FAILED), }; let surface_desc = vk::MetalSurfaceCreateInfoEXT::default().layer(&*layer); @@ -119,6 +173,21 @@ pub unsafe fn create_surface( /// /// The returned extensions will include all extension dependencies. pub fn enumerate_required_extensions( + display_handle: DisplayHandle<'_>, +) -> VkResult<&'static [*const c_char]> { + #[allow(deprecated)] + enumerate_required_extensions_raw(display_handle.as_raw()) +} + +/// Query the required instance extensions for creating a surface from a display handle. +/// +/// This [`RawDisplayHandle`] can typically be acquired from a window, but is usually also +/// accessible earlier through an "event loop" concept to allow querying required instance +/// extensions and creation of a compatible Vulkan instance prior to creating a window. +/// +/// The returned extensions will include all extension dependencies. +#[deprecated = "Direct use of raw display handles is deprecated"] +pub fn enumerate_required_extensions_raw( display_handle: RawDisplayHandle, ) -> VkResult<&'static [*const c_char]> { let extensions = match display_handle {