aboutsummaryrefslogtreecommitdiff
path: root/src/command_buffer/submit/bind_sparse.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/command_buffer/submit/bind_sparse.rs')
-rw-r--r--src/command_buffer/submit/bind_sparse.rs511
1 files changed, 511 insertions, 0 deletions
diff --git a/src/command_buffer/submit/bind_sparse.rs b/src/command_buffer/submit/bind_sparse.rs
new file mode 100644
index 0000000..313a09c
--- /dev/null
+++ b/src/command_buffer/submit/bind_sparse.rs
@@ -0,0 +1,511 @@
+// 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),
+ }
+ }
+}