diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/gfx-backend-vulkan/src/lib.rs | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/gfx-backend-vulkan/src/lib.rs')
-rw-r--r-- | third_party/rust/gfx-backend-vulkan/src/lib.rs | 1575 |
1 files changed, 1575 insertions, 0 deletions
diff --git a/third_party/rust/gfx-backend-vulkan/src/lib.rs b/third_party/rust/gfx-backend-vulkan/src/lib.rs new file mode 100644 index 0000000000..150fcac866 --- /dev/null +++ b/third_party/rust/gfx-backend-vulkan/src/lib.rs @@ -0,0 +1,1575 @@ +#![allow(non_snake_case)] + +#[macro_use] +extern crate log; +#[macro_use] +extern crate lazy_static; + +#[cfg(target_os = "macos")] +#[macro_use] +extern crate objc; + +use ash::extensions::{ + self, + ext::{DebugReport, DebugUtils}, + khr::DrawIndirectCount, +}; +use ash::extensions::{khr::Swapchain, nv::MeshShader}; +use ash::version::{DeviceV1_0, EntryV1_0, InstanceV1_0}; +use ash::vk; +#[cfg(not(feature = "use-rtld-next"))] +use ash::{Entry, LoadingError}; + +use hal::{ + adapter, + device::{CreationError as DeviceCreationError, DeviceLost, OutOfMemory, SurfaceLost}, + format, image, memory, + pso::{PatchSize, PipelineStage}, + queue, + window::{PresentError, Suboptimal}, + Features, Hints, Limits, +}; + +use std::borrow::{Borrow, Cow}; +use std::ffi::{CStr, CString}; +use std::sync::Arc; +use std::{fmt, mem, slice}; + +#[cfg(feature = "use-rtld-next")] +use ash::{EntryCustom, LoadingError}; +#[cfg(feature = "use-rtld-next")] +use shared_library::dynamic_library::{DynamicLibrary, SpecialHandles}; + +mod command; +mod conv; +mod device; +mod info; +mod native; +mod pool; +mod window; + +// CStr's cannot be constant yet, until const fn lands we need to use a lazy_static +lazy_static! { + static ref LAYERS: Vec<&'static CStr> = if cfg!(debug_assertions) { + vec![CStr::from_bytes_with_nul(b"VK_LAYER_KHRONOS_validation\0").unwrap()] + } else { + vec![] + }; + static ref EXTENSIONS: Vec<&'static CStr> = if cfg!(debug_assertions) { + vec![ + DebugUtils::name(), + DebugReport::name(), + *KHR_GET_PHYSICAL_DEVICE_PROPERTIES2, + ] + } else { + vec![*KHR_GET_PHYSICAL_DEVICE_PROPERTIES2] + }; + static ref DEVICE_EXTENSIONS: Vec<&'static CStr> = vec![extensions::khr::Swapchain::name()]; + static ref SURFACE_EXTENSIONS: Vec<&'static CStr> = vec![ + extensions::khr::Surface::name(), + // Platform-specific WSI extensions + #[cfg(all(unix, not(target_os = "android"), not(target_os = "macos")))] + extensions::khr::XlibSurface::name(), + #[cfg(all(unix, not(target_os = "android"), not(target_os = "macos")))] + extensions::khr::XcbSurface::name(), + #[cfg(all(unix, not(target_os = "android"), not(target_os = "macos")))] + extensions::khr::WaylandSurface::name(), + #[cfg(target_os = "android")] + extensions::khr::AndroidSurface::name(), + #[cfg(target_os = "windows")] + extensions::khr::Win32Surface::name(), + #[cfg(target_os = "macos")] + extensions::mvk::MacOSSurface::name(), + ]; + static ref AMD_NEGATIVE_VIEWPORT_HEIGHT: &'static CStr = + CStr::from_bytes_with_nul(b"VK_AMD_negative_viewport_height\0").unwrap(); + static ref KHR_MAINTENANCE1: &'static CStr = + CStr::from_bytes_with_nul(b"VK_KHR_maintenance1\0").unwrap(); + static ref KHR_MAINTENANCE3: &'static CStr = + CStr::from_bytes_with_nul(b"VK_KHR_maintenance3\0").unwrap(); + static ref KHR_SAMPLER_MIRROR_MIRROR_CLAMP_TO_EDGE : &'static CStr = + CStr::from_bytes_with_nul(b"VK_KHR_sampler_mirror_clamp_to_edge\0").unwrap(); + static ref KHR_GET_PHYSICAL_DEVICE_PROPERTIES2: &'static CStr = + CStr::from_bytes_with_nul(b"VK_KHR_get_physical_device_properties2\0").unwrap(); + static ref KHR_DRAW_INDIRECT_COUNT: &'static CStr = + CStr::from_bytes_with_nul(b"VK_KHR_draw_indirect_count\0").unwrap(); + static ref EXT_DESCRIPTOR_INDEXING: &'static CStr = + CStr::from_bytes_with_nul(b"VK_EXT_descriptor_indexing\0").unwrap(); + static ref MESH_SHADER: &'static CStr = MeshShader::name(); +} + +#[cfg(not(feature = "use-rtld-next"))] +lazy_static! { + // Entry function pointers + pub static ref VK_ENTRY: Result<Entry, LoadingError> = Entry::new(); +} + +#[cfg(feature = "use-rtld-next")] +lazy_static! { + // Entry function pointers + pub static ref VK_ENTRY: Result<EntryCustom<V1_0, ()>, LoadingError> + = EntryCustom::new_custom( + || Ok(()), + |_, name| unsafe { + DynamicLibrary::symbol_special(SpecialHandles::Next, &*name.to_string_lossy()) + .unwrap_or(ptr::null_mut()) + } + ); +} + +pub struct RawInstance { + inner: ash::Instance, + debug_messenger: Option<DebugMessenger>, + get_physical_device_properties: Option<vk::KhrGetPhysicalDeviceProperties2Fn>, +} + +pub enum DebugMessenger { + Utils(DebugUtils, vk::DebugUtilsMessengerEXT), + Report(DebugReport, vk::DebugReportCallbackEXT), +} + +impl Drop for RawInstance { + fn drop(&mut self) { + unsafe { + #[cfg(debug_assertions)] + { + match self.debug_messenger { + Some(DebugMessenger::Utils(ref ext, callback)) => { + ext.destroy_debug_utils_messenger(callback, None) + } + Some(DebugMessenger::Report(ref ext, callback)) => { + ext.destroy_debug_report_callback(callback, None) + } + None => {} + } + } + + self.inner.destroy_instance(None); + } + } +} + +pub struct Instance { + pub raw: Arc<RawInstance>, + + /// Supported extensions of this instance. + pub extensions: Vec<&'static CStr>, +} + +impl fmt::Debug for Instance { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("Instance") + } +} + +fn map_queue_type(flags: vk::QueueFlags) -> queue::QueueType { + if flags.contains(vk::QueueFlags::GRAPHICS | vk::QueueFlags::COMPUTE) { + // TRANSFER_BIT optional + queue::QueueType::General + } else if flags.contains(vk::QueueFlags::GRAPHICS) { + // TRANSFER_BIT optional + queue::QueueType::Graphics + } else if flags.contains(vk::QueueFlags::COMPUTE) { + // TRANSFER_BIT optional + queue::QueueType::Compute + } else if flags.contains(vk::QueueFlags::TRANSFER) { + queue::QueueType::Transfer + } else { + // TODO: present only queues? + unimplemented!() + } +} + +unsafe fn display_debug_utils_label_ext( + label_structs: *mut vk::DebugUtilsLabelEXT, + count: usize, +) -> Option<String> { + if count == 0 { + return None; + } + + Some( + slice::from_raw_parts::<vk::DebugUtilsLabelEXT>(label_structs, count) + .iter() + .flat_map(|dul_obj| { + dul_obj + .p_label_name + .as_ref() + .map(|lbl| CStr::from_ptr(lbl).to_string_lossy().into_owned()) + }) + .collect::<Vec<String>>() + .join(", "), + ) +} + +unsafe fn display_debug_utils_object_name_info_ext( + info_structs: *mut vk::DebugUtilsObjectNameInfoEXT, + count: usize, +) -> Option<String> { + if count == 0 { + return None; + } + + //TODO: use color field of vk::DebugUtilsLabelsExt in a meaningful way? + Some( + slice::from_raw_parts::<vk::DebugUtilsObjectNameInfoEXT>(info_structs, count) + .iter() + .map(|obj_info| { + let object_name = obj_info + .p_object_name + .as_ref() + .map(|name| CStr::from_ptr(name).to_string_lossy().into_owned()); + + match object_name { + Some(name) => format!( + "(type: {:?}, hndl: {}, name: {})", + obj_info.object_type, + &obj_info.object_handle.to_string(), + name + ), + None => format!( + "(type: {:?}, hndl: {})", + obj_info.object_type, + &obj_info.object_handle.to_string() + ), + } + }) + .collect::<Vec<String>>() + .join(", "), + ) +} + +unsafe extern "system" fn debug_utils_messenger_callback( + message_severity: vk::DebugUtilsMessageSeverityFlagsEXT, + message_type: vk::DebugUtilsMessageTypeFlagsEXT, + p_callback_data: *const vk::DebugUtilsMessengerCallbackDataEXT, + _user_data: *mut std::os::raw::c_void, +) -> vk::Bool32 { + let callback_data = *p_callback_data; + + let message_severity = match message_severity { + vk::DebugUtilsMessageSeverityFlagsEXT::ERROR => log::Level::Error, + vk::DebugUtilsMessageSeverityFlagsEXT::WARNING => log::Level::Warn, + vk::DebugUtilsMessageSeverityFlagsEXT::INFO => log::Level::Info, + vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE => log::Level::Trace, + _ => log::Level::Warn, + }; + let message_type = &format!("{:?}", message_type); + let message_id_number: i32 = callback_data.message_id_number as i32; + + let message_id_name = if callback_data.p_message_id_name.is_null() { + Cow::from("") + } else { + CStr::from_ptr(callback_data.p_message_id_name).to_string_lossy() + }; + + let message = if callback_data.p_message.is_null() { + Cow::from("") + } else { + CStr::from_ptr(callback_data.p_message).to_string_lossy() + }; + + let additional_info: [(&str, Option<String>); 3] = [ + ( + "queue info", + display_debug_utils_label_ext( + callback_data.p_queue_labels as *mut _, + callback_data.queue_label_count as usize, + ), + ), + ( + "cmd buf info", + display_debug_utils_label_ext( + callback_data.p_cmd_buf_labels as *mut _, + callback_data.cmd_buf_label_count as usize, + ), + ), + ( + "object info", + display_debug_utils_object_name_info_ext( + callback_data.p_objects as *mut _, + callback_data.object_count as usize, + ), + ), + ]; + + log!(message_severity, "{}\n", { + let mut msg = format!( + "\n{} [{} ({})] : {}", + message_type, + message_id_name, + &message_id_number.to_string(), + message + ); + + #[allow(array_into_iter)] + for (info_label, info) in additional_info.into_iter() { + match info { + Some(data) => { + msg = format!("{}\n{}: {}", msg, info_label, data); + } + None => {} + } + } + + msg + }); + + vk::FALSE +} + +unsafe extern "system" fn debug_report_callback( + type_: vk::DebugReportFlagsEXT, + _: vk::DebugReportObjectTypeEXT, + _object: u64, + _location: usize, + _msg_code: i32, + layer_prefix: *const std::os::raw::c_char, + description: *const std::os::raw::c_char, + _user_data: *mut std::os::raw::c_void, +) -> vk::Bool32 { + let level = match type_ { + vk::DebugReportFlagsEXT::ERROR => log::Level::Error, + vk::DebugReportFlagsEXT::WARNING => log::Level::Warn, + vk::DebugReportFlagsEXT::INFORMATION => log::Level::Info, + vk::DebugReportFlagsEXT::DEBUG => log::Level::Debug, + _ => log::Level::Warn, + }; + + let layer_prefix = CStr::from_ptr(layer_prefix).to_str().unwrap(); + let description = CStr::from_ptr(description).to_str().unwrap(); + log!(level, "[{}] {}", layer_prefix, description); + vk::FALSE +} + +impl hal::Instance<Backend> for Instance { + fn create(name: &str, version: u32) -> Result<Self, hal::UnsupportedBackend> { + // TODO: return errors instead of panic + let entry = VK_ENTRY.as_ref().map_err(|e| { + info!("Missing Vulkan entry points: {:?}", e); + hal::UnsupportedBackend + })?; + + let app_name = CString::new(name).unwrap(); + let app_info = vk::ApplicationInfo::builder() + .application_name(app_name.as_c_str()) + .application_version(version) + .engine_name(CStr::from_bytes_with_nul(b"gfx-rs\0").unwrap()) + .engine_version(1) + .api_version(vk::make_version(1, 0, 0)); + + let instance_extensions = entry + .enumerate_instance_extension_properties() + .map_err(|e| { + info!("Unable to enumerate instance extensions: {:?}", e); + hal::UnsupportedBackend + })?; + + let instance_layers = entry.enumerate_instance_layer_properties().map_err(|e| { + info!("Unable to enumerate instance layers: {:?}", e); + hal::UnsupportedBackend + })?; + + // Check our extensions against the available extensions + let extensions = SURFACE_EXTENSIONS + .iter() + .chain(EXTENSIONS.iter()) + .filter_map(|&ext| { + instance_extensions + .iter() + .find(|inst_ext| unsafe { + CStr::from_ptr(inst_ext.extension_name.as_ptr()) == ext + }) + .map(|_| ext) + .or_else(|| { + info!("Unable to find extension: {}", ext.to_string_lossy()); + None + }) + }) + .collect::<Vec<&CStr>>(); + + // Check requested layers against the available layers + let layers = LAYERS + .iter() + .filter_map(|&layer| { + instance_layers + .iter() + .find(|inst_layer| unsafe { + CStr::from_ptr(inst_layer.layer_name.as_ptr()) == layer + }) + .map(|_| layer) + .or_else(|| { + warn!("Unable to find layer: {}", layer.to_string_lossy()); + None + }) + }) + .collect::<Vec<&CStr>>(); + + let instance = { + let cstrings = layers + .iter() + .chain(extensions.iter()) + .map(|&s| CString::from(s)) + .collect::<Vec<_>>(); + + let str_pointers = cstrings.iter().map(|s| s.as_ptr()).collect::<Vec<_>>(); + + let create_info = vk::InstanceCreateInfo::builder() + .flags(vk::InstanceCreateFlags::empty()) + .application_info(&app_info) + .enabled_layer_names(&str_pointers[..layers.len()]) + .enabled_extension_names(&str_pointers[layers.len()..]); + + unsafe { entry.create_instance(&create_info, None) }.map_err(|e| { + warn!("Unable to create Vulkan instance: {:?}", e); + hal::UnsupportedBackend + })? + }; + + let get_physical_device_properties = extensions + .iter() + .find(|&&ext| ext == *KHR_GET_PHYSICAL_DEVICE_PROPERTIES2) + .map(|_| { + vk::KhrGetPhysicalDeviceProperties2Fn::load(|name| unsafe { + std::mem::transmute( + entry.get_instance_proc_addr(instance.handle(), name.as_ptr()), + ) + }) + }); + + #[cfg(debug_assertions)] + let debug_messenger = { + // make sure VK_EXT_debug_utils is available + if instance_extensions.iter().any(|props| unsafe { + CStr::from_ptr(props.extension_name.as_ptr()) == DebugUtils::name() + }) { + let ext = DebugUtils::new(entry, &instance); + let info = vk::DebugUtilsMessengerCreateInfoEXT::builder() + .flags(vk::DebugUtilsMessengerCreateFlagsEXT::empty()) + .message_severity(vk::DebugUtilsMessageSeverityFlagsEXT::all()) + .message_type(vk::DebugUtilsMessageTypeFlagsEXT::all()) + .pfn_user_callback(Some(debug_utils_messenger_callback)); + let handle = unsafe { ext.create_debug_utils_messenger(&info, None) }.unwrap(); + Some(DebugMessenger::Utils(ext, handle)) + } else if instance_extensions.iter().any(|props| unsafe { + CStr::from_ptr(props.extension_name.as_ptr()) == DebugReport::name() + }) { + let ext = DebugReport::new(entry, &instance); + let info = vk::DebugReportCallbackCreateInfoEXT::builder() + .flags(vk::DebugReportFlagsEXT::all()) + .pfn_callback(Some(debug_report_callback)); + let handle = unsafe { ext.create_debug_report_callback(&info, None) }.unwrap(); + Some(DebugMessenger::Report(ext, handle)) + } else { + None + } + }; + #[cfg(not(debug_assertions))] + let debug_messenger = None; + + Ok(Instance { + raw: Arc::new(RawInstance { + inner: instance, + debug_messenger, + get_physical_device_properties, + }), + extensions, + }) + } + + fn enumerate_adapters(&self) -> Vec<adapter::Adapter<Backend>> { + let devices = match unsafe { self.raw.inner.enumerate_physical_devices() } { + Ok(devices) => devices, + Err(err) => { + error!("Could not enumerate physical devices! {}", err); + vec![] + } + }; + + devices + .into_iter() + .map(|device| { + let extensions = + unsafe { self.raw.inner.enumerate_device_extension_properties(device) } + .unwrap(); + let properties = unsafe { self.raw.inner.get_physical_device_properties(device) }; + let info = adapter::AdapterInfo { + name: unsafe { + CStr::from_ptr(properties.device_name.as_ptr()) + .to_str() + .unwrap_or("Unknown") + .to_owned() + }, + vendor: properties.vendor_id as usize, + device: properties.device_id as usize, + device_type: match properties.device_type { + ash::vk::PhysicalDeviceType::OTHER => adapter::DeviceType::Other, + ash::vk::PhysicalDeviceType::INTEGRATED_GPU => { + adapter::DeviceType::IntegratedGpu + } + ash::vk::PhysicalDeviceType::DISCRETE_GPU => { + adapter::DeviceType::DiscreteGpu + } + ash::vk::PhysicalDeviceType::VIRTUAL_GPU => adapter::DeviceType::VirtualGpu, + ash::vk::PhysicalDeviceType::CPU => adapter::DeviceType::Cpu, + _ => adapter::DeviceType::Other, + }, + }; + let physical_device = PhysicalDevice { + instance: self.raw.clone(), + handle: device, + extensions, + properties, + known_memory_flags: vk::MemoryPropertyFlags::DEVICE_LOCAL + | vk::MemoryPropertyFlags::HOST_VISIBLE + | vk::MemoryPropertyFlags::HOST_COHERENT + | vk::MemoryPropertyFlags::HOST_CACHED + | vk::MemoryPropertyFlags::LAZILY_ALLOCATED, + }; + let queue_families = unsafe { + self.raw + .inner + .get_physical_device_queue_family_properties(device) + .into_iter() + .enumerate() + .map(|(i, properties)| QueueFamily { + properties, + device, + index: i as u32, + }) + .collect() + }; + + adapter::Adapter { + info, + physical_device, + queue_families, + } + }) + .collect() + } + + unsafe fn create_surface( + &self, + has_handle: &impl raw_window_handle::HasRawWindowHandle, + ) -> Result<window::Surface, hal::window::InitError> { + use raw_window_handle::RawWindowHandle; + + match has_handle.raw_window_handle() { + #[cfg(all( + unix, + not(target_os = "android"), + not(target_os = "macos"), + not(target_os = "solaris") + ))] + RawWindowHandle::Wayland(handle) + if self + .extensions + .contains(&extensions::khr::WaylandSurface::name()) => + { + Ok(self.create_surface_from_wayland(handle.display, handle.surface)) + } + #[cfg(all( + unix, + not(target_os = "android"), + not(target_os = "macos"), + not(target_os = "solaris") + ))] + RawWindowHandle::Xlib(handle) + if self + .extensions + .contains(&extensions::khr::XlibSurface::name()) => + { + Ok(self.create_surface_from_xlib(handle.display as *mut _, handle.window)) + } + #[cfg(all( + unix, + not(target_os = "android"), + not(target_os = "macos"), + not(target_os = "ios") + ))] + RawWindowHandle::Xcb(handle) + if self + .extensions + .contains(&extensions::khr::XcbSurface::name()) => + { + Ok(self.create_surface_from_xcb(handle.connection as *mut _, handle.window)) + } + #[cfg(target_os = "android")] + RawWindowHandle::Android(handle) => { + Ok(self.create_surface_android(handle.a_native_window)) + } + #[cfg(windows)] + RawWindowHandle::Windows(handle) => { + use winapi::um::libloaderapi::GetModuleHandleW; + + let hinstance = GetModuleHandleW(std::ptr::null()); + Ok(self.create_surface_from_hwnd(hinstance as *mut _, handle.hwnd)) + } + #[cfg(target_os = "macos")] + RawWindowHandle::MacOS(handle) => Ok(self.create_surface_from_ns_view(handle.ns_view)), + _ => Err(hal::window::InitError::UnsupportedWindowHandle), + } + } + + unsafe fn destroy_surface(&self, surface: window::Surface) { + surface + .raw + .functor + .destroy_surface(surface.raw.handle, None); + } +} + +#[derive(Debug, Clone)] +pub struct QueueFamily { + properties: vk::QueueFamilyProperties, + device: vk::PhysicalDevice, + index: u32, +} + +impl queue::QueueFamily for QueueFamily { + fn queue_type(&self) -> queue::QueueType { + map_queue_type(self.properties.queue_flags) + } + fn max_queues(&self) -> usize { + self.properties.queue_count as _ + } + fn id(&self) -> queue::QueueFamilyId { + queue::QueueFamilyId(self.index as _) + } +} + +pub struct PhysicalDevice { + instance: Arc<RawInstance>, + handle: vk::PhysicalDevice, + extensions: Vec<vk::ExtensionProperties>, + properties: vk::PhysicalDeviceProperties, + known_memory_flags: vk::MemoryPropertyFlags, +} + +impl PhysicalDevice { + fn supports_extension(&self, extension: &CStr) -> bool { + self.extensions + .iter() + .any(|ep| unsafe { CStr::from_ptr(ep.extension_name.as_ptr()) } == extension) + } +} + +impl fmt::Debug for PhysicalDevice { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("PhysicalDevice") + } +} + +pub struct DeviceCreationFeatures { + core: vk::PhysicalDeviceFeatures, + descriptor_indexing: Option<vk::PhysicalDeviceDescriptorIndexingFeaturesEXT>, + mesh_shaders: Option<vk::PhysicalDeviceMeshShaderFeaturesNV>, +} + +impl adapter::PhysicalDevice<Backend> for PhysicalDevice { + unsafe fn open( + &self, + families: &[(&QueueFamily, &[queue::QueuePriority])], + requested_features: Features, + ) -> Result<adapter::Gpu<Backend>, DeviceCreationError> { + let family_infos = families + .iter() + .map(|&(family, priorities)| { + vk::DeviceQueueCreateInfo::builder() + .flags(vk::DeviceQueueCreateFlags::empty()) + .queue_family_index(family.index) + .queue_priorities(priorities) + .build() + }) + .collect::<Vec<_>>(); + + if !self.features().contains(requested_features) { + return Err(DeviceCreationError::MissingFeature); + } + + let maintenance_level = if self.supports_extension(*KHR_MAINTENANCE1) { + 1 + } else { + 0 + }; + let mut enabled_features = conv::map_device_features(requested_features); + let enabled_extensions = DEVICE_EXTENSIONS + .iter() + .cloned() + .chain( + if requested_features.contains(Features::NDC_Y_UP) && maintenance_level == 0 { + Some(*AMD_NEGATIVE_VIEWPORT_HEIGHT) + } else { + None + }, + ) + .chain(match maintenance_level { + 0 => None, + 1 => Some(*KHR_MAINTENANCE1), + _ => unreachable!(), + }) + .chain( + if requested_features.intersects( + Features::SAMPLED_TEXTURE_DESCRIPTOR_INDEXING + | Features::STORAGE_TEXTURE_DESCRIPTOR_INDEXING + | Features::UNSIZED_DESCRIPTOR_ARRAY, + ) { + vec![*KHR_MAINTENANCE3, *EXT_DESCRIPTOR_INDEXING] + } else { + vec![] + }, + ) + .chain( + if requested_features.intersects(Features::TASK_SHADER | Features::MESH_SHADER) { + Some(*MESH_SHADER) + } else { + None + }, + ) + .chain( + if requested_features.contains(Features::DRAW_INDIRECT_COUNT) { + Some(*KHR_DRAW_INDIRECT_COUNT) + } else { + None + }, + ); + + let valid_ash_memory_types = { + let mem_properties = self + .instance + .inner + .get_physical_device_memory_properties(self.handle); + mem_properties.memory_types[..mem_properties.memory_type_count as usize] + .iter() + .enumerate() + .fold(0, |u, (i, mem)| { + if self.known_memory_flags.contains(mem.property_flags) { + u | (1 << i) + } else { + u + } + }) + }; + + // Create device + let device_raw = { + let cstrings = enabled_extensions.map(CString::from).collect::<Vec<_>>(); + + let str_pointers = cstrings.iter().map(|s| s.as_ptr()).collect::<Vec<_>>(); + + let info = vk::DeviceCreateInfo::builder() + .queue_create_infos(&family_infos) + .enabled_extension_names(&str_pointers) + .enabled_features(&enabled_features.core); + + let info = + if let Some(ref mut descriptor_indexing) = enabled_features.descriptor_indexing { + info.push_next(descriptor_indexing) + } else { + info + }; + + let info = if let Some(ref mut mesh_shaders) = enabled_features.mesh_shaders { + info.push_next(mesh_shaders) + } else { + info + }; + + match self.instance.inner.create_device(self.handle, &info, None) { + Ok(device) => device, + Err(e) => { + return Err(match e { + vk::Result::ERROR_OUT_OF_HOST_MEMORY => { + DeviceCreationError::OutOfMemory(OutOfMemory::Host) + } + vk::Result::ERROR_OUT_OF_DEVICE_MEMORY => { + DeviceCreationError::OutOfMemory(OutOfMemory::Device) + } + vk::Result::ERROR_INITIALIZATION_FAILED => { + DeviceCreationError::InitializationFailed + } + vk::Result::ERROR_DEVICE_LOST => DeviceCreationError::DeviceLost, + vk::Result::ERROR_TOO_MANY_OBJECTS => DeviceCreationError::TooManyObjects, + _ => unreachable!(), + }) + } + } + }; + + let swapchain_fn = Swapchain::new(&self.instance.inner, &device_raw); + + let mesh_fn = + if requested_features.intersects(Features::TASK_SHADER | Features::MESH_SHADER) { + Some(MeshShader::new(&self.instance.inner, &device_raw)) + } else { + None + }; + + let indirect_count_fn = if requested_features.contains(Features::DRAW_INDIRECT_COUNT) { + Some(DrawIndirectCount::new(&self.instance.inner, &device_raw)) + } else { + None + }; + + let device = Device { + shared: Arc::new(RawDevice { + raw: device_raw, + features: requested_features, + instance: Arc::clone(&self.instance), + extension_fns: DeviceExtensionFunctions { + mesh_shaders: mesh_fn, + draw_indirect_count: indirect_count_fn, + }, + maintenance_level, + }), + vendor_id: self.properties.vendor_id, + valid_ash_memory_types, + }; + + let device_arc = Arc::clone(&device.shared); + let queue_groups = families + .into_iter() + .map(|&(family, ref priorities)| { + let mut family_raw = + queue::QueueGroup::new(queue::QueueFamilyId(family.index as usize)); + for id in 0..priorities.len() { + let queue_raw = device_arc.raw.get_device_queue(family.index, id as _); + family_raw.add_queue(CommandQueue { + raw: Arc::new(queue_raw), + device: device_arc.clone(), + swapchain_fn: swapchain_fn.clone(), + }); + } + family_raw + }) + .collect(); + + Ok(adapter::Gpu { + device, + queue_groups, + }) + } + + fn format_properties(&self, format: Option<format::Format>) -> format::Properties { + let properties = unsafe { + self.instance.inner.get_physical_device_format_properties( + self.handle, + format.map_or(vk::Format::UNDEFINED, conv::map_format), + ) + }; + + format::Properties { + linear_tiling: conv::map_image_features(properties.linear_tiling_features), + optimal_tiling: conv::map_image_features(properties.optimal_tiling_features), + buffer_features: conv::map_buffer_features(properties.buffer_features), + } + } + + fn image_format_properties( + &self, + format: format::Format, + dimensions: u8, + tiling: image::Tiling, + usage: image::Usage, + view_caps: image::ViewCapabilities, + ) -> Option<image::FormatProperties> { + let format_properties = unsafe { + self.instance + .inner + .get_physical_device_image_format_properties( + self.handle, + conv::map_format(format), + match dimensions { + 1 => vk::ImageType::TYPE_1D, + 2 => vk::ImageType::TYPE_2D, + 3 => vk::ImageType::TYPE_3D, + _ => panic!("Unexpected image dimensionality: {}", dimensions), + }, + conv::map_tiling(tiling), + conv::map_image_usage(usage), + conv::map_view_capabilities(view_caps), + ) + }; + + match format_properties { + Ok(props) => Some(image::FormatProperties { + max_extent: image::Extent { + width: props.max_extent.width, + height: props.max_extent.height, + depth: props.max_extent.depth, + }, + max_levels: props.max_mip_levels as _, + max_layers: props.max_array_layers as _, + sample_count_mask: props.sample_counts.as_raw() as _, + max_resource_size: props.max_resource_size as _, + }), + Err(vk::Result::ERROR_FORMAT_NOT_SUPPORTED) => None, + Err(other) => { + error!("Unexpected error in `image_format_properties`: {:?}", other); + None + } + } + } + + fn memory_properties(&self) -> adapter::MemoryProperties { + let mem_properties = unsafe { + self.instance + .inner + .get_physical_device_memory_properties(self.handle) + }; + let memory_heaps = mem_properties.memory_heaps[..mem_properties.memory_heap_count as usize] + .iter() + .map(|mem| adapter::MemoryHeap { + size: mem.size, + flags: conv::map_memory_heap_flags(mem.flags), + }) + .collect(); + let memory_types = mem_properties.memory_types[..mem_properties.memory_type_count as usize] + .iter() + .filter_map(|mem| { + if self.known_memory_flags.contains(mem.property_flags) { + Some(adapter::MemoryType { + properties: conv::map_memory_properties(mem.property_flags), + heap_index: mem.heap_index as usize, + }) + } else { + warn!( + "Skipping memory type with unknown flags {:?}", + mem.property_flags + ); + None + } + }) + .collect(); + + adapter::MemoryProperties { + memory_heaps, + memory_types, + } + } + + fn features(&self) -> Features { + // see https://github.com/gfx-rs/gfx/issues/1930 + let is_windows_intel_dual_src_bug = cfg!(windows) + && self.properties.vendor_id == info::intel::VENDOR + && (self.properties.device_id & info::intel::DEVICE_KABY_LAKE_MASK + == info::intel::DEVICE_KABY_LAKE_MASK + || self.properties.device_id & info::intel::DEVICE_SKY_LAKE_MASK + == info::intel::DEVICE_SKY_LAKE_MASK); + + let mut descriptor_indexing_features = None; + let features = if let Some(ref get_device_properties) = + self.instance.get_physical_device_properties + { + let features = vk::PhysicalDeviceFeatures::builder().build(); + let mut features2 = vk::PhysicalDeviceFeatures2KHR::builder() + .features(features) + .build(); + + // Add extension infos to the p_next chain + if self.supports_extension(*EXT_DESCRIPTOR_INDEXING) { + descriptor_indexing_features = + Some(vk::PhysicalDeviceDescriptorIndexingFeaturesEXT::builder().build()); + + let mut_ref = descriptor_indexing_features.as_mut().unwrap(); + mut_ref.p_next = mem::replace(&mut features2.p_next, mut_ref as *mut _ as *mut _); + } + + unsafe { + get_device_properties + .get_physical_device_features2_khr(self.handle, &mut features2 as *mut _); + } + features2.features + } else { + unsafe { + self.instance + .inner + .get_physical_device_features(self.handle) + } + }; + + let mut bits = Features::empty() + | Features::TRIANGLE_FAN + | Features::SEPARATE_STENCIL_REF_VALUES + | Features::SAMPLER_MIP_LOD_BIAS + | Features::SAMPLER_BORDER_COLOR + | Features::MUTABLE_COMPARISON_SAMPLER + | Features::TEXTURE_DESCRIPTOR_ARRAY; + + if self.supports_extension(*AMD_NEGATIVE_VIEWPORT_HEIGHT) + || self.supports_extension(*KHR_MAINTENANCE1) + { + bits |= Features::NDC_Y_UP; + } + if self.supports_extension(*KHR_SAMPLER_MIRROR_MIRROR_CLAMP_TO_EDGE) { + bits |= Features::SAMPLER_MIRROR_CLAMP_EDGE; + } + if self.supports_extension(*KHR_DRAW_INDIRECT_COUNT) { + bits |= Features::DRAW_INDIRECT_COUNT + } + // This will only be some if the extension exists + if let Some(ref desc_indexing) = descriptor_indexing_features { + if desc_indexing.shader_sampled_image_array_non_uniform_indexing != 0 { + bits |= Features::SAMPLED_TEXTURE_DESCRIPTOR_INDEXING; + } + if desc_indexing.shader_storage_image_array_non_uniform_indexing != 0 { + bits |= Features::STORAGE_TEXTURE_DESCRIPTOR_INDEXING; + } + if desc_indexing.runtime_descriptor_array != 0 { + bits |= Features::UNSIZED_DESCRIPTOR_ARRAY; + } + } + + if features.robust_buffer_access != 0 { + bits |= Features::ROBUST_BUFFER_ACCESS; + } + if features.full_draw_index_uint32 != 0 { + bits |= Features::FULL_DRAW_INDEX_U32; + } + if features.image_cube_array != 0 { + bits |= Features::IMAGE_CUBE_ARRAY; + } + if features.independent_blend != 0 { + bits |= Features::INDEPENDENT_BLENDING; + } + if features.geometry_shader != 0 { + bits |= Features::GEOMETRY_SHADER; + } + if features.tessellation_shader != 0 { + bits |= Features::TESSELLATION_SHADER; + } + if features.sample_rate_shading != 0 { + bits |= Features::SAMPLE_RATE_SHADING; + } + if features.dual_src_blend != 0 && !is_windows_intel_dual_src_bug { + bits |= Features::DUAL_SRC_BLENDING; + } + if features.logic_op != 0 { + bits |= Features::LOGIC_OP; + } + if features.multi_draw_indirect != 0 { + bits |= Features::MULTI_DRAW_INDIRECT; + } + if features.draw_indirect_first_instance != 0 { + bits |= Features::DRAW_INDIRECT_FIRST_INSTANCE; + } + if features.depth_clamp != 0 { + bits |= Features::DEPTH_CLAMP; + } + if features.depth_bias_clamp != 0 { + bits |= Features::DEPTH_BIAS_CLAMP; + } + if features.fill_mode_non_solid != 0 { + bits |= Features::NON_FILL_POLYGON_MODE; + } + if features.depth_bounds != 0 { + bits |= Features::DEPTH_BOUNDS; + } + if features.wide_lines != 0 { + bits |= Features::LINE_WIDTH; + } + if features.large_points != 0 { + bits |= Features::POINT_SIZE; + } + if features.alpha_to_one != 0 { + bits |= Features::ALPHA_TO_ONE; + } + if features.multi_viewport != 0 { + bits |= Features::MULTI_VIEWPORTS; + } + if features.sampler_anisotropy != 0 { + bits |= Features::SAMPLER_ANISOTROPY; + } + if features.texture_compression_etc2 != 0 { + bits |= Features::FORMAT_ETC2; + } + if features.texture_compression_astc_ldr != 0 { + bits |= Features::FORMAT_ASTC_LDR; + } + if features.texture_compression_bc != 0 { + bits |= Features::FORMAT_BC; + } + if features.occlusion_query_precise != 0 { + bits |= Features::PRECISE_OCCLUSION_QUERY; + } + if features.pipeline_statistics_query != 0 { + bits |= Features::PIPELINE_STATISTICS_QUERY; + } + if features.vertex_pipeline_stores_and_atomics != 0 { + bits |= Features::VERTEX_STORES_AND_ATOMICS; + } + if features.fragment_stores_and_atomics != 0 { + bits |= Features::FRAGMENT_STORES_AND_ATOMICS; + } + if features.shader_tessellation_and_geometry_point_size != 0 { + bits |= Features::SHADER_TESSELLATION_AND_GEOMETRY_POINT_SIZE; + } + if features.shader_image_gather_extended != 0 { + bits |= Features::SHADER_IMAGE_GATHER_EXTENDED; + } + if features.shader_storage_image_extended_formats != 0 { + bits |= Features::SHADER_STORAGE_IMAGE_EXTENDED_FORMATS; + } + if features.shader_storage_image_multisample != 0 { + bits |= Features::SHADER_STORAGE_IMAGE_MULTISAMPLE; + } + if features.shader_storage_image_read_without_format != 0 { + bits |= Features::SHADER_STORAGE_IMAGE_READ_WITHOUT_FORMAT; + } + if features.shader_storage_image_write_without_format != 0 { + bits |= Features::SHADER_STORAGE_IMAGE_WRITE_WITHOUT_FORMAT; + } + if features.shader_uniform_buffer_array_dynamic_indexing != 0 { + bits |= Features::SHADER_UNIFORM_BUFFER_ARRAY_DYNAMIC_INDEXING; + } + if features.shader_sampled_image_array_dynamic_indexing != 0 { + bits |= Features::SHADER_SAMPLED_IMAGE_ARRAY_DYNAMIC_INDEXING; + } + if features.shader_storage_buffer_array_dynamic_indexing != 0 { + bits |= Features::SHADER_STORAGE_BUFFER_ARRAY_DYNAMIC_INDEXING; + } + if features.shader_storage_image_array_dynamic_indexing != 0 { + bits |= Features::SHADER_STORAGE_IMAGE_ARRAY_DYNAMIC_INDEXING; + } + if features.shader_clip_distance != 0 { + bits |= Features::SHADER_CLIP_DISTANCE; + } + if features.shader_cull_distance != 0 { + bits |= Features::SHADER_CULL_DISTANCE; + } + if features.shader_float64 != 0 { + bits |= Features::SHADER_FLOAT64; + } + if features.shader_int64 != 0 { + bits |= Features::SHADER_INT64; + } + if features.shader_int16 != 0 { + bits |= Features::SHADER_INT16; + } + if features.shader_resource_residency != 0 { + bits |= Features::SHADER_RESOURCE_RESIDENCY; + } + if features.shader_resource_min_lod != 0 { + bits |= Features::SHADER_RESOURCE_MIN_LOD; + } + if features.sparse_binding != 0 { + bits |= Features::SPARSE_BINDING; + } + if features.sparse_residency_buffer != 0 { + bits |= Features::SPARSE_RESIDENCY_BUFFER; + } + if features.sparse_residency_image2_d != 0 { + bits |= Features::SPARSE_RESIDENCY_IMAGE_2D; + } + if features.sparse_residency_image3_d != 0 { + bits |= Features::SPARSE_RESIDENCY_IMAGE_3D; + } + if features.sparse_residency2_samples != 0 { + bits |= Features::SPARSE_RESIDENCY_2_SAMPLES; + } + if features.sparse_residency4_samples != 0 { + bits |= Features::SPARSE_RESIDENCY_4_SAMPLES; + } + if features.sparse_residency8_samples != 0 { + bits |= Features::SPARSE_RESIDENCY_8_SAMPLES; + } + if features.sparse_residency16_samples != 0 { + bits |= Features::SPARSE_RESIDENCY_16_SAMPLES; + } + if features.sparse_residency_aliased != 0 { + bits |= Features::SPARSE_RESIDENCY_ALIASED; + } + if features.variable_multisample_rate != 0 { + bits |= Features::VARIABLE_MULTISAMPLE_RATE; + } + if features.inherited_queries != 0 { + bits |= Features::INHERITED_QUERIES; + } + if self.supports_extension(*MESH_SHADER) { + bits |= Features::TASK_SHADER; + bits |= Features::MESH_SHADER + } + + bits + } + + fn hints(&self) -> Hints { + Hints::BASE_VERTEX_INSTANCE_DRAWING + } + + fn limits(&self) -> Limits { + let limits = &self.properties.limits; + let max_group_count = limits.max_compute_work_group_count; + let max_group_size = limits.max_compute_work_group_size; + + Limits { + max_image_1d_size: limits.max_image_dimension1_d, + max_image_2d_size: limits.max_image_dimension2_d, + max_image_3d_size: limits.max_image_dimension3_d, + max_image_cube_size: limits.max_image_dimension_cube, + max_image_array_layers: limits.max_image_array_layers as _, + max_texel_elements: limits.max_texel_buffer_elements as _, + max_patch_size: limits.max_tessellation_patch_size as PatchSize, + max_viewports: limits.max_viewports as _, + max_viewport_dimensions: limits.max_viewport_dimensions, + max_framebuffer_extent: image::Extent { + width: limits.max_framebuffer_width, + height: limits.max_framebuffer_height, + depth: limits.max_framebuffer_layers, + }, + max_compute_work_group_count: [ + max_group_count[0] as _, + max_group_count[1] as _, + max_group_count[2] as _, + ], + max_compute_work_group_size: [ + max_group_size[0] as _, + max_group_size[1] as _, + max_group_size[2] as _, + ], + max_vertex_input_attributes: limits.max_vertex_input_attributes as _, + max_vertex_input_bindings: limits.max_vertex_input_bindings as _, + max_vertex_input_attribute_offset: limits.max_vertex_input_attribute_offset as _, + max_vertex_input_binding_stride: limits.max_vertex_input_binding_stride as _, + max_vertex_output_components: limits.max_vertex_output_components as _, + optimal_buffer_copy_offset_alignment: limits.optimal_buffer_copy_offset_alignment as _, + optimal_buffer_copy_pitch_alignment: limits.optimal_buffer_copy_row_pitch_alignment + as _, + min_texel_buffer_offset_alignment: limits.min_texel_buffer_offset_alignment as _, + min_uniform_buffer_offset_alignment: limits.min_uniform_buffer_offset_alignment as _, + min_storage_buffer_offset_alignment: limits.min_storage_buffer_offset_alignment as _, + framebuffer_color_sample_counts: limits.framebuffer_color_sample_counts.as_raw() as _, + framebuffer_depth_sample_counts: limits.framebuffer_depth_sample_counts.as_raw() as _, + framebuffer_stencil_sample_counts: limits.framebuffer_stencil_sample_counts.as_raw() + as _, + max_color_attachments: limits.max_color_attachments as _, + buffer_image_granularity: limits.buffer_image_granularity, + non_coherent_atom_size: limits.non_coherent_atom_size as _, + max_sampler_anisotropy: limits.max_sampler_anisotropy, + min_vertex_input_binding_stride_alignment: 1, + max_bound_descriptor_sets: limits.max_bound_descriptor_sets as _, + max_compute_shared_memory_size: limits.max_compute_shared_memory_size as _, + max_compute_work_group_invocations: limits.max_compute_work_group_invocations as _, + max_descriptor_set_input_attachments: limits.max_descriptor_set_input_attachments as _, + max_descriptor_set_sampled_images: limits.max_descriptor_set_sampled_images as _, + max_descriptor_set_samplers: limits.max_descriptor_set_samplers as _, + max_descriptor_set_storage_buffers: limits.max_descriptor_set_storage_buffers as _, + max_descriptor_set_storage_buffers_dynamic: limits + .max_descriptor_set_storage_buffers_dynamic + as _, + max_descriptor_set_storage_images: limits.max_descriptor_set_storage_images as _, + max_descriptor_set_uniform_buffers: limits.max_descriptor_set_uniform_buffers as _, + max_descriptor_set_uniform_buffers_dynamic: limits + .max_descriptor_set_uniform_buffers_dynamic + as _, + max_draw_indexed_index_value: limits.max_draw_indexed_index_value, + max_draw_indirect_count: limits.max_draw_indirect_count, + max_fragment_combined_output_resources: limits.max_fragment_combined_output_resources + as _, + max_fragment_dual_source_attachments: limits.max_fragment_dual_src_attachments as _, + max_fragment_input_components: limits.max_fragment_input_components as _, + max_fragment_output_attachments: limits.max_fragment_output_attachments as _, + max_framebuffer_layers: limits.max_framebuffer_layers as _, + max_geometry_input_components: limits.max_geometry_input_components as _, + max_geometry_output_components: limits.max_geometry_output_components as _, + max_geometry_output_vertices: limits.max_geometry_output_vertices as _, + max_geometry_shader_invocations: limits.max_geometry_shader_invocations as _, + max_geometry_total_output_components: limits.max_geometry_total_output_components as _, + max_memory_allocation_count: limits.max_memory_allocation_count as _, + max_per_stage_descriptor_input_attachments: limits + .max_per_stage_descriptor_input_attachments + as _, + max_per_stage_descriptor_sampled_images: limits.max_per_stage_descriptor_sampled_images + as _, + max_per_stage_descriptor_samplers: limits.max_per_stage_descriptor_samplers as _, + max_per_stage_descriptor_storage_buffers: limits + .max_per_stage_descriptor_storage_buffers + as _, + max_per_stage_descriptor_storage_images: limits.max_per_stage_descriptor_storage_images + as _, + max_per_stage_descriptor_uniform_buffers: limits + .max_per_stage_descriptor_uniform_buffers + as _, + max_per_stage_resources: limits.max_per_stage_resources as _, + max_push_constants_size: limits.max_push_constants_size as _, + max_sampler_allocation_count: limits.max_sampler_allocation_count as _, + max_sampler_lod_bias: limits.max_sampler_lod_bias as _, + max_storage_buffer_range: limits.max_storage_buffer_range as _, + max_uniform_buffer_range: limits.max_uniform_buffer_range as _, + min_memory_map_alignment: limits.min_memory_map_alignment, + standard_sample_locations: limits.standard_sample_locations == ash::vk::TRUE, + + // TODO: Implement Limits for Mesh Shaders + // Depends on VkPhysicalDeviceMeshShaderPropertiesNV which depends on VkPhysicalProperties2 + max_draw_mesh_tasks_count: 0, + max_task_work_group_invocations: 0, + max_task_work_group_size: [0; 3], + max_task_total_memory_size: 0, + max_task_output_count: 0, + max_mesh_work_group_invocations: 0, + max_mesh_work_group_size: [0; 3], + max_mesh_total_memory_size: 0, + max_mesh_output_vertices: 0, + max_mesh_output_primitives: 0, + max_mesh_multiview_view_count: 0, + mesh_output_per_vertex_granularity: 0, + mesh_output_per_primitive_granularity: 0, + } + } + + fn is_valid_cache(&self, cache: &[u8]) -> bool { + const HEADER_SIZE: usize = 16 + vk::UUID_SIZE; + + if cache.len() < HEADER_SIZE { + warn!("Bad cache data length {:?}", cache.len()); + return false; + } + + let header_len = u32::from_le_bytes([cache[0], cache[1], cache[2], cache[3]]); + let header_version = u32::from_le_bytes([cache[4], cache[5], cache[6], cache[7]]); + let vendor_id = u32::from_le_bytes([cache[8], cache[9], cache[10], cache[11]]); + let device_id = u32::from_le_bytes([cache[12], cache[13], cache[14], cache[15]]); + + // header length + if (header_len as usize) < HEADER_SIZE { + warn!("Bad header length {:?}", header_len); + return false; + } + + // cache header version + if header_version != vk::PipelineCacheHeaderVersion::ONE.as_raw() as u32 { + warn!("Unsupported cache header version: {:?}", header_version); + return false; + } + + // vendor id + if vendor_id != self.properties.vendor_id { + warn!( + "Vendor ID mismatch. Device: {:?}, cache: {:?}.", + self.properties.vendor_id, vendor_id, + ); + return false; + } + + // device id + if device_id != self.properties.device_id { + warn!( + "Device ID mismatch. Device: {:?}, cache: {:?}.", + self.properties.device_id, device_id, + ); + return false; + } + + if self.properties.pipeline_cache_uuid != cache[16..16 + vk::UUID_SIZE] { + warn!( + "Pipeline cache UUID mismatch. Device: {:?}, cache: {:?}.", + self.properties.pipeline_cache_uuid, + &cache[16..16 + vk::UUID_SIZE], + ); + return false; + } + true + } +} + +struct DeviceExtensionFunctions { + mesh_shaders: Option<MeshShader>, + draw_indirect_count: Option<DrawIndirectCount>, +} + +#[doc(hidden)] +pub struct RawDevice { + raw: ash::Device, + features: Features, + instance: Arc<RawInstance>, + extension_fns: DeviceExtensionFunctions, + maintenance_level: u8, +} + +impl fmt::Debug for RawDevice { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "RawDevice") // TODO: Real Debug impl + } +} +impl Drop for RawDevice { + fn drop(&mut self) { + unsafe { + self.raw.destroy_device(None); + } + } +} + +impl RawDevice { + fn debug_messenger(&self) -> Option<&DebugMessenger> { + self.instance.debug_messenger.as_ref() + } + + fn map_viewport(&self, rect: &hal::pso::Viewport) -> vk::Viewport { + let flip_y = self.features.contains(hal::Features::NDC_Y_UP); + let shift_y = flip_y && self.maintenance_level != 0; + conv::map_viewport(rect, flip_y, shift_y) + } +} + +// Need to explicitly synchronize on submission and present. +pub type RawCommandQueue = Arc<vk::Queue>; + +pub struct CommandQueue { + raw: RawCommandQueue, + device: Arc<RawDevice>, + swapchain_fn: Swapchain, +} + +impl fmt::Debug for CommandQueue { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str("CommandQueue") + } +} + +impl queue::CommandQueue<Backend> for CommandQueue { + unsafe fn submit<'a, T, Ic, S, Iw, Is>( + &mut self, + submission: queue::Submission<Ic, Iw, Is>, + fence: Option<&native::Fence>, + ) where + T: 'a + Borrow<command::CommandBuffer>, + Ic: IntoIterator<Item = &'a T>, + S: 'a + Borrow<native::Semaphore>, + Iw: IntoIterator<Item = (&'a S, PipelineStage)>, + Is: IntoIterator<Item = &'a S>, + { + //TODO: avoid heap allocations + let mut waits = Vec::new(); + let mut stages = Vec::new(); + + let buffers = submission + .command_buffers + .into_iter() + .map(|cmd| cmd.borrow().raw) + .collect::<Vec<_>>(); + for (semaphore, stage) in submission.wait_semaphores { + waits.push(semaphore.borrow().0); + stages.push(conv::map_pipeline_stage(stage)); + } + let signals = submission + .signal_semaphores + .into_iter() + .map(|semaphore| semaphore.borrow().0) + .collect::<Vec<_>>(); + + let mut info = vk::SubmitInfo::builder() + .wait_semaphores(&waits) + .command_buffers(&buffers) + .signal_semaphores(&signals); + // If count is zero, AMD driver crashes if nullptr is not set for stage masks + if !stages.is_empty() { + info = info.wait_dst_stage_mask(&stages); + } + + let fence_raw = fence.map(|fence| fence.0).unwrap_or(vk::Fence::null()); + + let result = self.device.raw.queue_submit(*self.raw, &[*info], fence_raw); + assert_eq!(Ok(()), result); + } + + unsafe fn present( + &mut self, + surface: &mut window::Surface, + image: window::SurfaceImage, + wait_semaphore: Option<&native::Semaphore>, + ) -> Result<Option<Suboptimal>, PresentError> { + let ssc = surface.swapchain.as_ref().unwrap(); + let wait_semaphore = if let Some(wait_semaphore) = wait_semaphore { + wait_semaphore.0 + } else { + let signals = &[ssc.semaphore.0]; + let submit_info = vk::SubmitInfo::builder() + .wait_dst_stage_mask(&[vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT]) + .signal_semaphores(signals); + self.device + .raw + .queue_submit(*self.raw, &[*submit_info], vk::Fence::null()) + .unwrap(); + ssc.semaphore.0 + }; + + let wait_semaphores = &[wait_semaphore]; + let swapchains = &[ssc.swapchain.raw]; + let image_indices = &[image.index]; + let present_info = vk::PresentInfoKHR::builder() + .wait_semaphores(wait_semaphores) + .swapchains(swapchains) + .image_indices(image_indices); + + match self.swapchain_fn.queue_present(*self.raw, &present_info) { + Ok(true) => Ok(None), + Ok(false) => Ok(Some(Suboptimal)), + Err(vk::Result::ERROR_OUT_OF_HOST_MEMORY) => { + Err(PresentError::OutOfMemory(OutOfMemory::Host)) + } + Err(vk::Result::ERROR_OUT_OF_DEVICE_MEMORY) => { + Err(PresentError::OutOfMemory(OutOfMemory::Device)) + } + Err(vk::Result::ERROR_DEVICE_LOST) => Err(PresentError::DeviceLost(DeviceLost)), + Err(vk::Result::ERROR_OUT_OF_DATE_KHR) => Err(PresentError::OutOfDate), + Err(vk::Result::ERROR_SURFACE_LOST_KHR) => Err(PresentError::SurfaceLost(SurfaceLost)), + _ => panic!("Failed to present frame"), + } + } + + fn wait_idle(&self) -> Result<(), OutOfMemory> { + match unsafe { self.device.raw.queue_wait_idle(*self.raw) } { + Ok(()) => Ok(()), + Err(vk::Result::ERROR_OUT_OF_HOST_MEMORY) => Err(OutOfMemory::Host), + Err(vk::Result::ERROR_OUT_OF_DEVICE_MEMORY) => Err(OutOfMemory::Device), + Err(_) => unreachable!(), + } + } +} + +#[derive(Debug)] +pub struct Device { + shared: Arc<RawDevice>, + vendor_id: u32, + valid_ash_memory_types: u32, +} + +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] +pub enum Backend {} +impl hal::Backend for Backend { + type Instance = Instance; + type PhysicalDevice = PhysicalDevice; + type Device = Device; + type Surface = window::Surface; + + type QueueFamily = QueueFamily; + type CommandQueue = CommandQueue; + type CommandBuffer = command::CommandBuffer; + + type Memory = native::Memory; + type CommandPool = pool::RawCommandPool; + + type ShaderModule = native::ShaderModule; + type RenderPass = native::RenderPass; + type Framebuffer = native::Framebuffer; + + type Buffer = native::Buffer; + type BufferView = native::BufferView; + type Image = native::Image; + type ImageView = native::ImageView; + type Sampler = native::Sampler; + + type ComputePipeline = native::ComputePipeline; + type GraphicsPipeline = native::GraphicsPipeline; + type PipelineLayout = native::PipelineLayout; + type PipelineCache = native::PipelineCache; + type DescriptorSetLayout = native::DescriptorSetLayout; + type DescriptorPool = native::DescriptorPool; + type DescriptorSet = native::DescriptorSet; + + type Fence = native::Fence; + type Semaphore = native::Semaphore; + type Event = native::Event; + type QueryPool = native::QueryPool; +} |