| // Copyright (c) 2017 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::buffer::sys::UnsafeBuffer; |
| use crate::check_errors; |
| use crate::device::Queue; |
| use crate::image::sys::UnsafeImage; |
| use crate::memory::DeviceMemory; |
| use crate::sync::Fence; |
| use crate::sync::Semaphore; |
| use crate::DeviceSize; |
| use crate::Error; |
| use crate::OomError; |
| use crate::SynchronizedVulkanObject; |
| use crate::VulkanObject; |
| use smallvec::SmallVec; |
| use std::error; |
| use std::fmt; |
| use std::marker::PhantomData; |
| |
| // TODO: correctly implement Debug on all the structs of this module |
| |
| /// Prototype for a submission that binds sparse memory. |
| // TODO: example here |
| pub struct SubmitBindSparseBuilder<'a> { |
| infos: SmallVec<[SubmitBindSparseBatchBuilder<'a>; 1]>, |
| fence: ash::vk::Fence, |
| } |
| |
| impl<'a> SubmitBindSparseBuilder<'a> { |
| /// Builds a new empty `SubmitBindSparseBuilder`. |
| #[inline] |
| pub fn new() -> SubmitBindSparseBuilder<'a> { |
| SubmitBindSparseBuilder { |
| infos: SmallVec::new(), |
| fence: ash::vk::Fence::null(), |
| } |
| } |
| |
| /// Adds a batch to the command. |
| /// |
| /// Batches start execution in order, but can finish in a different order. In other words any |
| /// wait semaphore added to a batch will apply to further batches as well, but when a semaphore |
| /// is signalled, it does **not** mean that previous batches have been completed. |
| #[inline] |
| pub fn add(&mut self, builder: SubmitBindSparseBatchBuilder<'a>) { |
| self.infos.push(builder); |
| } |
| |
| /// Returns true if this builder will signal a fence when submitted. |
| /// |
| /// # Example |
| /// |
| /// ``` |
| /// use vulkano::command_buffer::submit::SubmitBindSparseBuilder; |
| /// use vulkano::sync::Fence; |
| /// # let device: std::sync::Arc<vulkano::device::Device> = return; |
| /// |
| /// unsafe { |
| /// let fence = Fence::from_pool(device.clone()).unwrap(); |
| /// |
| /// let mut builder = SubmitBindSparseBuilder::new(); |
| /// assert!(!builder.has_fence()); |
| /// builder.set_fence_signal(&fence); |
| /// assert!(builder.has_fence()); |
| /// } |
| /// ``` |
| #[inline] |
| pub fn has_fence(&self) -> bool { |
| self.fence != ash::vk::Fence::null() |
| } |
| |
| /// Adds an operation that signals a fence after this submission ends. |
| /// |
| /// # Example |
| /// |
| /// ``` |
| /// use std::time::Duration; |
| /// use vulkano::command_buffer::submit::SubmitBindSparseBuilder; |
| /// use vulkano::sync::Fence; |
| /// # let device: std::sync::Arc<vulkano::device::Device> = return; |
| /// # let queue: std::sync::Arc<vulkano::device::Queue> = return; |
| /// |
| /// unsafe { |
| /// let fence = Fence::from_pool(device.clone()).unwrap(); |
| /// |
| /// let mut builder = SubmitBindSparseBuilder::new(); |
| /// builder.set_fence_signal(&fence); |
| /// |
| /// builder.submit(&queue).unwrap(); |
| /// |
| /// // We must not destroy the fence before it is signaled. |
| /// fence.wait(None).unwrap(); |
| /// } |
| /// ``` |
| /// |
| /// # Safety |
| /// |
| /// - The fence must not be signaled at the time when you call `submit()`. |
| /// |
| /// - If you use the fence for multiple submissions, only one at a time must be executed by the |
| /// GPU. In other words, you must submit one, wait for the fence to be signaled, then reset |
| /// the fence, and then only submit the second. |
| /// |
| /// - If you submit this builder, the fence must be kept alive until it is signaled by the GPU. |
| /// Destroying the fence earlier is an undefined behavior. |
| /// |
| /// - The fence, buffers, images, and semaphores must all belong to the same device. |
| /// |
| #[inline] |
| pub unsafe fn set_fence_signal(&mut self, fence: &'a Fence) { |
| self.fence = fence.internal_object(); |
| } |
| |
| /// Attempts to merge this builder with another one. |
| /// |
| /// If both builders have a fence already set, then this function will return `other` as an |
| /// error. |
| #[inline] |
| pub fn merge( |
| &mut self, |
| other: SubmitBindSparseBuilder<'a>, |
| ) -> Result<(), SubmitBindSparseBuilder<'a>> { |
| if self.fence != ash::vk::Fence::null() && other.fence != ash::vk::Fence::null() { |
| return Err(other); |
| } |
| |
| self.infos.extend(other.infos.into_iter()); |
| Ok(()) |
| } |
| |
| /// Submits the command. Calls `vkQueueBindSparse`. |
| pub fn submit(self, queue: &Queue) -> Result<(), SubmitBindSparseError> { |
| unsafe { |
| debug_assert!(queue.family().supports_sparse_binding()); |
| |
| let fns = queue.device().fns(); |
| let queue = queue.internal_object_guard(); |
| |
| // We start by storing all the `VkSparseBufferMemoryBindInfo`s of the whole command |
| // in the same collection. |
| let buffer_binds_storage: SmallVec<[_; 4]> = self |
| .infos |
| .iter() |
| .flat_map(|infos| infos.buffer_binds.iter()) |
| .map(|buf_bind| ash::vk::SparseBufferMemoryBindInfo { |
| buffer: buf_bind.buffer, |
| bind_count: buf_bind.binds.len() as u32, |
| p_binds: buf_bind.binds.as_ptr(), |
| }) |
| .collect(); |
| |
| // Same for all the `VkSparseImageOpaqueMemoryBindInfo`s. |
| let image_opaque_binds_storage: SmallVec<[_; 4]> = self |
| .infos |
| .iter() |
| .flat_map(|infos| infos.image_opaque_binds.iter()) |
| .map(|img_bind| ash::vk::SparseImageOpaqueMemoryBindInfo { |
| image: img_bind.image, |
| bind_count: img_bind.binds.len() as u32, |
| p_binds: img_bind.binds.as_ptr(), |
| }) |
| .collect(); |
| |
| // And finally the `VkSparseImageMemoryBindInfo`s. |
| let image_binds_storage: SmallVec<[_; 4]> = self |
| .infos |
| .iter() |
| .flat_map(|infos| infos.image_binds.iter()) |
| .map(|img_bind| ash::vk::SparseImageMemoryBindInfo { |
| image: img_bind.image, |
| bind_count: img_bind.binds.len() as u32, |
| p_binds: img_bind.binds.as_ptr(), |
| }) |
| .collect(); |
| |
| // Now building the collection of `VkBindSparseInfo`s. |
| let bs_infos = { |
| let mut bs_infos: SmallVec<[_; 4]> = SmallVec::new(); |
| |
| // Since we stores all the bind infos contiguously, we keep track of the current |
| // offset within these containers. |
| let mut next_buffer_bind = 0; |
| let mut next_image_opaque_bind = 0; |
| let mut next_image_bind = 0; |
| |
| for builder in self.infos.iter() { |
| bs_infos.push(ash::vk::BindSparseInfo { |
| wait_semaphore_count: builder.wait_semaphores.len() as u32, |
| p_wait_semaphores: builder.wait_semaphores.as_ptr(), |
| buffer_bind_count: builder.buffer_binds.len() as u32, |
| p_buffer_binds: if next_buffer_bind != 0 { |
| // We need that `if` because `.as_ptr().offset(0)` is technically UB. |
| buffer_binds_storage.as_ptr().offset(next_buffer_bind) |
| } else { |
| buffer_binds_storage.as_ptr() |
| }, |
| image_opaque_bind_count: builder.image_opaque_binds.len() as u32, |
| p_image_opaque_binds: if next_image_opaque_bind != 0 { |
| // We need that `if` because `.as_ptr().offset(0)` is technically UB. |
| image_opaque_binds_storage |
| .as_ptr() |
| .offset(next_image_opaque_bind) |
| } else { |
| image_opaque_binds_storage.as_ptr() |
| }, |
| image_bind_count: builder.image_binds.len() as u32, |
| p_image_binds: if next_image_bind != 0 { |
| // We need that `if` because `.as_ptr().offset(0)` is technically UB. |
| image_binds_storage.as_ptr().offset(next_image_bind) |
| } else { |
| image_binds_storage.as_ptr() |
| }, |
| signal_semaphore_count: builder.signal_semaphores.len() as u32, |
| p_signal_semaphores: builder.signal_semaphores.as_ptr(), |
| ..Default::default() |
| }); |
| |
| next_buffer_bind += builder.buffer_binds.len() as isize; |
| next_image_opaque_bind += builder.image_opaque_binds.len() as isize; |
| next_image_bind += builder.image_binds.len() as isize; |
| } |
| |
| // If these assertions fail, then there's something wrong in the code above. |
| debug_assert_eq!(next_buffer_bind as usize, buffer_binds_storage.len()); |
| debug_assert_eq!( |
| next_image_opaque_bind as usize, |
| image_opaque_binds_storage.len() |
| ); |
| debug_assert_eq!(next_image_bind as usize, image_binds_storage.len()); |
| |
| bs_infos |
| }; |
| |
| // Finally executing the command. |
| check_errors(fns.v1_0.queue_bind_sparse( |
| *queue, |
| bs_infos.len() as u32, |
| bs_infos.as_ptr(), |
| self.fence, |
| ))?; |
| Ok(()) |
| } |
| } |
| } |
| |
| impl<'a> fmt::Debug for SubmitBindSparseBuilder<'a> { |
| #[inline] |
| fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { |
| write!(fmt, "<Bind sparse operation>") |
| } |
| } |
| |
| /// A single batch of a sparse bind operation. |
| pub struct SubmitBindSparseBatchBuilder<'a> { |
| wait_semaphores: SmallVec<[ash::vk::Semaphore; 8]>, |
| buffer_binds: SmallVec<[SubmitBindSparseBufferBindBuilder<'a>; 2]>, |
| image_opaque_binds: SmallVec<[SubmitBindSparseImageOpaqueBindBuilder<'a>; 2]>, |
| image_binds: SmallVec<[SubmitBindSparseImageBindBuilder<'a>; 2]>, |
| signal_semaphores: SmallVec<[ash::vk::Semaphore; 8]>, |
| marker: PhantomData<&'a ()>, |
| } |
| |
| impl<'a> SubmitBindSparseBatchBuilder<'a> { |
| /// Builds a new empty `SubmitBindSparseBatchBuilder`. |
| #[inline] |
| pub fn new() -> SubmitBindSparseBatchBuilder<'a> { |
| SubmitBindSparseBatchBuilder { |
| wait_semaphores: SmallVec::new(), |
| buffer_binds: SmallVec::new(), |
| image_opaque_binds: SmallVec::new(), |
| image_binds: SmallVec::new(), |
| signal_semaphores: SmallVec::new(), |
| marker: PhantomData, |
| } |
| } |
| |
| /// Adds an operation that binds memory to a buffer. |
| pub fn add_buffer(&mut self, cmd: SubmitBindSparseBufferBindBuilder<'a>) { |
| self.buffer_binds.push(cmd); |
| } |
| |
| /// Adds an operation that binds memory to an opaque image. |
| pub fn add_image_opaque(&mut self, cmd: SubmitBindSparseImageOpaqueBindBuilder<'a>) { |
| self.image_opaque_binds.push(cmd); |
| } |
| |
| /// Adds an operation that binds memory to an image. |
| pub fn add_image(&mut self, cmd: SubmitBindSparseImageBindBuilder<'a>) { |
| self.image_binds.push(cmd); |
| } |
| |
| /// Adds a semaphore to be waited upon before the sparse binding is executed. |
| /// |
| /// # Safety |
| /// |
| /// - If you submit this builder, the semaphore must be kept alive until you are guaranteed |
| /// that the GPU has at least started executing the operation. |
| /// |
| /// - If you submit this builder, no other queue must be waiting on these semaphores. In other |
| /// words, each semaphore signal can only correspond to one semaphore wait. |
| /// |
| /// - If you submit this builder, the semaphores must be signaled when the queue execution |
| /// reaches this submission, or there must be one or more submissions in queues that are |
| /// going to signal these semaphores. In other words, you must not block the queue with |
| /// semaphores that can't get signaled. |
| /// |
| /// - The fence, buffers, images, and semaphores must all belong to the same device. |
| /// |
| #[inline] |
| pub unsafe fn add_wait_semaphore(&mut self, semaphore: &'a Semaphore) { |
| self.wait_semaphores.push(semaphore.internal_object()); |
| } |
| |
| /// Returns the number of semaphores to signal. |
| /// |
| /// In other words, this is the number of times `add_signal_semaphore` has been called. |
| #[inline] |
| pub fn num_signal_semaphores(&self) -> usize { |
| self.signal_semaphores.len() |
| } |
| |
| /// Adds a semaphore that is going to be signaled at the end of the submission. |
| /// |
| /// # Safety |
| /// |
| /// - If you submit this builder, the semaphore must be kept alive until you are guaranteed |
| /// that the GPU has finished executing this submission. |
| /// |
| /// - The semaphore must be in the unsignaled state when queue execution reaches this |
| /// submission. |
| /// |
| /// - The fence, buffers, images, and semaphores must all belong to the same device. |
| /// |
| #[inline] |
| pub unsafe fn add_signal_semaphore(&mut self, semaphore: &'a Semaphore) { |
| self.signal_semaphores.push(semaphore.internal_object()); |
| } |
| } |
| |
| pub struct SubmitBindSparseBufferBindBuilder<'a> { |
| buffer: ash::vk::Buffer, |
| binds: SmallVec<[ash::vk::SparseMemoryBind; 1]>, |
| marker: PhantomData<&'a ()>, |
| } |
| |
| impl<'a> SubmitBindSparseBufferBindBuilder<'a> { |
| /// |
| /// # Safety |
| /// |
| /// - `buffer` must be a buffer with sparse binding enabled. |
| pub unsafe fn new(buffer: &'a UnsafeBuffer) -> SubmitBindSparseBufferBindBuilder { |
| SubmitBindSparseBufferBindBuilder { |
| buffer: buffer.internal_object(), |
| binds: SmallVec::new(), |
| marker: PhantomData, |
| } |
| } |
| |
| pub unsafe fn add_bind( |
| &mut self, |
| offset: DeviceSize, |
| size: DeviceSize, |
| memory: &DeviceMemory, |
| memory_offset: DeviceSize, |
| ) { |
| self.binds.push(ash::vk::SparseMemoryBind { |
| resource_offset: offset, |
| size, |
| memory: memory.internal_object(), |
| memory_offset, |
| flags: ash::vk::SparseMemoryBindFlags::empty(), // Flags are only relevant for images. |
| }); |
| } |
| |
| pub unsafe fn add_unbind(&mut self, offset: DeviceSize, size: DeviceSize) { |
| self.binds.push(ash::vk::SparseMemoryBind { |
| resource_offset: offset, |
| size, |
| memory: ash::vk::DeviceMemory::null(), |
| memory_offset: 0, |
| flags: ash::vk::SparseMemoryBindFlags::empty(), |
| }); |
| } |
| } |
| |
| pub struct SubmitBindSparseImageOpaqueBindBuilder<'a> { |
| image: ash::vk::Image, |
| binds: SmallVec<[ash::vk::SparseMemoryBind; 1]>, |
| marker: PhantomData<&'a ()>, |
| } |
| |
| impl<'a> SubmitBindSparseImageOpaqueBindBuilder<'a> { |
| /// |
| /// # Safety |
| /// |
| /// - `image` must be an image with sparse binding enabled. |
| pub unsafe fn new(image: &'a UnsafeImage) -> SubmitBindSparseImageOpaqueBindBuilder { |
| SubmitBindSparseImageOpaqueBindBuilder { |
| image: image.internal_object(), |
| binds: SmallVec::new(), |
| marker: PhantomData, |
| } |
| } |
| |
| pub unsafe fn add_bind( |
| &mut self, |
| offset: DeviceSize, |
| size: DeviceSize, |
| memory: &DeviceMemory, |
| memory_offset: DeviceSize, |
| bind_metadata: bool, |
| ) { |
| self.binds.push(ash::vk::SparseMemoryBind { |
| resource_offset: offset, |
| size, |
| memory: memory.internal_object(), |
| memory_offset, |
| flags: if bind_metadata { |
| ash::vk::SparseMemoryBindFlags::METADATA |
| } else { |
| ash::vk::SparseMemoryBindFlags::empty() |
| }, |
| }); |
| } |
| |
| pub unsafe fn add_unbind(&mut self, offset: DeviceSize, size: DeviceSize) { |
| self.binds.push(ash::vk::SparseMemoryBind { |
| resource_offset: offset, |
| size, |
| memory: ash::vk::DeviceMemory::null(), |
| memory_offset: 0, |
| flags: ash::vk::SparseMemoryBindFlags::empty(), // TODO: is that relevant? |
| }); |
| } |
| } |
| |
| pub struct SubmitBindSparseImageBindBuilder<'a> { |
| image: ash::vk::Image, |
| binds: SmallVec<[ash::vk::SparseImageMemoryBind; 1]>, |
| marker: PhantomData<&'a ()>, |
| } |
| |
| impl<'a> SubmitBindSparseImageBindBuilder<'a> { |
| /// |
| /// # Safety |
| /// |
| /// - `image` must be an image with sparse binding enabled. |
| pub unsafe fn new(image: &'a UnsafeImage) -> SubmitBindSparseImageBindBuilder { |
| SubmitBindSparseImageBindBuilder { |
| image: image.internal_object(), |
| binds: SmallVec::new(), |
| marker: PhantomData, |
| } |
| } |
| |
| // TODO: finish |
| } |
| |
| /// Error that can happen when submitting the present prototype. |
| #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
| #[repr(u32)] |
| pub enum SubmitBindSparseError { |
| /// Not enough memory. |
| OomError(OomError), |
| |
| /// The connection to the device has been lost. |
| DeviceLost, |
| } |
| |
| impl error::Error for SubmitBindSparseError { |
| #[inline] |
| fn source(&self) -> Option<&(dyn error::Error + 'static)> { |
| match *self { |
| SubmitBindSparseError::OomError(ref err) => Some(err), |
| _ => None, |
| } |
| } |
| } |
| |
| impl fmt::Display for SubmitBindSparseError { |
| #[inline] |
| fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { |
| write!( |
| fmt, |
| "{}", |
| match *self { |
| SubmitBindSparseError::OomError(_) => "not enough memory", |
| SubmitBindSparseError::DeviceLost => "the connection to the device has been lost", |
| } |
| ) |
| } |
| } |
| |
| impl From<Error> for SubmitBindSparseError { |
| #[inline] |
| fn from(err: Error) -> SubmitBindSparseError { |
| match err { |
| err @ Error::OutOfHostMemory => SubmitBindSparseError::OomError(OomError::from(err)), |
| err @ Error::OutOfDeviceMemory => SubmitBindSparseError::OomError(OomError::from(err)), |
| Error::DeviceLost => SubmitBindSparseError::DeviceLost, |
| _ => panic!("unexpected error: {:?}", err), |
| } |
| } |
| } |