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