use crate::format::Format;
use crate::gfx_api::GfxBuffer;
use crate::gfx_apis::vulkan::VulkanError;
use crate::gfx_apis::vulkan::device::VulkanDevice;
use ash::Device;
use ash::vk;
use ash::vk::BufferCreateInfo;
use ash::vk::BufferUsageFlags;
use ash::vk::ExternalMemoryBufferCreateInfo;
use ash::vk::ExternalMemoryHandleTypeFlags;
use ash::vk::ImportMemoryFdInfoKHR;
use ash::vk::MemoryAllocateInfo;
use ash::vk::MemoryDedicatedAllocateInfo;
use ash::vk::MemoryFdPropertiesKHR;
use ash::vk::MemoryPropertyFlags;
use jay_algorithms::oserror::OsErrorExt2;
use run_on_drop::on_drop;
use std::any::Any;
use std::rc::Rc;
use uapi::OwnedFd;
pub(super) const TRANSFER_QUEUE_BUFFER_ALIGNMENT: u64 = 4;
pub struct VulkanDmabufBuffer {
device: Rc<VulkanDevice>,
pub(super) size: u64,
pub(super) offset: u64,
pub(super) buffer: vk::Buffer,
memory: vk::DeviceMemory,
}
impl VulkanDevice {
pub fn create_dmabuf_buffer(
self: &Rc<Self>,
dmabuf: &OwnedFd,
offset: u64,
size: u64,
format: &'static Format,
) -> Result<Rc<VulkanDmabufBuffer>, VulkanError> {
if offset % TRANSFER_QUEUE_BUFFER_ALIGNMENT != 0 || offset % format.bpp as u64 != 0 {
return Err(VulkanError::DmaBufBufferOffsetAlignment);
}
let mut memory_fd_properties = MemoryFdPropertiesKHR::default();
unsafe {
self.external_memory_fd
.get_memory_fd_properties(
ExternalMemoryHandleTypeFlags::DMA_BUF_EXT,
dmabuf.raw(),
&mut memory_fd_properties,
)
.map_err(VulkanError::MemoryFdProperties)?;
}
let buffer = {
let mut external_info = ExternalMemoryBufferCreateInfo::default()
.handle_types(ExternalMemoryHandleTypeFlags::DMA_BUF_EXT);
let create_info = BufferCreateInfo::default()
.size(size)
.usage(BufferUsageFlags::TRANSFER_SRC)
.push_next(&mut external_info);
unsafe {
self.device
.create_buffer(&create_info, None)
.map_err(VulkanError::CreateBuffer)?
}
};
let destroy_buffer = on_drop(|| unsafe { self.device.destroy_buffer(buffer, None) });
let requirements = unsafe { self.device.get_buffer_memory_requirements(buffer) };
let memory_type = self.find_memory_type(
MemoryPropertyFlags::HOST_VISIBLE,
requirements.memory_type_bits & memory_fd_properties.memory_type_bits,
);
let Some(memory_type) = memory_type else {
return Err(VulkanError::MemoryType);
};
let fd = uapi::fcntl_dupfd_cloexec(dmabuf.raw(), 0).map_os_err(VulkanError::Dupfd)?;
let memory = {
let mut dedicated = MemoryDedicatedAllocateInfo::default().buffer(buffer);
let mut import_info = ImportMemoryFdInfoKHR::default()
.fd(fd.raw())
.handle_type(ExternalMemoryHandleTypeFlags::DMA_BUF_EXT);
let allocate_info = MemoryAllocateInfo::default()
.allocation_size(requirements.size)
.memory_type_index(memory_type)
.push_next(&mut import_info)
.push_next(&mut dedicated);
unsafe {
self.device
.allocate_memory(&allocate_info, None)
.map_err(VulkanError::AllocateMemory)?
}
};
fd.unwrap();
let free_memory = on_drop(|| unsafe { self.device.free_memory(memory, None) });
unsafe {
self.device
.bind_buffer_memory(buffer, memory, 0)
.map_err(VulkanError::BindBufferMemory)?;
}
free_memory.forget();
destroy_buffer.forget();
Ok(Rc::new(VulkanDmabufBuffer {
device: self.clone(),
size,
offset,
buffer,
memory,
}))
}
}
impl Drop for VulkanDmabufBuffer {
fn drop(&mut self) {
unsafe {
self.device.device.free_memory(self.memory, None);
self.device.device.destroy_buffer(self.buffer, None);
}
}
}
impl VulkanDmabufBuffer {
fn assert_device(&self, device: &Device) {
assert_eq!(
self.device.device.handle(),
device.handle(),
"Mixed vulkan device use"
);
}
}
impl GfxBuffer for VulkanDmabufBuffer {}
impl dyn GfxBuffer {
pub(super) fn into_vk(self: Rc<Self>, device: &Device) -> Rc<VulkanDmabufBuffer> {
let buffer: Rc<VulkanDmabufBuffer> = (self as Rc<dyn Any>)
.downcast()
.expect("Non-vulkan buffer passed into vulkan");
buffer.assert_device(device);
buffer
}
}