• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2016 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 //! Contains `SyncCommandBufferBuilder` and `SyncCommandBuffer`.
11 //!
12 //! # How pipeline stages work in Vulkan
13 //!
14 //! Imagine you create a command buffer that contains 10 dispatch commands, and submit that command
15 //! buffer. According to the Vulkan specs, the implementation is free to execute the 10 commands
16 //! simultaneously.
17 //!
18 //! Now imagine that the command buffer contains 10 draw commands instead. Contrary to the dispatch
19 //! commands, the draw pipeline contains multiple stages: draw indirect, vertex input, vertex shader,
20 //! ..., fragment shader, late fragment test, color output. When there are multiple stages, the
21 //! implementations must start and end the stages in order. In other words it can start the draw
22 //! indirect stage of all 10 commands, then start the vertex input stage of all 10 commands, and so
23 //! on. But it can't for example start the fragment shader stage of a command before starting the
24 //! vertex shader stage of another command. Same thing for ending the stages in the right order.
25 //!
26 //! Depending on the type of the command, the pipeline stages are different. Compute shaders use the
27 //! compute stage, while transfer commands use the transfer stage. The compute and transfer stages
28 //! aren't ordered.
29 //!
30 //! When you submit multiple command buffers to a queue, the implementation doesn't do anything in
31 //! particular and behaves as if the command buffers were appended to one another. Therefore if you
32 //! submit a command buffer with 10 dispatch commands, followed with another command buffer with 5
33 //! dispatch commands, then the implementation can perform the 15 commands simultaneously.
34 //!
35 //! ## Introducing barriers
36 //!
37 //! In some situations this is not the desired behaviour. If you add a command that writes to a
38 //! buffer followed with another command that reads that buffer, you don't want them to execute
39 //! simultaneously. Instead you want the second one to wait until the first one is finished. This
40 //! is done by adding a pipeline barrier between the two commands.
41 //!
42 //! A pipeline barriers has a source stage and a destination stage (plus various other things).
43 //! A barrier represents a split in the list of commands. When you add it, the stages of the commands
44 //! before the barrier corresponding to the source stage of the barrier, must finish before the
45 //! stages of the commands after the barrier corresponding to the destination stage of the barrier
46 //! can start.
47 //!
48 //! For example if you add a barrier that transitions from the compute stage to the compute stage,
49 //! then the compute stage of all the commands before the barrier must end before the compute stage
50 //! of all the commands after the barrier can start. This is appropriate for the example about
51 //! writing then reading the same buffer.
52 //!
53 //! ## Batching barriers
54 //!
55 //! Since barriers are "expensive" (as the queue must block), vulkano attempts to group as many
56 //! pipeline barriers as possible into one.
57 //!
58 //! Adding a command to a sync command buffer builder does not immediately add it to the underlying
59 //! command buffer builder. Instead the command is added to a queue, and the builder keeps a
60 //! prototype of a barrier that must be added before the commands in the queue are flushed.
61 //!
62 //! Whenever you add a command, the builder will find out whether a barrier is needed before the
63 //! command. If so, it will try to merge this barrier with the prototype and add the command to the
64 //! queue. If not possible, the queue will be entirely flushed and the command added to a fresh new
65 //! queue with a fresh new barrier prototype.
66 
67 pub use self::builder::{
68     CommandBufferBuilderState, SetOrPush, StencilOpStateDynamic, StencilStateDynamic,
69     SyncCommandBufferBuilder, SyncCommandBufferBuilderBindDescriptorSets,
70     SyncCommandBufferBuilderBindVertexBuffer, SyncCommandBufferBuilderError,
71     SyncCommandBufferBuilderExecuteCommands,
72 };
73 use super::{
74     sys::{UnsafeCommandBuffer, UnsafeCommandBufferBuilder},
75     CommandBufferResourcesUsage, SecondaryCommandBufferResourcesUsage,
76 };
77 use crate::{
78     buffer::Subbuffer,
79     device::{Device, DeviceOwned},
80     image::{ImageAccess, ImageLayout, ImageSubresourceRange},
81     sync::PipelineMemoryAccess,
82     DeviceSize,
83 };
84 use std::{
85     fmt::{Debug, Error as FmtError, Formatter},
86     ops::Range,
87     sync::Arc,
88 };
89 
90 mod builder;
91 
92 /// Command buffer built from a `SyncCommandBufferBuilder` that provides utilities to handle
93 /// synchronization.
94 pub struct SyncCommandBuffer {
95     // The actual Vulkan command buffer.
96     inner: UnsafeCommandBuffer,
97 
98     // List of commands used by the command buffer. Used to hold the various resources that are
99     // being used.
100     _commands: Vec<Box<dyn Command>>,
101 
102     // Locations within commands that pipeline barriers were inserted. For debugging purposes.
103     // TODO: present only in cfg(debug_assertions)?
104     _barriers: Vec<usize>,
105 
106     // Resources accessed by this command buffer.
107     resources_usage: CommandBufferResourcesUsage,
108 
109     // Resources and their accesses. Used for executing secondary command buffers in a primary.
110     secondary_resources_usage: SecondaryCommandBufferResourcesUsage,
111 }
112 
113 impl SyncCommandBuffer {
114     #[inline]
resources_usage(&self) -> &CommandBufferResourcesUsage115     pub(super) fn resources_usage(&self) -> &CommandBufferResourcesUsage {
116         &self.resources_usage
117     }
118 
119     #[inline]
secondary_resources_usage(&self) -> &SecondaryCommandBufferResourcesUsage120     pub(super) fn secondary_resources_usage(&self) -> &SecondaryCommandBufferResourcesUsage {
121         &self.secondary_resources_usage
122     }
123 }
124 
125 impl AsRef<UnsafeCommandBuffer> for SyncCommandBuffer {
126     #[inline]
as_ref(&self) -> &UnsafeCommandBuffer127     fn as_ref(&self) -> &UnsafeCommandBuffer {
128         &self.inner
129     }
130 }
131 
132 unsafe impl DeviceOwned for SyncCommandBuffer {
133     #[inline]
device(&self) -> &Arc<Device>134     fn device(&self) -> &Arc<Device> {
135         self.inner.device()
136     }
137 }
138 
139 /// Type of resource whose state is to be tracked.
140 #[derive(Clone)]
141 pub(super) enum Resource {
142     Buffer {
143         buffer: Subbuffer<[u8]>,
144         range: Range<DeviceSize>,
145         memory: PipelineMemoryAccess,
146     },
147     Image {
148         image: Arc<dyn ImageAccess>,
149         subresource_range: ImageSubresourceRange,
150         memory: PipelineMemoryAccess,
151         start_layout: ImageLayout,
152         end_layout: ImageLayout,
153     },
154 }
155 
156 // Trait for single commands within the list of commands.
157 pub(super) trait Command: Send + Sync {
158     // Returns a user-friendly name for the command, for error reporting purposes.
name(&self) -> &'static str159     fn name(&self) -> &'static str;
160 
161     // Sends the command to the `UnsafeCommandBufferBuilder`. Calling this method twice on the same
162     // object will likely lead to a panic.
send(&self, out: &mut UnsafeCommandBufferBuilder)163     unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder);
164 }
165 
166 impl Debug for dyn Command {
fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError>167     fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
168         f.write_str(self.name())
169     }
170 }
171 
172 #[cfg(test)]
173 mod tests {
174     use super::*;
175     use crate::{
176         buffer::{Buffer, BufferCreateInfo, BufferUsage},
177         command_buffer::{
178             allocator::{
179                 CommandBufferAllocator, CommandBufferBuilderAlloc, StandardCommandBufferAllocator,
180             },
181             sys::CommandBufferBeginInfo,
182             AutoCommandBufferBuilder, CommandBufferLevel, CommandBufferUsage,
183             PrimaryCommandBufferAbstract,
184         },
185         descriptor_set::{
186             allocator::StandardDescriptorSetAllocator,
187             layout::{
188                 DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorSetLayoutCreateInfo,
189                 DescriptorType,
190             },
191             PersistentDescriptorSet, WriteDescriptorSet,
192         },
193         memory::allocator::{AllocationCreateInfo, MemoryUsage, StandardMemoryAllocator},
194         pipeline::{layout::PipelineLayoutCreateInfo, PipelineBindPoint, PipelineLayout},
195         sampler::{Sampler, SamplerCreateInfo},
196         shader::ShaderStages,
197         sync::GpuFuture,
198     };
199 
200     #[test]
basic_creation()201     fn basic_creation() {
202         unsafe {
203             let (device, queue) = gfx_dev_and_queue!();
204 
205             let allocator = StandardCommandBufferAllocator::new(device, Default::default());
206 
207             let builder_alloc = allocator
208                 .allocate(queue.queue_family_index(), CommandBufferLevel::Primary, 1)
209                 .unwrap()
210                 .next()
211                 .unwrap();
212 
213             SyncCommandBufferBuilder::new(
214                 builder_alloc.inner(),
215                 CommandBufferBeginInfo {
216                     usage: CommandBufferUsage::MultipleSubmit,
217                     ..Default::default()
218                 },
219             )
220             .unwrap();
221         }
222     }
223 
224     #[test]
secondary_conflicting_writes()225     fn secondary_conflicting_writes() {
226         unsafe {
227             let (device, queue) = gfx_dev_and_queue!();
228 
229             let cb_allocator =
230                 StandardCommandBufferAllocator::new(device.clone(), Default::default());
231             let cbb = AutoCommandBufferBuilder::primary(
232                 &cb_allocator,
233                 queue.queue_family_index(),
234                 CommandBufferUsage::OneTimeSubmit,
235             )
236             .unwrap();
237 
238             let memory_allocator = StandardMemoryAllocator::new_default(device);
239             // Create a tiny test buffer
240             let buffer = Buffer::from_data(
241                 &memory_allocator,
242                 BufferCreateInfo {
243                     usage: BufferUsage::TRANSFER_DST,
244                     ..Default::default()
245                 },
246                 AllocationCreateInfo {
247                     usage: MemoryUsage::Upload,
248                     ..Default::default()
249                 },
250                 0u32,
251             )
252             .unwrap();
253 
254             cbb.build()
255                 .unwrap()
256                 .execute(queue.clone())
257                 .unwrap()
258                 .then_signal_fence_and_flush()
259                 .unwrap()
260                 .wait(None)
261                 .unwrap();
262 
263             // Two secondary command buffers that both write to the buffer
264             let secondary = (0..2)
265                 .map(|_| {
266                     let mut builder = AutoCommandBufferBuilder::secondary(
267                         &cb_allocator,
268                         queue.queue_family_index(),
269                         CommandBufferUsage::SimultaneousUse,
270                         Default::default(),
271                     )
272                     .unwrap();
273                     builder
274                         .fill_buffer(buffer.clone().into_slice(), 42)
275                         .unwrap();
276                     Arc::new(builder.build().unwrap())
277                 })
278                 .collect::<Vec<_>>();
279 
280             let allocs = cb_allocator
281                 .allocate(queue.queue_family_index(), CommandBufferLevel::Primary, 2)
282                 .unwrap()
283                 .collect::<Vec<_>>();
284 
285             {
286                 let mut builder = SyncCommandBufferBuilder::new(
287                     allocs[0].inner(),
288                     CommandBufferBeginInfo {
289                         usage: CommandBufferUsage::SimultaneousUse,
290                         ..Default::default()
291                     },
292                 )
293                 .unwrap();
294 
295                 // Add both secondary command buffers using separate execute_commands calls.
296                 secondary.iter().cloned().for_each(|secondary| {
297                     let mut ec = builder.execute_commands();
298                     ec.add(secondary);
299                     ec.submit().unwrap();
300                 });
301 
302                 let primary = builder.build().unwrap();
303                 let names = primary
304                     ._commands
305                     .iter()
306                     .map(|c| c.name())
307                     .collect::<Vec<_>>();
308 
309                 // Ensure that the builder added a barrier between the two writes
310                 assert_eq!(&names, &["execute_commands", "execute_commands"]);
311                 assert_eq!(&primary._barriers, &[0, 1]);
312             }
313 
314             {
315                 let mut builder = SyncCommandBufferBuilder::new(
316                     allocs[1].inner(),
317                     CommandBufferBeginInfo {
318                         usage: CommandBufferUsage::SimultaneousUse,
319                         ..Default::default()
320                     },
321                 )
322                 .unwrap();
323 
324                 // Add a single execute_commands for all secondary command buffers at once
325                 let mut ec = builder.execute_commands();
326                 secondary.into_iter().for_each(|secondary| {
327                     ec.add(secondary);
328                 });
329                 ec.submit().unwrap();
330             }
331         }
332     }
333 
334     #[test]
vertex_buffer_binding()335     fn vertex_buffer_binding() {
336         unsafe {
337             let (device, queue) = gfx_dev_and_queue!();
338 
339             let cb_allocator =
340                 StandardCommandBufferAllocator::new(device.clone(), Default::default());
341             let builder_alloc = cb_allocator
342                 .allocate(queue.queue_family_index(), CommandBufferLevel::Primary, 1)
343                 .unwrap()
344                 .next()
345                 .unwrap();
346             let mut sync = SyncCommandBufferBuilder::new(
347                 builder_alloc.inner(),
348                 CommandBufferBeginInfo {
349                     usage: CommandBufferUsage::MultipleSubmit,
350                     ..Default::default()
351                 },
352             )
353             .unwrap();
354 
355             let memory_allocator = StandardMemoryAllocator::new_default(device);
356             let buf = Buffer::from_data(
357                 &memory_allocator,
358                 BufferCreateInfo {
359                     usage: BufferUsage::VERTEX_BUFFER,
360                     ..Default::default()
361                 },
362                 AllocationCreateInfo {
363                     usage: MemoryUsage::Upload,
364                     ..Default::default()
365                 },
366                 0u32,
367             )
368             .unwrap();
369             let mut buf_builder = sync.bind_vertex_buffers();
370             buf_builder.add(buf.into_bytes());
371             buf_builder.submit(1);
372 
373             assert!(sync.state().vertex_buffer(0).is_none());
374             assert!(sync.state().vertex_buffer(1).is_some());
375             assert!(sync.state().vertex_buffer(2).is_none());
376         }
377     }
378 
379     #[test]
descriptor_set_binding()380     fn descriptor_set_binding() {
381         unsafe {
382             let (device, queue) = gfx_dev_and_queue!();
383 
384             let cb_allocator =
385                 StandardCommandBufferAllocator::new(device.clone(), Default::default());
386             let builder_alloc = cb_allocator
387                 .allocate(queue.queue_family_index(), CommandBufferLevel::Primary, 1)
388                 .unwrap()
389                 .next()
390                 .unwrap();
391             let mut sync = SyncCommandBufferBuilder::new(
392                 builder_alloc.inner(),
393                 CommandBufferBeginInfo {
394                     usage: CommandBufferUsage::MultipleSubmit,
395                     ..Default::default()
396                 },
397             )
398             .unwrap();
399             let set_layout = DescriptorSetLayout::new(
400                 device.clone(),
401                 DescriptorSetLayoutCreateInfo {
402                     bindings: [(
403                         0,
404                         DescriptorSetLayoutBinding {
405                             stages: ShaderStages::all_graphics(),
406                             ..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::Sampler)
407                         },
408                     )]
409                     .into(),
410                     ..Default::default()
411                 },
412             )
413             .unwrap();
414             let pipeline_layout = PipelineLayout::new(
415                 device.clone(),
416                 PipelineLayoutCreateInfo {
417                     set_layouts: [set_layout.clone(), set_layout.clone()].into(),
418                     ..Default::default()
419                 },
420             )
421             .unwrap();
422 
423             let ds_allocator = StandardDescriptorSetAllocator::new(device.clone());
424 
425             let set = PersistentDescriptorSet::new(
426                 &ds_allocator,
427                 set_layout.clone(),
428                 [WriteDescriptorSet::sampler(
429                     0,
430                     Sampler::new(device.clone(), SamplerCreateInfo::simple_repeat_linear())
431                         .unwrap(),
432                 )],
433             )
434             .unwrap();
435 
436             let mut set_builder = sync.bind_descriptor_sets();
437             set_builder.add(set.clone());
438             set_builder.submit(PipelineBindPoint::Graphics, pipeline_layout.clone(), 1);
439 
440             assert!(sync
441                 .state()
442                 .descriptor_set(PipelineBindPoint::Compute, 0)
443                 .is_none());
444             assert!(sync
445                 .state()
446                 .descriptor_set(PipelineBindPoint::Graphics, 0)
447                 .is_none());
448             assert!(sync
449                 .state()
450                 .descriptor_set(PipelineBindPoint::Graphics, 1)
451                 .is_some());
452             assert!(sync
453                 .state()
454                 .descriptor_set(PipelineBindPoint::Graphics, 2)
455                 .is_none());
456 
457             let mut set_builder = sync.bind_descriptor_sets();
458             set_builder.add(set);
459             set_builder.submit(PipelineBindPoint::Graphics, pipeline_layout, 0);
460 
461             assert!(sync
462                 .state()
463                 .descriptor_set(PipelineBindPoint::Graphics, 0)
464                 .is_some());
465             assert!(sync
466                 .state()
467                 .descriptor_set(PipelineBindPoint::Graphics, 1)
468                 .is_some());
469 
470             let pipeline_layout = PipelineLayout::new(
471                 device.clone(),
472                 PipelineLayoutCreateInfo {
473                     set_layouts: [
474                         DescriptorSetLayout::new(device.clone(), Default::default()).unwrap(),
475                         set_layout.clone(),
476                     ]
477                     .into(),
478                     ..Default::default()
479                 },
480             )
481             .unwrap();
482 
483             let set = PersistentDescriptorSet::new(
484                 &ds_allocator,
485                 set_layout,
486                 [WriteDescriptorSet::sampler(
487                     0,
488                     Sampler::new(device, SamplerCreateInfo::simple_repeat_linear()).unwrap(),
489                 )],
490             )
491             .unwrap();
492 
493             let mut set_builder = sync.bind_descriptor_sets();
494             set_builder.add(set);
495             set_builder.submit(PipelineBindPoint::Graphics, pipeline_layout, 1);
496 
497             assert!(sync
498                 .state()
499                 .descriptor_set(PipelineBindPoint::Graphics, 0)
500                 .is_none());
501             assert!(sync
502                 .state()
503                 .descriptor_set(PipelineBindPoint::Graphics, 1)
504                 .is_some());
505         }
506     }
507 }
508