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, TimestampQuery, TimestampQueryPlacement,
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    /// Records a timestamp query point at the current position in this command.
260    ///
261    /// A timestamp written before any command work is recorded at the start of this command. After
262    /// command work is recorded, timestamps are recorded after the most recently added execution.
263    ///
264    /// See [`Graph::write_timestamp`] for graph-boundary timestamps.
265    pub fn write_timestamp(&mut self) -> TimestampQuery {
266        let (exec_idx, placement) = if self.exec_idx == 0 {
267            (0, TimestampQueryPlacement::BeforeExec)
268        } else {
269            (self.exec_idx - 1, TimestampQueryPlacement::AfterExec)
270        };
271
272        self.graph
273            .write_timestamp_at(self.cmd_idx, exec_idx, placement)
274    }
275
276    fn cmd(&self) -> &CommandData {
277        &self.graph.cmds[self.cmd_idx]
278    }
279
280    fn cmd_mut(&mut self) -> &mut CommandData {
281        &mut self.graph.cmds[self.cmd_idx]
282    }
283
284    /// Binds a Vulkan buffer, image, or acceleration structure resource to the graph associated
285    /// with this command.
286    ///
287    /// Bound nodes may be used in commands for pipeline and shader operations.
288    pub fn bind_resource<R>(&mut self, resource: R) -> R::Node
289    where
290        R: Resource,
291    {
292        self.graph.bind_resource(resource)
293    }
294
295    /// Binds a shader pipeline to the current command, allowing for strongly typed access to the
296    /// related functions.
297    ///
298    /// | `P` | `P::Command` |
299    /// | --- | --- |
300    /// | [`ComputePipeline`](crate::driver::compute::ComputePipeline) | [`PipelineCommand<'_, ComputePipeline>`] |
301    /// | [`GraphicsPipeline`](crate::driver::graphics::GraphicsPipeline) | [`PipelineCommand<'_, GraphicsPipeline>`] |
302    /// | [`RayTracingPipeline`](crate::driver::ray_tracing::RayTracingPipeline) | [`PipelineCommand<'_, RayTracingPipeline>`] |
303    pub fn bind_pipeline<P>(self, pipeline: P) -> P::Command
304    where
305        P: Pipeline<'a>,
306    {
307        pipeline.bind_cmd(self)
308    }
309
310    /// Sets a debugging name, but only in debug builds.
311    pub fn debug_name(mut self, name: impl Into<String>) -> Self {
312        self.set_debug_name(name);
313        self
314    }
315
316    /// Finalize the recording of this command and return to the `Graph` where you may record
317    /// additional commands.
318    pub fn end_cmd(self) -> &'a mut Graph {
319        // If nothing was done in this command we can just ignore it.
320        if self.exec_idx == 0 {
321            self.graph.cmds.pop();
322        }
323
324        self.graph
325    }
326
327    fn push_exec(&mut self, func: impl FnOnce(CommandRef) + Send + 'static) {
328        let cmd = self.cmd_mut();
329        let exec = {
330            let last_exec = cmd.expect_last_exec_mut();
331            last_exec.func = Some(CommandFunction::Once(Box::new(func)));
332
333            Execution {
334                pipeline: last_exec.pipeline.clone(),
335                ..Default::default()
336            }
337        };
338
339        cmd.execs.push(exec);
340        self.exec_idx += 1;
341    }
342
343    pub(crate) fn push_reusable_exec(
344        &mut self,
345        func: impl for<'r> Fn(CommandRef<'r>) + Send + Sync + 'static,
346    ) {
347        let cmd = self.cmd_mut();
348        let exec = {
349            let last_exec = cmd.expect_last_exec_mut();
350            last_exec.func = Some(CommandFunction::Reusable(Arc::new(func)));
351
352            Execution {
353                pipeline: last_exec.pipeline.clone(),
354                ..Default::default()
355            }
356        };
357
358        cmd.execs.push(exec);
359        self.exec_idx += 1;
360    }
361
362    fn push_subresource_access(
363        &mut self,
364        resource_node: impl Node,
365        subresource: SubresourceRange,
366        access: AccessType,
367    ) {
368        self.graph.assert_node_owner(&resource_node);
369
370        let node_idx = resource_node.index();
371
372        self.push_subresource_access_index(node_idx, subresource, access);
373    }
374
375    pub(crate) fn push_subresource_access_index(
376        &mut self,
377        node_idx: NodeIndex,
378        subresource: SubresourceRange,
379        access: AccessType,
380    ) {
381        debug_assert!(self.graph.resources.get(node_idx).is_some());
382
383        self.cmd_mut().expect_last_exec_mut().accesses.push(
384            node_idx,
385            SubresourceAccess {
386                access,
387                subresource,
388            },
389        );
390    }
391
392    /// Begin recording general-purpose work for this graph command.
393    ///
394    /// This is the entry point for building and updating an
395    /// [`AccelerationStructure`](crate::driver::accel_struct::AccelerationStructure) instance.
396    ///
397    /// The provided closure allows you to run any Vulkan code, or interoperate with other Vulkan
398    /// code and interfaces.
399    pub fn record_cmd(mut self, func: impl FnOnce(CommandRef<'_>) + Send + 'static) -> Self {
400        self.record_cmd_mut(func);
401        self
402    }
403
404    /// Mutable-borrow form of [`Self::record_cmd`].
405    pub fn record_cmd_mut(&mut self, func: impl FnOnce(CommandRef<'_>) + Send + 'static) {
406        self.push_exec(move |cmd| {
407            func(cmd);
408        });
409    }
410
411    /// Blits image regions.
412    pub fn blit_image(
413        mut self,
414        src: impl Into<AnyImageNode>,
415        dst: impl Into<AnyImageNode>,
416        filter: vk::Filter,
417        regions: impl AsRef<[vk::ImageBlit]> + 'static + Send,
418    ) -> Self {
419        let src = src.into();
420        let dst = dst.into();
421        let regions = Arc::<[vk::ImageBlit]>::from(regions.as_ref());
422
423        for region in regions.as_ref() {
424            self.set_subresource_access(
425                src,
426                image_subresource_range_from_layers(region.src_subresource),
427                AccessType::TransferRead,
428            );
429            self.set_subresource_access(
430                dst,
431                image_subresource_range_from_layers(region.dst_subresource),
432                AccessType::TransferWrite,
433            );
434        }
435
436        self.record_stream_mut(move |cmd| {
437            let src_image = cmd.resource(src).handle;
438            let dst_image = cmd.resource(dst).handle;
439
440            unsafe {
441                cmd.device.cmd_blit_image(
442                    cmd.handle,
443                    src_image,
444                    vk::ImageLayout::TRANSFER_SRC_OPTIMAL,
445                    dst_image,
446                    vk::ImageLayout::TRANSFER_DST_OPTIMAL,
447                    regions.as_ref(),
448                    filter,
449                );
450            }
451        });
452        self
453    }
454
455    /// Clears a color image.
456    pub fn clear_color_image(
457        mut self,
458        image: impl Into<AnyImageNode>,
459        color: impl Into<ClearColorValue>,
460    ) -> Self {
461        let color = color.into().into();
462        let image = image.into();
463        let image_view = self.graph.resources[image.index()]
464            .expect_image_info()
465            .into();
466
467        self.set_subresource_access(image, image_view, AccessType::TransferWrite);
468        self.record_stream_mut(move |cmd| {
469            let image = cmd.resource(image);
470
471            unsafe {
472                cmd.device.cmd_clear_color_image(
473                    cmd.handle,
474                    image.handle,
475                    vk::ImageLayout::TRANSFER_DST_OPTIMAL,
476                    &color,
477                    &[image_view],
478                );
479            }
480        });
481        self
482    }
483
484    /// Clears a depth/stencil image.
485    pub fn clear_depth_stencil_image(
486        mut self,
487        image: impl Into<AnyImageNode>,
488        depth: f32,
489        stencil: u32,
490    ) -> Self {
491        let image = image.into();
492        let image_view = self.graph.resources[image.index()]
493            .expect_image_info()
494            .into();
495
496        self.set_subresource_access(image, image_view, AccessType::TransferWrite);
497        self.record_stream_mut(move |cmd| {
498            let image = cmd.resource(image);
499
500            unsafe {
501                cmd.device.cmd_clear_depth_stencil_image(
502                    cmd.handle,
503                    image.handle,
504                    vk::ImageLayout::TRANSFER_DST_OPTIMAL,
505                    &vk::ClearDepthStencilValue { depth, stencil },
506                    &[image_view],
507                );
508            }
509        });
510        self
511    }
512
513    /// Copies data between buffer regions.
514    pub fn copy_buffer(
515        mut self,
516        src: impl Into<AnyBufferNode>,
517        dst: impl Into<AnyBufferNode>,
518        regions: impl AsRef<[vk::BufferCopy]> + 'static + Send,
519    ) -> Self {
520        let src = src.into();
521        let dst = dst.into();
522        let regions = Arc::<[vk::BufferCopy]>::from(regions.as_ref());
523
524        #[cfg(feature = "checked")]
525        let src_size = self.graph.resources[src.index()].expect_buffer_info().size;
526
527        #[cfg(feature = "checked")]
528        let dst_size = self.graph.resources[dst.index()].expect_buffer_info().size;
529
530        for region in regions.iter() {
531            #[cfg(feature = "checked")]
532            {
533                assert!(
534                    region.src_offset + region.size <= src_size,
535                    "source range end ({}) exceeds source size ({src_size})",
536                    region.src_offset + region.size
537                );
538                assert!(
539                    region.dst_offset + region.size <= dst_size,
540                    "destination range end ({}) exceeds destination size ({dst_size})",
541                    region.dst_offset + region.size
542                );
543            };
544
545            self.set_subresource_access(
546                src,
547                region.src_offset..region.src_offset + region.size,
548                AccessType::TransferRead,
549            );
550            self.set_subresource_access(
551                dst,
552                region.dst_offset..region.dst_offset + region.size,
553                AccessType::TransferWrite,
554            );
555        }
556
557        self.record_stream_mut(move |cmd| {
558            let src = cmd.resource(src);
559            let dst = cmd.resource(dst);
560
561            unsafe {
562                cmd.device
563                    .cmd_copy_buffer(cmd.handle, src.handle, dst.handle, &regions);
564            }
565        });
566        self
567    }
568
569    /// Copies data from a buffer into image regions.
570    pub fn copy_buffer_to_image(
571        mut self,
572        src: impl Into<AnyBufferNode>,
573        dst: impl Into<AnyImageNode>,
574        regions: impl AsRef<[vk::BufferImageCopy]> + 'static + Send,
575    ) -> Self {
576        let src = src.into();
577        let dst = dst.into();
578        let dst_info = self.graph.resources[dst.index()].expect_image_info();
579        let regions = Arc::<[vk::BufferImageCopy]>::from(regions.as_ref());
580
581        for region in regions.iter() {
582            let block_bytes_size = format_texel_block_size(dst_info.format);
583            let (block_height, block_width) = format_texel_block_extent(dst_info.format);
584            let data_size = block_bytes_size
585                * (region.buffer_row_length / block_width)
586                * (region.buffer_image_height / block_height);
587
588            self.set_subresource_access(
589                src,
590                region.buffer_offset..region.buffer_offset + data_size as vk::DeviceSize,
591                AccessType::TransferRead,
592            );
593            self.set_subresource_access(
594                dst,
595                image_subresource_range_from_layers(region.image_subresource),
596                AccessType::TransferWrite,
597            );
598        }
599
600        self.record_stream_mut(move |cmd| {
601            let src = cmd.resource(src);
602            let dst = cmd.resource(dst);
603
604            unsafe {
605                cmd.device.cmd_copy_buffer_to_image(
606                    cmd.handle,
607                    src.handle,
608                    dst.handle,
609                    vk::ImageLayout::TRANSFER_DST_OPTIMAL,
610                    &regions,
611                );
612            }
613        });
614        self
615    }
616
617    /// Copies data between image regions.
618    pub fn copy_image(
619        mut self,
620        src: impl Into<AnyImageNode>,
621        dst: impl Into<AnyImageNode>,
622        regions: impl AsRef<[vk::ImageCopy]> + 'static + Send,
623    ) -> Self {
624        let src = src.into();
625        let dst = dst.into();
626        let regions = Arc::<[vk::ImageCopy]>::from(regions.as_ref());
627
628        for region in regions.iter() {
629            self.set_subresource_access(
630                src,
631                image_subresource_range_from_layers(region.src_subresource),
632                AccessType::TransferRead,
633            );
634            self.set_subresource_access(
635                dst,
636                image_subresource_range_from_layers(region.dst_subresource),
637                AccessType::TransferWrite,
638            );
639        }
640
641        self.record_stream_mut(move |cmd| {
642            let src = cmd.resource(src);
643            let dst = cmd.resource(dst);
644
645            unsafe {
646                cmd.device.cmd_copy_image(
647                    cmd.handle,
648                    src.handle,
649                    vk::ImageLayout::TRANSFER_SRC_OPTIMAL,
650                    dst.handle,
651                    vk::ImageLayout::TRANSFER_DST_OPTIMAL,
652                    &regions,
653                );
654            }
655        });
656        self
657    }
658
659    /// Copies image region data into a buffer.
660    pub fn copy_image_to_buffer(
661        mut self,
662        src: impl Into<AnyImageNode>,
663        dst: impl Into<AnyBufferNode>,
664        regions: impl AsRef<[vk::BufferImageCopy]> + 'static + Send,
665    ) -> Self {
666        let src = src.into();
667        let src_info = self.graph.resources[src.index()].expect_image_info();
668        let dst = dst.into();
669        let regions = Arc::<[vk::BufferImageCopy]>::from(regions.as_ref());
670
671        for region in regions.iter() {
672            let block_bytes_size = format_texel_block_size(src_info.format);
673            let (block_height, block_width) = format_texel_block_extent(src_info.format);
674            let data_size = block_bytes_size
675                * (region.buffer_row_length / block_width)
676                * (region.buffer_image_height / block_height);
677
678            self.set_subresource_access(
679                src,
680                image_subresource_range_from_layers(region.image_subresource),
681                AccessType::TransferRead,
682            );
683            self.set_subresource_access(
684                dst,
685                region.buffer_offset..region.buffer_offset + data_size as vk::DeviceSize,
686                AccessType::TransferWrite,
687            );
688        }
689
690        self.record_stream_mut(move |cmd| {
691            let src = cmd.resource(src);
692            let dst = cmd.resource(dst);
693
694            unsafe {
695                cmd.device.cmd_copy_image_to_buffer(
696                    cmd.handle,
697                    src.handle,
698                    vk::ImageLayout::TRANSFER_SRC_OPTIMAL,
699                    dst.handle,
700                    &regions,
701                );
702            }
703        });
704        self
705    }
706
707    /// Fills a region of a buffer with a fixed value.
708    pub fn fill_buffer(
709        mut self,
710        buffer: impl Into<AnyBufferNode>,
711        region: Range<vk::DeviceSize>,
712        data: u32,
713    ) -> Self {
714        let buffer = buffer.into();
715
716        self.set_subresource_access(buffer, region.clone(), AccessType::TransferWrite);
717        self.record_stream_mut(move |cmd| {
718            let buffer = cmd.resource(buffer);
719
720            unsafe {
721                cmd.device.cmd_fill_buffer(
722                    cmd.handle,
723                    buffer.handle,
724                    region.start,
725                    region.end - region.start,
726                    data,
727                );
728            }
729        });
730        self
731    }
732
733    pub(crate) fn record_stream(
734        mut self,
735        func: impl for<'r> Fn(CommandRef<'r>) + Send + Sync + 'static,
736    ) -> Self {
737        self.record_stream_mut(func);
738        self
739    }
740
741    pub(crate) fn record_stream_mut(
742        &mut self,
743        func: impl for<'r> Fn(CommandRef<'r>) + Send + Sync + 'static,
744    ) {
745        self.push_reusable_exec(func);
746    }
747
748    /// Returns a borrow of the original Vulkan resource (buffer, image or acceleration structure)
749    /// which the given bound resource node represents.
750    pub fn resource<N>(&self, resource_node: N) -> &N::Resource
751    where
752        N: Node,
753    {
754        self.graph.resource(resource_node)
755    }
756
757    /// Informs the command that recorded work will read or write `resource_node`
758    /// using `access`.
759    ///
760    /// An access function must be called for `resource_node` before it is used within a recording
761    /// function.
762    pub fn resource_access<N>(mut self, resource_node: N, access: AccessType) -> Self
763    where
764        N: Node + Subresource,
765        SubresourceRange: From<N::Range>,
766    {
767        self.set_resource_access(resource_node, access);
768        self
769    }
770
771    /// Mutable-borrow form of [`Self::debug_name`].
772    pub fn set_debug_name(&mut self, name: impl Into<String>) -> &mut Self {
773        #[cfg(debug_assertions)]
774        {
775            self.cmd_mut().name = Some(name.into());
776        }
777
778        #[cfg(not(debug_assertions))]
779        {
780            let _ = name;
781        }
782
783        self
784    }
785
786    /// Mutable-borrow form of [`Self::resource_access`].
787    pub fn set_resource_access<N>(&mut self, resource_node: N, access: AccessType)
788    where
789        N: Node + Subresource,
790        SubresourceRange: From<N::Range>,
791    {
792        let whole_resource = resource_node.range(&self.graph.resources);
793        let subresource = SubresourceRange::from(whole_resource);
794
795        self.push_subresource_access(resource_node, subresource, access);
796    }
797
798    pub(crate) fn set_stream_scope_id(&mut self, stream_scope_id: u64) {
799        self.cmd_mut().stream_scope_id = Some(stream_scope_id);
800    }
801
802    /// Mutable-borrow form of [`Self::subresource_access`].
803    pub fn set_subresource_access<N>(
804        &mut self,
805        resource_node: N,
806        subresource: impl Into<N::Range>,
807        access: AccessType,
808    ) where
809        N: Node + Subresource,
810        SubresourceRange: From<N::Range>,
811    {
812        let subresource = subresource.into();
813        let subresource = SubresourceRange::from(subresource);
814
815        self.push_subresource_access(resource_node, subresource, access);
816    }
817
818    /// Informs the command that recorded work will read or write the `subresource` of
819    /// `resource_node` using `access`.
820    ///
821    /// An access function must be called for `resource_node` before it is used within a recording
822    /// function.
823    pub fn subresource_access<N>(
824        mut self,
825        resource_node: N,
826        subresource: impl Into<N::Range>,
827        access: AccessType,
828    ) -> Self
829    where
830        N: Node + Subresource,
831        SubresourceRange: From<N::Range>,
832    {
833        self.set_subresource_access(resource_node, subresource, access);
834        self
835    }
836
837    /// Records a [`vkCmdUpdateBuffer`](https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdUpdateBuffer.html)
838    /// command.
839    ///
840    /// Vulkan requires `data` to be at most `65536` bytes.
841    ///
842    /// These constraints are validated by the Vulkan Validation Layer (VVL) when it is active.
843    /// When the `checked` feature is enabled, `vk-graph` also validates the data size and bounds
844    /// before recording the command.
845    #[profiling::function]
846    pub fn update_buffer(
847        mut self,
848        buffer: impl Into<AnyBufferNode>,
849        offset: vk::DeviceSize,
850        data: impl AsRef<[u8]> + 'static + Send,
851    ) -> Self {
852        debug_assert!(data.as_ref().len() <= 64 * 1024);
853
854        let buffer = buffer.into();
855        let data_end = offset + data.as_ref().len() as vk::DeviceSize;
856
857        #[cfg(feature = "checked")]
858        {
859            assert!(
860                data.as_ref().len() <= 64 * 1024,
861                "data length ({}) exceeds vkCmdUpdateBuffer limit (65536)",
862                data.as_ref().len()
863            );
864
865            let buffer_info = self.graph.resources[buffer.index()].expect_buffer_info();
866
867            assert!(
868                data_end <= buffer_info.size,
869                "data range end ({data_end}) exceeds buffer size ({})",
870                buffer_info.size
871            );
872        }
873
874        let data = Arc::<[u8]>::from(data.as_ref());
875
876        self.set_subresource_access(buffer, offset..data_end, AccessType::TransferWrite);
877        self.record_stream_mut(move |cmd| {
878            let buffer = cmd.resource(buffer);
879
880            unsafe {
881                cmd.device
882                    .cmd_update_buffer(cmd.handle, buffer.handle, offset, &data);
883            }
884        });
885        self
886    }
887}
888
889/// Describes the SPIR-V binding index, and optionally a specific descriptor set
890/// and array index.
891///
892/// Generally you might pass a function a descriptor using a simple integer:
893///
894/// ```rust
895/// # fn my_func(_: usize, _: ()) {}
896/// # let image = ();
897/// let descriptor = 42;
898/// my_func(descriptor, image);
899/// ```
900///
901/// But also:
902///
903/// - `(0, 42)` for descriptor set `0` and binding index `42`
904/// - `(42, [8])` for the same binding, but the 8th element
905/// - `(0, 42, [8])` same as the previous example
906#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
907pub struct Binding {
908    /// The value of the descriptor binding decoration applied to the variable.
909    pub binding: u32,
910
911    /// An array-element offset applied to this descriptor.
912    pub offset: u32,
913
914    /// An optional descriptor set index value.
915    pub set: u32,
916}
917
918impl Binding {
919    pub(super) fn into_tuple(self) -> (DescriptorSetIndex, BindingIndex, BindingOffset) {
920        (self.set, self.binding, self.offset)
921    }
922
923    pub(super) fn set(self) -> DescriptorSetIndex {
924        let (res, _, _) = self.into_tuple();
925        res
926    }
927}
928
929impl From<BindingIndex> for Binding {
930    fn from(binding: BindingIndex) -> Self {
931        Self {
932            binding,
933            offset: 0,
934            set: 0,
935        }
936    }
937}
938
939impl From<(DescriptorSetIndex, BindingIndex)> for Binding {
940    fn from((set, binding): (DescriptorSetIndex, BindingIndex)) -> Self {
941        Self {
942            binding,
943            offset: 0,
944            set,
945        }
946    }
947}
948
949impl From<(BindingIndex, [BindingOffset; 1])> for Binding {
950    fn from((binding, [offset]): (BindingIndex, [BindingOffset; 1])) -> Self {
951        Self {
952            binding,
953            offset,
954            set: 0,
955        }
956    }
957}
958
959impl From<(DescriptorSetIndex, BindingIndex, [BindingOffset; 1])> for Binding {
960    fn from(
961        (set, binding, [offset]): (DescriptorSetIndex, BindingIndex, [BindingOffset; 1]),
962    ) -> Self {
963        Self {
964            binding,
965            offset,
966            set,
967        }
968    }
969}
970
971/// Allows for a resource to be reinterpreted as differently formatted data.
972#[allow(private_bounds)]
973pub trait Subresource: private::SubresourceSealed {
974    /// The information about the subresource when bound directly to shader descriptors.
975    type Info;
976
977    /// The information about the subresource when used indirectly by any part of a graph.
978    type Range;
979}
980
981macro_rules! view_accel_struct {
982    ($name:ty) => {
983        impl Subresource for $name {
984            type Info = Self::Range;
985            type Range = ();
986        }
987
988        impl private::SubresourceSealed for $name {
989            fn info(&self, _: &[AnyResource]) -> <Self as Subresource>::Info
990            where
991                Self: Node + Subresource,
992            {
993            }
994
995            fn range(&self, resources: &[AnyResource]) -> <Self as Subresource>::Range
996            where
997                Self: Node + Subresource,
998            {
999                resources[self.index()].expect_accel_struct_info();
1000            }
1001        }
1002    };
1003}
1004
1005view_accel_struct!(AnyAccelerationStructureNode);
1006view_accel_struct!(AccelerationStructureArg);
1007view_accel_struct!(AccelerationStructureLeaseNode);
1008view_accel_struct!(AccelerationStructureNode);
1009
1010macro_rules! view_buffer {
1011    ($name:ty) => {
1012        impl Subresource for $name {
1013            type Info = Self::Range;
1014            type Range = BufferSubresourceRange;
1015        }
1016
1017        impl private::SubresourceSealed for $name {
1018            fn info(&self, resources: &[AnyResource]) -> <Self as Subresource>::Info
1019            where
1020                Self: Node + Subresource,
1021            {
1022                self.range(resources)
1023            }
1024
1025            fn range(&self, resources: &[AnyResource]) -> <Self as Subresource>::Range
1026            where
1027                Self: Node + Subresource,
1028            {
1029                let idx = self.index();
1030
1031                resources[idx].expect_buffer_info().into()
1032            }
1033        }
1034    };
1035}
1036
1037view_buffer!(AnyBufferNode);
1038view_buffer!(BufferArg);
1039view_buffer!(BufferLeaseNode);
1040view_buffer!(BufferNode);
1041
1042macro_rules! view_image {
1043    ($name:ty) => {
1044        impl Subresource for $name {
1045            type Info = ImageViewInfo;
1046            type Range = vk::ImageSubresourceRange;
1047        }
1048
1049        impl private::SubresourceSealed for $name {
1050            fn info(&self, resources: &[AnyResource]) -> <Self as Subresource>::Info
1051            where
1052                Self: Node + Subresource,
1053            {
1054                let idx = self.index();
1055
1056                resources[idx].expect_image_info().into()
1057            }
1058
1059            fn range(&self, resources: &[AnyResource]) -> <Self as Subresource>::Range
1060            where
1061                Self: Node + Subresource,
1062            {
1063                self.info(resources).into()
1064            }
1065        }
1066    };
1067}
1068
1069view_image!(AnyImageNode);
1070view_image!(ImageArg);
1071view_image!(ImageLeaseNode);
1072view_image!(ImageNode);
1073view_image!(SwapchainImageNode);
1074
1075#[derive(Clone, Copy, Debug)]
1076pub(crate) enum SubresourceRange {
1077    /// Acceleration structures are bound whole.
1078    AccelerationStructure,
1079
1080    /// Images may be partially bound.
1081    Image(vk::ImageSubresourceRange),
1082
1083    /// Buffers may be partially bound.
1084    Buffer(BufferSubresourceRange),
1085}
1086
1087impl SubresourceRange {
1088    pub(super) fn as_image(&self) -> Option<&vk::ImageSubresourceRange> {
1089        if let Self::Image(subresource) = self {
1090            Some(subresource)
1091        } else {
1092            None
1093        }
1094    }
1095
1096    pub(super) fn expect_image(&self) -> &vk::ImageSubresourceRange {
1097        self.as_image().expect("missing image subresource")
1098    }
1099}
1100
1101impl From<BufferSubresourceRange> for SubresourceRange {
1102    fn from(subresource: BufferSubresourceRange) -> Self {
1103        Self::Buffer(subresource)
1104    }
1105}
1106
1107impl From<()> for SubresourceRange {
1108    fn from(_: ()) -> Self {
1109        Self::AccelerationStructure
1110    }
1111}
1112
1113impl From<ImageViewInfo> for SubresourceRange {
1114    fn from(subresource: ImageViewInfo) -> Self {
1115        Self::Image(subresource.into())
1116    }
1117}
1118
1119impl From<vk::ImageSubresourceRange> for SubresourceRange {
1120    fn from(subresource: vk::ImageSubresourceRange) -> Self {
1121        Self::Image(subresource)
1122    }
1123}
1124
1125#[derive(Clone, Copy, Debug)]
1126pub(super) struct SubresourceAccess {
1127    pub access: AccessType,
1128    pub subresource: SubresourceRange,
1129}
1130
1131/// Describes the interpretation of a resource.
1132#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1133pub(crate) enum ViewInfo {
1134    /// Acceleration structures are always whole resources.
1135    AccelerationStructure,
1136
1137    /// Images may be interpreted as differently formatted images.
1138    Image(ImageViewInfo),
1139
1140    /// Buffers may be interpreted as subregions of the same buffer.
1141    Buffer(BufferSubresourceRange),
1142}
1143
1144impl ViewInfo {
1145    pub(crate) fn as_buffer(&self) -> Option<&BufferSubresourceRange> {
1146        match self {
1147            Self::Buffer(info) => Some(info),
1148            _ => None,
1149        }
1150    }
1151
1152    pub(crate) fn as_image(&self) -> Option<&ImageViewInfo> {
1153        match self {
1154            Self::Image(info) => Some(info),
1155            _ => None,
1156        }
1157    }
1158
1159    pub(crate) fn expect_buffer(&self) -> &BufferSubresourceRange {
1160        self.as_buffer().expect("missing buffer view info")
1161    }
1162
1163    pub(crate) fn expect_image(&self) -> &ImageViewInfo {
1164        self.as_image().expect("missing image view info")
1165    }
1166}
1167
1168impl From<()> for ViewInfo {
1169    fn from(_: ()) -> Self {
1170        Self::AccelerationStructure
1171    }
1172}
1173
1174impl From<BufferSubresourceRange> for ViewInfo {
1175    fn from(info: BufferSubresourceRange) -> Self {
1176        Self::Buffer(info)
1177    }
1178}
1179
1180impl From<ImageViewInfo> for ViewInfo {
1181    fn from(info: ImageViewInfo) -> Self {
1182        Self::Image(info)
1183    }
1184}
1185
1186impl From<Range<vk::DeviceSize>> for ViewInfo {
1187    fn from(range: Range<vk::DeviceSize>) -> Self {
1188        Self::Buffer(BufferSubresourceRange {
1189            start: range.start,
1190            end: range.end,
1191        })
1192    }
1193}
1194
1195mod private {
1196    use crate::{AnyResource, Node};
1197
1198    pub(crate) trait SubresourceSealed: Sized {
1199        fn info(&self, resources: &[AnyResource]) -> <Self as super::Subresource>::Info
1200        where
1201            Self: Node + super::Subresource;
1202
1203        fn range(&self, resources: &[AnyResource]) -> <Self as super::Subresource>::Range
1204        where
1205            Self: Node + super::Subresource;
1206    }
1207}