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