1 // Copyright (c) 2016 The vulkano developers 2 // Licensed under the Apache License, Version 2.0 3 // <LICENSE-APACHE or 4 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT 5 // license <LICENSE-MIT or https://opensource.org/licenses/MIT>, 6 // at your option. All files in the project carrying such 7 // notice may not be copied, modified, or distributed except 8 // according to those terms. 9 10 use super::{ 11 sys::{Image, ImageMemory, RawImage}, 12 traits::ImageContent, 13 ImageAccess, ImageAspects, ImageCreateFlags, ImageDescriptorLayouts, ImageDimensions, 14 ImageError, ImageInner, ImageLayout, ImageUsage, 15 }; 16 use crate::{ 17 device::{Device, DeviceOwned, Queue}, 18 format::Format, 19 image::{sys::ImageCreateInfo, view::ImageView, ImageFormatInfo}, 20 memory::{ 21 allocator::{ 22 AllocationCreateInfo, AllocationType, MemoryAllocatePreference, MemoryAllocator, 23 MemoryUsage, 24 }, 25 is_aligned, DedicatedAllocation, DeviceMemoryError, ExternalMemoryHandleType, 26 ExternalMemoryHandleTypes, 27 }, 28 sync::Sharing, 29 DeviceSize, 30 }; 31 use smallvec::SmallVec; 32 33 #[cfg(target_os = "linux")] 34 use crate::{ 35 image::ImageTiling, 36 memory::{allocator::MemoryAlloc, DeviceMemory, MemoryAllocateFlags, MemoryAllocateInfo}, 37 }; 38 #[cfg(target_os = "linux")] 39 use ash::vk::{ImageDrmFormatModifierExplicitCreateInfoEXT, SubresourceLayout}; 40 #[cfg(target_os = "linux")] 41 use std::os::unix::prelude::{FromRawFd, IntoRawFd, RawFd}; 42 43 use std::{ 44 fs::File, 45 hash::{Hash, Hasher}, 46 sync::{ 47 atomic::{AtomicBool, Ordering}, 48 Arc, 49 }, 50 }; 51 52 /// General-purpose image in device memory. Can be used for any usage, but will be slower than a 53 /// specialized image. 54 #[derive(Debug)] 55 pub struct StorageImage { 56 inner: Arc<Image>, 57 58 // If true, then the image is in the layout `General`. If false, then it 59 // is still `Undefined`. 60 layout_initialized: AtomicBool, 61 } 62 63 impl StorageImage { 64 /// Creates a new image with the given dimensions and format. new( allocator: &(impl MemoryAllocator + ?Sized), dimensions: ImageDimensions, format: Format, queue_family_indices: impl IntoIterator<Item = u32>, ) -> Result<Arc<StorageImage>, ImageError>65 pub fn new( 66 allocator: &(impl MemoryAllocator + ?Sized), 67 dimensions: ImageDimensions, 68 format: Format, 69 queue_family_indices: impl IntoIterator<Item = u32>, 70 ) -> Result<Arc<StorageImage>, ImageError> { 71 let aspects = format.aspects(); 72 let is_depth_stencil = aspects.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL); 73 74 if format.compression().is_some() { 75 panic!() // TODO: message? 76 } 77 78 let usage = ImageUsage::TRANSFER_SRC 79 | ImageUsage::TRANSFER_DST 80 | ImageUsage::SAMPLED 81 | ImageUsage::STORAGE 82 | ImageUsage::INPUT_ATTACHMENT 83 | if is_depth_stencil { 84 ImageUsage::DEPTH_STENCIL_ATTACHMENT 85 } else { 86 ImageUsage::COLOR_ATTACHMENT 87 }; 88 let flags = ImageCreateFlags::empty(); 89 90 StorageImage::with_usage( 91 allocator, 92 dimensions, 93 format, 94 usage, 95 flags, 96 queue_family_indices, 97 ) 98 } 99 100 /// Same as `new`, but allows specifying the usage. with_usage( allocator: &(impl MemoryAllocator + ?Sized), dimensions: ImageDimensions, format: Format, usage: ImageUsage, flags: ImageCreateFlags, queue_family_indices: impl IntoIterator<Item = u32>, ) -> Result<Arc<StorageImage>, ImageError>101 pub fn with_usage( 102 allocator: &(impl MemoryAllocator + ?Sized), 103 dimensions: ImageDimensions, 104 format: Format, 105 usage: ImageUsage, 106 flags: ImageCreateFlags, 107 queue_family_indices: impl IntoIterator<Item = u32>, 108 ) -> Result<Arc<StorageImage>, ImageError> { 109 let queue_family_indices: SmallVec<[_; 4]> = queue_family_indices.into_iter().collect(); 110 assert!(!flags.intersects(ImageCreateFlags::DISJOINT)); // TODO: adjust the code below to make this safe 111 112 let raw_image = RawImage::new( 113 allocator.device().clone(), 114 ImageCreateInfo { 115 flags, 116 dimensions, 117 format: Some(format), 118 usage, 119 sharing: if queue_family_indices.len() >= 2 { 120 Sharing::Concurrent(queue_family_indices) 121 } else { 122 Sharing::Exclusive 123 }, 124 ..Default::default() 125 }, 126 )?; 127 let requirements = raw_image.memory_requirements()[0]; 128 let res = unsafe { 129 allocator.allocate_unchecked( 130 requirements, 131 AllocationType::NonLinear, 132 AllocationCreateInfo { 133 usage: MemoryUsage::DeviceOnly, 134 allocate_preference: MemoryAllocatePreference::Unknown, 135 _ne: crate::NonExhaustive(()), 136 }, 137 Some(DedicatedAllocation::Image(&raw_image)), 138 ) 139 }; 140 141 match res { 142 Ok(alloc) => { 143 debug_assert!(is_aligned(alloc.offset(), requirements.layout.alignment())); 144 debug_assert!(alloc.size() == requirements.layout.size()); 145 146 let inner = Arc::new( 147 unsafe { raw_image.bind_memory_unchecked([alloc]) } 148 .map_err(|(err, _, _)| err)?, 149 ); 150 151 Ok(Arc::new(StorageImage { 152 inner, 153 layout_initialized: AtomicBool::new(false), 154 })) 155 } 156 Err(err) => Err(err.into()), 157 } 158 } 159 new_with_exportable_fd( allocator: &(impl MemoryAllocator + ?Sized), dimensions: ImageDimensions, format: Format, usage: ImageUsage, flags: ImageCreateFlags, queue_family_indices: impl IntoIterator<Item = u32>, ) -> Result<Arc<StorageImage>, ImageError>160 pub fn new_with_exportable_fd( 161 allocator: &(impl MemoryAllocator + ?Sized), 162 dimensions: ImageDimensions, 163 format: Format, 164 usage: ImageUsage, 165 flags: ImageCreateFlags, 166 queue_family_indices: impl IntoIterator<Item = u32>, 167 ) -> Result<Arc<StorageImage>, ImageError> { 168 let queue_family_indices: SmallVec<[_; 4]> = queue_family_indices.into_iter().collect(); 169 assert!(!flags.intersects(ImageCreateFlags::DISJOINT)); // TODO: adjust the code below to make this safe 170 171 let external_memory_properties = allocator 172 .device() 173 .physical_device() 174 .image_format_properties(ImageFormatInfo { 175 flags, 176 format: Some(format), 177 image_type: dimensions.image_type(), 178 usage, 179 external_memory_handle_type: Some(ExternalMemoryHandleType::OpaqueFd), 180 ..Default::default() 181 }) 182 .unwrap() 183 .unwrap() 184 .external_memory_properties; 185 // VUID-VkExportMemoryAllocateInfo-handleTypes-00656 186 assert!(external_memory_properties.exportable); 187 188 // VUID-VkMemoryAllocateInfo-pNext-00639 189 // Guaranteed because we always create a dedicated allocation 190 191 let external_memory_handle_types = ExternalMemoryHandleTypes::OPAQUE_FD; 192 let raw_image = RawImage::new( 193 allocator.device().clone(), 194 ImageCreateInfo { 195 flags, 196 dimensions, 197 format: Some(format), 198 usage, 199 sharing: if queue_family_indices.len() >= 2 { 200 Sharing::Concurrent(queue_family_indices) 201 } else { 202 Sharing::Exclusive 203 }, 204 external_memory_handle_types, 205 ..Default::default() 206 }, 207 )?; 208 let requirements = raw_image.memory_requirements()[0]; 209 let memory_type_index = allocator 210 .find_memory_type_index( 211 requirements.memory_type_bits, 212 MemoryUsage::DeviceOnly.into(), 213 ) 214 .expect("failed to find a suitable memory type"); 215 216 match unsafe { 217 allocator.allocate_dedicated_unchecked( 218 memory_type_index, 219 requirements.layout.size(), 220 Some(DedicatedAllocation::Image(&raw_image)), 221 external_memory_handle_types, 222 ) 223 } { 224 Ok(alloc) => { 225 debug_assert!(is_aligned(alloc.offset(), requirements.layout.alignment())); 226 debug_assert!(alloc.size() == requirements.layout.size()); 227 228 let inner = Arc::new(unsafe { 229 raw_image 230 .bind_memory_unchecked([alloc]) 231 .map_err(|(err, _, _)| err)? 232 }); 233 234 Ok(Arc::new(StorageImage { 235 inner, 236 layout_initialized: AtomicBool::new(false), 237 })) 238 } 239 Err(err) => Err(err.into()), 240 } 241 } 242 243 #[cfg(target_os = "linux")] 244 /// Creates a new image from a set of Linux dma_buf file descriptors. The memory will be imported from the file desciptors, and will be bound to the image. 245 /// # Arguments 246 /// * `fds` - The list of file descriptors to import from. Single planar images should only use one, and multiplanar images can use multiple, for example, for each color. 247 /// * `offset` - The byte offset from the start of the image of the plane where the image subresource begins. 248 /// * `pitch` - Describes the number of bytes between each row of texels in an image. new_from_dma_buf_fd( allocator: &(impl MemoryAllocator + ?Sized), device: Arc<Device>, dimensions: ImageDimensions, format: Format, usage: ImageUsage, flags: ImageCreateFlags, queue_family_indices: impl IntoIterator<Item = u32>, mut subresource_data: Vec<SubresourceData>, drm_format_modifier: u64, ) -> Result<Arc<StorageImage>, ImageError>249 pub fn new_from_dma_buf_fd( 250 allocator: &(impl MemoryAllocator + ?Sized), 251 device: Arc<Device>, 252 dimensions: ImageDimensions, 253 format: Format, 254 usage: ImageUsage, 255 flags: ImageCreateFlags, 256 queue_family_indices: impl IntoIterator<Item = u32>, 257 mut subresource_data: Vec<SubresourceData>, 258 drm_format_modifier: u64, 259 ) -> Result<Arc<StorageImage>, ImageError> { 260 let queue_family_indices: SmallVec<[_; 4]> = queue_family_indices.into_iter().collect(); 261 262 // TODO: Support multiplanar image importing from Linux FD 263 if subresource_data.len() > 1 { 264 todo!(); 265 } 266 267 // Create a vector of the layout of each image plane. 268 269 // All of the following are automatically true, since the values are explicitly set as such: 270 // VUID-VkImageDrmFormatModifierExplicitCreateInfoEXT-size-02267 271 // VUID-VkImageDrmFormatModifierExplicitCreateInfoEXT-arrayPitch-02268 272 // VUID-VkImageDrmFormatModifierExplicitCreateInfoEXT-depthPitch-02269 273 let layout: Vec<SubresourceLayout> = subresource_data 274 .iter_mut() 275 .map( 276 |SubresourceData { 277 fd: _, 278 offset, 279 row_pitch, 280 }| { 281 SubresourceLayout { 282 offset: *offset, 283 size: 0, 284 row_pitch: *row_pitch, 285 array_pitch: 0_u64, 286 depth_pitch: 0_u64, 287 } 288 }, 289 ) 290 .collect(); 291 292 let fds: Vec<RawFd> = subresource_data 293 .iter_mut() 294 .map( 295 |SubresourceData { 296 fd, 297 offset: _, 298 row_pitch: _, 299 }| { *fd }, 300 ) 301 .collect(); 302 303 let drm_mod = ImageDrmFormatModifierExplicitCreateInfoEXT::builder() 304 .drm_format_modifier(drm_format_modifier) 305 .plane_layouts(layout.as_ref()) 306 .build(); 307 308 let external_memory_handle_types = ExternalMemoryHandleTypes::DMA_BUF; 309 310 let image = RawImage::new( 311 device.clone(), 312 ImageCreateInfo { 313 flags, 314 dimensions, 315 format: Some(format), 316 usage, 317 sharing: if queue_family_indices.len() >= 2 { 318 Sharing::Concurrent(queue_family_indices) 319 } else { 320 Sharing::Exclusive 321 }, 322 external_memory_handle_types, 323 tiling: ImageTiling::DrmFormatModifier, 324 image_drm_format_modifier_create_info: Some(drm_mod), 325 ..Default::default() 326 }, 327 )?; 328 329 let requirements = image.memory_requirements()[0]; 330 let memory_type_index = allocator 331 .find_memory_type_index( 332 requirements.memory_type_bits, 333 MemoryUsage::DeviceOnly.into(), 334 ) 335 .expect("failed to find a suitable memory type"); 336 337 assert!(device.enabled_extensions().khr_external_memory_fd); 338 assert!(device.enabled_extensions().khr_external_memory); 339 assert!(device.enabled_extensions().ext_external_memory_dma_buf); 340 341 let memory = unsafe { 342 // TODO: For completeness, importing memory from muliple file descriptors should be added (In order to support importing multiplanar images). As of now, only single planar image importing will work. 343 if fds.len() != 1 { 344 todo!(); 345 } 346 347 // Try cloning underlying fd 348 let file = File::from_raw_fd(*fds.first().expect("file descriptor Vec is empty")); 349 let new_file = file.try_clone().expect("error cloning file descriptor"); 350 351 // Turn the original file descriptor back into a raw fd to avoid ownership problems 352 file.into_raw_fd(); 353 DeviceMemory::import( 354 device, 355 MemoryAllocateInfo { 356 allocation_size: requirements.layout.size(), 357 memory_type_index, 358 dedicated_allocation: Some(DedicatedAllocation::Image(&image)), 359 export_handle_types: ExternalMemoryHandleTypes::empty(), 360 flags: MemoryAllocateFlags::empty(), 361 ..Default::default() 362 }, 363 crate::memory::MemoryImportInfo::Fd { 364 handle_type: crate::memory::ExternalMemoryHandleType::DmaBuf, 365 file: new_file, 366 }, 367 ) 368 .unwrap() // TODO: Handle 369 }; 370 371 let mem_alloc = MemoryAlloc::new(memory).unwrap(); 372 373 debug_assert!(mem_alloc.offset() % requirements.layout.alignment().as_nonzero() == 0); 374 debug_assert!(mem_alloc.size() == requirements.layout.size()); 375 376 let inner = Arc::new(unsafe { 377 image 378 .bind_memory_unchecked([mem_alloc]) 379 .map_err(|(err, _, _)| err)? 380 }); 381 Ok(Arc::new(StorageImage { 382 inner, 383 layout_initialized: AtomicBool::new(false), 384 })) 385 } 386 /// Allows the creation of a simple 2D general purpose image view from `StorageImage`. 387 #[inline] general_purpose_image_view( allocator: &(impl MemoryAllocator + ?Sized), queue: Arc<Queue>, size: [u32; 2], format: Format, usage: ImageUsage, ) -> Result<Arc<ImageView<StorageImage>>, ImageError>388 pub fn general_purpose_image_view( 389 allocator: &(impl MemoryAllocator + ?Sized), 390 queue: Arc<Queue>, 391 size: [u32; 2], 392 format: Format, 393 usage: ImageUsage, 394 ) -> Result<Arc<ImageView<StorageImage>>, ImageError> { 395 let dims = ImageDimensions::Dim2d { 396 width: size[0], 397 height: size[1], 398 array_layers: 1, 399 }; 400 let flags = ImageCreateFlags::empty(); 401 let image_result = StorageImage::with_usage( 402 allocator, 403 dims, 404 format, 405 usage, 406 flags, 407 Some(queue.queue_family_index()), 408 ); 409 410 match image_result { 411 Ok(image) => { 412 let image_view = ImageView::new_default(image); 413 match image_view { 414 Ok(view) => Ok(view), 415 Err(e) => Err(ImageError::DirectImageViewCreationFailed(e)), 416 } 417 } 418 Err(e) => Err(e), 419 } 420 } 421 422 /// Exports posix file descriptor for the allocated memory. 423 /// Requires `khr_external_memory_fd` and `khr_external_memory` extensions to be loaded. 424 #[inline] export_posix_fd(&self) -> Result<File, DeviceMemoryError>425 pub fn export_posix_fd(&self) -> Result<File, DeviceMemoryError> { 426 let allocation = match self.inner.memory() { 427 ImageMemory::Normal(a) => &a[0], 428 _ => unreachable!(), 429 }; 430 431 allocation 432 .device_memory() 433 .export_fd(ExternalMemoryHandleType::OpaqueFd) 434 } 435 436 /// Return the size of the allocated memory (used e.g. with cuda). 437 #[inline] mem_size(&self) -> DeviceSize438 pub fn mem_size(&self) -> DeviceSize { 439 let allocation = match self.inner.memory() { 440 ImageMemory::Normal(a) => &a[0], 441 _ => unreachable!(), 442 }; 443 444 allocation.device_memory().allocation_size() 445 } 446 } 447 448 #[cfg(target_os = "linux")] 449 /// Struct that contains a Linux file descriptor for importing, when creating an image. Since a file descriptor is used for each 450 /// plane in the case of multiplanar images, each fd needs to have an offset and a row pitch in order to interpret the imported data. 451 pub struct SubresourceData { 452 /// The file descriptor handle of a layer of an image. 453 pub fd: RawFd, 454 455 /// The byte offset from the start of the plane where the image subresource begins. 456 pub offset: u64, 457 458 /// Describes the number of bytes between each row of texels in an image plane. 459 pub row_pitch: u64, 460 } 461 462 unsafe impl DeviceOwned for StorageImage { 463 #[inline] device(&self) -> &Arc<Device>464 fn device(&self) -> &Arc<Device> { 465 self.inner.device() 466 } 467 } 468 469 unsafe impl ImageAccess for StorageImage { 470 #[inline] inner(&self) -> ImageInner<'_>471 fn inner(&self) -> ImageInner<'_> { 472 ImageInner { 473 image: &self.inner, 474 first_layer: 0, 475 num_layers: self.inner.dimensions().array_layers(), 476 first_mipmap_level: 0, 477 num_mipmap_levels: 1, 478 } 479 } 480 481 #[inline] initial_layout_requirement(&self) -> ImageLayout482 fn initial_layout_requirement(&self) -> ImageLayout { 483 ImageLayout::General 484 } 485 486 #[inline] final_layout_requirement(&self) -> ImageLayout487 fn final_layout_requirement(&self) -> ImageLayout { 488 ImageLayout::General 489 } 490 491 #[inline] layout_initialized(&self)492 unsafe fn layout_initialized(&self) { 493 self.layout_initialized.store(true, Ordering::Relaxed); 494 } 495 496 #[inline] is_layout_initialized(&self) -> bool497 fn is_layout_initialized(&self) -> bool { 498 self.layout_initialized.load(Ordering::Relaxed) 499 } 500 501 #[inline] descriptor_layouts(&self) -> Option<ImageDescriptorLayouts>502 fn descriptor_layouts(&self) -> Option<ImageDescriptorLayouts> { 503 Some(ImageDescriptorLayouts { 504 storage_image: ImageLayout::General, 505 combined_image_sampler: ImageLayout::General, 506 sampled_image: ImageLayout::General, 507 input_attachment: ImageLayout::General, 508 }) 509 } 510 } 511 512 unsafe impl<P> ImageContent<P> for StorageImage { matches_format(&self) -> bool513 fn matches_format(&self) -> bool { 514 true // FIXME: 515 } 516 } 517 518 impl PartialEq for StorageImage { 519 #[inline] eq(&self, other: &Self) -> bool520 fn eq(&self, other: &Self) -> bool { 521 self.inner() == other.inner() 522 } 523 } 524 525 impl Eq for StorageImage {} 526 527 impl Hash for StorageImage { hash<H: Hasher>(&self, state: &mut H)528 fn hash<H: Hasher>(&self, state: &mut H) { 529 self.inner().hash(state); 530 } 531 } 532 533 #[cfg(test)] 534 mod tests { 535 use super::*; 536 use crate::{image::view::ImageViewCreationError, memory::allocator::StandardMemoryAllocator}; 537 538 #[test] create()539 fn create() { 540 let (device, queue) = gfx_dev_and_queue!(); 541 let memory_allocator = StandardMemoryAllocator::new_default(device); 542 let _img = StorageImage::new( 543 &memory_allocator, 544 ImageDimensions::Dim2d { 545 width: 32, 546 height: 32, 547 array_layers: 1, 548 }, 549 Format::R8G8B8A8_UNORM, 550 Some(queue.queue_family_index()), 551 ) 552 .unwrap(); 553 } 554 555 #[test] create_general_purpose_image_view()556 fn create_general_purpose_image_view() { 557 let (device, queue) = gfx_dev_and_queue!(); 558 let memory_allocator = StandardMemoryAllocator::new_default(device); 559 let usage = 560 ImageUsage::TRANSFER_SRC | ImageUsage::TRANSFER_DST | ImageUsage::COLOR_ATTACHMENT; 561 let img_view = StorageImage::general_purpose_image_view( 562 &memory_allocator, 563 queue, 564 [32, 32], 565 Format::R8G8B8A8_UNORM, 566 usage, 567 ) 568 .unwrap(); 569 assert_eq!(img_view.image().usage(), usage); 570 } 571 572 #[test] create_general_purpose_image_view_failed()573 fn create_general_purpose_image_view_failed() { 574 let (device, queue) = gfx_dev_and_queue!(); 575 let memory_allocator = StandardMemoryAllocator::new_default(device); 576 // Not valid for image view... 577 let usage = ImageUsage::TRANSFER_SRC; 578 let img_result = StorageImage::general_purpose_image_view( 579 &memory_allocator, 580 queue, 581 [32, 32], 582 Format::R8G8B8A8_UNORM, 583 usage, 584 ); 585 assert_eq!( 586 img_result, 587 Err(ImageError::DirectImageViewCreationFailed( 588 ImageViewCreationError::ImageMissingUsage 589 )) 590 ); 591 } 592 } 593