1 // Copyright (c) 2017 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::check_errors; 11 use crate::command_buffer::sys::UnsafeCommandBuffer; 12 use crate::device::Queue; 13 use crate::sync::Fence; 14 use crate::sync::PipelineStages; 15 use crate::sync::Semaphore; 16 use crate::Error; 17 use crate::OomError; 18 use crate::SynchronizedVulkanObject; 19 use crate::VulkanObject; 20 use smallvec::SmallVec; 21 use std::error; 22 use std::fmt; 23 use std::marker::PhantomData; 24 25 /// Prototype for a submission that executes command buffers. 26 // TODO: example here 27 #[derive(Debug)] 28 pub struct SubmitCommandBufferBuilder<'a> { 29 wait_semaphores: SmallVec<[ash::vk::Semaphore; 16]>, 30 destination_stages: SmallVec<[ash::vk::PipelineStageFlags; 8]>, 31 signal_semaphores: SmallVec<[ash::vk::Semaphore; 16]>, 32 command_buffers: SmallVec<[ash::vk::CommandBuffer; 4]>, 33 fence: ash::vk::Fence, 34 marker: PhantomData<&'a ()>, 35 } 36 37 impl<'a> SubmitCommandBufferBuilder<'a> { 38 /// Builds a new empty `SubmitCommandBufferBuilder`. 39 #[inline] new() -> SubmitCommandBufferBuilder<'a>40 pub fn new() -> SubmitCommandBufferBuilder<'a> { 41 SubmitCommandBufferBuilder { 42 wait_semaphores: SmallVec::new(), 43 destination_stages: SmallVec::new(), 44 signal_semaphores: SmallVec::new(), 45 command_buffers: SmallVec::new(), 46 fence: ash::vk::Fence::null(), 47 marker: PhantomData, 48 } 49 } 50 51 /// Returns true if this builder will signal a fence when submitted. 52 /// 53 /// # Example 54 /// 55 /// ``` 56 /// use vulkano::command_buffer::submit::SubmitCommandBufferBuilder; 57 /// use vulkano::sync::Fence; 58 /// # let device: std::sync::Arc<vulkano::device::Device> = return; 59 /// 60 /// unsafe { 61 /// let fence = Fence::from_pool(device.clone()).unwrap(); 62 /// 63 /// let mut builder = SubmitCommandBufferBuilder::new(); 64 /// assert!(!builder.has_fence()); 65 /// builder.set_fence_signal(&fence); 66 /// assert!(builder.has_fence()); 67 /// } 68 /// ``` 69 #[inline] has_fence(&self) -> bool70 pub fn has_fence(&self) -> bool { 71 self.fence != ash::vk::Fence::null() 72 } 73 74 /// Adds an operation that signals a fence after this submission ends. 75 /// 76 /// # Example 77 /// 78 /// ``` 79 /// use std::time::Duration; 80 /// use vulkano::command_buffer::submit::SubmitCommandBufferBuilder; 81 /// use vulkano::sync::Fence; 82 /// # let device: std::sync::Arc<vulkano::device::Device> = return; 83 /// # let queue: std::sync::Arc<vulkano::device::Queue> = return; 84 /// 85 /// unsafe { 86 /// let fence = Fence::from_pool(device.clone()).unwrap(); 87 /// 88 /// let mut builder = SubmitCommandBufferBuilder::new(); 89 /// builder.set_fence_signal(&fence); 90 /// 91 /// builder.submit(&queue).unwrap(); 92 /// 93 /// // We must not destroy the fence before it is signaled. 94 /// fence.wait(Some(Duration::from_secs(5))).unwrap(); 95 /// } 96 /// ``` 97 /// 98 /// # Safety 99 /// 100 /// - The fence must not be signaled at the time when you call `submit()`. 101 /// 102 /// - If you use the fence for multiple submissions, only one at a time must be executed by the 103 /// GPU. In other words, you must submit one, wait for the fence to be signaled, then reset 104 /// the fence, and then only submit the second. 105 /// 106 /// - If you submit this builder, the fence must be kept alive until it is signaled by the GPU. 107 /// Destroying the fence earlier is an undefined behavior. 108 /// 109 /// - The fence, command buffers, and semaphores must all belong to the same device. 110 /// 111 #[inline] set_fence_signal(&mut self, fence: &'a Fence)112 pub unsafe fn set_fence_signal(&mut self, fence: &'a Fence) { 113 self.fence = fence.internal_object(); 114 } 115 116 /// Adds a semaphore to be waited upon before the command buffers are executed. 117 /// 118 /// Only the given `stages` of the command buffers added afterwards will wait upon 119 /// the semaphore. Other stages not included in `stages` can execute before waiting. 120 /// 121 /// # Safety 122 /// 123 /// - The stages must be supported by the device. 124 /// 125 /// - If you submit this builder, the semaphore must be kept alive until you are guaranteed 126 /// that the GPU has at least started executing the command buffers. 127 /// 128 /// - If you submit this builder, no other queue must be waiting on these semaphores. In other 129 /// words, each semaphore signal can only correspond to one semaphore wait. 130 /// 131 /// - If you submit this builder, the semaphores must be signaled when the queue execution 132 /// reaches this submission, or there must be one or more submissions in queues that are 133 /// going to signal these semaphores. In other words, you must not block the queue with 134 /// semaphores that can't get signaled. 135 /// 136 /// - The fence, command buffers, and semaphores must all belong to the same device. 137 /// 138 #[inline] add_wait_semaphore(&mut self, semaphore: &'a Semaphore, stages: PipelineStages)139 pub unsafe fn add_wait_semaphore(&mut self, semaphore: &'a Semaphore, stages: PipelineStages) { 140 debug_assert!(!ash::vk::PipelineStageFlags::from(stages).is_empty()); 141 // TODO: debug assert that the device supports the stages 142 self.wait_semaphores.push(semaphore.internal_object()); 143 self.destination_stages.push(stages.into()); 144 } 145 146 /// Adds a command buffer that is executed as part of this command. 147 /// 148 /// The command buffers are submitted in the order in which they are added. 149 /// 150 /// # Safety 151 /// 152 /// - If you submit this builder, the command buffer must be kept alive until you are 153 /// guaranteed that the GPU has finished executing it. 154 /// 155 /// - Any calls to vkCmdSetEvent, vkCmdResetEvent or vkCmdWaitEvents that have been recorded 156 /// into the command buffer must not reference any VkEvent that is referenced by any of 157 /// those commands that is pending execution on another queue. 158 /// TODO: rephrase ^ ? 159 /// 160 /// - The fence, command buffers, and semaphores must all belong to the same device. 161 /// 162 /// TODO: more here 163 /// 164 #[inline] add_command_buffer(&mut self, command_buffer: &'a UnsafeCommandBuffer)165 pub unsafe fn add_command_buffer(&mut self, command_buffer: &'a UnsafeCommandBuffer) { 166 self.command_buffers.push(command_buffer.internal_object()); 167 } 168 169 /// Returns the number of semaphores to signal. 170 /// 171 /// In other words, this is the number of times `add_signal_semaphore` has been called. 172 #[inline] num_signal_semaphores(&self) -> usize173 pub fn num_signal_semaphores(&self) -> usize { 174 self.signal_semaphores.len() 175 } 176 177 /// Adds a semaphore that is going to be signaled at the end of the submission. 178 /// 179 /// # Safety 180 /// 181 /// - If you submit this builder, the semaphore must be kept alive until you are guaranteed 182 /// that the GPU has finished executing this submission. 183 /// 184 /// - The semaphore must be in the unsignaled state when queue execution reaches this 185 /// submission. 186 /// 187 /// - The fence, command buffers, and semaphores must all belong to the same device. 188 /// 189 #[inline] add_signal_semaphore(&mut self, semaphore: &'a Semaphore)190 pub unsafe fn add_signal_semaphore(&mut self, semaphore: &'a Semaphore) { 191 self.signal_semaphores.push(semaphore.internal_object()); 192 } 193 194 /// Submits the command buffer to the given queue. 195 /// 196 /// > **Note**: This is an expensive operation, so you may want to merge as many builders as 197 /// > possible together and avoid submitting them one by one. 198 /// submit(self, queue: &Queue) -> Result<(), SubmitCommandBufferError>199 pub fn submit(self, queue: &Queue) -> Result<(), SubmitCommandBufferError> { 200 unsafe { 201 let fns = queue.device().fns(); 202 let queue = queue.internal_object_guard(); 203 204 debug_assert_eq!(self.wait_semaphores.len(), self.destination_stages.len()); 205 206 let batch = ash::vk::SubmitInfo { 207 wait_semaphore_count: self.wait_semaphores.len() as u32, 208 p_wait_semaphores: self.wait_semaphores.as_ptr(), 209 p_wait_dst_stage_mask: self.destination_stages.as_ptr(), 210 command_buffer_count: self.command_buffers.len() as u32, 211 p_command_buffers: self.command_buffers.as_ptr(), 212 signal_semaphore_count: self.signal_semaphores.len() as u32, 213 p_signal_semaphores: self.signal_semaphores.as_ptr(), 214 ..Default::default() 215 }; 216 217 check_errors(fns.v1_0.queue_submit(*queue, 1, &batch, self.fence))?; 218 Ok(()) 219 } 220 } 221 222 /// Merges this builder with another builder. 223 /// 224 /// # Panic 225 /// 226 /// Panics if both builders have a fence already set. 227 // TODO: create multiple batches instead merge(mut self, other: Self) -> Self228 pub fn merge(mut self, other: Self) -> Self { 229 assert!( 230 self.fence == ash::vk::Fence::null() || other.fence == ash::vk::Fence::null(), 231 "Can't merge two queue submits that both have a fence" 232 ); 233 234 self.wait_semaphores.extend(other.wait_semaphores); 235 self.destination_stages.extend(other.destination_stages); // TODO: meh? will be solved if we submit multiple batches 236 self.signal_semaphores.extend(other.signal_semaphores); 237 self.command_buffers.extend(other.command_buffers); 238 239 if self.fence == ash::vk::Fence::null() { 240 self.fence = other.fence; 241 } 242 243 self 244 } 245 } 246 247 /// Error that can happen when submitting the prototype. 248 #[derive(Copy, Clone, Debug, PartialEq, Eq)] 249 #[repr(u32)] 250 pub enum SubmitCommandBufferError { 251 /// Not enough memory. 252 OomError(OomError), 253 254 /// The connection to the device has been lost. 255 DeviceLost, 256 } 257 258 impl error::Error for SubmitCommandBufferError { 259 #[inline] source(&self) -> Option<&(dyn error::Error + 'static)>260 fn source(&self) -> Option<&(dyn error::Error + 'static)> { 261 match *self { 262 SubmitCommandBufferError::OomError(ref err) => Some(err), 263 _ => None, 264 } 265 } 266 } 267 268 impl fmt::Display for SubmitCommandBufferError { 269 #[inline] fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error>270 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { 271 write!( 272 fmt, 273 "{}", 274 match *self { 275 SubmitCommandBufferError::OomError(_) => "not enough memory", 276 SubmitCommandBufferError::DeviceLost => 277 "the connection to the device has been lost", 278 } 279 ) 280 } 281 } 282 283 impl From<Error> for SubmitCommandBufferError { 284 #[inline] from(err: Error) -> SubmitCommandBufferError285 fn from(err: Error) -> SubmitCommandBufferError { 286 match err { 287 err @ Error::OutOfHostMemory => SubmitCommandBufferError::OomError(OomError::from(err)), 288 err @ Error::OutOfDeviceMemory => { 289 SubmitCommandBufferError::OomError(OomError::from(err)) 290 } 291 Error::DeviceLost => SubmitCommandBufferError::DeviceLost, 292 _ => panic!("unexpected error: {:?}", err), 293 } 294 } 295 } 296 297 #[cfg(test)] 298 mod tests { 299 use super::*; 300 use crate::sync::Fence; 301 use std::time::Duration; 302 303 #[test] empty_submit()304 fn empty_submit() { 305 let (device, queue) = gfx_dev_and_queue!(); 306 let builder = SubmitCommandBufferBuilder::new(); 307 builder.submit(&queue).unwrap(); 308 } 309 310 #[test] signal_fence()311 fn signal_fence() { 312 unsafe { 313 let (device, queue) = gfx_dev_and_queue!(); 314 315 let fence = Fence::alloc(device.clone()).unwrap(); 316 assert!(!fence.ready().unwrap()); 317 318 let mut builder = SubmitCommandBufferBuilder::new(); 319 builder.set_fence_signal(&fence); 320 321 builder.submit(&queue).unwrap(); 322 fence.wait(Some(Duration::from_secs(5))).unwrap(); 323 assert!(fence.ready().unwrap()); 324 } 325 } 326 327 #[test] has_fence()328 fn has_fence() { 329 unsafe { 330 let (device, queue) = gfx_dev_and_queue!(); 331 332 let fence = Fence::alloc(device.clone()).unwrap(); 333 334 let mut builder = SubmitCommandBufferBuilder::new(); 335 assert!(!builder.has_fence()); 336 builder.set_fence_signal(&fence); 337 assert!(builder.has_fence()); 338 } 339 } 340 341 #[test] merge_both_have_fences()342 fn merge_both_have_fences() { 343 unsafe { 344 let (device, _) = gfx_dev_and_queue!(); 345 346 let fence1 = Fence::alloc(device.clone()).unwrap(); 347 let fence2 = Fence::alloc(device.clone()).unwrap(); 348 349 let mut builder1 = SubmitCommandBufferBuilder::new(); 350 builder1.set_fence_signal(&fence1); 351 let mut builder2 = SubmitCommandBufferBuilder::new(); 352 builder2.set_fence_signal(&fence2); 353 354 assert_should_panic!("Can't merge two queue submits that both have a fence", { 355 let _ = builder1.merge(builder2); 356 }); 357 } 358 } 359 } 360