rendy_graph/node/
mod.rs

1//! Defines node - building block for framegraph.
2//!
3
4pub mod present;
5pub mod render;
6
7use {
8    crate::{
9        command::{Capability, Families, Family, FamilyId, Fence, Queue, Submission, Submittable},
10        factory::{Factory, UploadError},
11        frame::Frames,
12        graph::GraphContext,
13        wsi::SwapchainError,
14        BufferId, ImageId, NodeId,
15    },
16    rendy_core::hal::{queue::QueueFamilyId, Backend},
17};
18
19/// Buffer access node will perform.
20/// Node must not perform any access to the buffer not specified in `access`.
21/// All access must be between logically first and last `stages`.
22#[derive(Clone, Copy, Debug)]
23pub struct BufferAccess {
24    /// Access flags.
25    pub access: rendy_core::hal::buffer::Access,
26
27    /// Intended usage flags for buffer.
28    /// TODO: Could derive from access?
29    pub usage: rendy_core::hal::buffer::Usage,
30
31    /// Pipeline stages at which buffer is accessd.
32    pub stages: rendy_core::hal::pso::PipelineStage,
33}
34
35/// Buffer pipeline barrier.
36#[derive(Clone, Debug)]
37pub struct BufferBarrier {
38    /// State transition for the buffer.
39    pub states: std::ops::Range<rendy_core::hal::buffer::State>,
40
41    /// Stages at which buffer is accessd.
42    pub stages: std::ops::Range<rendy_core::hal::pso::PipelineStage>,
43
44    /// Transfer between families.
45    pub families: Option<std::ops::Range<QueueFamilyId>>,
46}
47
48/// Buffer shared between nodes.
49///
50/// If Node doesn't actually use the buffer it can merge acquire and release barriers into one.
51/// TODO: Make merge function.
52#[derive(Clone, Debug)]
53pub struct NodeBuffer {
54    /// Id of the buffer.
55    pub id: BufferId,
56
57    /// Region of the buffer that is the transient resource.
58    pub range: std::ops::Range<u64>,
59
60    /// Acquire barrier.
61    /// Node implementation must insert it before first command that uses the buffer.
62    /// Barrier must be inserted even if this node doesn't use the buffer.
63    pub acquire: Option<BufferBarrier>,
64
65    /// Release barrier.
66    /// Node implementation must insert it after last command that uses the buffer.
67    /// Barrier must be inserted even if this node doesn't use the buffer.
68    pub release: Option<BufferBarrier>,
69}
70
71/// Image access node wants to perform.
72#[derive(Clone, Copy, Debug)]
73pub struct ImageAccess {
74    /// Access flags.
75    pub access: rendy_core::hal::image::Access,
76
77    /// Intended usage flags for image.
78    /// TODO: Could derive from access?
79    pub usage: rendy_core::hal::image::Usage,
80
81    /// Preferred layout for access.
82    /// Actual layout will be reported int `NodeImage`.
83    /// Actual layout is guaranteed to support same operations.
84    /// TODO: Could derive from access?
85    pub layout: rendy_core::hal::image::Layout,
86
87    /// Pipeline stages at which image is accessd.
88    pub stages: rendy_core::hal::pso::PipelineStage,
89}
90
91/// Image pipeline barrier.
92/// Node implementation must insert it before first command that uses the image.
93/// Barrier must be inserted even if this node doesn't use the image.
94#[derive(Clone, Debug)]
95pub struct ImageBarrier {
96    /// State transition for the image.
97    pub states: std::ops::Range<rendy_core::hal::image::State>,
98
99    /// Stages at which image is accessd.
100    pub stages: std::ops::Range<rendy_core::hal::pso::PipelineStage>,
101
102    /// Transfer between families.
103    pub families: Option<std::ops::Range<QueueFamilyId>>,
104}
105
106/// Image shared between nodes.
107#[derive(Clone, Debug)]
108pub struct NodeImage {
109    /// Id of the image.
110    pub id: ImageId,
111
112    /// Region of the image that is the transient resource.
113    pub range: rendy_core::hal::image::SubresourceRange,
114
115    /// Image state for node.
116    pub layout: rendy_core::hal::image::Layout,
117
118    /// Specify that node should clear image to this value.
119    pub clear: Option<rendy_core::hal::command::ClearValue>,
120
121    /// Acquire barrier.
122    /// Node implementation must insert it before first command that uses the image.
123    /// Barrier must be inserted even if this node doesn't use the image.
124    pub acquire: Option<ImageBarrier>,
125
126    /// Release barrier.
127    /// Node implementation must insert it after last command that uses the image.
128    /// Barrier must be inserted even if this node doesn't use the image.
129    pub release: Option<ImageBarrier>,
130}
131
132/// NodeSubmittable
133pub trait NodeSubmittable<'a, B: Backend> {
134    /// Submittable type returned from `Node`.
135    type Submittable: Submittable<B> + 'a;
136
137    /// Iterator over submittables returned from `Node`.
138    type Submittables: IntoIterator<Item = Self::Submittable>;
139}
140
141/// The node is building block of the framegraph.
142/// Node defines set of resources and operations to perform over them.
143/// Read-only data for operations comes from auxiliary data source `T`.
144///
145/// # Parameters
146///
147/// `B` - backend type.
148/// `T` - auxiliary data type.
149///
150pub trait Node<B: Backend, T: ?Sized>:
151    for<'a> NodeSubmittable<'a, B> + std::fmt::Debug + Sized + Sync + Send + 'static
152{
153    /// Capability required by node.
154    /// Graph will execute this node on command queue that supports this capability level.
155    type Capability: Capability;
156
157    /// Record commands required by node.
158    /// Returned submits are guaranteed to be submitted within specified frame.
159    fn run<'a>(
160        &'a mut self,
161        ctx: &GraphContext<B>,
162        factory: &Factory<B>,
163        aux: &T,
164        frames: &'a Frames<B>,
165    ) -> <Self as NodeSubmittable<'a, B>>::Submittables;
166
167    /// Dispose of the node.
168    ///
169    /// # Safety
170    ///
171    /// Must be called after waiting for device idle.
172    unsafe fn dispose(self, factory: &mut Factory<B>, aux: &T);
173}
174
175/// Description of the node.
176/// Implementation of the builder type provide framegraph with static information about node
177/// that is used for building the node.
178pub trait NodeDesc<B: Backend, T: ?Sized>: std::fmt::Debug + Sized + 'static {
179    /// Node this builder builds.
180    type Node: Node<B, T>;
181
182    /// Make node builder.
183    fn builder(self) -> DescBuilder<B, T, Self> {
184        DescBuilder::new(self)
185    }
186
187    /// Get set or buffer resources the node uses.
188    fn buffers(&self) -> Vec<BufferAccess> {
189        Vec::new()
190    }
191
192    /// Get set or image resources the node uses.
193    fn images(&self) -> Vec<ImageAccess> {
194        Vec::new()
195    }
196
197    /// Build the node.
198    ///
199    /// # Parameters
200    ///
201    /// `factory`    - factory instance.
202    /// `aux`       - auxiliary data.
203    /// `family`    - id of the family this node will be executed on.
204    /// `resources` - set of transient resources managed by graph.
205    ///               with barriers required for interface resources.
206    ///
207    fn build<'a>(
208        self,
209        ctx: &GraphContext<B>,
210        factory: &mut Factory<B>,
211        family: &mut Family<B>,
212        queue: usize,
213        aux: &T,
214        buffers: Vec<NodeBuffer>,
215        images: Vec<NodeImage>,
216    ) -> Result<Self::Node, NodeBuildError>;
217}
218
219/// Trait-object safe `Node`.
220pub trait DynNode<B: Backend, T: ?Sized>: std::fmt::Debug + Sync + Send {
221    /// Record commands required by node.
222    /// Recorded buffers go into `submits`.
223    unsafe fn run<'a>(
224        &mut self,
225        ctx: &GraphContext<B>,
226        factory: &Factory<B>,
227        queue: &mut Queue<B>,
228        aux: &T,
229        frames: &Frames<B>,
230        waits: &[(&'a B::Semaphore, rendy_core::hal::pso::PipelineStage)],
231        signals: &[&'a B::Semaphore],
232        fence: Option<&mut Fence<B>>,
233    );
234
235    /// Dispose of the node.
236    ///
237    /// # Safety
238    ///
239    /// Must be called after waiting for device idle.
240    unsafe fn dispose(self: Box<Self>, factory: &mut Factory<B>, aux: &T);
241}
242
243impl<B, T, N> DynNode<B, T> for (N,)
244where
245    B: Backend,
246    T: ?Sized,
247    N: Node<B, T>,
248{
249    unsafe fn run<'a>(
250        &mut self,
251        ctx: &GraphContext<B>,
252        factory: &Factory<B>,
253        queue: &mut Queue<B>,
254        aux: &T,
255        frames: &Frames<B>,
256        waits: &[(&'a B::Semaphore, rendy_core::hal::pso::PipelineStage)],
257        signals: &[&'a B::Semaphore],
258        fence: Option<&mut Fence<B>>,
259    ) {
260        let submittables = Node::run(&mut self.0, ctx, factory, aux, frames);
261        queue.submit(
262            Some(
263                Submission::new()
264                    .submits(submittables)
265                    .wait(waits.iter().cloned())
266                    .signal(signals.iter().cloned()),
267            ),
268            fence,
269        )
270    }
271
272    unsafe fn dispose(self: Box<Self>, factory: &mut Factory<B>, aux: &T) {
273        N::dispose(self.0, factory, aux);
274    }
275}
276
277/// Error building a node of the graph.
278#[derive(Debug)]
279pub enum NodeBuildError {
280    /// Filed to uplaod the data.
281    Upload(UploadError),
282    /// Mismatched queue family.
283    QueueFamily(FamilyId),
284    /// Failed to create an imate view.
285    View(rendy_core::hal::image::ViewError),
286    /// Failed to create a pipeline.
287    Pipeline(rendy_core::hal::pso::CreationError),
288    /// Failed to create a swap chain.
289    Swapchain(SwapchainError),
290    /// Ran out of memory when creating something.
291    OutOfMemory(rendy_core::hal::device::OutOfMemory),
292}
293
294/// Dynamic node builder that emits `DynNode`.
295pub trait NodeBuilder<B: Backend, T: ?Sized>: std::fmt::Debug {
296    /// Pick family for this node to be executed onto.
297    fn family(&self, factory: &mut Factory<B>, families: &Families<B>) -> Option<FamilyId>;
298
299    /// Get buffer accessed by the node.
300    fn buffers(&self) -> Vec<(BufferId, BufferAccess)>;
301
302    /// Get images accessed by the node.
303    fn images(&self) -> Vec<(ImageId, ImageAccess)>;
304
305    /// Indices of nodes this one dependes on.
306    fn dependencies(&self) -> Vec<NodeId>;
307
308    /// Build node.
309    fn build<'a>(
310        self: Box<Self>,
311        ctx: &GraphContext<B>,
312        factory: &mut Factory<B>,
313        family: &mut Family<B>,
314        queue: usize,
315        aux: &T,
316        buffers: Vec<NodeBuffer>,
317        images: Vec<NodeImage>,
318    ) -> Result<Box<dyn DynNode<B, T>>, NodeBuildError>;
319}
320
321/// Builder for the node.
322pub struct DescBuilder<B: Backend, T: ?Sized, N> {
323    desc: N,
324    buffers: Vec<BufferId>,
325    images: Vec<ImageId>,
326    dependencies: Vec<NodeId>,
327    marker: std::marker::PhantomData<fn(B, &T)>,
328}
329
330impl<B, T, N> std::fmt::Debug for DescBuilder<B, T, N>
331where
332    B: Backend,
333    T: ?Sized,
334    N: std::fmt::Debug,
335{
336    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
337        fmt.debug_struct("DescBuilder")
338            .field("desc", &self.desc)
339            .field("buffers", &self.buffers)
340            .field("images", &self.images)
341            .field("dependencies", &self.dependencies)
342            .finish()
343    }
344}
345
346impl<B, T, N> DescBuilder<B, T, N>
347where
348    B: Backend,
349    T: ?Sized,
350{
351    /// Create new builder out of desc
352    pub fn new(desc: N) -> Self {
353        DescBuilder {
354            desc,
355            buffers: Vec::new(),
356            images: Vec::new(),
357            dependencies: Vec::new(),
358            marker: std::marker::PhantomData,
359        }
360    }
361    /// Add buffer to the node.
362    /// This method must be called for each buffer node uses.
363    pub fn add_buffer(&mut self, buffer: BufferId) -> &mut Self {
364        self.buffers.push(buffer);
365        self
366    }
367
368    /// Add buffer to the node.
369    /// This method must be called for each buffer node uses.
370    pub fn with_buffer(mut self, buffer: BufferId) -> Self {
371        self.add_buffer(buffer);
372        self
373    }
374
375    /// Add image to the node.
376    /// This method must be called for each image node uses.
377    pub fn add_image(&mut self, image: ImageId) -> &mut Self {
378        self.images.push(image);
379        self
380    }
381
382    /// Add image to the node.
383    /// This method must be called for each image node uses.
384    pub fn with_image(mut self, image: ImageId) -> Self {
385        self.add_image(image);
386        self
387    }
388
389    /// Add dependency.
390    /// Node will be placed after its dependencies.
391    pub fn add_dependency(&mut self, dependency: NodeId) -> &mut Self {
392        self.dependencies.push(dependency);
393        self
394    }
395
396    /// Add dependency.
397    /// Node will be placed after its dependencies.
398    pub fn with_dependency(mut self, dependency: NodeId) -> Self {
399        self.add_dependency(dependency);
400        self
401    }
402}
403
404impl<B, T, N> NodeBuilder<B, T> for DescBuilder<B, T, N>
405where
406    B: Backend,
407    T: ?Sized,
408    N: NodeDesc<B, T>,
409{
410    fn family(&self, _factory: &mut Factory<B>, families: &Families<B>) -> Option<FamilyId> {
411        families.with_capability::<<N::Node as Node<B, T>>::Capability>()
412    }
413
414    fn buffers(&self) -> Vec<(BufferId, BufferAccess)> {
415        let desc_buffers = self.desc.buffers();
416        assert_eq!(self.buffers.len(), desc_buffers.len());
417
418        self.buffers.iter().cloned().zip(desc_buffers).collect()
419    }
420
421    fn images(&self) -> Vec<(ImageId, ImageAccess)> {
422        let desc_images = self.desc.images();
423        assert_eq!(self.images.len(), desc_images.len());
424
425        self.images.iter().cloned().zip(desc_images).collect()
426    }
427
428    fn dependencies(&self) -> Vec<NodeId> {
429        self.dependencies.clone()
430    }
431
432    fn build<'a>(
433        self: Box<Self>,
434        ctx: &GraphContext<B>,
435        factory: &mut Factory<B>,
436        family: &mut Family<B>,
437        queue: usize,
438        aux: &T,
439        buffers: Vec<NodeBuffer>,
440        images: Vec<NodeImage>,
441    ) -> Result<Box<dyn DynNode<B, T>>, NodeBuildError> {
442        Ok(Box::new((self.desc.build(
443            ctx, factory, family, queue, aux, buffers, images,
444        )?,)))
445    }
446}
447
448/// Convert graph barriers into gfx barriers.
449pub fn gfx_acquire_barriers<'a, 'b, B: Backend>(
450    ctx: &'a GraphContext<B>,
451    buffers: impl IntoIterator<Item = &'b NodeBuffer>,
452    images: impl IntoIterator<Item = &'b NodeImage>,
453) -> (
454    std::ops::Range<rendy_core::hal::pso::PipelineStage>,
455    Vec<rendy_core::hal::memory::Barrier<'a, B>>,
456) {
457    let mut bstart = rendy_core::hal::pso::PipelineStage::empty();
458    let mut bend = rendy_core::hal::pso::PipelineStage::empty();
459
460    let mut istart = rendy_core::hal::pso::PipelineStage::empty();
461    let mut iend = rendy_core::hal::pso::PipelineStage::empty();
462
463    let barriers: Vec<rendy_core::hal::memory::Barrier<'_, B>> = buffers
464        .into_iter()
465        .filter_map(|buffer| {
466            buffer.acquire.as_ref().map(|acquire| {
467                bstart |= acquire.stages.start;
468                bend |= acquire.stages.end;
469
470                rendy_core::hal::memory::Barrier::Buffer {
471                    states: acquire.states.clone(),
472                    families: acquire.families.clone(),
473                    target: ctx
474                        .get_buffer(buffer.id)
475                        .expect("Buffer does not exist")
476                        .raw(),
477                    range: Some(buffer.range.start)..Some(buffer.range.end),
478                }
479            })
480        })
481        .chain(images.into_iter().filter_map(|image| {
482            image.acquire.as_ref().map(|acquire| {
483                istart |= acquire.stages.start;
484                iend |= acquire.stages.end;
485
486                rendy_core::hal::memory::Barrier::Image {
487                    states: acquire.states.clone(),
488                    families: acquire.families.clone(),
489                    target: ctx.get_image(image.id).expect("Image does not exist").raw(),
490                    range: image.range.clone(),
491                }
492            })
493        }))
494        .collect();
495
496    (bstart | istart..bend | iend, barriers)
497}
498
499/// Convert graph barriers into gfx barriers.
500pub fn gfx_release_barriers<'a, B: Backend>(
501    ctx: &'a GraphContext<B>,
502    buffers: impl IntoIterator<Item = &'a NodeBuffer>,
503    images: impl IntoIterator<Item = &'a NodeImage>,
504) -> (
505    std::ops::Range<rendy_core::hal::pso::PipelineStage>,
506    Vec<rendy_core::hal::memory::Barrier<'a, B>>,
507) {
508    let mut bstart = rendy_core::hal::pso::PipelineStage::empty();
509    let mut bend = rendy_core::hal::pso::PipelineStage::empty();
510
511    let mut istart = rendy_core::hal::pso::PipelineStage::empty();
512    let mut iend = rendy_core::hal::pso::PipelineStage::empty();
513
514    let barriers: Vec<rendy_core::hal::memory::Barrier<'_, B>> = buffers
515        .into_iter()
516        .filter_map(|buffer| {
517            buffer.release.as_ref().map(|release| {
518                bstart |= release.stages.start;
519                bend |= release.stages.end;
520
521                rendy_core::hal::memory::Barrier::Buffer {
522                    states: release.states.clone(),
523                    families: release.families.clone(),
524                    target: ctx
525                        .get_buffer(buffer.id)
526                        .expect("Buffer does not exist")
527                        .raw(),
528                    range: Some(buffer.range.start)..Some(buffer.range.end),
529                }
530            })
531        })
532        .chain(images.into_iter().filter_map(|image| {
533            image.release.as_ref().map(|release| {
534                istart |= release.stages.start;
535                iend |= release.stages.end;
536
537                rendy_core::hal::memory::Barrier::Image {
538                    states: release.states.clone(),
539                    families: release.families.clone(),
540                    target: ctx.get_image(image.id).expect("Image does not exist").raw(),
541                    range: image.range.clone(),
542                }
543            })
544        }))
545        .collect();
546
547    (bstart | istart..bend | iend, barriers)
548}