Commit 7de49c2aa70b
tarball11 files changed+949-105
@@ -428,6 +428,7 @@ dependencies = [
428428
"ash",429429
"glam",430430
"gpu-allocator",431
+ "rustix",431432
"smithay",432433
"thiserror 2.0.21",433434
]@@ -11,6 +11,7 @@ workspace = true
1111
[dependencies]1212
ash = "0.38"1313
glam = { version = "0.33", default-features = false, features = ["std"] }14
+rustix = { version = "1", features = ["event"] }1415
gpu-allocator = { version = "0.27", default-features = false, features = ["vulkan"] }1516
smithay.workspace = true1617
thiserror = "2"@@ -11,18 +11,31 @@ pub(crate) struct Shared {
1111
pub device: Device,1212
}1313
14
+/// Loaders for the extensions that dmabuf import, export and fence export15
+/// need. Absent when the device lacks any of them.16
+pub(crate) struct DmabufExt {17
+ pub memory_fd: ash::khr::external_memory_fd::Device,18
+ pub fence_fd: ash::khr::external_fence_fd::Device,19
+ pub modifier: ash::ext::image_drm_format_modifier::Device,20
+}21
+22
+const DMABUF_EXTENSIONS: [&std::ffi::CStr; 5] = [23
+ ash::khr::external_memory_fd::NAME,24
+ ash::ext::external_memory_dma_buf::NAME,25
+ ash::ext::image_drm_format_modifier::NAME,26
+ ash::ext::queue_family_foreign::NAME,27
+ ash::khr::external_fence_fd::NAME,28
+];29
+1430
pub(crate) struct Device {1531
pub _entry: ash::Entry,1632
pub instance: ash::Instance,17
- #[expect(18
- dead_code,19
- reason = "dmabuf import queries format properties through it"20
- )]2133
pub physical: vk::PhysicalDevice,2234
pub device: ash::Device,2335
pub queue: Mutex<vk::Queue>,2436
pub queue_family: u32,2537
pub push_descriptor: ash::khr::push_descriptor::Device,38
+ pub dmabuf: Option<DmabufExt>,2639
pub name: String,2740
}2841
@@ -61,7 +74,18 @@ impl Shared {
6174
let queues = [vk::DeviceQueueCreateInfo::default()6275
.queue_family_index(queue_family)6376
.queue_priorities(&priorities)];64
- let extensions = [ash::khr::push_descriptor::NAME.as_ptr()];77
+ let available: Vec<_> = unsafe { instance.enumerate_device_extension_properties(physical) }78
+ .unwrap_or_default()79
+ .iter()80
+ .filter_map(|e| e.extension_name_as_c_str().ok().map(ToOwned::to_owned))81
+ .collect();82
+ let has_dmabuf = DMABUF_EXTENSIONS83
+ .iter()84
+ .all(|n| available.iter().any(|a| a.as_c_str() == *n));85
+ let mut extensions = vec![ash::khr::push_descriptor::NAME.as_ptr()];86
+ if has_dmabuf {87
+ extensions.extend(DMABUF_EXTENSIONS.iter().map(|n| n.as_ptr()));88
+ }6589
let mut features13 = vk::PhysicalDeviceVulkan13Features::default()6690
.dynamic_rendering(true)6791
.synchronization2(true);@@ -78,6 +102,11 @@ impl Shared {
78102
};79103
let queue = unsafe { device.get_device_queue(queue_family, 0) };80104
let push_descriptor = ash::khr::push_descriptor::Device::new(&instance, &device);105
+ let dmabuf = has_dmabuf.then(|| DmabufExt {106
+ memory_fd: ash::khr::external_memory_fd::Device::new(&instance, &device),107
+ fence_fd: ash::khr::external_fence_fd::Device::new(&instance, &device),108
+ modifier: ash::ext::image_drm_format_modifier::Device::new(&instance, &device),109
+ });81110
let allocator = Allocator::new(&AllocatorCreateDesc {82111
instance: instance.clone(),83112
device: device.clone(),@@ -94,6 +123,7 @@ impl Shared {
94123
queue: Mutex::new(queue),95124
queue_family,96125
push_descriptor,126
+ dmabuf,97127
name,98128
};99129
Ok(Self {@@ -0,0 +1,392 @@
1
+use crate::Error;2
+use crate::device::Shared;3
+use crate::format::{SUPPORTED, vk_format};4
+use crate::image::{ImageInner, Memory};5
+use ash::vk;6
+use smithay::backend::allocator::dmabuf::{Dmabuf, DmabufFlags};7
+use smithay::backend::allocator::format::FormatSet;8
+use smithay::backend::allocator::{Allocator, Buffer, Format, Fourcc, Modifier};9
+use std::os::fd::{AsRawFd, FromRawFd, IntoRawFd, OwnedFd};10
+use std::sync::Arc;11
+12
+/// Which (format, modifier) pairs the device can import for sampling and13
+/// render into, queried once at startup.14
+#[derive(Debug, Default)]15
+pub(crate) struct DmabufCaps {16
+ pub sample: FormatSet,17
+ pub render: FormatSet,18
+}19
+20
+pub(crate) const RENDER_USAGE: vk::ImageUsageFlags = vk::ImageUsageFlags::from_raw(21
+ vk::ImageUsageFlags::COLOR_ATTACHMENT.as_raw()22
+ | vk::ImageUsageFlags::SAMPLED.as_raw()23
+ | vk::ImageUsageFlags::TRANSFER_SRC.as_raw(),24
+);25
+pub(crate) const SAMPLE_USAGE: vk::ImageUsageFlags = vk::ImageUsageFlags::SAMPLED;26
+27
+impl DmabufCaps {28
+ pub fn query(shared: &Shared) -> Self {29
+ if shared.device.dmabuf.is_none() {30
+ return Self::default();31
+ }32
+ let (mut sample, mut render) = (Vec::new(), Vec::new());33
+ for fourcc in SUPPORTED {34
+ let Some(fmt) = vk_format(fourcc) else {35
+ continue;36
+ };37
+ for props in modifier_properties(shared, fmt.format) {38
+ if props.drm_format_modifier_plane_count != 1 {39
+ continue;40
+ }41
+ let features = props.drm_format_modifier_tiling_features;42
+ let format = Format {43
+ code: fourcc,44
+ modifier: Modifier::from(props.drm_format_modifier),45
+ };46
+ let supports =47
+ |usage| external_support(shared, fmt.format, props.drm_format_modifier, usage);48
+ if features.contains(vk::FormatFeatureFlags::SAMPLED_IMAGE)49
+ && supports(SAMPLE_USAGE).importable50
+ {51
+ sample.push(format);52
+ }53
+ let renderable = vk::FormatFeatureFlags::COLOR_ATTACHMENT54
+ | vk::FormatFeatureFlags::COLOR_ATTACHMENT_BLEND;55
+ let s = supports(RENDER_USAGE);56
+ if features.contains(renderable) && s.importable && s.exportable {57
+ render.push(format);58
+ }59
+ }60
+ }61
+ Self {62
+ sample: sample.into_iter().collect(),63
+ render: render.into_iter().collect(),64
+ }65
+ }66
+}67
+68
+fn modifier_properties(69
+ shared: &Shared,70
+ format: vk::Format,71
+) -> Vec<vk::DrmFormatModifierPropertiesEXT> {72
+ let dev = &shared.device;73
+ let mut list = vk::DrmFormatModifierPropertiesListEXT::default();74
+ let mut props = vk::FormatProperties2::default().push_next(&mut list);75
+ unsafe {76
+ dev.instance77
+ .get_physical_device_format_properties2(dev.physical, format, &mut props)78
+ };79
+ let count = list.drm_format_modifier_count as usize;80
+ let mut out = vec![vk::DrmFormatModifierPropertiesEXT::default(); count];81
+ let mut list =82
+ vk::DrmFormatModifierPropertiesListEXT::default().drm_format_modifier_properties(&mut out);83
+ let mut props = vk::FormatProperties2::default().push_next(&mut list);84
+ unsafe {85
+ dev.instance86
+ .get_physical_device_format_properties2(dev.physical, format, &mut props)87
+ };88
+ out89
+}90
+91
+struct External {92
+ importable: bool,93
+ exportable: bool,94
+}95
+96
+fn external_support(97
+ shared: &Shared,98
+ format: vk::Format,99
+ modifier: u64,100
+ usage: vk::ImageUsageFlags,101
+) -> External {102
+ let dev = &shared.device;103
+ let mut modifier_info = vk::PhysicalDeviceImageDrmFormatModifierInfoEXT::default()104
+ .drm_format_modifier(modifier)105
+ .sharing_mode(vk::SharingMode::EXCLUSIVE);106
+ let mut external_info = vk::PhysicalDeviceExternalImageFormatInfo::default()107
+ .handle_type(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT);108
+ let info = vk::PhysicalDeviceImageFormatInfo2::default()109
+ .format(format)110
+ .ty(vk::ImageType::TYPE_2D)111
+ .tiling(vk::ImageTiling::DRM_FORMAT_MODIFIER_EXT)112
+ .usage(usage)113
+ .push_next(&mut external_info)114
+ .push_next(&mut modifier_info);115
+ let mut external_props = vk::ExternalImageFormatProperties::default();116
+ let mut props = vk::ImageFormatProperties2::default().push_next(&mut external_props);117
+ let ok = unsafe {118
+ dev.instance119
+ .get_physical_device_image_format_properties2(dev.physical, &info, &mut props)120
+ }121
+ .is_ok();122
+ let features = external_props123
+ .external_memory_properties124
+ .external_memory_features;125
+ External {126
+ importable: ok && features.contains(vk::ExternalMemoryFeatureFlags::IMPORTABLE),127
+ exportable: ok && features.contains(vk::ExternalMemoryFeatureFlags::EXPORTABLE),128
+ }129
+}130
+131
+fn memory_type(shared: &Shared, bits: u32, prefer: vk::MemoryPropertyFlags) -> Option<u32> {132
+ let props = unsafe {133
+ shared134
+ .device135
+ .instance136
+ .get_physical_device_memory_properties(shared.device.physical)137
+ };138
+ let types = &props.memory_types[..props.memory_type_count as usize];139
+ let allowed = |i: usize| bits & (1 << i) != 0;140
+ (0..types.len())141
+ .find(|&i| allowed(i) && types[i].property_flags.contains(prefer))142
+ .or_else(|| (0..types.len()).find(|&i| allowed(i)))143
+ .map(|i| i as u32)144
+}145
+146
+fn image_info<'a>(147
+ fourcc: Fourcc,148
+ size: (u32, u32),149
+ usage: vk::ImageUsageFlags,150
+) -> Result<vk::ImageCreateInfo<'a>, Error> {151
+ let fmt = vk_format(fourcc).ok_or(Error::UnsupportedFormat(fourcc))?;152
+ Ok(vk::ImageCreateInfo::default()153
+ .image_type(vk::ImageType::TYPE_2D)154
+ .format(fmt.format)155
+ .extent(vk::Extent3D {156
+ width: size.0,157
+ height: size.1,158
+ depth: 1,159
+ })160
+ .mip_levels(1)161
+ .array_layers(1)162
+ .samples(vk::SampleCountFlags::TYPE_1)163
+ .tiling(vk::ImageTiling::DRM_FORMAT_MODIFIER_EXT)164
+ .usage(usage)165
+ .sharing_mode(vk::SharingMode::EXCLUSIVE)166
+ .initial_layout(vk::ImageLayout::UNDEFINED))167
+}168
+169
+/// Imports a single-plane dmabuf as an image, without copying.170
+pub(crate) fn import(171
+ shared: &Arc<Shared>,172
+ caps: &DmabufCaps,173
+ dmabuf: &Dmabuf,174
+ usage: vk::ImageUsageFlags,175
+) -> Result<Arc<ImageInner>, Error> {176
+ let ext = shared177
+ .device178
+ .dmabuf179
+ .as_ref()180
+ .ok_or(Error::DmabufUnsupported)?;181
+ let format = dmabuf.format();182
+ let set = if usage.contains(vk::ImageUsageFlags::COLOR_ATTACHMENT) {183
+ &caps.render184
+ } else {185
+ &caps.sample186
+ };187
+ if dmabuf.num_planes() != 1 || !set.contains(&format) {188
+ return Err(Error::UnsupportedDmabuf(format));189
+ }190
+ let (Some(fd), Some(offset), Some(stride)) = (191
+ dmabuf.handles().next(),192
+ dmabuf.offsets().next(),193
+ dmabuf.strides().next(),194
+ ) else {195
+ return Err(Error::UnsupportedDmabuf(format));196
+ };197
+ let size = (dmabuf.width(), dmabuf.height());198
+ let d = &shared.device.device;199
+ let layouts = [vk::SubresourceLayout {200
+ offset: u64::from(offset),201
+ size: 0,202
+ row_pitch: u64::from(stride),203
+ array_pitch: 0,204
+ depth_pitch: 0,205
+ }];206
+ let mut explicit = vk::ImageDrmFormatModifierExplicitCreateInfoEXT::default()207
+ .drm_format_modifier(u64::from(format.modifier))208
+ .plane_layouts(&layouts);209
+ let mut external = vk::ExternalMemoryImageCreateInfo::default()210
+ .handle_types(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT);211
+ let info = image_info(format.code, size, usage)?212
+ .push_next(&mut external)213
+ .push_next(&mut explicit);214
+ let image = unsafe { d.create_image(&info, None)? };215
+ let destroy = || unsafe { d.destroy_image(image, None) };216
+ let owned = match fd.try_clone_to_owned() {217
+ Ok(o) => o,218
+ Err(_) => {219
+ destroy();220
+ return Err(Error::UnsupportedDmabuf(format));221
+ }222
+ };223
+ let mut fd_props = vk::MemoryFdPropertiesKHR::default();224
+ if let Err(e) = unsafe {225
+ ext.memory_fd.get_memory_fd_properties(226
+ vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT,227
+ owned.as_raw_fd(),228
+ &mut fd_props,229
+ )230
+ } {231
+ destroy();232
+ return Err(e.into());233
+ }234
+ let requirements = unsafe { d.get_image_memory_requirements(image) };235
+ let Some(type_index) = memory_type(236
+ shared,237
+ requirements.memory_type_bits & fd_props.memory_type_bits,238
+ vk::MemoryPropertyFlags::empty(),239
+ ) else {240
+ destroy();241
+ return Err(Error::UnsupportedDmabuf(format));242
+ };243
+ let raw = owned.into_raw_fd();244
+ let mut import = vk::ImportMemoryFdInfoKHR::default()245
+ .handle_type(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT)246
+ .fd(raw);247
+ let mut dedicated = vk::MemoryDedicatedAllocateInfo::default().image(image);248
+ let alloc = vk::MemoryAllocateInfo::default()249
+ .allocation_size(requirements.size)250
+ .memory_type_index(type_index)251
+ .push_next(&mut import)252
+ .push_next(&mut dedicated);253
+ let memory = match unsafe { d.allocate_memory(&alloc, None) } {254
+ Ok(m) => m,255
+ Err(e) => {256
+ drop(unsafe { OwnedFd::from_raw_fd(raw) });257
+ destroy();258
+ return Err(e.into());259
+ }260
+ };261
+ ImageInner::finish(262
+ shared,263
+ image,264
+ Memory::Imported(memory),265
+ memory,266
+ 0,267
+ format.code,268
+ size.0,269
+ size.1,270
+ true,271
+ )272
+}273
+274
+/// Allocates a dmabuf the device can render into, picking among `modifiers`.275
+pub(crate) fn export(276
+ shared: &Arc<Shared>,277
+ caps: &DmabufCaps,278
+ fourcc: Fourcc,279
+ size: (u32, u32),280
+ modifiers: &[Modifier],281
+) -> Result<Dmabuf, Error> {282
+ let ext = shared283
+ .device284
+ .dmabuf285
+ .as_ref()286
+ .ok_or(Error::DmabufUnsupported)?;287
+ let allowed: Vec<u64> = modifiers288
+ .iter()289
+ .filter(|&&modifier| {290
+ caps.render.contains(&Format {291
+ code: fourcc,292
+ modifier,293
+ })294
+ })295
+ .map(|&m| u64::from(m))296
+ .collect();297
+ if allowed.is_empty() {298
+ return Err(Error::UnsupportedFormat(fourcc));299
+ }300
+ let d = &shared.device.device;301
+ let mut list =302
+ vk::ImageDrmFormatModifierListCreateInfoEXT::default().drm_format_modifiers(&allowed);303
+ let mut external = vk::ExternalMemoryImageCreateInfo::default()304
+ .handle_types(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT);305
+ let info = image_info(fourcc, size, RENDER_USAGE)?306
+ .push_next(&mut external)307
+ .push_next(&mut list);308
+ let image = unsafe { d.create_image(&info, None)? };309
+ let result = (|| -> Result<Dmabuf, Error> {310
+ let requirements = unsafe { d.get_image_memory_requirements(image) };311
+ let type_index = memory_type(312
+ shared,313
+ requirements.memory_type_bits,314
+ vk::MemoryPropertyFlags::DEVICE_LOCAL,315
+ )316
+ .ok_or(Error::UnsupportedFormat(fourcc))?;317
+ let mut export = vk::ExportMemoryAllocateInfo::default()318
+ .handle_types(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT);319
+ let mut dedicated = vk::MemoryDedicatedAllocateInfo::default().image(image);320
+ let alloc = vk::MemoryAllocateInfo::default()321
+ .allocation_size(requirements.size)322
+ .memory_type_index(type_index)323
+ .push_next(&mut export)324
+ .push_next(&mut dedicated);325
+ let memory = unsafe { d.allocate_memory(&alloc, None)? };326
+ let exported = (|| -> Result<Dmabuf, Error> {327
+ unsafe { d.bind_image_memory(image, memory, 0)? };328
+ let raw = unsafe {329
+ ext.memory_fd.get_memory_fd(330
+ &vk::MemoryGetFdInfoKHR::default()331
+ .memory(memory)332
+ .handle_type(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT),333
+ )?334
+ };335
+ let fd = unsafe { OwnedFd::from_raw_fd(raw) };336
+ let mut props = vk::ImageDrmFormatModifierPropertiesEXT::default();337
+ unsafe {338
+ ext.modifier339
+ .get_image_drm_format_modifier_properties(image, &mut props)?340
+ };341
+ let layout = unsafe {342
+ d.get_image_subresource_layout(343
+ image,344
+ vk::ImageSubresource::default()345
+ .aspect_mask(vk::ImageAspectFlags::MEMORY_PLANE_0_EXT),346
+ )347
+ };348
+ let mut builder = Dmabuf::builder(349
+ (size.0 as i32, size.1 as i32),350
+ fourcc,351
+ Modifier::from(props.drm_format_modifier),352
+ DmabufFlags::empty(),353
+ );354
+ builder.add_plane(fd, layout.offset as u32, layout.row_pitch as u32);355
+ builder.build().ok_or(Error::UnsupportedFormat(fourcc))356
+ })();357
+ unsafe { d.free_memory(memory, None) };358
+ exported359
+ })();360
+ unsafe { d.destroy_image(image, None) };361
+ result362
+}363
+364
+/// Allocates render-capable dmabufs, for swapchains.365
+#[derive(Clone, Debug)]366
+pub struct VulkanAllocator {367
+ pub(crate) shared: Arc<Shared>,368
+ pub(crate) caps: Arc<DmabufCaps>,369
+}370
+371
+impl Allocator for VulkanAllocator {372
+ type Buffer = Dmabuf;373
+ type Error = Error;374
+375
+ fn create_buffer(376
+ &mut self,377
+ width: u32,378
+ height: u32,379
+ fourcc: Fourcc,380
+ modifiers: &[Modifier],381
+ ) -> Result<Dmabuf, Error> {382
+ export(&self.shared, &self.caps, fourcc, (width, height), modifiers)383
+ }384
+}385
+386
+impl std::fmt::Debug for Shared {387
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {388
+ f.debug_struct("Shared")389
+ .field("device", &self.device.name)390
+ .finish()391
+ }392
+}@@ -17,6 +17,10 @@ pub enum Error {
1717
BufferTooSmall { needed: usize, got: usize },1818
#[error("texture belongs to a different renderer")]1919
ForeignTexture,20
+ #[error("the device cannot share memory through dmabufs")]21
+ DmabufUnsupported,22
+ #[error("cannot import dmabuf with format {0:?}")]23
+ UnsupportedDmabuf(smithay::backend::allocator::Format),2024
#[error("interrupted while waiting for a sync point")]2125
Interrupted,2226
#[error("invalid size {0}x{1}")]@@ -159,7 +159,7 @@ impl Frame for VulkanFrame<'_, '_> {
159159
if !Arc::ptr_eq(&texture.0.shared, self.renderer.shared()) {160160
return Err(Error::ForeignTexture);161161
}162
- if dst.size.w <= 0 || dst.size.h <= 0 || Arc::ptr_eq(&texture.0, &self.target.0.0) {162
+ if dst.size.w <= 0 || dst.size.h <= 0 || Arc::ptr_eq(&texture.0, &self.target.0) {163163
return Ok(());164164
}165165
let scissors = self.scissors(dst, damage);@@ -7,18 +7,28 @@ use gpu_allocator::vulkan::{Allocation, AllocationCreateDesc, AllocationScheme};
77
use smithay::backend::allocator::Fourcc;88
use smithay::backend::renderer::{Texture, TextureMapping};99
use std::fmt;10
+use std::marker::PhantomData;1011
use std::sync::{Arc, Mutex};1112
13
+pub(crate) enum Memory {14
+ Allocated(Allocation),15
+ /// Memory imported from a dmabuf, owned directly.16
+ Imported(vk::DeviceMemory),17
+}18
+1219
pub(crate) struct ImageInner {1320
pub shared: Arc<Shared>,1421
pub image: vk::Image,1522
pub view: vk::ImageView,16
- pub allocation: Mutex<Option<Allocation>>,23
+ pub memory: Mutex<Option<Memory>>,1724
pub fourcc: Fourcc,1825
pub format: vk::Format,1926
pub width: u32,2027
pub height: u32,2128
pub layout: Mutex<vk::ImageLayout>,29
+ /// Shared with other devices or processes through a dmabuf. Every use30
+ /// acquires the image from the foreign queue family and releases it after.31
+ pub external: bool,2232
}2333
2434
impl fmt::Debug for ImageInner {@@ -27,6 +37,7 @@ impl fmt::Debug for ImageInner {
2737
.field("image", &self.image)2838
.field("fourcc", &self.fourcc)2939
.field("size", &(self.width, self.height))40
+ .field("external", &self.external)3041
.finish()3142
}3243
}@@ -38,18 +49,17 @@ impl Drop for ImageInner {
3849
d.destroy_image_view(self.view, None);3950
d.destroy_image(self.image, None);4051
}41
- if let Some(a) = self42
- .allocation43
- .lock()44
- .unwrap_or_else(|e| e.into_inner())45
- .take()46
- {47
- let _ = self48
- .shared49
- .allocator50
- .lock()51
- .unwrap_or_else(|e| e.into_inner())52
- .free(a);52
+ match self.memory.lock().unwrap_or_else(|e| e.into_inner()).take() {53
+ Some(Memory::Allocated(a)) => {54
+ let _ = self55
+ .shared56
+ .allocator57
+ .lock()58
+ .unwrap_or_else(|e| e.into_inner())59
+ .free(a);60
+ }61
+ Some(Memory::Imported(m)) => unsafe { d.free_memory(m, None) },62
+ None => {}5363
}5464
}5565
}@@ -101,25 +111,57 @@ impl ImageInner {
101111
return Err(e.into());102112
}103113
};104
- unsafe { d.bind_image_memory(image, allocation.memory(), allocation.offset())? };105
- let view_info = vk::ImageViewCreateInfo::default()106
- .image(image)107
- .view_type(vk::ImageViewType::TYPE_2D)108
- .format(fmt.format)109
- .components(components(fmt.opaque))110
- .subresource_range(color_range());111
- let view = unsafe { d.create_image_view(&view_info, None)? };112
- Ok(Arc::new(Self {114
+ let memory = Memory::Allocated(allocation);115
+ let (mem, offset) = match &memory {116
+ Memory::Allocated(a) => (unsafe { a.memory() }, a.offset()),117
+ Memory::Imported(m) => (*m, 0),118
+ };119
+ Self::finish(120
+ shared, image, memory, mem, offset, fourcc, width, height, false,121
+ )122
+ }123
+124
+ /// Binds memory, creates the view and wraps everything up. Takes125
+ /// ownership of `image` and `memory`, releasing both on failure.126
+ #[expect(clippy::too_many_arguments)]127
+ pub fn finish(128
+ shared: &Arc<Shared>,129
+ image: vk::Image,130
+ memory: Memory,131
+ mem: vk::DeviceMemory,132
+ offset: u64,133
+ fourcc: Fourcc,134
+ width: u32,135
+ height: u32,136
+ external: bool,137
+ ) -> Result<Arc<Self>, Error> {138
+ let fmt = vk_format(fourcc).ok_or(Error::UnsupportedFormat(fourcc))?;139
+ let d = &shared.device.device;140
+ let mut inner = Self {113141
shared: shared.clone(),114142
image,115
- view,116
- allocation: Mutex::new(Some(allocation)),143
+ view: vk::ImageView::null(),144
+ memory: Mutex::new(Some(memory)),117145
fourcc,118146
format: fmt.format,119147
width,120148
height,121
- layout: Mutex::new(vk::ImageLayout::UNDEFINED),122
- }))149
+ layout: Mutex::new(if external {150
+ vk::ImageLayout::GENERAL151
+ } else {152
+ vk::ImageLayout::UNDEFINED153
+ }),154
+ external,155
+ };156
+ unsafe { d.bind_image_memory(image, mem, offset)? };157
+ let view_info = vk::ImageViewCreateInfo::default()158
+ .image(image)159
+ .view_type(vk::ImageViewType::TYPE_2D)160
+ .format(fmt.format)161
+ .components(components(fmt.opaque))162
+ .subresource_range(color_range());163
+ inner.view = unsafe { d.create_image_view(&view_info, None)? };164
+ Ok(Arc::new(inner))123165
}124166
125167
pub fn layout(&self) -> vk::ImageLayout {@@ -232,9 +274,12 @@ impl VulkanImage {
232274
}233275
}234276
235
-/// A bound render target.277
+/// A bound render target, borrowing whatever it was bound from.236278
#[derive(Debug)]237
-pub struct VulkanTarget<'a>(pub(crate) &'a mut VulkanImage);279
+pub struct VulkanTarget<'a>(280
+ pub(crate) Arc<ImageInner>,281
+ pub(crate) PhantomData<&'a mut ()>,282
+);238283
239284
/// Pixels read back from the GPU.240285
#[derive(Debug)]@@ -266,7 +311,7 @@ macro_rules! texture_impl {
266311
267312
texture_impl!(VulkanTexture, |s| s.0);268313
texture_impl!(VulkanImage, |s| s.0);269
-texture_impl!(VulkanTarget<'_>, |s| s.0.0);314
+texture_impl!(VulkanTarget<'_>, |s| s.0);270315
texture_impl!(VulkanMapping, |s| s);271316
272317
impl TextureMapping for VulkanMapping {@@ -4,6 +4,7 @@
44
//! command buffer when it finishes, returning a fence as its sync point.55
66
mod device;7
+mod dmabuf;78
mod error;89
mod format;910
mod frame;@@ -13,6 +14,7 @@ mod renderer;
1314
mod sync;1415
mod transform;1516
17
+pub use dmabuf::VulkanAllocator;1618
pub use error::Error;1719
pub use frame::VulkanFrame;1820
pub use image::{VulkanImage, VulkanMapping, VulkanTarget, VulkanTexture};@@ -1,5 +1,6 @@
11
use crate::Error;22
use crate::device::Shared;3
+use crate::dmabuf::{self, DmabufCaps, RENDER_USAGE, SAMPLE_USAGE, VulkanAllocator};34
use crate::format::{BYTES_PER_PIXEL, SUPPORTED};45
use crate::frame::{Op, VulkanFrame};56
use crate::image::{@@ -10,23 +11,29 @@ use crate::sync::{FenceInner, VkFence};
1011
use ash::vk;1112
use gpu_allocator::MemoryLocation;1213
use smithay::backend::allocator::Fourcc;14
+use smithay::backend::allocator::dmabuf::{Dmabuf, WeakDmabuf};15
+use smithay::backend::allocator::format::FormatSet;1316
use smithay::backend::renderer::sync::SyncPoint;1417
use smithay::backend::renderer::{15
- Bind, ContextId, DebugFlags, ExportMem, ImportMem, Offscreen, Renderer, RendererSuper, Texture,16
- TextureFilter,18
+ Bind, ContextId, DebugFlags, ExportMem, ImportDma, ImportDmaWl, ImportMem, Offscreen, Renderer,19
+ RendererSuper, Texture, TextureFilter,1720
};1821
use smithay::utils::{Buffer, Physical, Rectangle, Size, Transform};22
+use std::collections::HashMap;1923
use std::fmt;24
+use std::marker::PhantomData;2025
use std::sync::Arc;2126
2227
struct Pending {2328
fence: Arc<FenceInner>,2429
cmd: vk::CommandBuffer,25
- _keep: Vec<VulkanTexture>,30
+ _keep: Vec<Arc<ImageInner>>,2631
}2732
2833
pub struct VulkanRenderer {2934
pending: Vec<Pending>,35
+ dmabuf_textures: HashMap<WeakDmabuf, VulkanTexture>,36
+ dmabuf_targets: HashMap<WeakDmabuf, Arc<ImageInner>>,3037
pipelines: Pipelines,3138
pool: vk::CommandPool,3239
samplers: [vk::Sampler; 4],@@ -34,6 +41,7 @@ pub struct VulkanRenderer {
3441
mag_filter: TextureFilter,3542
debug: DebugFlags,3643
context: ContextId<VulkanTexture>,44
+ caps: Arc<DmabufCaps>,3745
shared: Arc<Shared>,3846
}3947
@@ -78,6 +86,8 @@ impl VulkanRenderer {
7886
}7987
Ok(Self {8088
pending: Vec::new(),89
+ dmabuf_textures: HashMap::new(),90
+ dmabuf_targets: HashMap::new(),8191
pipelines: Pipelines::new(&shared)?,8292
pool,8393
samplers,@@ -85,6 +95,7 @@ impl VulkanRenderer {
8595
mag_filter: TextureFilter::Linear,8696
debug: DebugFlags::empty(),8797
context: ContextId::new(),98
+ caps: Arc::new(DmabufCaps::query(&shared)),8899
shared,89100
})90101
}@@ -94,6 +105,24 @@ impl VulkanRenderer {
94105
&self.shared.device.name95106
}96107
108
+ /// Whether dmabufs can be imported, rendered into and allocated.109
+ pub fn supports_dmabuf(&self) -> bool {110
+ self.caps.render.iter().next().is_some()111
+ }112
+113
+ /// Formats and modifiers this renderer can render into as dmabufs.114
+ pub fn render_formats(&self) -> FormatSet {115
+ self.caps.render.clone()116
+ }117
+118
+ /// An allocator for render-capable dmabufs on this device.119
+ pub fn allocator(&self) -> VulkanAllocator {120
+ VulkanAllocator {121
+ shared: self.shared.clone(),122
+ caps: self.caps.clone(),123
+ }124
+ }125
+97126
pub(crate) fn shared(&self) -> &Arc<Shared> {98127
&self.shared99128
}@@ -117,6 +146,8 @@ impl VulkanRenderer {
117146
unsafe { d.free_command_buffers(pool, &[p.cmd]) };118147
false119148
});149
+ self.dmabuf_textures.retain(|k, _| !k.is_gone());150
+ self.dmabuf_targets.retain(|k, _| !k.is_gone());120151
}121152
122153
fn begin(&self) -> Result<vk::CommandBuffer, Error> {@@ -151,17 +182,21 @@ impl VulkanRenderer {
151182
.queue152183
.lock()153184
.unwrap_or_else(|e| e.into_inner());154
- unsafe { d.queue_submit2(*queue, &[submit], fence.fence)? };185
+ unsafe { d.queue_submit2(*queue, &[submit], fence.fence())? };155186
Ok(fence)156187
}157188
158189
/// Records, submits and waits for a short command buffer.159190
fn one_shot(160191
&mut self,161
- record: impl FnOnce(&ash::Device, vk::CommandBuffer),192
+ record: impl FnOnce(&ash::Device, vk::CommandBuffer, u32),162193
) -> Result<(), Error> {163194
let cmd = self.begin()?;164
- record(&self.shared.device.device, cmd);195
+ record(196
+ &self.shared.device.device,197
+ cmd,198
+ self.shared.device.queue_family,199
+ );165200
let fence = self.end_and_submit(cmd)?;166201
let res = fence.wait();167202
unsafe {@@ -179,18 +214,15 @@ impl VulkanRenderer {
179214
ops: Vec<Op>,180215
) -> Result<SyncPoint, Error> {181216
self.reap();182
- let image = target.0.0.clone();183
- let mut keep = vec![VulkanTexture(image.clone())];184
- let mut transitions = Vec::new();217
+ let image = target.0.clone();218
+ let mut sampled: Vec<Arc<ImageInner>> = Vec::new();185219
for op in &ops {186220
if let Op::Draw {187221
texture: Some(t), ..188222
} = op223
+ && !sampled.iter().any(|s| Arc::ptr_eq(s, &t.0))189224
{190
- if t.0.layout() != vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL {191
- transitions.push(t.clone());192
- }193
- keep.push(t.clone());225
+ sampled.push(t.0.clone());194226
}195227
}196228
let mut pipelines = Vec::with_capacity(ops.len());@@ -202,25 +234,19 @@ impl VulkanRenderer {
202234
}203235
let cmd = self.begin()?;204236
let d = &self.shared.device.device;237
+ let family = self.shared.device.queue_family;205238
let push = &self.shared.device.push_descriptor;206239
let (w, h) = (image.width, image.height);207240
let sampler = self.sampler();208241
unsafe {209
- for t in &transitions {210
- barrier(211
- d,212
- cmd,213
- t.0.image,214
- t.0.layout(),215
- vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,216
- );217
- t.0.set_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL);242
+ for t in &sampled {243
+ acquire(d, cmd, family, t, vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL);218244
}219
- barrier(245
+ acquire(220246
d,221247
cmd,222
- image.image,223
- image.layout(),248
+ family,249
+ &image,224250
vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,225251
);226252
let attachments = [vk::RenderingAttachmentInfo::default()@@ -318,13 +344,23 @@ impl VulkanRenderer {
318344
}319345
}320346
d.cmd_end_rendering(cmd);347
+ release(348
+ d,349
+ cmd,350
+ family,351
+ &image,352
+ vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,353
+ );354
+ for t in &sampled {355
+ release(d, cmd, family, t, vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL);356
+ }321357
}322
- image.set_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL);323358
let fence = self.end_and_submit(cmd)?;359
+ sampled.push(image);324360
self.pending.push(Pending {325361
fence: fence.clone(),326362
cmd,327
- _keep: keep,363
+ _keep: sampled,328364
});329365
Ok(SyncPoint::from(VkFence(fence)))330366
}@@ -336,6 +372,9 @@ impl VulkanRenderer {
336372
region: Rectangle<i32, Buffer>,337373
flipped: bool,338374
) -> Result<(), Error> {375
+ if image.external {376
+ return Err(Error::ForeignTexture);377
+ }339378
let width = image.width as usize;340379
let needed = width * image.height as usize * BYTES_PER_PIXEL;341380
if data.len() < needed {@@ -357,7 +396,6 @@ impl VulkanRenderer {
357396
let from = if flipped { rows - 1 - y } else { y };358397
dst[y * row..(y + 1) * row].copy_from_slice(&data[from * row..(from + 1) * row]);359398
}360
- let old = image.layout();361399
let copy = vk::BufferImageCopy::default()362400
.buffer_offset(363401
((region.loc.y as usize * width + region.loc.x as usize) * BYTES_PER_PIXEL) as u64,@@ -378,26 +416,31 @@ impl VulkanRenderer {
378416
height: region.size.h as u32,379417
depth: 1,380418
});381
- let (img, buffer) = (image.image, staging.buffer);382
- self.one_shot(|d, cmd| unsafe {383
- barrier(d, cmd, img, old, vk::ImageLayout::TRANSFER_DST_OPTIMAL);419
+ let buffer = staging.buffer;420
+ let target = image.clone();421
+ self.one_shot(|d, cmd, family| unsafe {422
+ acquire(423
+ d,424
+ cmd,425
+ family,426
+ &target,427
+ vk::ImageLayout::TRANSFER_DST_OPTIMAL,428
+ );384429
d.cmd_copy_buffer_to_image(385430
cmd,386431
buffer,387
- img,432
+ target.image,388433
vk::ImageLayout::TRANSFER_DST_OPTIMAL,389434
&[copy],390435
);391
- barrier(436
+ acquire(392437
d,393438
cmd,394
- img,395
- vk::ImageLayout::TRANSFER_DST_OPTIMAL,439
+ family,440
+ &target,396441
vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,397442
);398
- })?;399
- image.set_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL);400
- Ok(())443
+ })401444
}402445
403446
fn read_back(@@ -421,7 +464,6 @@ impl VulkanRenderer {
421464
MemoryLocation::GpuToCpu,422465
)?;423466
if w > 0 && h > 0 {424
- let old = image.layout();425467
let copy = vk::BufferImageCopy::default()426468
.image_subresource(427469
vk::ImageSubresourceLayers::default()@@ -438,18 +480,31 @@ impl VulkanRenderer {
438480
height: h,439481
depth: 1,440482
});441
- let (img, buffer) = (image.image, staging.buffer);442
- self.one_shot(|d, cmd| unsafe {443
- barrier(d, cmd, img, old, vk::ImageLayout::TRANSFER_SRC_OPTIMAL);483
+ let buffer = staging.buffer;484
+ let source = image.clone();485
+ self.one_shot(|d, cmd, family| unsafe {486
+ acquire(487
+ d,488
+ cmd,489
+ family,490
+ &source,491
+ vk::ImageLayout::TRANSFER_SRC_OPTIMAL,492
+ );444493
d.cmd_copy_image_to_buffer(445494
cmd,446
- img,495
+ source.image,447496
vk::ImageLayout::TRANSFER_SRC_OPTIMAL,448497
buffer,449498
&[copy],450499
);500
+ release(501
+ d,502
+ cmd,503
+ family,504
+ &source,505
+ vk::ImageLayout::TRANSFER_SRC_OPTIMAL,506
+ );451507
})?;452
- image.set_layout(vk::ImageLayout::TRANSFER_SRC_OPTIMAL);453508
}454509
let mut data = staging.bytes()[..w as usize * h as usize * BYTES_PER_PIXEL].to_vec();455510
if swap {@@ -498,14 +553,17 @@ fn sampler_filter(bit: usize) -> TextureFilter {
498553
}499554
}500555
501
-/// A full barrier between all earlier and later work on the image. Coarse,502
-/// but correct for every transition the renderer makes.556
+/// A full barrier between all earlier and later work on the image, optionally557
+/// transferring ownership between queue families. Coarse, but correct for558
+/// every transition the renderer makes.503559
unsafe fn barrier(504560
d: &ash::Device,505561
cmd: vk::CommandBuffer,506562
image: vk::Image,507563
old: vk::ImageLayout,508564
new: vk::ImageLayout,565
+ src_family: u32,566
+ dst_family: u32,509567
) {510568
let b = [vk::ImageMemoryBarrier2::default()511569
.src_stage_mask(vk::PipelineStageFlags2::ALL_COMMANDS)@@ -514,6 +572,8 @@ unsafe fn barrier(
514572
.dst_access_mask(vk::AccessFlags2::MEMORY_READ | vk::AccessFlags2::MEMORY_WRITE)515573
.old_layout(old)516574
.new_layout(new)575
+ .src_queue_family_index(src_family)576
+ .dst_queue_family_index(dst_family)517577
.image(image)518578
.subresource_range(color_range())];519579
unsafe {@@ -524,6 +584,70 @@ unsafe fn barrier(
524584
};525585
}526586
587
+/// Moves an image into `layout` for use on this queue. Dmabuf images come588
+/// from the foreign queue family in the general layout, others continue from589
+/// their tracked layout.590
+unsafe fn acquire(591
+ d: &ash::Device,592
+ cmd: vk::CommandBuffer,593
+ family: u32,594
+ image: &ImageInner,595
+ layout: vk::ImageLayout,596
+) {597
+ if image.external {598
+ unsafe {599
+ barrier(600
+ d,601
+ cmd,602
+ image.image,603
+ vk::ImageLayout::GENERAL,604
+ layout,605
+ vk::QUEUE_FAMILY_FOREIGN_EXT,606
+ family,607
+ )608
+ };609
+ } else {610
+ let old = image.layout();611
+ if old != layout {612
+ unsafe {613
+ barrier(614
+ d,615
+ cmd,616
+ image.image,617
+ old,618
+ layout,619
+ vk::QUEUE_FAMILY_IGNORED,620
+ vk::QUEUE_FAMILY_IGNORED,621
+ )622
+ };623
+ image.set_layout(layout);624
+ }625
+ }626
+}627
+628
+/// Hands a dmabuf image back to the foreign queue family after use.629
+unsafe fn release(630
+ d: &ash::Device,631
+ cmd: vk::CommandBuffer,632
+ family: u32,633
+ image: &ImageInner,634
+ layout: vk::ImageLayout,635
+) {636
+ if image.external {637
+ unsafe {638
+ barrier(639
+ d,640
+ cmd,641
+ image.image,642
+ layout,643
+ vk::ImageLayout::GENERAL,644
+ family,645
+ vk::QUEUE_FAMILY_FOREIGN_EXT,646
+ )647
+ };648
+ }649
+}650
+527651
/// Whether reading `from` as `to` needs red and blue swapped.528652
fn needs_swizzle(from: Fourcc, to: Fourcc) -> Result<bool, Error> {529653
let bgr = |f: Fourcc| match f {@@ -577,7 +701,7 @@ impl Renderer for VulkanRenderer {
577701
where578702
'buffer: 'frame,579703
{580
- self.own(&framebuffer.0.0)?;704
+ self.own(&framebuffer.0)?;581705
self.reap();582706
Ok(VulkanFrame::new(583707
self,@@ -595,12 +719,38 @@ impl Renderer for VulkanRenderer {
595719
self.reap();596720
Ok(())597721
}722
+723
+ fn invalidate_caches(&mut self) -> Result<(), Error> {724
+ self.reap();725
+ self.dmabuf_textures.clear();726
+ self.dmabuf_targets.clear();727
+ Ok(())728
+ }598729
}599730
600731
impl Bind<VulkanImage> for VulkanRenderer {601732
fn bind<'a>(&mut self, target: &'a mut VulkanImage) -> Result<VulkanTarget<'a>, Error> {602733
self.own(&target.0)?;603
- Ok(VulkanTarget(target))734
+ Ok(VulkanTarget(target.0.clone(), PhantomData))735
+ }736
+}737
+738
+impl Bind<Dmabuf> for VulkanRenderer {739
+ fn bind<'a>(&mut self, target: &'a mut Dmabuf) -> Result<VulkanTarget<'a>, Error> {740
+ let key = target.weak();741
+ let image = match self.dmabuf_targets.get(&key) {742
+ Some(image) => image.clone(),743
+ None => {744
+ let image = dmabuf::import(&self.shared, &self.caps, target, RENDER_USAGE)?;745
+ self.dmabuf_targets.insert(key, image.clone());746
+ image747
+ }748
+ };749
+ Ok(VulkanTarget(image, PhantomData))750
+ }751
+752
+ fn supported_formats(&self) -> Option<FormatSet> {753
+ Some(self.caps.render.clone())604754
}605755
}606756
@@ -664,6 +814,33 @@ impl ImportMem for VulkanRenderer {
664814
}665815
}666816
817
+impl ImportDma for VulkanRenderer {818
+ fn dmabuf_formats(&self) -> FormatSet {819
+ self.caps.sample.clone()820
+ }821
+822
+ fn import_dmabuf(823
+ &mut self,824
+ buffer: &Dmabuf,825
+ _damage: Option<&[Rectangle<i32, Buffer>]>,826
+ ) -> Result<VulkanTexture, Error> {827
+ let key = buffer.weak();828
+ if let Some(t) = self.dmabuf_textures.get(&key) {829
+ return Ok(t.clone());830
+ }831
+ let texture = VulkanTexture(dmabuf::import(832
+ &self.shared,833
+ &self.caps,834
+ buffer,835
+ SAMPLE_USAGE,836
+ )?);837
+ self.dmabuf_textures.insert(key, texture.clone());838
+ Ok(texture)839
+ }840
+}841
+842
+impl ImportDmaWl for VulkanRenderer {}843
+667844
impl ExportMem for VulkanRenderer {668845
type TextureMapping = VulkanMapping;669846
@@ -673,8 +850,8 @@ impl ExportMem for VulkanRenderer {
673850
region: Rectangle<i32, Buffer>,674851
format: Fourcc,675852
) -> Result<VulkanMapping, Error> {676
- self.own(&target.0.0)?;677
- let image = target.0.0.clone();853
+ self.own(&target.0)?;854
+ let image = target.0.clone();678855
self.read_back(&image, region, format)679856
}680857
@@ -688,8 +865,8 @@ impl ExportMem for VulkanRenderer {
688865
self.read_back(&texture.0, region, format)689866
}690867
691
- fn can_read_texture(&mut self, _texture: &VulkanTexture) -> Result<bool, Error> {692
- Ok(true)868
+ fn can_read_texture(&mut self, texture: &VulkanTexture) -> Result<bool, Error> {869
+ Ok(texture.0.external || texture.0.layout() != vk::ImageLayout::UNDEFINED)693870
}694871
695872
fn map_texture<'a>(&mut self, mapping: &'a VulkanMapping) -> Result<&'a [u8], Error> {@@ -1,38 +1,101 @@
11
use crate::device::Shared;22
use ash::vk;3
+use rustix::event::{PollFd, PollFlags, poll};34
use smithay::backend::renderer::sync::{Fence, Interrupted};45
use std::fmt;5
-use std::sync::Arc;6
+use std::os::fd::{FromRawFd, OwnedFd};7
+use std::sync::{Arc, Mutex};8
+9
+enum State {10
+ Pending,11
+ /// Exporting a sync_file resets the Vulkan fence, so from then on the12
+ /// file is the only record of completion. `None` means it had already13
+ /// signalled when exported.14
+ Exported(Option<OwnedFd>),15
+}616
717
pub(crate) struct FenceInner {8
- pub shared: Arc<Shared>,9
- pub fence: vk::Fence,18
+ shared: Arc<Shared>,19
+ fence: vk::Fence,20
+ exportable: bool,21
+ state: Mutex<State>,1022
}1123
1224
impl FenceInner {1325
pub fn new(shared: &Arc<Shared>) -> Result<Arc<Self>, vk::Result> {14
- let fence = unsafe {15
- shared16
- .device17
- .device18
- .create_fence(&vk::FenceCreateInfo::default(), None)?19
- };26
+ let exportable = shared.device.dmabuf.is_some();27
+ let mut export = vk::ExportFenceCreateInfo::default()28
+ .handle_types(vk::ExternalFenceHandleTypeFlags::SYNC_FD);29
+ let mut info = vk::FenceCreateInfo::default();30
+ if exportable {31
+ info = info.push_next(&mut export);32
+ }33
+ let fence = unsafe { shared.device.device.create_fence(&info, None)? };2034
Ok(Arc::new(Self {2135
shared: shared.clone(),2236
fence,37
+ exportable,38
+ state: Mutex::new(State::Pending),2339
}))2440
}2541
42
+ pub fn fence(&self) -> vk::Fence {43
+ self.fence44
+ }45
+2646
pub fn signaled(&self) -> bool {27
- unsafe { self.shared.device.device.get_fence_status(self.fence) }.unwrap_or(true)47
+ match &*self.state.lock().unwrap_or_else(|e| e.into_inner()) {48
+ State::Pending => {49
+ unsafe { self.shared.device.device.get_fence_status(self.fence) }.unwrap_or(true)50
+ }51
+ State::Exported(None) => true,52
+ State::Exported(Some(fd)) => readable(fd, Some(&Default::default())),53
+ }2854
}2955
3056
pub fn wait(&self) -> Result<(), vk::Result> {31
- unsafe {32
- self.shared33
- .device34
- .device35
- .wait_for_fences(&[self.fence], true, u64::MAX)57
+ match &*self.state.lock().unwrap_or_else(|e| e.into_inner()) {58
+ State::Pending => unsafe {59
+ self.shared60
+ .device61
+ .device62
+ .wait_for_fences(&[self.fence], true, u64::MAX)63
+ },64
+ State::Exported(None) => Ok(()),65
+ State::Exported(Some(fd)) => {66
+ readable(fd, None);67
+ Ok(())68
+ }69
+ }70
+ }71
+72
+ fn export(&self) -> Option<OwnedFd> {73
+ if !self.exportable {74
+ return None;75
+ }76
+ let ext = self.shared.device.dmabuf.as_ref()?;77
+ let mut state = self.state.lock().unwrap_or_else(|e| e.into_inner());78
+ if let State::Pending = *state {79
+ let info = vk::FenceGetFdInfoKHR::default()80
+ .fence(self.fence)81
+ .handle_type(vk::ExternalFenceHandleTypeFlags::SYNC_FD);82
+ let raw = unsafe { ext.fence_fd.get_fence_fd(&info) }.ok()?;83
+ *state = State::Exported((raw >= 0).then(|| unsafe { OwnedFd::from_raw_fd(raw) }));84
+ }85
+ match &*state {86
+ State::Exported(Some(fd)) => fd.try_clone().ok(),87
+ _ => None,88
+ }89
+ }90
+}91
+92
+fn readable(fd: &OwnedFd, timeout: Option<&rustix::event::Timespec>) -> bool {93
+ let mut fds = [PollFd::new(fd, PollFlags::IN)];94
+ loop {95
+ match poll(&mut fds, timeout) {96
+ Ok(n) => return n > 0,97
+ Err(rustix::io::Errno::INTR) => continue,98
+ Err(_) => return true,3699
}37100
}38101
}@@ -62,10 +125,10 @@ impl Fence for VkFence {
62125
}63126
64127
fn is_exportable(&self) -> bool {65
- false128
+ self.0.exportable66129
}67130
68
- fn export(&self) -> Option<std::os::fd::OwnedFd> {69
- None131
+ fn export(&self) -> Option<OwnedFd> {132
+ self.0.export()70133
}71134
}@@ -0,0 +1,129 @@
1
+//! Dmabuf allocation, rendering, import and fence export. Skips on devices2
+//! without dmabuf support, such as lavapipe inside the nix sandbox.3
+4
+use monolith_render_vk::VulkanRenderer;5
+use smithay::backend::allocator::dmabuf::Dmabuf;6
+use smithay::backend::allocator::{Allocator, Fourcc, Modifier};7
+use smithay::backend::renderer::{8
+ Bind, Color32F, ExportMem, Frame, ImportDma, Offscreen, Renderer,9
+};10
+use smithay::utils::{Rectangle, Transform};11
+12
+const SIZE: i32 = 32;13
+14
+fn renderer() -> Option<VulkanRenderer> {15
+ let r = VulkanRenderer::new().expect("no usable Vulkan device");16
+ if !r.supports_dmabuf() {17
+ eprintln!("skipping, {} cannot share dmabufs", r.device_name());18
+ return None;19
+ }20
+ Some(r)21
+}22
+23
+fn dmabuf(r: &VulkanRenderer) -> Dmabuf {24
+ let modifiers: Vec<Modifier> = r25
+ .render_formats()26
+ .iter()27
+ .filter(|f| f.code == Fourcc::Argb8888)28
+ .map(|f| f.modifier)29
+ .collect();30
+ r.allocator()31
+ .create_buffer(SIZE as u32, SIZE as u32, Fourcc::Argb8888, &modifiers)32
+ .unwrap()33
+}34
+35
+fn fill(36
+ r: &mut VulkanRenderer,37
+ buf: &mut Dmabuf,38
+ colour: [f32; 4],39
+) -> smithay::backend::renderer::sync::SyncPoint {40
+ let mut target = r.bind(buf).unwrap();41
+ let mut frame = r42
+ .render(&mut target, (SIZE, SIZE).into(), Transform::Normal)43
+ .unwrap();44
+ frame45
+ .clear(46
+ Color32F::from(colour),47
+ &[Rectangle::from_size((SIZE, SIZE).into())],48
+ )49
+ .unwrap();50
+ frame.finish().unwrap()51
+}52
+53
+fn all_rgba(bytes: &[u8], want: [u8; 4]) -> bool {54
+ bytes.as_chunks::<4>().0.iter().all(|p| *p == want)55
+}56
+57
+#[test]58
+fn renders_into_an_allocated_dmabuf_and_reads_it_back() {59
+ let Some(mut r) = renderer() else { return };60
+ let mut buf = dmabuf(&r);61
+ fill(&mut r, &mut buf, [1.0, 0.0, 0.0, 1.0]).wait().unwrap();62
+ let target = r.bind(&mut buf).unwrap();63
+ let mapping = r64
+ .copy_framebuffer(65
+ &target,66
+ Rectangle::from_size((SIZE, SIZE).into()),67
+ Fourcc::Abgr8888,68
+ )69
+ .unwrap();70
+ assert!(all_rgba(r.map_texture(&mapping).unwrap(), [255, 0, 0, 255]));71
+}72
+73
+#[test]74
+fn samples_an_imported_dmabuf() {75
+ let Some(mut r) = renderer() else { return };76
+ let mut buf = dmabuf(&r);77
+ fill(&mut r, &mut buf, [0.0, 0.0, 1.0, 1.0]).wait().unwrap();78
+ let texture = r.import_dmabuf(&buf, None).unwrap();79
+ let mut out = r80
+ .create_buffer(Fourcc::Argb8888, (SIZE, SIZE).into())81
+ .unwrap();82
+ let mut target = r.bind(&mut out).unwrap();83
+ {84
+ let full = Rectangle::from_size((SIZE, SIZE).into());85
+ let mut frame = r86
+ .render(&mut target, (SIZE, SIZE).into(), Transform::Normal)87
+ .unwrap();88
+ frame89
+ .render_texture_from_to(90
+ &texture,91
+ Rectangle::from_size((SIZE as f64, SIZE as f64).into()),92
+ full,93
+ &[full],94
+ &[],95
+ Transform::Normal,96
+ 1.0,97
+ )98
+ .unwrap();99
+ frame.finish().unwrap().wait().unwrap();100
+ }101
+ let mapping = r102
+ .copy_framebuffer(103
+ &target,104
+ Rectangle::from_size((SIZE, SIZE).into()),105
+ Fourcc::Abgr8888,106
+ )107
+ .unwrap();108
+ assert!(all_rgba(r.map_texture(&mapping).unwrap(), [0, 0, 255, 255]));109
+}110
+111
+#[test]112
+fn imports_are_cached_per_buffer() {113
+ let Some(mut r) = renderer() else { return };114
+ let buf = dmabuf(&r);115
+ let a = r.import_dmabuf(&buf, None).unwrap();116
+ let b = r.import_dmabuf(&buf, None).unwrap();117
+ assert_eq!(format!("{a:?}"), format!("{b:?}"));118
+}119
+120
+#[test]121
+fn frame_fences_export_as_sync_files() {122
+ let Some(mut r) = renderer() else { return };123
+ let mut buf = dmabuf(&r);124
+ let sync = fill(&mut r, &mut buf, [0.0, 1.0, 0.0, 1.0]);125
+ assert!(sync.is_exportable());126
+ let _fd = sync.export();127
+ sync.wait().unwrap();128
+ assert!(sync.is_reached());129
+}