• 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::physical::QueueFamily;
11 use crate::device::Device;
12 use crate::format::ClearValue;
13 use crate::format::Format;
14 use crate::format::FormatTy;
15 use crate::image::sys::ImageCreationError;
16 use crate::image::sys::UnsafeImage;
17 use crate::image::traits::ImageAccess;
18 use crate::image::traits::ImageClearValue;
19 use crate::image::traits::ImageContent;
20 use crate::image::ImageCreateFlags;
21 use crate::image::ImageDescriptorLayouts;
22 use crate::image::ImageDimensions;
23 use crate::image::ImageInner;
24 use crate::image::ImageLayout;
25 use crate::image::ImageUsage;
26 use crate::image::SampleCount;
27 use crate::memory::pool::AllocFromRequirementsFilter;
28 use crate::memory::pool::AllocLayout;
29 use crate::memory::pool::MappingRequirement;
30 use crate::memory::pool::MemoryPool;
31 use crate::memory::pool::MemoryPoolAlloc;
32 use crate::memory::pool::PotentialDedicatedAllocation;
33 use crate::memory::pool::StdMemoryPool;
34 use crate::memory::DedicatedAlloc;
35 use crate::sync::AccessError;
36 use crate::sync::Sharing;
37 use smallvec::SmallVec;
38 use std::hash::Hash;
39 use std::hash::Hasher;
40 use std::sync::atomic::AtomicUsize;
41 use std::sync::atomic::Ordering;
42 use std::sync::Arc;
43 
44 /// General-purpose image in device memory. Can be used for any usage, but will be slower than a
45 /// specialized image.
46 #[derive(Debug)]
47 pub struct StorageImage<A = Arc<StdMemoryPool>>
48 where
49     A: MemoryPool,
50 {
51     // Inner implementation.
52     image: UnsafeImage,
53 
54     // Memory used to back the image.
55     memory: PotentialDedicatedAllocation<A::Alloc>,
56 
57     // Dimensions of the image.
58     dimensions: ImageDimensions,
59 
60     // Format.
61     format: Format,
62 
63     // Queue families allowed to access this image.
64     queue_families: SmallVec<[u32; 4]>,
65 
66     // Number of times this image is locked on the GPU side.
67     gpu_lock: AtomicUsize,
68 }
69 
70 impl StorageImage {
71     /// Creates a new image with the given dimensions and format.
72     #[inline]
new<'a, I>( device: Arc<Device>, dimensions: ImageDimensions, format: Format, queue_families: I, ) -> Result<Arc<StorageImage>, ImageCreationError> where I: IntoIterator<Item = QueueFamily<'a>>,73     pub fn new<'a, I>(
74         device: Arc<Device>,
75         dimensions: ImageDimensions,
76         format: Format,
77         queue_families: I,
78     ) -> Result<Arc<StorageImage>, ImageCreationError>
79     where
80         I: IntoIterator<Item = QueueFamily<'a>>,
81     {
82         let is_depth = match format.ty() {
83             FormatTy::Depth => true,
84             FormatTy::DepthStencil => true,
85             FormatTy::Stencil => true,
86             FormatTy::Compressed => panic!(),
87             _ => false,
88         };
89 
90         let usage = ImageUsage {
91             transfer_source: true,
92             transfer_destination: true,
93             sampled: true,
94             storage: true,
95             color_attachment: !is_depth,
96             depth_stencil_attachment: is_depth,
97             input_attachment: true,
98             transient_attachment: false,
99         };
100         let flags = ImageCreateFlags::none();
101 
102         StorageImage::with_usage(device, dimensions, format, usage, flags, queue_families)
103     }
104 
105     /// Same as `new`, but allows specifying the usage.
with_usage<'a, I>( device: Arc<Device>, dimensions: ImageDimensions, format: Format, usage: ImageUsage, flags: ImageCreateFlags, queue_families: I, ) -> Result<Arc<StorageImage>, ImageCreationError> where I: IntoIterator<Item = QueueFamily<'a>>,106     pub fn with_usage<'a, I>(
107         device: Arc<Device>,
108         dimensions: ImageDimensions,
109         format: Format,
110         usage: ImageUsage,
111         flags: ImageCreateFlags,
112         queue_families: I,
113     ) -> Result<Arc<StorageImage>, ImageCreationError>
114     where
115         I: IntoIterator<Item = QueueFamily<'a>>,
116     {
117         let queue_families = queue_families
118             .into_iter()
119             .map(|f| f.id())
120             .collect::<SmallVec<[u32; 4]>>();
121 
122         let (image, mem_reqs) = unsafe {
123             let sharing = if queue_families.len() >= 2 {
124                 Sharing::Concurrent(queue_families.iter().cloned())
125             } else {
126                 Sharing::Exclusive
127             };
128 
129             UnsafeImage::new(
130                 device.clone(),
131                 usage,
132                 format,
133                 flags,
134                 dimensions,
135                 SampleCount::Sample1,
136                 1,
137                 sharing,
138                 false,
139                 false,
140             )?
141         };
142 
143         let memory = MemoryPool::alloc_from_requirements(
144             &Device::standard_pool(&device),
145             &mem_reqs,
146             AllocLayout::Optimal,
147             MappingRequirement::DoNotMap,
148             DedicatedAlloc::Image(&image),
149             |t| {
150                 if t.is_device_local() {
151                     AllocFromRequirementsFilter::Preferred
152                 } else {
153                     AllocFromRequirementsFilter::Allowed
154                 }
155             },
156         )?;
157         debug_assert!((memory.offset() % mem_reqs.alignment) == 0);
158         unsafe {
159             image.bind_memory(memory.memory(), memory.offset())?;
160         }
161 
162         Ok(Arc::new(StorageImage {
163             image,
164             memory,
165             dimensions,
166             format,
167             queue_families,
168             gpu_lock: AtomicUsize::new(0),
169         }))
170     }
171 }
172 
173 impl<A> StorageImage<A>
174 where
175     A: MemoryPool,
176 {
177     /// Returns the dimensions of the image.
178     #[inline]
dimensions(&self) -> ImageDimensions179     pub fn dimensions(&self) -> ImageDimensions {
180         self.dimensions
181     }
182 }
183 
184 unsafe impl<A> ImageAccess for StorageImage<A>
185 where
186     A: MemoryPool,
187 {
188     #[inline]
inner(&self) -> ImageInner189     fn inner(&self) -> ImageInner {
190         ImageInner {
191             image: &self.image,
192             first_layer: 0,
193             num_layers: self.dimensions.array_layers() as usize,
194             first_mipmap_level: 0,
195             num_mipmap_levels: 1,
196         }
197     }
198 
199     #[inline]
initial_layout_requirement(&self) -> ImageLayout200     fn initial_layout_requirement(&self) -> ImageLayout {
201         ImageLayout::General
202     }
203 
204     #[inline]
final_layout_requirement(&self) -> ImageLayout205     fn final_layout_requirement(&self) -> ImageLayout {
206         ImageLayout::General
207     }
208 
209     #[inline]
descriptor_layouts(&self) -> Option<ImageDescriptorLayouts>210     fn descriptor_layouts(&self) -> Option<ImageDescriptorLayouts> {
211         Some(ImageDescriptorLayouts {
212             storage_image: ImageLayout::General,
213             combined_image_sampler: ImageLayout::General,
214             sampled_image: ImageLayout::General,
215             input_attachment: ImageLayout::General,
216         })
217     }
218 
219     #[inline]
conflict_key(&self) -> u64220     fn conflict_key(&self) -> u64 {
221         self.image.key()
222     }
223 
224     #[inline]
try_gpu_lock( &self, _: bool, uninitialized_safe: bool, expected_layout: ImageLayout, ) -> Result<(), AccessError>225     fn try_gpu_lock(
226         &self,
227         _: bool,
228         uninitialized_safe: bool,
229         expected_layout: ImageLayout,
230     ) -> Result<(), AccessError> {
231         // TODO: handle initial layout transition
232         if expected_layout != ImageLayout::General && expected_layout != ImageLayout::Undefined {
233             return Err(AccessError::UnexpectedImageLayout {
234                 requested: expected_layout,
235                 allowed: ImageLayout::General,
236             });
237         }
238 
239         let val = self
240             .gpu_lock
241             .compare_exchange(0, 1, Ordering::SeqCst, Ordering::SeqCst)
242             .unwrap_or_else(|e| e);
243         if val == 0 {
244             Ok(())
245         } else {
246             Err(AccessError::AlreadyInUse)
247         }
248     }
249 
250     #[inline]
increase_gpu_lock(&self)251     unsafe fn increase_gpu_lock(&self) {
252         let val = self.gpu_lock.fetch_add(1, Ordering::SeqCst);
253         debug_assert!(val >= 1);
254     }
255 
256     #[inline]
unlock(&self, new_layout: Option<ImageLayout>)257     unsafe fn unlock(&self, new_layout: Option<ImageLayout>) {
258         assert!(new_layout.is_none() || new_layout == Some(ImageLayout::General));
259         self.gpu_lock.fetch_sub(1, Ordering::SeqCst);
260     }
261 
262     #[inline]
current_miplevels_access(&self) -> std::ops::Range<u32>263     fn current_miplevels_access(&self) -> std::ops::Range<u32> {
264         0..self.mipmap_levels()
265     }
266 
267     #[inline]
current_layer_levels_access(&self) -> std::ops::Range<u32>268     fn current_layer_levels_access(&self) -> std::ops::Range<u32> {
269         0..self.dimensions().array_layers()
270     }
271 }
272 
273 unsafe impl<A> ImageClearValue<ClearValue> for StorageImage<A>
274 where
275     A: MemoryPool,
276 {
277     #[inline]
decode(&self, value: ClearValue) -> Option<ClearValue>278     fn decode(&self, value: ClearValue) -> Option<ClearValue> {
279         Some(self.format.decode_clear_value(value))
280     }
281 }
282 
283 unsafe impl<P, A> ImageContent<P> for StorageImage<A>
284 where
285     A: MemoryPool,
286 {
287     #[inline]
matches_format(&self) -> bool288     fn matches_format(&self) -> bool {
289         true // FIXME:
290     }
291 }
292 
293 impl<A> PartialEq for StorageImage<A>
294 where
295     A: MemoryPool,
296 {
297     #[inline]
eq(&self, other: &Self) -> bool298     fn eq(&self, other: &Self) -> bool {
299         ImageAccess::inner(self) == ImageAccess::inner(other)
300     }
301 }
302 
303 impl<A> Eq for StorageImage<A> where A: MemoryPool {}
304 
305 impl<A> Hash for StorageImage<A>
306 where
307     A: MemoryPool,
308 {
309     #[inline]
hash<H: Hasher>(&self, state: &mut H)310     fn hash<H: Hasher>(&self, state: &mut H) {
311         ImageAccess::inner(self).hash(state);
312     }
313 }
314 
315 #[cfg(test)]
316 mod tests {
317     use super::StorageImage;
318     use crate::format::Format;
319     use crate::image::ImageDimensions;
320 
321     #[test]
create()322     fn create() {
323         let (device, queue) = gfx_dev_and_queue!();
324         let _img = StorageImage::new(
325             device,
326             ImageDimensions::Dim2d {
327                 width: 32,
328                 height: 32,
329                 array_layers: 1,
330             },
331             Format::R8G8B8A8Unorm,
332             Some(queue.family()),
333         )
334         .unwrap();
335     }
336 }
337