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 //! Low-level implementation of images. 11 //! 12 //! This module contains low-level wrappers around the Vulkan image types. All 13 //! other image types of this library, and all custom image types 14 //! that you create must wrap around the types in this module. 15 16 use crate::check_errors; 17 use crate::device::Device; 18 use crate::format::Format; 19 use crate::format::FormatFeatures; 20 use crate::format::FormatTy; 21 use crate::image::ImageAspect; 22 use crate::image::ImageCreateFlags; 23 use crate::image::ImageDimensions; 24 use crate::image::ImageUsage; 25 use crate::image::MipmapsCount; 26 use crate::image::SampleCount; 27 use crate::memory::DeviceMemory; 28 use crate::memory::DeviceMemoryAllocError; 29 use crate::memory::MemoryRequirements; 30 use crate::sync::Sharing; 31 use crate::DeviceSize; 32 use crate::Error; 33 use crate::OomError; 34 use crate::Version; 35 use crate::VulkanObject; 36 use ash::vk::Handle; 37 use smallvec::SmallVec; 38 use std::error; 39 use std::fmt; 40 use std::hash::Hash; 41 use std::hash::Hasher; 42 use std::mem::MaybeUninit; 43 use std::ops::Range; 44 use std::ptr; 45 use std::sync::Arc; 46 47 /// A storage for pixels or arbitrary data. 48 /// 49 /// # Safety 50 /// 51 /// This type is not just unsafe but very unsafe. Don't use it directly. 52 /// 53 /// - You must manually bind memory to the image with `bind_memory`. The memory must respect the 54 /// requirements returned by `new`. 55 /// - The memory that you bind to the image must be manually kept alive. 56 /// - The queue family ownership must be manually enforced. 57 /// - The usage must be manually enforced. 58 /// - The image layout must be manually enforced and transitioned. 59 /// 60 pub struct UnsafeImage { 61 image: ash::vk::Image, 62 device: Arc<Device>, 63 usage: ImageUsage, 64 format: Format, 65 flags: ImageCreateFlags, 66 67 dimensions: ImageDimensions, 68 samples: SampleCount, 69 mipmaps: u32, 70 71 // Features that are supported for this particular format. 72 format_features: FormatFeatures, 73 74 // `vkDestroyImage` is called only if `needs_destruction` is true. 75 needs_destruction: bool, 76 preinitialized_layout: bool, 77 } 78 79 impl UnsafeImage { 80 /// Creates a new image and allocates memory for it. 81 /// 82 /// # Panic 83 /// 84 /// - Panics if one of the dimensions is 0. 85 /// - Panics if the number of mipmaps is 0. 86 /// - Panics if the number of samples is 0. 87 /// 88 #[inline] new<'a, Mi, I>( device: Arc<Device>, usage: ImageUsage, format: Format, flags: ImageCreateFlags, dimensions: ImageDimensions, num_samples: SampleCount, mipmaps: Mi, sharing: Sharing<I>, linear_tiling: bool, preinitialized_layout: bool, ) -> Result<(UnsafeImage, MemoryRequirements), ImageCreationError> where Mi: Into<MipmapsCount>, I: Iterator<Item = u32>,89 pub unsafe fn new<'a, Mi, I>( 90 device: Arc<Device>, 91 usage: ImageUsage, 92 format: Format, 93 flags: ImageCreateFlags, 94 dimensions: ImageDimensions, 95 num_samples: SampleCount, 96 mipmaps: Mi, 97 sharing: Sharing<I>, 98 linear_tiling: bool, 99 preinitialized_layout: bool, 100 ) -> Result<(UnsafeImage, MemoryRequirements), ImageCreationError> 101 where 102 Mi: Into<MipmapsCount>, 103 I: Iterator<Item = u32>, 104 { 105 let sharing = match sharing { 106 Sharing::Exclusive => (ash::vk::SharingMode::EXCLUSIVE, SmallVec::<[u32; 8]>::new()), 107 Sharing::Concurrent(ids) => (ash::vk::SharingMode::CONCURRENT, ids.collect()), 108 }; 109 110 UnsafeImage::new_impl( 111 device, 112 usage, 113 format, 114 flags, 115 dimensions, 116 num_samples, 117 mipmaps.into(), 118 sharing, 119 linear_tiling, 120 preinitialized_layout, 121 ) 122 } 123 124 // Non-templated version to avoid inlining and improve compile times. new_impl( device: Arc<Device>, usage: ImageUsage, format: Format, flags: ImageCreateFlags, dimensions: ImageDimensions, num_samples: SampleCount, mipmaps: MipmapsCount, (sh_mode, sh_indices): (ash::vk::SharingMode, SmallVec<[u32; 8]>), linear_tiling: bool, preinitialized_layout: bool, ) -> Result<(UnsafeImage, MemoryRequirements), ImageCreationError>125 unsafe fn new_impl( 126 device: Arc<Device>, 127 usage: ImageUsage, 128 format: Format, 129 flags: ImageCreateFlags, 130 dimensions: ImageDimensions, 131 num_samples: SampleCount, 132 mipmaps: MipmapsCount, 133 (sh_mode, sh_indices): (ash::vk::SharingMode, SmallVec<[u32; 8]>), 134 linear_tiling: bool, 135 preinitialized_layout: bool, 136 ) -> Result<(UnsafeImage, MemoryRequirements), ImageCreationError> { 137 // TODO: doesn't check that the proper features are enabled 138 139 if flags.sparse_binding 140 || flags.sparse_residency 141 || flags.sparse_aliased 142 || flags.mutable_format 143 { 144 unimplemented!(); 145 } 146 147 let fns = device.fns(); 148 let fns_i = device.instance().fns(); 149 150 // Checking if image usage conforms to what is supported. 151 let format_features = { 152 let format_properties = format.properties(device.physical_device()); 153 154 let features = if linear_tiling { 155 format_properties.linear_tiling_features 156 } else { 157 format_properties.optimal_tiling_features 158 }; 159 160 if features == FormatFeatures::default() { 161 return Err(ImageCreationError::FormatNotSupported); 162 } 163 164 if usage.sampled && !features.sampled_image { 165 return Err(ImageCreationError::UnsupportedUsage); 166 } 167 if usage.storage && !features.storage_image { 168 return Err(ImageCreationError::UnsupportedUsage); 169 } 170 if usage.color_attachment && !features.color_attachment { 171 return Err(ImageCreationError::UnsupportedUsage); 172 } 173 if usage.depth_stencil_attachment && !features.depth_stencil_attachment { 174 return Err(ImageCreationError::UnsupportedUsage); 175 } 176 if usage.input_attachment 177 && !(features.color_attachment || features.depth_stencil_attachment) 178 { 179 return Err(ImageCreationError::UnsupportedUsage); 180 } 181 if device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_maintenance1 182 { 183 if usage.transfer_source && !features.transfer_src { 184 return Err(ImageCreationError::UnsupportedUsage); 185 } 186 if usage.transfer_destination && !features.transfer_dst { 187 return Err(ImageCreationError::UnsupportedUsage); 188 } 189 } 190 191 features 192 }; 193 194 // VUID-VkImageCreateInfo-usage-requiredbitmask: usage must not be 0 195 if usage == ImageUsage::none() { 196 return Err(ImageCreationError::UnsupportedUsage); 197 } 198 199 // If `transient_attachment` is true, then only `color_attachment`, 200 // `depth_stencil_attachment` and `input_attachment` can be true as well. 201 if usage.transient_attachment { 202 let u = ImageUsage { 203 transient_attachment: false, 204 color_attachment: false, 205 depth_stencil_attachment: false, 206 input_attachment: false, 207 ..usage.clone() 208 }; 209 210 if u != ImageUsage::none() { 211 return Err(ImageCreationError::UnsupportedUsage); 212 } 213 } 214 215 // This function is going to perform various checks and write to `capabilities_error` in 216 // case of error. 217 // 218 // If `capabilities_error` is not `None` after the checks are finished, the function will 219 // check for additional image capabilities (section 31.4 of the specs). 220 let mut capabilities_error = None; 221 222 // Compute the number of mipmaps. 223 let mipmaps = match mipmaps.into() { 224 MipmapsCount::Specific(num) => { 225 let max_mipmaps = dimensions.max_mipmaps(); 226 debug_assert!(max_mipmaps >= 1); 227 if num < 1 { 228 return Err(ImageCreationError::InvalidMipmapsCount { 229 obtained: num, 230 valid_range: 1..max_mipmaps + 1, 231 }); 232 } else if num > max_mipmaps { 233 capabilities_error = Some(ImageCreationError::InvalidMipmapsCount { 234 obtained: num, 235 valid_range: 1..max_mipmaps + 1, 236 }); 237 } 238 239 num 240 } 241 MipmapsCount::Log2 => dimensions.max_mipmaps(), 242 MipmapsCount::One => 1, 243 }; 244 245 // Checking whether the number of samples is supported. 246 let mut supported_samples = ash::vk::SampleCountFlags::from_raw(0x7f); // all bits up to VK_SAMPLE_COUNT_64_BIT 247 248 if usage.sampled { 249 match format.ty() { 250 FormatTy::Float | FormatTy::Compressed => { 251 supported_samples &= device 252 .physical_device() 253 .properties() 254 .sampled_image_color_sample_counts 255 .into(); 256 } 257 FormatTy::Uint | FormatTy::Sint => { 258 supported_samples &= device 259 .physical_device() 260 .properties() 261 .sampled_image_integer_sample_counts 262 .into(); 263 } 264 FormatTy::Depth => { 265 supported_samples &= device 266 .physical_device() 267 .properties() 268 .sampled_image_depth_sample_counts 269 .into(); 270 } 271 FormatTy::Stencil => { 272 supported_samples &= device 273 .physical_device() 274 .properties() 275 .sampled_image_stencil_sample_counts 276 .into(); 277 } 278 FormatTy::DepthStencil => { 279 supported_samples &= device 280 .physical_device() 281 .properties() 282 .sampled_image_depth_sample_counts 283 .into(); 284 supported_samples &= device 285 .physical_device() 286 .properties() 287 .sampled_image_stencil_sample_counts 288 .into(); 289 } 290 FormatTy::Ycbcr => { 291 /* 292 * VUID-VkImageCreateInfo-format-02562: If the image format is one of 293 * those formats requiring sampler ycbcr conversion, samples *must* be 294 * VK_SAMPLE_COUNT_1_BIT 295 */ 296 supported_samples &= ash::vk::SampleCountFlags::TYPE_1; 297 } 298 } 299 300 if usage.storage { 301 supported_samples &= device 302 .physical_device() 303 .properties() 304 .storage_image_sample_counts 305 .into(); 306 } 307 308 if usage.color_attachment 309 || usage.depth_stencil_attachment 310 || usage.input_attachment 311 || usage.transient_attachment 312 { 313 match format.ty() { 314 FormatTy::Float | FormatTy::Compressed | FormatTy::Uint | FormatTy::Sint => { 315 supported_samples &= device 316 .physical_device() 317 .properties() 318 .framebuffer_color_sample_counts 319 .into(); 320 } 321 FormatTy::Depth => { 322 supported_samples &= device 323 .physical_device() 324 .properties() 325 .framebuffer_depth_sample_counts 326 .into(); 327 } 328 FormatTy::Stencil => { 329 supported_samples &= device 330 .physical_device() 331 .properties() 332 .framebuffer_stencil_sample_counts 333 .into(); 334 } 335 FormatTy::DepthStencil => { 336 supported_samples &= device 337 .physical_device() 338 .properties() 339 .framebuffer_depth_sample_counts 340 .into(); 341 supported_samples &= device 342 .physical_device() 343 .properties() 344 .framebuffer_stencil_sample_counts 345 .into(); 346 } 347 FormatTy::Ycbcr => { 348 /* 349 * It's generally not possible to use a Ycbcr image as a framebuffer color 350 * attachment. 351 */ 352 return Err(ImageCreationError::UnsupportedUsage); 353 } 354 } 355 } 356 357 if (ash::vk::SampleCountFlags::from(num_samples) & supported_samples).is_empty() { 358 let err = ImageCreationError::UnsupportedSamplesCount { 359 obtained: num_samples, 360 }; 361 capabilities_error = Some(err); 362 } 363 } 364 365 // If the `shaderStorageImageMultisample` feature is not enabled and we have 366 // `usage_storage` set to true, then the number of samples must be 1. 367 if usage.storage && num_samples as u32 > 1 { 368 if !device.enabled_features().shader_storage_image_multisample { 369 return Err(ImageCreationError::ShaderStorageImageMultisampleFeatureNotEnabled); 370 } 371 } 372 373 // Decoding the dimensions. 374 let (ty, extent, array_layers) = match dimensions { 375 ImageDimensions::Dim1d { 376 width, 377 array_layers, 378 } => { 379 if width == 0 || array_layers == 0 { 380 return Err(ImageCreationError::UnsupportedDimensions { dimensions }); 381 } 382 let extent = ash::vk::Extent3D { 383 width, 384 height: 1, 385 depth: 1, 386 }; 387 (ash::vk::ImageType::TYPE_1D, extent, array_layers) 388 } 389 ImageDimensions::Dim2d { 390 width, 391 height, 392 array_layers, 393 } => { 394 if width == 0 || height == 0 || array_layers == 0 { 395 return Err(ImageCreationError::UnsupportedDimensions { dimensions }); 396 } 397 let extent = ash::vk::Extent3D { 398 width, 399 height, 400 depth: 1, 401 }; 402 (ash::vk::ImageType::TYPE_2D, extent, array_layers) 403 } 404 ImageDimensions::Dim3d { 405 width, 406 height, 407 depth, 408 } => { 409 if width == 0 || height == 0 || depth == 0 { 410 return Err(ImageCreationError::UnsupportedDimensions { dimensions }); 411 } 412 let extent = ash::vk::Extent3D { 413 width, 414 height, 415 depth, 416 }; 417 (ash::vk::ImageType::TYPE_3D, extent, 1) 418 } 419 }; 420 421 // Checking flags requirements. 422 if flags.cube_compatible { 423 if !(ty == ash::vk::ImageType::TYPE_2D 424 && extent.width == extent.height 425 && array_layers >= 6) 426 { 427 return Err(ImageCreationError::CreationFlagRequirementsNotMet); 428 } 429 } 430 431 if flags.array_2d_compatible { 432 if !(ty == ash::vk::ImageType::TYPE_3D) { 433 return Err(ImageCreationError::CreationFlagRequirementsNotMet); 434 } 435 } 436 437 // Checking the dimensions against the limits. 438 if array_layers 439 > device 440 .physical_device() 441 .properties() 442 .max_image_array_layers 443 { 444 let err = ImageCreationError::UnsupportedDimensions { dimensions }; 445 capabilities_error = Some(err); 446 } 447 match ty { 448 ash::vk::ImageType::TYPE_1D => { 449 if extent.width 450 > device 451 .physical_device() 452 .properties() 453 .max_image_dimension1_d 454 { 455 let err = ImageCreationError::UnsupportedDimensions { dimensions }; 456 capabilities_error = Some(err); 457 } 458 } 459 ash::vk::ImageType::TYPE_2D => { 460 let limit = device 461 .physical_device() 462 .properties() 463 .max_image_dimension2_d; 464 if extent.width > limit || extent.height > limit { 465 let err = ImageCreationError::UnsupportedDimensions { dimensions }; 466 capabilities_error = Some(err); 467 } 468 469 if flags.cube_compatible { 470 let limit = device 471 .physical_device() 472 .properties() 473 .max_image_dimension_cube; 474 if extent.width > limit { 475 let err = ImageCreationError::UnsupportedDimensions { dimensions }; 476 capabilities_error = Some(err); 477 } 478 } 479 } 480 ash::vk::ImageType::TYPE_3D => { 481 let limit = device 482 .physical_device() 483 .properties() 484 .max_image_dimension3_d; 485 if extent.width > limit || extent.height > limit || extent.depth > limit { 486 let err = ImageCreationError::UnsupportedDimensions { dimensions }; 487 capabilities_error = Some(err); 488 } 489 } 490 _ => unreachable!(), 491 }; 492 493 let usage_bits = usage.into(); 494 495 // Now that all checks have been performed, if any of the check failed we query the Vulkan 496 // implementation for additional image capabilities. 497 if let Some(capabilities_error) = capabilities_error { 498 let tiling = if linear_tiling { 499 ash::vk::ImageTiling::LINEAR 500 } else { 501 ash::vk::ImageTiling::OPTIMAL 502 }; 503 504 let mut output = MaybeUninit::uninit(); 505 let physical_device = device.physical_device().internal_object(); 506 let r = fns_i.v1_0.get_physical_device_image_format_properties( 507 physical_device, 508 format.into(), 509 ty, 510 tiling, 511 usage_bits, 512 ash::vk::ImageCreateFlags::empty(), /* TODO */ 513 output.as_mut_ptr(), 514 ); 515 516 match check_errors(r) { 517 Ok(_) => (), 518 Err(Error::FormatNotSupported) => { 519 return Err(ImageCreationError::FormatNotSupported) 520 } 521 Err(err) => return Err(err.into()), 522 } 523 524 let output = output.assume_init(); 525 526 if extent.width > output.max_extent.width 527 || extent.height > output.max_extent.height 528 || extent.depth > output.max_extent.depth 529 || mipmaps > output.max_mip_levels 530 || array_layers > output.max_array_layers 531 || (ash::vk::SampleCountFlags::from(num_samples) & output.sample_counts).is_empty() 532 { 533 return Err(capabilities_error); 534 } 535 } 536 537 // Everything now ok. Creating the image. 538 let image = { 539 let infos = ash::vk::ImageCreateInfo { 540 flags: flags.into(), 541 image_type: ty, 542 format: format.into(), 543 extent, 544 mip_levels: mipmaps, 545 array_layers: array_layers, 546 samples: num_samples.into(), 547 tiling: if linear_tiling { 548 ash::vk::ImageTiling::LINEAR 549 } else { 550 ash::vk::ImageTiling::OPTIMAL 551 }, 552 usage: usage_bits, 553 sharing_mode: sh_mode, 554 queue_family_index_count: sh_indices.len() as u32, 555 p_queue_family_indices: sh_indices.as_ptr(), 556 initial_layout: if preinitialized_layout { 557 ash::vk::ImageLayout::PREINITIALIZED 558 } else { 559 ash::vk::ImageLayout::UNDEFINED 560 }, 561 ..Default::default() 562 }; 563 564 let mut output = MaybeUninit::uninit(); 565 check_errors(fns.v1_0.create_image( 566 device.internal_object(), 567 &infos, 568 ptr::null(), 569 output.as_mut_ptr(), 570 ))?; 571 output.assume_init() 572 }; 573 574 let mem_reqs = if device.api_version() >= Version::V1_1 575 || device.enabled_extensions().khr_get_memory_requirements2 576 { 577 let infos = ash::vk::ImageMemoryRequirementsInfo2 { 578 image, 579 ..Default::default() 580 }; 581 582 let mut output2 = if device.api_version() >= Version::V1_1 583 || device.enabled_extensions().khr_dedicated_allocation 584 { 585 Some(ash::vk::MemoryDedicatedRequirements::default()) 586 } else { 587 None 588 }; 589 590 let mut output = ash::vk::MemoryRequirements2 { 591 p_next: output2 592 .as_mut() 593 .map(|o| o as *mut _) 594 .unwrap_or(ptr::null_mut()) as *mut _, 595 ..Default::default() 596 }; 597 598 if device.api_version() >= Version::V1_1 { 599 fns.v1_1.get_image_memory_requirements2( 600 device.internal_object(), 601 &infos, 602 &mut output, 603 ); 604 } else { 605 fns.khr_get_memory_requirements2 606 .get_image_memory_requirements2_khr( 607 device.internal_object(), 608 &infos, 609 &mut output, 610 ); 611 } 612 613 debug_assert!(output.memory_requirements.memory_type_bits != 0); 614 615 let mut out = MemoryRequirements::from(output.memory_requirements); 616 if let Some(output2) = output2 { 617 debug_assert_eq!(output2.requires_dedicated_allocation, 0); 618 out.prefer_dedicated = output2.prefers_dedicated_allocation != 0; 619 } 620 out 621 } else { 622 let mut output: MaybeUninit<ash::vk::MemoryRequirements> = MaybeUninit::uninit(); 623 fns.v1_0.get_image_memory_requirements( 624 device.internal_object(), 625 image, 626 output.as_mut_ptr(), 627 ); 628 let output = output.assume_init(); 629 debug_assert!(output.memory_type_bits != 0); 630 MemoryRequirements::from(output) 631 }; 632 633 let image = UnsafeImage { 634 device: device.clone(), 635 image, 636 usage, 637 format, 638 flags, 639 dimensions, 640 samples: num_samples, 641 mipmaps, 642 format_features, 643 needs_destruction: true, 644 preinitialized_layout, 645 }; 646 647 Ok((image, mem_reqs)) 648 } 649 650 /// Creates an image from a raw handle. The image won't be destroyed. 651 /// 652 /// This function is for example used at the swapchain's initialization. from_raw( device: Arc<Device>, handle: ash::vk::Image, usage: ImageUsage, format: Format, flags: ImageCreateFlags, dimensions: ImageDimensions, samples: SampleCount, mipmaps: u32, ) -> UnsafeImage653 pub unsafe fn from_raw( 654 device: Arc<Device>, 655 handle: ash::vk::Image, 656 usage: ImageUsage, 657 format: Format, 658 flags: ImageCreateFlags, 659 dimensions: ImageDimensions, 660 samples: SampleCount, 661 mipmaps: u32, 662 ) -> UnsafeImage { 663 let format_properties = format.properties(device.physical_device()); 664 665 // TODO: check that usage is correct in regard to `output`? 666 667 UnsafeImage { 668 device: device.clone(), 669 image: handle, 670 usage, 671 format, 672 flags, 673 dimensions, 674 samples, 675 mipmaps, 676 format_features: format_properties.optimal_tiling_features, 677 needs_destruction: false, // TODO: pass as parameter 678 preinitialized_layout: false, // TODO: Maybe this should be passed in? 679 } 680 } 681 bind_memory( &self, memory: &DeviceMemory, offset: DeviceSize, ) -> Result<(), OomError>682 pub unsafe fn bind_memory( 683 &self, 684 memory: &DeviceMemory, 685 offset: DeviceSize, 686 ) -> Result<(), OomError> { 687 let fns = self.device.fns(); 688 689 // We check for correctness in debug mode. 690 debug_assert!({ 691 let mut mem_reqs = MaybeUninit::uninit(); 692 fns.v1_0.get_image_memory_requirements( 693 self.device.internal_object(), 694 self.image, 695 mem_reqs.as_mut_ptr(), 696 ); 697 698 let mem_reqs = mem_reqs.assume_init(); 699 mem_reqs.size <= memory.size() - offset 700 && offset % mem_reqs.alignment == 0 701 && mem_reqs.memory_type_bits & (1 << memory.memory_type().id()) != 0 702 }); 703 704 check_errors(fns.v1_0.bind_image_memory( 705 self.device.internal_object(), 706 self.image, 707 memory.internal_object(), 708 offset, 709 ))?; 710 Ok(()) 711 } 712 713 #[inline] device(&self) -> &Arc<Device>714 pub fn device(&self) -> &Arc<Device> { 715 &self.device 716 } 717 718 #[inline] format(&self) -> Format719 pub fn format(&self) -> Format { 720 self.format 721 } 722 create_flags(&self) -> ImageCreateFlags723 pub fn create_flags(&self) -> ImageCreateFlags { 724 self.flags 725 } 726 727 #[inline] mipmap_levels(&self) -> u32728 pub fn mipmap_levels(&self) -> u32 { 729 self.mipmaps 730 } 731 732 #[inline] dimensions(&self) -> ImageDimensions733 pub fn dimensions(&self) -> ImageDimensions { 734 self.dimensions 735 } 736 737 #[inline] samples(&self) -> SampleCount738 pub fn samples(&self) -> SampleCount { 739 self.samples 740 } 741 742 /// Returns a key unique to each `UnsafeImage`. Can be used for the `conflicts_key` method. 743 #[inline] key(&self) -> u64744 pub fn key(&self) -> u64 { 745 self.image.as_raw() 746 } 747 748 /// Queries the layout of an image in memory. Only valid for images with linear tiling. 749 /// 750 /// This function is only valid for images with a color format. See the other similar functions 751 /// for the other aspects. 752 /// 753 /// The layout is invariant for each image. However it is not cached, as this would waste 754 /// memory in the case of non-linear-tiling images. You are encouraged to store the layout 755 /// somewhere in order to avoid calling this semi-expensive function at every single memory 756 /// access. 757 /// 758 /// Note that while Vulkan allows querying the array layers other than 0, it is redundant as 759 /// you can easily calculate the position of any layer. 760 /// 761 /// # Panic 762 /// 763 /// - Panics if the mipmap level is out of range. 764 /// 765 /// # Safety 766 /// 767 /// - The image must *not* have a depth, stencil or depth-stencil format. 768 /// - The image must have been created with linear tiling. 769 /// 770 #[inline] color_linear_layout(&self, mip_level: u32) -> LinearLayout771 pub unsafe fn color_linear_layout(&self, mip_level: u32) -> LinearLayout { 772 self.linear_layout_impl(mip_level, ImageAspect::Color) 773 } 774 775 /// Same as `color_linear_layout`, except that it retrieves the depth component of the image. 776 /// 777 /// # Panic 778 /// 779 /// - Panics if the mipmap level is out of range. 780 /// 781 /// # Safety 782 /// 783 /// - The image must have a depth or depth-stencil format. 784 /// - The image must have been created with linear tiling. 785 /// 786 #[inline] depth_linear_layout(&self, mip_level: u32) -> LinearLayout787 pub unsafe fn depth_linear_layout(&self, mip_level: u32) -> LinearLayout { 788 self.linear_layout_impl(mip_level, ImageAspect::Depth) 789 } 790 791 /// Same as `color_linear_layout`, except that it retrieves the stencil component of the image. 792 /// 793 /// # Panic 794 /// 795 /// - Panics if the mipmap level is out of range. 796 /// 797 /// # Safety 798 /// 799 /// - The image must have a stencil or depth-stencil format. 800 /// - The image must have been created with linear tiling. 801 /// 802 #[inline] stencil_linear_layout(&self, mip_level: u32) -> LinearLayout803 pub unsafe fn stencil_linear_layout(&self, mip_level: u32) -> LinearLayout { 804 self.linear_layout_impl(mip_level, ImageAspect::Stencil) 805 } 806 807 /// Same as `color_linear_layout`, except that it retrieves layout for the requested ycbcr 808 /// component too if the format is a YcbCr format. 809 /// 810 /// # Panic 811 /// 812 /// - Panics if plane aspect is out of range. 813 /// - Panics if the aspect is not a color or planar aspect. 814 /// - Panics if the number of mipmaps is not 1. 815 #[inline] multiplane_color_layout(&self, aspect: ImageAspect) -> LinearLayout816 pub unsafe fn multiplane_color_layout(&self, aspect: ImageAspect) -> LinearLayout { 817 // This function only supports color and planar aspects currently. 818 assert!(matches!( 819 aspect, 820 ImageAspect::Color | ImageAspect::Plane0 | ImageAspect::Plane1 | ImageAspect::Plane2 821 )); 822 assert!(self.mipmaps == 1); 823 824 if matches!( 825 aspect, 826 ImageAspect::Plane0 | ImageAspect::Plane1 | ImageAspect::Plane2 827 ) { 828 assert_eq!(self.format.ty(), FormatTy::Ycbcr); 829 if aspect == ImageAspect::Plane2 { 830 // Vulkano only supports NV12 and YV12 currently. If that changes, this will too. 831 assert!(self.format == Format::G8B8R8_3PLANE420Unorm); 832 } 833 } 834 835 self.linear_layout_impl(0, aspect) 836 } 837 838 // Implementation of the `*_layout` functions. linear_layout_impl(&self, mip_level: u32, aspect: ImageAspect) -> LinearLayout839 unsafe fn linear_layout_impl(&self, mip_level: u32, aspect: ImageAspect) -> LinearLayout { 840 let fns = self.device.fns(); 841 842 assert!(mip_level < self.mipmaps); 843 844 let subresource = ash::vk::ImageSubresource { 845 aspect_mask: ash::vk::ImageAspectFlags::from(aspect), 846 mip_level: mip_level, 847 array_layer: 0, 848 }; 849 850 let mut out = MaybeUninit::uninit(); 851 fns.v1_0.get_image_subresource_layout( 852 self.device.internal_object(), 853 self.image, 854 &subresource, 855 out.as_mut_ptr(), 856 ); 857 858 let out = out.assume_init(); 859 LinearLayout { 860 offset: out.offset, 861 size: out.size, 862 row_pitch: out.row_pitch, 863 array_pitch: out.array_pitch, 864 depth_pitch: out.depth_pitch, 865 } 866 } 867 868 /// Returns the flags the image was created with. 869 #[inline] flags(&self) -> ImageCreateFlags870 pub fn flags(&self) -> ImageCreateFlags { 871 self.flags 872 } 873 874 /// Returns the features supported by the image's format. 875 #[inline] format_features(&self) -> FormatFeatures876 pub fn format_features(&self) -> FormatFeatures { 877 self.format_features 878 } 879 880 /// Returns the usage the image was created with. 881 #[inline] usage(&self) -> ImageUsage882 pub fn usage(&self) -> ImageUsage { 883 self.usage 884 } 885 886 #[inline] preinitialized_layout(&self) -> bool887 pub fn preinitialized_layout(&self) -> bool { 888 self.preinitialized_layout 889 } 890 } 891 892 unsafe impl VulkanObject for UnsafeImage { 893 type Object = ash::vk::Image; 894 895 #[inline] internal_object(&self) -> ash::vk::Image896 fn internal_object(&self) -> ash::vk::Image { 897 self.image 898 } 899 } 900 901 impl fmt::Debug for UnsafeImage { 902 #[inline] fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error>903 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { 904 write!(fmt, "<Vulkan image {:?}>", self.image) 905 } 906 } 907 908 impl Drop for UnsafeImage { 909 #[inline] drop(&mut self)910 fn drop(&mut self) { 911 if !self.needs_destruction { 912 return; 913 } 914 915 unsafe { 916 let fns = self.device.fns(); 917 fns.v1_0 918 .destroy_image(self.device.internal_object(), self.image, ptr::null()); 919 } 920 } 921 } 922 923 impl PartialEq for UnsafeImage { 924 #[inline] eq(&self, other: &Self) -> bool925 fn eq(&self, other: &Self) -> bool { 926 self.image == other.image && self.device == other.device 927 } 928 } 929 930 impl Eq for UnsafeImage {} 931 932 impl Hash for UnsafeImage { 933 #[inline] hash<H: Hasher>(&self, state: &mut H)934 fn hash<H: Hasher>(&self, state: &mut H) { 935 self.image.hash(state); 936 self.device.hash(state); 937 } 938 } 939 940 /// Error that can happen when creating an instance. 941 #[derive(Clone, Debug, PartialEq, Eq)] 942 pub enum ImageCreationError { 943 /// Allocating memory failed. 944 AllocError(DeviceMemoryAllocError), 945 /// The specified creation flags have requirements (e.g. specific dimension) that were not met. 946 CreationFlagRequirementsNotMet, 947 /// A wrong number of mipmaps was provided. 948 FormatNotSupported, 949 /// The format is supported, but at least one of the requested usages is not supported. 950 InvalidMipmapsCount { 951 obtained: u32, 952 valid_range: Range<u32>, 953 }, 954 /// The requested number of samples is not supported, or is 0. 955 UnsupportedSamplesCount { obtained: SampleCount }, 956 /// The dimensions are too large, or one of the dimensions is 0. 957 UnsupportedDimensions { dimensions: ImageDimensions }, 958 /// The requested format is not supported by the Vulkan implementation. 959 UnsupportedUsage, 960 /// The `shader_storage_image_multisample` feature must be enabled to create such an image. 961 ShaderStorageImageMultisampleFeatureNotEnabled, 962 } 963 964 impl error::Error for ImageCreationError { 965 #[inline] source(&self) -> Option<&(dyn error::Error + 'static)>966 fn source(&self) -> Option<&(dyn error::Error + 'static)> { 967 match *self { 968 ImageCreationError::AllocError(ref err) => Some(err), 969 _ => None, 970 } 971 } 972 } 973 974 impl fmt::Display for ImageCreationError { 975 #[inline] fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error>976 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { 977 write!( 978 fmt, 979 "{}", 980 match *self { 981 ImageCreationError::AllocError(_) => "allocating memory failed", 982 ImageCreationError::CreationFlagRequirementsNotMet => { 983 "the requested creation flags have additional requirements that were not met" 984 } 985 ImageCreationError::FormatNotSupported => { 986 "the requested format is not supported by the Vulkan implementation" 987 } 988 ImageCreationError::InvalidMipmapsCount { .. } => { 989 "a wrong number of mipmaps was provided" 990 } 991 ImageCreationError::UnsupportedSamplesCount { .. } => { 992 "the requested number of samples is not supported, or is 0" 993 } 994 ImageCreationError::UnsupportedDimensions { .. } => { 995 "the dimensions are too large, or one of the dimensions is 0" 996 } 997 ImageCreationError::UnsupportedUsage => { 998 "the format is supported, but at least one of the requested usages is not \ 999 supported" 1000 } 1001 ImageCreationError::ShaderStorageImageMultisampleFeatureNotEnabled => { 1002 "the `shader_storage_image_multisample` feature must be enabled to create such \ 1003 an image" 1004 } 1005 } 1006 ) 1007 } 1008 } 1009 1010 impl From<OomError> for ImageCreationError { 1011 #[inline] from(err: OomError) -> ImageCreationError1012 fn from(err: OomError) -> ImageCreationError { 1013 ImageCreationError::AllocError(DeviceMemoryAllocError::OomError(err)) 1014 } 1015 } 1016 1017 impl From<DeviceMemoryAllocError> for ImageCreationError { 1018 #[inline] from(err: DeviceMemoryAllocError) -> ImageCreationError1019 fn from(err: DeviceMemoryAllocError) -> ImageCreationError { 1020 ImageCreationError::AllocError(err) 1021 } 1022 } 1023 1024 impl From<Error> for ImageCreationError { 1025 #[inline] from(err: Error) -> ImageCreationError1026 fn from(err: Error) -> ImageCreationError { 1027 match err { 1028 err @ Error::OutOfHostMemory => ImageCreationError::AllocError(err.into()), 1029 err @ Error::OutOfDeviceMemory => ImageCreationError::AllocError(err.into()), 1030 _ => panic!("unexpected error: {:?}", err), 1031 } 1032 } 1033 } 1034 1035 /// Describes the memory layout of an image with linear tiling. 1036 /// 1037 /// Obtained by calling `*_linear_layout` on the image. 1038 /// 1039 /// The address of a texel at `(x, y, z, layer)` is `layer * array_pitch + z * depth_pitch + 1040 /// y * row_pitch + x * size_of_each_texel + offset`. `size_of_each_texel` must be determined 1041 /// depending on the format. The same formula applies for compressed formats, except that the 1042 /// coordinates must be in number of blocks. 1043 #[derive(Debug, Copy, Clone, PartialEq, Eq)] 1044 pub struct LinearLayout { 1045 /// Number of bytes from the start of the memory and the start of the queried subresource. 1046 pub offset: DeviceSize, 1047 /// Total number of bytes for the queried subresource. Can be used for a safety check. 1048 pub size: DeviceSize, 1049 /// Number of bytes between two texels or two blocks in adjacent rows. 1050 pub row_pitch: DeviceSize, 1051 /// Number of bytes between two texels or two blocks in adjacent array layers. This value is 1052 /// undefined for images with only one array layer. 1053 pub array_pitch: DeviceSize, 1054 /// Number of bytes between two texels or two blocks in adjacent depth layers. This value is 1055 /// undefined for images that are not three-dimensional. 1056 pub depth_pitch: DeviceSize, 1057 } 1058 1059 #[cfg(test)] 1060 mod tests { 1061 use super::ImageCreateFlags; 1062 use super::ImageCreationError; 1063 use super::ImageUsage; 1064 use super::UnsafeImage; 1065 use crate::format::Format; 1066 use crate::image::ImageDimensions; 1067 use crate::image::SampleCount; 1068 use crate::sync::Sharing; 1069 use std::iter::Empty; 1070 use std::u32; 1071 1072 #[test] create_sampled()1073 fn create_sampled() { 1074 let (device, _) = gfx_dev_and_queue!(); 1075 1076 let usage = ImageUsage { 1077 sampled: true, 1078 ..ImageUsage::none() 1079 }; 1080 1081 let (_img, _) = unsafe { 1082 UnsafeImage::new( 1083 device, 1084 usage, 1085 Format::R8G8B8A8Unorm, 1086 ImageCreateFlags::none(), 1087 ImageDimensions::Dim2d { 1088 width: 32, 1089 height: 32, 1090 array_layers: 1, 1091 }, 1092 SampleCount::Sample1, 1093 1, 1094 Sharing::Exclusive::<Empty<_>>, 1095 false, 1096 false, 1097 ) 1098 } 1099 .unwrap(); 1100 } 1101 1102 #[test] create_transient()1103 fn create_transient() { 1104 let (device, _) = gfx_dev_and_queue!(); 1105 1106 let usage = ImageUsage { 1107 transient_attachment: true, 1108 color_attachment: true, 1109 ..ImageUsage::none() 1110 }; 1111 1112 let (_img, _) = unsafe { 1113 UnsafeImage::new( 1114 device, 1115 usage, 1116 Format::R8G8B8A8Unorm, 1117 ImageCreateFlags::none(), 1118 ImageDimensions::Dim2d { 1119 width: 32, 1120 height: 32, 1121 array_layers: 1, 1122 }, 1123 SampleCount::Sample1, 1124 1, 1125 Sharing::Exclusive::<Empty<_>>, 1126 false, 1127 false, 1128 ) 1129 } 1130 .unwrap(); 1131 } 1132 1133 #[test] zero_mipmap()1134 fn zero_mipmap() { 1135 let (device, _) = gfx_dev_and_queue!(); 1136 1137 let usage = ImageUsage { 1138 sampled: true, 1139 ..ImageUsage::none() 1140 }; 1141 1142 let res = unsafe { 1143 UnsafeImage::new( 1144 device, 1145 usage, 1146 Format::R8G8B8A8Unorm, 1147 ImageCreateFlags::none(), 1148 ImageDimensions::Dim2d { 1149 width: 32, 1150 height: 32, 1151 array_layers: 1, 1152 }, 1153 SampleCount::Sample1, 1154 0, 1155 Sharing::Exclusive::<Empty<_>>, 1156 false, 1157 false, 1158 ) 1159 }; 1160 1161 match res { 1162 Err(ImageCreationError::InvalidMipmapsCount { .. }) => (), 1163 _ => panic!(), 1164 }; 1165 } 1166 1167 #[test] 1168 #[ignore] // TODO: AMD card seems to support a u32::MAX number of mipmaps mipmaps_too_high()1169 fn mipmaps_too_high() { 1170 let (device, _) = gfx_dev_and_queue!(); 1171 1172 let usage = ImageUsage { 1173 sampled: true, 1174 ..ImageUsage::none() 1175 }; 1176 1177 let res = unsafe { 1178 UnsafeImage::new( 1179 device, 1180 usage, 1181 Format::R8G8B8A8Unorm, 1182 ImageCreateFlags::none(), 1183 ImageDimensions::Dim2d { 1184 width: 32, 1185 height: 32, 1186 array_layers: 1, 1187 }, 1188 SampleCount::Sample1, 1189 u32::MAX, 1190 Sharing::Exclusive::<Empty<_>>, 1191 false, 1192 false, 1193 ) 1194 }; 1195 1196 match res { 1197 Err(ImageCreationError::InvalidMipmapsCount { 1198 obtained, 1199 valid_range, 1200 }) => { 1201 assert_eq!(obtained, u32::MAX); 1202 assert_eq!(valid_range.start, 1); 1203 } 1204 _ => panic!(), 1205 }; 1206 } 1207 1208 #[test] shader_storage_image_multisample()1209 fn shader_storage_image_multisample() { 1210 let (device, _) = gfx_dev_and_queue!(); 1211 1212 let usage = ImageUsage { 1213 storage: true, 1214 ..ImageUsage::none() 1215 }; 1216 1217 let res = unsafe { 1218 UnsafeImage::new( 1219 device, 1220 usage, 1221 Format::R8G8B8A8Unorm, 1222 ImageCreateFlags::none(), 1223 ImageDimensions::Dim2d { 1224 width: 32, 1225 height: 32, 1226 array_layers: 1, 1227 }, 1228 SampleCount::Sample2, 1229 1, 1230 Sharing::Exclusive::<Empty<_>>, 1231 false, 1232 false, 1233 ) 1234 }; 1235 1236 match res { 1237 Err(ImageCreationError::ShaderStorageImageMultisampleFeatureNotEnabled) => (), 1238 Err(ImageCreationError::UnsupportedSamplesCount { .. }) => (), // unlikely but possible 1239 _ => panic!(), 1240 }; 1241 } 1242 1243 #[test] compressed_not_color_attachment()1244 fn compressed_not_color_attachment() { 1245 let (device, _) = gfx_dev_and_queue!(); 1246 1247 let usage = ImageUsage { 1248 color_attachment: true, 1249 ..ImageUsage::none() 1250 }; 1251 1252 let res = unsafe { 1253 UnsafeImage::new( 1254 device, 1255 usage, 1256 Format::ASTC_5x4UnormBlock, 1257 ImageCreateFlags::none(), 1258 ImageDimensions::Dim2d { 1259 width: 32, 1260 height: 32, 1261 array_layers: 1, 1262 }, 1263 SampleCount::Sample1, 1264 u32::MAX, 1265 Sharing::Exclusive::<Empty<_>>, 1266 false, 1267 false, 1268 ) 1269 }; 1270 1271 match res { 1272 Err(ImageCreationError::FormatNotSupported) => (), 1273 Err(ImageCreationError::UnsupportedUsage) => (), 1274 _ => panic!(), 1275 }; 1276 } 1277 1278 #[test] transient_forbidden_with_some_usages()1279 fn transient_forbidden_with_some_usages() { 1280 let (device, _) = gfx_dev_and_queue!(); 1281 1282 let usage = ImageUsage { 1283 transient_attachment: true, 1284 sampled: true, 1285 ..ImageUsage::none() 1286 }; 1287 1288 let res = unsafe { 1289 UnsafeImage::new( 1290 device, 1291 usage, 1292 Format::R8G8B8A8Unorm, 1293 ImageCreateFlags::none(), 1294 ImageDimensions::Dim2d { 1295 width: 32, 1296 height: 32, 1297 array_layers: 1, 1298 }, 1299 SampleCount::Sample1, 1300 1, 1301 Sharing::Exclusive::<Empty<_>>, 1302 false, 1303 false, 1304 ) 1305 }; 1306 1307 match res { 1308 Err(ImageCreationError::UnsupportedUsage) => (), 1309 _ => panic!(), 1310 }; 1311 } 1312 1313 #[test] cubecompatible_dims_mismatch()1314 fn cubecompatible_dims_mismatch() { 1315 let (device, _) = gfx_dev_and_queue!(); 1316 1317 let usage = ImageUsage { 1318 sampled: true, 1319 ..ImageUsage::none() 1320 }; 1321 1322 let res = unsafe { 1323 UnsafeImage::new( 1324 device, 1325 usage, 1326 Format::R8G8B8A8Unorm, 1327 ImageCreateFlags { 1328 cube_compatible: true, 1329 ..ImageCreateFlags::none() 1330 }, 1331 ImageDimensions::Dim2d { 1332 width: 32, 1333 height: 64, 1334 array_layers: 1, 1335 }, 1336 SampleCount::Sample1, 1337 1, 1338 Sharing::Exclusive::<Empty<_>>, 1339 false, 1340 false, 1341 ) 1342 }; 1343 1344 match res { 1345 Err(ImageCreationError::CreationFlagRequirementsNotMet) => (), 1346 _ => panic!(), 1347 }; 1348 } 1349 } 1350