Skip to main content

vk_graph/
lib.rs

1/*!
2
3A high-performance [Vulkan](https://www.vulkan.org/) driver with automated resource management and
4execution. Start with [`Graph`] — bind resources, record commands, and submit for execution.
5
6- **Beginner**: [`Graph`], [`cmd`], [`node`], [`pool`] — the high-level graph API
7- **Intermediate**: [`driver`] — smart-pointer Vulkan wrappers (resources, pipelines, device)
8- **Expert**: [`driver`] re-exports `ash` and `vk_sync` — raw Vulkan bindings
9
10For installation, guides, and examples see the [Guide Book](https://attackgoat.github.io/vk-graph).
11
12*/
13
14#![deny(missing_docs)]
15#![deny(rustdoc::broken_intra_doc_links)]
16
17pub mod cmd;
18pub mod driver;
19pub mod node;
20pub mod pool;
21pub mod stream;
22pub mod submission;
23
24mod lazy_str;
25
26pub use self::lazy_str::LazyStr;
27
28use {
29    self::{
30        cmd::{AttachmentIndex, Binding, Command, SubresourceAccess, ViewInfo},
31        node::{
32            AccelerationStructureLeaseNode, AccelerationStructureNode,
33            AnyAccelerationStructureNode, AnyBufferNode, AnyImageNode, BufferLeaseNode, BufferNode,
34            ImageLeaseNode, ImageNode, SwapchainImageNode,
35        },
36    },
37    crate::{
38        cmd::{ClearColorValue, CommandRef},
39        driver::{
40            DescriptorBindingMap,
41            accel_struct::AccelerationStructureInfo,
42            buffer::BufferInfo,
43            compute::ComputePipeline,
44            format_aspect_mask,
45            graphics::{DepthStencilInfo, GraphicsPipeline},
46            image::{ImageInfo, ImageViewInfo, SampleCount},
47            ray_tracing::RayTracingPipeline,
48            render_pass::ResolveMode,
49            shader::PipelineDescriptorInfo,
50        },
51        driver::{
52            accel_struct::AccelerationStructure, buffer::Buffer, image::Image,
53            swapchain::SwapchainImage,
54        },
55        pool::Lease,
56        submission::Submission,
57    },
58    ash::vk,
59    smallvec::SmallVec,
60    std::{
61        cmp::Ord,
62        collections::{BTreeMap, HashMap},
63        fmt::{Debug, Formatter},
64        mem,
65        ops::Range,
66        ops::{Deref, DerefMut},
67        slice::Iter,
68        sync::{
69            Arc,
70            atomic::{AtomicU8, Ordering},
71        },
72    },
73    vk_sync::AccessType,
74};
75
76#[cfg(feature = "checked")]
77use std::sync::atomic::AtomicU64;
78
79type CommandFn = Arc<dyn for<'a> Fn(CommandRef<'a>) + Send + Sync>;
80type CommandFnOnce = Box<dyn FnOnce(CommandRef) + Send>;
81type NodeIndex = usize;
82
83#[derive(Debug)]
84struct AtomicCommandExecution(AtomicU8);
85
86impl AtomicCommandExecution {
87    const PENDING: u8 = 0xf0;
88    const EXECUTED: u8 = 0xf1;
89    const ABANDONED: u8 = 0xf2;
90
91    fn new_pending() -> Arc<Self> {
92        Arc::new(Self(AtomicU8::new(Self::PENDING)))
93    }
94
95    fn compare_pending_exchange_abandoned(&self) {
96        let _ = self.0.compare_exchange(
97            Self::PENDING,
98            Self::ABANDONED,
99            Ordering::AcqRel,
100            Ordering::Acquire,
101        );
102    }
103
104    fn compare_pending_exchange_executed(&self) {
105        let _ = self.0.compare_exchange(
106            Self::PENDING,
107            Self::EXECUTED,
108            Ordering::AcqRel,
109            Ordering::Acquire,
110        );
111    }
112
113    fn load(&self) -> u8 {
114        self.0.load(Ordering::Acquire)
115    }
116}
117
118impl Drop for AtomicCommandExecution {
119    fn drop(&mut self) {
120        self.compare_pending_exchange_abandoned();
121    }
122}
123
124/// Tracks whether a graph command has completed device execution.
125#[derive(Clone, Debug)]
126pub struct CommandExecution(Arc<AtomicCommandExecution>);
127
128impl CommandExecution {
129    /// Returns `true` when the tracked command has completed device execution.
130    ///
131    /// Returns [`CommandExecutionAbandoned`] if the graph command can no longer execute, such as
132    /// when the graph, submission, or queued work was dropped before successful completion.
133    pub fn has_executed(&self) -> Result<bool, CommandExecutionAbandoned> {
134        match self.0.load() {
135            AtomicCommandExecution::PENDING => Ok(false),
136            AtomicCommandExecution::EXECUTED => Ok(true),
137            _ => Err(CommandExecutionAbandoned),
138        }
139    }
140}
141
142/// Error returned when a tracked command execution can no longer complete.
143#[derive(Clone, Copy, Debug, Eq, PartialEq)]
144pub struct CommandExecutionAbandoned;
145
146impl From<CommandExecutionAbandoned> for crate::driver::DriverError {
147    fn from(_: CommandExecutionAbandoned) -> Self {
148        Self::InvalidData
149    }
150}
151
152#[derive(Debug, Default)]
153enum CommandExecutions {
154    #[default]
155    None,
156    One(Arc<AtomicCommandExecution>),
157    Many(Arc<[Arc<AtomicCommandExecution>]>),
158}
159
160impl Clone for CommandExecutions {
161    fn clone(&self) -> Self {
162        Self::None
163    }
164}
165
166impl CommandExecutions {
167    fn signal_abandoned(&self) {
168        self.for_each(AtomicCommandExecution::compare_pending_exchange_abandoned);
169    }
170
171    fn signal_executed(&self) {
172        self.for_each(AtomicCommandExecution::compare_pending_exchange_executed);
173    }
174
175    fn extend(&mut self, other: Self) {
176        match (mem::take(self), other) {
177            (Self::None, rhs) => *self = rhs,
178            (lhs, Self::None) => *self = lhs,
179            (Self::One(lhs), Self::One(rhs)) => *self = Self::Many(Arc::from([lhs, rhs])),
180            (Self::One(lhs), Self::Many(rhs)) => {
181                let mut states = Vec::with_capacity(rhs.len() + 1);
182                states.push(lhs);
183                states.extend(rhs.iter().cloned());
184                *self = Self::Many(Arc::from(states));
185            }
186            (Self::Many(lhs), Self::One(rhs)) => {
187                let mut states = Vec::with_capacity(lhs.len() + 1);
188                states.extend(lhs.iter().cloned());
189                states.push(rhs);
190                *self = Self::Many(Arc::from(states));
191            }
192            (Self::Many(lhs), Self::Many(rhs)) => {
193                let mut states = Vec::with_capacity(lhs.len() + rhs.len());
194                states.extend(lhs.iter().cloned());
195                states.extend(rhs.iter().cloned());
196                *self = Self::Many(Arc::from(states));
197            }
198        }
199    }
200
201    fn for_each(&self, mut f: impl FnMut(&AtomicCommandExecution)) {
202        match self {
203            Self::None => {}
204            Self::One(state) => f(state),
205            Self::Many(states) => {
206                for state in states.iter() {
207                    f(state);
208                }
209            }
210        }
211    }
212
213    fn track(&mut self) -> CommandExecution {
214        let tracker = AtomicCommandExecution::new_pending();
215        let cmd_exec = CommandExecution(tracker.clone());
216        self.extend(Self::One(tracker));
217
218        cmd_exec
219    }
220}
221
222#[cfg(feature = "checked")]
223#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
224pub(crate) struct GraphId(u64);
225
226#[cfg(feature = "checked")]
227impl GraphId {
228    fn next() -> Self {
229        static NEXT_ID: AtomicU64 = AtomicU64::new(1);
230
231        Self(NEXT_ID.fetch_add(1, Ordering::Relaxed))
232    }
233}
234
235#[derive(Debug)]
236enum AnyResource {
237    AccelerationStructure(Arc<AccelerationStructure>),
238    AccelerationStructureArg(AccelerationStructureInfo),
239    AccelerationStructureLease(Arc<Lease<AccelerationStructure>>),
240    Buffer(Arc<Buffer>),
241    BufferArg(BufferInfo),
242    BufferLease(Arc<Lease<Buffer>>),
243    Image(Arc<Image>),
244    ImageArg(ImageInfo),
245    ImageLease(Arc<Lease<Image>>),
246    SwapchainImage(Box<SwapchainImage>),
247}
248
249impl Clone for AnyResource {
250    fn clone(&self) -> Self {
251        match self {
252            Self::AccelerationStructure(resource) => {
253                Self::AccelerationStructure(Arc::clone(resource))
254            }
255            Self::AccelerationStructureArg(info) => Self::AccelerationStructureArg(*info),
256            Self::AccelerationStructureLease(resource) => {
257                Self::AccelerationStructureLease(Arc::clone(resource))
258            }
259            Self::Buffer(resource) => Self::Buffer(Arc::clone(resource)),
260            Self::BufferArg(info) => Self::BufferArg(*info),
261            Self::BufferLease(resource) => Self::BufferLease(Arc::clone(resource)),
262            Self::Image(resource) => Self::Image(Arc::clone(resource)),
263            Self::ImageArg(info) => Self::ImageArg(*info),
264            Self::ImageLease(resource) => Self::ImageLease(Arc::clone(resource)),
265            Self::SwapchainImage(resource) => {
266                Self::SwapchainImage(Box::new(unsafe { resource.to_detached() }))
267            }
268        }
269    }
270}
271
272macro_rules! any_resource_from_arc {
273    ($name:ident) => {
274        paste::paste! {
275            impl From<Arc<$name>> for AnyResource {
276                fn from(resource: Arc<$name>) -> Self {
277                    Self::$name(resource)
278                }
279            }
280
281            impl From<Arc<Lease<$name>>> for AnyResource {
282                fn from(resource: Arc<Lease<$name>>) -> Self {
283                    Self::[<$name Lease>](resource)
284                }
285            }
286        }
287    };
288}
289
290any_resource_from_arc!(AccelerationStructure);
291any_resource_from_arc!(Buffer);
292any_resource_from_arc!(Image);
293
294impl AnyResource {
295    fn as_accel_struct(&self) -> Option<&AccelerationStructure> {
296        Some(match self {
297            Self::AccelerationStructure(resource) => resource,
298            Self::AccelerationStructureLease(resource) => resource,
299            _ => return None,
300        })
301    }
302
303    fn as_buffer(&self) -> Option<&Buffer> {
304        Some(match self {
305            Self::Buffer(resource) => resource,
306            Self::BufferLease(resource) => resource,
307            _ => return None,
308        })
309    }
310
311    fn as_image(&self) -> Option<&Image> {
312        Some(match self {
313            Self::Image(resource) => resource,
314            Self::ImageLease(resource) => resource,
315            Self::SwapchainImage(resource) => resource,
316            _ => return None,
317        })
318    }
319
320    fn expect_accel_struct(&self) -> &AccelerationStructure {
321        self.as_accel_struct()
322            .expect("missing acceleration structure resource")
323    }
324
325    pub(crate) fn expect_accel_struct_info(
326        &self,
327    ) -> crate::driver::accel_struct::AccelerationStructureInfo {
328        match self {
329            Self::AccelerationStructure(resource) => resource.info,
330            Self::AccelerationStructureArg(info) => *info,
331            Self::AccelerationStructureLease(resource) => resource.info,
332            _ => panic!("missing acceleration structure resource"),
333        }
334    }
335
336    fn expect_buffer(&self) -> &Buffer {
337        self.as_buffer().expect("missing buffer resource")
338    }
339
340    pub(crate) fn expect_buffer_info(&self) -> crate::driver::buffer::BufferInfo {
341        match self {
342            Self::Buffer(resource) => resource.info,
343            Self::BufferArg(info) => *info,
344            Self::BufferLease(resource) => resource.info,
345            _ => panic!("missing buffer resource"),
346        }
347    }
348
349    fn expect_image(&self) -> &Image {
350        self.as_image().expect("missing image resource")
351    }
352
353    pub(crate) fn expect_image_info(&self) -> ImageInfo {
354        match self {
355            Self::Image(resource) => resource.info,
356            Self::ImageArg(info) => *info,
357            Self::ImageLease(resource) => resource.info,
358            Self::SwapchainImage(resource) => resource.info,
359            _ => panic!("missing image resource"),
360        }
361    }
362}
363
364#[derive(Clone, Copy, Debug)]
365struct Attachment {
366    array_layer_count: u32,
367    aspect_mask: vk::ImageAspectFlags,
368    base_array_layer: u32,
369    base_mip_level: u32,
370    format: vk::Format,
371    mip_level_count: u32,
372    sample_count: SampleCount,
373    target: NodeIndex,
374}
375
376impl Attachment {
377    fn new(image_view_info: ImageViewInfo, sample_count: SampleCount, target: NodeIndex) -> Self {
378        Self {
379            array_layer_count: image_view_info.array_layer_count,
380            aspect_mask: image_view_info.aspect_mask,
381            base_array_layer: image_view_info.base_array_layer,
382            base_mip_level: image_view_info.base_mip_level,
383            format: image_view_info.format,
384            mip_level_count: image_view_info.mip_level_count,
385            sample_count,
386            target,
387        }
388    }
389
390    fn are_compatible(lhs: Option<Self>, rhs: Option<Self>) -> bool {
391        // Two attachment references are compatible if they have matching format and sample
392        // count, or are both VK_ATTACHMENT_UNUSED or the pointer that would contain the
393        // reference is NULL
394        let (Some(lhs), Some(rhs)) = (lhs, rhs) else {
395            return true;
396        };
397
398        Self::are_identical(lhs, rhs)
399    }
400
401    fn are_identical(lhs: Self, rhs: Self) -> bool {
402        lhs.array_layer_count == rhs.array_layer_count
403            && lhs.base_array_layer == rhs.base_array_layer
404            && lhs.base_mip_level == rhs.base_mip_level
405            && lhs.format == rhs.format
406            && lhs.mip_level_count == rhs.mip_level_count
407            && lhs.sample_count == rhs.sample_count
408            && lhs.target == rhs.target
409    }
410
411    fn image_view_info(self, image_info: ImageInfo) -> ImageViewInfo {
412        image_info
413            .into_builder()
414            .array_layer_count(self.array_layer_count)
415            .mip_level_count(self.mip_level_count)
416            .format(self.format)
417            .into_image_view()
418            .aspect_mask(self.aspect_mask)
419            .base_array_layer(self.base_array_layer)
420            .base_mip_level(self.base_mip_level)
421            .build()
422    }
423
424    fn remap_nodes(&mut self, node_map: &[NodeIndex]) {
425        self.target = node_map[self.target];
426    }
427}
428
429#[derive(Clone, Copy, Debug)]
430struct ColorAttachment {
431    attachment: Attachment,
432    load: LoadOp<[f32; 4]>,
433    store: StoreOp,
434    resolve: Option<ColorResolve>,
435    is_input: bool,
436    is_attachment: bool,
437}
438
439#[derive(Clone, Debug, Default)]
440struct ExecutionAttachmentMap {
441    color: Vec<Option<ColorAttachment>>,
442    depth_stencil: Option<DepthStencilAttachment>,
443}
444
445impl ExecutionAttachmentMap {
446    fn color_attachment(&self, attachment_idx: AttachmentIndex) -> Option<&ColorAttachment> {
447        self.color
448            .get(attachment_idx as usize)
449            .and_then(|slot| slot.as_ref())
450    }
451
452    fn color_attachment_mut(
453        &mut self,
454        attachment_idx: AttachmentIndex,
455    ) -> Option<&mut ColorAttachment> {
456        self.color
457            .get_mut(attachment_idx as usize)
458            .and_then(|slot| slot.as_mut())
459    }
460
461    fn color_attachments(&self) -> impl Iterator<Item = (AttachmentIndex, &ColorAttachment)> + '_ {
462        self.color
463            .iter()
464            .enumerate()
465            .filter_map(|(attachment_idx, slot)| {
466                Some((attachment_idx as AttachmentIndex, slot.as_ref()?))
467            })
468    }
469
470    fn depth_stencil_attachment(&self) -> Option<&DepthStencilAttachment> {
471        self.depth_stencil.as_ref()
472    }
473
474    fn depth_stencil_attachment_mut(&mut self) -> Option<&mut DepthStencilAttachment> {
475        self.depth_stencil.as_mut()
476    }
477
478    fn set_color_attachment(
479        &mut self,
480        attachment_idx: AttachmentIndex,
481        attachment: ColorAttachment,
482    ) {
483        let attachment_idx = attachment_idx as usize;
484
485        if self.color.len() <= attachment_idx {
486            self.color.resize(attachment_idx + 1, None);
487        }
488
489        #[cfg(feature = "checked")]
490        {
491            let existing_attachment = self.color[attachment_idx]
492                .as_ref()
493                .map(|&color| color.attachment);
494
495            assert!(
496                Attachment::are_compatible(existing_attachment, Some(attachment.attachment)),
497                "incompatible with existing attachment"
498            );
499        }
500
501        self.color[attachment_idx] = Some(attachment);
502    }
503
504    fn set_depth_stencil_attachment(&mut self, attachment: DepthStencilAttachment) {
505        #[cfg(feature = "checked")]
506        {
507            let existing_attachment = self
508                .depth_stencil
509                .as_ref()
510                .map(|&depth_stencil| depth_stencil.attachment);
511
512            assert!(
513                Attachment::are_compatible(existing_attachment, Some(attachment.attachment)),
514                "incompatible with existing attachment"
515            );
516        }
517
518        self.depth_stencil = Some(attachment);
519    }
520
521    fn remap_nodes(&mut self, node_map: &[NodeIndex]) {
522        for attachment in self.color.iter_mut().flatten() {
523            attachment.attachment.remap_nodes(node_map);
524
525            if let Some(resolve) = &mut attachment.resolve {
526                resolve.attachment.remap_nodes(node_map);
527            }
528        }
529
530        if let Some(attachment) = &mut self.depth_stencil {
531            attachment.attachment.remap_nodes(node_map);
532
533            if let Some(resolve) = &mut attachment.resolve {
534                resolve.attachment.remap_nodes(node_map);
535            }
536        }
537    }
538}
539
540#[derive(Clone, Copy, Debug)]
541struct ColorResolve {
542    attachment: Attachment,
543    src_attachment_idx: AttachmentIndex,
544}
545
546#[derive(Clone, Debug)]
547struct CommandData {
548    execs: Vec<Execution>,
549
550    #[cfg(debug_assertions)]
551    name: Option<String>,
552
553    stream_scope_id: Option<u64>,
554    tracking: CommandExecutions,
555}
556
557impl CommandData {
558    fn descriptor_pools_sizes(
559        &self,
560    ) -> impl Iterator<Item = impl Iterator<Item = (&vk::DescriptorType, &u32)>> {
561        self.execs
562            .iter()
563            .flat_map(|exec| &exec.pipeline)
564            .map(|pipeline| {
565                pipeline
566                    .descriptor_info()
567                    .pool_sizes
568                    .values()
569                    .flat_map(HashMap::iter)
570            })
571    }
572
573    fn expect_first_exec(&self) -> &Execution {
574        self.execs.first().expect("missing command execution")
575    }
576
577    /// # Panics
578    ///
579    /// Panics if the execution list is empty (a command always has at least one execution).
580    fn expect_last_exec(&self) -> &Execution {
581        self.execs.last().expect("missing command execution")
582    }
583
584    /// # Panics
585    ///
586    /// Panics if the execution list is empty (a command always has at least one execution).
587    fn expect_last_exec_mut(&mut self) -> &mut Execution {
588        self.execs.last_mut().expect("missing command execution")
589    }
590
591    fn expect_last_pipeline(&self) -> &ExecutionPipeline {
592        self.expect_last_exec()
593            .pipeline
594            .as_ref()
595            .expect("missing command pipeline")
596    }
597
598    fn name(&self) -> &str {
599        const DEFAULT: &str = "command";
600
601        #[cfg(debug_assertions)]
602        {
603            self.name.as_deref().unwrap_or(DEFAULT)
604        }
605
606        #[cfg(not(debug_assertions))]
607        {
608            DEFAULT
609        }
610    }
611
612    fn remap_nodes(&mut self, node_map: &[NodeIndex]) {
613        for exec in &mut self.execs {
614            exec.remap_nodes(node_map);
615        }
616    }
617}
618
619impl Drop for CommandData {
620    fn drop(&mut self) {
621        self.tracking.signal_abandoned();
622    }
623}
624
625enum CommandFunction {
626    Once(CommandFnOnce),
627    Reusable(CommandFn),
628}
629
630impl CommandFunction {
631    fn is_reusable(&self) -> bool {
632        matches!(self, Self::Reusable(_))
633    }
634
635    fn record(self, cmd: CommandRef<'_>) -> Option<Self> {
636        match self {
637            Self::Once(func) => {
638                func(cmd);
639                None
640            }
641            Self::Reusable(func) => {
642                func(cmd);
643                Some(Self::Reusable(func))
644            }
645        }
646    }
647}
648
649impl Clone for CommandFunction {
650    fn clone(&self) -> Self {
651        match self {
652            Self::Once(_) => panic!("one-shot command callback cannot be cloned"),
653            Self::Reusable(func) => Self::Reusable(Arc::clone(func)),
654        }
655    }
656}
657
658#[derive(Clone, Copy, Debug)]
659struct DepthStencilAttachment {
660    attachment: Attachment,
661    load: LoadOp<vk::ClearDepthStencilValue>,
662    store: StoreOp,
663    resolve: Option<DepthStencilResolve>,
664    is_attachment: bool,
665}
666
667#[derive(Clone, Copy, Debug)]
668struct DepthStencilResolve {
669    attachment: Attachment,
670    dst_attachment_idx: AttachmentIndex,
671    depth_mode: Option<ResolveMode>,
672    stencil_mode: Option<ResolveMode>,
673}
674
675#[derive(Clone)]
676enum ExecutionAccess {
677    Building(ExecutionAccessBuilder),
678    Frozen(FrozenExecutionAccess),
679}
680
681impl ExecutionAccess {
682    fn contains(&self, node_idx: NodeIndex) -> bool {
683        match self {
684            Self::Building(builder) => builder.lookup.contains_key(&node_idx),
685            Self::Frozen(frozen) => frozen.lookup.contains_key(&node_idx),
686        }
687    }
688
689    fn freeze(&mut self) {
690        let Self::Building(builder) = mem::take(self) else {
691            return;
692        };
693
694        let ExecutionAccessBuilder { entries, lookup } = builder;
695        let entries = entries
696            .into_iter()
697            .map(|entry| NodeAccess {
698                node_idx: entry.node_idx,
699                accesses: entry.accesses.into_vec().into_boxed_slice(),
700            })
701            .collect();
702
703        *self = Self::Frozen(FrozenExecutionAccess { entries, lookup });
704    }
705
706    fn get_mut(&mut self, node_idx: &NodeIndex) -> Option<&mut [SubresourceAccess]> {
707        let Self::Building(builder) = self else {
708            panic!("execution accesses are frozen")
709        };
710
711        builder
712            .lookup
713            .get(node_idx)
714            .copied()
715            .map(|entry_idx| builder.entries[entry_idx].accesses.as_mut_slice())
716    }
717
718    fn iter(&self) -> ExecutionAccessIter<'_> {
719        match self {
720            Self::Building(builder) => ExecutionAccessIter::Building(builder.entries.iter()),
721            Self::Frozen(frozen) => ExecutionAccessIter::Frozen(frozen.entries.iter()),
722        }
723    }
724
725    fn push(&mut self, node_idx: NodeIndex, access: SubresourceAccess) {
726        let Self::Building(builder) = self else {
727            panic!("execution accesses are frozen")
728        };
729
730        let idx = *builder.lookup.entry(node_idx).or_insert_with(|| {
731            let idx = builder.entries.len();
732            builder.entries.push(NodeAccessBuilder {
733                node_idx,
734                accesses: Default::default(),
735            });
736
737            idx
738        });
739        builder.entries[idx].accesses.push(access);
740    }
741
742    fn remap_nodes(&mut self, node_map: &[NodeIndex]) {
743        match self {
744            Self::Building(builder) => {
745                for entry in &mut builder.entries {
746                    entry.node_idx = node_map[entry.node_idx];
747                }
748
749                builder.lookup = builder
750                    .entries
751                    .iter()
752                    .enumerate()
753                    .map(|(idx, entry)| (entry.node_idx, idx))
754                    .collect();
755            }
756            Self::Frozen(frozen) => {
757                for entry in frozen.entries.iter_mut() {
758                    entry.node_idx = node_map[entry.node_idx];
759                }
760
761                frozen.lookup = frozen
762                    .entries
763                    .iter()
764                    .enumerate()
765                    .map(|(idx, entry)| (entry.node_idx, idx))
766                    .collect();
767            }
768        }
769    }
770}
771
772impl Debug for ExecutionAccess {
773    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
774        match self {
775            Self::Building(builder) => builder.entries.fmt(f),
776            Self::Frozen(frozen) => frozen.entries.fmt(f),
777        }
778    }
779}
780
781impl Default for ExecutionAccess {
782    fn default() -> Self {
783        Self::Building(Default::default())
784    }
785}
786
787#[derive(Clone, Debug, Default)]
788struct ExecutionAccessBuilder {
789    entries: Vec<NodeAccessBuilder>,
790    lookup: HashMap<NodeIndex, usize>,
791}
792
793enum ExecutionAccessIter<'a> {
794    Building(Iter<'a, NodeAccessBuilder>),
795    Frozen(Iter<'a, NodeAccess>),
796}
797
798impl<'a> Iterator for ExecutionAccessIter<'a> {
799    type Item = (NodeIndex, &'a [SubresourceAccess]);
800
801    fn next(&mut self) -> Option<Self::Item> {
802        match self {
803            ExecutionAccessIter::Building(iter) => iter
804                .next()
805                .map(|entry| (entry.node_idx, entry.accesses.as_slice())),
806            ExecutionAccessIter::Frozen(iter) => iter
807                .next()
808                .map(|entry| (entry.node_idx, entry.accesses.as_ref())),
809        }
810    }
811
812    fn size_hint(&self) -> (usize, Option<usize>) {
813        let len = self.len();
814
815        (len, Some(len))
816    }
817}
818
819impl ExactSizeIterator for ExecutionAccessIter<'_> {
820    fn len(&self) -> usize {
821        match self {
822            ExecutionAccessIter::Building(iter) => iter.len(),
823            ExecutionAccessIter::Frozen(iter) => iter.len(),
824        }
825    }
826}
827
828#[derive(Clone, Default)]
829struct Execution {
830    accesses: ExecutionAccess,
831    attachments: ExecutionAttachmentMap,
832    bindings: BTreeMap<Binding, (NodeIndex, ViewInfo)>,
833
834    correlated_view_mask: u32,
835    depth_stencil: Option<DepthStencilInfo>,
836    render_area: Option<vk::Rect2D>,
837    view_mask: u32,
838
839    func: Option<CommandFunction>,
840    node_map: Option<Arc<[NodeIndex]>>,
841    pipeline: Option<ExecutionPipeline>,
842
843    #[cfg(feature = "checked")]
844    stream_graph_id: Option<GraphId>,
845}
846
847impl Execution {
848    fn remap_nodes(&mut self, node_map: &[NodeIndex]) {
849        let original_node_map = Arc::<[NodeIndex]>::from(node_map.to_vec());
850        self.accesses.remap_nodes(node_map);
851        self.attachments.remap_nodes(node_map);
852
853        self.bindings = mem::take(&mut self.bindings)
854            .into_iter()
855            .map(|(binding, (node_idx, view))| (binding, (node_map[node_idx], view)))
856            .collect();
857        self.node_map = Some(original_node_map);
858    }
859}
860
861impl Debug for Execution {
862    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
863        // The only field missing is func which cannot easily be implemented because it is a
864        // FnOnce
865        f.debug_struct("Execution")
866            .field("accesses", &self.accesses)
867            .field("attachments", &self.attachments)
868            .field("bindings", &self.bindings)
869            .field("correlated_view_mask", &self.correlated_view_mask)
870            .field("depth_stencil", &self.depth_stencil)
871            .field("render_area", &self.render_area)
872            .field("view_mask", &self.view_mask)
873            .field("pipeline", &self.pipeline)
874            .finish()
875    }
876}
877
878#[derive(Clone, Debug)]
879enum ExecutionPipeline {
880    Compute(ComputePipeline),
881    Graphics(GraphicsPipeline),
882    RayTracing(RayTracingPipeline),
883}
884
885impl ExecutionPipeline {
886    fn as_graphics(&self) -> Option<&GraphicsPipeline> {
887        if let Self::Graphics(pipeline) = self {
888            Some(pipeline)
889        } else {
890            None
891        }
892    }
893
894    fn bind_point(&self) -> vk::PipelineBindPoint {
895        match self {
896            ExecutionPipeline::Compute(_) => vk::PipelineBindPoint::COMPUTE,
897            ExecutionPipeline::Graphics(_) => vk::PipelineBindPoint::GRAPHICS,
898            ExecutionPipeline::RayTracing(_) => vk::PipelineBindPoint::RAY_TRACING_KHR,
899        }
900    }
901
902    fn descriptor_bindings(&self) -> &DescriptorBindingMap {
903        match self {
904            ExecutionPipeline::Compute(pipeline) => &pipeline.inner.descriptor_bindings,
905            ExecutionPipeline::Graphics(pipeline) => &pipeline.inner.descriptor_bindings,
906            ExecutionPipeline::RayTracing(pipeline) => &pipeline.inner.descriptor_bindings,
907        }
908    }
909
910    fn descriptor_info(&self) -> &PipelineDescriptorInfo {
911        match self {
912            ExecutionPipeline::Compute(pipeline) => &pipeline.inner.descriptor_info,
913            ExecutionPipeline::Graphics(pipeline) => &pipeline.inner.descriptor_info,
914            ExecutionPipeline::RayTracing(pipeline) => &pipeline.inner.descriptor_info,
915        }
916    }
917
918    fn expect_compute(&self) -> &ComputePipeline {
919        if let Self::Compute(pipeline) = self {
920            pipeline
921        } else {
922            panic!("missing compute pipeline")
923        }
924    }
925
926    fn expect_graphics(&self) -> &GraphicsPipeline {
927        self.as_graphics().expect("missing graphics pipeline")
928    }
929
930    fn expect_ray_tracing(&self) -> &RayTracingPipeline {
931        if let Self::RayTracing(pipeline) = self {
932            pipeline
933        } else {
934            panic!("missing ray tracing pipeline")
935        }
936    }
937
938    fn layout(&self) -> vk::PipelineLayout {
939        match self {
940            ExecutionPipeline::Compute(pipeline) => pipeline.inner.layout,
941            ExecutionPipeline::Graphics(pipeline) => pipeline.inner.layout,
942            ExecutionPipeline::RayTracing(pipeline) => pipeline.inner.layout,
943        }
944    }
945}
946
947#[derive(Clone, Debug)]
948struct FrozenExecutionAccess {
949    entries: Box<[NodeAccess]>,
950    lookup: HashMap<NodeIndex, usize>,
951}
952
953/// A composable graph of Vulkan command buffer operations.
954///
955/// `Graph` instances are intended for one-time use.
956///
957/// The design of this code originated with a combination of
958/// [`PassBuilder`](https://github.com/EmbarkStudios/kajiya/blob/main/crates/lib/kajiya-rg/src/pass_builder.rs)
959/// and
960/// [`graph.cpp`](https://github.com/Themaister/Granite/blob/master/renderer/graph.cpp).
961#[derive(Debug)]
962pub struct Graph {
963    cmds: Vec<CommandData>,
964    resources: ResourceMap,
965
966    #[cfg(feature = "checked")]
967    graph_id: GraphId,
968}
969
970/// Builder for incrementally constructing a [`Graph`].
971pub struct GraphBuilder {
972    graph: Graph,
973}
974
975impl GraphBuilder {
976    /// Creates an empty graph builder.
977    pub fn new() -> Self {
978        Self {
979            graph: Graph::new(),
980        }
981    }
982
983    /// Builds the graph.
984    pub fn build(self) -> Graph {
985        self.graph
986    }
987
988    /// Binds a Vulkan buffer, image, or acceleration structure resource to this graph.
989    pub fn bind_resource<R>(&mut self, resource: R) -> R::Node
990    where
991        R: Resource,
992    {
993        self.graph.bind_resource(resource)
994    }
995
996    /// Copies an image, potentially performing format conversion.
997    pub fn blit_image(
998        mut self,
999        src: impl Into<AnyImageNode>,
1000        dst: impl Into<AnyImageNode>,
1001        filter: vk::Filter,
1002    ) -> Self {
1003        self.graph.blit_image(src, dst, filter);
1004        self
1005    }
1006
1007    /// Clears a color image.
1008    pub fn clear_color_image(
1009        mut self,
1010        image: impl Into<AnyImageNode>,
1011        color: impl Into<ClearColorValue>,
1012    ) -> Self {
1013        self.graph.clear_color_image(image, color);
1014        self
1015    }
1016
1017    /// Clears a depth/stencil image.
1018    pub fn clear_depth_stencil_image(
1019        mut self,
1020        image: impl Into<AnyImageNode>,
1021        depth: f32,
1022        stencil: u32,
1023    ) -> Self {
1024        self.graph.clear_depth_stencil_image(image, depth, stencil);
1025        self
1026    }
1027
1028    /// Copies data between buffers.
1029    pub fn copy_buffer(
1030        mut self,
1031        src: impl Into<AnyBufferNode>,
1032        dst: impl Into<AnyBufferNode>,
1033    ) -> Self {
1034        self.graph.copy_buffer(src, dst);
1035        self
1036    }
1037
1038    /// Copies data from a buffer into an image.
1039    pub fn copy_buffer_to_image(
1040        mut self,
1041        src: impl Into<AnyBufferNode>,
1042        dst: impl Into<AnyImageNode>,
1043    ) -> Self {
1044        self.graph.copy_buffer_to_image(src, dst);
1045        self
1046    }
1047
1048    /// Copies all layers of a source image to a destination image.
1049    pub fn copy_image(
1050        mut self,
1051        src: impl Into<AnyImageNode>,
1052        dst: impl Into<AnyImageNode>,
1053    ) -> Self {
1054        self.graph.copy_image(src, dst);
1055        self
1056    }
1057
1058    /// Copies image data into a buffer.
1059    pub fn copy_image_to_buffer(
1060        mut self,
1061        src: impl Into<AnyImageNode>,
1062        dst: impl Into<AnyBufferNode>,
1063    ) -> Self {
1064        self.graph.copy_image_to_buffer(src, dst);
1065        self
1066    }
1067
1068    /// Fills a region of a buffer with a fixed value.
1069    pub fn fill_buffer(
1070        mut self,
1071        buffer: impl Into<AnyBufferNode>,
1072        region: Range<vk::DeviceSize>,
1073        data: u32,
1074    ) -> Self {
1075        self.graph.fill_buffer(buffer, region, data);
1076        self
1077    }
1078
1079    /// Records a [`vkCmdUpdateBuffer`](https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdUpdateBuffer.html) command.
1080    pub fn update_buffer(
1081        mut self,
1082        buffer: impl Into<AnyBufferNode>,
1083        offset: vk::DeviceSize,
1084        data: impl AsRef<[u8]> + 'static + Send,
1085    ) -> Self {
1086        self.graph.update_buffer(buffer, offset, data);
1087        self
1088    }
1089}
1090
1091impl Default for GraphBuilder {
1092    fn default() -> Self {
1093        Self::new()
1094    }
1095}
1096
1097impl Default for Graph {
1098    fn default() -> Self {
1099        Self {
1100            cmds: Default::default(),
1101            resources: Default::default(),
1102
1103            #[cfg(feature = "checked")]
1104            graph_id: GraphId::next(),
1105        }
1106    }
1107}
1108
1109impl Graph {
1110    /// Constructs a default `Graph`.
1111    pub fn new() -> Self {
1112        Self::default()
1113    }
1114
1115    /// Creates an empty graph builder.
1116    pub fn builder() -> GraphBuilder {
1117        GraphBuilder::new()
1118    }
1119
1120    /// Converts this graph into a builder.
1121    pub fn into_builder(self) -> GraphBuilder {
1122        GraphBuilder { graph: self }
1123    }
1124
1125    pub(crate) fn assert_node_owner<N>(&self, _resource_node: &N)
1126    where
1127        N: Node,
1128    {
1129        #[cfg(feature = "checked")]
1130        _resource_node.assert_owner(self.graph_id);
1131    }
1132
1133    #[cfg(feature = "checked")]
1134    pub(crate) fn graph_id(&self) -> GraphId {
1135        self.graph_id
1136    }
1137
1138    /// Allocates and begins writing a new command.
1139    pub fn begin_cmd(&mut self) -> Command<'_> {
1140        Command::new(self)
1141    }
1142
1143    /// Binds a Vulkan buffer, image, or acceleration structure resource to this graph.
1144    ///
1145    /// Bound resource nodes may be used in commands for shader pipeline operations and other
1146    /// general functions.
1147    pub fn bind_resource<R>(&mut self, resource: R) -> R::Node
1148    where
1149        R: Resource,
1150    {
1151        resource.bind_graph(self)
1152    }
1153
1154    pub(crate) fn bind_stream_arg_resource(&mut self, resource: AnyResource) -> NodeIndex {
1155        self.resources.bind(resource)
1156    }
1157
1158    /// Copies an image, potentially performing format conversion.
1159    ///
1160    /// Records a [`vkCmdBlitImage`] operation covering the full extent of the source and
1161    /// destination images.
1162    ///
1163    /// [`vkCmdBlitImage`]: https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdBlitImage.html
1164    pub fn blit_image(
1165        &mut self,
1166        src: impl Into<AnyImageNode>,
1167        dst: impl Into<AnyImageNode>,
1168        filter: vk::Filter,
1169    ) -> &mut Self {
1170        let src = src.into();
1171        let src_info = self.resources[src.index()].expect_image_info();
1172
1173        let dst = dst.into();
1174        let dst_info = self.resources[dst.index()].expect_image_info();
1175
1176        self.begin_cmd()
1177            .debug_name("blit image")
1178            .blit_image(
1179                src,
1180                dst,
1181                filter,
1182                [vk::ImageBlit {
1183                    src_subresource: vk::ImageSubresourceLayers {
1184                        aspect_mask: format_aspect_mask(src_info.format),
1185                        mip_level: 0,
1186                        base_array_layer: 0,
1187                        layer_count: 1,
1188                    },
1189                    src_offsets: [
1190                        vk::Offset3D { x: 0, y: 0, z: 0 },
1191                        vk::Offset3D {
1192                            x: src_info.width as _,
1193                            y: src_info.height as _,
1194                            z: src_info.depth as _,
1195                        },
1196                    ],
1197                    dst_subresource: vk::ImageSubresourceLayers {
1198                        aspect_mask: format_aspect_mask(dst_info.format),
1199                        mip_level: 0,
1200                        base_array_layer: 0,
1201                        layer_count: 1,
1202                    },
1203                    dst_offsets: [
1204                        vk::Offset3D { x: 0, y: 0, z: 0 },
1205                        vk::Offset3D {
1206                            x: dst_info.width as _,
1207                            y: dst_info.height as _,
1208                            z: dst_info.depth as _,
1209                        },
1210                    ],
1211                }],
1212            )
1213            .end_cmd()
1214    }
1215
1216    /// Copies regions of an image, potentially performing format conversion.
1217    ///
1218    /// Records a [`vkCmdBlitImage`] operation. The caller supplies the Vulkan blit regions and
1219    /// filter exactly as they will be passed to Vulkan.
1220    ///
1221    /// [`vkCmdBlitImage`]: https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdBlitImage.html
1222    #[profiling::function]
1223    #[doc(hidden)]
1224    #[deprecated(note = "use Graph::begin_cmd().blit_image(...).end_cmd() for explicit regions")]
1225    pub fn blit_image_region(
1226        &mut self,
1227        src: impl Into<AnyImageNode>,
1228        dst: impl Into<AnyImageNode>,
1229        filter: vk::Filter,
1230        regions: impl AsRef<[vk::ImageBlit]> + 'static + Send,
1231    ) -> &mut Self {
1232        self.begin_cmd()
1233            .debug_name("blit image")
1234            .blit_image(src, dst, filter, regions)
1235            .end_cmd()
1236    }
1237
1238    /// Clears a color image.
1239    ///
1240    /// Records a [`vkCmdClearColorImage`] operation for the full image subresource range described
1241    /// by the image's [`ImageInfo`].
1242    ///
1243    /// [`vkCmdClearColorImage`]: https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdClearColorImage.html
1244    #[profiling::function]
1245    pub fn clear_color_image(
1246        &mut self,
1247        image: impl Into<AnyImageNode>,
1248        color: impl Into<ClearColorValue>,
1249    ) -> &mut Self {
1250        self.begin_cmd()
1251            .debug_name("clear color")
1252            .clear_color_image(image, color)
1253            .end_cmd()
1254    }
1255
1256    /// Clears a depth/stencil image.
1257    ///
1258    /// Records a [`vkCmdClearDepthStencilImage`] operation for the full image subresource range
1259    /// described by the image's [`ImageInfo`].
1260    ///
1261    /// [`vkCmdClearDepthStencilImage`]: https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdClearDepthStencilImage.html
1262    #[profiling::function]
1263    pub fn clear_depth_stencil_image(
1264        &mut self,
1265        image: impl Into<AnyImageNode>,
1266        depth: f32,
1267        stencil: u32,
1268    ) -> &mut Self {
1269        self.begin_cmd()
1270            .debug_name("clear depth/stencil")
1271            .clear_depth_stencil_image(image, depth, stencil)
1272            .end_cmd()
1273    }
1274
1275    /// Copies data between buffers.
1276    ///
1277    /// Records a [`vkCmdCopyBuffer`] operation covering the common size of the source and
1278    /// destination buffers.
1279    ///
1280    /// [`vkCmdCopyBuffer`]: https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdCopyBuffer.html
1281    pub fn copy_buffer(
1282        &mut self,
1283        src: impl Into<AnyBufferNode>,
1284        dst: impl Into<AnyBufferNode>,
1285    ) -> &mut Self {
1286        let src = src.into();
1287        let dst = dst.into();
1288        let src_info = self.resources[src.index()].expect_buffer_info();
1289        let dst_info = self.resources[dst.index()].expect_buffer_info();
1290
1291        self.begin_cmd()
1292            .debug_name("copy buffer")
1293            .copy_buffer(
1294                src,
1295                dst,
1296                [vk::BufferCopy {
1297                    src_offset: 0,
1298                    dst_offset: 0,
1299                    size: src_info.size.min(dst_info.size),
1300                }],
1301            )
1302            .end_cmd()
1303    }
1304
1305    /// Copies data between buffer regions.
1306    ///
1307    /// Records a [`vkCmdCopyBuffer`] operation. The caller supplies the Vulkan copy regions exactly
1308    /// as they will be passed to Vulkan.
1309    ///
1310    /// [`vkCmdCopyBuffer`]: https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdCopyBuffer.html
1311    #[profiling::function]
1312    #[doc(hidden)]
1313    #[deprecated(note = "use Graph::begin_cmd().copy_buffer(...).end_cmd() for explicit regions")]
1314    pub fn copy_buffer_region(
1315        &mut self,
1316        src: impl Into<AnyBufferNode>,
1317        dst: impl Into<AnyBufferNode>,
1318        regions: impl AsRef<[vk::BufferCopy]> + 'static + Send,
1319    ) -> &mut Self {
1320        self.begin_cmd()
1321            .debug_name("copy buffer")
1322            .copy_buffer(src, dst, regions)
1323            .end_cmd()
1324    }
1325
1326    /// Copies data from a buffer into an image.
1327    ///
1328    /// Records a [`vkCmdCopyBufferToImage`] operation covering the full destination image.
1329    ///
1330    /// [`vkCmdCopyBufferToImage`]: https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdCopyBufferToImage.html
1331    pub fn copy_buffer_to_image(
1332        &mut self,
1333        src: impl Into<AnyBufferNode>,
1334        dst: impl Into<AnyImageNode>,
1335    ) -> &mut Self {
1336        let dst = dst.into();
1337        let dst_info = self.resources[dst.index()].expect_image_info();
1338
1339        self.begin_cmd()
1340            .debug_name("copy buffer to image")
1341            .copy_buffer_to_image(
1342                src,
1343                dst,
1344                [vk::BufferImageCopy {
1345                    buffer_offset: 0,
1346                    buffer_row_length: dst_info.width,
1347                    buffer_image_height: dst_info.height,
1348                    image_subresource: vk::ImageSubresourceLayers {
1349                        aspect_mask: format_aspect_mask(dst_info.format),
1350                        mip_level: 0,
1351                        base_array_layer: 0,
1352                        layer_count: 1,
1353                    },
1354                    image_offset: Default::default(),
1355                    image_extent: vk::Extent3D {
1356                        depth: dst_info.depth,
1357                        height: dst_info.height,
1358                        width: dst_info.width,
1359                    },
1360                }],
1361            )
1362            .end_cmd()
1363    }
1364
1365    /// Copies data from a buffer into image regions.
1366    ///
1367    /// Records a [`vkCmdCopyBufferToImage`] operation. The caller supplies the Vulkan copy regions
1368    /// exactly as they will be passed to Vulkan.
1369    ///
1370    /// [`vkCmdCopyBufferToImage`]: https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdCopyBufferToImage.html
1371    #[profiling::function]
1372    #[doc(hidden)]
1373    #[deprecated(
1374        note = "use Graph::begin_cmd().copy_buffer_to_image(...).end_cmd() for explicit regions"
1375    )]
1376    pub fn copy_buffer_to_image_region(
1377        &mut self,
1378        src: impl Into<AnyBufferNode>,
1379        dst: impl Into<AnyImageNode>,
1380        regions: impl AsRef<[vk::BufferImageCopy]> + 'static + Send,
1381    ) -> &mut Self {
1382        self.begin_cmd()
1383            .debug_name("copy buffer to image")
1384            .copy_buffer_to_image(src, dst, regions)
1385            .end_cmd()
1386    }
1387
1388    /// Copies all layers of a source image to a destination image.
1389    ///
1390    /// Records a [`vkCmdCopyImage`] operation covering the common extent of the source and
1391    /// destination images.
1392    ///
1393    /// [`vkCmdCopyImage`]: https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdCopyImage.html
1394    pub fn copy_image(
1395        &mut self,
1396        src: impl Into<AnyImageNode>,
1397        dst: impl Into<AnyImageNode>,
1398    ) -> &mut Self {
1399        let src = src.into();
1400        let src_info = self.resources[src.index()].expect_image_info();
1401
1402        let dst = dst.into();
1403        let dst_info = self.resources[dst.index()].expect_image_info();
1404
1405        self.begin_cmd()
1406            .debug_name("copy image")
1407            .copy_image(
1408                src,
1409                dst,
1410                [vk::ImageCopy {
1411                    src_subresource: vk::ImageSubresourceLayers {
1412                        aspect_mask: format_aspect_mask(src_info.format),
1413                        mip_level: 0,
1414                        base_array_layer: 0,
1415                        layer_count: src_info.array_layer_count,
1416                    },
1417                    src_offset: vk::Offset3D { x: 0, y: 0, z: 0 },
1418                    dst_subresource: vk::ImageSubresourceLayers {
1419                        aspect_mask: format_aspect_mask(dst_info.format),
1420                        mip_level: 0,
1421                        base_array_layer: 0,
1422                        layer_count: src_info.array_layer_count,
1423                    },
1424                    dst_offset: vk::Offset3D { x: 0, y: 0, z: 0 },
1425                    extent: vk::Extent3D {
1426                        depth: src_info.depth.clamp(1, dst_info.depth),
1427                        height: src_info.height.clamp(1, dst_info.height),
1428                        width: src_info.width.min(dst_info.width),
1429                    },
1430                }],
1431            )
1432            .end_cmd()
1433    }
1434
1435    /// Copies data between image regions.
1436    ///
1437    /// Records a [`vkCmdCopyImage`] operation. The caller supplies the Vulkan copy regions exactly
1438    /// as they will be passed to Vulkan.
1439    ///
1440    /// [`vkCmdCopyImage`]: https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdCopyImage.html
1441    #[profiling::function]
1442    #[doc(hidden)]
1443    #[deprecated(note = "use Graph::begin_cmd().copy_image(...).end_cmd() for explicit regions")]
1444    pub fn copy_image_region(
1445        &mut self,
1446        src: impl Into<AnyImageNode>,
1447        dst: impl Into<AnyImageNode>,
1448        regions: impl AsRef<[vk::ImageCopy]> + 'static + Send,
1449    ) -> &mut Self {
1450        self.begin_cmd()
1451            .debug_name("copy image")
1452            .copy_image(src, dst, regions)
1453            .end_cmd()
1454    }
1455
1456    /// Copies image data into a buffer.
1457    ///
1458    /// Records a [`vkCmdCopyImageToBuffer`] operation covering the full source image.
1459    ///
1460    /// [`vkCmdCopyImageToBuffer`]: https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdCopyImageToBuffer.html
1461    pub fn copy_image_to_buffer(
1462        &mut self,
1463        src: impl Into<AnyImageNode>,
1464        dst: impl Into<AnyBufferNode>,
1465    ) -> &mut Self {
1466        let src = src.into();
1467        let dst = dst.into();
1468
1469        let src_info = self.resources[src.index()].expect_image_info();
1470
1471        self.begin_cmd()
1472            .debug_name("copy image to buffer")
1473            .copy_image_to_buffer(
1474                src,
1475                dst,
1476                [vk::BufferImageCopy {
1477                    buffer_offset: 0,
1478                    buffer_row_length: src_info.width,
1479                    buffer_image_height: src_info.height,
1480                    image_subresource: vk::ImageSubresourceLayers {
1481                        aspect_mask: format_aspect_mask(src_info.format),
1482                        mip_level: 0,
1483                        base_array_layer: 0,
1484                        layer_count: 1,
1485                    },
1486                    image_offset: Default::default(),
1487                    image_extent: vk::Extent3D {
1488                        depth: src_info.depth,
1489                        height: src_info.height,
1490                        width: src_info.width,
1491                    },
1492                }],
1493            )
1494            .end_cmd()
1495    }
1496
1497    /// Copies image region data into a buffer.
1498    ///
1499    /// Records a [`vkCmdCopyImageToBuffer`] operation. The caller supplies the Vulkan copy regions
1500    /// exactly as they will be passed to Vulkan.
1501    ///
1502    /// [`vkCmdCopyImageToBuffer`]: https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdCopyImageToBuffer.html
1503    #[profiling::function]
1504    #[doc(hidden)]
1505    #[deprecated(
1506        note = "use Graph::begin_cmd().copy_image_to_buffer(...).end_cmd() for explicit regions"
1507    )]
1508    pub fn copy_image_to_buffer_region(
1509        &mut self,
1510        src: impl Into<AnyImageNode>,
1511        dst: impl Into<AnyBufferNode>,
1512        regions: impl AsRef<[vk::BufferImageCopy]> + 'static + Send,
1513    ) -> &mut Self {
1514        self.begin_cmd()
1515            .debug_name("copy image to buffer")
1516            .copy_image_to_buffer(src, dst, regions)
1517            .end_cmd()
1518    }
1519
1520    /// Fills a region of a buffer with a fixed value.
1521    ///
1522    /// Records a [`vkCmdFillBuffer`] operation.
1523    ///
1524    /// [`vkCmdFillBuffer`]: https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdFillBuffer.html
1525    pub fn fill_buffer(
1526        &mut self,
1527        buffer: impl Into<AnyBufferNode>,
1528        region: Range<vk::DeviceSize>,
1529        data: u32,
1530    ) -> &mut Self {
1531        self.begin_cmd()
1532            .debug_name("fill buffer")
1533            .fill_buffer(buffer, region, data)
1534            .end_cmd()
1535    }
1536
1537    /// Returns the index of the first command which accesses a given node.
1538    #[profiling::function]
1539    fn first_node_access_pass_index(&self, resource_node: impl Node) -> Option<usize> {
1540        self.assert_node_owner(&resource_node);
1541
1542        let node_idx = resource_node.index();
1543
1544        for (pass_idx, pass) in self.cmds.iter().enumerate() {
1545            for exec in pass.execs.iter() {
1546                if exec.accesses.contains(node_idx) {
1547                    return Some(pass_idx);
1548                }
1549            }
1550        }
1551
1552        None
1553    }
1554
1555    /// Finalizes the graph and provides an object with functions for submitting the resulting
1556    /// commands.
1557    #[profiling::function]
1558    pub fn finalize(mut self) -> Submission {
1559        // The final execution of each command has no function.
1560        self.cmds.retain_mut(|cmd| {
1561            debug_assert!(cmd.expect_last_exec().func.is_none());
1562
1563            cmd.execs.pop();
1564
1565            for exec in &mut cmd.execs {
1566                exec.accesses.freeze();
1567            }
1568
1569            !cmd.execs.is_empty()
1570        });
1571
1572        Submission::new(self)
1573    }
1574
1575    /// Returns a borrow of the Vulkan resource represented by `resource_node`.
1576    ///
1577    /// The exact return type depends on the node type:
1578    ///
1579    /// - Concrete nodes such as [`BufferNode`] and [`ImageNode`] return the exact stored handle
1580    ///   type, such as
1581    ///   `&Arc<Buffer>` or `&Arc<Image>`.
1582    /// - Erased nodes such as [`AnyBufferNode`] and [`AnyImageNode`] return a borrow of the
1583    ///   underlying resource,
1584    ///   such as `&Buffer` or `&Image`.
1585    ///
1586    /// This distinction lets erased node enums unify owned, leased, and swapchain-backed resources
1587    /// behind a single resource view.
1588    ///
1589    /// Node ownership is validated here when the `checked` feature is enabled. With `checked`
1590    /// disabled, callers must ensure `resource_node` came from this graph.
1591    pub fn resource<N>(&self, resource_node: N) -> &N::Resource
1592    where
1593        N: Node,
1594    {
1595        self.assert_node_owner(&resource_node);
1596        resource_node.borrow(&self.resources)
1597    }
1598
1599    /// Records a [`vkCmdUpdateBuffer`](https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdUpdateBuffer.html)
1600    /// command.
1601    ///
1602    /// Vulkan requires `data` to be at most `65536` bytes.
1603    ///
1604    /// These constraints are validated by the Vulkan Validation Layer (VVL) when it is active.
1605    /// When the `checked` feature is enabled, `vk-graph` also validates the data size and bounds
1606    /// before recording the command.
1607    #[profiling::function]
1608    pub fn update_buffer(
1609        &mut self,
1610        buffer: impl Into<AnyBufferNode>,
1611        offset: vk::DeviceSize,
1612        data: impl AsRef<[u8]> + 'static + Send,
1613    ) -> &mut Self {
1614        debug_assert!(data.as_ref().len() <= 64 * 1024);
1615
1616        let buffer = buffer.into();
1617        let data_end = offset + data.as_ref().len() as vk::DeviceSize;
1618
1619        #[cfg(feature = "checked")]
1620        {
1621            assert!(
1622                data.as_ref().len() <= 64 * 1024,
1623                "data length ({}) exceeds vkCmdUpdateBuffer limit (65536)",
1624                data.as_ref().len()
1625            );
1626
1627            let buffer_info = self.resources[buffer.index()].expect_buffer_info();
1628
1629            assert!(
1630                data_end <= buffer_info.size,
1631                "data range end ({data_end}) exceeds buffer size ({})",
1632                buffer_info.size
1633            );
1634        }
1635
1636        let data = Arc::<[u8]>::from(data.as_ref());
1637
1638        self.begin_cmd()
1639            .debug_name("update buffer")
1640            .subresource_access(buffer, offset..data_end, AccessType::TransferWrite)
1641            .record_stream(move |cmd| {
1642                let buffer = cmd.resource(buffer);
1643
1644                unsafe {
1645                    cmd.device
1646                        .cmd_update_buffer(cmd.handle, buffer.handle, offset, &data);
1647                }
1648            })
1649            .end_cmd()
1650    }
1651}
1652
1653/// Specifies the state of a color or combined depth and stencil attachment image during graphics
1654/// render pass framebuffer load operations.
1655///
1656/// Use this to specify the desired contents of any image before use in a pipeline command buffer.
1657#[derive(Clone, Copy, Debug)]
1658pub enum LoadOp<T> {
1659    /// Clears the attachment.
1660    ///
1661    /// `T` will be [ClearColorValue] for color images or [vk::ClearDepthStencilValue] for
1662    /// combined depth and stencil images.
1663    Clear(T),
1664
1665    /// The attachment will become undefined and reads will produce garbage data.
1666    DontCare,
1667
1668    /// The attachment will be preserved in memory.
1669    Load,
1670}
1671
1672/// A Vulkan resource which has been bound to a [`Graph`].
1673///
1674/// See [`Graph::bind_resource`].
1675///
1676/// This trait is sealed and cannot be implemented outside of `vk-graph`.
1677#[allow(private_bounds)]
1678pub trait Node: private::NodeSealed {
1679    /// The Vulkan buffer, image, or acceleration structure type.
1680    type Resource;
1681
1682    /// Synchronization state snapshot returned for this node's resource type.
1683    type SyncInfo;
1684
1685    #[doc(hidden)]
1686    fn index(&self) -> usize;
1687}
1688
1689#[derive(Clone, Debug)]
1690struct NodeAccess {
1691    node_idx: NodeIndex,
1692    accesses: Box<[SubresourceAccess]>,
1693}
1694
1695#[derive(Clone, Debug)]
1696struct NodeAccessBuilder {
1697    node_idx: NodeIndex,
1698    accesses: SmallVec<[SubresourceAccess; 2]>,
1699}
1700
1701mod private {
1702    use super::{AnyResource, Node};
1703
1704    #[cfg(feature = "checked")]
1705    use super::GraphId;
1706
1707    /// Prevents external implementations of [`Node`] and provides crate-private node internals.
1708    pub(crate) trait NodeSealed: Sized {
1709        fn borrow(self, resources: &[AnyResource]) -> &<Self as Node>::Resource
1710        where
1711            Self: Node;
1712
1713        fn borrow_at(self, resources: &[AnyResource], index: usize) -> &<Self as Node>::Resource
1714        where
1715            Self: Node,
1716        {
1717            debug_assert_eq!(self.index(), index);
1718            self.borrow(resources)
1719        }
1720
1721        #[cfg(feature = "checked")]
1722        fn assert_owner(&self, _graph_id: GraphId) {}
1723    }
1724
1725    /// Prevents external implementations of [`Resource`](super::Resource).
1726    pub(crate) trait ResourceSealed {}
1727}
1728
1729/// A Vulkan resource which may be bound to a [`Graph`].
1730///
1731/// See [`Graph::bind_resource`] and
1732/// [`Command::bind_resource`](crate::cmd::Command::bind_resource).
1733///
1734/// This trait is sealed and cannot be implemented outside of `vk-graph`.
1735#[allow(private_bounds)]
1736pub trait Resource: private::ResourceSealed {
1737    /// The resource handle type.
1738    type Node;
1739
1740    #[doc(hidden)]
1741    fn bind_graph(self, _: &mut Graph) -> Self::Node;
1742}
1743
1744impl private::ResourceSealed for SwapchainImage {}
1745
1746impl Resource for SwapchainImage {
1747    type Node = SwapchainImageNode;
1748
1749    fn bind_graph(self, graph: &mut Graph) -> Self::Node {
1750        let node = Self::Node::new(
1751            graph.resources.len(),
1752            #[cfg(feature = "checked")]
1753            graph.graph_id,
1754        );
1755
1756        //trace!("Node {}: {:?}", res.idx, &self);
1757
1758        let resource = AnyResource::SwapchainImage(Box::new(self));
1759        graph.resources.bind(resource);
1760
1761        node
1762    }
1763}
1764
1765macro_rules! resource {
1766    ($name:ident) => {
1767        paste::paste! {
1768            impl private::ResourceSealed for $name {}
1769            impl private::ResourceSealed for Arc<$name> {}
1770            impl<'a> private::ResourceSealed for &'a Arc<$name> {}
1771            impl private::ResourceSealed for Lease<$name> {}
1772            impl private::ResourceSealed for Arc<Lease<$name>> {}
1773            impl<'a> private::ResourceSealed for &'a Arc<Lease<$name>> {}
1774
1775            impl Resource for $name {
1776                type Node = [<$name Node>];
1777
1778                #[profiling::function]
1779                fn bind_graph(self, graph: &mut Graph) -> Self::Node {
1780                    // Bind a new owned resource, such as Image or Buffer.
1781
1782                    // We will return a new node
1783                    Arc::new(self).bind_graph(graph)
1784                }
1785            }
1786
1787            impl Resource for Arc<$name> {
1788                type Node = [<$name Node>];
1789
1790                #[profiling::function]
1791                fn bind_graph(self, graph: &mut Graph) -> Self::Node {
1792                    // Bind an existing shared resource, such as Arc<Image> or Arc<Buffer>.
1793
1794                    // We will return an existing node, if possible
1795                    Self::Node::new(
1796                        graph.resources.bind_shared(self),
1797                        #[cfg(feature = "checked")]
1798                        graph.graph_id,
1799                    )
1800                }
1801            }
1802
1803            impl<'a> Resource for &'a Arc<$name> {
1804                type Node = [<$name Node>];
1805
1806                fn bind_graph(self, graph: &mut Graph) -> Self::Node {
1807                    // Bind a borrowed shared resource, such as &Arc<Image> or &Arc<Buffer>.
1808
1809                    Arc::clone(self).bind_graph(graph)
1810                }
1811            }
1812
1813            impl Resource for Lease<$name> {
1814                type Node = [<$name LeaseNode>];
1815
1816                #[profiling::function]
1817                fn bind_graph(self, graph: &mut Graph) -> Self::Node {
1818                    // Bind a new pooled resource, such as Lease<Image> or Lease<Buffer>.
1819
1820                    // We will return a new node
1821                    Arc::new(self).bind_graph(graph)
1822                }
1823            }
1824
1825            impl Resource  for Arc<Lease<$name>> {
1826                type Node = [<$name LeaseNode>];
1827
1828                #[profiling::function]
1829                fn bind_graph(self, graph: &mut Graph) -> Self::Node {
1830                    // Bind an existing shared pooled resource, such as Arc<Lease<Image>> or
1831                    // Arc<Lease<Buffer>>.
1832
1833                    // We will return an existing node, if possible
1834                    Self::Node::new(
1835                        graph.resources.bind_shared(self),
1836                        #[cfg(feature = "checked")]
1837                        graph.graph_id,
1838                    )
1839                }
1840            }
1841
1842            impl<'a> Resource for &'a Arc<Lease<$name>> {
1843                type Node = [<$name LeaseNode>];
1844
1845                fn bind_graph(self, graph: &mut Graph) -> Self::Node {
1846                    // Bind a borrowed shared pooled resource, such as &Arc<Lease<Image>> or
1847                    // &Arc<Lease<Buffer>>.
1848
1849                    Arc::clone(self).bind_graph(graph)
1850                }
1851            }
1852        }
1853    };
1854}
1855
1856resource!(AccelerationStructure);
1857resource!(Image);
1858resource!(Buffer);
1859
1860#[derive(Debug, Default)]
1861struct ResourceMap {
1862    addr_index: HashMap<usize, NodeIndex>,
1863    resources: Vec<AnyResource>,
1864}
1865
1866impl ResourceMap {
1867    pub(crate) fn from_resources(resources: Vec<AnyResource>) -> Self {
1868        Self {
1869            addr_index: HashMap::new(),
1870            resources,
1871        }
1872    }
1873
1874    fn bind(&mut self, resource: AnyResource) -> NodeIndex {
1875        let node_idx = self.resources.len();
1876        self.resources.push(resource);
1877
1878        node_idx
1879    }
1880
1881    fn bind_shared<T>(&mut self, resource: Arc<T>) -> NodeIndex
1882    where
1883        Arc<T>: Into<AnyResource>,
1884    {
1885        let addr = Arc::as_ptr(&resource) as usize;
1886
1887        *self.addr_index.entry(addr).or_insert_with(|| {
1888            let node_idx = self.resources.len();
1889            self.resources.push(resource.into());
1890
1891            node_idx
1892        })
1893    }
1894}
1895
1896impl Deref for ResourceMap {
1897    type Target = [AnyResource];
1898
1899    fn deref(&self) -> &Self::Target {
1900        &self.resources
1901    }
1902}
1903
1904impl DerefMut for ResourceMap {
1905    fn deref_mut(&mut self) -> &mut Self::Target {
1906        &mut self.resources
1907    }
1908}
1909
1910/// Specifies the state of a color or combined depth and stencil attachment image after graphics
1911/// render pass framebuffer store operations.
1912///
1913/// Use this to specify the desired contents of any image after use in a pipeline command buffer.
1914#[derive(Clone, Copy, Debug, PartialEq)]
1915pub enum StoreOp {
1916    /// The attachment will become undefined and reads will produce garbage data.
1917    DontCare,
1918
1919    /// The attachment will be preserved in memory.
1920    Store,
1921}
1922
1923#[cfg(test)]
1924mod test {
1925    use std::sync::Arc;
1926
1927    use ash::vk;
1928
1929    use super::{
1930        AnyResource, CommandExecutionAbandoned, CommandExecutions, Graph, Node, ResourceMap,
1931    };
1932    use crate::driver::{
1933        DriverError,
1934        accel_struct::{AccelerationStructure, AccelerationStructureInfo},
1935        buffer::{Buffer, BufferInfo},
1936        device::{Device, DeviceInfo},
1937        image::{Image, ImageInfo},
1938        swapchain::SwapchainImage,
1939    };
1940    use crate::pool::{Pool, hash::HashPool};
1941
1942    #[test]
1943    fn command_execution_starts_pending() {
1944        let mut graph = Graph::new();
1945        let mut cmd = graph.begin_cmd();
1946        let execution = cmd.track_execution();
1947
1948        assert_eq!(execution.has_executed(), Ok(false));
1949    }
1950
1951    #[test]
1952    fn command_execution_is_abandoned_when_graph_drops() {
1953        let execution = {
1954            let mut graph = Graph::new();
1955            let mut cmd = graph.begin_cmd();
1956
1957            cmd.track_execution()
1958        };
1959
1960        assert_eq!(execution.has_executed(), Err(CommandExecutionAbandoned));
1961    }
1962
1963    #[test]
1964    fn command_executions_track_multiple_handles() {
1965        let mut executions = CommandExecutions::default();
1966        let first = executions.track();
1967        let second = executions.track();
1968
1969        executions.signal_executed();
1970
1971        assert_eq!(first.has_executed(), Ok(true));
1972        assert_eq!(second.has_executed(), Ok(true));
1973    }
1974
1975    #[test]
1976    fn command_executions_extend_preserves_both_sides() {
1977        let mut lhs = CommandExecutions::default();
1978        let first = lhs.track();
1979        let mut rhs = CommandExecutions::default();
1980        let second = rhs.track();
1981
1982        lhs.extend(rhs);
1983        lhs.signal_executed();
1984
1985        assert_eq!(first.has_executed(), Ok(true));
1986        assert_eq!(second.has_executed(), Ok(true));
1987    }
1988
1989    #[test]
1990    fn command_execution_stays_executed_after_tracker_drops() {
1991        let execution = {
1992            let mut executions = CommandExecutions::default();
1993            let execution = executions.track();
1994
1995            executions.signal_executed();
1996
1997            execution
1998        };
1999
2000        assert_eq!(execution.has_executed(), Ok(true));
2001    }
2002
2003    #[test]
2004    fn command_execution_abandoned_converts_to_driver_error() {
2005        let error = DriverError::from(CommandExecutionAbandoned);
2006
2007        assert!(matches!(error, DriverError::InvalidData));
2008    }
2009
2010    mod integration {
2011        use super::*;
2012
2013        fn test_device() -> Result<Device, DriverError> {
2014            Device::create(DeviceInfo::default())
2015        }
2016
2017        mod resource_map {
2018            use super::*;
2019
2020            #[test]
2021            #[ignore = "requires Vulkan device"]
2022            fn bind_assigns_a_new_node_index_every_time() -> Result<(), DriverError> {
2023                let device = test_device()?;
2024                let buffer = Arc::new(Buffer::create(
2025                    &device,
2026                    BufferInfo::device_mem(4, vk::BufferUsageFlags::STORAGE_BUFFER),
2027                )?);
2028                let image = Arc::new(Image::create(
2029                    &device,
2030                    ImageInfo::image_2d(
2031                        1,
2032                        1,
2033                        vk::Format::R8G8B8A8_UNORM,
2034                        vk::ImageUsageFlags::SAMPLED,
2035                    ),
2036                )?);
2037                let mut resources = ResourceMap::default();
2038
2039                assert_eq!(resources.bind(AnyResource::from(buffer)), 0);
2040                assert_eq!(resources.bind(AnyResource::from(image)), 1);
2041                assert_eq!(resources.len(), 2);
2042
2043                Ok(())
2044            }
2045
2046            #[test]
2047            #[ignore = "requires Vulkan device"]
2048            fn bind_shared_reuses_the_existing_node_index_for_the_same_address()
2049            -> Result<(), DriverError> {
2050                let device = test_device()?;
2051                let buffer = Arc::new(Buffer::create(
2052                    &device,
2053                    BufferInfo::device_mem(4, vk::BufferUsageFlags::STORAGE_BUFFER),
2054                )?);
2055                let mut resources = ResourceMap::default();
2056
2057                assert_eq!(resources.bind_shared(Arc::clone(&buffer)), 0);
2058                assert_eq!(resources.bind_shared(buffer), 0);
2059                assert_eq!(resources.len(), 1);
2060
2061                Ok(())
2062            }
2063
2064            #[test]
2065            #[ignore = "requires Vulkan device"]
2066            fn bind_shared_creates_distinct_node_indices_for_different_addresses()
2067            -> Result<(), DriverError> {
2068                let device = test_device()?;
2069                let buffer = Arc::new(Buffer::create(
2070                    &device,
2071                    BufferInfo::device_mem(4, vk::BufferUsageFlags::STORAGE_BUFFER),
2072                )?);
2073                let image = Arc::new(Image::create(
2074                    &device,
2075                    ImageInfo::image_2d(
2076                        1,
2077                        1,
2078                        vk::Format::R8G8B8A8_UNORM,
2079                        vk::ImageUsageFlags::SAMPLED,
2080                    ),
2081                )?);
2082                let mut resources = ResourceMap::default();
2083
2084                assert_eq!(resources.bind_shared(buffer), 0);
2085                assert_eq!(resources.bind_shared(image), 1);
2086                assert_eq!(resources.len(), 2);
2087
2088                Ok(())
2089            }
2090
2091            #[test]
2092            #[ignore = "requires Vulkan device"]
2093            fn graph_bind_fuzzes_all_resource_paths() -> Result<(), DriverError> {
2094                #[derive(Clone, Copy)]
2095                enum ResourceKind {
2096                    OwnedBuffer,
2097                    SharedBuffer,
2098                    OwnedBufferLease,
2099                    SharedBufferLease,
2100                    OwnedImage,
2101                    SharedImage,
2102                    OwnedImageLease,
2103                    SharedImageLease,
2104                    SwapchainImage,
2105                    OwnedAccelerationStructure,
2106                    SharedAccelerationStructure,
2107                    OwnedAccelerationStructureLease,
2108                    SharedAccelerationStructureLease,
2109                }
2110
2111                struct SharedNodes<T> {
2112                    values: Vec<(Arc<T>, usize)>,
2113                }
2114
2115                impl<T> Default for SharedNodes<T> {
2116                    fn default() -> Self {
2117                        Self { values: Vec::new() }
2118                    }
2119                }
2120
2121                impl<T> SharedNodes<T> {
2122                    fn get(&self, idx: usize) -> Option<(Arc<T>, usize)> {
2123                        self.values
2124                            .get(idx)
2125                            .map(|(resource, node_idx)| (Arc::clone(resource), *node_idx))
2126                    }
2127
2128                    fn push(&mut self, resource: Arc<T>, node_idx: usize) {
2129                        self.values.push((resource, node_idx));
2130                    }
2131
2132                    fn len(&self) -> usize {
2133                        self.values.len()
2134                    }
2135                }
2136
2137                fn next_rand(state: &mut u64) -> u64 {
2138                    *state ^= *state << 13;
2139                    *state ^= *state >> 7;
2140                    *state ^= *state << 17;
2141                    *state
2142                }
2143
2144                let device = test_device()?;
2145                let mut pool = HashPool::new(&device);
2146                let mut graph = Graph::new();
2147
2148                let mut rand_state = 0x5eed_u64;
2149                let mut shared_buffers = SharedNodes::<Buffer>::default();
2150                let mut shared_buffer_leases = SharedNodes::<crate::pool::Lease<Buffer>>::default();
2151                let mut shared_images = SharedNodes::<Image>::default();
2152                let mut shared_image_leases = SharedNodes::<crate::pool::Lease<Image>>::default();
2153                let mut shared_accels = SharedNodes::<AccelerationStructure>::default();
2154                let mut shared_accel_leases =
2155                    SharedNodes::<crate::pool::Lease<AccelerationStructure>>::default();
2156                let accel_supported = device.physical.vk_khr_acceleration_structure.is_some();
2157
2158                let mut resource_kinds = vec![
2159                    ResourceKind::OwnedBuffer,
2160                    ResourceKind::SharedBuffer,
2161                    ResourceKind::OwnedBufferLease,
2162                    ResourceKind::SharedBufferLease,
2163                    ResourceKind::OwnedImage,
2164                    ResourceKind::SharedImage,
2165                    ResourceKind::OwnedImageLease,
2166                    ResourceKind::SharedImageLease,
2167                    ResourceKind::SwapchainImage,
2168                ];
2169
2170                if accel_supported {
2171                    resource_kinds.push(ResourceKind::OwnedAccelerationStructure);
2172                    resource_kinds.push(ResourceKind::SharedAccelerationStructure);
2173                    resource_kinds.push(ResourceKind::OwnedAccelerationStructureLease);
2174                    resource_kinds.push(ResourceKind::SharedAccelerationStructureLease);
2175                }
2176
2177                for step in 0..64 {
2178                    let kind = resource_kinds
2179                        [(next_rand(&mut rand_state) as usize) % resource_kinds.len()];
2180                    let expect_new = match kind {
2181                        ResourceKind::OwnedBuffer
2182                        | ResourceKind::OwnedBufferLease
2183                        | ResourceKind::OwnedImage
2184                        | ResourceKind::OwnedImageLease
2185                        | ResourceKind::SwapchainImage
2186                        | ResourceKind::OwnedAccelerationStructure
2187                        | ResourceKind::OwnedAccelerationStructureLease => true,
2188                        ResourceKind::SharedBuffer => {
2189                            shared_buffers.len() == 0 || next_rand(&mut rand_state) & 1 == 0
2190                        }
2191                        ResourceKind::SharedBufferLease => {
2192                            shared_buffer_leases.len() == 0 || next_rand(&mut rand_state) & 1 == 0
2193                        }
2194                        ResourceKind::SharedImage => {
2195                            shared_images.len() == 0 || next_rand(&mut rand_state) & 1 == 0
2196                        }
2197                        ResourceKind::SharedImageLease => {
2198                            shared_image_leases.len() == 0 || next_rand(&mut rand_state) & 1 == 0
2199                        }
2200                        ResourceKind::SharedAccelerationStructure => {
2201                            shared_accels.len() == 0 || next_rand(&mut rand_state) & 1 == 0
2202                        }
2203                        ResourceKind::SharedAccelerationStructureLease => {
2204                            shared_accel_leases.len() == 0 || next_rand(&mut rand_state) & 1 == 0
2205                        }
2206                    };
2207
2208                    let expected_node_idx = graph.resources.len();
2209
2210                    let node_idx = match kind {
2211                        ResourceKind::OwnedBuffer => graph
2212                            .bind_resource(Buffer::create(
2213                                &device,
2214                                BufferInfo::device_mem(
2215                                    16 + step,
2216                                    vk::BufferUsageFlags::STORAGE_BUFFER,
2217                                ),
2218                            )?)
2219                            .index(),
2220                        ResourceKind::SharedBuffer if expect_new => {
2221                            let resource = Arc::new(Buffer::create(
2222                                &device,
2223                                BufferInfo::device_mem(
2224                                    16 + step,
2225                                    vk::BufferUsageFlags::STORAGE_BUFFER,
2226                                ),
2227                            )?);
2228                            let node_idx = graph.bind_resource(Arc::clone(&resource)).index();
2229                            shared_buffers.push(resource, node_idx);
2230                            node_idx
2231                        }
2232                        ResourceKind::SharedBuffer => {
2233                            let reuse_idx =
2234                                (next_rand(&mut rand_state) as usize) % shared_buffers.len();
2235                            let (resource, node_idx) = shared_buffers.get(reuse_idx).unwrap();
2236                            assert_eq!(graph.bind_resource(resource).index(), node_idx);
2237                            node_idx
2238                        }
2239                        ResourceKind::OwnedBufferLease => graph
2240                            .bind_resource(pool.resource(BufferInfo::device_mem(
2241                                32 + step,
2242                                vk::BufferUsageFlags::STORAGE_BUFFER,
2243                            ))?)
2244                            .index(),
2245                        ResourceKind::SharedBufferLease if expect_new => {
2246                            let resource = Arc::new(pool.resource(BufferInfo::device_mem(
2247                                32 + step,
2248                                vk::BufferUsageFlags::STORAGE_BUFFER,
2249                            ))?);
2250                            let node_idx = graph.bind_resource(Arc::clone(&resource)).index();
2251                            shared_buffer_leases.push(resource, node_idx);
2252                            node_idx
2253                        }
2254                        ResourceKind::SharedBufferLease => {
2255                            let reuse_idx =
2256                                (next_rand(&mut rand_state) as usize) % shared_buffer_leases.len();
2257                            let (resource, node_idx) = shared_buffer_leases.get(reuse_idx).unwrap();
2258                            assert_eq!(graph.bind_resource(resource).index(), node_idx);
2259                            node_idx
2260                        }
2261                        ResourceKind::OwnedImage => graph
2262                            .bind_resource(Image::create(
2263                                &device,
2264                                ImageInfo::image_2d(
2265                                    1,
2266                                    1,
2267                                    vk::Format::R8G8B8A8_UNORM,
2268                                    vk::ImageUsageFlags::SAMPLED,
2269                                ),
2270                            )?)
2271                            .index(),
2272                        ResourceKind::SharedImage if expect_new => {
2273                            let resource = Arc::new(Image::create(
2274                                &device,
2275                                ImageInfo::image_2d(
2276                                    1,
2277                                    1,
2278                                    vk::Format::R8G8B8A8_UNORM,
2279                                    vk::ImageUsageFlags::SAMPLED,
2280                                ),
2281                            )?);
2282                            let node_idx = graph.bind_resource(Arc::clone(&resource)).index();
2283                            shared_images.push(resource, node_idx);
2284                            node_idx
2285                        }
2286                        ResourceKind::SharedImage => {
2287                            let reuse_idx =
2288                                (next_rand(&mut rand_state) as usize) % shared_images.len();
2289                            let (resource, node_idx) = shared_images.get(reuse_idx).unwrap();
2290                            assert_eq!(graph.bind_resource(resource).index(), node_idx);
2291                            node_idx
2292                        }
2293                        ResourceKind::OwnedImageLease => graph
2294                            .bind_resource(pool.resource(ImageInfo::image_2d(
2295                                1,
2296                                1,
2297                                vk::Format::R8G8B8A8_UNORM,
2298                                vk::ImageUsageFlags::SAMPLED,
2299                            ))?)
2300                            .index(),
2301                        ResourceKind::SharedImageLease if expect_new => {
2302                            let resource = Arc::new(pool.resource(ImageInfo::image_2d(
2303                                1,
2304                                1,
2305                                vk::Format::R8G8B8A8_UNORM,
2306                                vk::ImageUsageFlags::SAMPLED,
2307                            ))?);
2308                            let node_idx = graph.bind_resource(Arc::clone(&resource)).index();
2309                            shared_image_leases.push(resource, node_idx);
2310                            node_idx
2311                        }
2312                        ResourceKind::SharedImageLease => {
2313                            let reuse_idx =
2314                                (next_rand(&mut rand_state) as usize) % shared_image_leases.len();
2315                            let (resource, node_idx) = shared_image_leases.get(reuse_idx).unwrap();
2316                            assert_eq!(graph.bind_resource(resource).index(), node_idx);
2317                            node_idx
2318                        }
2319                        ResourceKind::SwapchainImage => graph
2320                            .bind_resource(SwapchainImage::from_raw(
2321                                &device,
2322                                vk::Image::null(),
2323                                ImageInfo::image_2d(
2324                                    1,
2325                                    1,
2326                                    vk::Format::R8G8B8A8_UNORM,
2327                                    vk::ImageUsageFlags::COLOR_ATTACHMENT,
2328                                ),
2329                                step as u32,
2330                            ))
2331                            .index(),
2332                        ResourceKind::OwnedAccelerationStructure => graph
2333                            .bind_resource(AccelerationStructure::create(
2334                                &device,
2335                                AccelerationStructureInfo::blas(256 + step),
2336                            )?)
2337                            .index(),
2338                        ResourceKind::SharedAccelerationStructure if expect_new => {
2339                            let resource = Arc::new(AccelerationStructure::create(
2340                                &device,
2341                                AccelerationStructureInfo::blas(256 + step),
2342                            )?);
2343                            let node_idx = graph.bind_resource(Arc::clone(&resource)).index();
2344                            shared_accels.push(resource, node_idx);
2345                            node_idx
2346                        }
2347                        ResourceKind::SharedAccelerationStructure => {
2348                            let reuse_idx =
2349                                (next_rand(&mut rand_state) as usize) % shared_accels.len();
2350                            let (resource, node_idx) = shared_accels.get(reuse_idx).unwrap();
2351                            assert_eq!(graph.bind_resource(resource).index(), node_idx);
2352                            node_idx
2353                        }
2354                        ResourceKind::OwnedAccelerationStructureLease => graph
2355                            .bind_resource(
2356                                pool.resource(AccelerationStructureInfo::blas(512 + step))?,
2357                            )
2358                            .index(),
2359                        ResourceKind::SharedAccelerationStructureLease if expect_new => {
2360                            let resource = Arc::new(
2361                                pool.resource(AccelerationStructureInfo::blas(512 + step))?,
2362                            );
2363                            let node_idx = graph.bind_resource(Arc::clone(&resource)).index();
2364                            shared_accel_leases.push(resource, node_idx);
2365                            node_idx
2366                        }
2367                        ResourceKind::SharedAccelerationStructureLease => {
2368                            let reuse_idx =
2369                                (next_rand(&mut rand_state) as usize) % shared_accel_leases.len();
2370                            let (resource, node_idx) = shared_accel_leases.get(reuse_idx).unwrap();
2371                            assert_eq!(graph.bind_resource(resource).index(), node_idx);
2372                            node_idx
2373                        }
2374                    };
2375
2376                    if expect_new {
2377                        assert_eq!(node_idx, expected_node_idx);
2378                        assert_eq!(graph.resources.len(), expected_node_idx + 1);
2379                    } else {
2380                        assert!(node_idx < expected_node_idx);
2381                        assert_eq!(graph.resources.len(), expected_node_idx);
2382                    }
2383                }
2384
2385                Ok(())
2386            }
2387        }
2388    }
2389}