Skip to main content

vk_graph/
lib.rs

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