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