| 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 and |
| 13 | /// 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).importable |
| 50 | { |
| 51 | sample.push(format); |
| 52 | } |
| 53 | let renderable = vk::FormatFeatureFlags::COLOR_ATTACHMENT |
| 54 | | 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.instance |
| 77 | .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.instance |
| 86 | .get_physical_device_format_properties2(dev.physical, format, &mut props) |
| 87 | }; |
| 88 | out |
| 89 | } |
| 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.instance |
| 119 | .get_physical_device_image_format_properties2(dev.physical, &info, &mut props) |
| 120 | } |
| 121 | .is_ok(); |
| 122 | let features = external_props |
| 123 | .external_memory_properties |
| 124 | .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 | shared |
| 134 | .device |
| 135 | .instance |
| 136 | .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 = shared |
| 177 | .device |
| 178 | .dmabuf |
| 179 | .as_ref() |
| 180 | .ok_or(Error::DmabufUnsupported)?; |
| 181 | let format = dmabuf.format(); |
| 182 | let set = if usage.contains(vk::ImageUsageFlags::COLOR_ATTACHMENT) { |
| 183 | &caps.render |
| 184 | } else { |
| 185 | &caps.sample |
| 186 | }; |
| 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 | // SAFETY: a failed import leaves the fd with us, per |
| 257 | // VK_KHR_external_memory_fd. |
| 258 | drop(unsafe { OwnedFd::from_raw_fd(raw) }); |
| 259 | destroy(); |
| 260 | return Err(e.into()); |
| 261 | } |
| 262 | }; |
| 263 | ImageInner::finish( |
| 264 | shared, |
| 265 | image, |
| 266 | Memory::Imported(memory), |
| 267 | memory, |
| 268 | 0, |
| 269 | format.code, |
| 270 | size.0, |
| 271 | size.1, |
| 272 | true, |
| 273 | ) |
| 274 | } |
| 275 | |
| 276 | /// Allocates a dmabuf the device can render into, picking among `modifiers`. |
| 277 | pub(crate) fn export( |
| 278 | shared: &Arc<Shared>, |
| 279 | caps: &DmabufCaps, |
| 280 | fourcc: Fourcc, |
| 281 | size: (u32, u32), |
| 282 | modifiers: &[Modifier], |
| 283 | ) -> Result<Dmabuf, Error> { |
| 284 | let ext = shared |
| 285 | .device |
| 286 | .dmabuf |
| 287 | .as_ref() |
| 288 | .ok_or(Error::DmabufUnsupported)?; |
| 289 | let allowed: Vec<u64> = modifiers |
| 290 | .iter() |
| 291 | .filter(|&&modifier| { |
| 292 | caps.render.contains(&Format { |
| 293 | code: fourcc, |
| 294 | modifier, |
| 295 | }) |
| 296 | }) |
| 297 | .map(|&m| u64::from(m)) |
| 298 | .collect(); |
| 299 | if allowed.is_empty() { |
| 300 | return Err(Error::UnsupportedFormat(fourcc)); |
| 301 | } |
| 302 | let d = &shared.device.device; |
| 303 | let mut list = |
| 304 | vk::ImageDrmFormatModifierListCreateInfoEXT::default().drm_format_modifiers(&allowed); |
| 305 | let mut external = vk::ExternalMemoryImageCreateInfo::default() |
| 306 | .handle_types(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT); |
| 307 | let info = image_info(fourcc, size, RENDER_USAGE)? |
| 308 | .push_next(&mut external) |
| 309 | .push_next(&mut list); |
| 310 | let image = unsafe { d.create_image(&info, None)? }; |
| 311 | let result = (|| -> Result<Dmabuf, Error> { |
| 312 | let requirements = unsafe { d.get_image_memory_requirements(image) }; |
| 313 | let type_index = memory_type( |
| 314 | shared, |
| 315 | requirements.memory_type_bits, |
| 316 | vk::MemoryPropertyFlags::DEVICE_LOCAL, |
| 317 | ) |
| 318 | .ok_or(Error::UnsupportedFormat(fourcc))?; |
| 319 | let mut export = vk::ExportMemoryAllocateInfo::default() |
| 320 | .handle_types(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT); |
| 321 | let mut dedicated = vk::MemoryDedicatedAllocateInfo::default().image(image); |
| 322 | let alloc = vk::MemoryAllocateInfo::default() |
| 323 | .allocation_size(requirements.size) |
| 324 | .memory_type_index(type_index) |
| 325 | .push_next(&mut export) |
| 326 | .push_next(&mut dedicated); |
| 327 | let memory = unsafe { d.allocate_memory(&alloc, None)? }; |
| 328 | let exported = (|| -> Result<Dmabuf, Error> { |
| 329 | unsafe { d.bind_image_memory(image, memory, 0)? }; |
| 330 | let raw = unsafe { |
| 331 | ext.memory_fd.get_memory_fd( |
| 332 | &vk::MemoryGetFdInfoKHR::default() |
| 333 | .memory(memory) |
| 334 | .handle_type(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT), |
| 335 | )? |
| 336 | }; |
| 337 | // SAFETY: vkGetMemoryFdKHR hands the caller a new fd to own. |
| 338 | let fd = unsafe { OwnedFd::from_raw_fd(raw) }; |
| 339 | let mut props = vk::ImageDrmFormatModifierPropertiesEXT::default(); |
| 340 | unsafe { |
| 341 | ext.modifier |
| 342 | .get_image_drm_format_modifier_properties(image, &mut props)? |
| 343 | }; |
| 344 | let layout = unsafe { |
| 345 | d.get_image_subresource_layout( |
| 346 | image, |
| 347 | vk::ImageSubresource::default() |
| 348 | .aspect_mask(vk::ImageAspectFlags::MEMORY_PLANE_0_EXT), |
| 349 | ) |
| 350 | }; |
| 351 | let mut builder = Dmabuf::builder( |
| 352 | (size.0 as i32, size.1 as i32), |
| 353 | fourcc, |
| 354 | Modifier::from(props.drm_format_modifier), |
| 355 | DmabufFlags::empty(), |
| 356 | ); |
| 357 | builder.add_plane(fd, layout.offset as u32, layout.row_pitch as u32); |
| 358 | builder.build().ok_or(Error::UnsupportedFormat(fourcc)) |
| 359 | })(); |
| 360 | unsafe { d.free_memory(memory, None) }; |
| 361 | exported |
| 362 | })(); |
| 363 | unsafe { d.destroy_image(image, None) }; |
| 364 | result |
| 365 | } |
| 366 | |
| 367 | /// Allocates render-capable dmabufs, for swapchains. |
| 368 | #[derive(Clone, Debug)] |
| 369 | pub struct VulkanAllocator { |
| 370 | pub(crate) shared: Arc<Shared>, |
| 371 | pub(crate) caps: Arc<DmabufCaps>, |
| 372 | } |
| 373 | |
| 374 | impl Allocator for VulkanAllocator { |
| 375 | type Buffer = Dmabuf; |
| 376 | type Error = Error; |
| 377 | |
| 378 | fn create_buffer( |
| 379 | &mut self, |
| 380 | width: u32, |
| 381 | height: u32, |
| 382 | fourcc: Fourcc, |
| 383 | modifiers: &[Modifier], |
| 384 | ) -> Result<Dmabuf, Error> { |
| 385 | export(&self.shared, &self.caps, fourcc, (width, height), modifiers) |
| 386 | } |
| 387 | } |
| 388 | |
| 389 | impl std::fmt::Debug for Shared { |
| 390 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| 391 | f.debug_struct("Shared") |
| 392 | .field("device", &self.device.name) |
| 393 | .finish() |
| 394 | } |
| 395 | } |