vulkano_taskgraph/graph/
mod.rs

1//! The task graph data structure and associated types.
2
3pub use self::{
4    compile::{CompileError, CompileErrorKind, CompileInfo},
5    execute::{ExecuteError, ResourceMap},
6};
7use crate::{
8    linear_map::LinearMap,
9    resource::{self, AccessTypes, Flight, HostAccessType, ImageLayoutType},
10    Id, InvalidSlotError, Object, ObjectType, QueueFamilyType, Task,
11};
12use ash::vk;
13use concurrent_slotmap::{IterMut, IterUnprotected, SlotId, SlotMap};
14use foldhash::HashMap;
15use smallvec::SmallVec;
16use std::{
17    borrow::Cow, cell::RefCell, error::Error, fmt, hint, iter::FusedIterator, ops::Range, sync::Arc,
18};
19use vulkano::{
20    buffer::{Buffer, BufferCreateInfo},
21    device::{Device, DeviceOwned, Queue},
22    format::Format,
23    image::{
24        sampler::ComponentMapping, Image, ImageAspects, ImageCreateInfo, ImageLayout, ImageUsage,
25        SampleCount,
26    },
27    render_pass::{Framebuffer, RenderPass, Subpass},
28    swapchain::{Swapchain, SwapchainCreateInfo},
29    sync::{semaphore::Semaphore, AccessFlags, PipelineStages},
30};
31
32mod compile;
33mod execute;
34
35/// The task graph is a [directed acyclic graph] consisting of [`Task`] nodes, with edges
36/// representing happens-before relations.
37///
38/// [directed acyclic graph]: https://en.wikipedia.org/wiki/Directed_acyclic_graph
39pub struct TaskGraph<W: ?Sized> {
40    nodes: Nodes<W>,
41    resources: Resources,
42}
43
44struct Nodes<W: ?Sized> {
45    inner: SlotMap<Node<W>>,
46}
47
48struct Node<W: ?Sized> {
49    // TODO:
50    #[allow(unused)]
51    name: Cow<'static, str>,
52    inner: NodeInner<W>,
53    in_edges: Vec<NodeIndex>,
54    out_edges: Vec<NodeIndex>,
55}
56
57enum NodeInner<W: ?Sized> {
58    Task(TaskNode<W>),
59    // TODO:
60    #[allow(unused)]
61    Semaphore,
62}
63
64type NodeIndex = u32;
65
66pub(crate) struct Resources {
67    inner: SlotMap<ResourceInfo>,
68    physical_resources: Arc<resource::Resources>,
69    physical_map: HashMap<Id, Id>,
70    host_reads: Vec<Id<Buffer>>,
71    host_writes: Vec<Id<Buffer>>,
72    framebuffers: SlotMap<()>,
73}
74
75struct ResourceInfo {
76    format: Format,
77    samples: SampleCount,
78    usage: ImageUsage,
79}
80
81impl<W: ?Sized> TaskGraph<W> {
82    /// Creates a new `TaskGraph`.
83    ///
84    /// `max_nodes` is the maximum number of nodes the graph can ever have. `max_resources` is the
85    /// maximum number of virtual resources the graph can ever have.
86    #[must_use]
87    pub fn new(
88        physical_resources: &Arc<resource::Resources>,
89        max_nodes: u32,
90        max_resources: u32,
91    ) -> Self {
92        TaskGraph {
93            nodes: Nodes {
94                inner: SlotMap::new(max_nodes),
95            },
96            resources: Resources {
97                inner: SlotMap::new(max_resources),
98                physical_resources: physical_resources.clone(),
99                physical_map: HashMap::default(),
100                host_reads: Vec::new(),
101                host_writes: Vec::new(),
102                // FIXME:
103                framebuffers: SlotMap::new(256),
104            },
105        }
106    }
107
108    /// Creates a new [`TaskNode`] from the given `task` and adds it to the graph. Returns a
109    /// builder allowing you to add resource accesses to the task node.
110    ///
111    /// `queue_family_type` is the type of queue family the task can be executed on.
112    #[must_use]
113    pub fn create_task_node(
114        &mut self,
115        name: impl Into<Cow<'static, str>>,
116        queue_family_type: QueueFamilyType,
117        task: impl Task<World = W>,
118    ) -> TaskNodeBuilder<'_> {
119        let id = self.nodes.add_node(
120            name.into(),
121            NodeInner::Task(TaskNode::new(queue_family_type, task)),
122        );
123
124        // SAFETY: We just inserted this task node.
125        let task_node = unsafe { self.nodes.task_node_unchecked_mut(id.index()) };
126
127        TaskNodeBuilder {
128            id,
129            accesses: &mut task_node.accesses,
130            attachments: &mut task_node.attachments,
131            resources: &mut self.resources,
132        }
133    }
134
135    /// Removes the task node corresponding to `id` from the graph.
136    pub fn remove_task_node(&mut self, id: NodeId) -> Result<TaskNode<W>> {
137        self.task_node(id)?;
138
139        let task = match self.nodes.remove_node(id).inner {
140            NodeInner::Task(task) => task,
141            // We checked that the node is a task above.
142            _ => unreachable!(),
143        };
144
145        Ok(task)
146    }
147
148    /// Adds an edge starting at the node corresponding to `from` and ending at the node
149    /// corresponding to `to`.
150    pub fn add_edge(&mut self, from: NodeId, to: NodeId) -> Result {
151        let [from_node, to_node] = self.nodes.node_many_mut([from, to])?;
152        let out_edges = &mut from_node.out_edges;
153        let in_edges = &mut to_node.in_edges;
154
155        if !out_edges.contains(&to.index()) {
156            out_edges.push(to.index());
157            in_edges.push(from.index());
158
159            Ok(())
160        } else {
161            Err(TaskGraphError::DuplicateEdge)
162        }
163    }
164
165    /// Removes an edge starting at the node corresponding to `from` and ending at the node
166    /// corresponding to `to`.
167    pub fn remove_edge(&mut self, from: NodeId, to: NodeId) -> Result {
168        let [from_node, to_node] = self.nodes.node_many_mut([from, to])?;
169        let out_edges = &mut from_node.out_edges;
170        let in_edges = &mut to_node.in_edges;
171
172        if let Some(index) = out_edges.iter().position(|&i| i == to.index()) {
173            out_edges.remove(index);
174            let edge_index = in_edges.iter().position(|&i| i == from.index()).unwrap();
175            in_edges.remove(edge_index);
176
177            Ok(())
178        } else {
179            Err(TaskGraphError::InvalidEdge)
180        }
181    }
182
183    /// Returns a reference to the task node corresponding to `id`.
184    #[inline]
185    pub fn task_node(&self, id: NodeId) -> Result<&TaskNode<W>> {
186        self.nodes.task_node(id)
187    }
188
189    /// Returns a mutable reference to the task node corresponding to `id`.
190    #[inline]
191    pub fn task_node_mut(&mut self, id: NodeId) -> Result<&mut TaskNode<W>> {
192        self.nodes.task_node_mut(id)
193    }
194
195    /// Returns an iterator over all [`TaskNode`]s.
196    #[inline]
197    pub fn task_nodes(&self) -> TaskNodes<'_, W> {
198        TaskNodes {
199            inner: self.nodes.nodes(),
200        }
201    }
202
203    /// Returns an iterator over all [`TaskNode`]s that allows you to mutate them.
204    #[inline]
205    pub fn task_nodes_mut(&mut self) -> TaskNodesMut<'_, W> {
206        TaskNodesMut {
207            inner: self.nodes.nodes_mut(),
208        }
209    }
210
211    /// Add a [virtual buffer resource] to the task graph.
212    #[must_use]
213    pub fn add_buffer(&mut self, create_info: &BufferCreateInfo) -> Id<Buffer> {
214        self.resources.add_buffer(create_info)
215    }
216
217    /// Add a [virtual image resource] to the task graph.
218    #[must_use]
219    pub fn add_image(&mut self, create_info: &ImageCreateInfo) -> Id<Image> {
220        self.resources.add_image(create_info)
221    }
222
223    /// Add a [virtual swapchain resource] to the task graph.
224    #[must_use]
225    pub fn add_swapchain(&mut self, create_info: &SwapchainCreateInfo) -> Id<Swapchain> {
226        self.resources.add_swapchain(create_info)
227    }
228
229    /// Adds a host buffer access to the task graph.
230    ///
231    /// # Panics
232    ///
233    /// - Panics if `id` is not a valid virtual resource ID nor a valid physical ID.
234    pub fn add_host_buffer_access(&mut self, id: Id<Buffer>, access_type: HostAccessType) {
235        self.resources.add_host_buffer_access(id, access_type)
236    }
237
238    /// Adds a [virtual framebuffer] to the task graph.
239    #[must_use]
240    pub fn add_framebuffer(&mut self) -> Id<Framebuffer> {
241        self.resources.add_framebuffer()
242    }
243}
244
245impl<W: ?Sized> Nodes<W> {
246    fn add_node(&mut self, name: Cow<'static, str>, inner: NodeInner<W>) -> NodeId {
247        let slot = self.inner.insert_mut(Node {
248            name,
249            inner,
250            in_edges: Vec::new(),
251            out_edges: Vec::new(),
252        });
253
254        NodeId { slot }
255    }
256
257    fn remove_node(&mut self, id: NodeId) -> Node<W> {
258        let node = self.inner.remove_mut(id.slot).unwrap();
259
260        // NOTE(Marc): We must not leave any broken edges because the rest of the code relies on
261        // this being impossible.
262
263        for &in_node_index in &node.in_edges {
264            // SAFETY: By our own invariant, node indices in the edges vectors must be valid.
265            let out_edges = &mut unsafe { self.node_unchecked_mut(in_node_index) }.out_edges;
266            let edge_index = out_edges.iter().position(|&i| i == id.index()).unwrap();
267            out_edges.remove(edge_index);
268        }
269
270        for &out_node_index in &node.out_edges {
271            // SAFETY: By our own invariant, node indices in the edges vectors must be valid.
272            let in_edges = &mut unsafe { self.node_unchecked_mut(out_node_index) }.in_edges;
273            let edge_index = in_edges.iter().position(|&i| i == id.index()).unwrap();
274            in_edges.remove(edge_index);
275        }
276
277        node
278    }
279
280    fn capacity(&self) -> u32 {
281        self.inner.capacity()
282    }
283
284    fn len(&self) -> u32 {
285        self.inner.len()
286    }
287
288    fn task_node(&self, id: NodeId) -> Result<&TaskNode<W>> {
289        let node = self.node(id)?;
290
291        if let NodeInner::Task(task_node) = &node.inner {
292            Ok(task_node)
293        } else {
294            Err(TaskGraphError::InvalidNodeType)
295        }
296    }
297
298    unsafe fn task_node_unchecked(&self, index: NodeIndex) -> &TaskNode<W> {
299        // SAFETY: The caller must ensure that the `index` is valid.
300        let node = unsafe { self.node_unchecked(index) };
301
302        if let NodeInner::Task(task_node) = &node.inner {
303            task_node
304        } else {
305            // SAFETY: The caller must ensure that the node is a `TaskNode`.
306            unsafe { hint::unreachable_unchecked() }
307        }
308    }
309
310    fn task_node_mut(&mut self, id: NodeId) -> Result<&mut TaskNode<W>> {
311        let node = self.node_mut(id)?;
312
313        if let NodeInner::Task(task_node) = &mut node.inner {
314            Ok(task_node)
315        } else {
316            Err(TaskGraphError::InvalidNodeType)
317        }
318    }
319
320    unsafe fn task_node_unchecked_mut(&mut self, index: NodeIndex) -> &mut TaskNode<W> {
321        // SAFETY: The caller must ensure that the `index` is valid.
322        let node = unsafe { self.node_unchecked_mut(index) };
323
324        if let NodeInner::Task(task_node) = &mut node.inner {
325            task_node
326        } else {
327            // SAFETY: The caller must ensure that the node is a `TaskNode`.
328            unsafe { hint::unreachable_unchecked() }
329        }
330    }
331
332    fn node(&self, id: NodeId) -> Result<&Node<W>> {
333        // SAFETY: We never modify the map concurrently.
334        unsafe { self.inner.get_unprotected(id.slot) }.ok_or(TaskGraphError::InvalidNode)
335    }
336
337    unsafe fn node_unchecked(&self, index: NodeIndex) -> &Node<W> {
338        // SAFETY:
339        // * The caller must ensure that the `index` is valid.
340        // * We never modify the map concurrently.
341        unsafe { self.inner.index_unchecked_unprotected(index) }
342    }
343
344    fn node_mut(&mut self, id: NodeId) -> Result<&mut Node<W>> {
345        self.inner
346            .get_mut(id.slot)
347            .ok_or(TaskGraphError::InvalidNode)
348    }
349
350    unsafe fn node_unchecked_mut(&mut self, index: NodeIndex) -> &mut Node<W> {
351        // SAFETY: The caller must ensure that the `index` is valid.
352        unsafe { self.inner.index_unchecked_mut(index) }
353    }
354
355    fn node_many_mut<const N: usize>(&mut self, ids: [NodeId; N]) -> Result<[&mut Node<W>; N]> {
356        union Transmute<const N: usize> {
357            src: [NodeId; N],
358            dst: [SlotId; N],
359        }
360
361        // HACK: `transmute_unchecked` is not exposed even unstably at the moment, and the compiler
362        // isn't currently smart enough to figure this out using `transmute`.
363        //
364        // SAFETY: `NodeId` is `#[repr(transparent)]` over `SlotId` and both arrays have the same
365        // length.
366        let ids = unsafe { Transmute { src: ids }.dst };
367
368        self.inner
369            .get_many_mut(ids)
370            .ok_or(TaskGraphError::InvalidNode)
371    }
372
373    fn nodes(&self) -> IterUnprotected<'_, Node<W>> {
374        // SAFETY: We never modify the map concurrently.
375        unsafe { self.inner.iter_unprotected() }
376    }
377
378    fn nodes_mut(&mut self) -> IterMut<'_, Node<W>> {
379        self.inner.iter_mut()
380    }
381}
382
383impl Resources {
384    fn add_buffer(&mut self, create_info: &BufferCreateInfo) -> Id<Buffer> {
385        let mut tag = Buffer::TAG | Id::VIRTUAL_BIT;
386
387        if create_info.sharing.is_exclusive() {
388            tag |= Id::EXCLUSIVE_BIT;
389        }
390
391        let resource_info = ResourceInfo {
392            format: Format::UNDEFINED,
393            samples: SampleCount::Sample1,
394            usage: ImageUsage::empty(),
395        };
396
397        let slot = self.inner.insert_with_tag_mut(resource_info, tag);
398
399        unsafe { Id::new(slot) }
400    }
401
402    fn add_image(&mut self, create_info: &ImageCreateInfo) -> Id<Image> {
403        let mut tag = Image::TAG | Id::VIRTUAL_BIT;
404
405        if create_info.sharing.is_exclusive() {
406            tag |= Id::EXCLUSIVE_BIT;
407        }
408
409        let resource_info = ResourceInfo {
410            format: create_info.format,
411            samples: create_info.samples,
412            usage: create_info.usage,
413        };
414
415        let slot = self.inner.insert_with_tag_mut(resource_info, tag);
416
417        unsafe { Id::new(slot) }
418    }
419
420    fn add_swapchain(&mut self, create_info: &SwapchainCreateInfo) -> Id<Swapchain> {
421        let mut tag = Swapchain::TAG | Id::VIRTUAL_BIT;
422
423        if create_info.image_sharing.is_exclusive() {
424            tag |= Id::EXCLUSIVE_BIT;
425        }
426
427        let resource_info = ResourceInfo {
428            format: create_info.image_format,
429            samples: SampleCount::Sample1,
430            usage: create_info.image_usage,
431        };
432
433        let slot = self.inner.insert_with_tag_mut(resource_info, tag);
434
435        unsafe { Id::new(slot) }
436    }
437
438    fn add_physical_buffer(
439        &mut self,
440        physical_id: Id<Buffer>,
441    ) -> Result<Id<Buffer>, InvalidSlotError> {
442        let physical_resources = self.physical_resources.clone();
443        let buffer_state = physical_resources.buffer(physical_id)?;
444        let buffer = buffer_state.buffer();
445        let virtual_id = self.add_buffer(&BufferCreateInfo {
446            sharing: buffer.sharing().clone(),
447            ..Default::default()
448        });
449        self.physical_map
450            .insert(physical_id.erase(), virtual_id.erase());
451
452        Ok(virtual_id)
453    }
454
455    fn add_physical_image(
456        &mut self,
457        physical_id: Id<Image>,
458    ) -> Result<Id<Image>, InvalidSlotError> {
459        let physical_resources = self.physical_resources.clone();
460        let image_state = physical_resources.image(physical_id)?;
461        let image = image_state.image();
462        let virtual_id = self.add_image(&ImageCreateInfo {
463            sharing: image.sharing().clone(),
464            ..Default::default()
465        });
466        self.physical_map
467            .insert(physical_id.erase(), virtual_id.erase());
468
469        Ok(virtual_id)
470    }
471
472    fn add_physical_swapchain(
473        &mut self,
474        id: Id<Swapchain>,
475    ) -> Result<Id<Swapchain>, InvalidSlotError> {
476        let physical_resources = self.physical_resources.clone();
477        let swapchain_state = physical_resources.swapchain(id)?;
478        let swapchain = swapchain_state.swapchain();
479        let virtual_id = self.add_swapchain(&SwapchainCreateInfo {
480            image_sharing: swapchain.image_sharing().clone(),
481            ..Default::default()
482        });
483        self.physical_map.insert(id.erase(), virtual_id.erase());
484
485        Ok(virtual_id)
486    }
487
488    fn add_host_buffer_access(&mut self, mut id: Id<Buffer>, access_type: HostAccessType) {
489        if id.is_virtual() {
490            self.get(id.erase()).expect("invalid buffer");
491        } else if let Some(&virtual_id) = self.physical_map.get(&id.erase()) {
492            id = unsafe { virtual_id.parametrize() };
493        } else {
494            id = self.add_physical_buffer(id).expect("invalid buffer");
495        }
496
497        let host_accesses = match access_type {
498            HostAccessType::Read => &mut self.host_reads,
499            HostAccessType::Write => &mut self.host_writes,
500        };
501
502        if !host_accesses.contains(&id) {
503            host_accesses.push(id);
504        }
505    }
506
507    fn add_framebuffer(&mut self) -> Id<Framebuffer> {
508        let tag = Framebuffer::TAG | Id::VIRTUAL_BIT;
509        let slot = self.framebuffers.insert_with_tag_mut((), tag);
510
511        unsafe { Id::new(slot) }
512    }
513
514    fn capacity(&self) -> u32 {
515        self.inner.capacity()
516    }
517
518    fn len(&self) -> u32 {
519        self.inner.len()
520    }
521
522    pub(crate) fn physical_map(&self) -> &HashMap<Id, Id> {
523        &self.physical_map
524    }
525
526    fn get(&self, id: Id) -> Result<&ResourceInfo, InvalidSlotError> {
527        // SAFETY: We never modify the map concurrently.
528        unsafe { self.inner.get_unprotected(id.slot) }.ok_or(InvalidSlotError::new(id))
529    }
530
531    fn iter(&self) -> impl Iterator<Item = (Id, &ResourceInfo)> {
532        // SAFETY: We never modify the map concurrently.
533        unsafe { self.inner.iter_unprotected() }.map(|(slot, v)| (unsafe { Id::new(slot) }, v))
534    }
535
536    pub(crate) fn contains_host_buffer_access(
537        &self,
538        mut id: Id<Buffer>,
539        access_type: HostAccessType,
540    ) -> bool {
541        if !id.is_virtual() {
542            if let Some(&virtual_id) = self.physical_map.get(&id.erase()) {
543                id = unsafe { virtual_id.parametrize() };
544            } else {
545                return false;
546            }
547        }
548
549        let host_accesses = match access_type {
550            HostAccessType::Read => &self.host_reads,
551            HostAccessType::Write => &self.host_writes,
552        };
553
554        host_accesses.contains(&id)
555    }
556
557    fn framebuffer_mut(&mut self, id: Id<Framebuffer>) -> Option<&mut ()> {
558        self.framebuffers.get_mut(id.slot)
559    }
560}
561
562impl<W: ?Sized> fmt::Debug for TaskGraph<W> {
563    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
564        // FIXME:
565        f.debug_struct("TaskGraph").finish_non_exhaustive()
566    }
567}
568
569unsafe impl<W: ?Sized> DeviceOwned for TaskGraph<W> {
570    #[inline]
571    fn device(&self) -> &Arc<Device> {
572        self.resources.physical_resources.device()
573    }
574}
575
576/// The ID type used to refer to a node within a [`TaskGraph`].
577#[derive(Clone, Copy, PartialEq, Eq, Hash)]
578#[repr(transparent)]
579pub struct NodeId {
580    slot: SlotId,
581}
582
583impl NodeId {
584    fn index(self) -> NodeIndex {
585        self.slot.index()
586    }
587}
588
589impl fmt::Debug for NodeId {
590    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
591        f.debug_struct("NodeId")
592            .field("index", &self.slot.index())
593            .field("generation", &self.slot.generation())
594            .finish()
595    }
596}
597
598/// A node within a [`TaskGraph`] that represents a [`Task`] to be executed along with its resource
599/// accesses.
600pub struct TaskNode<W: ?Sized> {
601    accesses: ResourceAccesses,
602    attachments: Option<Attachments>,
603    queue_family_type: QueueFamilyType,
604    queue_family_index: u32,
605    dependency_level_index: u32,
606    subpass: Option<Subpass>,
607    task: Box<dyn Task<World = W>>,
608}
609
610pub(crate) struct ResourceAccesses {
611    inner: LinearMap<Id, ResourceAccess>,
612}
613
614#[derive(Clone, Copy, Default)]
615struct ResourceAccess {
616    stage_mask: PipelineStages,
617    access_mask: AccessFlags,
618    image_layout: ImageLayout,
619    queue_family_index: u32,
620}
621
622pub(crate) struct Attachments {
623    framebuffer_id: Id<Framebuffer>,
624    input_attachments: LinearMap<Id, AttachmentInfo<'static>>,
625    color_attachments: LinearMap<Id, AttachmentInfo<'static>>,
626    depth_stencil_attachment: Option<(Id, AttachmentInfo<'static>)>,
627}
628
629impl<W: ?Sized> TaskNode<W> {
630    fn new(queue_family_type: QueueFamilyType, task: impl Task<World = W>) -> Self {
631        TaskNode {
632            accesses: ResourceAccesses::new(),
633            attachments: None,
634            queue_family_type,
635            queue_family_index: 0,
636            dependency_level_index: 0,
637            subpass: None,
638            task: Box::new(task),
639        }
640    }
641
642    /// Returns the queue family type the task node was created with.
643    #[inline]
644    #[must_use]
645    pub fn queue_family_type(&self) -> QueueFamilyType {
646        self.queue_family_type
647    }
648
649    /// Returns a reference to the task the task node was created with.
650    #[inline]
651    #[must_use]
652    pub fn task(&self) -> &dyn Task<World = W> {
653        &*self.task
654    }
655
656    /// Returns a mutable reference to the task the task node was created with.
657    #[inline]
658    #[must_use]
659    pub fn task_mut(&mut self) -> &mut dyn Task<World = W> {
660        &mut *self.task
661    }
662
663    /// Returns the subpass that the task node is part of, if any.
664    #[inline]
665    pub fn subpass(&self) -> Option<&Subpass> {
666        self.subpass.as_ref()
667    }
668}
669
670impl ResourceAccesses {
671    const fn new() -> Self {
672        ResourceAccesses {
673            inner: LinearMap::new(),
674        }
675    }
676
677    fn get(&self, id: Id) -> Option<&ResourceAccess> {
678        self.inner.get(&id)
679    }
680
681    fn buffer_mut(
682        &mut self,
683        resources: &mut Resources,
684        id: Id<Buffer>,
685    ) -> (Id<Buffer>, Option<&mut ResourceAccess>) {
686        let (id, access) = self.get_mut(resources, id.erase()).expect("invalid buffer");
687
688        (unsafe { id.parametrize() }, access)
689    }
690
691    fn image_mut(
692        &mut self,
693        resources: &mut Resources,
694        id: Id<Image>,
695    ) -> (Id<Image>, Option<&mut ResourceAccess>) {
696        let (id, access) = self.get_mut(resources, id.erase()).expect("invalid image");
697
698        (unsafe { id.parametrize() }, access)
699    }
700
701    fn get_mut(
702        &mut self,
703        resources: &mut Resources,
704        mut id: Id,
705    ) -> Result<(Id, Option<&mut ResourceAccess>), InvalidSlotError> {
706        if id.is_virtual() {
707            resources.get(id)?;
708        } else if let Some(&virtual_id) = resources.physical_map.get(&id) {
709            id = virtual_id;
710        } else {
711            id = match id.object_type() {
712                ObjectType::Buffer => resources
713                    .add_physical_buffer(unsafe { id.parametrize() })?
714                    .erase(),
715                ObjectType::Image => resources
716                    .add_physical_image(unsafe { id.parametrize() })?
717                    .erase(),
718                ObjectType::Swapchain => resources
719                    .add_physical_swapchain(unsafe { id.parametrize() })?
720                    .erase(),
721                _ => unreachable!(),
722            };
723        }
724
725        let access = self.inner.get_mut(&id);
726
727        Ok((id, access))
728    }
729
730    fn iter(&self) -> impl Iterator<Item = (Id, &ResourceAccess)> {
731        self.inner.iter().map(|(id, access)| (*id, access))
732    }
733}
734
735impl Attachments {
736    fn keys(&self) -> impl Iterator<Item = &Id> {
737        self.input_attachments
738            .keys()
739            .chain(self.color_attachments.keys())
740            .chain(self.depth_stencil_attachment.iter().map(|(id, _)| id))
741    }
742
743    fn iter(&self) -> impl Iterator<Item = (&Id, &AttachmentInfo<'static>)> {
744        self.input_attachments
745            .iter()
746            .chain(self.color_attachments.iter())
747            .chain(
748                self.depth_stencil_attachment
749                    .iter()
750                    .map(|(id, attachment_state)| (id, attachment_state)),
751            )
752    }
753}
754
755const INPUT_ATTACHMENT_ACCESS_FLAGS: AccessFlags = AccessFlags::INPUT_ATTACHMENT_READ;
756const COLOR_ATTACHMENT_ACCESS_FLAGS: AccessFlags =
757    AccessFlags::COLOR_ATTACHMENT_READ.union(AccessFlags::COLOR_ATTACHMENT_WRITE);
758const DEPTH_STENCIL_ATTACHMENT_ACCESS_FLAGS: AccessFlags =
759    AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ.union(AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE);
760const ATTACHMENT_ACCESS_FLAGS: AccessFlags = INPUT_ATTACHMENT_ACCESS_FLAGS
761    .union(COLOR_ATTACHMENT_ACCESS_FLAGS)
762    .union(DEPTH_STENCIL_ATTACHMENT_ACCESS_FLAGS);
763
764const COLOR_ASPECTS: ImageAspects = ImageAspects::COLOR
765    .union(ImageAspects::PLANE_0)
766    .union(ImageAspects::PLANE_1)
767    .union(ImageAspects::PLANE_2);
768const DEPTH_STENCIL_ASPECTS: ImageAspects = ImageAspects::DEPTH.union(ImageAspects::STENCIL);
769
770/// A builder used to add resource accesses and attachments to a [`TaskNode`].
771pub struct TaskNodeBuilder<'a> {
772    id: NodeId,
773    accesses: &'a mut ResourceAccesses,
774    attachments: &'a mut Option<Attachments>,
775    resources: &'a mut Resources,
776}
777
778impl TaskNodeBuilder<'_> {
779    /// Adds a buffer access to the task node.
780    ///
781    /// # Panics
782    ///
783    /// - Panics if `id` is not a valid virtual resource ID nor a valid physical ID.
784    /// - Panics if `access_types` contains any access type that's not a valid buffer access type.
785    #[track_caller]
786    pub fn buffer_access(&mut self, id: Id<Buffer>, access_types: AccessTypes) -> &mut Self {
787        assert!(access_types.are_valid_buffer_access_types());
788
789        let (id, access) = self.accesses.buffer_mut(self.resources, id);
790
791        if let Some(access) = access {
792            access.stage_mask |= access_types.stage_mask();
793            access.access_mask |= access_types.access_mask();
794        } else {
795            self.accesses.inner.insert(
796                id.erase(),
797                ResourceAccess {
798                    stage_mask: access_types.stage_mask(),
799                    access_mask: access_types.access_mask(),
800                    image_layout: ImageLayout::Undefined,
801                    queue_family_index: vk::QUEUE_FAMILY_IGNORED,
802                },
803            );
804        }
805
806        self
807    }
808
809    /// Adds an image access to the task node.
810    ///
811    /// # Panics
812    ///
813    /// - Panics if `id` is not a valid virtual resource ID nor a valid physical ID.
814    /// - Panics if `access_types` contains any access type that's not a valid image access type.
815    /// - Panics if `access_types` contains attachment access types as well as other access types.
816    /// - Panics if `access_types` contains both color and depth/stencil access types.
817    /// - Panics if an access for `id` was already added and its image layout doesn't equal
818    ///   `access_types.image_layout(layout_type)`.
819    #[track_caller]
820    pub fn image_access(
821        &mut self,
822        id: Id<Image>,
823        access_types: AccessTypes,
824        layout_type: ImageLayoutType,
825    ) -> &mut Self {
826        assert!(access_types.are_valid_image_access_types());
827
828        let (id, access) = self.accesses.image_mut(self.resources, id);
829
830        let access_mask = access_types.access_mask();
831
832        if access_mask.intersects(ATTACHMENT_ACCESS_FLAGS) {
833            assert!(
834                ATTACHMENT_ACCESS_FLAGS.contains(access_mask),
835                "an image access that contains attachment access types must not contain any other \
836                access types",
837            );
838
839            assert!(
840                !(access_mask.intersects(COLOR_ATTACHMENT_ACCESS_FLAGS)
841                    && access_mask.intersects(DEPTH_STENCIL_ATTACHMENT_ACCESS_FLAGS)),
842                "an image access can't contain both color and depth/stencil attachment access \
843                types",
844            );
845        }
846
847        if let Some(access) = access {
848            assert_eq!(access.image_layout, access_types.image_layout(layout_type));
849
850            access.stage_mask |= access_types.stage_mask();
851            access.access_mask |= access_types.access_mask();
852        } else {
853            self.accesses.inner.insert(
854                id.erase(),
855                ResourceAccess {
856                    stage_mask: access_types.stage_mask(),
857                    access_mask: access_types.access_mask(),
858                    image_layout: access_types.image_layout(layout_type),
859                    queue_family_index: vk::QUEUE_FAMILY_IGNORED,
860                },
861            );
862        }
863
864        self
865    }
866
867    /// Sets the framebuffer that the task node's attachments will belong to. You must call this
868    /// method before adding attachments to the task node.
869    ///
870    /// # Panics
871    ///
872    /// - Panics if `id` is not a valid framebuffer ID.
873    /// - Panics if the framebuffer was already set.
874    #[track_caller]
875    pub fn framebuffer(&mut self, id: Id<Framebuffer>) -> &mut Self {
876        assert!(self.resources.framebuffer_mut(id).is_some());
877
878        assert!(
879            self.attachments.is_none(),
880            "a task node must use at most one framebuffer",
881        );
882
883        *self.attachments = Some(Attachments {
884            framebuffer_id: id,
885            color_attachments: LinearMap::new(),
886            input_attachments: LinearMap::new(),
887            depth_stencil_attachment: None,
888        });
889
890        self
891    }
892
893    /// Adds an input attachment to the task node.
894    ///
895    /// # Panics
896    ///
897    /// - Panics if the framebuffer wasn't set beforehand.
898    /// - Panics if `id` is not a valid virtual resource ID nor a valid physical ID.
899    /// - Panics if `access_types` contains any access type that's not an input attachment access
900    ///   type.
901    /// - Panics if an input attachment for `id` was already added.
902    /// - Panics if an input attachment using the `attachment_info.index` was already added.
903    /// - Panics if a color or depth/stencil attachment using `id` was added but the attachment
904    ///   infos don't match.
905    /// - Panics if an access for `id` was already added and its image layout doesn't equal
906    ///   `access_types.image_layout(layout_type)`.
907    #[track_caller]
908    pub fn input_attachment(
909        &mut self,
910        id: Id<Image>,
911        mut access_types: AccessTypes,
912        layout_type: ImageLayoutType,
913        attachment_info: &AttachmentInfo<'_>,
914    ) -> &mut Self {
915        let attachments = self
916            .attachments
917            .as_mut()
918            .expect("the framebuffer must be set before adding attachments");
919
920        let (id, _access) = self.accesses.image_mut(self.resources, id);
921
922        assert!(INPUT_ATTACHMENT_ACCESS_FLAGS.contains(access_types.access_mask()));
923
924        assert!(
925            attachments.input_attachments.get(&id.erase()).is_none(),
926            "a task node must use an image as at most one input attachment",
927        );
928
929        let input_attachment_index = attachment_info.index;
930
931        assert!(
932            !attachments
933                .input_attachments
934                .values()
935                .any(|info| info.index == input_attachment_index),
936            "the task node already has an input attachment that uses the input attachment index \
937            `{input_attachment_index}`",
938        );
939
940        assert!(
941            !attachments
942                .color_attachments
943                .get(&id.erase())
944                .is_some_and(|attachment_info2| attachment_info2 == attachment_info),
945            "the task node also uses the image as a color attachment but the attachment infos \
946            don't match",
947        );
948
949        assert!(
950            !attachments
951                .depth_stencil_attachment
952                .as_ref()
953                .is_some_and(|(_, attachment_info2)| attachment_info2 == attachment_info),
954            "the task node also uses the image as a depth/stencil attachment but the attachment \
955            infos dont' match",
956        );
957
958        let resource_info = self.resources.get(id.erase()).unwrap();
959        let format = if attachment_info.format == Format::UNDEFINED {
960            resource_info.format
961        } else {
962            attachment_info.format
963        };
964
965        // Clear operations (whether using `AttachmentLoadOp::Clear` or the `clear_attachments`
966        // command) are attachment writes.
967        if attachment_info.clear {
968            if format.aspects().intersects(COLOR_ASPECTS) {
969                access_types |= AccessTypes::COLOR_ATTACHMENT_WRITE;
970            } else if format.aspects().intersects(DEPTH_STENCIL_ASPECTS) {
971                access_types |= AccessTypes::DEPTH_STENCIL_ATTACHMENT_WRITE;
972            } else {
973                unreachable!();
974            }
975        }
976
977        attachments.input_attachments.insert(
978            id.erase(),
979            AttachmentInfo {
980                format,
981                _ne: crate::NE,
982                ..attachment_info.clone()
983            },
984        );
985
986        self.image_access(id, access_types, layout_type)
987    }
988
989    /// Adds a color attachment to the task node.
990    ///
991    /// # Panics
992    ///
993    /// - Panics if the framebuffer wasn't set beforehand.
994    /// - Panics if `id` is not a valid virtual resource ID nor a valid physical ID.
995    /// - Panics if `access_types` contains any access type that's not a color attachment access
996    ///   type.
997    /// - Panics if a color attachment using `id` was already added.
998    /// - Panics if a color attachment using the `attachment_info.index` was already added.
999    /// - Panics if an input attachment using `id` was added but the attachment infos don't match.
1000    /// - Panics if `attachment_info.format` is not a color format.
1001    /// - Panics if an access for `id` was already added and its image layout doesn't equal
1002    ///   `access_types.image_layout(layout_type)`.
1003    #[track_caller]
1004    pub fn color_attachment(
1005        &mut self,
1006        id: Id<Image>,
1007        mut access_types: AccessTypes,
1008        layout_type: ImageLayoutType,
1009        attachment_info: &AttachmentInfo<'_>,
1010    ) -> &mut Self {
1011        let attachments = self
1012            .attachments
1013            .as_mut()
1014            .expect("the framebuffer must be set before adding attachments");
1015
1016        let (id, _access) = self.accesses.image_mut(self.resources, id);
1017
1018        assert!(COLOR_ATTACHMENT_ACCESS_FLAGS.contains(access_types.access_mask()));
1019
1020        assert!(
1021            attachments.color_attachments.get(&id.erase()).is_none(),
1022            "a task node must use an image as at most one color attachment",
1023        );
1024
1025        let location = attachment_info.index;
1026
1027        assert!(
1028            !attachments
1029                .color_attachments
1030                .values()
1031                .any(|info| info.index == location),
1032            "the task node already has a color attachment that uses the location `{location}`",
1033        );
1034
1035        assert!(
1036            !attachments
1037                .input_attachments
1038                .get(&id.erase())
1039                .is_some_and(|attachment_info2| attachment_info2 == attachment_info),
1040            "the task node also uses the image as an input attachment but the attachment infos \
1041            don't match",
1042        );
1043
1044        let resource_info = self.resources.get(id.erase()).unwrap();
1045        let format = if attachment_info.format == Format::UNDEFINED {
1046            resource_info.format
1047        } else {
1048            attachment_info.format
1049        };
1050
1051        assert!(
1052            format.aspects().intersects(COLOR_ASPECTS),
1053            "an image can only be used as a color attachment if it has a color format",
1054        );
1055
1056        // Clear operations (whether using `AttachmentLoadOp::Clear` or the `clear_attachments`
1057        // command) are attachment writes.
1058        if attachment_info.clear {
1059            access_types |= AccessTypes::COLOR_ATTACHMENT_WRITE;
1060        }
1061
1062        attachments.color_attachments.insert(
1063            id.erase(),
1064            AttachmentInfo {
1065                format,
1066                _ne: crate::NE,
1067                ..attachment_info.clone()
1068            },
1069        );
1070
1071        self.image_access(id, access_types, layout_type)
1072    }
1073
1074    /// Adds a depth/stencil attachment to the task node.
1075    ///
1076    /// # Panics
1077    ///
1078    /// - Panics if the framebuffer wasn't set beforehand.
1079    /// - Panics if `id` is not a valid virtual resource ID nor a valid physical ID.
1080    /// - Panics if `access_types` contains any access type that's not a depth/stencil attachment
1081    ///   access type.
1082    /// - Panics if a depth/stencil attachment was already added.
1083    /// - Panics if an input attachment using `id` was added but the attachment infos don't match.
1084    /// - Panics if `attachment_info.format` is not a depth/stencil format.
1085    /// - Panics if an access for `id` was already added and its image layout doesn't equal
1086    ///   `access_types.image_layout(layout_type)`.
1087    #[track_caller]
1088    pub fn depth_stencil_attachment(
1089        &mut self,
1090        id: Id<Image>,
1091        mut access_types: AccessTypes,
1092        layout_type: ImageLayoutType,
1093        attachment_info: &AttachmentInfo<'_>,
1094    ) -> &mut Self {
1095        let attachments = self
1096            .attachments
1097            .as_mut()
1098            .expect("the framebuffer must be set before adding attachments");
1099
1100        let (id, _access) = self.accesses.image_mut(self.resources, id);
1101
1102        assert!(DEPTH_STENCIL_ATTACHMENT_ACCESS_FLAGS.contains(access_types.access_mask()));
1103
1104        assert!(
1105            attachments.depth_stencil_attachment.is_none(),
1106            "a task node must have at most one depth/stencil attachment",
1107        );
1108
1109        assert!(
1110            !attachments
1111                .input_attachments
1112                .get(&id.erase())
1113                .is_some_and(|attachment_info2| attachment_info2 == attachment_info),
1114            "the task node also uses the image as an input attachment but the attachment infos \
1115            don't match",
1116        );
1117
1118        let resource_info = self.resources.get(id.erase()).unwrap();
1119        let format = if attachment_info.format == Format::UNDEFINED {
1120            resource_info.format
1121        } else {
1122            attachment_info.format
1123        };
1124
1125        assert!(
1126            format.aspects().intersects(DEPTH_STENCIL_ASPECTS),
1127            "an image can only be used as a depth/stencil attachment if it has a depth/stencil \
1128            format",
1129        );
1130
1131        // Clear operations (whether using `AttachmentLoadOp::Clear` or the `clear_attachments`
1132        // command) are attachment writes.
1133        if attachment_info.clear {
1134            access_types |= AccessTypes::DEPTH_STENCIL_ATTACHMENT_WRITE;
1135        }
1136
1137        attachments.depth_stencil_attachment = Some((
1138            id.erase(),
1139            AttachmentInfo {
1140                format,
1141                _ne: crate::NE,
1142                ..attachment_info.clone()
1143            },
1144        ));
1145
1146        self.image_access(id, access_types, layout_type)
1147    }
1148
1149    /// Finishes building the task node and returns the ID of the built node.
1150    #[inline]
1151    pub fn build(&mut self) -> NodeId {
1152        self.id
1153    }
1154}
1155
1156/// Parameters describing a rendering attachment.
1157#[derive(Clone, Debug, PartialEq, Eq)]
1158pub struct AttachmentInfo<'a> {
1159    /// The input attachment index or location to use.
1160    ///
1161    /// For input attachments, this corresponds to the input attachment index you read from the
1162    /// shader. For color attachments, this corresponds to the location you read or write from the
1163    /// shader. For depth/stencil attachments, this is ignored.
1164    ///
1165    /// The default value is `0`.
1166    pub index: u32,
1167
1168    /// If set to `true`, the attachment is cleared before the task is executed. The clear value is
1169    /// set by the task in [`Task::clear_values`].
1170    ///
1171    /// The default value is `false`.
1172    pub clear: bool,
1173
1174    /// The format of the attachment.
1175    ///
1176    /// If left as the default, the format of the image is used.
1177    ///
1178    /// If this is set to a format that is different from the image, the image must be created with
1179    /// the `MUTABLE_FORMAT` flag.
1180    ///
1181    /// On [portability subset] devices, if `format` does not have the same number of components
1182    /// and bits per component as the parent image's format, the
1183    /// [`image_view_format_reinterpretation`] feature must be enabled on the device.
1184    ///
1185    /// The default value is `Format::UNDEFINED`.
1186    ///
1187    /// [portability subset]: vulkano::instance#portability-subset-devices-and-the-enumerate_portability-flag
1188    /// [`image_view_format_reinterpretation`]: vulkano::device::DeviceFeatures::image_view_format_reinterpretation
1189    pub format: Format,
1190
1191    /// How to map components of each pixel.
1192    ///
1193    /// On [portability subset] devices, if `component_mapping` is not the identity mapping, the
1194    /// [`image_view_format_swizzle`] feature must be enabled on the device.
1195    ///
1196    /// The default value is [`ComponentMapping::identity()`].
1197    ///
1198    /// [portability subset]: vulkano::instance#portability-subset-devices-and-the-enumerate_portability-flag
1199    /// [`image_view_format_swizzle`]: crate::device::DeviceFeatures::image_view_format_swizzle
1200    pub component_mapping: ComponentMapping,
1201
1202    /// The mip level to render to.
1203    ///
1204    /// The default value is `0`.
1205    pub mip_level: u32,
1206
1207    /// The base array layer to render to.
1208    ///
1209    /// The default value is `0`.
1210    pub base_array_layer: u32,
1211
1212    pub _ne: crate::NonExhaustive<'a>,
1213}
1214
1215impl Default for AttachmentInfo<'_> {
1216    #[inline]
1217    fn default() -> Self {
1218        AttachmentInfo {
1219            index: 0,
1220            clear: false,
1221            format: Format::UNDEFINED,
1222            component_mapping: ComponentMapping::identity(),
1223            mip_level: 0,
1224            base_array_layer: 0,
1225            _ne: crate::NE,
1226        }
1227    }
1228}
1229
1230/// A [`TaskGraph`] that has been compiled into an executable form.
1231pub struct ExecutableTaskGraph<W: ?Sized> {
1232    graph: TaskGraph<W>,
1233    flight_id: Id<Flight>,
1234    instructions: Vec<Instruction>,
1235    submissions: Vec<Submission>,
1236    barriers: Vec<MemoryBarrier>,
1237    render_passes: RefCell<Vec<RenderPassState>>,
1238    clear_attachments: Vec<Id>,
1239    semaphores: RefCell<Vec<Arc<Semaphore>>>,
1240    swapchains: SmallVec<[Id<Swapchain>; 1]>,
1241    present_queue: Option<Arc<Queue>>,
1242    last_accesses: Vec<ResourceAccess>,
1243}
1244
1245// FIXME: Initial queue family ownership transfers
1246#[derive(Debug)]
1247struct Submission {
1248    queue: Arc<Queue>,
1249    initial_barrier_range: Range<BarrierIndex>,
1250    instruction_range: Range<InstructionIndex>,
1251}
1252
1253type InstructionIndex = usize;
1254
1255#[derive(Clone, Debug)]
1256enum Instruction {
1257    WaitAcquire {
1258        swapchain_id: Id<Swapchain>,
1259        stage_mask: PipelineStages,
1260    },
1261    WaitSemaphore {
1262        semaphore_index: SemaphoreIndex,
1263        stage_mask: PipelineStages,
1264    },
1265    ExecuteTask {
1266        node_index: NodeIndex,
1267    },
1268    PipelineBarrier {
1269        barrier_range: Range<BarrierIndex>,
1270    },
1271    // TODO:
1272    // SetEvent {
1273    //     event_index: EventIndex,
1274    //     barrier_range: Range<BarrierIndex>,
1275    // },
1276    // WaitEvent {
1277    //     event_index: EventIndex,
1278    //     barrier_range: Range<BarrierIndex>,
1279    // },
1280    BeginRenderPass {
1281        render_pass_index: RenderPassIndex,
1282    },
1283    NextSubpass,
1284    EndRenderPass,
1285    ClearAttachments {
1286        node_index: NodeIndex,
1287        render_pass_index: RenderPassIndex,
1288        clear_attachment_range: Range<ClearAttachmentIndex>,
1289    },
1290    SignalSemaphore {
1291        semaphore_index: SemaphoreIndex,
1292        stage_mask: PipelineStages,
1293    },
1294    SignalPrePresent {
1295        swapchain_id: Id<Swapchain>,
1296        stage_mask: PipelineStages,
1297    },
1298    WaitPrePresent {
1299        swapchain_id: Id<Swapchain>,
1300        stage_mask: PipelineStages,
1301    },
1302    SignalPresent {
1303        swapchain_id: Id<Swapchain>,
1304        stage_mask: PipelineStages,
1305    },
1306    FlushSubmit,
1307    Submit,
1308}
1309
1310type RenderPassIndex = usize;
1311
1312type SemaphoreIndex = usize;
1313
1314type BarrierIndex = u32;
1315
1316type ClearAttachmentIndex = usize;
1317
1318#[derive(Clone, Debug)]
1319struct MemoryBarrier {
1320    src_stage_mask: PipelineStages,
1321    src_access_mask: AccessFlags,
1322    dst_stage_mask: PipelineStages,
1323    dst_access_mask: AccessFlags,
1324    old_layout: ImageLayout,
1325    new_layout: ImageLayout,
1326    src_queue_family_index: u32,
1327    dst_queue_family_index: u32,
1328    resource: Id,
1329}
1330
1331#[derive(Debug)]
1332struct RenderPassState {
1333    render_pass: Arc<RenderPass>,
1334    attachments: LinearMap<Id, AttachmentState>,
1335    framebuffers: Vec<Arc<Framebuffer>>,
1336    clear_node_indices: Vec<NodeIndex>,
1337}
1338
1339#[derive(Debug)]
1340struct AttachmentState {
1341    index: u32,
1342    format: Format,
1343    component_mapping: ComponentMapping,
1344    mip_level: u32,
1345    base_array_layer: u32,
1346}
1347
1348impl<W: ?Sized> ExecutableTaskGraph<W> {
1349    /// Returns a reference to the task node corresponding to `id`.
1350    #[inline]
1351    pub fn task_node(&self, id: NodeId) -> Result<&TaskNode<W>> {
1352        self.graph.task_node(id)
1353    }
1354
1355    /// Returns a mutable reference to the task node corresponding to `id`.
1356    #[inline]
1357    pub fn task_node_mut(&mut self, id: NodeId) -> Result<&mut TaskNode<W>> {
1358        self.graph.task_node_mut(id)
1359    }
1360
1361    /// Returns an iterator over all [`TaskNode`]s.
1362    #[inline]
1363    pub fn task_nodes(&self) -> TaskNodes<'_, W> {
1364        self.graph.task_nodes()
1365    }
1366
1367    /// Returns an iterator over all [`TaskNode`]s that allows you to mutate them.
1368    #[inline]
1369    pub fn task_nodes_mut(&mut self) -> TaskNodesMut<'_, W> {
1370        self.graph.task_nodes_mut()
1371    }
1372
1373    /// Returns the flight ID that the task graph was compiled with.
1374    #[inline]
1375    pub fn flight_id(&self) -> Id<Flight> {
1376        self.flight_id
1377    }
1378}
1379
1380impl<W: ?Sized> fmt::Debug for ExecutableTaskGraph<W> {
1381    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1382        let mut debug = f.debug_struct("ExecutableTaskGraph");
1383
1384        debug
1385            .field("graph", &self.graph)
1386            .field("flight_id", &self.flight_id)
1387            .field("instructions", &self.instructions)
1388            .field("submissions", &self.submissions)
1389            .field("barriers", &self.barriers)
1390            .field("render_passes", &self.render_passes)
1391            .field("clear_attachments", &self.clear_attachments)
1392            .field("semaphores", &self.semaphores)
1393            .field("swapchains", &self.swapchains)
1394            .field("present_queue", &self.present_queue)
1395            .finish_non_exhaustive()
1396    }
1397}
1398
1399unsafe impl<W: ?Sized> DeviceOwned for ExecutableTaskGraph<W> {
1400    #[inline]
1401    fn device(&self) -> &Arc<Device> {
1402        self.graph.device()
1403    }
1404}
1405
1406/// An iterator over all [`TaskNode`]s within a [`TaskGraph`].
1407///
1408/// This type is created by the [`task_nodes`] method on [`TaskGraph`].
1409///
1410/// [`task_nodes`]: TaskGraph::task_nodes
1411pub struct TaskNodes<'a, W: ?Sized> {
1412    inner: concurrent_slotmap::IterUnprotected<'a, Node<W>>,
1413}
1414
1415impl<W: ?Sized> fmt::Debug for TaskNodes<'_, W> {
1416    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1417        f.debug_struct("TaskNodes").finish_non_exhaustive()
1418    }
1419}
1420
1421impl<'a, W: ?Sized> Iterator for TaskNodes<'a, W> {
1422    type Item = &'a TaskNode<W>;
1423
1424    #[inline]
1425    fn next(&mut self) -> Option<Self::Item> {
1426        loop {
1427            let (_, node) = self.inner.next()?;
1428
1429            if let NodeInner::Task(task_node) = &node.inner {
1430                break Some(task_node);
1431            }
1432        }
1433    }
1434
1435    #[inline]
1436    fn size_hint(&self) -> (usize, Option<usize>) {
1437        self.inner.size_hint()
1438    }
1439}
1440
1441impl<W: ?Sized> DoubleEndedIterator for TaskNodes<'_, W> {
1442    #[inline]
1443    fn next_back(&mut self) -> Option<Self::Item> {
1444        loop {
1445            let (_, node) = self.inner.next_back()?;
1446
1447            if let NodeInner::Task(task_node) = &node.inner {
1448                break Some(task_node);
1449            }
1450        }
1451    }
1452}
1453
1454impl<W: ?Sized> FusedIterator for TaskNodes<'_, W> {}
1455
1456/// An iterator over all [`TaskNode`]s within a [`TaskGraph`] that allows you to mutate them.
1457///
1458/// This type is created by the [`task_nodes_mut`] method on [`TaskGraph`].
1459///
1460/// [`task_nodes_mut`]: TaskGraph::task_nodes_mut
1461pub struct TaskNodesMut<'a, W: ?Sized> {
1462    inner: concurrent_slotmap::IterMut<'a, Node<W>>,
1463}
1464
1465impl<W: ?Sized> fmt::Debug for TaskNodesMut<'_, W> {
1466    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1467        f.debug_struct("TaskNodesMut").finish_non_exhaustive()
1468    }
1469}
1470
1471impl<'a, W: ?Sized> Iterator for TaskNodesMut<'a, W> {
1472    type Item = &'a mut TaskNode<W>;
1473
1474    #[inline]
1475    fn next(&mut self) -> Option<Self::Item> {
1476        loop {
1477            let (_, node) = self.inner.next()?;
1478
1479            if let NodeInner::Task(task_node) = &mut node.inner {
1480                break Some(task_node);
1481            }
1482        }
1483    }
1484
1485    #[inline]
1486    fn size_hint(&self) -> (usize, Option<usize>) {
1487        self.inner.size_hint()
1488    }
1489}
1490
1491impl<W: ?Sized> DoubleEndedIterator for TaskNodesMut<'_, W> {
1492    #[inline]
1493    fn next_back(&mut self) -> Option<Self::Item> {
1494        loop {
1495            let (_, node) = self.inner.next_back()?;
1496
1497            if let NodeInner::Task(task_node) = &mut node.inner {
1498                break Some(task_node);
1499            }
1500        }
1501    }
1502}
1503
1504impl<W: ?Sized> FusedIterator for TaskNodesMut<'_, W> {}
1505
1506type Result<T = (), E = TaskGraphError> = ::std::result::Result<T, E>;
1507
1508/// Error that can happen when doing operations on a [`TaskGraph`].
1509#[derive(Debug, PartialEq, Eq)]
1510pub enum TaskGraphError {
1511    InvalidNode,
1512    InvalidNodeType,
1513    InvalidEdge,
1514    DuplicateEdge,
1515}
1516
1517impl fmt::Display for TaskGraphError {
1518    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1519        let msg = match self {
1520            Self::InvalidNode => "a node with the given ID does not exist",
1521            Self::InvalidNodeType => {
1522                "the node with the given ID has a type that is incompatible with the operation"
1523            }
1524            Self::InvalidEdge => "an edge between the given nodes does not exist",
1525            Self::DuplicateEdge => "an edge between the given nodes already exists",
1526        };
1527
1528        f.write_str(msg)
1529    }
1530}
1531
1532impl Error for TaskGraphError {}
1533
1534#[cfg(test)]
1535mod tests {
1536    use super::*;
1537    use crate::tests::test_queues;
1538    use std::marker::PhantomData;
1539
1540    #[test]
1541    fn basic_usage1() {
1542        let (resources, _) = test_queues!();
1543        let mut graph = TaskGraph::<()>::new(&resources, 10, 0);
1544
1545        let x = graph
1546            .create_task_node("X", QueueFamilyType::Graphics, PhantomData)
1547            .build();
1548        let y = graph
1549            .create_task_node("Y", QueueFamilyType::Graphics, PhantomData)
1550            .build();
1551
1552        graph.add_edge(x, y).unwrap();
1553        assert!(graph.nodes.node(x).unwrap().out_edges.contains(&y.index()));
1554        assert!(graph.nodes.node(y).unwrap().in_edges.contains(&x.index()));
1555        assert_eq!(graph.add_edge(x, y), Err(TaskGraphError::DuplicateEdge));
1556
1557        graph.remove_edge(x, y).unwrap();
1558        assert!(!graph.nodes.node(x).unwrap().out_edges.contains(&y.index()));
1559        assert!(!graph.nodes.node(y).unwrap().in_edges.contains(&x.index()));
1560
1561        assert_eq!(graph.remove_edge(x, y), Err(TaskGraphError::InvalidEdge));
1562
1563        graph.add_edge(y, x).unwrap();
1564        assert!(graph.nodes.node(y).unwrap().out_edges.contains(&x.index()));
1565        assert!(graph.nodes.node(x).unwrap().in_edges.contains(&y.index()));
1566        assert_eq!(graph.add_edge(y, x), Err(TaskGraphError::DuplicateEdge));
1567
1568        graph.remove_edge(y, x).unwrap();
1569        assert!(!graph.nodes.node(y).unwrap().out_edges.contains(&x.index()));
1570        assert!(!graph.nodes.node(x).unwrap().in_edges.contains(&y.index()));
1571
1572        assert_eq!(graph.remove_edge(y, x), Err(TaskGraphError::InvalidEdge));
1573    }
1574
1575    #[test]
1576    fn basic_usage2() {
1577        let (resources, _) = test_queues!();
1578        let mut graph = TaskGraph::<()>::new(&resources, 10, 0);
1579
1580        let x = graph
1581            .create_task_node("X", QueueFamilyType::Graphics, PhantomData)
1582            .build();
1583        let y = graph
1584            .create_task_node("Y", QueueFamilyType::Graphics, PhantomData)
1585            .build();
1586        let z = graph
1587            .create_task_node("Z", QueueFamilyType::Graphics, PhantomData)
1588            .build();
1589
1590        assert!(graph.task_node(x).is_ok());
1591        assert!(graph.task_node(y).is_ok());
1592        assert!(graph.task_node(z).is_ok());
1593        assert!(graph.task_node_mut(x).is_ok());
1594        assert!(graph.task_node_mut(y).is_ok());
1595        assert!(graph.task_node_mut(z).is_ok());
1596
1597        graph.add_edge(x, y).unwrap();
1598        graph.add_edge(y, z).unwrap();
1599        assert!(graph.nodes.node(x).unwrap().out_edges.contains(&y.index()));
1600        assert!(graph.nodes.node(z).unwrap().in_edges.contains(&y.index()));
1601
1602        graph.remove_task_node(y).unwrap();
1603        assert!(!graph.nodes.node(x).unwrap().out_edges.contains(&y.index()));
1604        assert!(!graph.nodes.node(z).unwrap().in_edges.contains(&y.index()));
1605
1606        assert!(matches!(
1607            graph.remove_task_node(y),
1608            Err(TaskGraphError::InvalidNode),
1609        ));
1610    }
1611
1612    #[test]
1613    fn self_referential_node() {
1614        let (resources, _) = test_queues!();
1615        let mut graph = TaskGraph::<()>::new(&resources, 10, 0);
1616
1617        let x = graph
1618            .create_task_node("X", QueueFamilyType::Graphics, PhantomData)
1619            .build();
1620
1621        assert_eq!(graph.add_edge(x, x), Err(TaskGraphError::InvalidNode));
1622    }
1623}