• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2016 The vulkano developers
2 // Licensed under the Apache License, Version 2.0
3 // <LICENSE-APACHE or
4 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT
5 // license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
6 // at your option. All files in the project carrying such
7 // notice may not be copied, modified, or distributed except
8 // according to those terms.
9 
10 use crate::device::Device;
11 use crate::format::ClearValue;
12 use crate::format::Format;
13 use crate::format::FormatTy;
14 use crate::image::sys::ImageCreationError;
15 use crate::image::sys::UnsafeImage;
16 use crate::image::traits::ImageAccess;
17 use crate::image::traits::ImageClearValue;
18 use crate::image::traits::ImageContent;
19 use crate::image::ImageCreateFlags;
20 use crate::image::ImageDescriptorLayouts;
21 use crate::image::ImageDimensions;
22 use crate::image::ImageInner;
23 use crate::image::ImageLayout;
24 use crate::image::ImageUsage;
25 use crate::image::SampleCount;
26 use crate::memory::pool::AllocFromRequirementsFilter;
27 use crate::memory::pool::AllocLayout;
28 use crate::memory::pool::MappingRequirement;
29 use crate::memory::pool::MemoryPool;
30 use crate::memory::pool::MemoryPoolAlloc;
31 use crate::memory::pool::PotentialDedicatedAllocation;
32 use crate::memory::pool::StdMemoryPoolAlloc;
33 use crate::memory::DedicatedAlloc;
34 use crate::sync::AccessError;
35 use crate::sync::Sharing;
36 use std::hash::Hash;
37 use std::hash::Hasher;
38 use std::iter::Empty;
39 use std::sync::atomic::AtomicBool;
40 use std::sync::atomic::AtomicUsize;
41 use std::sync::atomic::Ordering;
42 use std::sync::Arc;
43 
44 /// ImageAccess whose purpose is to be used as a framebuffer attachment.
45 ///
46 /// The image is always two-dimensional and has only one mipmap, but it can have any kind of
47 /// format. Trying to use a format that the backend doesn't support for rendering will result in
48 /// an error being returned when creating the image. Once you have an `AttachmentImage`, you are
49 /// guaranteed that you will be able to draw on it.
50 ///
51 /// The template parameter of `AttachmentImage` is a type that describes the format of the image.
52 ///
53 /// # Regular vs transient
54 ///
55 /// Calling `AttachmentImage::new` will create a regular image, while calling
56 /// `AttachmentImage::transient` will create a *transient* image. Transient image are only
57 /// relevant for images that serve as attachments, so `AttachmentImage` is the only type of
58 /// image in vulkano that provides a shortcut for this.
59 ///
60 /// A transient image is a special kind of image whose content is undefined outside of render
61 /// passes. Once you finish drawing, reading from it will returned undefined data (which can be
62 /// either valid or garbage, depending on the implementation).
63 ///
64 /// This gives a hint to the Vulkan implementation that it is possible for the image's content to
65 /// live exclusively in some cache memory, and that no real memory has to be allocated for it.
66 ///
67 /// In other words, if you are going to read from the image after drawing to it, use a regular
68 /// image. If you don't need to read from it (for example if it's some kind of intermediary color,
69 /// or a depth buffer that is only used once) then use a transient image as it may improve
70 /// performance.
71 ///
72 // TODO: forbid reading transient images outside render passes?
73 #[derive(Debug)]
74 pub struct AttachmentImage<A = PotentialDedicatedAllocation<StdMemoryPoolAlloc>> {
75     // Inner implementation.
76     image: UnsafeImage,
77 
78     // Memory used to back the image.
79     memory: A,
80 
81     // Format.
82     format: Format,
83 
84     // Layout to use when the image is used as a framebuffer attachment.
85     // Must be either "depth-stencil optimal" or "color optimal".
86     attachment_layout: ImageLayout,
87 
88     // If true, then the image is in the layout of `attachment_layout` (above). If false, then it
89     // is still `Undefined`.
90     initialized: AtomicBool,
91 
92     // Number of times this image is locked on the GPU side.
93     gpu_lock: AtomicUsize,
94 }
95 
96 impl AttachmentImage {
97     /// Creates a new image with the given dimensions and format.
98     ///
99     /// Returns an error if the dimensions are too large or if the backend doesn't support this
100     /// format as a framebuffer attachment.
101     #[inline]
new( device: Arc<Device>, dimensions: [u32; 2], format: Format, ) -> Result<Arc<AttachmentImage>, ImageCreationError>102     pub fn new(
103         device: Arc<Device>,
104         dimensions: [u32; 2],
105         format: Format,
106     ) -> Result<Arc<AttachmentImage>, ImageCreationError> {
107         AttachmentImage::new_impl(
108             device,
109             dimensions,
110             1,
111             format,
112             ImageUsage::none(),
113             SampleCount::Sample1,
114         )
115     }
116 
117     /// Same as `new`, but creates an image that can be used as an input attachment.
118     ///
119     /// > **Note**: This function is just a convenient shortcut for `with_usage`.
120     #[inline]
input_attachment( device: Arc<Device>, dimensions: [u32; 2], format: Format, ) -> Result<Arc<AttachmentImage>, ImageCreationError>121     pub fn input_attachment(
122         device: Arc<Device>,
123         dimensions: [u32; 2],
124         format: Format,
125     ) -> Result<Arc<AttachmentImage>, ImageCreationError> {
126         let base_usage = ImageUsage {
127             input_attachment: true,
128             ..ImageUsage::none()
129         };
130 
131         AttachmentImage::new_impl(
132             device,
133             dimensions,
134             1,
135             format,
136             base_usage,
137             SampleCount::Sample1,
138         )
139     }
140 
141     /// Same as `new`, but creates a multisampled image.
142     ///
143     /// > **Note**: You can also use this function and pass `1` for the number of samples if you
144     /// > want a regular image.
145     #[inline]
multisampled( device: Arc<Device>, dimensions: [u32; 2], samples: SampleCount, format: Format, ) -> Result<Arc<AttachmentImage>, ImageCreationError>146     pub fn multisampled(
147         device: Arc<Device>,
148         dimensions: [u32; 2],
149         samples: SampleCount,
150         format: Format,
151     ) -> Result<Arc<AttachmentImage>, ImageCreationError> {
152         AttachmentImage::new_impl(device, dimensions, 1, format, ImageUsage::none(), samples)
153     }
154 
155     /// Same as `multisampled`, but creates an image that can be used as an input attachment.
156     ///
157     /// > **Note**: This function is just a convenient shortcut for `multisampled_with_usage`.
158     #[inline]
multisampled_input_attachment( device: Arc<Device>, dimensions: [u32; 2], samples: SampleCount, format: Format, ) -> Result<Arc<AttachmentImage>, ImageCreationError>159     pub fn multisampled_input_attachment(
160         device: Arc<Device>,
161         dimensions: [u32; 2],
162         samples: SampleCount,
163         format: Format,
164     ) -> Result<Arc<AttachmentImage>, ImageCreationError> {
165         let base_usage = ImageUsage {
166             input_attachment: true,
167             ..ImageUsage::none()
168         };
169 
170         AttachmentImage::new_impl(device, dimensions, 1, format, base_usage, samples)
171     }
172 
173     /// Same as `new`, but lets you specify additional usages.
174     ///
175     /// The `color_attachment` or `depth_stencil_attachment` usages are automatically added based
176     /// on the format of the usage. Therefore the `usage` parameter allows you specify usages in
177     /// addition to these two.
178     #[inline]
with_usage( device: Arc<Device>, dimensions: [u32; 2], format: Format, usage: ImageUsage, ) -> Result<Arc<AttachmentImage>, ImageCreationError>179     pub fn with_usage(
180         device: Arc<Device>,
181         dimensions: [u32; 2],
182         format: Format,
183         usage: ImageUsage,
184     ) -> Result<Arc<AttachmentImage>, ImageCreationError> {
185         AttachmentImage::new_impl(device, dimensions, 1, format, usage, SampleCount::Sample1)
186     }
187 
188     /// Same as `with_usage`, but creates a multisampled image.
189     ///
190     /// > **Note**: You can also use this function and pass `1` for the number of samples if you
191     /// > want a regular image.
192     #[inline]
multisampled_with_usage( device: Arc<Device>, dimensions: [u32; 2], samples: SampleCount, format: Format, usage: ImageUsage, ) -> Result<Arc<AttachmentImage>, ImageCreationError>193     pub fn multisampled_with_usage(
194         device: Arc<Device>,
195         dimensions: [u32; 2],
196         samples: SampleCount,
197         format: Format,
198         usage: ImageUsage,
199     ) -> Result<Arc<AttachmentImage>, ImageCreationError> {
200         AttachmentImage::new_impl(device, dimensions, 1, format, usage, samples)
201     }
202 
203     /// Same as `multisampled_with_usage`, but creates an image with multiple layers.
204     ///
205     /// > **Note**: You can also use this function and pass `1` for the number of layers if you
206     /// > want a regular image.
207     #[inline]
multisampled_with_usage_with_layers( device: Arc<Device>, dimensions: [u32; 2], array_layers: u32, samples: SampleCount, format: Format, usage: ImageUsage, ) -> Result<Arc<AttachmentImage>, ImageCreationError>208     pub fn multisampled_with_usage_with_layers(
209         device: Arc<Device>,
210         dimensions: [u32; 2],
211         array_layers: u32,
212         samples: SampleCount,
213         format: Format,
214         usage: ImageUsage,
215     ) -> Result<Arc<AttachmentImage>, ImageCreationError> {
216         AttachmentImage::new_impl(device, dimensions, array_layers, format, usage, samples)
217     }
218 
219     /// Same as `new`, except that the image can later be sampled.
220     ///
221     /// > **Note**: This function is just a convenient shortcut for `with_usage`.
222     #[inline]
sampled( device: Arc<Device>, dimensions: [u32; 2], format: Format, ) -> Result<Arc<AttachmentImage>, ImageCreationError>223     pub fn sampled(
224         device: Arc<Device>,
225         dimensions: [u32; 2],
226         format: Format,
227     ) -> Result<Arc<AttachmentImage>, ImageCreationError> {
228         let base_usage = ImageUsage {
229             sampled: true,
230             ..ImageUsage::none()
231         };
232 
233         AttachmentImage::new_impl(
234             device,
235             dimensions,
236             1,
237             format,
238             base_usage,
239             SampleCount::Sample1,
240         )
241     }
242 
243     /// Same as `sampled`, except that the image can be used as an input attachment.
244     ///
245     /// > **Note**: This function is just a convenient shortcut for `with_usage`.
246     #[inline]
sampled_input_attachment( device: Arc<Device>, dimensions: [u32; 2], format: Format, ) -> Result<Arc<AttachmentImage>, ImageCreationError>247     pub fn sampled_input_attachment(
248         device: Arc<Device>,
249         dimensions: [u32; 2],
250         format: Format,
251     ) -> Result<Arc<AttachmentImage>, ImageCreationError> {
252         let base_usage = ImageUsage {
253             sampled: true,
254             input_attachment: true,
255             ..ImageUsage::none()
256         };
257 
258         AttachmentImage::new_impl(
259             device,
260             dimensions,
261             1,
262             format,
263             base_usage,
264             SampleCount::Sample1,
265         )
266     }
267 
268     /// Same as `sampled`, but creates a multisampled image.
269     ///
270     /// > **Note**: You can also use this function and pass `1` for the number of samples if you
271     /// > want a regular image.
272     ///
273     /// > **Note**: This function is just a convenient shortcut for `multisampled_with_usage`.
274     #[inline]
sampled_multisampled( device: Arc<Device>, dimensions: [u32; 2], samples: SampleCount, format: Format, ) -> Result<Arc<AttachmentImage>, ImageCreationError>275     pub fn sampled_multisampled(
276         device: Arc<Device>,
277         dimensions: [u32; 2],
278         samples: SampleCount,
279         format: Format,
280     ) -> Result<Arc<AttachmentImage>, ImageCreationError> {
281         let base_usage = ImageUsage {
282             sampled: true,
283             ..ImageUsage::none()
284         };
285 
286         AttachmentImage::new_impl(device, dimensions, 1, format, base_usage, samples)
287     }
288 
289     /// Same as `sampled_multisampled`, but creates an image that can be used as an input
290     /// attachment.
291     ///
292     /// > **Note**: This function is just a convenient shortcut for `multisampled_with_usage`.
293     #[inline]
sampled_multisampled_input_attachment( device: Arc<Device>, dimensions: [u32; 2], samples: SampleCount, format: Format, ) -> Result<Arc<AttachmentImage>, ImageCreationError>294     pub fn sampled_multisampled_input_attachment(
295         device: Arc<Device>,
296         dimensions: [u32; 2],
297         samples: SampleCount,
298         format: Format,
299     ) -> Result<Arc<AttachmentImage>, ImageCreationError> {
300         let base_usage = ImageUsage {
301             sampled: true,
302             input_attachment: true,
303             ..ImageUsage::none()
304         };
305 
306         AttachmentImage::new_impl(device, dimensions, 1, format, base_usage, samples)
307     }
308 
309     /// Same as `new`, except that the image will be transient.
310     ///
311     /// A transient image is special because its content is undefined outside of a render pass.
312     /// This means that the implementation has the possibility to not allocate any memory for it.
313     ///
314     /// > **Note**: This function is just a convenient shortcut for `with_usage`.
315     #[inline]
transient( device: Arc<Device>, dimensions: [u32; 2], format: Format, ) -> Result<Arc<AttachmentImage>, ImageCreationError>316     pub fn transient(
317         device: Arc<Device>,
318         dimensions: [u32; 2],
319         format: Format,
320     ) -> Result<Arc<AttachmentImage>, ImageCreationError> {
321         let base_usage = ImageUsage {
322             transient_attachment: true,
323             ..ImageUsage::none()
324         };
325 
326         AttachmentImage::new_impl(
327             device,
328             dimensions,
329             1,
330             format,
331             base_usage,
332             SampleCount::Sample1,
333         )
334     }
335 
336     /// Same as `transient`, except that the image can be used as an input attachment.
337     ///
338     /// > **Note**: This function is just a convenient shortcut for `with_usage`.
339     #[inline]
transient_input_attachment( device: Arc<Device>, dimensions: [u32; 2], format: Format, ) -> Result<Arc<AttachmentImage>, ImageCreationError>340     pub fn transient_input_attachment(
341         device: Arc<Device>,
342         dimensions: [u32; 2],
343         format: Format,
344     ) -> Result<Arc<AttachmentImage>, ImageCreationError> {
345         let base_usage = ImageUsage {
346             transient_attachment: true,
347             input_attachment: true,
348             ..ImageUsage::none()
349         };
350 
351         AttachmentImage::new_impl(
352             device,
353             dimensions,
354             1,
355             format,
356             base_usage,
357             SampleCount::Sample1,
358         )
359     }
360 
361     /// Same as `transient`, but creates a multisampled image.
362     ///
363     /// > **Note**: You can also use this function and pass `1` for the number of samples if you
364     /// > want a regular image.
365     ///
366     /// > **Note**: This function is just a convenient shortcut for `multisampled_with_usage`.
367     #[inline]
transient_multisampled( device: Arc<Device>, dimensions: [u32; 2], samples: SampleCount, format: Format, ) -> Result<Arc<AttachmentImage>, ImageCreationError>368     pub fn transient_multisampled(
369         device: Arc<Device>,
370         dimensions: [u32; 2],
371         samples: SampleCount,
372         format: Format,
373     ) -> Result<Arc<AttachmentImage>, ImageCreationError> {
374         let base_usage = ImageUsage {
375             transient_attachment: true,
376             ..ImageUsage::none()
377         };
378 
379         AttachmentImage::new_impl(device, dimensions, 1, format, base_usage, samples)
380     }
381 
382     /// Same as `transient_multisampled`, but creates an image that can be used as an input
383     /// attachment.
384     ///
385     /// > **Note**: This function is just a convenient shortcut for `multisampled_with_usage`.
386     #[inline]
transient_multisampled_input_attachment( device: Arc<Device>, dimensions: [u32; 2], samples: SampleCount, format: Format, ) -> Result<Arc<AttachmentImage>, ImageCreationError>387     pub fn transient_multisampled_input_attachment(
388         device: Arc<Device>,
389         dimensions: [u32; 2],
390         samples: SampleCount,
391         format: Format,
392     ) -> Result<Arc<AttachmentImage>, ImageCreationError> {
393         let base_usage = ImageUsage {
394             transient_attachment: true,
395             input_attachment: true,
396             ..ImageUsage::none()
397         };
398 
399         AttachmentImage::new_impl(device, dimensions, 1, format, base_usage, samples)
400     }
401 
402     // All constructors dispatch to this one.
new_impl( device: Arc<Device>, dimensions: [u32; 2], array_layers: u32, format: Format, base_usage: ImageUsage, samples: SampleCount, ) -> Result<Arc<AttachmentImage>, ImageCreationError>403     fn new_impl(
404         device: Arc<Device>,
405         dimensions: [u32; 2],
406         array_layers: u32,
407         format: Format,
408         base_usage: ImageUsage,
409         samples: SampleCount,
410     ) -> Result<Arc<AttachmentImage>, ImageCreationError> {
411         // TODO: check dimensions against the max_framebuffer_width/height/layers limits
412 
413         let is_depth = match format.ty() {
414             FormatTy::Depth => true,
415             FormatTy::DepthStencil => true,
416             FormatTy::Stencil => true,
417             FormatTy::Compressed => panic!(),
418             _ => false,
419         };
420 
421         let usage = ImageUsage {
422             color_attachment: !is_depth,
423             depth_stencil_attachment: is_depth,
424             ..base_usage
425         };
426 
427         let (image, mem_reqs) = unsafe {
428             let dims = ImageDimensions::Dim2d {
429                 width: dimensions[0],
430                 height: dimensions[1],
431                 array_layers,
432             };
433 
434             UnsafeImage::new(
435                 device.clone(),
436                 usage,
437                 format,
438                 ImageCreateFlags::none(),
439                 dims,
440                 samples,
441                 1,
442                 Sharing::Exclusive::<Empty<u32>>,
443                 false,
444                 false,
445             )?
446         };
447 
448         let memory = MemoryPool::alloc_from_requirements(
449             &Device::standard_pool(&device),
450             &mem_reqs,
451             AllocLayout::Optimal,
452             MappingRequirement::DoNotMap,
453             DedicatedAlloc::Image(&image),
454             |t| {
455                 if t.is_device_local() {
456                     AllocFromRequirementsFilter::Preferred
457                 } else {
458                     AllocFromRequirementsFilter::Allowed
459                 }
460             },
461         )?;
462         debug_assert!((memory.offset() % mem_reqs.alignment) == 0);
463         unsafe {
464             image.bind_memory(memory.memory(), memory.offset())?;
465         }
466 
467         Ok(Arc::new(AttachmentImage {
468             image,
469             memory,
470             format,
471             attachment_layout: if is_depth {
472                 ImageLayout::DepthStencilAttachmentOptimal
473             } else {
474                 ImageLayout::ColorAttachmentOptimal
475             },
476             initialized: AtomicBool::new(false),
477             gpu_lock: AtomicUsize::new(0),
478         }))
479     }
480 }
481 
482 impl<A> AttachmentImage<A> {
483     /// Returns the dimensions of the image.
484     #[inline]
dimensions(&self) -> [u32; 2]485     pub fn dimensions(&self) -> [u32; 2] {
486         let dims = self.image.dimensions();
487         [dims.width(), dims.height()]
488     }
489 }
490 
491 unsafe impl<A> ImageAccess for AttachmentImage<A> {
492     #[inline]
inner(&self) -> ImageInner493     fn inner(&self) -> ImageInner {
494         ImageInner {
495             image: &self.image,
496             first_layer: 0,
497             num_layers: self.image.dimensions().array_layers() as usize,
498             first_mipmap_level: 0,
499             num_mipmap_levels: 1,
500         }
501     }
502 
503     #[inline]
initial_layout_requirement(&self) -> ImageLayout504     fn initial_layout_requirement(&self) -> ImageLayout {
505         self.attachment_layout
506     }
507 
508     #[inline]
final_layout_requirement(&self) -> ImageLayout509     fn final_layout_requirement(&self) -> ImageLayout {
510         self.attachment_layout
511     }
512 
513     #[inline]
descriptor_layouts(&self) -> Option<ImageDescriptorLayouts>514     fn descriptor_layouts(&self) -> Option<ImageDescriptorLayouts> {
515         Some(ImageDescriptorLayouts {
516             storage_image: ImageLayout::ShaderReadOnlyOptimal,
517             combined_image_sampler: ImageLayout::ShaderReadOnlyOptimal,
518             sampled_image: ImageLayout::ShaderReadOnlyOptimal,
519             input_attachment: ImageLayout::ShaderReadOnlyOptimal,
520         })
521     }
522 
523     #[inline]
conflict_key(&self) -> u64524     fn conflict_key(&self) -> u64 {
525         self.image.key()
526     }
527 
528     #[inline]
try_gpu_lock( &self, _: bool, uninitialized_safe: bool, expected_layout: ImageLayout, ) -> Result<(), AccessError>529     fn try_gpu_lock(
530         &self,
531         _: bool,
532         uninitialized_safe: bool,
533         expected_layout: ImageLayout,
534     ) -> Result<(), AccessError> {
535         if expected_layout != self.attachment_layout && expected_layout != ImageLayout::Undefined {
536             if self.initialized.load(Ordering::SeqCst) {
537                 return Err(AccessError::UnexpectedImageLayout {
538                     requested: expected_layout,
539                     allowed: self.attachment_layout,
540                 });
541             } else {
542                 return Err(AccessError::UnexpectedImageLayout {
543                     requested: expected_layout,
544                     allowed: ImageLayout::Undefined,
545                 });
546             }
547         }
548 
549         if !uninitialized_safe && expected_layout != ImageLayout::Undefined {
550             if !self.initialized.load(Ordering::SeqCst) {
551                 return Err(AccessError::ImageNotInitialized {
552                     requested: expected_layout,
553                 });
554             }
555         }
556 
557         if self
558             .gpu_lock
559             .compare_exchange(0, 1, Ordering::SeqCst, Ordering::SeqCst)
560             .unwrap_or_else(|e| e)
561             == 0
562         {
563             Ok(())
564         } else {
565             Err(AccessError::AlreadyInUse)
566         }
567     }
568 
569     #[inline]
increase_gpu_lock(&self)570     unsafe fn increase_gpu_lock(&self) {
571         let val = self.gpu_lock.fetch_add(1, Ordering::SeqCst);
572         debug_assert!(val >= 1);
573     }
574 
575     #[inline]
unlock(&self, new_layout: Option<ImageLayout>)576     unsafe fn unlock(&self, new_layout: Option<ImageLayout>) {
577         if let Some(new_layout) = new_layout {
578             debug_assert_eq!(new_layout, self.attachment_layout);
579             self.initialized.store(true, Ordering::SeqCst);
580         }
581 
582         let prev_val = self.gpu_lock.fetch_sub(1, Ordering::SeqCst);
583         debug_assert!(prev_val >= 1);
584     }
585 
586     #[inline]
layout_initialized(&self)587     unsafe fn layout_initialized(&self) {
588         self.initialized.store(true, Ordering::SeqCst);
589     }
590 
591     #[inline]
is_layout_initialized(&self) -> bool592     fn is_layout_initialized(&self) -> bool {
593         self.initialized.load(Ordering::SeqCst)
594     }
595 
596     #[inline]
current_miplevels_access(&self) -> std::ops::Range<u32>597     fn current_miplevels_access(&self) -> std::ops::Range<u32> {
598         0..self.mipmap_levels()
599     }
600 
601     #[inline]
current_layer_levels_access(&self) -> std::ops::Range<u32>602     fn current_layer_levels_access(&self) -> std::ops::Range<u32> {
603         0..1
604     }
605 }
606 
607 unsafe impl<A> ImageClearValue<ClearValue> for Arc<AttachmentImage<A>> {
608     #[inline]
decode(&self, value: ClearValue) -> Option<ClearValue>609     fn decode(&self, value: ClearValue) -> Option<ClearValue> {
610         Some(self.format.decode_clear_value(value))
611     }
612 }
613 
614 unsafe impl<P, A> ImageContent<P> for Arc<AttachmentImage<A>> {
615     #[inline]
matches_format(&self) -> bool616     fn matches_format(&self) -> bool {
617         true // FIXME:
618     }
619 }
620 
621 impl<A> PartialEq for AttachmentImage<A> {
622     #[inline]
eq(&self, other: &Self) -> bool623     fn eq(&self, other: &Self) -> bool {
624         ImageAccess::inner(self) == ImageAccess::inner(other)
625     }
626 }
627 
628 impl<A> Eq for AttachmentImage<A> {}
629 
630 impl<A> Hash for AttachmentImage<A> {
631     #[inline]
hash<H: Hasher>(&self, state: &mut H)632     fn hash<H: Hasher>(&self, state: &mut H) {
633         ImageAccess::inner(self).hash(state);
634     }
635 }
636 
637 #[cfg(test)]
638 mod tests {
639     use super::AttachmentImage;
640     use crate::format::Format;
641 
642     #[test]
create_regular()643     fn create_regular() {
644         let (device, _) = gfx_dev_and_queue!();
645         let _img = AttachmentImage::new(device, [32, 32], Format::R8G8B8A8Unorm).unwrap();
646     }
647 
648     #[test]
create_transient()649     fn create_transient() {
650         let (device, _) = gfx_dev_and_queue!();
651         let _img = AttachmentImage::transient(device, [32, 32], Format::R8G8B8A8Unorm).unwrap();
652     }
653 
654     #[test]
d16_unorm_always_supported()655     fn d16_unorm_always_supported() {
656         let (device, _) = gfx_dev_and_queue!();
657         let _img = AttachmentImage::new(device, [32, 32], Format::D16Unorm).unwrap();
658     }
659 }
660