Skip to main content

vk_graph/
lib.rs

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