Skip to main content

vk_graph/
node.rs

1//! Handles for Vulkan smart-pointer resources.
2//!
3//! When you bind a resource to a [`Graph`](crate::Graph), you get back a node handle:
4//!
5//! ```no_run
6//! # use std::sync::Arc;
7//! # use vk_graph::{Graph, driver::{buffer::Buffer, image::Image}, node::{BufferNode, ImageNode}};
8//! # let mut graph = Graph::new();
9//! # let my_buffer: Arc<Buffer> = todo!();
10//! # let my_image: Arc<Image> = todo!();
11//! let buf_node: BufferNode = graph.bind_resource(my_buffer);
12//! let img_node: ImageNode   = graph.bind_resource(my_image);
13//! ```
14//!
15//! These handles are then passed to command-building methods like
16//! [`resource_access`](crate::cmd::PipelineCommand::resource_access) or
17//! [`shader_resource_access`](crate::cmd::PipelineCommand::shader_resource_access).
18//!
19//! ## Node kinds
20//!
21//! | Handle | Resource type | Use case |
22//! |---|---|:--|
23//! | [`BufferNode`] | Owned [`Buffer`] | Most common |
24//! | [`ImageNode`] | Owned [`Image`] | Most common |
25//! | [`AccelerationStructureNode`] | Owned [`AccelerationStructure`] | Ray tracing |
26//! | [`SwapchainImageNode`] | [`SwapchainImage`] | Swapchain presentation |
27//! | [`BufferLeaseNode`], [`ImageLeaseNode`], [`AccelerationStructureLeaseNode`] | Pool-leased resource | Pool-based allocation |
28//! | [`AnyBufferNode`], [`AnyImageNode`], [`AnyAccelerationStructureNode`] | Any of the above | Heterogeneous collections |
29//!
30//! For most users, [`BufferNode`] and [`ImageNode`] are all you need. The `Lease` and
31//! `Any*` variants exist for advanced pooling and dynamic dispatch scenarios.
32//!
33//! When borrowing resources back out of a graph with [`Graph::resource`](crate::Graph::resource),
34//! concrete node types return the exact stored handle type, while `Any*` node types return a
35//! borrow of the underlying resource. For example, `BufferNode` yields `&Arc<Buffer>`, but
36//! `AnyBufferNode` yields `&Buffer`.
37
38use std::sync::Arc;
39
40use crate::{
41    Node,
42    driver::{
43        accel_struct::{AccelerationStructure, AccelerationStructureSyncInfo},
44        buffer::{Buffer, BufferSyncInfo},
45        image::{Image, ImageSyncInfo},
46        swapchain::SwapchainImage,
47    },
48    pool::Lease,
49    private,
50    stream::{AccelerationStructureArg, BufferArg, ImageArg},
51};
52
53#[cfg(feature = "checked")]
54use crate::GraphId;
55
56use super::{AnyResource, NodeIndex};
57
58/// Specifies either an owned acceleration structure or one obtained from a pool.
59#[derive(Clone, Copy, Debug)]
60pub enum AnyAccelerationStructureNode {
61    /// An acceleration structure supplied as a command stream argument.
62    Arg(AccelerationStructureArg),
63
64    /// An owned acceleration structure.
65    Owned(AccelerationStructureNode),
66
67    /// An acceleration structure obtained from a pool.
68    Pooled(AccelerationStructureLeaseNode),
69}
70
71impl From<AccelerationStructureNode> for AnyAccelerationStructureNode {
72    fn from(node: AccelerationStructureNode) -> Self {
73        Self::Owned(node)
74    }
75}
76
77impl From<AccelerationStructureArg> for AnyAccelerationStructureNode {
78    fn from(node: AccelerationStructureArg) -> Self {
79        Self::Arg(node)
80    }
81}
82
83impl From<AccelerationStructureLeaseNode> for AnyAccelerationStructureNode {
84    fn from(node: AccelerationStructureLeaseNode) -> Self {
85        Self::Pooled(node)
86    }
87}
88
89impl private::NodeSealed for AnyAccelerationStructureNode {
90    fn borrow(self, resources: &[AnyResource]) -> &<Self as Node>::Resource {
91        resources[self.index()].expect_accel_struct()
92    }
93
94    fn borrow_at(self, resources: &[AnyResource], index: usize) -> &<Self as Node>::Resource {
95        resources[index].expect_accel_struct()
96    }
97
98    #[cfg(feature = "checked")]
99    fn assert_owner(&self, graph_id: GraphId) {
100        match self {
101            Self::Arg(node) => node.assert_owner(graph_id),
102            Self::Owned(node) => node.assert_owner(graph_id),
103            Self::Pooled(node) => node.assert_owner(graph_id),
104        }
105    }
106}
107
108impl Node for AnyAccelerationStructureNode {
109    type Resource = AccelerationStructure;
110    type SyncInfo = AccelerationStructureSyncInfo;
111
112    fn index(&self) -> usize {
113        match self {
114            Self::Arg(node) => node.index(),
115            Self::Owned(node) => node.index(),
116            Self::Pooled(node) => node.index(),
117        }
118    }
119}
120
121/// Specifies either an owned buffer or one obtained from a pool.
122#[derive(Clone, Copy, Debug)]
123pub enum AnyBufferNode {
124    /// A buffer supplied as a command stream argument.
125    Arg(BufferArg),
126
127    /// An owned buffer.
128    Owned(BufferNode),
129
130    /// A buffer obtained from a pool.
131    Pooled(BufferLeaseNode),
132}
133
134impl From<BufferNode> for AnyBufferNode {
135    fn from(node: BufferNode) -> Self {
136        Self::Owned(node)
137    }
138}
139
140impl From<BufferArg> for AnyBufferNode {
141    fn from(node: BufferArg) -> Self {
142        Self::Arg(node)
143    }
144}
145
146impl From<BufferLeaseNode> for AnyBufferNode {
147    fn from(node: BufferLeaseNode) -> Self {
148        Self::Pooled(node)
149    }
150}
151
152impl private::NodeSealed for AnyBufferNode {
153    fn borrow(self, resources: &[AnyResource]) -> &<Self as Node>::Resource {
154        resources[self.index()].expect_buffer()
155    }
156
157    fn borrow_at(self, resources: &[AnyResource], index: usize) -> &<Self as Node>::Resource {
158        resources[index].expect_buffer()
159    }
160
161    #[cfg(feature = "checked")]
162    fn assert_owner(&self, graph_id: GraphId) {
163        match self {
164            Self::Arg(node) => node.assert_owner(graph_id),
165            Self::Owned(node) => node.assert_owner(graph_id),
166            Self::Pooled(node) => node.assert_owner(graph_id),
167        }
168    }
169}
170
171impl Node for AnyBufferNode {
172    type Resource = Buffer;
173    type SyncInfo = BufferSyncInfo;
174
175    fn index(&self) -> usize {
176        match self {
177            Self::Arg(node) => node.index(),
178            Self::Owned(node) => node.index(),
179            Self::Pooled(node) => node.index(),
180        }
181    }
182}
183
184/// Specifies either an owned image or one obtained from a pool.
185///
186/// The image may also be a special swapchain type of image.
187#[derive(Clone, Copy, Debug)]
188pub enum AnyImageNode {
189    /// An image supplied as a command stream argument.
190    Arg(ImageArg),
191
192    /// An owned image.
193    Owned(ImageNode),
194
195    /// An image obtained from a pool.
196    Pooled(ImageLeaseNode),
197
198    /// A special swapchain image.
199    Swapchain(SwapchainImageNode),
200}
201
202impl From<ImageNode> for AnyImageNode {
203    fn from(node: ImageNode) -> Self {
204        Self::Owned(node)
205    }
206}
207
208impl From<ImageArg> for AnyImageNode {
209    fn from(node: ImageArg) -> Self {
210        Self::Arg(node)
211    }
212}
213
214impl From<ImageLeaseNode> for AnyImageNode {
215    fn from(node: ImageLeaseNode) -> Self {
216        Self::Pooled(node)
217    }
218}
219
220impl From<SwapchainImageNode> for AnyImageNode {
221    fn from(node: SwapchainImageNode) -> Self {
222        Self::Swapchain(node)
223    }
224}
225
226impl private::NodeSealed for AnyImageNode {
227    fn borrow(self, resources: &[AnyResource]) -> &<Self as Node>::Resource {
228        resources[self.index()].expect_image()
229    }
230
231    fn borrow_at(self, resources: &[AnyResource], index: usize) -> &<Self as Node>::Resource {
232        resources[index].expect_image()
233    }
234
235    #[cfg(feature = "checked")]
236    fn assert_owner(&self, graph_id: GraphId) {
237        match self {
238            Self::Arg(node) => node.assert_owner(graph_id),
239            Self::Owned(node) => node.assert_owner(graph_id),
240            Self::Pooled(node) => node.assert_owner(graph_id),
241            Self::Swapchain(node) => node.assert_owner(graph_id),
242        }
243    }
244}
245
246impl Node for AnyImageNode {
247    type Resource = Image;
248    type SyncInfo = ImageSyncInfo;
249
250    fn index(&self) -> usize {
251        match self {
252            Self::Arg(node) => node.index(),
253            Self::Owned(node) => node.index(),
254            Self::Pooled(node) => node.index(),
255            Self::Swapchain(node) => node.index(),
256        }
257    }
258}
259
260/// A type-erased graph node for any buffer, image, or acceleration structure.
261#[derive(Clone, Copy, Debug)]
262pub enum AnyNode {
263    /// An acceleration-structure node.
264    AccelerationStructure(AnyAccelerationStructureNode),
265
266    /// A buffer node.
267    Buffer(AnyBufferNode),
268
269    /// An image node, including swapchain image nodes.
270    Image(AnyImageNode),
271}
272
273macro_rules! any_node_from {
274    ($node:ty => $variant:ident) => {
275        impl From<$node> for AnyNode {
276            fn from(node: $node) -> Self {
277                Self::$variant(node.into())
278            }
279        }
280    };
281}
282
283any_node_from!(AnyAccelerationStructureNode => AccelerationStructure);
284any_node_from!(AnyBufferNode => Buffer);
285any_node_from!(AnyImageNode => Image);
286
287any_node_from!(AccelerationStructureNode => AccelerationStructure);
288any_node_from!(AccelerationStructureLeaseNode => AccelerationStructure);
289any_node_from!(BufferNode => Buffer);
290any_node_from!(BufferLeaseNode => Buffer);
291any_node_from!(ImageNode => Image);
292any_node_from!(ImageLeaseNode => Image);
293any_node_from!(SwapchainImageNode => Image);
294
295macro_rules! node {
296    ($name:ident, $resource:ty, $sync_info:ty, $fn_name:ident) => {
297        paste::paste! {
298            /// A graph-local handle for a bound resource.
299            ///
300            /// Node handles are only valid with the graph that produced them.
301            ///
302            /// When the `checked` feature is enabled, using a node with a different graph will
303            /// panic immediately.
304            ///
305            /// When `checked` is disabled, this ownership check is skipped for zero-overhead
306            /// builds, so cross-graph node misuse is invalid and may resolve to the wrong resource.
307            #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
308            pub struct [<$name Node>] {
309                index: NodeIndex,
310
311                #[cfg(feature = "checked")]
312                graph_id: GraphId,
313            }
314
315            impl [<$name Node>] {
316                pub(crate) fn new(
317                    index: usize,
318                    #[cfg(feature = "checked")] graph_id: GraphId,
319                ) -> Self {
320                    Self {
321                        index,
322
323                        #[cfg(feature = "checked")]
324                        graph_id,
325                    }
326                }
327            }
328
329            impl private::NodeSealed for [<$name Node>] {
330                fn borrow(self, resources: &[AnyResource]) -> &<Self as Node>::Resource {
331                    let AnyResource::$name(res) = &resources[self.index] else {
332                        panic!("invalid resource node handle");
333                    };
334
335                    res
336                }
337
338                fn borrow_at(
339                    self,
340                    resources: &[AnyResource],
341                    index: usize,
342                ) -> &<Self as Node>::Resource {
343                    let AnyResource::$name(res) = &resources[index] else {
344                        panic!("invalid resource node handle");
345                    };
346
347                    res
348                }
349
350                #[cfg(feature = "checked")]
351                fn assert_owner(&self, _graph_id: GraphId) {
352                    #[cfg(feature = "checked")]
353                    assert!(self.graph_id == _graph_id, "node belongs to a different graph");
354                }
355            }
356
357            impl Node for [<$name Node>] {
358                type Resource = $resource;
359                type SyncInfo = $sync_info;
360
361                fn index(&self) -> usize {
362                    self.index
363                }
364            }
365        }
366    };
367}
368
369node!(
370    AccelerationStructure,
371    Arc<AccelerationStructure>,
372    AccelerationStructureSyncInfo,
373    as_accel_struct
374);
375node!(
376    AccelerationStructureLease,
377    Arc<Lease<AccelerationStructure>>,
378    AccelerationStructureSyncInfo,
379    as_accel_struct
380);
381node!(Buffer, Arc<Buffer>, BufferSyncInfo, as_buffer);
382node!(BufferLease, Arc<Lease<Buffer>>, BufferSyncInfo, as_buffer);
383node!(Image, Arc<Image>, ImageSyncInfo, as_image);
384node!(ImageLease, Arc<Lease<Image>>, ImageSyncInfo, as_image);
385node!(
386    SwapchainImage,
387    SwapchainImage,
388    ImageSyncInfo,
389    as_swapchain_image
390);