Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ash-window: Add get_present_support() helper #774

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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

Expand Down
12 changes: 12 additions & 0 deletions ash-window/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,25 @@ 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"

[dev-dependencies]
winit = "0.28.0"
ash = { path = "../ash", version = "0.37", default-features = false, features = ["linked"] }

[features]
default = ["x11-dl", "xcb"]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requiring xcb by default means we need libxcb present at buildtime, right? While probably most environments will have this, disabling it will make it easier for downstream users to get CI working in minimal VMs and such.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason for defaulting this is to have the least surprising behavior by default. I dont think the default should be to not support a very broadly used platform.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We know winit doesn't use xcb. Where in the raw-window-handle ecosystem is xcb broadly used?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, however if winit at some point decides to switch to xcb, it should be defaulted again...

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think winit will want to avoid adding foreign build-time dependencies for the same reason.


[[example]]
name = "winit"
required-features = ["ash/linked"]

[[example]]
name = "xcb"
required-features = ["ash/linked", "xcb"]
1 change: 1 addition & 0 deletions ash-window/Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
27 changes: 26 additions & 1 deletion ash-window/examples/winit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand All @@ -29,6 +29,31 @@ fn main() -> Result<(), Box<dyn Error>> {

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();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self: I should replace this in #746.


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::<u32>::from((800, 600)))
.build(&event_loop)?;
Expand Down
92 changes: 92 additions & 0 deletions ash-window/examples/xcb.rs
Original file line number Diff line number Diff line change
@@ -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<dyn Error>> {
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::<x::Window>();
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(())
}
91 changes: 91 additions & 0 deletions ash-window/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<bool> {
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,
MarijnS95 marked this conversation as resolved.
Show resolved Hide resolved
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),
}
}
2 changes: 1 addition & 1 deletion ash/src/extensions/khr/wayland_surface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should write these down as breaking changes in the CHANGELOG (maybe via a separate PR so that it shows up properly in the commit history), though if someone passes exactly a &mut vk::wl_display it'll automatically coerce to the raw pointer.

) -> bool {
let b = (self.fp.get_physical_device_wayland_presentation_support_khr)(
physical_device,
Expand Down
2 changes: 1 addition & 1 deletion ash/src/extensions/khr/xcb_surface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)(
Expand Down