diff --git a/Changelog.md b/Changelog.md index f7185f3d5..50cbdb178 100644 --- a/Changelog.md +++ b/Changelog.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `VK_NV_memory_decompression` device extension (#761) - Added `VK_GOOGLE_display_timing` device extension (#765) - Added `VK_ANDROID_external_memory_android_hardware_buffer` device extension (#769) +- Added `get_present_support()` to ash-window (#774) ### Changed @@ -32,6 +33,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Define `Display` as `c_void` instead of `*mut c_void` to match Xlib (#751) - `VK_KHR_device_group_creation`: Take borrow of `Entry` in `fn new()` (#753) - `VK_KHR_device_group_creation`: Rename `vk::Instance`-returning function from `device()` to `instance()` (#759) +- `VK_KHR_xcb_surface`: Take `*mut vk::xcb_connection_t` instead of mutable reference in `get_physical_device_xcb_presentation_support` (#774) +- `VK_KHR_wayland_surface`: Take `*mut vk::wl_display` instead of mutable reference in `get_physical_device_wayland_presentation_support` (#774) ### Removed diff --git a/ash-window/Cargo.toml b/ash-window/Cargo.toml index 76ad7c154..50031b029 100644 --- a/ash-window/Cargo.toml +++ b/ash-window/Cargo.toml @@ -18,6 +18,11 @@ rust-version = "1.64.0" ash = { path = "../ash", version = "0.37", default-features = false } raw-window-handle = "0.5" +[target.'cfg(unix)'.dependencies] +x11-dl = { version = "2.21.0", optional = true } +x11 = { version = "2.21.0", features = ["xlib"], optional = true } +xcb = { version = "1.2.2", optional = true } + [target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies] raw-window-metal = "0.3" @@ -25,6 +30,13 @@ raw-window-metal = "0.3" winit = "0.28.0" ash = { path = "../ash", version = "0.37", default-features = false, features = ["linked"] } +[features] +default = ["x11-dl", "xcb"] + [[example]] name = "winit" required-features = ["ash/linked"] + +[[example]] +name = "xcb" +required-features = ["ash/linked", "xcb"] diff --git a/ash-window/Changelog.md b/ash-window/Changelog.md index ec2cbbf8f..0221a82b1 100644 --- a/ash-window/Changelog.md +++ b/ash-window/Changelog.md @@ -3,6 +3,7 @@ ## [Unreleased] - ReleaseDate - Bumped MSRV from 1.59 to 1.64 for `winit 0.28` and `raw-window-handle 0.5.1`. (#709, #716) +- Added `get_present_support` (#774) ## [0.12.0] - 2022-09-23 diff --git a/ash-window/examples/winit.rs b/ash-window/examples/winit.rs index b730e94a8..2cf817295 100644 --- a/ash-window/examples/winit.rs +++ b/ash-window/examples/winit.rs @@ -7,7 +7,7 @@ use ash::vk; use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle}; -use std::error::Error; +use std::{error::Error, ffi::CStr}; use winit::{ dpi::PhysicalSize, event::{Event, VirtualKeyCode, WindowEvent}, @@ -29,6 +29,31 @@ fn main() -> Result<(), Box> { let instance = entry.create_instance(&instance_desc, None)?; + let devices = instance.enumerate_physical_devices()?; + for dev in devices { + let props = instance.get_physical_device_properties(dev); + let queue_families = instance.get_physical_device_queue_family_properties(dev); + let dev_name = CStr::from_ptr(props.device_name.as_ptr()).to_str().unwrap(); + + for i in 0..queue_families.len() { + let present_support = ash_window::get_present_support( + &entry, + &instance, + dev, + i as _, + event_loop.raw_display_handle(), + )?; + println!( + "{dev_name}, queue {i} {} presenting to surfaces", + if present_support { + "supports" + } else { + "does not support" + } + ); + } + } + let window = WindowBuilder::new() .with_inner_size(PhysicalSize::::from((800, 600))) .build(&event_loop)?; diff --git a/ash-window/examples/xcb.rs b/ash-window/examples/xcb.rs new file mode 100644 index 000000000..15577b750 --- /dev/null +++ b/ash-window/examples/xcb.rs @@ -0,0 +1,92 @@ +use std::{error::Error, ffi::CStr}; + +use ash::vk; +use raw_window_handle::{RawDisplayHandle, RawWindowHandle, XcbDisplayHandle, XcbWindowHandle}; +use xcb::{x, Xid}; + +fn main() -> Result<(), Box> { + let (xcb, screen_index) = xcb::Connection::connect(None)?; + + let screen = xcb.get_setup().roots().nth(screen_index as usize).unwrap(); + + let mut display_handle = XcbDisplayHandle::empty(); + display_handle.connection = xcb.get_raw_conn().cast(); + display_handle.screen = screen_index; + let display_handle = RawDisplayHandle::Xcb(display_handle); + + unsafe { + let entry = ash::Entry::linked(); + let surface_extensions = ash_window::enumerate_required_extensions(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) + .enabled_extension_names(surface_extensions); + + let instance = entry.create_instance(&instance_desc, None)?; + + let devices = instance.enumerate_physical_devices()?; + for dev in devices { + let props = instance.get_physical_device_properties(dev); + let queue_families = instance.get_physical_device_queue_family_properties(dev); + let dev_name = CStr::from_ptr(props.device_name.as_ptr()).to_str().unwrap(); + + for i in 0..queue_families.len() { + let present_support = ash_window::get_present_support( + &entry, + &instance, + dev, + i as _, + display_handle, + )?; + println!( + "{dev_name}, queue {i} {} presenting to surfaces", + if present_support { + "supports" + } else { + "does not support" + } + ); + } + } + + let window = xcb.generate_id::(); + xcb.send_request(&x::CreateWindow { + depth: x::COPY_FROM_PARENT as _, + wid: window, + parent: screen.root(), + x: 0, + y: 0, + width: 800, + height: 600, + border_width: 10, + class: x::WindowClass::InputOutput, + visual: screen.root_visual(), + value_list: &[ + x::Cw::BackPixel(screen.white_pixel()), + x::Cw::EventMask(x::EventMask::EXPOSURE | x::EventMask::KEY_PRESS), + ], + }); + + let cookie = xcb.send_request_checked(&x::MapWindow { window }); + xcb.check_request(cookie)?; + + let mut window_handle = XcbWindowHandle::empty(); + window_handle.window = window.resource_id(); + window_handle.visual_id = screen.root_visual(); + let window_handle = RawWindowHandle::Xcb(window_handle); + + // Create a surface from winit window. + let surface = + ash_window::create_surface(&entry, &instance, display_handle, window_handle, None)?; + let surface_fn = ash::extensions::khr::Surface::new(&entry, &instance); + println!("surface: {surface:?}"); + + while xcb.wait_for_event().is_ok() {} + + surface_fn.destroy_surface(surface, None); + instance.destroy_instance(None); + } + + Ok(()) +} diff --git a/ash-window/src/lib.rs b/ash-window/src/lib.rs index 885c48f37..b41231d94 100644 --- a/ash-window/src/lib.rs +++ b/ash-window/src/lib.rs @@ -171,3 +171,94 @@ pub fn enumerate_required_extensions( Ok(extensions) } + +/// Query whether a `queue_family` of the given `physical_device` supports presenting to any +/// surface that might be created. This function can be used to find a suitable +/// [`vk::PhysicalDevice`] and queue family for rendering before a single surface is created. +/// +/// This function can be a more useful alternative for [`khr::Surface::get_physical_device_surface_support()`], +/// which requires having an actual surface available before choosing a physical device. +/// +/// For more information see [the Vulkan spec on WSI integration][_querying_for_wsi_support]. +/// +/// [_querying_for_wsi_support]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap34.html#_querying_for_wsi_support +pub fn get_present_support( + entry: &Entry, + instance: &Instance, + physical_device: vk::PhysicalDevice, + queue_family_index: u32, + display_handle: RawDisplayHandle, +) -> VkResult { + match display_handle { + RawDisplayHandle::Android(_) | RawDisplayHandle::UiKit(_) | RawDisplayHandle::AppKit(_) => { + // https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap34.html#platformQuerySupport_android + // https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap34.html#platformQuerySupport_ios + // https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap34.html#platformQuerySupport_macos + // On Android, iOS and macOS, every queue family supports presenting to any surface + Ok(true) + } + RawDisplayHandle::Wayland(h) => unsafe { + // https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap34.html#platformQuerySupport_walyand + let ext = khr::WaylandSurface::new(entry, instance); + Ok(ext.get_physical_device_wayland_presentation_support( + physical_device, + queue_family_index, + h.display, + )) + }, + RawDisplayHandle::Windows(_) => unsafe { + // https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap34.html#platformQuerySupport_win32 + let ext = khr::Win32Surface::new(entry, instance); + Ok(ext.get_physical_device_win32_presentation_support( + physical_device, + queue_family_index, + )) + }, + #[cfg(feature = "xcb")] + RawDisplayHandle::Xcb(h) => unsafe { + // https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap34.html#platformQuerySupport_xcb + let ext = khr::XcbSurface::new(entry, instance); + + let xcb = xcb::Connection::from_raw_conn(h.connection.cast()); + let setup = xcb.get_setup(); + let screen = setup.roots().nth(h.screen as usize).unwrap(); + let visual = screen.root_visual(); + let connection = xcb.into_raw_conn(); + + Ok(ext.get_physical_device_xcb_presentation_support( + physical_device, + queue_family_index, + connection.cast(), + visual, + )) + }, + #[cfg(any(feature = "x11-dl", feature = "x11"))] + RawDisplayHandle::Xlib(h) => unsafe { + // https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap34.html#platformQuerySupport_xlib + let ext = khr::XlibSurface::new(entry, instance); + + let visual_id; + #[cfg(feature = "x11")] + { + let default_visual = x11::xlib::XDefaultVisual(h.display.cast(), h.screen); + visual_id = x11::xlib::XVisualIDFromVisual(default_visual); + } + #[cfg(all(feature = "x11-dl", not(feature = "x11")))] + { + let xlib = x11_dl::xlib::Xlib::open().unwrap(); + let default_visual = (xlib.XDefaultVisual)(h.display.cast(), h.screen); + visual_id = (xlib.XVisualIDFromVisual)(default_visual); + } + + Ok(ext.get_physical_device_xlib_presentation_support( + physical_device, + queue_family_index, + h.display, + visual_id as _, + )) + }, + // All other platforms mentioned in the Vulkan spec don't + // currently have an implementation in ash-window. + _ => Err(vk::Result::ERROR_EXTENSION_NOT_PRESENT), + } +} diff --git a/ash/src/extensions/khr/wayland_surface.rs b/ash/src/extensions/khr/wayland_surface.rs index 64b3f5a11..9db93b98d 100755 --- a/ash/src/extensions/khr/wayland_surface.rs +++ b/ash/src/extensions/khr/wayland_surface.rs @@ -43,7 +43,7 @@ impl WaylandSurface { &self, physical_device: vk::PhysicalDevice, queue_family_index: u32, - wl_display: &mut vk::wl_display, + wl_display: *mut vk::wl_display, ) -> bool { let b = (self.fp.get_physical_device_wayland_presentation_support_khr)( physical_device, diff --git a/ash/src/extensions/khr/xcb_surface.rs b/ash/src/extensions/khr/xcb_surface.rs index 80107f6f6..be0dbc5ea 100755 --- a/ash/src/extensions/khr/xcb_surface.rs +++ b/ash/src/extensions/khr/xcb_surface.rs @@ -43,7 +43,7 @@ impl XcbSurface { &self, physical_device: vk::PhysicalDevice, queue_family_index: u32, - connection: &mut vk::xcb_connection_t, + connection: *mut vk::xcb_connection_t, visual_id: vk::xcb_visualid_t, ) -> bool { let b = (self.fp.get_physical_device_xcb_presentation_support_khr)(