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