• 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::format::ClearValue;
11 use crate::format::Format;
12 use crate::format::FormatTy;
13 use crate::image::sys::UnsafeImage;
14 use crate::image::ImageDescriptorLayouts;
15 use crate::image::ImageDimensions;
16 use crate::image::ImageLayout;
17 use crate::image::SampleCount;
18 use crate::sync::AccessError;
19 use crate::SafeDeref;
20 use std::hash::Hash;
21 use std::hash::Hasher;
22 
23 /// Trait for types that represent the way a GPU can access an image.
24 pub unsafe trait ImageAccess {
25     /// Returns the inner unsafe image object used by this image.
inner(&self) -> ImageInner26     fn inner(&self) -> ImageInner;
27 
28     /// Returns the format of this image.
29     #[inline]
format(&self) -> Format30     fn format(&self) -> Format {
31         self.inner().image.format()
32     }
33 
34     /// Returns true if the image is a color image.
35     #[inline]
has_color(&self) -> bool36     fn has_color(&self) -> bool {
37         matches!(
38             self.format().ty(),
39             FormatTy::Float | FormatTy::Uint | FormatTy::Sint | FormatTy::Compressed
40         )
41     }
42 
43     /// Returns true if the image has a depth component. In other words, if it is a depth or a
44     /// depth-stencil format.
45     #[inline]
has_depth(&self) -> bool46     fn has_depth(&self) -> bool {
47         matches!(self.format().ty(), FormatTy::Depth | FormatTy::DepthStencil)
48     }
49 
50     /// Returns true if the image has a stencil component. In other words, if it is a stencil or a
51     /// depth-stencil format.
52     #[inline]
has_stencil(&self) -> bool53     fn has_stencil(&self) -> bool {
54         matches!(
55             self.format().ty(),
56             FormatTy::Stencil | FormatTy::DepthStencil
57         )
58     }
59 
60     /// Returns the number of mipmap levels of this image.
61     #[inline]
mipmap_levels(&self) -> u3262     fn mipmap_levels(&self) -> u32 {
63         // TODO: not necessarily correct because of the new inner() design?
64         self.inner().image.mipmap_levels()
65     }
66 
67     /// Returns the number of samples of this image.
68     #[inline]
samples(&self) -> SampleCount69     fn samples(&self) -> SampleCount {
70         self.inner().image.samples()
71     }
72 
73     /// Returns the dimensions of the image.
74     #[inline]
dimensions(&self) -> ImageDimensions75     fn dimensions(&self) -> ImageDimensions {
76         // TODO: not necessarily correct because of the new inner() design?
77         self.inner().image.dimensions()
78     }
79 
80     /// Returns true if the image can be used as a source for blits.
81     #[inline]
supports_blit_source(&self) -> bool82     fn supports_blit_source(&self) -> bool {
83         self.inner().image.format_features().blit_src
84     }
85 
86     /// Returns true if the image can be used as a destination for blits.
87     #[inline]
supports_blit_destination(&self) -> bool88     fn supports_blit_destination(&self) -> bool {
89         self.inner().image.format_features().blit_dst
90     }
91 
92     /// When images are created their memory layout is initially `Undefined` or `Preinitialized`.
93     /// This method allows the image memory barrier creation process to signal when an image
94     /// has been transitioned out of its initial `Undefined` or `Preinitialized` state. This
95     /// allows vulkano to avoid creating unnecessary image memory barriers between future
96     /// uses of the image.
97     ///
98     /// ## Unsafe
99     ///
100     /// If a user calls this method outside of the intended context and signals that the layout
101     /// is no longer `Undefined` or `Preinitialized` when it is still in an `Undefined` or
102     /// `Preinitialized` state, this may result in the vulkan implementation attempting to use
103     /// an image in an invalid layout. The same problem must be considered by the implementer
104     /// of the method.
layout_initialized(&self)105     unsafe fn layout_initialized(&self) {}
106 
is_layout_initialized(&self) -> bool107     fn is_layout_initialized(&self) -> bool {
108         false
109     }
110 
preinitialized_layout(&self) -> bool111     unsafe fn preinitialized_layout(&self) -> bool {
112         self.inner().image.preinitialized_layout()
113     }
114 
115     /// Returns the layout that the image has when it is first used in a primary command buffer.
116     ///
117     /// The first time you use an image in an `AutoCommandBufferBuilder`, vulkano will suppose that
118     /// the image is in the layout returned by this function. Later when the command buffer is
119     /// submitted vulkano will check whether the image is actually in this layout, and if it is not
120     /// the case then an error will be returned.
121     /// TODO: ^ that check is not yet implemented
initial_layout_requirement(&self) -> ImageLayout122     fn initial_layout_requirement(&self) -> ImageLayout;
123 
124     /// Returns the layout that the image must be returned to before the end of the command buffer.
125     ///
126     /// When an image is used in an `AutoCommandBufferBuilder` vulkano will automatically
127     /// transition this image to the layout returned by this function at the end of the command
128     /// buffer, if necessary.
129     ///
130     /// Except for special cases, this value should likely be the same as the one returned by
131     /// `initial_layout_requirement` so that the user can submit multiple command buffers that use
132     /// this image one after the other.
final_layout_requirement(&self) -> ImageLayout133     fn final_layout_requirement(&self) -> ImageLayout;
134 
135     /// Wraps around this `ImageAccess` and returns an identical `ImageAccess` but whose initial
136     /// layout requirement is either `Undefined` or `Preinitialized`.
137     #[inline]
forced_undefined_initial_layout( self, preinitialized: bool, ) -> ImageAccessFromUndefinedLayout<Self> where Self: Sized,138     unsafe fn forced_undefined_initial_layout(
139         self,
140         preinitialized: bool,
141     ) -> ImageAccessFromUndefinedLayout<Self>
142     where
143         Self: Sized,
144     {
145         ImageAccessFromUndefinedLayout {
146             image: self,
147             preinitialized,
148         }
149     }
150 
151     /// Returns an [`ImageDescriptorLayouts`] structure specifying the image layout to use
152     /// in descriptors of various kinds.
153     ///
154     /// This must return `Some` if the image is to be used to create an image view.
descriptor_layouts(&self) -> Option<ImageDescriptorLayouts>155     fn descriptor_layouts(&self) -> Option<ImageDescriptorLayouts>;
156 
157     /// Returns a key that uniquely identifies the memory content of the image.
158     /// Two ranges that potentially overlap in memory must return the same key.
159     ///
160     /// The key is shared amongst all buffers and images, which means that you can make several
161     /// different image objects share the same memory, or make some image objects share memory
162     /// with buffers, as long as they return the same key.
163     ///
164     /// Since it is possible to accidentally return the same key for memory ranges that don't
165     /// overlap, the `conflicts_image` or `conflicts_buffer` function should always be called to
166     /// verify whether they actually overlap.
conflict_key(&self) -> u64167     fn conflict_key(&self) -> u64;
168 
169     /// Returns the current mip level that is accessed by the gpu
current_miplevels_access(&self) -> std::ops::Range<u32>170     fn current_miplevels_access(&self) -> std::ops::Range<u32>;
171 
172     /// Returns the current layer level that is accessed by the gpu
current_layer_levels_access(&self) -> std::ops::Range<u32>173     fn current_layer_levels_access(&self) -> std::ops::Range<u32>;
174 
175     /// Locks the resource for usage on the GPU. Returns an error if the lock can't be acquired.
176     ///
177     /// After this function returns `Ok`, you are authorized to use the image on the GPU. If the
178     /// GPU operation requires an exclusive access to the image (which includes image layout
179     /// transitions) then `exclusive_access` should be true.
180     ///
181     /// The `expected_layout` is the layout we expect the image to be in when we lock it. If the
182     /// actual layout doesn't match this expected layout, then an error should be returned. If
183     /// `Undefined` is passed, that means that the caller doesn't care about the actual layout,
184     /// and that a layout mismatch shouldn't return an error.
185     ///
186     /// This function exists to prevent the user from causing a data race by reading and writing
187     /// to the same resource at the same time.
188     ///
189     /// If you call this function, you should call `unlock()` once the resource is no longer in use
190     /// by the GPU. The implementation is not expected to automatically perform any unlocking and
191     /// can rely on the fact that `unlock()` is going to be called.
try_gpu_lock( &self, exclusive_access: bool, uninitialized_safe: bool, expected_layout: ImageLayout, ) -> Result<(), AccessError>192     fn try_gpu_lock(
193         &self,
194         exclusive_access: bool,
195         uninitialized_safe: bool,
196         expected_layout: ImageLayout,
197     ) -> Result<(), AccessError>;
198 
199     /// Locks the resource for usage on the GPU. Supposes that the resource is already locked, and
200     /// simply increases the lock by one.
201     ///
202     /// Must only be called after `try_gpu_lock()` succeeded.
203     ///
204     /// If you call this function, you should call `unlock()` once the resource is no longer in use
205     /// by the GPU. The implementation is not expected to automatically perform any unlocking and
206     /// can rely on the fact that `unlock()` is going to be called.
increase_gpu_lock(&self)207     unsafe fn increase_gpu_lock(&self);
208 
209     /// Unlocks the resource previously acquired with `try_gpu_lock` or `increase_gpu_lock`.
210     ///
211     /// If the GPU operation that we unlock from transitioned the image to another layout, then
212     /// it should be passed as parameter.
213     ///
214     /// A layout transition requires exclusive access to the image, which means two things:
215     ///
216     /// - The implementation can panic if it finds out that the layout is not the same as it
217     ///   currently is and that it is not locked in exclusive mode.
218     /// - There shouldn't be any possible race between `unlock` and `try_gpu_lock`, since
219     ///   `try_gpu_lock` should fail if the image is already locked in exclusive mode.
220     ///
221     /// # Safety
222     ///
223     /// - Must only be called once per previous lock.
224     /// - The transitioned layout must be supported by the image (eg. the layout shouldn't be
225     ///   `ColorAttachmentOptimal` if the image wasn't created with the `color_attachment` usage).
226     /// - The transitioned layout must not be `Undefined`.
227     ///
unlock(&self, transitioned_layout: Option<ImageLayout>)228     unsafe fn unlock(&self, transitioned_layout: Option<ImageLayout>);
229 }
230 
231 /// Inner information about an image.
232 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
233 pub struct ImageInner<'a> {
234     /// The underlying image object.
235     pub image: &'a UnsafeImage,
236 
237     /// The first layer of `image` to consider.
238     pub first_layer: usize,
239 
240     /// The number of layers of `image` to consider.
241     pub num_layers: usize,
242 
243     /// The first mipmap level of `image` to consider.
244     pub first_mipmap_level: usize,
245 
246     /// The number of mipmap levels of `image` to consider.
247     pub num_mipmap_levels: usize,
248 }
249 
250 unsafe impl<T> ImageAccess for T
251 where
252     T: SafeDeref,
253     T::Target: ImageAccess,
254 {
255     #[inline]
inner(&self) -> ImageInner256     fn inner(&self) -> ImageInner {
257         (**self).inner()
258     }
259 
260     #[inline]
initial_layout_requirement(&self) -> ImageLayout261     fn initial_layout_requirement(&self) -> ImageLayout {
262         (**self).initial_layout_requirement()
263     }
264 
265     #[inline]
final_layout_requirement(&self) -> ImageLayout266     fn final_layout_requirement(&self) -> ImageLayout {
267         (**self).final_layout_requirement()
268     }
269 
270     #[inline]
descriptor_layouts(&self) -> Option<ImageDescriptorLayouts>271     fn descriptor_layouts(&self) -> Option<ImageDescriptorLayouts> {
272         (**self).descriptor_layouts()
273     }
274 
275     #[inline]
conflict_key(&self) -> u64276     fn conflict_key(&self) -> u64 {
277         (**self).conflict_key()
278     }
279 
280     #[inline]
try_gpu_lock( &self, exclusive_access: bool, uninitialized_safe: bool, expected_layout: ImageLayout, ) -> Result<(), AccessError>281     fn try_gpu_lock(
282         &self,
283         exclusive_access: bool,
284         uninitialized_safe: bool,
285         expected_layout: ImageLayout,
286     ) -> Result<(), AccessError> {
287         (**self).try_gpu_lock(exclusive_access, uninitialized_safe, expected_layout)
288     }
289 
290     #[inline]
increase_gpu_lock(&self)291     unsafe fn increase_gpu_lock(&self) {
292         (**self).increase_gpu_lock()
293     }
294 
295     #[inline]
unlock(&self, transitioned_layout: Option<ImageLayout>)296     unsafe fn unlock(&self, transitioned_layout: Option<ImageLayout>) {
297         (**self).unlock(transitioned_layout)
298     }
299 
300     #[inline]
layout_initialized(&self)301     unsafe fn layout_initialized(&self) {
302         (**self).layout_initialized();
303     }
304 
305     #[inline]
is_layout_initialized(&self) -> bool306     fn is_layout_initialized(&self) -> bool {
307         (**self).is_layout_initialized()
308     }
309 
current_miplevels_access(&self) -> std::ops::Range<u32>310     fn current_miplevels_access(&self) -> std::ops::Range<u32> {
311         (**self).current_miplevels_access()
312     }
313 
current_layer_levels_access(&self) -> std::ops::Range<u32>314     fn current_layer_levels_access(&self) -> std::ops::Range<u32> {
315         (**self).current_layer_levels_access()
316     }
317 }
318 
319 impl PartialEq for dyn ImageAccess + Send + Sync {
320     #[inline]
eq(&self, other: &Self) -> bool321     fn eq(&self, other: &Self) -> bool {
322         self.inner() == other.inner()
323     }
324 }
325 
326 impl Eq for dyn ImageAccess + Send + Sync {}
327 
328 impl Hash for dyn ImageAccess + Send + Sync {
329     #[inline]
hash<H: Hasher>(&self, state: &mut H)330     fn hash<H: Hasher>(&self, state: &mut H) {
331         self.inner().hash(state);
332     }
333 }
334 
335 /// Wraps around an object that implements `ImageAccess` and modifies the initial layout
336 /// requirement to be either `Undefined` or `Preinitialized`.
337 #[derive(Debug, Copy, Clone)]
338 pub struct ImageAccessFromUndefinedLayout<I> {
339     image: I,
340     preinitialized: bool,
341 }
342 
343 unsafe impl<I> ImageAccess for ImageAccessFromUndefinedLayout<I>
344 where
345     I: ImageAccess,
346 {
347     #[inline]
inner(&self) -> ImageInner348     fn inner(&self) -> ImageInner {
349         self.image.inner()
350     }
351 
352     #[inline]
initial_layout_requirement(&self) -> ImageLayout353     fn initial_layout_requirement(&self) -> ImageLayout {
354         if self.preinitialized {
355             ImageLayout::Preinitialized
356         } else {
357             ImageLayout::Undefined
358         }
359     }
360 
361     #[inline]
final_layout_requirement(&self) -> ImageLayout362     fn final_layout_requirement(&self) -> ImageLayout {
363         self.image.final_layout_requirement()
364     }
365 
366     #[inline]
descriptor_layouts(&self) -> Option<ImageDescriptorLayouts>367     fn descriptor_layouts(&self) -> Option<ImageDescriptorLayouts> {
368         self.image.descriptor_layouts()
369     }
370 
371     #[inline]
conflict_key(&self) -> u64372     fn conflict_key(&self) -> u64 {
373         self.image.conflict_key()
374     }
375 
376     #[inline]
try_gpu_lock( &self, exclusive_access: bool, uninitialized_safe: bool, expected_layout: ImageLayout, ) -> Result<(), AccessError>377     fn try_gpu_lock(
378         &self,
379         exclusive_access: bool,
380         uninitialized_safe: bool,
381         expected_layout: ImageLayout,
382     ) -> Result<(), AccessError> {
383         self.image
384             .try_gpu_lock(exclusive_access, uninitialized_safe, expected_layout)
385     }
386 
387     #[inline]
increase_gpu_lock(&self)388     unsafe fn increase_gpu_lock(&self) {
389         self.image.increase_gpu_lock()
390     }
391 
392     #[inline]
unlock(&self, new_layout: Option<ImageLayout>)393     unsafe fn unlock(&self, new_layout: Option<ImageLayout>) {
394         self.image.unlock(new_layout)
395     }
396 
current_miplevels_access(&self) -> std::ops::Range<u32>397     fn current_miplevels_access(&self) -> std::ops::Range<u32> {
398         self.image.current_miplevels_access()
399     }
400 
current_layer_levels_access(&self) -> std::ops::Range<u32>401     fn current_layer_levels_access(&self) -> std::ops::Range<u32> {
402         self.image.current_layer_levels_access()
403     }
404 }
405 
406 impl<I> PartialEq for ImageAccessFromUndefinedLayout<I>
407 where
408     I: ImageAccess,
409 {
410     #[inline]
eq(&self, other: &Self) -> bool411     fn eq(&self, other: &Self) -> bool {
412         self.inner() == other.inner()
413     }
414 }
415 
416 impl<I> Eq for ImageAccessFromUndefinedLayout<I> where I: ImageAccess {}
417 
418 impl<I> Hash for ImageAccessFromUndefinedLayout<I>
419 where
420     I: ImageAccess,
421 {
422     #[inline]
hash<H: Hasher>(&self, state: &mut H)423     fn hash<H: Hasher>(&self, state: &mut H) {
424         self.inner().hash(state);
425     }
426 }
427 
428 /// Extension trait for images. Checks whether the value `T` can be used as a clear value for the
429 /// given image.
430 // TODO: isn't that for image views instead?
431 pub unsafe trait ImageClearValue<T>: ImageAccess {
decode(&self, value: T) -> Option<ClearValue>432     fn decode(&self, value: T) -> Option<ClearValue>;
433 }
434 
435 pub unsafe trait ImageContent<P>: ImageAccess {
436     /// Checks whether pixels of type `P` match the format of the image.
matches_format(&self) -> bool437     fn matches_format(&self) -> bool;
438 }
439