• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2016 The vulkano developers
2 // Licensed under the Apache License, Version 2.0
3 // <LICENSE-APACHE or
4 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT
5 // license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
6 // at your option. All files in the project carrying such
7 // notice may not be copied, modified, or distributed except
8 // according to those terms.
9 
10 //! 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