rendy_graph/graph/
mod.rs

1use {
2    crate::{
3        chain,
4        command::{Families, FamilyId, QueueId},
5        core::{device_owned, DeviceId},
6        factory::Factory,
7        frame::{Fences, Frame, Frames},
8        memory::Data,
9        node::{
10            BufferBarrier, DynNode, ImageBarrier, NodeBuffer, NodeBuildError, NodeBuilder,
11            NodeImage,
12        },
13        resource::{
14            Buffer, BufferCreationError, BufferInfo, Handle, Image, ImageCreationError, ImageInfo,
15        },
16        BufferId, ImageId, NodeId,
17    },
18    rendy_core::hal::{queue::QueueFamilyId, Backend},
19    thread_profiler::profile_scope,
20};
21
22#[derive(Debug)]
23struct GraphNode<B: Backend, T: ?Sized> {
24    node: Box<dyn DynNode<B, T>>,
25    queue: (usize, usize),
26}
27
28/// Graph that renders whole frame.
29#[derive(Debug)]
30pub struct Graph<B: Backend, T: ?Sized> {
31    device: DeviceId,
32    nodes: Vec<GraphNode<B, T>>,
33    schedule: chain::Schedule<chain::SyncData<usize, usize>>,
34    semaphores: Vec<B::Semaphore>,
35    frames: Frames<B>,
36    fences: Vec<Fences<B>>,
37    inflight: u32,
38    ctx: GraphContext<B>,
39}
40
41device_owned!(Graph<B, T: ?Sized>);
42
43/// Error building the graph itself or one of it's nodes.
44#[derive(Debug)]
45pub enum GraphBuildError {
46    /// Failed to create a buffer.
47    Buffer(BufferCreationError),
48    /// Failed to create an image.
49    Image(ImageCreationError),
50    /// Failed to create a semaphore.
51    Semaphore(rendy_core::hal::device::OutOfMemory),
52    /// Failed to build a node.
53    Node(NodeBuildError),
54}
55
56/// Graphics context contains all transient resources managed by graph.
57#[derive(Debug)]
58pub struct GraphContext<B: Backend> {
59    buffers: Vec<Option<Handle<Buffer<B>>>>,
60    images: Vec<
61        Option<(
62            Handle<Image<B>>,
63            Option<rendy_core::hal::command::ClearValue>,
64        )>,
65    >,
66    /// Number of potential frames in flight
67    pub frames_in_flight: u32,
68}
69
70impl<B: Backend> GraphContext<B> {
71    fn alloc<'a>(
72        factory: &Factory<B>,
73        chains: &chain::Chains,
74        buffers: impl IntoIterator<Item = &'a BufferInfo>,
75        images: impl IntoIterator<Item = &'a (ImageInfo, Option<rendy_core::hal::command::ClearValue>)>,
76        frames_in_flight: u32,
77    ) -> Result<Self, GraphBuildError> {
78        profile_scope!("alloc");
79
80        log::trace!("Allocate buffers");
81        let buffers: Vec<Option<Handle<Buffer<B>>>> = buffers
82            .into_iter()
83            .enumerate()
84            .map(|(index, info)| {
85                chains
86                    .buffers
87                    .get(&chain::Id(index))
88                    .map(|buffer| {
89                        factory
90                            .create_buffer(
91                                BufferInfo {
92                                    usage: buffer.usage(),
93                                    ..info.clone()
94                                },
95                                Data,
96                            )
97                            .map(|buffer| Some(buffer.into()))
98                    })
99                    .unwrap_or(Ok(None))
100            })
101            .collect::<Result<_, _>>()
102            .map_err(GraphBuildError::Buffer)?;
103
104        log::trace!("Allocate images");
105        let images: Vec<Option<(Handle<Image<B>>, _)>> = images
106            .into_iter()
107            .enumerate()
108            .map(|(index, (info, clear))| {
109                chains
110                    .images
111                    .get(&chain::Id(index))
112                    .map(|image| {
113                        factory
114                            .create_image(
115                                ImageInfo {
116                                    usage: image.usage(),
117                                    ..info.clone()
118                                },
119                                Data,
120                            )
121                            .map(|image| Some((image.into(), *clear)))
122                    })
123                    .unwrap_or(Ok(None))
124            })
125            .collect::<Result<_, _>>()
126            .map_err(GraphBuildError::Image)?;
127
128        Ok(Self {
129            buffers,
130            images,
131            frames_in_flight,
132        })
133    }
134
135    /// Get reference to transient image by id.
136    pub fn get_image(&self, id: ImageId) -> Option<&Handle<Image<B>>> {
137        self.get_image_with_clear(id).map(|(i, _)| i)
138    }
139
140    /// Get reference to transient image and clear value by id.
141    pub fn get_image_with_clear(
142        &self,
143        id: ImageId,
144    ) -> Option<(
145        &Handle<Image<B>>,
146        Option<rendy_core::hal::command::ClearValue>,
147    )> {
148        self.images
149            .get(id.0)
150            .and_then(|x| x.as_ref())
151            .map(|&(ref x, ref y)| (&*x, *y))
152    }
153
154    /// Get reference to transient buffer by id.
155    pub fn get_buffer(&self, id: BufferId) -> Option<&Handle<Buffer<B>>> {
156        self.buffers.get(id.0).and_then(|x| x.as_ref()).map(|x| &*x)
157    }
158}
159
160impl<B, T> Graph<B, T>
161where
162    B: Backend,
163    T: ?Sized,
164{
165    /// Perform graph execution.
166    /// Run every node of the graph and submit resulting command buffers to the queues.
167    pub fn run(&mut self, factory: &mut Factory<B>, families: &mut Families<B>, aux: &T) {
168        profile_scope!("run");
169
170        self.assert_device_owner(factory.device());
171
172        if self.frames.next().index() >= self.inflight as _ {
173            let wait = Frame::with_index(self.frames.next().index() - self.inflight as u64);
174            let ref mut self_fences = self.fences;
175            self.frames.wait_complete(wait, factory, |mut fences| {
176                factory.reset_fences(&mut fences).unwrap();
177                self_fences.push(fences);
178            });
179        }
180
181        let mut fences = self.fences.pop().unwrap_or_else(Fences::<B>::default);
182        let mut fences_used = 0;
183        let ref semaphores = self.semaphores;
184
185        for submission in self.schedule.ordered() {
186            log::trace!("Run node {}", submission.node());
187            let sid = submission.id();
188            let qid = sid.queue();
189
190            let GraphNode { node, queue } = self
191                .nodes
192                .get_mut(submission.node())
193                .expect("Submission references node with out of bound index");
194            debug_assert_eq!(
195                (qid.family(), qid.index()),
196                (QueueFamilyId(queue.0), queue.1),
197                "Node's queue doesn't match schedule"
198            );
199
200            let last_in_queue = sid.index() + 1 == self.schedule.queue(qid).unwrap().len();
201            let fence = if last_in_queue {
202                if fences_used >= fences.len() {
203                    fences.push(factory.create_fence(false).unwrap());
204                }
205                fences_used += 1;
206                Some(&mut fences[fences_used - 1])
207            } else {
208                None
209            };
210
211            unsafe {
212                node.run(
213                    &self.ctx,
214                    factory,
215                    families.family_by_index_mut(queue.0).queue_mut(queue.1),
216                    aux,
217                    &self.frames,
218                    &submission
219                        .sync()
220                        .wait
221                        .iter()
222                        .map(|wait| {
223                            log::trace!(
224                                "Node {} waits for {}",
225                                submission.node(),
226                                *wait.semaphore()
227                            );
228                            (&semaphores[*wait.semaphore()], wait.stage())
229                        })
230                        .collect::<smallvec::SmallVec<[_; 16]>>(),
231                    &submission
232                        .sync()
233                        .signal
234                        .iter()
235                        .map(|signal| {
236                            log::trace!(
237                                "Node {} signals {}",
238                                submission.node(),
239                                *signal.semaphore()
240                            );
241                            &semaphores[*signal.semaphore()]
242                        })
243                        .collect::<smallvec::SmallVec<[_; 16]>>(),
244                    fence,
245                )
246            }
247        }
248
249        fences.truncate(fences_used);
250        self.frames.advance(fences);
251    }
252
253    /// Get queue that will exeute given node.
254    pub fn node_queue(&self, node: NodeId) -> QueueId {
255        let (f, i) = self.nodes[node.0].queue;
256        QueueId {
257            family: FamilyId {
258                device: self.device,
259                index: f,
260            },
261            index: i,
262        }
263    }
264
265    /// Dispose of the `Graph`.
266    pub fn dispose(self, factory: &mut Factory<B>, data: &T) {
267        profile_scope!("dispose");
268
269        self.assert_device_owner(factory.device());
270
271        assert!(factory.wait_idle().is_ok());
272        self.frames.dispose(factory);
273
274        unsafe {
275            // Device is idle.
276            for node in self.nodes {
277                node.node.dispose(factory, data);
278            }
279
280            for semaphore in self.semaphores {
281                factory.destroy_semaphore(semaphore);
282            }
283        }
284        drop(self.device);
285        drop(self.schedule);
286        drop(self.fences);
287        drop(self.inflight);
288        drop(self.ctx);
289    }
290}
291
292/// Build graph from nodes and resource.
293pub struct GraphBuilder<B: Backend, T: ?Sized> {
294    nodes: Vec<Box<dyn NodeBuilder<B, T>>>,
295    buffers: Vec<BufferInfo>,
296    images: Vec<(ImageInfo, Option<rendy_core::hal::command::ClearValue>)>,
297    frames_in_flight: u32,
298}
299
300impl<B, T> Default for GraphBuilder<B, T>
301where
302    B: Backend,
303    T: ?Sized,
304{
305    fn default() -> Self {
306        GraphBuilder {
307            nodes: Vec::default(),
308            buffers: Vec::default(),
309            images: Vec::default(),
310            frames_in_flight: u32::default(),
311        }
312    }
313}
314
315impl<B, T> std::fmt::Debug for GraphBuilder<B, T>
316where
317    B: Backend,
318    T: ?Sized,
319{
320    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
321        fmt.debug_struct("GraphBuilder")
322            .field("nodes", &self.nodes)
323            .field("buffers", &self.buffers)
324            .field("images", &self.images)
325            .field("frames_in_flight", &self.frames_in_flight)
326            .finish()
327    }
328}
329
330impl<B, T> GraphBuilder<B, T>
331where
332    B: Backend,
333    T: ?Sized,
334{
335    /// Create new `GraphBuilder`
336    pub fn new() -> Self {
337        GraphBuilder {
338            nodes: Vec::new(),
339            buffers: Vec::new(),
340            images: Vec::new(),
341            frames_in_flight: 3,
342        }
343    }
344
345    /// Create new buffer owned by graph.
346    pub fn create_buffer(&mut self, size: u64) -> BufferId {
347        profile_scope!("create_buffer");
348
349        self.buffers.push(BufferInfo {
350            size,
351            usage: rendy_core::hal::buffer::Usage::empty(),
352        });
353        BufferId(self.buffers.len() - 1)
354    }
355
356    /// Create new image owned by graph.
357    pub fn create_image(
358        &mut self,
359        kind: rendy_core::hal::image::Kind,
360        levels: rendy_core::hal::image::Level,
361        format: rendy_core::hal::format::Format,
362        clear: Option<rendy_core::hal::command::ClearValue>,
363    ) -> ImageId {
364        profile_scope!("create_image");
365
366        self.images.push((
367            ImageInfo {
368                kind,
369                levels,
370                format,
371                tiling: rendy_core::hal::image::Tiling::Optimal,
372                view_caps: rendy_core::hal::image::ViewCapabilities::empty(),
373                usage: rendy_core::hal::image::Usage::empty(),
374            },
375            clear,
376        ));
377        ImageId(self.images.len() - 1)
378    }
379
380    /// Add node to the graph.
381    pub fn add_node<N: NodeBuilder<B, T> + 'static>(&mut self, builder: N) -> NodeId {
382        self.add_dyn_node(Box::new(builder))
383    }
384
385    /// Add boxed node to the graph.
386    pub fn add_dyn_node(&mut self, builder: Box<dyn NodeBuilder<B, T> + 'static>) -> NodeId {
387        self.nodes.push(builder);
388        NodeId(self.nodes.len() - 1)
389    }
390
391    /// Choose number of frames in flight for the graph
392    pub fn with_frames_in_flight(mut self, frames_in_flight: u32) -> Self {
393        self.frames_in_flight = frames_in_flight;
394        self
395    }
396
397    /// Build `Graph`.
398    ///
399    /// # Parameters
400    ///
401    /// `frames`        - maximum number of frames `Graph` will render simultaneously.
402    ///
403    /// `families`      - `Iterator` of `B::QueueFamily`s.
404    ///
405    /// `device`    - `Device<B>` implementation. `Device<B>` or wrapper.
406    ///
407    /// `aux`       - auxiliary data that `Node`s use.
408    pub fn build(
409        self,
410        factory: &mut Factory<B>,
411        families: &mut Families<B>,
412        aux: &T,
413    ) -> Result<Graph<B, T>, GraphBuildError> {
414        profile_scope!("build");
415
416        log::trace!("Schedule nodes execution");
417        let chain_nodes: Vec<chain::Node> = {
418            profile_scope!("schedule_nodes");
419            self.nodes
420                .iter()
421                .enumerate()
422                .map(|(i, b)| make_chain_node(&**b, i, factory, families))
423                .collect()
424        };
425
426        let chains = chain::collect(chain_nodes, |id| {
427            families.family_by_index(id.0).as_slice().len()
428        });
429        log::trace!("Scheduled nodes execution {:#?}", chains);
430
431        let mut ctx = GraphContext::alloc(
432            factory,
433            &chains,
434            &self.buffers,
435            &self.images,
436            self.frames_in_flight,
437        )?;
438
439        log::trace!("Synchronize");
440
441        let mut semaphores = 0..;
442        let mut schedule = chain::sync(&chains, || {
443            let id = semaphores.next().unwrap();
444            (id, id)
445        });
446        schedule.build_order();
447        log::trace!("Schedule: {:#?}", schedule);
448
449        log::trace!("Build nodes");
450        let mut built_nodes: Vec<_> = (0..self.nodes.len()).map(|_| None).collect();
451        let mut node_descs: Vec<_> = self.nodes.into_iter().map(Some).collect();
452
453        {
454            profile_scope!("build_nodes");
455
456            for family in schedule.iter() {
457                log::trace!("For family {:#?}", family);
458                for queue in family.iter() {
459                    log::trace!("For queue {:#?}", queue.id());
460                    for submission in queue.iter() {
461                        log::trace!("For submission {:#?}", submission.id());
462                        let builder = node_descs[submission.node()].take().unwrap();
463                        log::trace!("Build node {:#?}", builder);
464                        let node = build_node(
465                            &mut ctx,
466                            builder,
467                            factory,
468                            families.family_by_index_mut(family.id().0),
469                            queue.id().index(),
470                            aux,
471                            &chains,
472                            &submission,
473                        )
474                        .map_err(GraphBuildError::Node)?;
475                        log::debug!("Node built: {:#?}", node);
476                        built_nodes[submission.node()] = Some((node, submission.id().queue()));
477                    }
478                }
479            }
480        }
481
482        log::debug!("Create {} semaphores", semaphores.start);
483        let semaphores = (0..semaphores.start)
484            .map(|_| factory.create_semaphore())
485            .collect::<Result<_, _>>()
486            .map_err(GraphBuildError::Semaphore)?;
487
488        Ok(Graph {
489            device: factory.device().id(),
490            ctx,
491            nodes: built_nodes
492                .into_iter()
493                .map(Option::unwrap)
494                .map(|(node, qid)| GraphNode {
495                    node,
496                    queue: (qid.family().0, qid.index()),
497                })
498                .collect(),
499            schedule,
500            semaphores,
501            inflight: self.frames_in_flight,
502            frames: Frames::new(),
503            fences: Vec::new(),
504        })
505    }
506}
507
508fn build_node<'a, B: Backend, T: ?Sized>(
509    ctx: &GraphContext<B>,
510    builder: Box<dyn NodeBuilder<B, T>>,
511    factory: &mut Factory<B>,
512    family: &mut rendy_command::Family<B>,
513    queue: usize,
514    aux: &T,
515    chains: &chain::Chains,
516    submission: &chain::Submission<chain::SyncData<usize, usize>>,
517) -> Result<Box<dyn DynNode<B, T>>, NodeBuildError> {
518    let mut buffer_ids: Vec<_> = builder.buffers().into_iter().map(|(id, _)| id).collect();
519    buffer_ids.sort();
520    buffer_ids.dedup();
521
522    let buffers: Vec<_> = buffer_ids
523        .into_iter()
524        .map(|id| {
525            let chain_id = chain::Id(id.0);
526            let sync = submission.sync();
527            let buffer = ctx
528                .get_buffer(id)
529                .expect("Buffer referenced from at least one node must be instantiated");
530            NodeBuffer {
531                id,
532                range: 0..buffer.size(),
533                acquire: sync.acquire.buffers.get(&chain_id).map(
534                    |chain::Barrier { states, families }| BufferBarrier {
535                        states: states.start.0..states.end.0,
536                        stages: states.start.2..states.end.2,
537                        families: families.clone(),
538                    },
539                ),
540                release: sync.release.buffers.get(&chain_id).map(
541                    |chain::Barrier { states, families }| BufferBarrier {
542                        states: states.start.0..states.end.0,
543                        stages: states.start.2..states.end.2,
544                        families: families.clone(),
545                    },
546                ),
547            }
548        })
549        .collect();
550
551    let mut image_ids: Vec<_> = builder.images().into_iter().map(|(id, _)| id).collect();
552    image_ids.sort();
553    image_ids.dedup();
554
555    let images: Vec<_> = image_ids
556        .into_iter()
557        .map(|id| {
558            let chain_id = chain::Id(id.0);
559            let sync = submission.sync();
560            let link = submission.image_link_index(chain_id);
561            let (image, clear) = ctx
562                .get_image_with_clear(id)
563                .expect("Image referenced from at least one node must be instantiated");
564            NodeImage {
565                id,
566                range: rendy_core::hal::image::SubresourceRange {
567                    aspects: image.format().surface_desc().aspects,
568                    levels: 0..image.levels(),
569                    layers: 0..image.layers(),
570                },
571                layout: chains.images[&chain_id].links()[link]
572                    .submission_state(submission.id())
573                    .layout,
574                clear: if link == 0 { clear } else { None },
575                acquire: sync.acquire.images.get(&chain_id).map(
576                    |chain::Barrier { states, families }| ImageBarrier {
577                        states: (
578                            states.start.0,
579                            if link == 0 {
580                                rendy_core::hal::image::Layout::Undefined
581                            } else {
582                                states.start.1
583                            },
584                        )..(states.end.0, states.end.1),
585                        stages: states.start.2..states.end.2,
586                        families: families.clone(),
587                    },
588                ),
589                release: sync.release.images.get(&chain_id).map(
590                    |chain::Barrier { states, families }| ImageBarrier {
591                        states: (states.start.0, states.start.1)..(states.end.0, states.end.1),
592                        stages: states.start.2..states.end.2,
593                        families: families.clone(),
594                    },
595                ),
596            }
597        })
598        .collect();
599    builder.build(ctx, factory, family, queue, aux, buffers, images)
600}
601
602fn make_chain_node<B, T>(
603    builder: &dyn NodeBuilder<B, T>,
604    id: usize,
605    factory: &mut Factory<B>,
606    families: &Families<B>,
607) -> chain::Node
608where
609    B: Backend,
610    T: ?Sized,
611{
612    let buffers = builder.buffers();
613    let images = builder.images();
614    chain::Node {
615        id,
616        family: QueueFamilyId(builder.family(factory, families).unwrap().index),
617        dependencies: builder.dependencies().into_iter().map(|id| id.0).collect(),
618        buffers: buffers
619            .into_iter()
620            .map(|(id, access)| {
621                (
622                    chain::Id(id.0),
623                    chain::BufferState {
624                        access: access.access,
625                        stages: access.stages,
626                        layout: (),
627                        usage: access.usage,
628                    },
629                )
630            })
631            .collect(),
632        images: images
633            .into_iter()
634            .map(|(id, access)| {
635                (
636                    chain::Id(id.0),
637                    chain::ImageState {
638                        access: access.access,
639                        stages: access.stages,
640                        layout: access.layout,
641                        usage: access.usage,
642                    },
643                )
644            })
645            .collect(),
646    }
647}