• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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