• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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::{DedicatedAllocation, DedicatedTo, DeviceAlignment};
11 use crate::{
12     device::{Device, DeviceOwned},
13     macros::{impl_id_counter, vulkan_bitflags, vulkan_bitflags_enum},
14     memory::{is_aligned, MemoryPropertyFlags},
15     DeviceSize, OomError, RequirementNotMet, RequiresOneOf, Version, VulkanError, VulkanObject,
16 };
17 use std::{
18     error::Error,
19     ffi::c_void,
20     fmt::{Display, Error as FmtError, Formatter},
21     fs::File,
22     mem::MaybeUninit,
23     num::NonZeroU64,
24     ops::Range,
25     ptr, slice,
26     sync::{atomic::Ordering, Arc},
27 };
28 
29 /// Represents memory that has been allocated from the device.
30 ///
31 /// The destructor of `DeviceMemory` automatically frees the memory.
32 ///
33 /// # Examples
34 ///
35 /// ```
36 /// use vulkano::memory::{DeviceMemory, MemoryAllocateInfo};
37 ///
38 /// # let device: std::sync::Arc<vulkano::device::Device> = return;
39 /// let memory_type_index = 0;
40 ///
41 /// // Allocates 1KB of memory.
42 /// let memory = DeviceMemory::allocate(
43 ///     device.clone(),
44 ///     MemoryAllocateInfo {
45 ///         allocation_size: 1024,
46 ///         memory_type_index,
47 ///         ..Default::default()
48 ///     },
49 /// )
50 /// .unwrap();
51 /// ```
52 #[derive(Debug)]
53 pub struct DeviceMemory {
54     handle: ash::vk::DeviceMemory,
55     device: Arc<Device>,
56     id: NonZeroU64,
57 
58     allocation_size: DeviceSize,
59     memory_type_index: u32,
60     dedicated_to: Option<DedicatedTo>,
61     export_handle_types: ExternalMemoryHandleTypes,
62     imported_handle_type: Option<ExternalMemoryHandleType>,
63     flags: MemoryAllocateFlags,
64 }
65 
66 impl DeviceMemory {
67     /// Allocates a block of memory from the device.
68     ///
69     /// Some platforms may have a limit on the maximum size of a single allocation. For example,
70     /// certain systems may fail to create allocations with a size greater than or equal to 4GB.
71     ///
72     /// # Panics
73     ///
74     /// - Panics if `allocate_info.allocation_size` is 0.
75     /// - Panics if `allocate_info.dedicated_allocation` is `Some` and the contained buffer or
76     ///   image does not belong to `device`.
77     #[inline]
allocate( device: Arc<Device>, mut allocate_info: MemoryAllocateInfo<'_>, ) -> Result<Self, DeviceMemoryError>78     pub fn allocate(
79         device: Arc<Device>,
80         mut allocate_info: MemoryAllocateInfo<'_>,
81     ) -> Result<Self, DeviceMemoryError> {
82         Self::validate(&device, &mut allocate_info, None)?;
83 
84         unsafe { Self::allocate_unchecked(device, allocate_info, None) }.map_err(Into::into)
85     }
86 
87     /// Creates a new `DeviceMemory` from a raw object handle.
88     ///
89     /// # Safety
90     ///
91     /// - `handle` must be a valid Vulkan object handle created from `device`.
92     /// - `allocate_info` must match the info used to create the object.
93     #[inline]
from_handle( device: Arc<Device>, handle: ash::vk::DeviceMemory, allocate_info: MemoryAllocateInfo<'_>, ) -> Self94     pub unsafe fn from_handle(
95         device: Arc<Device>,
96         handle: ash::vk::DeviceMemory,
97         allocate_info: MemoryAllocateInfo<'_>,
98     ) -> Self {
99         let MemoryAllocateInfo {
100             allocation_size,
101             memory_type_index,
102             dedicated_allocation,
103             export_handle_types,
104             flags,
105             _ne: _,
106         } = allocate_info;
107 
108         DeviceMemory {
109             handle,
110             device,
111             id: Self::next_id(),
112             allocation_size,
113             memory_type_index,
114             dedicated_to: dedicated_allocation.map(Into::into),
115             export_handle_types,
116             imported_handle_type: None,
117             flags,
118         }
119     }
120 
121     /// Imports a block of memory from an external source.
122     ///
123     /// # Safety
124     ///
125     /// - See the documentation of the variants of [`MemoryImportInfo`].
126     ///
127     /// # Panics
128     ///
129     /// - Panics if `allocate_info.allocation_size` is 0.
130     /// - Panics if `allocate_info.dedicated_allocation` is `Some` and the contained buffer or
131     ///   image does not belong to `device`.
132     #[inline]
import( device: Arc<Device>, mut allocate_info: MemoryAllocateInfo<'_>, import_info: MemoryImportInfo, ) -> Result<Self, DeviceMemoryError>133     pub unsafe fn import(
134         device: Arc<Device>,
135         mut allocate_info: MemoryAllocateInfo<'_>,
136         import_info: MemoryImportInfo,
137     ) -> Result<Self, DeviceMemoryError> {
138         Self::validate(&device, &mut allocate_info, Some(&import_info))?;
139 
140         Self::allocate_unchecked(device, allocate_info, Some(import_info)).map_err(Into::into)
141     }
142 
143     #[inline(never)]
validate( device: &Device, allocate_info: &mut MemoryAllocateInfo<'_>, import_info: Option<&MemoryImportInfo>, ) -> Result<(), DeviceMemoryError>144     fn validate(
145         device: &Device,
146         allocate_info: &mut MemoryAllocateInfo<'_>,
147         import_info: Option<&MemoryImportInfo>,
148     ) -> Result<(), DeviceMemoryError> {
149         let &mut MemoryAllocateInfo {
150             allocation_size,
151             memory_type_index,
152             ref mut dedicated_allocation,
153             export_handle_types,
154             flags,
155             _ne: _,
156         } = allocate_info;
157 
158         if !(device.api_version() >= Version::V1_1
159             || device.enabled_extensions().khr_dedicated_allocation)
160         {
161             // Fall back instead of erroring out
162             *dedicated_allocation = None;
163         }
164 
165         let memory_properties = device.physical_device().memory_properties();
166 
167         // VUID-vkAllocateMemory-pAllocateInfo-01714
168         let memory_type = memory_properties
169             .memory_types
170             .get(memory_type_index as usize)
171             .ok_or(DeviceMemoryError::MemoryTypeIndexOutOfRange {
172                 memory_type_index,
173                 memory_type_count: memory_properties.memory_types.len() as u32,
174             })?;
175 
176         // VUID-VkMemoryAllocateInfo-memoryTypeIndex-01872
177         if memory_type
178             .property_flags
179             .intersects(MemoryPropertyFlags::PROTECTED)
180             && !device.enabled_features().protected_memory
181         {
182             return Err(DeviceMemoryError::RequirementNotMet {
183                 required_for: "`allocate_info.memory_type_index` refers to a memory type where \
184                     `property_flags` contains `MemoryPropertyFlags::PROTECTED`",
185                 requires_one_of: RequiresOneOf {
186                     features: &["protected_memory"],
187                     ..Default::default()
188                 },
189             });
190         }
191 
192         // VUID-VkMemoryAllocateInfo-pNext-01874
193         assert!(allocation_size != 0);
194 
195         // VUID-vkAllocateMemory-pAllocateInfo-01713
196         let heap_size = memory_properties.memory_heaps[memory_type.heap_index as usize].size;
197         if heap_size != 0 && allocation_size > heap_size {
198             return Err(DeviceMemoryError::MemoryTypeHeapSizeExceeded {
199                 allocation_size,
200                 heap_size,
201             });
202         }
203 
204         // VUID-vkAllocateMemory-deviceCoherentMemory-02790
205         if memory_type
206             .property_flags
207             .intersects(MemoryPropertyFlags::DEVICE_COHERENT)
208             && !device.enabled_features().device_coherent_memory
209         {
210             return Err(DeviceMemoryError::RequirementNotMet {
211                 required_for: "`allocate_info.memory_type_index` refers to a memory type where \
212                     `property_flags` contains `MemoryPropertyFlags::DEVICE_COHERENT`",
213                 requires_one_of: RequiresOneOf {
214                     features: &["device_coherent_memory"],
215                     ..Default::default()
216                 },
217             });
218         }
219 
220         if let Some(dedicated_allocation) = dedicated_allocation {
221             match dedicated_allocation {
222                 DedicatedAllocation::Buffer(buffer) => {
223                     // VUID-VkMemoryDedicatedAllocateInfo-commonparent
224                     assert_eq!(device, buffer.device().as_ref());
225 
226                     let required_size = buffer.memory_requirements().layout.size();
227 
228                     // VUID-VkMemoryDedicatedAllocateInfo-buffer-02965
229                     if allocation_size != required_size {
230                         return Err(DeviceMemoryError::DedicatedAllocationSizeMismatch {
231                             allocation_size,
232                             required_size,
233                         });
234                     }
235                 }
236                 DedicatedAllocation::Image(image) => {
237                     // VUID-VkMemoryDedicatedAllocateInfo-commonparent
238                     assert_eq!(device, image.device().as_ref());
239 
240                     let required_size = image.memory_requirements()[0].layout.size();
241 
242                     // VUID-VkMemoryDedicatedAllocateInfo-image-02964
243                     if allocation_size != required_size {
244                         return Err(DeviceMemoryError::DedicatedAllocationSizeMismatch {
245                             allocation_size,
246                             required_size,
247                         });
248                     }
249                 }
250             }
251         }
252 
253         if !export_handle_types.is_empty() {
254             if !(device.api_version() >= Version::V1_1
255                 || device.enabled_extensions().khr_external_memory)
256             {
257                 return Err(DeviceMemoryError::RequirementNotMet {
258                     required_for: "`allocate_info.export_handle_types` is not empty",
259                     requires_one_of: RequiresOneOf {
260                         api_version: Some(Version::V1_1),
261                         device_extensions: &["khr_external_memory"],
262                         ..Default::default()
263                     },
264                 });
265             }
266 
267             // VUID-VkExportMemoryAllocateInfo-handleTypes-parameter
268             export_handle_types.validate_device(device)?;
269 
270             // VUID-VkMemoryAllocateInfo-pNext-00639
271             // VUID-VkExportMemoryAllocateInfo-handleTypes-00656
272             // TODO: how do you fullfill this when you don't know the image or buffer parameters?
273             // Does exporting memory require specifying these parameters up front, and does it tie
274             // the allocation to only images or buffers of that type?
275         }
276 
277         if let Some(import_info) = import_info {
278             match *import_info {
279                 MemoryImportInfo::Fd {
280                     #[cfg(unix)]
281                     handle_type,
282                     #[cfg(not(unix))]
283                         handle_type: _,
284                     file: _,
285                 } => {
286                     if !device.enabled_extensions().khr_external_memory_fd {
287                         return Err(DeviceMemoryError::RequirementNotMet {
288                             required_for: "`allocate_info.import_info` is \
289                                 `Some(MemoryImportInfo::Fd)`",
290                             requires_one_of: RequiresOneOf {
291                                 device_extensions: &["khr_external_memory_fd"],
292                                 ..Default::default()
293                             },
294                         });
295                     }
296 
297                     #[cfg(not(unix))]
298                     unreachable!(
299                         "`khr_external_memory_fd` was somehow enabled on a non-Unix system"
300                     );
301 
302                     #[cfg(unix)]
303                     {
304                         // VUID-VkImportMemoryFdInfoKHR-handleType-parameter
305                         handle_type.validate_device(device)?;
306 
307                         // VUID-VkImportMemoryFdInfoKHR-handleType-00669
308                         match handle_type {
309                             ExternalMemoryHandleType::OpaqueFd => {
310                                 // VUID-VkMemoryAllocateInfo-allocationSize-01742
311                                 // Can't validate, must be ensured by user
312 
313                                 // VUID-VkMemoryDedicatedAllocateInfo-buffer-01879
314                                 // Can't validate, must be ensured by user
315 
316                                 // VUID-VkMemoryDedicatedAllocateInfo-image-01878
317                                 // Can't validate, must be ensured by user
318                             }
319                             ExternalMemoryHandleType::DmaBuf => {}
320                             _ => {
321                                 return Err(DeviceMemoryError::ImportFdHandleTypeNotSupported {
322                                     handle_type,
323                                 })
324                             }
325                         }
326 
327                         // VUID-VkMemoryAllocateInfo-memoryTypeIndex-00648
328                         // Can't validate, must be ensured by user
329                     }
330                 }
331                 MemoryImportInfo::Win32 {
332                     #[cfg(windows)]
333                     handle_type,
334                     #[cfg(not(windows))]
335                         handle_type: _,
336                     handle: _,
337                 } => {
338                     if !device.enabled_extensions().khr_external_memory_win32 {
339                         return Err(DeviceMemoryError::RequirementNotMet {
340                             required_for: "`allocate_info.import_info` is \
341                                 `Some(MemoryImportInfo::Win32)`",
342                             requires_one_of: RequiresOneOf {
343                                 device_extensions: &["khr_external_memory_win32"],
344                                 ..Default::default()
345                             },
346                         });
347                     }
348 
349                     #[cfg(not(windows))]
350                     unreachable!(
351                         "`khr_external_memory_win32` was somehow enabled on a non-Windows system"
352                     );
353 
354                     #[cfg(windows)]
355                     {
356                         // VUID-VkImportMemoryWin32HandleInfoKHR-handleType-parameter
357                         handle_type.validate_device(device)?;
358 
359                         // VUID-VkImportMemoryWin32HandleInfoKHR-handleType-00660
360                         match handle_type {
361                             ExternalMemoryHandleType::OpaqueWin32
362                             | ExternalMemoryHandleType::OpaqueWin32Kmt => {
363                                 // VUID-VkMemoryAllocateInfo-allocationSize-01742
364                                 // Can't validate, must be ensured by user
365 
366                                 // VUID-VkMemoryDedicatedAllocateInfo-buffer-01879
367                                 // Can't validate, must be ensured by user
368 
369                                 // VUID-VkMemoryDedicatedAllocateInfo-image-01878
370                                 // Can't validate, must be ensured by user
371                             }
372                             _ => {
373                                 return Err(DeviceMemoryError::ImportWin32HandleTypeNotSupported {
374                                     handle_type,
375                                 })
376                             }
377                         }
378 
379                         // VUID-VkMemoryAllocateInfo-memoryTypeIndex-00645
380                         // Can't validate, must be ensured by user
381                     }
382                 }
383             }
384         }
385 
386         if !flags.is_empty()
387             && device.physical_device().api_version() < Version::V1_1
388             && !device.enabled_extensions().khr_device_group
389         {
390             return Err(DeviceMemoryError::RequirementNotMet {
391                 required_for: "`allocate_info.flags` is not empty",
392                 requires_one_of: RequiresOneOf {
393                     api_version: Some(Version::V1_1),
394                     device_extensions: &["khr_device_group"],
395                     ..Default::default()
396                 },
397             });
398         }
399 
400         if flags.intersects(MemoryAllocateFlags::DEVICE_ADDRESS) {
401             // VUID-VkMemoryAllocateInfo-flags-03331
402             if !device.enabled_features().buffer_device_address {
403                 return Err(DeviceMemoryError::RequirementNotMet {
404                     required_for: "`allocate_info.flags` contains \
405                         `MemoryAllocateFlags::DEVICE_ADDRESS`",
406                     requires_one_of: RequiresOneOf {
407                         features: &["buffer_device_address"],
408                         ..Default::default()
409                     },
410                 });
411             }
412 
413             if device.enabled_extensions().ext_buffer_device_address {
414                 return Err(DeviceMemoryError::RequirementNotMet {
415                     required_for: "`allocate_info.flags` contains \
416                         `MemoryAllocateFlags::DEVICE_ADDRESS`",
417                     requires_one_of: RequiresOneOf {
418                         api_version: Some(Version::V1_2),
419                         device_extensions: &["khr_buffer_device_address"],
420                         ..Default::default()
421                     },
422                 });
423             }
424         }
425 
426         Ok(())
427     }
428 
429     #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
430     #[inline(never)]
allocate_unchecked( device: Arc<Device>, allocate_info: MemoryAllocateInfo<'_>, import_info: Option<MemoryImportInfo>, ) -> Result<Self, VulkanError>431     pub unsafe fn allocate_unchecked(
432         device: Arc<Device>,
433         allocate_info: MemoryAllocateInfo<'_>,
434         import_info: Option<MemoryImportInfo>,
435     ) -> Result<Self, VulkanError> {
436         let MemoryAllocateInfo {
437             allocation_size,
438             memory_type_index,
439             dedicated_allocation,
440             export_handle_types,
441             flags,
442             _ne: _,
443         } = allocate_info;
444 
445         let mut allocate_info = ash::vk::MemoryAllocateInfo::builder()
446             .allocation_size(allocation_size)
447             .memory_type_index(memory_type_index);
448 
449         // VUID-VkMemoryDedicatedAllocateInfo-image-01432
450         let mut dedicated_allocate_info =
451             dedicated_allocation.map(|dedicated_allocation| match dedicated_allocation {
452                 DedicatedAllocation::Buffer(buffer) => ash::vk::MemoryDedicatedAllocateInfo {
453                     buffer: buffer.handle(),
454                     ..Default::default()
455                 },
456                 DedicatedAllocation::Image(image) => ash::vk::MemoryDedicatedAllocateInfo {
457                     image: image.handle(),
458                     ..Default::default()
459                 },
460             });
461 
462         if let Some(info) = dedicated_allocate_info.as_mut() {
463             allocate_info = allocate_info.push_next(info);
464         }
465 
466         let mut export_allocate_info = if !export_handle_types.is_empty() {
467             Some(ash::vk::ExportMemoryAllocateInfo {
468                 handle_types: export_handle_types.into(),
469                 ..Default::default()
470             })
471         } else {
472             None
473         };
474 
475         if let Some(info) = export_allocate_info.as_mut() {
476             allocate_info = allocate_info.push_next(info);
477         }
478 
479         let imported_handle_type = import_info.as_ref().map(|import_info| match import_info {
480             MemoryImportInfo::Fd { handle_type, .. } => *handle_type,
481             MemoryImportInfo::Win32 { handle_type, .. } => *handle_type,
482         });
483 
484         #[cfg(unix)]
485         let mut import_fd_info = match import_info {
486             Some(MemoryImportInfo::Fd { handle_type, file }) => {
487                 use std::os::unix::io::IntoRawFd;
488 
489                 Some(ash::vk::ImportMemoryFdInfoKHR {
490                     handle_type: handle_type.into(),
491                     fd: file.into_raw_fd(),
492                     ..Default::default()
493                 })
494             }
495             _ => None,
496         };
497 
498         #[cfg(unix)]
499         if let Some(info) = import_fd_info.as_mut() {
500             allocate_info = allocate_info.push_next(info);
501         }
502 
503         #[cfg(windows)]
504         let mut import_win32_handle_info = match import_info {
505             Some(MemoryImportInfo::Win32 {
506                 handle_type,
507                 handle,
508             }) => Some(ash::vk::ImportMemoryWin32HandleInfoKHR {
509                 handle_type: handle_type.into(),
510                 handle,
511                 ..Default::default()
512             }),
513             _ => None,
514         };
515 
516         #[cfg(windows)]
517         if let Some(info) = import_win32_handle_info.as_mut() {
518             allocate_info = allocate_info.push_next(info);
519         }
520 
521         let mut flags_info = ash::vk::MemoryAllocateFlagsInfo {
522             flags: flags.into(),
523             ..Default::default()
524         };
525 
526         if !flags.is_empty() {
527             allocate_info = allocate_info.push_next(&mut flags_info);
528         }
529 
530         // VUID-vkAllocateMemory-maxMemoryAllocationCount-04101
531         let max_allocations = device
532             .physical_device()
533             .properties()
534             .max_memory_allocation_count;
535         device
536             .allocation_count
537             .fetch_update(Ordering::Acquire, Ordering::Relaxed, move |count| {
538                 (count < max_allocations).then_some(count + 1)
539             })
540             .map_err(|_| VulkanError::TooManyObjects)?;
541 
542         let handle = {
543             let fns = device.fns();
544             let mut output = MaybeUninit::uninit();
545             (fns.v1_0.allocate_memory)(
546                 device.handle(),
547                 &allocate_info.build(),
548                 ptr::null(),
549                 output.as_mut_ptr(),
550             )
551             .result()
552             .map_err(|e| {
553                 device.allocation_count.fetch_sub(1, Ordering::Release);
554                 VulkanError::from(e)
555             })?;
556 
557             output.assume_init()
558         };
559 
560         Ok(DeviceMemory {
561             handle,
562             device,
563             id: Self::next_id(),
564             allocation_size,
565             memory_type_index,
566             dedicated_to: dedicated_allocation.map(Into::into),
567             export_handle_types,
568             imported_handle_type,
569             flags,
570         })
571     }
572 
573     /// Returns the index of the memory type that this memory was allocated from.
574     #[inline]
memory_type_index(&self) -> u32575     pub fn memory_type_index(&self) -> u32 {
576         self.memory_type_index
577     }
578 
579     /// Returns the size in bytes of the memory allocation.
580     #[inline]
allocation_size(&self) -> DeviceSize581     pub fn allocation_size(&self) -> DeviceSize {
582         self.allocation_size
583     }
584 
585     /// Returns `true` if the memory is a [dedicated] to a resource.
586     ///
587     /// [dedicated]: MemoryAllocateInfo#structfield.dedicated_allocation
588     #[inline]
is_dedicated(&self) -> bool589     pub fn is_dedicated(&self) -> bool {
590         self.dedicated_to.is_some()
591     }
592 
dedicated_to(&self) -> Option<DedicatedTo>593     pub(crate) fn dedicated_to(&self) -> Option<DedicatedTo> {
594         self.dedicated_to
595     }
596 
597     /// Returns the handle types that can be exported from the memory allocation.
598     #[inline]
export_handle_types(&self) -> ExternalMemoryHandleTypes599     pub fn export_handle_types(&self) -> ExternalMemoryHandleTypes {
600         self.export_handle_types
601     }
602 
603     /// Returns the handle type that the memory allocation was imported from, if any.
604     #[inline]
imported_handle_type(&self) -> Option<ExternalMemoryHandleType>605     pub fn imported_handle_type(&self) -> Option<ExternalMemoryHandleType> {
606         self.imported_handle_type
607     }
608 
609     /// Returns the flags the memory was allocated with.
610     #[inline]
flags(&self) -> MemoryAllocateFlags611     pub fn flags(&self) -> MemoryAllocateFlags {
612         self.flags
613     }
614 
615     /// Retrieves the amount of lazily-allocated memory that is currently commited to this
616     /// memory object.
617     ///
618     /// The device may change this value at any time, and the returned value may be
619     /// already out-of-date.
620     ///
621     /// `self` must have been allocated from a memory type that has the [`LAZILY_ALLOCATED`] flag
622     /// set.
623     ///
624     /// [`LAZILY_ALLOCATED`]: crate::memory::MemoryPropertyFlags::LAZILY_ALLOCATED
625     #[inline]
commitment(&self) -> Result<DeviceSize, DeviceMemoryError>626     pub fn commitment(&self) -> Result<DeviceSize, DeviceMemoryError> {
627         self.validate_commitment()?;
628 
629         unsafe { Ok(self.commitment_unchecked()) }
630     }
631 
validate_commitment(&self) -> Result<(), DeviceMemoryError>632     fn validate_commitment(&self) -> Result<(), DeviceMemoryError> {
633         let memory_type = &self
634             .device
635             .physical_device()
636             .memory_properties()
637             .memory_types[self.memory_type_index as usize];
638 
639         // VUID-vkGetDeviceMemoryCommitment-memory-00690
640         if !memory_type
641             .property_flags
642             .intersects(MemoryPropertyFlags::LAZILY_ALLOCATED)
643         {
644             return Err(DeviceMemoryError::NotLazilyAllocated);
645         }
646 
647         Ok(())
648     }
649 
650     #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
651     #[inline]
commitment_unchecked(&self) -> DeviceSize652     pub unsafe fn commitment_unchecked(&self) -> DeviceSize {
653         let mut output: DeviceSize = 0;
654 
655         let fns = self.device.fns();
656         (fns.v1_0.get_device_memory_commitment)(self.device.handle(), self.handle, &mut output);
657 
658         output
659     }
660 
661     /// Exports the device memory into a Unix file descriptor. The caller owns the returned `File`.
662     ///
663     /// # Panics
664     ///
665     /// - Panics if the user requests an invalid handle type for this device memory object.
666     #[inline]
export_fd( &self, handle_type: ExternalMemoryHandleType, ) -> Result<std::fs::File, DeviceMemoryError>667     pub fn export_fd(
668         &self,
669         handle_type: ExternalMemoryHandleType,
670     ) -> Result<std::fs::File, DeviceMemoryError> {
671         // VUID-VkMemoryGetFdInfoKHR-handleType-parameter
672         handle_type.validate_device(&self.device)?;
673 
674         // VUID-VkMemoryGetFdInfoKHR-handleType-00672
675         if !matches!(
676             handle_type,
677             ExternalMemoryHandleType::OpaqueFd | ExternalMemoryHandleType::DmaBuf
678         ) {
679             return Err(DeviceMemoryError::HandleTypeNotSupported { handle_type });
680         }
681 
682         // VUID-VkMemoryGetFdInfoKHR-handleType-00671
683         if !ash::vk::ExternalMemoryHandleTypeFlags::from(self.export_handle_types)
684             .intersects(ash::vk::ExternalMemoryHandleTypeFlags::from(handle_type))
685         {
686             return Err(DeviceMemoryError::HandleTypeNotSupported { handle_type });
687         }
688 
689         debug_assert!(self.device().enabled_extensions().khr_external_memory_fd);
690 
691         #[cfg(not(unix))]
692         unreachable!("`khr_external_memory_fd` was somehow enabled on a non-Unix system");
693 
694         #[cfg(unix)]
695         {
696             use std::os::unix::io::FromRawFd;
697 
698             let fd = unsafe {
699                 let fns = self.device.fns();
700                 let info = ash::vk::MemoryGetFdInfoKHR {
701                     memory: self.handle,
702                     handle_type: handle_type.into(),
703                     ..Default::default()
704                 };
705 
706                 let mut output = MaybeUninit::uninit();
707                 (fns.khr_external_memory_fd.get_memory_fd_khr)(
708                     self.device.handle(),
709                     &info,
710                     output.as_mut_ptr(),
711                 )
712                 .result()
713                 .map_err(VulkanError::from)?;
714                 output.assume_init()
715             };
716 
717             let file = unsafe { std::fs::File::from_raw_fd(fd) };
718 
719             Ok(file)
720         }
721     }
722 }
723 
724 impl Drop for DeviceMemory {
725     #[inline]
drop(&mut self)726     fn drop(&mut self) {
727         unsafe {
728             let fns = self.device.fns();
729             (fns.v1_0.free_memory)(self.device.handle(), self.handle, ptr::null());
730             self.device.allocation_count.fetch_sub(1, Ordering::Release);
731         }
732     }
733 }
734 
735 unsafe impl VulkanObject for DeviceMemory {
736     type Handle = ash::vk::DeviceMemory;
737 
738     #[inline]
handle(&self) -> Self::Handle739     fn handle(&self) -> Self::Handle {
740         self.handle
741     }
742 }
743 
744 unsafe impl DeviceOwned for DeviceMemory {
745     #[inline]
device(&self) -> &Arc<Device>746     fn device(&self) -> &Arc<Device> {
747         &self.device
748     }
749 }
750 
751 impl_id_counter!(DeviceMemory);
752 
753 /// Parameters to allocate a new `DeviceMemory`.
754 #[derive(Clone, Debug)]
755 pub struct MemoryAllocateInfo<'d> {
756     /// The number of bytes to allocate.
757     ///
758     /// The default value is `0`, which must be overridden.
759     pub allocation_size: DeviceSize,
760 
761     /// The index of the memory type that should be allocated.
762     ///
763     /// The default value is [`u32::MAX`], which must be overridden.
764     pub memory_type_index: u32,
765 
766     /// Allocates memory for a specific buffer or image.
767     ///
768     /// This value is silently ignored (treated as `None`) if the device API version is less than
769     /// 1.1 and the
770     /// [`khr_dedicated_allocation`](crate::device::DeviceExtensions::khr_dedicated_allocation)
771     /// extension is not enabled on the device.
772     pub dedicated_allocation: Option<DedicatedAllocation<'d>>,
773 
774     /// The handle types that can be exported from the allocated memory.
775     pub export_handle_types: ExternalMemoryHandleTypes,
776 
777     /// Additional flags for the memory allocation.
778     ///
779     /// If not empty, the device API version must be at least 1.1, or the
780     /// [`khr_device_group`](crate::device::DeviceExtensions::khr_device_group) extension must be
781     /// enabled on the device.
782     ///
783     /// The default value is [`MemoryAllocateFlags::empty()`].
784     pub flags: MemoryAllocateFlags,
785 
786     pub _ne: crate::NonExhaustive,
787 }
788 
789 impl Default for MemoryAllocateInfo<'static> {
790     #[inline]
default() -> Self791     fn default() -> Self {
792         Self {
793             allocation_size: 0,
794             memory_type_index: u32::MAX,
795             dedicated_allocation: None,
796             export_handle_types: ExternalMemoryHandleTypes::empty(),
797             flags: MemoryAllocateFlags::empty(),
798             _ne: crate::NonExhaustive(()),
799         }
800     }
801 }
802 
803 impl<'d> MemoryAllocateInfo<'d> {
804     /// Returns a `MemoryAllocateInfo` with the specified `dedicated_allocation`.
805     #[inline]
dedicated_allocation(dedicated_allocation: DedicatedAllocation<'d>) -> Self806     pub fn dedicated_allocation(dedicated_allocation: DedicatedAllocation<'d>) -> Self {
807         Self {
808             allocation_size: 0,
809             memory_type_index: u32::MAX,
810             dedicated_allocation: Some(dedicated_allocation),
811             export_handle_types: ExternalMemoryHandleTypes::empty(),
812             flags: MemoryAllocateFlags::empty(),
813             _ne: crate::NonExhaustive(()),
814         }
815     }
816 }
817 
818 /// Parameters to import memory from an external source.
819 #[derive(Debug)]
820 #[non_exhaustive]
821 pub enum MemoryImportInfo {
822     /// Import memory from a Unix file descriptor.
823     ///
824     /// `handle_type` must be either [`ExternalMemoryHandleType::OpaqueFd`] or
825     /// [`ExternalMemoryHandleType::DmaBuf`].
826     ///
827     /// # Safety
828     ///
829     /// - `file` must be a valid Unix file descriptor.
830     /// - Vulkan will take ownership of `file`, and once the memory is imported, you must not
831     ///   perform any operations on `file` nor on any of its clones/duplicates.
832     /// - If `file` was created by the Vulkan API, and `handle_type` is
833     ///   [`ExternalMemoryHandleType::OpaqueFd`]:
834     ///   - [`MemoryAllocateInfo::allocation_size`] and [`MemoryAllocateInfo::memory_type_index`]
835     ///     must match those of the original memory allocation.
836     ///   - If the original memory allocation used [`MemoryAllocateInfo::dedicated_allocation`],
837     ///     the imported one must also use it, and the associated buffer or image must be defined
838     ///     identically to the original.
839     /// - If `file` was not created by the Vulkan API, then
840     ///   [`MemoryAllocateInfo::memory_type_index`] must be one of the memory types returned by
841     ///   [`Device::memory_fd_properties`].
842     Fd {
843         handle_type: ExternalMemoryHandleType,
844         file: File,
845     },
846 
847     /// Import memory from a Windows handle.
848     ///
849     /// `handle_type` must be either [`ExternalMemoryHandleType::OpaqueWin32`] or
850     /// [`ExternalMemoryHandleType::OpaqueWin32Kmt`].
851     ///
852     /// # Safety
853     ///
854     /// - `handle` must be a valid Windows handle.
855     /// - Vulkan will not take ownership of `handle`.
856     /// - If `handle_type` is [`ExternalMemoryHandleType::OpaqueWin32`], it owns a reference
857     ///   to the underlying resource and must eventually be closed by the caller.
858     /// - If `handle_type` is [`ExternalMemoryHandleType::OpaqueWin32Kmt`], it does not own a
859     ///   reference to the underlying resource.
860     /// - `handle` must be created by the Vulkan API.
861     /// - [`MemoryAllocateInfo::allocation_size`] and [`MemoryAllocateInfo::memory_type_index`]
862     ///   must match those of the original memory allocation.
863     /// - If the original memory allocation used [`MemoryAllocateInfo::dedicated_allocation`],
864     ///   the imported one must also use it, and the associated buffer or image must be defined
865     ///   identically to the original.
866     Win32 {
867         handle_type: ExternalMemoryHandleType,
868         handle: ash::vk::HANDLE,
869     },
870 }
871 
872 vulkan_bitflags_enum! {
873     #[non_exhaustive]
874 
875     /// A set of [`ExternalMemoryHandleType`] values.
876     ExternalMemoryHandleTypes,
877 
878     /// A handle type used to export or import memory to/from an external source.
879     ExternalMemoryHandleType,
880 
881     = ExternalMemoryHandleTypeFlags(u32);
882 
883     /// A POSIX file descriptor handle that is only usable with Vulkan and compatible APIs.
884     OPAQUE_FD, OpaqueFd = OPAQUE_FD,
885 
886     /// A Windows NT handle that is only usable with Vulkan and compatible APIs.
887     OPAQUE_WIN32, OpaqueWin32 = OPAQUE_WIN32,
888 
889     /// A Windows global share handle that is only usable with Vulkan and compatible APIs.
890     OPAQUE_WIN32_KMT, OpaqueWin32Kmt = OPAQUE_WIN32_KMT,
891 
892     /// A Windows NT handle that refers to a Direct3D 10 or 11 texture resource.
893     D3D11_TEXTURE, D3D11Texture = D3D11_TEXTURE,
894 
895     /// A Windows global share handle that refers to a Direct3D 10 or 11 texture resource.
896     D3D11_TEXTURE_KMT, D3D11TextureKmt = D3D11_TEXTURE_KMT,
897 
898     /// A Windows NT handle that refers to a Direct3D 12 heap resource.
899     D3D12_HEAP, D3D12Heap = D3D12_HEAP,
900 
901     /// A Windows NT handle that refers to a Direct3D 12 committed resource.
902     D3D12_RESOURCE, D3D12Resource = D3D12_RESOURCE,
903 
904     /// A POSIX file descriptor handle that refers to a Linux dma-buf.
905     DMA_BUF, DmaBuf = DMA_BUF_EXT {
906         device_extensions: [ext_external_memory_dma_buf],
907     },
908 
909     /// A handle for an Android `AHardwareBuffer` object.
910     ANDROID_HARDWARE_BUFFER, AndroidHardwareBuffer = ANDROID_HARDWARE_BUFFER_ANDROID {
911         device_extensions: [android_external_memory_android_hardware_buffer],
912     },
913 
914     /// A pointer to memory that was allocated by the host.
915     HOST_ALLOCATION, HostAllocation = HOST_ALLOCATION_EXT {
916         device_extensions: [ext_external_memory_host],
917     },
918 
919     /// A pointer to a memory mapping on the host that maps non-host memory.
920     HOST_MAPPED_FOREIGN_MEMORY, HostMappedForeignMemory = HOST_MAPPED_FOREIGN_MEMORY_EXT {
921         device_extensions: [ext_external_memory_host],
922     },
923 
924     /// A Zircon handle to a virtual memory object.
925     ZIRCON_VMO, ZirconVmo = ZIRCON_VMO_FUCHSIA {
926         device_extensions: [fuchsia_external_memory],
927     },
928 
929     /// A Remote Direct Memory Address handle to an allocation that is accessible by remote devices.
930     RDMA_ADDRESS, RdmaAddress = RDMA_ADDRESS_NV {
931         device_extensions: [nv_external_memory_rdma],
932     },
933 }
934 
935 vulkan_bitflags! {
936     #[non_exhaustive]
937 
938     /// A mask specifying flags for device memory allocation.
939     MemoryAllocateFlags = MemoryAllocateFlags(u32);
940 
941     /* TODO: enable
942     DEVICE_MASK = DEVICE_MASK,*/
943 
944     /// Specifies that the allocated device memory can be bound to a buffer created with the
945     /// [`SHADER_DEVICE_ADDRESS`] usage. This requires that the [`buffer_device_address`] feature
946     /// is enabled on the device and the [`ext_buffer_device_address`] extension is not enabled on
947     /// the device.
948     ///
949     /// [`SHADER_DEVICE_ADDRESS`]: crate::buffer::BufferUsage::SHADER_DEVICE_ADDRESS
950     /// [`buffer_device_address`]: crate::device::Features::buffer_device_address
951     /// [`ext_buffer_device_address`]: crate::device::DeviceExtensions::ext_buffer_device_address
952     DEVICE_ADDRESS = DEVICE_ADDRESS,
953 
954     /* TODO: enable
955     DEVICE_ADDRESS_CAPTURE_REPLAY = DEVICE_ADDRESS_CAPTURE_REPLAY,*/
956 }
957 
958 /// Error type returned by functions related to `DeviceMemory`.
959 #[derive(Clone, Debug, PartialEq, Eq)]
960 pub enum DeviceMemoryError {
961     /// Not enough memory available.
962     OomError(OomError),
963 
964     /// The maximum number of allocations has been exceeded.
965     TooManyObjects,
966 
967     /// An error occurred when mapping the memory.
968     MemoryMapError(MemoryMapError),
969 
970     RequirementNotMet {
971         required_for: &'static str,
972         requires_one_of: RequiresOneOf,
973     },
974 
975     /// `dedicated_allocation` was `Some`, but the provided `allocation_size`  was different from
976     /// the required size of the buffer or image.
977     DedicatedAllocationSizeMismatch {
978         allocation_size: DeviceSize,
979         required_size: DeviceSize,
980     },
981 
982     /// The requested export handle type is not supported for this operation, or was not provided in
983     /// `export_handle_types` when allocating the memory.
984     HandleTypeNotSupported {
985         handle_type: ExternalMemoryHandleType,
986     },
987 
988     /// The provided `MemoryImportInfo::Fd::handle_type` is not supported for file descriptors.
989     ImportFdHandleTypeNotSupported {
990         handle_type: ExternalMemoryHandleType,
991     },
992 
993     /// The provided `MemoryImportInfo::Win32::handle_type` is not supported.
994     ImportWin32HandleTypeNotSupported {
995         handle_type: ExternalMemoryHandleType,
996     },
997 
998     /// The provided `allocation_size` was greater than the memory type's heap size.
999     MemoryTypeHeapSizeExceeded {
1000         allocation_size: DeviceSize,
1001         heap_size: DeviceSize,
1002     },
1003 
1004     /// The provided `memory_type_index` was not less than the number of memory types in the
1005     /// physical device.
1006     MemoryTypeIndexOutOfRange {
1007         memory_type_index: u32,
1008         memory_type_count: u32,
1009     },
1010 
1011     /// The memory type from which this memory was allocated does not have the [`LAZILY_ALLOCATED`]
1012     /// flag set.
1013     ///
1014     /// [`LAZILY_ALLOCATED`]: crate::memory::MemoryPropertyFlags::LAZILY_ALLOCATED
1015     NotLazilyAllocated,
1016 
1017     /// Spec violation, containing the Valid Usage ID (VUID) from the Vulkan spec.
1018     // TODO: Remove
1019     SpecViolation(u32),
1020 
1021     /// An implicit violation that's convered in the Vulkan spec.
1022     // TODO: Remove
1023     ImplicitSpecViolation(&'static str),
1024 }
1025 
1026 impl Error for DeviceMemoryError {
source(&self) -> Option<&(dyn Error + 'static)>1027     fn source(&self) -> Option<&(dyn Error + 'static)> {
1028         match self {
1029             Self::OomError(err) => Some(err),
1030             Self::MemoryMapError(err) => Some(err),
1031             _ => None,
1032         }
1033     }
1034 }
1035 
1036 impl Display for DeviceMemoryError {
fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError>1037     fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
1038         match self {
1039             Self::OomError(_) => write!(f, "not enough memory available"),
1040             Self::TooManyObjects => {
1041                 write!(f, "the maximum number of allocations has been exceeded")
1042             }
1043             Self::MemoryMapError(_) => write!(f, "error occurred when mapping the memory"),
1044             Self::RequirementNotMet {
1045                 required_for,
1046                 requires_one_of,
1047             } => write!(
1048                 f,
1049                 "a requirement was not met for: {}; requires one of: {}",
1050                 required_for, requires_one_of,
1051             ),
1052             Self::DedicatedAllocationSizeMismatch {
1053                 allocation_size,
1054                 required_size,
1055             } => write!(
1056                 f,
1057                 "`dedicated_allocation` was `Some`, but the provided `allocation_size` ({}) was \
1058                 different from the required size of the buffer or image ({})",
1059                 allocation_size, required_size,
1060             ),
1061             Self::HandleTypeNotSupported { handle_type } => write!(
1062                 f,
1063                 "the requested export handle type ({:?}) is not supported for this operation, or \
1064                 was not provided in `export_handle_types` when allocating the memory",
1065                 handle_type,
1066             ),
1067             Self::ImportFdHandleTypeNotSupported { handle_type } => write!(
1068                 f,
1069                 "the provided `MemoryImportInfo::Fd::handle_type` ({:?}) is not supported for file \
1070                 descriptors",
1071                 handle_type,
1072             ),
1073             Self::ImportWin32HandleTypeNotSupported { handle_type } => write!(
1074                 f,
1075                 "the provided `MemoryImportInfo::Win32::handle_type` ({:?}) is not supported",
1076                 handle_type,
1077             ),
1078             Self::MemoryTypeHeapSizeExceeded {
1079                 allocation_size,
1080                 heap_size,
1081             } => write!(
1082                 f,
1083                 "the provided `allocation_size` ({}) was greater than the memory type's heap size \
1084                 ({})",
1085                 allocation_size, heap_size,
1086             ),
1087             Self::MemoryTypeIndexOutOfRange {
1088                 memory_type_index,
1089                 memory_type_count,
1090             } => write!(
1091                 f,
1092                 "the provided `memory_type_index` ({}) was not less than the number of memory \
1093                 types in the physical device ({})",
1094                 memory_type_index, memory_type_count,
1095             ),
1096             Self::NotLazilyAllocated => write!(
1097                 f,
1098                 "the memory type from which this memory was allocated does not have the \
1099                 `lazily_allocated` flag set",
1100             ),
1101 
1102             Self::SpecViolation(u) => {
1103                 write!(f, "valid usage ID check {} failed", u)
1104             }
1105             Self::ImplicitSpecViolation(e) => {
1106                 write!(f, "Implicit spec violation failed {}", e)
1107             }
1108         }
1109     }
1110 }
1111 
1112 impl From<VulkanError> for DeviceMemoryError {
from(err: VulkanError) -> Self1113     fn from(err: VulkanError) -> Self {
1114         match err {
1115             e @ VulkanError::OutOfHostMemory | e @ VulkanError::OutOfDeviceMemory => {
1116                 Self::OomError(e.into())
1117             }
1118             VulkanError::TooManyObjects => Self::TooManyObjects,
1119             _ => panic!("unexpected error: {:?}", err),
1120         }
1121     }
1122 }
1123 
1124 impl From<OomError> for DeviceMemoryError {
from(err: OomError) -> Self1125     fn from(err: OomError) -> Self {
1126         Self::OomError(err)
1127     }
1128 }
1129 
1130 impl From<MemoryMapError> for DeviceMemoryError {
from(err: MemoryMapError) -> Self1131     fn from(err: MemoryMapError) -> Self {
1132         Self::MemoryMapError(err)
1133     }
1134 }
1135 
1136 impl From<RequirementNotMet> for DeviceMemoryError {
from(err: RequirementNotMet) -> Self1137     fn from(err: RequirementNotMet) -> Self {
1138         Self::RequirementNotMet {
1139             required_for: err.required_for,
1140             requires_one_of: err.requires_one_of,
1141         }
1142     }
1143 }
1144 
1145 /// Represents device memory that has been mapped in a CPU-accessible space.
1146 ///
1147 /// In order to access the contents of the allocated memory, you can use the `read` and `write`
1148 /// methods.
1149 ///
1150 /// # Examples
1151 ///
1152 /// ```
1153 /// use vulkano::memory::{DeviceMemory, MappedDeviceMemory, MemoryAllocateInfo, MemoryPropertyFlags};
1154 ///
1155 /// # let device: std::sync::Arc<vulkano::device::Device> = return;
1156 /// // The memory type must be mappable.
1157 /// let memory_type_index = device
1158 ///     .physical_device()
1159 ///     .memory_properties()
1160 ///     .memory_types
1161 ///     .iter()
1162 ///     .position(|t| t.property_flags.intersects(MemoryPropertyFlags::HOST_VISIBLE))
1163 ///     .map(|i| i as u32)
1164 ///     .unwrap(); // Vk specs guarantee that this can't fail
1165 ///
1166 /// // Allocates 1KB of memory.
1167 /// let memory = DeviceMemory::allocate(
1168 ///     device.clone(),
1169 ///     MemoryAllocateInfo {
1170 ///         allocation_size: 1024,
1171 ///         memory_type_index,
1172 ///         ..Default::default()
1173 ///     },
1174 /// )
1175 /// .unwrap();
1176 /// let mapped_memory = MappedDeviceMemory::new(memory, 0..1024).unwrap();
1177 ///
1178 /// // Get access to the content.
1179 /// // Note that this is very unsafe because the access is unsynchronized.
1180 /// unsafe {
1181 ///     let content = mapped_memory.write(0..1024).unwrap();
1182 ///     content[12] = 54;
1183 /// }
1184 /// ```
1185 #[derive(Debug)]
1186 pub struct MappedDeviceMemory {
1187     memory: DeviceMemory,
1188     pointer: *mut c_void, // points to `range.start`
1189     range: Range<DeviceSize>,
1190 
1191     atom_size: DeviceAlignment,
1192     coherent: bool,
1193 }
1194 
1195 // Note that `MappedDeviceMemory` doesn't implement `Drop`, as we don't need to unmap memory before
1196 // freeing it.
1197 //
1198 // Vulkan specs, documentation of `vkFreeMemory`:
1199 // > If a memory object is mapped at the time it is freed, it is implicitly unmapped.
1200 
1201 impl MappedDeviceMemory {
1202     /// Maps a range of memory to be accessed by the CPU.
1203     ///
1204     /// `memory` must be allocated from host-visible memory.
1205     ///
1206     /// `range` is specified in bytes relative to the start of the memory allocation, and must fall
1207     /// within the range of the allocation (`0..allocation_size`). If `memory` was not allocated
1208     /// from host-coherent memory, then the start and end of `range` must be a multiple of the
1209     /// [`non_coherent_atom_size`](crate::device::Properties::non_coherent_atom_size) device
1210     /// property, but `range.end` can also the memory's `allocation_size`.
1211     ///
1212     /// # Panics
1213     ///
1214     /// - Panics if `range` is empty.
new(memory: DeviceMemory, range: Range<DeviceSize>) -> Result<Self, MemoryMapError>1215     pub fn new(memory: DeviceMemory, range: Range<DeviceSize>) -> Result<Self, MemoryMapError> {
1216         // VUID-vkMapMemory-size-00680
1217         assert!(!range.is_empty());
1218 
1219         // VUID-vkMapMemory-memory-00678
1220         // Guaranteed because we take ownership of `memory`, no other mapping can exist.
1221 
1222         // VUID-vkMapMemory-offset-00679
1223         // VUID-vkMapMemory-size-00681
1224         if range.end > memory.allocation_size {
1225             return Err(MemoryMapError::OutOfRange {
1226                 provided_range: range,
1227                 allowed_range: 0..memory.allocation_size,
1228             });
1229         }
1230 
1231         let device = memory.device();
1232         let memory_type = &device.physical_device().memory_properties().memory_types
1233             [memory.memory_type_index() as usize];
1234 
1235         // VUID-vkMapMemory-memory-00682
1236         if !memory_type
1237             .property_flags
1238             .intersects(MemoryPropertyFlags::HOST_VISIBLE)
1239         {
1240             return Err(MemoryMapError::NotHostVisible);
1241         }
1242 
1243         let coherent = memory_type
1244             .property_flags
1245             .intersects(MemoryPropertyFlags::HOST_COHERENT);
1246         let atom_size = device.physical_device().properties().non_coherent_atom_size;
1247 
1248         // Not required for merely mapping, but without this check the user can end up with
1249         // parts of the mapped memory at the start and end that they're not able to
1250         // invalidate/flush, which is probably unintended.
1251         if !coherent
1252             && (!is_aligned(range.start, atom_size)
1253                 || (!is_aligned(range.end, atom_size) && range.end != memory.allocation_size))
1254         {
1255             return Err(MemoryMapError::RangeNotAlignedToAtomSize { range, atom_size });
1256         }
1257 
1258         let pointer = unsafe {
1259             let fns = device.fns();
1260             let mut output = MaybeUninit::uninit();
1261             (fns.v1_0.map_memory)(
1262                 device.handle(),
1263                 memory.handle,
1264                 range.start,
1265                 range.end - range.start,
1266                 ash::vk::MemoryMapFlags::empty(),
1267                 output.as_mut_ptr(),
1268             )
1269             .result()
1270             .map_err(VulkanError::from)?;
1271             output.assume_init()
1272         };
1273 
1274         Ok(MappedDeviceMemory {
1275             memory,
1276             pointer,
1277             range,
1278             atom_size,
1279             coherent,
1280         })
1281     }
1282 
1283     /// Unmaps the memory. It will no longer be accessible from the CPU.
1284     #[inline]
unmap(self) -> DeviceMemory1285     pub fn unmap(self) -> DeviceMemory {
1286         unsafe {
1287             let device = self.memory.device();
1288             let fns = device.fns();
1289             (fns.v1_0.unmap_memory)(device.handle(), self.memory.handle);
1290         }
1291 
1292         self.memory
1293     }
1294 
1295     /// Invalidates the host (CPU) cache for a range of mapped memory.
1296     ///
1297     /// If the mapped memory is not host-coherent, you must call this function before the memory is
1298     /// read by the host, if the device previously wrote to the memory. It has no effect if the
1299     /// mapped memory is host-coherent.
1300     ///
1301     /// `range` is specified in bytes relative to the start of the memory allocation, and must fall
1302     /// within the range of the memory mapping given to `new`. If the memory was not allocated
1303     /// from host-coherent memory, then the start and end of `range` must be a multiple of the
1304     /// [`non_coherent_atom_size`](crate::device::Properties::non_coherent_atom_size) device
1305     /// property, but `range.end` can also equal the memory's `allocation_size`.
1306     ///
1307     /// # Safety
1308     ///
1309     /// - If there are memory writes by the GPU that have not been propagated into the CPU cache,
1310     ///   then there must not be any references in Rust code to the specified `range` of the memory.
1311     ///
1312     /// # Panics
1313     ///
1314     /// - Panics if `range` is empty.
1315     #[inline]
invalidate_range(&self, range: Range<DeviceSize>) -> Result<(), MemoryMapError>1316     pub unsafe fn invalidate_range(&self, range: Range<DeviceSize>) -> Result<(), MemoryMapError> {
1317         if self.coherent {
1318             return Ok(());
1319         }
1320 
1321         self.check_range(range.clone())?;
1322 
1323         // VUID-VkMappedMemoryRange-memory-00684
1324         // Guaranteed because `self` owns the memory and it's mapped during our lifetime.
1325 
1326         let range = ash::vk::MappedMemoryRange {
1327             memory: self.memory.handle(),
1328             offset: range.start,
1329             size: range.end - range.start,
1330             ..Default::default()
1331         };
1332 
1333         let fns = self.memory.device().fns();
1334         (fns.v1_0.invalidate_mapped_memory_ranges)(self.memory.device().handle(), 1, &range)
1335             .result()
1336             .map_err(VulkanError::from)?;
1337 
1338         Ok(())
1339     }
1340 
1341     /// Flushes the host (CPU) cache for a range of mapped memory.
1342     ///
1343     /// If the mapped memory is not host-coherent, you must call this function after writing to the
1344     /// memory, if the device is going to read the memory. It has no effect if the
1345     /// mapped memory is host-coherent.
1346     ///
1347     /// `range` is specified in bytes relative to the start of the memory allocation, and must fall
1348     /// within the range of the memory mapping given to `map`. If the memory was not allocated
1349     /// from host-coherent memory, then the start and end of `range` must be a multiple of the
1350     /// [`non_coherent_atom_size`](crate::device::Properties::non_coherent_atom_size) device
1351     /// property, but `range.end` can also equal the memory's `allocation_size`.
1352     ///
1353     /// # Safety
1354     ///
1355     /// - There must be no operations pending or executing in a GPU queue, that access the specified
1356     ///   `range` of the memory.
1357     ///
1358     /// # Panics
1359     ///
1360     /// - Panics if `range` is empty.
1361     #[inline]
flush_range(&self, range: Range<DeviceSize>) -> Result<(), MemoryMapError>1362     pub unsafe fn flush_range(&self, range: Range<DeviceSize>) -> Result<(), MemoryMapError> {
1363         self.check_range(range.clone())?;
1364 
1365         if self.coherent {
1366             return Ok(());
1367         }
1368 
1369         // VUID-VkMappedMemoryRange-memory-00684
1370         // Guaranteed because `self` owns the memory and it's mapped during our lifetime.
1371 
1372         let range = ash::vk::MappedMemoryRange {
1373             memory: self.memory.handle(),
1374             offset: range.start,
1375             size: range.end - range.start,
1376             ..Default::default()
1377         };
1378 
1379         let fns = self.device().fns();
1380         (fns.v1_0.flush_mapped_memory_ranges)(self.memory.device().handle(), 1, &range)
1381             .result()
1382             .map_err(VulkanError::from)?;
1383 
1384         Ok(())
1385     }
1386 
1387     /// Returns a reference to bytes in the mapped memory.
1388     ///
1389     /// `range` is specified in bytes relative to the start of the memory allocation, and must fall
1390     /// within the range of the memory mapping given to `map`. If the memory was not allocated
1391     /// from host-coherent memory, then the start and end of `range` must be a multiple of the
1392     /// [`non_coherent_atom_size`](crate::device::Properties::non_coherent_atom_size) device
1393     /// property, but `range.end` can also equal the memory's `allocation_size`.
1394     ///
1395     /// # Safety
1396     ///
1397     /// - While the returned reference exists, there must not be any mutable references in Rust code
1398     ///   to the same memory.
1399     /// - While the returned reference exists, there must be no operations pending or executing in
1400     ///   a GPU queue, that write to the same memory.
1401     ///
1402     /// # Panics
1403     ///
1404     /// - Panics if `range` is empty.
1405     #[inline]
read(&self, range: Range<DeviceSize>) -> Result<&[u8], MemoryMapError>1406     pub unsafe fn read(&self, range: Range<DeviceSize>) -> Result<&[u8], MemoryMapError> {
1407         self.check_range(range.clone())?;
1408 
1409         let bytes = slice::from_raw_parts(
1410             self.pointer.add((range.start - self.range.start) as usize) as *const u8,
1411             (range.end - range.start) as usize,
1412         );
1413 
1414         Ok(bytes)
1415     }
1416 
1417     /// Returns a mutable reference to bytes in the mapped memory.
1418     ///
1419     /// `range` is specified in bytes relative to the start of the memory allocation, and must fall
1420     /// within the range of the memory mapping given to `map`. If the memory was not allocated
1421     /// from host-coherent memory, then the start and end of `range` must be a multiple of the
1422     /// [`non_coherent_atom_size`](crate::device::Properties::non_coherent_atom_size) device
1423     /// property, but `range.end` can also equal the memory's `allocation_size`.
1424     ///
1425     /// # Safety
1426     ///
1427     /// - While the returned reference exists, there must not be any other references in Rust code
1428     ///   to the same memory.
1429     /// - While the returned reference exists, there must be no operations pending or executing in
1430     ///   a GPU queue, that access the same memory.
1431     ///
1432     /// # Panics
1433     ///
1434     /// - Panics if `range` is empty.
1435     #[inline]
write(&self, range: Range<DeviceSize>) -> Result<&mut [u8], MemoryMapError>1436     pub unsafe fn write(&self, range: Range<DeviceSize>) -> Result<&mut [u8], MemoryMapError> {
1437         self.check_range(range.clone())?;
1438 
1439         let bytes = slice::from_raw_parts_mut(
1440             self.pointer.add((range.start - self.range.start) as usize) as *mut u8,
1441             (range.end - range.start) as usize,
1442         );
1443 
1444         Ok(bytes)
1445     }
1446 
1447     #[inline]
check_range(&self, range: Range<DeviceSize>) -> Result<(), MemoryMapError>1448     fn check_range(&self, range: Range<DeviceSize>) -> Result<(), MemoryMapError> {
1449         assert!(!range.is_empty());
1450 
1451         // VUID-VkMappedMemoryRange-size-00685
1452         if range.start < self.range.start || range.end > self.range.end {
1453             return Err(MemoryMapError::OutOfRange {
1454                 provided_range: range,
1455                 allowed_range: self.range.clone(),
1456             });
1457         }
1458 
1459         if !self.coherent {
1460             // VUID-VkMappedMemoryRange-offset-00687
1461             // VUID-VkMappedMemoryRange-size-01390
1462             if !is_aligned(range.start, self.atom_size)
1463                 || (!is_aligned(range.end, self.atom_size)
1464                     && range.end != self.memory.allocation_size)
1465             {
1466                 return Err(MemoryMapError::RangeNotAlignedToAtomSize {
1467                     range,
1468                     atom_size: self.atom_size,
1469                 });
1470             }
1471         }
1472 
1473         Ok(())
1474     }
1475 }
1476 
1477 impl AsRef<DeviceMemory> for MappedDeviceMemory {
1478     #[inline]
as_ref(&self) -> &DeviceMemory1479     fn as_ref(&self) -> &DeviceMemory {
1480         &self.memory
1481     }
1482 }
1483 
1484 impl AsMut<DeviceMemory> for MappedDeviceMemory {
1485     #[inline]
as_mut(&mut self) -> &mut DeviceMemory1486     fn as_mut(&mut self) -> &mut DeviceMemory {
1487         &mut self.memory
1488     }
1489 }
1490 
1491 unsafe impl DeviceOwned for MappedDeviceMemory {
1492     #[inline]
device(&self) -> &Arc<Device>1493     fn device(&self) -> &Arc<Device> {
1494         self.memory.device()
1495     }
1496 }
1497 
1498 unsafe impl Send for MappedDeviceMemory {}
1499 unsafe impl Sync for MappedDeviceMemory {}
1500 
1501 /// Error type returned by functions related to `DeviceMemory`.
1502 #[derive(Clone, Debug, PartialEq, Eq)]
1503 pub enum MemoryMapError {
1504     /// Not enough memory available.
1505     OomError(OomError),
1506 
1507     /// Memory map failed.
1508     MemoryMapFailed,
1509 
1510     /// Tried to map memory whose type is not host-visible.
1511     NotHostVisible,
1512 
1513     /// The specified `range` is not contained within the allocated or mapped memory range.
1514     OutOfRange {
1515         provided_range: Range<DeviceSize>,
1516         allowed_range: Range<DeviceSize>,
1517     },
1518 
1519     /// The memory is not host-coherent, and the specified `range` bounds are not a multiple of the
1520     /// [`non_coherent_atom_size`](crate::device::Properties::non_coherent_atom_size) device
1521     /// property.
1522     RangeNotAlignedToAtomSize {
1523         range: Range<DeviceSize>,
1524         atom_size: DeviceAlignment,
1525     },
1526 }
1527 
1528 impl Error for MemoryMapError {
source(&self) -> Option<&(dyn Error + 'static)>1529     fn source(&self) -> Option<&(dyn Error + 'static)> {
1530         match self {
1531             Self::OomError(err) => Some(err),
1532             _ => None,
1533         }
1534     }
1535 }
1536 
1537 impl Display for MemoryMapError {
fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError>1538     fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
1539         match self {
1540             Self::OomError(_) => write!(f, "not enough memory available"),
1541             Self::MemoryMapFailed => write!(f, "memory map failed"),
1542             Self::NotHostVisible => {
1543                 write!(f, "tried to map memory whose type is not host-visible")
1544             }
1545             Self::OutOfRange {
1546                 provided_range,
1547                 allowed_range,
1548             } => write!(
1549                 f,
1550                 "the specified `range` ({:?}) was not contained within the allocated or mapped \
1551                 memory range ({:?})",
1552                 provided_range, allowed_range,
1553             ),
1554             Self::RangeNotAlignedToAtomSize { range, atom_size } => write!(
1555                 f,
1556                 "the memory is not host-coherent, and the specified `range` bounds ({:?}) are not \
1557                 a multiple of the `non_coherent_atom_size` device property ({:?})",
1558                 range, atom_size,
1559             ),
1560         }
1561     }
1562 }
1563 
1564 impl From<VulkanError> for MemoryMapError {
from(err: VulkanError) -> Self1565     fn from(err: VulkanError) -> Self {
1566         match err {
1567             e @ VulkanError::OutOfHostMemory | e @ VulkanError::OutOfDeviceMemory => {
1568                 Self::OomError(e.into())
1569             }
1570             VulkanError::MemoryMapFailed => Self::MemoryMapFailed,
1571             _ => panic!("unexpected error: {:?}", err),
1572         }
1573     }
1574 }
1575 
1576 impl From<OomError> for MemoryMapError {
from(err: OomError) -> Self1577     fn from(err: OomError) -> Self {
1578         Self::OomError(err)
1579     }
1580 }
1581 
1582 #[cfg(test)]
1583 mod tests {
1584     use super::MemoryAllocateInfo;
1585     use crate::{
1586         memory::{DeviceMemory, DeviceMemoryError, MemoryPropertyFlags},
1587         OomError,
1588     };
1589 
1590     #[test]
create()1591     fn create() {
1592         let (device, _) = gfx_dev_and_queue!();
1593         let _ = DeviceMemory::allocate(
1594             device,
1595             MemoryAllocateInfo {
1596                 allocation_size: 256,
1597                 memory_type_index: 0,
1598                 ..Default::default()
1599             },
1600         )
1601         .unwrap();
1602     }
1603 
1604     #[test]
zero_size()1605     fn zero_size() {
1606         let (device, _) = gfx_dev_and_queue!();
1607         assert_should_panic!({
1608             let _ = DeviceMemory::allocate(
1609                 device.clone(),
1610                 MemoryAllocateInfo {
1611                     allocation_size: 0,
1612                     memory_type_index: 0,
1613                     ..Default::default()
1614                 },
1615             )
1616             .unwrap();
1617         });
1618     }
1619 
1620     #[test]
1621     #[cfg(target_pointer_width = "64")]
oom_single()1622     fn oom_single() {
1623         let (device, _) = gfx_dev_and_queue!();
1624         let memory_type_index = device
1625             .physical_device()
1626             .memory_properties()
1627             .memory_types
1628             .iter()
1629             .enumerate()
1630             .find_map(|(i, m)| {
1631                 (!m.property_flags
1632                     .intersects(MemoryPropertyFlags::LAZILY_ALLOCATED))
1633                 .then_some(i as u32)
1634             })
1635             .unwrap();
1636 
1637         match DeviceMemory::allocate(
1638             device,
1639             MemoryAllocateInfo {
1640                 allocation_size: 0xffffffffffffffff,
1641                 memory_type_index,
1642                 ..Default::default()
1643             },
1644         ) {
1645             Err(DeviceMemoryError::MemoryTypeHeapSizeExceeded { .. }) => (),
1646             _ => panic!(),
1647         }
1648     }
1649 
1650     #[test]
1651     #[ignore] // TODO: test fails for now on Mesa+Intel
oom_multi()1652     fn oom_multi() {
1653         let (device, _) = gfx_dev_and_queue!();
1654         let (memory_type_index, memory_type) = device
1655             .physical_device()
1656             .memory_properties()
1657             .memory_types
1658             .iter()
1659             .enumerate()
1660             .find_map(|(i, m)| {
1661                 (!m.property_flags
1662                     .intersects(MemoryPropertyFlags::LAZILY_ALLOCATED))
1663                 .then_some((i as u32, m))
1664             })
1665             .unwrap();
1666         let heap_size = device.physical_device().memory_properties().memory_heaps
1667             [memory_type.heap_index as usize]
1668             .size;
1669 
1670         let mut allocs = Vec::new();
1671 
1672         for _ in 0..4 {
1673             match DeviceMemory::allocate(
1674                 device.clone(),
1675                 MemoryAllocateInfo {
1676                     allocation_size: heap_size / 3,
1677                     memory_type_index,
1678                     ..Default::default()
1679                 },
1680             ) {
1681                 Err(DeviceMemoryError::OomError(OomError::OutOfDeviceMemory)) => return, // test succeeded
1682                 Ok(a) => allocs.push(a),
1683                 _ => (),
1684             }
1685         }
1686 
1687         panic!()
1688     }
1689 
1690     #[test]
allocation_count()1691     fn allocation_count() {
1692         let (device, _) = gfx_dev_and_queue!();
1693         assert_eq!(device.allocation_count(), 0);
1694         let _mem1 = DeviceMemory::allocate(
1695             device.clone(),
1696             MemoryAllocateInfo {
1697                 allocation_size: 256,
1698                 memory_type_index: 0,
1699                 ..Default::default()
1700             },
1701         )
1702         .unwrap();
1703         assert_eq!(device.allocation_count(), 1);
1704         {
1705             let _mem2 = DeviceMemory::allocate(
1706                 device.clone(),
1707                 MemoryAllocateInfo {
1708                     allocation_size: 256,
1709                     memory_type_index: 0,
1710                     ..Default::default()
1711                 },
1712             )
1713             .unwrap();
1714             assert_eq!(device.allocation_count(), 2);
1715         }
1716         assert_eq!(device.allocation_count(), 1);
1717     }
1718 }
1719