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