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 use super::{Command, Resource, SyncCommandBuffer}; 11 pub use crate::command_buffer::commands::{ 12 bind_push::{ 13 SyncCommandBufferBuilderBindDescriptorSets, SyncCommandBufferBuilderBindVertexBuffer, 14 }, 15 secondary::SyncCommandBufferBuilderExecuteCommands, 16 }; 17 use crate::{ 18 buffer::{Buffer, Subbuffer}, 19 command_buffer::{ 20 pool::CommandPoolAlloc, 21 sys::{CommandBufferBeginInfo, UnsafeCommandBufferBuilder}, 22 CommandBufferBufferRangeUsage, CommandBufferBufferUsage, CommandBufferExecError, 23 CommandBufferImageRangeUsage, CommandBufferImageUsage, CommandBufferLevel, 24 CommandBufferResourcesUsage, ResourceUseRef, SecondaryCommandBufferBufferUsage, 25 SecondaryCommandBufferImageUsage, SecondaryCommandBufferResourcesUsage, 26 }, 27 descriptor_set::{DescriptorSetResources, DescriptorSetWithOffsets}, 28 device::{Device, DeviceOwned}, 29 image::{sys::Image, ImageAccess, ImageAspects, ImageLayout, ImageSubresourceRange}, 30 pipeline::{ 31 graphics::{ 32 color_blend::LogicOp, 33 depth_stencil::{CompareOp, StencilOps}, 34 input_assembly::{IndexType, PrimitiveTopology}, 35 rasterization::{CullMode, DepthBias, FrontFace, LineStipple}, 36 viewport::{Scissor, Viewport}, 37 }, 38 ComputePipeline, DynamicState, GraphicsPipeline, PipelineBindPoint, PipelineLayout, 39 }, 40 range_map::RangeMap, 41 range_set::RangeSet, 42 sync::{ 43 AccessFlags, BufferMemoryBarrier, DependencyInfo, ImageMemoryBarrier, PipelineMemoryAccess, 44 PipelineStages, 45 }, 46 DeviceSize, OomError, VulkanObject, 47 }; 48 use ahash::HashMap; 49 use smallvec::SmallVec; 50 use std::{ 51 collections::hash_map::Entry, 52 error::Error, 53 fmt::{Debug, Display, Error as FmtError, Formatter}, 54 ops::{Range, RangeInclusive}, 55 sync::Arc, 56 }; 57 58 /// Wrapper around `UnsafeCommandBufferBuilder` that handles synchronization for you. 59 /// 60 /// Each method of the `UnsafeCommandBufferBuilder` has an equivalent in this wrapper, except 61 /// for `pipeline_layout` which is automatically handled. This wrapper automatically builds 62 /// pipeline barriers, keeps used resources alive and implements the `CommandBuffer` trait. 63 /// 64 /// If this builder finds out that a command isn't valid because of synchronization reasons (eg. 65 /// trying to copy from a buffer to an image which share the same memory), then an error is 66 /// returned. 67 /// Note that all methods are still unsafe, because this builder doesn't check the validity of 68 /// the commands except for synchronization purposes. The builder may panic if you pass invalid 69 /// commands. 70 pub struct SyncCommandBufferBuilder { 71 inner: UnsafeCommandBufferBuilder, 72 level: CommandBufferLevel, 73 74 // Stores all the commands that were added to the sync builder. Some of them are maybe not 75 // submitted to the inner builder yet. 76 pub(in crate::command_buffer) commands: Vec<Box<dyn Command>>, 77 78 // Prototype for the pipeline barrier that must be submitted before flushing the commands 79 // in `commands`. 80 pending_barrier: DependencyInfo, 81 82 // Locations within commands that pipeline barriers were inserted. For debugging purposes. 83 // TODO: present only in cfg(debug_assertions)? 84 barriers: Vec<usize>, 85 86 // Only the commands before `first_unflushed` have already been sent to the inner 87 // `UnsafeCommandBufferBuilder`. 88 first_unflushed: usize, 89 90 // If we're currently inside a render pass, contains the index of the `CmdBeginRenderPass` 91 // command. 92 pub(in crate::command_buffer) latest_render_pass_enter: Option<usize>, 93 94 // Stores the current state of buffers and images that are in use by the command buffer. 95 buffers2: HashMap<Arc<Buffer>, RangeMap<DeviceSize, BufferState>>, 96 images2: HashMap<Arc<Image>, RangeMap<DeviceSize, ImageState>>, 97 98 // Resources and their accesses. Used for executing secondary command buffers in a primary. 99 secondary_resources_usage: SecondaryCommandBufferResourcesUsage, 100 101 // Current binding/setting state. 102 pub(in crate::command_buffer) current_state: CurrentState, 103 } 104 105 impl SyncCommandBufferBuilder { 106 /// Builds a new `SyncCommandBufferBuilder`. The parameters are the same as the 107 /// `UnsafeCommandBufferBuilder::new` function. 108 /// 109 /// # Safety 110 /// 111 /// See `UnsafeCommandBufferBuilder::new()`. 112 #[inline] new( pool_alloc: &CommandPoolAlloc, begin_info: CommandBufferBeginInfo, ) -> Result<SyncCommandBufferBuilder, OomError>113 pub unsafe fn new( 114 pool_alloc: &CommandPoolAlloc, 115 begin_info: CommandBufferBeginInfo, 116 ) -> Result<SyncCommandBufferBuilder, OomError> { 117 let level = pool_alloc.level(); 118 let inside_render_pass = level == CommandBufferLevel::Secondary 119 && begin_info 120 .inheritance_info 121 .as_ref() 122 .unwrap() 123 .render_pass 124 .is_some(); 125 let inner = UnsafeCommandBufferBuilder::new(pool_alloc, begin_info)?; 126 127 Ok(SyncCommandBufferBuilder::from_unsafe_cmd( 128 inner, 129 level, 130 inside_render_pass, 131 )) 132 } 133 134 /// Builds a `SyncCommandBufferBuilder` from an existing `UnsafeCommandBufferBuilder`. 135 /// 136 /// # Safety 137 /// 138 /// See `UnsafeCommandBufferBuilder::new()`. 139 /// 140 /// In addition to this, the `UnsafeCommandBufferBuilder` should be empty. If it isn't, then 141 /// you must take into account the fact that the `SyncCommandBufferBuilder` won't be aware of 142 /// any existing resource usage. 143 #[inline] from_unsafe_cmd( inner: UnsafeCommandBufferBuilder, level: CommandBufferLevel, inside_render_pass: bool, ) -> SyncCommandBufferBuilder144 pub unsafe fn from_unsafe_cmd( 145 inner: UnsafeCommandBufferBuilder, 146 level: CommandBufferLevel, 147 inside_render_pass: bool, 148 ) -> SyncCommandBufferBuilder { 149 let latest_render_pass_enter = if inside_render_pass { Some(0) } else { None }; 150 151 SyncCommandBufferBuilder { 152 inner, 153 level, 154 commands: Vec::new(), 155 pending_barrier: DependencyInfo::default(), 156 barriers: Vec::new(), 157 first_unflushed: 0, 158 latest_render_pass_enter, 159 buffers2: HashMap::default(), 160 images2: HashMap::default(), 161 secondary_resources_usage: Default::default(), 162 current_state: Default::default(), 163 } 164 } 165 166 /// Returns the binding/setting state. 167 #[inline] state(&self) -> CommandBufferBuilderState<'_>168 pub fn state(&self) -> CommandBufferBuilderState<'_> { 169 CommandBufferBuilderState { 170 current_state: &self.current_state, 171 } 172 } 173 174 /// Resets the binding/setting state. 175 /// 176 /// This must be called after any command that changes the state in an undefined way, e.g. 177 /// executing a secondary command buffer. 178 #[inline] reset_state(&mut self)179 pub fn reset_state(&mut self) { 180 self.current_state = Default::default(); 181 } 182 check_resource_conflicts( &self, resource: &(ResourceUseRef, Resource), ) -> Result<(), SyncCommandBufferBuilderError>183 pub(in crate::command_buffer) fn check_resource_conflicts( 184 &self, 185 resource: &(ResourceUseRef, Resource), 186 ) -> Result<(), SyncCommandBufferBuilderError> { 187 let &(current_use_ref, ref resource) = resource; 188 189 match *resource { 190 Resource::Buffer { 191 ref buffer, 192 ref range, 193 ref memory, 194 } => { 195 debug_assert!(AccessFlags::from(memory.stages).contains(memory.access)); 196 197 if let Some(previous_use_ref) = 198 self.find_buffer_conflict(buffer, range.clone(), memory) 199 { 200 return Err(SyncCommandBufferBuilderError::Conflict { 201 current_use_ref, 202 previous_use_ref, 203 }); 204 } 205 } 206 Resource::Image { 207 ref image, 208 ref subresource_range, 209 ref memory, 210 start_layout, 211 end_layout, 212 } => { 213 debug_assert!(memory.exclusive || start_layout == end_layout); 214 debug_assert!(AccessFlags::from(memory.stages).contains(memory.access)); 215 debug_assert!(end_layout != ImageLayout::Undefined); 216 debug_assert!(end_layout != ImageLayout::Preinitialized); 217 218 if let Some(previous_use) = self.find_image_conflict( 219 image, 220 subresource_range.clone(), 221 memory, 222 start_layout, 223 end_layout, 224 ) { 225 return Err(SyncCommandBufferBuilderError::Conflict { 226 current_use_ref, 227 previous_use_ref: previous_use, 228 }); 229 } 230 } 231 } 232 233 Ok(()) 234 } 235 find_buffer_conflict( &self, buffer: &Subbuffer<[u8]>, mut range: Range<DeviceSize>, memory: &PipelineMemoryAccess, ) -> Option<ResourceUseRef>236 fn find_buffer_conflict( 237 &self, 238 buffer: &Subbuffer<[u8]>, 239 mut range: Range<DeviceSize>, 240 memory: &PipelineMemoryAccess, 241 ) -> Option<ResourceUseRef> { 242 // Barriers work differently in render passes, so if we're in one, we can only insert a 243 // barrier before the start of the render pass. 244 let last_allowed_barrier_index = 245 self.latest_render_pass_enter.unwrap_or(self.commands.len()); 246 247 range.start += buffer.offset(); 248 range.end += buffer.offset(); 249 250 let range_map = self.buffers2.get(buffer.buffer())?; 251 252 for (_range, state) in range_map 253 .range(&range) 254 .filter(|(_range, state)| !state.resource_uses.is_empty()) 255 { 256 debug_assert!(state 257 .resource_uses 258 .iter() 259 .all(|resource_use| resource_use.command_index <= self.commands.len())); 260 261 if memory.exclusive || state.memory.exclusive { 262 // If there is a resource use at a position beyond where we can insert a 263 // barrier, then there is an unsolvable conflict. 264 if let Some(&use_ref) = state 265 .resource_uses 266 .iter() 267 .find(|resource_use| resource_use.command_index >= last_allowed_barrier_index) 268 { 269 return Some(use_ref); 270 } 271 } 272 } 273 274 None 275 } 276 find_image_conflict( &self, image: &dyn ImageAccess, mut subresource_range: ImageSubresourceRange, memory: &PipelineMemoryAccess, start_layout: ImageLayout, _end_layout: ImageLayout, ) -> Option<ResourceUseRef>277 fn find_image_conflict( 278 &self, 279 image: &dyn ImageAccess, 280 mut subresource_range: ImageSubresourceRange, 281 memory: &PipelineMemoryAccess, 282 start_layout: ImageLayout, 283 _end_layout: ImageLayout, 284 ) -> Option<ResourceUseRef> { 285 // Barriers work differently in render passes, so if we're in one, we can only insert a 286 // barrier before the start of the render pass. 287 let last_allowed_barrier_index = 288 self.latest_render_pass_enter.unwrap_or(self.commands.len()); 289 290 let inner = image.inner(); 291 subresource_range.array_layers.start += inner.first_layer; 292 subresource_range.array_layers.end += inner.first_layer; 293 subresource_range.mip_levels.start += inner.first_mipmap_level; 294 subresource_range.mip_levels.end += inner.first_mipmap_level; 295 296 let range_map = self.images2.get(inner.image)?; 297 298 for range in inner.image.iter_ranges(subresource_range) { 299 for (_range, state) in range_map 300 .range(&range) 301 .filter(|(_range, state)| !state.resource_uses.is_empty()) 302 { 303 debug_assert!(state 304 .resource_uses 305 .iter() 306 .all(|resource_use| resource_use.command_index <= self.commands.len())); 307 308 // If the command expects the image to be undefined, then we can't 309 // transition it, so use the current layout for both old and new layout. 310 let start_layout = if start_layout == ImageLayout::Undefined { 311 state.current_layout 312 } else { 313 start_layout 314 }; 315 316 if memory.exclusive 317 || state.memory.exclusive 318 || state.current_layout != start_layout 319 { 320 // If there is a resource use at a position beyond where we can insert a 321 // barrier, then there is an unsolvable conflict. 322 if let Some(&use_ref) = state.resource_uses.iter().find(|resource_use| { 323 resource_use.command_index >= last_allowed_barrier_index 324 }) { 325 return Some(use_ref); 326 } 327 } 328 } 329 } 330 331 None 332 } 333 334 /// Adds a command to be processed by the builder. 335 /// 336 /// The `resources` argument should contain each buffer or image used by the command. 337 /// The function will take care of handling the pipeline barrier or flushing. 338 /// 339 /// - The index of the resource within the `resources` slice maps to the resource accessed 340 /// through `Command::buffer(..)` or `Command::image(..)`. 341 /// - `PipelineMemoryAccess` must match the way the resource has been used. 342 /// - `start_layout` and `end_layout` designate the image layout that the image is expected to 343 /// be in when the command starts, and the image layout that the image will be transitioned to 344 /// during the command. When it comes to buffers, you should pass `Undefined` for both. add_resource(&mut self, resource: (ResourceUseRef, Resource))345 pub(in crate::command_buffer) fn add_resource(&mut self, resource: (ResourceUseRef, Resource)) { 346 let (use_ref, resource) = resource; 347 348 match resource { 349 Resource::Buffer { 350 buffer, 351 range, 352 memory, 353 } => { 354 self.add_buffer(use_ref, buffer, range, memory); 355 } 356 Resource::Image { 357 image, 358 subresource_range, 359 memory, 360 start_layout, 361 end_layout, 362 } => { 363 self.add_image( 364 use_ref, 365 image, 366 subresource_range, 367 memory, 368 start_layout, 369 end_layout, 370 ); 371 } 372 } 373 } 374 add_buffer( &mut self, use_ref: ResourceUseRef, buffer: Subbuffer<[u8]>, mut range: Range<DeviceSize>, memory: PipelineMemoryAccess, )375 fn add_buffer( 376 &mut self, 377 use_ref: ResourceUseRef, 378 buffer: Subbuffer<[u8]>, 379 mut range: Range<DeviceSize>, 380 memory: PipelineMemoryAccess, 381 ) { 382 self.secondary_resources_usage 383 .buffers 384 .push(SecondaryCommandBufferBufferUsage { 385 use_ref, 386 buffer: buffer.clone(), 387 range: range.clone(), 388 memory, 389 }); 390 391 // Barriers work differently in render passes, so if we're in one, we can only insert a 392 // barrier before the start of the render pass. 393 let last_allowed_barrier_index = self 394 .latest_render_pass_enter 395 .unwrap_or(self.commands.len() - 1); 396 397 range.start += buffer.offset(); 398 range.end += buffer.offset(); 399 400 let range_map = self 401 .buffers2 402 .entry(buffer.buffer().clone()) 403 .or_insert_with(|| { 404 [( 405 0..buffer.buffer().size(), 406 BufferState { 407 resource_uses: Vec::new(), 408 memory: PipelineMemoryAccess::default(), 409 exclusive_any: false, 410 }, 411 )] 412 .into_iter() 413 .collect() 414 }); 415 range_map.split_at(&range.start); 416 range_map.split_at(&range.end); 417 418 for (range, state) in range_map.range_mut(&range) { 419 if state.resource_uses.is_empty() { 420 // This is the first time we use this resource range in this command buffer. 421 state.resource_uses.push(use_ref); 422 state.memory = PipelineMemoryAccess { 423 stages: memory.stages, 424 access: memory.access, 425 exclusive: memory.exclusive, 426 }; 427 state.exclusive_any = memory.exclusive; 428 429 match self.level { 430 CommandBufferLevel::Primary => { 431 // To be safe, we insert a barrier for all stages and accesses before 432 // the first use, so that there are no hazards with any command buffer 433 // that was previously submitted to the same queue. 434 // This is rather overkill, but since command buffers don't know what 435 // will come before them, it's the only thing that works for now. 436 // TODO: come up with something better 437 let barrier = BufferMemoryBarrier { 438 src_stages: PipelineStages::ALL_COMMANDS, 439 src_access: AccessFlags::MEMORY_READ | AccessFlags::MEMORY_WRITE, 440 dst_stages: PipelineStages::ALL_COMMANDS, 441 dst_access: AccessFlags::MEMORY_READ | AccessFlags::MEMORY_WRITE, 442 range: range.clone(), 443 ..BufferMemoryBarrier::buffer(buffer.buffer().clone()) 444 }; 445 446 self.pending_barrier.buffer_memory_barriers.push(barrier); 447 } 448 CommandBufferLevel::Secondary => (), 449 } 450 } else { 451 // This resource range was used before in this command buffer. 452 453 // Find out if we have a collision with the pending commands. 454 if memory.exclusive || state.memory.exclusive { 455 // Collision found between `latest_command_id` and `collision_cmd_id`. 456 457 // We now want to modify the current pipeline barrier in order to handle the 458 // collision. But since the pipeline barrier is going to be submitted before 459 // the flushed commands, it would be a mistake if `collision_cmd_id` hasn't 460 // been flushed yet. 461 if state 462 .resource_uses 463 .iter() 464 .any(|resource_use| resource_use.command_index >= self.first_unflushed) 465 { 466 unsafe { 467 // Flush the pending barrier. 468 self.inner.pipeline_barrier(&self.pending_barrier); 469 self.pending_barrier.clear(); 470 self.barriers.push(self.first_unflushed); // Track inserted barriers 471 472 for command in 473 &mut self.commands[self.first_unflushed..last_allowed_barrier_index] 474 { 475 command.send(&mut self.inner); 476 } 477 478 self.first_unflushed = last_allowed_barrier_index; 479 } 480 } 481 482 // Modify the pipeline barrier to handle the collision. 483 self.pending_barrier 484 .buffer_memory_barriers 485 .push(BufferMemoryBarrier { 486 src_stages: state.memory.stages, 487 src_access: state.memory.access, 488 dst_stages: memory.stages, 489 dst_access: memory.access, 490 range: range.clone(), 491 ..BufferMemoryBarrier::buffer(buffer.buffer().clone()) 492 }); 493 494 // Update state. 495 state.memory = memory; 496 state.exclusive_any = true; 497 } else { 498 // There is no collision. Simply merge the stages and accesses. 499 state.memory.stages |= memory.stages; 500 state.memory.access |= memory.access; 501 } 502 503 state.resource_uses.push(use_ref); 504 } 505 } 506 } 507 add_image( &mut self, use_ref: ResourceUseRef, image: Arc<dyn ImageAccess>, mut subresource_range: ImageSubresourceRange, memory: PipelineMemoryAccess, start_layout: ImageLayout, end_layout: ImageLayout, )508 fn add_image( 509 &mut self, 510 use_ref: ResourceUseRef, 511 image: Arc<dyn ImageAccess>, 512 mut subresource_range: ImageSubresourceRange, 513 memory: PipelineMemoryAccess, 514 start_layout: ImageLayout, 515 end_layout: ImageLayout, 516 ) { 517 self.secondary_resources_usage 518 .images 519 .push(SecondaryCommandBufferImageUsage { 520 use_ref, 521 image: image.clone(), 522 subresource_range: subresource_range.clone(), 523 memory, 524 start_layout, 525 end_layout, 526 }); 527 528 // Barriers work differently in render passes, so if we're in one, we can only insert a 529 // barrier before the start of the render pass. 530 let last_allowed_barrier_index = self 531 .latest_render_pass_enter 532 .unwrap_or(self.commands.len() - 1); 533 534 let inner = image.inner(); 535 subresource_range.array_layers.start += inner.first_layer; 536 subresource_range.array_layers.end += inner.first_layer; 537 subresource_range.mip_levels.start += inner.first_mipmap_level; 538 subresource_range.mip_levels.end += inner.first_mipmap_level; 539 540 // VUID-VkImageMemoryBarrier2-image-03320 541 if !self 542 .device() 543 .enabled_features() 544 .separate_depth_stencil_layouts 545 && image 546 .format() 547 .aspects() 548 .contains(ImageAspects::DEPTH | ImageAspects::STENCIL) 549 { 550 subresource_range.aspects = ImageAspects::DEPTH | ImageAspects::STENCIL; 551 } 552 553 let range_map = self.images2.entry(inner.image.clone()).or_insert_with(|| { 554 [( 555 0..inner.image.range_size(), 556 match self.level { 557 CommandBufferLevel::Primary => { 558 // In a primary command buffer, the initial layout is determined 559 // by the image. 560 let initial_layout = if !image.is_layout_initialized() { 561 unsafe { 562 image.layout_initialized(); 563 } 564 565 image.initial_layout() 566 } else { 567 image.initial_layout_requirement() 568 }; 569 570 ImageState { 571 resource_uses: Vec::new(), 572 memory: PipelineMemoryAccess::default(), 573 exclusive_any: false, 574 initial_layout, 575 current_layout: initial_layout, 576 final_layout: image.final_layout_requirement(), 577 } 578 } 579 CommandBufferLevel::Secondary => { 580 // In a secondary command buffer, the initial layout is the layout 581 // of the first use. 582 ImageState { 583 resource_uses: Vec::new(), 584 memory: PipelineMemoryAccess::default(), 585 exclusive_any: false, 586 initial_layout: ImageLayout::Undefined, 587 current_layout: ImageLayout::Undefined, 588 final_layout: ImageLayout::Undefined, 589 } 590 } 591 }, 592 )] 593 .into_iter() 594 .collect() 595 }); 596 597 for range in inner.image.iter_ranges(subresource_range) { 598 range_map.split_at(&range.start); 599 range_map.split_at(&range.end); 600 601 for (range, state) in range_map.range_mut(&range) { 602 if state.resource_uses.is_empty() { 603 // This is the first time we use this resource range in this command buffer. 604 605 debug_assert_eq!(state.initial_layout, state.current_layout); 606 607 state.resource_uses.push(use_ref); 608 state.memory = PipelineMemoryAccess { 609 stages: memory.stages, 610 access: memory.access, 611 exclusive: memory.exclusive, 612 }; 613 state.exclusive_any = memory.exclusive; 614 state.current_layout = end_layout; 615 616 match self.level { 617 CommandBufferLevel::Primary => { 618 // To be safe, we insert a barrier for all stages and accesses before 619 // the first use, so that there are no hazards with any command buffer 620 // that was previously submitted to the same queue. 621 // This is rather overkill, but since command buffers don't know what 622 // will come before them, it's the only thing that works for now. 623 // TODO: come up with something better 624 let mut barrier = ImageMemoryBarrier { 625 src_stages: PipelineStages::ALL_COMMANDS, 626 src_access: AccessFlags::MEMORY_READ | AccessFlags::MEMORY_WRITE, 627 dst_stages: PipelineStages::ALL_COMMANDS, 628 dst_access: AccessFlags::MEMORY_READ | AccessFlags::MEMORY_WRITE, 629 old_layout: state.initial_layout, 630 new_layout: start_layout, 631 subresource_range: inner.image.range_to_subresources(range.clone()), 632 ..ImageMemoryBarrier::image(inner.image.clone()) 633 }; 634 635 // If the `new_layout` is Undefined or Preinitialized, this requires 636 // special handling. With the synchronization2 feature enabled, these 637 // values are permitted, as long as `new_layout` equals `old_layout`. 638 // Without synchronization2, these values are never permitted. 639 match barrier.new_layout { 640 ImageLayout::Undefined => { 641 // If the command expects Undefined, that really means it 642 // doesn't care about the layout at all and anything is valid. 643 // We try to keep the old layout, or do a "dummy" transition to 644 // the image's final layout if that isn't possible. 645 barrier.new_layout = 646 if !self.inner.device.enabled_features().synchronization2 647 && matches!( 648 barrier.old_layout, 649 ImageLayout::Undefined 650 | ImageLayout::Preinitialized 651 ) 652 { 653 image.final_layout_requirement() 654 } else { 655 barrier.old_layout 656 }; 657 } 658 ImageLayout::Preinitialized => { 659 // TODO: put this in find_image_conflict instead? 660 661 // The image must be in the Preinitialized layout already, we 662 // can't transition it. 663 if state.initial_layout != ImageLayout::Preinitialized { 664 panic!( 665 "The command requires the `Preinitialized layout`, but 666 the initial layout of the image is not `Preinitialized`" 667 ); 668 } 669 670 // The image is in the Preinitialized layout, but we can't keep 671 // that layout because of the limitations of 672 // pre-synchronization2 barriers. So an error is all we can do. 673 if !self.inner.device.enabled_features().synchronization2 { 674 panic!( 675 "The command requires the `Preinitialized` layout, \ 676 but this is not allowed in pipeline barriers without 677 the `synchronization2` feature enabled" 678 ); 679 } 680 } 681 _ => (), 682 } 683 684 // A layout transition is a write, so if we perform one, we 685 // need exclusive access. 686 if barrier.old_layout != barrier.new_layout { 687 state.exclusive_any = true; 688 } 689 690 self.pending_barrier.image_memory_barriers.push(barrier); 691 } 692 CommandBufferLevel::Secondary => { 693 state.initial_layout = start_layout; 694 } 695 } 696 } else { 697 // This resource range was used before in this command buffer. 698 699 // If the command expects the image to be undefined, then we can't 700 // transition it, so use the current layout for both old and new layout. 701 let start_layout = if start_layout == ImageLayout::Undefined { 702 state.current_layout 703 } else { 704 start_layout 705 }; 706 707 // Find out if we have a collision with the pending commands. 708 if memory.exclusive 709 || state.memory.exclusive 710 || state.current_layout != start_layout 711 { 712 // Collision found between `latest_command_id` and `collision_cmd_id`. 713 714 // We now want to modify the current pipeline barrier in order to handle the 715 // collision. But since the pipeline barrier is going to be submitted before 716 // the flushed commands, it would be a mistake if `collision_cmd_id` hasn't 717 // been flushed yet. 718 if state 719 .resource_uses 720 .iter() 721 .any(|resource_use| resource_use.command_index >= self.first_unflushed) 722 || state.current_layout != start_layout 723 { 724 unsafe { 725 // Flush the pending barrier. 726 self.inner.pipeline_barrier(&self.pending_barrier); 727 self.pending_barrier.clear(); 728 self.barriers.push(self.first_unflushed); // Track inserted barriers 729 730 for command in &mut self.commands 731 [self.first_unflushed..last_allowed_barrier_index] 732 { 733 command.send(&mut self.inner); 734 } 735 self.first_unflushed = last_allowed_barrier_index; 736 } 737 } 738 739 // Modify the pipeline barrier to handle the collision. 740 self.pending_barrier 741 .image_memory_barriers 742 .push(ImageMemoryBarrier { 743 src_stages: state.memory.stages, 744 src_access: state.memory.access, 745 dst_stages: memory.stages, 746 dst_access: memory.access, 747 old_layout: state.current_layout, 748 new_layout: start_layout, 749 subresource_range: inner.image.range_to_subresources(range.clone()), 750 ..ImageMemoryBarrier::image(inner.image.clone()) 751 }); 752 753 // Update state. 754 state.memory = memory; 755 state.exclusive_any = true; 756 if memory.exclusive || end_layout != ImageLayout::Undefined { 757 // Only modify the layout in case of a write, because buffer operations 758 // pass `Undefined` for the layout. While a buffer write *must* set the 759 // layout to `Undefined`, a buffer read must not touch it. 760 state.current_layout = end_layout; 761 } 762 } else { 763 // There is no collision. Simply merge the stages and accesses. 764 state.memory.stages |= memory.stages; 765 state.memory.access |= memory.access; 766 } 767 768 state.resource_uses.push(use_ref); 769 } 770 } 771 } 772 } 773 774 /// Builds the command buffer and turns it into a `SyncCommandBuffer`. 775 #[inline] build(mut self) -> Result<SyncCommandBuffer, OomError>776 pub fn build(mut self) -> Result<SyncCommandBuffer, OomError> { 777 debug_assert!(self.latest_render_pass_enter.is_none() || self.pending_barrier.is_empty()); 778 779 // The commands that haven't been sent to the inner command buffer yet need to be sent. 780 unsafe { 781 self.inner.pipeline_barrier(&self.pending_barrier); 782 self.pending_barrier.clear(); 783 let start = self.first_unflushed; 784 self.barriers.push(start); // Track inserted barriers 785 786 for command in &mut self.commands[start..] { 787 command.send(&mut self.inner); 788 } 789 } 790 791 // Transition images to their desired final layout. 792 if self.level == CommandBufferLevel::Primary { 793 unsafe { 794 for (image, range_map) in self.images2.iter_mut() { 795 for (range, state) in range_map 796 .iter_mut() 797 .filter(|(_range, state)| state.final_layout != state.current_layout) 798 { 799 self.pending_barrier 800 .image_memory_barriers 801 .push(ImageMemoryBarrier { 802 src_stages: state.memory.stages, 803 src_access: state.memory.access, 804 dst_stages: PipelineStages::TOP_OF_PIPE, 805 dst_access: AccessFlags::empty(), 806 old_layout: state.current_layout, 807 new_layout: state.final_layout, 808 subresource_range: image.range_to_subresources(range.clone()), 809 ..ImageMemoryBarrier::image(image.clone()) 810 }); 811 812 state.exclusive_any = true; 813 } 814 } 815 816 self.inner.pipeline_barrier(&self.pending_barrier); 817 } 818 } 819 820 let mut resource_usage = CommandBufferResourcesUsage { 821 buffers: self 822 .buffers2 823 .into_iter() 824 .map(|(buffer, ranges)| CommandBufferBufferUsage { 825 buffer, 826 ranges: ranges 827 .into_iter() 828 .filter(|(_range, state)| !state.resource_uses.is_empty()) 829 .map(|(range, state)| { 830 let first_use = state.resource_uses.into_iter().next(); 831 ( 832 range, 833 CommandBufferBufferRangeUsage { 834 first_use, 835 mutable: state.exclusive_any, 836 }, 837 ) 838 }) 839 .collect(), 840 }) 841 .collect(), 842 images: self 843 .images2 844 .into_iter() 845 .map(|(image, ranges)| CommandBufferImageUsage { 846 image, 847 ranges: ranges 848 .into_iter() 849 .filter(|(_range, state)| { 850 !state.resource_uses.is_empty() 851 || (self.level == CommandBufferLevel::Primary 852 && state.current_layout != state.final_layout) 853 }) 854 .map(|(range, mut state)| { 855 if self.level == CommandBufferLevel::Primary { 856 state.current_layout = state.final_layout; 857 } 858 859 let first_use = state.resource_uses.into_iter().next(); 860 ( 861 range, 862 CommandBufferImageRangeUsage { 863 first_use, 864 mutable: state.exclusive_any, 865 expected_layout: state.initial_layout, 866 final_layout: state.current_layout, 867 }, 868 ) 869 }) 870 .collect(), 871 }) 872 .collect(), 873 buffer_indices: Default::default(), 874 image_indices: Default::default(), 875 }; 876 877 resource_usage.buffer_indices = resource_usage 878 .buffers 879 .iter() 880 .enumerate() 881 .map(|(index, usage)| (usage.buffer.clone(), index)) 882 .collect(); 883 resource_usage.image_indices = resource_usage 884 .images 885 .iter() 886 .enumerate() 887 .map(|(index, usage)| (usage.image.clone(), index)) 888 .collect(); 889 890 Ok(SyncCommandBuffer { 891 inner: self.inner.build()?, 892 resources_usage: resource_usage, 893 secondary_resources_usage: self.secondary_resources_usage, 894 _commands: self.commands, 895 _barriers: self.barriers, 896 }) 897 } 898 } 899 900 unsafe impl DeviceOwned for SyncCommandBufferBuilder { 901 #[inline] device(&self) -> &Arc<Device>902 fn device(&self) -> &Arc<Device> { 903 self.inner.device() 904 } 905 } 906 907 impl Debug for SyncCommandBufferBuilder { fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError>908 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { 909 Debug::fmt(&self.inner, f) 910 } 911 } 912 913 /// Error returned if the builder detects that there's an unsolvable conflict. 914 #[derive(Debug, Clone)] 915 pub enum SyncCommandBufferBuilderError { 916 /// Unsolvable conflict. 917 Conflict { 918 current_use_ref: ResourceUseRef, 919 previous_use_ref: ResourceUseRef, 920 }, 921 922 ExecError(CommandBufferExecError), 923 } 924 925 impl Error for SyncCommandBufferBuilderError {} 926 927 impl Display for SyncCommandBufferBuilderError { fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError>928 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { 929 match self { 930 SyncCommandBufferBuilderError::Conflict { .. } => write!(f, "unsolvable conflict"), 931 SyncCommandBufferBuilderError::ExecError(err) => Display::fmt(err, f), 932 } 933 } 934 } 935 936 impl From<CommandBufferExecError> for SyncCommandBufferBuilderError { from(val: CommandBufferExecError) -> Self937 fn from(val: CommandBufferExecError) -> Self { 938 SyncCommandBufferBuilderError::ExecError(val) 939 } 940 } 941 942 // State of a resource during the building of the command buffer. 943 #[derive(Clone, PartialEq, Eq)] 944 struct BufferState { 945 // Lists every use of the resource. 946 resource_uses: Vec<ResourceUseRef>, 947 948 // Memory access of the command that last used this resource. 949 memory: PipelineMemoryAccess, 950 951 // True if the resource was used in exclusive mode at any point during the building of the 952 // command buffer. Also true if an image layout transition or queue transfer has been performed. 953 exclusive_any: bool, 954 } 955 956 // State of a resource during the building of the command buffer. 957 #[derive(Clone, PartialEq, Eq)] 958 struct ImageState { 959 // Lists every use of the resource. 960 resource_uses: Vec<ResourceUseRef>, 961 962 // Memory access of the command that last used this resource. 963 memory: PipelineMemoryAccess, 964 965 // True if the resource was used in exclusive mode at any point during the building of the 966 // command buffer. Also true if an image layout transition or queue transfer has been performed. 967 exclusive_any: bool, 968 969 // The layout that the image range must have when this command buffer is executed. 970 // Can be `Undefined` if we don't care. 971 initial_layout: ImageLayout, 972 973 // Current layout at this stage of the building. 974 current_layout: ImageLayout, 975 976 // The layout that the image range will have at the end of the command buffer. 977 // This is only used for primary command buffers. 978 final_layout: ImageLayout, 979 } 980 981 /// Holds the current binding and setting state. 982 #[derive(Default)] 983 pub(in crate::command_buffer) struct CurrentState { 984 pub(in crate::command_buffer) descriptor_sets: HashMap<PipelineBindPoint, DescriptorSetState>, 985 pub(in crate::command_buffer) index_buffer: Option<(Subbuffer<[u8]>, IndexType)>, 986 pub(in crate::command_buffer) pipeline_compute: Option<Arc<ComputePipeline>>, 987 pub(in crate::command_buffer) pipeline_graphics: Option<Arc<GraphicsPipeline>>, 988 pub(in crate::command_buffer) vertex_buffers: HashMap<u32, Subbuffer<[u8]>>, 989 990 pub(in crate::command_buffer) push_constants: RangeSet<u32>, 991 pub(in crate::command_buffer) push_constants_pipeline_layout: Option<Arc<PipelineLayout>>, 992 993 pub(in crate::command_buffer) blend_constants: Option<[f32; 4]>, 994 pub(in crate::command_buffer) color_write_enable: Option<SmallVec<[bool; 4]>>, 995 pub(in crate::command_buffer) cull_mode: Option<CullMode>, 996 pub(in crate::command_buffer) depth_bias: Option<DepthBias>, 997 pub(in crate::command_buffer) depth_bias_enable: Option<bool>, 998 pub(in crate::command_buffer) depth_bounds: Option<RangeInclusive<f32>>, 999 pub(in crate::command_buffer) depth_bounds_test_enable: Option<bool>, 1000 pub(in crate::command_buffer) depth_compare_op: Option<CompareOp>, 1001 pub(in crate::command_buffer) depth_test_enable: Option<bool>, 1002 pub(in crate::command_buffer) depth_write_enable: Option<bool>, 1003 pub(in crate::command_buffer) discard_rectangle: HashMap<u32, Scissor>, 1004 pub(in crate::command_buffer) front_face: Option<FrontFace>, 1005 pub(in crate::command_buffer) line_stipple: Option<LineStipple>, 1006 pub(in crate::command_buffer) line_width: Option<f32>, 1007 pub(in crate::command_buffer) logic_op: Option<LogicOp>, 1008 pub(in crate::command_buffer) patch_control_points: Option<u32>, 1009 pub(in crate::command_buffer) primitive_restart_enable: Option<bool>, 1010 pub(in crate::command_buffer) primitive_topology: Option<PrimitiveTopology>, 1011 pub(in crate::command_buffer) rasterizer_discard_enable: Option<bool>, 1012 pub(in crate::command_buffer) scissor: HashMap<u32, Scissor>, 1013 pub(in crate::command_buffer) scissor_with_count: Option<SmallVec<[Scissor; 2]>>, 1014 pub(in crate::command_buffer) stencil_compare_mask: StencilStateDynamic, 1015 pub(in crate::command_buffer) stencil_op: StencilOpStateDynamic, 1016 pub(in crate::command_buffer) stencil_reference: StencilStateDynamic, 1017 pub(in crate::command_buffer) stencil_test_enable: Option<bool>, 1018 pub(in crate::command_buffer) stencil_write_mask: StencilStateDynamic, 1019 pub(in crate::command_buffer) viewport: HashMap<u32, Viewport>, 1020 pub(in crate::command_buffer) viewport_with_count: Option<SmallVec<[Viewport; 2]>>, 1021 } 1022 1023 impl CurrentState { reset_dynamic_states( &mut self, states: impl IntoIterator<Item = DynamicState>, )1024 pub(in crate::command_buffer) fn reset_dynamic_states( 1025 &mut self, 1026 states: impl IntoIterator<Item = DynamicState>, 1027 ) { 1028 for state in states { 1029 match state { 1030 DynamicState::BlendConstants => self.blend_constants = None, 1031 DynamicState::ColorWriteEnable => self.color_write_enable = None, 1032 DynamicState::CullMode => self.cull_mode = None, 1033 DynamicState::DepthBias => self.depth_bias = None, 1034 DynamicState::DepthBiasEnable => self.depth_bias_enable = None, 1035 DynamicState::DepthBounds => self.depth_bounds = None, 1036 DynamicState::DepthBoundsTestEnable => self.depth_bounds_test_enable = None, 1037 DynamicState::DepthCompareOp => self.depth_compare_op = None, 1038 DynamicState::DepthTestEnable => self.depth_test_enable = None, 1039 DynamicState::DepthWriteEnable => self.depth_write_enable = None, 1040 DynamicState::DiscardRectangle => self.discard_rectangle.clear(), 1041 DynamicState::ExclusiveScissor => (), // TODO; 1042 DynamicState::FragmentShadingRate => (), // TODO: 1043 DynamicState::FrontFace => self.front_face = None, 1044 DynamicState::LineStipple => self.line_stipple = None, 1045 DynamicState::LineWidth => self.line_width = None, 1046 DynamicState::LogicOp => self.logic_op = None, 1047 DynamicState::PatchControlPoints => self.patch_control_points = None, 1048 DynamicState::PrimitiveRestartEnable => self.primitive_restart_enable = None, 1049 DynamicState::PrimitiveTopology => self.primitive_topology = None, 1050 DynamicState::RasterizerDiscardEnable => self.rasterizer_discard_enable = None, 1051 DynamicState::RayTracingPipelineStackSize => (), // TODO: 1052 DynamicState::SampleLocations => (), // TODO: 1053 DynamicState::Scissor => self.scissor.clear(), 1054 DynamicState::ScissorWithCount => self.scissor_with_count = None, 1055 DynamicState::StencilCompareMask => self.stencil_compare_mask = Default::default(), 1056 DynamicState::StencilOp => self.stencil_op = Default::default(), 1057 DynamicState::StencilReference => self.stencil_reference = Default::default(), 1058 DynamicState::StencilTestEnable => self.stencil_test_enable = None, 1059 DynamicState::StencilWriteMask => self.stencil_write_mask = Default::default(), 1060 DynamicState::VertexInput => (), // TODO: 1061 DynamicState::VertexInputBindingStride => (), // TODO: 1062 DynamicState::Viewport => self.viewport.clear(), 1063 DynamicState::ViewportCoarseSampleOrder => (), // TODO: 1064 DynamicState::ViewportShadingRatePalette => (), // TODO: 1065 DynamicState::ViewportWScaling => (), // TODO: 1066 DynamicState::ViewportWithCount => self.viewport_with_count = None, 1067 DynamicState::TessellationDomainOrigin => (), // TODO: 1068 DynamicState::DepthClampEnable => (), // TODO: 1069 DynamicState::PolygonMode => (), // TODO: 1070 DynamicState::RasterizationSamples => (), // TODO: 1071 DynamicState::SampleMask => (), // TODO: 1072 DynamicState::AlphaToCoverageEnable => (), // TODO: 1073 DynamicState::AlphaToOneEnable => (), // TODO: 1074 DynamicState::LogicOpEnable => (), // TODO: 1075 DynamicState::ColorBlendEnable => (), // TODO: 1076 DynamicState::ColorBlendEquation => (), // TODO: 1077 DynamicState::ColorWriteMask => (), // TODO: 1078 DynamicState::RasterizationStream => (), // TODO: 1079 DynamicState::ConservativeRasterizationMode => (), // TODO: 1080 DynamicState::ExtraPrimitiveOverestimationSize => (), // TODO: 1081 DynamicState::DepthClipEnable => (), // TODO: 1082 DynamicState::SampleLocationsEnable => (), // TODO: 1083 DynamicState::ColorBlendAdvanced => (), // TODO: 1084 DynamicState::ProvokingVertexMode => (), // TODO: 1085 DynamicState::LineRasterizationMode => (), // TODO: 1086 DynamicState::LineStippleEnable => (), // TODO: 1087 DynamicState::DepthClipNegativeOneToOne => (), // TODO: 1088 DynamicState::ViewportWScalingEnable => (), // TODO: 1089 DynamicState::ViewportSwizzle => (), // TODO: 1090 DynamicState::CoverageToColorEnable => (), // TODO: 1091 DynamicState::CoverageToColorLocation => (), // TODO: 1092 DynamicState::CoverageModulationMode => (), // TODO: 1093 DynamicState::CoverageModulationTableEnable => (), // TODO: 1094 DynamicState::CoverageModulationTable => (), // TODO: 1095 DynamicState::ShadingRateImageEnable => (), // TODO: 1096 DynamicState::RepresentativeFragmentTestEnable => (), // TODO: 1097 DynamicState::CoverageReductionMode => (), // TODO: 1098 } 1099 } 1100 } 1101 invalidate_descriptor_sets( &mut self, pipeline_bind_point: PipelineBindPoint, pipeline_layout: Arc<PipelineLayout>, first_set: u32, num_descriptor_sets: u32, ) -> &mut DescriptorSetState1102 pub(in crate::command_buffer) fn invalidate_descriptor_sets( 1103 &mut self, 1104 pipeline_bind_point: PipelineBindPoint, 1105 pipeline_layout: Arc<PipelineLayout>, 1106 first_set: u32, 1107 num_descriptor_sets: u32, 1108 ) -> &mut DescriptorSetState { 1109 match self.descriptor_sets.entry(pipeline_bind_point) { 1110 Entry::Vacant(entry) => entry.insert(DescriptorSetState { 1111 descriptor_sets: Default::default(), 1112 pipeline_layout, 1113 }), 1114 Entry::Occupied(entry) => { 1115 let state = entry.into_mut(); 1116 1117 let invalidate_from = if state.pipeline_layout.handle() == pipeline_layout.handle() 1118 { 1119 // If we're still using the exact same layout, then of course it's compatible. 1120 None 1121 } else if state.pipeline_layout.push_constant_ranges() 1122 != pipeline_layout.push_constant_ranges() 1123 { 1124 // If the push constant ranges don't match, 1125 // all bound descriptor sets are disturbed. 1126 Some(0) 1127 } else { 1128 // Find the first descriptor set layout in the current pipeline layout that 1129 // isn't compatible with the corresponding set in the new pipeline layout. 1130 // If an incompatible set was found, all bound sets from that slot onwards will 1131 // be disturbed. 1132 let current_layouts = state.pipeline_layout.set_layouts(); 1133 let new_layouts = pipeline_layout.set_layouts(); 1134 let max = (current_layouts.len() as u32).min(first_set + num_descriptor_sets); 1135 (0..max).find(|&num| { 1136 let num = num as usize; 1137 !current_layouts[num].is_compatible_with(&new_layouts[num]) 1138 }) 1139 }; 1140 1141 if let Some(invalidate_from) = invalidate_from { 1142 // Remove disturbed sets and set new pipeline layout. 1143 state 1144 .descriptor_sets 1145 .retain(|&num, _| num < invalidate_from); 1146 state.pipeline_layout = pipeline_layout; 1147 } else if (first_set + num_descriptor_sets) as usize 1148 >= state.pipeline_layout.set_layouts().len() 1149 { 1150 // New layout is a superset of the old one. 1151 state.pipeline_layout = pipeline_layout; 1152 } 1153 1154 state 1155 } 1156 } 1157 } 1158 } 1159 1160 pub(in crate::command_buffer) struct DescriptorSetState { 1161 pub(in crate::command_buffer) descriptor_sets: HashMap<u32, SetOrPush>, 1162 pub(in crate::command_buffer) pipeline_layout: Arc<PipelineLayout>, 1163 } 1164 1165 #[derive(Clone)] 1166 pub enum SetOrPush { 1167 Set(DescriptorSetWithOffsets), 1168 Push(DescriptorSetResources), 1169 } 1170 1171 impl SetOrPush { 1172 #[inline] resources(&self) -> &DescriptorSetResources1173 pub fn resources(&self) -> &DescriptorSetResources { 1174 match self { 1175 Self::Set(set) => set.as_ref().0.resources(), 1176 Self::Push(resources) => resources, 1177 } 1178 } 1179 1180 #[inline] dynamic_offsets(&self) -> &[u32]1181 pub fn dynamic_offsets(&self) -> &[u32] { 1182 match self { 1183 Self::Set(set) => set.as_ref().1, 1184 Self::Push(_) => &[], 1185 } 1186 } 1187 } 1188 1189 /// Allows you to retrieve the current state of a command buffer builder. 1190 #[derive(Clone, Copy)] 1191 pub struct CommandBufferBuilderState<'a> { 1192 current_state: &'a CurrentState, 1193 } 1194 1195 impl<'a> CommandBufferBuilderState<'a> { 1196 /// Returns the descriptor set currently bound to a given set number, or `None` if nothing has 1197 /// been bound yet. 1198 #[inline] descriptor_set( &self, pipeline_bind_point: PipelineBindPoint, set_num: u32, ) -> Option<&'a SetOrPush>1199 pub fn descriptor_set( 1200 &self, 1201 pipeline_bind_point: PipelineBindPoint, 1202 set_num: u32, 1203 ) -> Option<&'a SetOrPush> { 1204 self.current_state 1205 .descriptor_sets 1206 .get(&pipeline_bind_point) 1207 .and_then(|state| state.descriptor_sets.get(&set_num)) 1208 } 1209 1210 /// Returns the pipeline layout that describes all currently bound descriptor sets. 1211 /// 1212 /// This can be the layout used to perform the last bind operation, but it can also be the 1213 /// layout of an earlier bind if it was compatible with more recent binds. 1214 #[inline] descriptor_sets_pipeline_layout( &self, pipeline_bind_point: PipelineBindPoint, ) -> Option<&'a Arc<PipelineLayout>>1215 pub fn descriptor_sets_pipeline_layout( 1216 &self, 1217 pipeline_bind_point: PipelineBindPoint, 1218 ) -> Option<&'a Arc<PipelineLayout>> { 1219 self.current_state 1220 .descriptor_sets 1221 .get(&pipeline_bind_point) 1222 .map(|state| &state.pipeline_layout) 1223 } 1224 1225 /// Returns the index buffer currently bound, or `None` if nothing has been bound yet. 1226 #[inline] index_buffer(&self) -> Option<(&'a Subbuffer<[u8]>, IndexType)>1227 pub fn index_buffer(&self) -> Option<(&'a Subbuffer<[u8]>, IndexType)> { 1228 self.current_state 1229 .index_buffer 1230 .as_ref() 1231 .map(|(b, i)| (b, *i)) 1232 } 1233 1234 /// Returns the compute pipeline currently bound, or `None` if nothing has been bound yet. 1235 #[inline] pipeline_compute(&self) -> Option<&'a Arc<ComputePipeline>>1236 pub fn pipeline_compute(&self) -> Option<&'a Arc<ComputePipeline>> { 1237 self.current_state.pipeline_compute.as_ref() 1238 } 1239 1240 /// Returns the graphics pipeline currently bound, or `None` if nothing has been bound yet. 1241 #[inline] pipeline_graphics(&self) -> Option<&'a Arc<GraphicsPipeline>>1242 pub fn pipeline_graphics(&self) -> Option<&'a Arc<GraphicsPipeline>> { 1243 self.current_state.pipeline_graphics.as_ref() 1244 } 1245 1246 /// Returns the vertex buffer currently bound to a given binding slot number, or `None` if 1247 /// nothing has been bound yet. 1248 #[inline] vertex_buffer(&self, binding_num: u32) -> Option<&'a Subbuffer<[u8]>>1249 pub fn vertex_buffer(&self, binding_num: u32) -> Option<&'a Subbuffer<[u8]>> { 1250 self.current_state.vertex_buffers.get(&binding_num) 1251 } 1252 1253 /// Returns a set containing push constant bytes that have been set. 1254 #[inline] push_constants(&self) -> &'a RangeSet<u32>1255 pub fn push_constants(&self) -> &'a RangeSet<u32> { 1256 &self.current_state.push_constants 1257 } 1258 1259 /// Returns the pipeline layout that describes the current push constants. 1260 /// 1261 /// This is the layout used to perform the last push constant write operation. 1262 #[inline] push_constants_pipeline_layout(&self) -> Option<&'a Arc<PipelineLayout>>1263 pub fn push_constants_pipeline_layout(&self) -> Option<&'a Arc<PipelineLayout>> { 1264 self.current_state.push_constants_pipeline_layout.as_ref() 1265 } 1266 1267 /// Returns the current blend constants, or `None` if nothing has been set yet. 1268 #[inline] blend_constants(&self) -> Option<[f32; 4]>1269 pub fn blend_constants(&self) -> Option<[f32; 4]> { 1270 self.current_state.blend_constants 1271 } 1272 1273 /// Returns the current color write enable settings, or `None` if nothing has been set yet. 1274 #[inline] color_write_enable(&self) -> Option<&'a [bool]>1275 pub fn color_write_enable(&self) -> Option<&'a [bool]> { 1276 self.current_state 1277 .color_write_enable 1278 .as_ref() 1279 .map(|x| x.as_slice()) 1280 } 1281 1282 /// Returns the current cull mode, or `None` if nothing has been set yet. 1283 #[inline] cull_mode(&self) -> Option<CullMode>1284 pub fn cull_mode(&self) -> Option<CullMode> { 1285 self.current_state.cull_mode 1286 } 1287 1288 /// Returns the current depth bias settings, or `None` if nothing has been set yet. 1289 #[inline] depth_bias(&self) -> Option<DepthBias>1290 pub fn depth_bias(&self) -> Option<DepthBias> { 1291 self.current_state.depth_bias 1292 } 1293 1294 /// Returns whether depth bias is enabled, or `None` if nothing has been set yet. 1295 #[inline] depth_bias_enable(&self) -> Option<bool>1296 pub fn depth_bias_enable(&self) -> Option<bool> { 1297 self.current_state.depth_bias_enable 1298 } 1299 1300 /// Returns the current depth bounds settings, or `None` if nothing has been set yet. 1301 #[inline] depth_bounds(&self) -> Option<RangeInclusive<f32>>1302 pub fn depth_bounds(&self) -> Option<RangeInclusive<f32>> { 1303 self.current_state.depth_bounds.clone() 1304 } 1305 1306 /// Returns whether depth bound testing is enabled, or `None` if nothing has been set yet. 1307 #[inline] depth_bounds_test_enable(&self) -> Option<bool>1308 pub fn depth_bounds_test_enable(&self) -> Option<bool> { 1309 self.current_state.depth_bounds_test_enable 1310 } 1311 1312 /// Returns the current depth compare op, or `None` if nothing has been set yet. 1313 #[inline] depth_compare_op(&self) -> Option<CompareOp>1314 pub fn depth_compare_op(&self) -> Option<CompareOp> { 1315 self.current_state.depth_compare_op 1316 } 1317 1318 /// Returns whether depth testing is enabled, or `None` if nothing has been set yet. 1319 #[inline] depth_test_enable(&self) -> Option<bool>1320 pub fn depth_test_enable(&self) -> Option<bool> { 1321 self.current_state.depth_test_enable 1322 } 1323 1324 /// Returns whether depth write is enabled, or `None` if nothing has been set yet. 1325 #[inline] depth_write_enable(&self) -> Option<bool>1326 pub fn depth_write_enable(&self) -> Option<bool> { 1327 self.current_state.depth_write_enable 1328 } 1329 1330 /// Returns the current discard rectangles, or `None` if nothing has been set yet. 1331 #[inline] discard_rectangle(&self, num: u32) -> Option<&'a Scissor>1332 pub fn discard_rectangle(&self, num: u32) -> Option<&'a Scissor> { 1333 self.current_state.discard_rectangle.get(&num) 1334 } 1335 1336 /// Returns the current front face, or `None` if nothing has been set yet. 1337 #[inline] front_face(&self) -> Option<FrontFace>1338 pub fn front_face(&self) -> Option<FrontFace> { 1339 self.current_state.front_face 1340 } 1341 1342 /// Returns the current line stipple settings, or `None` if nothing has been set yet. 1343 #[inline] line_stipple(&self) -> Option<LineStipple>1344 pub fn line_stipple(&self) -> Option<LineStipple> { 1345 self.current_state.line_stipple 1346 } 1347 1348 /// Returns the current line width, or `None` if nothing has been set yet. 1349 #[inline] line_width(&self) -> Option<f32>1350 pub fn line_width(&self) -> Option<f32> { 1351 self.current_state.line_width 1352 } 1353 1354 /// Returns the current logic op, or `None` if nothing has been set yet. 1355 #[inline] logic_op(&self) -> Option<LogicOp>1356 pub fn logic_op(&self) -> Option<LogicOp> { 1357 self.current_state.logic_op 1358 } 1359 1360 /// Returns the current number of patch control points, or `None` if nothing has been set yet. 1361 #[inline] patch_control_points(&self) -> Option<u32>1362 pub fn patch_control_points(&self) -> Option<u32> { 1363 self.current_state.patch_control_points 1364 } 1365 1366 /// Returns whether primitive restart is enabled, or `None` if nothing has been set yet. 1367 #[inline] primitive_restart_enable(&self) -> Option<bool>1368 pub fn primitive_restart_enable(&self) -> Option<bool> { 1369 self.current_state.primitive_restart_enable 1370 } 1371 1372 /// Returns the current primitive topology, or `None` if nothing has been set yet. 1373 #[inline] primitive_topology(&self) -> Option<PrimitiveTopology>1374 pub fn primitive_topology(&self) -> Option<PrimitiveTopology> { 1375 self.current_state.primitive_topology 1376 } 1377 1378 /// Returns whether rasterizer discard is enabled, or `None` if nothing has been set yet. 1379 #[inline] rasterizer_discard_enable(&self) -> Option<bool>1380 pub fn rasterizer_discard_enable(&self) -> Option<bool> { 1381 self.current_state.rasterizer_discard_enable 1382 } 1383 1384 /// Returns the current scissor for a given viewport slot, or `None` if nothing has been set 1385 /// yet. 1386 #[inline] scissor(&self, num: u32) -> Option<&'a Scissor>1387 pub fn scissor(&self, num: u32) -> Option<&'a Scissor> { 1388 self.current_state.scissor.get(&num) 1389 } 1390 1391 /// Returns the current viewport-with-count settings, or `None` if nothing has been set yet. 1392 #[inline] scissor_with_count(&self) -> Option<&'a [Scissor]>1393 pub fn scissor_with_count(&self) -> Option<&'a [Scissor]> { 1394 self.current_state 1395 .scissor_with_count 1396 .as_ref() 1397 .map(|x| x.as_slice()) 1398 } 1399 1400 /// Returns the current stencil compare masks. 1401 #[inline] stencil_compare_mask(&self) -> StencilStateDynamic1402 pub fn stencil_compare_mask(&self) -> StencilStateDynamic { 1403 self.current_state.stencil_compare_mask 1404 } 1405 1406 /// Returns the current stencil ops. 1407 #[inline] stencil_op(&self) -> StencilOpStateDynamic1408 pub fn stencil_op(&self) -> StencilOpStateDynamic { 1409 self.current_state.stencil_op 1410 } 1411 1412 /// Returns the current stencil references. 1413 #[inline] stencil_reference(&self) -> StencilStateDynamic1414 pub fn stencil_reference(&self) -> StencilStateDynamic { 1415 self.current_state.stencil_reference 1416 } 1417 1418 /// Returns whether stencil testing is enabled, or `None` if nothing has been set yet. 1419 #[inline] stencil_test_enable(&self) -> Option<bool>1420 pub fn stencil_test_enable(&self) -> Option<bool> { 1421 self.current_state.stencil_test_enable 1422 } 1423 1424 /// Returns the current stencil write masks. 1425 #[inline] stencil_write_mask(&self) -> StencilStateDynamic1426 pub fn stencil_write_mask(&self) -> StencilStateDynamic { 1427 self.current_state.stencil_write_mask 1428 } 1429 1430 /// Returns the current viewport for a given viewport slot, or `None` if nothing has been set 1431 /// yet. 1432 #[inline] viewport(&self, num: u32) -> Option<&'a Viewport>1433 pub fn viewport(&self, num: u32) -> Option<&'a Viewport> { 1434 self.current_state.viewport.get(&num) 1435 } 1436 1437 /// Returns the current viewport-with-count settings, or `None` if nothing has been set yet. 1438 #[inline] viewport_with_count(&self) -> Option<&'a [Viewport]>1439 pub fn viewport_with_count(&self) -> Option<&'a [Viewport]> { 1440 self.current_state 1441 .viewport_with_count 1442 .as_ref() 1443 .map(|x| x.as_slice()) 1444 } 1445 } 1446 1447 /// Holds the current stencil state of a command buffer builder. 1448 #[derive(Clone, Copy, Debug, Default)] 1449 pub struct StencilStateDynamic { 1450 pub front: Option<u32>, 1451 pub back: Option<u32>, 1452 } 1453 1454 /// Holds the current per-face stencil op state of a command buffer builder. 1455 #[derive(Clone, Copy, Debug, Default)] 1456 pub struct StencilOpStateDynamic { 1457 pub front: Option<StencilOps>, 1458 pub back: Option<StencilOps>, 1459 } 1460