use ash::vk;
use gpu_allocator::vulkan::Allocator;
use librashader::runtime::vk::VulkanObjects;
use parking_lot::Mutex;
use std::ffi::CStr;
use std::sync::Arc;
pub struct VulkanBase {
device: Arc<ash::Device>,
graphics_queue: vk::Queue,
allocator: Arc<Mutex<Allocator>>,
cmd_buffer: vk::CommandBuffer,
pool: vk::CommandPool,
_instance: ash::Instance,
_entry: ash::Entry,
}
impl From<&VulkanBase> for VulkanObjects {
fn from(value: &VulkanBase) -> Self {
VulkanObjects {
device: Arc::clone(&value.device),
alloc: Arc::clone(&value.allocator),
queue: value.graphics_queue.clone(),
}
}
}
const LIBRASHADER_VULKAN: &[u8] = b"librashader Vulkan\0";
impl VulkanBase {
pub fn new() -> anyhow::Result<Self> {
let entry = unsafe { ash::Entry::load() }?;
let app_info = vk::ApplicationInfo::default()
.application_name(unsafe { CStr::from_bytes_with_nul_unchecked(LIBRASHADER_VULKAN) })
.engine_name(unsafe { CStr::from_bytes_with_nul_unchecked(LIBRASHADER_VULKAN) })
.engine_version(0)
.application_version(0)
.api_version(vk::make_api_version(0, 1, 0, 0));
let create_info = vk::InstanceCreateInfo::default().application_info(&app_info);
let instance = unsafe { entry.create_instance(&create_info, None) }?;
let physical_device = super::physical_device::pick_physical_device(&instance)?;
let (device, queue, cmd_pool) = Self::create_device(&instance, &physical_device)?;
let alloc = super::memory::create_allocator(
device.clone(),
instance.clone(),
physical_device.clone(),
);
let buffer_info = vk::CommandBufferAllocateInfo::default()
.command_pool(cmd_pool)
.level(vk::CommandBufferLevel::PRIMARY)
.command_buffer_count(1);
let buffers = unsafe { device.allocate_command_buffers(&buffer_info) }?
.into_iter()
.next()
.unwrap();
Ok(Self {
device: Arc::new(device),
graphics_queue: queue,
allocator: alloc,
pool: cmd_pool,
cmd_buffer: buffers,
_instance: instance,
_entry: entry,
})
}
pub(crate) fn device(&self) -> &Arc<ash::Device> {
&self.device
}
pub(crate) fn allocator(&self) -> &Arc<Mutex<Allocator>> {
&self.allocator
}
fn create_device(
instance: &ash::Instance,
physical_device: &vk::PhysicalDevice,
) -> anyhow::Result<(ash::Device, vk::Queue, vk::CommandPool)> {
let indices = super::physical_device::find_queue_family(&instance, *physical_device);
let queue_info = [vk::DeviceQueueCreateInfo::default()
.queue_family_index(indices.graphics_family()?)
.queue_priorities(&[1.0f32])];
let device_create_info = vk::DeviceCreateInfo::default().queue_create_infos(&queue_info);
let device =
unsafe { instance.create_device(*physical_device, &device_create_info, None)? };
let queue = unsafe { device.get_device_queue(indices.graphics_family()?, 0) };
let create_info = vk::CommandPoolCreateInfo::default()
.flags(vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER)
.queue_family_index(indices.graphics_family()?);
let pool = unsafe { device.create_command_pool(&create_info, None) }?;
Ok((device, queue, pool))
}
pub fn queue_work<T>(&self, f: impl FnOnce(vk::CommandBuffer) -> T) -> anyhow::Result<T> {
unsafe {
self.device.begin_command_buffer(
self.cmd_buffer,
&vk::CommandBufferBeginInfo::default()
.flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT),
)?
}
let result = f(self.cmd_buffer);
unsafe {
self.device.end_command_buffer(self.cmd_buffer)?;
self.device.queue_submit(
self.graphics_queue,
&[vk::SubmitInfo::default().command_buffers(&[self.cmd_buffer])],
vk::Fence::null(),
)?;
self.device.queue_wait_idle(self.graphics_queue)?;
self.device
.reset_command_buffer(self.cmd_buffer, vk::CommandBufferResetFlags::empty())?;
}
Ok(result)
}
}
impl Drop for VulkanBase {
fn drop(&mut self) {
unsafe {
self.device.destroy_command_pool(self.pool, None);
}
}
}