1use {
20 super::{
21 AnyResource, Attachment, CommandData, ExecutionAccess, ExecutionPipeline, Graph, LoadOp,
22 Node, NodeIndex,
23 cmd::{SubresourceAccess, SubresourceRange},
24 },
25 crate::{
26 StoreOp,
27 cmd::CommandRef,
28 driver::{
29 AttachmentInfo, AttachmentRef, Descriptor, DescriptorInfo, DescriptorSet, DriverError,
30 FramebufferAttachmentImageInfo, FramebufferInfo, SharingMode, SubpassDependency,
31 SubpassInfo,
32 accel_struct::AccelerationStructure,
33 buffer::{Buffer, BufferSubresourceRange},
34 cmd_buf::{CommandBuffer, CommandBufferInfo},
35 descriptor_set::{DescriptorPool, DescriptorPoolInfo},
36 device::Device,
37 fence::Fence,
38 format_aspect_mask,
39 graphics::{DepthStencilInfo, GraphicsPipeline},
40 image::{
41 DenseMap, Image, image_subresource_range_contains,
42 image_subresource_range_intersection,
43 },
44 initial_image_layout_access, is_read_access, pipeline_stage_access_flags,
45 render_pass::{RenderPass, RenderPassInfo},
46 },
47 lazy_str,
48 node::AnyNode,
49 pool::{Lease, Pool, SubmissionPool},
50 },
51 ash::vk,
52 fixedbitset::FixedBitSet,
53 log::{
54 Level::{Debug, Trace},
55 debug, log_enabled, trace, warn,
56 },
57 smallvec::SmallVec,
58 std::{
59 cell::RefCell,
60 collections::{BTreeMap, HashMap, HashSet, VecDeque},
61 fmt::Debug as FmtDebug,
62 iter::repeat_n,
63 mem::take,
64 ops::Range,
65 slice,
66 sync::{Arc, Mutex},
67 },
68 vk_sync::{
69 AccessType, BufferBarrier, GlobalBarrier, ImageBarrier, ImageLayout,
70 get_buffer_memory_barrier, get_image_memory_barrier, get_memory_barrier,
71 },
72};
73
74#[cfg(not(feature = "checked"))]
75use std::hint::unreachable_unchecked;
76
77thread_local! {
78 static SUBMIT: RefCell<SubmitScratch> = Default::default();
79}
80
81fn aspect_mask_for_span(base_aspect: u32, start: u32, end: u32) -> vk::ImageAspectFlags {
82 let mut mask = vk::ImageAspectFlags::empty();
83
84 for ordinal in start..end {
85 mask |= vk::ImageAspectFlags::from_raw(1 << (base_aspect + ordinal));
86 }
87
88 mask
89}
90
91fn buffer_barriers_from_transfers<'a>(
92 buffer: vk::Buffer,
93 prev_access: &'a AccessType,
94 next_access: &'a AccessType,
95 range: BufferSubresourceRange,
96 transfers: &'a [BufferQueueOwnershipTransfer],
97) -> impl Iterator<Item = BufferBarrier<'a>> + 'a {
98 struct BufferBarrierIter<'a> {
99 buffer: vk::Buffer,
100 cuts: SmallVec<[vk::DeviceSize; 4]>,
101 cut_idx: usize,
102 next_access: &'a AccessType,
103 prev_access: &'a AccessType,
104 transfers: &'a [BufferQueueOwnershipTransfer],
105 }
106
107 impl<'a> Iterator for BufferBarrierIter<'a> {
108 type Item = BufferBarrier<'a>;
109
110 fn next(&mut self) -> Option<Self::Item> {
111 while self.cut_idx + 1 < self.cuts.len() {
112 let range = BufferSubresourceRange {
113 start: self.cuts[self.cut_idx],
114 end: self.cuts[self.cut_idx + 1],
115 };
116 self.cut_idx += 1;
117
118 if range.start == range.end {
119 continue;
120 }
121
122 let transfer = self
123 .transfers
124 .iter()
125 .find(|transfer| transfer.range.contains(range));
126
127 trace!(
128 " buffer {:?} {:?} {:?}->{:?}",
129 self.buffer,
130 range.start..range.end,
131 self.prev_access,
132 self.next_access,
133 );
134
135 return Some(BufferBarrier {
136 next_accesses: slice::from_ref(self.next_access),
137 previous_accesses: slice::from_ref(self.prev_access),
138 src_queue_family_index: transfer.map_or(vk::QUEUE_FAMILY_IGNORED, |transfer| {
139 transfer.src_queue_family_index
140 }),
141 dst_queue_family_index: transfer.map_or(vk::QUEUE_FAMILY_IGNORED, |transfer| {
142 transfer.dst_queue_family_index
143 }),
144 buffer: self.buffer,
145 offset: range.start as _,
146 size: (range.end - range.start) as _,
147 });
148 }
149
150 None
151 }
152 }
153
154 let mut cuts = SmallVec::<[vk::DeviceSize; 4]>::with_capacity(
155 transfers.len().saturating_mul(2).saturating_add(2),
156 );
157 cuts.extend([range.start, range.end]);
158
159 for transfer in transfers {
160 if let Some(overlap) = range.intersection(transfer.range) {
161 cuts.push(overlap.start);
162 cuts.push(overlap.end);
163 }
164 }
165
166 cuts.sort_unstable();
167 cuts.dedup();
168
169 BufferBarrierIter {
170 buffer,
171 cuts,
172 cut_idx: 0,
173 next_access,
174 prev_access,
175 transfers,
176 }
177}
178
179fn buffer_subresource_range_intersects(
180 lhs: BufferSubresourceRange,
181 rhs: BufferSubresourceRange,
182) -> bool {
183 lhs.start < rhs.end && lhs.end > rhs.start
184}
185
186fn check_queue_submit_args(
187 waits: &[SemaphoreSubmitInfo],
188 signals: &[SemaphoreSubmitInfo],
189) -> Result<(), DriverError> {
190 waits
191 .iter()
192 .chain(signals.iter())
193 .all(SemaphoreSubmitInfo::is_supported_legacy_submit)
194 .then_some(())
195 .ok_or(DriverError::Unsupported)
196}
197
198fn check_queue_submit2_args(
199 device: &Device,
200 waits: &[SemaphoreSubmit2Info],
201 signals: &[SemaphoreSubmit2Info],
202) -> Result<(), DriverError> {
203 if !device.physical.vk_khr_synchronization2 {
204 return Err(DriverError::Unsupported);
205 }
206
207 if (waits.iter().any(|wait| wait.value != 0) || signals.iter().any(|signal| signal.value != 0))
208 && !supports_timeline_semaphores(device)
209 {
210 return Err(DriverError::Unsupported);
211 }
212
213 Ok(())
214}
215
216fn consume_pending_buffer_transfers(
217 transfers: &mut Vec<BufferQueueOwnershipTransfer>,
218 range: BufferSubresourceRange,
219) -> bool {
220 transfers.retain(|transfer| !buffer_subresource_range_intersects(transfer.range, range));
221 transfers.is_empty()
222}
223
224fn consume_pending_image_transfers(
225 transfers: &mut Vec<ImageQueueOwnershipTransfer>,
226 range: vk::ImageSubresourceRange,
227) -> bool {
228 transfers
229 .retain(|transfer| image_subresource_range_intersection(transfer.range, range).is_none());
230 transfers.is_empty()
231}
232
233fn exclusive_transfer_source(sharing: SharingMode, queue_family_index: u32) -> Option<(u32, u32)> {
234 let SharingMode::Exclusive(Some((src_queue_family_index, src_queue_index))) = sharing else {
235 return None;
236 };
237
238 (src_queue_family_index != queue_family_index)
239 .then_some((src_queue_family_index, src_queue_index))
240}
241
242const fn image_access_layout(access: AccessType) -> ImageLayout {
243 if matches!(access, AccessType::Present | AccessType::ComputeShaderWrite) {
244 ImageLayout::General
245 } else {
246 ImageLayout::Optimal
247 }
248}
249
250fn image_barriers_from_transfers<'a>(
251 image: vk::Image,
252 prev_access: &'a AccessType,
253 next_access: &'a AccessType,
254 range: vk::ImageSubresourceRange,
255 transfers: &'a [ImageQueueOwnershipTransfer],
256 discard_contents: bool,
257) -> impl Iterator<Item = ImageBarrier<'a>> + 'a {
258 image_barrier_transfer_ranges(transfers, range).map(move |(range, transfer)| {
259 trace!(
260 " image {:?} {:?} {:?}->{:?}",
261 image,
262 ImageSubresourceRangeDebug(range),
263 prev_access,
264 next_access,
265 );
266
267 ImageBarrier {
268 next_accesses: slice::from_ref(next_access),
269 next_layout: image_access_layout(*next_access),
270 previous_accesses: slice::from_ref(prev_access),
271 previous_layout: image_access_layout(*prev_access),
272 discard_contents,
273 src_queue_family_index: transfer.map_or(vk::QUEUE_FAMILY_IGNORED, |transfer| {
274 transfer.src_queue_family_index
275 }),
276 dst_queue_family_index: transfer.map_or(vk::QUEUE_FAMILY_IGNORED, |transfer| {
277 transfer.dst_queue_family_index
278 }),
279 image,
280 range,
281 }
282 })
283}
284
285fn image_barrier_transfer_ranges<'a>(
286 transfers: &'a [ImageQueueOwnershipTransfer],
287 range: vk::ImageSubresourceRange,
288) -> impl Iterator<
289 Item = (
290 vk::ImageSubresourceRange,
291 Option<&'a ImageQueueOwnershipTransfer>,
292 ),
293> + 'a {
294 thread_local! {
295 static IMAGE_TRANSFER: RefCell<ImageTransferScratch> = Default::default();
296 }
297
298 #[derive(Default)]
299 struct ImageTransferScratch {
300 overlaps: Vec<(usize, vk::ImageSubresourceRange)>,
301 aspect_cuts: Vec<u32>,
302 layer_cuts: Vec<u32>,
303 mip_cuts: Vec<u32>,
304 }
305
306 struct ImageBarrierTransferIter<'a> {
307 transfers: &'a [ImageQueueOwnershipTransfer],
308 overlaps: Vec<(usize, vk::ImageSubresourceRange)>,
309 aspect_cuts: Vec<u32>,
310 layer_cuts: Vec<u32>,
311 mip_cuts: Vec<u32>,
312 base_aspect: u32,
313 range: vk::ImageSubresourceRange,
314 aspect_idx: usize,
315 layer_idx: usize,
316 mip_idx: usize,
317 yielded_empty: bool,
318 }
319
320 impl<'a> Iterator for ImageBarrierTransferIter<'a> {
321 type Item = (
322 vk::ImageSubresourceRange,
323 Option<&'a ImageQueueOwnershipTransfer>,
324 );
325
326 fn next(&mut self) -> Option<Self::Item> {
327 if self.overlaps.is_empty() {
328 return if self.yielded_empty {
329 None
330 } else {
331 self.yielded_empty = true;
332 Some((self.range, None))
333 };
334 }
335
336 let aspect_windows = self.aspect_cuts.len().saturating_sub(1);
337 let layer_windows = self.layer_cuts.len().saturating_sub(1);
338 let mip_windows = self.mip_cuts.len().saturating_sub(1);
339
340 while self.aspect_idx < aspect_windows {
341 let aspect_start = self.aspect_cuts[self.aspect_idx];
342 let aspect_end = self.aspect_cuts[self.aspect_idx + 1];
343 if aspect_start == aspect_end {
344 self.aspect_idx += 1;
345 self.layer_idx = 0;
346 self.mip_idx = 0;
347 continue;
348 }
349
350 let aspect_mask = aspect_mask_for_span(self.base_aspect, aspect_start, aspect_end);
351
352 while self.layer_idx < layer_windows {
353 let layer_start = self.layer_cuts[self.layer_idx];
354 let layer_end = self.layer_cuts[self.layer_idx + 1];
355 if layer_start == layer_end {
356 self.layer_idx += 1;
357 self.mip_idx = 0;
358 continue;
359 }
360
361 while self.mip_idx < mip_windows {
362 let mip_start = self.mip_cuts[self.mip_idx];
363 let mip_end = self.mip_cuts[self.mip_idx + 1];
364 self.mip_idx += 1;
365 if mip_start == mip_end {
366 continue;
367 }
368
369 let subrange = vk::ImageSubresourceRange {
370 aspect_mask,
371 base_array_layer: self.range.base_array_layer + layer_start,
372 layer_count: layer_end - layer_start,
373 base_mip_level: self.range.base_mip_level + mip_start,
374 level_count: mip_end - mip_start,
375 };
376
377 let transfer = self
378 .overlaps
379 .iter()
380 .find(|(_, overlap)| {
381 image_subresource_range_contains(*overlap, subrange)
382 })
383 .map(|(transfer_idx, _)| &self.transfers[*transfer_idx]);
384
385 return Some((subrange, transfer));
386 }
387
388 self.layer_idx += 1;
389 self.mip_idx = 0;
390 }
391
392 self.aspect_idx += 1;
393 self.layer_idx = 0;
394 self.mip_idx = 0;
395 }
396
397 None
398 }
399 }
400
401 impl Drop for ImageBarrierTransferIter<'_> {
402 fn drop(&mut self) {
403 IMAGE_TRANSFER.with_borrow_mut(|tls| {
404 tls.overlaps = take(&mut self.overlaps);
405 tls.aspect_cuts = take(&mut self.aspect_cuts);
406 tls.layer_cuts = take(&mut self.layer_cuts);
407 tls.mip_cuts = take(&mut self.mip_cuts);
408 });
409 }
410 }
411
412 IMAGE_TRANSFER.with_borrow_mut(|tls| {
413 let mut overlaps = take(&mut tls.overlaps);
414 let mut aspect_cuts = take(&mut tls.aspect_cuts);
415 let mut layer_cuts = take(&mut tls.layer_cuts);
416 let mut mip_cuts = take(&mut tls.mip_cuts);
417
418 overlaps.clear();
419 aspect_cuts.clear();
420 layer_cuts.clear();
421 mip_cuts.clear();
422
423 overlaps.extend(
424 transfers
425 .iter()
426 .enumerate()
427 .filter_map(|(transfer_idx, transfer)| {
428 image_subresource_range_intersection(transfer.range, range)
429 .map(|intersection| (transfer_idx, intersection))
430 }),
431 );
432
433 let base_aspect = range.aspect_mask.as_raw().trailing_zeros();
434
435 if overlaps.is_empty() {
436 } else {
438 let aspect_count = range.aspect_mask.as_raw().count_ones();
439
440 aspect_cuts.extend([0, aspect_count]);
441 layer_cuts.extend([0, range.layer_count]);
442 mip_cuts.extend([0, range.level_count]);
443
444 for (_, overlap) in &overlaps {
445 let aspect_start = overlap.aspect_mask.as_raw().trailing_zeros() - base_aspect;
446 let aspect_end = aspect_start + overlap.aspect_mask.as_raw().count_ones();
447 aspect_cuts.push(aspect_start);
448 aspect_cuts.push(aspect_end);
449
450 let layer_start = overlap.base_array_layer - range.base_array_layer;
451 let layer_end = layer_start + overlap.layer_count;
452 layer_cuts.push(layer_start);
453 layer_cuts.push(layer_end);
454
455 let mip_start = overlap.base_mip_level - range.base_mip_level;
456 let mip_end = mip_start + overlap.level_count;
457 mip_cuts.push(mip_start);
458 mip_cuts.push(mip_end);
459 }
460
461 aspect_cuts.sort_unstable();
462 aspect_cuts.dedup();
463 layer_cuts.sort_unstable();
464 layer_cuts.dedup();
465 mip_cuts.sort_unstable();
466 mip_cuts.dedup();
467 }
468
469 ImageBarrierTransferIter {
470 transfers,
471 overlaps,
472 aspect_cuts,
473 layer_cuts,
474 mip_cuts,
475 base_aspect,
476 range,
477 aspect_idx: 0,
478 layer_idx: 0,
479 mip_idx: 0,
480 yielded_empty: false,
481 }
482 })
483}
484
485fn image_execution_discard_contents(prev_access: AccessType) -> bool {
486 prev_access == AccessType::Nothing
487}
488
489fn image_layout_transition_discard_contents(
490 prev_access: AccessType,
491 next_access: AccessType,
492) -> bool {
493 prev_access == AccessType::Nothing || !is_read_access(next_access)
497}
498
499fn image_subresource_range_eq(
500 lhs: vk::ImageSubresourceRange,
501 rhs: vk::ImageSubresourceRange,
502) -> bool {
503 lhs.aspect_mask == rhs.aspect_mask
504 && lhs.base_array_layer == rhs.base_array_layer
505 && lhs.layer_count == rhs.layer_count
506 && lhs.base_mip_level == rhs.base_mip_level
507 && lhs.level_count == rhs.level_count
508}
509
510fn pipeline_barrier_from_iters<'a>(
512 device: &Device,
513 command_buffer: vk::CommandBuffer,
514 global_barrier: Option<GlobalBarrier<'a>>,
515 buffer_barriers: impl IntoIterator<Item = BufferBarrier<'a>>,
516 image_barriers: impl IntoIterator<Item = ImageBarrier<'a>>,
517) {
518 #[derive(Default)]
519 struct BarrierScratch {
520 memory_barriers: Vec<vk::MemoryBarrier<'static>>,
521 buffer_barriers: Vec<vk::BufferMemoryBarrier<'static>>,
522 image_barriers: Vec<vk::ImageMemoryBarrier<'static>>,
523 }
524
525 thread_local! {
526 static BARRIER: RefCell<BarrierScratch> = Default::default();
527 }
528
529 BARRIER.with_borrow_mut(|tls| {
530 tls.memory_barriers.clear();
531 tls.buffer_barriers.clear();
532 tls.image_barriers.clear();
533
534 let mut src_stage_mask = vk::PipelineStageFlags::TOP_OF_PIPE;
535 let mut dst_stage_mask = vk::PipelineStageFlags::BOTTOM_OF_PIPE;
536
537 if let Some(ref barrier) = global_barrier {
538 let (src_mask, dst_mask, barrier) = get_memory_barrier(barrier);
539 src_stage_mask |= src_mask;
540 dst_stage_mask |= dst_mask;
541 tls.memory_barriers.push(vk::MemoryBarrier {
542 src_access_mask: barrier.src_access_mask,
543 dst_access_mask: barrier.dst_access_mask,
544 ..Default::default()
545 });
546 }
547
548 for buffer_barrier in buffer_barriers {
549 let (src_mask, dst_mask, barrier) = get_buffer_memory_barrier(&buffer_barrier);
550 src_stage_mask |= src_mask;
551 dst_stage_mask |= dst_mask;
552 tls.buffer_barriers.push(vk::BufferMemoryBarrier {
553 src_access_mask: barrier.src_access_mask,
554 dst_access_mask: barrier.dst_access_mask,
555 src_queue_family_index: barrier.src_queue_family_index,
556 dst_queue_family_index: barrier.dst_queue_family_index,
557 buffer: barrier.buffer,
558 offset: barrier.offset,
559 size: barrier.size,
560 ..Default::default()
561 });
562 }
563
564 for image_barrier in image_barriers {
565 let (src_mask, dst_mask, barrier) = get_image_memory_barrier(&image_barrier);
566 src_stage_mask |= src_mask;
567 dst_stage_mask |= dst_mask;
568 tls.image_barriers.push(vk::ImageMemoryBarrier {
569 src_access_mask: barrier.src_access_mask,
570 dst_access_mask: barrier.dst_access_mask,
571 old_layout: barrier.old_layout,
572 new_layout: barrier.new_layout,
573 src_queue_family_index: barrier.src_queue_family_index,
574 dst_queue_family_index: barrier.dst_queue_family_index,
575 image: barrier.image,
576 subresource_range: barrier.subresource_range,
577 ..Default::default()
578 });
579 }
580
581 unsafe {
582 device.cmd_pipeline_barrier(
583 command_buffer,
584 src_stage_mask,
585 dst_stage_mask,
586 vk::DependencyFlags::empty(),
587 tls.memory_barriers.as_slice(),
588 tls.buffer_barriers.as_slice(),
589 tls.image_barriers.as_slice(),
590 );
591 }
592 });
593}
594
595fn schedule_dependency_cmds_before_target_access(
596 access_index: &CommandAccessIndex,
597 target_node_idx: usize,
598 first_target_cmd_idx: usize,
599 schedule: &mut Vec<usize>,
600) {
601 let mut pending_nodes = VecDeque::new();
602 let mut scheduled = FixedBitSet::with_capacity(first_target_cmd_idx);
603 let mut queued_nodes = FixedBitSet::with_capacity(access_index.cmds_by_node.len());
604
605 for node_idx in access_index.read_nodes_for_cmd(first_target_cmd_idx) {
606 if node_idx != target_node_idx {
607 pending_nodes.push_back((node_idx, first_target_cmd_idx));
608 queued_nodes.insert(node_idx);
609 }
610 }
611
612 while let Some((node_idx, end_cmd_idx)) = pending_nodes.pop_front() {
613 for cmd_idx in access_index.prior_cmds_for_node(node_idx, end_cmd_idx) {
614 if scheduled.put(cmd_idx) {
615 continue;
616 }
617
618 schedule.push(cmd_idx);
619
620 for read_node_idx in access_index.read_nodes_for_cmd(cmd_idx) {
621 if queued_nodes.put(read_node_idx) {
622 continue;
623 }
624
625 pending_nodes.push_back((read_node_idx, cmd_idx));
626 }
627 }
628 }
629
630 schedule.sort_unstable();
631}
632
633fn submit_stage_mask_legacy(stage_mask: vk::PipelineStageFlags2) -> vk::PipelineStageFlags {
634 match stage_mask {
635 vk::PipelineStageFlags2::NONE => vk::PipelineStageFlags::ALL_COMMANDS,
636 vk::PipelineStageFlags2::ALL_COMMANDS => vk::PipelineStageFlags::ALL_COMMANDS,
637 _ => {
638 #[cfg(feature = "checked")]
639 panic!("invalid legacy submit wait stage mask: {stage_mask:?}");
640
641 #[cfg(not(feature = "checked"))]
642 {
643 vk::PipelineStageFlags::ALL_COMMANDS
644 }
645 }
646 }
647}
648
649fn supports_timeline_semaphores(device: &Device) -> bool {
650 device.physical.features_v1_2.timeline_semaphore
651}
652
653fn submit_queue_ownership_releases<P>(
656 pool: &mut P,
657 release_groups: &[QueueOwnershipReleaseGroup],
658 target_queue_family_index: u32,
659 submit_release: impl Fn(
660 &Device,
661 vk::Queue,
662 vk::CommandBuffer,
663 vk::Fence,
664 vk::Semaphore,
665 ) -> Result<(), DriverError>,
666) -> Result<Vec<QueueOwnershipRelease>, DriverError>
667where
668 P: Pool<CommandBufferInfo, CommandBuffer>,
669{
670 let mut releases = Vec::new();
671
672 if !release_groups.is_empty() {
673 for group in release_groups {
674 let mut release_cmd =
675 pool.resource(CommandBufferInfo::new(group.src_queue_family_index as _))?;
676 let mut release_fence = Fence::create(&release_cmd.device, false)?;
677
678 #[cfg(feature = "checked")]
679 {
680 release_fence.wait_signaled()?;
681 release_fence.reset()?;
682 }
683
684 let semaphore = release_cmd.release_semaphore()?;
685
686 release_cmd.set_debug_name(lazy_str!(
687 "queue ownership release qf{}:{} -> qf{}",
688 group.src_queue_family_index,
689 group.src_queue_index,
690 target_queue_family_index
691 ));
692
693 Device::begin_command_buffer(
694 &release_cmd.device,
695 release_cmd.handle,
696 &vk::CommandBufferBeginInfo::default()
697 .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT),
698 )?;
699
700 {
701 let _ = CommandBufferDebugLabel::begin(
702 &release_cmd,
703 lazy_str!(
704 "queue ownership release qf{}:{} -> qf{}",
705 group.src_queue_family_index,
706 group.src_queue_index,
707 target_queue_family_index
708 ),
709 );
710
711 SUBMIT.with_borrow_mut(|tls| {
712 let _ = CommandBufferDebugLabel::begin(&release_cmd, "queue ownership barrier");
713
714 tls.release_image_barriers.clear();
715 tls.release_buffer_barriers.clear();
716 tls.release_buffer_barriers.reserve(group.buffers.len());
717 tls.release_image_barriers.reserve(group.images.len());
718
719 tls.release_buffer_barriers.extend(group.buffers.iter().map(
720 |&(handle, range)| {
721 vk::BufferMemoryBarrier::default()
722 .src_access_mask(vk::AccessFlags::MEMORY_WRITE)
723 .dst_access_mask(vk::AccessFlags::empty())
724 .src_queue_family_index(group.src_queue_family_index)
725 .dst_queue_family_index(target_queue_family_index)
726 .buffer(handle)
727 .offset(range.start)
728 .size(range.end - range.start)
729 },
730 ));
731
732 tls.release_image_barriers.extend(group.images.iter().map(
733 |&(handle, current_layout, subresource_range)| {
734 vk::ImageMemoryBarrier::default()
735 .src_access_mask(vk::AccessFlags::MEMORY_WRITE)
736 .dst_access_mask(vk::AccessFlags::empty())
737 .old_layout(current_layout)
738 .new_layout(current_layout)
739 .src_queue_family_index(group.src_queue_family_index)
740 .dst_queue_family_index(target_queue_family_index)
741 .image(handle)
742 .subresource_range(subresource_range)
743 },
744 ));
745
746 unsafe {
747 release_cmd.device.cmd_pipeline_barrier(
748 release_cmd.handle,
749 vk::PipelineStageFlags::ALL_COMMANDS,
750 vk::PipelineStageFlags::ALL_COMMANDS,
751 vk::DependencyFlags::empty(),
752 &[],
753 tls.release_buffer_barriers.as_slice(),
754 tls.release_image_barriers.as_slice(),
755 );
756 }
757 });
758
759 Device::with_queue(
760 &release_cmd.device,
761 group.src_queue_family_index,
762 group.src_queue_index,
763 |queue| {
764 Device::end_command_buffer(&release_cmd.device, release_cmd.handle)?;
765 submit_release(
766 &release_cmd.device,
767 queue,
768 release_cmd.handle,
769 release_fence.handle,
770 semaphore,
771 )?;
772
773 release_fence.mark_queued();
774
775 Ok::<_, DriverError>(())
776 },
777 )?;
778 }
779
780 releases.push(QueueOwnershipRelease {
781 _cmd_buf: release_cmd,
782 _fence: release_fence,
783 semaphore,
784 });
785 }
786 }
787
788 Ok(releases)
789}
790
791#[derive(Clone, Copy, Debug)]
792struct BufferQueueOwnershipTransfer {
793 range: BufferSubresourceRange,
794 dst_queue_family_index: u32,
795 src_queue_family_index: u32,
796 src_queue_index: u32,
797}
798
799#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
800struct BufferSubresourceRangeKey {
801 start: vk::DeviceSize,
802 end: vk::DeviceSize,
803}
804
805impl BufferSubresourceRangeKey {
806 fn from_range(BufferSubresourceRange { start, end }: BufferSubresourceRange) -> Self {
807 Self { start, end }
808 }
809
810 fn into_range(self) -> BufferSubresourceRange {
811 BufferSubresourceRange {
812 start: self.start,
813 end: self.end,
814 }
815 }
816}
817
818#[derive(Clone, Default)]
819struct CommandAccessIndex {
820 cmds_by_node: Vec<Vec<usize>>,
821 accessed_nodes_by_cmd: Vec<Vec<usize>>,
822}
823
824impl CommandAccessIndex {
825 #[profiling::function]
826 fn read_nodes_for_cmd(&self, cmd_idx: usize) -> impl ExactSizeIterator<Item = usize> + '_ {
827 self.accessed_nodes_by_cmd[cmd_idx].iter().copied()
828 }
829
830 #[profiling::function]
831 fn prior_cmds_for_node(
832 &self,
833 node_idx: usize,
834 end_cmd_idx: usize,
835 ) -> impl Iterator<Item = usize> + '_ {
836 let cmds = &self.cmds_by_node[node_idx];
837 let end_idx = cmds.partition_point(|&cmd_idx| cmd_idx < end_cmd_idx);
838
839 cmds[..end_idx].iter().rev().copied()
840 }
841
842 #[profiling::function]
843 fn prior_read_dependency_cmds(
844 &self,
845 cmd_idx: usize,
846 end_cmd_idx: usize,
847 ) -> impl Iterator<Item = usize> + '_ {
848 self.read_nodes_for_cmd(cmd_idx)
849 .flat_map(move |node_idx| self.prior_cmds_for_node(node_idx, end_cmd_idx))
850 }
851
852 fn update(&mut self, graph: &Graph, end_cmd_idx: usize) {
853 let binding_count = graph.resources.len();
854 let cmds = &graph.cmds[0..end_cmd_idx];
855 self.update_from_cmds(cmds, binding_count);
856 }
857
858 fn update_from_cmds(&mut self, cmds: &[CommandData], binding_count: usize) {
859 self.cmds_by_node.clear();
860 self.cmds_by_node.resize_with(binding_count, Vec::new);
861
862 self.accessed_nodes_by_cmd.clear();
863 self.accessed_nodes_by_cmd.resize_with(cmds.len(), Vec::new);
864
865 thread_local! {
866 static SEEN_NODES: RefCell<(FixedBitSet, FixedBitSet)> = Default::default();
867 }
868
869 SEEN_NODES.with_borrow_mut(|(seen_nodes, seen_accesses)| {
870 seen_nodes.clear();
871 seen_nodes.grow(binding_count);
872
873 seen_accesses.clear();
874 seen_accesses.grow(binding_count);
875
876 for (cmd_idx, cmd) in cmds.iter().enumerate() {
877 let accessed_nodes = &mut self.accessed_nodes_by_cmd[cmd_idx];
878
879 for (node_idx, _) in cmd.execs.iter().flat_map(|exec| exec.accesses.iter()) {
880 if !seen_nodes.put(node_idx) {
881 self.cmds_by_node[node_idx].push(cmd_idx);
882 }
883
884 if !seen_accesses.put(node_idx) {
885 accessed_nodes.push(node_idx);
886 }
887 }
888
889 seen_nodes.clear();
890 seen_nodes.grow(binding_count);
891 seen_accesses.clear();
892 seen_accesses.grow(binding_count);
893 }
894 });
895 }
896}
897
898struct CommandBufferDebugLabel<'a> {
899 cmd_buf: &'a CommandBuffer,
900}
901
902impl<'a> CommandBufferDebugLabel<'a> {
903 fn begin(cmd_buf: &'a CommandBuffer, name: impl AsRef<str>) -> Option<Self> {
904 Device::begin_debug_utils_label(&cmd_buf.device, cmd_buf.handle, name)
905 .ok()
906 .map(|_| Self { cmd_buf })
907 }
908}
909
910impl Drop for CommandBufferDebugLabel<'_> {
911 fn drop(&mut self) {
912 let _ = Device::end_debug_utils_label(&self.cmd_buf.device, self.cmd_buf.handle);
913 }
914}
915
916#[derive(Default)]
917struct ExternalRenderPassAccessHistory {
918 accesses_by_node: Vec<Vec<PipelineStageAccessFlags>>,
919}
920
921impl ExternalRenderPassAccessHistory {
922 fn new(node_count: usize) -> Self {
923 let mut accesses_by_node = Vec::with_capacity(node_count);
924 accesses_by_node.resize_with(node_count, Vec::new);
925
926 Self { accesses_by_node }
927 }
928
929 fn accesses(&self, node_idx: usize) -> &[PipelineStageAccessFlags] {
930 &self.accesses_by_node[node_idx]
931 }
932
933 fn record_cmd(&mut self, cmd: &CommandData) {
934 for exec in &cmd.execs {
935 for (node_idx, accesses) in exec.accesses.iter() {
936 self.accesses_by_node[node_idx].extend(
937 accesses
938 .iter()
939 .map(|access| PipelineStageAccessFlags::new(access.access)),
940 );
941 }
942 }
943 }
944}
945
946#[derive(Clone, Copy, Debug)]
947struct QueueOwnershipReleaseWait {
948 semaphore: vk::Semaphore,
949 stage_mask: vk::PipelineStageFlags2,
950 value: u64,
951 device_index: u32,
952}
953
954#[derive(Debug)]
955struct CommandRecordingResources {
956 descriptor_pool: Option<Lease<DescriptorPool>>,
957 descriptor_sets: Vec<Vec<DescriptorSet>>,
958 render_pass: Option<Lease<RenderPass>>,
959}
960
961impl CommandRecordingResources {
962 fn expect_render_pass_mut(&mut self) -> &mut Lease<RenderPass> {
966 self.render_pass.as_mut().expect("missing render pass")
967 }
968}
969
970impl Drop for CommandRecordingResources {
971 fn drop(&mut self) {
972 self.descriptor_sets.clear();
973 self.descriptor_pool = None;
974 }
975}
976
977#[derive(Clone, Copy, Debug)]
978struct ImageQueueOwnershipTransfer {
979 dst_queue_family_index: u32,
980 layout: vk::ImageLayout,
981 range: vk::ImageSubresourceRange,
982 src_queue_family_index: u32,
983 src_queue_index: u32,
984}
985
986impl PartialEq for ImageQueueOwnershipTransfer {
987 fn eq(&self, other: &Self) -> bool {
988 self.dst_queue_family_index == other.dst_queue_family_index
989 && self.layout == other.layout
990 && self.src_queue_family_index == other.src_queue_family_index
991 && self.src_queue_index == other.src_queue_index
992 && image_subresource_range_eq(self.range, other.range)
993 }
994}
995
996#[derive(Debug)]
997struct ImageRangeSet {
998 image: vk::Image,
999 range_keys: HashSet<ImageSubresourceRangeKey>,
1000}
1001
1002struct ImageSubresourceRangeDebug(vk::ImageSubresourceRange);
1003
1004impl std::fmt::Debug for ImageSubresourceRangeDebug {
1005 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1006 self.0.aspect_mask.fmt(f)?;
1007
1008 f.write_str(" array: ")?;
1009
1010 let array_layers = self.0.base_array_layer..self.0.base_array_layer + self.0.layer_count;
1011 array_layers.fmt(f)?;
1012
1013 f.write_str(" mip: ")?;
1014
1015 let mip_levels = self.0.base_mip_level..self.0.base_mip_level + self.0.level_count;
1016 mip_levels.fmt(f)
1017 }
1018}
1019
1020#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
1022struct ImageSubresourceRangeKey {
1023 aspect_mask: vk::ImageAspectFlags,
1024 base_array_layer: u32,
1025 layer_count: u32,
1026 base_mip_level: u32,
1027 level_count: u32,
1028}
1029
1030impl ImageSubresourceRangeKey {
1031 fn from_range(
1032 vk::ImageSubresourceRange {
1033 aspect_mask,
1034 base_array_layer,
1035 layer_count,
1036 base_mip_level,
1037 level_count,
1038 }: vk::ImageSubresourceRange,
1039 ) -> Self {
1040 Self {
1041 aspect_mask,
1042 base_array_layer,
1043 layer_count,
1044 base_mip_level,
1045 level_count,
1046 }
1047 }
1048
1049 fn into_range(
1050 Self {
1051 aspect_mask,
1052 base_array_layer,
1053 layer_count,
1054 base_mip_level,
1055 level_count,
1056 }: Self,
1057 ) -> vk::ImageSubresourceRange {
1058 vk::ImageSubresourceRange {
1059 aspect_mask,
1060 base_array_layer,
1061 layer_count,
1062 base_mip_level,
1063 level_count,
1064 }
1065 }
1066}
1067
1068#[derive(Debug)]
1069struct NodeIndexedScratch<T> {
1070 entries: Vec<NodeIndexedScratchEntry<T>>,
1071 indices: Vec<NodeIndex>,
1072}
1073
1074impl<T> NodeIndexedScratch<T> {
1075 fn clear(&mut self) {
1076 for &node_idx in self.indices.iter() {
1077 let Some(entry) = self.entries.get_mut(node_idx) else {
1078 continue;
1079 };
1080
1081 entry.occupied = false;
1082 entry.values.clear();
1083 }
1084
1085 self.indices.clear();
1086 }
1087
1088 fn get(&self, node_idx: NodeIndex) -> &[T] {
1089 self.entries
1090 .get(node_idx)
1091 .filter(|entry| entry.occupied)
1092 .map_or_else(Default::default, |entry| entry.values.as_slice())
1093 }
1094
1095 fn push(&mut self, node_idx: NodeIndex, value: T) {
1096 if self.entries.len() <= node_idx {
1097 self.entries
1098 .resize_with(node_idx.saturating_add(1), Default::default);
1099 }
1100
1101 let entry = &mut self.entries[node_idx];
1102
1103 if !entry.occupied {
1104 entry.occupied = true;
1105 self.indices.push(node_idx);
1106 }
1107
1108 entry.values.push(value);
1109 }
1110}
1111
1112impl<T> Default for NodeIndexedScratch<T> {
1113 fn default() -> Self {
1114 Self {
1115 entries: Default::default(),
1116 indices: Default::default(),
1117 }
1118 }
1119}
1120
1121#[derive(Debug)]
1122struct NodeIndexedScratchEntry<T> {
1123 occupied: bool,
1124 values: Vec<T>,
1125}
1126
1127impl<T> Default for NodeIndexedScratchEntry<T> {
1128 fn default() -> Self {
1129 Self {
1130 occupied: false,
1131 values: Default::default(),
1132 }
1133 }
1134}
1135
1136#[derive(Debug)]
1137struct PendingTransferNode<H, T> {
1138 handle: H,
1139 transfers: Vec<T>,
1140}
1141
1142#[derive(Debug)]
1143struct PendingTransferNodes<H, T> {
1144 entries: Vec<Option<PendingTransferNode<H, T>>>,
1145 indices: Vec<NodeIndex>,
1146}
1147
1148impl<H, T> PendingTransferNodes<H, T>
1149where
1150 H: Copy,
1151{
1152 fn new(node_count: usize) -> Self {
1153 let mut entries = Vec::with_capacity(node_count);
1154 entries.resize_with(node_count, || None);
1155
1156 Self {
1157 entries,
1158 indices: Vec::new(),
1159 }
1160 }
1161
1162 fn contains(&self, node_idx: NodeIndex) -> bool {
1163 self.entries[node_idx].is_some()
1164 }
1165
1166 fn is_empty(&self) -> bool {
1167 self.indices.is_empty()
1168 }
1169
1170 fn iter(&self) -> impl Iterator<Item = (NodeIndex, H, &[T])> + '_ {
1171 self.indices.iter().filter_map(|&node_idx| {
1172 self.entries[node_idx]
1173 .as_ref()
1174 .map(|entry| (node_idx, entry.handle, entry.transfers.as_slice()))
1175 })
1176 }
1177
1178 fn push_transfer(&mut self, node_idx: NodeIndex, handle: H, transfer: T) -> bool {
1179 let inserted = self.entries[node_idx].is_none();
1180
1181 if inserted {
1182 self.indices.push(node_idx);
1183 self.entries[node_idx] = Some(PendingTransferNode {
1184 handle,
1185 transfers: vec![transfer],
1186 });
1187 } else {
1188 let entry = self.entries[node_idx]
1189 .as_mut()
1190 .expect("missing pending transfer node");
1191
1192 entry.handle = handle;
1193 entry.transfers.push(transfer);
1194 }
1195
1196 inserted
1197 }
1198
1199 fn remove_where<F>(&mut self, mut remove: F)
1200 where
1201 F: FnMut(NodeIndex, H, &mut Vec<T>) -> bool,
1202 {
1203 let mut pending_idx = 0;
1204
1205 while pending_idx < self.indices.len() {
1206 let node_idx = self.indices[pending_idx];
1207
1208 let Some(entry) = self.entries[node_idx].as_mut() else {
1209 self.indices.swap_remove(pending_idx);
1210 continue;
1211 };
1212
1213 if remove(node_idx, entry.handle, &mut entry.transfers) {
1214 self.entries[node_idx] = None;
1215 self.indices.swap_remove(pending_idx);
1216 } else {
1217 pending_idx += 1;
1218 }
1219 }
1220 }
1221}
1222
1223#[derive(Clone, Copy)]
1224struct PipelineStageAccessFlags {
1225 access_flags: vk::AccessFlags,
1226 stage_flags: vk::PipelineStageFlags,
1227}
1228
1229impl PipelineStageAccessFlags {
1230 fn new(access: AccessType) -> Self {
1231 let (mut stage_flags, access_flags) = pipeline_stage_access_flags(access);
1232 if stage_flags.contains(vk::PipelineStageFlags::ALL_COMMANDS) {
1233 stage_flags |= vk::PipelineStageFlags::ALL_GRAPHICS;
1234 stage_flags &= !vk::PipelineStageFlags::ALL_COMMANDS;
1235 }
1236
1237 Self {
1238 access_flags,
1239 stage_flags,
1240 }
1241 }
1242}
1243
1244#[derive(Debug)]
1245struct QueueOwnershipRelease {
1246 _cmd_buf: Lease<CommandBuffer>,
1247 _fence: Fence,
1248 semaphore: vk::Semaphore,
1249}
1250
1251#[derive(Debug)]
1252struct QueueOwnershipReleaseGroup {
1253 buffers: Vec<(vk::Buffer, BufferSubresourceRange)>,
1254 images: Vec<(vk::Image, vk::ImageLayout, vk::ImageSubresourceRange)>,
1255 src_queue_family_index: u32,
1256 src_queue_index: u32,
1257}
1258
1259#[derive(Clone, Copy, Debug)]
1261pub enum QueueSubmitInfo<'a> {
1262 QueueSubmit {
1266 waits: &'a [SemaphoreSubmitInfo],
1268
1269 signals: &'a [SemaphoreSubmitInfo],
1271 },
1272
1273 QueueSubmit2 {
1277 waits: &'a [SemaphoreSubmit2Info],
1279
1280 signals: &'a [SemaphoreSubmit2Info],
1282 },
1283}
1284
1285impl QueueSubmitInfo<'static> {
1286 pub const QUEUE_SUBMIT: Self = Self::QueueSubmit {
1288 waits: &[],
1289 signals: &[],
1290 };
1291
1292 pub const QUEUE_SUBMIT2: Self = Self::QueueSubmit2 {
1294 waits: &[],
1295 signals: &[],
1296 };
1297}
1298
1299impl<'a> QueueSubmitInfo<'a> {
1300 pub fn queue_submit(
1302 waits: &'a [SemaphoreSubmitInfo],
1303 signals: &'a [SemaphoreSubmitInfo],
1304 ) -> Self {
1305 Self::QueueSubmit { waits, signals }
1306 }
1307
1308 pub fn queue_submit2(
1310 waits: &'a [SemaphoreSubmit2Info],
1311 signals: &'a [SemaphoreSubmit2Info],
1312 ) -> Self {
1313 Self::QueueSubmit2 { waits, signals }
1314 }
1315}
1316
1317impl<'a> From<(&'a [SemaphoreSubmitInfo], &'a [SemaphoreSubmitInfo])> for QueueSubmitInfo<'a> {
1318 fn from((waits, signals): (&'a [SemaphoreSubmitInfo], &'a [SemaphoreSubmitInfo])) -> Self {
1319 Self::QueueSubmit { waits, signals }
1320 }
1321}
1322
1323impl<'a> From<(&'a [SemaphoreSubmit2Info], &'a [SemaphoreSubmit2Info])> for QueueSubmitInfo<'a> {
1324 fn from((waits, signals): (&'a [SemaphoreSubmit2Info], &'a [SemaphoreSubmit2Info])) -> Self {
1325 Self::QueueSubmit2 { waits, signals }
1326 }
1327}
1328
1329#[derive(Clone, Copy, Debug)]
1331pub enum RecordSelection<'a> {
1332 All,
1334
1335 Dependencies(AnyNode),
1337
1338 Node(AnyNode),
1340
1341 Nodes(&'a [AnyNode]),
1345}
1346
1347impl<'a> RecordSelection<'a> {
1348 pub fn dependencies(node: impl Into<AnyNode>) -> Self {
1351 Self::Dependencies(node.into())
1352 }
1353
1354 pub fn node(node: impl Into<AnyNode>) -> Self {
1356 Self::Node(node.into())
1357 }
1358
1359 pub fn nodes(nodes: &'a [AnyNode]) -> Self {
1363 Self::Nodes(nodes)
1364 }
1365}
1366
1367impl<'a> From<AnyNode> for RecordSelection<'a> {
1368 fn from(node: AnyNode) -> Self {
1369 Self::Node(node)
1370 }
1371}
1372
1373macro_rules! record_selection_from_node {
1374 ($node:ty) => {
1375 impl<'a> From<$node> for RecordSelection<'a> {
1376 fn from(node: $node) -> Self {
1377 Self::Node(node.into())
1378 }
1379 }
1380 };
1381}
1382
1383record_selection_from_node!(crate::node::AnyAccelerationStructureNode);
1384record_selection_from_node!(crate::node::AnyBufferNode);
1385record_selection_from_node!(crate::node::AnyImageNode);
1386record_selection_from_node!(crate::node::AccelerationStructureNode);
1387record_selection_from_node!(crate::node::AccelerationStructureLeaseNode);
1388record_selection_from_node!(crate::node::BufferNode);
1389record_selection_from_node!(crate::node::BufferLeaseNode);
1390record_selection_from_node!(crate::node::ImageNode);
1391record_selection_from_node!(crate::node::ImageLeaseNode);
1392record_selection_from_node!(crate::node::SwapchainImageNode);
1393
1394#[derive(Debug)]
1396#[read_only::cast]
1397pub struct RecordedSubmission<Cb> {
1398 cmd_buf: Cb,
1399 queue_ownership_release_waits: Vec<QueueOwnershipReleaseWait>,
1400 state: Arc<Mutex<RecordedSubmissionState>>,
1401}
1402
1403impl<Cb> RecordedSubmission<Cb>
1404where
1405 Cb: AsRef<CommandBuffer>,
1406{
1407 fn attach(&self, fence: &mut Fence, queue_index: u32) {
1408 let state = self
1409 .state
1410 .lock()
1411 .expect("poisoned recorded submission state");
1412
1413 let queue_family_index = self.cmd_buf.as_ref().info.queue_family_index;
1414
1415 for (node_idx, ranges) in &state.submission.exclusive_buffer_ranges {
1416 if let Some(resource) = state.submission.graph.resources[*node_idx].as_buffer() {
1417 resource.set_sharing_ranges(
1418 SharingMode::Exclusive(Some((queue_family_index, queue_index))),
1419 ranges.as_slice(),
1420 );
1421 }
1422 }
1423
1424 for (node_idx, ranges) in &state.submission.exclusive_image_ranges {
1425 if let Some(resource) = state.submission.graph.resources[*node_idx].as_image() {
1426 resource.set_sharing_ranges(
1427 SharingMode::Exclusive(Some((queue_family_index, queue_index))),
1428 ranges.as_slice(),
1429 );
1430 }
1431 }
1432
1433 drop(state);
1434
1435 fence.drop_when_signaled(self.state.clone());
1436 }
1437
1438 pub fn queue_submit<'a>(
1440 &mut self,
1441 fence: &mut Fence,
1442 queue_index: u32,
1443 submit_info: impl Into<QueueSubmitInfo<'a>>,
1444 ) -> Result<(), DriverError> {
1445 #[cfg(feature = "checked")]
1446 if fence.queued {
1447 fence.wait_signaled()?;
1448 fence.reset()?;
1449 }
1450
1451 let command_buffer = self.cmd_buf.as_ref();
1452 let device = &command_buffer.device;
1453 let queue_family_index = command_buffer.info.queue_family_index;
1454
1455 match submit_info.into() {
1456 QueueSubmitInfo::QueueSubmit { waits, signals } => {
1457 check_queue_submit_args(waits, signals)?;
1458
1459 let extra_waits = self.queue_ownership_release_waits.as_slice();
1460 let wait_count = waits.len() + extra_waits.len();
1461
1462 Device::with_queue(device, queue_family_index, queue_index, |queue| {
1463 SUBMIT.with_borrow_mut(|tls| {
1464 tls.wait_semaphores.clear();
1465 tls.wait_stage_masks.clear();
1466 tls.signal_semaphores.clear();
1467 tls.wait_semaphores.reserve(wait_count);
1468 tls.wait_stage_masks.reserve(wait_count);
1469 tls.signal_semaphores.reserve(signals.len());
1470
1471 tls.wait_semaphores
1472 .extend(waits.iter().map(|wait| wait.semaphore));
1473 tls.wait_stage_masks.extend(
1474 waits
1475 .iter()
1476 .map(|wait| submit_stage_mask_legacy(wait.stage_mask)),
1477 );
1478 tls.wait_semaphores
1479 .extend(extra_waits.iter().map(|wait| wait.semaphore));
1480 tls.wait_stage_masks.extend(
1481 extra_waits
1482 .iter()
1483 .map(|wait| submit_stage_mask_legacy(wait.stage_mask)),
1484 );
1485 tls.signal_semaphores
1486 .extend(signals.iter().map(|signal| signal.semaphore));
1487
1488 let mut submit_info = vk::SubmitInfo::default()
1489 .command_buffers(slice::from_ref(&command_buffer.handle))
1490 .signal_semaphores(tls.signal_semaphores.as_slice());
1491
1492 if !tls.wait_semaphores.is_empty() {
1493 submit_info = submit_info
1494 .wait_semaphores(tls.wait_semaphores.as_slice())
1495 .wait_dst_stage_mask(tls.wait_stage_masks.as_slice());
1496 }
1497
1498 Device::queue_submit(
1499 device,
1500 queue,
1501 slice::from_ref(&submit_info),
1502 fence.handle,
1503 )?;
1504
1505 Ok::<(), DriverError>(())
1506 })
1507 })?;
1508 fence.mark_queued();
1509 }
1510 QueueSubmitInfo::QueueSubmit2 { waits, signals } => {
1511 check_queue_submit2_args(device, waits, signals)?;
1512
1513 let extra_waits = self.queue_ownership_release_waits.as_slice();
1514 let wait_count = waits.len() + extra_waits.len();
1515
1516 Device::with_queue(device, queue_family_index, queue_index, |queue| {
1517 SUBMIT.with_borrow_mut(|tls| {
1518 tls.wait_infos.clear();
1519 tls.signal_infos.clear();
1520 tls.wait_infos.reserve(wait_count);
1521 tls.signal_infos.reserve(signals.len());
1522
1523 tls.wait_infos.extend(waits.iter().map(|wait| {
1524 vk::SemaphoreSubmitInfo::default()
1525 .semaphore(wait.semaphore)
1526 .stage_mask(wait.stage_mask)
1527 .value(wait.value)
1528 .device_index(wait.device_index)
1529 }));
1530 tls.wait_infos.extend(extra_waits.iter().map(|wait| {
1531 vk::SemaphoreSubmitInfo::default()
1532 .semaphore(wait.semaphore)
1533 .stage_mask(wait.stage_mask)
1534 .value(wait.value)
1535 .device_index(wait.device_index)
1536 }));
1537 tls.signal_infos.extend(signals.iter().map(|signal| {
1538 vk::SemaphoreSubmitInfo::default()
1539 .semaphore(signal.semaphore)
1540 .stage_mask(signal.stage_mask)
1541 .value(signal.value)
1542 .device_index(signal.device_index)
1543 }));
1544
1545 let command_buffer_info = vk::CommandBufferSubmitInfo::default()
1546 .command_buffer(command_buffer.handle);
1547 let mut submit_info = vk::SubmitInfo2::default()
1548 .command_buffer_infos(slice::from_ref(&command_buffer_info));
1549
1550 if !tls.wait_infos.is_empty() {
1551 submit_info =
1552 submit_info.wait_semaphore_infos(tls.wait_infos.as_slice());
1553 }
1554
1555 if !tls.signal_infos.is_empty() {
1556 submit_info =
1557 submit_info.signal_semaphore_infos(tls.signal_infos.as_slice());
1558 }
1559
1560 Device::queue_submit2(
1561 device,
1562 queue,
1563 slice::from_ref(&submit_info),
1564 fence.handle,
1565 )?;
1566
1567 Ok::<(), DriverError>(())
1568 })
1569 })?;
1570 fence.mark_queued();
1571 }
1572 }
1573
1574 self.attach(fence, queue_index);
1575 self.queue_ownership_release_waits.clear();
1576
1577 Ok(())
1578 }
1579}
1580
1581#[derive(Debug)]
1582struct RecordedSubmissionState {
1583 _releases: Vec<QueueOwnershipRelease>,
1584 submission: Submission,
1585}
1586
1587#[derive(Debug)]
1589#[read_only::cast]
1590pub struct Recording<'p, P, Cb> {
1591 #[readonly]
1595 pub cmd_buf: Cb,
1596
1597 #[readonly]
1602 pub resource_pool: &'p mut P,
1603
1604 submission: Submission,
1605}
1606
1607impl<'p, P, Cb> Recording<'p, P, Cb>
1608where
1609 Cb: AsRef<CommandBuffer>,
1610{
1611 pub fn is_empty(&self) -> bool {
1613 self.submission.is_empty()
1614 }
1615
1616 pub fn resource<N>(&self, resource_node: N) -> &N::Resource
1619 where
1620 N: Node,
1621 {
1622 self.submission.resource(resource_node)
1623 }
1624
1625 pub fn finish(self) -> Result<RecordedSubmission<Cb>, DriverError>
1627 where
1628 P: Pool<CommandBufferInfo, CommandBuffer>,
1629 {
1630 let Self {
1631 cmd_buf,
1632 resource_pool,
1633 submission,
1634 } = self;
1635
1636 let queue_family_index = cmd_buf.as_ref().info.queue_family_index;
1637 let releases = submit_queue_ownership_releases(
1638 resource_pool,
1639 &submission.queue_ownership_release_groups,
1640 queue_family_index,
1641 |device, queue, cmd_handle, fence, semaphore| {
1642 let submit_info = vk::SubmitInfo::default()
1643 .command_buffers(slice::from_ref(&cmd_handle))
1644 .signal_semaphores(slice::from_ref(&semaphore));
1645 Device::queue_submit(device, queue, slice::from_ref(&submit_info), fence)
1646 },
1647 )?;
1648 let waits = releases
1649 .iter()
1650 .map(|release| QueueOwnershipReleaseWait {
1651 semaphore: release.semaphore,
1652 stage_mask: vk::PipelineStageFlags2::ALL_COMMANDS,
1653 value: 0,
1654 device_index: 0,
1655 })
1656 .collect();
1657
1658 Ok(submission.into_recorded_submission(cmd_buf, releases, waits))
1659 }
1660}
1661
1662impl<'p, P, Cb> Recording<'p, P, Cb>
1663where
1664 P: SubmissionPool,
1665 Cb: AsRef<CommandBuffer>,
1666{
1667 #[profiling::function]
1672 pub fn record<'s>(
1673 &mut self,
1674 selection: impl Into<RecordSelection<'s>>,
1675 ) -> Result<(), DriverError> {
1676 self.submission.record_selection_impl(
1677 self.resource_pool,
1678 self.cmd_buf.as_ref(),
1679 selection.into(),
1680 )
1681 }
1682}
1683
1684#[derive(Default)]
1685struct Schedule {
1686 access_index: CommandAccessIndex,
1687 interdependent: Vec<Vec<usize>>,
1688 cmds: Vec<usize>,
1689}
1690
1691impl Schedule {
1692 #[profiling::function]
1693 fn reorder_cmds(&mut self, end_cmd_idx: usize) {
1694 if self.cmds.len() < 3 {
1695 return;
1696 }
1697
1698 let cmd_count = self.cmds.len();
1699
1700 for dep_cmds in self.interdependent.iter_mut() {
1701 dep_cmds.clear();
1702 }
1703
1704 self.interdependent.resize_with(cmd_count, Vec::new);
1705
1706 let mut local_of_global = vec![usize::MAX; end_cmd_idx];
1707
1708 for (local_idx, &cmd_idx) in self.cmds.iter().enumerate() {
1709 local_of_global[cmd_idx] = local_idx;
1710 }
1711
1712 let mut seen_deps = FixedBitSet::with_capacity(cmd_count);
1713
1714 for (local_idx, &cmd_idx) in self.cmds.iter().enumerate() {
1715 for dep_cmd_idx in self
1716 .access_index
1717 .prior_read_dependency_cmds(cmd_idx, end_cmd_idx)
1718 {
1719 let dep_local_idx = local_of_global[dep_cmd_idx];
1720 if dep_local_idx == usize::MAX || dep_local_idx == local_idx {
1721 continue;
1722 }
1723
1724 if !seen_deps.put(dep_local_idx) {
1725 self.interdependent[local_idx].push(dep_local_idx);
1726 }
1727 }
1728
1729 for dep_cmd_idx in self
1730 .access_index
1731 .prior_read_dependency_cmds(cmd_idx, end_cmd_idx)
1732 {
1733 let dep_local_idx = local_of_global[dep_cmd_idx];
1734 if dep_local_idx != usize::MAX && dep_local_idx != local_idx {
1735 seen_deps.set(dep_local_idx, false);
1736 }
1737 }
1738 }
1739
1740 let mut scheduled = FixedBitSet::with_capacity(cmd_count);
1741 let mut scheduled_count = 0;
1742
1743 while scheduled_count < cmd_count {
1744 let mut best_idx = scheduled_count;
1745 let mut best_overlap = self.interdependent[best_idx].len();
1746
1747 for idx in (scheduled_count + 1)..cmd_count {
1748 let mut overlap = 0;
1749
1750 for &dep_local in &self.interdependent[idx] {
1751 if scheduled.contains(dep_local) {
1752 overlap += 1;
1753 } else {
1754 break;
1755 }
1756 }
1757
1758 if overlap > best_overlap {
1759 best_overlap = overlap;
1760 best_idx = idx;
1761 }
1762 }
1763
1764 scheduled.insert(best_idx);
1765 self.cmds.swap(scheduled_count, best_idx);
1766 self.interdependent.swap(scheduled_count, best_idx);
1767 scheduled_count += 1;
1768 }
1769 }
1770}
1771
1772#[derive(Clone, Copy, Debug, Default)]
1782pub struct SemaphoreSubmitInfo {
1783 pub semaphore: vk::Semaphore,
1787
1788 pub stage_mask: vk::PipelineStageFlags2,
1792
1793 pub value: u64,
1795}
1796
1797impl SemaphoreSubmitInfo {
1798 fn is_supported_legacy_submit(&self) -> bool {
1799 self.value == 0
1800 && matches!(
1801 self.stage_mask,
1802 vk::PipelineStageFlags2::ALL_COMMANDS | vk::PipelineStageFlags2::NONE
1803 )
1804 }
1805}
1806
1807#[derive(Clone, Copy, Debug, Default)]
1816pub struct SemaphoreSubmit2Info {
1817 pub semaphore: vk::Semaphore,
1821
1822 pub stage_mask: vk::PipelineStageFlags2,
1826
1827 pub value: u64,
1829
1830 pub device_index: u32,
1832}
1833
1834#[derive(Debug)]
1843pub struct Submission {
1844 exclusive_buffer_ranges: HashMap<usize, Vec<BufferSubresourceRange>>,
1845 exclusive_image_ranges: HashMap<usize, Vec<vk::ImageSubresourceRange>>,
1846 graph: Graph,
1847 pending_buffer_transfer_nodes:
1848 Option<PendingTransferNodes<vk::Buffer, BufferQueueOwnershipTransfer>>,
1849 pending_image_transfer_nodes:
1850 Option<PendingTransferNodes<vk::Image, ImageQueueOwnershipTransfer>>,
1851 queue_ownership_release_groups: Vec<QueueOwnershipReleaseGroup>,
1852 recorded_commands: Vec<CommandRecordingResources>,
1853 submit_retained: Vec<Box<dyn FmtDebug + Send + 'static>>,
1854}
1855
1856impl Submission {
1857 const GRAPHICS_STAGES: vk::PipelineStageFlags = vk::PipelineStageFlags::from_raw(
1858 vk::PipelineStageFlags::DRAW_INDIRECT.as_raw()
1859 | vk::PipelineStageFlags::VERTEX_INPUT.as_raw()
1860 | vk::PipelineStageFlags::VERTEX_SHADER.as_raw()
1861 | vk::PipelineStageFlags::TESSELLATION_CONTROL_SHADER.as_raw()
1862 | vk::PipelineStageFlags::TESSELLATION_EVALUATION_SHADER.as_raw()
1863 | vk::PipelineStageFlags::GEOMETRY_SHADER.as_raw()
1864 | vk::PipelineStageFlags::FRAGMENT_SHADER.as_raw()
1865 | vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS.as_raw()
1866 | vk::PipelineStageFlags::LATE_FRAGMENT_TESTS.as_raw()
1867 | vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT.as_raw()
1868 | vk::PipelineStageFlags::TASK_SHADER_EXT.as_raw()
1869 | vk::PipelineStageFlags::MESH_SHADER_EXT.as_raw(),
1870 );
1871
1872 pub(super) fn new(graph: Graph) -> Self {
1873 let recorded_commands = Vec::with_capacity(graph.cmds.len());
1874 Self {
1875 exclusive_buffer_ranges: HashMap::new(),
1876 exclusive_image_ranges: HashMap::new(),
1877 pending_buffer_transfer_nodes: None,
1878 graph,
1879 queue_ownership_release_groups: Vec::new(),
1880 recorded_commands,
1881 pending_image_transfer_nodes: None,
1882 submit_retained: Vec::new(),
1883 }
1884 }
1885
1886 pub(crate) fn graph(&self) -> &Graph {
1887 &self.graph
1888 }
1889
1890 pub(crate) fn assert_reusable_commands(&self) {
1891 for cmd in &self.graph.cmds {
1892 for exec in &cmd.execs {
1893 assert!(
1894 exec.func
1895 .as_ref()
1896 .is_some_and(crate::CommandFunction::is_reusable),
1897 "command stream contains a one-shot callback"
1898 );
1899 }
1900 }
1901 }
1902
1903 pub(crate) fn prepare_command_stream<P>(&mut self, pool: &mut P) -> Result<(), DriverError>
1904 where
1905 P: SubmissionPool,
1906 {
1907 if self.graph.cmds.is_empty() {
1908 return Ok(());
1909 }
1910
1911 thread_local! {
1912 static SCHEDULE: RefCell<Schedule> = Default::default();
1913 }
1914
1915 SCHEDULE.with_borrow_mut(|schedule| {
1916 schedule
1917 .access_index
1918 .update(&self.graph, self.graph.cmds.len());
1919 schedule.cmds.clear();
1920 schedule.cmds.extend(0..self.graph.cmds.len());
1921
1922 debug_assert!(
1923 schedule.cmds.windows(2).all(|w| w[0] <= w[1]),
1924 "Unsorted schedule"
1925 );
1926
1927 schedule.reorder_cmds(self.graph.cmds.len());
1928 self.merge_scheduled_cmds(&mut schedule.cmds);
1929 self.lease_scheduled_resources(pool, &schedule.cmds)
1930 })
1931 }
1932
1933 pub(crate) fn record_prepared_command_stream(
1934 &mut self,
1935 cmd_buf: &CommandBuffer,
1936 resources: crate::ResourceMap,
1937 ) -> Result<(), DriverError> {
1938 let original_resources = std::mem::replace(&mut self.graph.resources, resources);
1939
1940 let result = self.record_prepared_command_stream_inner(cmd_buf);
1941
1942 self.graph.resources = original_resources;
1943
1944 result
1945 }
1946
1947 fn record_prepared_command_stream_inner(
1948 &mut self,
1949 cmd_buf: &CommandBuffer,
1950 ) -> Result<(), DriverError> {
1951 thread_local! {
1952 static SCHEDULE: RefCell<Schedule> = Default::default();
1953 }
1954
1955 SCHEDULE.with_borrow_mut(|schedule| {
1956 schedule
1957 .access_index
1958 .update(&self.graph, self.graph.cmds.len());
1959 schedule.cmds.clear();
1960 schedule.cmds.extend(0..self.graph.cmds.len());
1961 self.track_pending_transfers(schedule, cmd_buf.info.queue_family_index);
1962 self.queue_ownership_release_groups
1963 .extend(self.collect_queue_ownership_release_groups());
1964 });
1965
1966 self.record_cmd_indices(cmd_buf, 0..self.graph.cmds.len())?;
1967
1968 Ok(())
1969 }
1970
1971 fn into_recorded_submission<Cb>(
1972 self,
1973 cmd_buf: Cb,
1974 releases: Vec<QueueOwnershipRelease>,
1975 waits: Vec<QueueOwnershipReleaseWait>,
1976 ) -> RecordedSubmission<Cb>
1977 where
1978 Cb: AsRef<CommandBuffer>,
1979 {
1980 RecordedSubmission {
1981 cmd_buf,
1982 queue_ownership_release_waits: waits,
1983 state: Arc::new(Mutex::new(RecordedSubmissionState {
1984 _releases: releases,
1985 submission: self,
1986 })),
1987 }
1988 }
1989
1990 fn is_framebuffer_space(stages: vk::PipelineStageFlags) -> bool {
1991 stages.intersects(
1992 vk::PipelineStageFlags::FRAGMENT_SHADER
1993 | vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS
1994 | vk::PipelineStageFlags::LATE_FRAGMENT_TESTS
1995 | vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT,
1996 )
1997 }
1998
1999 fn subpass_dependency_stage_masks(
2000 previous: vk::PipelineStageFlags,
2001 current: vk::PipelineStageFlags,
2002 ) -> Option<(vk::PipelineStageFlags, vk::PipelineStageFlags)> {
2003 let all_graphics = vk::PipelineStageFlags::ALL_GRAPHICS;
2004 let previous_all_graphics = previous.contains(all_graphics);
2005 let current_all_graphics = current.contains(all_graphics);
2006
2007 let overlaps = if previous_all_graphics && current_all_graphics {
2008 true
2009 } else if previous_all_graphics {
2010 current.intersects(Self::GRAPHICS_STAGES)
2011 } else if current_all_graphics {
2012 previous.intersects(Self::GRAPHICS_STAGES)
2013 } else {
2014 previous.intersects(current)
2015 };
2016
2017 if !overlaps {
2018 return None;
2019 }
2020
2021 if previous_all_graphics || current_all_graphics {
2022 Some((previous, current))
2023 } else {
2024 let stages = previous & current;
2025
2026 Some((stages, stages))
2027 }
2028 }
2029
2030 fn record_subpass_dependency(
2031 dependencies: &mut BTreeMap<(usize, usize), SubpassDependency>,
2032 src_subpass: usize,
2033 dst_subpass: usize,
2034 previous: PipelineStageAccessFlags,
2035 dst_stage_mask: vk::PipelineStageFlags,
2036 current: &mut PipelineStageAccessFlags,
2037 ) -> bool {
2038 let Some((src_stage_mask, matched_dst_stages)) =
2039 Self::subpass_dependency_stage_masks(previous.stage_flags, current.stage_flags)
2040 else {
2041 return false;
2042 };
2043
2044 let dep = dependencies
2045 .entry((src_subpass, dst_subpass))
2046 .or_insert_with(|| SubpassDependency::new(src_subpass as _, dst_subpass as _));
2047
2048 dep.src_stage_mask |= src_stage_mask;
2049 dep.src_access_mask |= previous.access_flags;
2050 dep.dst_stage_mask |= dst_stage_mask;
2051 dep.dst_access_mask |= current.access_flags;
2052
2053 if Self::is_framebuffer_space(previous.stage_flags | current.stage_flags) {
2054 dep.dependency_flags |= vk::DependencyFlags::BY_REGION;
2055 }
2056
2057 current.stage_flags &= !matched_dst_stages;
2058
2059 current.stage_flags.is_empty()
2060 }
2061
2062 #[profiling::function]
2063 fn allow_merge_passes(lhs: &CommandData, rhs: &CommandData) -> bool {
2064 fn first_graphic_pipeline(pass: &CommandData) -> Option<&GraphicsPipeline> {
2065 pass.execs
2066 .first()
2067 .and_then(|exec| exec.pipeline.as_ref().map(ExecutionPipeline::as_graphics))
2068 .flatten()
2069 }
2070
2071 fn is_multiview(view_mask: u32) -> bool {
2072 view_mask != 0
2073 }
2074
2075 let lhs_pipeline = first_graphic_pipeline(lhs);
2076 if lhs_pipeline.is_none() {
2077 trace!(" {} is not graphics", lhs.name());
2078
2079 return false;
2080 }
2081
2082 let rhs_pipeline = first_graphic_pipeline(rhs);
2083 if rhs_pipeline.is_none() {
2084 trace!(" {} is not graphics", rhs.name());
2085
2086 return false;
2087 }
2088
2089 let lhs_pipeline = unsafe { lhs_pipeline.unwrap_unchecked() };
2090 let rhs_pipeline = unsafe { rhs_pipeline.unwrap_unchecked() };
2091
2092 let lhs_info = lhs_pipeline.inner.info;
2094 let rhs_info = rhs_pipeline.inner.info;
2095 if lhs_info.blend != rhs_info.blend
2096 || lhs_info.cull_mode != rhs_info.cull_mode
2097 || lhs_info.front_face != rhs_info.front_face
2098 || lhs_info.polygon_mode != rhs_info.polygon_mode
2099 || lhs_info.samples != rhs_info.samples
2100 {
2101 trace!(" different rasterization modes",);
2102
2103 return false;
2104 }
2105
2106 let rhs = rhs.execs.first();
2107
2108 debug_assert!(rhs.is_some());
2110
2111 let rhs = unsafe { rhs.unwrap_unchecked() };
2112
2113 let mut common_color_attachment = false;
2114 let mut common_depth_attachment = false;
2115
2116 for lhs in lhs.execs.iter().rev() {
2118 if is_multiview(lhs.view_mask) != is_multiview(rhs.view_mask) {
2120 trace!(" incompatible multiview");
2121
2122 return false;
2123 }
2124
2125 for (attachment_idx, lhs_attachment) in lhs.attachments.color_attachments() {
2127 let rhs_attachment = rhs
2128 .attachments
2129 .color_attachment(attachment_idx)
2130 .map(|state| state.attachment);
2131
2132 if !Attachment::are_compatible(Some(lhs_attachment.attachment), rhs_attachment) {
2133 trace!(" incompatible color attachments");
2134
2135 return false;
2136 }
2137
2138 common_color_attachment = true;
2139 }
2140
2141 let lhs_depth_stencil = lhs
2143 .attachments
2144 .depth_stencil_attachment()
2145 .map(|state| state.attachment);
2146
2147 let rhs_depth_stencil = rhs
2148 .attachments
2149 .depth_stencil_attachment()
2150 .map(|state| state.attachment);
2151
2152 if !Attachment::are_compatible(lhs_depth_stencil, rhs_depth_stencil) {
2153 trace!(" incompatible depth/stencil attachments");
2154
2155 return false;
2156 }
2157
2158 common_depth_attachment |= lhs_depth_stencil.is_some() && rhs_depth_stencil.is_some();
2159 }
2160
2161 if common_color_attachment || common_depth_attachment {
2163 trace!(" merging due to common image");
2164
2165 return true;
2166 }
2167
2168 if !rhs_pipeline.inner.input_attachments.is_empty() {
2170 trace!(" merging due to subpass input");
2171
2172 return true;
2173 }
2174
2175 trace!(" not merging");
2176
2177 false
2179 }
2180
2181 fn attachment_layout(
2182 aspect_mask: vk::ImageAspectFlags,
2183 is_random_access: bool,
2184 is_input: bool,
2185 ) -> vk::ImageLayout {
2186 if aspect_mask.contains(vk::ImageAspectFlags::COLOR) {
2187 if is_input {
2188 vk::ImageLayout::GENERAL
2189 } else {
2190 vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL
2191 }
2192 } else if aspect_mask.contains(vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL)
2193 {
2194 if is_random_access {
2195 if is_input {
2196 vk::ImageLayout::GENERAL
2197 } else {
2198 vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL
2199 }
2200 } else {
2201 vk::ImageLayout::DEPTH_STENCIL_READ_ONLY_OPTIMAL
2202 }
2203 } else if aspect_mask.contains(vk::ImageAspectFlags::DEPTH) {
2204 if is_random_access {
2205 if is_input {
2206 vk::ImageLayout::GENERAL
2207 } else {
2208 vk::ImageLayout::DEPTH_ATTACHMENT_OPTIMAL
2209 }
2210 } else {
2211 vk::ImageLayout::DEPTH_READ_ONLY_OPTIMAL
2212 }
2213 } else if aspect_mask.contains(vk::ImageAspectFlags::STENCIL) {
2214 if is_random_access {
2215 if is_input {
2216 vk::ImageLayout::GENERAL
2217 } else {
2218 vk::ImageLayout::STENCIL_ATTACHMENT_OPTIMAL
2219 }
2220 } else {
2221 vk::ImageLayout::STENCIL_READ_ONLY_OPTIMAL
2222 }
2223 } else {
2224 vk::ImageLayout::UNDEFINED
2225 }
2226 }
2227
2228 fn attachment_stage(aspect_mask: vk::ImageAspectFlags) -> vk::PipelineStageFlags {
2229 match aspect_mask {
2230 mask if mask.contains(vk::ImageAspectFlags::COLOR) => {
2231 vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT
2232 }
2233 mask if mask
2234 .intersects(vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL) =>
2235 {
2236 vk::PipelineStageFlags::LATE_FRAGMENT_TESTS
2237 }
2238 _ => vk::PipelineStageFlags::ALL_GRAPHICS,
2239 }
2240 }
2241
2242 fn attachment_read_stage(aspect_mask: vk::ImageAspectFlags) -> vk::PipelineStageFlags {
2243 match aspect_mask {
2244 mask if mask.contains(vk::ImageAspectFlags::COLOR) => {
2245 vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT
2246 }
2247 mask if mask
2248 .intersects(vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL) =>
2249 {
2250 vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS
2251 | vk::PipelineStageFlags::LATE_FRAGMENT_TESTS
2252 }
2253 _ => vk::PipelineStageFlags::ALL_GRAPHICS,
2254 }
2255 }
2256
2257 fn subpass_stage_mask(stages: vk::PipelineStageFlags) -> vk::PipelineStageFlags {
2258 if stages.is_empty() {
2259 return stages;
2260 }
2261
2262 if stages.contains(vk::PipelineStageFlags::ALL_GRAPHICS) {
2263 return vk::PipelineStageFlags::ALL_GRAPHICS;
2264 }
2265
2266 let graphics_stages = stages & Self::GRAPHICS_STAGES;
2267 if graphics_stages.is_empty() {
2268 vk::PipelineStageFlags::ALL_GRAPHICS
2269 } else {
2270 graphics_stages
2271 }
2272 }
2273
2274 fn attachment_write_access(aspect_mask: vk::ImageAspectFlags) -> vk::AccessFlags {
2275 match aspect_mask {
2276 mask if mask.contains(vk::ImageAspectFlags::COLOR) => {
2277 vk::AccessFlags::COLOR_ATTACHMENT_WRITE
2278 }
2279 mask if mask
2280 .intersects(vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL) =>
2281 {
2282 vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE
2283 }
2284 _ => vk::AccessFlags::MEMORY_READ | vk::AccessFlags::MEMORY_WRITE,
2285 }
2286 }
2287
2288 fn accel_struct_canonical_accesses<'a>(
2289 accesses: &'a [SubresourceAccess],
2290 scratch: &'a mut Vec<AccessType>,
2291 ) -> &'a [AccessType] {
2292 scratch.clear();
2293
2294 let [access] = accesses else {
2295 for access in accesses {
2296 if !scratch.contains(&access.access) {
2297 scratch.push(access.access);
2298 }
2299 }
2300
2301 return scratch.as_slice();
2302 };
2303
2304 slice::from_ref(&access.access)
2305 }
2306
2307 fn attachment_read_write_access(
2308 aspect_mask: vk::ImageAspectFlags,
2309 ) -> (vk::AccessFlags, vk::AccessFlags) {
2310 match aspect_mask {
2311 mask if mask.contains(vk::ImageAspectFlags::COLOR) => (
2312 vk::AccessFlags::COLOR_ATTACHMENT_READ,
2313 vk::AccessFlags::COLOR_ATTACHMENT_WRITE,
2314 ),
2315 mask if mask
2316 .intersects(vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL) =>
2317 {
2318 (
2319 vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ,
2320 vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE,
2321 )
2322 }
2323 _ => (
2324 vk::AccessFlags::MEMORY_READ | vk::AccessFlags::MEMORY_WRITE,
2325 vk::AccessFlags::MEMORY_READ | vk::AccessFlags::MEMORY_WRITE,
2326 ),
2327 }
2328 }
2329
2330 fn color_attachment_is_read(load: LoadOp<[f32; 4]>) -> bool {
2331 matches!(load, LoadOp::Load)
2332 }
2333
2334 fn color_attachment_is_write(
2335 load: LoadOp<[f32; 4]>,
2336 store: StoreOp,
2337 has_resolve: bool,
2338 ) -> bool {
2339 matches!(load, LoadOp::Clear(_)) || store == StoreOp::Store || has_resolve
2340 }
2341
2342 fn depth_stencil_attachment_is_read(load: LoadOp<vk::ClearDepthStencilValue>) -> bool {
2343 matches!(load, LoadOp::Load)
2344 }
2345
2346 fn depth_stencil_attachment_is_write(
2347 load: LoadOp<vk::ClearDepthStencilValue>,
2348 store: StoreOp,
2349 has_resolve: bool,
2350 ) -> bool {
2351 matches!(load, LoadOp::Clear(_)) || store == StoreOp::Store || has_resolve
2352 }
2353
2354 fn expect_attachment_image<'a>(
2355 bindings: &'a [AnyResource],
2356 attachment: &Attachment,
2357 ) -> &'a Image {
2358 bindings[attachment.target]
2359 .as_image()
2360 .expect("invalid attachment target image")
2361 }
2362
2363 #[profiling::function]
2364 fn begin_render_pass(
2365 cmd_buf: &CommandBuffer,
2366 bindings: &[AnyResource],
2367 pass: &CommandData,
2368 recorded_command: &mut CommandRecordingResources,
2369 render_area: vk::Rect2D,
2370 ) -> Result<(), DriverError> {
2371 trace!(" begin render pass");
2372
2373 let render_pass = recorded_command.expect_render_pass_mut();
2374 let attachment_count = render_pass.info.attachments.len();
2375
2376 let mut attachments = Vec::with_capacity(attachment_count);
2377 attachments.resize(
2378 attachment_count,
2379 FramebufferAttachmentImageInfo {
2380 flags: vk::ImageCreateFlags::empty(),
2381 usage: vk::ImageUsageFlags::empty(),
2382 width: 0,
2383 height: 0,
2384 layer_count: 0,
2385 view_formats: vec![],
2386 },
2387 );
2388
2389 thread_local! {
2390 static CLEARS_VIEWS: RefCell<(
2391 Vec<vk::ClearValue>,
2392 Vec<vk::ImageView>,
2393 )> = Default::default();
2394 }
2395
2396 CLEARS_VIEWS.with_borrow_mut(|(clear_values, image_views)| {
2397 clear_values.resize_with(attachment_count, vk::ClearValue::default);
2398 image_views.resize(attachment_count, vk::ImageView::null());
2399
2400 for exec in &pass.execs {
2401 for (attachment_idx, state) in exec.attachments.color_attachments() {
2402 let attachment = state.attachment;
2403 let attachment_image = &mut attachments[attachment_idx as usize];
2404 if let Err(idx) = attachment_image
2405 .view_formats
2406 .binary_search(&attachment.format)
2407 {
2408 if let LoadOp::Clear(clear_value) = state.load {
2409 clear_values[attachment_idx as usize] = vk::ClearValue {
2410 color: vk::ClearColorValue {
2411 float32: clear_value,
2412 },
2413 };
2414 }
2415
2416 let image = Self::expect_attachment_image(bindings, &attachment);
2417
2418 attachment_image.flags = image.info.flags;
2419 attachment_image.usage = image.info.usage;
2420 attachment_image.width = image.info.width >> attachment.base_mip_level;
2421 attachment_image.height = image.info.height >> attachment.base_mip_level;
2422 attachment_image.layer_count = attachment.array_layer_count;
2423 attachment_image.view_formats.insert(idx, attachment.format);
2424
2425 image_views[attachment_idx as usize] =
2426 Image::view(image, attachment.image_view_info(image.info))?;
2427 }
2428 }
2429
2430 if let Some(state) = exec.attachments.depth_stencil_attachment()
2431 && state.is_attachment
2432 {
2433 let attachment = state.attachment;
2434 let attachment_idx = attachments.len() - 1 - state.resolve.is_some() as usize;
2435 let attachment_image = &mut attachments[attachment_idx];
2436 if let Err(idx) = attachment_image
2437 .view_formats
2438 .binary_search(&attachment.format)
2439 {
2440 if let LoadOp::Clear(depth_stencil) = state.load {
2441 clear_values[attachment_idx] = vk::ClearValue { depth_stencil };
2442 }
2443
2444 let image = Self::expect_attachment_image(bindings, &attachment);
2445
2446 attachment_image.flags = image.info.flags;
2447 attachment_image.usage = image.info.usage;
2448 attachment_image.width = image.info.width >> attachment.base_mip_level;
2449 attachment_image.height = image.info.height >> attachment.base_mip_level;
2450 attachment_image.layer_count = attachment.array_layer_count;
2451 attachment_image.view_formats.insert(idx, attachment.format);
2452
2453 image_views[attachment_idx] =
2454 Image::view(image, attachment.image_view_info(image.info))?;
2455 }
2456 }
2457
2458 if let Some(state) = exec
2459 .attachments
2460 .depth_stencil_attachment()
2461 .and_then(|state| state.resolve)
2462 {
2463 let attachment_idx = attachments.len() - 1;
2464 let attachment_image = &mut attachments[attachment_idx];
2465 if let Err(idx) = attachment_image
2466 .view_formats
2467 .binary_search(&state.attachment.format)
2468 {
2469 let image = Self::expect_attachment_image(bindings, &state.attachment);
2470
2471 attachment_image.flags = image.info.flags;
2472 attachment_image.usage = image.info.usage;
2473 attachment_image.width =
2474 image.info.width >> state.attachment.base_mip_level;
2475 attachment_image.height =
2476 image.info.height >> state.attachment.base_mip_level;
2477 attachment_image.layer_count = state.attachment.array_layer_count;
2478 attachment_image
2479 .view_formats
2480 .insert(idx, state.attachment.format);
2481
2482 image_views[attachment_idx] =
2483 Image::view(image, state.attachment.image_view_info(image.info))?;
2484 }
2485 }
2486 }
2487
2488 let framebuffer =
2489 RenderPass::framebuffer(render_pass, FramebufferInfo { attachments })?;
2490
2491 unsafe {
2492 cmd_buf.device.cmd_begin_render_pass(
2493 cmd_buf.handle,
2494 &vk::RenderPassBeginInfo::default()
2495 .render_pass(render_pass.handle)
2496 .framebuffer(framebuffer)
2497 .render_area(render_area)
2498 .clear_values(clear_values)
2499 .push_next(
2500 &mut vk::RenderPassAttachmentBeginInfoKHR::default()
2501 .attachments(image_views),
2502 ),
2503 vk::SubpassContents::INLINE,
2504 );
2505 }
2506
2507 Ok(())
2508 })
2509 }
2510
2511 #[profiling::function]
2512 fn bind_descriptor_sets(
2513 cmd_buf: &CommandBuffer,
2514 pipeline: &ExecutionPipeline,
2515 recorded_command: &CommandRecordingResources,
2516 exec_idx: usize,
2517 ) {
2518 if let Some(exec_descriptor_sets) = recorded_command.descriptor_sets.get(exec_idx) {
2519 thread_local! {
2520 static DESCRIPTOR_SETS: RefCell<Vec<vk::DescriptorSet>> = Default::default();
2521 }
2522
2523 if exec_descriptor_sets.is_empty() {
2524 return;
2525 }
2526
2527 DESCRIPTOR_SETS.with_borrow_mut(|descriptor_sets| {
2528 descriptor_sets.clear();
2529 descriptor_sets.extend(
2530 exec_descriptor_sets
2531 .iter()
2532 .map(|descriptor_set| **descriptor_set),
2533 );
2534
2535 trace!(" bind descriptor sets {:?}", descriptor_sets);
2536
2537 unsafe {
2538 cmd_buf.device.cmd_bind_descriptor_sets(
2539 cmd_buf.handle,
2540 pipeline.bind_point(),
2541 pipeline.layout(),
2542 0,
2543 descriptor_sets,
2544 &[],
2545 );
2546 }
2547 });
2548 }
2549 }
2550
2551 #[profiling::function]
2552 fn bind_pipeline(
2553 cmd_buf: &CommandBuffer,
2554 recorded_command: &mut CommandRecordingResources,
2555 exec_idx: usize,
2556 pipeline: &mut ExecutionPipeline,
2557 depth_stencil: Option<DepthStencilInfo>,
2558 ) -> Result<(), DriverError> {
2559 if log_enabled!(Trace) {
2560 let (pipeline_kind, name, vk_pipeline) = match pipeline {
2561 ExecutionPipeline::Compute(pipeline) => (
2562 "compute",
2563 Device::private_data_object_name(
2564 pipeline.device(),
2565 vk::ObjectType::PIPELINE,
2566 pipeline.handle(),
2567 ),
2568 pipeline.handle(),
2569 ),
2570 ExecutionPipeline::Graphics(pipeline) => (
2571 "graphics",
2572 Device::private_data_object_name(
2573 pipeline.device(),
2574 vk::ObjectType::PIPELINE_LAYOUT,
2575 pipeline.inner.layout,
2576 ),
2577 vk::Pipeline::null(),
2578 ),
2579 ExecutionPipeline::RayTracing(pipeline) => (
2580 "ray tracing",
2581 Device::private_data_object_name(
2582 pipeline.device(),
2583 vk::ObjectType::PIPELINE,
2584 pipeline.handle(),
2585 ),
2586 pipeline.handle(),
2587 ),
2588 };
2589 if let Some(name) = name {
2590 trace!(" bind {pipeline_kind} pipeline {name} ({vk_pipeline:?})");
2591 } else {
2592 trace!(" bind {pipeline_kind} pipeline {vk_pipeline:?}");
2593 }
2594 }
2595
2596 let bind_point = pipeline.bind_point();
2598 let pipeline = match pipeline {
2599 ExecutionPipeline::Compute(pipeline) => pipeline.handle(),
2600 ExecutionPipeline::Graphics(pipeline) => RenderPass::pipeline_handle(
2601 recorded_command.expect_render_pass_mut(),
2602 pipeline,
2603 depth_stencil,
2604 exec_idx as _,
2605 )?,
2606 ExecutionPipeline::RayTracing(pipeline) => pipeline.handle(),
2607 };
2608
2609 unsafe {
2610 cmd_buf
2611 .device
2612 .cmd_bind_pipeline(cmd_buf.handle, bind_point, pipeline);
2613 }
2614
2615 Ok(())
2616 }
2617
2618 fn collect_queue_ownership_release_groups(&self) -> Box<[QueueOwnershipReleaseGroup]> {
2620 let mut release_groups = Vec::<QueueOwnershipReleaseGroup>::new();
2621
2622 thread_local! {
2623 static TRANSFER_GROUP_INDICES: RefCell<HashMap<(u32, u32), usize>> = Default::default();
2624 }
2625
2626 TRANSFER_GROUP_INDICES.with_borrow_mut(|tls| {
2627 tls.clear();
2628
2629 if let Some(pending_buffer_transfer_nodes) = &self.pending_buffer_transfer_nodes {
2630 for (_, buffer, transfers) in pending_buffer_transfer_nodes.iter() {
2631 for transfer in transfers.iter().copied() {
2632 let key = (transfer.src_queue_family_index, transfer.src_queue_index);
2633 let buffer_transfer = (buffer, transfer.range);
2634
2635 if let Some(&group_idx) = tls.get(&key) {
2636 release_groups[group_idx].buffers.push(buffer_transfer);
2637 } else {
2638 let group_idx = release_groups.len();
2639
2640 release_groups.push(QueueOwnershipReleaseGroup {
2641 src_queue_family_index: key.0,
2642 src_queue_index: key.1,
2643 buffers: vec![buffer_transfer],
2644 images: Vec::new(),
2645 });
2646
2647 tls.insert(key, group_idx);
2648 }
2649 }
2650 }
2651 }
2652
2653 if let Some(pending_image_transfer_nodes) = &self.pending_image_transfer_nodes {
2654 for (_, image, transfers) in pending_image_transfer_nodes.iter() {
2655 for transfer in transfers.iter().copied() {
2656 let key = (transfer.src_queue_family_index, transfer.src_queue_index);
2657 let image_transfer = (image, transfer.layout, transfer.range);
2658
2659 if let Some(&group_idx) = tls.get(&key) {
2660 release_groups[group_idx].images.push(image_transfer);
2661 } else {
2662 let group_idx = release_groups.len();
2663
2664 release_groups.push(QueueOwnershipReleaseGroup {
2665 src_queue_family_index: key.0,
2666 src_queue_index: key.1,
2667 buffers: Vec::new(),
2668 images: vec![image_transfer],
2669 });
2670
2671 tls.insert(key, group_idx);
2672 }
2673 }
2674 }
2675 }
2676 });
2677
2678 release_groups.into_boxed_slice()
2679 }
2680
2681 pub fn is_empty(&self) -> bool {
2683 self.graph.cmds.is_empty()
2684 }
2685
2686 #[allow(clippy::type_complexity)]
2687 #[profiling::function]
2688 fn lease_descriptor_pool<P>(
2689 pool: &mut P,
2690 pass: &CommandData,
2691 ) -> Result<Option<Lease<DescriptorPool>>, DriverError>
2692 where
2693 P: SubmissionPool,
2694 {
2695 let max_set_idx = pass
2696 .execs
2697 .iter()
2698 .flat_map(|exec| exec.bindings.keys())
2699 .map(|descriptor| descriptor.set())
2700 .max()
2701 .unwrap_or_default();
2702 let max_sets = pass.execs.len() as u32 * (max_set_idx + 1);
2703 let mut info = DescriptorPoolInfo {
2704 max_sets,
2705 ..Default::default()
2706 };
2707
2708 for pool_size in pass.descriptor_pools_sizes() {
2710 for (&descriptor_ty, &descriptor_count) in pool_size {
2711 debug_assert_ne!(descriptor_count, 0);
2712
2713 match descriptor_ty {
2714 vk::DescriptorType::ACCELERATION_STRUCTURE_KHR => {
2715 info.acceleration_structure_count += descriptor_count;
2716 }
2717 vk::DescriptorType::COMBINED_IMAGE_SAMPLER => {
2718 info.combined_image_sampler_count += descriptor_count;
2719 }
2720 vk::DescriptorType::INPUT_ATTACHMENT => {
2721 info.input_attachment_count += descriptor_count;
2722 }
2723 vk::DescriptorType::SAMPLED_IMAGE => {
2724 info.sampled_image_count += descriptor_count;
2725 }
2726 vk::DescriptorType::SAMPLER => {
2727 info.sampler_count += descriptor_count;
2728 }
2729 vk::DescriptorType::STORAGE_BUFFER => {
2730 info.storage_buffer_count += descriptor_count;
2731 }
2732 vk::DescriptorType::STORAGE_BUFFER_DYNAMIC => {
2733 info.storage_buffer_dynamic_count += descriptor_count;
2734 }
2735 vk::DescriptorType::STORAGE_IMAGE => {
2736 info.storage_image_count += descriptor_count;
2737 }
2738 vk::DescriptorType::STORAGE_TEXEL_BUFFER => {
2739 info.storage_texel_buffer_count += descriptor_count;
2740 }
2741 vk::DescriptorType::UNIFORM_BUFFER => {
2742 info.uniform_buffer_count += descriptor_count;
2743 }
2744 vk::DescriptorType::UNIFORM_BUFFER_DYNAMIC => {
2745 info.uniform_buffer_dynamic_count += descriptor_count;
2746 }
2747 vk::DescriptorType::UNIFORM_TEXEL_BUFFER => {
2748 info.uniform_texel_buffer_count += descriptor_count;
2749 }
2750 _ => {
2751 warn!(
2752 "unsupported descriptor type {:?} for command {}",
2753 descriptor_ty,
2754 pass.name(),
2755 );
2756
2757 return Err(DriverError::Unsupported);
2758 }
2759 };
2760 }
2761 }
2762
2763 if info.is_empty() {
2765 return Ok(None);
2766 }
2767
2768 const ATOM: u32 = 1 << 5;
2770 info.acceleration_structure_count =
2771 info.acceleration_structure_count.next_multiple_of(ATOM);
2772 info.combined_image_sampler_count =
2773 info.combined_image_sampler_count.next_multiple_of(ATOM);
2774 info.input_attachment_count = info.input_attachment_count.next_multiple_of(ATOM);
2775 info.sampled_image_count = info.sampled_image_count.next_multiple_of(ATOM);
2776 info.sampler_count = info.sampler_count.next_multiple_of(ATOM);
2777 info.storage_buffer_count = info.storage_buffer_count.next_multiple_of(ATOM);
2778 info.storage_buffer_dynamic_count =
2779 info.storage_buffer_dynamic_count.next_multiple_of(ATOM);
2780 info.storage_image_count = info.storage_image_count.next_multiple_of(ATOM);
2781 info.storage_texel_buffer_count = info.storage_texel_buffer_count.next_multiple_of(ATOM);
2782 info.uniform_buffer_count = info.uniform_buffer_count.next_multiple_of(ATOM);
2783 info.uniform_buffer_dynamic_count =
2784 info.uniform_buffer_dynamic_count.next_multiple_of(ATOM);
2785 info.uniform_texel_buffer_count = info.uniform_texel_buffer_count.next_multiple_of(ATOM);
2786
2787 Ok(Some(pool.descriptor_pool(info)?))
2792 }
2793
2794 #[profiling::function]
2795 fn lease_render_pass<P>(
2796 &self,
2797 pool: &mut P,
2798 pass_idx: usize,
2799 external_access_history: &ExternalRenderPassAccessHistory,
2800 ) -> Result<Lease<RenderPass>, DriverError>
2801 where
2802 P: SubmissionPool,
2803 {
2804 let pass = &self.graph.cmds[pass_idx];
2805 let (mut color_attachment_count, mut depth_stencil_attachment_count) = (0, 0);
2806 for exec in &pass.execs {
2807 color_attachment_count = color_attachment_count.max(exec.attachments.color.len());
2808
2809 let depth_stencil = exec.attachments.depth_stencil_attachment();
2810 let has_depth_stencil_attachment =
2811 depth_stencil.is_some_and(|state| state.is_attachment);
2812 let has_depth_stencil_resolve = depth_stencil.and_then(|state| state.resolve).is_some();
2813
2814 depth_stencil_attachment_count = depth_stencil_attachment_count
2815 .max(has_depth_stencil_attachment as usize + has_depth_stencil_resolve as usize);
2816 }
2817
2818 let attachment_count = color_attachment_count + depth_stencil_attachment_count;
2819 let mut attachments = Vec::with_capacity(attachment_count);
2820 attachments.resize_with(attachment_count, AttachmentInfo::default);
2821
2822 let mut subpasses = Vec::<SubpassInfo>::with_capacity(pass.execs.len());
2823
2824 {
2825 let mut color_set = FixedBitSet::with_capacity(attachment_count);
2826 color_set.grow(attachment_count);
2827 let mut depth_stencil_set = false;
2828
2829 for exec in &pass.execs {
2831 for (attachment_idx, state) in exec.attachments.color_attachments() {
2832 let attachment_idx = attachment_idx as usize;
2833 if color_set.put(attachment_idx) {
2834 continue;
2835 }
2836
2837 let attachment = &mut attachments[attachment_idx];
2838 attachment.format = state.attachment.format;
2839 attachment.sample_count = state.attachment.sample_count;
2840 attachment.initial_layout = vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL;
2841 attachment.load_op = match state.load {
2842 LoadOp::DontCare => vk::AttachmentLoadOp::DONT_CARE,
2843 LoadOp::Load => vk::AttachmentLoadOp::LOAD,
2844 LoadOp::Clear(_) => vk::AttachmentLoadOp::CLEAR,
2845 };
2846 }
2847
2848 if !depth_stencil_set {
2849 if let Some(state) = exec
2850 .attachments
2851 .depth_stencil_attachment()
2852 .filter(|state| state.is_attachment)
2853 {
2854 let attachment = &mut attachments[color_attachment_count];
2855 attachment.format = state.attachment.format;
2856 attachment.sample_count = state.attachment.sample_count;
2857 let is_load = matches!(state.load, LoadOp::Load);
2858 attachment.initial_layout =
2859 if state.attachment.aspect_mask.contains(
2860 vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL,
2861 ) {
2862 attachment.load_op = match state.load {
2863 LoadOp::DontCare => vk::AttachmentLoadOp::DONT_CARE,
2864 LoadOp::Load => vk::AttachmentLoadOp::LOAD,
2865 LoadOp::Clear(_) => vk::AttachmentLoadOp::CLEAR,
2866 };
2867 attachment.stencil_load_op = match state.load {
2868 LoadOp::DontCare => vk::AttachmentLoadOp::DONT_CARE,
2869 LoadOp::Load => vk::AttachmentLoadOp::LOAD,
2870 LoadOp::Clear(_) => vk::AttachmentLoadOp::CLEAR,
2871 };
2872
2873 if is_load {
2874 vk::ImageLayout::DEPTH_STENCIL_READ_ONLY_OPTIMAL
2875 } else {
2876 vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL
2877 }
2878 } else if state
2879 .attachment
2880 .aspect_mask
2881 .contains(vk::ImageAspectFlags::DEPTH)
2882 {
2883 attachment.load_op = match state.load {
2884 LoadOp::DontCare => vk::AttachmentLoadOp::DONT_CARE,
2885 LoadOp::Load => vk::AttachmentLoadOp::LOAD,
2886 LoadOp::Clear(_) => vk::AttachmentLoadOp::CLEAR,
2887 };
2888
2889 if is_load {
2890 vk::ImageLayout::DEPTH_READ_ONLY_OPTIMAL
2891 } else {
2892 vk::ImageLayout::DEPTH_ATTACHMENT_OPTIMAL
2893 }
2894 } else {
2895 attachment.stencil_load_op = match state.load {
2896 LoadOp::DontCare => vk::AttachmentLoadOp::DONT_CARE,
2897 LoadOp::Load => vk::AttachmentLoadOp::LOAD,
2898 LoadOp::Clear(_) => vk::AttachmentLoadOp::CLEAR,
2899 };
2900
2901 if is_load {
2902 vk::ImageLayout::STENCIL_READ_ONLY_OPTIMAL
2903 } else {
2904 vk::ImageLayout::STENCIL_ATTACHMENT_OPTIMAL
2905 }
2906 };
2907 depth_stencil_set = true;
2908 } else if exec.attachments.depth_stencil_attachment().is_some() {
2909 depth_stencil_set = true;
2910 }
2911 }
2912 }
2913 }
2914
2915 {
2916 let mut color_set = FixedBitSet::with_capacity(attachment_count);
2917 color_set.grow(attachment_count);
2918 let mut depth_stencil_set = false;
2919 let mut depth_stencil_resolve_set = false;
2920
2921 for exec in pass.execs.iter().rev() {
2923 for (attachment_idx, state) in exec.attachments.color_attachments() {
2924 let attachment_idx = attachment_idx as usize;
2925 if color_set.put(attachment_idx) {
2926 continue;
2927 }
2928
2929 let attachment = &mut attachments[attachment_idx];
2930 attachment.format = state.attachment.format;
2931 attachment.sample_count = state.attachment.sample_count;
2932 attachment.store_op = if state.store == StoreOp::Store {
2933 vk::AttachmentStoreOp::STORE
2934 } else {
2935 vk::AttachmentStoreOp::DONT_CARE
2936 };
2937 attachment.final_layout = vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL;
2938 }
2939
2940 if !depth_stencil_set
2941 && let Some(state) = exec
2942 .attachments
2943 .depth_stencil_attachment()
2944 .filter(|state| state.is_attachment)
2945 {
2946 let attachment = &mut attachments[color_attachment_count];
2947 attachment.format = state.attachment.format;
2948 attachment.sample_count = state.attachment.sample_count;
2949 attachment.final_layout = if state
2950 .attachment
2951 .aspect_mask
2952 .contains(vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL)
2953 {
2954 attachment.store_op = if state.store == StoreOp::Store {
2955 vk::AttachmentStoreOp::STORE
2956 } else {
2957 vk::AttachmentStoreOp::DONT_CARE
2958 };
2959 attachment.stencil_store_op = if state.store == StoreOp::Store {
2960 vk::AttachmentStoreOp::STORE
2961 } else {
2962 vk::AttachmentStoreOp::DONT_CARE
2963 };
2964
2965 vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL
2966 } else if state
2967 .attachment
2968 .aspect_mask
2969 .contains(vk::ImageAspectFlags::DEPTH)
2970 {
2971 attachment.store_op = if state.store == StoreOp::Store {
2972 vk::AttachmentStoreOp::STORE
2973 } else {
2974 vk::AttachmentStoreOp::DONT_CARE
2975 };
2976
2977 vk::ImageLayout::DEPTH_ATTACHMENT_OPTIMAL
2978 } else {
2979 attachment.stencil_store_op = if state.store == StoreOp::Store {
2980 vk::AttachmentStoreOp::STORE
2981 } else {
2982 vk::AttachmentStoreOp::DONT_CARE
2983 };
2984
2985 vk::ImageLayout::STENCIL_ATTACHMENT_OPTIMAL
2986 };
2987 depth_stencil_set = true;
2988 }
2989
2990 if !depth_stencil_resolve_set
2991 && let Some(state) = exec
2992 .attachments
2993 .depth_stencil_attachment()
2994 .and_then(|state| state.resolve)
2995 {
2996 let attachment = attachments
2997 .last_mut()
2998 .expect("missing depth stencil resolve attachment");
2999 attachment.format = state.attachment.format;
3000 attachment.sample_count = state.attachment.sample_count;
3001 attachment.final_layout = if state
3002 .attachment
3003 .aspect_mask
3004 .contains(vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL)
3005 {
3006 vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL
3007 } else if state
3008 .attachment
3009 .aspect_mask
3010 .contains(vk::ImageAspectFlags::DEPTH)
3011 {
3012 vk::ImageLayout::DEPTH_ATTACHMENT_OPTIMAL
3013 } else {
3014 vk::ImageLayout::STENCIL_ATTACHMENT_OPTIMAL
3015 };
3016 depth_stencil_resolve_set = true;
3017 }
3018 }
3019 }
3020
3021 for attachment in &mut attachments {
3022 if attachment.load_op == vk::AttachmentLoadOp::DONT_CARE {
3023 attachment.initial_layout = vk::ImageLayout::UNDEFINED;
3024 } else if attachment.store_op == vk::AttachmentStoreOp::DONT_CARE
3025 && attachment.stencil_store_op == vk::AttachmentStoreOp::DONT_CARE
3026 {
3027 attachment.final_layout = attachment.initial_layout;
3028 }
3029 }
3030
3031 for (exec_idx, exec) in pass.execs.iter().enumerate() {
3033 let pipeline = exec
3034 .pipeline
3035 .as_ref()
3036 .expect("missing graphics pipeline")
3037 .expect_graphics();
3038 let mut subpass_info = SubpassInfo::with_capacity(attachment_count);
3039
3040 for attachment_idx in pipeline.inner.input_attachments.iter() {
3042 let exec_attachment = exec
3043 .attachments
3044 .color_attachment(*attachment_idx)
3045 .expect("missing input attachment");
3046 debug_assert!(
3047 !matches!(exec_attachment.load, LoadOp::Clear(_)),
3048 "cannot clear color attachment {attachment_idx} because it uses subpass input",
3049 );
3050
3051 let is_random_access = exec_attachment.store == StoreOp::Store;
3052 subpass_info.input_attachments.push(AttachmentRef {
3053 attachment: *attachment_idx,
3054 aspect_mask: exec_attachment.attachment.aspect_mask,
3055 layout: Self::attachment_layout(
3056 exec_attachment.attachment.aspect_mask,
3057 is_random_access,
3058 true,
3059 ),
3060 });
3061
3062 for prev_exec_idx in (0..exec_idx).rev() {
3065 let prev_exec = &pass.execs[prev_exec_idx];
3066 if prev_exec
3067 .attachments
3068 .color_attachment(*attachment_idx)
3069 .is_some_and(|state| state.store == StoreOp::Store)
3070 {
3071 break;
3072 }
3073
3074 let prev_subpass = &mut subpasses[prev_exec_idx];
3075 prev_subpass.preserve_attachments.push(*attachment_idx);
3076 }
3077 }
3078
3079 for attachment_idx in 0..color_attachment_count as u32 {
3081 let is_input = subpass_info
3082 .input_attachments
3083 .iter()
3084 .any(|input| input.attachment == attachment_idx);
3085 subpass_info.color_attachments.push(AttachmentRef {
3086 attachment: vk::ATTACHMENT_UNUSED,
3087 aspect_mask: vk::ImageAspectFlags::COLOR,
3088 layout: Self::attachment_layout(vk::ImageAspectFlags::COLOR, true, is_input),
3089 });
3090 }
3091
3092 for (attachment_idx, state) in exec.attachments.color_attachments() {
3093 if state.is_attachment {
3094 subpass_info.color_attachments[attachment_idx as usize].attachment =
3095 attachment_idx;
3096 }
3097 }
3098
3099 if let Some(state) = exec
3101 .attachments
3102 .depth_stencil_attachment()
3103 .filter(|state| state.is_attachment)
3104 {
3105 let is_random_access = matches!(state.load, LoadOp::Clear(_))
3106 || matches!(state.load, LoadOp::Load)
3107 || state.store == StoreOp::Store;
3108 subpass_info.depth_stencil_attachment = Some(AttachmentRef {
3109 attachment: color_attachment_count as u32,
3110 aspect_mask: state.attachment.aspect_mask,
3111 layout: Self::attachment_layout(
3112 state.attachment.aspect_mask,
3113 is_random_access,
3114 false,
3115 ),
3116 });
3117 }
3118
3119 subpass_info.color_resolve_attachments.extend(repeat_n(
3121 AttachmentRef {
3122 attachment: vk::ATTACHMENT_UNUSED,
3123 aspect_mask: vk::ImageAspectFlags::empty(),
3124 layout: vk::ImageLayout::UNDEFINED,
3125 },
3126 color_attachment_count,
3127 ));
3128
3129 for (dst_attachment_idx, state) in exec.attachments.color_attachments() {
3131 let Some(state) = state.resolve else {
3132 continue;
3133 };
3134
3135 let is_input = subpass_info
3136 .input_attachments
3137 .iter()
3138 .any(|input| input.attachment == dst_attachment_idx);
3139 subpass_info.color_resolve_attachments[state.src_attachment_idx as usize] =
3140 AttachmentRef {
3141 attachment: dst_attachment_idx,
3142 aspect_mask: state.attachment.aspect_mask,
3143 layout: Self::attachment_layout(
3144 state.attachment.aspect_mask,
3145 true,
3146 is_input,
3147 ),
3148 };
3149 }
3150
3151 if let Some(state) = exec
3152 .attachments
3153 .depth_stencil_attachment()
3154 .and_then(|state| state.resolve)
3155 {
3156 subpass_info.depth_stencil_resolve_attachment = Some((
3157 AttachmentRef {
3158 attachment: state.dst_attachment_idx + 1,
3159 aspect_mask: state.attachment.aspect_mask,
3160 layout: Self::attachment_layout(state.attachment.aspect_mask, true, false),
3161 },
3162 state.depth_mode,
3163 state.stencil_mode,
3164 ))
3165 }
3166
3167 subpass_info.view_mask = exec.view_mask;
3168 subpass_info.correlated_view_mask = exec.correlated_view_mask;
3169
3170 subpasses.push(subpass_info);
3171 }
3172
3173 let dependencies = Self::build_subpass_dependencies(pass, external_access_history);
3174
3175 pool.render_pass(RenderPassInfo {
3184 attachments,
3185 dependencies,
3186 subpasses,
3187 })
3188 }
3189
3190 fn build_subpass_dependencies(
3191 pass: &CommandData,
3192 external_access_history: &ExternalRenderPassAccessHistory,
3193 ) -> Vec<SubpassDependency> {
3194 let mut dependencies = BTreeMap::new();
3195 let mut pass_access_history =
3196 HashMap::<NodeIndex, Vec<(usize, PipelineStageAccessFlags)>>::new();
3197
3198 for (exec_idx, exec) in pass.execs.iter().enumerate() {
3199 'exec_accesses: for (node_idx, accesses) in exec.accesses.iter() {
3200 for access in accesses {
3201 let mut current = PipelineStageAccessFlags::new(access.access);
3202 current.stage_flags = Self::subpass_stage_mask(current.stage_flags);
3203
3204 if let Some(prev_accesses) = pass_access_history.get(&node_idx) {
3205 for &(prev_exec_idx, previous) in prev_accesses.iter().rev() {
3206 if Self::record_subpass_dependency(
3207 &mut dependencies,
3208 prev_exec_idx,
3209 exec_idx,
3210 previous,
3211 current.stage_flags,
3212 &mut current,
3213 ) {
3214 continue 'exec_accesses;
3215 }
3216 }
3217 }
3218
3219 for &previous in external_access_history.accesses(node_idx).iter().rev() {
3220 if Self::record_subpass_dependency(
3221 &mut dependencies,
3222 vk::SUBPASS_EXTERNAL as usize,
3223 exec_idx,
3224 previous,
3225 current.stage_flags,
3226 &mut current,
3227 ) {
3228 continue 'exec_accesses;
3229 }
3230 }
3231
3232 if !current.stage_flags.is_empty() {
3233 let dep = dependencies
3234 .entry((vk::SUBPASS_EXTERNAL as usize, exec_idx))
3235 .or_insert_with(|| {
3236 SubpassDependency::new(vk::SUBPASS_EXTERNAL, exec_idx as _)
3237 });
3238
3239 dep.src_stage_mask |= vk::PipelineStageFlags::ALL_COMMANDS;
3240 dep.src_access_mask |=
3241 vk::AccessFlags::MEMORY_READ | vk::AccessFlags::MEMORY_WRITE;
3242 dep.dst_stage_mask |= current.stage_flags;
3243 dep.dst_access_mask |= current.access_flags;
3244 }
3245 }
3246 }
3247
3248 for (node_idx, accesses) in exec.accesses.iter() {
3249 let prev_accesses = pass_access_history.entry(node_idx).or_default();
3250 prev_accesses.extend(accesses.iter().map(|access| {
3251 let mut access_info = PipelineStageAccessFlags::new(access.access);
3252 access_info.stage_flags = Self::subpass_stage_mask(access_info.stage_flags);
3253
3254 (exec_idx, access_info)
3255 }));
3256 }
3257
3258 for (other_idx, other) in pass.execs[0..exec_idx].iter().enumerate() {
3261 for (attachment_idx, state) in
3263 exec.attachments.color_attachments().filter(|(_, state)| {
3264 state.is_input || Self::color_attachment_is_read(state.load)
3265 })
3266 {
3267 if let Some(other_state) = other.attachments.color_attachment(attachment_idx)
3269 && Self::color_attachment_is_write(
3270 other_state.load,
3271 other_state.store,
3272 other_state.resolve.is_some(),
3273 )
3274 {
3275 let dep = dependencies
3276 .entry((other_idx, exec_idx))
3277 .or_insert_with(|| {
3278 SubpassDependency::new(other_idx as _, exec_idx as _)
3279 });
3280
3281 dep.src_stage_mask |= vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT;
3282 dep.src_access_mask |= vk::AccessFlags::COLOR_ATTACHMENT_WRITE;
3283
3284 if state.is_input {
3285 dep.dst_stage_mask |= vk::PipelineStageFlags::FRAGMENT_SHADER;
3286 dep.dst_access_mask |= vk::AccessFlags::INPUT_ATTACHMENT_READ;
3287 } else {
3288 dep.dst_stage_mask |=
3289 Self::attachment_read_stage(state.attachment.aspect_mask);
3290 dep.dst_access_mask |= vk::AccessFlags::COLOR_ATTACHMENT_READ;
3291 }
3292 }
3293
3294 if let Some(other_state) = other.attachments.color_attachment(attachment_idx)
3295 && (other_state.is_input
3296 || Self::color_attachment_is_read(other_state.load))
3297 {
3298 let dep = dependencies
3299 .entry((other_idx, exec_idx))
3300 .or_insert_with(|| {
3301 SubpassDependency::new(other_idx as _, exec_idx as _)
3302 });
3303
3304 if other_state.is_input {
3305 dep.src_stage_mask |= vk::PipelineStageFlags::FRAGMENT_SHADER;
3306 dep.src_access_mask |= vk::AccessFlags::INPUT_ATTACHMENT_READ;
3307 } else {
3308 dep.src_stage_mask |=
3309 Self::attachment_read_stage(state.attachment.aspect_mask);
3310 dep.src_access_mask |= vk::AccessFlags::COLOR_ATTACHMENT_READ;
3311 }
3312
3313 if state.is_input {
3314 dep.dst_stage_mask |= vk::PipelineStageFlags::FRAGMENT_SHADER;
3315 dep.dst_access_mask |= vk::AccessFlags::INPUT_ATTACHMENT_READ;
3316 } else {
3317 dep.dst_stage_mask |=
3318 Self::attachment_read_stage(state.attachment.aspect_mask);
3319 dep.dst_access_mask |= vk::AccessFlags::COLOR_ATTACHMENT_READ;
3320 }
3321 }
3322 }
3323
3324 if let Some(state) = exec.attachments.depth_stencil_attachment().filter(|state| {
3325 state.is_attachment && Self::depth_stencil_attachment_is_read(state.load)
3326 }) {
3327 let aspect_mask = state.attachment.aspect_mask;
3328
3329 if other
3330 .attachments
3331 .depth_stencil_attachment()
3332 .is_some_and(|state| {
3333 Self::depth_stencil_attachment_is_write(
3334 state.load,
3335 state.store,
3336 state.resolve.is_some(),
3337 )
3338 })
3339 {
3340 let dep = dependencies
3341 .entry((other_idx, exec_idx))
3342 .or_insert_with(|| {
3343 SubpassDependency::new(other_idx as _, exec_idx as _)
3344 });
3345
3346 dep.src_stage_mask |= vk::PipelineStageFlags::LATE_FRAGMENT_TESTS;
3347 dep.src_access_mask |= vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE;
3348 dep.dst_stage_mask |= vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS;
3349 dep.dst_access_mask |= vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ;
3350 }
3351
3352 if other
3353 .attachments
3354 .depth_stencil_attachment()
3355 .is_some_and(|state| Self::depth_stencil_attachment_is_read(state.load))
3356 {
3357 let dep = dependencies
3358 .entry((other_idx, exec_idx))
3359 .or_insert_with(|| {
3360 SubpassDependency::new(other_idx as _, exec_idx as _)
3361 });
3362
3363 dep.src_stage_mask |= vk::PipelineStageFlags::LATE_FRAGMENT_TESTS;
3364 dep.src_access_mask |= vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ;
3365 dep.dst_stage_mask |= Self::attachment_read_stage(aspect_mask);
3366 dep.dst_access_mask |= vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ;
3367 }
3368 }
3369
3370 for (attachment_idx, state) in
3371 exec.attachments.color_attachments().filter(|(_, state)| {
3372 Self::color_attachment_is_write(
3373 state.load,
3374 state.store,
3375 state.resolve.is_some(),
3376 )
3377 })
3378 {
3379 let aspect_mask = state.attachment.aspect_mask;
3380 let stage = Self::attachment_stage(aspect_mask);
3381
3382 if other
3383 .attachments
3384 .color_attachment(attachment_idx)
3385 .is_some_and(|state| {
3386 Self::color_attachment_is_write(
3387 state.load,
3388 state.store,
3389 state.resolve.is_some(),
3390 )
3391 })
3392 {
3393 let access = Self::attachment_write_access(aspect_mask);
3394 let dep = dependencies
3395 .entry((other_idx, exec_idx))
3396 .or_insert_with(|| {
3397 SubpassDependency::new(other_idx as _, exec_idx as _)
3398 });
3399
3400 dep.src_stage_mask |= stage;
3401 dep.src_access_mask |= access;
3402 dep.dst_stage_mask |= stage;
3403 dep.dst_access_mask |= access;
3404 }
3405
3406 if let Some(other_state) = other.attachments.color_attachment(attachment_idx)
3407 && (other_state.is_input
3408 || Self::color_attachment_is_read(other_state.load))
3409 {
3410 let (src_access, dst_access) =
3411 Self::attachment_read_write_access(aspect_mask);
3412 let dep = dependencies
3413 .entry((other_idx, exec_idx))
3414 .or_insert_with(|| {
3415 SubpassDependency::new(other_idx as _, exec_idx as _)
3416 });
3417
3418 if other_state.is_input {
3419 dep.src_stage_mask |= vk::PipelineStageFlags::FRAGMENT_SHADER;
3420 dep.src_access_mask |= vk::AccessFlags::INPUT_ATTACHMENT_READ;
3421 } else {
3422 dep.src_stage_mask |= Self::attachment_read_stage(aspect_mask);
3423 dep.src_access_mask |= src_access;
3424 }
3425 dep.dst_stage_mask |= stage;
3426 dep.dst_access_mask |= dst_access;
3427 }
3428 }
3429
3430 if let Some(state) = exec.attachments.depth_stencil_attachment().filter(|state| {
3431 Self::depth_stencil_attachment_is_write(
3432 state.load,
3433 state.store,
3434 state.resolve.is_some(),
3435 )
3436 }) {
3437 let aspect_mask = state.attachment.aspect_mask;
3438 let stage = Self::attachment_stage(aspect_mask);
3439
3440 if other
3441 .attachments
3442 .depth_stencil_attachment()
3443 .is_some_and(|state| {
3444 Self::depth_stencil_attachment_is_write(
3445 state.load,
3446 state.store,
3447 state.resolve.is_some(),
3448 )
3449 })
3450 {
3451 let access = Self::attachment_write_access(aspect_mask);
3452 let dep = dependencies
3453 .entry((other_idx, exec_idx))
3454 .or_insert_with(|| {
3455 SubpassDependency::new(other_idx as _, exec_idx as _)
3456 });
3457
3458 dep.src_stage_mask |= stage;
3459 dep.src_access_mask |= access;
3460 dep.dst_stage_mask |= stage;
3461 dep.dst_access_mask |= access;
3462 }
3463
3464 if other
3465 .attachments
3466 .depth_stencil_attachment()
3467 .is_some_and(|state| Self::depth_stencil_attachment_is_read(state.load))
3468 {
3469 let (src_access, dst_access) =
3470 Self::attachment_read_write_access(aspect_mask);
3471 let dep = dependencies
3472 .entry((other_idx, exec_idx))
3473 .or_insert_with(|| {
3474 SubpassDependency::new(other_idx as _, exec_idx as _)
3475 });
3476
3477 dep.src_stage_mask |= Self::attachment_read_stage(aspect_mask);
3478 dep.src_access_mask |= src_access;
3479 dep.dst_stage_mask |= stage;
3480 dep.dst_access_mask |= dst_access;
3481 }
3482 }
3483 }
3484 }
3485
3486 dependencies.into_values().collect()
3487 }
3488
3489 #[profiling::function]
3490 fn lease_scheduled_resources<P>(
3491 &mut self,
3492 pool: &mut P,
3493 schedule: &[usize],
3494 ) -> Result<(), DriverError>
3495 where
3496 P: SubmissionPool,
3497 {
3498 let mut render_pass_access_history =
3499 ExternalRenderPassAccessHistory::new(self.graph.resources.len());
3500
3501 for pass_idx in schedule.iter().copied() {
3502 let pass = &self.graph.cmds[pass_idx];
3506
3507 trace!("requesting [{pass_idx}: {}]", pass.name());
3508
3509 let descriptor_pool = Self::lease_descriptor_pool(pool, pass)?;
3510 let mut descriptor_sets = Vec::with_capacity(pass.execs.len());
3511 descriptor_sets.resize_with(pass.execs.len(), Vec::new);
3512 if let Some(descriptor_pool) = descriptor_pool.as_ref() {
3513 for (exec_idx, exec) in pass.execs.iter().enumerate() {
3514 let Some(pipeline) = exec.pipeline.as_ref() else {
3515 continue;
3516 };
3517
3518 let layouts = pipeline.descriptor_info().layouts.values();
3519 descriptor_sets[exec_idx] = layouts
3520 .into_iter()
3521 .map(|descriptor_set_layout| {
3522 DescriptorPool::allocate_descriptor_set(
3523 descriptor_pool,
3524 descriptor_set_layout,
3525 )
3526 })
3527 .collect::<Result<_, _>>()?;
3528 }
3529 }
3530
3531 debug_assert!(!pass.execs.is_empty());
3537 debug_assert!(
3538 pass.expect_first_exec().pipeline.is_none()
3539 || !pass
3540 .expect_first_exec()
3541 .pipeline
3542 .as_ref()
3543 .is_some_and(|pipeline| pipeline.is_graphics())
3544 || pass
3545 .expect_first_exec()
3546 .pipeline
3547 .as_ref()
3548 .expect("missing graphics pipeline")
3549 .expect_graphics()
3550 .inner
3551 .descriptor_info
3552 .pool_sizes
3553 .values()
3554 .filter_map(|pool| pool.get(&vk::DescriptorType::INPUT_ATTACHMENT))
3555 .next()
3556 .is_none()
3557 );
3558
3559 let render_pass = if pass
3561 .expect_first_exec()
3562 .pipeline
3563 .as_ref()
3564 .map(|pipeline| pipeline.is_graphics())
3565 .unwrap_or_default()
3566 {
3567 Some(self.lease_render_pass(pool, pass_idx, &render_pass_access_history)?)
3568 } else {
3569 None
3570 };
3571
3572 render_pass_access_history.record_cmd(pass);
3573
3574 self.recorded_commands.push(CommandRecordingResources {
3575 descriptor_pool,
3576 descriptor_sets,
3577 render_pass,
3578 });
3579 }
3580
3581 Ok(())
3582 }
3583
3584 #[profiling::function]
3587 fn merge_scheduled_cmds(&mut self, schedule: &mut Vec<usize>) {
3588 thread_local! {
3589 static CMD_SLOTS: RefCell<Vec<Option<CommandData>>> = Default::default();
3590 }
3591
3592 CMD_SLOTS.with_borrow_mut(|cmds| {
3593 debug_assert!(cmds.is_empty());
3594
3595 cmds.extend(self.graph.cmds.drain(..).map(Some));
3596
3597 let mut schedule_idx = 0;
3598
3599 while schedule_idx < schedule.len() {
3602 let mut cmd = cmds[schedule[schedule_idx]]
3603 .take()
3604 .expect("missing scheduled cmd");
3605
3606 let merge_start = schedule_idx + 1;
3608 let mut merge_end = merge_start;
3609 while merge_end < schedule.len() {
3610 let other = cmds[schedule[merge_end]]
3611 .as_ref()
3612 .expect("missing scheduled cmd");
3613
3614 debug!(
3615 "attempting to merge [{schedule_idx}: {}] with [{merge_end}: {}]",
3616 cmd.name(),
3617 other.name()
3618 );
3619
3620 if Self::allow_merge_passes(&cmd, other) {
3621 merge_end += 1;
3622 } else {
3623 break;
3624 }
3625 }
3626
3627 if log_enabled!(Trace) && merge_start != merge_end {
3628 trace!(
3629 "merging {} passes into [{schedule_idx}: {}]",
3630 merge_end - merge_start,
3631 cmd.name()
3632 );
3633 }
3634
3635 let mut name = cmd.name().to_owned();
3636
3637 {
3639 let mut additional_name_len = 0;
3640 let mut additional_exec_count = 0;
3641 for merge_idx in merge_start..merge_end {
3642 let other = cmds[schedule[merge_idx]]
3643 .as_ref()
3644 .expect("missing scheduled cmd");
3645 additional_name_len += other.name().len() + 3;
3646 additional_exec_count += other.execs.len();
3647 }
3648
3649 name.reserve(additional_name_len);
3650 cmd.execs.reserve(additional_exec_count);
3651 }
3652
3653 for merge_idx in merge_start..merge_end {
3654 let mut other = cmds[schedule[merge_idx]]
3655 .take()
3656 .expect("missing scheduled cmd");
3657 name.push_str(" + ");
3658 name.push_str(other.name());
3659 cmd.execs.append(&mut other.execs);
3660 }
3661
3662 #[cfg(debug_assertions)]
3663 {
3664 cmd.name = Some(name);
3665 }
3666
3667 self.graph.cmds.push(cmd);
3668 schedule_idx += 1 + merge_end - merge_start;
3669 }
3670
3671 schedule.truncate(self.graph.cmds.len());
3673
3674 for (idx, cmd_idx) in schedule.iter_mut().enumerate() {
3675 *cmd_idx = idx;
3676 }
3677
3678 for cmd in cmds.drain(..).flatten() {
3680 self.graph.cmds.push(cmd);
3681 }
3682 });
3683 }
3684
3685 fn next_subpass(cmd: &CommandBuffer) {
3686 trace!("next_subpass");
3687
3688 unsafe {
3689 cmd.device
3690 .cmd_next_subpass(cmd.handle, vk::SubpassContents::INLINE);
3691 }
3692 }
3693
3694 fn record_node<P>(
3695 &mut self,
3696 resource_pool: &mut P,
3697 cmd_buf: &CommandBuffer,
3698 node: AnyNode,
3699 ) -> Result<(), DriverError>
3700 where
3701 P: SubmissionPool,
3702 {
3703 match node {
3704 AnyNode::AccelerationStructure(node) => {
3705 self.record_resource_impl(resource_pool, cmd_buf, node)
3706 }
3707 AnyNode::Buffer(node) => self.record_resource_impl(resource_pool, cmd_buf, node),
3708 AnyNode::Image(node) => self.record_resource_impl(resource_pool, cmd_buf, node),
3709 }
3710 }
3711
3712 #[profiling::function]
3713 fn record_selection_impl<'a, P>(
3714 &mut self,
3715 resource_pool: &mut P,
3716 cmd_buf: &CommandBuffer,
3717 selection: RecordSelection<'a>,
3718 ) -> Result<(), DriverError>
3719 where
3720 P: SubmissionPool,
3721 {
3722 let _ = CommandBufferDebugLabel::begin(cmd_buf, "graph submission");
3723
3724 match selection {
3725 RecordSelection::All => self.record_impl(resource_pool, cmd_buf),
3726 RecordSelection::Dependencies(node) => match node {
3727 AnyNode::AccelerationStructure(node) => {
3728 self.record_resource_dependencies_impl(resource_pool, cmd_buf, node)
3729 }
3730 AnyNode::Buffer(node) => {
3731 self.record_resource_dependencies_impl(resource_pool, cmd_buf, node)
3732 }
3733 AnyNode::Image(node) => {
3734 self.record_resource_dependencies_impl(resource_pool, cmd_buf, node)
3735 }
3736 },
3737 RecordSelection::Node(node) => self.record_node(resource_pool, cmd_buf, node),
3738 RecordSelection::Nodes(nodes) => {
3739 for &node in nodes {
3740 self.record_node(resource_pool, cmd_buf, node)?;
3741 }
3742
3743 Ok(())
3744 }
3745 }
3746 }
3747
3748 #[profiling::function]
3749 fn record_execution_barriers<'a>(
3750 cmd_buf: &CommandBuffer,
3751 resources: &mut [AnyResource],
3752 accesses: &'a ExecutionAccess,
3753 pending_buffer_transfer_nodes: &mut Option<
3754 PendingTransferNodes<vk::Buffer, BufferQueueOwnershipTransfer>,
3755 >,
3756 pending_image_transfer_nodes: &mut Option<
3757 PendingTransferNodes<vk::Image, ImageQueueOwnershipTransfer>,
3758 >,
3759 ) {
3760 thread_local! {
3762 static BARRIER: RefCell<BarrierScratch> = Default::default();
3763 }
3764
3765 struct AccessBarrier<T> {
3766 next_access: AccessType,
3767 prev_access: AccessType,
3768 resource: T,
3769 }
3770
3771 struct BufferBarrierTarget {
3772 buffer: vk::Buffer,
3773 range: BufferSubresourceRange,
3774 }
3775
3776 struct ImageBarrierTarget {
3777 image: vk::Image,
3778 range: vk::ImageSubresourceRange,
3779 }
3780
3781 #[derive(Default)]
3782 struct BarrierScratch {
3783 accel_struct_accesses: Vec<AccessType>,
3784 buffers: Vec<AccessBarrier<BufferBarrierTarget>>,
3785 images: Vec<AccessBarrier<ImageBarrierTarget>>,
3786 next_accesses: Vec<AccessType>,
3787 pending_buffers: NodeIndexedScratch<AccessBarrier<BufferBarrierTarget>>,
3788 pending_images: NodeIndexedScratch<AccessBarrier<ImageBarrierTarget>>,
3789 prev_accesses: Vec<AccessType>,
3790 }
3791
3792 BARRIER.with_borrow_mut(|tls| {
3793 tls.accel_struct_accesses.clear();
3795 tls.buffers.clear();
3796 tls.images.clear();
3797 tls.next_accesses.clear();
3798 tls.pending_buffers.clear();
3799 tls.pending_images.clear();
3800 tls.prev_accesses.clear();
3801
3802 for (node_idx, node_accesses) in accesses.iter() {
3806 enum ResourceRef<'a> {
3807 AccelerationStructure(&'a AccelerationStructure),
3808 Buffer(&'a Buffer),
3809 Image(&'a Image),
3810 }
3811
3812 let resource = match &resources[node_idx] {
3813 AnyResource::AccelerationStructure(resource) => {
3814 ResourceRef::AccelerationStructure(resource)
3815 }
3816 AnyResource::AccelerationStructureArg(_) => {
3817 panic!("unbound command stream acceleration structure argument")
3818 }
3819 AnyResource::AccelerationStructureLease(resource) => {
3820 ResourceRef::AccelerationStructure(resource)
3821 }
3822 AnyResource::Buffer(resource) => ResourceRef::Buffer(resource),
3823 AnyResource::BufferArg(_) => panic!("unbound command stream buffer argument"),
3824 AnyResource::BufferLease(resource) => ResourceRef::Buffer(resource),
3825 AnyResource::Image(resource) => ResourceRef::Image(resource),
3826 AnyResource::ImageArg(_) => panic!("unbound command stream image argument"),
3827 AnyResource::ImageLease(resource) => ResourceRef::Image(resource),
3828 AnyResource::SwapchainImage(resource) => ResourceRef::Image(resource),
3829 };
3830
3831 match resource {
3832 ResourceRef::AccelerationStructure(accel_struct) => {
3833 let canonical_accesses = Self::accel_struct_canonical_accesses(
3834 node_accesses,
3835 &mut tls.accel_struct_accesses,
3836 );
3837 tls.next_accesses.extend(canonical_accesses.iter().copied());
3838 tls.prev_accesses
3839 .extend(AccelerationStructure::swap_accesses(
3840 accel_struct,
3841 canonical_accesses,
3842 ));
3843 }
3844 ResourceRef::Buffer(buffer) => {
3845 for (next_access, prev_access, range) in Buffer::swap_accesses(
3846 buffer,
3847 node_accesses.iter().map(
3848 |&SubresourceAccess {
3849 access,
3850 subresource,
3851 }| {
3852 let SubresourceRange::Buffer(range) = subresource else {
3853 unreachable!()
3854 };
3855
3856 (access, range)
3857 },
3858 ),
3859 ) {
3860 let barrier = AccessBarrier {
3861 next_access,
3862 prev_access,
3863 resource: BufferBarrierTarget {
3864 buffer: buffer.handle,
3865 range,
3866 },
3867 };
3868
3869 if pending_buffer_transfer_nodes
3870 .as_ref()
3871 .is_some_and(|pending| pending.contains(node_idx))
3872 {
3873 tls.pending_buffers.push(node_idx, barrier);
3874 } else {
3875 tls.buffers.push(barrier);
3876 }
3877 }
3878 }
3879 ResourceRef::Image(image) => {
3880 for (next_access, prev_access, range) in Image::swap_accesses(
3881 image,
3882 node_accesses.iter().map(
3883 |&SubresourceAccess {
3884 access,
3885 subresource,
3886 }| {
3887 let SubresourceRange::Image(range) = subresource else {
3888 unreachable!()
3889 };
3890
3891 (access, range)
3892 },
3893 ),
3894 ) {
3895 let barrier = AccessBarrier {
3896 next_access,
3897 prev_access,
3898 resource: ImageBarrierTarget {
3899 image: image.handle,
3900 range,
3901 },
3902 };
3903
3904 if pending_image_transfer_nodes
3905 .as_ref()
3906 .is_some_and(|pending| pending.contains(node_idx))
3907 {
3908 tls.pending_images.push(node_idx, barrier);
3909 } else {
3910 tls.images.push(barrier);
3911 }
3912 }
3913 }
3914 }
3915 }
3916
3917 let global_barrier = if !tls.next_accesses.is_empty() {
3918 trace!(
3920 " global {:?}->{:?}",
3921 tls.next_accesses, tls.prev_accesses
3922 );
3923
3924 Some(GlobalBarrier {
3925 next_accesses: tls.next_accesses.as_slice(),
3926 previous_accesses: tls.prev_accesses.as_slice(),
3927 })
3928 } else {
3929 None
3930 };
3931 let mut buffer_barriers = Vec::new();
3932 for AccessBarrier {
3933 next_access,
3934 prev_access,
3935 resource,
3936 } in tls.buffers.iter()
3937 {
3938 let BufferBarrierTarget { buffer, range, .. } = *resource;
3939
3940 buffer_barriers.push(BufferBarrier {
3941 next_accesses: slice::from_ref(next_access),
3942 previous_accesses: slice::from_ref(prev_access),
3943 src_queue_family_index: vk::QUEUE_FAMILY_IGNORED,
3944 dst_queue_family_index: vk::QUEUE_FAMILY_IGNORED,
3945 buffer,
3946 offset: range.start as _,
3947 size: (range.end - range.start) as _,
3948 });
3949 }
3950
3951 if let Some(pending_buffer_transfer_nodes) = pending_buffer_transfer_nodes.as_ref() {
3952 for (node_idx, _buffer, transfers) in pending_buffer_transfer_nodes.iter() {
3953 for AccessBarrier {
3954 next_access,
3955 prev_access,
3956 resource,
3957 } in tls.pending_buffers.get(node_idx)
3958 {
3959 buffer_barriers.extend(buffer_barriers_from_transfers(
3960 resource.buffer,
3961 prev_access,
3962 next_access,
3963 resource.range,
3964 transfers,
3965 ));
3966 }
3967 }
3968 }
3969
3970 let mut image_barriers = Vec::new();
3971 for AccessBarrier {
3972 next_access,
3973 prev_access,
3974 resource,
3975 } in tls.images.iter()
3976 {
3977 let ImageBarrierTarget { image, range, .. } = *resource;
3978
3979 image_barriers.push(ImageBarrier {
3980 next_accesses: slice::from_ref(next_access),
3981 previous_accesses: slice::from_ref(prev_access),
3982 next_layout: image_access_layout(*next_access),
3983 previous_layout: image_access_layout(*prev_access),
3984 discard_contents: image_execution_discard_contents(*prev_access),
3985 src_queue_family_index: vk::QUEUE_FAMILY_IGNORED,
3986 dst_queue_family_index: vk::QUEUE_FAMILY_IGNORED,
3987 image,
3988 range,
3989 });
3990 }
3991
3992 if let Some(pending_image_transfer_nodes) = pending_image_transfer_nodes.as_ref() {
3993 for (node_idx, _image, transfers) in pending_image_transfer_nodes.iter() {
3994 for AccessBarrier {
3995 next_access,
3996 prev_access,
3997 resource,
3998 } in tls.pending_images.get(node_idx)
3999 {
4000 image_barriers.extend(image_barriers_from_transfers(
4001 resource.image,
4002 prev_access,
4003 next_access,
4004 resource.range,
4005 transfers,
4006 image_execution_discard_contents(*prev_access),
4007 ));
4008 }
4009 }
4010 }
4011
4012 pipeline_barrier_from_iters(
4013 &cmd_buf.device,
4014 cmd_buf.handle,
4015 global_barrier,
4016 buffer_barriers.into_iter(),
4017 image_barriers.into_iter(),
4018 );
4019
4020 if let Some(pending) = pending_buffer_transfer_nodes.as_mut() {
4021 pending.remove_where(|node_idx, _buffer, transfers| {
4022 for AccessBarrier { resource, .. } in tls.pending_buffers.get(node_idx) {
4023 let range = resource.range;
4024
4025 if consume_pending_buffer_transfers(transfers, range) {
4026 return true;
4027 }
4028 }
4029
4030 false
4031 });
4032
4033 if pending.is_empty() {
4034 *pending_buffer_transfer_nodes = None;
4035 }
4036 }
4037
4038 if let Some(pending) = pending_image_transfer_nodes.as_mut() {
4039 pending.remove_where(|node_idx, _image, transfers| {
4040 for AccessBarrier { resource, .. } in tls.pending_images.get(node_idx) {
4041 let range = resource.range;
4042
4043 if consume_pending_image_transfers(transfers, range) {
4044 return true;
4045 }
4046 }
4047
4048 false
4049 });
4050
4051 if pending.is_empty() {
4052 *pending_image_transfer_nodes = None;
4053 }
4054 }
4055 });
4056 }
4057
4058 #[profiling::function]
4059 fn record_image_layout_transitions(
4060 cmd_buf: &CommandBuffer,
4061 resources: &mut [AnyResource],
4062 pass: &mut CommandData,
4063 pending_buffer_transfer_nodes: &mut Option<
4064 PendingTransferNodes<vk::Buffer, BufferQueueOwnershipTransfer>,
4065 >,
4066 pending_image_transfer_nodes: &mut Option<
4067 PendingTransferNodes<vk::Image, ImageQueueOwnershipTransfer>,
4068 >,
4069 ) {
4070 struct ImageResourceBarrier {
4071 image: vk::Image,
4072 node_idx: NodeIndex,
4073 next_access: AccessType,
4074 prev_access: AccessType,
4075 range: vk::ImageSubresourceRange,
4076 }
4077
4078 struct BufferResourceBarrier {
4079 buffer: vk::Buffer,
4080 next_access: AccessType,
4081 prev_access: AccessType,
4082 range: BufferSubresourceRange,
4083 }
4084
4085 #[derive(Default)]
4086 struct LayoutTransitionScratch {
4087 buffers: Vec<BufferResourceBarrier>,
4088 images: Vec<ImageResourceBarrier>,
4089 first_layout_uses: HashMap<usize, DenseMap<bool>>,
4090 pending_buffers: NodeIndexedScratch<BufferResourceBarrier>,
4091 pending_images: NodeIndexedScratch<ImageResourceBarrier>,
4092 }
4093
4094 thread_local! {
4096 static LAYOUT_TRANSITION: RefCell<LayoutTransitionScratch> = Default::default();
4097 }
4098
4099 LAYOUT_TRANSITION.with_borrow_mut(|tls| {
4100 tls.buffers.clear();
4101 tls.images.clear();
4102 tls.first_layout_uses.clear();
4103 tls.pending_buffers.clear();
4104 tls.pending_images.clear();
4105
4106 for (node_idx, accesses) in pass.execs.iter_mut().flat_map(|exec| exec.accesses.iter())
4107 {
4108 debug_assert!(resources.get(node_idx).is_some());
4109
4110 let resource = unsafe {
4111 resources.get_unchecked(node_idx)
4113 };
4114
4115 enum ResourceRef<'a> {
4116 AccelerationStructure(&'a AccelerationStructure),
4117 Buffer(&'a Buffer),
4118 Image(&'a Image),
4119 }
4120
4121 let resource = match resource {
4122 AnyResource::AccelerationStructure(resource) => {
4123 ResourceRef::AccelerationStructure(resource)
4124 }
4125 AnyResource::AccelerationStructureArg(_) => {
4126 panic!("unbound command stream acceleration structure argument")
4127 }
4128 AnyResource::AccelerationStructureLease(resource) => {
4129 ResourceRef::AccelerationStructure(resource)
4130 }
4131 AnyResource::Buffer(resource) => ResourceRef::Buffer(resource),
4132 AnyResource::BufferArg(_) => panic!("unbound command stream buffer argument"),
4133 AnyResource::BufferLease(resource) => ResourceRef::Buffer(resource),
4134 AnyResource::Image(resource) => ResourceRef::Image(resource),
4135 AnyResource::ImageArg(_) => panic!("unbound command stream image argument"),
4136 AnyResource::ImageLease(resource) => ResourceRef::Image(resource),
4137 AnyResource::SwapchainImage(resource) => ResourceRef::Image(resource),
4138 };
4139
4140 match resource {
4141 ResourceRef::AccelerationStructure(accel_struct) => {
4142 AccelerationStructure::swap_access(accel_struct, AccessType::Nothing)
4143 .for_each(drop);
4144 }
4145 ResourceRef::Buffer(buffer) => {
4146 for subresource_access in accesses {
4147 let &SubresourceAccess {
4148 access,
4149 subresource: SubresourceRange::Buffer(access_range),
4150 } = subresource_access
4151 else {
4152 #[cfg(feature = "checked")]
4153 unreachable!();
4154
4155 #[cfg(not(feature = "checked"))]
4156 unsafe {
4157 unreachable_unchecked()
4160 }
4161 };
4162
4163 for (prev_access, range) in
4164 Buffer::swap_access(buffer, AccessType::Nothing, access_range)
4165 {
4166 if !pending_buffer_transfer_nodes
4167 .as_ref()
4168 .is_some_and(|pending| pending.contains(node_idx))
4169 {
4170 continue;
4171 }
4172
4173 tls.pending_buffers.push(
4174 node_idx,
4175 BufferResourceBarrier {
4176 buffer: buffer.handle,
4177 next_access: access,
4178 prev_access,
4179 range,
4180 },
4181 );
4182 }
4183 }
4184 }
4185 ResourceRef::Image(image) => {
4186 let first_layout_uses = tls
4187 .first_layout_uses
4188 .entry(node_idx)
4189 .or_insert_with(|| DenseMap::new(image.info, true));
4190
4191 for subresource_access in accesses {
4192 let &SubresourceAccess {
4193 access,
4194 subresource: SubresourceRange::Image(access_range),
4195 } = subresource_access
4196 else {
4197 #[cfg(feature = "checked")]
4198 unreachable!();
4199
4200 #[cfg(not(feature = "checked"))]
4201 unsafe {
4202 unreachable_unchecked()
4205 }
4206 };
4207
4208 let access_range = image.info.resolve_subresource_counts(access_range);
4209
4210 for (is_initial_layout, layout_range) in
4211 first_layout_uses.swap(false, access_range)
4212 {
4213 for (prev_access, range) in
4214 Image::swap_access(image, access, layout_range)
4215 {
4216 if is_initial_layout {
4217 let barrier = ImageResourceBarrier {
4218 image: image.handle,
4219 node_idx,
4220 next_access: initial_image_layout_access(access),
4221 prev_access,
4222 range,
4223 };
4224
4225 if pending_image_transfer_nodes
4226 .as_ref()
4227 .is_some_and(|pending| pending.contains(node_idx))
4228 {
4229 tls.pending_images.push(node_idx, barrier);
4230 } else {
4231 tls.images.push(barrier);
4232 }
4233 }
4234 }
4235 }
4236 }
4237 }
4238 }
4239 }
4240
4241 let mut buffer_barriers = Vec::new();
4242 if let Some(pending_buffer_transfer_nodes) = pending_buffer_transfer_nodes.as_ref() {
4243 for (node_idx, _buffer, transfers) in pending_buffer_transfer_nodes.iter() {
4244 for BufferResourceBarrier {
4245 buffer,
4246 next_access,
4247 prev_access,
4248 range,
4249 ..
4250 } in tls.pending_buffers.get(node_idx)
4251 {
4252 for transfer in transfers.iter().copied() {
4253 let Some(range) = range.intersection(transfer.range) else {
4254 continue;
4255 };
4256
4257 trace!(
4258 " buffer {:?} {:?} {:?}->{:?}",
4259 buffer,
4260 range.start..range.end,
4261 prev_access,
4262 next_access,
4263 );
4264
4265 buffer_barriers.push(BufferBarrier {
4266 next_accesses: slice::from_ref(next_access),
4267 previous_accesses: slice::from_ref(prev_access),
4268 src_queue_family_index: transfer.src_queue_family_index,
4269 dst_queue_family_index: transfer.dst_queue_family_index,
4270 buffer: *buffer,
4271 offset: range.start as _,
4272 size: (range.end - range.start) as _,
4273 });
4274 }
4275 }
4276 }
4277 }
4278
4279 let mut image_barriers = Vec::new();
4280 for ImageResourceBarrier {
4281 image,
4282 node_idx,
4283 next_access,
4284 prev_access,
4285 range,
4286 } in tls.images.iter()
4287 {
4288 if pending_image_transfer_nodes
4289 .as_ref()
4290 .is_some_and(|pending| pending.contains(*node_idx))
4291 {
4292 continue;
4293 }
4294
4295 image_barriers.extend(image_barriers_from_transfers(
4296 *image,
4297 prev_access,
4298 next_access,
4299 *range,
4300 &[],
4301 image_layout_transition_discard_contents(*prev_access, *next_access),
4302 ));
4303 }
4304
4305 if let Some(pending_image_transfer_nodes) = pending_image_transfer_nodes.as_ref() {
4306 for (node_idx, _image, transfers) in pending_image_transfer_nodes.iter() {
4307 for ImageResourceBarrier {
4308 image,
4309 next_access,
4310 prev_access,
4311 range,
4312 ..
4313 } in tls.pending_images.get(node_idx)
4314 {
4315 image_barriers.extend(image_barriers_from_transfers(
4316 *image,
4317 prev_access,
4318 next_access,
4319 *range,
4320 transfers,
4321 image_layout_transition_discard_contents(*prev_access, *next_access),
4322 ));
4323 }
4324 }
4325 }
4326
4327 pipeline_barrier_from_iters(
4328 &cmd_buf.device,
4329 cmd_buf.handle,
4330 None,
4331 buffer_barriers.into_iter(),
4332 image_barriers.into_iter(),
4333 );
4334
4335 if let Some(pending) = pending_buffer_transfer_nodes.as_mut() {
4336 pending.remove_where(|node_idx, _buffer, transfers| {
4337 for BufferResourceBarrier { range, .. } in tls.pending_buffers.get(node_idx) {
4338 if consume_pending_buffer_transfers(transfers, *range) {
4339 return true;
4340 }
4341 }
4342
4343 false
4344 });
4345
4346 if pending.is_empty() {
4347 *pending_buffer_transfer_nodes = None;
4348 }
4349 }
4350
4351 if let Some(pending) = pending_image_transfer_nodes.as_mut() {
4352 pending.remove_where(|node_idx, _image, transfers| {
4353 for ImageResourceBarrier { range, .. } in tls.pending_images.get(node_idx) {
4354 if consume_pending_image_transfers(transfers, *range) {
4355 return true;
4356 }
4357 }
4358
4359 false
4360 });
4361
4362 if pending.is_empty() {
4363 *pending_image_transfer_nodes = None;
4364 }
4365 }
4366 });
4367 }
4368
4369 #[profiling::function]
4370 fn record_node_cmds<P>(
4371 &mut self,
4372 pool: &mut P,
4373 cmd_buf: &CommandBuffer,
4374 node_idx: usize,
4375 end_cmd_idx: usize,
4376 ) -> Result<(), DriverError>
4377 where
4378 P: SubmissionPool,
4379 {
4380 thread_local! {
4381 static SCHEDULE: RefCell<Schedule> = Default::default();
4382 }
4383
4384 SCHEDULE.with_borrow_mut(|schedule| {
4385 schedule.access_index.update(&self.graph, end_cmd_idx);
4386 schedule.cmds.clear();
4387
4388 self.schedule_node_cmds(node_idx, end_cmd_idx, schedule);
4389 self.record_scheduled_cmds(pool, cmd_buf, schedule, end_cmd_idx)
4390 })
4391 }
4392
4393 fn track_pending_transfers(&mut self, schedule: &Schedule, queue_family_index: u32) {
4394 #[derive(Debug)]
4395 struct BufferRangeSet {
4396 buffer: vk::Buffer,
4397 range_keys: HashSet<BufferSubresourceRangeKey>,
4398 }
4399
4400 #[derive(Default)]
4401 struct PendingTransferScratch {
4402 buffers: HashMap<usize, BufferRangeSet>,
4403 images: HashMap<usize, ImageRangeSet>,
4404 }
4405
4406 thread_local! {
4407 static PENDING_TRANSFER: RefCell<PendingTransferScratch> = Default::default();
4408 }
4409
4410 PENDING_TRANSFER.with_borrow_mut(|tls| {
4411 tls.buffers.clear();
4412 tls.images.clear();
4413
4414 for cmd_idx in schedule.cmds.iter().copied() {
4415 let cmd = &self.graph.cmds[cmd_idx];
4416
4417 for (node_idx, accesses) in cmd.execs.iter().flat_map(|exec| exec.accesses.iter()) {
4418 if let Some(buffer) = self.graph.resources[node_idx].as_buffer() {
4419 if buffer.info.sharing_mode == vk::SharingMode::CONCURRENT {
4420 continue;
4421 }
4422
4423 let transfer =
4424 tls.buffers
4425 .entry(node_idx)
4426 .or_insert_with(|| BufferRangeSet {
4427 buffer: buffer.handle,
4428 range_keys: Default::default(),
4429 });
4430
4431 for access in accesses.iter() {
4432 let SubresourceRange::Buffer(access_range) = access.subresource else {
4433 continue;
4434 };
4435
4436 let access_range = BufferSubresourceRange {
4437 start: access_range.start,
4438 end: if access_range.end == vk::WHOLE_SIZE {
4439 buffer.info.size
4440 } else {
4441 access_range.end
4442 },
4443 };
4444 let access_key = BufferSubresourceRangeKey::from_range(access_range);
4445
4446 if !transfer.range_keys.insert(access_key) {
4447 continue;
4448 }
4449
4450 for (subresource, sharing) in
4451 buffer.sync_info_with_sharing_range(access_range)
4452 {
4453 let Some(range) = subresource.range.intersection(access_range)
4454 else {
4455 continue;
4456 };
4457
4458 let Some((src_queue_family_index, src_queue_index)) =
4459 exclusive_transfer_source(sharing, queue_family_index)
4460 else {
4461 continue;
4462 };
4463
4464 self.pending_buffer_transfer_nodes
4465 .get_or_insert_with(|| {
4466 PendingTransferNodes::new(self.graph.resources.len())
4467 })
4468 .push_transfer(
4469 node_idx,
4470 transfer.buffer,
4471 BufferQueueOwnershipTransfer {
4472 src_queue_family_index,
4473 src_queue_index,
4474 dst_queue_family_index: queue_family_index,
4475 range,
4476 },
4477 );
4478 }
4479 }
4480
4481 continue;
4482 }
4483
4484 let Some(image) = self.graph.resources[node_idx].as_image() else {
4485 continue;
4486 };
4487
4488 if image.info.sharing_mode == vk::SharingMode::CONCURRENT {
4489 continue;
4490 }
4491
4492 let transfer = tls.images.entry(node_idx).or_insert_with(|| ImageRangeSet {
4493 image: image.handle,
4494 range_keys: Default::default(),
4495 });
4496
4497 for access in accesses.iter() {
4498 let SubresourceRange::Image(access_range) = access.subresource else {
4499 continue;
4500 };
4501
4502 let access_range = image.info.resolve_subresource_counts(access_range);
4503 let access_key = ImageSubresourceRangeKey::from_range(access_range);
4504
4505 if !transfer.range_keys.insert(access_key) {
4506 continue;
4507 }
4508
4509 for (subresource, sharing) in
4510 image.sync_info_with_sharing_range(access_range)
4511 {
4512 let Some(range) = image_subresource_range_intersection(
4513 subresource.range,
4514 access_range,
4515 ) else {
4516 continue;
4517 };
4518
4519 let layout = subresource.layout.unwrap_or(vk::ImageLayout::UNDEFINED);
4520
4521 let Some((src_queue_family_index, src_queue_index)) =
4522 exclusive_transfer_source(sharing, queue_family_index)
4523 else {
4524 continue;
4525 };
4526
4527 self.pending_image_transfer_nodes
4528 .get_or_insert_with(|| {
4529 PendingTransferNodes::new(self.graph.resources.len())
4530 })
4531 .push_transfer(
4532 node_idx,
4533 transfer.image,
4534 ImageQueueOwnershipTransfer {
4535 src_queue_family_index,
4536 src_queue_index,
4537 dst_queue_family_index: queue_family_index,
4538 layout,
4539 range,
4540 },
4541 );
4542 }
4543 }
4544 }
4545 }
4546
4547 for (node_idx, transfer) in tls.buffers.iter() {
4548 self.exclusive_buffer_ranges
4549 .entry(*node_idx)
4550 .or_default()
4551 .extend(
4552 transfer
4553 .range_keys
4554 .iter()
4555 .copied()
4556 .map(BufferSubresourceRangeKey::into_range),
4557 );
4558 }
4559
4560 for (node_idx, transfer) in tls.images.iter() {
4561 self.exclusive_image_ranges
4562 .entry(*node_idx)
4563 .or_default()
4564 .extend(
4565 transfer
4566 .range_keys
4567 .iter()
4568 .copied()
4569 .map(ImageSubresourceRangeKey::into_range),
4570 );
4571 }
4572 });
4573 }
4574
4575 fn record_cmd_indices(
4576 &mut self,
4577 cmd_buf: &CommandBuffer,
4578 cmd_indices: impl IntoIterator<Item = usize>,
4579 ) -> Result<(), DriverError> {
4580 #[cfg(feature = "checked")]
4581 let graph_id = self.graph.graph_id();
4582 for cmd_idx in cmd_indices {
4583 let cmd = &mut self.graph.cmds[cmd_idx];
4584
4585 profiling::scope!("Cmd", cmd.name());
4586 let stream_label = cmd
4587 .stream_scope_id
4588 .and_then(|_| CommandBufferDebugLabel::begin(cmd_buf, "command stream boundary"));
4589 let _cmd_label = CommandBufferDebugLabel::begin(cmd_buf, cmd.name());
4590
4591 let recorded_command = &mut self.recorded_commands[cmd_idx];
4592 let is_graphics = recorded_command.render_pass.is_some();
4593
4594 trace!("recording cmd [{}: {}]", cmd_idx, cmd.name());
4595
4596 if !recorded_command.descriptor_sets.is_empty() {
4597 Self::write_descriptor_sets(cmd_buf, &self.graph.resources, cmd, recorded_command)?;
4598 }
4599
4600 let (render_area, render_pass_label) = if is_graphics {
4601 Self::record_image_layout_transitions(
4602 cmd_buf,
4603 &mut self.graph.resources,
4604 cmd,
4605 &mut self.pending_buffer_transfer_nodes,
4606 &mut self.pending_image_transfer_nodes,
4607 );
4608
4609 let render_area = vk::Rect2D {
4610 offset: vk::Offset2D { x: 0, y: 0 },
4611 extent: Self::render_extent(&self.graph.resources, cmd),
4612 };
4613 let render_pass_label = CommandBufferDebugLabel::begin(
4614 cmd_buf,
4615 format!("{} / render pass", cmd.name()),
4616 );
4617
4618 Self::begin_render_pass(
4619 cmd_buf,
4620 &self.graph.resources,
4621 cmd,
4622 recorded_command,
4623 render_area,
4624 )?;
4625
4626 (Some(render_area), render_pass_label)
4627 } else {
4628 (None, None)
4629 };
4630
4631 for exec_idx in 0..cmd.execs.len() {
4632 let render_area = if is_graphics {
4633 Some(
4634 cmd.execs[exec_idx]
4635 .render_area
4636 .unwrap_or(render_area.expect("missing render area")),
4637 )
4638 } else {
4639 None
4640 };
4641 let exec_label_name = cmd_buf
4642 .device
4643 .physical
4644 .instance
4645 .info
4646 .debug
4647 .then(|| format!("{} / exec {exec_idx}", cmd.name()));
4648
4649 let exec = &mut cmd.execs[exec_idx];
4650
4651 if is_graphics && exec_idx > 0 {
4652 Self::next_subpass(cmd_buf);
4653 }
4654
4655 if let Some(pipeline) = exec.pipeline.as_mut() {
4656 Self::bind_pipeline(
4657 cmd_buf,
4658 recorded_command,
4659 exec_idx,
4660 pipeline,
4661 exec.depth_stencil,
4662 )?;
4663
4664 if is_graphics {
4665 let render_area = render_area.expect("missing render area");
4666
4667 Self::set_viewport(
4669 cmd_buf,
4670 render_area.offset.x as _,
4671 render_area.offset.y as _,
4672 render_area.extent.width as _,
4673 render_area.extent.height as _,
4674 exec.depth_stencil
4675 .map(|depth_stencil| {
4676 let min = depth_stencil.min.0;
4677 let max = depth_stencil.max.0;
4678 min..max
4679 })
4680 .unwrap_or(0.0..1.0),
4681 );
4682 Self::set_scissor(
4683 cmd_buf,
4684 render_area.offset.x,
4685 render_area.offset.y,
4686 render_area.extent.width,
4687 render_area.extent.height,
4688 );
4689 }
4690
4691 Self::bind_descriptor_sets(cmd_buf, pipeline, recorded_command, exec_idx);
4692 }
4693
4694 if !is_graphics {
4695 Self::record_execution_barriers(
4696 cmd_buf,
4697 &mut self.graph.resources,
4698 &exec.accesses,
4699 &mut self.pending_buffer_transfer_nodes,
4700 &mut self.pending_image_transfer_nodes,
4701 );
4702 }
4703
4704 trace!(" > exec[{exec_idx}]");
4705
4706 {
4707 profiling::scope!("Execute callback");
4708 let _exec_label = exec_label_name.as_deref().and_then(|exec_label_name| {
4709 CommandBufferDebugLabel::begin(cmd_buf, exec_label_name)
4710 });
4711
4712 let exec_func = exec.func.take().expect("missing command function");
4713 exec.func = exec_func.record(CommandRef::new(
4714 cmd_buf,
4715 &self.graph.resources,
4716 exec,
4717 #[cfg(feature = "checked")]
4718 graph_id,
4719 ));
4720 }
4721 }
4722
4723 if is_graphics {
4724 trace!(" end render pass");
4725
4726 cmd_buf.end_render_pass();
4727 }
4728
4729 drop(render_pass_label);
4730 drop(stream_label);
4731 }
4732 Ok(())
4733 }
4734
4735 #[profiling::function]
4736 fn record_scheduled_cmds<P>(
4737 &mut self,
4738 pool: &mut P,
4739 cmd_buf: &CommandBuffer,
4740 schedule: &mut Schedule,
4741 end_cmd_idx: usize,
4742 ) -> Result<(), DriverError>
4743 where
4744 P: SubmissionPool,
4745 {
4746 if schedule.cmds.is_empty() {
4747 return Ok(());
4748 }
4749
4750 debug_assert!(
4756 schedule.cmds.windows(2).all(|w| w[0] <= w[1]),
4757 "Unsorted schedule"
4758 );
4759
4760 schedule.reorder_cmds(end_cmd_idx);
4762 self.merge_scheduled_cmds(&mut schedule.cmds);
4763 self.lease_scheduled_resources(pool, &schedule.cmds)?;
4764 self.track_pending_transfers(schedule, cmd_buf.info.queue_family_index);
4765 self.queue_ownership_release_groups
4766 .extend(self.collect_queue_ownership_release_groups());
4767
4768 self.record_cmd_indices(cmd_buf, schedule.cmds.iter().copied())?;
4769
4770 thread_local! {
4771 static PASSES: RefCell<Vec<CommandData>> = Default::default();
4772 }
4773
4774 PASSES.with_borrow_mut(|passes| {
4775 debug_assert!(passes.is_empty());
4776
4777 schedule.cmds.sort_unstable();
4779 while let Some(schedule_idx) = schedule.cmds.pop() {
4780 debug_assert!(!self.graph.cmds.is_empty());
4781
4782 while let Some(cmd) = self.graph.cmds.pop() {
4783 let cmd_idx = self.graph.cmds.len();
4784
4785 if cmd_idx == schedule_idx {
4786 self.submit_retained.push(Box::new((
4789 cmd,
4790 self.recorded_commands
4791 .pop()
4792 .expect("missing recorded command"),
4793 )));
4794 break;
4795 } else {
4796 debug_assert!(cmd_idx > schedule_idx);
4797
4798 passes.push(cmd);
4799 }
4800 }
4801 }
4802
4803 debug_assert!(self.recorded_commands.is_empty());
4804
4805 self.graph.cmds.extend(passes.drain(..).rev());
4807 });
4808
4809 log::trace!("Recorded passes");
4810
4811 Ok(())
4812 }
4813
4814 #[profiling::function]
4815 fn render_extent(bindings: &[AnyResource], pass: &CommandData) -> vk::Extent2D {
4816 let first_exec = pass.expect_first_exec();
4819
4820 let (mut width, mut height) = (u32::MAX, u32::MAX);
4823 for (attachment_width, attachment_height) in first_exec
4824 .attachments
4825 .color_attachments()
4826 .map(|(_, state)| state.attachment)
4827 .chain(
4828 first_exec
4829 .attachments
4830 .depth_stencil_attachment()
4831 .into_iter()
4832 .filter(|state| state.is_attachment)
4833 .map(|state| state.attachment),
4834 )
4835 .map(|attachment| {
4836 let info = Self::expect_attachment_image(bindings, &attachment).info;
4837
4838 (
4839 info.width >> attachment.base_mip_level,
4840 info.height >> attachment.base_mip_level,
4841 )
4842 })
4843 {
4844 width = width.min(attachment_width);
4845 height = height.min(attachment_height);
4846 }
4847
4848 vk::Extent2D { height, width }
4849 }
4850
4851 pub fn resource<N>(&self, resource_node: N) -> &N::Resource
4854 where
4855 N: Node,
4856 {
4857 self.graph.resource(resource_node)
4858 }
4859
4860 #[profiling::function]
4863 fn schedule_node_cmds(&self, node_idx: usize, end_cmd_idx: usize, schedule: &mut Schedule) {
4864 #[derive(Default)]
4865 struct ScheduleSearchScratch {
4866 pending_nodes: VecDeque<(usize, usize)>,
4867 resolved_nodes: FixedBitSet,
4868 scheduled_cmds: FixedBitSet,
4869 }
4870
4871 thread_local! {
4872 static SCHEDULE_SEARCH: RefCell<ScheduleSearchScratch> = Default::default();
4873 }
4874
4875 SCHEDULE_SEARCH.with_borrow_mut(|tls| {
4876 tls.scheduled_cmds.clear();
4877 tls.scheduled_cmds.grow(end_cmd_idx);
4878
4879 tls.resolved_nodes.clear();
4880 tls.resolved_nodes.grow(self.graph.resources.len());
4881
4882 debug_assert!(tls.pending_nodes.is_empty());
4883
4884 trace!("scheduling node {node_idx}");
4885
4886 tls.resolved_nodes.insert(node_idx);
4887
4888 for cmd_idx in schedule
4890 .access_index
4891 .prior_cmds_for_node(node_idx, end_cmd_idx)
4892 {
4893 trace!(
4894 " cmd [{cmd_idx}: {}] is dependent",
4895 self.graph.cmds[cmd_idx].name()
4896 );
4897
4898 debug_assert!(!tls.scheduled_cmds.contains(cmd_idx));
4899
4900 tls.scheduled_cmds.insert(cmd_idx);
4901 schedule.cmds.push(cmd_idx);
4902
4903 for node_idx in schedule.access_index.read_nodes_for_cmd(cmd_idx) {
4904 trace!(" node {node_idx} is dependent");
4905
4906 if !tls.resolved_nodes.put(node_idx) {
4907 tls.pending_nodes.push_back((node_idx, cmd_idx));
4908 }
4909 }
4910 }
4911
4912 trace!("secondary cmds below");
4913
4914 while let Some((node_idx, cmd_idx)) = tls.pending_nodes.pop_front() {
4916 trace!(" node {node_idx} is dependent");
4917
4918 for dep_cmd_idx in schedule
4919 .access_index
4920 .prior_cmds_for_node(node_idx, cmd_idx + 1)
4921 {
4922 if !tls.scheduled_cmds.put(dep_cmd_idx) {
4923 schedule.cmds.push(dep_cmd_idx);
4924
4925 trace!(
4926 " cmd [{dep_cmd_idx}: {}] is dependent",
4927 self.graph.cmds[dep_cmd_idx].name()
4928 );
4929
4930 for node_idx in schedule.access_index.read_nodes_for_cmd(dep_cmd_idx) {
4931 trace!(" node {node_idx} is dependent");
4932
4933 if !tls.resolved_nodes.put(node_idx) {
4934 tls.pending_nodes.push_back((node_idx, dep_cmd_idx));
4935 }
4936 }
4937 }
4938 }
4939 }
4940
4941 schedule.cmds.sort_unstable();
4942
4943 if log_enabled!(Debug) {
4944 if !schedule.cmds.is_empty() {
4945 debug!(
4947 "schedule: {}",
4948 schedule
4949 .cmds
4950 .iter()
4951 .copied()
4952 .map(|idx| format!("[{}: {}]", idx, self.graph.cmds[idx].name()))
4953 .collect::<Vec<_>>()
4954 .join(", ")
4955 );
4956 }
4957
4958 if log_enabled!(Trace) {
4959 let unscheduled = (0..end_cmd_idx)
4960 .filter(|&cmd_idx| !tls.scheduled_cmds.contains(cmd_idx))
4961 .collect::<Box<_>>();
4962
4963 if !unscheduled.is_empty() {
4964 trace!(
4967 "delaying: {}",
4968 unscheduled
4969 .iter()
4970 .copied()
4971 .map(|idx| format!("[{}: {}]", idx, self.graph.cmds[idx].name()))
4972 .collect::<Vec<_>>()
4973 .join(", ")
4974 );
4975 }
4976
4977 if end_cmd_idx < self.graph.cmds.len() {
4978 trace!(
4981 "ignoring: {}",
4982 self.graph.cmds[end_cmd_idx..]
4983 .iter()
4984 .enumerate()
4985 .map(|(idx, cmd)| format!(
4986 "[{}: {}]",
4987 idx + end_cmd_idx,
4988 cmd.name()
4989 ))
4990 .collect::<Vec<_>>()
4991 .join(", ")
4992 );
4993 }
4994 }
4995 }
4996 });
4997 }
4998
4999 fn set_scissor(cmd_buf: &CommandBuffer, x: i32, y: i32, width: u32, height: u32) {
5000 unsafe {
5001 cmd_buf.device.cmd_set_scissor(
5002 cmd_buf.handle,
5003 0,
5004 slice::from_ref(&vk::Rect2D {
5005 extent: vk::Extent2D { width, height },
5006 offset: vk::Offset2D { x, y },
5007 }),
5008 );
5009 }
5010 }
5011
5012 fn set_viewport(
5013 cmd_buf: &CommandBuffer,
5014 x: f32,
5015 y: f32,
5016 width: f32,
5017 height: f32,
5018 depth: Range<f32>,
5019 ) {
5020 unsafe {
5021 cmd_buf.device.cmd_set_viewport(
5022 cmd_buf.handle,
5023 0,
5024 slice::from_ref(&vk::Viewport {
5025 x,
5026 y,
5027 width,
5028 height,
5029 min_depth: depth.start,
5030 max_depth: depth.end,
5031 }),
5032 );
5033 }
5034 }
5035
5036 pub fn queue_submit<P>(
5041 self,
5042 resource_pool: &mut P,
5043 queue_family_index: u32,
5044 queue_index: u32,
5045 ) -> Result<Fence, DriverError>
5046 where
5047 P: Pool<CommandBufferInfo, CommandBuffer> + SubmissionPool,
5048 {
5049 trace!("queue_submit");
5050
5051 let cmd_buf = resource_pool.resource(CommandBufferInfo::new(queue_family_index as _))?;
5056 let mut fence = Fence::create(&cmd_buf.device, false)?;
5057 cmd_buf.begin(
5058 &vk::CommandBufferBeginInfo::default()
5059 .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT),
5060 )?;
5061 let recording = self.record(resource_pool, cmd_buf, RecordSelection::All)?;
5062 recording.cmd_buf.end()?;
5063
5064 let mut recorded = recording.finish()?;
5065 recorded.queue_submit(&mut fence, queue_index, QueueSubmitInfo::QUEUE_SUBMIT)?;
5066
5067 fence.drop_when_signaled(recorded);
5068
5069 Ok(fence)
5070 }
5071
5072 #[profiling::function]
5077 pub fn record<'p, 's, P, Cb>(
5078 mut self,
5079 resource_pool: &'p mut P,
5080 cmd_buf: Cb,
5081 selection: impl Into<RecordSelection<'s>>,
5082 ) -> Result<Recording<'p, P, Cb>, DriverError>
5083 where
5084 P: SubmissionPool,
5085 Cb: AsRef<CommandBuffer>,
5086 {
5087 self.record_selection_impl(resource_pool, cmd_buf.as_ref(), selection.into())?;
5088
5089 Ok(Recording {
5090 cmd_buf,
5091 resource_pool,
5092 submission: self,
5093 })
5094 }
5095
5096 #[profiling::function]
5097 fn record_impl<P>(&mut self, pool: &mut P, cmd_buf: &CommandBuffer) -> Result<(), DriverError>
5098 where
5099 P: SubmissionPool,
5100 {
5101 if self.graph.cmds.is_empty() {
5102 return Ok(());
5103 }
5104
5105 thread_local! {
5106 static SCHEDULE: RefCell<Schedule> = Default::default();
5107 }
5108
5109 SCHEDULE.with_borrow_mut(|schedule| {
5110 schedule
5111 .access_index
5112 .update(&self.graph, self.graph.cmds.len());
5113 schedule.cmds.clear();
5114 schedule.cmds.extend(0..self.graph.cmds.len());
5115
5116 self.record_scheduled_cmds(pool, cmd_buf, schedule, self.graph.cmds.len())
5117 })
5118 }
5119
5120 #[profiling::function]
5121 fn record_resource_dependencies_impl<P>(
5122 &mut self,
5123 pool: &mut P,
5124 cmd_buf: &CommandBuffer,
5125 resource_node: impl Node,
5126 ) -> Result<(), DriverError>
5127 where
5128 P: SubmissionPool,
5129 {
5130 self.graph.assert_node_owner(&resource_node);
5131
5132 let node_idx = resource_node.index();
5133
5134 debug_assert!(self.graph.resources.get(node_idx).is_some());
5135
5136 if let Some(end_pass_idx) = self.graph.first_node_access_pass_index(resource_node) {
5138 thread_local! {
5139 static SCHEDULE: RefCell<Schedule> = Default::default();
5140 }
5141
5142 SCHEDULE.with_borrow_mut(|tls| {
5143 tls.access_index.update(&self.graph, end_pass_idx + 1);
5144 tls.cmds.clear();
5145 schedule_dependency_cmds_before_target_access(
5146 &tls.access_index,
5147 node_idx,
5148 end_pass_idx,
5149 &mut tls.cmds,
5150 );
5151 self.record_scheduled_cmds(pool, cmd_buf, tls, end_pass_idx)
5152 })?;
5153 }
5154
5155 Ok(())
5156 }
5157
5158 #[profiling::function]
5159 fn record_resource_impl<P>(
5160 &mut self,
5161 pool: &mut P,
5162 cmd_buf: &CommandBuffer,
5163 resource_node: impl Node,
5164 ) -> Result<(), DriverError>
5165 where
5166 P: SubmissionPool,
5167 {
5168 self.graph.assert_node_owner(&resource_node);
5169
5170 let node_idx = resource_node.index();
5171
5172 debug_assert!(self.graph.resources.get(node_idx).is_some());
5173
5174 if self.graph.cmds.is_empty() {
5175 return Ok(());
5176 }
5177
5178 let end_pass_idx = self.graph.cmds.len();
5179 self.record_node_cmds(pool, cmd_buf, node_idx, end_pass_idx)
5180 }
5181
5182 #[profiling::function]
5183 fn write_descriptor_sets(
5184 cmd_buf: &CommandBuffer,
5185 bindings: &[AnyResource],
5186 pass: &CommandData,
5187 recorded_command: &CommandRecordingResources,
5188 ) -> Result<(), DriverError> {
5189 #[derive(Clone, Copy)]
5190 struct IndexedWrite<'a> {
5191 info_idx: usize,
5192 write: vk::WriteDescriptorSet<'a>,
5193 }
5194
5195 #[derive(Default)]
5196 struct DescriptorScratch<'a> {
5197 accel_struct_handles: Vec<vk::AccelerationStructureKHR>,
5198 accel_struct_infos: Vec<vk::WriteDescriptorSetAccelerationStructureKHR<'a>>,
5199 accel_struct_writes: Vec<IndexedWrite<'static>>,
5200 buffer_infos: Vec<vk::DescriptorBufferInfo>,
5201 buffer_writes: Vec<IndexedWrite<'a>>,
5202 descriptors: Vec<vk::WriteDescriptorSet<'a>>,
5203 image_infos: Vec<vk::DescriptorImageInfo>,
5204 image_writes: Vec<IndexedWrite<'a>>,
5205 }
5206
5207 thread_local! {
5208 static DESCRIPTOR: RefCell<DescriptorScratch<'static>> = Default::default();
5209 }
5210
5211 DESCRIPTOR.with_borrow_mut(|tls| {
5212 tls.accel_struct_handles.clear();
5213 tls.accel_struct_infos.clear();
5214 tls.accel_struct_writes.clear();
5215 tls.buffer_infos.clear();
5216 tls.buffer_writes.clear();
5217 tls.descriptors.clear();
5218 tls.image_infos.clear();
5219 tls.image_writes.clear();
5220
5221 for (exec_idx, exec, pipeline) in pass
5222 .execs
5223 .iter()
5224 .enumerate()
5225 .filter_map(|(exec_idx, exec)| {
5226 exec.pipeline
5227 .as_ref()
5228 .map(|pipeline| (exec_idx, exec, pipeline))
5229 })
5230 .filter(|(.., pipeline)| !pipeline.descriptor_info().layouts.is_empty())
5231 {
5232 let descriptor_sets = &recorded_command.descriptor_sets[exec_idx];
5233
5234 for (descriptor, (node_idx, view_info)) in exec.bindings.iter() {
5236 let (descriptor_set_idx, dst_binding, binding_offset) = descriptor.into_tuple();
5237 let Some((descriptor_info, _)) = pipeline.descriptor_bindings().get(&Descriptor {
5238 set: descriptor_set_idx,
5239 binding: dst_binding,
5240 }) else {
5241 warn!(
5242 "binding {}.{}[{}] not found in shader reflection for command \"{}\"",
5243 descriptor_set_idx,
5244 dst_binding,
5245 binding_offset,
5246 pass.name(),
5247 );
5248 return Err(DriverError::InvalidData);
5249 };
5250 let descriptor_type = descriptor_info.descriptor_type();
5251 let bound_node = &bindings[*node_idx];
5252 if let Some(image) = bound_node.as_image() {
5253 let mut image_view_info = *view_info.expect_image();
5254
5255 if image_view_info.aspect_mask.is_empty() {
5257 image_view_info.aspect_mask = format_aspect_mask(image.info.format);
5258 }
5259
5260 let image_view = Image::view(image, image_view_info)?;
5261 let image_layout = match descriptor_type {
5262 vk::DescriptorType::COMBINED_IMAGE_SAMPLER
5263 | vk::DescriptorType::SAMPLED_IMAGE => {
5264 if image_view_info.aspect_mask.contains(
5265 vk::ImageAspectFlags::DEPTH | vk::ImageAspectFlags::STENCIL,
5266 ) {
5267 vk::ImageLayout::DEPTH_STENCIL_READ_ONLY_OPTIMAL
5268 } else if image_view_info
5269 .aspect_mask
5270 .contains(vk::ImageAspectFlags::DEPTH)
5271 {
5272 vk::ImageLayout::DEPTH_READ_ONLY_OPTIMAL
5273 } else if image_view_info
5274 .aspect_mask
5275 .contains(vk::ImageAspectFlags::STENCIL)
5276 {
5277 vk::ImageLayout::STENCIL_READ_ONLY_OPTIMAL
5278 } else {
5279 vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL
5280 }
5281 }
5282 vk::DescriptorType::STORAGE_IMAGE => vk::ImageLayout::GENERAL,
5283 _ => {
5284 warn!(
5285 "invalid image descriptor type at binding {}.{}[{}] in command \"{}\"",
5286 descriptor_set_idx,
5287 dst_binding,
5288 binding_offset,
5289 pass.name()
5290 );
5291
5292 return Err(DriverError::InvalidData);
5293 }
5294 };
5295
5296 if binding_offset == 0 {
5297 tls.image_writes.push(IndexedWrite {
5298 info_idx: tls.image_infos.len(),
5299 write: vk::WriteDescriptorSet {
5300 dst_set: *descriptor_sets[descriptor_set_idx as usize],
5301 dst_binding,
5302 descriptor_type,
5303 descriptor_count: 1,
5304 ..Default::default()
5305 },
5306 });
5307 } else {
5308 tls.image_writes
5309 .last_mut()
5310 .expect("missing image descriptor write")
5311 .write
5312 .descriptor_count += 1;
5313 }
5314
5315 tls.image_infos.push(
5316 vk::DescriptorImageInfo::default()
5317 .image_layout(image_layout)
5318 .image_view(image_view),
5319 );
5320 } else if let Some(buffer) = bound_node.as_buffer() {
5321 let buffer_view_info = view_info.expect_buffer();
5322
5323 if binding_offset == 0 {
5324 tls.buffer_writes.push(IndexedWrite {
5325 info_idx: tls.buffer_infos.len(),
5326 write: vk::WriteDescriptorSet {
5327 dst_set: *descriptor_sets[descriptor_set_idx as usize],
5328 dst_binding,
5329 descriptor_type,
5330 descriptor_count: 1,
5331 ..Default::default()
5332 },
5333 });
5334 } else {
5335 tls.buffer_writes
5336 .last_mut()
5337 .expect("missing buffer descriptor write")
5338 .write
5339 .descriptor_count += 1;
5340 }
5341
5342 tls.buffer_infos.push(
5343 vk::DescriptorBufferInfo::default()
5344 .buffer(buffer.handle)
5345 .offset(buffer_view_info.start)
5346 .range(buffer_view_info.end - buffer_view_info.start),
5347 );
5348 } else if let Some(accel_struct) = bound_node.as_accel_struct() {
5349 if binding_offset == 0 {
5350 tls.accel_struct_writes.push(IndexedWrite {
5351 info_idx: tls.accel_struct_handles.len(),
5352 write: vk::WriteDescriptorSet::default()
5353 .dst_set(*descriptor_sets[descriptor_set_idx as usize])
5354 .dst_binding(dst_binding)
5355 .descriptor_type(descriptor_type)
5356 .descriptor_count(1),
5357 });
5358 } else {
5359 tls
5360 .accel_struct_writes
5361 .last_mut()
5362 .expect("missing acceleration structure descriptor write")
5363 .write
5364 .descriptor_count += 1;
5365 }
5366
5367 tls.accel_struct_handles.push(accel_struct.handle);
5368 } else {
5369 warn!(
5370 "invalid bound resource kind at descriptor {}.{}[{}] in command \"{}\"",
5371 descriptor_set_idx,
5372 dst_binding,
5373 binding_offset,
5374 pass.name()
5375 );
5376
5377 return Err(DriverError::InvalidData);
5378 }
5379 }
5380
5381 if let ExecutionPipeline::Graphics(pipeline) = pipeline {
5382 if exec_idx > 0 {
5384 for (
5385 &Descriptor {
5386 set: descriptor_set_idx,
5387 binding: dst_binding,
5388 },
5389 (descriptor_info, _),
5390 ) in &pipeline.inner.descriptor_bindings
5391 {
5392 if let DescriptorInfo::InputAttachment(_, attachment_idx) = *descriptor_info
5393 {
5394 let current_attachment = exec
5395 .attachments
5396 .color_attachment(attachment_idx)
5397 .map(|state| state.attachment)
5398 .expect("missing input attachment target");
5399 let attachment = pass.execs[0..exec_idx]
5400 .iter()
5401 .rev()
5402 .find_map(|exec| {
5403 exec.attachments
5404 .color_attachment(attachment_idx)
5405 .map(|state| state.attachment)
5406 .filter(|attachment| {
5407 Attachment::are_compatible(
5408 Some(current_attachment),
5409 Some(*attachment),
5410 )
5411 })
5412 })
5413 .expect("input attachment not written");
5414 let image_binding = &bindings[attachment.target];
5415 let image = image_binding.expect_image();
5416 let image_view =
5417 Image::view(image, attachment.image_view_info(image.info))?;
5418
5419 tls.image_writes.push(IndexedWrite {
5420 info_idx: tls.image_infos.len(),
5421 write: vk::WriteDescriptorSet {
5422 dst_set: *descriptor_sets[descriptor_set_idx as usize],
5423 dst_binding,
5424 descriptor_type: vk::DescriptorType::INPUT_ATTACHMENT,
5425 descriptor_count: 1,
5426 ..Default::default()
5427 },
5428 });
5429
5430 tls.image_infos.push(vk::DescriptorImageInfo {
5431 image_layout: Self::attachment_layout(
5432 attachment.aspect_mask,
5433 exec.attachments
5434 .color_attachment(attachment_idx)
5435 .map(|state| {
5436 state.store == StoreOp::Store || state.resolve.is_some()
5437 })
5438 .unwrap_or_default(),
5439 true,
5440 ),
5441 image_view,
5442 sampler: vk::Sampler::null(),
5443 });
5444 }
5445 }
5446 }
5447 }
5448 }
5449
5450 let accel_struct_handles = tls.accel_struct_handles.as_ptr();
5453 for write_idx in 0..tls.accel_struct_writes.len() {
5454 let IndexedWrite {
5455 info_idx: handle_idx,
5456 write,
5457 } = tls.accel_struct_writes[write_idx];
5458
5459 unsafe {
5460 tls.accel_struct_infos.push(
5461 vk::WriteDescriptorSetAccelerationStructureKHR {
5462 acceleration_structure_count: write.descriptor_count,
5463 p_acceleration_structures: accel_struct_handles.add(handle_idx),
5464 ..Default::default()
5465 },
5466 );
5467 }
5468 }
5469
5470 let infos = tls.accel_struct_infos.as_ptr();
5471 for (write_idx, IndexedWrite { mut write, .. }) in
5472 tls.accel_struct_writes.drain(..).enumerate()
5473 {
5474 unsafe {
5475 write.p_next = infos.add(write_idx) as *const _;
5476 }
5477
5478 tls.descriptors.push(write);
5479 }
5480
5481 let buffer_infos_ptr = tls.buffer_infos.as_ptr();
5482 for write_idx in 0..tls.buffer_writes.len() {
5483 let IndexedWrite {
5484 info_idx,
5485 mut write,
5486 } = tls.buffer_writes[write_idx];
5487 unsafe {
5488 write.p_buffer_info = buffer_infos_ptr.add(info_idx);
5489 }
5490 tls.descriptors.push(write);
5491 }
5492
5493 let image_infos_ptr = tls.image_infos.as_ptr();
5494 for write_idx in 0..tls.image_writes.len() {
5495 let IndexedWrite {
5496 info_idx,
5497 mut write,
5498 } = tls.image_writes[write_idx];
5499 unsafe {
5500 write.p_image_info = image_infos_ptr.add(info_idx);
5501 }
5502 tls.descriptors.push(write);
5503 }
5504
5505 if !tls.descriptors.is_empty() {
5506 trace!(
5507 " writing {} descriptors ({} buffers, {} images)",
5508 tls.descriptors.len(),
5509 tls.buffer_infos.len(),
5510 tls.image_infos.len()
5511 );
5512
5513 unsafe {
5514 cmd_buf
5515 .device
5516 .update_descriptor_sets(tls.descriptors.as_slice(), &[]);
5517 }
5518 }
5519
5520 Ok(())
5521 })
5522 }
5523}
5524
5525#[derive(Default)]
5526struct SubmitScratch {
5527 release_buffer_barriers: Vec<vk::BufferMemoryBarrier<'static>>,
5528 release_image_barriers: Vec<vk::ImageMemoryBarrier<'static>>,
5529 signal_infos: Vec<vk::SemaphoreSubmitInfo<'static>>,
5530 signal_semaphores: Vec<vk::Semaphore>,
5531 wait_infos: Vec<vk::SemaphoreSubmitInfo<'static>>,
5532 wait_semaphores: Vec<vk::Semaphore>,
5533 wait_stage_masks: Vec<vk::PipelineStageFlags>,
5534}
5535
5536#[doc(hidden)]
5537pub mod bench {
5538 use super::{CommandAccessIndex, Schedule};
5539
5540 #[derive(Clone, Copy, Debug)]
5542 pub struct ReorderBenchSpec {
5543 pub cmd_count: usize,
5545
5546 pub resource_count: usize,
5548
5549 pub short_lived_uses: usize,
5551
5552 pub long_lived_resource_count: usize,
5554
5555 pub long_lived_uses: usize,
5557 }
5558
5559 pub struct ReorderBenchHarness {
5561 schedule: Schedule,
5562 original_cmds: Vec<usize>,
5563 end_cmd_idx: usize,
5564 }
5565
5566 impl ReorderBenchHarness {
5567 pub fn new(spec: ReorderBenchSpec) -> Self {
5569 assert!(spec.cmd_count > 0, "cmd_count must be greater than zero");
5570 assert!(
5571 spec.resource_count > 0,
5572 "resource_count must be greater than zero"
5573 );
5574 assert!(
5575 spec.short_lived_uses > 0,
5576 "short_lived_uses must be greater than zero"
5577 );
5578
5579 let mut cmds_by_node = vec![Vec::new(); spec.resource_count];
5580 let mut accessed_nodes_by_cmd = vec![Vec::new(); spec.cmd_count];
5581
5582 for (node_idx, cmds) in cmds_by_node.iter_mut().enumerate() {
5583 let is_long_lived = node_idx < spec.long_lived_resource_count;
5584 let uses = if is_long_lived {
5585 spec.long_lived_uses.max(spec.short_lived_uses)
5586 } else {
5587 spec.short_lived_uses
5588 }
5589 .min(spec.cmd_count);
5590
5591 let seed = splitmix64(node_idx as u64 ^ ((spec.cmd_count as u64) << 32));
5592 let stride = odd_stride(seed, spec.cmd_count);
5593 let start = (seed as usize) % spec.cmd_count;
5594 let cluster_len = uses.max(1).min(spec.cmd_count);
5595
5596 cmds.reserve(uses);
5597
5598 for use_idx in 0..uses {
5599 let cmd_idx = if is_long_lived {
5600 (start + use_idx * stride) % spec.cmd_count
5601 } else {
5602 (start + use_idx % cluster_len + (use_idx / cluster_len) * stride)
5603 % spec.cmd_count
5604 };
5605
5606 cmds.push(cmd_idx);
5607 }
5608
5609 cmds.sort_unstable();
5610 cmds.dedup();
5611
5612 while cmds.len() < uses {
5613 let next_cmd = (start + cmds.len() * stride + cmds.len()) % spec.cmd_count;
5614 if cmds.binary_search(&next_cmd).is_err() {
5615 cmds.push(next_cmd);
5616 }
5617 }
5618
5619 cmds.sort_unstable();
5620
5621 for &cmd_idx in cmds.iter() {
5622 accessed_nodes_by_cmd[cmd_idx].push(node_idx);
5623 }
5624 }
5625
5626 for nodes in &mut accessed_nodes_by_cmd {
5627 nodes.sort_unstable();
5628 nodes.dedup();
5629 }
5630
5631 let cmds = (0..spec.cmd_count).collect::<Vec<_>>();
5632
5633 Self {
5634 schedule: Schedule {
5635 access_index: CommandAccessIndex {
5636 cmds_by_node,
5637 accessed_nodes_by_cmd,
5638 },
5639 cmds: cmds.clone(),
5640 ..Default::default()
5641 },
5642 original_cmds: cmds,
5643 end_cmd_idx: spec.cmd_count,
5644 }
5645 }
5646
5647 pub fn reorder_once(&mut self) -> u64 {
5649 self.schedule.cmds.clear();
5650 self.schedule
5651 .cmds
5652 .extend(self.original_cmds.iter().copied());
5653
5654 self.schedule.reorder_cmds(self.end_cmd_idx);
5655
5656 self.schedule
5657 .cmds
5658 .iter()
5659 .enumerate()
5660 .fold(0u64, |checksum, (idx, &pass_idx)| {
5661 checksum.wrapping_mul(1_099_511_628_211).wrapping_add(
5662 ((idx as u64) << 32) ^ pass_idx as u64 ^ 0x9e37_79b9_7f4a_7c15,
5663 )
5664 })
5665 }
5666 }
5667
5668 fn odd_stride(seed: u64, cmd_count: usize) -> usize {
5669 let stride = ((seed >> 32) as usize % cmd_count.max(2)) | 1;
5670
5671 stride.min(cmd_count.max(1) - 1).max(1)
5672 }
5673
5674 fn splitmix64(mut value: u64) -> u64 {
5675 value = value.wrapping_add(0x9e37_79b9_7f4a_7c15);
5676 value = (value ^ (value >> 30)).wrapping_mul(0xbf58_476d_1ce4_e5b9);
5677 value = (value ^ (value >> 27)).wrapping_mul(0x94d0_49bb_1331_11eb);
5678 value ^ (value >> 31)
5679 }
5680}
5681
5682#[doc(hidden)]
5683pub mod fuzz {
5684 use {
5685 super::{CommandAccessIndex, Schedule},
5686 fixedbitset::FixedBitSet,
5687 };
5688
5689 #[derive(Clone, Copy, Debug)]
5690 pub struct ResourceAccess {
5691 pub cmd_idx: usize,
5692 pub write: bool,
5693 }
5694
5695 pub fn check_schedule_reordering(cmd_count: usize, resource_accesses: &[Vec<ResourceAccess>]) {
5696 let cmd_count = cmd_count.min(256);
5697 if cmd_count == 0 {
5698 return;
5699 }
5700
5701 let (access_index, normalized_accesses) = build_access_index(cmd_count, resource_accesses);
5702
5703 let mut schedule = Schedule {
5704 access_index: access_index.clone(),
5705 cmds: (0..cmd_count).collect(),
5706 ..Default::default()
5707 };
5708
5709 schedule.reorder_cmds(cmd_count);
5710
5711 let reordered = schedule.cmds.clone();
5712
5713 assert_eq!(reordered.len(), cmd_count, "reordered cmd count changed");
5714
5715 let mut sorted = reordered.clone();
5716 sorted.sort_unstable();
5717 assert_eq!(
5718 sorted,
5719 (0..cmd_count).collect::<Vec<_>>(),
5720 "reordered cmds are not a permutation"
5721 );
5722
5723 let mut repeat = Schedule {
5724 access_index: access_index.clone(),
5725 cmds: (0..cmd_count).collect(),
5726 ..Default::default()
5727 };
5728 repeat.reorder_cmds(cmd_count);
5729 assert_eq!(reordered, repeat.cmds, "reordering is not deterministic");
5730
5731 assert_hazard_order_preserved(&reordered, &normalized_accesses);
5732
5733 let expected = reference_reorder(access_index, cmd_count);
5734 assert_eq!(
5735 reordered, expected,
5736 "reordering diverged from reference implementation"
5737 );
5738 }
5739
5740 fn build_access_index(
5741 cmd_count: usize,
5742 resource_accesses: &[Vec<ResourceAccess>],
5743 ) -> (CommandAccessIndex, Vec<Vec<ResourceAccess>>) {
5744 let mut cmds_by_node = Vec::with_capacity(resource_accesses.len());
5745 let mut accessed_nodes_by_cmd = vec![Vec::new(); cmd_count];
5746 let mut normalized_accesses = Vec::with_capacity(resource_accesses.len());
5747
5748 for (node_idx, accesses) in resource_accesses.iter().enumerate() {
5749 let mut normalized = accesses
5750 .iter()
5751 .copied()
5752 .filter(|access| access.cmd_idx < cmd_count)
5753 .collect::<Vec<_>>();
5754 normalized.sort_unstable_by_key(|access| access.cmd_idx);
5755
5756 let mut deduped = Vec::<ResourceAccess>::with_capacity(normalized.len());
5757 for access in normalized {
5758 if let Some(prev) = deduped.last_mut()
5759 && prev.cmd_idx == access.cmd_idx
5760 {
5761 prev.write |= access.write;
5762 continue;
5763 }
5764
5765 deduped.push(access);
5766 }
5767
5768 for access in &deduped {
5769 accessed_nodes_by_cmd[access.cmd_idx].push(node_idx);
5770 }
5771
5772 cmds_by_node.push(deduped.iter().map(|access| access.cmd_idx).collect());
5773 normalized_accesses.push(deduped);
5774 }
5775
5776 (
5777 CommandAccessIndex {
5778 cmds_by_node,
5779 accessed_nodes_by_cmd,
5780 },
5781 normalized_accesses,
5782 )
5783 }
5784
5785 fn assert_hazard_order_preserved(
5786 reordered: &[usize],
5787 resource_accesses: &[Vec<ResourceAccess>],
5788 ) {
5789 let mut positions = vec![usize::MAX; reordered.len()];
5790 for (position, &cmd_idx) in reordered.iter().enumerate() {
5791 positions[cmd_idx] = position;
5792 }
5793
5794 for accesses in resource_accesses {
5795 for (left_idx, left) in accesses.iter().enumerate() {
5796 for right in &accesses[(left_idx + 1)..] {
5797 if left.write || right.write {
5798 assert!(
5799 positions[left.cmd_idx] < positions[right.cmd_idx],
5800 "hazard order changed for resource accesses {:?} -> {:?}: {:?}",
5801 left,
5802 right,
5803 reordered
5804 );
5805 }
5806 }
5807 }
5808 }
5809 }
5810
5811 fn reference_reorder(access_index: CommandAccessIndex, cmd_count: usize) -> Vec<usize> {
5812 let mut cmds = (0..cmd_count).collect::<Vec<_>>();
5813 if cmds.len() < 3 {
5814 return cmds;
5815 }
5816
5817 let mut interdependent = vec![Vec::new(); cmd_count];
5818 let mut local_of_global = vec![usize::MAX; cmd_count];
5819 let mut seen_deps = FixedBitSet::with_capacity(cmd_count);
5820 let mut scheduled = FixedBitSet::with_capacity(cmd_count);
5821
5822 for (local_idx, &cmd_idx) in cmds.iter().enumerate() {
5823 local_of_global[cmd_idx] = local_idx;
5824 }
5825
5826 for (local_idx, &cmd_idx) in cmds.iter().enumerate() {
5827 for dep_cmd_idx in access_index.prior_read_dependency_cmds(cmd_idx, cmd_count) {
5828 let dep_local_idx = local_of_global[dep_cmd_idx];
5829 if dep_local_idx == usize::MAX || dep_local_idx == local_idx {
5830 continue;
5831 }
5832
5833 if !seen_deps.put(dep_local_idx) {
5834 interdependent[local_idx].push(dep_local_idx);
5835 }
5836 }
5837
5838 for dep_cmd_idx in access_index.prior_read_dependency_cmds(cmd_idx, cmd_count) {
5839 let dep_local_idx = local_of_global[dep_cmd_idx];
5840 if dep_local_idx != usize::MAX && dep_local_idx != local_idx {
5841 seen_deps.set(dep_local_idx, false);
5842 }
5843 }
5844 }
5845
5846 let mut scheduled_count = 0;
5847 while scheduled_count < cmd_count {
5848 let mut best_idx = scheduled_count;
5849 let mut best_overlap = interdependent[best_idx].len();
5850
5851 for (idx, dep_cmds) in interdependent[..cmd_count]
5852 .iter()
5853 .enumerate()
5854 .skip(scheduled_count + 1)
5855 {
5856 let mut overlap = 0;
5857
5858 for &dep_local in dep_cmds {
5859 if scheduled.contains(dep_local) {
5860 overlap += 1;
5861 } else {
5862 break;
5863 }
5864 }
5865
5866 if overlap > best_overlap {
5867 best_overlap = overlap;
5868 best_idx = idx;
5869 }
5870 }
5871
5872 scheduled.insert(best_idx);
5873 cmds.swap(scheduled_count, best_idx);
5874 interdependent.swap(scheduled_count, best_idx);
5875 scheduled_count += 1;
5876 }
5877
5878 cmds
5879 }
5880}
5881
5882#[cfg(test)]
5883mod tests {
5884 use super::{
5885 BufferQueueOwnershipTransfer, CommandAccessIndex, CommandData,
5886 ExternalRenderPassAccessHistory, ImageQueueOwnershipTransfer, NodeIndex,
5887 PipelineStageAccessFlags, QueueSubmitInfo, RecordSelection, RecordedSubmission,
5888 RecordedSubmissionState, Schedule, SemaphoreSubmitInfo, Submission, SubresourceAccess,
5889 SubresourceRange, check_queue_submit_args, fuzz,
5890 };
5891 use crate::{
5892 Attachment, DepthStencilAttachment, Execution, Graph, LoadOp, Node, StoreOp,
5893 driver::{
5894 DriverError, SharingMode,
5895 accel_struct::{AccelerationStructure, AccelerationStructureInfo},
5896 ash::vk,
5897 buffer::{Buffer, BufferInfo, BufferSubresourceRange},
5898 cmd_buf::{CommandBuffer, CommandBufferInfo},
5899 device::{Device, DeviceInfo},
5900 fence::Fence,
5901 graphics::{GraphicsPipeline, GraphicsPipelineInfo},
5902 image::{Image, ImageInfo, SampleCount},
5903 render_pass::SubpassDependency,
5904 },
5905 node::{AnyNode, BufferNode},
5906 pool::{Pool, hash::HashPool},
5907 };
5908 use {
5909 ash::vk::Handle,
5910 std::{
5911 env::set_var,
5912 mem::ManuallyDrop,
5913 ops::Deref,
5914 sync::{Arc, Mutex, MutexGuard, OnceLock},
5915 },
5916 vk_shader_macros::glsl,
5917 vk_sync::AccessType,
5918 };
5919
5920 fn color_subresource_range(
5921 array_layers: std::ops::Range<u32>,
5922 mip_levels: std::ops::Range<u32>,
5923 ) -> vk::ImageSubresourceRange {
5924 vk::ImageSubresourceRange {
5925 aspect_mask: vk::ImageAspectFlags::COLOR,
5926 base_array_layer: array_layers.start,
5927 layer_count: array_layers.end - array_layers.start,
5928 base_mip_level: mip_levels.start,
5929 level_count: mip_levels.end - mip_levels.start,
5930 }
5931 }
5932
5933 #[cfg(test)]
5934 fn sort_image_subresource_ranges(ranges: &mut [vk::ImageSubresourceRange]) {
5935 ranges.sort_unstable_by_key(|range| {
5936 (
5937 range.aspect_mask.as_raw(),
5938 range.base_array_layer,
5939 range.layer_count,
5940 range.base_mip_level,
5941 range.level_count,
5942 )
5943 });
5944 }
5945
5946 #[cfg(test)]
5947 fn sort_pending_image_transfers(transfers: &mut [ImageQueueOwnershipTransfer]) {
5948 transfers.sort_unstable_by_key(|transfer| {
5949 (
5950 transfer.src_queue_family_index,
5951 transfer.src_queue_index,
5952 transfer.dst_queue_family_index,
5953 transfer.layout.as_raw(),
5954 transfer.range.aspect_mask.as_raw(),
5955 transfer.range.base_array_layer,
5956 transfer.range.layer_count,
5957 transfer.range.base_mip_level,
5958 transfer.range.level_count,
5959 )
5960 });
5961 }
5962
5963 #[cfg(test)]
5964 fn sort_pending_buffer_transfers(transfers: &mut [BufferQueueOwnershipTransfer]) {
5965 transfers.sort_unstable_by_key(|transfer| {
5966 (
5967 transfer.src_queue_family_index,
5968 transfer.src_queue_index,
5969 transfer.dst_queue_family_index,
5970 transfer.range.start,
5971 transfer.range.end,
5972 )
5973 });
5974 }
5975
5976 fn pending_buffer_transfer_for_range(
5977 transfers: &[BufferQueueOwnershipTransfer],
5978 range: BufferSubresourceRange,
5979 ) -> Option<&BufferQueueOwnershipTransfer> {
5980 transfers.iter().find(|transfer| transfer.range == range)
5981 }
5982
5983 fn pending_transfer_for_node<H: Copy, T>(
5984 pending: &super::PendingTransferNodes<H, T>,
5985 node_idx: NodeIndex,
5986 ) -> Option<(H, &[T])> {
5987 pending
5988 .iter()
5989 .find_map(|(idx, handle, transfers)| (idx == node_idx).then_some((handle, transfers)))
5990 }
5991
5992 #[test]
5993 fn pending_transfer_nodes_set_tracks_each_node_once() {
5994 let mut pending = super::PendingTransferNodes::new(4);
5995
5996 assert!(pending.push_transfer(2, 10, 20));
5997 assert!(!pending.push_transfer(2, 11, 21));
5998
5999 assert!(pending.contains(2));
6000 let (handle, transfers) = pending_transfer_for_node(&pending, 2).unwrap();
6001 assert_eq!(handle, 11);
6002 assert_eq!(pending.indices, vec![2]);
6003 assert_eq!(transfers, &[20, 21]);
6004 assert_eq!(pending.iter().count(), 1);
6005 }
6006
6007 #[test]
6008 fn pending_transfer_nodes_remove_where_uses_swap_remove() {
6009 let mut pending = super::PendingTransferNodes::new(4);
6010
6011 pending.push_transfer(0, 10, 20);
6012 pending.push_transfer(1, 11, 21);
6013 pending.push_transfer(2, 12, 22);
6014
6015 pending.remove_where(|node_idx, _, _| node_idx == 1);
6016
6017 assert!(pending_transfer_for_node(&pending, 1).is_none());
6018 assert_eq!(pending.indices.len(), 2);
6019 assert!(pending.indices.contains(&0));
6020 assert!(pending.indices.contains(&2));
6021 assert_eq!(pending.iter().collect::<Vec<_>>().len(), 2);
6022 }
6023
6024 #[test]
6025 fn pending_transfer_nodes_remove_where_drops_stale_indices() {
6026 let mut pending = super::PendingTransferNodes::new(3);
6027
6028 pending.push_transfer(1, 11, 21);
6029 pending.entries[1] = None;
6030 pending.remove_where(|_, _, _| false);
6031
6032 assert!(pending.indices.is_empty());
6033 assert_eq!(pending.iter().count(), 0);
6034 }
6035
6036 #[test]
6037 fn node_indexed_scratch_tracks_each_node_once() {
6038 let mut scratch = super::NodeIndexedScratch::default();
6039
6040 scratch.push(2, 20);
6041 scratch.push(2, 21);
6042 scratch.push(0, 10);
6043
6044 assert_eq!(scratch.indices, vec![2, 0]);
6045 assert_eq!(scratch.get(2), &[20, 21]);
6046 assert_eq!(scratch.get(0), &[10]);
6047 assert_eq!(scratch.get(1), &[] as &[i32]);
6048 }
6049
6050 #[test]
6051 fn node_indexed_scratch_clear_resets_occupancy_and_reuses_entries() {
6052 let mut scratch = super::NodeIndexedScratch::default();
6053
6054 scratch.push(1, 10);
6055 scratch.clear();
6056
6057 assert!(scratch.indices.is_empty());
6058 assert_eq!(scratch.get(1), &[] as &[i32]);
6059
6060 scratch.push(1, 11);
6061 scratch.push(1, 12);
6062
6063 assert_eq!(scratch.indices, vec![1]);
6064 assert_eq!(scratch.get(1), &[11, 12]);
6065 }
6066
6067 #[test]
6068 fn node_indexed_scratch_resizes_for_high_indices() {
6069 let mut scratch = super::NodeIndexedScratch::default();
6070
6071 scratch.push(5, 50);
6072
6073 assert_eq!(scratch.indices, vec![5]);
6074 assert_eq!(scratch.get(5), &[50]);
6075 assert_eq!(scratch.get(4), &[] as &[i32]);
6076 }
6077
6078 #[test]
6079 fn pending_transfer_nodes_remove_where_keeps_partially_consumed_node() {
6080 let mut pending = super::PendingTransferNodes::new(2);
6081
6082 pending.push_transfer(1, 11, 20);
6083 pending.push_transfer(1, 11, 21);
6084
6085 pending.remove_where(|_, _, transfers| {
6086 transfers.retain(|&transfer| transfer != 20);
6087 transfers.is_empty()
6088 });
6089
6090 assert!(pending.contains(1));
6091 assert_eq!(pending_transfer_for_node(&pending, 1).unwrap().1, &[21]);
6092 assert!(!pending.is_empty());
6093
6094 pending.remove_where(|_, _, transfers| {
6095 transfers.retain(|&transfer| transfer != 21);
6096 transfers.is_empty()
6097 });
6098
6099 assert!(!pending.contains(1));
6100 assert!(pending_transfer_for_node(&pending, 1).is_none());
6101 assert!(pending.is_empty());
6102 }
6103
6104 #[test]
6105 fn consume_pending_buffer_transfers_removes_intersecting_ranges() {
6106 let consumed = BufferSubresourceRange { start: 4, end: 8 };
6107 let kept = BufferSubresourceRange { start: 8, end: 12 };
6108 let mut pending = vec![
6109 BufferQueueOwnershipTransfer {
6110 dst_queue_family_index: 0,
6111 range: consumed,
6112 src_queue_family_index: 1,
6113 src_queue_index: 0,
6114 },
6115 BufferQueueOwnershipTransfer {
6116 dst_queue_family_index: 0,
6117 range: kept,
6118 src_queue_family_index: 1,
6119 src_queue_index: 0,
6120 },
6121 ];
6122
6123 assert!(!super::consume_pending_buffer_transfers(
6124 &mut pending,
6125 consumed
6126 ));
6127
6128 assert_eq!(pending.len(), 1);
6129 assert_eq!(pending[0].range, kept);
6130 }
6131
6132 #[test]
6133 fn consume_pending_image_transfers_removes_intersecting_ranges() {
6134 let consumed = color_subresource_range(0..1, 0..1);
6135 let kept = color_subresource_range(1..2, 0..1);
6136 let mut pending = vec![
6137 ImageQueueOwnershipTransfer {
6138 dst_queue_family_index: 0,
6139 layout: vk::ImageLayout::GENERAL,
6140 range: consumed,
6141 src_queue_family_index: 1,
6142 src_queue_index: 0,
6143 },
6144 ImageQueueOwnershipTransfer {
6145 dst_queue_family_index: 0,
6146 layout: vk::ImageLayout::GENERAL,
6147 range: kept,
6148 src_queue_family_index: 1,
6149 src_queue_index: 0,
6150 },
6151 ];
6152
6153 assert!(!super::consume_pending_image_transfers(
6154 &mut pending,
6155 consumed
6156 ));
6157
6158 assert_eq!(pending.len(), 1);
6159 assert!(super::image_subresource_range_eq(pending[0].range, kept));
6160 }
6161
6162 #[test]
6163 fn dependency_selection_schedules_inputs_to_first_target_access() {
6164 let access_index = CommandAccessIndex {
6165 cmds_by_node: vec![vec![0, 1], vec![1]],
6170 accessed_nodes_by_cmd: vec![vec![0], vec![0, 1]],
6171 };
6172 let mut schedule = Vec::new();
6173
6174 super::schedule_dependency_cmds_before_target_access(&access_index, 1, 1, &mut schedule);
6175
6176 assert_eq!(schedule, vec![0]);
6177 }
6178
6179 #[cfg(test)]
6180 fn sort_queue_ownership_release_groups(groups: &mut [super::QueueOwnershipReleaseGroup]) {
6181 for group in groups.iter_mut() {
6182 group
6183 .buffers
6184 .sort_unstable_by_key(|(buffer, range)| (buffer.as_raw(), range.start, range.end));
6185
6186 group.images.sort_unstable_by_key(|(image, layout, range)| {
6187 (
6188 image.as_raw(),
6189 layout.as_raw(),
6190 range.aspect_mask.as_raw(),
6191 range.base_array_layer,
6192 range.layer_count,
6193 range.base_mip_level,
6194 range.level_count,
6195 )
6196 });
6197 }
6198
6199 groups.sort_unstable_by_key(|group| (group.src_queue_family_index, group.src_queue_index));
6200 }
6201
6202 #[cfg(test)]
6203 fn sort_image_subresource_sync_infos(
6204 subresources: &mut [crate::driver::image::ImageSubresourceSyncInfo],
6205 ) {
6206 subresources.sort_unstable_by_key(|subresource| {
6207 (
6208 subresource.range.aspect_mask.as_raw(),
6209 subresource.range.base_array_layer,
6210 subresource.range.layer_count,
6211 subresource.range.base_mip_level,
6212 subresource.range.level_count,
6213 )
6214 });
6215 }
6216
6217 #[derive(Debug)]
6218 struct TestDevice {
6219 _guard: MutexGuard<'static, ()>,
6220 device: ManuallyDrop<Device>,
6221 }
6222
6223 impl Drop for TestDevice {
6224 fn drop(&mut self) {
6225 unsafe {
6228 ManuallyDrop::drop(&mut self.device);
6229 }
6230 }
6231 }
6232
6233 impl Deref for TestDevice {
6234 type Target = Device;
6235
6236 fn deref(&self) -> &Self::Target {
6237 &self.device
6238 }
6239 }
6240
6241 fn test_device_lock() -> &'static Mutex<()> {
6242 static LOCK: OnceLock<Mutex<()>> = OnceLock::new();
6243
6244 LOCK.get_or_init(|| Mutex::new(()))
6245 }
6246
6247 fn assert_no_invalid_attachment_stage_access_pairs(dep: &SubpassDependency) {
6248 let dst_invalid_color_stages = dep.dst_stage_mask
6249 & (vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS
6250 | vk::PipelineStageFlags::LATE_FRAGMENT_TESTS
6251 | vk::PipelineStageFlags::FRAGMENT_SHADER);
6252 assert!(
6253 !dep.dst_access_mask
6254 .contains(vk::AccessFlags::COLOR_ATTACHMENT_READ)
6255 || dst_invalid_color_stages.is_empty(),
6256 "COLOR_ATTACHMENT_READ must not be paired with unsupported destination stages: {dep:?}"
6257 );
6258
6259 let src_invalid_color_stages = dep.src_stage_mask
6260 & (vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS
6261 | vk::PipelineStageFlags::LATE_FRAGMENT_TESTS
6262 | vk::PipelineStageFlags::FRAGMENT_SHADER);
6263 assert!(
6264 !dep.src_access_mask
6265 .contains(vk::AccessFlags::COLOR_ATTACHMENT_READ)
6266 || src_invalid_color_stages.is_empty(),
6267 "COLOR_ATTACHMENT_READ must not be paired with unsupported source stages: {dep:?}"
6268 );
6269
6270 assert!(
6271 !(dep
6272 .src_access_mask
6273 .contains(vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ)
6274 && dep
6275 .src_stage_mask
6276 .contains(vk::PipelineStageFlags::FRAGMENT_SHADER)),
6277 "DEPTH_STENCIL_ATTACHMENT_READ must not be paired with FRAGMENT_SHADER in source stages: {dep:?}"
6278 );
6279 assert!(
6280 !(dep
6281 .dst_access_mask
6282 .contains(vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ)
6283 && dep
6284 .dst_stage_mask
6285 .contains(vk::PipelineStageFlags::FRAGMENT_SHADER)),
6286 "DEPTH_STENCIL_ATTACHMENT_READ must not be paired with FRAGMENT_SHADER in destination stages: {dep:?}"
6287 );
6288 }
6289
6290 fn assert_attachment_read_stage_mappings(dep: &SubpassDependency) {
6291 if dep
6292 .src_access_mask
6293 .contains(vk::AccessFlags::COLOR_ATTACHMENT_READ)
6294 {
6295 assert!(
6296 dep.src_stage_mask
6297 .contains(vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT),
6298 "COLOR_ATTACHMENT_READ source access should use COLOR_ATTACHMENT_OUTPUT: {dep:?}"
6299 );
6300 }
6301
6302 if dep
6303 .dst_access_mask
6304 .contains(vk::AccessFlags::COLOR_ATTACHMENT_READ)
6305 {
6306 assert!(
6307 dep.dst_stage_mask
6308 .contains(vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT),
6309 "COLOR_ATTACHMENT_READ destination access should use COLOR_ATTACHMENT_OUTPUT: {dep:?}"
6310 );
6311 }
6312
6313 let fragment_tests = vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS
6314 | vk::PipelineStageFlags::LATE_FRAGMENT_TESTS;
6315
6316 if dep
6317 .src_access_mask
6318 .contains(vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ)
6319 {
6320 assert!(
6321 dep.src_stage_mask.intersects(fragment_tests),
6322 "DEPTH_STENCIL_ATTACHMENT_READ source access should use fragment-test stages: {dep:?}"
6323 );
6324 }
6325
6326 if dep
6327 .dst_access_mask
6328 .contains(vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ)
6329 {
6330 assert!(
6331 dep.dst_stage_mask.intersects(fragment_tests),
6332 "DEPTH_STENCIL_ATTACHMENT_READ destination access should use fragment-test stages: {dep:?}"
6333 );
6334 }
6335 }
6336
6337 fn exec_with_buffer_access(access: AccessType) -> Execution {
6338 let mut exec = Execution::default();
6339 exec.accesses.push(
6340 0,
6341 SubresourceAccess {
6342 access,
6343 subresource: SubresourceRange::Buffer((0..16).into()),
6344 },
6345 );
6346
6347 exec
6348 }
6349
6350 fn subpass_dependencies_for_accesses(
6351 previous: AccessType,
6352 current: AccessType,
6353 ) -> Vec<SubpassDependency> {
6354 let pass = CommandData {
6355 execs: vec![
6356 exec_with_buffer_access(previous),
6357 exec_with_buffer_access(current),
6358 ],
6359
6360 #[cfg(debug_assertions)]
6361 name: None,
6362
6363 stream_scope_id: None,
6364 };
6365
6366 Submission::build_subpass_dependencies(&pass, &ExternalRenderPassAccessHistory::new(1))
6367 }
6368
6369 fn depth_attachment_exec(
6370 load: LoadOp<vk::ClearDepthStencilValue>,
6371 store: StoreOp,
6372 ) -> Execution {
6373 let mut exec = Execution::default();
6374 exec.attachments.depth_stencil = Some(DepthStencilAttachment {
6375 attachment: Attachment {
6376 array_layer_count: 1,
6377 aspect_mask: vk::ImageAspectFlags::DEPTH,
6378 base_array_layer: 0,
6379 base_mip_level: 0,
6380 format: vk::Format::D32_SFLOAT,
6381 mip_level_count: 1,
6382 sample_count: SampleCount::Type1,
6383 target: 0,
6384 },
6385 load,
6386 store,
6387 resolve: None,
6388 is_attachment: true,
6389 });
6390
6391 exec
6392 }
6393
6394 fn depth_attachment_dependencies(
6395 previous_load: LoadOp<vk::ClearDepthStencilValue>,
6396 previous_store: StoreOp,
6397 current_load: LoadOp<vk::ClearDepthStencilValue>,
6398 current_store: StoreOp,
6399 ) -> Vec<SubpassDependency> {
6400 let pass = CommandData {
6401 execs: vec![
6402 depth_attachment_exec(previous_load, previous_store),
6403 depth_attachment_exec(current_load, current_store),
6404 ],
6405
6406 #[cfg(debug_assertions)]
6407 name: None,
6408
6409 stream_scope_id: None,
6410 };
6411
6412 Submission::build_subpass_dependencies(&pass, &ExternalRenderPassAccessHistory::new(1))
6413 }
6414
6415 fn schedule_with_access_index(
6416 cmds: &[usize],
6417 cmds_by_node: &[&[usize]],
6418 accessed_nodes_by_cmd: &[&[usize]],
6419 ) -> Schedule {
6420 Schedule {
6421 access_index: CommandAccessIndex {
6422 cmds_by_node: cmds_by_node.iter().map(|cmds| cmds.to_vec()).collect(),
6423 accessed_nodes_by_cmd: accessed_nodes_by_cmd
6424 .iter()
6425 .map(|nodes| nodes.to_vec())
6426 .collect(),
6427 },
6428 cmds: cmds.to_vec(),
6429 ..Default::default()
6430 }
6431 }
6432
6433 #[test]
6434 fn image_execution_discard_only_when_previous_access_is_nothing() {
6435 assert!(super::image_execution_discard_contents(AccessType::Nothing));
6436 assert!(!super::image_execution_discard_contents(
6437 AccessType::TransferRead
6438 ));
6439 assert!(!super::image_execution_discard_contents(
6440 AccessType::TransferWrite
6441 ));
6442 assert!(!super::image_execution_discard_contents(
6443 AccessType::ColorAttachmentReadWrite
6444 ));
6445 }
6446
6447 #[test]
6448 fn image_layout_transition_discard_keeps_attachment_write_policy() {
6449 assert!(super::image_layout_transition_discard_contents(
6450 AccessType::Nothing,
6451 AccessType::TransferWrite,
6452 ));
6453 assert!(super::image_layout_transition_discard_contents(
6454 AccessType::TransferRead,
6455 AccessType::TransferWrite,
6456 ));
6457 assert!(!super::image_layout_transition_discard_contents(
6458 AccessType::TransferWrite,
6459 AccessType::ColorAttachmentReadWrite,
6460 ));
6461 }
6462
6463 fn command_with_accesses(accesses: &[(usize, AccessType)]) -> CommandData {
6464 let mut exec = Execution::default();
6465
6466 for &(node_idx, access) in accesses {
6467 exec.accesses.push(
6468 node_idx,
6469 SubresourceAccess {
6470 access,
6471 subresource: SubresourceRange::Buffer(BufferSubresourceRange {
6472 start: 0,
6473 end: 1,
6474 }),
6475 },
6476 );
6477 }
6478
6479 CommandData {
6480 execs: vec![exec],
6481
6482 #[cfg(debug_assertions)]
6483 name: None,
6484
6485 stream_scope_id: None,
6486 }
6487 }
6488
6489 #[test]
6490 fn command_access_index_includes_read_and_write_accesses() {
6491 let cmds = vec![
6492 command_with_accesses(&[(0, AccessType::TransferRead)]),
6493 command_with_accesses(&[(1, AccessType::TransferWrite)]),
6494 command_with_accesses(&[(1, AccessType::TransferRead)]),
6495 command_with_accesses(&[(1, AccessType::TransferWrite)]),
6496 ];
6497 let mut access_index = CommandAccessIndex::default();
6498
6499 access_index.update_from_cmds(&cmds, 2);
6500
6501 assert_eq!(access_index.cmds_by_node[0], vec![0]);
6502 assert_eq!(access_index.cmds_by_node[1], vec![1, 2, 3]);
6503 assert_eq!(access_index.accessed_nodes_by_cmd[0], vec![0]);
6504 assert_eq!(access_index.accessed_nodes_by_cmd[1], vec![1]);
6505 assert_eq!(access_index.accessed_nodes_by_cmd[2], vec![1]);
6506 assert_eq!(access_index.accessed_nodes_by_cmd[3], vec![1]);
6507 }
6508
6509 #[test]
6510 fn command_access_index_dedupes_accesses_per_command_and_resets_between_commands() {
6511 let cmds = vec![
6512 command_with_accesses(&[
6513 (0, AccessType::TransferRead),
6514 (0, AccessType::TransferWrite),
6515 (1, AccessType::TransferRead),
6516 (1, AccessType::TransferWrite),
6517 ]),
6518 command_with_accesses(&[(0, AccessType::TransferRead), (1, AccessType::TransferRead)]),
6519 ];
6520 let mut access_index = CommandAccessIndex::default();
6521
6522 access_index.update_from_cmds(&cmds, 2);
6523
6524 assert_eq!(access_index.cmds_by_node[0], vec![0, 1]);
6525 assert_eq!(access_index.cmds_by_node[1], vec![0, 1]);
6526 assert_eq!(access_index.accessed_nodes_by_cmd[0], vec![0, 1]);
6527 assert_eq!(access_index.accessed_nodes_by_cmd[1], vec![0, 1]);
6528 }
6529
6530 #[test]
6531 fn dependency_selection_dedupes_repeated_read_dependencies() {
6532 let access_index = CommandAccessIndex {
6533 cmds_by_node: vec![vec![0, 1], vec![1]],
6538 accessed_nodes_by_cmd: vec![vec![0], vec![0, 0, 1]],
6539 };
6540 let mut schedule = Vec::new();
6541
6542 super::schedule_dependency_cmds_before_target_access(&access_index, 1, 1, &mut schedule);
6543
6544 assert_eq!(schedule, vec![0]);
6545 }
6546
6547 #[test]
6548 fn reorder_scheduled_cmds_preserves_hazards_from_command_access_index_update() {
6549 let cmds = vec![
6550 command_with_accesses(&[(0, AccessType::TransferRead)]),
6551 command_with_accesses(&[(1, AccessType::TransferWrite)]),
6552 command_with_accesses(&[(1, AccessType::TransferRead)]),
6553 command_with_accesses(&[(1, AccessType::TransferWrite)]),
6554 command_with_accesses(&[(0, AccessType::TransferRead)]),
6555 ];
6556 let mut access_index = CommandAccessIndex::default();
6557 access_index.update_from_cmds(&cmds, 2);
6558 let mut schedule = Schedule {
6559 access_index,
6560 cmds: (0..cmds.len()).collect(),
6561 ..Default::default()
6562 };
6563
6564 schedule.reorder_cmds(cmds.len());
6565
6566 let position = |cmd_idx| {
6567 schedule
6568 .cmds
6569 .iter()
6570 .position(|&scheduled_cmd_idx| scheduled_cmd_idx == cmd_idx)
6571 .expect("command was not scheduled")
6572 };
6573 assert!(position(1) < position(2), "write-read hazard crossed");
6574 assert!(position(2) < position(3), "read-write hazard crossed");
6575 }
6576
6577 #[test]
6578 fn reorder_scheduled_cmds_matches_original_seed_choice() {
6579 let mut schedule = schedule_with_access_index(
6580 &[0, 1, 2, 3],
6581 &[&[0, 1], &[1, 2], &[1, 3]],
6582 &[&[0], &[0, 1, 2], &[1], &[2]],
6583 );
6584
6585 schedule.reorder_cmds(4);
6586
6587 assert_eq!(schedule.cmds, vec![0, 1, 2, 3]);
6588 }
6589
6590 #[test]
6591 fn queue_ownership_release_groups_group_by_source_queue() {
6592 use super::{ImageQueueOwnershipTransfer, image_subresource_range_eq};
6593
6594 let mut submission = Submission::new(Graph::new());
6595 let image = vk::Image::null();
6596
6597 let range_a = vk::ImageSubresourceRange {
6598 aspect_mask: vk::ImageAspectFlags::COLOR,
6599 base_array_layer: 0,
6600 layer_count: 1,
6601 base_mip_level: 0,
6602 level_count: 1,
6603 };
6604 let range_b = vk::ImageSubresourceRange {
6605 aspect_mask: vk::ImageAspectFlags::COLOR,
6606 base_array_layer: 1,
6607 layer_count: 1,
6608 base_mip_level: 0,
6609 level_count: 1,
6610 };
6611
6612 let pending_image_transfer_nodes = submission
6613 .pending_image_transfer_nodes
6614 .get_or_insert_with(|| super::PendingTransferNodes::new(1));
6615 pending_image_transfer_nodes.push_transfer(
6616 0,
6617 image,
6618 ImageQueueOwnershipTransfer {
6619 src_queue_family_index: 1,
6620 src_queue_index: 2,
6621 dst_queue_family_index: 3,
6622 layout: vk::ImageLayout::GENERAL,
6623 range: range_a,
6624 },
6625 );
6626 pending_image_transfer_nodes.push_transfer(
6627 0,
6628 image,
6629 ImageQueueOwnershipTransfer {
6630 src_queue_family_index: 1,
6631 src_queue_index: 2,
6632 dst_queue_family_index: 3,
6633 layout: vk::ImageLayout::GENERAL,
6634 range: range_b,
6635 },
6636 );
6637 pending_image_transfer_nodes.push_transfer(
6638 0,
6639 image,
6640 ImageQueueOwnershipTransfer {
6641 src_queue_family_index: 4,
6642 src_queue_index: 5,
6643 dst_queue_family_index: 3,
6644 layout: vk::ImageLayout::GENERAL,
6645 range: range_a,
6646 },
6647 );
6648
6649 let groups = submission.collect_queue_ownership_release_groups();
6650 let mut groups = groups.into_vec();
6651 sort_queue_ownership_release_groups(&mut groups);
6652
6653 assert_eq!(groups.len(), 2);
6654 assert_eq!(groups[0].images.len(), 2);
6655 assert_eq!(groups[1].images.len(), 1);
6656 assert_eq!(groups[0].images[0].0, image);
6657 assert!(image_subresource_range_eq(groups[0].images[0].2, range_a));
6658 }
6659
6660 #[test]
6661 fn barrier_transfer_ranges_only_marks_overlapping_ranges() {
6662 use super::{image_barrier_transfer_ranges, image_subresource_range_eq};
6663
6664 let range_a = color_subresource_range(0..1, 0..1);
6665 let range_b = color_subresource_range(1..2, 0..1);
6666 let transfers = [ImageQueueOwnershipTransfer {
6667 src_queue_family_index: 1,
6668 src_queue_index: 2,
6669 dst_queue_family_index: 3,
6670 layout: vk::ImageLayout::GENERAL,
6671 range: range_a,
6672 }];
6673
6674 let ranges = image_barrier_transfer_ranges(&transfers, color_subresource_range(0..2, 0..1))
6675 .collect::<Vec<_>>();
6676
6677 assert_eq!(ranges.len(), 2);
6678 assert!(image_subresource_range_eq(ranges[0].0, range_a));
6679 assert_eq!(
6680 ranges[0].1.map(|transfer| (
6681 transfer.src_queue_family_index,
6682 transfer.src_queue_index,
6683 transfer.dst_queue_family_index,
6684 )),
6685 Some((1, 2, 3))
6686 );
6687 assert!(image_subresource_range_eq(ranges[1].0, range_b));
6688 assert!(ranges[1].1.is_none());
6689 }
6690
6691 #[test]
6692 #[ignore = "requires Vulkan device"]
6693 fn track_pending_transfers_only_collects_touched_subresources() -> Result<(), DriverError> {
6694 let device = test_device()?;
6695 let mut graph = Graph::new();
6696 let image = graph.bind_resource(Image::create(
6697 &device,
6698 ImageInfo::image_2d_array(1, 1, 2, vk::Format::R8_UINT, vk::ImageUsageFlags::SAMPLED),
6699 )?);
6700 let range_a = color_subresource_range(0..1, 0..1);
6701 let range_b = color_subresource_range(1..2, 0..1);
6702 let image_handle = graph.resource(image).handle;
6703
6704 {
6705 let image_resource = graph.resource(image);
6706 image_resource.set_sharing_ranges(SharingMode::Exclusive(Some((1, 0))), &[range_a]);
6707 image_resource.set_sharing_ranges(SharingMode::Exclusive(Some((2, 0))), &[range_b]);
6708
6709 image_resource
6710 .swap_access(AccessType::TransferRead, range_a)
6711 .for_each(drop);
6712 image_resource
6713 .swap_access(AccessType::TransferRead, range_b)
6714 .for_each(drop);
6715 }
6716
6717 graph
6718 .begin_cmd()
6719 .debug_name("touch first layer only")
6720 .subresource_access(image, range_a, AccessType::TransferWrite)
6721 .record_cmd(|_| {})
6722 .end_cmd();
6723
6724 let mut submission = graph.finalize();
6725 submission.track_pending_transfers(
6726 &Schedule {
6727 cmds: vec![0],
6728 ..Default::default()
6729 },
6730 3,
6731 );
6732
6733 let (handle, transfers) = pending_transfer_for_node(
6734 submission
6735 .pending_image_transfer_nodes
6736 .as_ref()
6737 .expect("missing pending transfer nodes"),
6738 image.index(),
6739 )
6740 .expect("missing pending transfer for touched subresource");
6741 assert_eq!(handle, image_handle);
6742 assert_eq!(
6743 submission
6744 .pending_image_transfer_nodes
6745 .as_ref()
6746 .expect("missing pending transfer nodes")
6747 .indices,
6748 vec![image.index()]
6749 );
6750 let mut transfers = transfers.to_vec();
6751 sort_pending_image_transfers(&mut transfers);
6752
6753 assert_eq!(transfers.len(), 1);
6754 assert!(super::image_subresource_range_eq(
6755 transfers[0].range,
6756 range_a
6757 ));
6758 let ranges = &submission.exclusive_image_ranges[&image.index()];
6759 let mut ranges = ranges.clone();
6760 sort_image_subresource_ranges(&mut ranges);
6761 assert_eq!(ranges.len(), 1);
6762 assert!(super::image_subresource_range_eq(ranges[0], range_a));
6763
6764 Ok(())
6765 }
6766
6767 #[test]
6768 #[ignore = "requires Vulkan device"]
6769 fn track_pending_transfers_only_collects_touched_buffer_ranges() -> Result<(), DriverError> {
6770 let device = test_device()?;
6771 let mut graph = Graph::new();
6772 let buffer = graph.bind_resource(Buffer::create(
6773 &device,
6774 BufferInfo::device_mem(16, vk::BufferUsageFlags::TRANSFER_DST),
6775 )?);
6776 let range_a = BufferSubresourceRange { start: 0, end: 8 };
6777 let range_b = BufferSubresourceRange { start: 8, end: 16 };
6778 let buffer_handle = graph.resource(buffer).handle;
6779
6780 {
6781 let buffer_resource = graph.resource(buffer);
6782 buffer_resource.set_sharing_ranges(SharingMode::Exclusive(Some((1, 0))), &[range_a]);
6783 buffer_resource.set_sharing_ranges(SharingMode::Exclusive(Some((2, 0))), &[range_b]);
6784
6785 buffer_resource
6786 .swap_access(AccessType::TransferRead, range_a)
6787 .for_each(drop);
6788 buffer_resource
6789 .swap_access(AccessType::TransferRead, range_b)
6790 .for_each(drop);
6791 }
6792
6793 graph
6794 .begin_cmd()
6795 .debug_name("touch first buffer range only")
6796 .subresource_access(buffer, range_a, AccessType::TransferWrite)
6797 .record_cmd(|_| {})
6798 .end_cmd();
6799
6800 let mut submission = graph.finalize();
6801 submission.track_pending_transfers(
6802 &Schedule {
6803 cmds: vec![0],
6804 ..Default::default()
6805 },
6806 3,
6807 );
6808
6809 let (handle, transfers) = pending_transfer_for_node(
6810 submission
6811 .pending_buffer_transfer_nodes
6812 .as_ref()
6813 .expect("missing pending transfer nodes"),
6814 buffer.index(),
6815 )
6816 .expect("missing pending transfer for touched buffer range");
6817 assert_eq!(handle, buffer_handle);
6818 assert_eq!(
6819 submission
6820 .pending_buffer_transfer_nodes
6821 .as_ref()
6822 .expect("missing pending transfer nodes")
6823 .indices,
6824 vec![buffer.index()]
6825 );
6826 let mut transfers = transfers.to_vec();
6827 sort_pending_buffer_transfers(&mut transfers);
6828
6829 assert_eq!(transfers.len(), 1);
6830 assert!(pending_buffer_transfer_for_range(&transfers, range_a).is_some());
6831 assert!(pending_buffer_transfer_for_range(&transfers, range_b).is_none());
6832
6833 let ranges = &submission.exclusive_buffer_ranges[&buffer.index()];
6834 let mut ranges = ranges.clone();
6835 ranges.sort_unstable_by_key(|range| (range.start, range.end));
6836 assert_eq!(ranges, vec![range_a]);
6837
6838 Ok(())
6839 }
6840
6841 #[test]
6842 #[ignore = "requires Vulkan device"]
6843 fn track_pending_transfers_keeps_exclusive_owner_without_known_layout()
6844 -> Result<(), DriverError> {
6845 let device = test_device()?;
6846 let mut graph = Graph::new();
6847 let image = graph.bind_resource(Image::create(
6848 &device,
6849 ImageInfo::image_2d_array(1, 1, 2, vk::Format::R8_UINT, vk::ImageUsageFlags::SAMPLED),
6850 )?);
6851 let range_a = color_subresource_range(0..1, 0..1);
6852 let range_b = color_subresource_range(1..2, 0..1);
6853 let image_handle = graph.resource(image).handle;
6854
6855 {
6856 let image_resource = graph.resource(image);
6857 image_resource.set_sharing_ranges(SharingMode::Exclusive(Some((1, 0))), &[range_a]);
6858 image_resource.set_sharing_ranges(SharingMode::Exclusive(Some((2, 0))), &[range_b]);
6859 }
6860
6861 graph
6862 .begin_cmd()
6863 .debug_name("touch first layer only")
6864 .subresource_access(image, range_a, AccessType::TransferWrite)
6865 .record_cmd(|_| {})
6866 .end_cmd();
6867
6868 let mut submission = graph.finalize();
6869 submission.track_pending_transfers(
6870 &Schedule {
6871 cmds: vec![0],
6872 ..Default::default()
6873 },
6874 3,
6875 );
6876
6877 let (handle, transfers) = pending_transfer_for_node(
6878 submission
6879 .pending_image_transfer_nodes
6880 .as_ref()
6881 .expect("missing pending transfer nodes"),
6882 image.index(),
6883 )
6884 .expect("missing pending transfer for touched subresource");
6885 assert_eq!(handle, image_handle);
6886 assert_eq!(
6887 submission
6888 .pending_image_transfer_nodes
6889 .as_ref()
6890 .expect("missing pending transfer nodes")
6891 .indices,
6892 vec![image.index()]
6893 );
6894 let mut transfers = transfers.to_vec();
6895 sort_pending_image_transfers(&mut transfers);
6896
6897 assert_eq!(transfers.len(), 1);
6898 assert!(super::image_subresource_range_eq(
6899 transfers[0].range,
6900 range_a
6901 ));
6902 assert_eq!(transfers[0].layout, vk::ImageLayout::UNDEFINED);
6903
6904 let ranges = &submission.exclusive_image_ranges[&image.index()];
6905 let mut ranges = ranges.clone();
6906 sort_image_subresource_ranges(&mut ranges);
6907 assert_eq!(ranges.len(), 1);
6908 assert!(super::image_subresource_range_eq(ranges[0], range_a));
6909
6910 Ok(())
6911 }
6912
6913 #[test]
6914 #[ignore = "requires Vulkan device"]
6915 fn recorded_submission_attach_updates_only_touched_subresources() -> Result<(), DriverError> {
6916 let device = test_device()?;
6917 let mut graph = Graph::new();
6918 let image = graph.bind_resource(Image::create(
6919 &device,
6920 ImageInfo::image_2d_array(1, 1, 2, vk::Format::R8_UINT, vk::ImageUsageFlags::SAMPLED),
6921 )?);
6922 let range_a = color_subresource_range(0..1, 0..1);
6923 let range_b = color_subresource_range(1..2, 0..1);
6924
6925 {
6926 let image_resource = graph.resource(image);
6927 image_resource.set_sharing_ranges(SharingMode::Exclusive(Some((1, 0))), &[range_a]);
6928 image_resource.set_sharing_ranges(SharingMode::Exclusive(Some((2, 0))), &[range_b]);
6929
6930 image_resource
6931 .swap_access(AccessType::TransferRead, range_a)
6932 .for_each(drop);
6933 image_resource
6934 .swap_access(AccessType::TransferRead, range_b)
6935 .for_each(drop);
6936 }
6937
6938 let mut submission = graph.finalize();
6939 submission
6940 .exclusive_image_ranges
6941 .insert(image.index(), vec![range_a]);
6942
6943 let mut fence = Fence::create(&device, false)?;
6944 let cmd_buf = CommandBuffer::create(&device, CommandBufferInfo::new(3))?;
6945 let recorded = RecordedSubmission {
6946 cmd_buf,
6947 queue_ownership_release_waits: Vec::new(),
6948 state: Arc::new(Mutex::new(RecordedSubmissionState {
6949 submission,
6950 _releases: Vec::new(),
6951 })),
6952 };
6953
6954 recorded.attach(&mut fence, 7);
6955
6956 let state = recorded.state.lock().expect("poisoned recorded state");
6957 let sync_info = state.submission.graph.resource(image).sync_info();
6958 let mut subresources = sync_info.subresources.into_vec();
6959 sort_image_subresource_sync_infos(&mut subresources);
6960
6961 assert_eq!(subresources.len(), 2);
6962 assert_eq!(subresources[0].queue_family_index, Some(3));
6963 assert_eq!(subresources[1].queue_family_index, Some(2));
6964
6965 Ok(())
6966 }
6967
6968 #[test]
6969 #[ignore = "requires Vulkan device"]
6970 fn recorded_submission_attach_updates_only_touched_buffer_ranges() -> Result<(), DriverError> {
6971 let device = test_device()?;
6972 let mut graph = Graph::new();
6973 let buffer = graph.bind_resource(Buffer::create(
6974 &device,
6975 BufferInfo::device_mem(16, vk::BufferUsageFlags::TRANSFER_DST),
6976 )?);
6977 let range_a = BufferSubresourceRange { start: 0, end: 8 };
6978 let range_b = BufferSubresourceRange { start: 8, end: 16 };
6979
6980 {
6981 let buffer_resource = graph.resource(buffer);
6982 buffer_resource.set_sharing_ranges(SharingMode::Exclusive(Some((1, 0))), &[range_a]);
6983 buffer_resource.set_sharing_ranges(SharingMode::Exclusive(Some((2, 0))), &[range_b]);
6984
6985 buffer_resource
6986 .swap_access(AccessType::TransferRead, range_a)
6987 .for_each(drop);
6988 buffer_resource
6989 .swap_access(AccessType::TransferRead, range_b)
6990 .for_each(drop);
6991 }
6992
6993 let mut submission = graph.finalize();
6994 submission
6995 .exclusive_buffer_ranges
6996 .insert(buffer.index(), vec![range_a]);
6997
6998 let mut fence = Fence::create(&device, false)?;
6999 let cmd_buf = CommandBuffer::create(&device, CommandBufferInfo::new(3))?;
7000 let recorded = RecordedSubmission {
7001 cmd_buf,
7002 queue_ownership_release_waits: Vec::new(),
7003 state: Arc::new(Mutex::new(RecordedSubmissionState {
7004 submission,
7005 _releases: Vec::new(),
7006 })),
7007 };
7008
7009 recorded.attach(&mut fence, 7);
7010
7011 let state = recorded.state.lock().expect("poisoned recorded state");
7012 let sync_info = state.submission.graph.resource(buffer).sync_info();
7013 let mut ranges = sync_info.ranges.into_vec();
7014 ranges.sort_unstable_by_key(|range| (range.range.start, range.range.end));
7015
7016 assert_eq!(ranges.len(), 2);
7017 assert_eq!(ranges[0].queue_family_index, Some(3));
7018 assert_eq!(ranges[1].queue_family_index, Some(2));
7019
7020 Ok(())
7021 }
7022
7023 #[test]
7024 fn reorder_scheduled_cmds_keeps_disconnected_groups_deterministic() {
7025 let mut schedule = schedule_with_access_index(
7026 &[0, 1, 2, 3, 4],
7027 &[&[0, 1, 2], &[3, 4]],
7028 &[&[0], &[0], &[0], &[1], &[1]],
7029 );
7030
7031 schedule.reorder_cmds(5);
7032
7033 assert_eq!(schedule.cmds, vec![0, 1, 2, 3, 4]);
7034 }
7035
7036 #[test]
7037 fn reorder_scheduled_cmds_preserves_write_only_order() {
7038 let mut schedule = schedule_with_access_index(
7039 &[0, 1, 2, 3],
7040 &[&[0, 3], &[1, 2]],
7045 &[&[0], &[1], &[1], &[0]],
7046 );
7047
7048 schedule.reorder_cmds(4);
7049
7050 let cmd_1_position = schedule
7051 .cmds
7052 .iter()
7053 .position(|&cmd_idx| cmd_idx == 1)
7054 .expect("cmd 1 was not scheduled");
7055 let cmd_2_position = schedule
7056 .cmds
7057 .iter()
7058 .position(|&cmd_idx| cmd_idx == 2)
7059 .expect("cmd 2 was not scheduled");
7060
7061 assert!(
7062 cmd_1_position < cmd_2_position,
7063 "write-only commands were reordered: {:?}",
7064 schedule.cmds
7065 );
7066 }
7067
7068 #[test]
7069 fn reorder_scheduled_cmds_preserves_write_after_write_hazard() {
7070 fuzz::check_schedule_reordering(
7071 4,
7072 &[
7073 vec![
7074 fuzz::ResourceAccess {
7075 cmd_idx: 1,
7076 write: true,
7077 },
7078 fuzz::ResourceAccess {
7079 cmd_idx: 2,
7080 write: true,
7081 },
7082 ],
7083 vec![fuzz::ResourceAccess {
7084 cmd_idx: 0,
7085 write: false,
7086 }],
7087 vec![fuzz::ResourceAccess {
7088 cmd_idx: 3,
7089 write: false,
7090 }],
7091 ],
7092 );
7093 }
7094
7095 #[test]
7096 fn reorder_scheduled_cmds_preserves_write_then_read_hazard() {
7097 fuzz::check_schedule_reordering(
7098 4,
7099 &[
7100 vec![
7101 fuzz::ResourceAccess {
7102 cmd_idx: 1,
7103 write: true,
7104 },
7105 fuzz::ResourceAccess {
7106 cmd_idx: 2,
7107 write: false,
7108 },
7109 ],
7110 vec![fuzz::ResourceAccess {
7111 cmd_idx: 0,
7112 write: false,
7113 }],
7114 vec![fuzz::ResourceAccess {
7115 cmd_idx: 3,
7116 write: false,
7117 }],
7118 ],
7119 );
7120 }
7121
7122 #[test]
7123 fn reorder_scheduled_cmds_preserves_read_then_write_hazard() {
7124 fuzz::check_schedule_reordering(
7125 4,
7126 &[
7127 vec![
7128 fuzz::ResourceAccess {
7129 cmd_idx: 1,
7130 write: false,
7131 },
7132 fuzz::ResourceAccess {
7133 cmd_idx: 2,
7134 write: true,
7135 },
7136 ],
7137 vec![fuzz::ResourceAccess {
7138 cmd_idx: 0,
7139 write: false,
7140 }],
7141 vec![fuzz::ResourceAccess {
7142 cmd_idx: 3,
7143 write: false,
7144 }],
7145 ],
7146 );
7147 }
7148
7149 #[test]
7150 fn reorder_scheduled_cmds_allows_unrelated_moves_without_crossing_hazard() {
7151 fuzz::check_schedule_reordering(
7152 6,
7153 &[
7154 vec![
7155 fuzz::ResourceAccess {
7156 cmd_idx: 1,
7157 write: true,
7158 },
7159 fuzz::ResourceAccess {
7160 cmd_idx: 4,
7161 write: true,
7162 },
7163 ],
7164 vec![
7165 fuzz::ResourceAccess {
7166 cmd_idx: 0,
7167 write: false,
7168 },
7169 fuzz::ResourceAccess {
7170 cmd_idx: 2,
7171 write: false,
7172 },
7173 fuzz::ResourceAccess {
7174 cmd_idx: 5,
7175 write: false,
7176 },
7177 ],
7178 vec![fuzz::ResourceAccess {
7179 cmd_idx: 3,
7180 write: false,
7181 }],
7182 ],
7183 );
7184 }
7185
7186 #[test]
7187 fn record_selection_from_node_creates_node_variant() {
7188 let node = BufferNode::new(
7189 7,
7190 #[cfg(feature = "checked")]
7191 crate::GraphId(1),
7192 );
7193
7194 let selection = RecordSelection::from(node);
7195
7196 match selection {
7197 RecordSelection::Node(AnyNode::Buffer(actual)) => assert_eq!(actual.index(), 7),
7198 _ => panic!("expected RecordSelection::Node(Buffer)"),
7199 }
7200 }
7201
7202 #[test]
7203 fn record_selection_nodes_preserves_slice() {
7204 let lhs = AnyNode::from(BufferNode::new(
7205 1,
7206 #[cfg(feature = "checked")]
7207 crate::GraphId(1),
7208 ));
7209 let rhs = AnyNode::from(BufferNode::new(
7210 2,
7211 #[cfg(feature = "checked")]
7212 crate::GraphId(1),
7213 ));
7214 let nodes = [lhs, rhs];
7215
7216 match RecordSelection::nodes(&nodes) {
7217 RecordSelection::Nodes(actual) => assert_eq!(actual.len(), 2),
7218 _ => panic!("expected RecordSelection::Nodes"),
7219 }
7220 }
7221
7222 #[test]
7223 fn legacy_submit_accepts_all_commands_and_none_wait_masks() {
7224 let waits = [
7225 SemaphoreSubmitInfo {
7226 semaphore: vk::Semaphore::null(),
7227 stage_mask: vk::PipelineStageFlags2::ALL_COMMANDS,
7228 value: 0,
7229 },
7230 SemaphoreSubmitInfo {
7231 semaphore: vk::Semaphore::null(),
7232 stage_mask: vk::PipelineStageFlags2::NONE,
7233 value: 0,
7234 },
7235 ];
7236 let signals = [SemaphoreSubmitInfo {
7237 semaphore: vk::Semaphore::null(),
7238 stage_mask: vk::PipelineStageFlags2::ALL_COMMANDS,
7239 value: 0,
7240 }];
7241
7242 assert!(check_queue_submit_args(&waits, &signals).is_ok());
7243 }
7244
7245 #[test]
7246 fn legacy_submit_rejects_precise_wait_stage_masks() {
7247 let waits = [SemaphoreSubmitInfo {
7248 semaphore: vk::Semaphore::null(),
7249 stage_mask: vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT,
7250 value: 0,
7251 }];
7252
7253 assert!(matches!(
7254 check_queue_submit_args(&waits, &[]),
7255 Err(DriverError::Unsupported)
7256 ));
7257 }
7258
7259 #[test]
7260 fn legacy_submit_rejects_timeline_values() {
7261 let waits = [SemaphoreSubmitInfo {
7262 semaphore: vk::Semaphore::null(),
7263 stage_mask: vk::PipelineStageFlags2::ALL_COMMANDS,
7264 value: 1,
7265 }];
7266
7267 assert!(matches!(
7268 check_queue_submit_args(&waits, &[]),
7269 Err(DriverError::Unsupported)
7270 ));
7271 }
7272
7273 fn test_device() -> Result<TestDevice, DriverError> {
7274 let guard = test_device_lock()
7275 .lock()
7276 .expect("poisoned test device lock");
7277 let device = Device::create(DeviceInfo::default())?;
7278
7279 Ok(TestDevice {
7280 _guard: guard,
7281 device: ManuallyDrop::new(device),
7282 })
7283 }
7284
7285 fn test_debug_device() -> Result<TestDevice, DriverError> {
7286 let guard = test_device_lock()
7287 .lock()
7288 .expect("poisoned test device lock");
7289 let device = Device::create(DeviceInfo::builder().debug(true).build())?;
7290
7291 Ok(TestDevice {
7292 _guard: guard,
7293 device: ManuallyDrop::new(device),
7294 })
7295 }
7296
7297 fn init_validation_test_logging() {
7298 static INIT: OnceLock<()> = OnceLock::new();
7299
7300 INIT.get_or_init(|| {
7301 unsafe {
7302 set_var("RUST_LOG", "trace");
7303 set_var("VK_GRAPH_SKIP_VALIDATION_PARK", "1");
7304 }
7305
7306 let _ = pretty_env_logger::try_init();
7307 });
7308 }
7309
7310 fn test_triangle_pipeline(device: &Device) -> Result<GraphicsPipeline, DriverError> {
7311 GraphicsPipeline::create(
7312 device,
7313 GraphicsPipelineInfo::default(),
7314 [
7315 glsl!(
7316 r#"
7317 #version 460 core
7318 #pragma shader_stage(vertex)
7319
7320 vec2 POSITIONS[3] = vec2[](
7321 vec2(-1.0, -1.0),
7322 vec2(3.0, -1.0),
7323 vec2(-1.0, 3.0)
7324 );
7325
7326 void main() {
7327 gl_Position = vec4(POSITIONS[gl_VertexIndex], 0.0, 1.0);
7328 }
7329 "#
7330 )
7331 .as_slice(),
7332 glsl!(
7333 r#"
7334 #version 460 core
7335 #pragma shader_stage(fragment)
7336
7337 layout(location = 0) out vec4 vk_Color;
7338
7339 void main() {
7340 vk_Color = vec4(1.0, 0.0, 0.0, 1.0);
7341 }
7342 "#
7343 )
7344 .as_slice(),
7345 ],
7346 )
7347 }
7348
7349 fn test_input_attachment_pipelines(
7350 device: &Device,
7351 ) -> Result<(GraphicsPipeline, GraphicsPipeline), DriverError> {
7352 let vertex = glsl!(
7353 r#"
7354 #version 460 core
7355 #pragma shader_stage(vertex)
7356
7357 vec2 POSITIONS[3] = vec2[](
7358 vec2(-1.0, -1.0),
7359 vec2(3.0, -1.0),
7360 vec2(-1.0, 3.0)
7361 );
7362
7363 void main() {
7364 gl_Position = vec4(POSITIONS[gl_VertexIndex], 0.0, 1.0);
7365 }
7366 "#
7367 );
7368 let pipeline_a = GraphicsPipeline::create(
7369 device,
7370 GraphicsPipelineInfo::default(),
7371 [
7372 vertex.as_slice(),
7373 glsl!(
7374 kind: frag,
7375 r#"
7376 #version 460 core
7377 #pragma shader_stage(fragment)
7378
7379 layout(location = 0) out vec4 color_out;
7380
7381 void main() {
7382 color_out = vec4(0.25, 0.5, 0.75, 1.0);
7383 }
7384 "#
7385 )
7386 .as_slice(),
7387 ],
7388 )?;
7389 let pipeline_b = GraphicsPipeline::create(
7390 device,
7391 GraphicsPipelineInfo::default(),
7392 [
7393 vertex.as_slice(),
7394 glsl!(
7395 kind: frag,
7396 r#"
7397 #version 460 core
7398 #pragma shader_stage(fragment)
7399
7400 layout(input_attachment_index = 0, binding = 0) uniform subpassInput color_in;
7401 layout(location = 0) out vec4 color_out;
7402
7403 void main() {
7404 color_out = subpassLoad(color_in);
7405 }
7406 "#
7407 )
7408 .as_slice(),
7409 ],
7410 )?;
7411
7412 Ok((pipeline_a, pipeline_b))
7413 }
7414
7415 #[test]
7416 #[ignore = "requires Vulkan device"]
7417 fn submission_record_all_consumes_single_pass_graph() -> Result<(), DriverError> {
7418 let device = test_device()?;
7419 let mut pool = HashPool::new(&device);
7420 let mut graph = Graph::new();
7421 let buffer = graph.bind_resource(Buffer::create(
7422 &device,
7423 BufferInfo::device_mem(16, vk::BufferUsageFlags::TRANSFER_DST),
7424 )?);
7425
7426 graph.fill_buffer(buffer, 0..16, 0xdead_beef);
7427
7428 let submission = graph.finalize();
7429 let mut cmd_buf = pool.resource(CommandBufferInfo::new(0))?;
7430
7431 cmd_buf.begin(
7432 &vk::CommandBufferBeginInfo::default()
7433 .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT),
7434 )?;
7435
7436 let recorded = submission.record(&mut pool, &mut cmd_buf, RecordSelection::All)?;
7437
7438 assert!(recorded.is_empty());
7439
7440 Ok(())
7441 }
7442
7443 #[test]
7444 #[ignore = "requires Vulkan device"]
7445 fn submission_record_nodes_consumes_requested_outputs() -> Result<(), DriverError> {
7446 let device = test_device()?;
7447 let mut pool = HashPool::new(&device);
7448 let mut graph = Graph::new();
7449 let lhs = graph.bind_resource(Buffer::create(
7450 &device,
7451 BufferInfo::device_mem(16, vk::BufferUsageFlags::TRANSFER_DST),
7452 )?);
7453 let rhs = graph.bind_resource(Buffer::create(
7454 &device,
7455 BufferInfo::device_mem(16, vk::BufferUsageFlags::TRANSFER_DST),
7456 )?);
7457
7458 graph.fill_buffer(lhs, 0..16, 1);
7459 graph.fill_buffer(rhs, 0..16, 2);
7460
7461 let nodes = [AnyNode::from(lhs), AnyNode::from(rhs)];
7462 let submission = graph.finalize();
7463 let mut cmd_buf = pool.resource(CommandBufferInfo::new(0))?;
7464
7465 cmd_buf.begin(
7466 &vk::CommandBufferBeginInfo::default()
7467 .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT),
7468 )?;
7469
7470 let recorded =
7471 submission.record(&mut pool, &mut cmd_buf, RecordSelection::nodes(&nodes))?;
7472
7473 assert!(recorded.is_empty());
7474
7475 Ok(())
7476 }
7477
7478 #[test]
7479 #[ignore = "requires Vulkan device"]
7480 fn submission_record_can_be_reused() -> Result<(), DriverError> {
7481 let device = test_device()?;
7482 let mut pool = HashPool::new(&device);
7483 let mut graph = Graph::new();
7484 let buffer = graph.bind_resource(Buffer::create(
7485 &device,
7486 BufferInfo::device_mem(16, vk::BufferUsageFlags::TRANSFER_DST),
7487 )?);
7488
7489 graph.fill_buffer(buffer, 0..16, 0xdead_beef);
7490
7491 let submission = graph.finalize();
7492 let mut cmd_buf = pool.resource(CommandBufferInfo::new(0))?;
7493 let mut fence = Fence::create(&device, false)?;
7494
7495 cmd_buf.begin(
7496 &vk::CommandBufferBeginInfo::default()
7497 .flags(vk::CommandBufferUsageFlags::SIMULTANEOUS_USE),
7498 )?;
7499
7500 let recorded = submission.record(&mut pool, &mut cmd_buf, RecordSelection::All)?;
7501 recorded.cmd_buf.end()?;
7502 let replay = recorded.finish()?;
7503 replay.attach(&mut fence, 0);
7504
7505 Ok(())
7506 }
7507
7508 #[test]
7509 #[ignore = "requires Vulkan device"]
7510 fn accel_struct_mixed_accesses_preserve_all_stage_bits() -> Result<(), DriverError> {
7511 let device = test_device()?;
7512 let mut pool = HashPool::new(&device);
7513 let mut graph = Graph::new();
7514 let accel_struct = graph.bind_resource(AccelerationStructure::create(
7515 &device,
7516 AccelerationStructureInfo::blas(1024),
7517 )?);
7518
7519 graph
7520 .begin_cmd()
7521 .debug_name("mixed accel struct accesses")
7522 .resource_access(accel_struct, AccessType::AccelerationStructureBuildRead)
7523 .resource_access(
7524 accel_struct,
7525 AccessType::RayTracingShaderReadAccelerationStructure,
7526 )
7527 .record_cmd(|_| {});
7528
7529 let submission = graph.finalize();
7530 let mut cmd_buf = pool.resource(CommandBufferInfo::new(0))?;
7531
7532 cmd_buf.begin(
7533 &vk::CommandBufferBeginInfo::default()
7534 .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT),
7535 )?;
7536
7537 let recording = submission.record(&mut pool, &mut cmd_buf, RecordSelection::All)?;
7538 let sync_info = recording.resource(accel_struct).sync_info();
7539
7540 assert!(
7541 sync_info
7542 .stage_mask
7543 .contains(vk::PipelineStageFlags::ACCELERATION_STRUCTURE_BUILD_KHR),
7544 "sync info should preserve build-read stage bits"
7545 );
7546 assert!(
7547 sync_info
7548 .stage_mask
7549 .contains(vk::PipelineStageFlags::RAY_TRACING_SHADER_KHR),
7550 "sync info should preserve ray-tracing-read stage bits"
7551 );
7552 assert_eq!(
7553 sync_info.access_mask,
7554 vk::AccessFlags::ACCELERATION_STRUCTURE_READ_KHR,
7555 "mixed read-only accesses should stay read-only"
7556 );
7557
7558 Ok(())
7559 }
7560
7561 #[test]
7562 #[ignore = "requires Vulkan validation layers; inspect validation output"]
7563 fn submission_external_subpass_dependency_validation_repro() -> Result<(), DriverError> {
7564 init_validation_test_logging();
7565
7566 let device = test_debug_device()?;
7567 let mut pool = HashPool::new(&device);
7568 let pipeline = test_triangle_pipeline(&device)?;
7569 let mut graph = Graph::new();
7570 let image = graph.bind_resource(Image::create(
7571 &device,
7572 ImageInfo::image_2d(
7573 4,
7574 4,
7575 vk::Format::R8G8B8A8_UNORM,
7576 vk::ImageUsageFlags::TRANSFER_DST | vk::ImageUsageFlags::COLOR_ATTACHMENT,
7577 ),
7578 )?);
7579
7580 graph.clear_color_image(image, [0.0, 0.0, 0.0, 1.0]);
7583 graph
7584 .begin_cmd()
7585 .debug_name("validation repro render pass")
7586 .bind_pipeline(&pipeline)
7587 .color_attachment_image(0, image, LoadOp::Load, StoreOp::Store)
7588 .record_cmd(|cmd| {
7589 cmd.draw(3, 1, 0, 0);
7590 });
7591
7592 let submission = graph.finalize();
7593 let mut cmd_buf = pool.resource(CommandBufferInfo::new(0))?;
7594
7595 cmd_buf.begin(
7596 &vk::CommandBufferBeginInfo::default()
7597 .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT),
7598 )?;
7599
7600 let recorded = submission.record(&mut pool, &mut cmd_buf, RecordSelection::All)?;
7601 recorded.cmd_buf.end()?;
7602
7603 let mut fence = Fence::create(&device, false)?;
7604 let mut recorded = recorded.finish()?;
7605
7606 recorded.queue_submit(&mut fence, 0, QueueSubmitInfo::QUEUE_SUBMIT)?;
7607 fence.wait_signaled()?;
7608
7609 Ok(())
7610 }
7611
7612 #[test]
7613 #[ignore = "requires Vulkan device"]
7614 fn external_subpass_dependency_targets_first_subpass_consumer() -> Result<(), DriverError> {
7615 let device = test_device()?;
7616 let pipeline = test_triangle_pipeline(&device)?;
7617 let mut graph = Graph::new();
7618 let image = graph.bind_resource(Image::create(
7619 &device,
7620 ImageInfo::image_2d(
7621 4,
7622 4,
7623 vk::Format::R8G8B8A8_UNORM,
7624 vk::ImageUsageFlags::TRANSFER_DST | vk::ImageUsageFlags::COLOR_ATTACHMENT,
7625 ),
7626 )?);
7627
7628 graph.clear_color_image(image, [0.0, 0.0, 0.0, 1.0]);
7629 graph
7630 .begin_cmd()
7631 .debug_name("dependency inspection render pass")
7632 .bind_pipeline(&pipeline)
7633 .color_attachment_image(0, image, LoadOp::Load, StoreOp::Store)
7634 .record_cmd(|cmd| {
7635 cmd.draw(3, 1, 0, 0);
7636 });
7637
7638 let submission = graph.finalize();
7639 let mut external_access_history =
7640 ExternalRenderPassAccessHistory::new(submission.graph.resources.len());
7641 external_access_history.record_cmd(&submission.graph.cmds[0]);
7642
7643 let dependencies = Submission::build_subpass_dependencies(
7644 &submission.graph.cmds[1],
7645 &external_access_history,
7646 );
7647 let dep = dependencies
7648 .iter()
7649 .find(|dep| dep.src_subpass == vk::SUBPASS_EXTERNAL && dep.dst_subpass == 0)
7650 .expect("missing external -> first subpass dependency");
7651
7652 assert_eq!(
7653 dep.dst_stage_mask,
7654 vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT,
7655 "destination stage should describe the first subpass consumer"
7656 );
7657 assert_eq!(
7658 dep.dst_access_mask,
7659 vk::AccessFlags::COLOR_ATTACHMENT_READ | vk::AccessFlags::COLOR_ATTACHMENT_WRITE,
7660 "destination access should describe the first subpass attachment access"
7661 );
7662
7663 Ok(())
7664 }
7665
7666 #[test]
7667 #[ignore = "requires Vulkan device"]
7668 fn color_input_attachment_dependencies_use_fragment_shader_input_reads()
7669 -> Result<(), DriverError> {
7670 let device = test_device()?;
7671 let (pipeline_a, pipeline_b) = test_input_attachment_pipelines(&device)?;
7672 let mut graph = Graph::new();
7673 let image = graph.bind_resource(Image::create(
7674 &device,
7675 ImageInfo::image_2d(
7676 4,
7677 4,
7678 vk::Format::R8G8B8A8_UNORM,
7679 vk::ImageUsageFlags::COLOR_ATTACHMENT
7680 | vk::ImageUsageFlags::INPUT_ATTACHMENT
7681 | vk::ImageUsageFlags::TRANSFER_DST,
7682 ),
7683 )?);
7684
7685 graph
7686 .begin_cmd()
7687 .debug_name("input attachment writer")
7688 .bind_pipeline(&pipeline_a)
7689 .color_attachment_image(0, image, LoadOp::CLEAR_BLACK_ALPHA_ZERO, StoreOp::Store)
7690 .record_cmd(|cmd| {
7691 cmd.draw(3, 1, 0, 0);
7692 });
7693 graph
7694 .begin_cmd()
7695 .debug_name("input attachment reader")
7696 .bind_pipeline(&pipeline_b)
7697 .color_attachment_image(0, image, LoadOp::DontCare, StoreOp::Store)
7698 .record_cmd(|cmd| {
7699 cmd.draw(3, 1, 0, 0);
7700 });
7701
7702 let mut submission = graph.finalize();
7703 let mut schedule = vec![0, 1];
7704 submission.merge_scheduled_cmds(&mut schedule);
7705
7706 let dependencies = Submission::build_subpass_dependencies(
7707 &submission.graph.cmds[0],
7708 &ExternalRenderPassAccessHistory::new(submission.graph.resources.len()),
7709 );
7710 let dep = dependencies
7711 .iter()
7712 .find(|dep| dep.src_subpass == 0 && dep.dst_subpass == 1)
7713 .expect("missing subpass dependency for input attachment read");
7714
7715 assert!(
7716 dep.src_stage_mask
7717 .contains(vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT),
7718 "source stage should include color attachment output"
7719 );
7720 assert!(
7721 dep.src_access_mask
7722 .contains(vk::AccessFlags::COLOR_ATTACHMENT_WRITE),
7723 "source access should include color attachment write"
7724 );
7725 assert!(
7726 dep.dst_stage_mask
7727 .contains(vk::PipelineStageFlags::FRAGMENT_SHADER),
7728 "destination stage should include fragment shader input attachment reads"
7729 );
7730 assert!(
7731 dep.dst_access_mask
7732 .contains(vk::AccessFlags::INPUT_ATTACHMENT_READ),
7733 "destination access should include input attachment reads"
7734 );
7735
7736 Ok(())
7737 }
7738
7739 #[test]
7740 #[ignore = "requires Vulkan device"]
7741 fn color_attachment_load_dependencies_avoid_invalid_stage_access_pairs()
7742 -> Result<(), DriverError> {
7743 let device = test_device()?;
7744 let pipeline = test_triangle_pipeline(&device)?;
7745 let mut graph = Graph::new();
7746 let image = graph.bind_resource(Image::create(
7747 &device,
7748 ImageInfo::image_2d(
7749 4,
7750 4,
7751 vk::Format::R8G8B8A8_UNORM,
7752 vk::ImageUsageFlags::COLOR_ATTACHMENT,
7753 ),
7754 )?);
7755
7756 graph
7757 .begin_cmd()
7758 .debug_name("color attachment writer")
7759 .bind_pipeline(&pipeline)
7760 .color_attachment_image(0, image, LoadOp::CLEAR_BLACK_ALPHA_ZERO, StoreOp::Store)
7761 .record_cmd(|cmd| {
7762 cmd.draw(3, 1, 0, 0);
7763 });
7764 graph
7765 .begin_cmd()
7766 .debug_name("color attachment reader")
7767 .bind_pipeline(&pipeline)
7768 .color_attachment_image(0, image, LoadOp::Load, StoreOp::Store)
7769 .record_cmd(|cmd| {
7770 cmd.draw(3, 1, 0, 0);
7771 });
7772
7773 let mut submission = graph.finalize();
7774 let mut schedule = vec![0, 1];
7775 submission.merge_scheduled_cmds(&mut schedule);
7776
7777 let dependencies = Submission::build_subpass_dependencies(
7778 &submission.graph.cmds[0],
7779 &ExternalRenderPassAccessHistory::new(submission.graph.resources.len()),
7780 );
7781 let dep = dependencies
7782 .iter()
7783 .find(|dep| dep.src_subpass == 0 && dep.dst_subpass == 1)
7784 .expect("missing subpass dependency for color attachment load");
7785
7786 assert!(
7787 dep.src_access_mask
7788 .contains(vk::AccessFlags::COLOR_ATTACHMENT_WRITE),
7789 "source access should include color attachment writes"
7790 );
7791 assert!(
7792 dep.dst_access_mask
7793 .contains(vk::AccessFlags::COLOR_ATTACHMENT_READ),
7794 "destination access should include color attachment reads"
7795 );
7796 assert_no_invalid_attachment_stage_access_pairs(dep);
7797 assert_attachment_read_stage_mappings(dep);
7798
7799 Ok(())
7800 }
7801
7802 #[test]
7803 #[ignore = "requires Vulkan device"]
7804 fn color_attachment_read_dependencies_avoid_invalid_stage_access_pairs()
7805 -> Result<(), DriverError> {
7806 let device = test_device()?;
7807 let pipeline = test_triangle_pipeline(&device)?;
7808 let mut graph = Graph::new();
7809 let image = graph.bind_resource(Image::create(
7810 &device,
7811 ImageInfo::image_2d(
7812 4,
7813 4,
7814 vk::Format::R8G8B8A8_UNORM,
7815 vk::ImageUsageFlags::COLOR_ATTACHMENT,
7816 ),
7817 )?);
7818
7819 graph
7820 .begin_cmd()
7821 .debug_name("color attachment first reader")
7822 .bind_pipeline(&pipeline)
7823 .color_attachment_image(0, image, LoadOp::Load, StoreOp::DontCare)
7824 .record_cmd(|cmd| {
7825 cmd.draw(3, 1, 0, 0);
7826 });
7827 graph
7828 .begin_cmd()
7829 .debug_name("color attachment second reader")
7830 .bind_pipeline(&pipeline)
7831 .color_attachment_image(0, image, LoadOp::Load, StoreOp::DontCare)
7832 .record_cmd(|cmd| {
7833 cmd.draw(3, 1, 0, 0);
7834 });
7835
7836 let mut submission = graph.finalize();
7837 let mut schedule = vec![0, 1];
7838 submission.merge_scheduled_cmds(&mut schedule);
7839
7840 let dependencies = Submission::build_subpass_dependencies(
7841 &submission.graph.cmds[0],
7842 &ExternalRenderPassAccessHistory::new(submission.graph.resources.len()),
7843 );
7844 let dep = dependencies
7845 .iter()
7846 .find(|dep| dep.src_subpass == 0 && dep.dst_subpass == 1)
7847 .expect("missing subpass dependency for color attachment read");
7848
7849 assert!(
7850 dep.src_access_mask
7851 .contains(vk::AccessFlags::COLOR_ATTACHMENT_READ),
7852 "source access should include color attachment reads"
7853 );
7854 assert!(
7855 dep.dst_access_mask
7856 .contains(vk::AccessFlags::COLOR_ATTACHMENT_READ),
7857 "destination access should include color attachment reads"
7858 );
7859 assert_no_invalid_attachment_stage_access_pairs(dep);
7860 assert_attachment_read_stage_mappings(dep);
7861
7862 Ok(())
7863 }
7864
7865 #[test]
7866 #[ignore = "requires Vulkan device"]
7867 fn color_attachment_read_to_write_dependencies_avoid_invalid_stage_access_pairs()
7868 -> Result<(), DriverError> {
7869 let device = test_device()?;
7870 let pipeline = test_triangle_pipeline(&device)?;
7871 let mut graph = Graph::new();
7872 let image = graph.bind_resource(Image::create(
7873 &device,
7874 ImageInfo::image_2d(
7875 4,
7876 4,
7877 vk::Format::R8G8B8A8_UNORM,
7878 vk::ImageUsageFlags::COLOR_ATTACHMENT,
7879 ),
7880 )?);
7881
7882 graph
7883 .begin_cmd()
7884 .debug_name("color attachment reader")
7885 .bind_pipeline(&pipeline)
7886 .color_attachment_image(0, image, LoadOp::Load, StoreOp::DontCare)
7887 .record_cmd(|cmd| {
7888 cmd.draw(3, 1, 0, 0);
7889 });
7890 graph
7891 .begin_cmd()
7892 .debug_name("color attachment writer")
7893 .bind_pipeline(&pipeline)
7894 .color_attachment_image(0, image, LoadOp::CLEAR_BLACK_ALPHA_ZERO, StoreOp::Store)
7895 .record_cmd(|cmd| {
7896 cmd.draw(3, 1, 0, 0);
7897 });
7898
7899 let mut submission = graph.finalize();
7900 let mut schedule = vec![0, 1];
7901 submission.merge_scheduled_cmds(&mut schedule);
7902
7903 let dependencies = Submission::build_subpass_dependencies(
7904 &submission.graph.cmds[0],
7905 &ExternalRenderPassAccessHistory::new(submission.graph.resources.len()),
7906 );
7907 let dep = dependencies
7908 .iter()
7909 .find(|dep| dep.src_subpass == 0 && dep.dst_subpass == 1)
7910 .expect("missing subpass dependency for color attachment read to write");
7911
7912 assert!(
7913 dep.src_access_mask
7914 .contains(vk::AccessFlags::COLOR_ATTACHMENT_READ),
7915 "source access should include color attachment reads"
7916 );
7917 assert!(
7918 dep.dst_access_mask
7919 .contains(vk::AccessFlags::COLOR_ATTACHMENT_WRITE),
7920 "destination access should include color attachment writes"
7921 );
7922 assert_no_invalid_attachment_stage_access_pairs(dep);
7923 assert_attachment_read_stage_mappings(dep);
7924
7925 Ok(())
7926 }
7927
7928 #[test]
7929 #[ignore = "requires Vulkan device"]
7930 fn depth_attachment_load_dependencies_avoid_invalid_stage_access_pairs()
7931 -> Result<(), DriverError> {
7932 let device = test_device()?;
7933 let pipeline = test_triangle_pipeline(&device)?;
7934 let mut graph = Graph::new();
7935 let image = graph.bind_resource(Image::create(
7936 &device,
7937 ImageInfo::image_2d(
7938 4,
7939 4,
7940 vk::Format::D32_SFLOAT,
7941 vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT,
7942 ),
7943 )?);
7944
7945 graph
7946 .begin_cmd()
7947 .debug_name("depth attachment first reader")
7948 .bind_pipeline(&pipeline)
7949 .depth_stencil_attachment_image(image, LoadOp::Load, StoreOp::Store)
7950 .record_cmd(|cmd| {
7951 cmd.draw(3, 1, 0, 0);
7952 });
7953 graph
7954 .begin_cmd()
7955 .debug_name("depth attachment second reader")
7956 .bind_pipeline(&pipeline)
7957 .depth_stencil_attachment_image(image, LoadOp::Load, StoreOp::Store)
7958 .record_cmd(|cmd| {
7959 cmd.draw(3, 1, 0, 0);
7960 });
7961
7962 let mut submission = graph.finalize();
7963 let mut schedule = vec![0, 1];
7964 submission.merge_scheduled_cmds(&mut schedule);
7965
7966 let dependencies = Submission::build_subpass_dependencies(
7967 &submission.graph.cmds[0],
7968 &ExternalRenderPassAccessHistory::new(submission.graph.resources.len()),
7969 );
7970 let dep = dependencies
7971 .iter()
7972 .find(|dep| dep.src_subpass == 0 && dep.dst_subpass == 1)
7973 .expect("missing subpass dependency for depth attachment load");
7974
7975 assert!(
7976 dep.src_access_mask
7977 .contains(vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ),
7978 "source access should include depth/stencil attachment reads"
7979 );
7980 assert!(
7981 dep.dst_access_mask
7982 .contains(vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ),
7983 "destination access should include depth/stencil attachment reads"
7984 );
7985 assert_no_invalid_attachment_stage_access_pairs(dep);
7986 assert_attachment_read_stage_mappings(dep);
7987
7988 Ok(())
7989 }
7990
7991 #[test]
7992 fn depth_attachment_read_to_write_dependency_includes_late_read_stage() {
7993 let dependencies = depth_attachment_dependencies(
7994 LoadOp::Load,
7995 StoreOp::DontCare,
7996 LoadOp::CLEAR_ONE_STENCIL_ZERO,
7997 StoreOp::Store,
7998 );
7999 let dep = dependencies
8000 .iter()
8001 .find(|dep| dep.src_subpass == 0 && dep.dst_subpass == 1)
8002 .expect("missing subpass dependency for depth attachment read to write");
8003
8004 assert!(
8005 dep.src_access_mask
8006 .contains(vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ),
8007 "source access should include depth/stencil attachment reads"
8008 );
8009 assert!(
8010 dep.dst_access_mask
8011 .contains(vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE),
8012 "destination access should include depth/stencil attachment writes"
8013 );
8014 assert!(
8015 dep.src_stage_mask
8016 .contains(vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS),
8017 "source stage should include early fragment tests"
8018 );
8019 assert!(
8020 dep.src_stage_mask
8021 .contains(vk::PipelineStageFlags::LATE_FRAGMENT_TESTS),
8022 "source stage should include late fragment tests"
8023 );
8024 }
8025
8026 #[test]
8027 fn depth_attachment_write_to_write_dependency_uses_write_access() {
8028 let dependencies = depth_attachment_dependencies(
8029 LoadOp::CLEAR_ONE_STENCIL_ZERO,
8030 StoreOp::Store,
8031 LoadOp::CLEAR_ONE_STENCIL_ZERO,
8032 StoreOp::Store,
8033 );
8034 let dep = dependencies
8035 .iter()
8036 .find(|dep| dep.src_subpass == 0 && dep.dst_subpass == 1)
8037 .expect("missing subpass dependency for depth attachment write to write");
8038
8039 assert!(
8040 dep.src_access_mask
8041 .contains(vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE),
8042 "source access should include depth/stencil attachment writes"
8043 );
8044 assert!(
8045 !dep.src_access_mask
8046 .contains(vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ),
8047 "source access should not include depth/stencil attachment reads"
8048 );
8049 assert!(
8050 dep.dst_access_mask
8051 .contains(vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE),
8052 "destination access should include depth/stencil attachment writes"
8053 );
8054 }
8055
8056 #[test]
8057 fn subpass_stage_mask_clamps_non_graphics_stages() {
8058 assert_eq!(
8059 Submission::subpass_stage_mask(vk::PipelineStageFlags::RAY_TRACING_SHADER_KHR),
8060 vk::PipelineStageFlags::ALL_GRAPHICS,
8061 );
8062 assert_eq!(
8063 Submission::subpass_stage_mask(
8064 vk::PipelineStageFlags::FRAGMENT_SHADER
8065 | vk::PipelineStageFlags::RAY_TRACING_SHADER_KHR,
8066 ),
8067 vk::PipelineStageFlags::FRAGMENT_SHADER,
8068 );
8069 }
8070
8071 #[test]
8072 fn subpass_dependency_matches_all_graphics_source_stage() {
8073 let dependencies = subpass_dependencies_for_accesses(
8074 AccessType::AnyShaderWrite,
8075 AccessType::FragmentShaderReadOther,
8076 );
8077 let dep = dependencies
8078 .iter()
8079 .find(|dep| dep.src_subpass == 0 && dep.dst_subpass == 1)
8080 .expect("missing subpass dependency for ALL_GRAPHICS source stage");
8081
8082 assert!(
8083 dep.src_stage_mask
8084 .contains(vk::PipelineStageFlags::ALL_GRAPHICS),
8085 "source stage should include ALL_GRAPHICS"
8086 );
8087 assert!(
8088 dep.src_access_mask.contains(vk::AccessFlags::SHADER_WRITE),
8089 "source access should include shader writes"
8090 );
8091 assert!(
8092 dep.dst_stage_mask
8093 .contains(vk::PipelineStageFlags::FRAGMENT_SHADER),
8094 "destination stage should include fragment shader"
8095 );
8096 assert!(
8097 dep.dst_access_mask.contains(vk::AccessFlags::SHADER_READ),
8098 "destination access should include shader reads"
8099 );
8100 }
8101
8102 #[test]
8103 fn subpass_dependency_matches_all_graphics_destination_stage() {
8104 let dependencies = subpass_dependencies_for_accesses(
8105 AccessType::FragmentShaderWrite,
8106 AccessType::AnyShaderReadOther,
8107 );
8108 let dep = dependencies
8109 .iter()
8110 .find(|dep| dep.src_subpass == 0 && dep.dst_subpass == 1)
8111 .expect("missing subpass dependency for ALL_GRAPHICS destination stage");
8112
8113 assert!(
8114 dep.src_stage_mask
8115 .contains(vk::PipelineStageFlags::FRAGMENT_SHADER),
8116 "source stage should include fragment shader"
8117 );
8118 assert!(
8119 dep.src_access_mask.contains(vk::AccessFlags::SHADER_WRITE),
8120 "source access should include shader writes"
8121 );
8122 assert!(
8123 dep.dst_stage_mask
8124 .contains(vk::PipelineStageFlags::ALL_GRAPHICS),
8125 "destination stage should include ALL_GRAPHICS"
8126 );
8127 assert!(
8128 dep.dst_access_mask.contains(vk::AccessFlags::SHADER_READ),
8129 "destination access should include shader reads"
8130 );
8131 }
8132
8133 #[test]
8134 fn record_subpass_dependency_preserves_dst_access_for_unmatched_stages() {
8135 let mut dependencies = std::collections::BTreeMap::new();
8136 let mut current = PipelineStageAccessFlags {
8137 stage_flags: vk::PipelineStageFlags::VERTEX_SHADER
8138 | vk::PipelineStageFlags::FRAGMENT_SHADER,
8139 access_flags: vk::AccessFlags::SHADER_READ,
8140 };
8141
8142 assert!(!Submission::record_subpass_dependency(
8143 &mut dependencies,
8144 0,
8145 2,
8146 PipelineStageAccessFlags {
8147 stage_flags: vk::PipelineStageFlags::VERTEX_SHADER,
8148 access_flags: vk::AccessFlags::SHADER_READ,
8149 },
8150 current.stage_flags,
8151 &mut current,
8152 ));
8153 assert!(Submission::record_subpass_dependency(
8154 &mut dependencies,
8155 1,
8156 2,
8157 PipelineStageAccessFlags {
8158 stage_flags: vk::PipelineStageFlags::FRAGMENT_SHADER,
8159 access_flags: vk::AccessFlags::SHADER_READ,
8160 },
8161 current.stage_flags,
8162 &mut current,
8163 ));
8164
8165 let dep = dependencies
8166 .get(&(1, 2))
8167 .expect("missing dependency for later matched stage");
8168 assert!(
8169 dep.dst_access_mask.contains(vk::AccessFlags::SHADER_READ),
8170 "later matched stage should retain destination access mask"
8171 );
8172 }
8173
8174 #[test]
8175 fn record_subpass_dependency_ignores_non_overlapping_stage() {
8176 let mut dependencies = std::collections::BTreeMap::new();
8177 let mut current = PipelineStageAccessFlags {
8178 stage_flags: vk::PipelineStageFlags::FRAGMENT_SHADER,
8179 access_flags: vk::AccessFlags::SHADER_READ,
8180 };
8181
8182 assert!(!Submission::record_subpass_dependency(
8183 &mut dependencies,
8184 0,
8185 1,
8186 PipelineStageAccessFlags {
8187 stage_flags: vk::PipelineStageFlags::VERTEX_SHADER,
8188 access_flags: vk::AccessFlags::SHADER_WRITE,
8189 },
8190 current.stage_flags,
8191 &mut current,
8192 ));
8193
8194 assert!(dependencies.is_empty());
8195 assert_eq!(current.stage_flags, vk::PipelineStageFlags::FRAGMENT_SHADER);
8196 assert_eq!(current.access_flags, vk::AccessFlags::SHADER_READ);
8197 }
8198
8199 #[test]
8200 fn build_subpass_dependencies_includes_later_access_stage_bits() {
8201 let mut exec = Execution::default();
8202
8203 exec.accesses.push(
8204 0,
8205 SubresourceAccess {
8206 access: AccessType::IndexBuffer,
8207 subresource: SubresourceRange::Buffer((0..16).into()),
8208 },
8209 );
8210 exec.accesses.push(
8211 0,
8212 SubresourceAccess {
8213 access: AccessType::FragmentShaderReadOther,
8214 subresource: SubresourceRange::Buffer((0..16).into()),
8215 },
8216 );
8217
8218 let pass = CommandData {
8219 execs: vec![exec],
8220
8221 #[cfg(debug_assertions)]
8222 name: None,
8223
8224 stream_scope_id: None,
8225 };
8226 let dependencies =
8227 Submission::build_subpass_dependencies(&pass, &ExternalRenderPassAccessHistory::new(1));
8228 let dep = dependencies
8229 .iter()
8230 .find(|dep| dep.src_subpass == vk::SUBPASS_EXTERNAL && dep.dst_subpass == 0)
8231 .expect("missing external dependency for mixed access slice");
8232
8233 assert!(
8234 dep.dst_stage_mask
8235 .contains(vk::PipelineStageFlags::VERTEX_INPUT),
8236 "first access stage should be preserved"
8237 );
8238 assert!(
8239 dep.dst_stage_mask
8240 .contains(vk::PipelineStageFlags::FRAGMENT_SHADER),
8241 "later access stages should also contribute"
8242 );
8243 }
8244
8245 #[test]
8246 fn accel_struct_canonical_accesses_preserves_mixed_slice_accesses() {
8247 let accesses = [
8248 SubresourceAccess {
8249 access: AccessType::AccelerationStructureBuildRead,
8250 subresource: SubresourceRange::AccelerationStructure,
8251 },
8252 SubresourceAccess {
8253 access: AccessType::RayTracingShaderReadAccelerationStructure,
8254 subresource: SubresourceRange::AccelerationStructure,
8255 },
8256 ];
8257
8258 let mut scratch = Vec::new();
8259 assert_eq!(
8260 Submission::accel_struct_canonical_accesses(&accesses, &mut scratch),
8261 &[
8262 AccessType::AccelerationStructureBuildRead,
8263 AccessType::RayTracingShaderReadAccelerationStructure,
8264 ],
8265 "mixed acceleration-structure slices should preserve all accesses for next-state tracking"
8266 );
8267 }
8268}