diff options
Diffstat (limited to 'src/image/traits.rs')
-rw-r--r-- | src/image/traits.rs | 438 |
1 files changed, 438 insertions, 0 deletions
diff --git a/src/image/traits.rs b/src/image/traits.rs new file mode 100644 index 0000000..4c6a5c8 --- /dev/null +++ b/src/image/traits.rs @@ -0,0 +1,438 @@ +// Copyright (c) 2016 The vulkano developers +// Licensed under the Apache License, Version 2.0 +// <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +use crate::format::ClearValue; +use crate::format::Format; +use crate::format::FormatTy; +use crate::image::sys::UnsafeImage; +use crate::image::ImageDescriptorLayouts; +use crate::image::ImageDimensions; +use crate::image::ImageLayout; +use crate::image::SampleCount; +use crate::sync::AccessError; +use crate::SafeDeref; +use std::hash::Hash; +use std::hash::Hasher; + +/// Trait for types that represent the way a GPU can access an image. +pub unsafe trait ImageAccess { + /// Returns the inner unsafe image object used by this image. + fn inner(&self) -> ImageInner; + + /// Returns the format of this image. + #[inline] + fn format(&self) -> Format { + self.inner().image.format() + } + + /// Returns true if the image is a color image. + #[inline] + fn has_color(&self) -> bool { + matches!( + self.format().ty(), + FormatTy::Float | FormatTy::Uint | FormatTy::Sint | FormatTy::Compressed + ) + } + + /// Returns true if the image has a depth component. In other words, if it is a depth or a + /// depth-stencil format. + #[inline] + fn has_depth(&self) -> bool { + matches!(self.format().ty(), FormatTy::Depth | FormatTy::DepthStencil) + } + + /// Returns true if the image has a stencil component. In other words, if it is a stencil or a + /// depth-stencil format. + #[inline] + fn has_stencil(&self) -> bool { + matches!( + self.format().ty(), + FormatTy::Stencil | FormatTy::DepthStencil + ) + } + + /// Returns the number of mipmap levels of this image. + #[inline] + fn mipmap_levels(&self) -> u32 { + // TODO: not necessarily correct because of the new inner() design? + self.inner().image.mipmap_levels() + } + + /// Returns the number of samples of this image. + #[inline] + fn samples(&self) -> SampleCount { + self.inner().image.samples() + } + + /// Returns the dimensions of the image. + #[inline] + fn dimensions(&self) -> ImageDimensions { + // TODO: not necessarily correct because of the new inner() design? + self.inner().image.dimensions() + } + + /// Returns true if the image can be used as a source for blits. + #[inline] + fn supports_blit_source(&self) -> bool { + self.inner().image.format_features().blit_src + } + + /// Returns true if the image can be used as a destination for blits. + #[inline] + fn supports_blit_destination(&self) -> bool { + self.inner().image.format_features().blit_dst + } + + /// When images are created their memory layout is initially `Undefined` or `Preinitialized`. + /// This method allows the image memory barrier creation process to signal when an image + /// has been transitioned out of its initial `Undefined` or `Preinitialized` state. This + /// allows vulkano to avoid creating unnecessary image memory barriers between future + /// uses of the image. + /// + /// ## Unsafe + /// + /// If a user calls this method outside of the intended context and signals that the layout + /// is no longer `Undefined` or `Preinitialized` when it is still in an `Undefined` or + /// `Preinitialized` state, this may result in the vulkan implementation attempting to use + /// an image in an invalid layout. The same problem must be considered by the implementer + /// of the method. + unsafe fn layout_initialized(&self) {} + + fn is_layout_initialized(&self) -> bool { + false + } + + unsafe fn preinitialized_layout(&self) -> bool { + self.inner().image.preinitialized_layout() + } + + /// Returns the layout that the image has when it is first used in a primary command buffer. + /// + /// The first time you use an image in an `AutoCommandBufferBuilder`, vulkano will suppose that + /// the image is in the layout returned by this function. Later when the command buffer is + /// submitted vulkano will check whether the image is actually in this layout, and if it is not + /// the case then an error will be returned. + /// TODO: ^ that check is not yet implemented + fn initial_layout_requirement(&self) -> ImageLayout; + + /// Returns the layout that the image must be returned to before the end of the command buffer. + /// + /// When an image is used in an `AutoCommandBufferBuilder` vulkano will automatically + /// transition this image to the layout returned by this function at the end of the command + /// buffer, if necessary. + /// + /// Except for special cases, this value should likely be the same as the one returned by + /// `initial_layout_requirement` so that the user can submit multiple command buffers that use + /// this image one after the other. + fn final_layout_requirement(&self) -> ImageLayout; + + /// Wraps around this `ImageAccess` and returns an identical `ImageAccess` but whose initial + /// layout requirement is either `Undefined` or `Preinitialized`. + #[inline] + unsafe fn forced_undefined_initial_layout( + self, + preinitialized: bool, + ) -> ImageAccessFromUndefinedLayout<Self> + where + Self: Sized, + { + ImageAccessFromUndefinedLayout { + image: self, + preinitialized, + } + } + + /// Returns an [`ImageDescriptorLayouts`] structure specifying the image layout to use + /// in descriptors of various kinds. + /// + /// This must return `Some` if the image is to be used to create an image view. + fn descriptor_layouts(&self) -> Option<ImageDescriptorLayouts>; + + /// Returns a key that uniquely identifies the memory content of the image. + /// Two ranges that potentially overlap in memory must return the same key. + /// + /// The key is shared amongst all buffers and images, which means that you can make several + /// different image objects share the same memory, or make some image objects share memory + /// with buffers, as long as they return the same key. + /// + /// Since it is possible to accidentally return the same key for memory ranges that don't + /// overlap, the `conflicts_image` or `conflicts_buffer` function should always be called to + /// verify whether they actually overlap. + fn conflict_key(&self) -> u64; + + /// Returns the current mip level that is accessed by the gpu + fn current_miplevels_access(&self) -> std::ops::Range<u32>; + + /// Returns the current layer level that is accessed by the gpu + fn current_layer_levels_access(&self) -> std::ops::Range<u32>; + + /// Locks the resource for usage on the GPU. Returns an error if the lock can't be acquired. + /// + /// After this function returns `Ok`, you are authorized to use the image on the GPU. If the + /// GPU operation requires an exclusive access to the image (which includes image layout + /// transitions) then `exclusive_access` should be true. + /// + /// The `expected_layout` is the layout we expect the image to be in when we lock it. If the + /// actual layout doesn't match this expected layout, then an error should be returned. If + /// `Undefined` is passed, that means that the caller doesn't care about the actual layout, + /// and that a layout mismatch shouldn't return an error. + /// + /// This function exists to prevent the user from causing a data race by reading and writing + /// to the same resource at the same time. + /// + /// If you call this function, you should call `unlock()` once the resource is no longer in use + /// by the GPU. The implementation is not expected to automatically perform any unlocking and + /// can rely on the fact that `unlock()` is going to be called. + fn try_gpu_lock( + &self, + exclusive_access: bool, + uninitialized_safe: bool, + expected_layout: ImageLayout, + ) -> Result<(), AccessError>; + + /// Locks the resource for usage on the GPU. Supposes that the resource is already locked, and + /// simply increases the lock by one. + /// + /// Must only be called after `try_gpu_lock()` succeeded. + /// + /// If you call this function, you should call `unlock()` once the resource is no longer in use + /// by the GPU. The implementation is not expected to automatically perform any unlocking and + /// can rely on the fact that `unlock()` is going to be called. + unsafe fn increase_gpu_lock(&self); + + /// Unlocks the resource previously acquired with `try_gpu_lock` or `increase_gpu_lock`. + /// + /// If the GPU operation that we unlock from transitioned the image to another layout, then + /// it should be passed as parameter. + /// + /// A layout transition requires exclusive access to the image, which means two things: + /// + /// - The implementation can panic if it finds out that the layout is not the same as it + /// currently is and that it is not locked in exclusive mode. + /// - There shouldn't be any possible race between `unlock` and `try_gpu_lock`, since + /// `try_gpu_lock` should fail if the image is already locked in exclusive mode. + /// + /// # Safety + /// + /// - Must only be called once per previous lock. + /// - The transitioned layout must be supported by the image (eg. the layout shouldn't be + /// `ColorAttachmentOptimal` if the image wasn't created with the `color_attachment` usage). + /// - The transitioned layout must not be `Undefined`. + /// + unsafe fn unlock(&self, transitioned_layout: Option<ImageLayout>); +} + +/// Inner information about an image. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub struct ImageInner<'a> { + /// The underlying image object. + pub image: &'a UnsafeImage, + + /// The first layer of `image` to consider. + pub first_layer: usize, + + /// The number of layers of `image` to consider. + pub num_layers: usize, + + /// The first mipmap level of `image` to consider. + pub first_mipmap_level: usize, + + /// The number of mipmap levels of `image` to consider. + pub num_mipmap_levels: usize, +} + +unsafe impl<T> ImageAccess for T +where + T: SafeDeref, + T::Target: ImageAccess, +{ + #[inline] + fn inner(&self) -> ImageInner { + (**self).inner() + } + + #[inline] + fn initial_layout_requirement(&self) -> ImageLayout { + (**self).initial_layout_requirement() + } + + #[inline] + fn final_layout_requirement(&self) -> ImageLayout { + (**self).final_layout_requirement() + } + + #[inline] + fn descriptor_layouts(&self) -> Option<ImageDescriptorLayouts> { + (**self).descriptor_layouts() + } + + #[inline] + fn conflict_key(&self) -> u64 { + (**self).conflict_key() + } + + #[inline] + fn try_gpu_lock( + &self, + exclusive_access: bool, + uninitialized_safe: bool, + expected_layout: ImageLayout, + ) -> Result<(), AccessError> { + (**self).try_gpu_lock(exclusive_access, uninitialized_safe, expected_layout) + } + + #[inline] + unsafe fn increase_gpu_lock(&self) { + (**self).increase_gpu_lock() + } + + #[inline] + unsafe fn unlock(&self, transitioned_layout: Option<ImageLayout>) { + (**self).unlock(transitioned_layout) + } + + #[inline] + unsafe fn layout_initialized(&self) { + (**self).layout_initialized(); + } + + #[inline] + fn is_layout_initialized(&self) -> bool { + (**self).is_layout_initialized() + } + + fn current_miplevels_access(&self) -> std::ops::Range<u32> { + (**self).current_miplevels_access() + } + + fn current_layer_levels_access(&self) -> std::ops::Range<u32> { + (**self).current_layer_levels_access() + } +} + +impl PartialEq for dyn ImageAccess + Send + Sync { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.inner() == other.inner() + } +} + +impl Eq for dyn ImageAccess + Send + Sync {} + +impl Hash for dyn ImageAccess + Send + Sync { + #[inline] + fn hash<H: Hasher>(&self, state: &mut H) { + self.inner().hash(state); + } +} + +/// Wraps around an object that implements `ImageAccess` and modifies the initial layout +/// requirement to be either `Undefined` or `Preinitialized`. +#[derive(Debug, Copy, Clone)] +pub struct ImageAccessFromUndefinedLayout<I> { + image: I, + preinitialized: bool, +} + +unsafe impl<I> ImageAccess for ImageAccessFromUndefinedLayout<I> +where + I: ImageAccess, +{ + #[inline] + fn inner(&self) -> ImageInner { + self.image.inner() + } + + #[inline] + fn initial_layout_requirement(&self) -> ImageLayout { + if self.preinitialized { + ImageLayout::Preinitialized + } else { + ImageLayout::Undefined + } + } + + #[inline] + fn final_layout_requirement(&self) -> ImageLayout { + self.image.final_layout_requirement() + } + + #[inline] + fn descriptor_layouts(&self) -> Option<ImageDescriptorLayouts> { + self.image.descriptor_layouts() + } + + #[inline] + fn conflict_key(&self) -> u64 { + self.image.conflict_key() + } + + #[inline] + fn try_gpu_lock( + &self, + exclusive_access: bool, + uninitialized_safe: bool, + expected_layout: ImageLayout, + ) -> Result<(), AccessError> { + self.image + .try_gpu_lock(exclusive_access, uninitialized_safe, expected_layout) + } + + #[inline] + unsafe fn increase_gpu_lock(&self) { + self.image.increase_gpu_lock() + } + + #[inline] + unsafe fn unlock(&self, new_layout: Option<ImageLayout>) { + self.image.unlock(new_layout) + } + + fn current_miplevels_access(&self) -> std::ops::Range<u32> { + self.image.current_miplevels_access() + } + + fn current_layer_levels_access(&self) -> std::ops::Range<u32> { + self.image.current_layer_levels_access() + } +} + +impl<I> PartialEq for ImageAccessFromUndefinedLayout<I> +where + I: ImageAccess, +{ + #[inline] + fn eq(&self, other: &Self) -> bool { + self.inner() == other.inner() + } +} + +impl<I> Eq for ImageAccessFromUndefinedLayout<I> where I: ImageAccess {} + +impl<I> Hash for ImageAccessFromUndefinedLayout<I> +where + I: ImageAccess, +{ + #[inline] + fn hash<H: Hasher>(&self, state: &mut H) { + self.inner().hash(state); + } +} + +/// Extension trait for images. Checks whether the value `T` can be used as a clear value for the +/// given image. +// TODO: isn't that for image views instead? +pub unsafe trait ImageClearValue<T>: ImageAccess { + fn decode(&self, value: T) -> Option<ClearValue>; +} + +pub unsafe trait ImageContent<P>: ImageAccess { + /// Checks whether pixels of type `P` match the format of the image. + fn matches_format(&self) -> bool; +} |