rendy_graph/node/render/
pass.rs

1use {
2    crate::{
3        command::{
4            CommandBuffer, CommandPool, ExecutableState, Families, Family, FamilyId, Fence,
5            Graphics, IndividualReset, MultiShot, NoSimultaneousUse, PendingState, Queue, QueueId,
6            SecondaryLevel, SimultaneousUse, Submission, Submit,
7        },
8        core::{
9            hal::{device::Device as _, image::Layout, Backend},
10            uses_pipeline_barriers,
11        },
12        factory::Factory,
13        frame::{
14            cirque::{CirqueRef, CommandCirque},
15            Frames,
16        },
17        graph::GraphContext,
18        node::{
19            gfx_acquire_barriers, gfx_release_barriers,
20            render::group::{RenderGroup, RenderGroupBuilder},
21            BufferAccess, DynNode, ImageAccess, NodeBuffer, NodeBuildError, NodeBuilder, NodeImage,
22        },
23        wsi::{Surface, Target},
24        BufferId, ImageId, NodeId,
25    },
26    either::Either,
27    std::{cmp::min, collections::HashMap},
28};
29
30#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
31struct RenderPassSurface;
32
33type Attachment = Either<ImageId, RenderPassSurface>;
34
35/// Build for rendering sub-pass.
36pub struct SubpassBuilder<B: Backend, T: ?Sized> {
37    groups: Vec<Box<dyn RenderGroupBuilder<B, T>>>,
38    inputs: Vec<Attachment>,
39    colors: Vec<Attachment>,
40    depth_stencil: Option<Attachment>,
41    dependencies: Vec<NodeId>,
42}
43
44impl<B, T> std::fmt::Debug for SubpassBuilder<B, T>
45where
46    B: Backend,
47    T: ?Sized,
48{
49    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50        fmt.debug_struct("SubpassBuilder")
51            .field("groups", &self.groups)
52            .field("inputs", &self.inputs)
53            .field("colors", &self.colors)
54            .field("depth_stencil", &self.depth_stencil)
55            .field("dependencies", &self.dependencies)
56            .finish()
57    }
58}
59
60impl<B, T> Default for SubpassBuilder<B, T>
61where
62    B: Backend,
63    T: ?Sized,
64{
65    fn default() -> Self {
66        SubpassBuilder {
67            groups: Vec::default(),
68            inputs: Vec::default(),
69            colors: Vec::default(),
70            depth_stencil: None,
71            dependencies: Vec::default(),
72        }
73    }
74}
75
76impl<B, T> SubpassBuilder<B, T>
77where
78    B: Backend,
79    T: ?Sized,
80{
81    /// Create new empty subpass builder.
82    pub fn new() -> Self {
83        Self::default()
84    }
85
86    /// Add render group to this subpass.
87    pub fn add_group<R>(&mut self, group: R) -> &mut Self
88    where
89        R: RenderGroupBuilder<B, T> + 'static,
90    {
91        self.groups.push(Box::new(group));
92        self
93    }
94
95    /// Add render group to this subpass.
96    pub fn with_group<R>(mut self, group: R) -> Self
97    where
98        R: RenderGroupBuilder<B, T> + 'static,
99    {
100        self.add_group(group);
101        self
102    }
103
104    /// Add render group of dynamic type to this subpass.
105    pub fn add_dyn_group(&mut self, group: Box<dyn RenderGroupBuilder<B, T>>) -> &mut Self {
106        self.groups.push(group);
107        self
108    }
109
110    /// Add render group of dynamic type to this subpass.
111    pub fn with_dyn_group(mut self, group: Box<dyn RenderGroupBuilder<B, T>>) -> Self {
112        self.add_dyn_group(group);
113        self
114    }
115
116    /// Add input attachment to the subpass.
117    pub fn add_input(&mut self, input: ImageId) -> &mut Self {
118        self.inputs.push(Either::Left(input));
119        self
120    }
121
122    /// Add input attachment to the subpass.
123    pub fn with_input(mut self, input: ImageId) -> Self {
124        self.add_input(input);
125        self
126    }
127
128    /// Add color attachment to the subpass.
129    pub fn add_color(&mut self, color: ImageId) -> &mut Self {
130        self.colors.push(Either::Left(color));
131        self
132    }
133
134    /// Add color attachment to the subpass.
135    pub fn with_color(mut self, color: ImageId) -> Self {
136        self.add_color(color);
137        self
138    }
139
140    /// Add surface as color attachment to the subpass.
141    pub fn add_color_surface(&mut self) -> &mut Self {
142        self.colors.push(Either::Right(RenderPassSurface));
143        self
144    }
145
146    /// Add surface as color attachment to the subpass.
147    pub fn with_color_surface(mut self) -> Self {
148        self.add_color_surface();
149        self
150    }
151
152    /// Set depth-stencil attachment to the subpass.
153    pub fn set_depth_stencil(&mut self, depth_stencil: ImageId) -> &mut Self {
154        self.depth_stencil = Some(Either::Left(depth_stencil));
155        self
156    }
157
158    /// Set depth-stencil attachment to the subpass.
159    pub fn with_depth_stencil(mut self, depth_stencil: ImageId) -> Self {
160        self.set_depth_stencil(depth_stencil);
161        self
162    }
163
164    /// Set surface as depth-stencil attachment to the subpass.
165    pub fn set_depth_stencil_surface(&mut self) -> &mut Self {
166        self.depth_stencil = Some(Either::Right(RenderPassSurface));
167        self
168    }
169
170    /// Set surface as depth-stencil attachment to the subpass.
171    pub fn with_depth_stencil_surface(mut self) -> Self {
172        self.set_depth_stencil_surface();
173        self
174    }
175
176    /// Add dependency.
177    /// `RenderPassNode` will be placed after its dependencies.
178    pub fn add_dependency(&mut self, dependency: NodeId) -> &mut Self {
179        self.dependencies.push(dependency);
180        self
181    }
182
183    /// Add dependency.
184    /// `RenderPassNode` will be placed after its dependencies.
185    pub fn with_dependency(mut self, dependency: NodeId) -> Self {
186        self.add_dependency(dependency);
187        self
188    }
189
190    /// Make render pass from subpass.
191    pub fn into_pass(self) -> RenderPassNodeBuilder<B, T> {
192        RenderPassNodeBuilder::new().with_subpass(self)
193    }
194}
195
196/// Builder for render-pass node.
197pub struct RenderPassNodeBuilder<B: Backend, T: ?Sized> {
198    subpasses: Vec<SubpassBuilder<B, T>>,
199    surface: Option<(
200        Surface<B>,
201        rendy_core::hal::window::Extent2D,
202        Option<rendy_core::hal::command::ClearValue>,
203    )>,
204}
205
206impl<B, T> std::fmt::Debug for RenderPassNodeBuilder<B, T>
207where
208    B: Backend,
209    T: ?Sized,
210{
211    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
212        fmt.debug_struct("RenderPassNodeBuilder")
213            .field("subpasses", &self.subpasses)
214            .field("surface", &self.surface)
215            .finish()
216    }
217}
218
219impl<B, T> Default for RenderPassNodeBuilder<B, T>
220where
221    B: Backend,
222    T: ?Sized,
223{
224    fn default() -> Self {
225        RenderPassNodeBuilder {
226            subpasses: Vec::default(),
227            surface: None,
228        }
229    }
230}
231
232impl<B, T> RenderPassNodeBuilder<B, T>
233where
234    B: Backend,
235    T: ?Sized,
236{
237    /// Make render pass node builder.
238    pub fn new() -> Self {
239        Self::default()
240    }
241
242    /// Add sub-pass to the render-pass.
243    pub fn add_subpass(&mut self, subpass: SubpassBuilder<B, T>) -> &mut Self {
244        self.subpasses.push(subpass);
245        self
246    }
247
248    /// Add sub-pass to the render-pass.
249    pub fn with_subpass(mut self, subpass: SubpassBuilder<B, T>) -> Self {
250        self.add_subpass(subpass);
251        self
252    }
253
254    /// Add surface to the render pass.
255    pub fn add_surface(
256        &mut self,
257        surface: Surface<B>,
258        suggested_extent: rendy_core::hal::window::Extent2D,
259        clear: Option<rendy_core::hal::command::ClearValue>,
260    ) -> &mut Self {
261        assert!(
262            self.surface.is_none(),
263            "Only one surface can be attachend to rende pass"
264        );
265        self.surface = Some((surface, suggested_extent, clear));
266        self
267    }
268
269    /// Add surface to the render pass.
270    pub fn with_surface(
271        mut self,
272        surface: Surface<B>,
273        suggested_extent: rendy_core::hal::window::Extent2D,
274        clear: Option<rendy_core::hal::command::ClearValue>,
275    ) -> Self {
276        self.add_surface(surface, suggested_extent, clear);
277        self
278    }
279}
280
281impl<B, T> NodeBuilder<B, T> for RenderPassNodeBuilder<B, T>
282where
283    B: Backend,
284    T: ?Sized + 'static,
285{
286    fn family(&self, _factory: &mut Factory<B>, families: &Families<B>) -> Option<FamilyId> {
287        families.with_capability::<Graphics>()
288    }
289
290    fn buffers(&self) -> Vec<(BufferId, BufferAccess)> {
291        let empty = BufferAccess {
292            access: rendy_core::hal::buffer::Access::empty(),
293            usage: rendy_core::hal::buffer::Usage::empty(),
294            stages: rendy_core::hal::pso::PipelineStage::empty(),
295        };
296        let mut buffers = HashMap::new();
297
298        for subpass in &self.subpasses {
299            for group in &subpass.groups {
300                for (index, access) in group.buffers() {
301                    let entry = buffers.entry(index).or_insert(empty);
302                    entry.access |= access.access;
303                    entry.usage |= access.usage;
304                    entry.stages |= access.stages;
305                }
306            }
307        }
308
309        buffers.into_iter().collect()
310    }
311
312    fn images(&self) -> Vec<(ImageId, ImageAccess)> {
313        let empty = ImageAccess {
314            access: rendy_core::hal::image::Access::empty(),
315            usage: rendy_core::hal::image::Usage::empty(),
316            stages: rendy_core::hal::pso::PipelineStage::empty(),
317            layout: Layout::Undefined,
318        };
319        let mut attachments = HashMap::new();
320        let mut images = HashMap::new();
321
322        for subpass in &self.subpasses {
323            for &id in subpass.inputs.iter().filter_map(|e| e.as_ref().left()) {
324                let entry = attachments.entry(id).or_insert(ImageAccess {
325                    layout: Layout::ShaderReadOnlyOptimal,
326                    ..empty
327                });
328                entry.access |= rendy_core::hal::image::Access::INPUT_ATTACHMENT_READ;
329                entry.usage |= rendy_core::hal::image::Usage::INPUT_ATTACHMENT;
330                entry.stages |= rendy_core::hal::pso::PipelineStage::FRAGMENT_SHADER;
331            }
332
333            for &id in subpass.colors.iter().filter_map(|e| e.as_ref().left()) {
334                let entry = attachments.entry(id).or_insert(ImageAccess {
335                    layout: Layout::ColorAttachmentOptimal,
336                    ..empty
337                });
338                entry.access |= rendy_core::hal::image::Access::COLOR_ATTACHMENT_READ
339                    | rendy_core::hal::image::Access::COLOR_ATTACHMENT_WRITE;
340                entry.usage |= rendy_core::hal::image::Usage::COLOR_ATTACHMENT;
341                entry.stages |= rendy_core::hal::pso::PipelineStage::COLOR_ATTACHMENT_OUTPUT;
342            }
343
344            if let Some(id) = subpass.depth_stencil.and_then(Either::left) {
345                let entry = attachments.entry(id).or_insert(ImageAccess {
346                    layout: Layout::DepthStencilAttachmentOptimal,
347                    ..empty
348                });
349                entry.access |= rendy_core::hal::image::Access::DEPTH_STENCIL_ATTACHMENT_READ
350                    | rendy_core::hal::image::Access::DEPTH_STENCIL_ATTACHMENT_WRITE;
351                entry.usage |= rendy_core::hal::image::Usage::DEPTH_STENCIL_ATTACHMENT;
352                entry.stages |= rendy_core::hal::pso::PipelineStage::EARLY_FRAGMENT_TESTS
353                    | rendy_core::hal::pso::PipelineStage::LATE_FRAGMENT_TESTS;
354            }
355
356            for group in &subpass.groups {
357                for (id, access) in group.images() {
358                    assert!(
359                        !attachments.contains_key(&id),
360                        "Attachment image can't be used otherwise in render pass"
361                    );
362                    let entry = images.entry(id).or_insert(empty);
363                    entry.access |= access.access;
364                    entry.usage |= access.usage;
365                    entry.stages |= access.stages;
366                    entry.layout = common_layout(entry.layout, access.layout);
367                }
368            }
369        }
370
371        attachments.into_iter().chain(images.into_iter()).collect()
372    }
373
374    fn dependencies(&self) -> Vec<NodeId> {
375        let mut dependencies: Vec<_> = self
376            .subpasses
377            .iter()
378            .flat_map(|subpass| {
379                subpass
380                    .dependencies
381                    .iter()
382                    .cloned()
383                    .chain(subpass.groups.iter().flat_map(|group| group.dependencies()))
384            })
385            .collect();
386        dependencies.sort();
387        dependencies.dedup();
388        dependencies
389    }
390
391    fn build<'a>(
392        self: Box<Self>,
393        ctx: &GraphContext<B>,
394        factory: &mut Factory<B>,
395        family: &mut Family<B>,
396        queue: usize,
397        aux: &T,
398        buffers: Vec<NodeBuffer>,
399        images: Vec<NodeImage>,
400    ) -> Result<Box<dyn DynNode<B, T>>, NodeBuildError> {
401        use rendy_core::hal::window::PresentMode;
402
403        let mut surface_color_usage = false;
404        let mut surface_depth_usage = false;
405
406        let (mut surface, suggested_extent, surface_clear) = self
407            .surface
408            .map_or((None, None, None), |(s, e, c)| (Some(s), Some(e), c));
409        log::debug!(
410            "Build render pass node {} surface",
411            surface.as_ref().map_or("without", |_| "with")
412        );
413
414        let mut attachments: Vec<Attachment> = self
415            .subpasses
416            .iter()
417            .flat_map(|subpass| {
418                subpass
419                    .inputs
420                    .iter()
421                    .chain(subpass.colors.iter().inspect(|a| {
422                        surface_color_usage = surface_color_usage || a.is_right();
423                    }))
424                    .chain(subpass.depth_stencil.as_ref().into_iter().inspect(|a| {
425                        surface_depth_usage = surface_depth_usage || a.is_right();
426                    }))
427                    .cloned()
428                    .collect::<Vec<_>>()
429            })
430            .collect();
431
432        let mut surface_usage = rendy_core::hal::image::Usage::empty();
433        if surface_color_usage {
434            surface_usage |= rendy_core::hal::image::Usage::COLOR_ATTACHMENT;
435        }
436        if surface_depth_usage {
437            surface_usage |= rendy_core::hal::image::Usage::DEPTH_STENCIL_ATTACHMENT;
438        }
439
440        if surface.is_some() {
441            log::debug!("Surface usage {:#?}", surface_usage);
442        } else {
443            debug_assert_eq!(surface_usage, rendy_core::hal::image::Usage::empty());
444        }
445
446        attachments.sort();
447        attachments.dedup();
448
449        let find_attachment_node_image = |id: ImageId| -> &NodeImage {
450            images
451                .iter()
452                .find(|a| a.id == id)
453                .expect("Attachment image wasn't provided")
454        };
455
456        let mut framebuffer_width = u32::max_value();
457        let mut framebuffer_height = u32::max_value();
458        let mut framebuffer_layers = u16::max_value();
459
460        let mut node_target = None;
461
462        log::trace!("Configure attachments");
463
464        let views: Vec<_> = attachments
465            .iter()
466            .map(|&attachment| -> Result<Vec<_>, NodeBuildError> {
467                match attachment {
468                    Either::Left(image_id) => {
469                        log::debug!("Image {:?} attachment", image_id);
470
471                        let node_image = find_attachment_node_image(image_id);
472                        let image = ctx.get_image(image_id).expect("Image does not exist");
473                        let extent = image.kind().extent();
474                        framebuffer_width = min(framebuffer_width, extent.width);
475                        framebuffer_height = min(framebuffer_height, extent.height);
476                        framebuffer_layers = min(
477                            framebuffer_layers,
478                            node_image.range.layers.end - node_image.range.layers.start,
479                        );
480                        Ok(vec![unsafe {
481                            factory
482                                .device()
483                                .create_image_view(
484                                    image.raw(),
485                                    rendy_core::hal::image::ViewKind::D2,
486                                    image.format(),
487                                    rendy_core::hal::format::Swizzle::NO,
488                                    rendy_core::hal::image::SubresourceRange {
489                                        // NOTE: Framebuffer must always be created with only one mip level. If image contains multiple levels,
490                                        // only the first one is bound as an attachment.
491                                        // TODO: Allow customizing this behaviour to choose which level to bind.
492                                        levels: 0 .. 1,
493                                        ..node_image.range.clone()
494                                    }
495                                )
496                                .map_err(NodeBuildError::View)?
497                            }])
498                    },
499                    Either::Right(RenderPassSurface) => {
500                        log::trace!("Surface attachment");
501
502                        let surface = surface.take().expect("Render pass should be configured with Surface instance if at least one subpass uses surface attachment");
503                        let surface_extent = unsafe {
504                            surface.extent(factory.physical()).unwrap_or(suggested_extent.expect("Must be set with surface"))
505                        };
506
507                        log::debug!("Surface extent {:#?}", surface_extent);
508
509                        if !factory.surface_support(family.id(), &surface) {
510                            log::warn!("Surface {:?} presentation is unsupported by family {:?} bound to the node", surface, family);
511                            return Err(NodeBuildError::QueueFamily(family.id()));
512                        }
513
514                        let caps = factory.get_surface_capabilities(&surface);
515
516                        let present_mode = match () {
517                            _ if caps.present_modes.contains(PresentMode::FIFO) => PresentMode::FIFO,
518                            _ if caps.present_modes.contains(PresentMode::MAILBOX) => PresentMode::MAILBOX,
519                            _ if caps.present_modes.contains(PresentMode::RELAXED) => PresentMode::RELAXED,
520                            _ if caps.present_modes.contains(PresentMode::IMMEDIATE) => PresentMode::IMMEDIATE,
521                            _ => panic!("No known present modes found"),
522                        };
523
524                        let img_count_caps = caps.image_count;
525                        let image_count = 3.min(*img_count_caps.end()).max(*img_count_caps.start());
526
527                        let target = factory
528                            .create_target(
529                                surface,
530                                surface_extent,
531                                image_count,
532                                present_mode,
533                                surface_usage,
534                            )
535                            .map_err(NodeBuildError::Swapchain)?;
536
537                        framebuffer_width = min(framebuffer_width, target.extent().width);
538                        framebuffer_height = min(framebuffer_height, target.extent().height);
539                        framebuffer_layers = min(
540                            framebuffer_layers,
541                            target.backbuffer()[0].layers(),
542                        );
543
544                        let views = target.backbuffer().iter().map(|image| unsafe {
545                            factory
546                                .device()
547                                .create_image_view(
548                                    image.raw(),
549                                    rendy_core::hal::image::ViewKind::D2,
550                                    image.format(),
551                                    rendy_core::hal::format::Swizzle::NO,
552                                    rendy_core::hal::image::SubresourceRange {
553                                        aspects: image.format().surface_desc().aspects,
554                                        levels: 0 .. 1,
555                                        layers: 0 .. 1,
556                                    },
557                                )
558                                .map_err(NodeBuildError::View)
559                        }).collect::<Result<Vec<_>, NodeBuildError>>()?;
560
561                        node_target = Some(target);
562                        Ok(views)
563                    }
564                }
565            }).collect::<Result<Vec<_>, _>>()?
566            .into_iter().flatten().collect();
567
568        log::trace!("Configure render pass instance");
569
570        let render_pass: B::RenderPass = {
571            let pass_attachments: Vec<_> = attachments
572                .iter()
573                .map(|&attachment| {
574                    let (format, clear, layout, samples) = match attachment {
575                        Either::Left(image_id) => {
576                            let node_image = find_attachment_node_image(image_id);
577                            let image = ctx.get_image(image_id).expect("Image does not exist");
578                            (
579                                image.format(),
580                                node_image.clear,
581                                node_image.layout,
582                                image.kind().num_samples(),
583                            )
584                        }
585                        Either::Right(RenderPassSurface) => (
586                            node_target
587                                .as_ref()
588                                .expect("Expect target created")
589                                .backbuffer()[0]
590                                .format(),
591                            surface_clear,
592                            rendy_core::hal::image::Layout::Present,
593                            1,
594                        ),
595                    };
596
597                    rendy_core::hal::pass::Attachment {
598                        format: Some(format),
599                        ops: rendy_core::hal::pass::AttachmentOps {
600                            load: if clear.is_some() {
601                                rendy_core::hal::pass::AttachmentLoadOp::Clear
602                            } else {
603                                rendy_core::hal::pass::AttachmentLoadOp::Load
604                            },
605                            store: rendy_core::hal::pass::AttachmentStoreOp::Store,
606                        },
607                        stencil_ops: rendy_core::hal::pass::AttachmentOps::DONT_CARE,
608                        layouts: if clear.is_some() {
609                            rendy_core::hal::image::Layout::Undefined..layout
610                        } else {
611                            layout..layout
612                        },
613                        samples,
614                    }
615                })
616                .collect();
617
618            log::debug!("Attachments {:#?}", pass_attachments);
619
620            #[derive(Debug)]
621            struct OwningSubpassDesc {
622                inputs: Vec<(usize, Layout)>,
623                colors: Vec<(usize, Layout)>,
624                depth_stencil: Option<(usize, Layout)>,
625            }
626
627            let subpasses: Vec<_> = self
628                .subpasses
629                .iter()
630                .map(|subpass| OwningSubpassDesc {
631                    inputs: subpass
632                        .inputs
633                        .iter()
634                        .map(|&i| {
635                            (
636                                attachments.iter().position(|&a| a == i).unwrap(),
637                                match i {
638                                    Either::Left(image_id) => {
639                                        find_attachment_node_image(image_id).layout
640                                    }
641                                    Either::Right(RenderPassSurface) => {
642                                        rendy_core::hal::image::Layout::ShaderReadOnlyOptimal
643                                    }
644                                },
645                            )
646                        })
647                        .collect(),
648                    colors: subpass
649                        .colors
650                        .iter()
651                        .map(|&c| {
652                            (
653                                attachments.iter().position(|&a| a == c).unwrap(),
654                                match c {
655                                    Either::Left(image_id) => {
656                                        find_attachment_node_image(image_id).layout
657                                    }
658                                    Either::Right(RenderPassSurface) => {
659                                        rendy_core::hal::image::Layout::ColorAttachmentOptimal
660                                    }
661                                },
662                            )
663                        })
664                        .collect(),
665                    depth_stencil: subpass.depth_stencil.map(|ds| {
666                        (
667                            attachments.iter().position(|&a| a == ds).unwrap(),
668                            match ds {
669                                Either::Left(image_id) => {
670                                    find_attachment_node_image(image_id).layout
671                                }
672                                Either::Right(RenderPassSurface) => {
673                                    rendy_core::hal::image::Layout::DepthStencilAttachmentOptimal
674                                }
675                            },
676                        )
677                    }),
678                })
679                .collect();
680
681            log::debug!("Subpasses {:#?}", subpasses);
682
683            let subpasses: Vec<_> = subpasses
684                .iter()
685                .map(|subpass| rendy_core::hal::pass::SubpassDesc {
686                    inputs: &subpass.inputs[..],
687                    colors: &subpass.colors[..],
688                    depth_stencil: subpass.depth_stencil.as_ref(),
689                    resolves: &[],
690                    preserves: &[],
691                })
692                .collect();
693
694            let result = unsafe {
695                factory
696                    .device()
697                    .create_render_pass(pass_attachments, subpasses, {
698                        assert_eq!(
699                            self.subpasses.len(),
700                            1,
701                            "TODO: Implement subpass dependencies to allow more than one subpass"
702                        );
703                        std::iter::empty::<rendy_core::hal::pass::SubpassDependency>()
704                    })
705            }
706            .unwrap();
707
708            log::trace!("RenderPass instance created");
709            result
710        };
711
712        log::trace!(
713            "Create {} framebuffers",
714            views.len() - attachments.len() + 1
715        );
716
717        // Swapchain image views, if any, are last ones.
718        let mut framebuffers = (attachments.len() - 1..views.len())
719            .map(|i| unsafe {
720                log::trace!(
721                    "Create framebuffer for views {}..{} and {}",
722                    0,
723                    attachments.len() - 1,
724                    i,
725                );
726                factory
727                    .device()
728                    .create_framebuffer(
729                        &render_pass,
730                        views[..attachments.len() - 1].iter().chain(Some(&views[i])),
731                        rendy_core::hal::image::Extent {
732                            width: framebuffer_width,
733                            height: framebuffer_height,
734                            depth: framebuffer_layers as u32, // This is gfx-hal BUG as this parameter actually means framebuffer layers number,
735                        },
736                    )
737                    .map_err(NodeBuildError::OutOfMemory)
738            })
739            .collect::<Result<Vec<_>, _>>()?;
740
741        log::trace!("Collect clears for render pass");
742
743        let clears: Vec<_> = attachments
744            .iter()
745            .filter_map(|&a| match a {
746                Either::Left(image_id) => find_attachment_node_image(image_id).clear,
747                Either::Right(RenderPassSurface) => surface_clear,
748            })
749            .map(Into::into)
750            .collect();
751
752        let mut command_pool = factory
753            .create_command_pool(family)
754            .map_err(NodeBuildError::OutOfMemory)?
755            .with_capability()
756            .expect("Graph must specify family that supports `Graphics`");
757
758        let command_cirque = CommandCirque::new();
759
760        let acquire = if uses_pipeline_barriers::<B>(factory.device()) {
761            let (stages, barriers) = gfx_acquire_barriers(ctx, &buffers, &images);
762
763            if !barriers.is_empty() {
764                let initial = command_pool.allocate_buffers(1).pop().unwrap();
765                let mut recording = initial.begin(MultiShot(SimultaneousUse), ());
766                log::debug!("Acquire {:?} : {:#?}", stages, barriers);
767                unsafe {
768                    recording.encoder().pipeline_barrier(
769                        stages,
770                        rendy_core::hal::memory::Dependencies::empty(),
771                        barriers,
772                    );
773                }
774                let (acquire_submit, acquire_buffer) = recording.finish().submit();
775                Some(BarriersCommands {
776                    buffer: acquire_buffer,
777                    submit: acquire_submit,
778                })
779            } else {
780                None
781            }
782        } else {
783            None
784        };
785
786        let release = if uses_pipeline_barriers::<B>(factory.device()) {
787            let (stages, barriers) = gfx_release_barriers(ctx, &buffers, &images);
788
789            if !barriers.is_empty() {
790                let initial = command_pool.allocate_buffers(1).pop().unwrap();
791                let mut recording = initial.begin(MultiShot(SimultaneousUse), ());
792                log::debug!("Release {:?} : {:#?}", stages, barriers);
793                unsafe {
794                    recording.encoder().pipeline_barrier(
795                        stages,
796                        rendy_core::hal::memory::Dependencies::empty(),
797                        barriers,
798                    );
799                }
800                let (release_submit, release_buffer) = recording.finish().submit();
801                Some(BarriersCommands {
802                    buffer: release_buffer,
803                    submit: release_submit,
804                })
805            } else {
806                None
807            }
808        } else {
809            None
810        };
811
812        let subpasses = self
813            .subpasses
814            .into_iter()
815            .enumerate()
816            .map(|(index, subpass)| {
817                let subpass_colors = subpass.colors.len();
818                let subpass_depth = subpass.depth_stencil.is_some();
819
820                subpass
821                    .groups
822                    .into_iter()
823                    .map(|group| {
824                        assert_eq!(group.colors(), subpass_colors);
825                        assert_eq!(group.depth(), subpass_depth);
826
827                        let buffers: Vec<_> = group
828                            .buffers()
829                            .into_iter()
830                            .map(|(id, _)| {
831                                buffers
832                                    .iter()
833                                    .find(|b| b.id == id)
834                                    .expect("Transient buffer wasn't provided")
835                                    .clone()
836                            })
837                            .collect();
838                        let images: Vec<_> = group
839                            .images()
840                            .into_iter()
841                            .map(|(id, _)| {
842                                images
843                                    .iter()
844                                    .find(|i| i.id == id)
845                                    .expect("Transient image wasn't provided")
846                                    .clone()
847                            })
848                            .collect();
849
850                        group.build(
851                            ctx,
852                            factory,
853                            QueueId {
854                                family: family.id(),
855                                index: queue,
856                            },
857                            aux,
858                            framebuffer_width,
859                            framebuffer_height,
860                            rendy_core::hal::pass::Subpass {
861                                index,
862                                main_pass: &render_pass,
863                            },
864                            buffers,
865                            images,
866                        )
867                    })
868                    .collect::<Result<Vec<_>, _>>()
869                    .map(|groups| SubpassNode { groups })
870            })
871            .collect::<Result<Vec<_>, _>>()
872            .map_err(NodeBuildError::Pipeline)?;
873
874        let node: Box<dyn DynNode<B, T>> = match node_target {
875            Some(target) => {
876                log::debug!("Construct RenderPassNodeWithSurface");
877                Box::new(RenderPassNodeWithSurface {
878                    common: RenderPassNodeCommon {
879                        subpasses,
880
881                        framebuffer_width,
882                        framebuffer_height,
883                        _framebuffer_layers: framebuffer_layers,
884
885                        render_pass,
886                        views,
887                        clears,
888
889                        command_pool,
890                        command_cirque,
891
892                        acquire,
893                        release,
894
895                        relevant: relevant::Relevant,
896                    },
897
898                    per_image: framebuffers
899                        .into_iter()
900                        .map(|fb| PerImage {
901                            framebuffer: fb,
902                            acquire: factory.create_semaphore().unwrap(),
903                            release: factory.create_semaphore().unwrap(),
904                            index: 0,
905                        })
906                        .collect(),
907                    free_acquire: factory.create_semaphore().unwrap(),
908                    target,
909                })
910            }
911            None => {
912                log::debug!("Construct RenderPassNodeWithoutSurface");
913                Box::new(RenderPassNodeWithoutSurface {
914                    common: RenderPassNodeCommon {
915                        subpasses,
916
917                        framebuffer_width,
918                        framebuffer_height,
919                        _framebuffer_layers: framebuffer_layers,
920
921                        render_pass,
922                        views,
923                        clears,
924
925                        command_pool,
926                        command_cirque,
927
928                        acquire,
929                        release,
930
931                        relevant: relevant::Relevant,
932                    },
933                    framebuffer: {
934                        assert_eq!(framebuffers.len(), 1);
935                        framebuffers.remove(0)
936                    },
937                })
938            }
939        };
940
941        Ok(node)
942    }
943}
944
945/// Subpass of the `RenderPassNode`.
946struct SubpassNode<B: Backend, T: ?Sized> {
947    /// RenderGroups of pipelines to exeucte withing subpass.
948    groups: Vec<Box<dyn RenderGroup<B, T>>>,
949}
950
951impl<B, T> std::fmt::Debug for SubpassNode<B, T>
952where
953    B: Backend,
954    T: ?Sized,
955{
956    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
957        fmt.debug_struct("SubpassNode")
958            .field("groups", &self.groups)
959            .finish()
960    }
961}
962
963struct BarriersCommands<B: Backend> {
964    submit: Submit<B, SimultaneousUse, SecondaryLevel>,
965    buffer: CommandBuffer<
966        B,
967        Graphics,
968        PendingState<ExecutableState<MultiShot<SimultaneousUse>>>,
969        SecondaryLevel,
970        IndividualReset,
971    >,
972}
973
974impl<B> std::fmt::Debug for BarriersCommands<B>
975where
976    B: Backend,
977{
978    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
979        fmt.debug_struct("BarriersCommands")
980            .field("submit", &self.submit)
981            .field("buffer", &self.buffer)
982            .finish()
983    }
984}
985
986struct RenderPassNodeCommon<B: Backend, T: ?Sized> {
987    subpasses: Vec<SubpassNode<B, T>>,
988
989    framebuffer_width: u32,
990    framebuffer_height: u32,
991    _framebuffer_layers: u16,
992
993    render_pass: B::RenderPass,
994    views: Vec<B::ImageView>,
995    clears: Vec<rendy_core::hal::command::ClearValue>,
996
997    command_pool: CommandPool<B, Graphics, IndividualReset>,
998    command_cirque: CommandCirque<B, Graphics>,
999
1000    acquire: Option<BarriersCommands<B>>,
1001    release: Option<BarriersCommands<B>>,
1002
1003    relevant: relevant::Relevant,
1004}
1005
1006impl<B, T> std::fmt::Debug for RenderPassNodeCommon<B, T>
1007where
1008    B: Backend,
1009    T: ?Sized,
1010{
1011    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1012        fmt.debug_struct("RenderPassNodeCommon")
1013            .field("subpasses", &self.subpasses)
1014            .field("framebuffer_width", &self.framebuffer_width)
1015            .field("framebuffer_height", &self.framebuffer_height)
1016            .field("_framebuffer_layers", &self._framebuffer_layers)
1017            .field("render_pass", &self.render_pass)
1018            .field("views", &self.views)
1019            .field("clears", &self.clears)
1020            .field("command_pool", &self.command_pool)
1021            .field("command_cirque", &self.command_cirque)
1022            .field("acquire", &self.acquire)
1023            .field("release", &self.release)
1024            .field("relevant", &self.relevant)
1025            .finish()
1026    }
1027}
1028
1029impl<B, T> RenderPassNodeCommon<B, T>
1030where
1031    B: Backend,
1032    T: ?Sized,
1033{
1034    unsafe fn dispose(mut self, factory: &mut Factory<B>, aux: &T) {
1035        self.relevant.dispose();
1036        for subpass in self.subpasses {
1037            for group in subpass.groups {
1038                group.dispose(factory, aux)
1039            }
1040        }
1041        let pool = &mut self.command_pool;
1042        self.command_cirque.dispose(|buffer| {
1043            buffer.either_with(
1044                &mut *pool,
1045                |pool, executable| pool.free_buffers(Some(executable)),
1046                |pool, pending| {
1047                    let executable = pending.mark_complete();
1048                    pool.free_buffers(Some(executable))
1049                },
1050            );
1051        });
1052        if let Some(BarriersCommands { submit, buffer }) = self.acquire.take() {
1053            drop(submit);
1054            let executable = buffer.mark_complete();
1055            pool.free_buffers(Some(executable));
1056        }
1057        if let Some(BarriersCommands { submit, buffer }) = self.release.take() {
1058            drop(submit);
1059            let executable = buffer.mark_complete();
1060            pool.free_buffers(Some(executable));
1061        }
1062        factory.destroy_command_pool(self.command_pool.with_queue_type());
1063
1064        for view in self.views {
1065            factory.device().destroy_image_view(view);
1066        }
1067        factory.device().destroy_render_pass(self.render_pass);
1068    }
1069}
1070
1071#[derive(Debug)]
1072struct PerImage<B: Backend> {
1073    framebuffer: B::Framebuffer,
1074    acquire: B::Semaphore,
1075    release: B::Semaphore,
1076    index: usize,
1077}
1078
1079struct RenderPassNodeWithSurface<B: Backend, T: ?Sized> {
1080    common: RenderPassNodeCommon<B, T>,
1081    per_image: Vec<PerImage<B>>,
1082    free_acquire: B::Semaphore,
1083    target: Target<B>,
1084}
1085
1086impl<B, T> std::fmt::Debug for RenderPassNodeWithSurface<B, T>
1087where
1088    B: Backend,
1089    T: ?Sized,
1090{
1091    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1092        fmt.debug_struct("RenderPassNodeWithSurface")
1093            .field("common", &self.common)
1094            .field("per_image", &self.per_image)
1095            .field("free_acquire", &self.free_acquire)
1096            .field("target", &self.target)
1097            .finish()
1098    }
1099}
1100
1101impl<B, T> DynNode<B, T> for RenderPassNodeWithSurface<B, T>
1102where
1103    B: Backend,
1104    T: ?Sized,
1105{
1106    unsafe fn run<'a>(
1107        &mut self,
1108        _ctx: &GraphContext<B>,
1109        factory: &Factory<B>,
1110        queue: &mut Queue<B>,
1111        aux: &T,
1112        frames: &Frames<B>,
1113        waits: &[(&'a B::Semaphore, rendy_core::hal::pso::PipelineStage)],
1114        signals: &[&'a B::Semaphore],
1115        fence: Option<&mut Fence<B>>,
1116    ) {
1117        let RenderPassNodeWithSurface {
1118            common:
1119                RenderPassNodeCommon {
1120                    subpasses,
1121
1122                    framebuffer_width,
1123                    framebuffer_height,
1124
1125                    render_pass,
1126                    clears,
1127
1128                    command_cirque,
1129                    command_pool,
1130
1131                    acquire,
1132                    release,
1133                    ..
1134                },
1135            target,
1136            free_acquire,
1137            per_image,
1138        } = self;
1139
1140        let next = match target.next_image(&free_acquire) {
1141            Ok(next) => {
1142                log::trace!("Presentable image acquired: {:#?}", next);
1143                std::mem::swap(&mut per_image[next[0] as usize].acquire, free_acquire);
1144                Some(next)
1145            }
1146            Err(err) => {
1147                log::debug!("Swapchain acquisition error: {:#?}", err);
1148                None
1149            }
1150        };
1151
1152        let submit = command_cirque.encode(frames, command_pool, |mut cbuf| {
1153            let index = cbuf.index();
1154
1155            if let Some(next) = &next {
1156                let ref mut for_image = per_image[next[0] as usize];
1157
1158                let force_record = subpasses.iter_mut().enumerate().fold(
1159                    false,
1160                    |force_record, (subpass_index, subpass)| {
1161                        subpass
1162                            .groups
1163                            .iter_mut()
1164                            .fold(force_record, |force_record, group| {
1165                                group
1166                                    .prepare(
1167                                        factory,
1168                                        queue.id(),
1169                                        index,
1170                                        rendy_core::hal::pass::Subpass {
1171                                            index: subpass_index,
1172                                            main_pass: &render_pass,
1173                                        },
1174                                        aux,
1175                                    )
1176                                    .force_record()
1177                                    || force_record
1178                            })
1179                    },
1180                );
1181
1182                if force_record || for_image.index != index {
1183                    for_image.index = index;
1184                    cbuf = CirqueRef::Initial(cbuf.or_reset(|cbuf| cbuf.reset()));
1185                }
1186            }
1187
1188            cbuf.or_init(|cbuf| {
1189                let mut cbuf = cbuf.begin(MultiShot(NoSimultaneousUse), ());
1190                let mut encoder = cbuf.encoder();
1191
1192                if let Some(barriers) = &acquire {
1193                    encoder.execute_commands(std::iter::once(&barriers.submit));
1194                }
1195
1196                if let Some(next) = &next {
1197                    let ref mut for_image = per_image[next[0] as usize];
1198
1199                    let area = rendy_core::hal::pso::Rect {
1200                        x: 0,
1201                        y: 0,
1202                        w: *framebuffer_width as _,
1203                        h: *framebuffer_height as _,
1204                    };
1205
1206                    let mut pass_encoder = encoder.begin_render_pass_inline(
1207                        &render_pass,
1208                        &for_image.framebuffer,
1209                        area,
1210                        &clears,
1211                    );
1212
1213                    subpasses
1214                        .iter_mut()
1215                        .enumerate()
1216                        .for_each(|(subpass_index, subpass)| {
1217                            subpass.groups.iter_mut().for_each(|group| {
1218                                group.draw_inline(
1219                                    pass_encoder.reborrow(),
1220                                    index,
1221                                    rendy_core::hal::pass::Subpass {
1222                                        index: subpass_index,
1223                                        main_pass: &render_pass,
1224                                    },
1225                                    aux,
1226                                )
1227                            })
1228                        });
1229
1230                    drop(pass_encoder);
1231                }
1232
1233                if let Some(barriers) = &release {
1234                    encoder.execute_commands(std::iter::once(&barriers.submit));
1235                }
1236                cbuf.finish()
1237            })
1238        });
1239
1240        log::trace!("Submit render pass");
1241
1242        queue.submit(
1243            Some(
1244                Submission::new()
1245                    .submits(Some(submit))
1246                    .wait(waits.iter().cloned().chain(next.as_ref().map(|n| {
1247                        (
1248                            &per_image[n[0] as usize].acquire,
1249                            rendy_core::hal::pso::PipelineStage::TOP_OF_PIPE,
1250                        )
1251                    })))
1252                    .signal(
1253                        signals
1254                            .iter()
1255                            .cloned()
1256                            .chain(next.as_ref().map(|n| (&per_image[n[0] as usize].release))),
1257                    ),
1258            ),
1259            fence,
1260        );
1261
1262        if let Some(next) = next {
1263            log::trace!("Present");
1264            let ref mut for_image = per_image[next[0] as usize];
1265            if let Err(err) = next.present(queue.raw(), Some(&for_image.release)) {
1266                log::debug!("Swapchain presentation error: {:#?}", err);
1267            }
1268        }
1269    }
1270
1271    unsafe fn dispose(self: Box<Self>, factory: &mut Factory<B>, aux: &T) {
1272        for per_image in self.per_image {
1273            factory.device().destroy_framebuffer(per_image.framebuffer);
1274            factory.destroy_semaphore(per_image.acquire);
1275            factory.destroy_semaphore(per_image.release);
1276        }
1277        self.common.dispose(factory, aux);
1278        factory.destroy_surface(factory.destroy_target(self.target));
1279    }
1280}
1281
1282struct RenderPassNodeWithoutSurface<B: Backend, T: ?Sized> {
1283    common: RenderPassNodeCommon<B, T>,
1284    framebuffer: B::Framebuffer,
1285}
1286
1287impl<B, T> std::fmt::Debug for RenderPassNodeWithoutSurface<B, T>
1288where
1289    B: Backend,
1290    T: ?Sized,
1291{
1292    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1293        fmt.debug_struct("RenderPassNodeWithoutSurface")
1294            .field("common", &self.common)
1295            .field("framebuffer", &self.framebuffer)
1296            .finish()
1297    }
1298}
1299
1300impl<B, T> DynNode<B, T> for RenderPassNodeWithoutSurface<B, T>
1301where
1302    B: Backend,
1303    T: ?Sized,
1304{
1305    unsafe fn run<'a>(
1306        &mut self,
1307        _ctx: &GraphContext<B>,
1308        factory: &Factory<B>,
1309        queue: &mut Queue<B>,
1310        aux: &T,
1311        frames: &Frames<B>,
1312        waits: &[(&'a B::Semaphore, rendy_core::hal::pso::PipelineStage)],
1313        signals: &[&'a B::Semaphore],
1314        fence: Option<&mut Fence<B>>,
1315    ) {
1316        let RenderPassNodeWithoutSurface {
1317            common:
1318                RenderPassNodeCommon {
1319                    subpasses,
1320
1321                    framebuffer_width,
1322                    framebuffer_height,
1323
1324                    render_pass,
1325                    clears,
1326
1327                    command_cirque,
1328                    command_pool,
1329
1330                    acquire,
1331                    release,
1332                    ..
1333                },
1334            framebuffer,
1335        } = self;
1336
1337        let submit = command_cirque.encode(frames, command_pool, |mut cbuf| {
1338            let index = cbuf.index();
1339
1340            let force_record = subpasses.iter_mut().enumerate().fold(
1341                false,
1342                |force_record, (subpass_index, subpass)| {
1343                    subpass
1344                        .groups
1345                        .iter_mut()
1346                        .fold(force_record, |force_record, group| {
1347                            group
1348                                .prepare(
1349                                    factory,
1350                                    queue.id(),
1351                                    index,
1352                                    rendy_core::hal::pass::Subpass {
1353                                        index: subpass_index,
1354                                        main_pass: &render_pass,
1355                                    },
1356                                    aux,
1357                                )
1358                                .force_record()
1359                                || force_record
1360                        })
1361                },
1362            );
1363
1364            if force_record {
1365                cbuf = CirqueRef::Initial(cbuf.or_reset(|cbuf| cbuf.reset()));
1366            }
1367
1368            cbuf.or_init(|cbuf| {
1369                let mut cbuf = cbuf.begin(MultiShot(NoSimultaneousUse), ());
1370                let mut encoder = cbuf.encoder();
1371
1372                if let Some(barriers) = &acquire {
1373                    encoder.execute_commands(std::iter::once(&barriers.submit));
1374                }
1375
1376                let area = rendy_core::hal::pso::Rect {
1377                    x: 0,
1378                    y: 0,
1379                    w: *framebuffer_width as _,
1380                    h: *framebuffer_height as _,
1381                };
1382
1383                let mut pass_encoder =
1384                    encoder.begin_render_pass_inline(&render_pass, framebuffer, area, &clears);
1385
1386                subpasses
1387                    .iter_mut()
1388                    .enumerate()
1389                    .for_each(|(subpass_index, subpass)| {
1390                        subpass.groups.iter_mut().for_each(|group| {
1391                            group.draw_inline(
1392                                pass_encoder.reborrow(),
1393                                index,
1394                                rendy_core::hal::pass::Subpass {
1395                                    index: subpass_index,
1396                                    main_pass: &render_pass,
1397                                },
1398                                aux,
1399                            )
1400                        })
1401                    });
1402
1403                drop(pass_encoder);
1404
1405                if let Some(barriers) = &release {
1406                    encoder.execute_commands(std::iter::once(&barriers.submit));
1407                }
1408                cbuf.finish()
1409            })
1410        });
1411
1412        queue.submit(
1413            Some(
1414                Submission::new()
1415                    .submits(Some(submit))
1416                    .wait(waits.iter().cloned())
1417                    .signal(signals.iter().cloned()),
1418            ),
1419            fence,
1420        );
1421    }
1422
1423    unsafe fn dispose(self: Box<Self>, factory: &mut Factory<B>, aux: &T) {
1424        self.common.dispose(factory, aux);
1425        factory.device().destroy_framebuffer(self.framebuffer);
1426    }
1427}
1428
1429fn common_layout(acc: Layout, layout: Layout) -> Layout {
1430    match (acc, layout) {
1431        (Layout::Undefined, layout) => layout,
1432        (left, right) if left == right => left,
1433        (Layout::DepthStencilReadOnlyOptimal, Layout::DepthStencilAttachmentOptimal) => {
1434            Layout::DepthStencilAttachmentOptimal
1435        }
1436        (Layout::DepthStencilAttachmentOptimal, Layout::DepthStencilReadOnlyOptimal) => {
1437            Layout::DepthStencilAttachmentOptimal
1438        }
1439        (_, _) => Layout::General,
1440    }
1441}