use std::{
ffi::CStr,
sync::{Arc, Mutex, MutexGuard},
};
use ash::{Instance, vk};
use crate::{
debug::{error, log},
errors::{GraphicsError, GraphicsResult},
vulkan::presentation::PresentSurface,
};
pub(crate) struct Queue {
pub device: Arc<ash::Device>,
pub flags: vk::QueueFlags,
pub present_support: bool,
pub family_index: u32,
handle: Mutex<vk::Queue>,
}
impl std::fmt::Debug for Queue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"Queue:\n\
family = {}\n\
flags = {:?}\n\
present = {}",
self.family_index, self.flags, self.present_support
))
}
}
impl Queue {
fn new(
device: Arc<ash::Device>,
queue_families: &[(vk::QueueFlags, QueueFamilyInfo)],
) -> Vec<Arc<Queue>> {
queue_families
.iter()
.map(|(flags, info)| {
(0..info.queue_count)
.map(|idx| {
Arc::new(Queue {
device: device.clone(),
flags: *flags,
handle: Mutex::new(unsafe {
device.get_device_queue(info.family, idx)
}),
present_support: info.present_support,
family_index: info.family,
})
})
.collect::<Vec<Arc<Queue>>>()
})
.collect::<Vec<Vec<Arc<Queue>>>>()
.concat()
}
pub(crate) fn wait_idle(&self) -> GraphicsResult<()> {
match unsafe { self.device.queue_wait_idle(*self.handle.lock().unwrap()) } {
Ok(()) => Ok(()),
Err(e) => {
error!("queue wait idle error: {:?}", e);
Err(GraphicsError::SyncError)
}
}
}
pub(crate) fn submit(
&self,
submits: &[vk::SubmitInfo<'_>],
fence: vk::Fence,
) -> GraphicsResult<()> {
let lock = self.handle.lock().unwrap();
if let Err(e) = unsafe { self.device.queue_submit(*lock, submits, fence) } {
error!("queue submit error: {:?}", e);
return Err(GraphicsError::SyncError);
}
Ok(())
}
pub(crate) fn submit_still_lock(
&self,
submits: &[vk::SubmitInfo<'_>],
fence: vk::Fence,
) -> GraphicsResult<MutexGuard<'_, vk::Queue>> {
let lock = self.handle.lock().unwrap();
if let Err(e) = unsafe { self.device.queue_submit(*lock, submits, fence) } {
error!("queue submit error: {:?}", e);
return Err(GraphicsError::SyncError);
}
Ok(lock)
}
}
pub(crate) struct DeviceManager {
pub _entry: Arc<ash::Entry>,
pub instance: Arc<ash::Instance>,
pub device: Arc<ash::Device>,
pub physical_device: vk::PhysicalDevice,
pub device_name: String,
pub memory_properties: vk::PhysicalDeviceMemoryProperties,
pub device_properties: vk::PhysicalDeviceProperties,
pub queues: Vec<Arc<Queue>>,
pub supported_extensions: Vec<String>,
}
impl Drop for DeviceManager {
fn drop(&mut self) {
unsafe {
self.device.destroy_device(None);
self.instance.destroy_instance(None);
}
}
}
impl DeviceManager {
pub(crate) fn wait_idle(&self) -> GraphicsResult<()> {
let locks: Vec<MutexGuard<vk::Queue>> = self
.queues
.iter()
.map(|queue| queue.handle.lock().unwrap())
.collect();
if let Err(e) = unsafe { self.device.device_wait_idle() } {
error!("cannot device wait idle: {:?}", e);
return Err(GraphicsError::SyncError);
}
drop(locks);
Ok(())
}
pub(crate) fn find_memory_type_index(
&self,
flags: vk::MemoryPropertyFlags,
type_filter: u32,
) -> GraphicsResult<u32> {
for i in 0..self.memory_properties.memory_type_count {
if (type_filter & (1 << i)) != 0
&& (self.memory_properties.memory_types[i as usize].property_flags & flags) == flags
{
return Ok(i);
}
}
error!("cannot find suitable memory type");
Err(GraphicsError::MemoryError)
}
pub(crate) fn new(
entry: Arc<ash::Entry>,
instance: Arc<Instance>,
surface: Option<Arc<PresentSurface>>,
) -> GraphicsResult<Arc<Self>> {
let (physical_device, device_name) =
pick_physical_device(instance.clone(), surface.is_none())?;
let mut supported_extensions = query_extensions_support(instance.clone(), physical_device)?;
if surface.is_none()
&& let Some((idx, _)) = supported_extensions
.iter()
.enumerate()
.find(|(_, ext)| **ext == vk::KHR_SWAPCHAIN_NAME)
{
supported_extensions.swap_remove(idx);
}
let memory_properties =
unsafe { instance.get_physical_device_memory_properties(physical_device) };
let device_properties = unsafe { instance.get_physical_device_properties(physical_device) };
let queue_families = find_queue_families(instance.clone(), surface, physical_device);
if queue_families.is_empty() {
return Err(GraphicsError::NotSupportedDevice);
}
let (logical_device, queues) = create_logical_device(
instance.clone(),
physical_device,
&queue_families,
&supported_extensions,
vk::PhysicalDeviceFeatures::default().sampler_anisotropy(true),
)?;
Ok(Arc::new(Self {
_entry: entry,
instance,
device: logical_device.clone(),
physical_device,
device_name,
memory_properties,
device_properties,
queues,
supported_extensions: supported_extensions
.iter()
.map(|ext| ext.to_str().unwrap().to_string())
.collect(),
}))
}
pub(crate) fn log_device(&self) {
let (maj, min, pat, var) = {
let version = self.device_properties.api_version;
(
vk::api_version_major(version),
vk::api_version_minor(version),
vk::api_version_patch(version),
vk::api_version_variant(version),
)
};
log!(
"picked device: [ {} ] vulkan version: [ {}.{}.{}.{} ]",
self.device_name,
maj,
min,
pat,
var
);
}
}
struct QueueFamilyInfo {
family: u32,
queue_count: u32,
present_support: bool,
}
impl std::fmt::Debug for QueueFamilyInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"QueueFamilyInfo:\n\
family = {}\n\
queue_count = {}\n\
present = {}",
self.family, self.queue_count, self.present_support
))
}
}
fn query_extensions_support<'a>(
instance: Arc<Instance>,
device: vk::PhysicalDevice,
) -> GraphicsResult<Vec<&'a CStr>> {
let extensions = [
vk::KHR_SWAPCHAIN_NAME,
vk::KHR_ACCELERATION_STRUCTURE_NAME,
vk::KHR_RAY_TRACING_PIPELINE_NAME,
vk::KHR_DEFERRED_HOST_OPERATIONS_NAME,
vk::EXT_IMAGE_COMPRESSION_CONTROL_NAME,
vk::EXT_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_NAME,
];
let mut supported_extensions = Vec::with_capacity(extensions.len());
let extension_props = match unsafe { instance.enumerate_device_extension_properties(device) } {
Ok(props) => props,
Err(e) => {
error!("cannot enumerate device extension properties: {}", e);
return Err(GraphicsError::ConnotInitLibrary);
}
};
let device_supported_extensions: Vec<&CStr> = extension_props
.iter()
.map(|ext| ext.extension_name_as_c_str().unwrap())
.collect();
for sup_ext in &device_supported_extensions {
if let Some(ext) = extensions.iter().find(|&req_ext| *req_ext == *sup_ext) {
supported_extensions.push(*ext)
}
}
Ok(supported_extensions)
}
fn pick_physical_device(
instance: Arc<Instance>,
get_first: bool,
) -> GraphicsResult<(vk::PhysicalDevice, String)> {
let devices = match unsafe { instance.enumerate_physical_devices() } {
Ok(devices) => devices,
Err(e) => {
error!("cannot enumerate physical devices: {}", e);
return Err(GraphicsError::NoDevice);
}
};
if devices.is_empty() {
error!("no vulkan devices found");
return Err(GraphicsError::NoDevice);
}
if get_first {
let device = devices[0];
let props = unsafe { instance.get_physical_device_properties(device) };
let device_name = unsafe { CStr::from_ptr(props.device_name.as_ptr()) }
.to_str()
.unwrap();
return Ok((device, device_name.to_string()));
}
let mut found = Err(GraphicsError::NotSupportedDevice);
for device in devices {
let props = unsafe { instance.get_physical_device_properties(device) };
let device_name = unsafe { CStr::from_ptr(props.device_name.as_ptr()) }
.to_str()
.unwrap()
.to_string();
match props.device_type {
vk::PhysicalDeviceType::DISCRETE_GPU => return Ok((device, device_name)),
vk::PhysicalDeviceType::INTEGRATED_GPU => found = Ok((device, device_name)),
_ => {
if found.is_err() {
found = Ok((device, device_name))
}
}
}
}
found
}
fn find_queue_families(
instance: Arc<Instance>,
surface: Option<Arc<PresentSurface>>,
device: vk::PhysicalDevice,
) -> Vec<(vk::QueueFlags, QueueFamilyInfo)> {
let mut queue_families = Vec::<(vk::QueueFlags, QueueFamilyInfo)>::new();
let props = unsafe { instance.get_physical_device_queue_family_properties(device) };
for (index, family) in props.iter().filter(|f| f.queue_count > 0).enumerate() {
let index = index as u32;
let present_support = unsafe {
match surface.clone() {
Some(surface) => surface
.surface
.get_physical_device_surface_support(device, index, surface.surface_khr)
.unwrap(),
None => false,
}
};
let mut to_push = false;
queue_families.iter_mut().for_each(|(flags, info)| {
if info.family == index {
*flags |= family.queue_flags;
info.present_support = present_support
} else if !flags.intersects(family.queue_flags) {
to_push = true;
}
});
if to_push || queue_families.is_empty() {
queue_families.push((
family.queue_flags,
QueueFamilyInfo {
family: index,
queue_count: family.queue_count,
present_support,
},
));
}
}
queue_families
}
fn create_logical_device(
instance: Arc<Instance>,
physical_device: vk::PhysicalDevice,
queue_families: &[(vk::QueueFlags, QueueFamilyInfo)],
extensions: &[&CStr],
features: vk::PhysicalDeviceFeatures,
) -> GraphicsResult<(Arc<ash::Device>, Vec<Arc<Queue>>)> {
let mut queues_create_infos = vec![];
let priorities = vec![1.; 256];
for (_, info) in queue_families {
queues_create_infos.push(
vk::DeviceQueueCreateInfo::default()
.queue_family_index(info.family)
.queue_priorities(&priorities[0..info.queue_count as usize]),
)
}
let mut accel_feature =
vk::PhysicalDeviceAccelerationStructureFeaturesKHR::default().acceleration_structure(true);
let mut rt_pipeline_feature =
vk::PhysicalDeviceRayTracingPipelineFeaturesKHR::default().ray_tracing_pipeline(true);
let portability = if cfg!(target_os = "macos") {
vec![vk::KHR_PORTABILITY_SUBSET_NAME.as_ptr()]
} else {
Vec::with_capacity(0)
};
let extension_names: Vec<_> = extensions
.iter()
.map(|ext| ext.as_ptr())
.chain(portability)
.collect();
let device_create_info = vk::DeviceCreateInfo::default()
.queue_create_infos(&queues_create_infos)
.enabled_features(&features)
.enabled_extension_names(&extension_names)
.push_next(&mut rt_pipeline_feature)
.push_next(&mut accel_feature);
let device = match unsafe { instance.create_device(physical_device, &device_create_info, None) }
{
Err(e) => {
error!("cannot create logical device: {}", e);
return Err(GraphicsError::NotSupportedDevice);
}
Ok(device) => Arc::new(device),
};
let queues = Queue::new(device.clone(), queue_families);
Ok((device, queues))
}