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