use std::os::fd::IntoRawFd;
use std::sync::Arc;
use crate::error::{Error, Result};
use super::{ExportedFrame, platform};
impl ExportedFrame {
pub fn into_wgpu_texture(mut self, device: &wgpu::Device) -> Result<wgpu::Texture> {
let (width, height) = (self.width(), self.height());
if !device
.features()
.contains(wgpu::Features::VULKAN_EXTERNAL_MEMORY_DMA_BUF)
{
return Err(Error::WgpuImport(
"device lacks VULKAN_EXTERNAL_MEMORY_DMA_BUF — request it in required_features \
(needs driver support for VK_EXT_external_memory_dma_buf and \
VK_EXT_image_drm_format_modifier)"
.into(),
));
}
let fourcc = self.fourcc();
if fourcc != platform::FOURCC_ARGB8888 && fourcc != platform::FOURCC_XRGB8888 {
return Err(Error::WgpuImport(format!(
"unsupported DRM fourcc {fourcc:#010x} (expected AR24/XR24)"
)));
}
let fd = self
.dma_buf_fd()
.try_clone_to_owned()
.map_err(|e| Error::WgpuImport(format!("duplicating the dmabuf fd: {e}")))?;
let stride = u64::from(self.stride());
let hal_desc = wgpu_hal::TextureDescriptor {
label: Some("mpv-exported-frame"),
size: wgpu::Extent3d {
width,
height,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Bgra8Unorm,
usage: wgpu::TextureUses::RESOURCE | wgpu::TextureUses::COPY_SRC,
memory_flags: wgpu_hal::MemoryFlags::empty(),
view_formats: vec![],
};
use ash::vk;
let hal_texture = {
let hal_device = unsafe { device.as_hal::<wgpu_hal::api::Vulkan>() };
let Some(hal_device) = hal_device else {
return Err(Error::WgpuImport(
"wgpu device is not on the Vulkan backend".into(),
));
};
let raw_device = hal_device.raw_device().clone();
let phys = hal_device.raw_physical_device();
let instance = hal_device.shared_instance().raw_instance();
let ext_fd = ash::khr::external_memory_fd::Device::new(instance, &raw_device);
let mut ext_img = vk::ExternalMemoryImageCreateInfo::default()
.handle_types(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT);
let plane = vk::SubresourceLayout {
offset: 0,
row_pitch: stride,
size: 0,
array_pitch: 0,
depth_pitch: 0,
};
let mut drm = vk::ImageDrmFormatModifierExplicitCreateInfoEXT::default()
.drm_format_modifier(platform::MODIFIER_LINEAR)
.plane_layouts(core::slice::from_ref(&plane));
let image_info = vk::ImageCreateInfo::default()
.image_type(vk::ImageType::TYPE_2D)
.format(vk::Format::B8G8R8A8_UNORM)
.extent(vk::Extent3D {
width,
height,
depth: 1,
})
.mip_levels(1)
.array_layers(1)
.samples(vk::SampleCountFlags::TYPE_1)
.tiling(vk::ImageTiling::DRM_FORMAT_MODIFIER_EXT)
.usage(vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::TRANSFER_SRC)
.sharing_mode(vk::SharingMode::EXCLUSIVE)
.initial_layout(vk::ImageLayout::UNDEFINED)
.push_next(&mut ext_img)
.push_next(&mut drm);
let image = unsafe { raw_device.create_image(&image_info, None) }
.map_err(|e| Error::WgpuImport(format!("vkCreateImage (dmabuf): {e}")))?;
let reqs = unsafe { raw_device.get_image_memory_requirements(image) };
let fd_raw = fd.into_raw_fd();
let mut fd_props = vk::MemoryFdPropertiesKHR::default();
if let Err(e) = unsafe {
ext_fd.get_memory_fd_properties(
vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT,
fd_raw,
&mut fd_props,
)
} {
unsafe {
libc::close(fd_raw);
raw_device.destroy_image(image, None);
}
return Err(Error::WgpuImport(format!(
"vkGetMemoryFdPropertiesKHR: {e}"
)));
}
let mem_props = unsafe { instance.get_physical_device_memory_properties(phys) };
let type_bits = reqs.memory_type_bits & fd_props.memory_type_bits;
let Some(mem_type) =
(0..mem_props.memory_type_count).find(|i| type_bits & (1 << i) != 0)
else {
unsafe {
libc::close(fd_raw);
raw_device.destroy_image(image, None);
}
return Err(Error::WgpuImport(
"no Vulkan memory type accepts this dmabuf".into(),
));
};
let mut dedicated = vk::MemoryDedicatedAllocateInfo::default().image(image);
let mut import = vk::ImportMemoryFdInfoKHR::default()
.handle_type(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT)
.fd(fd_raw);
let alloc_info = vk::MemoryAllocateInfo::default()
.allocation_size(reqs.size)
.memory_type_index(mem_type)
.push_next(&mut import)
.push_next(&mut dedicated);
let memory = match unsafe { raw_device.allocate_memory(&alloc_info, None) } {
Ok(memory) => memory,
Err(e) => {
unsafe {
libc::close(fd_raw);
raw_device.destroy_image(image, None);
}
return Err(Error::WgpuImport(format!(
"vkAllocateMemory (import dmabuf): {e}"
)));
}
};
if let Err(e) = unsafe { raw_device.bind_image_memory(image, memory, 0) } {
unsafe {
raw_device.free_memory(memory, None);
raw_device.destroy_image(image, None);
}
return Err(Error::WgpuImport(format!(
"vkBindImageMemory (dmabuf): {e}"
)));
}
let buffer = self
.buffer
.take()
.expect("buffer present until drop/presented");
let shared = Arc::clone(&self.shared);
let cb_device = raw_device.clone();
let drop_cb: wgpu_hal::DropCallback = Box::new(move || {
unsafe {
cb_device.destroy_image(image, None);
cb_device.free_memory(memory, None);
}
super::return_buffer(&shared, buffer);
});
unsafe {
hal_device.texture_from_raw(
image,
&hal_desc,
Some(drop_cb),
wgpu_hal::vulkan::TextureMemory::External,
)
}
};
let desc = super::exported_texture_desc(width, height);
Ok(unsafe {
device.create_texture_from_hal::<wgpu_hal::api::Vulkan>(
hal_texture,
&desc,
wgpu::TextureUses::RESOURCE,
)
})
}
}