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