1pub 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
35pub 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 #[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 #[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 #[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 framebuffers: SlotMap::new(256),
104 },
105 }
106 }
107
108 #[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 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 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 _ => unreachable!(),
143 };
144
145 Ok(task)
146 }
147
148 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 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 #[inline]
185 pub fn task_node(&self, id: NodeId) -> Result<&TaskNode<W>> {
186 self.nodes.task_node(id)
187 }
188
189 #[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 #[inline]
197 pub fn task_nodes(&self) -> TaskNodes<'_, W> {
198 TaskNodes {
199 inner: self.nodes.nodes(),
200 }
201 }
202
203 #[inline]
205 pub fn task_nodes_mut(&mut self) -> TaskNodesMut<'_, W> {
206 TaskNodesMut {
207 inner: self.nodes.nodes_mut(),
208 }
209 }
210
211 #[must_use]
213 pub fn add_buffer(&mut self, create_info: &BufferCreateInfo) -> Id<Buffer> {
214 self.resources.add_buffer(create_info)
215 }
216
217 #[must_use]
219 pub fn add_image(&mut self, create_info: &ImageCreateInfo) -> Id<Image> {
220 self.resources.add_image(create_info)
221 }
222
223 #[must_use]
225 pub fn add_swapchain(&mut self, create_info: &SwapchainCreateInfo) -> Id<Swapchain> {
226 self.resources.add_swapchain(create_info)
227 }
228
229 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 #[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 for &in_node_index in &node.in_edges {
264 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 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 let node = unsafe { self.node_unchecked(index) };
301
302 if let NodeInner::Task(task_node) = &node.inner {
303 task_node
304 } else {
305 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 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 unsafe { hint::unreachable_unchecked() }
329 }
330 }
331
332 fn node(&self, id: NodeId) -> Result<&Node<W>> {
333 unsafe { self.inner.get_unprotected(id.slot) }.ok_or(TaskGraphError::InvalidNode)
335 }
336
337 unsafe fn node_unchecked(&self, index: NodeIndex) -> &Node<W> {
338 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 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 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 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 unsafe { self.inner.get_unprotected(id.slot) }.ok_or(InvalidSlotError::new(id))
529 }
530
531 fn iter(&self) -> impl Iterator<Item = (Id, &ResourceInfo)> {
532 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 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#[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
598pub 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 #[inline]
644 #[must_use]
645 pub fn queue_family_type(&self) -> QueueFamilyType {
646 self.queue_family_type
647 }
648
649 #[inline]
651 #[must_use]
652 pub fn task(&self) -> &dyn Task<World = W> {
653 &*self.task
654 }
655
656 #[inline]
658 #[must_use]
659 pub fn task_mut(&mut self) -> &mut dyn Task<World = W> {
660 &mut *self.task
661 }
662
663 #[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
770pub 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 #[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 #[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 #[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 #[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 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 #[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 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 #[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 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 #[inline]
1151 pub fn build(&mut self) -> NodeId {
1152 self.id
1153 }
1154}
1155
1156#[derive(Clone, Debug, PartialEq, Eq)]
1158pub struct AttachmentInfo<'a> {
1159 pub index: u32,
1167
1168 pub clear: bool,
1173
1174 pub format: Format,
1190
1191 pub component_mapping: ComponentMapping,
1201
1202 pub mip_level: u32,
1206
1207 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
1230pub 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#[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 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 #[inline]
1351 pub fn task_node(&self, id: NodeId) -> Result<&TaskNode<W>> {
1352 self.graph.task_node(id)
1353 }
1354
1355 #[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 #[inline]
1363 pub fn task_nodes(&self) -> TaskNodes<'_, W> {
1364 self.graph.task_nodes()
1365 }
1366
1367 #[inline]
1369 pub fn task_nodes_mut(&mut self) -> TaskNodesMut<'_, W> {
1370 self.graph.task_nodes_mut()
1371 }
1372
1373 #[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
1406pub 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
1456pub 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#[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}