Skip to main content

vk_graph/cmd/
mod.rs

1//! Strongly-typed [`Graph`] commands.
2//!
3//! ## Lifecycle
4//!
5//! Commands follow a builder-style chain:
6//!
7//! 1. [`Graph::begin_cmd`] opens a [`Command`].
8//! 2. Declare resource accesses with [`Command::resource_access`] or bind a shader pipeline
9//!    with [`Command::bind_pipeline`], returning a [`PipelineCommand`].
10//! 3. With a pipeline, declare shader bindings with [`PipelineCommand::shader_resource_access`].
11//! 4. Record work with [`record_cmd`](Command::record_cmd) — available on both
12//!    [`Command`] and [`PipelineCommand`].
13//! 5. The command auto-closes when dropped or when [`Graph::finalize`] is called.
14//!
15//! A single command can call `record_cmd` multiple times — each call creates a separate
16//! "execution" within that command. Executions within a command stay in the specified
17//! order, but the graph system may re-order entire commands or merge them during
18//! submission for optimal scheduling.
19
20mod cmd_ref;
21mod compute;
22mod graphics;
23mod pipeline;
24mod ray_tracing;
25
26pub use {
27    self::{
28        cmd_ref::{
29            BuildAccelerationStructureIndirectInfo, BuildAccelerationStructureInfo, CommandRef,
30            UpdateAccelerationStructureIndirectInfo, UpdateAccelerationStructureInfo,
31        },
32        compute::ComputeCommandRef,
33        graphics::{ClearColorValue, GraphicsCommandRef},
34        pipeline::{Pipeline, PipelineCommand},
35        ray_tracing::RayTracingCommandRef,
36    },
37    super::{LoadOp, StoreOp},
38};
39
40use {
41    super::{
42        AccelerationStructureLeaseNode, AccelerationStructureNode, AnyAccelerationStructureNode,
43        AnyBufferNode, AnyImageNode, AnyResource, BufferLeaseNode, BufferNode, CommandData,
44        CommandExecution, CommandFunction, Execution, Graph, ImageLeaseNode, ImageNode, Node,
45        Resource, SwapchainImageNode,
46    },
47    crate::{
48        NodeIndex,
49        driver::{
50            buffer::BufferSubresourceRange, format_texel_block_extent, format_texel_block_size,
51            image::ImageViewInfo, image_subresource_range_from_layers,
52        },
53        stream::{AccelerationStructureArg, BufferArg, ImageArg},
54    },
55    ash::vk,
56    std::{ops::Range, sync::Arc},
57    vk_sync::AccessType,
58};
59
60/// Alias for the index of a framebuffer attachment.
61pub(crate) type AttachmentIndex = u32;
62
63/// Alias for the binding index of a shader descriptor.
64pub(crate) type BindingIndex = u32;
65
66/// Alias for the binding offset of a shader descriptor array element.
67pub(crate) type BindingOffset = u32;
68
69/// Alias for the descriptor set index of a shader descriptor.
70pub(crate) type DescriptorSetIndex = u32;
71
72/// A general-purpose Vulkan command which may contain acceleration structure operations, transfers,
73/// or shader pipelines.
74///
75/// There are four main uses of a [`Command`]:
76///
77/// 1. Bind resources ([`Self::bind_resource`])
78/// 1. Declare resource accesses ([`Self::resource_access`])
79/// 1. Record general-purpose command buffers or acceleration structure operations
80///    ([`Self::record_cmd`])
81/// 1. Bind shader pipelines ([`Self::bind_pipeline`])
82///
83/// When bound, a shader pipeline consumes the `Command` and returns a [`PipelineCommand`] which
84/// provides command recording functions specific to each pipeline type.
85pub struct Command<'a> {
86    pub(super) cmd_idx: usize,
87    pub(super) exec_idx: usize,
88    pub(super) graph: &'a mut Graph,
89}
90
91/// Builder for incrementally constructing a [`Command`].
92pub struct CommandBuilder<'a> {
93    cmd: Command<'a>,
94}
95
96impl<'a> CommandBuilder<'a> {
97    /// Begins a new command in `graph`.
98    pub fn new(graph: &'a mut Graph) -> Self {
99        Self {
100            cmd: graph.begin_cmd(),
101        }
102    }
103
104    /// Builds the command without pushing it to the graph.
105    pub fn build(self) -> Command<'a> {
106        self.cmd
107    }
108
109    /// Pushes the command onto its graph and returns the graph.
110    pub fn push_cmd(self) -> &'a mut Graph {
111        self.cmd.end_cmd()
112    }
113
114    /// Blits image regions.
115    #[allow(deprecated)]
116    pub fn blit_image(
117        mut self,
118        src: impl Into<AnyImageNode>,
119        dst: impl Into<AnyImageNode>,
120        filter: vk::Filter,
121        regions: impl AsRef<[vk::ImageBlit]> + 'static + Send,
122    ) -> Self {
123        self.cmd = self.cmd.blit_image(src, dst, filter, regions);
124        self
125    }
126
127    /// Clears a color image.
128    #[allow(deprecated)]
129    pub fn clear_color_image(
130        mut self,
131        image: impl Into<AnyImageNode>,
132        color: impl Into<ClearColorValue>,
133    ) -> Self {
134        self.cmd = self.cmd.clear_color_image(image, color);
135        self
136    }
137
138    /// Clears a depth/stencil image.
139    #[allow(deprecated)]
140    pub fn clear_depth_stencil_image(
141        mut self,
142        image: impl Into<AnyImageNode>,
143        depth: f32,
144        stencil: u32,
145    ) -> Self {
146        self.cmd = self.cmd.clear_depth_stencil_image(image, depth, stencil);
147        self
148    }
149
150    /// Copies data between buffer regions.
151    #[allow(deprecated)]
152    pub fn copy_buffer(
153        mut self,
154        src: impl Into<AnyBufferNode>,
155        dst: impl Into<AnyBufferNode>,
156        regions: impl AsRef<[vk::BufferCopy]> + 'static + Send,
157    ) -> Self {
158        self.cmd = self.cmd.copy_buffer(src, dst, regions);
159        self
160    }
161
162    /// Copies data from a buffer into image regions.
163    #[allow(deprecated)]
164    pub fn copy_buffer_to_image(
165        mut self,
166        src: impl Into<AnyBufferNode>,
167        dst: impl Into<AnyImageNode>,
168        regions: impl AsRef<[vk::BufferImageCopy]> + 'static + Send,
169    ) -> Self {
170        self.cmd = self.cmd.copy_buffer_to_image(src, dst, regions);
171        self
172    }
173
174    /// Copies data between image regions.
175    #[allow(deprecated)]
176    pub fn copy_image(
177        mut self,
178        src: impl Into<AnyImageNode>,
179        dst: impl Into<AnyImageNode>,
180        regions: impl AsRef<[vk::ImageCopy]> + 'static + Send,
181    ) -> Self {
182        self.cmd = self.cmd.copy_image(src, dst, regions);
183        self
184    }
185
186    /// Copies image region data into a buffer.
187    #[allow(deprecated)]
188    pub fn copy_image_to_buffer(
189        mut self,
190        src: impl Into<AnyImageNode>,
191        dst: impl Into<AnyBufferNode>,
192        regions: impl AsRef<[vk::BufferImageCopy]> + 'static + Send,
193    ) -> Self {
194        self.cmd = self.cmd.copy_image_to_buffer(src, dst, regions);
195        self
196    }
197
198    /// Fills a region of a buffer with a fixed value.
199    #[allow(deprecated)]
200    pub fn fill_buffer(
201        mut self,
202        buffer: impl Into<AnyBufferNode>,
203        region: Range<vk::DeviceSize>,
204        data: u32,
205    ) -> Self {
206        self.cmd = self.cmd.fill_buffer(buffer, region, data);
207        self
208    }
209
210    /// Records a [`vkCmdUpdateBuffer`](https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdUpdateBuffer.html) command.
211    pub fn update_buffer(
212        mut self,
213        buffer: impl Into<AnyBufferNode>,
214        offset: vk::DeviceSize,
215        data: impl AsRef<[u8]> + 'static + Send,
216    ) -> Self {
217        self.cmd = self.cmd.update_buffer(buffer, offset, data);
218        self
219    }
220}
221
222#[allow(private_bounds)]
223impl<'a> Command<'a> {
224    pub(super) fn new(graph: &'a mut Graph) -> Self {
225        let cmd_idx = graph.cmds.len();
226        graph.cmds.push(CommandData {
227            execs: vec![Default::default()], // We start off with a default execution!
228            #[cfg(debug_assertions)]
229            name: None,
230            stream_scope_id: None,
231            tracking: Default::default(),
232        });
233
234        Self {
235            cmd_idx,
236            exec_idx: 0,
237            graph,
238        }
239    }
240
241    /// Begins a command builder in `graph`.
242    pub fn builder(graph: &'a mut Graph) -> CommandBuilder<'a> {
243        CommandBuilder::new(graph)
244    }
245
246    /// Converts this command into a builder.
247    pub fn into_builder(self) -> CommandBuilder<'a> {
248        CommandBuilder { cmd: self }
249    }
250
251    /// Returns a handle that tracks whether this graph command has completed device execution.
252    ///
253    /// This may be called multiple times. Each returned handle independently observes the same
254    /// command execution.
255    pub fn track_execution(&mut self) -> CommandExecution {
256        self.cmd_mut().tracking.track()
257    }
258
259    fn cmd(&self) -> &CommandData {
260        &self.graph.cmds[self.cmd_idx]
261    }
262
263    fn cmd_mut(&mut self) -> &mut CommandData {
264        &mut self.graph.cmds[self.cmd_idx]
265    }
266
267    /// Binds a Vulkan buffer, image, or acceleration structure resource to the graph associated
268    /// with this command.
269    ///
270    /// Bound nodes may be used in commands for pipeline and shader operations.
271    pub fn bind_resource<R>(&mut self, resource: R) -> R::Node
272    where
273        R: Resource,
274    {
275        self.graph.bind_resource(resource)
276    }
277
278    /// Binds a shader pipeline to the current command, allowing for strongly typed access to the
279    /// related functions.
280    ///
281    /// | `P` | `P::Command` |
282    /// | --- | --- |
283    /// | [`ComputePipeline`](crate::driver::compute::ComputePipeline) | [`PipelineCommand<'_, ComputePipeline>`] |
284    /// | [`GraphicsPipeline`](crate::driver::graphics::GraphicsPipeline) | [`PipelineCommand<'_, GraphicsPipeline>`] |
285    /// | [`RayTracingPipeline`](crate::driver::ray_tracing::RayTracingPipeline) | [`PipelineCommand<'_, RayTracingPipeline>`] |
286    pub fn bind_pipeline<P>(self, pipeline: P) -> P::Command
287    where
288        P: Pipeline<'a>,
289    {
290        pipeline.bind_cmd(self)
291    }
292
293    /// Sets a debugging name, but only in debug builds.
294    pub fn debug_name(mut self, name: impl Into<String>) -> Self {
295        self.set_debug_name(name);
296        self
297    }
298
299    /// Finalize the recording of this command and return to the `Graph` where you may record
300    /// additional commands.
301    pub fn end_cmd(self) -> &'a mut Graph {
302        // If nothing was done in this command we can just ignore it.
303        if self.exec_idx == 0 {
304            self.graph.cmds.pop();
305        }
306
307        self.graph
308    }
309
310    fn push_exec(&mut self, func: impl FnOnce(CommandRef) + Send + 'static) {
311        let cmd = self.cmd_mut();
312        let exec = {
313            let last_exec = cmd.expect_last_exec_mut();
314            last_exec.func = Some(CommandFunction::Once(Box::new(func)));
315
316            Execution {
317                pipeline: last_exec.pipeline.clone(),
318                ..Default::default()
319            }
320        };
321
322        cmd.execs.push(exec);
323        self.exec_idx += 1;
324    }
325
326    pub(crate) fn push_reusable_exec(
327        &mut self,
328        func: impl for<'r> Fn(CommandRef<'r>) + Send + Sync + 'static,
329    ) {
330        let cmd = self.cmd_mut();
331        let exec = {
332            let last_exec = cmd.expect_last_exec_mut();
333            last_exec.func = Some(CommandFunction::Reusable(Arc::new(func)));
334
335            Execution {
336                pipeline: last_exec.pipeline.clone(),
337                ..Default::default()
338            }
339        };
340
341        cmd.execs.push(exec);
342        self.exec_idx += 1;
343    }
344
345    fn push_subresource_access(
346        &mut self,
347        resource_node: impl Node,
348        subresource: SubresourceRange,
349        access: AccessType,
350    ) {
351        self.graph.assert_node_owner(&resource_node);
352
353        let node_idx = resource_node.index();
354
355        self.push_subresource_access_index(node_idx, subresource, access);
356    }
357
358    pub(crate) fn push_subresource_access_index(
359        &mut self,
360        node_idx: NodeIndex,
361        subresource: SubresourceRange,
362        access: AccessType,
363    ) {
364        debug_assert!(self.graph.resources.get(node_idx).is_some());
365
366        self.cmd_mut().expect_last_exec_mut().accesses.push(
367            node_idx,
368            SubresourceAccess {
369                access,
370                subresource,
371            },
372        );
373    }
374
375    /// Begin recording general-purpose work for this graph command.
376    ///
377    /// This is the entry point for building and updating an
378    /// [`AccelerationStructure`](crate::driver::accel_struct::AccelerationStructure) instance.
379    ///
380    /// The provided closure allows you to run any Vulkan code, or interoperate with other Vulkan
381    /// code and interfaces.
382    pub fn record_cmd(mut self, func: impl FnOnce(CommandRef<'_>) + Send + 'static) -> Self {
383        self.record_cmd_mut(func);
384        self
385    }
386
387    /// Mutable-borrow form of [`Self::record_cmd`].
388    pub fn record_cmd_mut(&mut self, func: impl FnOnce(CommandRef<'_>) + Send + 'static) {
389        self.push_exec(move |cmd| {
390            func(cmd);
391        });
392    }
393
394    /// Blits image regions.
395    pub fn blit_image(
396        mut self,
397        src: impl Into<AnyImageNode>,
398        dst: impl Into<AnyImageNode>,
399        filter: vk::Filter,
400        regions: impl AsRef<[vk::ImageBlit]> + 'static + Send,
401    ) -> Self {
402        let src = src.into();
403        let dst = dst.into();
404        let regions = Arc::<[vk::ImageBlit]>::from(regions.as_ref());
405
406        for region in regions.as_ref() {
407            self.set_subresource_access(
408                src,
409                image_subresource_range_from_layers(region.src_subresource),
410                AccessType::TransferRead,
411            );
412            self.set_subresource_access(
413                dst,
414                image_subresource_range_from_layers(region.dst_subresource),
415                AccessType::TransferWrite,
416            );
417        }
418
419        self.record_stream_mut(move |cmd| {
420            let src_image = cmd.resource(src).handle;
421            let dst_image = cmd.resource(dst).handle;
422
423            unsafe {
424                cmd.device.cmd_blit_image(
425                    cmd.handle,
426                    src_image,
427                    vk::ImageLayout::TRANSFER_SRC_OPTIMAL,
428                    dst_image,
429                    vk::ImageLayout::TRANSFER_DST_OPTIMAL,
430                    regions.as_ref(),
431                    filter,
432                );
433            }
434        });
435        self
436    }
437
438    /// Clears a color image.
439    pub fn clear_color_image(
440        mut self,
441        image: impl Into<AnyImageNode>,
442        color: impl Into<ClearColorValue>,
443    ) -> Self {
444        let color = color.into().into();
445        let image = image.into();
446        let image_view = self.graph.resources[image.index()]
447            .expect_image_info()
448            .into();
449
450        self.set_subresource_access(image, image_view, AccessType::TransferWrite);
451        self.record_stream_mut(move |cmd| {
452            let image = cmd.resource(image);
453
454            unsafe {
455                cmd.device.cmd_clear_color_image(
456                    cmd.handle,
457                    image.handle,
458                    vk::ImageLayout::TRANSFER_DST_OPTIMAL,
459                    &color,
460                    &[image_view],
461                );
462            }
463        });
464        self
465    }
466
467    /// Clears a depth/stencil image.
468    pub fn clear_depth_stencil_image(
469        mut self,
470        image: impl Into<AnyImageNode>,
471        depth: f32,
472        stencil: u32,
473    ) -> Self {
474        let image = image.into();
475        let image_view = self.graph.resources[image.index()]
476            .expect_image_info()
477            .into();
478
479        self.set_subresource_access(image, image_view, AccessType::TransferWrite);
480        self.record_stream_mut(move |cmd| {
481            let image = cmd.resource(image);
482
483            unsafe {
484                cmd.device.cmd_clear_depth_stencil_image(
485                    cmd.handle,
486                    image.handle,
487                    vk::ImageLayout::TRANSFER_DST_OPTIMAL,
488                    &vk::ClearDepthStencilValue { depth, stencil },
489                    &[image_view],
490                );
491            }
492        });
493        self
494    }
495
496    /// Copies data between buffer regions.
497    pub fn copy_buffer(
498        mut self,
499        src: impl Into<AnyBufferNode>,
500        dst: impl Into<AnyBufferNode>,
501        regions: impl AsRef<[vk::BufferCopy]> + 'static + Send,
502    ) -> Self {
503        let src = src.into();
504        let dst = dst.into();
505        let regions = Arc::<[vk::BufferCopy]>::from(regions.as_ref());
506
507        #[cfg(feature = "checked")]
508        let src_size = self.graph.resources[src.index()].expect_buffer_info().size;
509
510        #[cfg(feature = "checked")]
511        let dst_size = self.graph.resources[dst.index()].expect_buffer_info().size;
512
513        for region in regions.iter() {
514            #[cfg(feature = "checked")]
515            {
516                assert!(
517                    region.src_offset + region.size <= src_size,
518                    "source range end ({}) exceeds source size ({src_size})",
519                    region.src_offset + region.size
520                );
521                assert!(
522                    region.dst_offset + region.size <= dst_size,
523                    "destination range end ({}) exceeds destination size ({dst_size})",
524                    region.dst_offset + region.size
525                );
526            };
527
528            self.set_subresource_access(
529                src,
530                region.src_offset..region.src_offset + region.size,
531                AccessType::TransferRead,
532            );
533            self.set_subresource_access(
534                dst,
535                region.dst_offset..region.dst_offset + region.size,
536                AccessType::TransferWrite,
537            );
538        }
539
540        self.record_stream_mut(move |cmd| {
541            let src = cmd.resource(src);
542            let dst = cmd.resource(dst);
543
544            unsafe {
545                cmd.device
546                    .cmd_copy_buffer(cmd.handle, src.handle, dst.handle, &regions);
547            }
548        });
549        self
550    }
551
552    /// Copies data from a buffer into image regions.
553    pub fn copy_buffer_to_image(
554        mut self,
555        src: impl Into<AnyBufferNode>,
556        dst: impl Into<AnyImageNode>,
557        regions: impl AsRef<[vk::BufferImageCopy]> + 'static + Send,
558    ) -> Self {
559        let src = src.into();
560        let dst = dst.into();
561        let dst_info = self.graph.resources[dst.index()].expect_image_info();
562        let regions = Arc::<[vk::BufferImageCopy]>::from(regions.as_ref());
563
564        for region in regions.iter() {
565            let block_bytes_size = format_texel_block_size(dst_info.format);
566            let (block_height, block_width) = format_texel_block_extent(dst_info.format);
567            let data_size = block_bytes_size
568                * (region.buffer_row_length / block_width)
569                * (region.buffer_image_height / block_height);
570
571            self.set_subresource_access(
572                src,
573                region.buffer_offset..region.buffer_offset + data_size as vk::DeviceSize,
574                AccessType::TransferRead,
575            );
576            self.set_subresource_access(
577                dst,
578                image_subresource_range_from_layers(region.image_subresource),
579                AccessType::TransferWrite,
580            );
581        }
582
583        self.record_stream_mut(move |cmd| {
584            let src = cmd.resource(src);
585            let dst = cmd.resource(dst);
586
587            unsafe {
588                cmd.device.cmd_copy_buffer_to_image(
589                    cmd.handle,
590                    src.handle,
591                    dst.handle,
592                    vk::ImageLayout::TRANSFER_DST_OPTIMAL,
593                    &regions,
594                );
595            }
596        });
597        self
598    }
599
600    /// Copies data between image regions.
601    pub fn copy_image(
602        mut self,
603        src: impl Into<AnyImageNode>,
604        dst: impl Into<AnyImageNode>,
605        regions: impl AsRef<[vk::ImageCopy]> + 'static + Send,
606    ) -> Self {
607        let src = src.into();
608        let dst = dst.into();
609        let regions = Arc::<[vk::ImageCopy]>::from(regions.as_ref());
610
611        for region in regions.iter() {
612            self.set_subresource_access(
613                src,
614                image_subresource_range_from_layers(region.src_subresource),
615                AccessType::TransferRead,
616            );
617            self.set_subresource_access(
618                dst,
619                image_subresource_range_from_layers(region.dst_subresource),
620                AccessType::TransferWrite,
621            );
622        }
623
624        self.record_stream_mut(move |cmd| {
625            let src = cmd.resource(src);
626            let dst = cmd.resource(dst);
627
628            unsafe {
629                cmd.device.cmd_copy_image(
630                    cmd.handle,
631                    src.handle,
632                    vk::ImageLayout::TRANSFER_SRC_OPTIMAL,
633                    dst.handle,
634                    vk::ImageLayout::TRANSFER_DST_OPTIMAL,
635                    &regions,
636                );
637            }
638        });
639        self
640    }
641
642    /// Copies image region data into a buffer.
643    pub fn copy_image_to_buffer(
644        mut self,
645        src: impl Into<AnyImageNode>,
646        dst: impl Into<AnyBufferNode>,
647        regions: impl AsRef<[vk::BufferImageCopy]> + 'static + Send,
648    ) -> Self {
649        let src = src.into();
650        let src_info = self.graph.resources[src.index()].expect_image_info();
651        let dst = dst.into();
652        let regions = Arc::<[vk::BufferImageCopy]>::from(regions.as_ref());
653
654        for region in regions.iter() {
655            let block_bytes_size = format_texel_block_size(src_info.format);
656            let (block_height, block_width) = format_texel_block_extent(src_info.format);
657            let data_size = block_bytes_size
658                * (region.buffer_row_length / block_width)
659                * (region.buffer_image_height / block_height);
660
661            self.set_subresource_access(
662                src,
663                image_subresource_range_from_layers(region.image_subresource),
664                AccessType::TransferRead,
665            );
666            self.set_subresource_access(
667                dst,
668                region.buffer_offset..region.buffer_offset + data_size as vk::DeviceSize,
669                AccessType::TransferWrite,
670            );
671        }
672
673        self.record_stream_mut(move |cmd| {
674            let src = cmd.resource(src);
675            let dst = cmd.resource(dst);
676
677            unsafe {
678                cmd.device.cmd_copy_image_to_buffer(
679                    cmd.handle,
680                    src.handle,
681                    vk::ImageLayout::TRANSFER_SRC_OPTIMAL,
682                    dst.handle,
683                    &regions,
684                );
685            }
686        });
687        self
688    }
689
690    /// Fills a region of a buffer with a fixed value.
691    pub fn fill_buffer(
692        mut self,
693        buffer: impl Into<AnyBufferNode>,
694        region: Range<vk::DeviceSize>,
695        data: u32,
696    ) -> Self {
697        let buffer = buffer.into();
698
699        self.set_subresource_access(buffer, region.clone(), AccessType::TransferWrite);
700        self.record_stream_mut(move |cmd| {
701            let buffer = cmd.resource(buffer);
702
703            unsafe {
704                cmd.device.cmd_fill_buffer(
705                    cmd.handle,
706                    buffer.handle,
707                    region.start,
708                    region.end - region.start,
709                    data,
710                );
711            }
712        });
713        self
714    }
715
716    pub(crate) fn record_stream(
717        mut self,
718        func: impl for<'r> Fn(CommandRef<'r>) + Send + Sync + 'static,
719    ) -> Self {
720        self.record_stream_mut(func);
721        self
722    }
723
724    pub(crate) fn record_stream_mut(
725        &mut self,
726        func: impl for<'r> Fn(CommandRef<'r>) + Send + Sync + 'static,
727    ) {
728        self.push_reusable_exec(func);
729    }
730
731    /// Returns a borrow of the original Vulkan resource (buffer, image or acceleration structure)
732    /// which the given bound resource node represents.
733    pub fn resource<N>(&self, resource_node: N) -> &N::Resource
734    where
735        N: Node,
736    {
737        self.graph.resource(resource_node)
738    }
739
740    /// Informs the command that recorded work will read or write `resource_node`
741    /// using `access`.
742    ///
743    /// An access function must be called for `resource_node` before it is used within a recording
744    /// function.
745    pub fn resource_access<N>(mut self, resource_node: N, access: AccessType) -> Self
746    where
747        N: Node + Subresource,
748        SubresourceRange: From<N::Range>,
749    {
750        self.set_resource_access(resource_node, access);
751        self
752    }
753
754    /// Mutable-borrow form of [`Self::debug_name`].
755    pub fn set_debug_name(&mut self, name: impl Into<String>) -> &mut Self {
756        #[cfg(debug_assertions)]
757        {
758            self.cmd_mut().name = Some(name.into());
759        }
760
761        #[cfg(not(debug_assertions))]
762        {
763            let _ = name;
764        }
765
766        self
767    }
768
769    /// Mutable-borrow form of [`Self::resource_access`].
770    pub fn set_resource_access<N>(&mut self, resource_node: N, access: AccessType)
771    where
772        N: Node + Subresource,
773        SubresourceRange: From<N::Range>,
774    {
775        let whole_resource = resource_node.range(&self.graph.resources);
776        let subresource = SubresourceRange::from(whole_resource);
777
778        self.push_subresource_access(resource_node, subresource, access);
779    }
780
781    pub(crate) fn set_stream_scope_id(&mut self, stream_scope_id: u64) {
782        self.cmd_mut().stream_scope_id = Some(stream_scope_id);
783    }
784
785    /// Mutable-borrow form of [`Self::subresource_access`].
786    pub fn set_subresource_access<N>(
787        &mut self,
788        resource_node: N,
789        subresource: impl Into<N::Range>,
790        access: AccessType,
791    ) where
792        N: Node + Subresource,
793        SubresourceRange: From<N::Range>,
794    {
795        let subresource = subresource.into();
796        let subresource = SubresourceRange::from(subresource);
797
798        self.push_subresource_access(resource_node, subresource, access);
799    }
800
801    /// Informs the command that recorded work will read or write the `subresource` of
802    /// `resource_node` using `access`.
803    ///
804    /// An access function must be called for `resource_node` before it is used within a recording
805    /// function.
806    pub fn subresource_access<N>(
807        mut self,
808        resource_node: N,
809        subresource: impl Into<N::Range>,
810        access: AccessType,
811    ) -> Self
812    where
813        N: Node + Subresource,
814        SubresourceRange: From<N::Range>,
815    {
816        self.set_subresource_access(resource_node, subresource, access);
817        self
818    }
819
820    /// Records a [`vkCmdUpdateBuffer`](https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdUpdateBuffer.html)
821    /// command.
822    ///
823    /// Vulkan requires `data` to be at most `65536` bytes.
824    ///
825    /// These constraints are validated by the Vulkan Validation Layer (VVL) when it is active.
826    /// When the `checked` feature is enabled, `vk-graph` also validates the data size and bounds
827    /// before recording the command.
828    #[profiling::function]
829    pub fn update_buffer(
830        mut self,
831        buffer: impl Into<AnyBufferNode>,
832        offset: vk::DeviceSize,
833        data: impl AsRef<[u8]> + 'static + Send,
834    ) -> Self {
835        debug_assert!(data.as_ref().len() <= 64 * 1024);
836
837        let buffer = buffer.into();
838        let data_end = offset + data.as_ref().len() as vk::DeviceSize;
839
840        #[cfg(feature = "checked")]
841        {
842            assert!(
843                data.as_ref().len() <= 64 * 1024,
844                "data length ({}) exceeds vkCmdUpdateBuffer limit (65536)",
845                data.as_ref().len()
846            );
847
848            let buffer_info = self.graph.resources[buffer.index()].expect_buffer_info();
849
850            assert!(
851                data_end <= buffer_info.size,
852                "data range end ({data_end}) exceeds buffer size ({})",
853                buffer_info.size
854            );
855        }
856
857        let data = Arc::<[u8]>::from(data.as_ref());
858
859        self.set_subresource_access(buffer, offset..data_end, AccessType::TransferWrite);
860        self.record_stream_mut(move |cmd| {
861            let buffer = cmd.resource(buffer);
862
863            unsafe {
864                cmd.device
865                    .cmd_update_buffer(cmd.handle, buffer.handle, offset, &data);
866            }
867        });
868        self
869    }
870}
871
872/// Describes the SPIR-V binding index, and optionally a specific descriptor set
873/// and array index.
874///
875/// Generally you might pass a function a descriptor using a simple integer:
876///
877/// ```rust
878/// # fn my_func(_: usize, _: ()) {}
879/// # let image = ();
880/// let descriptor = 42;
881/// my_func(descriptor, image);
882/// ```
883///
884/// But also:
885///
886/// - `(0, 42)` for descriptor set `0` and binding index `42`
887/// - `(42, [8])` for the same binding, but the 8th element
888/// - `(0, 42, [8])` same as the previous example
889#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
890pub struct Binding {
891    /// The value of the descriptor binding decoration applied to the variable.
892    pub binding: u32,
893
894    /// An array-element offset applied to this descriptor.
895    pub offset: u32,
896
897    /// An optional descriptor set index value.
898    pub set: u32,
899}
900
901impl Binding {
902    pub(super) fn into_tuple(self) -> (DescriptorSetIndex, BindingIndex, BindingOffset) {
903        (self.set, self.binding, self.offset)
904    }
905
906    pub(super) fn set(self) -> DescriptorSetIndex {
907        let (res, _, _) = self.into_tuple();
908        res
909    }
910}
911
912impl From<BindingIndex> for Binding {
913    fn from(binding: BindingIndex) -> Self {
914        Self {
915            binding,
916            offset: 0,
917            set: 0,
918        }
919    }
920}
921
922impl From<(DescriptorSetIndex, BindingIndex)> for Binding {
923    fn from((set, binding): (DescriptorSetIndex, BindingIndex)) -> Self {
924        Self {
925            binding,
926            offset: 0,
927            set,
928        }
929    }
930}
931
932impl From<(BindingIndex, [BindingOffset; 1])> for Binding {
933    fn from((binding, [offset]): (BindingIndex, [BindingOffset; 1])) -> Self {
934        Self {
935            binding,
936            offset,
937            set: 0,
938        }
939    }
940}
941
942impl From<(DescriptorSetIndex, BindingIndex, [BindingOffset; 1])> for Binding {
943    fn from(
944        (set, binding, [offset]): (DescriptorSetIndex, BindingIndex, [BindingOffset; 1]),
945    ) -> Self {
946        Self {
947            binding,
948            offset,
949            set,
950        }
951    }
952}
953
954/// Allows for a resource to be reinterpreted as differently formatted data.
955#[allow(private_bounds)]
956pub trait Subresource: private::SubresourceSealed {
957    /// The information about the subresource when bound directly to shader descriptors.
958    type Info;
959
960    /// The information about the subresource when used indirectly by any part of a graph.
961    type Range;
962}
963
964macro_rules! view_accel_struct {
965    ($name:ty) => {
966        impl Subresource for $name {
967            type Info = Self::Range;
968            type Range = ();
969        }
970
971        impl private::SubresourceSealed for $name {
972            fn info(&self, _: &[AnyResource]) -> <Self as Subresource>::Info
973            where
974                Self: Node + Subresource,
975            {
976            }
977
978            fn range(&self, resources: &[AnyResource]) -> <Self as Subresource>::Range
979            where
980                Self: Node + Subresource,
981            {
982                resources[self.index()].expect_accel_struct_info();
983            }
984        }
985    };
986}
987
988view_accel_struct!(AnyAccelerationStructureNode);
989view_accel_struct!(AccelerationStructureArg);
990view_accel_struct!(AccelerationStructureLeaseNode);
991view_accel_struct!(AccelerationStructureNode);
992
993macro_rules! view_buffer {
994    ($name:ty) => {
995        impl Subresource for $name {
996            type Info = Self::Range;
997            type Range = BufferSubresourceRange;
998        }
999
1000        impl private::SubresourceSealed for $name {
1001            fn info(&self, resources: &[AnyResource]) -> <Self as Subresource>::Info
1002            where
1003                Self: Node + Subresource,
1004            {
1005                self.range(resources)
1006            }
1007
1008            fn range(&self, resources: &[AnyResource]) -> <Self as Subresource>::Range
1009            where
1010                Self: Node + Subresource,
1011            {
1012                let idx = self.index();
1013
1014                resources[idx].expect_buffer_info().into()
1015            }
1016        }
1017    };
1018}
1019
1020view_buffer!(AnyBufferNode);
1021view_buffer!(BufferArg);
1022view_buffer!(BufferLeaseNode);
1023view_buffer!(BufferNode);
1024
1025macro_rules! view_image {
1026    ($name:ty) => {
1027        impl Subresource for $name {
1028            type Info = ImageViewInfo;
1029            type Range = vk::ImageSubresourceRange;
1030        }
1031
1032        impl private::SubresourceSealed for $name {
1033            fn info(&self, resources: &[AnyResource]) -> <Self as Subresource>::Info
1034            where
1035                Self: Node + Subresource,
1036            {
1037                let idx = self.index();
1038
1039                resources[idx].expect_image_info().into()
1040            }
1041
1042            fn range(&self, resources: &[AnyResource]) -> <Self as Subresource>::Range
1043            where
1044                Self: Node + Subresource,
1045            {
1046                self.info(resources).into()
1047            }
1048        }
1049    };
1050}
1051
1052view_image!(AnyImageNode);
1053view_image!(ImageArg);
1054view_image!(ImageLeaseNode);
1055view_image!(ImageNode);
1056view_image!(SwapchainImageNode);
1057
1058#[derive(Clone, Copy, Debug)]
1059pub(crate) enum SubresourceRange {
1060    /// Acceleration structures are bound whole.
1061    AccelerationStructure,
1062
1063    /// Images may be partially bound.
1064    Image(vk::ImageSubresourceRange),
1065
1066    /// Buffers may be partially bound.
1067    Buffer(BufferSubresourceRange),
1068}
1069
1070impl SubresourceRange {
1071    pub(super) fn as_image(&self) -> Option<&vk::ImageSubresourceRange> {
1072        if let Self::Image(subresource) = self {
1073            Some(subresource)
1074        } else {
1075            None
1076        }
1077    }
1078
1079    pub(super) fn expect_image(&self) -> &vk::ImageSubresourceRange {
1080        self.as_image().expect("missing image subresource")
1081    }
1082}
1083
1084impl From<BufferSubresourceRange> for SubresourceRange {
1085    fn from(subresource: BufferSubresourceRange) -> Self {
1086        Self::Buffer(subresource)
1087    }
1088}
1089
1090impl From<()> for SubresourceRange {
1091    fn from(_: ()) -> Self {
1092        Self::AccelerationStructure
1093    }
1094}
1095
1096impl From<ImageViewInfo> for SubresourceRange {
1097    fn from(subresource: ImageViewInfo) -> Self {
1098        Self::Image(subresource.into())
1099    }
1100}
1101
1102impl From<vk::ImageSubresourceRange> for SubresourceRange {
1103    fn from(subresource: vk::ImageSubresourceRange) -> Self {
1104        Self::Image(subresource)
1105    }
1106}
1107
1108#[derive(Clone, Copy, Debug)]
1109pub(super) struct SubresourceAccess {
1110    pub access: AccessType,
1111    pub subresource: SubresourceRange,
1112}
1113
1114/// Describes the interpretation of a resource.
1115#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1116pub(crate) enum ViewInfo {
1117    /// Acceleration structures are always whole resources.
1118    AccelerationStructure,
1119
1120    /// Images may be interpreted as differently formatted images.
1121    Image(ImageViewInfo),
1122
1123    /// Buffers may be interpreted as subregions of the same buffer.
1124    Buffer(BufferSubresourceRange),
1125}
1126
1127impl ViewInfo {
1128    pub(crate) fn as_buffer(&self) -> Option<&BufferSubresourceRange> {
1129        match self {
1130            Self::Buffer(info) => Some(info),
1131            _ => None,
1132        }
1133    }
1134
1135    pub(crate) fn as_image(&self) -> Option<&ImageViewInfo> {
1136        match self {
1137            Self::Image(info) => Some(info),
1138            _ => None,
1139        }
1140    }
1141
1142    pub(crate) fn expect_buffer(&self) -> &BufferSubresourceRange {
1143        self.as_buffer().expect("missing buffer view info")
1144    }
1145
1146    pub(crate) fn expect_image(&self) -> &ImageViewInfo {
1147        self.as_image().expect("missing image view info")
1148    }
1149}
1150
1151impl From<()> for ViewInfo {
1152    fn from(_: ()) -> Self {
1153        Self::AccelerationStructure
1154    }
1155}
1156
1157impl From<BufferSubresourceRange> for ViewInfo {
1158    fn from(info: BufferSubresourceRange) -> Self {
1159        Self::Buffer(info)
1160    }
1161}
1162
1163impl From<ImageViewInfo> for ViewInfo {
1164    fn from(info: ImageViewInfo) -> Self {
1165        Self::Image(info)
1166    }
1167}
1168
1169impl From<Range<vk::DeviceSize>> for ViewInfo {
1170    fn from(range: Range<vk::DeviceSize>) -> Self {
1171        Self::Buffer(BufferSubresourceRange {
1172            start: range.start,
1173            end: range.end,
1174        })
1175    }
1176}
1177
1178mod private {
1179    use crate::{AnyResource, Node};
1180
1181    pub(crate) trait SubresourceSealed: Sized {
1182        fn info(&self, resources: &[AnyResource]) -> <Self as super::Subresource>::Info
1183        where
1184            Self: Node + super::Subresource;
1185
1186        fn range(&self, resources: &[AnyResource]) -> <Self as super::Subresource>::Range
1187        where
1188            Self: Node + super::Subresource;
1189    }
1190}