use crate::utils::copyhashmap::CopyHashMap;
use crate::utils::errorfmt::ErrorFmt;
use crate::utils::hash_map_ext::HashMapExt;
use crate::utils::major_minor::MajorMinor;
use crate::utils::major_minor::major_minor;
use crate::video::dmabuf::DmaBuf;
use crate::video::dmabuf::PlaneVec;
use crate::vulkan_core::VulkanCoreError;
use crate::vulkan_core::VulkanCoreInstance;
use crate::vulkan_core::map_extension_properties;
use crate::vulkan_core::vk_is_drm_dev;
use ash::Device;
use ash::ext::external_memory_dma_buf;
use ash::ext::physical_device_drm;
use ash::khr::external_memory_fd;
use ash::khr::maintenance9;
use ash::vk;
use ash::vk::API_VERSION_1_1;
use ash::vk::DeviceCreateInfo;
use ash::vk::ExternalMemoryHandleTypeFlags;
use ash::vk::MemoryFdPropertiesKHR;
use ash::vk::MemoryPropertyFlags;
use ash::vk::MemoryType;
use ash::vk::PhysicalDeviceDrmPropertiesEXT;
use ash::vk::PhysicalDeviceMaintenance9FeaturesKHR;
use ash::vk::PhysicalDeviceProperties2;
use bstr::ByteSlice;
use log::Level;
use run_on_drop::on_drop;
use std::error::Error;
use std::ffi::CStr;
use std::fmt::Debug;
use std::fmt::Formatter;
use std::rc::Rc;
use thiserror::Error;
use uapi::AsUstr;
use uapi::c;
#[derive(Debug, Error)]
pub enum BufferIdDeviceError {
#[error(transparent)]
Core(#[from] VulkanCoreError),
#[error("Could not enumerate the physical devices")]
EnumeratePhysicalDevice(#[source] vk::Result),
#[error("Could not find a corresponding vulkan device")]
NoVulkanDevice,
#[error("Device does not support vulkan 1.1")]
NoVulkan11,
#[error("Device does not support the device extension {}", .0.as_ustr().as_bytes().as_bstr())]
MissingDeviceExtensions(&'static CStr),
#[error("Could not create the device")]
CreateDevice(#[source] vk::Result),
#[error("Could not query memory fd properties")]
GetMemoryFdProperties(#[source] vk::Result),
}
pub struct BufferIdDevice {
_instance: VulkanCoreInstance,
memory_types: Vec<MemoryType>,
dev: Device,
external_memory_fd: external_memory_fd::Device,
}
#[derive(Default)]
pub struct BufferIdDeviceRegistry {
devs: CopyHashMap<c::dev_t, Option<Rc<BufferIdDevice>>>,
}
pub trait BufferIdDeviceDyn {
fn is_on_device(&self, buf: &DmaBuf) -> Result<bool, Box<dyn Error>>;
}
const DEVICE_EXTENSIONS: [&CStr; 3] = [
external_memory_fd::NAME,
external_memory_dma_buf::NAME,
maintenance9::NAME,
];
impl BufferIdDevice {
fn new(dev: c::dev_t) -> Result<Rc<Self>, BufferIdDeviceError> {
let core_instance = VulkanCoreInstance::new(Level::Debug)?;
let instance = &core_instance.instance;
let physical_device;
let device_extensions;
let device_properties;
'find_device: {
let devices = unsafe {
instance
.enumerate_physical_devices()
.map_err(BufferIdDeviceError::EnumeratePhysicalDevice)?
};
'outer: for phy in devices {
let res = unsafe { instance.enumerate_device_extension_properties(phy) };
let exts = match res {
Ok(res) => map_extension_properties(res),
Err(e) => {
log::error!(
"Could not enumerate extensions of physical device: {}",
ErrorFmt(e),
);
continue;
}
};
if exts.not_contains_key(physical_device_drm::NAME) {
continue 'outer;
}
let mut drm_props = PhysicalDeviceDrmPropertiesEXT::default();
let mut props = PhysicalDeviceProperties2::default().push_next(&mut drm_props);
unsafe {
instance.get_physical_device_properties2(phy, &mut props);
}
let props = props.properties;
if vk_is_drm_dev(&drm_props, dev) {
physical_device = phy;
device_extensions = exts;
device_properties = props;
break 'find_device;
}
}
return Err(BufferIdDeviceError::NoVulkanDevice);
}
if device_properties.api_version < API_VERSION_1_1 {
return Err(BufferIdDeviceError::NoVulkan11);
}
for ext in DEVICE_EXTENSIONS {
if device_extensions.not_contains_key(ext) {
return Err(BufferIdDeviceError::MissingDeviceExtensions(ext));
}
}
let memory_info =
unsafe { instance.get_physical_device_memory_properties(physical_device) };
let device = {
let extensions = DEVICE_EXTENSIONS.map(|e| e.as_ptr());
let mut maint9_features =
PhysicalDeviceMaintenance9FeaturesKHR::default().maintenance9(true);
let info = DeviceCreateInfo::default()
.enabled_extension_names(&extensions)
.push_next(&mut maint9_features);
unsafe {
instance
.create_device(physical_device, &info, None)
.map_err(BufferIdDeviceError::CreateDevice)?
}
};
let destroy_device = on_drop(|| unsafe { device.destroy_device(None) });
let external_memory_fd = external_memory_fd::Device::new(instance, &device);
destroy_device.forget();
Ok(Rc::new(BufferIdDevice {
_instance: core_instance,
memory_types: memory_info.memory_types_as_slice().to_vec(),
dev: device,
external_memory_fd,
}))
}
}
impl BufferIdDevice {
fn is_on_device(&self, buf: &DmaBuf) -> Result<bool, BufferIdDeviceError> {
let mut fd_props = PlaneVec::new();
for plane in &buf.planes {
let mut props = MemoryFdPropertiesKHR::default();
unsafe {
self.external_memory_fd
.get_memory_fd_properties(
ExternalMemoryHandleTypeFlags::DMA_BUF_EXT,
plane.fd.raw(),
&mut props,
)
.map_err(BufferIdDeviceError::GetMemoryFdProperties)?;
}
fd_props.push(props);
if buf.is_one_file() {
break;
}
}
let mut on_device = true;
for prop in &fd_props {
let mut plane_on_device = false;
for (idx, ty) in self.memory_types.iter().enumerate() {
if prop.memory_type_bits & (1 << idx) != 0
&& ty
.property_flags
.contains(MemoryPropertyFlags::DEVICE_LOCAL)
{
plane_on_device = true;
break;
}
}
if !plane_on_device {
on_device = false;
break;
}
}
Ok(on_device)
}
}
impl BufferIdDeviceDyn for BufferIdDevice {
fn is_on_device(&self, buf: &DmaBuf) -> Result<bool, Box<dyn Error>> {
self.is_on_device(buf).map_err(|e| Box::new(e) as _)
}
}
impl BufferIdDeviceRegistry {
pub fn remove(&self, dev: c::dev_t) {
self.devs.remove(&dev);
}
pub fn get(&self, dev: c::dev_t) -> Option<Rc<BufferIdDevice>> {
if let Some(dev) = self.devs.get(&dev) {
return dev;
}
match BufferIdDevice::new(dev).map(Some) {
Ok(cd) => {
self.devs.set(dev, cd.clone());
cd
}
Err(e) => {
let MajorMinor { major, minor } = major_minor(dev);
log::warn!(
"Could not create buffer id device for {major}:{minor}: {}",
ErrorFmt(e),
);
self.devs.set(dev, None);
None
}
}
}
}
impl Drop for BufferIdDevice {
fn drop(&mut self) {
unsafe {
self.dev.destroy_device(None);
}
}
}
impl Debug for BufferIdDevice {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("BufferIdDevice").finish_non_exhaustive()
}
}