use std::sync::Arc;
#[cfg(target_os = "macos")]
use vulkano::instance::InstanceCreateFlags;
use vulkano::{
device::{
physical::{PhysicalDevice, PhysicalDeviceType},
Device, DeviceCreateInfo, DeviceExtensions, DeviceFeatures, Queue, QueueCreateInfo,
QueueFlags,
},
instance::{
debug::{DebugUtilsMessenger, DebugUtilsMessengerCreateInfo},
Instance, InstanceCreateInfo, InstanceExtensions,
},
memory::allocator::StandardMemoryAllocator,
Version, VulkanLibrary,
};
pub struct VulkanoConfig {
pub instance_create_info: InstanceCreateInfo,
pub debug_create_info: Option<DebugUtilsMessengerCreateInfo>,
pub device_filter_fn: Arc<dyn Fn(&PhysicalDevice) -> bool>,
pub device_priority_fn: Arc<dyn Fn(&PhysicalDevice) -> u32>,
pub device_extensions: DeviceExtensions,
pub device_features: DeviceFeatures,
pub print_device_name: bool,
}
impl Default for VulkanoConfig {
#[inline]
fn default() -> Self {
let device_extensions = DeviceExtensions {
khr_swapchain: true,
..DeviceExtensions::empty()
};
VulkanoConfig {
instance_create_info: InstanceCreateInfo {
#[cfg(target_vendor = "apple")]
flags: InstanceCreateFlags::ENUMERATE_PORTABILITY,
application_version: Version::V1_3,
..Default::default()
},
debug_create_info: None,
device_filter_fn: Arc::new(move |p| {
p.supported_extensions().contains(&device_extensions)
}),
device_priority_fn: Arc::new(|p| match p.properties().device_type {
PhysicalDeviceType::DiscreteGpu => 1,
PhysicalDeviceType::IntegratedGpu => 2,
PhysicalDeviceType::VirtualGpu => 3,
PhysicalDeviceType::Cpu => 4,
PhysicalDeviceType::Other => 5,
_ => 6,
}),
print_device_name: false,
device_extensions,
device_features: DeviceFeatures::empty(),
}
}
}
pub struct VulkanoContext {
instance: Arc<Instance>,
_debug_utils_messenger: Option<DebugUtilsMessenger>,
device: Arc<Device>,
queues: Queues,
memory_allocator: Arc<StandardMemoryAllocator>,
}
impl Default for VulkanoContext {
#[inline]
fn default() -> Self {
VulkanoContext::new(VulkanoConfig::default())
}
}
impl VulkanoContext {
pub fn new(mut config: VulkanoConfig) -> Self {
let library = match VulkanLibrary::new() {
Ok(x) => x,
#[cfg(target_vendor = "apple")]
Err(vulkano::library::LoadingError::LibraryLoadFailure(err)) => panic!(
"failed to load Vulkan library: {err}; did you install VulkanSDK from \
https://vulkan.lunarg.com/sdk/home?",
),
Err(err) => panic!("failed to load Vulkan library: {err}"),
};
config.instance_create_info.enabled_extensions = library
.supported_extensions()
.intersection(&InstanceExtensions {
khr_surface: true,
khr_xlib_surface: true,
khr_xcb_surface: true,
khr_wayland_surface: true,
khr_android_surface: true,
khr_win32_surface: true,
ext_metal_surface: true,
..InstanceExtensions::empty()
})
.union(&config.instance_create_info.enabled_extensions);
let instance =
Instance::new(library, config.instance_create_info).expect("failed to create instance");
let _debug_utils_messenger = config.debug_create_info.take().map(|dbg_create_info| {
DebugUtilsMessenger::new(instance.clone(), dbg_create_info)
.expect("failed to create debug callback")
});
let physical_device = instance
.enumerate_physical_devices()
.expect("failed to enumerate physical devices")
.filter(|p| (config.device_filter_fn)(p))
.min_by_key(|p| (config.device_priority_fn)(p))
.expect("failed to create physical device");
if config.print_device_name {
println!(
"Using device {}, type: {:?}",
physical_device.properties().device_name,
physical_device.properties().device_type,
);
}
let (device, queues) = Self::create_device(
physical_device,
config.device_extensions,
config.device_features,
);
let memory_allocator = Arc::new(StandardMemoryAllocator::new_default(device.clone()));
Self {
instance,
_debug_utils_messenger,
device,
queues,
memory_allocator,
}
}
fn create_device(
physical_device: Arc<PhysicalDevice>,
device_extensions: DeviceExtensions,
device_features: DeviceFeatures,
) -> (Arc<Device>, Queues) {
let queue_family_graphics = physical_device
.queue_family_properties()
.iter()
.enumerate()
.map(|(i, q)| (i as u32, q))
.find(|(_i, q)| q.queue_flags.intersects(QueueFlags::GRAPHICS))
.map(|(i, _)| i)
.expect("could not find a queue that supports graphics");
let queue_family_compute = physical_device
.queue_family_properties()
.iter()
.enumerate()
.map(|(i, q)| (i as u32, q))
.find(|(i, q)| {
q.queue_flags.intersects(QueueFlags::COMPUTE) && *i != queue_family_graphics
})
.map(|(i, _)| i);
let queue_family_transfer = physical_device
.queue_family_properties()
.iter()
.enumerate()
.map(|(i, q)| (i as u32, q))
.find(|(_i, q)| {
q.queue_flags.intersects(QueueFlags::TRANSFER)
&& !q
.queue_flags
.intersects(QueueFlags::GRAPHICS | QueueFlags::COMPUTE)
})
.map(|(i, _)| i);
let queue_create_infos: Vec<_> = [
Some(QueueCreateInfo {
queue_family_index: queue_family_graphics,
..Default::default()
}),
queue_family_compute.map(|queue_family_index| QueueCreateInfo {
queue_family_index,
..Default::default()
}),
queue_family_transfer.map(|queue_family_index| QueueCreateInfo {
queue_family_index,
..Default::default()
}),
]
.into_iter()
.flatten()
.collect();
let (device, mut queues) = {
Device::new(
physical_device,
DeviceCreateInfo {
queue_create_infos,
enabled_extensions: device_extensions,
enabled_features: device_features,
..Default::default()
},
)
.expect("failed to create device")
};
let graphics_queue = queues.next().unwrap();
let compute_queue = queue_family_compute
.is_some()
.then(|| queues.next().unwrap())
.unwrap_or(graphics_queue.clone());
let transfer_queue = queue_family_transfer
.is_some()
.then(|| queues.next().unwrap());
(
device,
Queues {
graphics_queue,
compute_queue,
transfer_queue,
},
)
}
#[inline]
pub fn device_name(&self) -> &str {
&self.device.physical_device().properties().device_name
}
#[inline]
pub fn device_type(&self) -> PhysicalDeviceType {
self.device.physical_device().properties().device_type
}
#[inline]
pub fn max_memory(&self) -> u32 {
self.device
.physical_device()
.properties()
.max_memory_allocation_count
}
#[inline]
pub fn instance(&self) -> &Arc<Instance> {
&self.instance
}
#[inline]
pub fn device(&self) -> &Arc<Device> {
&self.device
}
#[inline]
pub fn graphics_queue(&self) -> &Arc<Queue> {
&self.queues.graphics_queue
}
#[inline]
pub fn compute_queue(&self) -> &Arc<Queue> {
&self.queues.compute_queue
}
#[inline]
pub fn transfer_queue(&self) -> Option<&Arc<Queue>> {
self.queues.transfer_queue.as_ref()
}
#[inline]
pub fn memory_allocator(&self) -> &Arc<StandardMemoryAllocator> {
&self.memory_allocator
}
}
struct Queues {
graphics_queue: Arc<Queue>,
compute_queue: Arc<Queue>,
transfer_queue: Option<Arc<Queue>>,
}