• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2016 The vulkano developers
2 // Licensed under the Apache License, Version 2.0
3 // <LICENSE-APACHE or
4 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT
5 // license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
6 // at your option. All files in the project carrying such
7 // notice may not be copied, modified, or distributed except
8 // according to those terms.
9 
10 pub use self::commands::SyncCommandBufferBuilderBindDescriptorSets;
11 pub use self::commands::SyncCommandBufferBuilderBindVertexBuffer;
12 pub use self::commands::SyncCommandBufferBuilderExecuteCommands;
13 use super::Command;
14 use super::ResourceFinalState;
15 use super::ResourceKey;
16 use super::ResourceLocation;
17 use super::SyncCommandBuffer;
18 use crate::buffer::BufferAccess;
19 use crate::command_buffer::pool::UnsafeCommandPoolAlloc;
20 use crate::command_buffer::sys::UnsafeCommandBufferBuilder;
21 use crate::command_buffer::sys::UnsafeCommandBufferBuilderPipelineBarrier;
22 use crate::command_buffer::CommandBufferExecError;
23 use crate::command_buffer::CommandBufferLevel;
24 use crate::command_buffer::CommandBufferUsage;
25 use crate::command_buffer::ImageUninitializedSafe;
26 use crate::descriptor_set::DescriptorSet;
27 use crate::device::Device;
28 use crate::device::DeviceOwned;
29 use crate::image::ImageLayout;
30 use crate::pipeline::{ComputePipelineAbstract, GraphicsPipelineAbstract, PipelineBindPoint};
31 use crate::render_pass::FramebufferAbstract;
32 use crate::sync::AccessFlags;
33 use crate::sync::PipelineMemoryAccess;
34 use crate::sync::PipelineStages;
35 use crate::OomError;
36 use fnv::FnvHashMap;
37 use std::borrow::Cow;
38 use std::collections::hash_map::Entry;
39 use std::error;
40 use std::fmt;
41 use std::sync::Arc;
42 
43 #[path = "commands.rs"]
44 mod commands;
45 
46 /// Wrapper around `UnsafeCommandBufferBuilder` that handles synchronization for you.
47 ///
48 /// Each method of the `UnsafeCommandBufferBuilder` has an equivalent in this wrapper, except
49 /// for `pipeline_layout` which is automatically handled. This wrapper automatically builds
50 /// pipeline barriers, keeps used resources alive and implements the `CommandBuffer` trait.
51 ///
52 /// Since the implementation needs to cache commands in a `Vec`, most methods have additional
53 /// `Send + Sync + 'static` trait requirements on their generics.
54 ///
55 /// If this builder finds out that a command isn't valid because of synchronization reasons (eg.
56 /// trying to copy from a buffer to an image which share the same memory), then an error is
57 /// returned.
58 /// Note that all methods are still unsafe, because this builder doesn't check the validity of
59 /// the commands except for synchronization purposes. The builder may panic if you pass invalid
60 /// commands.
61 pub struct SyncCommandBufferBuilder {
62     // The actual Vulkan command buffer builder.
63     inner: UnsafeCommandBufferBuilder,
64 
65     // Stores all the commands that were added to the sync builder. Some of them are maybe not
66     // submitted to the inner builder yet.
67     // Each command owns the resources it uses (buffers, images, pipelines, descriptor sets etc.),
68     // references to any of these must be indirect in the form of a command index + resource id.
69     commands: Vec<Arc<dyn Command + Send + Sync>>,
70 
71     // Prototype for the pipeline barrier that must be submitted before flushing the commands
72     // in `commands`.
73     pending_barrier: UnsafeCommandBufferBuilderPipelineBarrier,
74 
75     // Locations within commands that pipeline barriers were inserted. For debugging purposes.
76     // TODO: present only in cfg(debug_assertions)?
77     barriers: Vec<usize>,
78 
79     // Only the commands before `first_unflushed` have already been sent to the inner
80     // `UnsafeCommandBufferBuilder`.
81     first_unflushed: usize,
82 
83     // If we're currently inside a render pass, contains the index of the `CmdBeginRenderPass`
84     // command.
85     latest_render_pass_enter: Option<usize>,
86 
87     // Stores the current state of buffers and images that are in use by the command buffer.
88     resources: FnvHashMap<ResourceKey, ResourceState>,
89 
90     // Resources and their accesses. Used for executing secondary command buffers in a primary.
91     buffers: Vec<(ResourceLocation, PipelineMemoryAccess)>,
92     images: Vec<(
93         ResourceLocation,
94         PipelineMemoryAccess,
95         ImageLayout,
96         ImageLayout,
97         ImageUninitializedSafe,
98     )>,
99 
100     // State of bindings.
101     bindings: BindingState,
102 
103     // `true` if the builder has been put in an inconsistent state. This happens when
104     // `append_command` throws an error, because some changes to the internal state have already
105     // been made at that point and can't be reverted.
106     // TODO: throw the error in `append_command` _before_ any state changes are made,
107     // so that this is no longer needed.
108     is_poisoned: bool,
109 
110     // True if we're a secondary command buffer.
111     is_secondary: bool,
112 }
113 
114 impl SyncCommandBufferBuilder {
115     /// Builds a new `SyncCommandBufferBuilder`. The parameters are the same as the
116     /// `UnsafeCommandBufferBuilder::new` function.
117     ///
118     /// # Safety
119     ///
120     /// See `UnsafeCommandBufferBuilder::new()`.
new<F>( pool_alloc: &UnsafeCommandPoolAlloc, level: CommandBufferLevel<F>, usage: CommandBufferUsage, ) -> Result<SyncCommandBufferBuilder, OomError> where F: FramebufferAbstract,121     pub unsafe fn new<F>(
122         pool_alloc: &UnsafeCommandPoolAlloc,
123         level: CommandBufferLevel<F>,
124         usage: CommandBufferUsage,
125     ) -> Result<SyncCommandBufferBuilder, OomError>
126     where
127         F: FramebufferAbstract,
128     {
129         let (is_secondary, inside_render_pass) = match level {
130             CommandBufferLevel::Primary => (false, false),
131             CommandBufferLevel::Secondary(ref inheritance) => {
132                 (true, inheritance.render_pass.is_some())
133             }
134         };
135 
136         let cmd = UnsafeCommandBufferBuilder::new(pool_alloc, level, usage)?;
137         Ok(SyncCommandBufferBuilder::from_unsafe_cmd(
138             cmd,
139             is_secondary,
140             inside_render_pass,
141         ))
142     }
143 
144     /// Builds a `SyncCommandBufferBuilder` from an existing `UnsafeCommandBufferBuilder`.
145     ///
146     /// # Safety
147     ///
148     /// See `UnsafeCommandBufferBuilder::new()`.
149     ///
150     /// In addition to this, the `UnsafeCommandBufferBuilder` should be empty. If it isn't, then
151     /// you must take into account the fact that the `SyncCommandBufferBuilder` won't be aware of
152     /// any existing resource usage.
153     #[inline]
from_unsafe_cmd( cmd: UnsafeCommandBufferBuilder, is_secondary: bool, inside_render_pass: bool, ) -> SyncCommandBufferBuilder154     pub unsafe fn from_unsafe_cmd(
155         cmd: UnsafeCommandBufferBuilder,
156         is_secondary: bool,
157         inside_render_pass: bool,
158     ) -> SyncCommandBufferBuilder {
159         let latest_render_pass_enter = if inside_render_pass { Some(0) } else { None };
160 
161         SyncCommandBufferBuilder {
162             inner: cmd,
163             commands: Vec::new(),
164             pending_barrier: UnsafeCommandBufferBuilderPipelineBarrier::new(),
165             barriers: Vec::new(),
166             first_unflushed: 0,
167             latest_render_pass_enter,
168             resources: FnvHashMap::default(),
169             buffers: Vec::new(),
170             images: Vec::new(),
171             bindings: Default::default(),
172             is_poisoned: false,
173             is_secondary,
174         }
175     }
176 
177     // Adds a command to be processed by the builder.
178     //
179     // The `resources` argument should contain each buffer or image used by the command.
180     // The function will take care of handling the pipeline barrier or flushing.
181     //
182     // - The index of the resource within the `resources` slice maps to the resource accessed
183     //   through `Command::buffer(..)` or `Command::image(..)`.
184     // - `PipelineMemoryAccess` must match the way the resource has been used.
185     // - `start_layout` and `end_layout` designate the image layout that the image is expected to be
186     //   in when the command starts, and the image layout that the image will be transitioned to
187     //   during the command. When it comes to buffers, you should pass `Undefined` for both.
188     #[inline]
append_command<C>( &mut self, command: C, resources: &[( KeyTy, Option<( PipelineMemoryAccess, ImageLayout, ImageLayout, ImageUninitializedSafe, )>, )], ) -> Result<(), SyncCommandBufferBuilderError> where C: Command + Send + Sync + 'static,189     fn append_command<C>(
190         &mut self,
191         command: C,
192         resources: &[(
193             KeyTy,
194             Option<(
195                 PipelineMemoryAccess,
196                 ImageLayout,
197                 ImageLayout,
198                 ImageUninitializedSafe,
199             )>,
200         )],
201     ) -> Result<(), SyncCommandBufferBuilderError>
202     where
203         C: Command + Send + Sync + 'static,
204     {
205         // TODO: see comment for the `is_poisoned` member in the struct
206         assert!(
207             !self.is_poisoned,
208             "The builder has been put in an inconsistent state by a previous error"
209         );
210 
211         // Note that we don't submit the command to the inner command buffer yet.
212         let (latest_command_id, end) = {
213             self.commands.push(Arc::new(command));
214             let latest_command_id = self.commands.len() - 1;
215             let end = self.latest_render_pass_enter.unwrap_or(latest_command_id);
216             (latest_command_id, end)
217         };
218         let mut last_cmd_buffer = 0;
219         let mut last_cmd_image = 0;
220 
221         for &(resource_ty, resource) in resources {
222             if let Some((memory, start_layout, end_layout, image_uninitialized_safe)) = resource {
223                 // Anti-dumbness checks.
224                 debug_assert!(memory.exclusive || start_layout == end_layout);
225                 debug_assert!(memory.access.is_compatible_with(&memory.stages));
226                 debug_assert!(resource_ty != KeyTy::Image || end_layout != ImageLayout::Undefined);
227                 debug_assert!(
228                     resource_ty != KeyTy::Buffer || start_layout == ImageLayout::Undefined
229                 );
230                 debug_assert!(resource_ty != KeyTy::Buffer || end_layout == ImageLayout::Undefined);
231                 debug_assert_ne!(end_layout, ImageLayout::Preinitialized);
232 
233                 let (resource_key, resource_index) = match resource_ty {
234                     KeyTy::Buffer => {
235                         let buffer = self.commands[latest_command_id].buffer(last_cmd_buffer);
236                         (ResourceKey::from(buffer), last_cmd_buffer)
237                     }
238                     KeyTy::Image => {
239                         let image = self.commands[latest_command_id].image(last_cmd_image);
240                         (ResourceKey::from(image), last_cmd_image)
241                     }
242                 };
243 
244                 match self.resources.entry(resource_key) {
245                     // Situation where this resource was used before in this command buffer.
246                     Entry::Occupied(mut entry) => {
247                         // `collision_cmd_ids` contains the IDs of the commands that we are potentially
248                         // colliding with.
249                         let collision_cmd_ids = &entry.get().command_ids;
250                         debug_assert!(collision_cmd_ids.iter().all(|id| *id <= latest_command_id));
251 
252                         let entry_key_resource_index = entry.get().resource_index;
253 
254                         // Find out if we have a collision with the pending commands.
255                         if memory.exclusive
256                             || entry.get().memory.exclusive
257                             || entry.get().current_layout != start_layout
258                         {
259                             // Collision found between `latest_command_id` and `collision_cmd_id`.
260 
261                             // We now want to modify the current pipeline barrier in order to handle the
262                             // collision. But since the pipeline barrier is going to be submitted before
263                             // the flushed commands, it would be a mistake if `collision_cmd_id` hasn't
264                             // been flushed yet.
265                             let first_unflushed_cmd_id = self.first_unflushed;
266 
267                             if collision_cmd_ids
268                                 .iter()
269                                 .any(|command_id| *command_id >= first_unflushed_cmd_id)
270                                 || entry.get().current_layout != start_layout
271                             {
272                                 unsafe {
273                                     // Flush the pending barrier.
274                                     self.inner.pipeline_barrier(&self.pending_barrier);
275                                     self.pending_barrier =
276                                         UnsafeCommandBufferBuilderPipelineBarrier::new();
277 
278                                     // Flush the commands if possible, or return an error if not possible.
279                                     {
280                                         let start = self.first_unflushed;
281                                         self.barriers.push(start); // Track inserted barriers
282 
283                                         if let Some(collision_cmd_id) = collision_cmd_ids
284                                             .iter()
285                                             .find(|command_id| **command_id >= end)
286                                         {
287                                             // TODO: see comment for the `is_poisoned` member in the struct
288                                             self.is_poisoned = true;
289 
290                                             let cmd1 = &self.commands[*collision_cmd_id];
291                                             let cmd2 = &self.commands[latest_command_id];
292 
293                                             return Err(SyncCommandBufferBuilderError::Conflict {
294                                                 command1_name: cmd1.name(),
295                                                 command1_param: match resource_ty {
296                                                     KeyTy::Buffer => {
297                                                         cmd1.buffer_name(entry_key_resource_index)
298                                                     }
299                                                     KeyTy::Image => {
300                                                         cmd1.image_name(entry_key_resource_index)
301                                                     }
302                                                 },
303                                                 command1_offset: *collision_cmd_id,
304 
305                                                 command2_name: cmd2.name(),
306                                                 command2_param: match resource_ty {
307                                                     KeyTy::Buffer => {
308                                                         cmd2.buffer_name(resource_index)
309                                                     }
310                                                     KeyTy::Image => cmd2.image_name(resource_index),
311                                                 },
312                                                 command2_offset: latest_command_id,
313                                             });
314                                         }
315                                         for command in &mut self.commands[start..end] {
316                                             command.send(&mut self.inner);
317                                         }
318                                         self.first_unflushed = end;
319                                     }
320                                 }
321                             }
322 
323                             entry.get_mut().command_ids.push(latest_command_id);
324                             let entry = entry.into_mut();
325 
326                             // Modify the pipeline barrier to handle the collision.
327                             unsafe {
328                                 match resource_ty {
329                                     KeyTy::Buffer => {
330                                         let buf =
331                                             self.commands[latest_command_id].buffer(resource_index);
332 
333                                         let b = &mut self.pending_barrier;
334                                         b.add_buffer_memory_barrier(
335                                             buf,
336                                             entry.memory.stages,
337                                             entry.memory.access,
338                                             memory.stages,
339                                             memory.access,
340                                             true,
341                                             None,
342                                             0,
343                                             buf.size(),
344                                         );
345                                     }
346 
347                                     KeyTy::Image => {
348                                         let img =
349                                             self.commands[latest_command_id].image(resource_index);
350 
351                                         let b = &mut self.pending_barrier;
352                                         b.add_image_memory_barrier(
353                                             img,
354                                             img.current_miplevels_access(),
355                                             img.current_layer_levels_access(),
356                                             entry.memory.stages,
357                                             entry.memory.access,
358                                             memory.stages,
359                                             memory.access,
360                                             true,
361                                             None,
362                                             entry.current_layout,
363                                             start_layout,
364                                         );
365                                     }
366                                 };
367                             }
368 
369                             // Update state.
370                             entry.memory = memory;
371                             entry.exclusive_any = true;
372                             if memory.exclusive || end_layout != ImageLayout::Undefined {
373                                 // Only modify the layout in case of a write, because buffer operations
374                                 // pass `Undefined` for the layout. While a buffer write *must* set the
375                                 // layout to `Undefined`, a buffer read must not touch it.
376                                 entry.current_layout = end_layout;
377                             }
378                         } else {
379                             // There is no collision. Simply merge the stages and accesses.
380                             // TODO: what about simplifying the newly-constructed stages/accesses?
381                             //       this would simplify the job of the driver, but is it worth it?
382                             let entry = entry.into_mut();
383                             entry.memory.stages |= memory.stages;
384                             entry.memory.access |= memory.access;
385                         }
386                     }
387 
388                     // Situation where this is the first time we use this resource in this command buffer.
389                     Entry::Vacant(entry) => {
390                         // We need to perform some tweaks if the initial layout requirement of the image
391                         // is different from the first layout usage.
392                         let mut actually_exclusive = memory.exclusive;
393                         let mut actual_start_layout = start_layout;
394 
395                         if !self.is_secondary
396                             && resource_ty == KeyTy::Image
397                             && start_layout != ImageLayout::Undefined
398                             && start_layout != ImageLayout::Preinitialized
399                         {
400                             let img = self.commands[latest_command_id].image(resource_index);
401                             let initial_layout_requirement = img.initial_layout_requirement();
402 
403                             // Checks if the image is initialized and transitions it
404                             // if it isn't
405                             let is_layout_initialized = img.is_layout_initialized();
406 
407                             if initial_layout_requirement != start_layout || !is_layout_initialized
408                             {
409                                 // Note that we transition from `bottom_of_pipe`, which means that we
410                                 // wait for all the previous commands to be entirely finished. This is
411                                 // suboptimal, but:
412                                 //
413                                 // - If we're at the start of the command buffer we have no choice anyway,
414                                 //   because we have no knowledge about what comes before.
415                                 // - If we're in the middle of the command buffer, this pipeline is going
416                                 //   to be merged with an existing barrier. While it may still be
417                                 //   suboptimal in some cases, in the general situation it will be ok.
418                                 //
419                                 unsafe {
420                                     let from_layout = if is_layout_initialized {
421                                         actually_exclusive = true;
422                                         initial_layout_requirement
423                                     } else {
424                                         if img.preinitialized_layout() {
425                                             ImageLayout::Preinitialized
426                                         } else {
427                                             ImageLayout::Undefined
428                                         }
429                                     };
430                                     if initial_layout_requirement != start_layout {
431                                         actual_start_layout = initial_layout_requirement;
432                                     }
433                                     let b = &mut self.pending_barrier;
434                                     b.add_image_memory_barrier(
435                                         img,
436                                         img.current_miplevels_access(),
437                                         img.current_layer_levels_access(),
438                                         PipelineStages {
439                                             bottom_of_pipe: true,
440                                             ..PipelineStages::none()
441                                         },
442                                         AccessFlags::none(),
443                                         memory.stages,
444                                         memory.access,
445                                         true,
446                                         None,
447                                         from_layout,
448                                         start_layout,
449                                     );
450                                     img.layout_initialized();
451                                 }
452                             }
453                         }
454 
455                         entry.insert(ResourceState {
456                             command_ids: vec![latest_command_id],
457                             resource_index,
458 
459                             memory: PipelineMemoryAccess {
460                                 stages: memory.stages,
461                                 access: memory.access,
462                                 exclusive: actually_exclusive,
463                             },
464                             exclusive_any: actually_exclusive,
465                             initial_layout: actual_start_layout,
466                             current_layout: end_layout, // TODO: what if we reach the end with Undefined? that's not correct?
467                             image_uninitialized_safe,
468                         });
469                     }
470                 }
471 
472                 // Add the resources to the lists
473                 // TODO: Perhaps any barriers for a resource in the secondary command buffer will "protect"
474                 // its accesses so the primary needs less strict barriers.
475                 // Less barriers is more efficient, so worth investigating!
476                 let location = ResourceLocation {
477                     command_id: latest_command_id,
478                     resource_index,
479                 };
480 
481                 match resource_ty {
482                     KeyTy::Buffer => {
483                         self.buffers.push((location, memory));
484                         last_cmd_buffer += 1;
485                     }
486                     KeyTy::Image => {
487                         self.images.push((
488                             location,
489                             memory,
490                             start_layout,
491                             end_layout,
492                             image_uninitialized_safe,
493                         ));
494                         last_cmd_image += 1;
495                     }
496                 }
497             } else {
498                 match resource_ty {
499                     KeyTy::Buffer => {
500                         last_cmd_buffer += 1;
501                     }
502                     KeyTy::Image => {
503                         last_cmd_image += 1;
504                     }
505                 }
506             }
507         }
508 
509         Ok(())
510     }
511 
512     /// Builds the command buffer and turns it into a `SyncCommandBuffer`.
513     #[inline]
build(mut self) -> Result<SyncCommandBuffer, OomError>514     pub fn build(mut self) -> Result<SyncCommandBuffer, OomError> {
515         // TODO: see comment for the `is_poisoned` member in the struct
516         assert!(
517             !self.is_poisoned,
518             "The builder has been put in an inconsistent state by a previous error"
519         );
520 
521         debug_assert!(self.latest_render_pass_enter.is_none() || self.pending_barrier.is_empty());
522 
523         // The commands that haven't been sent to the inner command buffer yet need to be sent.
524         unsafe {
525             self.inner.pipeline_barrier(&self.pending_barrier);
526             let start = self.first_unflushed;
527             self.barriers.push(start); // Track inserted barriers
528             for command in &mut self.commands[start..] {
529                 command.send(&mut self.inner);
530             }
531         }
532 
533         // Transition images to their desired final layout.
534         if !self.is_secondary {
535             unsafe {
536                 // TODO: this could be optimized by merging the barrier with the barrier above?
537                 let mut barrier = UnsafeCommandBufferBuilderPipelineBarrier::new();
538 
539                 for (key, state) in self
540                     .resources
541                     .iter_mut()
542                     .filter(|(key, _)| matches!(key, ResourceKey::Image(..)))
543                 {
544                     let img = self.commands[state.command_ids[0]].image(state.resource_index);
545                     let requested_layout = img.final_layout_requirement();
546                     if requested_layout == state.current_layout {
547                         continue;
548                     }
549 
550                     barrier.add_image_memory_barrier(
551                         img,
552                         img.current_miplevels_access(),
553                         img.current_layer_levels_access(),
554                         state.memory.stages,
555                         state.memory.access,
556                         PipelineStages {
557                             top_of_pipe: true,
558                             ..PipelineStages::none()
559                         },
560                         AccessFlags::none(),
561                         true,
562                         None, // TODO: queue transfers?
563                         state.current_layout,
564                         requested_layout,
565                     );
566 
567                     state.exclusive_any = true;
568                     state.current_layout = requested_layout;
569                 }
570 
571                 self.inner.pipeline_barrier(&barrier);
572             }
573         }
574 
575         // Build the final resources states.
576         let final_resources_states: FnvHashMap<_, _> = {
577             self.resources
578                 .into_iter()
579                 .map(|(resource, state)| {
580                     let final_state = ResourceFinalState {
581                         command_ids: state.command_ids,
582                         resource_index: state.resource_index,
583                         final_stages: state.memory.stages,
584                         final_access: state.memory.access,
585                         exclusive: state.exclusive_any,
586                         initial_layout: state.initial_layout,
587                         final_layout: state.current_layout,
588                         image_uninitialized_safe: state.image_uninitialized_safe,
589                     };
590                     (resource, final_state)
591                 })
592                 .collect()
593         };
594 
595         Ok(SyncCommandBuffer {
596             inner: self.inner.build()?,
597             buffers: self.buffers,
598             images: self.images,
599             resources: final_resources_states,
600             commands: self.commands,
601             barriers: self.barriers,
602         })
603     }
604 
605     /// Returns the descriptor set currently bound to a given set number, or `None` if nothing has
606     /// been bound yet.
bound_descriptor_set( &self, pipeline_bind_point: PipelineBindPoint, set_num: u32, ) -> Option<(&dyn DescriptorSet, &[u32])>607     pub(crate) fn bound_descriptor_set(
608         &self,
609         pipeline_bind_point: PipelineBindPoint,
610         set_num: u32,
611     ) -> Option<(&dyn DescriptorSet, &[u32])> {
612         self.bindings
613             .descriptor_sets
614             .get(&pipeline_bind_point)
615             .and_then(|sets| {
616                 sets.get(&set_num)
617                     .map(|cmd| cmd.bound_descriptor_set(set_num))
618             })
619     }
620 
621     /// Returns the index buffer currently bound, or `None` if nothing has been bound yet.
bound_index_buffer(&self) -> Option<&dyn BufferAccess>622     pub(crate) fn bound_index_buffer(&self) -> Option<&dyn BufferAccess> {
623         self.bindings
624             .index_buffer
625             .as_ref()
626             .map(|cmd| cmd.bound_index_buffer())
627     }
628 
629     /// Returns the compute pipeline currently bound, or `None` if nothing has been bound yet.
bound_pipeline_compute(&self) -> Option<&dyn ComputePipelineAbstract>630     pub(crate) fn bound_pipeline_compute(&self) -> Option<&dyn ComputePipelineAbstract> {
631         self.bindings
632             .pipeline_compute
633             .as_ref()
634             .map(|cmd| cmd.bound_pipeline_compute())
635     }
636 
637     /// Returns the graphics pipeline currently bound, or `None` if nothing has been bound yet.
bound_pipeline_graphics(&self) -> Option<&dyn GraphicsPipelineAbstract>638     pub(crate) fn bound_pipeline_graphics(&self) -> Option<&dyn GraphicsPipelineAbstract> {
639         self.bindings
640             .pipeline_graphics
641             .as_ref()
642             .map(|cmd| cmd.bound_pipeline_graphics())
643     }
644 
645     /// Returns the vertex buffer currently bound to a given binding slot number, or `None` if
646     /// nothing has been bound yet.
bound_vertex_buffer(&self, binding_num: u32) -> Option<&dyn BufferAccess>647     pub(crate) fn bound_vertex_buffer(&self, binding_num: u32) -> Option<&dyn BufferAccess> {
648         self.bindings
649             .vertex_buffers
650             .get(&binding_num)
651             .map(|cmd| cmd.bound_vertex_buffer(binding_num))
652     }
653 }
654 
655 unsafe impl DeviceOwned for SyncCommandBufferBuilder {
656     #[inline]
device(&self) -> &Arc<Device>657     fn device(&self) -> &Arc<Device> {
658         self.inner.device()
659     }
660 }
661 
662 impl fmt::Debug for SyncCommandBufferBuilder {
663     #[inline]
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result664     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
665         fmt::Debug::fmt(&self.inner, f)
666     }
667 }
668 
669 /// Error returned if the builder detects that there's an unsolvable conflict.
670 #[derive(Debug, Clone)]
671 pub enum SyncCommandBufferBuilderError {
672     /// Unsolvable conflict.
673     Conflict {
674         command1_name: &'static str,
675         command1_param: Cow<'static, str>,
676         command1_offset: usize,
677 
678         command2_name: &'static str,
679         command2_param: Cow<'static, str>,
680         command2_offset: usize,
681     },
682 
683     ExecError(CommandBufferExecError),
684 }
685 
686 impl error::Error for SyncCommandBufferBuilderError {}
687 
688 impl fmt::Display for SyncCommandBufferBuilderError {
689     #[inline]
fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error>690     fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
691         match self {
692             SyncCommandBufferBuilderError::Conflict { .. } => write!(fmt, "unsolvable conflict"),
693             SyncCommandBufferBuilderError::ExecError(err) => err.fmt(fmt),
694         }
695     }
696 }
697 
698 impl From<CommandBufferExecError> for SyncCommandBufferBuilderError {
699     #[inline]
from(val: CommandBufferExecError) -> Self700     fn from(val: CommandBufferExecError) -> Self {
701         SyncCommandBufferBuilderError::ExecError(val)
702     }
703 }
704 
705 /// Type of resource whose state is to be tracked.
706 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
707 enum KeyTy {
708     Buffer,
709     Image,
710 }
711 
712 // State of a resource during the building of the command buffer.
713 #[derive(Debug, Clone)]
714 struct ResourceState {
715     // Indices of the commands that contain the resource.
716     command_ids: Vec<usize>,
717 
718     // Index of the resource within the first command in `command_ids`.
719     resource_index: usize,
720 
721     // Memory access of the command that last used this resource.
722     memory: PipelineMemoryAccess,
723 
724     // True if the resource was used in exclusive mode at any point during the building of the
725     // command buffer. Also true if an image layout transition or queue transfer has been performed.
726     exclusive_any: bool,
727 
728     // Layout at the first use of the resource by the command buffer. Can be `Undefined` if we
729     // don't care.
730     initial_layout: ImageLayout,
731 
732     // Current layout at this stage of the building.
733     current_layout: ImageLayout,
734 
735     // Extra context of how the image will be used
736     image_uninitialized_safe: ImageUninitializedSafe,
737 }
738 
739 /// Holds the index of the most recent command that binds a particular resource, or `None` if
740 /// nothing has been bound yet.
741 #[derive(Debug, Default)]
742 struct BindingState {
743     descriptor_sets: FnvHashMap<PipelineBindPoint, FnvHashMap<u32, Arc<dyn Command + Send + Sync>>>,
744     index_buffer: Option<Arc<dyn Command + Send + Sync>>,
745     pipeline_compute: Option<Arc<dyn Command + Send + Sync>>,
746     pipeline_graphics: Option<Arc<dyn Command + Send + Sync>>,
747     vertex_buffers: FnvHashMap<u32, Arc<dyn Command + Send + Sync>>,
748 }
749