Skip to main content

vk_graph/cmd/
pipeline.rs

1use {
2    super::{
3        AccessType, Binding, Command, Graph, Node, Resource, Subresource, SubresourceRange,
4        ViewInfo,
5    },
6    crate::{
7        ExecutionPipeline, TimestampQuery,
8        driver::{
9            compute::ComputePipeline, descriptor_set::DescriptorSet, graphics::GraphicsPipeline,
10            ray_tracing::RayTracingPipeline,
11        },
12    },
13    std::marker::PhantomData,
14};
15
16/// A trait for pipelines which may be bound to a `Command`.
17///
18/// See [`Command::bind_pipeline`](crate::cmd::Command::bind_pipeline) for details.
19pub trait Pipeline<'a> {
20    /// The resource reference type.
21    type Command;
22
23    /// Binds the resource to a command.
24    ///
25    /// Returns a reference type.
26    fn bind_cmd(self, _: Command<'a>) -> Self::Command;
27}
28
29macro_rules! pipeline {
30    ($variant:ident, $pipeline:ident, $is_fn:ident, $unwrap_fn:ident) => {
31        paste::paste! {
32            impl<'a> Pipeline<'a> for $pipeline {
33                type Command = PipelineCommand<'a, $pipeline>;
34
35                fn bind_cmd(self, mut cmd: Command<'a>) -> Self::Command {
36                    {
37                        let cmd = cmd.cmd_mut();
38                        if cmd.expect_last_exec().pipeline.is_some() {
39                            cmd.execs.push(Default::default());
40                        }
41
42                        cmd.expect_last_exec_mut().pipeline = Some(ExecutionPipeline::$variant(self));
43                    }
44
45                    Self::Command {
46                        __: PhantomData,
47                        cmd,
48                    }
49                }
50            }
51
52            impl<'a> Pipeline<'a> for &'a $pipeline {
53                type Command = PipelineCommand<'a, $pipeline>;
54
55                fn bind_cmd(self, mut cmd: Command<'a>) -> Self::Command {
56                    {
57                        let cmd = cmd.cmd_mut();
58                        if cmd.expect_last_exec().pipeline.is_some() {
59                            cmd.execs.push(Default::default());
60                        }
61
62                        cmd.expect_last_exec_mut().pipeline
63                            = Some(ExecutionPipeline::$variant(self.clone()));
64                    }
65
66                    Self::Command {
67                        __: PhantomData,
68                        cmd,
69                    }
70                }
71
72            }
73
74            impl ExecutionPipeline {
75                #[allow(unused)]
76                pub(crate) fn $is_fn(&self) -> bool {
77                    matches!(self, Self::$variant(_))
78                }
79
80                #[allow(unused)]
81                pub(crate) fn $unwrap_fn(&self) -> &$pipeline {
82                    if let Self::$variant(binding) = self {
83                        &binding
84                    } else {
85                        panic!();
86                    }
87                }
88            }
89        }
90    };
91}
92
93// Pipelines you can bind to a command ref
94pipeline!(Compute, ComputePipeline, is_compute, unwrap_compute);
95pipeline!(Graphics, GraphicsPipeline, is_graphics, unwrap_graphics);
96pipeline!(
97    RayTracing,
98    RayTracingPipeline,
99    is_ray_tracing,
100    unwrap_ray_tracing
101);
102
103/// A [`Command`] which has been bound to a particular compute, graphics, or ray tracing pipeline.
104pub struct PipelineCommand<'c, T> {
105    pub(super) __: PhantomData<T>,
106    pub(super) cmd: Command<'c>,
107}
108
109// NOTE: There are specific implementations of T in the compute, graphics, and ray tracing modules
110#[allow(private_bounds)]
111impl<'c, T> PipelineCommand<'c, T> {
112    /// Binds an explicitly populated descriptor set to this and subsequent executions using the
113    /// current pipeline.
114    ///
115    /// This does not declare graph resource accesses. Use [`Self::resource_access`] for every
116    /// resource the recorded work accesses through the set.
117    ///
118    /// # Panics
119    ///
120    /// Panics if the descriptor set index does not exist in the current pipeline or its layout is
121    /// incompatible.
122    pub fn bind_descriptor_set(mut self, descriptor_set: &DescriptorSet) -> Self {
123        self.set_descriptor_set(descriptor_set);
124        self
125    }
126
127    /// Mutable-borrow form of [`Self::bind_descriptor_set`].
128    pub fn set_descriptor_set(&mut self, descriptor_set: &DescriptorSet) -> &mut Self {
129        let set = descriptor_set.info().set;
130        let exec = self.cmd.cmd_mut().expect_last_exec_mut();
131        let pipeline = exec.pipeline.as_ref().expect("missing command pipeline");
132        let layout = pipeline
133            .descriptor_info()
134            .layouts
135            .get(&set)
136            .unwrap_or_else(|| panic!("pipeline descriptor set {set} does not exist"));
137
138        assert!(
139            descriptor_set.is_compatible(set, layout),
140            "descriptor set {set} is incompatible with the bound pipeline"
141        );
142
143        exec.descriptor_sets.insert(set, descriptor_set.clone());
144
145        self
146    }
147
148    /// Equivalent to [`Command::bind_pipeline`] for a command that already has a bound pipeline.
149    pub fn bind_pipeline<P>(self, pipeline: P) -> P::Command
150    where
151        P: Pipeline<'c>,
152    {
153        pipeline.bind_cmd(self.cmd)
154    }
155
156    /// Equivalent to [`Command::bind_resource`] for a command that already has a bound pipeline.
157    pub fn bind_resource<R>(&mut self, resource: R) -> R::Node
158    where
159        R: Resource,
160    {
161        self.cmd.bind_resource(resource)
162    }
163
164    /// Equivalent to [`Command::write_timestamp`] for a command that already has a bound pipeline.
165    pub fn write_timestamp(&mut self) -> TimestampQuery {
166        self.cmd.write_timestamp()
167    }
168
169    /// Equivalent to [`Command::end_cmd`] for a command that already has a bound pipeline.
170    pub fn end_cmd(self) -> &'c mut Graph {
171        self.cmd.end_cmd()
172    }
173
174    /// Equivalent to [`Command::resource`] for a command that already has a bound pipeline.
175    pub fn resource<N>(&self, resource_node: N) -> &N::Resource
176    where
177        N: Node,
178    {
179        self.cmd.resource(resource_node)
180    }
181
182    /// Informs the command that recorded work will read or write `resource_node` using `access`.
183    ///
184    /// An access function must be called for `resource_node` before it is used within a recording
185    /// function.
186    pub fn resource_access<N>(mut self, resource_node: N, access: AccessType) -> Self
187    where
188        N: Node + Subresource,
189        SubresourceRange: From<N::Range>,
190    {
191        self.cmd.set_resource_access(resource_node, access);
192        self
193    }
194
195    /// Mutable-borrow form of [`Self::resource_access`].
196    pub fn set_resource_access<N>(&mut self, resource_node: N, access: AccessType) -> &mut Self
197    where
198        N: Node + Subresource,
199        SubresourceRange: From<N::Range>,
200    {
201        self.cmd.set_resource_access(resource_node, access);
202        self
203    }
204
205    /// Mutable-borrow form of [`Self::shader_resource_access`].
206    pub fn set_shader_resource_access<N>(
207        &mut self,
208        binding: impl Into<Binding>,
209        resource_node: N,
210        access: AccessType,
211    ) -> &mut Self
212    where
213        N: Node + Subresource,
214        N::Info: Copy,
215        SubresourceRange: From<N::Info>,
216        ViewInfo: From<N::Info>,
217    {
218        let subresource = resource_node.info(&self.cmd.graph.resources);
219
220        self.set_shader_subresource_access(binding, resource_node, subresource, access)
221    }
222
223    /// Mutable-borrow form of [`Self::shader_subresource_access`].
224    pub fn set_shader_subresource_access<N>(
225        &mut self,
226        binding: impl Into<Binding>,
227        resource_node: N,
228        subresource: impl Into<N::Info>,
229        access: AccessType,
230    ) -> &mut Self
231    where
232        N: Node + Subresource,
233        N::Info: Copy,
234        SubresourceRange: From<N::Info>,
235        ViewInfo: From<N::Info>,
236    {
237        let binding = binding.into();
238        let subresource = subresource.into();
239        let node_idx = resource_node.index();
240        let view_info = subresource.into();
241
242        self.cmd.push_subresource_access(
243            resource_node,
244            SubresourceRange::from(subresource),
245            access,
246        );
247
248        #[cfg(feature = "checked")]
249        {
250            if let Some(prev) = self.cmd.cmd().expect_last_exec().bindings.get(&binding) {
251                assert!(
252                    *prev == (node_idx, view_info),
253                    "shader binding {binding:?} already bound to a different resource or view"
254                );
255            }
256        }
257
258        self.cmd
259            .cmd_mut()
260            .expect_last_exec_mut()
261            .bindings
262            .insert(binding, (node_idx, view_info));
263
264        self
265    }
266
267    /// Mutable-borrow form of [`Self::subresource_access`].
268    pub fn set_subresource_access<N>(
269        &mut self,
270        resource_node: N,
271        subresource: impl Into<N::Range>,
272        access: AccessType,
273    ) -> &mut Self
274    where
275        N: Node + Subresource,
276        SubresourceRange: From<N::Range>,
277    {
278        self.cmd
279            .set_subresource_access(resource_node, subresource, access);
280        self
281    }
282
283    /// Informs the command that recorded work will read or write the `resource_node` at the
284    /// specified shader `binding` using `access`.
285    ///
286    /// If the same `binding` slot is used more than once, the last call wins and the previous
287    /// binding is silently overwritten.
288    ///
289    /// An access function must be called for `resource_node` before it is used within a recording
290    /// function.
291    pub fn shader_resource_access<N>(
292        mut self,
293        binding: impl Into<Binding>,
294        resource_node: N,
295        access: AccessType,
296    ) -> Self
297    where
298        N: Node + Subresource,
299        N::Info: Copy,
300        SubresourceRange: From<N::Info>,
301        ViewInfo: From<N::Info>,
302    {
303        self.set_shader_resource_access(binding, resource_node, access);
304        self
305    }
306
307    /// Informs the command that recorded work will read or write the `resource_node` at the
308    /// specified shader `binding` using `access`. The resource will be interpreted using
309    /// `view_info`.
310    ///
311    /// If the same `binding` slot is used more than once, the last call wins and the previous
312    /// binding is silently overwritten.
313    ///
314    /// An access function must be called for `resource_node` before it is used within a recording
315    /// function.
316    pub fn shader_subresource_access<N>(
317        mut self,
318        binding: impl Into<Binding>,
319        resource_node: N,
320        subresource: impl Into<N::Info>,
321        access: AccessType,
322    ) -> Self
323    where
324        N: Node + Subresource,
325        N::Info: Copy,
326        SubresourceRange: From<N::Info>,
327        ViewInfo: From<N::Info>,
328    {
329        self.set_shader_subresource_access(binding, resource_node, subresource, access);
330        self
331    }
332
333    /// Informs the command that recorded work will read or write the `subresource` of
334    /// `resource_node` using `access`.
335    ///
336    /// An access function must be called for `resource_node` before it is used within a recording
337    /// function.
338    pub fn subresource_access<N>(
339        mut self,
340        resource_node: N,
341        subresource: impl Into<N::Range>,
342        access: AccessType,
343    ) -> Self
344    where
345        N: Node + Subresource,
346        SubresourceRange: From<N::Range>,
347    {
348        self.cmd
349            .set_subresource_access(resource_node, subresource, access);
350        self
351    }
352}