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