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