vulkano_taskgraph/graph/
compile.rs

1// FIXME: host read barriers
2
3use super::{
4    Attachments, BarrierIndex, ExecutableTaskGraph, Instruction, NodeIndex, NodeInner,
5    RenderPassIndex, ResourceAccess, ResourceAccesses, SemaphoreIndex, Submission, TaskGraph,
6};
7use crate::{linear_map::LinearMap, resource::Flight, Id, QueueFamilyType};
8use ash::vk;
9use smallvec::{smallvec, SmallVec};
10use std::{cell::RefCell, cmp, error::Error, fmt, ops::Range, sync::Arc};
11use vulkano::{
12    device::{Device, DeviceOwned, Queue, QueueFlags},
13    format::Format,
14    image::{sampler::ComponentMapping, Image, ImageLayout, ImageUsage},
15    render_pass::{
16        AttachmentDescription, AttachmentLoadOp, AttachmentReference, AttachmentStoreOp,
17        Framebuffer, RenderPass, RenderPassCreateInfo, SubpassDependency, SubpassDescription,
18    },
19    swapchain::Swapchain,
20    sync::{semaphore::Semaphore, AccessFlags, DependencyFlags, PipelineStages},
21    Validated, VulkanError,
22};
23
24impl<W: ?Sized> TaskGraph<W> {
25    /// Compiles the task graph into an executable form.
26    ///
27    /// # Safety
28    ///
29    /// - There must be no conflicting device accesses in task nodes with no path between them.
30    /// - There must be no accesses that are incompatible with the queue family type of the task
31    ///   node.
32    /// - There must be no accesses that are unsupported by the device.
33    ///
34    /// # Panics
35    ///
36    /// - Panics if `compile_info.queues` is empty.
37    /// - Panics if the device of any queue in `compile_info.queues` or
38    ///   `compile_info.present_queue` is not the same as that of `self`.
39    /// - Panics if `compile_info.queues` contains duplicate queue families.
40    /// - Panics if `compile_info.present_queue` is `None` and the task graph uses any swapchains.
41    ///
42    /// # Errors
43    ///
44    /// In order to get a successful compilation, the graph must satisfy the following conditions:
45    /// - It must be [weakly connected]: every node must be able to reach every other node when
46    ///   disregarding the direction of the edges.
47    /// - It must have no [directed cycles]: if you were to walk starting from any node following
48    ///   the direction of the edges, there must be no way to end up at the node you started at.
49    ///
50    /// [weakly connected]: https://en.wikipedia.org/wiki/Connectivity_(graph_theory)#Connected_vertices_and_graphs
51    /// [directed cycles]: https://en.wikipedia.org/wiki/Cycle_(graph_theory)#Directed_circuit_and_directed_cycle
52    pub unsafe fn compile(
53        mut self,
54        compile_info: &CompileInfo<'_>,
55    ) -> Result<ExecutableTaskGraph<W>, CompileError<W>> {
56        let &CompileInfo {
57            queues,
58            present_queue,
59            flight_id,
60            _ne: _,
61        } = compile_info;
62
63        assert_ne!(queues.len(), 0, "expected to be given at least one queue");
64
65        let device = &self.device().clone();
66
67        for queue in queues {
68            assert_eq!(queue.device(), device);
69            assert_eq!(
70                queues
71                    .iter()
72                    .filter(|q| q.queue_family_index() == queue.queue_family_index())
73                    .count(),
74                1,
75                "expected each queue in `compile_info.queues` to be from a unique queue family",
76            );
77        }
78
79        if let Some(present_queue) = &present_queue {
80            assert_eq!(present_queue.device(), device);
81        }
82
83        if !self.is_weakly_connected() {
84            return Err(CompileError::new(self, CompileErrorKind::Unconnected));
85        }
86
87        let topological_order = match self.topological_sort() {
88            Ok(topological_order) => topological_order,
89            Err(kind) => return Err(CompileError::new(self, kind)),
90        };
91        unsafe { self.dependency_levels(&topological_order) };
92        let queue_family_indices =
93            match unsafe { self.queue_family_indices(device, queues, &topological_order) } {
94                Ok(queue_family_indices) => queue_family_indices,
95                Err(kind) => return Err(CompileError::new(self, kind)),
96            };
97        let mut queues_by_queue_family_index: SmallVec<[_; 8]> =
98            smallvec![None; *queue_family_indices.iter().max().unwrap() as usize + 1];
99
100        for &queue in queues {
101            if let Some(x) =
102                queues_by_queue_family_index.get_mut(queue.queue_family_index() as usize)
103            {
104                *x = Some(queue);
105            }
106        }
107
108        let (
109            IntermediateRepresentationBuilder {
110                prev_accesses: last_accesses,
111                submissions,
112                nodes,
113                semaphore_count,
114                render_passes,
115                pre_present_queue_family_ownership_transfers,
116                ..
117            },
118            last_swapchain_accesses,
119        ) = match unsafe { self.lower(present_queue, &topological_order) } {
120            Ok(x) => x,
121            Err(kind) => return Err(CompileError::new(self, kind)),
122        };
123
124        let mut builder = FinalRepresentationBuilder::new(present_queue);
125        let mut prev_submission_end = 0;
126        let mut submission_index = 0;
127
128        while prev_submission_end < topological_order.len() {
129            let submission_state = &submissions[submission_index];
130            builder.initial_pipeline_barrier(&submission_state.initial_barriers);
131
132            for (i, &node_index) in
133                (prev_submission_end..).zip(&topological_order[prev_submission_end..])
134            {
135                let node = unsafe { self.nodes.node_unchecked_mut(node_index) };
136                let NodeInner::Task(task_node) = &mut node.inner else {
137                    unreachable!();
138                };
139                let queue_family_index = task_node.queue_family_index;
140                let node_state = &nodes[node_index as usize];
141
142                for &(swapchain_id, stage_mask) in &node_state.wait_acquire {
143                    builder.wait_acquire(swapchain_id, stage_mask);
144                }
145
146                for &semaphore_index in &node_state.wait_semaphores {
147                    builder.wait_semaphore(semaphore_index);
148                }
149
150                builder.pipeline_barrier(&node_state.start_barriers);
151
152                if let Some(subpass) = node_state.subpass {
153                    let render_pass_state = &render_passes[subpass.render_pass_index];
154
155                    if node_index == render_pass_state.first_node_index {
156                        builder.begin_render_pass(subpass.render_pass_index);
157                    }
158
159                    builder.next_subpass(subpass.subpass_index);
160
161                    if !node_state.clear_attachments.is_empty() {
162                        builder.clear_attachments(
163                            node_index,
164                            subpass.render_pass_index,
165                            &node_state.clear_attachments,
166                        );
167                    }
168                }
169
170                builder.execute_task(node_index);
171
172                if let Some(subpass) = node_state.subpass {
173                    let render_pass_state = &render_passes[subpass.render_pass_index];
174
175                    if node_index == render_pass_state.last_node_index {
176                        builder.end_render_pass();
177                    }
178                }
179
180                builder.pipeline_barrier(&node_state.end_barriers);
181
182                for &semaphore_index in &node_state.signal_semaphores {
183                    builder.signal_semaphore(semaphore_index);
184                }
185
186                for &(swapchain_id, stage_mask) in &node_state.signal_pre_present {
187                    builder.signal_pre_present(swapchain_id, stage_mask);
188                }
189
190                for &(swapchain_id, stage_mask) in &node_state.signal_present {
191                    builder.signal_present(swapchain_id, stage_mask);
192                }
193
194                let should_submit = if let Some(&next_node_index) = topological_order.get(i + 1) {
195                    let next_node = unsafe { self.nodes.node_unchecked(next_node_index) };
196                    let NodeInner::Task(next_task_node) = &next_node.inner else {
197                        unreachable!();
198                    };
199
200                    next_task_node.queue_family_index != queue_family_index
201                } else {
202                    true
203                };
204
205                if builder.should_flush_submit || should_submit {
206                    builder.flush_submit();
207                }
208
209                if should_submit {
210                    let queue = queues_by_queue_family_index[queue_family_index as usize]
211                        .unwrap()
212                        .clone();
213                    builder.submit(queue);
214                    prev_submission_end = i + 1;
215                    submission_index += 1;
216                    break;
217                }
218            }
219        }
220
221        if !pre_present_queue_family_ownership_transfers.is_empty() {
222            for swapchain_id in pre_present_queue_family_ownership_transfers {
223                builder.pre_present_acquire_queue_family_ownership(&last_accesses, swapchain_id);
224            }
225
226            builder.flush_submit();
227            builder.submit(present_queue.unwrap().clone());
228        }
229
230        let render_passes = match render_passes
231            .into_iter()
232            .map(|render_pass_state| create_render_pass(&self.resources, render_pass_state))
233            .collect::<Result<Vec<_>, _>>()
234        {
235            Ok(render_passes) => render_passes,
236            Err(kind) => return Err(CompileError::new(self, kind)),
237        };
238
239        if !render_passes.is_empty() {
240            for (id, node) in self.nodes.nodes_mut() {
241                let NodeInner::Task(task_node) = &mut node.inner else {
242                    unreachable!();
243                };
244
245                if let Some(subpass) = nodes[id.index() as usize].subpass {
246                    let render_pass = &render_passes[subpass.render_pass_index].render_pass;
247                    task_node.subpass = Some(
248                        vulkano::render_pass::Subpass::from(
249                            render_pass.clone(),
250                            subpass.subpass_index as u32,
251                        )
252                        .unwrap(),
253                    );
254                }
255            }
256        }
257
258        let semaphores = match (0..semaphore_count)
259            .map(|_| {
260                // SAFETY: The parameters are valid.
261                unsafe { Semaphore::new_unchecked(device.clone(), Default::default()) }
262                    .map(Arc::new)
263            })
264            .collect::<Result<_, _>>()
265        {
266            Ok(semaphores) => semaphores,
267            Err(err) => return Err(CompileError::new(self, CompileErrorKind::VulkanError(err))),
268        };
269
270        let swapchains = last_swapchain_accesses.keys().copied().collect();
271
272        Ok(ExecutableTaskGraph {
273            graph: self,
274            flight_id,
275            instructions: builder.instructions,
276            submissions: builder.submissions,
277            barriers: builder.barriers,
278            render_passes: RefCell::new(render_passes),
279            clear_attachments: builder.clear_attachments,
280            semaphores: RefCell::new(semaphores),
281            swapchains,
282            present_queue: present_queue.cloned(),
283            last_accesses,
284        })
285    }
286
287    /// Performs [depth-first search] on the equivalent undirected graph to determine if every node
288    /// is visited, meaning the undirected graph is [connected]. If it is, then the directed graph
289    /// is [weakly connected]. This property is required because otherwise it could happen that we
290    /// end up with a submit that is in no way synchronized with the host.
291    ///
292    /// [depth-first search]: https://en.wikipedia.org/wiki/Depth-first_search
293    /// [connected]: https://en.wikipedia.org/wiki/Connectivity_(graph_theory)#Connected_vertices_and_graphs
294    /// [weakly connected]: https://en.wikipedia.org/wiki/Connectivity_(graph_theory)#Connected_vertices_and_graphs
295    fn is_weakly_connected(&self) -> bool {
296        unsafe fn dfs<W: ?Sized>(
297            graph: &TaskGraph<W>,
298            node_index: NodeIndex,
299            visited: &mut [bool],
300            visited_count: &mut u32,
301        ) {
302            let is_visited = &mut visited[node_index as usize];
303
304            if *is_visited {
305                return;
306            }
307
308            *is_visited = true;
309            *visited_count += 1;
310
311            let node = unsafe { graph.nodes.node_unchecked(node_index) };
312
313            for &node_index in node.in_edges.iter().chain(&node.out_edges) {
314                unsafe { dfs(graph, node_index, visited, visited_count) };
315            }
316        }
317
318        let mut visited = vec![false; self.nodes.capacity() as usize];
319        let mut visited_count = 0;
320
321        if let Some((id, _)) = self.nodes.nodes().next() {
322            unsafe { dfs(self, id.index(), &mut visited, &mut visited_count) };
323        }
324
325        visited_count == self.nodes.len()
326    }
327
328    /// Performs [topological sort using depth-first search]. Returns a vector of node indices in
329    /// topological order.
330    ///
331    /// [topological sort using depth-first search]: https://en.wikipedia.org/wiki/Topological_sorting#Depth-first_search
332    fn topological_sort(&self) -> Result<Vec<NodeIndex>, CompileErrorKind> {
333        type NodeState = u8;
334
335        const VISITED_BIT: NodeState = 1 << 0;
336        const ON_STACK_BIT: NodeState = 1 << 1;
337
338        unsafe fn dfs<W: ?Sized>(
339            graph: &TaskGraph<W>,
340            node_index: NodeIndex,
341            state: &mut [NodeState],
342            output: &mut [NodeIndex],
343            mut output_index: u32,
344        ) -> Result<u32, CompileErrorKind> {
345            let node_state = &mut state[node_index as usize];
346
347            if *node_state == VISITED_BIT {
348                return Ok(output_index);
349            }
350
351            if *node_state == ON_STACK_BIT {
352                return Err(CompileErrorKind::Cycle);
353            }
354
355            *node_state = ON_STACK_BIT;
356
357            let node = unsafe { graph.nodes.node_unchecked(node_index) };
358
359            for &node_index in &node.out_edges {
360                output_index = unsafe { dfs(graph, node_index, state, output, output_index) }?;
361            }
362
363            state[node_index as usize] = VISITED_BIT;
364            output[output_index as usize] = node_index;
365
366            Ok(output_index.wrapping_sub(1))
367        }
368
369        let mut state = vec![0; self.nodes.capacity() as usize];
370        let mut output = vec![0; self.nodes.len() as usize];
371        let mut output_index = self.nodes.len().wrapping_sub(1);
372
373        for (id, _) in self.nodes.nodes() {
374            output_index = unsafe { dfs(self, id.index(), &mut state, &mut output, output_index) }?;
375        }
376
377        debug_assert_eq!(output_index, u32::MAX);
378
379        Ok(output)
380    }
381
382    /// Performs [longest path search] to assign the dependency level index to each task node.
383    /// Tasks in the same dependency level don't depend on eachother and can therefore be run in
384    /// parallel. Returns a vector of dependency levels in topological order indexed by the node's
385    /// dependency level index.
386    ///
387    /// [longest path search]: https://en.wikipedia.org/wiki/Longest_path_problem#Acyclic_graphs
388    unsafe fn dependency_levels(&mut self, topological_order: &[NodeIndex]) -> Vec<Vec<NodeIndex>> {
389        let mut distances = vec![0; self.nodes.capacity() as usize];
390        let mut max_level = 0;
391
392        for &node_index in topological_order {
393            let node = unsafe { self.nodes.node_unchecked(node_index) };
394
395            for &out_node_index in &node.out_edges {
396                let new_distance = distances[node_index as usize] + 1;
397
398                if distances[out_node_index as usize] < new_distance {
399                    distances[out_node_index as usize] = new_distance;
400                    max_level = cmp::max(max_level, new_distance);
401                }
402            }
403        }
404
405        let mut levels = vec![Vec::new(); max_level as usize + 1];
406
407        for (id, node) in self.nodes.nodes_mut() {
408            let NodeInner::Task(task_node) = &mut node.inner else {
409                unreachable!();
410            };
411
412            let level_index = distances[id.index() as usize];
413            levels[level_index as usize].push(id.index());
414            task_node.dependency_level_index = level_index;
415        }
416
417        levels
418    }
419
420    /// Assigns a queue family index to each task node. Returns a vector of the used queue family
421    /// indices in topological order.
422    unsafe fn queue_family_indices(
423        &mut self,
424        device: &Device,
425        queues: &[&Arc<Queue>],
426        topological_order: &[NodeIndex],
427    ) -> Result<SmallVec<[u32; 3]>, CompileErrorKind> {
428        let queue_family_properties = device.physical_device().queue_family_properties();
429        let graphics_queue_family_index = queues
430            .iter()
431            .find(|q| {
432                queue_family_properties[q.queue_family_index() as usize]
433                    .queue_flags
434                    .contains(QueueFlags::GRAPHICS)
435            })
436            .map(|q| q.queue_family_index());
437        let compute_queue_family_index = queues
438            .iter()
439            .filter(|q| {
440                queue_family_properties[q.queue_family_index() as usize]
441                    .queue_flags
442                    .contains(QueueFlags::COMPUTE)
443            })
444            .min_by_key(|q| {
445                queue_family_properties[q.queue_family_index() as usize]
446                    .queue_flags
447                    .count()
448            })
449            .map(|q| q.queue_family_index());
450        let transfer_queue_family_index = queues
451            .iter()
452            .filter(|q| {
453                queue_family_properties[q.queue_family_index() as usize]
454                    .queue_flags
455                    .contains(QueueFlags::TRANSFER)
456            })
457            .min_by_key(|q| {
458                queue_family_properties[q.queue_family_index() as usize]
459                    .queue_flags
460                    .count()
461            })
462            .map(|q| q.queue_family_index())
463            .or(compute_queue_family_index)
464            .or(graphics_queue_family_index);
465
466        let mut queue_family_indices = SmallVec::new();
467
468        for &node_index in topological_order {
469            let node = unsafe { self.nodes.node_unchecked_mut(node_index) };
470            let NodeInner::Task(task_node) = &mut node.inner else {
471                unreachable!();
472            };
473
474            let queue_family_index = match task_node.queue_family_type() {
475                QueueFamilyType::Graphics => graphics_queue_family_index,
476                QueueFamilyType::Compute => compute_queue_family_index,
477                QueueFamilyType::Transfer => transfer_queue_family_index,
478                QueueFamilyType::Specific { index } => queues
479                    .iter()
480                    .any(|q| q.queue_family_index() == index)
481                    .then_some(index),
482            }
483            .ok_or(CompileErrorKind::InsufficientQueues)?;
484
485            task_node.queue_family_index = queue_family_index;
486
487            if !queue_family_indices.contains(&queue_family_index) {
488                queue_family_indices.push(queue_family_index);
489            }
490        }
491
492        Ok(queue_family_indices)
493    }
494
495    /// Lowers the task graph to the intermediate representation.
496    // TODO: Cull redundant semaphores.
497    unsafe fn lower(
498        &mut self,
499        present_queue: Option<&Arc<Queue>>,
500        topological_order: &[NodeIndex],
501    ) -> Result<
502        (
503            IntermediateRepresentationBuilder,
504            LinearMap<Id<Swapchain>, NodeIndex>,
505        ),
506        CompileErrorKind,
507    > {
508        let mut builder = IntermediateRepresentationBuilder::new(
509            self.nodes.capacity(),
510            self.resources.capacity(),
511        );
512        let mut prev_queue_family_index = vk::QUEUE_FAMILY_IGNORED;
513        let mut last_swapchain_accesses = LinearMap::new();
514
515        for &node_index in topological_order {
516            let node = unsafe { self.nodes.node_unchecked(node_index) };
517            let NodeInner::Task(task_node) = &node.inner else {
518                unreachable!();
519            };
520            let queue_family_index = task_node.queue_family_index;
521
522            for &out_node_index in &node.out_edges {
523                let out_node = unsafe { self.nodes.node_unchecked(out_node_index) };
524                let NodeInner::Task(out_task_node) = &out_node.inner else {
525                    unreachable!();
526                };
527
528                if queue_family_index != out_task_node.queue_family_index {
529                    let semaphore_index = builder.semaphore_count;
530                    builder.nodes[node_index as usize]
531                        .signal_semaphores
532                        .push(semaphore_index);
533                    builder.nodes[out_node_index as usize]
534                        .wait_semaphores
535                        .push(semaphore_index);
536                    builder.semaphore_count += 1;
537                }
538            }
539
540            if prev_queue_family_index != queue_family_index {
541                builder.submissions.push(SubmissionState::new(node_index));
542                builder.is_render_pass_instance_active = false;
543            }
544
545            let submission_index = builder.submissions.len() - 1;
546            builder.nodes[node_index as usize].submission_index = submission_index;
547
548            if let Some(attachments) = &task_node.attachments {
549                builder.subpass(node_index, &task_node.accesses, attachments);
550                builder.is_render_pass_instance_active = true;
551            } else {
552                builder.is_render_pass_instance_active = false;
553            }
554
555            for (id, access) in task_node.accesses.iter() {
556                let access = ResourceAccess {
557                    queue_family_index,
558                    ..*access
559                };
560                builder.resource_access(node_index, id, access);
561
562                if id.is::<Swapchain>() {
563                    *last_swapchain_accesses
564                        .get_or_insert(unsafe { id.parametrize() }, node_index) = node_index;
565                }
566            }
567
568            builder.submissions.last_mut().unwrap().last_node_index = node_index;
569
570            prev_queue_family_index = queue_family_index;
571        }
572
573        if !last_swapchain_accesses.is_empty() {
574            let present_queue = present_queue.expect("expected to be given a present queue");
575
576            for (&swapchain_id, &node_index) in last_swapchain_accesses.iter() {
577                builder.swapchain_present(node_index, swapchain_id, present_queue);
578            }
579        }
580
581        Ok((builder, last_swapchain_accesses))
582    }
583}
584
585struct IntermediateRepresentationBuilder {
586    submissions: Vec<SubmissionState>,
587    nodes: Vec<NodeState>,
588    prev_accesses: Vec<ResourceAccess>,
589    prev_node_indices: Vec<NodeIndex>,
590    semaphore_count: usize,
591    render_passes: Vec<RenderPassState>,
592    is_render_pass_instance_active: bool,
593    pre_present_queue_family_ownership_transfers: Vec<Id<Swapchain>>,
594}
595
596struct SubmissionState {
597    initial_barriers: Vec<super::MemoryBarrier>,
598    first_node_index: NodeIndex,
599    last_node_index: NodeIndex,
600}
601
602#[derive(Clone, Default)]
603struct NodeState {
604    submission_index: usize,
605    wait_acquire: Vec<(Id<Swapchain>, PipelineStages)>,
606    wait_semaphores: Vec<SemaphoreIndex>,
607    start_barriers: Vec<super::MemoryBarrier>,
608    subpass: Option<Subpass>,
609    clear_attachments: Vec<Id>,
610    end_barriers: Vec<super::MemoryBarrier>,
611    signal_semaphores: Vec<SemaphoreIndex>,
612    signal_pre_present: Vec<(Id<Swapchain>, PipelineStages)>,
613    signal_present: Vec<(Id<Swapchain>, PipelineStages)>,
614}
615
616#[derive(Clone, Copy)]
617struct Subpass {
618    render_pass_index: RenderPassIndex,
619    subpass_index: usize,
620}
621
622struct RenderPassState {
623    framebuffer_id: Id<Framebuffer>,
624    attachments: LinearMap<Id, AttachmentState>,
625    subpasses: Vec<SubpassState>,
626    first_node_index: NodeIndex,
627    last_node_index: NodeIndex,
628    clear_node_indices: Vec<NodeIndex>,
629}
630
631struct AttachmentState {
632    first_node_index: NodeIndex,
633    first_access: ResourceAccess,
634    last_access: ResourceAccess,
635    has_reads: bool,
636    index: u32,
637    clear: bool,
638    format: Format,
639    component_mapping: ComponentMapping,
640    mip_level: u32,
641    base_array_layer: u32,
642}
643
644struct SubpassState {
645    input_attachments: LinearMap<Id, (u32, ImageLayout)>,
646    color_attachments: LinearMap<Id, (u32, ImageLayout)>,
647    depth_stencil_attachment: Option<(Id, ImageLayout)>,
648    accesses: LinearMap<Id, ResourceAccess>,
649}
650
651impl IntermediateRepresentationBuilder {
652    fn new(node_capacity: u32, resource_capacity: u32) -> Self {
653        IntermediateRepresentationBuilder {
654            submissions: Vec::new(),
655            nodes: vec![NodeState::default(); node_capacity as usize],
656            prev_accesses: vec![ResourceAccess::default(); resource_capacity as usize],
657            prev_node_indices: vec![0; resource_capacity as usize],
658            semaphore_count: 0,
659            render_passes: Vec::new(),
660            is_render_pass_instance_active: false,
661            pre_present_queue_family_ownership_transfers: Vec::new(),
662        }
663    }
664
665    fn subpass(
666        &mut self,
667        node_index: NodeIndex,
668        accesses: &ResourceAccesses,
669        attachments: &Attachments,
670    ) {
671        let is_same_render_pass = self.is_render_pass_instance_active
672            && is_render_pass_mergeable(self.render_passes.last().unwrap(), accesses, attachments);
673
674        if !is_same_render_pass {
675            self.render_passes
676                .push(RenderPassState::new(attachments.framebuffer_id, node_index));
677        }
678
679        let render_pass_state = self.render_passes.last_mut().unwrap();
680
681        let is_same_subpass = render_pass_state
682            .subpasses
683            .last()
684            .is_some_and(|subpass_state| {
685                is_subpass_mergeable(subpass_state, accesses, attachments)
686            });
687
688        if !is_same_subpass {
689            render_pass_state
690                .subpasses
691                .push(SubpassState::from_task_node(accesses, attachments));
692        }
693
694        let subpass_state = render_pass_state.subpasses.last_mut().unwrap();
695
696        for (id, access) in accesses.iter() {
697            let current_access = subpass_state
698                .accesses
699                .get_or_insert_with(id, Default::default);
700            current_access.stage_mask |= access.stage_mask;
701            current_access.access_mask |= access.access_mask;
702        }
703
704        for (&id, attachment_info) in attachments.iter() {
705            let access = *accesses.get(id.erase()).unwrap();
706            let attachment_state =
707                render_pass_state
708                    .attachments
709                    .get_or_insert_with(id, || AttachmentState {
710                        first_node_index: node_index,
711                        first_access: access,
712                        last_access: ResourceAccess::default(),
713                        has_reads: false,
714                        index: attachment_info.index,
715                        clear: attachment_info.clear,
716                        format: attachment_info.format,
717                        component_mapping: attachment_info.component_mapping,
718                        mip_level: attachment_info.mip_level,
719                        base_array_layer: attachment_info.base_array_layer,
720                    });
721            attachment_state.last_access = access;
722            attachment_state.has_reads |= access.access_mask.contains_reads();
723
724            if attachment_info.clear {
725                // If we are the first node in the render pass to clear this attachment, we can
726                // make use of `AttachmentLoadOp::Clear`. Otherwise, we have to use the
727                // `clear_attachments` command.
728                if attachment_state.first_node_index == node_index {
729                    if !render_pass_state.clear_node_indices.contains(&node_index) {
730                        render_pass_state.clear_node_indices.push(node_index);
731                    }
732                } else {
733                    let node_state = &mut self.nodes[node_index as usize];
734
735                    if !node_state.clear_attachments.contains(&id) {
736                        node_state.clear_attachments.push(id);
737                    }
738                }
739            }
740        }
741
742        render_pass_state.last_node_index = node_index;
743
744        self.nodes[node_index as usize].subpass = Some(Subpass {
745            subpass_index: render_pass_state.subpasses.len() - 1,
746            render_pass_index: self.render_passes.len() - 1,
747        });
748    }
749
750    fn resource_access(&mut self, node_index: NodeIndex, id: Id, access: ResourceAccess) {
751        let prev_access = self.prev_accesses[id.index() as usize];
752        let prev_node_index = self.prev_node_indices[id.index() as usize];
753        let mut barriered = true;
754
755        if prev_access.stage_mask.is_empty() {
756            if id.is::<Swapchain>() {
757                self.swapchain_acquire(node_index, unsafe { id.parametrize() }, access);
758            } else if id.is::<Image>() {
759                self.initial_image_layout_transition(id, access);
760            } else if access.access_mask.contains_reads() {
761                self.initial_memory_barrier(id, access);
762            }
763        } else if prev_access.queue_family_index != access.queue_family_index {
764            if id.is_exclusive() {
765                self.queue_family_ownership_release(prev_node_index, id, access);
766                self.queue_family_ownership_acquire(node_index, id, access);
767            } else {
768                let prev_access = &mut self.prev_accesses[id.index() as usize];
769                prev_access.stage_mask = PipelineStages::empty();
770                prev_access.access_mask = AccessFlags::empty();
771
772                if prev_access.image_layout != access.image_layout {
773                    self.image_layout_transition(node_index, id, access);
774                }
775            }
776        } else if self.is_render_pass_instance_active
777            && self.nodes[prev_node_index as usize]
778                .subpass
779                .is_some_and(|subpass| subpass.render_pass_index == self.render_passes.len() - 1)
780        {
781            // Dependencies within a render pass instance are handled using subpass dependencies.
782        } else if prev_access.image_layout != access.image_layout {
783            self.image_layout_transition(node_index, id, access);
784        } else if prev_access.access_mask.contains_writes() {
785            self.memory_barrier(node_index, id, access);
786        } else if access.access_mask.contains_writes() {
787            self.execution_barrier(node_index, id, access);
788        } else {
789            barriered = false;
790        }
791
792        let prev_access = &mut self.prev_accesses[id.index() as usize];
793        let prev_node_index = &mut self.prev_node_indices[id.index() as usize];
794
795        if barriered {
796            *prev_access = access;
797            *prev_node_index = node_index;
798        } else {
799            prev_access.stage_mask |= access.stage_mask;
800            prev_access.access_mask |= access.access_mask;
801
802            let prev_barrier = self.nodes[*prev_node_index as usize]
803                .start_barriers
804                .iter_mut()
805                .find(|barrier| barrier.resource == id)
806                .unwrap_or_else(|| {
807                    self.submissions
808                        .last_mut()
809                        .unwrap()
810                        .initial_barriers
811                        .iter_mut()
812                        .find(|barrier| barrier.resource == id)
813                        .unwrap()
814                });
815            prev_barrier.dst_stage_mask |= access.stage_mask;
816            prev_barrier.dst_access_mask |= access.access_mask;
817        }
818    }
819
820    fn swapchain_acquire(
821        &mut self,
822        node_index: NodeIndex,
823        swapchain_id: Id<Swapchain>,
824        access: ResourceAccess,
825    ) {
826        let src = ResourceAccess {
827            stage_mask: access.stage_mask,
828            access_mask: AccessFlags::empty(),
829            image_layout: ImageLayout::Undefined,
830            queue_family_index: vk::QUEUE_FAMILY_IGNORED,
831        };
832        let dst = ResourceAccess {
833            stage_mask: access.stage_mask,
834            access_mask: access.access_mask,
835            image_layout: access.image_layout,
836            queue_family_index: vk::QUEUE_FAMILY_IGNORED,
837        };
838
839        self.memory_barrier_inner(node_index, swapchain_id.erase(), src, dst, false);
840
841        let submission_state = self.submissions.last().unwrap();
842
843        self.nodes[submission_state.first_node_index as usize]
844            .wait_acquire
845            .push((swapchain_id, access.stage_mask));
846    }
847
848    fn initial_image_layout_transition(&mut self, id: Id, access: ResourceAccess) {
849        self.initial_memory_barrier(id, access);
850    }
851
852    fn initial_memory_barrier(&mut self, id: Id, access: ResourceAccess) {
853        let submission_state = self.submissions.last_mut().unwrap();
854
855        submission_state
856            .initial_barriers
857            .push(super::MemoryBarrier {
858                src_stage_mask: PipelineStages::empty(),
859                src_access_mask: AccessFlags::empty(),
860                dst_stage_mask: access.stage_mask,
861                dst_access_mask: access.access_mask,
862                old_layout: ImageLayout::Undefined,
863                new_layout: access.image_layout,
864                src_queue_family_index: vk::QUEUE_FAMILY_IGNORED,
865                dst_queue_family_index: vk::QUEUE_FAMILY_IGNORED,
866                resource: id,
867            });
868    }
869
870    fn queue_family_ownership_release(
871        &mut self,
872        node_index: NodeIndex,
873        id: Id,
874        access: ResourceAccess,
875    ) {
876        debug_assert!(id.is_exclusive());
877
878        let prev_access = &mut self.prev_accesses[id.index() as usize];
879        let mut src = *prev_access;
880        let dst = ResourceAccess {
881            stage_mask: PipelineStages::empty(),
882            access_mask: AccessFlags::empty(),
883            ..access
884        };
885
886        if !prev_access.access_mask.contains_writes() {
887            src.access_mask = AccessFlags::empty();
888        }
889
890        debug_assert_ne!(src.queue_family_index, dst.queue_family_index);
891
892        self.memory_barrier_inner(node_index, id, src, dst, true);
893    }
894
895    fn queue_family_ownership_acquire(
896        &mut self,
897        node_index: NodeIndex,
898        id: Id,
899        access: ResourceAccess,
900    ) {
901        debug_assert!(id.is_exclusive());
902
903        let prev_access = self.prev_accesses[id.index() as usize];
904        let src = ResourceAccess {
905            stage_mask: PipelineStages::empty(),
906            access_mask: AccessFlags::empty(),
907            ..prev_access
908        };
909        let dst = access;
910
911        debug_assert_ne!(src.queue_family_index, dst.queue_family_index);
912
913        self.memory_barrier_inner(node_index, id, src, dst, false);
914    }
915
916    fn image_layout_transition(&mut self, node_index: NodeIndex, id: Id, access: ResourceAccess) {
917        debug_assert_ne!(
918            self.prev_accesses[id.index() as usize].image_layout,
919            access.image_layout,
920        );
921
922        self.memory_barrier(node_index, id, access);
923    }
924
925    fn memory_barrier(&mut self, node_index: NodeIndex, id: Id, access: ResourceAccess) {
926        let prev_access = self.prev_accesses[id.index() as usize];
927        let src = ResourceAccess {
928            queue_family_index: vk::QUEUE_FAMILY_IGNORED,
929            ..prev_access
930        };
931        let dst = ResourceAccess {
932            queue_family_index: vk::QUEUE_FAMILY_IGNORED,
933            ..access
934        };
935
936        self.memory_barrier_inner(node_index, id, src, dst, false);
937    }
938
939    fn execution_barrier(&mut self, node_index: NodeIndex, id: Id, access: ResourceAccess) {
940        let prev_access = self.prev_accesses[id.index() as usize];
941        let src = ResourceAccess {
942            access_mask: AccessFlags::empty(),
943            queue_family_index: vk::QUEUE_FAMILY_IGNORED,
944            ..prev_access
945        };
946        let dst = ResourceAccess {
947            access_mask: AccessFlags::empty(),
948            queue_family_index: vk::QUEUE_FAMILY_IGNORED,
949            ..access
950        };
951
952        debug_assert_eq!(prev_access.image_layout, access.image_layout);
953
954        self.memory_barrier_inner(node_index, id, src, dst, false);
955    }
956
957    fn memory_barrier_inner(
958        &mut self,
959        node_index: NodeIndex,
960        id: Id,
961        src: ResourceAccess,
962        dst: ResourceAccess,
963        is_end_barrier: bool,
964    ) {
965        let mut node_state = &mut self.nodes[node_index as usize];
966
967        // Regular pipeline barriers are not permitted during a render pass instance, so we need to
968        // move them before/after the render pass instance. This works because dependencies between
969        // subpasses are handled with subpass dependencies and the only pipeline barriers that
970        // could be needed are image layout transitions and/or queue family ownership transfers on
971        // first/last use of a resource used during the render pass instance.
972        if let Some(subpass) = node_state.subpass {
973            let render_pass_state = &self.render_passes[subpass.render_pass_index];
974            let moved_node_index = if is_end_barrier {
975                render_pass_state.last_node_index
976            } else {
977                render_pass_state.first_node_index
978            };
979            node_state = &mut self.nodes[moved_node_index as usize];
980        }
981
982        let barriers = if is_end_barrier {
983            &mut node_state.end_barriers
984        } else {
985            &mut node_state.start_barriers
986        };
987
988        barriers.push(super::MemoryBarrier {
989            src_stage_mask: src.stage_mask,
990            src_access_mask: src.access_mask,
991            dst_stage_mask: dst.stage_mask,
992            dst_access_mask: dst.access_mask,
993            old_layout: src.image_layout,
994            new_layout: dst.image_layout,
995            src_queue_family_index: src.queue_family_index,
996            dst_queue_family_index: dst.queue_family_index,
997            resource: id,
998        });
999    }
1000
1001    fn swapchain_present(
1002        &mut self,
1003        node_index: NodeIndex,
1004        swapchain_id: Id<Swapchain>,
1005        present_queue: &Queue,
1006    ) {
1007        let prev_access = self.prev_accesses[swapchain_id.index() as usize];
1008        let src = ResourceAccess {
1009            queue_family_index: vk::QUEUE_FAMILY_IGNORED,
1010            ..prev_access
1011        };
1012
1013        let needs_queue_family_ownership_transfer = prev_access.queue_family_index
1014            != present_queue.queue_family_index()
1015            && swapchain_id.is_exclusive();
1016
1017        if needs_queue_family_ownership_transfer {
1018            self.queue_family_ownership_release(
1019                node_index,
1020                swapchain_id.erase(),
1021                ResourceAccess {
1022                    stage_mask: PipelineStages::empty(),
1023                    access_mask: AccessFlags::empty(),
1024                    image_layout: ImageLayout::PresentSrc,
1025                    queue_family_index: present_queue.queue_family_index(),
1026                },
1027            );
1028        } else {
1029            self.memory_barrier_inner(
1030                node_index,
1031                swapchain_id.erase(),
1032                src,
1033                ResourceAccess {
1034                    stage_mask: PipelineStages::empty(),
1035                    access_mask: AccessFlags::empty(),
1036                    image_layout: ImageLayout::PresentSrc,
1037                    queue_family_index: vk::QUEUE_FAMILY_IGNORED,
1038                },
1039                true,
1040            );
1041        }
1042
1043        let node_state = &self.nodes[node_index as usize];
1044        let submission_state = &self.submissions[node_state.submission_index];
1045
1046        if needs_queue_family_ownership_transfer {
1047            self.nodes[submission_state.last_node_index as usize]
1048                .signal_pre_present
1049                .push((swapchain_id, prev_access.stage_mask));
1050            self.pre_present_queue_family_ownership_transfers
1051                .push(swapchain_id);
1052        } else {
1053            self.nodes[submission_state.last_node_index as usize]
1054                .signal_present
1055                .push((swapchain_id, prev_access.stage_mask));
1056        }
1057    }
1058}
1059
1060impl SubmissionState {
1061    fn new(node_index: u32) -> Self {
1062        Self {
1063            initial_barriers: Vec::new(),
1064            first_node_index: node_index,
1065            last_node_index: node_index,
1066        }
1067    }
1068}
1069
1070impl RenderPassState {
1071    fn new(framebuffer: Id<Framebuffer>, node_index: NodeIndex) -> Self {
1072        RenderPassState {
1073            framebuffer_id: framebuffer,
1074            attachments: LinearMap::new(),
1075            subpasses: Vec::new(),
1076            first_node_index: node_index,
1077            last_node_index: node_index,
1078            clear_node_indices: Vec::new(),
1079        }
1080    }
1081}
1082
1083impl SubpassState {
1084    fn from_task_node(accesses: &ResourceAccesses, attachments: &Attachments) -> Self {
1085        SubpassState {
1086            input_attachments: attachments
1087                .input_attachments
1088                .iter()
1089                .map(|(id, attachment_info)| {
1090                    let input_attachment_index = attachment_info.index;
1091                    let image_layout = accesses.get(id.erase()).unwrap().image_layout;
1092
1093                    (*id, (input_attachment_index, image_layout))
1094                })
1095                .collect(),
1096            color_attachments: attachments
1097                .color_attachments
1098                .iter()
1099                .map(|(id, attachment_info)| {
1100                    let location = attachment_info.index;
1101                    let image_layout = accesses.get(id.erase()).unwrap().image_layout;
1102
1103                    (*id, (location, image_layout))
1104                })
1105                .collect(),
1106            depth_stencil_attachment: attachments.depth_stencil_attachment.as_ref().map(
1107                |(id, _)| {
1108                    let image_layout = accesses.get(id.erase()).unwrap().image_layout;
1109
1110                    (*id, image_layout)
1111                },
1112            ),
1113            accesses: accesses.inner.clone(),
1114        }
1115    }
1116}
1117
1118fn is_render_pass_mergeable(
1119    render_pass_state: &RenderPassState,
1120    accesses: &ResourceAccesses,
1121    attachments: &Attachments,
1122) -> bool {
1123    const ATTACHMENT_ACCESSES: AccessFlags = AccessFlags::INPUT_ATTACHMENT_READ
1124        .union(AccessFlags::COLOR_ATTACHMENT_READ)
1125        .union(AccessFlags::COLOR_ATTACHMENT_WRITE)
1126        .union(AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ)
1127        .union(AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE);
1128
1129    // Different framebuffers may have different dimensions so we can't merge them.
1130    if render_pass_state.framebuffer_id != attachments.framebuffer_id {
1131        return false;
1132    }
1133
1134    // We can't merge with a task node that accesses one of our attachments as anything other
1135    // than an attachment, as render passes don't allow this.
1136    if render_pass_state.attachments.keys().any(|id| {
1137        accesses
1138            .get(id.erase())
1139            .is_some_and(|access| !ATTACHMENT_ACCESSES.contains(access.access_mask))
1140    }) {
1141        return false;
1142    }
1143
1144    // We can't merge with a task node that has an attachment which we access as anything other
1145    // than an attachment, as render passes don't allow this.
1146    if attachments.keys().any(|id| {
1147        render_pass_state.subpasses.iter().any(|subpass_state| {
1148            subpass_state
1149                .accesses
1150                .get(&id.erase())
1151                .is_some_and(|access| !ATTACHMENT_ACCESSES.contains(access.access_mask))
1152        })
1153    }) {
1154        return false;
1155    }
1156
1157    true
1158}
1159
1160fn is_subpass_mergeable(
1161    subpass_state: &SubpassState,
1162    accesses: &ResourceAccesses,
1163    attachments: &Attachments,
1164) -> bool {
1165    // We can only merge with a task node that has the exact same attachments. Keep in mind that
1166    // being here, `is_render_pass_mergeable` must have returned `true`, which rules out the other
1167    // incompatibilities.
1168
1169    // Early nope-out to avoid the expense below.
1170    if subpass_state.color_attachments.len() != attachments.color_attachments.len()
1171        || subpass_state.input_attachments.len() != attachments.input_attachments.len()
1172        || subpass_state.depth_stencil_attachment.is_some()
1173            != attachments.depth_stencil_attachment.is_some()
1174    {
1175        return false;
1176    }
1177
1178    for (id, &(location, image_layout)) in subpass_state.color_attachments.iter() {
1179        if !attachments
1180            .color_attachments
1181            .get(id)
1182            .is_some_and(|attachment_info| attachment_info.index == location)
1183            || accesses.get(id.erase()).unwrap().image_layout != image_layout
1184        {
1185            return false;
1186        }
1187    }
1188
1189    for (id, &(input_attachment_index, image_layout)) in subpass_state.input_attachments.iter() {
1190        if !attachments
1191            .input_attachments
1192            .get(id)
1193            .is_some_and(|attachment_info| attachment_info.index == input_attachment_index)
1194            || accesses.get(id.erase()).unwrap().image_layout != image_layout
1195        {
1196            return false;
1197        }
1198    }
1199
1200    if subpass_state.depth_stencil_attachment
1201        != attachments
1202            .depth_stencil_attachment
1203            .as_ref()
1204            .map(|(id, _)| (*id, accesses.get(id.erase()).unwrap().image_layout))
1205    {
1206        return false;
1207    }
1208
1209    true
1210}
1211
1212fn create_render_pass(
1213    resources: &super::Resources,
1214    render_pass_state: RenderPassState,
1215) -> Result<super::RenderPassState, CompileErrorKind> {
1216    const FRAMEBUFFER_SPACE_ACCESS_FLAGS: AccessFlags = AccessFlags::INPUT_ATTACHMENT_READ
1217        .union(AccessFlags::COLOR_ATTACHMENT_READ)
1218        .union(AccessFlags::COLOR_ATTACHMENT_WRITE)
1219        .union(AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ)
1220        .union(AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE);
1221
1222    let attachments = render_pass_state
1223        .attachments
1224        .iter()
1225        .map(|(&id, attachment_state)| {
1226            let resource_info = resources.get(id.erase()).unwrap();
1227            let is_resident = !resource_info
1228                .usage
1229                .contains(ImageUsage::TRANSIENT_ATTACHMENT);
1230
1231            AttachmentDescription {
1232                format: attachment_state.format,
1233                samples: resource_info.samples,
1234                load_op: if attachment_state.clear {
1235                    AttachmentLoadOp::Clear
1236                } else if attachment_state.has_reads && is_resident {
1237                    AttachmentLoadOp::Load
1238                } else {
1239                    AttachmentLoadOp::DontCare
1240                },
1241                store_op: if is_resident {
1242                    AttachmentStoreOp::Store
1243                } else {
1244                    AttachmentStoreOp::DontCare
1245                },
1246                initial_layout: attachment_state.first_access.image_layout,
1247                final_layout: attachment_state.last_access.image_layout,
1248                ..Default::default()
1249            }
1250        })
1251        .collect();
1252
1253    let subpasses = render_pass_state
1254        .subpasses
1255        .iter()
1256        .map(|subpass_state| SubpassDescription {
1257            // FIXME:
1258            view_mask: 0,
1259            input_attachments: convert_attachments(
1260                &render_pass_state,
1261                &subpass_state.input_attachments,
1262            ),
1263            color_attachments: convert_attachments(
1264                &render_pass_state,
1265                &subpass_state.color_attachments,
1266            ),
1267            depth_stencil_attachment: subpass_state.depth_stencil_attachment.map(|(id, layout)| {
1268                AttachmentReference {
1269                    attachment: render_pass_state.attachments.index_of(&id).unwrap() as u32,
1270                    layout,
1271                    ..Default::default()
1272                }
1273            }),
1274            preserve_attachments: render_pass_state
1275                .attachments
1276                .keys()
1277                .enumerate()
1278                .filter_map(|(attachment, &id)| {
1279                    (!subpass_state.input_attachments.contains_key(&id)
1280                        && !subpass_state.color_attachments.contains_key(&id)
1281                        && !subpass_state
1282                            .depth_stencil_attachment
1283                            .is_some_and(|(x, _)| x == id))
1284                    .then_some(attachment as u32)
1285                })
1286                .collect(),
1287            ..Default::default()
1288        })
1289        .collect();
1290
1291    let mut dependencies = Vec::<SubpassDependency>::new();
1292
1293    // FIXME: subpass dependency chains
1294    for (src_subpass_index, src_subpass_state) in render_pass_state.subpasses.iter().enumerate() {
1295        let src_subpass = src_subpass_index as u32;
1296
1297        for (dst_subpass_index, dst_subpass_state) in render_pass_state
1298            .subpasses
1299            .iter()
1300            .enumerate()
1301            .skip(src_subpass_index + 1)
1302        {
1303            let dst_subpass = dst_subpass_index as u32;
1304
1305            for (id, src_access) in src_subpass_state.accesses.iter() {
1306                let Some(dst_access) = dst_subpass_state.accesses.get(id) else {
1307                    continue;
1308                };
1309                let src_access_mask;
1310                let dst_access_mask;
1311
1312                if src_access.access_mask.contains_writes() {
1313                    src_access_mask = src_access.access_mask;
1314                    dst_access_mask = dst_access.access_mask;
1315                } else if dst_access.access_mask.contains_writes() {
1316                    src_access_mask = AccessFlags::empty();
1317                    dst_access_mask = AccessFlags::empty();
1318                } else {
1319                    continue;
1320                }
1321
1322                let dependency =
1323                    get_or_insert_subpass_dependency(&mut dependencies, src_subpass, dst_subpass);
1324                dependency.src_stages |= src_access.stage_mask;
1325                dependency.src_access |= src_access_mask;
1326                dependency.dst_stages |= dst_access.stage_mask;
1327                dependency.dst_access |= dst_access_mask;
1328
1329                // Attachments can only have framebuffer-space accesses, for which
1330                // `DependencyFlags::BY_REGION` is always sound. For resources other than
1331                // attachments, it could be sound, but we have no way of knowing. There should be
1332                // no reason for this in the first place, or at least be really uncommon.
1333                if !FRAMEBUFFER_SPACE_ACCESS_FLAGS.contains(src_access_mask) {
1334                    dependency.dependency_flags = DependencyFlags::empty();
1335                }
1336            }
1337        }
1338    }
1339
1340    // TODO: What do we do about non-framebuffer-space subpass self-dependencies?
1341    for (subpass_index, subpass_state) in render_pass_state.subpasses.iter().enumerate() {
1342        let subpass = subpass_index as u32;
1343
1344        for access in subpass_state.accesses.values() {
1345            let access_mask = access.access_mask;
1346
1347            if access_mask.contains(AccessFlags::INPUT_ATTACHMENT_READ) {
1348                if access_mask.contains(AccessFlags::COLOR_ATTACHMENT_WRITE) {
1349                    let dependency =
1350                        get_or_insert_subpass_dependency(&mut dependencies, subpass, subpass);
1351                    dependency.src_stages |= PipelineStages::COLOR_ATTACHMENT_OUTPUT;
1352                    dependency.dst_stages |= PipelineStages::FRAGMENT_SHADER;
1353                    dependency.src_access |= AccessFlags::COLOR_ATTACHMENT_WRITE;
1354                    dependency.dst_access |= AccessFlags::INPUT_ATTACHMENT_READ;
1355                } else if access_mask.contains(AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE) {
1356                    let dependency =
1357                        get_or_insert_subpass_dependency(&mut dependencies, subpass, subpass);
1358                    dependency.src_stages |=
1359                        PipelineStages::EARLY_FRAGMENT_TESTS | PipelineStages::LATE_FRAGMENT_TESTS;
1360                    dependency.dst_stages |= PipelineStages::FRAGMENT_SHADER;
1361                    dependency.src_access |= AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE;
1362                    dependency.dst_access |= AccessFlags::INPUT_ATTACHMENT_READ;
1363                }
1364            }
1365        }
1366    }
1367
1368    let render_pass = RenderPass::new(
1369        resources.physical_resources.device().clone(),
1370        RenderPassCreateInfo {
1371            attachments,
1372            subpasses,
1373            dependencies,
1374            ..Default::default()
1375        },
1376    )
1377    // FIXME:
1378    .map_err(Validated::unwrap)
1379    .map_err(CompileErrorKind::VulkanError)?;
1380
1381    let attachments = render_pass_state
1382        .attachments
1383        .iter()
1384        .map(|(&id, attachment_state)| {
1385            let attachment = super::AttachmentState {
1386                index: attachment_state.index,
1387                format: attachment_state.format,
1388                component_mapping: attachment_state.component_mapping,
1389                mip_level: attachment_state.mip_level,
1390                base_array_layer: attachment_state.base_array_layer,
1391            };
1392
1393            (id, attachment)
1394        })
1395        .collect();
1396
1397    Ok(super::RenderPassState {
1398        render_pass,
1399        attachments,
1400        framebuffers: Vec::new(),
1401        clear_node_indices: render_pass_state.clear_node_indices,
1402    })
1403}
1404
1405fn convert_attachments(
1406    render_pass_state: &RenderPassState,
1407    attachments: &LinearMap<Id, (u32, ImageLayout)>,
1408) -> Vec<Option<AttachmentReference>> {
1409    let len = attachments.values().map(|(i, _)| *i + 1).max().unwrap_or(0);
1410    let mut attachments_out = vec![None; len as usize];
1411
1412    for (&id, &(index, layout)) in attachments.iter() {
1413        let attachment = &mut attachments_out[index as usize];
1414
1415        debug_assert!(attachment.is_none());
1416
1417        *attachment = Some(AttachmentReference {
1418            attachment: render_pass_state.attachments.index_of(&id).unwrap() as u32,
1419            layout,
1420            ..Default::default()
1421        });
1422    }
1423
1424    attachments_out
1425}
1426
1427fn get_or_insert_subpass_dependency(
1428    dependencies: &mut Vec<SubpassDependency>,
1429    src_subpass: u32,
1430    dst_subpass: u32,
1431) -> &mut SubpassDependency {
1432    let dependency_index = dependencies
1433        .iter_mut()
1434        .position(|dependency| {
1435            dependency.src_subpass == Some(src_subpass)
1436                && dependency.dst_subpass == Some(dst_subpass)
1437        })
1438        .unwrap_or_else(|| {
1439            let index = dependencies.len();
1440
1441            dependencies.push(SubpassDependency {
1442                src_subpass: Some(src_subpass),
1443                dst_subpass: Some(dst_subpass),
1444                dependency_flags: DependencyFlags::BY_REGION,
1445                ..Default::default()
1446            });
1447
1448            index
1449        });
1450
1451    &mut dependencies[dependency_index]
1452}
1453
1454struct FinalRepresentationBuilder {
1455    instructions: Vec<Instruction>,
1456    submissions: Vec<Submission>,
1457    barriers: Vec<super::MemoryBarrier>,
1458    clear_attachments: Vec<Id>,
1459    present_queue: Option<Arc<Queue>>,
1460    initial_barrier_range: Range<BarrierIndex>,
1461    has_flushed_submit: bool,
1462    should_flush_submit: bool,
1463    prev_barrier_index: usize,
1464    prev_subpass_index: usize,
1465}
1466
1467impl FinalRepresentationBuilder {
1468    fn new(present_queue: Option<&Arc<Queue>>) -> Self {
1469        FinalRepresentationBuilder {
1470            instructions: Vec::new(),
1471            submissions: Vec::new(),
1472            barriers: Vec::new(),
1473            clear_attachments: Vec::new(),
1474            present_queue: present_queue.cloned(),
1475            initial_barrier_range: 0..0,
1476            has_flushed_submit: true,
1477            should_flush_submit: false,
1478            prev_barrier_index: 0,
1479            prev_subpass_index: 0,
1480        }
1481    }
1482
1483    fn initial_pipeline_barrier(&mut self, barriers: &[super::MemoryBarrier]) {
1484        self.barriers.extend_from_slice(barriers);
1485        self.initial_barrier_range =
1486            self.prev_barrier_index as BarrierIndex..self.barriers.len() as BarrierIndex;
1487        self.prev_barrier_index = self.barriers.len();
1488    }
1489
1490    fn pipeline_barrier(&mut self, barriers: &[super::MemoryBarrier]) {
1491        self.barriers.extend_from_slice(barriers);
1492    }
1493
1494    fn wait_acquire(&mut self, swapchain_id: Id<Swapchain>, stage_mask: PipelineStages) {
1495        if !self.has_flushed_submit {
1496            self.flush_submit();
1497        }
1498
1499        self.instructions.push(Instruction::WaitAcquire {
1500            swapchain_id,
1501            stage_mask,
1502        });
1503    }
1504
1505    fn wait_semaphore(&mut self, semaphore_index: SemaphoreIndex) {
1506        if !self.has_flushed_submit {
1507            self.flush_submit();
1508        }
1509
1510        self.instructions.push(Instruction::WaitSemaphore {
1511            semaphore_index,
1512            stage_mask: PipelineStages::ALL_COMMANDS,
1513        });
1514    }
1515
1516    fn begin_render_pass(&mut self, render_pass_index: RenderPassIndex) {
1517        self.flush_barriers();
1518
1519        self.instructions
1520            .push(Instruction::BeginRenderPass { render_pass_index });
1521
1522        self.prev_subpass_index = 0;
1523    }
1524
1525    fn next_subpass(&mut self, subpass_index: usize) {
1526        if self.prev_subpass_index != subpass_index {
1527            self.instructions.push(Instruction::NextSubpass);
1528            self.prev_subpass_index = subpass_index;
1529        }
1530    }
1531
1532    fn execute_task(&mut self, node_index: NodeIndex) {
1533        self.flush_barriers();
1534
1535        self.instructions
1536            .push(Instruction::ExecuteTask { node_index });
1537
1538        self.has_flushed_submit = false;
1539    }
1540
1541    fn end_render_pass(&mut self) {
1542        self.instructions.push(Instruction::EndRenderPass);
1543    }
1544
1545    fn clear_attachments(
1546        &mut self,
1547        node_index: NodeIndex,
1548        render_pass_index: RenderPassIndex,
1549        clear_attachments: &[Id],
1550    ) {
1551        self.flush_barriers();
1552
1553        let clear_attachment_range_start = self.clear_attachments.len();
1554        self.clear_attachments.extend_from_slice(clear_attachments);
1555        self.instructions.push(Instruction::ClearAttachments {
1556            node_index,
1557            render_pass_index,
1558            clear_attachment_range: clear_attachment_range_start..self.clear_attachments.len(),
1559        });
1560    }
1561
1562    fn signal_semaphore(&mut self, semaphore_index: SemaphoreIndex) {
1563        self.instructions.push(Instruction::SignalSemaphore {
1564            semaphore_index,
1565            stage_mask: PipelineStages::ALL_COMMANDS,
1566        });
1567
1568        self.should_flush_submit = true;
1569    }
1570
1571    fn signal_present(&mut self, swapchain_id: Id<Swapchain>, stage_mask: PipelineStages) {
1572        self.instructions.push(Instruction::SignalPresent {
1573            swapchain_id,
1574            stage_mask,
1575        });
1576
1577        self.should_flush_submit = true;
1578    }
1579
1580    fn signal_pre_present(&mut self, swapchain_id: Id<Swapchain>, stage_mask: PipelineStages) {
1581        self.instructions.push(Instruction::SignalPrePresent {
1582            swapchain_id,
1583            stage_mask,
1584        });
1585
1586        self.should_flush_submit = true;
1587    }
1588
1589    fn pre_present_acquire_queue_family_ownership(
1590        &mut self,
1591        last_accesses: &[ResourceAccess],
1592        swapchain_id: Id<Swapchain>,
1593    ) {
1594        if !self.has_flushed_submit {
1595            self.flush_submit();
1596        }
1597
1598        self.instructions.push(Instruction::WaitPrePresent {
1599            swapchain_id,
1600            stage_mask: PipelineStages::ALL_COMMANDS,
1601        });
1602
1603        let last_access = last_accesses[swapchain_id.index() as usize];
1604
1605        self.barriers.push(super::MemoryBarrier {
1606            src_stage_mask: PipelineStages::empty(),
1607            src_access_mask: AccessFlags::empty(),
1608            dst_stage_mask: PipelineStages::empty(),
1609            dst_access_mask: AccessFlags::empty(),
1610            old_layout: last_access.image_layout,
1611            new_layout: ImageLayout::PresentSrc,
1612            src_queue_family_index: last_access.queue_family_index,
1613            dst_queue_family_index: self.present_queue.as_ref().unwrap().queue_family_index(),
1614            resource: swapchain_id.erase(),
1615        });
1616
1617        self.instructions.push(Instruction::SignalPresent {
1618            swapchain_id,
1619            stage_mask: PipelineStages::ALL_COMMANDS,
1620        });
1621    }
1622
1623    fn flush_barriers(&mut self) {
1624        if self.prev_barrier_index != self.barriers.len() {
1625            self.instructions.push(Instruction::PipelineBarrier {
1626                barrier_range: self.prev_barrier_index as BarrierIndex
1627                    ..self.barriers.len() as BarrierIndex,
1628            });
1629            self.prev_barrier_index = self.barriers.len();
1630        }
1631    }
1632
1633    fn flush_submit(&mut self) {
1634        self.flush_barriers();
1635        self.instructions.push(Instruction::FlushSubmit);
1636        self.has_flushed_submit = true;
1637        self.should_flush_submit = false;
1638    }
1639
1640    fn submit(&mut self, queue: Arc<Queue>) {
1641        self.instructions.push(Instruction::Submit);
1642
1643        let prev_instruction_range_end = self
1644            .submissions
1645            .last()
1646            .map(|s| s.instruction_range.end)
1647            .unwrap_or(0);
1648        self.submissions.push(Submission {
1649            queue,
1650            initial_barrier_range: self.initial_barrier_range.clone(),
1651            instruction_range: prev_instruction_range_end..self.instructions.len(),
1652        });
1653    }
1654}
1655
1656impl<W: ?Sized> ExecutableTaskGraph<W> {
1657    /// Decompiles the graph back into a modifiable form.
1658    #[inline]
1659    pub fn decompile(self) -> TaskGraph<W> {
1660        self.graph
1661    }
1662}
1663
1664/// Parameters to [compile] a [`TaskGraph`].
1665///
1666/// [compile]: TaskGraph::compile
1667#[derive(Clone, Debug)]
1668pub struct CompileInfo<'a> {
1669    /// The queues to work with.
1670    ///
1671    /// You must supply at least one queue and all queues must be from unique queue families.
1672    ///
1673    /// The default value is empty, which must be overridden.
1674    pub queues: &'a [&'a Arc<Queue>],
1675
1676    /// The queue to use for swapchain presentation, if any.
1677    ///
1678    /// You must supply this queue if the task graph uses any swapchains. It can be the same queue
1679    /// as one in the [`queues`] field, or a different one.
1680    ///
1681    /// The default value is `None`.
1682    ///
1683    /// [`queues`]: Self::queues
1684    pub present_queue: Option<&'a Arc<Queue>>,
1685
1686    /// The flight which will be executed.
1687    ///
1688    /// The default value is `Id::INVALID`, which must be overridden.
1689    pub flight_id: Id<Flight>,
1690
1691    pub _ne: crate::NonExhaustive<'a>,
1692}
1693
1694impl Default for CompileInfo<'_> {
1695    #[inline]
1696    fn default() -> Self {
1697        CompileInfo {
1698            queues: &[],
1699            present_queue: None,
1700            flight_id: Id::INVALID,
1701            _ne: crate::NE,
1702        }
1703    }
1704}
1705
1706/// Error that can happen when [compiling] a [`TaskGraph`].
1707///
1708/// [compiling]: TaskGraph::compile
1709pub struct CompileError<W: ?Sized> {
1710    pub graph: TaskGraph<W>,
1711    pub kind: CompileErrorKind,
1712}
1713
1714/// The kind of [`CompileError`] that occurred.
1715#[derive(Debug, PartialEq, Eq)]
1716pub enum CompileErrorKind {
1717    Unconnected,
1718    Cycle,
1719    InsufficientQueues,
1720    VulkanError(VulkanError),
1721}
1722
1723impl<W: ?Sized> CompileError<W> {
1724    fn new(graph: TaskGraph<W>, kind: CompileErrorKind) -> Self {
1725        CompileError { graph, kind }
1726    }
1727}
1728
1729impl<W: ?Sized> fmt::Debug for CompileError<W> {
1730    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1731        fmt::Debug::fmt(&self.kind, f)
1732    }
1733}
1734
1735impl<W: ?Sized> fmt::Display for CompileError<W> {
1736    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1737        match self.kind {
1738            CompileErrorKind::Unconnected => f.write_str("the graph is not weakly connected"),
1739            CompileErrorKind::Cycle => f.write_str("the graph contains a directed cycle"),
1740            CompileErrorKind::InsufficientQueues => {
1741                f.write_str("the given queues are not sufficient for the requirements of a task")
1742            }
1743            CompileErrorKind::VulkanError(_) => f.write_str("a runtime error occurred"),
1744        }
1745    }
1746}
1747
1748impl<W: ?Sized> Error for CompileError<W> {
1749    fn source(&self) -> Option<&(dyn Error + 'static)> {
1750        match &self.kind {
1751            CompileErrorKind::VulkanError(err) => Some(err),
1752            _ => None,
1753        }
1754    }
1755}
1756
1757#[cfg(test)]
1758mod tests {
1759    use super::*;
1760    use crate::{
1761        graph::AttachmentInfo,
1762        resource::{AccessTypes, ImageLayoutType},
1763        tests::test_queues,
1764    };
1765    use std::marker::PhantomData;
1766    use vulkano::{
1767        buffer::BufferCreateInfo, image::ImageCreateInfo, swapchain::SwapchainCreateInfo,
1768        sync::Sharing,
1769    };
1770
1771    #[test]
1772    fn unconnected1() {
1773        let (resources, queues) = test_queues!();
1774
1775        // ┌───┐
1776        // │ A │
1777        // └───┘
1778        // ┄┄┄┄┄
1779        // ┌───┐
1780        // │ B │
1781        // └───┘
1782        let mut graph = TaskGraph::<()>::new(&resources, 10, 0);
1783        graph
1784            .create_task_node("A", QueueFamilyType::Graphics, PhantomData)
1785            .build();
1786        graph
1787            .create_task_node("B", QueueFamilyType::Compute, PhantomData)
1788            .build();
1789
1790        let err = unsafe {
1791            graph.compile(&CompileInfo {
1792                queues: &queues.iter().collect::<Vec<_>>(),
1793                ..Default::default()
1794            })
1795        }
1796        .unwrap_err();
1797
1798        assert_eq!(err.kind, CompileErrorKind::Unconnected);
1799    }
1800
1801    #[test]
1802    fn unconnected2() {
1803        let (resources, queues) = test_queues!();
1804
1805        // ┌───┐
1806        // │ A ├─┐
1807        // └───┘ │
1808        // ┌───┐ │
1809        // │ B ├┐│
1810        // └───┘││
1811        // ┄┄┄┄┄││┄┄┄┄┄┄
1812        //      ││ ┌───┐
1813        //      │└►│ C │
1814        //      │  └───┘
1815        //      │  ┌───┐
1816        //      └─►│ D │
1817        //         └───┘
1818        let mut graph = TaskGraph::<()>::new(&resources, 10, 0);
1819        let a = graph
1820            .create_task_node("A", QueueFamilyType::Graphics, PhantomData)
1821            .build();
1822        let b = graph
1823            .create_task_node("B", QueueFamilyType::Graphics, PhantomData)
1824            .build();
1825        let c = graph
1826            .create_task_node("C", QueueFamilyType::Compute, PhantomData)
1827            .build();
1828        let d = graph
1829            .create_task_node("D", QueueFamilyType::Compute, PhantomData)
1830            .build();
1831        graph.add_edge(a, c).unwrap();
1832        graph.add_edge(b, d).unwrap();
1833
1834        let err = unsafe {
1835            graph.compile(&CompileInfo {
1836                queues: &queues.iter().collect::<Vec<_>>(),
1837                ..Default::default()
1838            })
1839        }
1840        .unwrap_err();
1841
1842        assert_eq!(err.kind, CompileErrorKind::Unconnected);
1843    }
1844
1845    #[test]
1846    fn unconnected3() {
1847        let (resources, queues) = test_queues!();
1848
1849        // ┌───┐  ┌───┐  ┌───┐
1850        // │ A ├─►│ B ├─►│ C │
1851        // └───┘  └───┘  └───┘
1852        // ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
1853        // ┌───┐  ┌───┐  ┌───┐   ┌───┐
1854        // │ D ├┬►│ E ├┬►│ F ├──►│   │
1855        // └───┘│ └───┘│ └───┘┌─►│ G │
1856        //      │      └──────┘┌►│   │
1857        //      └──────────────┘ └───┘
1858        let mut graph = TaskGraph::<()>::new(&resources, 10, 0);
1859        let a = graph
1860            .create_task_node("A", QueueFamilyType::Graphics, PhantomData)
1861            .build();
1862        let b = graph
1863            .create_task_node("B", QueueFamilyType::Graphics, PhantomData)
1864            .build();
1865        let c = graph
1866            .create_task_node("C", QueueFamilyType::Graphics, PhantomData)
1867            .build();
1868        graph.add_edge(a, b).unwrap();
1869        graph.add_edge(b, c).unwrap();
1870        let d = graph
1871            .create_task_node("D", QueueFamilyType::Compute, PhantomData)
1872            .build();
1873        let e = graph
1874            .create_task_node("E", QueueFamilyType::Compute, PhantomData)
1875            .build();
1876        let f = graph
1877            .create_task_node("F", QueueFamilyType::Compute, PhantomData)
1878            .build();
1879        let g = graph
1880            .create_task_node("G", QueueFamilyType::Compute, PhantomData)
1881            .build();
1882        graph.add_edge(d, e).unwrap();
1883        graph.add_edge(d, g).unwrap();
1884        graph.add_edge(e, f).unwrap();
1885        graph.add_edge(e, g).unwrap();
1886        graph.add_edge(f, g).unwrap();
1887
1888        let err = unsafe {
1889            graph.compile(&CompileInfo {
1890                queues: &queues.iter().collect::<Vec<_>>(),
1891                ..Default::default()
1892            })
1893        }
1894        .unwrap_err();
1895
1896        assert_eq!(err.kind, CompileErrorKind::Unconnected);
1897    }
1898
1899    #[test]
1900    fn cycle1() {
1901        let (resources, queues) = test_queues!();
1902
1903        //   ┌───┐  ┌───┐  ┌───┐
1904        // ┌►│ A ├─►│ B ├─►│ C ├┐
1905        // │ └───┘  └───┘  └───┘│
1906        // └────────────────────┘
1907        let mut graph = TaskGraph::<()>::new(&resources, 10, 0);
1908        let a = graph
1909            .create_task_node("A", QueueFamilyType::Graphics, PhantomData)
1910            .build();
1911        let b = graph
1912            .create_task_node("B", QueueFamilyType::Graphics, PhantomData)
1913            .build();
1914        let c = graph
1915            .create_task_node("C", QueueFamilyType::Graphics, PhantomData)
1916            .build();
1917        graph.add_edge(a, b).unwrap();
1918        graph.add_edge(b, c).unwrap();
1919        graph.add_edge(c, a).unwrap();
1920
1921        let err = unsafe {
1922            graph.compile(&CompileInfo {
1923                queues: &queues.iter().collect::<Vec<_>>(),
1924                ..Default::default()
1925            })
1926        }
1927        .unwrap_err();
1928
1929        assert_eq!(err.kind, CompileErrorKind::Cycle);
1930    }
1931
1932    #[test]
1933    fn cycle2() {
1934        let (resources, queues) = test_queues!();
1935
1936        //   ┌───┐  ┌───┐  ┌───┐
1937        // ┌►│ A ├┬►│ B ├─►│ C ├┐
1938        // │ └───┘│ └───┘  └───┘│
1939        // │┄┄┄┄┄┄│┄┄┄┄┄┄┄┄┄┄┄┄┄│┄┄┄┄┄┄┄
1940        // │      │ ┌───┐  ┌───┐│ ┌───┐
1941        // │      └►│ D ├─►│ E ├┴►│ F ├┐
1942        // │        └───┘  └───┘  └───┘│
1943        // └───────────────────────────┘
1944        let mut graph = TaskGraph::<()>::new(&resources, 10, 0);
1945        let a = graph
1946            .create_task_node("A", QueueFamilyType::Graphics, PhantomData)
1947            .build();
1948        let b = graph
1949            .create_task_node("B", QueueFamilyType::Graphics, PhantomData)
1950            .build();
1951        let c = graph
1952            .create_task_node("C", QueueFamilyType::Graphics, PhantomData)
1953            .build();
1954        let d = graph
1955            .create_task_node("D", QueueFamilyType::Compute, PhantomData)
1956            .build();
1957        let e = graph
1958            .create_task_node("E", QueueFamilyType::Compute, PhantomData)
1959            .build();
1960        let f = graph
1961            .create_task_node("F", QueueFamilyType::Compute, PhantomData)
1962            .build();
1963        graph.add_edge(a, b).unwrap();
1964        graph.add_edge(a, d).unwrap();
1965        graph.add_edge(b, c).unwrap();
1966        graph.add_edge(c, f).unwrap();
1967        graph.add_edge(d, e).unwrap();
1968        graph.add_edge(e, f).unwrap();
1969        graph.add_edge(f, a).unwrap();
1970
1971        let err = unsafe {
1972            graph.compile(&CompileInfo {
1973                queues: &queues.iter().collect::<Vec<_>>(),
1974                ..Default::default()
1975            })
1976        }
1977        .unwrap_err();
1978
1979        assert_eq!(err.kind, CompileErrorKind::Cycle);
1980    }
1981
1982    #[test]
1983    fn cycle3() {
1984        let (resources, queues) = test_queues!();
1985
1986        // ┌─────┐
1987        // │┌───┐└►┌───┐  ┌───┐
1988        // ││ A ├┬►│ B ├─►│ C ├┬──────┐
1989        // │└───┘│ └───┘┌►└───┘│      │
1990        // │┄┄┄┄┄│┄┄┄┄┄┄│┄┄┄┄┄┄│┄┄┄┄┄┄│┄
1991        // │     │ ┌───┐│ ┌───┐│ ┌───┐│
1992        // │     └►│ D ├┴►│ E │└►│ F ├│┐
1993        // │     ┌►└───┘  └───┘  └───┘││
1994        // │     └────────────────────┘│
1995        // └───────────────────────────┘
1996        let mut graph = TaskGraph::<()>::new(&resources, 10, 0);
1997        let a = graph
1998            .create_task_node("A", QueueFamilyType::Graphics, PhantomData)
1999            .build();
2000        let b = graph
2001            .create_task_node("B", QueueFamilyType::Graphics, PhantomData)
2002            .build();
2003        let c = graph
2004            .create_task_node("C", QueueFamilyType::Graphics, PhantomData)
2005            .build();
2006        let d = graph
2007            .create_task_node("D", QueueFamilyType::Compute, PhantomData)
2008            .build();
2009        let e = graph
2010            .create_task_node("E", QueueFamilyType::Compute, PhantomData)
2011            .build();
2012        let f = graph
2013            .create_task_node("F", QueueFamilyType::Compute, PhantomData)
2014            .build();
2015        graph.add_edge(a, b).unwrap();
2016        graph.add_edge(a, d).unwrap();
2017        graph.add_edge(b, c).unwrap();
2018        graph.add_edge(c, d).unwrap();
2019        graph.add_edge(c, f).unwrap();
2020        graph.add_edge(d, c).unwrap();
2021        graph.add_edge(d, e).unwrap();
2022        graph.add_edge(f, b).unwrap();
2023
2024        let err = unsafe {
2025            graph.compile(&CompileInfo {
2026                queues: &queues.iter().collect::<Vec<_>>(),
2027                ..Default::default()
2028            })
2029        }
2030        .unwrap_err();
2031
2032        assert_eq!(err.kind, CompileErrorKind::Cycle);
2033    }
2034
2035    #[test]
2036    fn initial_pipeline_barrier() {
2037        let (resources, queues) = test_queues!();
2038
2039        let mut graph = TaskGraph::<()>::new(&resources, 10, 10);
2040        let buffer = graph.add_buffer(&BufferCreateInfo::default());
2041        let image = graph.add_image(&ImageCreateInfo::default());
2042        let node1 = graph
2043            .create_task_node("", QueueFamilyType::Graphics, PhantomData)
2044            .buffer_access(buffer, AccessTypes::VERTEX_SHADER_UNIFORM_READ)
2045            .image_access(
2046                image,
2047                AccessTypes::FRAGMENT_SHADER_SAMPLED_READ,
2048                ImageLayoutType::General,
2049            )
2050            .build();
2051        let node2 = graph
2052            .create_task_node("", QueueFamilyType::Graphics, PhantomData)
2053            .buffer_access(buffer, AccessTypes::VERTEX_SHADER_SAMPLED_READ)
2054            .image_access(
2055                image,
2056                AccessTypes::FRAGMENT_SHADER_STORAGE_READ,
2057                ImageLayoutType::General,
2058            )
2059            .build();
2060        graph.add_edge(node1, node2).unwrap();
2061
2062        let graph = unsafe {
2063            graph.compile(&CompileInfo {
2064                queues: &queues.iter().collect::<Vec<_>>(),
2065                ..Default::default()
2066            })
2067        }
2068        .unwrap();
2069
2070        assert_matches_instructions!(
2071            graph,
2072            InitialPipelineBarrier {
2073                barriers: [
2074                    {
2075                        dst_stage_mask: VERTEX_SHADER,
2076                        dst_access_mask: UNIFORM_READ | SHADER_SAMPLED_READ,
2077                        new_layout: Undefined,
2078                        resource: buffer,
2079                    },
2080                    {
2081                        dst_stage_mask: FRAGMENT_SHADER,
2082                        dst_access_mask: SHADER_SAMPLED_READ | SHADER_STORAGE_READ,
2083                        new_layout: General,
2084                        resource: image,
2085                    },
2086                ],
2087            },
2088            ExecuteTask { node: node1 },
2089            ExecuteTask { node: node2 },
2090            FlushSubmit,
2091            Submit,
2092        );
2093    }
2094
2095    #[test]
2096    fn barrier1() {
2097        let (resources, queues) = test_queues!();
2098
2099        let mut graph = TaskGraph::<()>::new(&resources, 10, 10);
2100        let buffer = graph.add_buffer(&BufferCreateInfo::default());
2101        let image = graph.add_image(&ImageCreateInfo::default());
2102        let node1 = graph
2103            .create_task_node("", QueueFamilyType::Graphics, PhantomData)
2104            .buffer_access(buffer, AccessTypes::FRAGMENT_SHADER_STORAGE_WRITE)
2105            .image_access(
2106                image,
2107                AccessTypes::FRAGMENT_SHADER_STORAGE_WRITE,
2108                ImageLayoutType::General,
2109            )
2110            .build();
2111        let node2 = graph
2112            .create_task_node("", QueueFamilyType::Graphics, PhantomData)
2113            .buffer_access(buffer, AccessTypes::INDIRECT_COMMAND_READ)
2114            .image_access(
2115                image,
2116                AccessTypes::FRAGMENT_SHADER_COLOR_INPUT_ATTACHMENT_READ,
2117                ImageLayoutType::General,
2118            )
2119            .build();
2120        let node3 = graph
2121            .create_task_node("", QueueFamilyType::Graphics, PhantomData)
2122            .buffer_access(buffer, AccessTypes::RAY_TRACING_SHADER_STORAGE_READ)
2123            .image_access(
2124                image,
2125                AccessTypes::RAY_TRACING_SHADER_COLOR_INPUT_ATTACHMENT_READ,
2126                ImageLayoutType::General,
2127            )
2128            .build();
2129        graph.add_edge(node1, node2).unwrap();
2130        graph.add_edge(node2, node3).unwrap();
2131
2132        let graph = unsafe {
2133            graph.compile(&CompileInfo {
2134                queues: &queues.iter().collect::<Vec<_>>(),
2135                ..Default::default()
2136            })
2137        }
2138        .unwrap();
2139
2140        assert_matches_instructions!(
2141            graph,
2142            ExecuteTask { node: node1 },
2143            PipelineBarrier {
2144                barriers: [
2145                    {
2146                        src_stage_mask: FRAGMENT_SHADER,
2147                        src_access_mask: SHADER_STORAGE_WRITE,
2148                        dst_stage_mask: DRAW_INDIRECT | RAY_TRACING_SHADER,
2149                        dst_access_mask: INDIRECT_COMMAND_READ | SHADER_STORAGE_READ,
2150                        old_layout: Undefined,
2151                        new_layout: Undefined,
2152                        resource: buffer,
2153                    },
2154                    {
2155                        src_stage_mask: FRAGMENT_SHADER,
2156                        src_access_mask: SHADER_STORAGE_WRITE,
2157                        dst_stage_mask: FRAGMENT_SHADER | RAY_TRACING_SHADER,
2158                        dst_access_mask: INPUT_ATTACHMENT_READ,
2159                        old_layout: General,
2160                        new_layout: General,
2161                        resource: image,
2162                    },
2163                ],
2164            },
2165            ExecuteTask { node: node2 },
2166            ExecuteTask { node: node3 },
2167            FlushSubmit,
2168            Submit,
2169        );
2170    }
2171
2172    #[test]
2173    fn barrier2() {
2174        let (resources, queues) = test_queues!();
2175
2176        let mut graph = TaskGraph::<()>::new(&resources, 10, 10);
2177        let buffer = graph.add_buffer(&BufferCreateInfo::default());
2178        let image = graph.add_image(&ImageCreateInfo::default());
2179        let node1 = graph
2180            .create_task_node("", QueueFamilyType::Compute, PhantomData)
2181            .buffer_access(buffer, AccessTypes::COMPUTE_SHADER_STORAGE_WRITE)
2182            .image_access(
2183                image,
2184                AccessTypes::COPY_TRANSFER_WRITE,
2185                ImageLayoutType::General,
2186            )
2187            .build();
2188        let node2 = graph
2189            .create_task_node("", QueueFamilyType::Compute, PhantomData)
2190            .buffer_access(buffer, AccessTypes::COPY_TRANSFER_WRITE)
2191            .image_access(
2192                image,
2193                AccessTypes::COMPUTE_SHADER_STORAGE_WRITE,
2194                ImageLayoutType::General,
2195            )
2196            .build();
2197        let node3 = graph
2198            .create_task_node("", QueueFamilyType::Compute, PhantomData)
2199            .buffer_access(buffer, AccessTypes::INDIRECT_COMMAND_READ)
2200            .image_access(
2201                image,
2202                AccessTypes::COMPUTE_SHADER_SAMPLED_READ,
2203                ImageLayoutType::General,
2204            )
2205            .build();
2206        graph.add_edge(node1, node2).unwrap();
2207        graph.add_edge(node2, node3).unwrap();
2208
2209        let graph = unsafe {
2210            graph.compile(&CompileInfo {
2211                queues: &queues.iter().collect::<Vec<_>>(),
2212                ..Default::default()
2213            })
2214        }
2215        .unwrap();
2216
2217        assert_matches_instructions!(
2218            graph,
2219            ExecuteTask { node: node1 },
2220            PipelineBarrier {
2221                barriers: [
2222                    {
2223                        src_stage_mask: COMPUTE_SHADER,
2224                        src_access_mask: SHADER_STORAGE_WRITE,
2225                        dst_stage_mask: COPY,
2226                        dst_access_mask: TRANSFER_WRITE,
2227                        old_layout: Undefined,
2228                        new_layout: Undefined,
2229                        resource: buffer,
2230                    },
2231                    {
2232                        src_stage_mask: COPY,
2233                        src_access_mask: TRANSFER_WRITE,
2234                        dst_stage_mask: COMPUTE_SHADER,
2235                        dst_access_mask: SHADER_STORAGE_WRITE,
2236                        old_layout: General,
2237                        new_layout: General,
2238                        resource: image,
2239                    },
2240                ],
2241            },
2242            ExecuteTask { node: node2 },
2243            PipelineBarrier {
2244                barriers: [
2245                    {
2246                        src_stage_mask: COPY,
2247                        src_access_mask: TRANSFER_WRITE,
2248                        dst_stage_mask: DRAW_INDIRECT,
2249                        dst_access_mask: INDIRECT_COMMAND_READ,
2250                        old_layout: Undefined,
2251                        new_layout: Undefined,
2252                        resource: buffer,
2253                    },
2254                    {
2255                        src_stage_mask: COMPUTE_SHADER,
2256                        src_access_mask: SHADER_STORAGE_WRITE,
2257                        dst_stage_mask: COMPUTE_SHADER,
2258                        dst_access_mask: SHADER_SAMPLED_READ,
2259                        old_layout: General,
2260                        new_layout: General,
2261                        resource: image,
2262                    },
2263                ],
2264            },
2265            ExecuteTask { node: node3 },
2266            FlushSubmit,
2267            Submit,
2268        );
2269    }
2270
2271    #[test]
2272    fn semaphore1() {
2273        let (resources, queues) = test_queues!();
2274
2275        if !has_compute_only_queue(&queues) {
2276            return;
2277        }
2278
2279        // ┌───┐
2280        // │ A ├─┐
2281        // └───┘ │
2282        // ┌───┐ │
2283        // │ B ├┐│
2284        // └───┘││
2285        // ┄┄┄┄┄││┄┄┄┄┄┄
2286        //      │└►┌───┐
2287        //      └─►│ C │
2288        //         └───┘
2289        let mut graph = TaskGraph::<()>::new(&resources, 10, 10);
2290        let a = graph
2291            .create_task_node("A", QueueFamilyType::Graphics, PhantomData)
2292            .build();
2293        let b = graph
2294            .create_task_node("B", QueueFamilyType::Graphics, PhantomData)
2295            .build();
2296        let c = graph
2297            .create_task_node("C", QueueFamilyType::Compute, PhantomData)
2298            .build();
2299        graph.add_edge(a, c).unwrap();
2300        graph.add_edge(b, c).unwrap();
2301
2302        let graph = unsafe {
2303            graph.compile(&CompileInfo {
2304                queues: &queues.iter().collect::<Vec<_>>(),
2305                ..Default::default()
2306            })
2307        }
2308        .unwrap();
2309
2310        assert_matches_instructions!(
2311            graph,
2312            ExecuteTask { node: b },
2313            // TODO: This semaphore is redundant.
2314            SignalSemaphore {
2315                semaphore_index: semaphore1,
2316                stage_mask: ALL_COMMANDS,
2317            },
2318            FlushSubmit,
2319            ExecuteTask { node: a },
2320            SignalSemaphore {
2321                semaphore_index: semaphore2,
2322                stage_mask: ALL_COMMANDS,
2323            },
2324            FlushSubmit,
2325            Submit,
2326            WaitSemaphore {
2327                semaphore_index: semaphore1,
2328                stage_mask: ALL_COMMANDS,
2329            },
2330            WaitSemaphore {
2331                semaphore_index: semaphore2,
2332                stage_mask: ALL_COMMANDS,
2333            },
2334            ExecuteTask { node: c },
2335            FlushSubmit,
2336            Submit,
2337        );
2338    }
2339
2340    #[test]
2341    fn semaphore2() {
2342        let (resources, queues) = test_queues!();
2343
2344        if !has_compute_only_queue(&queues) {
2345            return;
2346        }
2347
2348        // ┌───┐
2349        // │ A ├┐
2350        // └───┘│
2351        // ┄┄┄┄┄│┄┄┄┄┄┄
2352        //      │ ┌───┐
2353        //      ├►│ B │
2354        //      │ └───┘
2355        //      │ ┌───┐
2356        //      └►│ C │
2357        //        └───┘
2358        let mut graph = TaskGraph::<()>::new(&resources, 10, 10);
2359        let a = graph
2360            .create_task_node("A", QueueFamilyType::Graphics, PhantomData)
2361            .build();
2362        let b = graph
2363            .create_task_node("B", QueueFamilyType::Compute, PhantomData)
2364            .build();
2365        let c = graph
2366            .create_task_node("C", QueueFamilyType::Compute, PhantomData)
2367            .build();
2368        graph.add_edge(a, b).unwrap();
2369        graph.add_edge(a, c).unwrap();
2370
2371        let graph = unsafe {
2372            graph.compile(&CompileInfo {
2373                queues: &queues.iter().collect::<Vec<_>>(),
2374                ..Default::default()
2375            })
2376        }
2377        .unwrap();
2378
2379        assert_matches_instructions!(
2380            graph,
2381            ExecuteTask { node: a },
2382            // TODO: This semaphore is redundant.
2383            SignalSemaphore {
2384                semaphore_index: semaphore1,
2385                stage_mask: ALL_COMMANDS,
2386            },
2387            SignalSemaphore {
2388                semaphore_index: semaphore2,
2389                stage_mask: ALL_COMMANDS,
2390            },
2391            FlushSubmit,
2392            Submit,
2393            WaitSemaphore {
2394                semaphore_index: semaphore2,
2395                stage_mask: ALL_COMMANDS,
2396            },
2397            ExecuteTask { node: c },
2398            FlushSubmit,
2399            WaitSemaphore {
2400                semaphore_index: semaphore1,
2401                stage_mask: ALL_COMMANDS,
2402            },
2403            ExecuteTask { node: b },
2404            FlushSubmit,
2405            Submit,
2406        );
2407    }
2408
2409    #[test]
2410    fn semaphore3() {
2411        let (resources, queues) = test_queues!();
2412
2413        if !has_compute_only_queue(&queues) {
2414            return;
2415        }
2416
2417        // ┌───┐                ┌───┐
2418        // │ A ├───────┬───────►│ E │
2419        // └───┘       │      ┌►└───┘
2420        // ┌───┐       │      │
2421        // │ B ├┐      │      │
2422        // └───┘│      │      │
2423        // ┄┄┄┄┄│┄┄┄┄┄┄│┄┄┄┄┄┄│┄┄┄┄┄┄
2424        //      │ ┌───┐└►┌───┐│
2425        //      └►│ C ├─►│ D ├┘
2426        //        └───┘  └───┘
2427        let mut graph = TaskGraph::<()>::new(&resources, 10, 10);
2428        let a = graph
2429            .create_task_node("A", QueueFamilyType::Graphics, PhantomData)
2430            .build();
2431        let b = graph
2432            .create_task_node("B", QueueFamilyType::Graphics, PhantomData)
2433            .build();
2434        let c = graph
2435            .create_task_node("C", QueueFamilyType::Compute, PhantomData)
2436            .build();
2437        let d = graph
2438            .create_task_node("D", QueueFamilyType::Compute, PhantomData)
2439            .build();
2440        let e = graph
2441            .create_task_node("E", QueueFamilyType::Graphics, PhantomData)
2442            .build();
2443        graph.add_edge(a, d).unwrap();
2444        graph.add_edge(a, e).unwrap();
2445        graph.add_edge(b, c).unwrap();
2446        graph.add_edge(c, d).unwrap();
2447        graph.add_edge(d, e).unwrap();
2448
2449        let graph = unsafe {
2450            graph.compile(&CompileInfo {
2451                queues: &queues.iter().collect::<Vec<_>>(),
2452                ..Default::default()
2453            })
2454        }
2455        .unwrap();
2456
2457        // TODO: This could be brought down to 3 submissions with task reordering.
2458        assert_matches_instructions!(
2459            graph,
2460            ExecuteTask { node: b },
2461            SignalSemaphore {
2462                semaphore_index: semaphore1,
2463                stage_mask: ALL_COMMANDS,
2464            },
2465            FlushSubmit,
2466            Submit,
2467            WaitSemaphore {
2468                semaphore_index: semaphore1,
2469                stage_mask: ALL_COMMANDS,
2470            },
2471            ExecuteTask { node: c },
2472            FlushSubmit,
2473            Submit,
2474            ExecuteTask { node: a },
2475            SignalSemaphore {
2476                semaphore_index: semaphore2,
2477                stage_mask: ALL_COMMANDS,
2478            },
2479            FlushSubmit,
2480            Submit,
2481            WaitSemaphore {
2482                semaphore_index: semaphore2,
2483                stage_mask: ALL_COMMANDS,
2484            },
2485            ExecuteTask { node: d },
2486            SignalSemaphore {
2487                semaphore_index: semaphore3,
2488                stage_mask: ALL_COMMANDS,
2489            },
2490            FlushSubmit,
2491            Submit,
2492            WaitSemaphore {
2493                semaphore_index: semaphore3,
2494                stage_mask: ALL_COMMANDS,
2495            },
2496            ExecuteTask { node: e },
2497            FlushSubmit,
2498            Submit,
2499        );
2500    }
2501
2502    #[test]
2503    fn render_pass1() {
2504        let (resources, queues) = test_queues!();
2505
2506        let mut graph = TaskGraph::<()>::new(&resources, 10, 10);
2507        let color_image = graph.add_image(&ImageCreateInfo {
2508            format: Format::R8G8B8A8_UNORM,
2509            ..Default::default()
2510        });
2511        let depth_image = graph.add_image(&ImageCreateInfo {
2512            format: Format::D16_UNORM,
2513            ..Default::default()
2514        });
2515        let framebuffer = graph.add_framebuffer();
2516        let node1 = graph
2517            .create_task_node("", QueueFamilyType::Graphics, PhantomData)
2518            .framebuffer(framebuffer)
2519            .color_attachment(
2520                color_image,
2521                AccessTypes::COLOR_ATTACHMENT_READ | AccessTypes::COLOR_ATTACHMENT_WRITE,
2522                ImageLayoutType::Optimal,
2523                &AttachmentInfo {
2524                    clear: true,
2525                    ..Default::default()
2526                },
2527            )
2528            .build();
2529        let node2 = graph
2530            .create_task_node("", QueueFamilyType::Graphics, PhantomData)
2531            .framebuffer(framebuffer)
2532            .color_attachment(
2533                color_image,
2534                AccessTypes::COLOR_ATTACHMENT_WRITE,
2535                ImageLayoutType::Optimal,
2536                &AttachmentInfo::default(),
2537            )
2538            .depth_stencil_attachment(
2539                depth_image,
2540                AccessTypes::DEPTH_STENCIL_ATTACHMENT_READ,
2541                ImageLayoutType::Optimal,
2542                &AttachmentInfo {
2543                    clear: true,
2544                    ..Default::default()
2545                },
2546            )
2547            .build();
2548        graph.add_edge(node1, node2).unwrap();
2549
2550        let graph = unsafe {
2551            graph.compile(&CompileInfo {
2552                queues: &queues.iter().collect::<Vec<_>>(),
2553                ..Default::default()
2554            })
2555        }
2556        .unwrap();
2557
2558        assert_matches_instructions!(
2559            graph,
2560            BeginRenderPass,
2561            ExecuteTask { node: node1 },
2562            NextSubpass,
2563            ExecuteTask { node: node2 },
2564            EndRenderPass,
2565            FlushSubmit,
2566            Submit,
2567        );
2568    }
2569
2570    #[test]
2571    fn render_pass2() {
2572        let (resources, queues) = test_queues!();
2573
2574        let mut graph = TaskGraph::<()>::new(&resources, 10, 10);
2575        let color_image = graph.add_image(&ImageCreateInfo {
2576            format: Format::R8G8B8A8_UNORM,
2577            ..Default::default()
2578        });
2579        let depth_image = graph.add_image(&ImageCreateInfo {
2580            format: Format::D16_UNORM,
2581            ..Default::default()
2582        });
2583        let framebuffer = graph.add_framebuffer();
2584        let node1 = graph
2585            .create_task_node("", QueueFamilyType::Graphics, PhantomData)
2586            .framebuffer(framebuffer)
2587            .color_attachment(
2588                color_image,
2589                AccessTypes::COLOR_ATTACHMENT_READ | AccessTypes::COLOR_ATTACHMENT_WRITE,
2590                ImageLayoutType::Optimal,
2591                &AttachmentInfo {
2592                    clear: true,
2593                    ..Default::default()
2594                },
2595            )
2596            .depth_stencil_attachment(
2597                depth_image,
2598                AccessTypes::DEPTH_STENCIL_ATTACHMENT_READ
2599                    | AccessTypes::DEPTH_STENCIL_ATTACHMENT_WRITE,
2600                ImageLayoutType::Optimal,
2601                &AttachmentInfo {
2602                    clear: true,
2603                    ..Default::default()
2604                },
2605            )
2606            .build();
2607        let node2 = graph
2608            .create_task_node("", QueueFamilyType::Graphics, PhantomData)
2609            .framebuffer(framebuffer)
2610            .input_attachment(
2611                color_image,
2612                AccessTypes::FRAGMENT_SHADER_COLOR_INPUT_ATTACHMENT_READ,
2613                ImageLayoutType::General,
2614                &AttachmentInfo {
2615                    clear: true,
2616                    ..Default::default()
2617                },
2618            )
2619            .color_attachment(
2620                color_image,
2621                AccessTypes::COLOR_ATTACHMENT_WRITE,
2622                ImageLayoutType::General,
2623                &AttachmentInfo {
2624                    clear: true,
2625                    ..Default::default()
2626                },
2627            )
2628            .depth_stencil_attachment(
2629                depth_image,
2630                AccessTypes::DEPTH_STENCIL_ATTACHMENT_READ,
2631                ImageLayoutType::Optimal,
2632                &AttachmentInfo {
2633                    clear: true,
2634                    ..Default::default()
2635                },
2636            )
2637            .build();
2638        graph.add_edge(node1, node2).unwrap();
2639
2640        let graph = unsafe {
2641            graph.compile(&CompileInfo {
2642                queues: &queues.iter().collect::<Vec<_>>(),
2643                ..Default::default()
2644            })
2645        }
2646        .unwrap();
2647
2648        assert_matches_instructions!(
2649            graph,
2650            BeginRenderPass,
2651            ExecuteTask { node: node1 },
2652            NextSubpass,
2653            ClearAttachments {
2654                node: node2,
2655                attachments: [color_image, depth_image],
2656            },
2657            ExecuteTask { node: node2 },
2658            EndRenderPass,
2659            FlushSubmit,
2660            Submit,
2661        );
2662    }
2663
2664    #[test]
2665    fn render_pass3() {
2666        let (resources, queues) = test_queues!();
2667
2668        let mut graph = TaskGraph::<()>::new(&resources, 10, 10);
2669        let color_image = graph.add_image(&ImageCreateInfo {
2670            format: Format::R8G8B8A8_UNORM,
2671            ..Default::default()
2672        });
2673        let depth_image = graph.add_image(&ImageCreateInfo {
2674            format: Format::D16_UNORM,
2675            ..Default::default()
2676        });
2677        let framebuffer = graph.add_framebuffer();
2678        let node1 = graph
2679            .create_task_node("", QueueFamilyType::Graphics, PhantomData)
2680            .framebuffer(framebuffer)
2681            .color_attachment(
2682                color_image,
2683                AccessTypes::COLOR_ATTACHMENT_READ | AccessTypes::COLOR_ATTACHMENT_WRITE,
2684                ImageLayoutType::Optimal,
2685                &AttachmentInfo {
2686                    clear: true,
2687                    ..Default::default()
2688                },
2689            )
2690            .depth_stencil_attachment(
2691                depth_image,
2692                AccessTypes::DEPTH_STENCIL_ATTACHMENT_READ
2693                    | AccessTypes::DEPTH_STENCIL_ATTACHMENT_WRITE,
2694                ImageLayoutType::Optimal,
2695                &AttachmentInfo {
2696                    clear: true,
2697                    ..Default::default()
2698                },
2699            )
2700            .build();
2701        let node2 = graph
2702            .create_task_node("", QueueFamilyType::Graphics, PhantomData)
2703            .framebuffer(framebuffer)
2704            .color_attachment(
2705                color_image,
2706                AccessTypes::COLOR_ATTACHMENT_WRITE,
2707                ImageLayoutType::Optimal,
2708                &AttachmentInfo {
2709                    clear: true,
2710                    ..Default::default()
2711                },
2712            )
2713            .depth_stencil_attachment(
2714                depth_image,
2715                AccessTypes::DEPTH_STENCIL_ATTACHMENT_READ,
2716                ImageLayoutType::Optimal,
2717                &AttachmentInfo {
2718                    clear: true,
2719                    ..Default::default()
2720                },
2721            )
2722            .build();
2723        let node3 = graph
2724            .create_task_node("", QueueFamilyType::Graphics, PhantomData)
2725            .framebuffer(framebuffer)
2726            .input_attachment(
2727                color_image,
2728                AccessTypes::FRAGMENT_SHADER_COLOR_INPUT_ATTACHMENT_READ,
2729                ImageLayoutType::Optimal,
2730                &AttachmentInfo::default(),
2731            )
2732            .build();
2733        graph.add_edge(node1, node2).unwrap();
2734        graph.add_edge(node2, node3).unwrap();
2735
2736        let graph = unsafe {
2737            graph.compile(&CompileInfo {
2738                queues: &queues.iter().collect::<Vec<_>>(),
2739                ..Default::default()
2740            })
2741        }
2742        .unwrap();
2743
2744        assert_matches_instructions!(
2745            graph,
2746            BeginRenderPass,
2747            ExecuteTask { node: node1 },
2748            ClearAttachments {
2749                node: node2,
2750                attachments: [color_image, depth_image],
2751            },
2752            ExecuteTask { node: node2 },
2753            NextSubpass,
2754            ExecuteTask { node: node3 },
2755            EndRenderPass,
2756            FlushSubmit,
2757            Submit,
2758        );
2759    }
2760
2761    #[test]
2762    fn queue_family_ownership_transfer1() {
2763        let (resources, queues) = test_queues!();
2764
2765        if !has_compute_only_queue(&queues) {
2766            return;
2767        }
2768
2769        let mut graph = TaskGraph::<()>::new(&resources, 10, 10);
2770        let buffer1 = graph.add_buffer(&BufferCreateInfo::default());
2771        let buffer2 = graph.add_buffer(&BufferCreateInfo::default());
2772        let image1 = graph.add_image(&ImageCreateInfo::default());
2773        let image2 = graph.add_image(&ImageCreateInfo::default());
2774        let compute_node = graph
2775            .create_task_node("", QueueFamilyType::Compute, PhantomData)
2776            .buffer_access(buffer1, AccessTypes::COMPUTE_SHADER_STORAGE_WRITE)
2777            .buffer_access(buffer2, AccessTypes::COMPUTE_SHADER_STORAGE_READ)
2778            .image_access(
2779                image1,
2780                AccessTypes::COMPUTE_SHADER_STORAGE_WRITE,
2781                ImageLayoutType::Optimal,
2782            )
2783            .image_access(
2784                image2,
2785                AccessTypes::COMPUTE_SHADER_SAMPLED_READ,
2786                ImageLayoutType::Optimal,
2787            )
2788            .build();
2789        let graphics_node = graph
2790            .create_task_node("", QueueFamilyType::Graphics, PhantomData)
2791            .buffer_access(buffer1, AccessTypes::INDEX_READ)
2792            .buffer_access(buffer2, AccessTypes::VERTEX_SHADER_UNIFORM_READ)
2793            .image_access(
2794                image1,
2795                AccessTypes::VERTEX_SHADER_SAMPLED_READ,
2796                ImageLayoutType::General,
2797            )
2798            .image_access(
2799                image2,
2800                AccessTypes::FRAGMENT_SHADER_SAMPLED_READ,
2801                ImageLayoutType::General,
2802            )
2803            .build();
2804        graph.add_edge(compute_node, graphics_node).unwrap();
2805
2806        let graph = unsafe {
2807            graph.compile(&CompileInfo {
2808                queues: &queues.iter().collect::<Vec<_>>(),
2809                ..Default::default()
2810            })
2811        }
2812        .unwrap();
2813
2814        assert_matches_instructions!(
2815            graph,
2816            ExecuteTask { node: compute_node },
2817            SignalSemaphore {
2818                semaphore_index: semaphore,
2819                stage_mask: ALL_COMMANDS,
2820            },
2821            PipelineBarrier {
2822                barriers: [
2823                    {
2824                        src_stage_mask: COMPUTE_SHADER,
2825                        src_access_mask: SHADER_STORAGE_WRITE,
2826                        dst_stage_mask: ,
2827                        dst_access_mask: ,
2828                        old_layout: Undefined,
2829                        new_layout: Undefined,
2830                        resource: buffer1,
2831                    },
2832                    {
2833                        src_stage_mask: COMPUTE_SHADER,
2834                        src_access_mask: ,
2835                        dst_stage_mask: ,
2836                        dst_access_mask: ,
2837                        old_layout: Undefined,
2838                        new_layout: Undefined,
2839                        resource: buffer2,
2840                    },
2841                    {
2842                        src_stage_mask: COMPUTE_SHADER,
2843                        src_access_mask: SHADER_STORAGE_WRITE,
2844                        dst_stage_mask: ,
2845                        dst_access_mask: ,
2846                        old_layout: General,
2847                        new_layout: General,
2848                        resource: image1,
2849                    },
2850                    {
2851                        src_stage_mask: COMPUTE_SHADER,
2852                        src_access_mask: ,
2853                        dst_stage_mask: ,
2854                        dst_access_mask: ,
2855                        old_layout: ShaderReadOnlyOptimal,
2856                        new_layout: General,
2857                        resource: image2,
2858                    },
2859                ],
2860            },
2861            FlushSubmit,
2862            Submit,
2863            WaitSemaphore {
2864                semaphore_index: semaphore,
2865                stage_mask: ALL_COMMANDS,
2866            },
2867            PipelineBarrier {
2868                barriers: [
2869                    {
2870                        src_stage_mask: ,
2871                        src_access_mask: ,
2872                        dst_stage_mask: INDEX_INPUT,
2873                        dst_access_mask: INDEX_READ,
2874                        old_layout: Undefined,
2875                        new_layout: Undefined,
2876                        resource: buffer1,
2877                    },
2878                    {
2879                        src_stage_mask: ,
2880                        src_access_mask: ,
2881                        dst_stage_mask: VERTEX_SHADER,
2882                        dst_access_mask: UNIFORM_READ,
2883                        old_layout: Undefined,
2884                        new_layout: Undefined,
2885                        resource: buffer2,
2886                    },
2887                    {
2888                        src_stage_mask: ,
2889                        src_access_mask: ,
2890                        dst_stage_mask: VERTEX_SHADER,
2891                        dst_access_mask: SHADER_SAMPLED_READ,
2892                        old_layout: General,
2893                        new_layout: General,
2894                        resource: image1,
2895                    },
2896                    {
2897                        src_stage_mask: ,
2898                        src_access_mask: ,
2899                        dst_stage_mask: FRAGMENT_SHADER,
2900                        dst_access_mask: SHADER_SAMPLED_READ,
2901                        old_layout: ShaderReadOnlyOptimal,
2902                        new_layout: General,
2903                        resource: image2,
2904                    },
2905                ],
2906            },
2907            ExecuteTask { node: graphics_node },
2908            FlushSubmit,
2909            Submit,
2910        );
2911    }
2912
2913    #[test]
2914    fn queue_family_ownership_transfer2() {
2915        let (resources, queues) = test_queues!();
2916
2917        if !has_compute_only_queue(&queues) {
2918            return;
2919        }
2920
2921        let mut graph = TaskGraph::<()>::new(&resources, 10, 10);
2922        let sharing = Sharing::Concurrent(queues.iter().map(|q| q.queue_family_index()).collect());
2923        let buffer1 = graph.add_buffer(&BufferCreateInfo {
2924            sharing: sharing.clone(),
2925            ..Default::default()
2926        });
2927        let buffer2 = graph.add_buffer(&BufferCreateInfo {
2928            sharing: sharing.clone(),
2929            ..Default::default()
2930        });
2931        let image1 = graph.add_image(&ImageCreateInfo {
2932            sharing: sharing.clone(),
2933            ..Default::default()
2934        });
2935        let image2 = graph.add_image(&ImageCreateInfo {
2936            sharing: sharing.clone(),
2937            ..Default::default()
2938        });
2939        let compute_node = graph
2940            .create_task_node("", QueueFamilyType::Compute, PhantomData)
2941            .buffer_access(buffer1, AccessTypes::COMPUTE_SHADER_STORAGE_WRITE)
2942            .buffer_access(buffer2, AccessTypes::COMPUTE_SHADER_STORAGE_READ)
2943            .image_access(
2944                image1,
2945                AccessTypes::COMPUTE_SHADER_STORAGE_WRITE,
2946                ImageLayoutType::Optimal,
2947            )
2948            .image_access(
2949                image2,
2950                AccessTypes::COMPUTE_SHADER_SAMPLED_READ,
2951                ImageLayoutType::Optimal,
2952            )
2953            .build();
2954        let graphics_node = graph
2955            .create_task_node("", QueueFamilyType::Graphics, PhantomData)
2956            .buffer_access(buffer1, AccessTypes::INDEX_READ)
2957            .buffer_access(buffer2, AccessTypes::VERTEX_SHADER_UNIFORM_READ)
2958            .image_access(
2959                image1,
2960                AccessTypes::VERTEX_SHADER_SAMPLED_READ,
2961                ImageLayoutType::General,
2962            )
2963            .image_access(
2964                image2,
2965                AccessTypes::FRAGMENT_SHADER_SAMPLED_READ,
2966                ImageLayoutType::General,
2967            )
2968            .build();
2969        graph.add_edge(compute_node, graphics_node).unwrap();
2970
2971        let graph = unsafe {
2972            graph.compile(&CompileInfo {
2973                queues: &queues.iter().collect::<Vec<_>>(),
2974                ..Default::default()
2975            })
2976        }
2977        .unwrap();
2978
2979        assert_matches_instructions!(
2980            graph,
2981            ExecuteTask { node: compute_node },
2982            SignalSemaphore {
2983                semaphore_index: semaphore,
2984                stage_mask: ALL_COMMANDS,
2985            },
2986            FlushSubmit,
2987            Submit,
2988            WaitSemaphore {
2989                semaphore_index: semaphore,
2990                stage_mask: ALL_COMMANDS,
2991            },
2992            PipelineBarrier {
2993                barriers: [
2994                    {
2995                        src_stage_mask: ,
2996                        src_access_mask: ,
2997                        dst_stage_mask: FRAGMENT_SHADER,
2998                        dst_access_mask: SHADER_SAMPLED_READ,
2999                        old_layout: ShaderReadOnlyOptimal,
3000                        new_layout: General,
3001                        resource: image2,
3002                    },
3003                ],
3004            },
3005            ExecuteTask { node: graphics_node },
3006            FlushSubmit,
3007            Submit,
3008        );
3009    }
3010
3011    #[test]
3012    fn queue_family_ownership_transfer3() {
3013        let (resources, queues) = test_queues!();
3014
3015        if !has_compute_only_queue(&queues) {
3016            return;
3017        }
3018
3019        let mut graph = TaskGraph::<()>::new(&resources, 10, 10);
3020        let buffer1 = graph.add_buffer(&BufferCreateInfo::default());
3021        let buffer2 = graph.add_buffer(&BufferCreateInfo::default());
3022        let image1 = graph.add_image(&ImageCreateInfo::default());
3023        let image2 = graph.add_image(&ImageCreateInfo::default());
3024        let color_image = graph.add_image(&ImageCreateInfo {
3025            format: Format::R8G8B8A8_UNORM,
3026            ..Default::default()
3027        });
3028        let depth_image = graph.add_image(&ImageCreateInfo {
3029            format: Format::D16_UNORM,
3030            ..Default::default()
3031        });
3032        let framebuffer = graph.add_framebuffer();
3033        let compute_node = graph
3034            .create_task_node("", QueueFamilyType::Compute, PhantomData)
3035            .buffer_access(buffer1, AccessTypes::COMPUTE_SHADER_STORAGE_WRITE)
3036            .buffer_access(buffer2, AccessTypes::COMPUTE_SHADER_STORAGE_READ)
3037            .image_access(
3038                image1,
3039                AccessTypes::COMPUTE_SHADER_STORAGE_WRITE,
3040                ImageLayoutType::Optimal,
3041            )
3042            .image_access(
3043                image2,
3044                AccessTypes::COMPUTE_SHADER_SAMPLED_READ,
3045                ImageLayoutType::Optimal,
3046            )
3047            .build();
3048        let graphics_node1 = graph
3049            .create_task_node("", QueueFamilyType::Graphics, PhantomData)
3050            .framebuffer(framebuffer)
3051            .color_attachment(
3052                color_image,
3053                AccessTypes::COLOR_ATTACHMENT_WRITE,
3054                ImageLayoutType::Optimal,
3055                &AttachmentInfo::default(),
3056            )
3057            .depth_stencil_attachment(
3058                depth_image,
3059                AccessTypes::DEPTH_STENCIL_ATTACHMENT_WRITE,
3060                ImageLayoutType::Optimal,
3061                &AttachmentInfo::default(),
3062            )
3063            .build();
3064        let graphics_node2 = graph
3065            .create_task_node("", QueueFamilyType::Graphics, PhantomData)
3066            .framebuffer(framebuffer)
3067            .color_attachment(
3068                color_image,
3069                AccessTypes::COLOR_ATTACHMENT_WRITE,
3070                ImageLayoutType::Optimal,
3071                &AttachmentInfo::default(),
3072            )
3073            .input_attachment(
3074                depth_image,
3075                AccessTypes::FRAGMENT_SHADER_DEPTH_STENCIL_INPUT_ATTACHMENT_READ,
3076                ImageLayoutType::Optimal,
3077                &AttachmentInfo::default(),
3078            )
3079            .buffer_access(buffer1, AccessTypes::INDEX_READ)
3080            .buffer_access(buffer2, AccessTypes::VERTEX_SHADER_UNIFORM_READ)
3081            .image_access(
3082                image1,
3083                AccessTypes::VERTEX_SHADER_SAMPLED_READ,
3084                ImageLayoutType::General,
3085            )
3086            .image_access(
3087                image2,
3088                AccessTypes::FRAGMENT_SHADER_SAMPLED_READ,
3089                ImageLayoutType::General,
3090            )
3091            .build();
3092        graph.add_edge(compute_node, graphics_node1).unwrap();
3093        graph.add_edge(graphics_node1, graphics_node2).unwrap();
3094
3095        let graph = unsafe {
3096            graph.compile(&CompileInfo {
3097                queues: &queues.iter().collect::<Vec<_>>(),
3098                ..Default::default()
3099            })
3100        }
3101        .unwrap();
3102
3103        assert_matches_instructions!(
3104            graph,
3105            ExecuteTask { node: compute_node },
3106            SignalSemaphore {
3107                semaphore_index: semaphore,
3108                stage_mask: ALL_COMMANDS,
3109            },
3110            PipelineBarrier {
3111                barriers: [
3112                    {
3113                        src_stage_mask: COMPUTE_SHADER,
3114                        src_access_mask: SHADER_STORAGE_WRITE,
3115                        dst_stage_mask: ,
3116                        dst_access_mask: ,
3117                        old_layout: Undefined,
3118                        new_layout: Undefined,
3119                        resource: buffer1,
3120                    },
3121                    {
3122                        src_stage_mask: COMPUTE_SHADER,
3123                        src_access_mask: ,
3124                        dst_stage_mask: ,
3125                        dst_access_mask: ,
3126                        old_layout: Undefined,
3127                        new_layout: Undefined,
3128                        resource: buffer2,
3129                    },
3130                    {
3131                        src_stage_mask: COMPUTE_SHADER,
3132                        src_access_mask: SHADER_STORAGE_WRITE,
3133                        dst_stage_mask: ,
3134                        dst_access_mask: ,
3135                        old_layout: General,
3136                        new_layout: General,
3137                        resource: image1,
3138                    },
3139                    {
3140                        src_stage_mask: COMPUTE_SHADER,
3141                        src_access_mask: ,
3142                        dst_stage_mask: ,
3143                        dst_access_mask: ,
3144                        old_layout: ShaderReadOnlyOptimal,
3145                        new_layout: General,
3146                        resource: image2,
3147                    },
3148                ],
3149            },
3150            FlushSubmit,
3151            Submit,
3152            WaitSemaphore {
3153                semaphore_index: semaphore,
3154                stage_mask: ALL_COMMANDS,
3155            },
3156            PipelineBarrier {
3157                barriers: [
3158                    {
3159                        src_stage_mask: ,
3160                        src_access_mask: ,
3161                        dst_stage_mask: INDEX_INPUT,
3162                        dst_access_mask: INDEX_READ,
3163                        old_layout: Undefined,
3164                        new_layout: Undefined,
3165                        resource: buffer1,
3166                    },
3167                    {
3168                        src_stage_mask: ,
3169                        src_access_mask: ,
3170                        dst_stage_mask: VERTEX_SHADER,
3171                        dst_access_mask: UNIFORM_READ,
3172                        old_layout: Undefined,
3173                        new_layout: Undefined,
3174                        resource: buffer2,
3175                    },
3176                    {
3177                        src_stage_mask: ,
3178                        src_access_mask: ,
3179                        dst_stage_mask: VERTEX_SHADER,
3180                        dst_access_mask: SHADER_SAMPLED_READ,
3181                        old_layout: General,
3182                        new_layout: General,
3183                        resource: image1,
3184                    },
3185                    {
3186                        src_stage_mask: ,
3187                        src_access_mask: ,
3188                        dst_stage_mask: FRAGMENT_SHADER,
3189                        dst_access_mask: SHADER_SAMPLED_READ,
3190                        old_layout: ShaderReadOnlyOptimal,
3191                        new_layout: General,
3192                        resource: image2,
3193                    },
3194                ],
3195            },
3196            BeginRenderPass,
3197            ExecuteTask { node: graphics_node1 },
3198            NextSubpass,
3199            ExecuteTask { node: graphics_node2 },
3200            EndRenderPass,
3201            FlushSubmit,
3202            Submit,
3203        );
3204    }
3205
3206    #[test]
3207    fn queue_family_ownership_transfer4() {
3208        let (resources, queues) = test_queues!();
3209
3210        if !has_compute_only_queue(&queues) {
3211            return;
3212        }
3213
3214        let mut graph = TaskGraph::<()>::new(&resources, 10, 10);
3215        let sharing = Sharing::Concurrent(queues.iter().map(|q| q.queue_family_index()).collect());
3216        let buffer1 = graph.add_buffer(&BufferCreateInfo {
3217            sharing: sharing.clone(),
3218            ..Default::default()
3219        });
3220        let buffer2 = graph.add_buffer(&BufferCreateInfo {
3221            sharing: sharing.clone(),
3222            ..Default::default()
3223        });
3224        let image1 = graph.add_image(&ImageCreateInfo {
3225            sharing: sharing.clone(),
3226            ..Default::default()
3227        });
3228        let image2 = graph.add_image(&ImageCreateInfo {
3229            sharing: sharing.clone(),
3230            ..Default::default()
3231        });
3232        let color_image = graph.add_image(&ImageCreateInfo {
3233            format: Format::R8G8B8A8_UNORM,
3234            ..Default::default()
3235        });
3236        let depth_image = graph.add_image(&ImageCreateInfo {
3237            format: Format::D16_UNORM,
3238            ..Default::default()
3239        });
3240        let framebuffer = graph.add_framebuffer();
3241        let compute_node = graph
3242            .create_task_node("", QueueFamilyType::Compute, PhantomData)
3243            .buffer_access(buffer1, AccessTypes::COMPUTE_SHADER_STORAGE_WRITE)
3244            .buffer_access(buffer2, AccessTypes::COMPUTE_SHADER_STORAGE_READ)
3245            .image_access(
3246                image1,
3247                AccessTypes::COMPUTE_SHADER_STORAGE_WRITE,
3248                ImageLayoutType::Optimal,
3249            )
3250            .image_access(
3251                image2,
3252                AccessTypes::COMPUTE_SHADER_SAMPLED_READ,
3253                ImageLayoutType::Optimal,
3254            )
3255            .build();
3256        let graphics_node1 = graph
3257            .create_task_node("", QueueFamilyType::Graphics, PhantomData)
3258            .framebuffer(framebuffer)
3259            .color_attachment(
3260                color_image,
3261                AccessTypes::COLOR_ATTACHMENT_WRITE,
3262                ImageLayoutType::Optimal,
3263                &AttachmentInfo::default(),
3264            )
3265            .depth_stencil_attachment(
3266                depth_image,
3267                AccessTypes::DEPTH_STENCIL_ATTACHMENT_WRITE,
3268                ImageLayoutType::Optimal,
3269                &AttachmentInfo::default(),
3270            )
3271            .build();
3272        let graphics_node2 = graph
3273            .create_task_node("", QueueFamilyType::Graphics, PhantomData)
3274            .framebuffer(framebuffer)
3275            .color_attachment(
3276                color_image,
3277                AccessTypes::COLOR_ATTACHMENT_WRITE,
3278                ImageLayoutType::Optimal,
3279                &AttachmentInfo::default(),
3280            )
3281            .input_attachment(
3282                depth_image,
3283                AccessTypes::FRAGMENT_SHADER_DEPTH_STENCIL_INPUT_ATTACHMENT_READ,
3284                ImageLayoutType::Optimal,
3285                &AttachmentInfo::default(),
3286            )
3287            .buffer_access(buffer1, AccessTypes::INDEX_READ)
3288            .buffer_access(buffer2, AccessTypes::VERTEX_SHADER_UNIFORM_READ)
3289            .image_access(
3290                image1,
3291                AccessTypes::VERTEX_SHADER_SAMPLED_READ,
3292                ImageLayoutType::General,
3293            )
3294            .image_access(
3295                image2,
3296                AccessTypes::FRAGMENT_SHADER_SAMPLED_READ,
3297                ImageLayoutType::General,
3298            )
3299            .build();
3300        graph.add_edge(compute_node, graphics_node1).unwrap();
3301        graph.add_edge(graphics_node1, graphics_node2).unwrap();
3302
3303        let graph = unsafe {
3304            graph.compile(&CompileInfo {
3305                queues: &queues.iter().collect::<Vec<_>>(),
3306                ..Default::default()
3307            })
3308        }
3309        .unwrap();
3310
3311        assert_matches_instructions!(
3312            graph,
3313            ExecuteTask {
3314                node: compute_node,
3315            },
3316            SignalSemaphore {
3317                semaphore_index: semaphore,
3318                stage_mask: ALL_COMMANDS,
3319            },
3320            FlushSubmit,
3321            Submit,
3322            WaitSemaphore {
3323                semaphore_index: semaphore,
3324                stage_mask: ALL_COMMANDS,
3325            },
3326            PipelineBarrier {
3327                barriers: [
3328                    {
3329                        src_stage_mask: ,
3330                        src_access_mask: ,
3331                        dst_stage_mask: FRAGMENT_SHADER,
3332                        dst_access_mask: SHADER_SAMPLED_READ,
3333                        old_layout: ShaderReadOnlyOptimal,
3334                        new_layout: General,
3335                        resource: image2,
3336                    },
3337                ],
3338            },
3339            BeginRenderPass,
3340            ExecuteTask { node: graphics_node1 },
3341            NextSubpass,
3342            ExecuteTask { node: graphics_node2 },
3343            EndRenderPass,
3344            FlushSubmit,
3345            Submit,
3346        );
3347    }
3348
3349    #[test]
3350    fn swapchain1() {
3351        let (resources, queues) = test_queues!();
3352
3353        let queue_family_properties = resources
3354            .device()
3355            .physical_device()
3356            .queue_family_properties();
3357
3358        let present_queue = queues.iter().find(|q| {
3359            let queue_flags = queue_family_properties[q.queue_family_index() as usize].queue_flags;
3360
3361            queue_flags.contains(QueueFlags::GRAPHICS)
3362        });
3363
3364        let mut graph = TaskGraph::<()>::new(&resources, 10, 10);
3365        let swapchain1 = graph.add_swapchain(&SwapchainCreateInfo::default());
3366        let swapchain2 = graph.add_swapchain(&SwapchainCreateInfo::default());
3367        let node = graph
3368            .create_task_node("", QueueFamilyType::Graphics, PhantomData)
3369            .image_access(
3370                swapchain1.current_image_id(),
3371                AccessTypes::COLOR_ATTACHMENT_WRITE,
3372                ImageLayoutType::Optimal,
3373            )
3374            .image_access(
3375                swapchain2.current_image_id(),
3376                AccessTypes::COMPUTE_SHADER_STORAGE_WRITE,
3377                ImageLayoutType::Optimal,
3378            )
3379            .build();
3380
3381        let graph = unsafe {
3382            graph.compile(&CompileInfo {
3383                queues: &queues.iter().collect::<Vec<_>>(),
3384                present_queue: Some(present_queue.unwrap()),
3385                ..Default::default()
3386            })
3387        }
3388        .unwrap();
3389
3390        assert_matches_instructions!(
3391            graph,
3392            WaitAcquire {
3393                swapchain_id: swapchain1,
3394                stage_mask: COLOR_ATTACHMENT_OUTPUT,
3395            },
3396            WaitAcquire {
3397                swapchain_id: swapchain2,
3398                stage_mask: COMPUTE_SHADER,
3399            },
3400            PipelineBarrier {
3401                barriers: [
3402                    {
3403                        src_stage_mask: COLOR_ATTACHMENT_OUTPUT,
3404                        src_access_mask: ,
3405                        dst_stage_mask: COLOR_ATTACHMENT_OUTPUT,
3406                        dst_access_mask: COLOR_ATTACHMENT_WRITE,
3407                        old_layout: Undefined,
3408                        new_layout: ColorAttachmentOptimal,
3409                        resource: swapchain1,
3410                    },
3411                    {
3412                        src_stage_mask: COMPUTE_SHADER,
3413                        src_access_mask: ,
3414                        dst_stage_mask: COMPUTE_SHADER,
3415                        dst_access_mask: SHADER_STORAGE_WRITE,
3416                        old_layout: Undefined,
3417                        new_layout: General,
3418                        resource: swapchain2,
3419                    },
3420                ],
3421            },
3422            ExecuteTask { node: node },
3423            SignalPresent {
3424                swapchain_id: swapchain1,
3425                stage_mask: COLOR_ATTACHMENT_OUTPUT,
3426            },
3427            SignalPresent {
3428                swapchain_id: swapchain2,
3429                stage_mask: COMPUTE_SHADER,
3430            },
3431            PipelineBarrier {
3432                barriers: [
3433                    {
3434                        src_stage_mask: COLOR_ATTACHMENT_OUTPUT,
3435                        src_access_mask: COLOR_ATTACHMENT_WRITE,
3436                        dst_stage_mask: ,
3437                        dst_access_mask: ,
3438                        old_layout: ColorAttachmentOptimal,
3439                        new_layout: PresentSrc,
3440                        resource: swapchain1,
3441                    },
3442                    {
3443                        src_stage_mask: COMPUTE_SHADER,
3444                        src_access_mask: SHADER_STORAGE_WRITE,
3445                        dst_stage_mask: ,
3446                        dst_access_mask: ,
3447                        old_layout: General,
3448                        new_layout: PresentSrc,
3449                        resource: swapchain2,
3450                    },
3451                ],
3452            },
3453            FlushSubmit,
3454            Submit,
3455        );
3456    }
3457
3458    #[test]
3459    fn swapchain2() {
3460        let (resources, queues) = test_queues!();
3461
3462        let queue_family_properties = resources
3463            .device()
3464            .physical_device()
3465            .queue_family_properties();
3466
3467        let present_queue = queues.iter().find(|q| {
3468            let queue_flags = queue_family_properties[q.queue_family_index() as usize].queue_flags;
3469
3470            queue_flags.contains(QueueFlags::COMPUTE) && !queue_flags.contains(QueueFlags::GRAPHICS)
3471        });
3472
3473        if !present_queue.is_some() {
3474            return;
3475        }
3476
3477        let mut graph = TaskGraph::<()>::new(&resources, 10, 10);
3478        let concurrent_sharing =
3479            Sharing::Concurrent(queues.iter().map(|q| q.queue_family_index()).collect());
3480        let swapchain1 = graph.add_swapchain(&SwapchainCreateInfo::default());
3481        let swapchain2 = graph.add_swapchain(&SwapchainCreateInfo {
3482            image_sharing: concurrent_sharing.clone(),
3483            ..Default::default()
3484        });
3485        let swapchain3 = graph.add_swapchain(&SwapchainCreateInfo::default());
3486        let swapchain4 = graph.add_swapchain(&SwapchainCreateInfo {
3487            image_sharing: concurrent_sharing.clone(),
3488            ..Default::default()
3489        });
3490        let node = graph
3491            .create_task_node("", QueueFamilyType::Graphics, PhantomData)
3492            .image_access(
3493                swapchain1.current_image_id(),
3494                AccessTypes::COLOR_ATTACHMENT_WRITE,
3495                ImageLayoutType::Optimal,
3496            )
3497            .image_access(
3498                swapchain2.current_image_id(),
3499                AccessTypes::COLOR_ATTACHMENT_WRITE,
3500                ImageLayoutType::Optimal,
3501            )
3502            .image_access(
3503                swapchain3.current_image_id(),
3504                AccessTypes::COMPUTE_SHADER_STORAGE_WRITE,
3505                ImageLayoutType::Optimal,
3506            )
3507            .image_access(
3508                swapchain4.current_image_id(),
3509                AccessTypes::COMPUTE_SHADER_STORAGE_WRITE,
3510                ImageLayoutType::Optimal,
3511            )
3512            .build();
3513
3514        let graph = unsafe {
3515            graph.compile(&CompileInfo {
3516                queues: &queues.iter().collect::<Vec<_>>(),
3517                present_queue: Some(present_queue.unwrap()),
3518                ..Default::default()
3519            })
3520        }
3521        .unwrap();
3522
3523        assert_matches_instructions!(
3524            graph,
3525            WaitAcquire {
3526                swapchain_id: swapchain1,
3527                stage_mask: COLOR_ATTACHMENT_OUTPUT,
3528            },
3529            WaitAcquire {
3530                swapchain_id: swapchain2,
3531                stage_mask: COLOR_ATTACHMENT_OUTPUT,
3532            },
3533            WaitAcquire {
3534                swapchain_id: swapchain3,
3535                stage_mask: COMPUTE_SHADER,
3536            },
3537            WaitAcquire {
3538                swapchain_id: swapchain4,
3539                stage_mask: COMPUTE_SHADER,
3540            },
3541            PipelineBarrier {
3542                barriers: [
3543                    {
3544                        src_stage_mask: COLOR_ATTACHMENT_OUTPUT,
3545                        src_access_mask: ,
3546                        dst_stage_mask: COLOR_ATTACHMENT_OUTPUT,
3547                        dst_access_mask: COLOR_ATTACHMENT_WRITE,
3548                        old_layout: Undefined,
3549                        new_layout: ColorAttachmentOptimal,
3550                        resource: swapchain1,
3551                    },
3552                    {
3553                        src_stage_mask: COLOR_ATTACHMENT_OUTPUT,
3554                        src_access_mask: ,
3555                        dst_stage_mask: COLOR_ATTACHMENT_OUTPUT,
3556                        dst_access_mask: COLOR_ATTACHMENT_WRITE,
3557                        old_layout: Undefined,
3558                        new_layout: ColorAttachmentOptimal,
3559                        resource: swapchain2,
3560                    },
3561                    {
3562                        src_stage_mask: COMPUTE_SHADER,
3563                        src_access_mask: ,
3564                        dst_stage_mask: COMPUTE_SHADER,
3565                        dst_access_mask: SHADER_STORAGE_WRITE,
3566                        old_layout: Undefined,
3567                        new_layout: General,
3568                        resource: swapchain3,
3569                    },
3570                    {
3571                        src_stage_mask: COMPUTE_SHADER,
3572                        src_access_mask: ,
3573                        dst_stage_mask: COMPUTE_SHADER,
3574                        dst_access_mask: SHADER_STORAGE_WRITE,
3575                        old_layout: Undefined,
3576                        new_layout: General,
3577                        resource: swapchain4,
3578                    },
3579                ],
3580            },
3581            ExecuteTask { node: node },
3582            SignalPrePresent {
3583                swapchain_id: swapchain1,
3584                stage_mask: COLOR_ATTACHMENT_OUTPUT,
3585            },
3586            SignalPrePresent {
3587                swapchain_id: swapchain3,
3588                stage_mask: COMPUTE_SHADER,
3589            },
3590            SignalPresent {
3591                swapchain_id: swapchain2,
3592                stage_mask: COLOR_ATTACHMENT_OUTPUT,
3593            },
3594            SignalPresent {
3595                swapchain_id: swapchain4,
3596                stage_mask: COMPUTE_SHADER,
3597            },
3598            PipelineBarrier {
3599                barriers: [
3600                    {
3601                        src_stage_mask: COLOR_ATTACHMENT_OUTPUT,
3602                        src_access_mask: COLOR_ATTACHMENT_WRITE,
3603                        dst_stage_mask: ,
3604                        dst_access_mask: ,
3605                        old_layout: ColorAttachmentOptimal,
3606                        new_layout: PresentSrc,
3607                        resource: swapchain1,
3608                    },
3609                    {
3610                        src_stage_mask: COLOR_ATTACHMENT_OUTPUT,
3611                        src_access_mask: COLOR_ATTACHMENT_WRITE,
3612                        dst_stage_mask: ,
3613                        dst_access_mask: ,
3614                        old_layout: ColorAttachmentOptimal,
3615                        new_layout: PresentSrc,
3616                        resource: swapchain2,
3617                    },
3618                    {
3619                        src_stage_mask: COMPUTE_SHADER,
3620                        src_access_mask: SHADER_STORAGE_WRITE,
3621                        dst_stage_mask: ,
3622                        dst_access_mask: ,
3623                        old_layout: General,
3624                        new_layout: PresentSrc,
3625                        resource: swapchain3,
3626                    },
3627                    {
3628                        src_stage_mask: COMPUTE_SHADER,
3629                        src_access_mask: SHADER_STORAGE_WRITE,
3630                        dst_stage_mask: ,
3631                        dst_access_mask: ,
3632                        old_layout: General,
3633                        new_layout: PresentSrc,
3634                        resource: swapchain4,
3635                    },
3636                ],
3637            },
3638            FlushSubmit,
3639            Submit,
3640            WaitPrePresent {
3641                swapchain_id: swapchain1,
3642                stage_mask: ALL_COMMANDS,
3643            },
3644            SignalPresent {
3645                swapchain_id: swapchain1,
3646                stage_mask: ALL_COMMANDS,
3647            },
3648            WaitPrePresent {
3649                swapchain_id: swapchain3,
3650                stage_mask: ALL_COMMANDS,
3651            },
3652            SignalPresent {
3653                swapchain_id: swapchain3,
3654                stage_mask: ALL_COMMANDS,
3655            },
3656            PipelineBarrier {
3657                barriers: [
3658                    {
3659                        src_stage_mask: ,
3660                        src_access_mask: ,
3661                        dst_stage_mask: ,
3662                        dst_access_mask: ,
3663                        old_layout: ColorAttachmentOptimal,
3664                        new_layout: PresentSrc,
3665                        resource: swapchain1,
3666                    },
3667                    {
3668                        src_stage_mask: ,
3669                        src_access_mask: ,
3670                        dst_stage_mask: ,
3671                        dst_access_mask: ,
3672                        old_layout: General,
3673                        new_layout: PresentSrc,
3674                        resource: swapchain3,
3675                    },
3676                ],
3677            },
3678            FlushSubmit,
3679            Submit,
3680        );
3681    }
3682
3683    #[test]
3684    fn swapchain3() {
3685        let (resources, queues) = test_queues!();
3686
3687        let queue_family_properties = resources
3688            .device()
3689            .physical_device()
3690            .queue_family_properties();
3691
3692        let present_queue = queues.iter().find(|q| {
3693            let queue_flags = queue_family_properties[q.queue_family_index() as usize].queue_flags;
3694
3695            queue_flags.contains(QueueFlags::GRAPHICS)
3696        });
3697
3698        let mut graph = TaskGraph::<()>::new(&resources, 10, 10);
3699        let swapchain = graph.add_swapchain(&SwapchainCreateInfo {
3700            image_format: Format::R8G8B8A8_UNORM,
3701            ..Default::default()
3702        });
3703        let depth_image = graph.add_image(&ImageCreateInfo {
3704            format: Format::D16_UNORM,
3705            ..Default::default()
3706        });
3707        let framebuffer = graph.add_framebuffer();
3708        let node1 = graph
3709            .create_task_node("", QueueFamilyType::Graphics, PhantomData)
3710            .framebuffer(framebuffer)
3711            .depth_stencil_attachment(
3712                depth_image,
3713                AccessTypes::DEPTH_STENCIL_ATTACHMENT_WRITE,
3714                ImageLayoutType::Optimal,
3715                &AttachmentInfo::default(),
3716            )
3717            .build();
3718        let node2 = graph
3719            .create_task_node("", QueueFamilyType::Graphics, PhantomData)
3720            .framebuffer(framebuffer)
3721            .color_attachment(
3722                swapchain.current_image_id(),
3723                AccessTypes::COLOR_ATTACHMENT_WRITE,
3724                ImageLayoutType::Optimal,
3725                &AttachmentInfo::default(),
3726            )
3727            .depth_stencil_attachment(
3728                depth_image,
3729                AccessTypes::DEPTH_STENCIL_ATTACHMENT_WRITE,
3730                ImageLayoutType::Optimal,
3731                &AttachmentInfo::default(),
3732            )
3733            .build();
3734        let node3 = graph
3735            .create_task_node("", QueueFamilyType::Graphics, PhantomData)
3736            .framebuffer(framebuffer)
3737            .input_attachment(
3738                depth_image,
3739                AccessTypes::FRAGMENT_SHADER_DEPTH_STENCIL_INPUT_ATTACHMENT_READ,
3740                ImageLayoutType::Optimal,
3741                &AttachmentInfo::default(),
3742            )
3743            .build();
3744        graph.add_edge(node1, node2).unwrap();
3745        graph.add_edge(node2, node3).unwrap();
3746
3747        let graph = unsafe {
3748            graph.compile(&CompileInfo {
3749                queues: &queues.iter().collect::<Vec<_>>(),
3750                present_queue: Some(present_queue.unwrap()),
3751                ..Default::default()
3752            })
3753        }
3754        .unwrap();
3755
3756        assert_matches_instructions!(
3757            graph,
3758            WaitAcquire {
3759                swapchain_id: swapchain,
3760                stage_mask: COLOR_ATTACHMENT_OUTPUT,
3761            },
3762            PipelineBarrier {
3763                barriers: [
3764                    {
3765                        src_stage_mask: COLOR_ATTACHMENT_OUTPUT,
3766                        src_access_mask: ,
3767                        dst_stage_mask: COLOR_ATTACHMENT_OUTPUT,
3768                        dst_access_mask: COLOR_ATTACHMENT_WRITE,
3769                        old_layout: Undefined,
3770                        new_layout: ColorAttachmentOptimal,
3771                        resource: swapchain,
3772                    },
3773                ],
3774            },
3775            BeginRenderPass,
3776            ExecuteTask { node: node1 },
3777            NextSubpass,
3778            ExecuteTask { node: node2 },
3779            NextSubpass,
3780            ExecuteTask { node: node3 },
3781            EndRenderPass,
3782            SignalPresent {
3783                swapchain_id: swapchain,
3784                stage_mask: COLOR_ATTACHMENT_OUTPUT,
3785            },
3786            PipelineBarrier {
3787                barriers: [
3788                    {
3789                        src_stage_mask: COLOR_ATTACHMENT_OUTPUT,
3790                        src_access_mask: COLOR_ATTACHMENT_WRITE,
3791                        dst_stage_mask: ,
3792                        dst_access_mask: ,
3793                        old_layout: ColorAttachmentOptimal,
3794                        new_layout: PresentSrc,
3795                        resource: swapchain,
3796                    },
3797                ],
3798            },
3799            FlushSubmit,
3800            Submit,
3801        );
3802    }
3803
3804    #[test]
3805    fn swapchain4() {
3806        let (resources, queues) = test_queues!();
3807
3808        let queue_family_properties = resources
3809            .device()
3810            .physical_device()
3811            .queue_family_properties();
3812
3813        let present_queue = queues.iter().find(|q| {
3814            let queue_flags = queue_family_properties[q.queue_family_index() as usize].queue_flags;
3815
3816            queue_flags.contains(QueueFlags::COMPUTE) && !queue_flags.contains(QueueFlags::GRAPHICS)
3817        });
3818
3819        if !present_queue.is_some() {
3820            return;
3821        }
3822
3823        let mut graph = TaskGraph::<()>::new(&resources, 10, 10);
3824        let concurrent_sharing =
3825            Sharing::Concurrent(queues.iter().map(|q| q.queue_family_index()).collect());
3826        let swapchain1 = graph.add_swapchain(&SwapchainCreateInfo {
3827            image_format: Format::R8G8B8A8_UNORM,
3828            ..SwapchainCreateInfo::default()
3829        });
3830        let swapchain2 = graph.add_swapchain(&SwapchainCreateInfo {
3831            image_format: Format::R8G8B8A8_UNORM,
3832            image_sharing: concurrent_sharing.clone(),
3833            ..Default::default()
3834        });
3835        let depth_image = graph.add_image(&ImageCreateInfo {
3836            format: Format::D16_UNORM,
3837            ..Default::default()
3838        });
3839        let framebuffer = graph.add_framebuffer();
3840        let node1 = graph
3841            .create_task_node("", QueueFamilyType::Graphics, PhantomData)
3842            .framebuffer(framebuffer)
3843            .depth_stencil_attachment(
3844                depth_image,
3845                AccessTypes::DEPTH_STENCIL_ATTACHMENT_WRITE,
3846                ImageLayoutType::Optimal,
3847                &AttachmentInfo::default(),
3848            )
3849            .build();
3850        let node2 = graph
3851            .create_task_node("", QueueFamilyType::Graphics, PhantomData)
3852            .framebuffer(framebuffer)
3853            .color_attachment(
3854                swapchain1.current_image_id(),
3855                AccessTypes::COLOR_ATTACHMENT_WRITE,
3856                ImageLayoutType::Optimal,
3857                &AttachmentInfo::default(),
3858            )
3859            .color_attachment(
3860                swapchain2.current_image_id(),
3861                AccessTypes::COLOR_ATTACHMENT_WRITE,
3862                ImageLayoutType::Optimal,
3863                &AttachmentInfo {
3864                    index: 1,
3865                    ..Default::default()
3866                },
3867            )
3868            .depth_stencil_attachment(
3869                depth_image,
3870                AccessTypes::DEPTH_STENCIL_ATTACHMENT_WRITE,
3871                ImageLayoutType::Optimal,
3872                &AttachmentInfo::default(),
3873            )
3874            .build();
3875        let node3 = graph
3876            .create_task_node("", QueueFamilyType::Graphics, PhantomData)
3877            .framebuffer(framebuffer)
3878            .input_attachment(
3879                depth_image,
3880                AccessTypes::FRAGMENT_SHADER_DEPTH_STENCIL_INPUT_ATTACHMENT_READ,
3881                ImageLayoutType::Optimal,
3882                &AttachmentInfo::default(),
3883            )
3884            .build();
3885        graph.add_edge(node1, node2).unwrap();
3886        graph.add_edge(node2, node3).unwrap();
3887
3888        let graph = unsafe {
3889            graph.compile(&CompileInfo {
3890                queues: &queues.iter().collect::<Vec<_>>(),
3891                present_queue: Some(present_queue.unwrap()),
3892                ..Default::default()
3893            })
3894        }
3895        .unwrap();
3896
3897        assert_matches_instructions!(
3898            graph,
3899            WaitAcquire {
3900                swapchain_id: swapchain1,
3901                stage_mask: COLOR_ATTACHMENT_OUTPUT,
3902            },
3903            WaitAcquire {
3904                swapchain_id: swapchain2,
3905                stage_mask: COLOR_ATTACHMENT_OUTPUT,
3906            },
3907            PipelineBarrier {
3908                barriers: [
3909                    {
3910                        src_stage_mask: COLOR_ATTACHMENT_OUTPUT,
3911                        src_access_mask: ,
3912                        dst_stage_mask: COLOR_ATTACHMENT_OUTPUT,
3913                        dst_access_mask: COLOR_ATTACHMENT_WRITE,
3914                        old_layout: Undefined,
3915                        new_layout: ColorAttachmentOptimal,
3916                        resource: swapchain1,
3917                    },
3918                    {
3919                        src_stage_mask: COLOR_ATTACHMENT_OUTPUT,
3920                        src_access_mask: ,
3921                        dst_stage_mask: COLOR_ATTACHMENT_OUTPUT,
3922                        dst_access_mask: COLOR_ATTACHMENT_WRITE,
3923                        old_layout: Undefined,
3924                        new_layout: ColorAttachmentOptimal,
3925                        resource: swapchain2,
3926                    },
3927                ],
3928            },
3929            BeginRenderPass,
3930            ExecuteTask { node: node1 },
3931            NextSubpass,
3932            ExecuteTask { node: node2 },
3933            NextSubpass,
3934            ExecuteTask { node: node3 },
3935            EndRenderPass,
3936            SignalPrePresent {
3937                swapchain_id: swapchain1,
3938                stage_mask: COLOR_ATTACHMENT_OUTPUT,
3939            },
3940            SignalPresent {
3941                swapchain_id: swapchain2,
3942                stage_mask: COLOR_ATTACHMENT_OUTPUT,
3943            },
3944            PipelineBarrier {
3945                barriers: [
3946                    {
3947                        src_stage_mask: COLOR_ATTACHMENT_OUTPUT,
3948                        src_access_mask: COLOR_ATTACHMENT_WRITE,
3949                        dst_stage_mask: ,
3950                        dst_access_mask: ,
3951                        old_layout: ColorAttachmentOptimal,
3952                        new_layout: PresentSrc,
3953                        resource: swapchain1,
3954                    },
3955                    {
3956                        src_stage_mask: COLOR_ATTACHMENT_OUTPUT,
3957                        src_access_mask: COLOR_ATTACHMENT_WRITE,
3958                        dst_stage_mask: ,
3959                        dst_access_mask: ,
3960                        old_layout: ColorAttachmentOptimal,
3961                        new_layout: PresentSrc,
3962                        resource: swapchain2,
3963                    },
3964                ],
3965            },
3966            FlushSubmit,
3967            Submit,
3968            WaitPrePresent {
3969                swapchain_id: swapchain1,
3970                stage_mask: ALL_COMMANDS,
3971            },
3972            SignalPresent {
3973                swapchain_id: swapchain1,
3974                stage_mask: ALL_COMMANDS,
3975            },
3976            PipelineBarrier {
3977                barriers: [
3978                    {
3979                        src_stage_mask: ,
3980                        src_access_mask: ,
3981                        dst_stage_mask: ,
3982                        dst_access_mask: ,
3983                        old_layout: ColorAttachmentOptimal,
3984                        new_layout: PresentSrc,
3985                        resource: swapchain1,
3986                    },
3987                ],
3988            },
3989            FlushSubmit,
3990            Submit,
3991        );
3992    }
3993
3994    fn has_compute_only_queue(queues: &[Arc<Queue>]) -> bool {
3995        let queue_family_properties = queues[0]
3996            .device()
3997            .physical_device()
3998            .queue_family_properties();
3999
4000        queues.iter().any(|q| {
4001            let queue_flags = queue_family_properties[q.queue_family_index() as usize].queue_flags;
4002
4003            queue_flags.contains(QueueFlags::COMPUTE) && !queue_flags.contains(QueueFlags::GRAPHICS)
4004        })
4005    }
4006
4007    struct MatchingState {
4008        submission_index: usize,
4009        instruction_index: usize,
4010        semaphores: foldhash::HashMap<&'static str, SemaphoreIndex>,
4011    }
4012
4013    macro_rules! assert_matches_instructions {
4014        (
4015            $graph:ident,
4016            $($arg:tt)+
4017        ) => {
4018            let mut state = MatchingState {
4019                submission_index: 0,
4020                instruction_index: 0,
4021                semaphores: Default::default(),
4022            };
4023            assert_matches_instructions!(@ $graph, state, $($arg)+);
4024        };
4025        (
4026            @
4027            $graph:ident,
4028            $state:ident,
4029            InitialPipelineBarrier {
4030                barriers: [
4031                    $({
4032                        dst_stage_mask: $($dst_stage:ident)|*,
4033                        dst_access_mask: $($dst_access:ident)|*,
4034                        new_layout: $new_layout:ident,
4035                        resource: $resource:ident,
4036                    },)*
4037                ],
4038            },
4039            $($arg:tt)*
4040        ) => {
4041            let submission = &$graph.submissions[$state.submission_index];
4042            let barrier_range = &submission.initial_barrier_range;
4043            let barrier_range = barrier_range.start as usize..barrier_range.end as usize;
4044            let barriers = &$graph.barriers[barrier_range];
4045
4046            #[allow(unused_mut)]
4047            let mut barrier_count = 0;
4048            $(
4049                let barrier = barriers
4050                    .iter()
4051                    .find(|barrier| barrier.resource == $resource.erase())
4052                    .unwrap();
4053                assert_eq!(barrier.src_stage_mask, PipelineStages::empty());
4054                assert_eq!(barrier.src_access_mask, AccessFlags::empty());
4055                assert_eq!(
4056                    barrier.dst_stage_mask,
4057                    PipelineStages::empty() $(| PipelineStages::$dst_stage)*,
4058                );
4059                assert_eq!(
4060                    barrier.dst_access_mask,
4061                    AccessFlags::empty() $(| AccessFlags::$dst_access)*,
4062                );
4063                assert_eq!(barrier.old_layout, ImageLayout::Undefined);
4064                assert_eq!(barrier.new_layout, ImageLayout::$new_layout);
4065                barrier_count += 1;
4066            )*
4067            assert_eq!(barriers.len(), barrier_count);
4068
4069            assert_matches_instructions!(@ $graph, $state, $($arg)*);
4070        };
4071        (
4072            @
4073            $graph:ident,
4074            $state:ident,
4075            WaitAcquire {
4076                swapchain_id: $swapchain_id:expr,
4077                stage_mask: $($stage:ident)|*,
4078            },
4079            $($arg:tt)*
4080        ) => {
4081            assert!(matches!(
4082                $graph.instructions[$state.instruction_index],
4083                Instruction::WaitAcquire {
4084                    swapchain_id,
4085                    stage_mask,
4086                } if swapchain_id == $swapchain_id
4087                    && stage_mask == PipelineStages::empty() $(| PipelineStages::$stage)*,
4088            ));
4089            $state.instruction_index += 1;
4090            assert_matches_instructions!(@ $graph, $state, $($arg)*);
4091        };
4092        (
4093            @
4094            $graph:ident,
4095            $state:ident,
4096            WaitSemaphore {
4097                semaphore_index: $semaphore_index:ident,
4098                stage_mask: $($stage:ident)|*,
4099            },
4100            $($arg:tt)*
4101        ) => {
4102            assert!(matches!(
4103                $graph.instructions[$state.instruction_index],
4104                Instruction::WaitSemaphore {
4105                    stage_mask,
4106                    ..
4107                } if stage_mask == PipelineStages::empty() $(| PipelineStages::$stage)*,
4108            ));
4109            let Instruction::WaitSemaphore { semaphore_index, .. } =
4110                &$graph.instructions[$state.instruction_index]
4111            else {
4112                unreachable!();
4113            };
4114
4115            assert_eq!(
4116                semaphore_index,
4117                $state.semaphores.get(stringify!($semaphore_index)).unwrap(),
4118            );
4119
4120            $state.instruction_index += 1;
4121            assert_matches_instructions!(@ $graph, $state, $($arg)*);
4122        };
4123        (
4124            @
4125            $graph:ident,
4126            $state:ident,
4127            ExecuteTask {
4128                node: $node:expr $(,)?
4129            },
4130            $($arg:tt)*
4131        ) => {
4132            assert!(matches!(
4133                $graph.instructions[$state.instruction_index],
4134                Instruction::ExecuteTask { node_index } if node_index == $node.index(),
4135            ));
4136            $state.instruction_index += 1;
4137            assert_matches_instructions!(@ $graph, $state, $($arg)*);
4138        };
4139        (
4140            @
4141            $graph:ident,
4142            $state:ident,
4143            PipelineBarrier {
4144                barriers: [
4145                    $({
4146                        src_stage_mask: $($src_stage:ident)|*,
4147                        src_access_mask: $($src_access:ident)|*,
4148                        dst_stage_mask: $($dst_stage:ident)|*,
4149                        dst_access_mask: $($dst_access:ident)|*,
4150                        old_layout: $old_layout:ident,
4151                        new_layout: $new_layout:ident,
4152                        resource: $resource:ident,
4153                    },)+
4154                ],
4155            },
4156            $($arg:tt)*
4157        ) => {
4158            assert!(matches!(
4159                $graph.instructions[$state.instruction_index],
4160                Instruction::PipelineBarrier { .. },
4161            ));
4162            let Instruction::PipelineBarrier { barrier_range } =
4163                &$graph.instructions[$state.instruction_index]
4164            else {
4165                unreachable!();
4166            };
4167            let barrier_range = barrier_range.start as usize..barrier_range.end as usize;
4168            let barriers = &$graph.barriers[barrier_range];
4169
4170            let mut barrier_count = 0;
4171            $(
4172                let barrier = barriers
4173                    .iter()
4174                    .find(|barrier| barrier.resource == $resource.erase())
4175                    .unwrap();
4176                assert_eq!(
4177                    barrier.src_stage_mask,
4178                    PipelineStages::empty() $(| PipelineStages::$src_stage)*,
4179                );
4180                assert_eq!(
4181                    barrier.src_access_mask,
4182                    AccessFlags::empty() $(| AccessFlags::$src_access)*,
4183                );
4184                assert_eq!(
4185                    barrier.dst_stage_mask,
4186                    PipelineStages::empty() $(| PipelineStages::$dst_stage)*,
4187                );
4188                assert_eq!(
4189                    barrier.dst_access_mask,
4190                    AccessFlags::empty() $(| AccessFlags::$dst_access)*,
4191                );
4192                assert_eq!(barrier.old_layout, ImageLayout::$old_layout);
4193                assert_eq!(barrier.new_layout, ImageLayout::$new_layout);
4194                barrier_count += 1;
4195            )+
4196            assert_eq!(barriers.len(), barrier_count);
4197
4198            $state.instruction_index += 1;
4199            assert_matches_instructions!(@ $graph, $state, $($arg)*);
4200        };
4201        (
4202            @
4203            $graph:ident,
4204            $state:ident,
4205            BeginRenderPass,
4206            $($arg:tt)*
4207        ) => {
4208            assert!(matches!(
4209                $graph.instructions[$state.instruction_index],
4210                Instruction::BeginRenderPass { .. },
4211            ));
4212            $state.instruction_index += 1;
4213            assert_matches_instructions!(@ $graph, $state, $($arg)*);
4214        };
4215        (
4216            @
4217            $graph:ident,
4218            $state:ident,
4219            NextSubpass,
4220            $($arg:tt)*
4221        ) => {
4222            assert!(matches!(
4223                $graph.instructions[$state.instruction_index],
4224                Instruction::NextSubpass,
4225            ));
4226            $state.instruction_index += 1;
4227            assert_matches_instructions!(@ $graph, $state, $($arg)*);
4228        };
4229        (
4230            @
4231            $graph:ident,
4232            $state:ident,
4233            EndRenderPass,
4234            $($arg:tt)*
4235        ) => {
4236            assert!(matches!(
4237                $graph.instructions[$state.instruction_index],
4238                Instruction::EndRenderPass,
4239            ));
4240            $state.instruction_index += 1;
4241            assert_matches_instructions!(@ $graph, $state, $($arg)*);
4242        };
4243        (
4244            @
4245            $graph:ident,
4246            $state:ident,
4247            ClearAttachments {
4248                node: $node:ident,
4249                attachments: [$($resource:ident),+ $(,)?],
4250            },
4251            $($arg:tt)*
4252        ) => {
4253            assert!(matches!(
4254                $graph.instructions[$state.instruction_index],
4255                Instruction::ClearAttachments { node_index, .. } if node_index == $node.index(),
4256            ));
4257            let Instruction::ClearAttachments { clear_attachment_range, .. } =
4258                &$graph.instructions[$state.instruction_index]
4259            else {
4260                unreachable!();
4261            };
4262            let clear_attachments = &$graph.clear_attachments[clear_attachment_range.clone()];
4263
4264            let mut clear_attachment_count = 0;
4265            $(
4266                assert!(clear_attachments.contains(&$resource.erase()));
4267                clear_attachment_count += 1;
4268            )+
4269            assert_eq!(clear_attachments.len(), clear_attachment_count);
4270
4271            $state.instruction_index += 1;
4272            assert_matches_instructions!(@ $graph, $state, $($arg)*);
4273        };
4274        (
4275            @
4276            $graph:ident,
4277            $state:ident,
4278            SignalSemaphore {
4279                semaphore_index: $semaphore_index:ident,
4280                stage_mask: $($stage:ident)|*,
4281            },
4282            $($arg:tt)*
4283        ) => {
4284            assert!(matches!(
4285                $graph.instructions[$state.instruction_index],
4286                Instruction::SignalSemaphore {
4287                    stage_mask,
4288                    ..
4289                } if stage_mask == PipelineStages::empty() $(| PipelineStages::$stage)*,
4290            ));
4291            let Instruction::SignalSemaphore { semaphore_index, .. } =
4292                &$graph.instructions[$state.instruction_index]
4293            else {
4294                unreachable!();
4295            };
4296
4297            assert!($state.semaphores.get(&stringify!($semaphore_index)).is_none());
4298            $state.semaphores.insert(stringify!($semaphore_index), *semaphore_index);
4299
4300            $state.instruction_index += 1;
4301            assert_matches_instructions!(@ $graph, $state, $($arg)*);
4302        };
4303        (
4304            @
4305            $graph:ident,
4306            $state:ident,
4307            SignalPrePresent {
4308                swapchain_id: $swapchain_id:expr,
4309                stage_mask: $($stage:ident)|*,
4310            },
4311            $($arg:tt)*
4312        ) => {
4313            assert!(matches!(
4314                $graph.instructions[$state.instruction_index],
4315                Instruction::SignalPrePresent {
4316                    swapchain_id,
4317                    stage_mask,
4318                } if swapchain_id == $swapchain_id
4319                    && stage_mask == PipelineStages::empty() $(| PipelineStages::$stage)*,
4320            ));
4321            $state.instruction_index += 1;
4322            assert_matches_instructions!(@ $graph, $state, $($arg)*);
4323        };
4324        (
4325            @
4326            $graph:ident,
4327            $state:ident,
4328            WaitPrePresent {
4329                swapchain_id: $swapchain_id:expr,
4330                stage_mask: $($stage:ident)|*,
4331            },
4332            $($arg:tt)*
4333        ) => {
4334            assert!(matches!(
4335                $graph.instructions[$state.instruction_index],
4336                Instruction::WaitPrePresent {
4337                    swapchain_id,
4338                    stage_mask,
4339                } if swapchain_id == $swapchain_id
4340                    && stage_mask == PipelineStages::empty() $(| PipelineStages::$stage)*,
4341            ));
4342            $state.instruction_index += 1;
4343            assert_matches_instructions!(@ $graph, $state, $($arg)*);
4344        };
4345        (
4346            @
4347            $graph:ident,
4348            $state:ident,
4349            SignalPresent {
4350                swapchain_id: $swapchain_id:expr,
4351                stage_mask: $($stage:ident)|*,
4352            },
4353            $($arg:tt)*
4354        ) => {
4355            assert!(matches!(
4356                $graph.instructions[$state.instruction_index],
4357                Instruction::SignalPresent {
4358                    swapchain_id,
4359                    stage_mask,
4360                } if swapchain_id == $swapchain_id
4361                    && stage_mask == PipelineStages::empty() $(| PipelineStages::$stage)*,
4362            ));
4363            $state.instruction_index += 1;
4364            assert_matches_instructions!(@ $graph, $state, $($arg)*);
4365        };
4366        (
4367            @
4368            $graph:ident,
4369            $state:ident,
4370            FlushSubmit,
4371            $($arg:tt)*
4372        ) => {
4373            assert!(matches!(
4374                $graph.instructions[$state.instruction_index],
4375                Instruction::FlushSubmit,
4376            ));
4377            $state.instruction_index += 1;
4378            assert_matches_instructions!(@ $graph, $state, $($arg)*);
4379        };
4380        (
4381            @
4382            $graph:ident,
4383            $state:ident,
4384            Submit,
4385            $($arg:tt)*
4386        ) => {
4387            assert!(matches!(
4388                $graph.instructions[$state.instruction_index],
4389                Instruction::Submit,
4390            ));
4391            $state.submission_index += 1;
4392            $state.instruction_index += 1;
4393            assert_matches_instructions!(@ $graph, $state, $($arg)*);
4394        };
4395        (
4396            @
4397            $graph:ident,
4398            $state:ident,
4399        ) => {
4400            assert_eq!($graph.submissions.len(), $state.submission_index);
4401            assert_eq!($graph.instructions.len(), $state.instruction_index);
4402        };
4403    }
4404    use assert_matches_instructions;
4405}