use std::{
ffi::{c_void, CStr, CString},
slice,
str::FromStr,
sync::Arc,
thread,
};
use arrayvec::ArrayVec;
use ash::{
extensions::{ext, khr},
vk,
};
use parking_lot::RwLock;
unsafe extern "system" fn debug_utils_messenger_callback(
message_severity: vk::DebugUtilsMessageSeverityFlagsEXT,
message_type: vk::DebugUtilsMessageTypeFlagsEXT,
callback_data_ptr: *const vk::DebugUtilsMessengerCallbackDataEXT,
user_data: *mut c_void,
) -> vk::Bool32 {
use std::borrow::Cow;
if thread::panicking() {
return vk::FALSE;
}
let cd = unsafe { &*callback_data_ptr };
let user_data = unsafe { &*(user_data as *mut super::DebugUtilsMessengerUserData) };
const VUID_VKCMDENDDEBUGUTILSLABELEXT_COMMANDBUFFER_01912: i32 = 0x56146426;
if cd.message_id_number == VUID_VKCMDENDDEBUGUTILSLABELEXT_COMMANDBUFFER_01912 {
let khronos_validation_layer =
std::ffi::CStr::from_bytes_with_nul(b"Khronos Validation Layer\0").unwrap();
if let Some(layer_properties) = user_data.validation_layer_properties.as_ref() {
if layer_properties.layer_description.as_ref() == khronos_validation_layer
&& layer_properties.layer_spec_version >= vk::make_api_version(0, 1, 3, 240)
&& layer_properties.layer_spec_version <= vk::make_api_version(0, 1, 3, 250)
{
return vk::FALSE;
}
}
}
const VUID_VKSWAPCHAINCREATEINFOKHR_PNEXT_07781: i32 = 0x4c8929c1;
if cd.message_id_number == VUID_VKSWAPCHAINCREATEINFOKHR_PNEXT_07781 {
return vk::FALSE;
}
const VUID_VKRENDERPASSBEGININFO_FRAMEBUFFER_04627: i32 = 0x45125641;
if cd.message_id_number == VUID_VKRENDERPASSBEGININFO_FRAMEBUFFER_04627
&& user_data.has_obs_layer
{
return vk::FALSE;
}
let level = match message_severity {
vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE => log::Level::Debug,
vk::DebugUtilsMessageSeverityFlagsEXT::INFO => log::Level::Info,
vk::DebugUtilsMessageSeverityFlagsEXT::WARNING => log::Level::Warn,
vk::DebugUtilsMessageSeverityFlagsEXT::ERROR => log::Level::Error,
_ => log::Level::Warn,
};
let message_id_name = if cd.p_message_id_name.is_null() {
Cow::from("")
} else {
unsafe { CStr::from_ptr(cd.p_message_id_name) }.to_string_lossy()
};
let message = if cd.p_message.is_null() {
Cow::from("")
} else {
unsafe { CStr::from_ptr(cd.p_message) }.to_string_lossy()
};
let _ = std::panic::catch_unwind(|| {
log::log!(
level,
"{:?} [{} (0x{:x})]\n\t{}",
message_type,
message_id_name,
cd.message_id_number,
message,
);
});
if cd.queue_label_count != 0 {
let labels =
unsafe { slice::from_raw_parts(cd.p_queue_labels, cd.queue_label_count as usize) };
let names = labels
.iter()
.flat_map(|dul_obj| {
unsafe { dul_obj.p_label_name.as_ref() }
.map(|lbl| unsafe { CStr::from_ptr(lbl) }.to_string_lossy())
})
.collect::<Vec<_>>();
let _ = std::panic::catch_unwind(|| {
log::log!(level, "\tqueues: {}", names.join(", "));
});
}
if cd.cmd_buf_label_count != 0 {
let labels =
unsafe { slice::from_raw_parts(cd.p_cmd_buf_labels, cd.cmd_buf_label_count as usize) };
let names = labels
.iter()
.flat_map(|dul_obj| {
unsafe { dul_obj.p_label_name.as_ref() }
.map(|lbl| unsafe { CStr::from_ptr(lbl) }.to_string_lossy())
})
.collect::<Vec<_>>();
let _ = std::panic::catch_unwind(|| {
log::log!(level, "\tcommand buffers: {}", names.join(", "));
});
}
if cd.object_count != 0 {
let labels = unsafe { slice::from_raw_parts(cd.p_objects, cd.object_count as usize) };
let names = labels
.iter()
.map(|obj_info| {
let name = unsafe { obj_info.p_object_name.as_ref() }
.map(|name| unsafe { CStr::from_ptr(name) }.to_string_lossy())
.unwrap_or(Cow::Borrowed("?"));
format!(
"(type: {:?}, hndl: 0x{:x}, name: {})",
obj_info.object_type, obj_info.object_handle, name
)
})
.collect::<Vec<_>>();
let _ = std::panic::catch_unwind(|| {
log::log!(level, "\tobjects: {}", names.join(", "));
});
}
if cfg!(debug_assertions) && level == log::Level::Error {
crate::VALIDATION_CANARY.add(message.to_string());
}
vk::FALSE
}
impl super::DebugUtilsCreateInfo {
fn to_vk_create_info(&self) -> vk::DebugUtilsMessengerCreateInfoEXTBuilder<'_> {
let user_data_ptr: *const super::DebugUtilsMessengerUserData = &*self.callback_data;
vk::DebugUtilsMessengerCreateInfoEXT::builder()
.message_severity(self.severity)
.message_type(self.message_type)
.user_data(user_data_ptr as *mut _)
.pfn_user_callback(Some(debug_utils_messenger_callback))
}
}
impl super::Swapchain {
unsafe fn release_resources(mut self, device: &ash::Device) -> Self {
profiling::scope!("Swapchain::release_resources");
{
profiling::scope!("vkDeviceWaitIdle");
let _ = unsafe { device.device_wait_idle() };
};
for semaphore in self.surface_semaphores.drain(..) {
let arc_removed = Arc::into_inner(semaphore).expect(
"Trying to destroy a SurfaceSemaphores that is still in use by a SurfaceTexture",
);
let mutex_removed = arc_removed.into_inner();
unsafe { mutex_removed.destroy(device) };
}
self
}
}
impl super::InstanceShared {
pub fn entry(&self) -> &ash::Entry {
&self.entry
}
pub fn raw_instance(&self) -> &ash::Instance {
&self.raw
}
pub fn instance_api_version(&self) -> u32 {
self.instance_api_version
}
pub fn extensions(&self) -> &[&'static CStr] {
&self.extensions[..]
}
}
impl super::Instance {
pub fn shared_instance(&self) -> &super::InstanceShared {
&self.shared
}
fn enumerate_instance_extension_properties(
entry: &ash::Entry,
layer_name: Option<&CStr>,
) -> Result<Vec<vk::ExtensionProperties>, crate::InstanceError> {
let instance_extensions = {
profiling::scope!("vkEnumerateInstanceExtensionProperties");
entry.enumerate_instance_extension_properties(layer_name)
};
instance_extensions.map_err(|e| {
crate::InstanceError::with_source(
String::from("enumerate_instance_extension_properties() failed"),
e,
)
})
}
pub fn desired_extensions(
entry: &ash::Entry,
_instance_api_version: u32,
flags: wgt::InstanceFlags,
) -> Result<Vec<&'static CStr>, crate::InstanceError> {
let instance_extensions = Self::enumerate_instance_extension_properties(entry, None)?;
let mut extensions: Vec<&'static CStr> = Vec::new();
extensions.push(khr::Surface::name());
if cfg!(all(
unix,
not(target_os = "android"),
not(target_os = "macos")
)) {
extensions.push(khr::XlibSurface::name());
extensions.push(khr::XcbSurface::name());
extensions.push(khr::WaylandSurface::name());
}
if cfg!(target_os = "android") {
extensions.push(khr::AndroidSurface::name());
}
if cfg!(target_os = "windows") {
extensions.push(khr::Win32Surface::name());
}
if cfg!(target_os = "macos") {
extensions.push(ext::MetalSurface::name());
extensions.push(ash::vk::KhrPortabilityEnumerationFn::name());
}
if flags.contains(wgt::InstanceFlags::DEBUG) {
extensions.push(ext::DebugUtils::name());
}
extensions.push(vk::ExtSwapchainColorspaceFn::name());
extensions.push(vk::KhrGetPhysicalDeviceProperties2Fn::name());
extensions.retain(|&ext| {
if instance_extensions.iter().any(|inst_ext| {
crate::auxil::cstr_from_bytes_until_nul(&inst_ext.extension_name) == Some(ext)
}) {
true
} else {
log::warn!("Unable to find extension: {}", ext.to_string_lossy());
false
}
});
Ok(extensions)
}
#[allow(clippy::too_many_arguments)]
pub unsafe fn from_raw(
entry: ash::Entry,
raw_instance: ash::Instance,
instance_api_version: u32,
android_sdk_version: u32,
debug_utils_create_info: Option<super::DebugUtilsCreateInfo>,
extensions: Vec<&'static CStr>,
flags: wgt::InstanceFlags,
has_nv_optimus: bool,
drop_guard: Option<crate::DropGuard>,
) -> Result<Self, crate::InstanceError> {
log::debug!("Instance version: 0x{:x}", instance_api_version);
let debug_utils = if let Some(debug_utils_create_info) = debug_utils_create_info {
if extensions.contains(&ext::DebugUtils::name()) {
log::info!("Enabling debug utils");
let extension = ext::DebugUtils::new(&entry, &raw_instance);
let vk_info = debug_utils_create_info.to_vk_create_info();
let messenger =
unsafe { extension.create_debug_utils_messenger(&vk_info, None) }.unwrap();
Some(super::DebugUtils {
extension,
messenger,
callback_data: debug_utils_create_info.callback_data,
})
} else {
log::info!("Debug utils not enabled: extension not listed");
None
}
} else {
log::info!(
"Debug utils not enabled: \
debug_utils_user_data not passed to Instance::from_raw"
);
None
};
let get_physical_device_properties =
if extensions.contains(&khr::GetPhysicalDeviceProperties2::name()) {
log::debug!("Enabling device properties2");
Some(khr::GetPhysicalDeviceProperties2::new(
&entry,
&raw_instance,
))
} else {
None
};
Ok(Self {
shared: Arc::new(super::InstanceShared {
raw: raw_instance,
extensions,
drop_guard,
flags,
debug_utils,
get_physical_device_properties,
entry,
has_nv_optimus,
instance_api_version,
android_sdk_version,
}),
})
}
#[allow(dead_code)]
fn create_surface_from_xlib(
&self,
dpy: *mut vk::Display,
window: vk::Window,
) -> Result<super::Surface, crate::InstanceError> {
if !self.shared.extensions.contains(&khr::XlibSurface::name()) {
return Err(crate::InstanceError::new(String::from(
"Vulkan driver does not support VK_KHR_xlib_surface",
)));
}
let surface = {
let xlib_loader = khr::XlibSurface::new(&self.shared.entry, &self.shared.raw);
let info = vk::XlibSurfaceCreateInfoKHR::builder()
.flags(vk::XlibSurfaceCreateFlagsKHR::empty())
.window(window)
.dpy(dpy);
unsafe { xlib_loader.create_xlib_surface(&info, None) }
.expect("XlibSurface::create_xlib_surface() failed")
};
Ok(self.create_surface_from_vk_surface_khr(surface))
}
#[allow(dead_code)]
fn create_surface_from_xcb(
&self,
connection: *mut vk::xcb_connection_t,
window: vk::xcb_window_t,
) -> Result<super::Surface, crate::InstanceError> {
if !self.shared.extensions.contains(&khr::XcbSurface::name()) {
return Err(crate::InstanceError::new(String::from(
"Vulkan driver does not support VK_KHR_xcb_surface",
)));
}
let surface = {
let xcb_loader = khr::XcbSurface::new(&self.shared.entry, &self.shared.raw);
let info = vk::XcbSurfaceCreateInfoKHR::builder()
.flags(vk::XcbSurfaceCreateFlagsKHR::empty())
.window(window)
.connection(connection);
unsafe { xcb_loader.create_xcb_surface(&info, None) }
.expect("XcbSurface::create_xcb_surface() failed")
};
Ok(self.create_surface_from_vk_surface_khr(surface))
}
#[allow(dead_code)]
fn create_surface_from_wayland(
&self,
display: *mut c_void,
surface: *mut c_void,
) -> Result<super::Surface, crate::InstanceError> {
if !self
.shared
.extensions
.contains(&khr::WaylandSurface::name())
{
return Err(crate::InstanceError::new(String::from(
"Vulkan driver does not support VK_KHR_wayland_surface",
)));
}
let surface = {
let w_loader = khr::WaylandSurface::new(&self.shared.entry, &self.shared.raw);
let info = vk::WaylandSurfaceCreateInfoKHR::builder()
.flags(vk::WaylandSurfaceCreateFlagsKHR::empty())
.display(display)
.surface(surface);
unsafe { w_loader.create_wayland_surface(&info, None) }.expect("WaylandSurface failed")
};
Ok(self.create_surface_from_vk_surface_khr(surface))
}
#[allow(dead_code)]
fn create_surface_android(
&self,
window: *const c_void,
) -> Result<super::Surface, crate::InstanceError> {
if !self
.shared
.extensions
.contains(&khr::AndroidSurface::name())
{
return Err(crate::InstanceError::new(String::from(
"Vulkan driver does not support VK_KHR_android_surface",
)));
}
let surface = {
let a_loader = khr::AndroidSurface::new(&self.shared.entry, &self.shared.raw);
let info = vk::AndroidSurfaceCreateInfoKHR::builder()
.flags(vk::AndroidSurfaceCreateFlagsKHR::empty())
.window(window as *mut _);
unsafe { a_loader.create_android_surface(&info, None) }.expect("AndroidSurface failed")
};
Ok(self.create_surface_from_vk_surface_khr(surface))
}
#[allow(dead_code)]
fn create_surface_from_hwnd(
&self,
hinstance: *mut c_void,
hwnd: *mut c_void,
) -> Result<super::Surface, crate::InstanceError> {
if !self.shared.extensions.contains(&khr::Win32Surface::name()) {
return Err(crate::InstanceError::new(String::from(
"Vulkan driver does not support VK_KHR_win32_surface",
)));
}
let surface = {
let info = vk::Win32SurfaceCreateInfoKHR::builder()
.flags(vk::Win32SurfaceCreateFlagsKHR::empty())
.hinstance(hinstance)
.hwnd(hwnd);
let win32_loader = khr::Win32Surface::new(&self.shared.entry, &self.shared.raw);
unsafe {
win32_loader
.create_win32_surface(&info, None)
.expect("Unable to create Win32 surface")
}
};
Ok(self.create_surface_from_vk_surface_khr(surface))
}
#[cfg(metal)]
fn create_surface_from_view(
&self,
view: *mut c_void,
) -> Result<super::Surface, crate::InstanceError> {
if !self.shared.extensions.contains(&ext::MetalSurface::name()) {
return Err(crate::InstanceError::new(String::from(
"Vulkan driver does not support VK_EXT_metal_surface",
)));
}
let layer = unsafe {
crate::metal::Surface::get_metal_layer(view as *mut objc::runtime::Object, None)
};
let surface = {
let metal_loader = ext::MetalSurface::new(&self.shared.entry, &self.shared.raw);
let vk_info = vk::MetalSurfaceCreateInfoEXT::builder()
.flags(vk::MetalSurfaceCreateFlagsEXT::empty())
.layer(layer as *mut _)
.build();
unsafe { metal_loader.create_metal_surface(&vk_info, None).unwrap() }
};
Ok(self.create_surface_from_vk_surface_khr(surface))
}
fn create_surface_from_vk_surface_khr(&self, surface: vk::SurfaceKHR) -> super::Surface {
let functor = khr::Surface::new(&self.shared.entry, &self.shared.raw);
super::Surface {
raw: surface,
functor,
instance: Arc::clone(&self.shared),
swapchain: RwLock::new(None),
}
}
}
impl Drop for super::InstanceShared {
fn drop(&mut self) {
unsafe {
let _du = self.debug_utils.take().map(|du| {
du.extension
.destroy_debug_utils_messenger(du.messenger, None);
du
});
if let Some(_drop_guard) = self.drop_guard.take() {
self.raw.destroy_instance(None);
}
}
}
}
impl crate::Instance for super::Instance {
type A = super::Api;
unsafe fn init(desc: &crate::InstanceDescriptor) -> Result<Self, crate::InstanceError> {
profiling::scope!("Init Vulkan Backend");
use crate::auxil::cstr_from_bytes_until_nul;
let entry = unsafe {
profiling::scope!("Load vk library");
ash::Entry::load()
}
.map_err(|err| {
crate::InstanceError::with_source(String::from("missing Vulkan entry points"), err)
})?;
let version = {
profiling::scope!("vkEnumerateInstanceVersion");
entry.try_enumerate_instance_version()
};
let instance_api_version = match version {
Ok(Some(version)) => version,
Ok(None) => vk::API_VERSION_1_0,
Err(err) => {
return Err(crate::InstanceError::with_source(
String::from("try_enumerate_instance_version() failed"),
err,
));
}
};
let app_name = CString::new(desc.name).unwrap();
let app_info = vk::ApplicationInfo::builder()
.application_name(app_name.as_c_str())
.application_version(1)
.engine_name(CStr::from_bytes_with_nul(b"wgpu-hal\0").unwrap())
.engine_version(2)
.api_version(
if instance_api_version < vk::API_VERSION_1_1 {
vk::API_VERSION_1_0
} else {
vk::API_VERSION_1_3
},
);
let extensions = Self::desired_extensions(&entry, instance_api_version, desc.flags)?;
let instance_layers = {
profiling::scope!("vkEnumerateInstanceLayerProperties");
entry.enumerate_instance_layer_properties()
};
let instance_layers = instance_layers.map_err(|e| {
log::debug!("enumerate_instance_layer_properties: {:?}", e);
crate::InstanceError::with_source(
String::from("enumerate_instance_layer_properties() failed"),
e,
)
})?;
fn find_layer<'layers>(
instance_layers: &'layers [vk::LayerProperties],
name: &CStr,
) -> Option<&'layers vk::LayerProperties> {
instance_layers
.iter()
.find(|inst_layer| cstr_from_bytes_until_nul(&inst_layer.layer_name) == Some(name))
}
let validation_layer_name =
CStr::from_bytes_with_nul(b"VK_LAYER_KHRONOS_validation\0").unwrap();
let validation_layer_properties = find_layer(&instance_layers, validation_layer_name);
let validation_features_are_enabled = if validation_layer_properties.is_some() {
let exts =
Self::enumerate_instance_extension_properties(&entry, Some(validation_layer_name))?;
let mut ext_names = exts
.iter()
.filter_map(|ext| cstr_from_bytes_until_nul(&ext.extension_name));
ext_names.any(|ext_name| ext_name == vk::ExtValidationFeaturesFn::name())
} else {
false
};
let should_enable_gpu_based_validation = desc
.flags
.intersects(wgt::InstanceFlags::GPU_BASED_VALIDATION)
&& validation_features_are_enabled;
let nv_optimus_layer = CStr::from_bytes_with_nul(b"VK_LAYER_NV_optimus\0").unwrap();
let has_nv_optimus = find_layer(&instance_layers, nv_optimus_layer).is_some();
let obs_layer = CStr::from_bytes_with_nul(b"VK_LAYER_OBS_HOOK\0").unwrap();
let has_obs_layer = find_layer(&instance_layers, obs_layer).is_some();
let mut layers: Vec<&'static CStr> = Vec::new();
let has_debug_extension = extensions.contains(&ext::DebugUtils::name());
let mut debug_user_data = has_debug_extension.then(|| {
Box::new(super::DebugUtilsMessengerUserData {
validation_layer_properties: None,
has_obs_layer,
})
});
if desc.flags.intersects(wgt::InstanceFlags::VALIDATION)
|| should_enable_gpu_based_validation
{
if let Some(layer_properties) = validation_layer_properties {
layers.push(validation_layer_name);
if let Some(debug_user_data) = debug_user_data.as_mut() {
debug_user_data.validation_layer_properties =
Some(super::ValidationLayerProperties {
layer_description: cstr_from_bytes_until_nul(
&layer_properties.description,
)
.unwrap()
.to_owned(),
layer_spec_version: layer_properties.spec_version,
});
}
} else {
log::warn!(
"InstanceFlags::VALIDATION requested, but unable to find layer: {}",
validation_layer_name.to_string_lossy()
);
}
}
let mut debug_utils = if let Some(callback_data) = debug_user_data {
let mut severity = vk::DebugUtilsMessageSeverityFlagsEXT::ERROR;
if log::max_level() >= log::LevelFilter::Debug {
severity |= vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE;
}
if log::max_level() >= log::LevelFilter::Info {
severity |= vk::DebugUtilsMessageSeverityFlagsEXT::INFO;
}
if log::max_level() >= log::LevelFilter::Warn {
severity |= vk::DebugUtilsMessageSeverityFlagsEXT::WARNING;
}
let message_type = vk::DebugUtilsMessageTypeFlagsEXT::GENERAL
| vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION
| vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE;
let create_info = super::DebugUtilsCreateInfo {
severity,
message_type,
callback_data,
};
let vk_create_info = create_info.to_vk_create_info().build();
Some((create_info, vk_create_info))
} else {
None
};
#[cfg(target_os = "android")]
let android_sdk_version = {
let properties = android_system_properties::AndroidSystemProperties::new();
if let Some(val) = properties.get("ro.build.version.sdk") {
match val.parse::<u32>() {
Ok(sdk_ver) => sdk_ver,
Err(err) => {
log::error!(
"Couldn't parse Android's ro.build.version.sdk system property ({val}): {err}"
);
0
}
}
} else {
log::error!("Couldn't read Android's ro.build.version.sdk system property");
0
}
};
#[cfg(not(target_os = "android"))]
let android_sdk_version = 0;
let mut flags = vk::InstanceCreateFlags::empty();
if extensions.contains(&ash::vk::KhrPortabilityEnumerationFn::name()) {
flags |= vk::InstanceCreateFlags::ENUMERATE_PORTABILITY_KHR;
}
let vk_instance = {
let str_pointers = layers
.iter()
.chain(extensions.iter())
.map(|&s: &&'static _| {
s.as_ptr()
})
.collect::<Vec<_>>();
let mut create_info = vk::InstanceCreateInfo::builder()
.flags(flags)
.application_info(&app_info)
.enabled_layer_names(&str_pointers[..layers.len()])
.enabled_extension_names(&str_pointers[layers.len()..]);
if let Some(&mut (_, ref mut vk_create_info)) = debug_utils.as_mut() {
create_info = create_info.push_next(vk_create_info);
}
let mut validation_features;
let mut validation_feature_list: ArrayVec<_, 3>;
if validation_features_are_enabled {
validation_feature_list = ArrayVec::new();
validation_feature_list
.push(vk::ValidationFeatureEnableEXT::SYNCHRONIZATION_VALIDATION);
if should_enable_gpu_based_validation {
validation_feature_list.push(vk::ValidationFeatureEnableEXT::GPU_ASSISTED);
validation_feature_list
.push(vk::ValidationFeatureEnableEXT::GPU_ASSISTED_RESERVE_BINDING_SLOT);
}
validation_features = vk::ValidationFeaturesEXT::builder()
.enabled_validation_features(&validation_feature_list);
create_info = create_info.push_next(&mut validation_features);
}
unsafe {
profiling::scope!("vkCreateInstance");
entry.create_instance(&create_info, None)
}
.map_err(|e| {
crate::InstanceError::with_source(
String::from("Entry::create_instance() failed"),
e,
)
})?
};
unsafe {
Self::from_raw(
entry,
vk_instance,
instance_api_version,
android_sdk_version,
debug_utils.map(|(i, _)| i),
extensions,
desc.flags,
has_nv_optimus,
Some(Box::new(())), )
}
}
unsafe fn create_surface(
&self,
display_handle: raw_window_handle::RawDisplayHandle,
window_handle: raw_window_handle::RawWindowHandle,
) -> Result<super::Surface, crate::InstanceError> {
use raw_window_handle::{RawDisplayHandle as Rdh, RawWindowHandle as Rwh};
match (window_handle, display_handle) {
(Rwh::Wayland(handle), Rdh::Wayland(display)) => {
self.create_surface_from_wayland(display.display.as_ptr(), handle.surface.as_ptr())
}
(Rwh::Xlib(handle), Rdh::Xlib(display)) => {
let display = display.display.expect("Display pointer is not set.");
self.create_surface_from_xlib(display.as_ptr() as *mut *const c_void, handle.window)
}
(Rwh::Xcb(handle), Rdh::Xcb(display)) => {
let connection = display.connection.expect("Pointer to X-Server is not set.");
self.create_surface_from_xcb(connection.as_ptr(), handle.window.get())
}
(Rwh::AndroidNdk(handle), _) => {
self.create_surface_android(handle.a_native_window.as_ptr())
}
#[cfg(windows)]
(Rwh::Win32(handle), _) => {
use winapi::um::libloaderapi::GetModuleHandleW;
let hinstance = unsafe { GetModuleHandleW(std::ptr::null()) };
self.create_surface_from_hwnd(hinstance as *mut _, handle.hwnd.get() as *mut _)
}
#[cfg(all(target_os = "macos", feature = "metal"))]
(Rwh::AppKit(handle), _)
if self.shared.extensions.contains(&ext::MetalSurface::name()) =>
{
self.create_surface_from_view(handle.ns_view.as_ptr())
}
#[cfg(all(target_os = "ios", feature = "metal"))]
(Rwh::UiKit(handle), _)
if self.shared.extensions.contains(&ext::MetalSurface::name()) =>
{
self.create_surface_from_view(handle.ui_view.as_ptr())
}
(_, _) => Err(crate::InstanceError::new(format!(
"window handle {window_handle:?} is not a Vulkan-compatible handle"
))),
}
}
unsafe fn destroy_surface(&self, surface: super::Surface) {
unsafe { surface.functor.destroy_surface(surface.raw, None) };
}
unsafe fn enumerate_adapters(&self) -> Vec<crate::ExposedAdapter<super::Api>> {
use crate::auxil::db;
let raw_devices = match unsafe { self.shared.raw.enumerate_physical_devices() } {
Ok(devices) => devices,
Err(err) => {
log::error!("enumerate_adapters: {}", err);
Vec::new()
}
};
let mut exposed_adapters = raw_devices
.into_iter()
.flat_map(|device| self.expose_adapter(device))
.collect::<Vec<_>>();
let has_nvidia_dgpu = exposed_adapters.iter().any(|exposed| {
exposed.info.device_type == wgt::DeviceType::DiscreteGpu
&& exposed.info.vendor == db::nvidia::VENDOR
});
if cfg!(target_os = "linux") && has_nvidia_dgpu && self.shared.has_nv_optimus {
for exposed in exposed_adapters.iter_mut() {
if exposed.info.device_type == wgt::DeviceType::IntegratedGpu
&& exposed.info.vendor == db::intel::VENDOR
{
if let Some(version) = exposed.info.driver_info.split_once("Mesa ").map(|s| {
let mut components = s.1.split('.');
let major = components.next().and_then(|s| u8::from_str(s).ok());
let minor = components.next().and_then(|s| u8::from_str(s).ok());
if let (Some(major), Some(minor)) = (major, minor) {
(major, minor)
} else {
(0, 0)
}
}) {
if version < (21, 2) {
log::warn!(
"Disabling presentation on '{}' (id {:?}) due to NV Optimus and Intel Mesa < v21.2",
exposed.info.name,
exposed.adapter.raw
);
exposed.adapter.private_caps.can_present = false;
}
}
}
}
}
exposed_adapters
}
}
impl crate::Surface for super::Surface {
type A = super::Api;
unsafe fn configure(
&self,
device: &super::Device,
config: &crate::SurfaceConfiguration,
) -> Result<(), crate::SurfaceError> {
let mut swap_chain = self.swapchain.write();
let old = swap_chain
.take()
.map(|sc| unsafe { sc.release_resources(&device.shared.raw) });
let swapchain = unsafe { device.create_swapchain(self, config, old)? };
*swap_chain = Some(swapchain);
Ok(())
}
unsafe fn unconfigure(&self, device: &super::Device) {
if let Some(sc) = self.swapchain.write().take() {
let swapchain = unsafe { sc.release_resources(&device.shared.raw) };
unsafe { swapchain.functor.destroy_swapchain(swapchain.raw, None) };
}
}
unsafe fn acquire_texture(
&self,
timeout: Option<std::time::Duration>,
fence: &super::Fence,
) -> Result<Option<crate::AcquiredSurfaceTexture<super::Api>>, crate::SurfaceError> {
let mut swapchain = self.swapchain.write();
let swapchain = swapchain.as_mut().unwrap();
let mut timeout_ns = match timeout {
Some(duration) => duration.as_nanos() as u64,
None => u64::MAX,
};
if cfg!(target_os = "android") && self.instance.android_sdk_version < 30 {
timeout_ns = u64::MAX;
}
let swapchain_semaphores_arc = swapchain.get_surface_semaphores();
let locked_swapchain_semaphores = swapchain_semaphores_arc
.try_lock()
.expect("Failed to lock a SwapchainSemaphores.");
swapchain.device.wait_for_fence(
fence,
locked_swapchain_semaphores.previously_used_submission_index,
timeout_ns,
)?;
let (index, suboptimal) = match unsafe {
profiling::scope!("vkAcquireNextImageKHR");
swapchain.functor.acquire_next_image(
swapchain.raw,
timeout_ns,
locked_swapchain_semaphores.acquire,
vk::Fence::null(),
)
} {
#[cfg(target_os = "android")]
Ok((index, _)) => (index, false),
#[cfg(not(target_os = "android"))]
Ok(pair) => pair,
Err(error) => {
return match error {
vk::Result::TIMEOUT => Ok(None),
vk::Result::NOT_READY | vk::Result::ERROR_OUT_OF_DATE_KHR => {
Err(crate::SurfaceError::Outdated)
}
vk::Result::ERROR_SURFACE_LOST_KHR => Err(crate::SurfaceError::Lost),
other => Err(crate::DeviceError::from(other).into()),
}
}
};
drop(locked_swapchain_semaphores);
swapchain.advance_surface_semaphores();
if swapchain.device.vendor_id == crate::auxil::db::intel::VENDOR && index > 0x100 {
return Err(crate::SurfaceError::Outdated);
}
let raw_flags = if swapchain
.raw_flags
.contains(vk::SwapchainCreateFlagsKHR::MUTABLE_FORMAT)
{
vk::ImageCreateFlags::MUTABLE_FORMAT | vk::ImageCreateFlags::EXTENDED_USAGE
} else {
vk::ImageCreateFlags::empty()
};
let texture = super::SurfaceTexture {
index,
texture: super::Texture {
raw: swapchain.images[index as usize],
drop_guard: None,
block: None,
usage: swapchain.config.usage,
format: swapchain.config.format,
raw_flags,
copy_size: crate::CopyExtent {
width: swapchain.config.extent.width,
height: swapchain.config.extent.height,
depth: 1,
},
view_formats: swapchain.view_formats.clone(),
},
surface_semaphores: swapchain_semaphores_arc,
};
Ok(Some(crate::AcquiredSurfaceTexture {
texture,
suboptimal,
}))
}
unsafe fn discard_texture(&self, _texture: super::SurfaceTexture) {}
}